From 3d96a07f9d1ef233e18f6275a5b7e6abbed77b9a Mon Sep 17 00:00:00 2001 From: Aisland Date: Fri, 21 Aug 2026 01:39:38 +0800 Subject: [PATCH] feat: console spacing/cards polish, create card, drop kids/jump, borderless entry --- 01_content/src/client/index.tsx | 15 ++++++- 01_content/src/client/locales.ts | 2 + 01_content/src/client/split.tsx | 26 ++++++------ 01_content/src/client/styles.ts | 41 ++++++++++--------- .../worklogs/2026-08-16_worktable-v2.md | 18 ++++++++ 04_test/probe-batch16.cjs | 26 ++++++++---- AGENTS.md | 17 ++++---- 7 files changed, 96 insertions(+), 49 deletions(-) diff --git a/01_content/src/client/index.tsx b/01_content/src/client/index.tsx index cc881b0..99181b7 100644 --- a/01_content/src/client/index.tsx +++ b/01_content/src/client/index.tsx @@ -372,8 +372,8 @@ async function coldPreviewOf(face: any): Promise { 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 diff --git a/01_content/src/client/locales.ts b/01_content/src/client/locales.ts index fd54595..313f6ad 100644 --- a/01_content/src/client/locales.ts +++ b/01_content/src/client/locales.ts @@ -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', diff --git a/01_content/src/client/split.tsx b/01_content/src/client/split.tsx index 54c4313..dfc977c 100644 --- a/01_content/src/client/split.tsx +++ b/01_content/src/client/split.tsx @@ -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() { {c.name} +
{statusLabel[c.status]} {c.runtimeMs != null && ⏱ {fmtDur(c.runtimeMs)}}
-
- {T('console.kids')} {c.kids} -
{c.preview || (c.bound ? T('console.noPreview') : T('console.unboundShort'))}
- {c.bound && !c.self && ( - - )}
))} + {/* 创建卡片:永远最后一位;点击 = 侧栏工作台「添加项目」同款流程 */} +
env?.onAdd?.()} + > + + + {T('console.addProject')} +
{cards.length === 0 &&
{T('console.empty')}
} diff --git a/01_content/src/client/styles.ts b/01_content/src/client/styles.ts index ad9d87e..a3eb4de 100644 --- a/01_content/src/client/styles.ts +++ b/01_content/src/client/styles.ts @@ -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)}', // 控制室强制绑定弹窗:左加入现有 / 右新建对话 diff --git a/02_process/worklogs/2026-08-16_worktable-v2.md b/02_process/worklogs/2026-08-16_worktable-v2.md index 6e5f999..81bb23f 100644 --- a/02_process/worklogs/2026-08-16_worktable-v2.md +++ b/02_process/worklogs/2026-08-16_worktable-v2.md @@ -1231,3 +1231,21 @@ - 验证:probe-batch16 healTabs=1、healType='console' ✓,全流程 ✓;functional-diag 全 20 STEP ERROR_COUNT: 0。 - 备注:纯客户端改动,F5 即生效;用户 F5 后坏存档自动修复,点「控制室」卡片即恢复面板。 + +## 补记(控制室第三轮打磨:下沉居中 + 4 倍间距 + 1.2 倍卡片 + 创建卡 + 去子代理/💬 + 入口无描边) + +- 用户反馈:① 卡片太靠上 → 网格下沉(margin-top clamp(32px,9vh,110px),接近画面中部); + ② 卡间距太小 → gap 16→64px(4 倍);③ 卡片整体放大 1.2 倍(padding 19/图标 28/ + 名字 17.5/状态 20/时长 15);④ 删掉右上角隐藏的 💬 跳转按钮(无意义);⑤ 网格最后 + 一位永远是一张「创建卡片」(虚线 + 居中加号),点击 = 侧栏工作台「添加项目」同款流程 + (openAddPanel 共用:setAddOpen + 默认父目录取当前会话 cwd);⑥ 侧栏控制室入口卡去掉 + 描边(border transparent),渐变颜色提亮提饱和(rgba(109,164,255,.30)→ + rgba(167,130,255,.22)),保留微光;⑦ 子代理徽章全删(都是 0 无信息);⑧ 卡片名字下方 + 加横向分隔线(标题/状态/预览层次分明);⑨ 预览字改小(11px)行数提到 4 行,抓取切 + 220 字符;⑩ 名字字号变大且**所有卡片共用类名**——以后新建的项目卡片自动同字号。 +- 验证(probe-batch16 更新):gridGap 64px / gridMarginTop 72px ✓、nameFont 17.5px / + previewFont 11px / statusFont 20px ✓、dividerExists ✓、kidsGone/jumpGone ✓、 + addCardLast(+)且点击开添加面板(addPopOpened)✓、入口 border rgba(0,0,0,0) + + 渐变 ✓、真实运行会话 busy 旋转光弧动效生效 ✓;全流程 + 主题 ✓;functional-diag + 全 20 STEP ERROR_COUNT: 0。 +- 备注:纯客户端改动,F5 即生效。 diff --git a/04_test/probe-batch16.cjs b/04_test/probe-batch16.cjs index 7143d49..88e97f0 100644 --- a/04_test/probe-batch16.cjs +++ b/04_test/probe-batch16.cjs @@ -135,16 +135,31 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); out.gridFound = !!grid; if (grid) { out.gridCols = getComputedStyle(grid).gridTemplateColumns.split(' ').length; + out.gridGap = getComputedStyle(grid).columnGap; + out.gridMarginTop = getComputedStyle(grid).marginTop; var cards = grid.querySelectorAll('.dsh-wt_consoleCard'); out.cardCount = cards.length; var c0 = cards[0]; out.firstCardSelf = !!(c0 && c0.classList.contains('dsh-wt_consoleCardSelf')); out.firstCardIcon = c0 ? c0.querySelector('.dsh-wt_consoleIcon').textContent : null; out.firstCardDot = c0 && c0.querySelector('.dsh-wt_consoleDot') ? c0.querySelector('.dsh-wt_consoleDot').className.replace('dsh-wt_consoleDot','').trim() : null; - out.firstCardBadges = c0 ? c0.querySelectorAll('.dsh-wt_consoleBadge').length : 0; + out.nameFont = c0 && c0.querySelector('.dsh-wt_consoleName') ? getComputedStyle(c0.querySelector('.dsh-wt_consoleName')).fontSize : null; + out.dividerExists = !!(c0 && c0.querySelector('.dsh-wt_consoleDivider')); + out.kidsGone = !(c0 && c0.querySelector('.dsh-wt_consoleBadge')); + out.jumpGone = !(c0 && c0.querySelector('.dsh-wt_consoleJump')); + out.previewFont = c0 && c0.querySelector('.dsh-wt_consolePreview') ? getComputedStyle(c0.querySelector('.dsh-wt_consolePreview')).fontSize : null; out.firstCardHasPreview = !!(c0 && c0.querySelector('.dsh-wt_consolePreview')); out.anyLayoutCard = [].slice.call(cards).some(function(c){ return (c.textContent||'').indexOf('绑定测试') >= 0; }); - out.layoutCardHasJump = [].slice.call(cards).some(function(c){ return (c.textContent||'').indexOf('绑定测试') >= 0 && !!c.querySelector('.dsh-wt_consoleJump'); }); + // 创建卡片永远最后一位;点击打开「添加项目」面板 + var addCard = grid.lastElementChild; + out.addCardLast = !!(addCard && addCard.classList.contains('dsh-wt_consoleAdd')); + out.addPlus = addCard && addCard.querySelector('.dsh-wt_consoleAddPlus') ? addCard.querySelector('.dsh-wt_consoleAddPlus').textContent : null; + if (addCard) { addCard.dispatchEvent(new MouseEvent('click', {bubbles:true, cancelable:true})); } + await sleep(500); + out.addPopOpened = !!vis('.dsh-wt_add'); + var bd = vis('.dsh-wt_popBackdrop'); + if (bd) { bd.dispatchEvent(new MouseEvent('click', {bubbles:true, cancelable:true})); } + await sleep(300); // 新版 UI:正方形比例、大字号状态、光效 CSS 接线(注入类验证,不依赖真实会话状态) var cardEl = c0; if (cardEl) { @@ -226,13 +241,8 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); out.bindPopNotShown = !vis('.dsh-wt_consoleBindPop'); var grid = vis('.dsh-wt_consoleGrid'); var lc = grid && [].slice.call(grid.querySelectorAll('.dsh-wt_consoleCard')).find(function(c){ return (c.textContent||'').indexOf('绑定测试') >= 0; }); - out.layoutJumpBtn = !!(lc && lc.querySelector('.dsh-wt_consoleJump')); + out.jumpBtnGone = !(lc && lc.querySelector('.dsh-wt_consoleJump')); out.layoutCardStatus = lc && lc.querySelector('.dsh-wt_consoleDot') ? lc.querySelector('.dsh-wt_consoleDot').className.replace('dsh-wt_consoleDot','').trim() : null; - if (lc && lc.querySelector('.dsh-wt_consoleJump')) { - lc.querySelector('.dsh-wt_consoleJump').dispatchEvent(new MouseEvent('click', {bubbles:true, cancelable:true})); - await sleep(700); - out.specAfterJump = st.active && st.spec ? st.spec.id : null; - } st.close(); await sleep(300); } catch (e) { out.err = String(e && e.stack || e); } return out; diff --git a/AGENTS.md b/AGENTS.md index 7c1860f..72b6eae 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -84,12 +84,11 @@ node --check lib/index.js 建空会话 sessions.create 后自动绑定并打开控制室)。绑定也走 projects.v1.bindings。 - 控制室面板(split.tsx ConsolePane):卡片网格每行 3 张、超出换行;每卡 = 图标/名称/ 三态圆点(need>done>busy>idle,不过滤 ack,永远显示事实状态)/运行时长(后台任务 - JobView.startedAt 或会话面 turnTimings 未结束轮次)/子代理数(byId.parentId + - subagentsByParent 双通道计数)/最近消息预览(会话面快照 nodes 末条文本,纯读内存 - 零 Token)。数据组装 = index.tsx getConsoleCards(env.console 注入),刷新走 - consoleListeners(项目/会话快照变化推送)+ 面板每秒 tick。 - - 卡片动作:点卡片 = 打开该项目(openSplit 或入驻项目切绑定对话);💬 = 跳绑定对话 - (markPluginSessionOpen 豁免联动,控制室保持打开);工作台自己的卡片点击无操作。 + JobView.startedAt 或会话面 turnTimings 未结束轮次)/最近消息预览(会话面快照 nodes + 末条文本,纯读内存零 Token)。数据组装 = index.tsx getConsoleCards(env.console 注入), + 刷新走 consoleListeners(项目/会话快照变化推送)+ 面板每秒 tick。 + - 卡片动作:点卡片 = 打开该项目(openSplit 或入驻项目切绑定对话);工作台自己的卡片 + 点击无操作。💬 跳转按钮已删除(用户定案无意义)。 - 主题:面板三选一开关(图标按钮 🌙/☀️/🖥️,title/aria 保留文字;存 view.v1 consoleTheme); system 读宿主 html 的 color-scheme(DSH 深色/白色/跟随系统设置都会反映到它)+ prefers-color-scheme 兜底;落成 .dsh-wt_console[data-wt-theme=dark|light] 作用域变量 @@ -104,8 +103,10 @@ node --check lib/index.js 「控制室」(console.name/console.title locale,工作台的控制室)。 - 控制室标签不可关:PaneBody 对 content.type==='console' 的标签 locked(不渲染 ✕、 禁拖拽)——关掉会退化成窗格选择器,不可逆。 - - 布局尺度:网格 max-width 720px 居中、gap 16px、面板 padding 22px;卡片 1:1(实测 - ~229px 宽),保持留白。 + - 布局尺度:网格 gap 64px(4 倍间距)、margin-top clamp(32px,9vh,110px) 下沉居中; + 卡片 1:1 整体 1.2 倍(名字 17.5px/状态 20px/预览 11px 四行截断);标题下横向分隔线; + 无子代理徽章、无 💬 跳转按钮(已删);网格最后一位恒为「创建卡片」(虚线+),点击 = + 侧栏「添加项目」同款 openAddPanel 流程;入口卡无描边(透明 border + 提亮渐变 + 微光)。 - 冷会话消息预览(方案 A):binding() 对冷会话不载入文本;预热走 face.history({maxMessages:2}) (运行期内建方法、非公开接口,只读无副作用)尾部扫 user/message 与 assistant/message 的 text 块 → previewCache;sweepPreviews 在打开控制室时 + 控制室开着且会话快照变化防抖 6s