dsh-retrace 0.4.131 → 0.4.133

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -455,6 +455,21 @@ the **host surface**, not by semver alone.
455
455
 
456
456
  ---
457
457
 
458
+ ### Compatibility & runtime contract (for marketplace review)
459
+
460
+ | Item | Declaration |
461
+ |---|---|
462
+ | **Node.js** | `>=20.0.0` (see `package.json#engines`) |
463
+ | **DSH** | `>=0.1.5 <0.2.0` (see `package.json#dsh.compatibility`); desktop and web modes supported |
464
+ | **Runtime files** | All files needed at runtime are **committed** in this repository and declared in `package.json#files` (`lib/**`, `cordis.patch.yml`, `README*.md`, `HUMANS.txt`, `LICENSE`) |
465
+ | **Build output** | Build output is committed (`lib/client.bundle.js`, `lib/dynamic-client.js`, `lib/dynamic-host.js`) and reproducible in-repo: `pnpm i && pnpm build` (`scripts/generate-dynamic.mjs`, `scripts/build-client.mjs`) |
466
+ | **Single-file size** | Every distributed file is **below 256 KiB** (largest ≈ 249 KB) |
467
+ | **Runtime dependencies** | `dsh-log-contract` (log-contract validation), `zod` (argument validation). No post-install scripts, no network access, no additional code download |
468
+ | **Peer dependencies** | `@deepseek-ai/cordis` and DSH client packages (provided by DSH itself, not bundled or redistributed here) |
469
+ | **Permissions / external services** | Reads and writes only the local DSH session data directory and session logs. No external services, no telemetry, no data upload |
470
+ | **Failure bounds** | Every write (recall / edit / restore / rollback) backs up outside the target file first, then performs frame-level verification and resurfacing checks. On any failure it aborts and leaves the original untouched; ambiguous cases are refused with an actionable message instead of degrading silently |
471
+ | **Reversibility** | Each operation has a documented reversible path; local management scripts can roll back to the previous snapshot |
472
+
458
473
  ## ⚠️ Requirements & limitations
459
474
 
460
475
  - Only **user messages** can be re-sent from; recall works on user and assistant
package/README.zh.md CHANGED
@@ -516,3 +516,16 @@ retrace lineage <session> # 会话 parent 链谱系
516
516
  **会话谱系**:`GET /api/plugins/retrace/lineage?sessionId=` 沿当前会话的
517
517
  `parentSession` 接续链遍历(只读、带环保护),与 CLI `retrace lineage` 同一语义 ——
518
518
  任何客户端都能查到「这个会话是从哪个会话接着干/分叉出来的」,也是分叉拓扑的元数据源。
519
+
520
+ ## 兼容性与运行契约(Compatibility & runtime contract)
521
+
522
+ | 项 | 声明 |
523
+ |---|---|
524
+ | **Node.js** | `>=20.0.0`(见 `package.json#engines`) |
525
+ | **DSH** | `>=0.1.5 <0.2.0`(见 `package.json#dsh.compatibility`;桌面与 Web 两种模式均支持) |
526
+ | **构建产物** | 运行所需构建产物**随包提交**(`lib/client.bundle.js`、`lib/dynamic-client.js`、`lib/dynamic-host.js`);仓库内可复现:`pnpm i && pnpm build`(生成器 `scripts/generate-dynamic.mjs`、`scripts/build-client.mjs`) |
527
+ | **运行时依赖** | `dsh-log-contract`(日志契约校验)、`zod`(参数校验);**不联网、不下载额外代码** |
528
+ | **宿主依赖(peer)** | `@deepseek-ai/cordis` 与若干 `@deepseek-ai/dsh-*` 客户端包(由 DSH 提供,不由本插件分发) |
529
+ | **权限 / 外部服务** | 只读写本机会话数据目录与会话日志;**不访问外部服务、不上报数据**;不改动 DSH 安装目录之外的系统路径 |
530
+ | **失败边界** | 所有写入(撤回/编辑/恢复/回档)均在**目标文件之外先备份**、写后做帧级校验与折叠重放;失败即中止并保留原文件;不可判定时按"拒绝 + 可操作提示"处理,不静默降级 |
531
+ | **可回退** | 每个操作都有可逆路径;本地管理脚本可一键回退到上一快照 |
@@ -0,0 +1,223 @@
1
+ /**
2
+ * dsh-retrace — client stylesheet (injected <style>).
3
+ *
4
+ * Split out of lib/client.js (published per-file byte limit). STYLE_ID/CSS were
5
+ * module-private in lib/client.js and stay private there (imported, not
6
+ * re-exported); only the file they live in changed.
7
+ */
8
+ // ---------------------------------------------------------------------------
9
+ // Styles (plain injected <style>; removed with the plugin)
10
+ // ---------------------------------------------------------------------------
11
+ export const STYLE_ID = 'dsh-retrace-css'
12
+ export const CSS = `
13
+ .dsh-rt-strip{display:inline-flex;align-items:center;gap:2px}
14
+ .dsh-rt-icon{width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:transparent;border:none;border-radius:28px;display:inline-flex;justify-content:center;align-items:center;padding:0;font-size:14px;line-height:1}
15
+ .dsh-rt-icon:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}
16
+ .dsh-rt-icon:disabled{opacity:.4;cursor:default}
17
+ .dsh-rt-user-row{display:flex;flex-direction:column;align-items:flex-end;gap:4px;margin-top:2px}
18
+ .dsh-rt-user-actions{display:inline-flex;gap:6px}
19
+ .dsh-rt-chip{color:var(--dsw-alias-label-tertiary);cursor:pointer;background:var(--dsw-alias-interactive-bg-hover);border:none;border-radius:12px;padding:2px 10px;font-size:12px;line-height:20px}
20
+ .dsh-rt-chip:hover:not(:disabled){color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover-solid)}
21
+ .dsh-rt-chip:disabled{opacity:.5;cursor:default}
22
+ .dsh-rt-editor{display:flex;flex-direction:column;gap:6px;width:min(525px,82%);border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);border-radius:12px;padding:8px}
23
+ .dsh-rt-textarea{resize:vertical;width:100%;box-sizing:border-box;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-elevated);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;padding:8px 10px;font:inherit;font-size:14px;line-height:20px}
24
+ .dsh-rt-textarea:focus{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary)}
25
+ .dsh-rt-editor-buttons{display:flex;justify-content:flex-end;gap:8px}
26
+ .dsh-rt-editor-send{color:#fff;cursor:pointer;background:var(--dsw-alias-button-info-fill);border:none;border-radius:999px;padding:4px 16px;font-size:13px;line-height:20px}
27
+ .dsh-rt-editor-send:hover:not(:disabled){background:var(--dsw-alias-button-info-hover)}
28
+ .dsh-rt-editor-send:disabled{opacity:.4;cursor:default}
29
+ .dsh-rt-editor-cancel{color:var(--dsw-alias-label-secondary);cursor:pointer;background:transparent;border:none;border-radius:999px;padding:4px 12px;font-size:13px;line-height:20px}
30
+ .dsh-rt-editor-cancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
31
+ .dsh-rt-error{display:flex;flex-direction:column;align-items:flex-end;gap:6px;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px;max-width:min(525px,82%)}
32
+ .dsh-rt-error-text{text-align:right}
33
+ /* 兜底动作(仅改文本/保留后续):只在重开被守卫拒绝时出现,**不与主按钮并列**。 */
34
+ .dsh-rt-error-fallback{display:flex;flex-direction:column;align-items:flex-end;gap:4px;padding:6px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-elevated)}
35
+ .dsh-rt-error-why{color:var(--dsw-alias-label-tertiary);text-align:right}
36
+ .dsh-rt-fallback-send{align-self:flex-end}
37
+ .dsh-rt-pause-note{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;max-width:min(525px,82%)}
38
+ .dsh-rt-marker-block{display:flex;flex-direction:column;align-items:center;gap:4px;width:100%;max-width:var(--dsh-chat-content-width);box-sizing:border-box;margin:0 auto;padding:2px 0}
39
+ .dsh-rt-marker{text-align:center;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px}
40
+ .dsh-rt-marker-hint{text-align:center;color:var(--dsw-alias-state-warning-primary);font-size:11px;line-height:16px;margin-top:2px}
41
+ /* 撤销撤回(0.4.124):标记行上的「恢复显示」按钮 + 「已恢复显示」状态行。 */
42
+ .dsh-rt-marker-unhide{cursor:pointer;margin-top:2px}
43
+ .dsh-rt-marker-restored{text-align:center;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;margin-top:2px}
44
+ .dsh-rt-reference{width:min(525px,82%);box-sizing:border-box;border:1px dashed var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-elevated);border-radius:10px;padding:2px 12px}
45
+ .dsh-rt-reference summary{color:var(--dsw-alias-label-caption);cursor:pointer;user-select:none;font-size:12px;line-height:22px;list-style:none;display:inline-flex;align-items:center;gap:6px;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
46
+ .dsh-rt-reference summary::-webkit-details-marker{display:none}
47
+ .dsh-rt-reference summary:before{content:"▸";transition:transform .12s;font-size:10px}
48
+ .dsh-rt-reference[open] summary:before{transform:rotate(90deg)}
49
+ .dsh-rt-reference summary:hover{color:var(--dsw-alias-label-secondary)}
50
+ .dsh-rt-reference-text{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;white-space:pre-wrap;overflow-wrap:anywhere;padding:2px 0 6px}
51
+ .dsh-rt-options{display:flex;flex-direction:column;gap:8px;padding:2px 0}
52
+ .dsh-rt-options-title{color:var(--dsw-alias-label-secondary);font-size:13px;font-weight:600;line-height:20px}
53
+ .dsh-rt-option{display:flex;align-items:center;gap:8px;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px;cursor:pointer}
54
+ .dsh-rt-option-text{display:flex;flex-direction:column;gap:1px;min-width:0}
55
+ .dsh-rt-option-label{color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px}
56
+ .dsh-rt-option-desc{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:17px}
57
+ .dsh-rt-option-number{align-items:flex-start}
58
+ .dsh-rt-retention-input{width:64px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-elevated);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:2px 6px;font:inherit;font-size:13px;outline:none;margin-top:1px}
59
+ .dsh-rt-retention-input:focus{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary)}
60
+ .dsh-rt-option input{accent-color:var(--dsw-alias-state-business-primary)}
61
+ /* ---- P1 timeline (conversation view tab) ---- */
62
+
63
+ .dsh-rt-view{box-sizing:border-box;display:flex;flex-direction:column;gap:6px;flex:1 1 0%;min-height:0;overflow:hidden;padding:12px 16px 0}
64
+ .dsh-rt-timeline-head{display:flex;align-items:center;gap:8px;flex:none}
65
+ .dsh-rt-timeline-title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600;line-height:20px;flex:1}
66
+ .dsh-rt-view-intro{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;flex:none;padding:2px 0 4px}
67
+ /* 页首:这是什么(带次数)+ 顺序提示 */
68
+ .dsh-rt-intro-order{color:var(--dsw-alias-label-tertiary);margin-left:6px}
69
+ /* 第二行的口径前缀 */
70
+ .dsh-rt-what-label{flex:none;color:var(--dsw-alias-label-caption);font-size:11px;line-height:16px}
71
+ /* 行尾「›」:这一档可以点开 */
72
+ /* 逐级引导线:文本里仍是「│ 」(复制成纯文本也读得出来),画面上是一条该级配色的
73
+ 细色条——缩进不靠量像素,色条本身就说清"谁在谁下面"。 */
74
+ .dsh-rt-indent-guide{flex:none;display:inline-block;width:3px;height:16px;margin:0 7px 0 0;border-radius:2px;overflow:hidden;color:transparent;font-size:1px;line-height:1px;white-space:pre;align-self:center}
75
+ .dsh-rt-indent-guide-1{background:var(--dsw-alias-label-secondary)}
76
+ .dsh-rt-indent-guide-2{background:var(--dsw-alias-state-warning-primary)}
77
+ .dsh-rt-indent-guide-3{background:var(--dsw-alias-state-info-primary)}
78
+ /* 展开层内悬浮收起条:sticky + 高度 0 ⇒ 视觉上钉在列表视口顶部,但不占内容高度
79
+ (虚拟化的前缀和只由行模型决定;任何进流元素都会让 offsets 与渲染错位)。 */
80
+ .dsh-rt-collapse-hint{position:sticky;top:6px;height:0;display:flex;justify-content:center;align-items:flex-start;z-index:3;pointer-events:none}
81
+ .dsh-rt-collapse-hint-btn{pointer-events:auto;box-shadow:0 2px 8px var(--dsw-alias-bg-mask,rgba(0,0,0,.18));background:var(--dsw-alias-bg-elevated,var(--dsw-alias-bg-base));border:1px solid var(--dsw-alias-border-l2)}
82
+ .dsh-rt-what-none{color:var(--dsw-alias-label-secondary);font-style:normal}
83
+ .dsh-rt-what-jump{background:none;border:0;padding:0;margin:0;font:inherit;color:var(--dsw-alias-state-info-primary);cursor:pointer;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
84
+ .dsh-rt-what-jump:hover{text-decoration:underline}
85
+ .dsh-rt-line-sep{flex:none;color:var(--dsw-alias-label-caption);font-size:11px;line-height:16px}
86
+ .dsh-rt-version-round{flex:none;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:16px}
87
+ .dsh-rt-row-open{flex:none;background:transparent;border:none;padding:0 2px;margin-top:1px;cursor:pointer;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:16px}
88
+ .dsh-rt-row-open:hover{color:var(--dsw-alias-label-primary)}
89
+ /* 面板级错误边界:坏的是这一块(不是整页) */
90
+ .dsh-rt-view-error{gap:6px;padding:12px}
91
+ .dsh-rt-error-title{color:var(--dsw-alias-state-error-primary);font-size:13px;font-weight:600;line-height:20px}
92
+ .dsh-rt-error-detail{margin:0;color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-mono);font-size:11px;line-height:16px;white-space:pre-wrap;word-break:break-all}
93
+ .dsh-rt-error-retry{align-self:flex-start}
94
+ /* Filtered host-side replacements: one muted line, never a timeline row. */
95
+ .dsh-rt-host-note{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;flex:none;padding:2px 0}
96
+ .dsh-rt-timeline-git{display:flex;align-items:center;gap:6px;flex:none;border:1px dashed var(--dsw-alias-border-l2);border-radius:8px;padding:4px 8px}
97
+ .dsh-rt-doctor{border-color:var(--dsw-alias-state-warning-primary);background:var(--dsw-alias-state-warn-tertiary)}
98
+ .dsh-rt-doctor .dsh-rt-timeline-git-text{color:var(--dsw-alias-state-warning-primary)}
99
+ .dsh-rt-timeline-git-text{color:var(--dsw-alias-label-caption);font-size:11px;line-height:16px}
100
+ .dsh-rt-timeline-list{overflow-y:auto;flex:1;min-height:0;position:relative;--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2)}
101
+ .dsh-rt-timeline-empty{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;padding:12px 4px;text-align:center}
102
+ .dsh-rt-version{position:absolute;left:0;right:0;height:60px;box-sizing:border-box;display:flex;align-items:flex-start;gap:8px;border:1px solid transparent;border-radius:10px;padding:6px 8px;overflow:hidden}
103
+ .dsh-rt-version:hover{background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l2)}
104
+ .dsh-rt-version-kind{flex:none;width:22px;height:22px;display:inline-flex;justify-content:center;align-items:center;border-radius:6px;background:var(--dsw-alias-fill-l2);color:var(--dsw-alias-label-secondary);font-size:12px;margin-top:1px}
105
+ .dsh-rt-version-kind-restore{background:var(--dsw-alias-state-success-bg);color:var(--dsw-alias-state-success-primary)}
106
+ .dsh-rt-version-kind-compaction{background:var(--dsw-alias-fill-l2);color:var(--dsw-alias-label-caption)}
107
+ .dsh-rt-version-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
108
+ .dsh-rt-version-line{display:flex;align-items:center;gap:8px;min-width:0;white-space:nowrap}
109
+ .dsh-rt-version-kind-label{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:600;line-height:16px}
110
+ .dsh-rt-version-time,.dsh-rt-version-msgs{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
111
+ /* 紧凑行首行的计数(原正文里的 impact 行已并入这里,同一信息不占两行) */
112
+ .dsh-rt-version-count{flex:none;color:var(--dsw-alias-state-warning-primary);font-size:11px;line-height:16px}
113
+ .dsh-rt-version-files{color:var(--dsw-alias-label-caption);font-size:11px;line-height:16px;overflow:hidden;text-overflow:ellipsis}
114
+ /* ---- boundary digest (what): verbatim quotes vs optional AI summary ---- */
115
+ .dsh-rt-what-quote{display:flex;align-items:baseline;gap:6px;min-width:0;font-size:11px;line-height:16px;white-space:nowrap;overflow:hidden;overflow-wrap:anywhere}
116
+ .dsh-rt-what-role,.dsh-rt-what-seq{flex:none;color:var(--dsw-alias-label-caption);font-size:11px}
117
+ .dsh-rt-what-text{min-width:0;overflow:hidden;text-overflow:ellipsis;font-family:var(--dsw-font-mono)}
118
+ .dsh-rt-what-more{flex:none;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
119
+ /* NEW path = emphasized accent (proposal green) */
120
+ .dsh-rt-what-new{border-left:2px solid var(--dsw-alias-state-success-primary);padding-left:6px;background:var(--dsw-alias-state-success-bg);border-radius:0 6px 6px 0;color:var(--dsw-alias-state-success-primary)}
121
+ .dsh-rt-what-new .dsh-rt-what-text{color:var(--dsw-alias-state-success-primary);text-decoration:none}
122
+ .dsh-rt-what-new-tag{flex:none;font-size:10px;line-height:16px;color:var(--dsw-alias-state-success-primary);font-weight:600}
123
+ /* OLD path = muted grey + explicitly void (struck-through quote) */
124
+ .dsh-rt-what-old{border-left:2px solid var(--dsw-alias-border-l2);padding-left:6px;color:var(--dsw-alias-label-tertiary);opacity:.72}
125
+ .dsh-rt-what-old .dsh-rt-what-text{color:var(--dsw-alias-label-tertiary);text-decoration:line-through}
126
+ .dsh-rt-what-void{flex:none;font-size:10px;line-height:16px;color:var(--dsw-alias-label-tertiary)}
127
+ /* AI summary = its own element, italic + faint background, never mixed with a quote */
128
+ .dsh-rt-what-summary{display:flex;align-items:baseline;gap:6px;min-width:0;font-size:11px;line-height:16px;font-style:italic;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-l2);border-radius:6px;padding:0 6px;white-space:nowrap;overflow:hidden}
129
+ .dsh-rt-what-summary-tag{flex:none;font-size:10px;line-height:16px;font-weight:600;font-style:normal;color:var(--dsw-alias-label-caption)}
130
+ .dsh-rt-what-summary-text{min-width:0;overflow:hidden;text-overflow:ellipsis}
131
+ /* ---- outline nesting: level colours (host alias tokens → both themes) ---- */
132
+ .dsh-rt-level-1{border-left:3px solid var(--dsw-alias-label-secondary)}
133
+ .dsh-rt-level-2{border-left:3px solid var(--dsw-alias-state-warning-primary)}
134
+ .dsh-rt-level-3{border-left:3px solid var(--dsw-alias-state-info-primary)}
135
+ .dsh-rt-current{border-left:3px solid var(--dsw-alias-state-success-primary)}
136
+ .dsh-rt-level-1 .dsh-rt-version-kind-label,.dsh-rt-level-1 .dsh-rt-tree-hint{color:var(--dsw-alias-label-secondary)}
137
+ .dsh-rt-level-2 .dsh-rt-version-kind-label,.dsh-rt-level-2 .dsh-rt-tree-hint{color:var(--dsw-alias-state-warning-primary)}
138
+ .dsh-rt-level-3 .dsh-rt-version-kind-label,.dsh-rt-level-3 .dsh-rt-tree-hint{color:var(--dsw-alias-state-info-primary)}
139
+ .dsh-rt-current .dsh-rt-version-kind-label{color:var(--dsw-alias-state-success-primary)}
140
+ .dsh-rt-tree-toggle{position:absolute;left:0;right:0;height:60px;box-sizing:border-box;display:flex;align-items:center;border:1px solid transparent;border-radius:10px;overflow:hidden}
141
+ .dsh-rt-tree-btn{color:var(--dsw-alias-label-tertiary);font-size:11px}
142
+ .dsh-rt-tree-hint{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
143
+ /* ---- plain-text row: a boundary with no digest content (quiet rows never
144
+ land in the timeline, so no absolute-positioned quiet row exists) ---- */
145
+ .dsh-rt-fallback{position:absolute;left:0;right:0;height:60px;box-sizing:border-box;display:flex;align-items:center;gap:8px;border:1px solid transparent;border-radius:10px;padding:6px 8px;overflow:hidden}
146
+ .dsh-rt-plain-text{min-width:0;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:16px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
147
+ /* ---- R20/R24: bottom COLLAPSED block of quiet/simple changes ---- */
148
+ .dsh-rt-quiet-block{display:flex;flex-direction:column;gap:2px;flex:none;border-top:1px solid var(--dsw-alias-border-l2);padding-top:6px;margin-top:6px;max-height:132px;overflow:hidden}
149
+ .dsh-rt-quiet-head{display:flex;align-items:center;background:transparent;border:none;padding:0;cursor:pointer;font:inherit;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
150
+ .dsh-rt-quiet-head:hover{color:var(--dsw-alias-label-secondary)}
151
+ .dsh-rt-quiet-list{display:flex;flex-direction:column;gap:1px;overflow-y:auto;min-height:0}
152
+ .dsh-rt-quiet-row{display:flex;align-items:center;gap:6px;min-width:0}
153
+ .dsh-rt-quiet-row .dsh-rt-plain-text{color:var(--dsw-alias-label-tertiary)}
154
+ .dsh-rt-quiet-run{display:flex;flex-direction:column;gap:1px;min-width:0}
155
+ .dsh-rt-quiet-run-head{display:flex;align-items:center;gap:6px;min-width:0}
156
+ .dsh-rt-quiet-run-head .dsh-rt-plain-text{color:var(--dsw-alias-label-tertiary)}
157
+ .dsh-rt-quiet-run-list{display:flex;flex-direction:column;gap:1px;padding-left:14px;min-height:0}
158
+ .dsh-rt-quiet-btn{flex:none}
159
+ /* ---- R31: the always-visible start line of 「现在的路」 ---- */
160
+ .dsh-rt-path-start{display:flex;align-items:baseline;gap:6px;min-width:0}
161
+ .dsh-rt-path-start-tag{flex:none;color:var(--dsw-alias-label-caption);font-size:11px;line-height:16px;font-weight:600}
162
+ .dsh-rt-path-start-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:16px}
163
+ .dsh-rt-path-start-counts{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
164
+ .dsh-rt-path-start-summary{display:flex;align-items:baseline;gap:6px;min-width:0;font-size:11px;line-height:16px;font-style:italic;color:var(--dsw-alias-label-secondary)}
165
+ .dsh-rt-path-start-summary-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
166
+ /* ---- 「现在的路」fixed bottom block ---- */
167
+ .dsh-rt-path{display:flex;flex-direction:column;gap:2px;flex:none;border-top:1px solid var(--dsw-alias-border-l2);padding-top:6px;margin-top:6px;max-height:132px;overflow:hidden}
168
+ .dsh-rt-path-title{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;line-height:18px;flex:none}
169
+ .dsh-rt-path-list{display:flex;flex-direction:column;gap:1px;overflow-y:auto;min-height:0}
170
+ .dsh-rt-path-row{display:flex;align-items:baseline;gap:6px;min-width:0;width:100%;box-sizing:border-box;background:transparent;border:none;border-radius:6px;padding:1px 4px;text-align:left;cursor:pointer;font:inherit}
171
+ .dsh-rt-path-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
172
+ .dsh-rt-path-n{flex:none;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
173
+ .dsh-rt-path-q{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-state-success-primary);font-size:11px;line-height:16px}
174
+ .dsh-rt-path-arrow{flex:none;color:var(--dsw-alias-label-caption);font-size:11px}
175
+ .dsh-rt-path-a{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:16px}
176
+ .dsh-rt-path-empty{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
177
+ .dsh-rt-plain{overflow:hidden}
178
+ .dsh-rt-plain-note{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;flex:none}
179
+ .dsh-rt-path-head{display:flex;align-items:center;background:transparent;border:none;padding:0;cursor:pointer;font:inherit;color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;line-height:18px;text-align:left}
180
+ .dsh-rt-path-head:hover{color:var(--dsw-alias-label-primary)}
181
+ .dsh-rt-version-text{color:var(--dsw-alias-label-secondary);font-size:11px;line-height:15px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
182
+ .dsh-rt-version-actions{flex:none;display:inline-flex;gap:4px;opacity:0;transition:opacity .1s}
183
+ .dsh-rt-version:hover .dsh-rt-version-actions{opacity:1}
184
+ .dsh-rt-chip-danger{color:var(--dsw-alias-state-error-primary)}
185
+ .dsh-rt-chip-danger:hover{color:var(--dsw-alias-state-error-primary)}
186
+ .dsh-rt-modal{position:absolute;inset:0;z-index:130;display:flex;flex-direction:column;gap:8px;box-sizing:border-box;border-radius:12px;background:var(--dsw-specific-menu);padding:10px;box-shadow:var(--dsw-shadow-lv3)}
187
+ /* 行内撤回二次确认:fixed 遮罩 + 同一张 .dsh-rt-modal 卡片(不作为定位容器时改 auto 尺寸)。 */
188
+ .dsh-rt-confirm-scrim{position:fixed;inset:0;z-index:140;display:flex;align-items:center;justify-content:center;padding:16px;background:rgba(0,0,0,.32)}
189
+ .dsh-rt-modal.dsh-rt-confirm-card{position:relative;inset:auto;width:min(360px,100%);max-height:70vh}
190
+ .dsh-rt-modal-title{display:flex;align-items:center;gap:8px;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600;line-height:20px}
191
+ .dsh-rt-modal-sub{color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:400}
192
+ .dsh-rt-modal-body{display:flex;flex-direction:column;gap:6px;overflow-y:auto;min-height:0}
193
+ .dsh-rt-modal-line{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
194
+ .dsh-rt-modal-files{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px;max-height:160px;overflow-y:auto}
195
+ .dsh-rt-modal-files li{display:flex;gap:8px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}
196
+ .dsh-rt-art-restore{color:var(--dsw-alias-state-success-primary);flex:none}
197
+ .dsh-rt-art-delete{color:var(--dsw-alias-state-error-primary);flex:none}
198
+ .dsh-rt-art-skip{color:var(--dsw-alias-label-caption);flex:none}
199
+ .dsh-rt-art-path{font-family:var(--dsw-font-mono);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
200
+ .dsh-rt-modal-scope{display:flex;flex-direction:column;gap:4px}
201
+ .dsh-rt-modal-buttons{display:flex;justify-content:flex-end;gap:8px;flex:none}
202
+ .dsh-rt-confirm{background:var(--dsw-alias-state-error-primary)}
203
+ .dsh-rt-modal-json{margin:0;overflow:auto;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-elevated);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px;font-family:var(--dsw-font-mono);font-size:11px;line-height:15px;white-space:pre-wrap;word-break:break-all}
204
+ .dsh-rt-fork-badge-code{font-family:var(--dsw-font-mono);font-size:12px;font-weight:600;line-height:16px;color:var(--dsw-alias-state-info-primary);flex:none}
205
+ /* 关闭守卫:运行中横幅 / A 明细模态 / 放行提示(独立于会话视图) */
206
+ .dsh-rt-guard-banner{position:fixed;top:10px;right:10px;z-index:2147483000;max-width:min(430px,calc(100vw - 20px));background:var(--dsw-alias-bg-elevated,#262626);border:1px solid #b8860b;border-radius:10px;padding:8px 10px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary,#eee);box-shadow:0 4px 20px rgba(0,0,0,.4);display:flex;flex-direction:column;gap:4px;text-align:left}
207
+ .dsh-rt-guard-banner-head{display:flex;align-items:center;gap:6px;font-weight:600;color:#f0c674}
208
+ .dsh-rt-guard-banner-note{color:var(--dsw-alias-label-caption,#aaa);font-size:11px;line-height:16px}
209
+ .dsh-rt-guard-banner-list{margin:2px 0 0;padding:0 0 0 14px;color:var(--dsw-alias-label-primary,#eee);font-family:var(--dsw-font-mono,ui-monospace,monospace);font-size:11px;line-height:17px;white-space:pre-line}
210
+ .dsh-rt-guard-link{background:none;border:none;padding:0;color:var(--dsw-alias-state-info-primary,#7ab8ff);font-size:11px;cursor:pointer}
211
+ .dsh-rt-guard-link:hover{text-decoration:underline}
212
+ .dsh-rt-guard-btn{border:1px solid var(--dsw-alias-border-l2,#555);background:transparent;color:var(--dsw-alias-label-primary,#eee);border-radius:6px;padding:2px 10px;font-size:12px;line-height:20px;cursor:pointer}
213
+ .dsh-rt-guard-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.08))}
214
+ .dsh-rt-guard-btn-primary{border-color:#a33;background:#8b1f1f;color:#fff;font-weight:600}
215
+ .dsh-rt-guard-btn-primary:hover{background:#a33}
216
+ .dsh-rt-guard-overlay{position:fixed;inset:0;z-index:2147483001;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;padding:16px}
217
+ .dsh-rt-guard-modal{background:var(--dsw-alias-bg-elevated,#202020);border:1px solid #b8860b;border-radius:12px;max-width:min(520px,calc(100vw - 32px));padding:14px 16px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#eee);box-shadow:0 10px 40px rgba(0,0,0,.5);display:flex;flex-direction:column;gap:10px}
218
+ .dsh-rt-guard-modal-title{font-weight:700;color:#f0c674;font-size:14px}
219
+ .dsh-rt-guard-modal-lines{margin:0;padding:0 0 0 16px;font-family:var(--dsw-font-mono,ui-monospace,monospace);font-size:12px;line-height:19px;white-space:pre-line}
220
+ .dsh-rt-guard-modal-hint{color:var(--dsw-alias-label-caption,#aaa);font-size:11px;line-height:16px}
221
+ .dsh-rt-guard-modal-actions{display:flex;justify-content:flex-end;gap:8px}
222
+ .dsh-rt-guard-toast{position:fixed;left:50%;bottom:28px;transform:translateX(-50%);z-index:2147483002;background:var(--dsw-alias-bg-elevated,#262626);border:1px solid var(--dsw-alias-border-l2,#555);border-radius:8px;padding:6px 12px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary,#eee);box-shadow:0 4px 16px rgba(0,0,0,.35);max-width:min(560px,calc(100vw - 24px))}
223
+ `