From 7917331e5a3e0ab0579b3bd5f5c48432b35f26a6 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 1 Sep 2026 10:04:00 +0800 Subject: [PATCH] feat(web): consolidate conversation navigation --- CHANGELOG.md | 2 +- tests/test_static_vendor.py | 10 ++- web/static/dev.html | 151 +++++++++++++++------------------ web/static/js/chat.js | 18 ++-- web/static/js/dom.js | 30 +++++++ web/static/js/software_jobs.js | 29 +++++-- 6 files changed, 139 insertions(+), 101 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4101be6..884a68e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ ## Unreleased -- 对话两侧的软件作业与轮次导航改为更紧凑的一致样式,并增加常显的名称和数量;既可悬停速览,也可点击固定展开,状态和当前位置更清楚。 +- 软件作业与轮次导航统一收在对话右侧,并增加醒目的数量徽章;既可悬停速览,也可互斥固定展开,进行中或未读作业会直接通过数字提醒。 - DeepSeek Flash 的思考过程恢复实时显示,不再等到正文开始后才集中出现。 diff --git a/tests/test_static_vendor.py b/tests/test_static_vendor.py index 020f8bd..595b469 100644 --- a/tests/test_static_vendor.py +++ b/tests/test_static_vendor.py @@ -242,9 +242,13 @@ class StaticVendorTests(unittest.TestCase): self.assertIn(".tool-call.activity > summary", css) self.assertIn("#task-progress-dock .task-progress", css) self.assertIn(".conversation-rail-caption", html) - self.assertIn("#msg-outline-rail.pinned .ol-label", html) - self.assertIn("点击固定展开或收起对话导航", frontend) - self.assertIn("点击固定展开或收起软件作业", frontend) + self.assertIn('id="conversation-side-rail"', html) + self.assertLess(html.index('id="msg-outline-rail"'), html.index('id="software-result-rail"')) + self.assertIn(".rail-count.attention", html) + self.assertIn("toggleConversationRailPinned", frontend) + self.assertIn("syncConversationSideRail", frontend) + self.assertIn("点击固定展开或收起导航", frontend) + self.assertIn("个新结果", frontend) def test_frontend_surfaces_context_and_cache_stats(self) -> None: src = _frontend_source() diff --git a/web/static/dev.html b/web/static/dev.html index 8a54abe..c55b5db 100644 --- a/web/static/dev.html +++ b/web/static/dev.html @@ -843,95 +843,88 @@ 0%, 100% { box-shadow: none; } 18% { box-shadow: 0 0 0 2px var(--accent), 0 0 0 6px var(--accent-soft); } } - /* 对话两侧索引轨共用紧凑视觉:收起时只占一条窄边轨,hover / 键盘聚焦后 - 展开可读标签。轨道本身不拦截正文滚动,只有条目可交互。 */ - #msg-outline-rail { + /* 对话辅助导航统一收在正文右缘:轮次在上、作业在下。默认只显示带数量的 + 文字入口;悬停可速览,点击后固定展开,且两组固定态互斥。 */ + #conversation-side-rail { position: absolute; right: 6px; top: 50%; transform: translateY(-50%); - max-height: 72%; display: flex; flex-direction: column; align-items: flex-end; - gap: 3px; padding: 5px 3px; overflow-y: auto; overflow-x: hidden; z-index: 5; - border: 1px solid rgba(225, 227, 229, .82); border-radius: 10px; - background: rgba(255, 255, 255, .84); box-shadow: 0 2px 10px rgba(24, 32, 41, .06); - backdrop-filter: blur(8px); pointer-events: none; scrollbar-width: none; - transition: border-color .15s ease, box-shadow .15s ease; + z-index: 6; display: flex; flex-direction: column; width: max-content; min-width: 72px; + max-width: min(280px, calc(100% - 24px)); max-height: 72%; padding: 4px; + overflow-y: auto; overflow-x: hidden; border: 1px solid rgba(225, 227, 229, .86); + border-radius: 11px; background: rgba(255, 255, 255, .92); + box-shadow: 0 4px 16px rgba(24, 32, 41, .09); backdrop-filter: blur(8px); + scrollbar-width: none; transition: border-color .15s ease, box-shadow .15s ease; } - #msg-outline-rail:hover, #msg-outline-rail:focus-within { - border-color: var(--border); box-shadow: 0 6px 20px rgba(24, 32, 41, .10); + #conversation-side-rail[hidden], .conversation-rail-section[hidden], + #conversation-side-divider[hidden] { display: none !important; } + #conversation-side-rail:hover, #conversation-side-rail:focus-within { + border-color: var(--border); box-shadow: 0 7px 22px rgba(24, 32, 41, .12); + } + #conversation-side-rail::-webkit-scrollbar { display: none; } + #conversation-side-divider { height: 1px; margin: 4px 3px; background: var(--border-soft); } + .conversation-rail-section { + display: flex; flex-direction: column; align-items: stretch; gap: 2px; min-width: 62px; } - #msg-outline-rail::-webkit-scrollbar { display: none; } .conversation-rail-caption { - display: inline-flex; align-items: center; gap: 4px; min-height: 20px; - padding: 2px 6px; border: 0; border-radius: 6px; background: var(--panel-muted); - color: var(--muted); font-size: 10px; font-weight: 600; line-height: 1; - white-space: nowrap; pointer-events: auto; + display: grid; grid-template-columns: 1fr auto 8px; align-items: center; gap: 4px; + width: 100%; min-height: 28px; padding: 3px 5px; border: 0; border-radius: 7px; + background: var(--panel-muted); color: var(--muted); font-size: 11px; + font-weight: 600; line-height: 1; white-space: nowrap; } - .conversation-rail-caption::after { - content: ""; width: 4px; height: 4px; border-right: 1.5px solid currentColor; - border-bottom: 1.5px solid currentColor; transform: rotate(45deg) translateY(-1px); - transition: transform .15s ease; + .rail-caption-label { text-align: left; } + .rail-count { + display: inline-flex; align-items: center; justify-content: center; min-width: 20px; height: 20px; + padding: 0 4px; + border-radius: 7px; background: #eceeef; color: #60656b; font-size: 12px; + font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; } - .conversation-rail-caption:hover, .conversation-rail-caption:focus-visible, - .pinned > .conversation-rail-caption { + #msg-outline-rail > .conversation-rail-caption, + .conversation-rail-section.pinned > .conversation-rail-caption { color: var(--accent); background: var(--accent-soft); } - .pinned > .conversation-rail-caption::after { - transform: rotate(225deg) translate(-1px, -1px); + #msg-outline-rail > .conversation-rail-caption .rail-count, + .conversation-rail-section.pinned > .conversation-rail-caption .rail-count:not(.attention) { + color: var(--accent); background: #fff; box-shadow: 0 0 0 1px rgba(184, 58, 49, .16); } + .rail-count.attention { + color: #fff; background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); + } + .rail-chevron { color: currentColor; font-size: 9px; text-align: right; transition: transform .15s ease; } + .conversation-rail-section.pinned > .conversation-rail-caption .rail-chevron { transform: rotate(180deg); } + .ol-dot, .sr-item { display: none; } + #conversation-side-rail:not(.has-pinned) .conversation-rail-section:hover > .ol-dot, + #conversation-side-rail:not(.has-pinned) .conversation-rail-section:focus-within > .ol-dot, + #conversation-side-rail:not(.has-pinned) .conversation-rail-section:hover > .sr-item, + #conversation-side-rail:not(.has-pinned) .conversation-rail-section:focus-within > .sr-item, + .conversation-rail-section.pinned > .ol-dot, + .conversation-rail-section.pinned > .sr-item { display: flex; } .ol-dot { - display: flex; align-items: center; justify-content: flex-end; gap: 6px; - min-height: 16px; max-width: 18px; padding: 0; border: 0; border-radius: 7px; - background: transparent; cursor: pointer; pointer-events: auto; - transition: max-width .18s ease, background .15s ease; + align-items: center; justify-content: flex-end; gap: 6px; min-height: 20px; + max-width: 246px; padding: 2px 5px; border: 0; border-radius: 5px; + background: transparent; cursor: pointer; } - #msg-outline-rail:hover .ol-dot, #msg-outline-rail:focus-within .ol-dot, - #msg-outline-rail.pinned .ol-dot { max-width: 246px; } .ol-dot .ol-num { display: none; } .ol-dot .ol-label { - font-size: 12px; line-height: 1.3; color: var(--muted); white-space: nowrap; - overflow: hidden; text-overflow: ellipsis; max-width: 0; opacity: 0; - padding: 1px 0; transition: max-width .18s ease, opacity .18s ease, padding .18s ease; - } - #msg-outline-rail:hover .ol-label, #msg-outline-rail:focus-within .ol-label, - #msg-outline-rail.pinned .ol-label { - max-width: 210px; opacity: 1; padding-left: 7px; + max-width: 210px; overflow: hidden; color: var(--muted); font-size: 11px; + line-height: 1.3; text-overflow: ellipsis; white-space: nowrap; } .ol-dot::after { content: ""; flex: none; width: 12px; height: 3px; border-radius: 3px; background: var(--border); transition: background .15s ease, width .15s ease; } - .ol-dot:hover, .ol-dot:focus-visible { background: var(--panel-muted); } + .ol-dot:hover, .ol-dot:focus-visible, .sr-item:hover, .sr-item:focus-visible { + background: var(--panel-muted); + } .ol-dot:hover::after { background: #9aa0a6; } .ol-dot.active .ol-label { color: var(--text-strong); font-weight: 600; } - .ol-dot.active::after { background: var(--accent); width: 16px; } - /* 专业软件结果轨:与右侧消息目录对称,按 Job 独立堆叠。折叠态只露状态点, - hover/focus 展开标题;结果与运行状态来自 software_jobs,不复制进聊天消息。 */ - #software-result-rail { - position: absolute; left: 6px; top: 50%; transform: translateY(-50%); - max-height: 72%; display: flex; flex-direction: column; align-items: flex-start; - gap: 3px; padding: 5px 3px; overflow-y: auto; overflow-x: hidden; z-index: 6; - border: 1px solid rgba(225, 227, 229, .82); border-radius: 10px; - background: rgba(255, 255, 255, .84); box-shadow: 0 2px 10px rgba(24, 32, 41, .06); - backdrop-filter: blur(8px); pointer-events: none; scrollbar-width: none; - transition: border-color .15s ease, box-shadow .15s ease; - } - #software-result-rail:hover, #software-result-rail:focus-within { - border-color: var(--border); box-shadow: 0 6px 20px rgba(24, 32, 41, .10); - } - #software-result-rail::-webkit-scrollbar { display: none; } + .ol-dot.active::after { width: 16px; background: var(--accent); } #mobile-software-results { display: none; } .sr-item { - display: flex; align-items: center; gap: 6px; max-width: 18px; min-height: 16px; - padding: 0; border: 0; border-radius: 7px; background: transparent; - color: var(--text); cursor: pointer; pointer-events: auto; - transition: max-width .18s ease, background .15s ease; + align-items: center; gap: 6px; min-height: 20px; max-width: 278px; padding: 2px 5px; + border: 0; border-radius: 5px; background: transparent; color: var(--text); cursor: pointer; } - #software-result-rail:hover .sr-item, - #software-result-rail:focus-within .sr-item, - #software-result-rail.pinned .sr-item { max-width: 278px; } - .sr-item:hover, .sr-item:focus-visible { background: var(--panel-muted); } .sr-item::before { - content: ""; flex: none; width: 8px; height: 8px; margin-left: 2px; - border: 1.5px solid var(--panel); border-radius: 50%; background: var(--border); - box-shadow: 0 0 0 1px var(--border); + content: ""; flex: none; width: 8px; height: 8px; border: 1.5px solid var(--panel); + border-radius: 50%; background: var(--border); box-shadow: 0 0 0 1px var(--border); } .sr-item.running::before, .sr-item.queued::before, .sr-item.dispatched::before, .sr-item.offered::before, .sr-item.disconnected::before, .sr-item.cancelling::before { @@ -943,17 +936,11 @@ .sr-item.cancelled::before { background: #8a929b; box-shadow: 0 0 0 1px #8a929b; } @keyframes sr-pulse { 50% { opacity: .45; transform: scale(.82); } } .sr-label { - display: flex; align-items: baseline; gap: 6px; min-width: 0; max-width: 0; - opacity: 0; overflow: hidden; white-space: nowrap; padding: 1px 0; - text-align: left; transition: max-width .18s ease, opacity .18s ease, padding .18s ease; - } - #software-result-rail:hover .sr-label, - #software-result-rail:focus-within .sr-label, - #software-result-rail.pinned .sr-label { - max-width: 250px; opacity: 1; padding-right: 7px; + display: flex; align-items: baseline; gap: 6px; min-width: 0; max-width: 250px; + overflow: hidden; white-space: nowrap; text-align: left; } .sr-label strong, .sr-label small { overflow: hidden; text-overflow: ellipsis; } - .sr-label strong { max-width: 142px; font-size: 12px; font-weight: 600; } + .sr-label strong { max-width: 142px; font-size: 11px; font-weight: 600; } .sr-label small { max-width: 100px; color: var(--muted); font-size: 10px; } /* 阅读宽度:assistant/system/tool 限到 ~48rem(约 60-80 字/行,长文不至于满屏铺开难回扫); user 气泡更窄(36rem)。宽屏下提升可读性,窄屏 92% 仍生效(min 取小者) */ @@ -1790,10 +1777,8 @@ /* chat / 文件 微调 */ .msg { max-width: 96%; } - /* 手机端窄屏:目录轨道会挡正文 / 滚动,直接藏(覆盖 JS 的 inline display) */ - #msg-outline-rail { display: none !important; } - /* 软件结果仍可从右侧“作业”页查看;窄屏不让悬浮轨遮挡正文。 */ - #software-result-rail { display: none !important; } + /* 手机端窄屏:统一辅助导航会挡正文,直接藏;软件结果仍从“作业”页查看。 */ + #conversation-side-rail { display: none !important; } #chat-meta { padding: 6px 10px; gap: 6px; font-size: 11px; } #chat-meta .tid { display: none; } #chat-meta .desc { @@ -2263,10 +2248,12 @@
请在左侧选一个任务
- - - - + +