feat: console spacing/cards polish, create card, drop kids/jump, borderless entry

This commit is contained in:
Aisland
2026-08-21 01:39:38 +08:00
parent 5196d0f768
commit 3d96a07f9d
7 changed files with 96 additions and 49 deletions
+13 -2
View File
@@ -372,8 +372,8 @@ async function coldPreviewOf(face: any): Promise<string> {
for (const b of blocks) {
const s = typeof b?.text === 'string' ? b.text.trim().replace(/\s+/g, ' ') : ''
if (!s) continue
if (b?.type === 'text') return s.slice(0, 140)
if (!fallback) fallback = s.slice(0, 140)
if (b?.type === 'text') return s.slice(0, 220)
if (!fallback) fallback = s.slice(0, 220)
}
return fallback
}
@@ -986,6 +986,16 @@ function WorktableSection(props: any) {
return cards
}
/** 打开「添加项目」面板(侧栏 + 与控制室创建卡共用):默认父目录 = 当前会话工作目录 */
const openAddPanel = () => {
setAddOpen(true)
setViewOptionsOpen(false)
if (!wsFolderParent) {
const cwd = sessionScopeStore.snapshot?.cwd ?? ''
if (cwd) setWsFolderParent(cwd)
}
}
// 会话作用域(当前会话 + 工作目录)与后台任务:注入分栏引擎环境
// 注意:不走 props.useSessions hook(其宿主包装在部分版本会触发 useSyncExternalStore
// 崩溃),改为 apply 里订阅 ctx.sessions.list 后写入模块级 store,此处直接读取。
@@ -1046,6 +1056,7 @@ function WorktableSection(props: any) {
if (previewTimer != null) { window.clearTimeout(previewTimer); previewTimer = null }
sweepPreviews()
},
onAdd: () => openAddPanel(),
onOpen: (id) => {
if (id === CONSOLE_ID) return
const pr = projectsRef.current.projects
+2
View File
@@ -138,6 +138,7 @@ export const zh = {
'console.createBind': '新建并绑定',
'console.bindFail': '创建失败,请重试',
'console.noSessions': '没有可用对话',
'console.addProject': '添加项目',
'pane.wip': '开发中(后续版本接入)',
'pane.explorerNoCwd': '当前会话没有工作目录',
'pane.openLater': '该类型暂不支持预览',
@@ -299,6 +300,7 @@ export const en = {
'console.createBind': 'Create & bind',
'console.bindFail': 'Creation failed, try again',
'console.noSessions': 'No sessions available',
'console.addProject': 'Add project',
'pane.wip': 'Coming in a later version',
'pane.explorerNoCwd': 'No working directory for the current session',
'pane.openLater': 'Preview not supported for this type',
+14 -12
View File
@@ -232,6 +232,8 @@ type SplitEnv = {
onAck: (id: string) => void
/** 冷会话消息预热(打开控制室时拉最近消息) */
refreshPreviews: () => void
/** 创建卡片:打开「添加项目」流程(同侧栏 +) */
onAdd: () => void
getTheme: () => 'dark' | 'light' | 'system'
setTheme: (th: 'dark' | 'light' | 'system') => void
}
@@ -1055,27 +1057,27 @@ function ConsolePane() {
<span className="dsh-wt_consoleName">{c.name}</span>
<span className={'dsh-wt_consoleDot dsh-wt_consoleDot-' + c.status} aria-hidden />
</div>
<div className="dsh-wt_consoleDivider" aria-hidden />
<div className="dsh-wt_consoleStatusRow">
<span className={'dsh-wt_consoleStatus dsh-wt_consoleStatus-' + c.status}>{statusLabel[c.status]}</span>
{c.runtimeMs != null && <span className="dsh-wt_consoleRuntime">⏱ {fmtDur(c.runtimeMs)}</span>}
</div>
<div className="dsh-wt_consoleBadges">
<span className="dsh-wt_consoleBadge">{T('console.kids')} {c.kids}</span>
</div>
<div className={'dsh-wt_consolePreview' + (c.preview ? '' : ' dsh-wt_consolePreviewNone')} title={c.preview}>
{c.preview || (c.bound ? T('console.noPreview') : T('console.unboundShort'))}
</div>
{c.bound && !c.self && (
<button
type="button"
className="dsh-wt_consoleJump"
title={T('console.jump')}
aria-label={T('console.jump')}
onClick={(e) => { e.stopPropagation(); env?.onJump(c.id) }}
>💬</button>
)}
</div>
))}
{/* 创建卡片:永远最后一位;点击 = 侧栏工作台「添加项目」同款流程 */}
<div
role="button"
tabIndex={0}
className="dsh-wt_consoleCard dsh-wt_consoleAdd"
title={T('console.addProject')}
onClick={() => env?.onAdd?.()}
>
<span className="dsh-wt_consoleAddPlus" aria-hidden>+</span>
<span className="dsh-wt_consoleAddLabel">{T('console.addProject')}</span>
</div>
</div>
{cards.length === 0 && <div className="dsh-wt_consoleEmpty">{T('console.empty')}</div>}
</div>
+22 -19
View File
@@ -390,26 +390,25 @@ export const css = xtermCss + '\n' + [
'.dsh-wt_consoleThemeBtn{flex:none;padding:3px 9px;border:none;border-radius:999px;background:transparent;color:var(--wt-text3);font:inherit;font-size:12px;line-height:18px;cursor:pointer;opacity:.85}',
'.dsh-wt_consoleThemeBtn:hover{opacity:1;background:var(--wt-chip)}',
'.dsh-wt_consoleThemeBtnOn{background:var(--wt-chip);color:var(--wt-text);opacity:1}',
'.dsh-wt_consoleGrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px;align-content:start;max-width:720px;margin:0 auto}',
'.dsh-wt_consoleCard{position:relative;display:flex;flex-direction:column;gap:10px;aspect-ratio:1/1;min-height:150px;padding:16px;background:var(--wt-card);border:1px solid var(--wt-border);border-radius:14px;cursor:pointer;transition:transform .12s ease,border-color .12s ease,box-shadow .12s ease;overflow:hidden}',
'.dsh-wt_consoleGrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:64px;align-content:start;margin-top:clamp(32px,9vh,110px)}',
'.dsh-wt_consoleCard{position:relative;display:flex;flex-direction:column;gap:12px;aspect-ratio:1/1;min-height:170px;padding:19px;background:var(--wt-card);border:1px solid var(--wt-border);border-radius:14px;cursor:pointer;transition:transform .12s ease,border-color .12s ease,box-shadow .12s ease;overflow:hidden}',
'.dsh-wt_consoleCard:hover{transform:translateY(-2px);border-color:var(--wt-borderHover);box-shadow:var(--wt-shadow)}',
'.dsh-wt_consoleCardSelf{cursor:default;opacity:.97}',
'.dsh-wt_consoleCardHead{display:flex;align-items:center;gap:9px;min-width:0}',
'.dsh-wt_consoleIcon{flex:none;font-size:24px;line-height:1}',
'.dsh-wt_consoleName{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14.5px;font-weight:700;line-height:22px;color:var(--wt-text)}',
'.dsh-wt_consoleDot{flex:none;width:9px;height:9px;border-radius:50%;background:var(--wt-text3)}',
'.dsh-wt_consoleCardHead{display:flex;align-items:center;gap:10px;min-width:0}',
'.dsh-wt_consoleIcon{flex:none;font-size:28px;line-height:1}',
'.dsh-wt_consoleName{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:17.5px;font-weight:700;line-height:26px;color:var(--wt-text)}',
'.dsh-wt_consoleDivider{flex:none;height:1px;background:var(--wt-border)}',
'.dsh-wt_consoleDot{flex:none;width:10px;height:10px;border-radius:50%;background:var(--wt-text3)}',
'.dsh-wt_consoleDot-busy{background:var(--dsw-alias-state-accent-primary,#4f8ef7);box-shadow:0 0 0 4px rgba(79,142,247,.16)}',
'.dsh-wt_consoleDot-need{background:var(--dsw-alias-state-warning,#d29922);box-shadow:0 0 0 4px rgba(210,153,34,.16)}',
'.dsh-wt_consoleDot-done{background:var(--dsw-alias-state-success,#3fb950);box-shadow:0 0 0 4px rgba(63,185,80,.16)}',
'.dsh-wt_consoleStatusRow{display:flex;align-items:baseline;gap:8px;min-width:0}',
'.dsh-wt_consoleStatus{flex:none;font-size:17px;font-weight:700;line-height:24px;color:var(--wt-text2)}',
'.dsh-wt_consoleStatusRow{display:flex;align-items:baseline;gap:9px;min-width:0}',
'.dsh-wt_consoleStatus{flex:none;font-size:20px;font-weight:700;line-height:28px;color:var(--wt-text2)}',
'.dsh-wt_consoleStatus-busy{color:var(--dsw-alias-state-accent-primary,#4f8ef7)}',
'.dsh-wt_consoleStatus-need{color:var(--dsw-alias-state-warning,#d29922)}',
'.dsh-wt_consoleStatus-done{color:var(--dsw-alias-state-success,#3fb950)}',
'.dsh-wt_consoleRuntime{flex:none;font-size:13px;font-weight:600;line-height:20px;color:var(--wt-text2);font-variant-numeric:tabular-nums}',
'.dsh-wt_consoleBadges{display:flex;align-items:center;gap:6px;flex-wrap:wrap}',
'.dsh-wt_consoleBadge{flex:none;padding:2px 9px;border-radius:999px;font-size:12px;font-weight:600;line-height:18px;color:var(--wt-text2);background:var(--wt-chip)}',
'.dsh-wt_consolePreview{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;font-size:12.5px;line-height:18px;color:var(--wt-text2);word-break:break-word}',
'.dsh-wt_consoleRuntime{flex:none;font-size:15px;font-weight:600;line-height:24px;color:var(--wt-text2);font-variant-numeric:tabular-nums}',
'.dsh-wt_consolePreview{display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden;font-size:11px;line-height:16px;color:var(--wt-text2);word-break:break-word}',
'.dsh-wt_consolePreviewNone{color:var(--wt-text3);font-style:italic;-webkit-line-clamp:1}',
// 三色光效:整卡霓虹描边光晕(像侧栏双圆点的发光质感)——完成=绿、待决=黄;工作=蓝色描边顺时针旋转
'.dsh-wt_consoleCard-glowDone{border-color:rgba(63,185,80,.9);box-shadow:0 0 8px rgba(63,185,80,.5),0 0 26px rgba(63,185,80,.3),inset 0 0 16px rgba(63,185,80,.07)}',
@@ -418,14 +417,18 @@ export const css = xtermCss + '\n' + [
'.dsh-wt_consoleCard-busy{box-shadow:0 0 14px rgba(79,142,247,.28)}',
'.dsh-wt_consoleCard-busy::before{content:"";position:absolute;inset:-2px;border-radius:16px;padding:2px;background:conic-gradient(from var(--consoleAngle),transparent 0deg,rgba(79,142,247,0) 20deg,#5aa0ff 80deg,rgba(79,142,247,0) 140deg,transparent 200deg);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask-composite:exclude;animation:consoleAngleSpin 1.6s linear infinite;pointer-events:none}',
'@keyframes consoleAngleSpin{to{--consoleAngle:360deg}}',
'.dsh-wt_consoleJump{position:absolute;top:12px;right:12px;width:24px;height:24px;padding:0;border:none;border-radius:7px;background:transparent;color:var(--wt-text3);font-size:13px;line-height:1;cursor:pointer;opacity:0;transition:opacity .12s ease}',
'.dsh-wt_consoleCard:hover .dsh-wt_consoleJump{opacity:1}',
'.dsh-wt_consoleJump:hover{color:var(--dsw-alias-state-accent-primary,#4f8ef7);background:var(--wt-chip)}',
'.dsh-wt_consoleEmpty{padding:40px 0;text-align:center;font-size:12px;color:var(--wt-text3)}',
// 控制室入口卡:统筹主程序的差异化(渐变底 + 青色描边 + 微光,克制不喧宾夺主)
'.dsh-wt_projects .dsh-wt_consoleEntry{background:linear-gradient(135deg,rgba(79,142,247,.14),rgba(139,92,246,.10));border-color:rgba(79,142,247,.4);box-shadow:0 0 12px rgba(79,142,247,.14)}',
'.dsh-wt_projects .dsh-wt_consoleEntry:hover{background:linear-gradient(135deg,rgba(79,142,247,.2),rgba(139,92,246,.14));border-color:rgba(79,142,247,.6);box-shadow:0 0 16px rgba(79,142,247,.22)}',
'.dsh-wt_projects .dsh-wt_consoleEntry[data-on=true]{border-color:rgba(79,142,247,.7);box-shadow:0 0 16px rgba(79,142,247,.28)}',
// 创建卡片:虚线 + 居中加号,永远最后一位
'.dsh-wt_consoleAdd{justify-content:center;align-items:center;gap:8px;background:transparent;border:1.5px dashed var(--wt-borderHover)}',
'.dsh-wt_consoleAdd:hover{border-color:var(--dsw-alias-state-accent-primary,#4f8ef7);background:var(--wt-chip)}',
'.dsh-wt_consoleAddPlus{font-size:30px;line-height:1;color:var(--wt-text2);font-weight:400}',
'.dsh-wt_consoleAdd:hover .dsh-wt_consoleAddPlus{color:var(--dsw-alias-state-accent-primary,#4f8ef7)}',
'.dsh-wt_consoleAddLabel{font-size:13px;font-weight:600;color:var(--wt-text2)}',
'.dsh-wt_consoleAdd:hover .dsh-wt_consoleAddLabel{color:var(--dsw-alias-state-accent-primary,#4f8ef7)}',
// 控制室入口卡:统筹主程序的差异化(渐变底 + 微光;无描边,颜色更亮更饱和)
'.dsh-wt_projects .dsh-wt_consoleEntry{background:linear-gradient(135deg,rgba(109,164,255,.30),rgba(167,130,255,.22));border-color:transparent;box-shadow:0 0 14px rgba(96,150,255,.22)}',
'.dsh-wt_projects .dsh-wt_consoleEntry:hover{background:linear-gradient(135deg,rgba(109,164,255,.42),rgba(167,130,255,.32));box-shadow:0 0 18px rgba(96,150,255,.32)}',
'.dsh-wt_projects .dsh-wt_consoleEntry[data-on=true]{background:linear-gradient(135deg,rgba(109,164,255,.38),rgba(167,130,255,.28));box-shadow:0 0 18px rgba(96,150,255,.34)}',
'.dsh-wt_projects .dsh-wt_consoleEntry .dsh-wt_layoutIcon{font-size:15px}',
'.dsh-wt_projects .dsh-wt_consoleEntry .dsh-wt_layoutName{font-weight:600;color:var(--dsw-alias-label-primary,#e6e8eb)}',
// 控制室强制绑定弹窗:左加入现有 / 右新建对话