diff --git a/01_content/src/client/locales.ts b/01_content/src/client/locales.ts index 79f7ade..fd54595 100644 --- a/01_content/src/client/locales.ts +++ b/01_content/src/client/locales.ts @@ -6,7 +6,7 @@ /** Simplified Chinese dictionary (the key-set source of truth). */ export const zh = { - 'title': '控制室', + 'title': '工作台', 'rail.title': '工作台(展开侧边栏查看)', 'handle.title': '按住拖动位置 · 双击复位', 'handle.aria': '拖动工作台位置', @@ -167,7 +167,7 @@ export type WorktableKey = keyof typeof zh /** English dictionary, checked complete against the zh key set. */ export const en = { - 'title': 'Console', + 'title': 'Worktable', 'rail.title': 'Worktable (expand sidebar to view)', 'handle.title': 'Drag to move · double-click to reset', 'handle.aria': 'Drag worktable position', diff --git a/01_content/src/client/split.tsx b/01_content/src/client/split.tsx index 60ddd8e..54c4313 100644 --- a/01_content/src/client/split.tsx +++ b/01_content/src/client/split.tsx @@ -1742,12 +1742,15 @@ function PaneBody(props: { pane: SplitPane; row: PaneRow; index: number }) { return ( <>
- {tabs.map((t, i) => ( + {tabs.map((t, i) => { + // 控制室标签不可关闭:关掉后窗格变选择器,用户回不到控制室(不可逆操作) + const locked = t.content?.kind === 'builtin' && t.content.type === 'console' + return ( { dragTab = { row, index, tabId: t.id }; try { e.dataTransfer.effectAllowed = 'move' } catch {} }} onDragEnd={() => { dragTab = null; setDropTarget(null) }} onClick={() => splitStore.setActiveTab(row, index, t.id)} @@ -1762,14 +1765,17 @@ function PaneBody(props: { pane: SplitPane; row: PaneRow; index: number }) { >↻ )} {t.title} - + {!locked && ( + + )} - ))} + ) + })}
diff --git a/01_content/src/client/styles.ts b/01_content/src/client/styles.ts index d4fc965..ad9d87e 100644 --- a/01_content/src/client/styles.ts +++ b/01_content/src/client/styles.ts @@ -382,7 +382,7 @@ export const css = xtermCss + '\n' + [ '.dsh-wt_subSection{flex:none;padding:6px 8px 2px;font-size:10px;font-weight:600;letter-spacing:.05em;color:var(--dsw-alias-label-tertiary,#6e7683)}', // 控制室面板:项目卡片网格(每行 3 张、超出换行;DSH 简洁 + 苹果式卡片)。 // 主题变量自带作用域:dark 缺省;data-wt-theme=light 换浅色;system 由面板读宿主 color-scheme 落成 dark/light。 - '.dsh-wt_console{--wt-bg:#10141b;--wt-card:#171c25;--wt-cardHover:#1d232e;--wt-border:#2a3140;--wt-borderHover:#39445a;--wt-text:#e6e8eb;--wt-text2:#9aa4b2;--wt-text3:#6e7683;--wt-chip:rgba(255,255,255,.06);--wt-shadow:0 6px 20px rgba(0,0,0,.35);flex:1;min-height:0;overflow:auto;padding:14px;background:var(--wt-bg)}', + '.dsh-wt_console{--wt-bg:#10141b;--wt-card:#171c25;--wt-cardHover:#1d232e;--wt-border:#2a3140;--wt-borderHover:#39445a;--wt-text:#e6e8eb;--wt-text2:#9aa4b2;--wt-text3:#6e7683;--wt-chip:rgba(255,255,255,.06);--wt-shadow:0 6px 20px rgba(0,0,0,.35);flex:1;min-height:0;overflow:auto;padding:22px;background:var(--wt-bg)}', '.dsh-wt_console[data-wt-theme=light]{--wt-bg:#f2f4f7;--wt-card:#ffffff;--wt-cardHover:#f7f9fb;--wt-border:#d8dee6;--wt-borderHover:#b9c4d2;--wt-text:#1c2128;--wt-text2:#57606a;--wt-text3:#8b949e;--wt-chip:rgba(27,31,36,.07);--wt-shadow:0 6px 20px rgba(31,41,55,.10)}', '.dsh-wt_consoleHead{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:12px}', '.dsh-wt_consoleTitle{font-size:14px;font-weight:700;line-height:20px;color:var(--wt-text)}', @@ -390,7 +390,7 @@ 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:14px;align-content:start}', + '.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_consoleCard:hover{transform:translateY(-2px);border-color:var(--wt-borderHover);box-shadow:var(--wt-shadow)}', '.dsh-wt_consoleCardSelf{cursor:default;opacity:.97}', @@ -411,13 +411,12 @@ export const css = xtermCss + '\n' + [ '.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_consolePreviewNone{color:var(--wt-text3);font-style:italic;-webkit-line-clamp:1}', - // 三色光效:完成=绿光、待决=黄光(背景泛光,点击确认后熄灭);工作=蓝色描边光晕顺时针旋转 - '.dsh-wt_consoleCard-glowDone{border-color:rgba(63,185,80,.45);box-shadow:0 0 22px rgba(63,185,80,.22)}', - '.dsh-wt_consoleCard-glowDone::after{content:"";position:absolute;inset:0;border-radius:inherit;background:radial-gradient(130% 130% at 50% 0%,rgba(63,185,80,.16),transparent 62%);pointer-events:none}', - '.dsh-wt_consoleCard-glowNeed{border-color:rgba(210,153,34,.5);box-shadow:0 0 22px rgba(210,153,34,.22)}', - '.dsh-wt_consoleCard-glowNeed::after{content:"";position:absolute;inset:0;border-radius:inherit;background:radial-gradient(130% 130% at 50% 0%,rgba(210,153,34,.17),transparent 62%);pointer-events:none}', + // 三色光效:整卡霓虹描边光晕(像侧栏双圆点的发光质感)——完成=绿、待决=黄;工作=蓝色描边顺时针旋转 + '.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)}', + '.dsh-wt_consoleCard-glowNeed{border-color:rgba(210,153,34,.95);box-shadow:0 0 8px rgba(210,153,34,.5),0 0 26px rgba(210,153,34,.3),inset 0 0 16px rgba(210,153,34,.08)}', '@property --consoleAngle{syntax:"";initial-value:0deg;inherits:false}', - '.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) 30deg,rgba(79,142,247,.95) 80deg,rgba(79,142,247,0) 130deg,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.8s linear infinite;pointer-events:none}', + '.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}', diff --git a/02_process/worklogs/2026-08-16_worktable-v2.md b/02_process/worklogs/2026-08-16_worktable-v2.md index 2c2b887..f366ba8 100644 --- a/02_process/worklogs/2026-08-16_worktable-v2.md +++ b/02_process/worklogs/2026-08-16_worktable-v2.md @@ -1202,3 +1202,21 @@ 三阶段全流程与主题切换全 ✓;functional-diag 全 20 STEP ERROR_COUNT: 0。 - 备注:纯客户端改动,F5 即生效;face.history 为非公开接口(只读、失败静默回退内存路径), AGENTS.md 已注明。 + +## 补记(控制室第二轮打磨:标签不可关 + 小卡片多留白 + 霓虹描边发光 + 命名纠正) + +- 用户反馈:① 控制室标签上的 ✕ 关掉后回不去(不可逆)——要么没有标签栏,要么关不掉; + ② 卡片太大把空间占满——要小一点、留白多一点;③ 发光理解错了:要的是「整个卡片」的 + 光(像侧栏绑定双圆点那种发光质感),不是圆点发光——做整卡光晕描边;④ 命名纠正:侧栏 + 最上面区块标题叫「工作台」(整个插件),第一个默认项目叫「控制室」(工作台的控制室)。 +- 实现:① PaneBody 标签渲染改 map 回调:content.type==='console' 的标签 locked——不渲染 + ✕ 关闭按钮、禁拖拽(标签栏保留标题,保证可回);② 网格 max-width 720px 居中 + gap 16px + + 面板 padding 22px(卡片实测 229px 宽,1:1);③ 光效重做:glowDone/glowNeed = 亮色 + 描边(rgba 0.9 边框)+ 双层外发光(8px 高亮 + 26px 泛光)+ 微弱内辉光(inset),删掉 + 原来过强的 radial 背景 tint;busy 旋转描边峰值改 #5aa0ff + 卡片蓝色外发光;④ locale + title 回「工作台」/Worktable,console.name 保持「控制室」/Console。 +- 验证(probe-batch16 更新):sectionTitle=工作台 ✓、consoleName=控制室 ✓、 + consoleTabCloseGone=true(控制室标签无 ✕)✓、cardWidth 229/1:1 ✓、glowDoneBorder + rgba(63,185,80,0.9) + 双层 box-shadow ✓、busy 旋转描边 #5aa0ff ✓;三阶段流程 + 主题 + 全 ✓;functional-diag 全 20 STEP ERROR_COUNT: 0。 +- 备注:纯客户端改动,F5 即生效。 diff --git a/04_test/probe-batch16.cjs b/04_test/probe-batch16.cjs index 856efd4..4c5cdfd 100644 --- a/04_test/probe-batch16.cjs +++ b/04_test/probe-batch16.cjs @@ -51,6 +51,9 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); var first = document.querySelector('.dsh-wt_projects > *'); out.firstIsConsole = !!(first && first.classList && first.classList.contains('dsh-wt_consoleEntry')); out.consoleName = first ? (first.querySelector('.dsh-wt_layoutName') ? first.querySelector('.dsh-wt_layoutName').textContent : null) : null; + var handle = document.querySelector('.dsh-wt_title'); + out.sectionTitle = handle ? (handle.textContent || '').trim() : null; + out.sectionTitleRight = out.sectionTitle === '工作台'; out.consoleIcon = first ? (first.querySelector('.dsh-wt_layoutIcon') ? first.querySelector('.dsh-wt_layoutIcon').textContent : null) : null; out.consoleOrder = first ? getComputedStyle(first).order : null; out.consoleHasBindBtn = !!(first && first.querySelector('.dsh-wt_bindBtn')); @@ -113,6 +116,12 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); } out.bindPopClosed = !vis('.dsh-wt_consoleBindPop'); out.specId = st.active && st.spec ? st.spec.id : null; + // 控制室标签关不掉:标签栏存在但该标签无 ✕ + var tabBar = vis('.dsh-wt_tabBar'); + var ctab = tabBar && tabBar.querySelector('.dsh-wt_tab'); + out.consoleTabExists = !!ctab; + out.consoleTabCloseGone = !!(ctab && !ctab.querySelector('.dsh-wt_tabClose')); + out.consoleTabTitle = ctab ? (ctab.textContent || '').trim() : null; out.mainType = st.spec && st.spec.main[0] && st.spec.main[0].tabs && st.spec.main[0].tabs[0] ? st.spec.main[0].tabs[0].content.type : null; var saved = JSON.parse(localStorage.getItem('dsh.worktable.projects.v1')); out.savedConsoleBinding = saved.bindings['wt-console']; @@ -136,6 +145,7 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); if (cardEl) { var cr = cardEl.getBoundingClientRect(); out.cardAspect = Math.round((cr.width / Math.max(1, cr.height)) * 100) / 100; + out.cardWidth = Math.round(cr.width); var statusEl = cardEl.querySelector('.dsh-wt_consoleStatus'); out.statusFont = statusEl ? getComputedStyle(statusEl).fontSize : null; cardEl.classList.add('dsh-wt_consoleCard-busy'); @@ -147,7 +157,8 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); await sleep(300); var gs = getComputedStyle(cardEl, '::after'); out.glowDoneBg = gs.backgroundImage ? gs.backgroundImage.slice(0, 60) : null; - out.glowDoneShadow = getComputedStyle(cardEl).boxShadow.slice(0, 60); + out.glowDoneShadow = getComputedStyle(cardEl).boxShadow.slice(0, 80); + out.glowDoneBorder = getComputedStyle(cardEl).borderColor; cardEl.classList.remove('dsh-wt_consoleCard-glowDone'); cardEl.classList.add('dsh-wt_consoleCard-glowNeed'); await sleep(300); diff --git a/AGENTS.md b/AGENTS.md index 1474f92..7c1860f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -95,12 +95,17 @@ node --check lib/index.js prefers-color-scheme 兜底;落成 .dsh-wt_console[data-wt-theme=dark|light] 作用域变量 --wt-*(宿主不发布 --dsw-alias-*,工作台全站一直靠回退色渲染——控制室自带主题作用域, 不受其影响)。 - - 状态光效:工作=蓝色描边光晕顺时针绕卡旋转(.dsh-wt_consoleCard-busy ::before - conic-gradient + @property --consoleAngle + consoleAngleSpin);完成=绿光、待决=黄光 - (-glowDone/-glowNeed:radial 泛光 + 外发光;glow 字段 = done/need 且本轮未 ack, - 点卡片先 onAck 熄光再进入,与提醒 ack 生命周期一致)。 - - 命名统一「控制室」:侧栏区块标题、默认项目卡名、面板标题均为 console.name/title 系列 - locale(不再是「工作台」)。 + - 状态光效(整卡霓虹描边,参考侧栏双圆点发光质感):工作=蓝色描边光晕顺时针绕卡旋转 + (.dsh-wt_consoleCard-busy ::before conic-gradient + @property --consoleAngle + + consoleAngleSpin,峰值 #5aa0ff);完成=绿光、待决=黄光(-glowDone/-glowNeed:亮色描边 + + 双层外发光 + 微弱内辉光;glow 字段 = done/need 且本轮未 ack,点卡片先 onAck 熄光 + 再进入,与提醒 ack 生命周期一致)。 + - 命名:侧栏区块标题 = 「工作台」(title locale,整个插件);默认项目卡名与面板标题 = + 「控制室」(console.name/console.title locale,工作台的控制室)。 + - 控制室标签不可关:PaneBody 对 content.type==='console' 的标签 locked(不渲染 ✕、 + 禁拖拽)——关掉会退化成窗格选择器,不可逆。 + - 布局尺度:网格 max-width 720px 居中、gap 16px、面板 padding 22px;卡片 1:1(实测 + ~229px 宽),保持留白。 - 冷会话消息预览(方案 A):binding() 对冷会话不载入文本;预热走 face.history({maxMessages:2}) (运行期内建方法、非公开接口,只读无副作用)尾部扫 user/message 与 assistant/message 的 text 块 → previewCache;sweepPreviews 在打开控制室时 + 控制室开着且会话快照变化防抖 6s