diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8c63038..4101be6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@
## Unreleased
+- 对话两侧的软件作业与轮次导航改为更紧凑的一致样式,并增加常显的名称和数量;既可悬停速览,也可点击固定展开,状态和当前位置更清楚。
+
- DeepSeek Flash 的思考过程恢复实时显示,不再等到正文开始后才集中出现。
- Windows 专业软件节点启用完整的新管理界面,节点状态、首次注册、软件环境、本机任务、ANSYS 验收和数据目录迁移均可直接管理;应用图标、导航、状态提示和窄窗口布局也更清晰,关闭窗口后仍会继续在托盘运行。
diff --git a/tests/test_static_vendor.py b/tests/test_static_vendor.py
index 8c16fd0..020f8bd 100644
--- a/tests/test_static_vendor.py
+++ b/tests/test_static_vendor.py
@@ -241,6 +241,10 @@ class StaticVendorTests(unittest.TestCase):
self.assertNotIn('
科研助手
', frontend)
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)
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 b4e45fc..8a54abe 100644
--- a/web/static/dev.html
+++ b/web/static/dev.html
@@ -843,56 +843,94 @@
0%, 100% { box-shadow: none; }
18% { box-shadow: 0 0 0 2px var(--accent), 0 0 0 6px var(--accent-soft); }
}
- /* 消息目录:悬浮在对话右缘的圆点轨道。容器 pointer-events:none 让圆点间隙漏给
- 滚动条/正文(不挡滚动),只有圆点本身可点;hover 圆点整列展开标题(ChatGPT 式)。 */
+ /* 对话两侧索引轨共用紧凑视觉:收起时只占一条窄边轨,hover / 键盘聚焦后
+ 展开可读标签。轨道本身不拦截正文滚动,只有条目可交互。 */
#msg-outline-rail {
- position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
+ position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
max-height: 72%; display: flex; flex-direction: column; align-items: flex-end;
- gap: 7px; padding: 8px 4px; overflow-y: auto; overflow-x: hidden; z-index: 5;
- pointer-events: none; scrollbar-width: none;
+ 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;
+ }
+ #msg-outline-rail:hover, #msg-outline-rail:focus-within {
+ border-color: var(--border); box-shadow: 0 6px 20px rgba(24, 32, 41, .10);
}
#msg-outline-rail::-webkit-scrollbar { display: none; }
- .ol-dot {
- display: flex; align-items: center; justify-content: flex-end; gap: 8px;
- background: transparent; border: none; padding: 0; cursor: pointer;
- max-width: 22px; pointer-events: auto; transition: max-width .18s ease;
+ .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;
}
- #msg-outline-rail:hover .ol-dot { max-width: 240px; }
+ .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;
+ }
+ .conversation-rail-caption:hover, .conversation-rail-caption:focus-visible,
+ .pinned > .conversation-rail-caption {
+ color: var(--accent); background: var(--accent-soft);
+ }
+ .pinned > .conversation-rail-caption::after {
+ transform: rotate(225deg) translate(-1px, -1px);
+ }
+ .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;
+ }
+ #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;
- background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
- padding: 2px 8px; box-shadow: 0 1px 4px rgba(0,0,0,0.06);
- transition: max-width .18s ease, opacity .18s ease;
+ 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;
}
- #msg-outline-rail:hover .ol-label { max-width: 210px; opacity: 1; }
.ol-dot::after {
- content: ""; flex: none; width: 16px; height: 4px; border-radius: 3px;
+ content: ""; flex: none; width: 12px; height: 3px; border-radius: 3px;
background: var(--border); transition: background .15s ease, width .15s ease;
}
- .ol-dot:hover::after { background: var(--muted); }
- .ol-dot.active::after { background: var(--accent); width: 20px; }
+ .ol-dot:hover, .ol-dot: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: 4px; top: 50%; transform: translateY(-50%);
+ position: absolute; left: 6px; top: 50%; transform: translateY(-50%);
max-height: 72%; display: flex; flex-direction: column; align-items: flex-start;
- gap: 7px; padding: 8px 4px; overflow-y: auto; overflow-x: hidden; z-index: 6;
- pointer-events: none; scrollbar-width: none;
+ 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; }
#mobile-software-results { display: none; }
.sr-item {
- display: flex; align-items: center; gap: 8px; max-width: 24px; min-height: 24px;
- padding: 0; border: 0; background: transparent; color: var(--text); cursor: pointer;
- pointer-events: auto; transition: max-width .18s ease;
+ 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;
}
#software-result-rail:hover .sr-item,
- #software-result-rail:focus-within .sr-item { max-width: 260px; }
+ #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: 10px; height: 10px; margin-left: 3px;
- border: 2px solid var(--panel); border-radius: 50%; background: var(--border);
+ 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);
}
.sr-item.running::before, .sr-item.queued::before, .sr-item.dispatched::before,
@@ -905,18 +943,18 @@
.sr-item.cancelled::before { background: #8a929b; box-shadow: 0 0 0 1px #8a929b; }
@keyframes sr-pulse { 50% { opacity: .45; transform: scale(.82); } }
.sr-label {
- min-width: 0; max-width: 0; opacity: 0; overflow: hidden; white-space: nowrap;
- border: 1px solid transparent; border-radius: 10px; padding: 4px 0;
- background: var(--panel); box-shadow: 0 2px 8px rgba(0,0,0,.08);
+ 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 {
- max-width: 228px; opacity: 1; padding: 4px 8px; border-color: var(--border);
+ #software-result-rail:focus-within .sr-label,
+ #software-result-rail.pinned .sr-label {
+ max-width: 250px; opacity: 1; padding-right: 7px;
}
- .sr-label strong, .sr-label small { display: block; overflow: hidden; text-overflow: ellipsis; }
- .sr-label strong { font-size: 12px; font-weight: 600; }
- .sr-label small { margin-top: 1px; color: var(--muted); font-size: 10px; }
+ .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 small { max-width: 100px; color: var(--muted); font-size: 10px; }
/* 阅读宽度:assistant/system/tool 限到 ~48rem(约 60-80 字/行,长文不至于满屏铺开难回扫);
user 气泡更窄(36rem)。宽屏下提升可读性,窄屏 92% 仍生效(min 取小者) */
.msg { border: 1px solid var(--border); border-radius: var(--r-md); padding: 8px 12px; max-width: min(92%, 48rem); animation: msg-in .22s cubic-bezier(.2,.7,.2,1); scroll-margin-top: 16px; }
diff --git a/web/static/js/chat.js b/web/static/js/chat.js
index e43702d..c470fc4 100644
--- a/web/static/js/chat.js
+++ b/web/static/js/chat.js
@@ -1257,12 +1257,17 @@ function renderOutlineRail() {
if (items.length < 2) { // 0/1 轮没必要显示目录
rail.style.display = "none";
rail.innerHTML = "";
+ rail.classList.remove("pinned");
return;
}
rail.style.display = "";
- rail.innerHTML = items.map((it, i) => {
+ const pinned = rail.classList.contains("pinned");
+ const caption = ``;
+ rail.innerHTML = caption + items.map((it, i) => {
const label = it.snippet || `第 ${i + 1} 轮`;
- return `