dsh-vscode-mode 0.22.0 → 0.23.1

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/lib/client.js CHANGED
@@ -55,8 +55,19 @@ window.__ModuleLoader__.load({
55
55
  _deepseek_ai_dsh_client_ui_primitives = __toESM(_deepseek_ai_dsh_client_ui_primitives, 1);
56
56
  let react_jsx_runtime = require("react/jsx-runtime");
57
57
  //#region \0dsh-css:/home/runner/work/DSH_VsCodeMode/DSH_VsCodeMode/src/client/styles/editor.css.mjs
58
- const css$3 = "/* dsh-vscode-mode 编辑区样式(原 ensureEdrvStyles 字符串,落盘为真 .css,由 tsdown CSS-inline 注入) */\n/* 主题:跟随官方 --dsw-alias-* 令牌(DSH 明暗与第三方皮肤生效)。\n 本作用域不再重定义官方令牌(旧实现曾在此硬覆盖浅色盘,导致插件不跟随 DSH 主题);\n 旧环境/缺令牌场景由每条声明的 var(--dsw-alias-*, 回落值) 兜底。\n @author ddj 2026-09-10 */\n[data-edrv-view] { box-sizing: border-box; --edrv-tab-h: 34px; }\n\n[data-edrv-view] .edrv-tabs { display: flex; align-items: center; gap: 2px; padding: 2px 8px 0; background: var(--dsw-alias-bg-layer-1, transparent); overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; flex-shrink: 0; overscroll-behavior-x: contain; } /* overscroll-behavior-x: 横滚到边界不把滚动链传给页面(滚轮横滚接管更可控)@author ddj 2026年09月18号 */\n[data-edrv-view] .edrv-tab { user-select: none; -webkit-user-select: none; display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 8px 0 12px; border: none; border-radius: 6px 6px 0 0; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 12px; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-tab.edrv-tab-active { background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); box-shadow: inset 0 -2px 0 var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-tab .edrv-tab-star { color: var(--dsw-alias-brand-primary, #4f8cff); font-size: 13px; font-weight: 700; line-height: 1; flex-shrink: 0; } /* 未保存修改星号 @author ddj 2026年09月09号 */\n[data-edrv-view] .edrv-tab .edrv-tab-x { appearance: none; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 16px; width: 16px; height: 16px; padding: 0; border: 0; border-radius: 4px; background: transparent; box-shadow: none; color: var(--dsw-alias-label-tertiary, #888); font-family: inherit; font-size: 11px; line-height: 1; cursor: pointer; }\n[data-edrv-view] .edrv-tab .edrv-tab-x:focus-visible { outline: 1px solid var(--dsw-alias-state-info-primary, #4d6bfe); outline-offset: 1px; }\n[data-edrv-view] .edrv-tab .edrv-tab-x:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.12)); color: var(--dsw-alias-label-primary, #eee); }\n/* 固定页签:📌 标记 + 免关闭按钮(固定页签不渲染 ×,仅可经右键菜单取消固定) */\n[data-edrv-view] .edrv-tab .edrv-tab-pin { flex-shrink: 0; font-size: 10px; line-height: 1; opacity: .85; }\n[data-edrv-view] .edrv-tab.edrv-tab-pinned { background: var(--dsw-alias-bg-layer-2, rgba(255,255,255,.04)); }\n/* 页签行 = 可横滚的页签区 + 行尾固定工具列(计划预览页签与磁盘页签同处这一行的滚动区)@author ddj 2026年10月20号 */\n[data-edrv-view] .edrv-tabs-row { display: flex; align-items: center; min-width: 0; flex: 0 0 auto; background: var(--dsw-alias-bg-layer-1, transparent); }\n/* 特异性需盖过下面 .edrv-group .edrv-tabs 的 flex: 0 0 auto,页签区才能吃掉行走宽、把工具列压到行尾 */\n[data-edrv-view] .edrv-group .edrv-tabs-row > .edrv-tabs { flex: 1 1 auto; min-width: 0; background: transparent; }\n[data-edrv-view] .edrv-tabs-tools { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; padding: 0 6px 0 2px; }\n[data-edrv-view] .edrv-tab-add { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; margin-left: 2px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 14px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab-add:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-pathbar { display: flex; align-items: center; gap: 8px; height: 26px; padding: 0 10px; background: var(--dsw-alias-bg-layer-1, #ffffff); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 12px; flex-shrink: 0; overflow: hidden; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-name { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-full { flex: 1; min-width: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; direction: rtl; text-align: left; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-meta { flex-shrink: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-status { color: var(--dsw-alias-label-secondary, #52606d); }\n/* 非焦点分组的静态路径条(GroupPathbar):可点击聚焦所属组,悬停给反馈 @author ddj 2026-10-16 */\n[data-edrv-view] .edrv-pathbar[data-edrv-pb-static] { cursor: pointer; }\n[data-edrv-view] .edrv-pathbar[data-edrv-pb-static]:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.06)); }\n[data-edrv-view] .edrv-pb-update { display: inline-flex; align-items: center; gap: 4px; height: 20px; flex: none; padding: 0 6px; border: 1px solid var(--dsw-alias-border-l1, #d9e0e4); border-radius: 4px; background: var(--dsw-alias-bg-layer-2, #f1f4f5); color: var(--dsw-alias-label-secondary, #485762); font-size: 11px; cursor: pointer; }\n[data-edrv-view] .edrv-pb-update:hover { background: var(--dsw-alias-interactive-bg-hover, #e7edf0); }\n[data-edrv-view] .edrv-pb-update:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #2869b4); outline-offset: 1px; }\n@media (max-width: 620px) { [data-edrv-view] .edrv-pb-update { width: 24px; padding: 0 4px; overflow: hidden; font-size: 0; } }\n[data-edrv-view] .edrv-path-input { flex: 1; min-width: 140px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-wrap { position: relative; flex-shrink: 0; display: flex; align-items: center; padding: 2px 8px 2px 4px; }\n[data-edrv-view] .edrv-search { width: 190px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-pop { position: absolute; top: calc(100% + 4px); right: 8px; width: min(460px, calc(100vw - 24px)); max-height: 300px; overflow: auto; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 10px; box-shadow: 0 6px 20px rgba(0,0,0,.4); z-index: 60; padding: 4px; }\n[data-edrv-view] .edrv-search-item { display: flex; flex-direction: column; gap: 1px; padding: 5px 8px; border-radius: 6px; cursor: pointer; }\n[data-edrv-view] .edrv-search-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.07)); }\n/* 键盘高亮项(↑↓ 选择):与 hover 同特异性但声明在后,故选中态稳定可辨 @author ddj 2026年09月11号 */\n[data-edrv-view] .edrv-search-item.edrv-search-sel { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.14)); }\n[data-edrv-view] .edrv-search-item .n { color: var(--dsw-alias-label-primary, #eee); font-size: 12px; word-break: break-all; }\n[data-edrv-view] .edrv-search-item .d { color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-search-empty { padding: 8px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #888); }\n[data-edrv-view] .edrv-btn-mini { font-size: 11px; padding: 2px 9px; border: none; border-radius: 999px; cursor: pointer; color: var(--dsw-alias-label-primary-inverted, #fff); font-weight: 600; white-space: nowrap; }\n[data-edrv-view] .edrv-btn-keep { background: var(--dsw-alias-state-success-primary, #2e9e44); }\n[data-edrv-view] .edrv-btn-undo { background: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-btn-mini:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-statusbar { display: flex; align-items: center; gap: 10px; padding: 3px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #333); background: var(--dsw-alias-bg-layer-1, transparent); font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); flex-shrink: 0; white-space: nowrap; overflow: hidden; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-path { flex: 1; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-lsp { flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-progress-message { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-statusbar .edrv-sp-progress { flex: 0 0 auto; min-width: 36px; text-align: right; font-variant-numeric: tabular-nums; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-lsp-status-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; background: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-lsp-status-dot.ready { background: var(--dsw-alias-state-success-primary, #0f9d58); }\n[data-edrv-view] .edrv-lsp-status-dot.busy { background: var(--dsw-alias-state-warn-primary, #d97706); animation: edrv-lsp-pulse 1.2s ease-in-out infinite; }\n@keyframes edrv-lsp-pulse { 50% { opacity: .45; } }\n/* Ctrl+hover 可导航标识符下划线提示(绑定 Monaco inlineClassName 装饰) */\n[data-edrv-view] .edrv-nav-underline { text-decoration: underline; text-decoration-color: var(--dsw-alias-interactive-primary, #4fc1ff); text-underline-offset: 1px; cursor: pointer; }\n/* 跳转目标区域高亮(LSP 定义/引用、搜索命中落点):落点一眼可见,约 1.2s 后由 EditorView 自动清除。\n 用官方令牌并带回落值,不在插件作用域重定义 --dsw-* (否则永久脱离官方主题)。 */\n[data-edrv-view] .edrv-nav-target { background: var(--dsw-alias-interactive-bg-hover, rgba(79,193,255,.28)); border-radius: 2px; box-shadow: 0 0 0 1px var(--dsw-alias-interactive-primary, rgba(79,193,255,.55)); }\n[data-edrv-view] .edrv-diffchip { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-state-warn-primary, #b7791f); cursor: pointer; white-space: nowrap; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn { font-size: 12px; padding: 2px 6px; border: none; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; border-radius: 4px; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-chip-btn:disabled { opacity: .35; cursor: default; }\n[data-edrv-view] .edrv-chip-btn:disabled:hover { background: transparent; }\n/* 行内和原生 Monaco diff 共用主题色盘;不再借用普通 hover 的灰底。 */\n[data-edrv-view] .edrv-mn-add-line { background: var(--edrv-diff-add, #e6f4e8); }\n[data-edrv-view] .edrv-mn-gutter-add { background: var(--edrv-diff-add, #e6f4e8); color: var(--dsw-alias-state-success-primary, #0f9d58) !important; display: flex !important; align-items: center; justify-content: center; }\n[data-edrv-view] .edrv-mn-gutter-add::before { content: \"+\"; }\n[data-edrv-view] .edrv-del-zone, [data-edrv-view] .edrv-del-margin { background: var(--edrv-diff-del, #fbe7e3); color: var(--edrv-diff-fg, #1e1e1e); }\n[data-edrv-view] .edrv-del-row { white-space: pre; height: var(--edrv-line-height, 20px); line-height: var(--edrv-line-height, 20px); }\n[data-edrv-view] .edrv-del-text { display: inline-block; white-space: pre; }\n[data-edrv-view] .edrv-del-margin-row { position: relative; height: var(--edrv-line-height, 20px); line-height: var(--edrv-line-height, 20px); }\n[data-edrv-view] .edrv-del-number { position: absolute; left: var(--edrv-number-left); width: var(--edrv-number-width); color: var(--edrv-diff-number, #6c6c6c); text-align: right; }\n[data-edrv-view] .edrv-del-marker { position: absolute; left: var(--edrv-marker-left); width: var(--edrv-marker-width); color: var(--dsw-alias-state-error-primary, #d9534f); text-align: center; }\n[data-edrv-view] .edrv-monaco-host { flex: 1 1 auto; min-width: 0; min-height: 0; position: relative; overflow: hidden; }\n[data-edrv-view] .edrv-monaco-host > div { position: absolute; inset: 0; min-width: 0; min-height: 0; }\n[data-edrv-view] .edrv-imgview { flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }\n[data-edrv-view] .edrv-imgview-bar { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, transparent); }\n[data-edrv-view] .edrv-imgview-meta { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; }\n/* 图片预览:居中交给子项 margin:auto(auto 外边距优先吃正向空闲空间;溢出时空闲为负 → 外边距归 0、\n flex-start 起点对齐 → 放大后四边均可滚动到达,规避 justify-content:center 双侧溢出不可达问题) */\n[data-edrv-view] .edrv-imgview-stage { flex: 1 1 auto; min-height: 0; display: flex; overflow: auto; padding: 14px; background-color: var(--dsw-alias-bg-layer-1, #f0f4f4); background-image: conic-gradient(rgba(128,128,128,.14) 25%, transparent 0 50%, rgba(128,128,128,.14) 0 75%, transparent 0); background-size: 16px 16px; }\n[data-edrv-view] .edrv-imgview-img { max-width: 100%; max-height: 100%; flex-shrink: 0; margin: auto; align-self: flex-start; object-fit: contain; border-radius: 4px; box-shadow: 0 2px 12px rgba(31,41,51,.18); }\n[data-edrv-view] .edrv-imgview-broken { display: flex; flex-direction: column; align-items: center; gap: 10px; font-size: 13px; color: var(--dsw-alias-label-tertiary, #777); margin: auto; }\n[data-edrv-view] .edrv-pdf-host { flex: 1 1 auto; min-width: 0; min-height: 0; position: relative; overflow: hidden; }\n[data-edrv-view] .edrv-pdf-host > .edrv-pdf { position: absolute; inset: 0; }\n[data-edrv-view] .edrv-pdf { min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }\n[data-edrv-view] .edrv-pdf-bar { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, transparent); }\n[data-edrv-view] .edrv-pdf-page { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; min-width: 52px; text-align: center; }\n[data-edrv-view] .edrv-pdf-zoom { min-width: 26px; text-align: center; padding: 3px 7px; }\n[data-edrv-view] .edrv-pdf-sep { width: 1px; height: 16px; background: var(--dsw-alias-border-l1, #e0e6e8); margin: 0 2px; }\n[data-edrv-view] .edrv-pdf-mode-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-brand-primary, #0f9d58); border-color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-pdf-dirty { color: var(--dsw-alias-state-warn-primary, #d97706); border-color: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-pdf-stage { position: absolute; top: 34px; left: 0; right: 0; bottom: 0; overflow: auto; }\n/* Markdown 预览(需求 4):工具条 + 居中阅读栏;正文排版由官方 MarkdownText 自带的 --dsw-* 样式负责 */\n[data-edrv-view] .edrv-mdview { flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }\n[data-edrv-view] .edrv-mdview-bar { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, transparent); }\n[data-edrv-view] .edrv-mdview-meta { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; }\n[data-edrv-view] .edrv-mdview-stage { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 14px 20px 28px; }\n[data-edrv-view] .edrv-mdview-doc { max-width: 880px; margin: 0 auto; width: 100%; }\n[data-edrv-view] .edrv-mdview-fallback { font-size: 12px; line-height: 1.7; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-mdview-pre { margin: 8px 0 0; padding: 10px 12px; border-radius: 8px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-primary, #1f2933); font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; line-height: 1.6; white-space: pre-wrap; word-break: break-word; }\n[data-edrv-view] .edrv-pb-mdbtn { height: 20px; padding: 0 8px; font-size: 11px; flex-shrink: 0; }\n[data-edrv-view] .edrv-pdf-stage .edrv-pdf-viewer { margin: 0 auto; }\n[data-edrv-view] .edrv-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; height: 100%; color: var(--dsw-alias-label-tertiary, #777); font-size: 13px; }\n[data-edrv-view] .edrv-diffbar { position: absolute; left: 0; right: 0; bottom: 10px; margin: 0 auto; z-index: 25; width: fit-content; max-width: calc(100% - 20px); border-radius: 12px; background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, #f0f4f4)); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); box-shadow: none; overflow: visible; display: flex; flex-direction: column; }\n[data-edrv-view] .edrv-editor-area > .edrv-diffbar { max-height: calc(100% - 20px); }\n[data-edrv-view] .edrv-diffbar-row { display: flex; align-items: center; gap: 6px; height: 36px; padding: 4px 5px 4px 12px; flex-wrap: nowrap; }\n[data-edrv-view] .edrv-diffbar-count { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); min-width: 42px; text-align: center; white-space: nowrap; cursor: pointer; }\n[data-edrv-view] .edrv-diffbar-count.edrv-count-file { min-width: 64px; cursor: default; }\n[data-edrv-view] .edrv-diffbar-file { flex: 1; min-width: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-pill { font-size: 11px; padding: 3px 10px; border: none; border-radius: 999px; cursor: pointer; white-space: nowrap; font-weight: 600; }\n[data-edrv-view] .edrv-pill-keep { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-state-success-primary, #0f7a45); border: 1px solid var(--dsw-alias-state-success-primary, rgba(15,157,88,.4)); }\n[data-edrv-view] .edrv-pill-undo { background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.14)); color: var(--dsw-alias-state-error-primary, #c0392b); border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); }\n[data-edrv-view] .edrv-pill-ghost { background: var(--dsw-alias-bg-layer-1, #ffffff); color: var(--dsw-alias-label-secondary, #52606d); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-pill:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-diffbar-menu { position: absolute; right: 8px; bottom: calc(100% + 6px); z-index: 45; min-width: 160px; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 6px 20px rgba(31,41,51,.16); padding: 4px; display: flex; flex-direction: column; gap: 2px; }\n[data-edrv-view] .edrv-diffmenu-item { display: flex; align-items: center; gap: 8px; font-size: 12px; padding: 6px 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; text-align: left; }\n[data-edrv-view] .edrv-diffmenu-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-diffmenu-item.danger { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-diffmenu-sep { height: 1px; margin: 2px 4px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n/* 编辑区 / Tab 右键菜单(页签右键菜单 + 文件树右键菜单共用)。\n 页签菜单 11 条 + 键位提示,故限高可滚(ContextMenu 以实测尺寸做 viewport clamp)。\n ⚠️ z-index 71 在既有浮层阶梯内(见文件末尾命令栏注释),不得上调越过命令栏/片段浮窗。 */\n[data-edrv-view] .edrv-ctxmenu { position: fixed; z-index: 71; min-width: 220px; max-width: 360px; max-height: min(70vh, 520px); overflow-y: auto; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 8px 26px rgba(31,41,51,.2); padding: 4px; display: flex; flex-direction: column; gap: 1px; }\n[data-edrv-view] .edrv-ctxmenu-item { display: flex; align-items: center; gap: 8px; font-size: 12px; line-height: 18px; padding: 7px 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-ctxmenu-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-ctxmenu-item.edrv-ctxmenu-danger { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-ctxmenu-item:disabled,\n[data-edrv-view] .edrv-ctxmenu-item.edrv-ctxmenu-disabled { color: var(--dsw-alias-label-disabled, #b0b7c0); cursor: default; background: transparent; }\n[data-edrv-view] .edrv-ctxmenu-sep { height: 1px; margin: 3px 4px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n/* 菜单项文案(弹性占宽,超出省略)与右侧键位提示(不缩放,等宽字体) */\n[data-edrv-view] .edrv-ctxmenu-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-ctxmenu-hint { flex: 0 0 auto; padding-left: 18px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; }\n[data-edrv-view] .edrv-ctxmenu-item:disabled .edrv-ctxmenu-hint,\n[data-edrv-view] .edrv-ctxmenu-item.edrv-ctxmenu-disabled .edrv-ctxmenu-hint { color: var(--dsw-alias-label-disabled, #b0b7c0); }\n[data-edrv-view] .edrv-diffbar-body { max-height: 130px; max-width: min(560px, calc(100vw - 40px)); overflow: auto; padding: 4px 8px 8px; display: flex; flex-direction: column; gap: 4px; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-diffrow { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); cursor: pointer; flex-wrap: wrap; }\n[data-edrv-view] .edrv-diffrow:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-diffrow-l { font-size: 11px; color: var(--dsw-alias-label-primary, #1f2933); min-width: 74px; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-o { font-size: 11px; color: var(--dsw-alias-state-error-primary, #d9534f); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-n { font-size: 11px; color: var(--dsw-alias-state-success-primary, #0f9d58); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-tag { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-diffrow-stale { border-color: var(--dsw-alias-state-warn-primary, rgba(217,119,6,.5)); cursor: default; }\n[data-edrv-view] .edrv-diffbox-others { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: 4px 2px 0; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); margin-top: 2px; }\n[data-edrv-view] .edrv-diffrow-file { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); cursor: pointer; white-space: nowrap; max-width: 200px; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-diffrow-file:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n/* 编辑区 hover 差异块的 Keep/Undo 浮层 */\n[data-edrv-view] .edrv-hoveract { position: absolute; z-index: 30; display: flex; align-items: center; gap: 4px; padding: 2px 6px; border-radius: 8px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 4px 14px rgba(31,41,51,.14); }\n[data-edrv-view] .edrv-launch { position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 40; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); box-shadow: 0 6px 24px rgba(0,0,0,.5); display: flex; flex-direction: column; max-height: 60%; overflow: hidden; }\n[data-edrv-view] .edrv-launch-head { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-bottom: 1px solid var(--dsw-alias-border-l1, #333); }\n[data-edrv-view] .edrv-launch-tab { font-size: 12px; padding: 3px 12px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; }\n[data-edrv-view] .edrv-launch-tab.on { background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-label-primary, #eee); font-weight: 600; }\n[data-edrv-view] .edrv-launch-body { flex: 1; min-height: 0; overflow: auto; padding: 6px 8px; display: flex; flex-direction: column; gap: 4px; }\n[data-edrv-view] .edrv-launch-row { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 6px; cursor: pointer; flex-wrap: wrap; }\n[data-edrv-view] .edrv-launch-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-launch-path { flex: 1; min-width: 0; font-size: 12px; color: var(--dsw-alias-label-primary, #eee); word-break: break-all; }\n[data-edrv-view] .edrv-launch-cnt { font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); white-space: nowrap; }\n[data-edrv-view] .edrv-launch-arch { display: flex; flex-direction: column; gap: 6px; }\n[data-edrv-view] .monaco-editor { overflow: hidden !important; }\n[data-edrv-view] .monaco-editor .sticky-widget { z-index: 30 !important; background: var(--dsw-alias-bg-base, #f8f8f8) !important; }\n[data-edrv-view] .monaco-editor .sticky-widget .sticky-line-content,\n[data-edrv-view] .monaco-editor .sticky-widget .sticky-line-number { background: var(--dsw-alias-bg-base, #f8f8f8) !important; }\n[data-edrv-view] .monaco-editor, [data-edrv-view] .monaco-editor .margin, [data-edrv-view] .monaco-editor-background { background: transparent !important; }\n[data-edrv-view] .monaco-editor .scrollbar .slider { background: rgba(128,128,128,.3) !important; }\n[data-edrv-view] .edrv-loading { gap: 8px; padding: 20px; }\n[data-edrv-view] .edrv-loading-title { color: var(--dsw-alias-label-primary, #1f2933); font-size: 13px; }\n[data-edrv-view] .edrv-progress { width: min(360px, 72%); height: 6px; overflow: hidden; border-radius: 999px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-progress-value { height: 100%; border-radius: inherit; background: var(--dsw-alias-brand-primary, #0f9d58); transition: width .24s ease; }\n[data-edrv-view] .edrv-loading-percent { color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; font-variant-numeric: tabular-nums; }\n@media (prefers-reduced-motion: reduce) {\n [data-edrv-view] .edrv-progress-value { transition: none; }\n}\n\n/* ===== 唯一会话级差异 dock(遵循 DSH todo/queue 原生布局) ===== */\n[data-edrv-diff-dock-shell] {\n box-sizing: border-box;\n width: calc(100% - var(--dsh-composer-side-clearance) - var(--dsh-composer-side-clearance) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));\n max-width: calc(var(--dsh-composer-card-max-width) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));\n margin: 0 auto;\n padding: 0;\n flex: none;\n border: 1px solid var(--dsw-alias-border-l1, #e0e6e8);\n border-radius: 12px;\n background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, transparent));\n box-shadow: none;\n overflow: hidden;\n}\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock {\n box-sizing: border-box;\n position: relative;\n left: auto;\n right: auto;\n bottom: auto;\n z-index: auto;\n width: 100%;\n max-width: none;\n margin: 0;\n display: flex;\n flex-direction: column;\n border: none;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n overflow: visible;\n}\n[data-edrv-diff-dock-shell] .edrv-diffbar-row { min-height: 36px; padding: 6px 12px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-row { min-height: 36px; padding: 6px 12px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-body { max-height: 180px; overflow: auto; padding: 6px 12px 10px; border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-button { box-sizing: border-box; display: flex; align-items: center; justify-content: center; width: 100%; height: 24px; border: none; border-radius: 8px; padding: 0 12px; background: transparent; color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 500; line-height: 24px; text-align: center; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-button:hover { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diffbar-count { display: inline-flex; align-items: center; color: var(--dsw-alias-label-primary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 500; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-file { display: inline-flex; align-items: center; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-editor-row { display: flex; align-items: center; height: 36px; min-height: 36px; padding: 4px 12px; overflow-x: auto; scrollbar-width: thin; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-editor-content { display: flex; align-items: center; gap: 10px; width: 100%; min-width: max-content; min-height: 28px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-nav,\n[data-edrv-diff-dock-shell] .edrv-diffbar-files,\n[data-edrv-diff-dock-shell] .edrv-diffbar-actions { display: flex; align-items: center; gap: 6px; flex: none; height: 28px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-actions { margin-left: auto; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-details-toggle { display: inline-flex; align-items: center; justify-content: center; width: 28px; min-width: 28px; padding: 0; font-size: 13px; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-details-toggle svg { width: 14px; height: 14px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-files { min-width: 0; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-files .edrv-diffbar-file { min-width: 72px; max-width: 240px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-progress { min-width: 0; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; font-variant-numeric: tabular-nums; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-summary { min-width: 0; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-pill { box-sizing: border-box; height: 28px; padding: 0 9px; border: none; border-radius: 999px; cursor: pointer; white-space: nowrap; font-size: 11px; line-height: 26px; font-weight: 600; }\n[data-edrv-diff-dock-shell] .edrv-pill-keep { background: var(--dsw-alias-state-success-tertiary); color: var(--dsw-alias-state-success-primary); border: 1px solid var(--dsw-alias-state-success-secondary); }\n[data-edrv-diff-dock-shell] .edrv-pill-undo { background: var(--dsw-alias-state-error-tertiary); color: var(--dsw-alias-state-error-primary); border: 1px solid var(--dsw-alias-state-error-secondary); }\n[data-edrv-diff-dock-shell] .edrv-pill-ghost { background: transparent; color: var(--dsw-alias-label-secondary); border: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-pill:hover:not(:disabled) { background-color: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-pill:focus-visible { outline: 2px solid var(--dsw-alias-label-tertiary); outline-offset: -2px; }\n[data-edrv-diff-dock-shell] .edrv-pill:disabled { opacity: .45; cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diffrow { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; background: transparent; cursor: pointer; flex-wrap: wrap; }\n[data-edrv-diff-dock-shell] .edrv-diffrow:hover { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-l { color: var(--dsw-alias-label-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-o { color: var(--dsw-alias-state-error-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-n { color: var(--dsw-alias-state-success-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-tag { color: var(--dsw-alias-label-tertiary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-stale { border-color: var(--dsw-alias-state-warn-secondary); cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diffbox-others { border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-file { border-color: var(--dsw-alias-border-l1); background: transparent; color: var(--dsw-alias-label-secondary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-file:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }\n[data-edrv-diff-dock-shell] .edrv-diff-details { display: flex; flex-direction: column; gap: 2px; padding: 6px 12px 8px; border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item { display: flex; align-items: center; width: 100%; min-height: 28px; gap: 8px; padding: 4px 8px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary); cursor: pointer; text-align: left; font-size: 12px; line-height: 20px; }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item.danger { color: var(--dsw-alias-state-error-primary); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item:disabled { opacity: .45; cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-sep { height: 1px; margin: 2px 4px; background: var(--dsw-alias-border-l1); }\n@media (max-width: 620px) {\n [data-edrv-diff-dock-shell] .edrv-diffbar-editor-content { width: max-content; }\n [data-edrv-diff-dock-shell] .edrv-diffbar-files .edrv-diffbar-file { max-width: 160px; }\n}\n/* 侧栏差异栏在编辑内容底部居中悬浮,按内容宽度收缩;窄栏只在工具条内横滚。@author ddj 2026年09月29号 */\n[data-edrv-view] .edrv-side-diff-dock[data-edrv-diff-dock-shell] { position: absolute; left: 50%; bottom: 28px; z-index: 25; width: max-content; max-width: calc(100% - 20px); max-height: min(300px, calc(100% - 38px)); margin: 0; transform: translateX(-50%); border-radius: 12px; background: var(--dsw-alias-bg-overlay, #fff); box-shadow: 0 6px 20px rgba(0,0,0,.18); overflow-x: hidden; overflow-y: auto; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-editor-row { padding-inline: 8px; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-editor-content { gap: 5px; width: max-content; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-nav,\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-files,\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-actions { gap: 3px; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-pill { padding-inline: 5px; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-files .edrv-diffbar-file { min-width: 0; max-width: 72px; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-details-toggle { width: 26px; min-width: 26px; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-body { max-height: min(180px, 25vh); max-width: 100%; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diff-details { max-height: min(260px, 35vh); overflow-y: auto; }\n\n/* Official review: file context above, navigation/actions below. Theme-owned surfaces; no neon status outlines. @author ddj 2026年10月27号 */\n[data-edrv-diff-dock-shell] .edrv-reviewbar {\n width: 520px; max-width: 100%; min-width: 0; box-sizing: border-box;\n color: var(--dsw-alias-label-primary, #26313d); font-family: var(--dsw-font-family, sans-serif);\n font-size: 12px; line-height: 1.4; container: edrv-review / inline-size;\n}\n[data-edrv-diff-dock-shell] .edrv-review-global { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px; padding: 6px 8px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e5e9ef); }\n[data-edrv-diff-dock-shell] .edrv-review-all-actions { display: flex; align-items: center; gap: 4px; }\n[data-edrv-diff-dock-shell] .edrv-review-total strong { font-variant-numeric: tabular-nums; }\n[data-edrv-diff-dock-shell] .edrv-review-heading {\n display: flex; align-items: center; gap: 8px; min-width: 0; padding: 10px 12px 8px;\n border-bottom: 1px solid var(--dsw-alias-border-l1, #e5e9ef);\n}\n[data-edrv-diff-dock-shell] .edrv-review-filemark { display: inline-flex; flex: none; color: var(--dsw-alias-label-tertiary, #697586); }\n[data-edrv-diff-dock-shell] .edrv-review-filename { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 600; }\n[data-edrv-diff-dock-shell] .edrv-review-pending { flex: none; display: inline-flex; gap: 5px; align-items: center; color: var(--dsw-alias-label-secondary, #697586); font-size: 11px; white-space: nowrap; }\n[data-edrv-diff-dock-shell] .edrv-review-pending strong { font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--dsw-alias-label-primary, #26313d); }\n[data-edrv-diff-dock-shell] .edrv-review-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 7px 8px; }\n[data-edrv-diff-dock-shell] .edrv-review-nav { display: flex; align-items: center; flex: none; gap: 1px; }\n[data-edrv-diff-dock-shell] .edrv-review-blocknav { border-left: 1px solid var(--dsw-alias-border-l1, #e5e9ef); padding-left: 6px; }\n[data-edrv-diff-dock-shell] .edrv-review-position { min-width: 32px; text-align: center; font-size: 11px; font-variant-numeric: tabular-nums; color: var(--dsw-alias-label-secondary, #697586); }\n[data-edrv-diff-dock-shell] .edrv-review-actions { display: flex; align-items: center; flex: none; gap: 4px; margin-left: auto; }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button { height: 28px; min-height: 28px; padding: 0 8px; border-radius: 6px; font-family: inherit; font-size: 12px; font-weight: 500; white-space: nowrap; gap: 5px; }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button.edrv-review-icon { width: 28px; min-width: 28px; padding: 0; }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button.edrv-review-record { padding: 0 6px; font-size: 11px; color: var(--dsw-alias-label-secondary, #697586); }\n[data-edrv-diff-dock-shell] .edrv-review-record svg { width: 12px; height: 12px; }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button.edrv-review-keep { background: var(--dsw-alias-brand-primary, #3b63d9); color: var(--dsw-alias-label-primary-inverted, #fff); border: 1px solid transparent; }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button.edrv-review-keep:hover:not(:disabled) { filter: brightness(.94); }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button.edrv-review-undo:hover:not(:disabled) { color: var(--dsw-alias-state-error-primary, #b64043); }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button:disabled { opacity: .4; cursor: not-allowed; }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #3b63d9); outline-offset: 2px; }\n[data-edrv-diff-dock-shell] .edrv-review-notice { padding: 7px 12px; border-top: 1px solid var(--dsw-alias-border-l1, #e5e9ef); color: var(--dsw-alias-label-secondary, #697586); font-size: 11px; overflow-wrap: anywhere; }\n[data-edrv-diff-dock-shell] .edrv-review-error { color: var(--dsw-alias-state-error-primary, #b64043); }\n[data-edrv-diff-dock-shell] .edrv-review-notice-warn { color: var(--dsw-alias-state-warning-primary, #a86a1f); }\n[data-edrv-diff-dock-shell] .edrv-review-source { flex: none; padding: 1px 6px; border-radius: 999px; border: 1px solid var(--dsw-alias-border-secondary, #d4dae3); color: var(--dsw-alias-label-secondary, #697586); font-size: 11px; line-height: 16px; white-space: nowrap; }\n[data-edrv-diff-dock-shell] .edrv-sidecar-down { display: flex; align-items: center; gap: 8px; }\n[data-edrv-diff-dock-shell] .edrv-sidecar-down-text { flex: 1; min-width: 0; }\n[data-edrv-diff-dock-shell] .edrv-review-body { border-top: 1px solid var(--dsw-alias-border-l1, #e5e9ef); max-height: min(240px, 30vh); overflow: auto; padding: 8px 12px; }\n@container edrv-review (max-width: 420px) {\n .edrv-reviewbar .edrv-review-heading { gap: 6px; padding-inline: 10px; }\n .edrv-reviewbar .edrv-review-actions { justify-content: flex-end; flex: 1 0 100%; border-top: 1px solid var(--dsw-alias-border-l1, #e5e9ef); padding-top: 6px; }\n}\n\n/* ===== 侧边栏(活动栏 + 面板区 + 文件树) ===== */\n[data-edrv-view] .edrv-editor-row { display: flex; flex: 1 1 auto; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-main-col { box-sizing: border-box; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-main-col > .edrv-pathbar,\n[data-edrv-view] .edrv-main-col > .edrv-tabs { flex: 0 0 auto; }\n[data-edrv-view] .edrv-editor-area { position: relative; display: flex; flex: 1 1 auto; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-sidebar { display: flex; flex-direction: row; flex-shrink: 1; min-width: 0; height: 100%; background: var(--dsw-alias-bg-layer-1, #ffffff); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); overflow: hidden; }\n[data-edrv-view] .edrv-rail { display: flex; flex-direction: column; align-items: center; width: 44px; flex-shrink: 0; padding: 6px 0; background: var(--dsw-alias-bg-layer-2, #f0f4f4); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); gap: 4px; }\n[data-edrv-view] .edrv-rail-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: none; border-radius: 8px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rail-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rail-btn.edrv-rail-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); box-shadow: inset 2px 0 0 var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-rail-icon { font-size: 15px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; }\n/* 树行图标:官方原语(文件夹/文件类型),尺寸与行高对齐 @author ddj 2026-09-10 */\n[data-edrv-view] .edrv-tree-icon { flex-shrink: 0; display: inline-flex; align-items: center; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-active .edrv-tree-icon { color: var(--dsw-alias-label-secondary, #52606d); }\n/* SVN overlays: official file icon remains visible, independent local/remote signs. ddj 2026-10-09 */\n[data-edrv-view] .edrv-tree-iconbox { position: relative; display: inline-flex; width: 20px; height: 20px; align-items: center; flex-shrink: 0; }\n[data-edrv-view] .edrv-svn-overlay { position: absolute; inset: 0; }\n[data-edrv-view] .edrv-svn-local { position: absolute; right: -1px; bottom: -2px; display: flex; border-radius: 50%; box-shadow: 0 0 0 1px var(--dsw-alias-bg-layer-1, #fff); }\n[data-edrv-view] .edrv-svn-icon-clean { color: #16853b; }\n[data-edrv-view] .edrv-svn-icon-modified, [data-edrv-view] .edrv-svn-icon-deleted { color: #d12e35; }\n[data-edrv-view] .edrv-svn-icon-conflict { color: #c27300; }\n[data-edrv-view] .edrv-svn-icon-added { color: #2465c9; }\n[data-edrv-view] .edrv-svn-icon-unversioned, [data-edrv-view] .edrv-svn-icon-ignored, [data-edrv-view] .edrv-svn-icon-external { color: #75808c; }\n[data-edrv-view] .edrv-svn-remote { position: absolute; right: -3px; top: -4px; color: #2465c9; background: var(--dsw-alias-bg-layer-1, #fff); border-radius: 3px; font-size: 11px; font-weight: 900; line-height: 12px; }\n[data-edrv-view] .edrv-rail-badge { position: absolute; top: 2px; right: 2px; min-width: 14px; height: 14px; padding: 0 3px; border-radius: 999px; background: var(--dsw-alias-brand-primary, #0f9d58); color: var(--dsw-alias-label-primary-inverted, #fff); font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; }\n[data-edrv-view] .edrv-side-body { flex: 1; min-width: 0; min-height: 0; display: flex; overflow: hidden; }\n[data-edrv-view] .edrv-side-panel { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; position: relative; }\n[data-edrv-view] .edrv-side-head { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px 0 12px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); flex-shrink: 0; }\n[data-edrv-view] .edrv-side-title { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-side-root { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-side-btn { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-side-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-tree { flex: 1; min-height: 0; overflow: auto; padding: 4px 6px 12px; font-size: 12px; }\n/* Virtual rows and placeholders share the same geometry as treeWindow.ts. @author ddj 2026年10月22号 */\n[data-edrv-view] .edrv-tree.edrv-tree-virtual { padding-top: 0; padding-bottom: 0; }\n[data-edrv-view] .edrv-tree-virtual .edrv-tree-row,\n[data-edrv-view] .edrv-tree-virtual .edrv-tree-loading { height: 24px; min-height: 24px; box-sizing: border-box; }\n[data-edrv-view] .edrv-tree-virtual .edrv-tree-loading { line-height: 24px; }\n[data-edrv-view] .edrv-large-text { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }\n[data-edrv-view] .edrv-large-text-toolbar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }\n[data-edrv-view] .edrv-large-text-content { margin: 0; flex: 1; min-height: 0; overflow: auto; white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); user-select: text; }\n[data-edrv-view] .edrv-tree-row { display: flex; align-items: center; gap: 4px; height: 22px; padding-right: 6px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-tree-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-active { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-context { box-shadow: inset 0 0 0 1px var(--dsw-alias-brand-primary, #0f9d58); background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); }\n/* 「在资源管理器视图中显示」的定位高亮(短时淡出,区别于常驻的 active 态) */\n[data-edrv-view] .edrv-tree-row.edrv-tree-reveal { box-shadow: inset 0 0 0 1px var(--dsw-alias-brand-primary, #0f9d58); background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.18)); animation: edrv-tree-reveal-fade 2s ease-out; }\n@keyframes edrv-tree-reveal-fade { from { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.32)); } to { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.18)); } }\n[data-edrv-view] .edrv-tree-chev { width: 12px; flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; text-align: center; }\n[data-edrv-view] .edrv-tree-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-tree-name.edrv-tree-dim { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-tree-badge { min-width: 14px; height: 14px; padding: 0 4px; border-radius: 999px; background: var(--dsw-alias-state-warn-primary, #d97706); color: var(--dsw-alias-label-primary-inverted, #fff); font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; flex-shrink: 0; }\n[data-edrv-view] .edrv-tree-loading { height: 22px; line-height: 22px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n/* SVN 状态独立占位:换行不覆盖文件树,长错误在栏内滚动。@author ddj 2026-10-12 */\n[data-edrv-view] .edrv-tree-status { flex: 0 0 auto; min-width: 0; max-height: 96px; overflow: auto; padding: 4px 8px; box-sizing: border-box; line-height: 18px; white-space: normal; overflow-wrap: anywhere; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-tree-error { display: flex; align-items: center; gap: 8px; margin: 4px 2px 6px; padding: 6px 8px; border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n/* 大纲面板:kind 字形 / detail / 行号 */\n[data-edrv-view] .edrv-outline-kind { display: inline-flex; align-items: center; justify-content: center; width: 14px; flex-shrink: 0; font-size: 12px; font-weight: 600; text-align: center; }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-func { color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-type { color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-data { color: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-ns { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-key { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-outline-detail { flex-shrink: 0; max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-outline-ln { flex-shrink: 0; padding-right: 2px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-side-resize { width: 5px; flex-shrink: 0; cursor: col-resize; background: transparent; }\n[data-edrv-view] .edrv-side-resize:hover { background: var(--dsw-alias-brand-primary, #0f9d58); opacity: .35; }\n[data-edrv-view] .edrv-chip-btn.edrv-chip-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); }\n\n/* ===== SVN 变更面板(工作副本状态列表;颜色分级对齐 TortoiseSVN 口径) @author ddj 2026-09-16 ===== */\n[data-edrv-view] .edrv-svn-toolbar { display: flex; align-items: center; gap: 12px; padding: 4px 10px 6px 12px; flex-shrink: 0; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-svn-check { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-svn-check input { margin: 0; cursor: pointer; }\n[data-edrv-view] .edrv-svn-bulk { display: flex; align-items: center; gap: 6px; padding: 0 10px 6px 12px; flex-shrink: 0; }\n[data-edrv-view] .edrv-svn-count { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n/* AI 助手 dock(12-panel-ai-bar 设计定稿):manifest 分带之 assistant 带,accent 左竖轨为唯一记忆点。\n AI 线程色三处同轨:竖轨 / dock 按钮描边 / 弹窗进度条(--dsw-alias-state-info-primary)。 @author ddj 2026-09-23 */\n[data-edrv-view] .edrv-svn-ai-bar { position: relative; display: flex; align-items: center; gap: 8px; padding: 6px 10px 6px 14px; background: var(--dsw-alias-bg-layer-2, #f7f9f9); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); flex-shrink: 0; }\n[data-edrv-view] .edrv-svn-ai-rail { position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--dsw-alias-state-info-primary, #2f6fed); }\n[data-edrv-view] .edrv-svn-ai-label { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-svn-ai-btn { border-color: var(--dsw-alias-state-info-primary, #2f6fed); color: var(--dsw-alias-state-info-primary, #2f6fed); }\n[data-edrv-view] .edrv-svn-ai-btn:disabled { border-color: var(--dsw-alias-border-l1, #e0e6e8); color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n/* 状态位定宽右对齐(就绪/分析中…/等待助手投递…互换不跳版) */\n[data-edrv-view] .edrv-svn-ai-status { flex-shrink: 0; min-width: 88px; text-align: right; font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; }\n/* 批量条语义分隔(导出簇 ┊ 动作簇之间 1px 竖线) */\n[data-edrv-view] .edrv-svn-sep { width: 1px; height: 14px; background: var(--dsw-alias-border-l1, #e0e6e8); flex-shrink: 0; }\n/* 头部图标组(检查远端/刷新成组,gap 收紧) */\n[data-edrv-view] .edrv-svn-head-icons { display: inline-flex; align-items: center; gap: 2px; flex-shrink: 0; }\n[data-edrv-view] .edrv-svn-list { flex: 1; min-height: 0; overflow: auto; padding: 2px 6px 12px; font-size: 12px; }\n[data-edrv-view] .edrv-svn-group { display: flex; align-items: center; gap: 6px; height: 22px; padding: 0 6px; margin: 2px 0; border-radius: 5px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); font-size: 10px; font-weight: 600; cursor: pointer; user-select: none; }\n[data-edrv-view] .edrv-svn-group:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-svn-fold-ch { width: 10px; flex-shrink: 0; text-align: center; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svn-group-n { margin-left: auto; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-weight: 400; }\n[data-edrv-view] .edrv-svn-row { display: flex; align-items: center; gap: 6px; height: 22px; padding: 0 6px; border-radius: 5px; cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-svn-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-svn-path { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-svn-acts { display: none; align-items: center; gap: 4px; flex-shrink: 0; }\n[data-edrv-view] .edrv-svn-row:hover .edrv-svn-acts { display: inline-flex; }\n[data-edrv-view] .edrv-svn-act { border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 5px; padding: 1px 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-secondary, #52606d); font-size: 10px; line-height: 14px; cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-svn-act:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-svn-act:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-svn-act-danger { color: var(--dsw-alias-state-error-primary, #d9534f); border-color: var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); }\n[data-edrv-view] .edrv-svn-ch { display: inline-flex; align-items: center; justify-content: center; width: 14px; flex-shrink: 0; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; font-weight: 700; }\n[data-edrv-view] .edrv-svn-ch-modified { color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svn-ch-added { color: var(--dsw-alias-state-warn-primary, #7c3aed); }\n[data-edrv-view] .edrv-svn-ch-deleted { color: var(--dsw-alias-label-secondary, #b91c1c); }\n[data-edrv-view] .edrv-svn-ch-conflict { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-svn-ch-plain { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n/* .edrv-svn-ch-* 动作词表补全:replaced(替换)= 琥珀;侧栏状态分级(SVN_STATUS_TONE)不受影响 @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svn-ch-replaced { color: var(--dsw-alias-state-warn-primary, #d97706); }\n/* 文件树行尾 SVN 状态徽标(与差异角标并列;方形字母,区别圆形差异计数) */\n[data-edrv-view] .edrv-tree-svn { min-width: 14px; height: 14px; padding: 0 4px; border-radius: 4px; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 9px; font-weight: 700; line-height: 14px; text-align: center; box-sizing: border-box; flex-shrink: 0; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-tree-svn.edrv-svn-ch-modified { color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-tree-svn.edrv-svn-ch-added { color: var(--dsw-alias-state-warn-primary, #7c3aed); }\n[data-edrv-view] .edrv-tree-svn.edrv-svn-ch-deleted { color: var(--dsw-alias-label-secondary, #b91c1c); }\n[data-edrv-view] .edrv-tree-svn.edrv-svn-ch-conflict { color: var(--dsw-alias-state-error-primary, #d9534f); }\n/* ===== SVN 基线差异视图(BASE 左 / 工作区右,只读并排) @author ddj 2026-09-16 ===== */\n[data-edrv-view] .edrv-svndiff { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-svndiff-bar { display: flex; align-items: center; gap: 8px; height: 30px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-svndiff-title { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-svndiff-tag { padding: 1px 6px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svndiff-host { flex: 1 1 auto; min-height: 0; }\n[data-edrv-view] .edrv-svndiff-empty { display: flex; flex: 1 1 auto; align-items: center; justify-content: center; padding: 24px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 12px; text-align: center; }\n/* 差异整行色块与行首 +/- 指示符:vendored Monaco(diffEditorWidget2)静态 CSS 无\n .line-insert/.line-delete 背景规则,整行色块靠主题键 + 此处固定色兜底保证可见。\n 注意不要用 --dsw-alias-interactive-bg-hover 类 token:实测为 92% 透明中性色,肉眼不可见。\n 固定色取自研差异 UI 同色相(绿 #0f9d58 / 红 #d9534f),alpha 提到肉眼可辨。 */\n[data-edrv-view] .monaco-editor .line-insert { background: rgba(15, 157, 88, 0.18); }\n[data-edrv-view] .monaco-editor .line-delete { background: rgba(217, 83, 79, 0.16); }\n[data-edrv-view] .monaco-editor .gutter-insert { background: rgba(15, 157, 88, 0.35); }\n[data-edrv-view] .monaco-editor .gutter-delete { background: rgba(217, 83, 79, 0.35); }\n[data-edrv-view] .monaco-editor .insert-sign { color: var(--dsw-alias-state-success-primary, #0f9d58); }\n[data-edrv-view] .monaco-editor .delete-sign { color: var(--dsw-alias-state-error-primary, #d9534f); }\n/* 差异条快速跳转簇(对齐自研审查条 ↑ i/N ↓ 的形态) */\n[data-edrv-view] .edrv-svndiff-nav { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }\n[data-edrv-view] .edrv-svndiff-nav-count { min-width: 36px; text-align: center; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); }\n/* W1-3 差异统计徽标(+新增/修改 −删除/修改;色相与行块绿红一致) @author ddj 2026-09-20 */\n[data-edrv-view] .edrv-svndiff-stat { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-svndiff-stat-add { color: var(--dsw-alias-state-success-primary, #0f9d58); }\n[data-edrv-view] .edrv-svndiff-stat-del { color: var(--dsw-alias-state-error-primary, #d9534f); }\n/* W1-1 差异视图选项条(右端开关组;复用 .edrv-svn-check 的 label 形态) */\n[data-edrv-view] .edrv-svndiff-opts { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }\n/* W1-2 变更面板名称过滤输入框(工具条行内,紧凑) @author ddj 2026-09-20 */\n[data-edrv-view] .edrv-svn-name-filter { width: 168px; padding: 1px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 5px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; line-height: 14px; }\n[data-edrv-view] .edrv-svn-name-filter:focus { outline: none; border-color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-svn-name-filter::placeholder { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n/* W2-3 远端更新提示条(工具条上方;warn = 失败降级) @author ddj 2026-09-20 */\n[data-edrv-view] .edrv-svn-hintbar { display: flex; align-items: center; gap: 6px; margin: 0 10px 4px 12px; padding: 3px 8px; border-radius: 5px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); font-size: 11px; flex-shrink: 0; }\n[data-edrv-view] .edrv-svn-hintbar-warn { background: rgba(217, 83, 79, 0.12); color: var(--dsw-alias-state-error-primary, #d9534f); }\n/* W2-5 疑似改名标记(行内 ⇄;hover 见 tooltip) */\n[data-edrv-view] .edrv-svn-pair-mark { flex-shrink: 0; color: var(--dsw-alias-state-warn-primary, #7c3aed); font-size: 11px; cursor: help; }\n/* W2-1 补丁对话框 / W2-2 目录对比对话框 @author ddj 2026-09-20 */\n[data-edrv-view] .edrv-svn-patch-dialog { max-width: calc(100vw - 48px); }\n[data-edrv-view] .edrv-svn-patch-head { display: flex; align-items: center; gap: 8px; padding-bottom: 8px; }\n[data-edrv-view] .edrv-svn-patch-notes { display: flex; flex-direction: column; gap: 4px; padding-bottom: 6px; }\n[data-edrv-view] .edrv-svn-patch-warn { color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n[data-edrv-view] .edrv-svn-patch-pre { max-height: 46vh; overflow: auto; margin: 0 0 8px; padding: 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f7f9f9); color: var(--dsw-alias-label-primary, #1f2933); font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; line-height: 1.5; white-space: pre; user-select: text; }\n[data-edrv-view] .edrv-svn-patch-foot { display: flex; align-items: center; gap: 8px; }\n/* AI 智能整理弹窗(11-ai-changelist-triage):预览三分组卡 + 执行进度 @author ddj 2026-09-23 */\n[data-edrv-view] .edrv-svnplan-dialog { max-width: calc(100vw - 48px); }\n[data-edrv-view] .edrv-svnplan-head { display: flex; align-items: center; gap: 8px; padding-bottom: 4px; }\n[data-edrv-view] .edrv-svnplan-meta { display: flex; gap: 10px; padding-bottom: 6px; }\n[data-edrv-view] .edrv-svnplan-note { color: var(--dsw-alias-state-warn-primary, #b7791f); font-size: 10px; }\n[data-edrv-view] .edrv-svnplan-sec { border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; padding: 6px 8px; margin-bottom: 8px; }\n[data-edrv-view] .edrv-svnplan-sechead { display: flex; align-items: center; gap: 6px; }\n[data-edrv-view] .edrv-svnplan-sectitle { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-svnplan-list { max-height: 26vh; overflow: auto; padding-left: 18px; }\n[data-edrv-view] .edrv-svnplan-card { border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); padding: 3px 0; }\n[data-edrv-view] .edrv-svnplan-row { display: flex; align-items: center; gap: 6px; }\n[data-edrv-view] .edrv-svnplan-item { display: flex; align-items: center; gap: 6px; padding-left: 18px; }\n[data-edrv-view] .edrv-svnplan-groupname { font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-svnplan-path { font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 46ch; }\n[data-edrv-view] .edrv-svnplan-reason { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 30ch; }\n[data-edrv-view] .edrv-svnplan-stage { display: flex; align-items: center; gap: 8px; padding-bottom: 6px; }\n[data-edrv-view] .edrv-svnplan-badge { border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 5px; padding: 1px 8px; font-size: 10px; color: var(--dsw-alias-label-secondary, #52606d); background: var(--dsw-alias-bg-layer-2, #f7f9f9); white-space: nowrap; }\n[data-edrv-view] .edrv-svnplan-badge-cur { font-family: var(--ds-font-family-code, ui-monospace, monospace); overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-svnplan-bar { height: 6px; border-radius: 3px; background: var(--dsw-alias-bg-layer-2, #f7f9f9); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); overflow: hidden; margin-bottom: 8px; }\n[data-edrv-view] .edrv-svnplan-bar-in { height: 100%; background: var(--dsw-alias-state-info-primary, #2f6fed); transition: width .2s; }\n[data-edrv-view] .edrv-svnplan-logwrap { max-height: 40vh; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; padding: 6px 8px; background: var(--dsw-alias-bg-layer-2, #f7f9f9); }\n[data-edrv-view] .edrv-svnplan-log { font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; line-height: 1.6; color: var(--dsw-alias-label-secondary, #52606d); user-select: text; }\n[data-edrv-view] .edrv-svnplan-log-bad { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-svnplan-foot { display: flex; align-items: center; gap: 8px; }\n[data-edrv-view] .edrv-svnplan-primary { font-weight: 600; border-color: var(--dsw-alias-state-info-primary, #2f6fed); }\n[data-edrv-view] .edrv-svn-sum-form { display: flex; align-items: center; gap: 10px; padding-bottom: 8px; }\n[data-edrv-view] .edrv-svn-sum-list { max-height: 44vh; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; padding: 2px; margin-bottom: 8px; }\n[data-edrv-view] .edrv-svnlog-path-kind { flex-shrink: 0; padding: 0 5px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; }\n\n/* ===== 通用弹窗外壳(ModalShell)+ SVN 日志弹窗 @author ddj 2026-09-16 ===== */\n/* z-index 190:落在既有阶梯内(命令栏 200 > 片段二级 197 > 片段一级 195 > 190),\n 不遮挡命令栏;既有浮层数值不受影响(见 tests/overlayZOrder.test.ts)。 */\n[data-edrv-view] .edrv-modal-mask { position: fixed; inset: 0; z-index: 190; display: flex; align-items: center; justify-content: center; padding: 24px; box-sizing: border-box; background: var(--dsw-alias-bg-mask-drop, #000000aa); }\n[data-edrv-view] .edrv-svnlog-dialog { position: relative; display: flex; flex-direction: column; gap: 8px; }\n[data-edrv-view] .edrv-svnlog-head { display: flex; align-items: center; gap: 10px; flex-wrap: nowrap; }\n[data-edrv-view] .edrv-svnlog-title { margin: 0; font-size: 16px; font-weight: 650; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n/* 头部目标路径 = 代码 chip(mono + 底色 + 全圆角;补丁/目录对比弹窗共用同视觉) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-target { font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); }\n/* 关闭按钮图标化(透明底,hover 显形;dialog 前缀提高特异性,压过底部按钮统一尺寸) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-dialog .edrv-svnlog-head .edrv-svn-act { border-color: transparent; background: transparent; width: 24px; height: 24px; padding: 0; font-size: 12px; line-height: 1; }\n[data-edrv-view] .edrv-svnlog-dialog .edrv-svnlog-head .edrv-svn-act:hover { border-color: var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.10)); }\n/* 过滤栏:浅色表面条 + 统一 24px 控件 + 焦点边框变色 + 日期组靠右 @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-filter { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; padding: 6px 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 8px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); }\n[data-edrv-view] .edrv-svnlog-field { flex: 0 0 auto; height: 24px; box-sizing: border-box; padding: 0 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-svnlog-search { min-width: 160px; flex: 1 1 220px; height: 24px; box-sizing: border-box; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-svnlog-field:focus, [data-edrv-view] .edrv-svnlog-search:focus, [data-edrv-view] .edrv-svnlog-date:focus { outline: none; border-color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-dates { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; }\n[data-edrv-view] .edrv-svnlog-date { height: 24px; box-sizing: border-box; padding: 0 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-svnlog-check { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); white-space: nowrap; cursor: pointer; }\n[data-edrv-view] .edrv-svnlog-check input[type=\"checkbox\"] { margin: 0; cursor: pointer; accent-color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-error { padding: 4px 8px; border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n/* P0-14 上中下三段全宽:body 纵向铺满弹窗(外框高度由拖拽受控) */\n[data-edrv-view] .edrv-svnlog-body { display: flex; flex-direction: column; gap: 6px; flex: 1 1 auto; min-height: 0; }\n/* 上段修订列表(13-log-ui-polish 变更台账):\n 1px 发丝边框 + 底色面 + 左侧时间脊线(每行 ::before 画节点圆点、行盒左缘 ::after 连线段,\n 行行相接即整列脊线;选中行节点实心着色 + 2px 左强调条)。列宽由 cell 类与 nth-child 对齐(P0-12/D30)。 */\n[data-edrv-view] .edrv-svnlog-list { position: relative; flex: 0 0 auto; display: flex; flex-direction: column; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 8px; padding: 0 4px 4px; background: var(--dsw-alias-bg-layer-1, #fff); }\n/* 列头 sticky:滚动常驻 + 实底防透;z-index 20 低于一切浮层(190 起),仅在列表滚动容器内生效 */\n[data-edrv-view] .edrv-svnlog-cols { position: sticky; top: 0; z-index: 20; display: flex; align-items: center; gap: 6px; padding: 6px 6px 6px 16px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, #fff); font-size: 10px; font-weight: 600; letter-spacing: .04em; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }\n[data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(1) { width: 52px; flex: none; }\n[data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(2) { width: 52px; flex: none; }\n[data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(3) { width: 88px; flex: none; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(4) { width: 82px; flex: none; }\n[data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(5) { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-svnlog-col-sortable { cursor: pointer; }\n[data-edrv-view] .edrv-svnlog-col-sortable:hover { color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-svnlog-col-on { color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-sort-reset { flex: none; }\n/* 行:position:relative 承载脊线节点(::before)与连线段(::after);选中 = 强调条 + 节点实心 @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-item { position: relative; display: flex; align-items: center; gap: 6px; padding: 3px 6px 3px 16px; border-radius: 6px; cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-svnlog-item::before { content: ''; position: absolute; left: 6px; top: 50%; transform: translateY(-50%); width: 7px; height: 7px; box-sizing: border-box; border-radius: 50%; border: 1.5px solid var(--dsw-alias-border-l2, #cbd2d9); background: var(--dsw-alias-bg-layer-1, #fff); z-index: 1; }\n[data-edrv-view] .edrv-svnlog-item::after { content: ''; position: absolute; left: 9px; top: 0; bottom: 0; width: 1px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-svnlog-item:last-child::after { bottom: 50%; }\n[data-edrv-view] .edrv-svnlog-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-svnlog-item.edrv-svnlog-item-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); box-shadow: inset 2px 0 0 var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-item.edrv-svnlog-item-on::before { border-color: var(--dsw-alias-brand-primary, #2563eb); background: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-item-copied .edrv-svnlog-cell-msg,\n[data-edrv-view] .edrv-svnlog-item-copied .edrv-svnlog-rev { color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-cell { overflow: hidden; text-overflow: ellipsis; font-size: 11px; color: var(--dsw-alias-label-primary, #1f2933); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-svnlog-cell-rev { width: 52px; flex: none; }\n[data-edrv-view] .edrv-svnlog-cell-act { width: 52px; flex: none; display: inline-flex; gap: 3px; }\n[data-edrv-view] .edrv-svnlog-cell-author { width: 88px; flex: none; }\n[data-edrv-view] .edrv-svnlog-cell-date { width: 82px; flex: none; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svnlog-cell-msg { flex: 1 1 auto; }\n[data-edrv-view] .edrv-svnlog-rev { font-family: var(--ds-font-family-code, ui-monospace, monospace); font-weight: 700; color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-badge { display: inline-flex; align-items: center; justify-content: center; width: 14px; height: 14px; border-radius: 4px; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 9px; font-weight: 700; }\n/* 动作语义色(13-log-ui-polish 归位):A=success 绿 · D=error 红 · M=brand 蓝 · R=warn 琥珀 @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-badge-modified { color: var(--dsw-alias-state-info-primary, #2f6fed); background: rgba(47,111,237,.14); }\n[data-edrv-view] .edrv-svnlog-badge-added { color: var(--dsw-alias-state-success-primary, #0f9d58); background: rgba(15,157,88,.14); }\n[data-edrv-view] .edrv-svnlog-badge-deleted { color: var(--dsw-alias-state-error-primary, #d9534f); background: rgba(217,83,79,.14); }\n[data-edrv-view] .edrv-svnlog-badge-replaced { color: var(--dsw-alias-state-warn-primary, #d97706); background: rgba(217,119,6,.16); }\n[data-edrv-view] .edrv-svnlog-badge-plain { color: var(--dsw-alias-label-tertiary, #9aa5b1); background: var(--dsw-alias-bg-layer-2, #f0f4f4); }\n[data-edrv-view] .edrv-svnlog-more { padding: 6px 4px 2px; text-align: center; }\n/* P0-14 三段 pane 公共外观(13-log-ui-polish:2px 粗框 → 1px 发丝线 + 面板底色) */\n[data-edrv-view] .edrv-svnlog-pane { border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 8px; padding: 8px 10px; overflow: hidden; background: var(--dsw-alias-bg-layer-1, #fff); }\n[data-edrv-view] .edrv-svnlog-msgpane { flex: 0 0 auto; display: flex; flex-direction: column; }\n[data-edrv-view] .edrv-svnlog-pathspane { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }\n/* P0-13/P0-14:分段横向拖拽分隔条 + 变更路径独立滚动容器 */\n[data-edrv-view] .edrv-svnlog-hsplit { flex: 0 0 6px; cursor: row-resize; margin: 2px 0; border-radius: 3px; touch-action: none; }\n[data-edrv-view] .edrv-svnlog-hsplit:hover, [data-edrv-view] .edrv-svnlog-hsplit:active { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.28)); }\n[data-edrv-view] .edrv-svnlog-paths-wrap { flex: 1 1 auto; min-height: 0; overflow: auto; display: flex; flex-direction: column; }\n/* 提交信息 meta 行:rev/日期 chip 化,author 常规字重(13-log-ui-polish) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-meta { display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); flex-wrap: wrap; }\n[data-edrv-view] .edrv-svnlog-meta .edrv-svnlog-rev { padding: 1px 8px; border-radius: 999px; background: var(--dsw-alias-state-info-primary, #2f6fed); color: var(--dsw-alias-label-primary-inverted, #fff); font-size: 11px; font-weight: 700; }\n[data-edrv-view] .edrv-svnlog-meta .edrv-svnlog-date { height: auto; padding: 1px 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-svnlog-date { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n/* 正文块:左侧品牌强调线 +1.7 行高(长提交信息可读性) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-msgpane pre.edrv-svnlog-body { margin: 8px 0 0; padding: 8px 10px; border-radius: 0 6px 6px 0; border-left: 2px solid var(--dsw-alias-state-info-primary, #2f6fed); background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11.5px; line-height: 1.7; white-space: pre-wrap; word-break: break-word; overflow: auto; flex: 1 1 auto; min-height: 0; }\n/* P0-14 外框尺寸手柄(13-log-ui-polish 增量3 八向:n/s/w/e 四边 + nw/ne/sw/se 四角);\n 热区避开内边距带(边8px/角14px 落在卡片22/24px padding 内不遮内容),角手柄后渲染盖过相邻边2px overlap。\n chevron 角标四向各自朝向,hover 显形。 @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-grip { position: absolute; z-index: 2; touch-action: none; }\n[data-edrv-view] .edrv-svnlog-grip-n { top: 0; left: 12px; right: 12px; height: 8px; cursor: ns-resize; }\n[data-edrv-view] .edrv-svnlog-grip-s { bottom: 0; left: 12px; right: 12px; height: 8px; cursor: ns-resize; }\n[data-edrv-view] .edrv-svnlog-grip-w { left: 0; top: 12px; bottom: 12px; width: 8px; cursor: ew-resize; }\n[data-edrv-view] .edrv-svnlog-grip-e { right: 0; top: 12px; bottom: 12px; width: 8px; cursor: ew-resize; }\n[data-edrv-view] .edrv-svnlog-grip-nw { left: 0; top: 0; width: 14px; height: 14px; cursor: nwse-resize; }\n[data-edrv-view] .edrv-svnlog-grip-ne { right: 0; top: 0; width: 14px; height: 14px; cursor: nesw-resize; }\n[data-edrv-view] .edrv-svnlog-grip-sw { left: 0; bottom: 0; width: 14px; height: 14px; cursor: nesw-resize; }\n[data-edrv-view] .edrv-svnlog-grip-se { right: 0; bottom: 0; width: 14px; height: 14px; cursor: nwse-resize; background: transparent; }\n[data-edrv-view] .edrv-svnlog-grip-nw::after,\n[data-edrv-view] .edrv-svnlog-grip-ne::after,\n[data-edrv-view] .edrv-svnlog-grip-sw::after,\n[data-edrv-view] .edrv-svnlog-grip-se::after { content: ''; position: absolute; width: 6px; height: 6px; opacity: .5; border-color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svnlog-grip-se::after { right: 3px; bottom: 3px; border-right: 1.5px solid; border-bottom: 1.5px solid; }\n[data-edrv-view] .edrv-svnlog-grip-sw::after { left: 3px; bottom: 3px; border-left: 1.5px solid; border-bottom: 1.5px solid; }\n[data-edrv-view] .edrv-svnlog-grip-ne::after { right: 3px; top: 3px; border-right: 1.5px solid; border-top: 1.5px solid; }\n[data-edrv-view] .edrv-svnlog-grip-nw::after { left: 3px; top: 3px; border-left: 1.5px solid; border-top: 1.5px solid; }\n[data-edrv-view] .edrv-svnlog-grip:hover, [data-edrv-view] .edrv-svnlog-grip:active { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.35)); }\n[data-edrv-view] .edrv-svnlog-grip:hover::after, [data-edrv-view] .edrv-svnlog-grip:active::after { opacity: 1; border-color: var(--dsw-alias-label-primary, #1f2933); }\n/* 行反馈微动效(仅 hover/选中背景过渡;用户要求减少动效时关闭) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-item { transition: background-color .12s ease, box-shadow .12s ease; }\n@media (prefers-reduced-motion: reduce) {\n [data-edrv-view] .edrv-svnlog-item { transition: none; }\n}\n/*13-log-ui-polish 增量:行容器可滚动(pane 仍 overflow:hidden 裁圆角),列头不被压缩 @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-paths-head { display: flex; align-items: center; gap: 6px; flex: none; font-size: 10px; font-weight: 600; letter-spacing: .04em; color: var(--dsw-alias-label-tertiary, #9aa5b1); margin-bottom: 4px; padding-bottom: 4px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-svnlog-hint { font-weight: 400; letter-spacing: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svnlog-paths { display: flex; flex-direction: column; gap: 1px; flex: 1 1 auto; min-height: 0; overflow: auto; }\n[data-edrv-view] .edrv-svnlog-path { display: flex; align-items: center; gap: 6px; height: 22px; padding: 0 6px; border-radius: 5px; cursor: default; white-space: nowrap; }\n[data-edrv-view] .edrv-svnlog-path:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-svnlog-path-dim .edrv-svnlog-pathtext,\n[data-edrv-view] .edrv-svnlog-path-dim .edrv-svnlog-copyfrom { opacity: .45; }\n[data-edrv-view] .edrv-svnlog-pathtext { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; color: var(--dsw-alias-label-primary, #1f2933); }\n/* 路径两段着色:目录段弱化 + 文件名加重(13-log-ui-polish) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-pdir { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svnlog-pbase { color: var(--dsw-alias-label-primary, #1f2933); font-weight: 600; }\n[data-edrv-view] .edrv-svnlog-copyfrom { flex: 0 1 160px; overflow: hidden; text-overflow: ellipsis; font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-svnlog-kind { font-size: 10px; padding: 0 5px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svnlog-acts { display: none; align-items: center; gap: 4px; flex-shrink: 0; }\n[data-edrv-view] .edrv-svnlog-path:hover .edrv-svnlog-acts { display: inline-flex; }\n/* 底部:状态行与按钮组分层 + 组间发丝线(13-log-ui-polish) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-footwrap { display: flex; flex-direction: column; gap: 6px; flex-shrink: 0; padding-top: 6px; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-svnlog-foot { display: flex; align-items: center; gap: 10px; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-svnlog-btns { display: flex; align-items: center; gap: 6px; }\n[data-edrv-view] .edrv-svnlog-btngroup { display: inline-flex; align-items: center; gap: 6px; }\n[data-edrv-view] .edrv-svnlog-btngroup + .edrv-svnlog-btngroup { margin-left: 6px; padding-left: 10px; border-left: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-svnlog-dialog .edrv-svn-act,\n[data-edrv-view] .edrv-svnlog-range .edrv-svn-act,\n[data-edrv-view] .edrv-svnlog-stats .edrv-svn-act { font-size: 11px; padding: 2px 9px; }\n[data-edrv-view] .edrv-svnlog-dialog .edrv-svn-act:focus-visible,\n[data-edrv-view] .edrv-svnlog-range .edrv-svn-act:focus-visible,\n[data-edrv-view] .edrv-svnlog-stats .edrv-svn-act:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #2563eb); outline-offset: 1px; }\n/* ===== SVN 日志统计窗(P1-10)@author ddj 2026-09-17 ===== */\n[data-edrv-view] .edrv-svnlog-stats { display: flex; flex-direction: column; max-height: 82vh; }\n[data-edrv-view] .edrv-svnlog-stats-head { display: flex; align-items: center; gap: 8px; padding: 12px 16px 0; }\n[data-edrv-view] .edrv-svnlog-stats-body { display: flex; flex-direction: column; gap: 14px; padding: 10px 16px 16px; overflow: auto; }\n[data-edrv-view] .edrv-svnlog-stat-line { display: flex; gap: 16px; flex-wrap: wrap; font-size: 12px; color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-svnlog-stat-block { display: flex; flex-direction: column; gap: 8px; }\n[data-edrv-view] .edrv-svnlog-stat-label { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-svnlog-stat-duo { display: flex; gap: 18px; align-items: flex-start; flex-wrap: wrap; }\n[data-edrv-view] .edrv-svnlog-stat-hist { flex: 1 1 260px; display: flex; flex-direction: column; gap: 4px; min-width: 220px; }\n[data-edrv-view] .edrv-svnlog-stat-row { display: flex; align-items: center; gap: 8px; font-size: 11px; }\n[data-edrv-view] .edrv-svnlog-stat-name { width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-svnlog-stat-barwrap { flex: 1; height: 12px; border-radius: 6px; background: rgba(127,127,127,.15); overflow: hidden; }\n[data-edrv-view] .edrv-svnlog-stat-bar { display: block; height: 100%; border-radius: 6px; }\n[data-edrv-view] .edrv-svnlog-stat-count { width: 36px; text-align: right; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-svnlog-stat-pie { width: 120px; height: 120px; flex-shrink: 0; border-radius: 50%; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-svnlog-stat-days { display: flex; align-items: flex-end; gap: 2px; height: 120px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); overflow-x: auto; }\n[data-edrv-view] .edrv-svnlog-stat-day { flex: 1 0 6px; min-width: 6px; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; }\n[data-edrv-view] .edrv-svnlog-stat-seg { display: block; width: 100%; }\n/* ===== SVN 日志区间弹窗(P1-6)@author ddj 2026-09-17 ===== */\n[data-edrv-view] .edrv-svnlog-range-body { display: flex; flex-direction: column; gap: 12px; padding: 14px 16px 16px; }\n[data-edrv-view] .edrv-svnlog-range-row { display: flex; gap: 14px; flex-wrap: wrap; }\n[data-edrv-view] .edrv-svnlog-range-input { width: 110px; padding: 4px 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-svnlog-range-btns { display: flex; gap: 8px; justify-content: flex-end; }\n/* SVN 日志 WC 版号行加粗(P1-9) */\n[data-edrv-view] .edrv-svnlog-item-wc { font-weight: 700; }\n/* SVN 日志 merged 行(P1-5):灰字 + 缩进(缩进量按层级内联),前置「↳」标记 */\n[data-edrv-view] .edrv-svnlog-item-merged { color: var(--dsw-alias-label-tertiary, #7b8794); }\n[data-edrv-view] .edrv-svnlog-item-merged .edrv-svnlog-merge-mark { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary, #7b8794); }\n[data-edrv-view] .edrv-svnlog-item-merged .edrv-svnlog-cell-msg { font-style: italic; }\n/* ===== 诊断日志弹窗(LogDialog)@author ddj 2026-09-17 ===== */\n[data-edrv-view] .edrv-dlog-dialog { display: flex; flex-direction: column; gap: 8px; max-height: 88vh; }\n[data-edrv-view] .edrv-dlog-head { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; }\n[data-edrv-view] .edrv-dlog-title { margin: 0; font-size: 15px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-dlog-file { max-width: 200px; padding: 4px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-dlog-level { padding: 4px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-dlog-search { min-width: 120px; flex: 0 1 200px; padding: 4px 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-dlog-auto { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); white-space: nowrap; cursor: pointer; }\n[data-edrv-view] .edrv-dlog-error { padding: 4px 8px; border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n[data-edrv-view] .edrv-dlog-scroll { flex: 1 1 auto; min-height: 240px; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 8px; padding: 4px 0; background: var(--dsw-alias-bg-layer-1, #fff); }\n[data-edrv-view] .edrv-dlog-row { display: flex; align-items: baseline; gap: 8px; padding: 1px 8px; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; line-height: 1.55; }\n[data-edrv-view] .edrv-dlog-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.06)); }\n[data-edrv-view] .edrv-dlog-at { flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-dlog-lvl { flex-shrink: 0; width: 26px; text-align: center; font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-dlog-text { flex: 1; min-width: 0; white-space: pre-wrap; word-break: break-word; color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-dlog-row-error .edrv-dlog-text { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-dlog-row-warn .edrv-dlog-text { color: var(--dsw-alias-state-warning-primary, #b45309); }\n[data-edrv-view] .edrv-dlog-row-debug .edrv-dlog-text { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-dlog-foot { display: flex; align-items: center; gap: 10px; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }\n[data-edrv-view] .edrv-dlog-path { font-family: var(--ds-font-family-code, ui-monospace, monospace); max-width: 46%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-dlog-stat { white-space: nowrap; }\n[data-edrv-view] .edrv-dlog-switch { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); white-space: nowrap; cursor: pointer; flex-shrink: 0; }\n/* 原生 SVN 更新窗口:逐条状态与路径是主信息;跟随 DSH 主题令牌。 */\n[data-edrv-view] .edrv-update-dialog { display: flex; flex-direction: column; padding: 0; border-radius: 8px; overflow: hidden; background: var(--dsw-alias-bg-overlay, #fff); }\n[data-edrv-view] .edrv-update-head { display: flex; align-items: center; gap: 12px; min-height: 48px; padding: 0 16px; border-bottom: 1px solid var(--dsw-alias-border-l1, #d9e0e4); }\n[data-edrv-view] .edrv-update-heading { display: flex; align-items: center; gap: 12px; min-width: 0; flex: 1; }\n[data-edrv-view] .edrv-update-heading h3 { margin: 0; flex: none; font-size: 15px; }\n[data-edrv-view] .edrv-update-target { min-width: 0; color: var(--dsw-alias-label-tertiary, #65737c); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; }\n[data-edrv-view] .edrv-update-phase { flex: none; font-size: 12px; color: var(--dsw-alias-label-secondary, #485762); }\n[data-edrv-view] .edrv-update-close { display: grid; place-items: center; flex: none; width: 28px; height: 28px; border: 0; border-radius: 4px; background: none; color: var(--dsw-alias-label-secondary, #485762); cursor: pointer; }\n[data-edrv-view] .edrv-update-close:disabled { opacity: .35; cursor: default; }\n[data-edrv-view] .edrv-update-close:not(:disabled):hover { background: var(--dsw-alias-interactive-bg-hover, #e7edf0); }\n[data-edrv-view] .edrv-update-table { display: flex; flex-direction: column; flex: 1; min-height: 0; margin: 12px 14px 0; border: 1px solid var(--dsw-alias-border-l1, #d9e0e4); border-radius: 5px; overflow: hidden; background: var(--dsw-alias-bg-layer-1, #fff); }\n[data-edrv-view] .edrv-update-cols, [data-edrv-view] .edrv-update-row { display: grid; grid-template-columns: 110px minmax(0, 1fr) 160px; align-items: center; }\n[data-edrv-view] .edrv-update-sort { width: 100%; padding: 0; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }\n[data-edrv-view] .edrv-update-sort:hover { color: var(--dsw-alias-brand-primary, #2869b4); }\n[data-edrv-view] .edrv-update-sort:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #2869b4); outline-offset: 2px; }\n[data-edrv-view] .edrv-update-jump { color: var(--dsw-alias-state-error-primary, #b93b43); }\n[data-edrv-view] .edrv-update-cols { flex: none; height: 30px; background: var(--dsw-alias-bg-layer-2, #f1f4f5); border-bottom: 1px solid var(--dsw-alias-border-l1, #d9e0e4); color: var(--dsw-alias-label-secondary, #485762); font-size: 11px; font-weight: 600; }\n[data-edrv-view] .edrv-update-cols > span, [data-edrv-view] .edrv-update-row > span { min-width: 0; padding: 0 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-update-cols > span + span, [data-edrv-view] .edrv-update-row > span + span { border-left: 1px solid var(--dsw-alias-border-l1, #d9e0e4); }\n[data-edrv-view] .edrv-update-list { position: relative; flex: 1; min-height: 0; overflow: auto; scrollbar-width: thin; }\n[data-edrv-view] .edrv-update-space { position: relative; }\n[data-edrv-view] .edrv-update-pos { position: absolute; left: 0; right: 0; }\n[data-edrv-view] .edrv-update-row { height: 100%; font-size: 11px; cursor: default; color: var(--dsw-alias-label-primary, #202932); }\n[data-edrv-view] .edrv-update-row:hover { background: var(--dsw-alias-interactive-bg-hover, #ecf3f9); }\n[data-edrv-view] .edrv-update-row.is-selected { background: var(--dsw-alias-interactive-bg-hover, #dceaf9); box-shadow: inset 2px 0 0 var(--dsw-alias-brand-primary, #2869b4); }\n[data-edrv-view] .edrv-update-action { font-weight: 600; }\n[data-edrv-view] .edrv-update-action.tone-added { color: var(--dsw-alias-state-success-primary, #23704e); }\n[data-edrv-view] .edrv-update-action.tone-deleted, [data-edrv-view] .edrv-update-action.tone-conflict { color: var(--dsw-alias-state-error-primary, #b93b43); }\n[data-edrv-view] .edrv-update-action.tone-merged { color: var(--dsw-alias-state-warn-primary, #a05d22); }\n[data-edrv-view] .edrv-update-action.tone-updated { color: var(--dsw-alias-state-info-primary, #2869b4); }\n[data-edrv-view] .edrv-update-path { font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-update-type { color: var(--dsw-alias-label-tertiary, #65737c); }\n[data-edrv-view] .edrv-update-empty { position: absolute; top: 50%; left: 0; right: 0; transform: translateY(-50%); text-align: center; font-size: 12px; color: var(--dsw-alias-label-tertiary, #65737c); pointer-events: none; }\n[data-edrv-view] .edrv-update-telemetry { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 18px; padding: 10px 15px; min-height: 43px; box-sizing: border-box; font-size: 11px; color: var(--dsw-alias-label-secondary, #485762); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-update-error { color: var(--dsw-alias-state-error-primary, #b93b43); overflow-wrap: anywhere; }\n[data-edrv-view] .edrv-update-warning { color: var(--dsw-alias-state-warn-primary, #a05d22); }\n[data-edrv-view] .edrv-update-locked { color: var(--dsw-alias-state-warn-primary, #a05d22); }\n[data-edrv-view] .edrv-update-foot .edrv-update-cleanup:not(:disabled) { color: var(--dsw-alias-state-warn-primary, #a05d22); }\n[data-edrv-view] .edrv-update-foot { display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-top: 1px solid var(--dsw-alias-border-l1, #d9e0e4); font-size: 11px; }\n[data-edrv-view] .edrv-update-counts { flex: 1; min-width: 0; color: var(--dsw-alias-label-secondary, #485762); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-update-foot .edrv-svn-act { flex: none; min-width: 74px; }\n[data-edrv-view] .edrv-update-foot .edrv-svn-act:focus-visible, [data-edrv-view] .edrv-update-close:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #2869b4); outline-offset: 2px; }\n[data-edrv-view] .edrv-update-foot .edrv-svn-act:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-update-raw { max-height: 120px; overflow: auto; padding: 0 14px 10px; font-size: 11px; }\n[data-edrv-view] .edrv-update-raw pre { white-space: pre-wrap; overflow-wrap: anywhere; margin: 5px 0; }\n@media (max-width: 620px) {\n [data-edrv-view] .edrv-update-head { gap: 6px; padding: 0 10px; }\n [data-edrv-view] .edrv-update-table { margin: 8px 8px 0; }\n [data-edrv-view] .edrv-update-cols, [data-edrv-view] .edrv-update-row { grid-template-columns: 76px minmax(0, 1fr); }\n [data-edrv-view] .edrv-update-cols > span:nth-child(3), [data-edrv-view] .edrv-update-row > span:nth-child(3) { display: none; }\n [data-edrv-view] .edrv-update-path { white-space: normal !important; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }\n [data-edrv-view] .edrv-update-foot { flex-wrap: wrap; padding: 8px; }\n [data-edrv-view] .edrv-update-counts { flex-basis: 100%; }\n}\n\n/* ===== 侧栏形态(betterSidebar 面板内编辑区) ===== */\n[data-edrv-view][data-edrv-layout='side'] { box-sizing: border-box; height: 100%; }\n\n/* ===== 侧边栏引导条(旧页签形态、未装 betterSidebar 时) ===== */\n[data-edrv-view] .edrv-side-hint { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; flex-wrap: wrap; padding: 6px 12px; font-size: 12px; color: var(--dsw-alias-label-secondary, #52606d); background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, #f0f4f4)); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-side-hint-text { white-space: nowrap; }\n[data-edrv-view] .edrv-side-hint-cmd { font-size: 11px; padding: 2px 6px; border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #ffffff); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-side-hint-close { margin-left: auto; border: none; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); cursor: pointer; font-size: 14px; line-height: 1; padding: 2px 6px; border-radius: 6px; flex-shrink: 0; }\n[data-edrv-view] .edrv-side-hint-close:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n\n/* ===== search panel (sidebar, VSCode search view) ===== */\n[data-edrv-view] .edrv-search-panel { display: flex; flex-direction: column; min-height: 0; flex: 1; padding: 6px; gap: 4px; }\n[data-edrv-view] .edrv-search-qrow { display: flex; gap: 4px; }\n[data-edrv-view] .edrv-search-input { flex: 1; min-width: 0; height: 26px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-input:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-opts { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-search-opt { min-width: 24px; height: 22px; padding: 0 6px; border: 1px solid transparent; border-radius: 5px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 11px; font-family: var(--ds-font-family-code, ui-monospace, monospace); cursor: pointer; }\n[data-edrv-view] .edrv-search-opt:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-opt.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); border-color: var(--dsw-alias-border-l2, #d5dde3); }\n[data-edrv-view] .edrv-search-spacer { flex: 1; }\n[data-edrv-view] .edrv-search-summary { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); padding: 2px 4px; }\n[data-edrv-view] .edrv-search-results { flex: 1; min-height: 0; overflow: auto; font-size: 12px; }\n[data-edrv-view] .edrv-search-group { margin-bottom: 2px; }\n[data-edrv-view] .edrv-search-file { display: flex; align-items: center; gap: 4px; height: 22px; padding: 0 4px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-search-file:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-chev { width: 12px; flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; text-align: center; }\n[data-edrv-view] .edrv-search-fname { font-weight: 600; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-search-fdir { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-search-fcount { flex-shrink: 0; min-width: 16px; height: 15px; padding: 0 4px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); font-size: 10px; line-height: 15px; text-align: center; }\n[data-edrv-view] .edrv-search-line { display: flex; gap: 6px; padding: 1px 4px 1px 16px; border-radius: 4px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; overflow: hidden; }\n[data-edrv-view] .edrv-search-line.alt { background: var(--dsw-alias-bg-layer-1, #ffffff); }\n[data-edrv-view] .edrv-search-line:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-ln { flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; min-width: 26px; text-align: right; user-select: none; }\n[data-edrv-view] .edrv-search-text { overflow: hidden; text-overflow: ellipsis; white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; }\n[data-edrv-view] .edrv-search-hit { background: var(--dsw-alias-state-warn-primary, #e5b93f); color: var(--dsw-alias-label-primary-inverted, #1f2933); border-radius: 2px; padding: 0 1px; }\n[data-edrv-view] .edrv-search-empty { padding: 8px 4px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-search-error { padding: 6px 8px; border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n[data-edrv-view] .edrv-search-warn { padding: 6px 8px; border: 1px solid var(--dsw-alias-state-warn-primary, rgba(240,173,78,.45)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover, rgba(240,173,78,.12)); color: var(--dsw-alias-state-warn-primary, #b8860b); font-size: 11px; word-break: break-all; }\n\n/* include/exclude filter rows (collapsible) */\n[data-edrv-view] .edrv-search-rows { display: flex; flex-direction: column; gap: 4px; padding: 2px 0 4px; }\n[data-edrv-view] .edrv-search-row { display: flex; align-items: center; gap: 4px; }\n[data-edrv-view] .edrv-search-field { flex: 1; min-width: 0; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 11px; outline: none; }\n[data-edrv-view] .edrv-search-field:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-field.dim { opacity: .45; }\n[data-edrv-view] .edrv-search-field::placeholder { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-search-toggle { min-width: 24px; height: 24px; padding: 0 5px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; line-height: 1; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-search-toggle:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-toggle.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); border-color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-search-toggle:disabled { opacity: .4; cursor: default; }\n\n/* ===== rules panel (sidebar, Codebuddy rules view) ===== */\n[data-edrv-view] .edrv-rules-panel { display: flex; flex-direction: column; min-width: 0; min-height: 0; flex: 1; padding: 6px; gap: 4px; }\n[data-edrv-view] .edrv-rules-tabs { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-tab { height: 24px; padding: 0 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; cursor: pointer; }\n[data-edrv-view] .edrv-rules-tab:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-tab.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); font-weight: 600; }\n[data-edrv-view] .edrv-rules-new { margin-left: auto; height: 24px; padding: 0 10px; border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-new:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-ws-line { font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); padding: 0 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 0 0 auto; user-select: none; }\n[data-edrv-view] .edrv-rules-hint { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); padding: 0 2px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-list { flex: 1; min-height: 0; overflow: auto; }\n[data-edrv-view] .edrv-rules-row { display: flex; align-items: center; gap: 4px; padding: 6px 4px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; }\n[data-edrv-view] .edrv-rules-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-row.off .edrv-rules-main { opacity: .5; }\n[data-edrv-view] .edrv-rules-main { flex: 1; min-width: 0; cursor: pointer; overflow: hidden; }\n[data-edrv-view] .edrv-rules-line1 { display: flex; align-items: center; gap: 6px; min-width: 0; }\n[data-edrv-view] .edrv-rules-sub { display: flex; align-items: center; gap: 5px; margin-top: 3px; min-width: 0; }\n[data-edrv-view] .edrv-rules-sublabel { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-file { font-weight: 600; font-size: 12px; color: var(--dsw-alias-label-primary, #1f2933); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-rules-path { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-rules-type { flex-shrink: 0; height: 15px; padding: 0 5px; border-radius: 999px; font-size: 10px; line-height: 15px; }\n[data-edrv-view] .edrv-rules-type-always { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.15)); color: var(--dsw-alias-state-success-primary, #0b7c46); }\n[data-edrv-view] .edrv-rules-type-auto { background: var(--dsw-alias-interactive-bg-hover-accent, rgba(79,140,255,.16)); color: var(--dsw-alias-link, #3367c9); }\n[data-edrv-view] .edrv-rules-type-manual { background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-rules-offtag { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-desc { font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 0 1 auto; }\n[data-edrv-view] .edrv-rules-actions { display: flex; align-items: center; gap: 2px; flex-shrink: 0; margin-left: auto; }\n[data-edrv-view] .edrv-rules-act { min-width: 22px; height: 22px; padding: 0 4px; border: none; border-radius: 5px; background: transparent; font-size: 12px; line-height: 1; cursor: pointer; color: var(--dsw-alias-label-secondary, #52606d); display: inline-flex; align-items: center; justify-content: center; }\n[data-edrv-view] .edrv-rules-act:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-switch { position: relative; width: 30px; height: 16px; border-radius: 999px; border: none; background: var(--dsw-alias-border-l2, #b9c4cc); cursor: pointer; transition: background .15s; flex-shrink: 0; padding: 0; }\n[data-edrv-view] .edrv-rules-switch.on { background: var(--dsw-alias-brand-primary, #7c5cff); }\n[data-edrv-view] .edrv-rules-knob { position: absolute; top: 2px; left: 2px; width: 12px; height: 12px; border-radius: 50%; background: var(--dsw-alias-bg-layer-1, #fff); transition: left .15s; }\n[data-edrv-view] .edrv-rules-switch.on .edrv-rules-knob { left: 16px; }\n[data-edrv-view] .edrv-rules-empty { padding: 10px 4px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); line-height: 1.6; }\n[data-edrv-view] .edrv-rules-err { padding: 6px 8px; border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; word-break: break-all; }\n[data-edrv-view] .edrv-rules-editor { display: flex; flex-direction: column; min-height: 0; flex: 1; gap: 6px; }\n[data-edrv-view] .edrv-rules-editor-head { display: flex; align-items: center; justify-content: space-between; gap: 6px; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-crumb { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-rules-editor-ctl { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-ctl-label { font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-select { height: 24px; padding: 0 4px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 11px; outline: none; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-select:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-rules-desc-input { flex: 1; min-width: 0; }\n[data-edrv-view] .edrv-rules-input { flex: 1; min-width: 0; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 11px; outline: none; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-rules-input:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-rules-body { flex: 1; min-height: 0; resize: none; padding: 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; line-height: 1.5; outline: none; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-rules-body:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-rules-editor-hint { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); line-height: 1.5; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-editor-foot { display: flex; justify-content: flex-end; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-save { height: 24px; padding: 0 14px; border: none; border-radius: 6px; background: var(--dsw-alias-brand-primary, #0f9d58); color: var(--dsw-alias-label-primary-inverted, #fff); font-size: 12px; cursor: pointer; }\n[data-edrv-view] .edrv-rules-save:disabled { opacity: .5; cursor: default; }\n\n/* 命令栏(Ctrl+Shift+P / F1):body 级 portal 浮层,故不依赖编辑区几何。\n ⚠️ 浮层 z-index 阶梯(自下而上;改动务必保持顺序,tests/overlayZOrder.test.ts 守约):\n 编辑器内浮层 60(.edrv-search-pop) / 71(.edrv-ctxmenu)\n < 190 命令栏遮罩 < 200 命令栏\n < 195 片段浮窗遮罩 < 197 片段二级弹窗遮罩\n 命令栏必须**高于**片段浮窗:曾因片段遮罩(192) 压过命令栏(191),导致片段浮窗打开时\n 命令栏被整体盖住,按 Ctrl+Shift+P 因「已打开即早退」而毫无反应(表现为快捷键失效)。 */\n[data-edrv-view] .edrv-palette-mask { position: fixed; inset: 0; z-index: 190; background: rgba(0, 0, 0, .28); }\n[data-edrv-view] .edrv-palette { position: fixed; top: 12vh; left: 50%; transform: translateX(-50%); z-index: 200; width: min(580px, calc(100vw - 32px)); max-height: 64vh; display: flex; flex-direction: column; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 12px 34px rgba(31, 41, 51, .24); overflow: hidden; }\n[data-edrv-view] .edrv-palette-input { height: 38px; flex: 0 0 auto; padding: 0 12px; border: none; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: transparent; color: var(--dsw-alias-label-primary, #1f2933); font-size: 13px; outline: none; }\n[data-edrv-view] .edrv-palette-list { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 4px; }\n[data-edrv-view] .edrv-palette-row { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-radius: 6px; font-size: 12px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; }\n[data-edrv-view] .edrv-palette-row.edrv-palette-sel { background: var(--dsw-alias-interactive-bg-hover, rgba(15, 157, 88, .10)); }\n[data-edrv-view] .edrv-palette-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-palette-cat { flex: 0 0 auto; font-size: 10px; padding: 1px 7px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-tertiary, #7b8794); }\n[data-edrv-view] .edrv-palette-key { flex: 0 0 auto; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 10px; padding: 1px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 5px; background: var(--dsw-alias-bg-layer-1, #f7f9fa); color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-palette-empty { padding: 14px 10px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #7b8794); text-align: center; }\n[data-edrv-view] .edrv-palette-notice { flex: 0 0 auto; padding: 6px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 11px; color: var(--dsw-alias-state-warn-primary, #b7791f); }\n\n/* 代码片段选择器(配置 / 插入):居中浮窗 + 新建二级弹窗。\n 卡片复用 mcp.css 的 .vsm-mcp-dialog(与「添加 MCP」「工作区选择」同一视觉语言),\n 此处只补遮罩、尺寸与列表布局。⚠️ 根节点须带 data-edrv-view,否则这些规则全部失配。 */\n[data-edrv-view] .edrv-snip-mask { position: fixed; inset: 0; z-index: 195; display: flex; align-items: center; justify-content: center; padding: 24px; box-sizing: border-box; background: var(--dsw-alias-bg-mask-drop, rgba(0, 0, 0, .55)); }\n/* 二级弹窗叠在一级浮窗之上(仍在命令栏之下,见上方阶梯说明) */\n[data-edrv-view] .edrv-snip-mask-top { z-index: 197; }\n[data-edrv-view] .edrv-snip-dialog { display: flex; flex-direction: column; overflow: hidden; width: min(620px, calc(100vw - 48px)); }\n[data-edrv-view] .edrv-snip-hint { margin: 0 0 12px; font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-snip-list { display: flex; flex-direction: column; gap: 1px; min-height: 0; max-height: 46vh; overflow: auto; }\n[data-edrv-view] .edrv-snip-group { padding: 12px 2px 4px; font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-secondary, #aaa); }\n[data-edrv-view] .edrv-snip-row { display: flex; align-items: center; gap: 4px; width: 100%; box-sizing: border-box; padding: 2px; border: none; border-radius: 8px; background: transparent; text-align: left; color: var(--dsw-alias-label-primary, #e6e6e6); font: inherit; }\n[data-edrv-view] .edrv-snip-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, .06)); }\n[data-edrv-view] button.edrv-snip-row { cursor: pointer; }\n[data-edrv-view] .edrv-snip-row-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; padding: 8px 10px; border: none; border-radius: 6px; background: transparent; text-align: left; cursor: pointer; color: inherit; font: inherit; }\n[data-edrv-view] .edrv-snip-label { font-size: 13px; font-weight: 600; color: var(--dsw-alias-label-primary, #e6e6e6); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-snip-desc { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-snip-src { flex: 0 0 auto; padding-right: 6px; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-snip-warn { flex: 0 0 auto; font-size: 11px; padding: 2px 7px; border-radius: 999px; background: var(--dsw-alias-state-warn-tertiary, rgba(229, 185, 63, .16)); color: var(--dsw-alias-state-warn-primary, #e5b93f); white-space: nowrap; }\n[data-edrv-view] .edrv-snip-act { flex: 0 0 auto; width: 26px; height: 26px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 13px; line-height: 1; cursor: pointer; }\n[data-edrv-view] .edrv-snip-act:hover { background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217, 83, 79, .16)); color: var(--dsw-alias-state-error-primary, #f07878); }\n[data-edrv-view] .edrv-snip-empty { padding: 12px 4px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); line-height: 1.6; }\n/* 当前文件语言提示条(片段按语言绑定,明确告知生效范围) */\n[data-edrv-view] .edrv-snip-langbar { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; margin: 0 0 10px; padding: 8px 10px; border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #2b2b2b); font-size: 12px; color: var(--dsw-alias-label-secondary, #aaa); }\n[data-edrv-view] .edrv-snip-langbar b { color: var(--dsw-alias-label-primary, #e6e6e6); }\n/* 二级弹窗的动作按钮(原生 .vsm-mcp-dialog-actions 已提供排版与按钮样式) */\n[data-edrv-view] .edrv-snip-dialog .vsm-mcp-dialog-actions { flex-wrap: wrap; }\n/* 删除图标按钮:.vsm-danger 的规则全部限定在 .vsm-mcp-actions button 之下,裸用无样式,此处补自有类 */\n[data-edrv-view] .edrv-snip-act.edrv-snip-danger:hover { background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217, 83, 79, .16)); color: var(--dsw-alias-state-error-primary, #f07878); }\n/* 浮窗内的原生空态(.vsm-mcp-empty 默认为整页尺寸)压缩为对话框尺寸 */\n[data-edrv-view] .edrv-snip-dialog .vsm-mcp-empty { padding: 26px 16px; border: 0; }\n\n\n\n\n\n/* 调试(DAP):断点圆点 / 停帧行 / 浮动调试工具条。\n 圆点挂在 Monaco glyphMargin(glyphMarginClassName),行背景为 isWholeLine className;\n 工具条绝对定位于编辑区右上(editorArea relative 由 .edrv-editor-area 自身定位)。 */\n/* 断点圆点:装饰类占满整行,圆点经 ::before 在本行盒子内居中(行归属明确、不跨行界) */\n[data-edrv-view] .edrv-dap-bp { cursor: pointer; }\n[data-edrv-view] .edrv-dap-bp::before { content: ''; display: block; width: 7px; height: 7px; margin: 6px auto 0; border-radius: 50%; background: var(--dsw-alias-state-error-primary, #e51400); }\n[data-edrv-view] .edrv-dap-bp-off { cursor: pointer; }\n[data-edrv-view] .edrv-dap-bp-off::before { content: ''; display: block; box-sizing: border-box; width: 7px; height: 7px; margin: 6px auto 0; border-radius: 50%; border: 1px solid var(--dsw-alias-label-tertiary, #9aa5b1); opacity: .6; }\n/* 未验证断点(适配器真实回执 verified:false):空心红圈,对齐 VS Code 空心圆语义 */\n[data-edrv-view] .edrv-dap-bp-unverified { cursor: pointer; }\n[data-edrv-view] .edrv-dap-bp-unverified::before { content: ''; display: block; box-sizing: border-box; width: 7px; height: 7px; margin: 6px auto 0; border-radius: 50%; border: 1px solid var(--dsw-alias-state-error-primary, #e51400); opacity: .75; }\n[data-edrv-view] .edrv-dap-paused-line { background: var(--dsw-alias-state-warn-tertiary, rgba(229, 185, 63, .22)); }\n[data-edrv-view] .edrv-dap-paused-glyph { cursor: pointer; }\n[data-edrv-view] .edrv-dap-paused-glyph::before { content: ''; display: block; width: 5px; height: 12px; margin: 4px auto 0; border-radius: 2px; background: var(--dsw-alias-state-warn-primary, #e5b93f); }\n[data-edrv-view] .edrv-editor-area { position: relative; }\n/* launch.json 右下角「添加配置」按钮(VSCode 同款形态):仅 launch.json 打开时渲染 */\n[data-edrv-view] .edrv-addcfg { position: absolute; right: 14px; bottom: 10px; z-index: 13; padding: 4px 12px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #f7f9fa); color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; line-height: 18px; cursor: pointer; box-shadow: 0 1px 4px rgba(0, 0, 0, .1); }\n[data-edrv-view] .edrv-addcfg:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .15)); color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-dapbar { position: absolute; top: 6px; right: 14px; z-index: 12; display: none; align-items: center; gap: 3px; padding: 3px 5px; border-radius: 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, #f7f9fa); box-shadow: 0 2px 8px rgba(0, 0, 0, .12); }\n[data-edrv-view] .edrv-dapbar.on { display: flex; border-color: var(--dsw-alias-border-l2, #cdd6da); }\n[data-edrv-view] .edrv-dapbar-grip { flex: 0 0 auto; width: 16px; height: 22px; display: inline-flex; align-items: center; justify-content: center; cursor: grab; user-select: none; color: var(--dsw-alias-label-tertiary, #7b8794); font-size: 12px; }\n[data-edrv-view] .edrv-dapbar-grip:active, [data-edrv-view] .edrv-dapbar.dragging .edrv-dapbar-grip { cursor: grabbing; }\n[data-edrv-view] .edrv-dapbar.dragging { user-select: none; }\n[data-edrv-view] .edrv-dapbar-cfg { max-width: 220px; height: 22px; font-size: 11px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); }\n[data-edrv-view] .edrv-dapbar-btn { min-width: 24px; height: 22px; padding: 0 6px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, inherit); font-size: 12px; line-height: 1; cursor: pointer; }\n[data-edrv-view] .edrv-dapbar-btn:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .15)); }\n[data-edrv-view] .edrv-dapbar-btn:disabled { opacity: .35; cursor: default; }\n[data-edrv-view] .edrv-dapbar-phase { padding: 0 4px; font-size: 11px; white-space: nowrap; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-dapbar-phase.paused { color: var(--dsw-alias-state-warn-primary, #b7791f); font-weight: 600; }\n[data-edrv-view] .edrv-dapbar-err { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; color: var(--dsw-alias-state-error-primary, #e05252); }\n\n/* 调试面板(活动栏「调试」页签):四段折叠区 + 断点列表 + REPL。 */\n[data-edrv-view] .edrv-dappanel { display: flex; flex-direction: column; gap: 10px; height: 100%; min-height: 0; overflow: auto; padding: 2px 2px 8px; }\n[data-edrv-view] .edrv-dappanel-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; row-gap: 4px; font-size: 12px; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-dappanel-phase { padding: 2px 8px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); }\n[data-edrv-view] .edrv-dappanel-phase.paused { background: var(--dsw-alias-state-warn-tertiary, rgba(229, 185, 63, .18)); color: var(--dsw-alias-state-warn-primary, #b7791f); font-weight: 600; }\n[data-edrv-view] .edrv-dappanel-cfg { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-dappanel-pid { font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; }\n[data-edrv-view] .edrv-dappanel-err { color: var(--dsw-alias-state-error-primary, #e05252); }\n[data-edrv-view] .edrv-dapseg { display: flex; flex-direction: column; gap: 2px; min-height: 0; }\n[data-edrv-view] .edrv-dapseg-body { min-height: 0; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; }\n/* 段间分隔条(splitview sash):拖动调整相邻两段高度占比;各段高度由 DebugPanel 内联接管。 */\n[data-edrv-view] .edrv-dapseg-sash { flex: 0 0 auto; height: 4px; margin: 2px 0; border-radius: 2px; background: transparent; cursor: row-resize; }\n[data-edrv-view] .edrv-dapseg-sash:hover, [data-edrv-view] .edrv-dapseg-sash.dragging { background: var(--dsw-alias-border-l2, #cdd6da); }\n/* 折叠段头:chevron + 标题 + 悬浮动作区(动作 hover 显隐;REPL 头不参与折叠)。 */\n[data-edrv-view] .edrv-dapseg-head { display: flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-tertiary, #7b8794); padding: 2px 2px; cursor: pointer; border-radius: 4px; }\n[data-edrv-view] .edrv-dapseg-head:hover { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-daprepl .edrv-dapseg-head { cursor: default; padding: 2px 0; }\n[data-edrv-view] .edrv-dapseg-chevron { flex: 0 0 auto; width: 14px; text-align: center; font-size: 10px; }\n[data-edrv-view] .edrv-dapseg-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-dapseg-act { flex: 0 0 auto; min-width: 20px; height: 18px; padding: 0 4px; border: none; border-radius: 4px; background: transparent; color: var(--dsw-alias-label-tertiary, #7b8794); font-size: 11px; line-height: 1; cursor: pointer; opacity: 0; }\n[data-edrv-view] .edrv-dapseg-head:hover .edrv-dapseg-act { opacity: 1; }\n[data-edrv-view] .edrv-dapseg-act:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .15)); color: var(--dsw-alias-label-primary, inherit); }\n[data-edrv-view] .edrv-dapempty { font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); padding: 4px 2px; }\n[data-edrv-view] .edrv-dapframe { display: flex; align-items: baseline; gap: 8px; padding: 3px 4px; border-radius: 6px; cursor: pointer; font-size: 12px; }\n[data-edrv-view] .edrv-dapframe:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12)); }\n[data-edrv-view] .edrv-dapframe-name { color: var(--dsw-alias-label-primary, inherit); font-weight: 600; }\n[data-edrv-view] .edrv-dapframe-loc { color: var(--dsw-alias-label-tertiary, #9aa5b1); font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-dapscope { font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-secondary, #52606d); padding: 4px 2px 1px; }\n[data-edrv-view] .edrv-dapvar { display: flex; align-items: baseline; gap: 6px; width: 100%; box-sizing: border-box; font-size: 12px; padding: 1px 2px; min-width: 0; }\n[data-edrv-view] .edrv-dapvar-tw { flex: 0 0 auto; width: 14px; border: none; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); cursor: pointer; font-size: 10px; padding: 0; }\n[data-edrv-view] span.edrv-dapvar-tw { cursor: default; }\n[data-edrv-view] .edrv-dapvar-name { flex: 0 1 34%; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-primary, inherit); font-family: var(--ds-font-family-code, ui-monospace, monospace); white-space: nowrap; }\n[data-edrv-view] .edrv-dapvar-val { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-dapvar-type { flex: 0 0 auto; font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-dapframe.selected { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .16)); outline: 1px solid var(--dsw-alias-border-l2, #cdd6da); }\n[data-edrv-view] .edrv-dapwatch { display: flex; align-items: baseline; gap: 6px; width: 100%; box-sizing: border-box; min-width: 0; font-size: 12px; padding: 1px 2px; }\n[data-edrv-view] .edrv-dapwatch-x { border: none; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); cursor: pointer; width: 14px; }\n[data-edrv-view] .edrv-dapwatch-x:hover { color: var(--dsw-alias-state-error-primary, #e05252); }\n[data-edrv-view] .edrv-dapwatch-add { padding: 2px 0; }\n[data-edrv-view] .edrv-dapbp { display: flex; align-items: center; gap: 6px; font-size: 12px; padding: 2px 2px; border-radius: 6px; cursor: pointer; }\n[data-edrv-view] .edrv-dapbp:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12)); }\n[data-edrv-view] .edrv-dapbp:active { background: var(--dsw-alias-interactive-bg-pressed, rgba(127, 127, 127, .2)); }\n[data-edrv-view] .edrv-dapbp-dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; background: var(--dsw-alias-state-error-primary, #e51400); }\n[data-edrv-view] .edrv-dapbp-dot.off { background: transparent; border: 1.5px solid var(--dsw-alias-label-tertiary, #9aa5b1); opacity: .6; }\n[data-edrv-view] .edrv-dapbp-file { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border: none; background: transparent; color: var(--dsw-alias-label-primary, inherit); cursor: pointer; padding: 0; font-size: 12px; }\n[data-edrv-view] .edrv-dapbp-dir { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; text-align: right; }\n[data-edrv-view] .edrv-dapbp-line { flex: 0 0 auto; font-size: 10px; padding: 1px 6px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-dapbp-x { flex: 0 0 auto; border: none; background: transparent; color: transparent; cursor: pointer; width: 14px; }\n[data-edrv-view] .edrv-dapbp:hover .edrv-dapbp-x { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-dapbp-x:hover { color: var(--dsw-alias-state-error-primary, #e05252) !important; }\n[data-edrv-view] .edrv-daprepl { display: flex; flex-direction: column; gap: 4px; }\n[data-edrv-view] .edrv-daprepl-log { display: flex; flex-direction: column; gap: 1px; max-height: 180px; overflow: auto; }\n[data-edrv-view] .edrv-daprepl-line { font-size: 11px; font-family: var(--ds-font-family-code, ui-monospace, monospace); color: var(--dsw-alias-label-secondary, #52606d); white-space: pre-wrap; word-break: break-all; display: flex; gap: 6px; }\n[data-edrv-view] .edrv-daprepl-line.out { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-daprepl-in { width: 100%; box-sizing: border-box; height: 26px; padding: 0 8px; font-size: 12px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); }\n[data-edrv-view] .edrv-dappanel-run { flex: 0 0 auto; width: 24px; height: 24px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, inherit); font-size: 13px; line-height: 1; cursor: pointer; }\n[data-edrv-view] .edrv-dappanel-run:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .15)); }\n[data-edrv-view] .edrv-dappanel-run:disabled { opacity: .35; cursor: default; background: transparent; }\n/* 面板顶部配置下拉(与编辑器浮动条 .edrv-dapbar-cfg 同视觉)。 */\n[data-edrv-view] .edrv-dappanel-cfg-select { max-width: 190px; height: 24px; padding: 0 4px; font-size: 11px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); }\n/* 断点行内编辑行(面板内展开编辑,控件复用 .edrv-bpwidget-* 视觉;左缩进对齐断点行文字列)。 */\n[data-edrv-view] .edrv-dapbp-edit { display: flex; align-items: center; gap: 6px; padding: 3px 2px 3px 20px; border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); }\n[data-edrv-view] .edrv-dapbp-edit .edrv-bpwidget-mode { height: 20px; }\n[data-edrv-view] .edrv-dapbp-edit .edrv-bpwidget-input { height: 20px; }\n/* 断点回执:未验证空心红圈(须排在 .off 之后,禁用态优先展示灰圈语义)。 */\n[data-edrv-view] .edrv-dapbp-dot.unverified { background: transparent; border: 1.5px solid var(--dsw-alias-state-error-primary, #e51400); opacity: .75; }\n/* 断点行内编辑浮层(Monaco content widget,锚定断点行上方;对齐 VS Code breakpointWidget 形态)。 */\n/* View Zone 浮层:宽度/左移由 BpWidget.syncWidth 按 layout 内联设置\n (Monaco .view-zones 仅内容区宽,需补偿 contentLeft 与滚动条),此处只管视觉。 */\n[data-edrv-view] .edrv-bpwidget { position: relative; box-sizing: border-box; display: flex; align-items: center; gap: 6px; padding: 3px 8px; border-top: 1px solid var(--dsw-alias-border-l2, #cdd6da); border-bottom: 1px solid var(--dsw-alias-border-l2, #cdd6da); background: var(--dsw-alias-bg-layer-1, #f7f9fa); }\n/* 断点编辑中的目标行高亮(浮层打开期间) */\n[data-edrv-view] .edrv-dap-editing-line { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12)); }\n[data-edrv-view] .edrv-bpwidget-mode { flex: 0 0 auto; height: 22px; padding: 0 4px; font-size: 12px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 3px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); cursor: pointer; }\n[data-edrv-view] .edrv-bpwidget-input { flex: 1 1 auto !important; min-width: 0; width: auto !important; box-sizing: border-box; height: 22px; padding: 0 6px; font-size: 12px; font-family: var(--ds-font-family-code, ui-monospace, monospace); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 3px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); }\n[data-edrv-view] .edrv-bpwidget-input:focus { outline: 1px solid var(--dsw-alias-border-l2, #cdd6da); }\n\n/* 断点 hover 预览(glyph 区):半透明小红点 + pointer 光标(对齐 VS Code .codicon-debug-hint)。 */\n[data-edrv-view] .edrv-dap-bp-hint { cursor: pointer; }\n[data-edrv-view] .edrv-dap-bp-hint::before { content: ''; display: block; width: 7px; height: 7px; margin: 6px auto 0; border-radius: 50%; background: var(--dsw-alias-state-error-primary, #e51400); opacity: .4; }\n/* 行号区也给出可点提示(与 VS Code 一致:glyph 区与行号区都可点/可 hover);\n 不拦截折叠箭头等自有控件的光标。 */\n[data-edrv-view] .edrv-editor-row .monaco-editor .margin-view-overlays .line-numbers:hover { cursor: pointer; }\n\n/* 暂停态 hover 调试块 + 变量树(成员全量直显 + table 成员折叠展开)。\n ⚠️ 选择器一律用 data-*:本仓 vendored Monaco 的 markdown→DOM 管线会剥掉我们 HTML 里的\n class(supportHtml/isTrusted 都不豁免),只有 data-* 存活;用 class 会静默失效。 */\n[data-edrv-view] [data-edrv-dap] { display: block; min-width: 0; }\n[data-edrv-view] [data-edrv-head] { display: flex; align-items: baseline; gap: 2px; }\n[data-edrv-view] [data-edrv-head] [data-edrv-expr] { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] [data-edrv-head] [data-edrv-val] { font-family: var(--ds-font-family-code, ui-monospace, monospace); word-break: break-all; }\n[data-edrv-view] [data-edrv-type] { margin-top: 2px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #8a949b); }\n[data-edrv-view] [data-edrv-tree] { margin: 2px 0 0; padding: 0; list-style: none; font-variant-numeric: tabular-nums; }\n[data-edrv-view] [data-edrv-line] { display: flex; align-items: baseline; gap: 4px; padding: 1px 2px; border-radius: 3px; white-space: nowrap; }\n[data-edrv-view] [data-edrv-line]:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12)); }\n/* 折叠按钮(对齐 CodeBuddy 调试浮窗变量树的 twist):热区 14px + hover 反馈,未可展开时留空占位 */\n[data-edrv-view] [data-edrv-twist] { flex: 0 0 auto; box-sizing: border-box; width: 14px; height: 14px; line-height: 13px; text-align: center; border-radius: 3px; color: var(--dsw-alias-label-tertiary, #8a949b); }\n[data-edrv-view] [data-edrv-twist]:not(:empty) { cursor: pointer; user-select: none; }\n[data-edrv-view] [data-edrv-twist]:not(:empty):hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .2)); color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] [data-edrv-name] { flex: 0 0 auto; color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] [data-edrv-line] [data-edrv-eq] { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary, #8a949b); }\n[data-edrv-view] [data-edrv-line] [data-edrv-val] { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] [data-edrv-note] { color: var(--dsw-alias-label-tertiary, #8a949b); font-style: italic; }\n/* Alt 提示层:由 hoverTree 注入到 .monaco-hover(滚动区之外)贴底常驻;容器预留同高内边距避免遮挡末行。\n 不再用 sticky / `:has()`:滚动链在 .monaco-hover-content 上,sticky 会被滚走。 */\n[data-edrv-view] .monaco-hover[data-edrv-hint-host=\"1\"] { padding-bottom: 22px; }\n[data-edrv-view] [data-edrv-hint=\"1\"] { position: absolute; left: 0; right: 0; bottom: 0; box-sizing: border-box; height: 22px; padding: 2px 8px; font-size: 11px; line-height: 18px; color: var(--dsw-alias-label-tertiary, #8a949b); background: var(--dsw-alias-bg-overlay, #ffffff); border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n\n/* Workbench panes: existing theme, action-driven drop previews. ddj 2026年10月14号 */\n[data-edrv-view] .edrv-group, [data-edrv-view] .edrv-group-body,\n[data-edrv-view] .edrv-full-slot, [data-edrv-view] .edrv-full-view,\n[data-edrv-view] .edrv-passive-view { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-group { border: 0; }\n[data-edrv-view] .edrv-group .edrv-tabs { flex: 0 0 auto; touch-action: none; }\n[data-edrv-view] .edrv-group-tab-name { overflow: hidden; text-overflow: ellipsis; max-width: 180px; }\n[data-edrv-view] .edrv-split { display: flex; flex: 1 1 auto; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-split-vertical { flex-direction: column; }\n[data-edrv-view] .edrv-split-child { display: flex; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-split-handle { flex: 0 0 5px; background: var(--dsw-alias-border-l1, #e0e6e8); cursor: col-resize; touch-action: none; }\n[data-edrv-view] .edrv-split-vertical > .edrv-split-handle { cursor: row-resize; }\n[data-edrv-view] .edrv-split-handle:hover, [data-edrv-view] .edrv-split-handle:focus-visible { background: var(--dsw-alias-state-info-primary, #4d6bfe); }\n[data-edrv-view].edrv-floating { position: fixed; display: flex; flex-direction: column; overflow: hidden; box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l2, #cdd6da); background: var(--dsw-alias-bg-base, #fff); color: var(--dsw-alias-label-primary, #1f2933); box-shadow: 0 8px 28px rgba(0,0,0,.22); }\n[data-edrv-view] .edrv-floating-title { display: flex; align-items: center; gap: 8px; flex: 0 0 32px; padding: 0 8px; background: var(--dsw-alias-bg-layer-1, #f7f9fa); cursor: move; touch-action: none; user-select: none; }\n[data-edrv-view] .edrv-floating-title > span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-floating-content { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; overflow: auto; box-sizing: border-box; }\n[data-edrv-view] .edrv-floating-content.edrv-svnlog-dialog { padding: 12px; overflow: auto; }\n[data-edrv-view] .edrv-floating-content.edrv-svn-patch-dialog,\n[data-edrv-view] .edrv-floating-content.edrv-svn-sum-dialog,\n[data-edrv-view] .edrv-floating-content.edrv-svnplan-dialog { padding: 12px; max-width: none; gap: 8px; }\n[data-edrv-view] .edrv-floating-content.edrv-svnlog-stats { max-height: none; }\n[data-edrv-view] .edrv-floating-content .edrv-svn-patch-pre,\n[data-edrv-view] .edrv-floating-content .edrv-svn-sum-list { flex: 1; min-height: 0; max-height: none; }\n[data-edrv-view] .edrv-floating-content .edrv-svnlog-filter,\n[data-edrv-view] .edrv-floating-content .edrv-svn-sum-form { flex-wrap: wrap; }\n[data-edrv-view] .edrv-floating-content > .edrv-svnlog-head,\n[data-edrv-view] .edrv-floating-content > .edrv-svnlog-filter,\n[data-edrv-view] .edrv-floating-content > .edrv-svnlog-foot { flex-shrink: 0; }\n[data-edrv-view] .edrv-floating-title > button { border: 0; background: transparent; color: inherit; cursor: pointer; }\n[data-edrv-view] .edrv-floating-grip { position: absolute; width: 16px; height: 16px; bottom: 0; right: 0; cursor: nwse-resize; touch-action: none; border-right: 3px solid var(--dsw-alias-border-l2, #cdd6da); border-bottom: 3px solid var(--dsw-alias-border-l2, #cdd6da); }\n.edrv-drag-overlay { position: fixed; z-index: 150; pointer-events: none; box-sizing: border-box; border: 0; background: color-mix(in srgb, var(--dsw-alias-state-info-primary, #4d6bfe) 25%, transparent); }\n.edrv-drop-label, .edrv-drag-ghost { position: fixed; z-index: 151; pointer-events: none; padding: 4px 8px; background: var(--dsw-alias-bg-overlay, #fff); color: var(--dsw-alias-label-primary, #1f2933); border: 1px solid var(--dsw-alias-border-l2, #cdd6da); font-size: 12px; white-space: nowrap; }\n.edrv-drop-label { position: absolute; left: 4px; top: 4px; }\n[data-edrv-view] .edrv-tab:focus-visible { outline: 2px solid var(--dsw-alias-state-info-primary, #4d6bfe); outline-offset: -2px; }\n@media (prefers-reduced-motion: reduce) { .edrv-drag-overlay { transition: none; } }\n";
59
- const tagId$3 = "dsh-vscode-mode/editor.css";
58
+ const css$4 = "/* dsh-vscode-mode 编辑区样式(原 ensureEdrvStyles 字符串,落盘为真 .css,由 tsdown CSS-inline 注入) */\n/* 主题:跟随官方 --dsw-alias-* 令牌(DSH 明暗与第三方皮肤生效)。\n 本作用域不再重定义官方令牌(旧实现曾在此硬覆盖浅色盘,导致插件不跟随 DSH 主题);\n 旧环境/缺令牌场景由每条声明的 var(--dsw-alias-*, 回落值) 兜底。\n @author ddj 2026-09-10 */\n[data-edrv-view] { box-sizing: border-box; --edrv-tab-h: 34px; }\n\n[data-edrv-view] .edrv-tabs { display: flex; align-items: center; gap: 2px; padding: 2px 8px 0; background: var(--dsw-alias-bg-layer-1, transparent); overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; flex-shrink: 0; overscroll-behavior-x: contain; } /* overscroll-behavior-x: 横滚到边界不把滚动链传给页面(滚轮横滚接管更可控)@author ddj 2026年09月18号 */\n[data-edrv-view] .edrv-tab { user-select: none; -webkit-user-select: none; display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 8px 0 12px; border: none; border-radius: 6px 6px 0 0; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 12px; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-tab.edrv-tab-active { background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); box-shadow: inset 0 -2px 0 var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-tab .edrv-tab-star { color: var(--dsw-alias-brand-primary, #4f8cff); font-size: 13px; font-weight: 700; line-height: 1; flex-shrink: 0; } /* 未保存修改星号 @author ddj 2026年09月09号 */\n[data-edrv-view] .edrv-tab .edrv-tab-x { appearance: none; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 16px; width: 16px; height: 16px; padding: 0; border: 0; border-radius: 4px; background: transparent; box-shadow: none; color: var(--dsw-alias-label-tertiary, #888); font-family: inherit; font-size: 11px; line-height: 1; cursor: pointer; }\n[data-edrv-view] .edrv-tab .edrv-tab-x:focus-visible { outline: 1px solid var(--dsw-alias-state-info-primary, #4d6bfe); outline-offset: 1px; }\n[data-edrv-view] .edrv-tab .edrv-tab-x:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.12)); color: var(--dsw-alias-label-primary, #eee); }\n/* 固定页签:📌 标记 + 免关闭按钮(固定页签不渲染 ×,仅可经右键菜单取消固定) */\n[data-edrv-view] .edrv-tab .edrv-tab-pin { flex-shrink: 0; font-size: 10px; line-height: 1; opacity: .85; }\n[data-edrv-view] .edrv-tab.edrv-tab-pinned { background: var(--dsw-alias-bg-layer-2, rgba(255,255,255,.04)); }\n/* 页签行 = 可横滚的页签区 + 行尾固定工具列(计划预览页签与磁盘页签同处这一行的滚动区)@author ddj 2026年10月20号 */\n[data-edrv-view] .edrv-tabs-row { display: flex; align-items: center; min-width: 0; flex: 0 0 auto; background: var(--dsw-alias-bg-layer-1, transparent); }\n/* 特异性需盖过下面 .edrv-group .edrv-tabs 的 flex: 0 0 auto,页签区才能吃掉行走宽、把工具列压到行尾 */\n[data-edrv-view] .edrv-group .edrv-tabs-row > .edrv-tabs { flex: 1 1 auto; min-width: 0; background: transparent; }\n[data-edrv-view] .edrv-tabs-tools { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; padding: 0 6px 0 2px; }\n[data-edrv-view] .edrv-tab-add { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; margin-left: 2px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 14px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab-add:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-pathbar { display: flex; align-items: center; gap: 8px; height: 26px; padding: 0 10px; background: var(--dsw-alias-bg-layer-1, #ffffff); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 12px; flex-shrink: 0; overflow: hidden; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-name { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-full { flex: 1; min-width: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; direction: rtl; text-align: left; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-meta { flex-shrink: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-status { color: var(--dsw-alias-label-secondary, #52606d); }\n/* 非焦点分组的静态路径条(GroupPathbar):可点击聚焦所属组,悬停给反馈 @author ddj 2026-10-16 */\n[data-edrv-view] .edrv-pathbar[data-edrv-pb-static] { cursor: pointer; }\n[data-edrv-view] .edrv-pathbar[data-edrv-pb-static]:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.06)); }\n[data-edrv-view] .edrv-pb-update { display: inline-flex; align-items: center; gap: 4px; height: 20px; flex: none; padding: 0 6px; border: 1px solid var(--dsw-alias-border-l1, #d9e0e4); border-radius: 4px; background: var(--dsw-alias-bg-layer-2, #f1f4f5); color: var(--dsw-alias-label-secondary, #485762); font-size: 11px; cursor: pointer; }\n[data-edrv-view] .edrv-pb-update:hover { background: var(--dsw-alias-interactive-bg-hover, #e7edf0); }\n[data-edrv-view] .edrv-pb-update:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #2869b4); outline-offset: 1px; }\n@media (max-width: 620px) { [data-edrv-view] .edrv-pb-update { width: 24px; padding: 0 4px; overflow: hidden; font-size: 0; } }\n[data-edrv-view] .edrv-path-input { flex: 1; min-width: 140px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-wrap { position: relative; flex-shrink: 0; display: flex; align-items: center; padding: 2px 8px 2px 4px; }\n[data-edrv-view] .edrv-search { width: 190px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-pop { position: absolute; top: calc(100% + 4px); right: 8px; width: min(460px, calc(100vw - 24px)); max-height: 300px; overflow: auto; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 10px; box-shadow: 0 6px 20px rgba(0,0,0,.4); z-index: 60; padding: 4px; }\n[data-edrv-view] .edrv-search-item { display: flex; flex-direction: column; gap: 1px; padding: 5px 8px; border-radius: 6px; cursor: pointer; }\n[data-edrv-view] .edrv-search-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.07)); }\n/* 键盘高亮项(↑↓ 选择):与 hover 同特异性但声明在后,故选中态稳定可辨 @author ddj 2026年09月11号 */\n[data-edrv-view] .edrv-search-item.edrv-search-sel { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.14)); }\n[data-edrv-view] .edrv-search-item .n { color: var(--dsw-alias-label-primary, #eee); font-size: 12px; word-break: break-all; }\n[data-edrv-view] .edrv-search-item .d { color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-search-empty { padding: 8px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #888); }\n[data-edrv-view] .edrv-btn-mini { font-size: 11px; padding: 2px 9px; border: none; border-radius: 999px; cursor: pointer; color: var(--dsw-alias-label-primary-inverted, #fff); font-weight: 600; white-space: nowrap; }\n[data-edrv-view] .edrv-btn-keep { background: var(--dsw-alias-state-success-primary, #2e9e44); }\n[data-edrv-view] .edrv-btn-undo { background: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-btn-mini:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-statusbar { display: flex; align-items: center; gap: 10px; padding: 3px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #333); background: var(--dsw-alias-bg-layer-1, transparent); font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); flex-shrink: 0; white-space: nowrap; overflow: hidden; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-path { flex: 1; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-lsp { flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-progress-message { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-statusbar .edrv-sp-progress { flex: 0 0 auto; min-width: 36px; text-align: right; font-variant-numeric: tabular-nums; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-lsp-status-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; background: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-lsp-status-dot.ready { background: var(--dsw-alias-state-success-primary, #0f9d58); }\n[data-edrv-view] .edrv-lsp-status-dot.busy { background: var(--dsw-alias-state-warn-primary, #d97706); animation: edrv-lsp-pulse 1.2s ease-in-out infinite; }\n@keyframes edrv-lsp-pulse { 50% { opacity: .45; } }\n/* Ctrl+hover 可导航标识符下划线提示(绑定 Monaco inlineClassName 装饰) */\n[data-edrv-view] .edrv-nav-underline { text-decoration: underline; text-decoration-color: var(--dsw-alias-interactive-primary, #4fc1ff); text-underline-offset: 1px; cursor: pointer; }\n/* 跳转目标区域高亮(LSP 定义/引用、搜索命中落点):落点一眼可见,约 1.2s 后由 EditorView 自动清除。\n 用官方令牌并带回落值,不在插件作用域重定义 --dsw-* (否则永久脱离官方主题)。 */\n[data-edrv-view] .edrv-nav-target { background: var(--dsw-alias-interactive-bg-hover, rgba(79,193,255,.28)); border-radius: 2px; box-shadow: 0 0 0 1px var(--dsw-alias-interactive-primary, rgba(79,193,255,.55)); }\n[data-edrv-view] .edrv-diffchip { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-state-warn-primary, #b7791f); cursor: pointer; white-space: nowrap; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn { font-size: 12px; padding: 2px 6px; border: none; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; border-radius: 4px; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-chip-btn:disabled { opacity: .35; cursor: default; }\n[data-edrv-view] .edrv-chip-btn:disabled:hover { background: transparent; }\n/* 行内和原生 Monaco diff 共用主题色盘;不再借用普通 hover 的灰底。 */\n[data-edrv-view] .edrv-mn-add-line { background: var(--edrv-diff-add, #e6f4e8); }\n[data-edrv-view] .edrv-mn-gutter-add { background: var(--edrv-diff-add, #e6f4e8); color: var(--dsw-alias-state-success-primary, #0f9d58) !important; display: flex !important; align-items: center; justify-content: center; }\n[data-edrv-view] .edrv-mn-gutter-add::before { content: \"+\"; }\n[data-edrv-view] .edrv-del-zone, [data-edrv-view] .edrv-del-margin { background: var(--edrv-diff-del, #fbe7e3); color: var(--edrv-diff-fg, #1e1e1e); }\n[data-edrv-view] .edrv-del-row { white-space: pre; height: var(--edrv-line-height, 20px); line-height: var(--edrv-line-height, 20px); }\n[data-edrv-view] .edrv-del-text { display: inline-block; white-space: pre; }\n[data-edrv-view] .edrv-del-margin-row { position: relative; height: var(--edrv-line-height, 20px); line-height: var(--edrv-line-height, 20px); }\n[data-edrv-view] .edrv-del-number { position: absolute; left: var(--edrv-number-left); width: var(--edrv-number-width); color: var(--edrv-diff-number, #6c6c6c); text-align: right; }\n[data-edrv-view] .edrv-del-marker { position: absolute; left: var(--edrv-marker-left); width: var(--edrv-marker-width); color: var(--dsw-alias-state-error-primary, #d9534f); text-align: center; }\n[data-edrv-view] .edrv-monaco-host { flex: 1 1 auto; min-width: 0; min-height: 0; position: relative; overflow: hidden; }\n[data-edrv-view] .edrv-monaco-host > div { position: absolute; inset: 0; min-width: 0; min-height: 0; }\n[data-edrv-view] .edrv-imgview { flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }\n[data-edrv-view] .edrv-imgview-bar { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, transparent); }\n[data-edrv-view] .edrv-imgview-meta { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; }\n/* 图片预览:居中交给子项 margin:auto(auto 外边距优先吃正向空闲空间;溢出时空闲为负 → 外边距归 0、\n flex-start 起点对齐 → 放大后四边均可滚动到达,规避 justify-content:center 双侧溢出不可达问题) */\n[data-edrv-view] .edrv-imgview-stage { flex: 1 1 auto; min-height: 0; display: flex; overflow: auto; padding: 14px; background-color: var(--dsw-alias-bg-layer-1, #f0f4f4); background-image: conic-gradient(rgba(128,128,128,.14) 25%, transparent 0 50%, rgba(128,128,128,.14) 0 75%, transparent 0); background-size: 16px 16px; }\n[data-edrv-view] .edrv-imgview-img { max-width: 100%; max-height: 100%; flex-shrink: 0; margin: auto; align-self: flex-start; object-fit: contain; border-radius: 4px; box-shadow: 0 2px 12px rgba(31,41,51,.18); }\n[data-edrv-view] .edrv-imgview-broken { display: flex; flex-direction: column; align-items: center; gap: 10px; font-size: 13px; color: var(--dsw-alias-label-tertiary, #777); margin: auto; }\n[data-edrv-view] .edrv-pdf-host { flex: 1 1 auto; min-width: 0; min-height: 0; position: relative; overflow: hidden; }\n[data-edrv-view] .edrv-pdf-host > .edrv-pdf { position: absolute; inset: 0; }\n[data-edrv-view] .edrv-pdf { min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }\n[data-edrv-view] .edrv-pdf-bar { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, transparent); }\n[data-edrv-view] .edrv-pdf-page { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; min-width: 52px; text-align: center; }\n[data-edrv-view] .edrv-pdf-zoom { min-width: 26px; text-align: center; padding: 3px 7px; }\n[data-edrv-view] .edrv-pdf-sep { width: 1px; height: 16px; background: var(--dsw-alias-border-l1, #e0e6e8); margin: 0 2px; }\n[data-edrv-view] .edrv-pdf-mode-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-brand-primary, #0f9d58); border-color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-pdf-dirty { color: var(--dsw-alias-state-warn-primary, #d97706); border-color: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-pdf-stage { position: absolute; top: 34px; left: 0; right: 0; bottom: 0; overflow: auto; }\n/* Markdown 预览(需求 4):工具条 + 居中阅读栏;正文排版由官方 MarkdownText 自带的 --dsw-* 样式负责 */\n[data-edrv-view] .edrv-mdview { flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }\n[data-edrv-view] .edrv-mdview-bar { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, transparent); }\n[data-edrv-view] .edrv-mdview-meta { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; }\n[data-edrv-view] .edrv-mdview-stage { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 14px 20px 28px; }\n[data-edrv-view] .edrv-mdview-doc { max-width: 880px; margin: 0 auto; width: 100%; }\n[data-edrv-view] .edrv-mdview-fallback { font-size: 12px; line-height: 1.7; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-mdview-pre { margin: 8px 0 0; padding: 10px 12px; border-radius: 8px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-primary, #1f2933); font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; line-height: 1.6; white-space: pre-wrap; word-break: break-word; }\n[data-edrv-view] .edrv-pb-mdbtn { height: 20px; padding: 0 8px; font-size: 11px; flex-shrink: 0; }\n[data-edrv-view] .edrv-pdf-stage .edrv-pdf-viewer { margin: 0 auto; }\n[data-edrv-view] .edrv-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; height: 100%; color: var(--dsw-alias-label-tertiary, #777); font-size: 13px; }\n[data-edrv-view] .edrv-diffbar { position: absolute; left: 0; right: 0; bottom: 10px; margin: 0 auto; z-index: 25; width: fit-content; max-width: calc(100% - 20px); border-radius: 12px; background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, #f0f4f4)); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); box-shadow: none; overflow: visible; display: flex; flex-direction: column; }\n[data-edrv-view] .edrv-editor-area > .edrv-diffbar { max-height: calc(100% - 20px); }\n[data-edrv-view] .edrv-diffbar-row { display: flex; align-items: center; gap: 6px; height: 36px; padding: 4px 5px 4px 12px; flex-wrap: nowrap; }\n[data-edrv-view] .edrv-diffbar-count { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); min-width: 42px; text-align: center; white-space: nowrap; cursor: pointer; }\n[data-edrv-view] .edrv-diffbar-count.edrv-count-file { min-width: 64px; cursor: default; }\n[data-edrv-view] .edrv-diffbar-file { flex: 1; min-width: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-pill { font-size: 11px; padding: 3px 10px; border: none; border-radius: 999px; cursor: pointer; white-space: nowrap; font-weight: 600; }\n[data-edrv-view] .edrv-pill-keep { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-state-success-primary, #0f7a45); border: 1px solid var(--dsw-alias-state-success-primary, rgba(15,157,88,.4)); }\n[data-edrv-view] .edrv-pill-undo { background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.14)); color: var(--dsw-alias-state-error-primary, #c0392b); border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); }\n[data-edrv-view] .edrv-pill-ghost { background: var(--dsw-alias-bg-layer-1, #ffffff); color: var(--dsw-alias-label-secondary, #52606d); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-pill:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-diffbar-menu { position: absolute; right: 8px; bottom: calc(100% + 6px); z-index: 45; min-width: 160px; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 6px 20px rgba(31,41,51,.16); padding: 4px; display: flex; flex-direction: column; gap: 2px; }\n[data-edrv-view] .edrv-diffmenu-item { display: flex; align-items: center; gap: 8px; font-size: 12px; padding: 6px 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; text-align: left; }\n[data-edrv-view] .edrv-diffmenu-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-diffmenu-item.danger { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-diffmenu-sep { height: 1px; margin: 2px 4px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n/* 编辑区 / Tab 右键菜单(页签右键菜单 + 文件树右键菜单共用)。\n 页签菜单 11 条 + 键位提示,故限高可滚(ContextMenu 以实测尺寸做 viewport clamp)。\n ⚠️ z-index 71 在既有浮层阶梯内(见文件末尾命令栏注释),不得上调越过命令栏/片段浮窗。 */\n[data-edrv-view] .edrv-ctxmenu { position: fixed; z-index: 71; min-width: 220px; max-width: 360px; max-height: min(70vh, 520px); overflow-y: auto; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 8px 26px rgba(31,41,51,.2); padding: 4px; display: flex; flex-direction: column; gap: 1px; }\n[data-edrv-view] .edrv-ctxmenu-item { display: flex; align-items: center; gap: 8px; font-size: 12px; line-height: 18px; padding: 7px 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-ctxmenu-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-ctxmenu-item.edrv-ctxmenu-danger { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-ctxmenu-item:disabled,\n[data-edrv-view] .edrv-ctxmenu-item.edrv-ctxmenu-disabled { color: var(--dsw-alias-label-disabled, #b0b7c0); cursor: default; background: transparent; }\n[data-edrv-view] .edrv-ctxmenu-sep { height: 1px; margin: 3px 4px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n/* 菜单项文案(弹性占宽,超出省略)与右侧键位提示(不缩放,等宽字体) */\n[data-edrv-view] .edrv-ctxmenu-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-ctxmenu-hint { flex: 0 0 auto; padding-left: 18px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; }\n[data-edrv-view] .edrv-ctxmenu-item:disabled .edrv-ctxmenu-hint,\n[data-edrv-view] .edrv-ctxmenu-item.edrv-ctxmenu-disabled .edrv-ctxmenu-hint { color: var(--dsw-alias-label-disabled, #b0b7c0); }\n[data-edrv-view] .edrv-diffbar-body { max-height: 130px; max-width: min(560px, calc(100vw - 40px)); overflow: auto; padding: 4px 8px 8px; display: flex; flex-direction: column; gap: 4px; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-diffrow { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); cursor: pointer; flex-wrap: wrap; }\n[data-edrv-view] .edrv-diffrow:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-diffrow-l { font-size: 11px; color: var(--dsw-alias-label-primary, #1f2933); min-width: 74px; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-o { font-size: 11px; color: var(--dsw-alias-state-error-primary, #d9534f); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-n { font-size: 11px; color: var(--dsw-alias-state-success-primary, #0f9d58); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-tag { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-diffrow-stale { border-color: var(--dsw-alias-state-warn-primary, rgba(217,119,6,.5)); cursor: default; }\n[data-edrv-view] .edrv-diffbox-others { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: 4px 2px 0; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); margin-top: 2px; }\n[data-edrv-view] .edrv-diffrow-file { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); cursor: pointer; white-space: nowrap; max-width: 200px; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-diffrow-file:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n/* 编辑区 hover 差异块的 Keep/Undo 浮层 */\n[data-edrv-view] .edrv-hoveract { position: absolute; z-index: 30; display: flex; align-items: center; gap: 4px; padding: 2px 6px; border-radius: 8px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 4px 14px rgba(31,41,51,.14); }\n[data-edrv-view] .edrv-launch { position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 40; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); box-shadow: 0 6px 24px rgba(0,0,0,.5); display: flex; flex-direction: column; max-height: 60%; overflow: hidden; }\n[data-edrv-view] .edrv-launch-head { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-bottom: 1px solid var(--dsw-alias-border-l1, #333); }\n[data-edrv-view] .edrv-launch-tab { font-size: 12px; padding: 3px 12px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; }\n[data-edrv-view] .edrv-launch-tab.on { background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-label-primary, #eee); font-weight: 600; }\n[data-edrv-view] .edrv-launch-body { flex: 1; min-height: 0; overflow: auto; padding: 6px 8px; display: flex; flex-direction: column; gap: 4px; }\n[data-edrv-view] .edrv-launch-row { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 6px; cursor: pointer; flex-wrap: wrap; }\n[data-edrv-view] .edrv-launch-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-launch-path { flex: 1; min-width: 0; font-size: 12px; color: var(--dsw-alias-label-primary, #eee); word-break: break-all; }\n[data-edrv-view] .edrv-launch-cnt { font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); white-space: nowrap; }\n[data-edrv-view] .edrv-launch-arch { display: flex; flex-direction: column; gap: 6px; }\n[data-edrv-view] .monaco-editor { overflow: hidden !important; }\n[data-edrv-view] .monaco-editor .sticky-widget { z-index: 30 !important; background: var(--dsw-alias-bg-base, #f8f8f8) !important; }\n[data-edrv-view] .monaco-editor .sticky-widget .sticky-line-content,\n[data-edrv-view] .monaco-editor .sticky-widget .sticky-line-number { background: var(--dsw-alias-bg-base, #f8f8f8) !important; }\n[data-edrv-view] .monaco-editor, [data-edrv-view] .monaco-editor .margin, [data-edrv-view] .monaco-editor-background { background: transparent !important; }\n[data-edrv-view] .monaco-editor .scrollbar .slider { background: rgba(128,128,128,.3) !important; }\n[data-edrv-view] .edrv-loading { gap: 8px; padding: 20px; }\n[data-edrv-view] .edrv-loading-title { color: var(--dsw-alias-label-primary, #1f2933); font-size: 13px; }\n[data-edrv-view] .edrv-progress { width: min(360px, 72%); height: 6px; overflow: hidden; border-radius: 999px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-progress-value { height: 100%; border-radius: inherit; background: var(--dsw-alias-brand-primary, #0f9d58); transition: width .24s ease; }\n[data-edrv-view] .edrv-loading-percent { color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; font-variant-numeric: tabular-nums; }\n@media (prefers-reduced-motion: reduce) {\n [data-edrv-view] .edrv-progress-value { transition: none; }\n}\n\n/* ===== 唯一会话级差异 dock(遵循 DSH todo/queue 原生布局) ===== */\n[data-edrv-diff-dock-shell] {\n box-sizing: border-box;\n width: calc(100% - var(--dsh-composer-side-clearance) - var(--dsh-composer-side-clearance) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));\n max-width: calc(var(--dsh-composer-card-max-width) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));\n margin: 0 auto;\n padding: 0;\n flex: none;\n border: 1px solid var(--dsw-alias-border-l1, #e0e6e8);\n border-radius: 12px;\n background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, transparent));\n box-shadow: none;\n overflow: hidden;\n}\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock {\n box-sizing: border-box;\n position: relative;\n left: auto;\n right: auto;\n bottom: auto;\n z-index: auto;\n width: 100%;\n max-width: none;\n margin: 0;\n display: flex;\n flex-direction: column;\n border: none;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n overflow: visible;\n}\n[data-edrv-diff-dock-shell] .edrv-diffbar-row { min-height: 36px; padding: 6px 12px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-row { min-height: 36px; padding: 6px 12px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-body { max-height: 180px; overflow: auto; padding: 6px 12px 10px; border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-button { box-sizing: border-box; display: flex; align-items: center; justify-content: center; width: 100%; height: 24px; border: none; border-radius: 8px; padding: 0 12px; background: transparent; color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 500; line-height: 24px; text-align: center; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-button:hover { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diffbar-count { display: inline-flex; align-items: center; color: var(--dsw-alias-label-primary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 500; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-file { display: inline-flex; align-items: center; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-editor-row { display: flex; align-items: center; height: 36px; min-height: 36px; padding: 4px 12px; overflow-x: auto; scrollbar-width: thin; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-editor-content { display: flex; align-items: center; gap: 10px; width: 100%; min-width: max-content; min-height: 28px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-nav,\n[data-edrv-diff-dock-shell] .edrv-diffbar-files,\n[data-edrv-diff-dock-shell] .edrv-diffbar-actions { display: flex; align-items: center; gap: 6px; flex: none; height: 28px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-actions { margin-left: auto; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-details-toggle { display: inline-flex; align-items: center; justify-content: center; width: 28px; min-width: 28px; padding: 0; font-size: 13px; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-details-toggle svg { width: 14px; height: 14px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-files { min-width: 0; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-files .edrv-diffbar-file { min-width: 72px; max-width: 240px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-progress { min-width: 0; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; font-variant-numeric: tabular-nums; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-summary { min-width: 0; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-pill { box-sizing: border-box; height: 28px; padding: 0 9px; border: none; border-radius: 999px; cursor: pointer; white-space: nowrap; font-size: 11px; line-height: 26px; font-weight: 600; }\n[data-edrv-diff-dock-shell] .edrv-pill-keep { background: var(--dsw-alias-state-success-tertiary); color: var(--dsw-alias-state-success-primary); border: 1px solid var(--dsw-alias-state-success-secondary); }\n[data-edrv-diff-dock-shell] .edrv-pill-undo { background: var(--dsw-alias-state-error-tertiary); color: var(--dsw-alias-state-error-primary); border: 1px solid var(--dsw-alias-state-error-secondary); }\n[data-edrv-diff-dock-shell] .edrv-pill-ghost { background: transparent; color: var(--dsw-alias-label-secondary); border: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-pill:hover:not(:disabled) { background-color: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-pill:focus-visible { outline: 2px solid var(--dsw-alias-label-tertiary); outline-offset: -2px; }\n[data-edrv-diff-dock-shell] .edrv-pill:disabled { opacity: .45; cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diffrow { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; background: transparent; cursor: pointer; flex-wrap: wrap; }\n[data-edrv-diff-dock-shell] .edrv-diffrow:hover { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-l { color: var(--dsw-alias-label-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-o { color: var(--dsw-alias-state-error-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-n { color: var(--dsw-alias-state-success-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-tag { color: var(--dsw-alias-label-tertiary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-stale { border-color: var(--dsw-alias-state-warn-secondary); cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diffbox-others { border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-file { border-color: var(--dsw-alias-border-l1); background: transparent; color: var(--dsw-alias-label-secondary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-file:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }\n[data-edrv-diff-dock-shell] .edrv-diff-details { display: flex; flex-direction: column; gap: 2px; padding: 6px 12px 8px; border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item { display: flex; align-items: center; width: 100%; min-height: 28px; gap: 8px; padding: 4px 8px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary); cursor: pointer; text-align: left; font-size: 12px; line-height: 20px; }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item.danger { color: var(--dsw-alias-state-error-primary); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item:disabled { opacity: .45; cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-sep { height: 1px; margin: 2px 4px; background: var(--dsw-alias-border-l1); }\n@media (max-width: 620px) {\n [data-edrv-diff-dock-shell] .edrv-diffbar-editor-content { width: max-content; }\n [data-edrv-diff-dock-shell] .edrv-diffbar-files .edrv-diffbar-file { max-width: 160px; }\n}\n/* 侧栏差异栏在编辑内容底部居中悬浮,按内容宽度收缩;窄栏只在工具条内横滚。@author ddj 2026年09月29号 */\n[data-edrv-view] .edrv-side-diff-dock[data-edrv-diff-dock-shell] { position: absolute; left: 50%; bottom: 28px; z-index: 25; width: max-content; max-width: calc(100% - 20px); max-height: min(300px, calc(100% - 38px)); margin: 0; transform: translateX(-50%); border-radius: 12px; background: var(--dsw-alias-bg-overlay, #fff); box-shadow: 0 6px 20px rgba(0,0,0,.18); overflow-x: hidden; overflow-y: auto; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-editor-row { padding-inline: 8px; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-editor-content { gap: 5px; width: max-content; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-nav,\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-files,\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-actions { gap: 3px; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-pill { padding-inline: 5px; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-files .edrv-diffbar-file { min-width: 0; max-width: 72px; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-details-toggle { width: 26px; min-width: 26px; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diffbar-body { max-height: min(180px, 25vh); max-width: 100%; }\n[data-edrv-view] .edrv-side-diff-dock .edrv-diff-details { max-height: min(260px, 35vh); overflow-y: auto; }\n\n/* Official review: file context above, navigation/actions below. Theme-owned surfaces; no neon status outlines. @author ddj 2026年10月27号 */\n[data-edrv-diff-dock-shell] .edrv-reviewbar {\n width: 520px; max-width: 100%; min-width: 0; box-sizing: border-box;\n color: var(--dsw-alias-label-primary, #26313d); font-family: var(--dsw-font-family, sans-serif);\n font-size: 12px; line-height: 1.4; container: edrv-review / inline-size;\n}\n[data-edrv-diff-dock-shell] .edrv-review-global { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px; padding: 6px 8px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e5e9ef); }\n[data-edrv-diff-dock-shell] .edrv-review-all-actions { display: flex; align-items: center; gap: 4px; }\n[data-edrv-diff-dock-shell] .edrv-review-total strong { font-variant-numeric: tabular-nums; }\n[data-edrv-diff-dock-shell] .edrv-review-heading {\n display: flex; align-items: center; gap: 8px; min-width: 0; padding: 10px 12px 8px;\n border-bottom: 1px solid var(--dsw-alias-border-l1, #e5e9ef);\n}\n[data-edrv-diff-dock-shell] .edrv-review-filemark { display: inline-flex; flex: none; color: var(--dsw-alias-label-tertiary, #697586); }\n[data-edrv-diff-dock-shell] .edrv-review-filename { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 600; }\n[data-edrv-diff-dock-shell] .edrv-review-pending { flex: none; display: inline-flex; gap: 5px; align-items: center; color: var(--dsw-alias-label-secondary, #697586); font-size: 11px; white-space: nowrap; }\n[data-edrv-diff-dock-shell] .edrv-review-pending strong { font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--dsw-alias-label-primary, #26313d); }\n[data-edrv-diff-dock-shell] .edrv-review-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 7px 8px; }\n[data-edrv-diff-dock-shell] .edrv-review-nav { display: flex; align-items: center; flex: none; gap: 1px; }\n[data-edrv-diff-dock-shell] .edrv-review-blocknav { border-left: 1px solid var(--dsw-alias-border-l1, #e5e9ef); padding-left: 6px; }\n[data-edrv-diff-dock-shell] .edrv-review-position { min-width: 32px; text-align: center; font-size: 11px; font-variant-numeric: tabular-nums; color: var(--dsw-alias-label-secondary, #697586); }\n[data-edrv-diff-dock-shell] .edrv-review-actions { display: flex; align-items: center; flex: none; gap: 4px; margin-left: auto; }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button { height: 28px; min-height: 28px; padding: 0 8px; border-radius: 6px; font-family: inherit; font-size: 12px; font-weight: 500; white-space: nowrap; gap: 5px; }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button.edrv-review-icon { width: 28px; min-width: 28px; padding: 0; }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button.edrv-review-record { padding: 0 6px; font-size: 11px; color: var(--dsw-alias-label-secondary, #697586); }\n[data-edrv-diff-dock-shell] .edrv-review-record svg { width: 12px; height: 12px; }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button.edrv-review-keep { background: var(--dsw-alias-brand-primary, #3b63d9); color: var(--dsw-alias-label-primary-inverted, #fff); border: 1px solid transparent; }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button.edrv-review-keep:hover:not(:disabled) { filter: brightness(.94); }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button.edrv-review-undo:hover:not(:disabled) { color: var(--dsw-alias-state-error-primary, #b64043); }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button:disabled { opacity: .4; cursor: not-allowed; }\n[data-edrv-diff-dock-shell] .edrv-reviewbar button:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #3b63d9); outline-offset: 2px; }\n[data-edrv-diff-dock-shell] .edrv-review-notice { padding: 7px 12px; border-top: 1px solid var(--dsw-alias-border-l1, #e5e9ef); color: var(--dsw-alias-label-secondary, #697586); font-size: 11px; overflow-wrap: anywhere; }\n[data-edrv-diff-dock-shell] .edrv-review-error { color: var(--dsw-alias-state-error-primary, #b64043); }\n[data-edrv-diff-dock-shell] .edrv-review-notice-warn { color: var(--dsw-alias-state-warning-primary, #a86a1f); }\n[data-edrv-diff-dock-shell] .edrv-review-source { flex: none; padding: 1px 6px; border-radius: 999px; border: 1px solid var(--dsw-alias-border-secondary, #d4dae3); color: var(--dsw-alias-label-secondary, #697586); font-size: 11px; line-height: 16px; white-space: nowrap; }\n[data-edrv-diff-dock-shell] .edrv-sidecar-down { display: flex; align-items: center; gap: 8px; }\n[data-edrv-diff-dock-shell] .edrv-sidecar-down-text { flex: 1; min-width: 0; }\n[data-edrv-diff-dock-shell] .edrv-review-body { border-top: 1px solid var(--dsw-alias-border-l1, #e5e9ef); max-height: min(240px, 30vh); overflow: auto; padding: 8px 12px; }\n@container edrv-review (max-width: 420px) {\n .edrv-reviewbar .edrv-review-heading { gap: 6px; padding-inline: 10px; }\n .edrv-reviewbar .edrv-review-actions { justify-content: flex-end; flex: 1 0 100%; border-top: 1px solid var(--dsw-alias-border-l1, #e5e9ef); padding-top: 6px; }\n}\n\n/* ===== 侧边栏(活动栏 + 面板区 + 文件树) ===== */\n[data-edrv-view] .edrv-editor-row { display: flex; flex: 1 1 auto; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-main-col { box-sizing: border-box; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-main-col > .edrv-pathbar,\n[data-edrv-view] .edrv-main-col > .edrv-tabs { flex: 0 0 auto; }\n[data-edrv-view] .edrv-editor-area { position: relative; display: flex; flex: 1 1 auto; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-sidebar { display: flex; flex-direction: row; flex-shrink: 1; min-width: 0; height: 100%; background: var(--dsw-alias-bg-layer-1, #ffffff); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); overflow: hidden; }\n[data-edrv-view] .edrv-rail { display: flex; flex-direction: column; align-items: center; width: 44px; flex-shrink: 0; padding: 6px 0; background: var(--dsw-alias-bg-layer-2, #f0f4f4); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); gap: 4px; }\n[data-edrv-view] .edrv-rail-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: none; border-radius: 8px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rail-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rail-btn.edrv-rail-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); box-shadow: inset 2px 0 0 var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-rail-icon { font-size: 15px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; }\n/* 树行图标:官方原语(文件夹/文件类型),尺寸与行高对齐 @author ddj 2026-09-10 */\n[data-edrv-view] .edrv-tree-icon { flex-shrink: 0; display: inline-flex; align-items: center; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-active .edrv-tree-icon { color: var(--dsw-alias-label-secondary, #52606d); }\n/* SVN overlays: official file icon remains visible, independent local/remote signs. ddj 2026-10-09 */\n[data-edrv-view] .edrv-tree-iconbox { position: relative; display: inline-flex; width: 20px; height: 20px; align-items: center; flex-shrink: 0; }\n[data-edrv-view] .edrv-svn-overlay { position: absolute; inset: 0; }\n[data-edrv-view] .edrv-svn-local { position: absolute; right: -1px; bottom: -2px; display: flex; border-radius: 50%; box-shadow: 0 0 0 1px var(--dsw-alias-bg-layer-1, #fff); }\n[data-edrv-view] .edrv-svn-icon-clean { color: #16853b; }\n[data-edrv-view] .edrv-svn-icon-modified, [data-edrv-view] .edrv-svn-icon-deleted { color: #d12e35; }\n[data-edrv-view] .edrv-svn-icon-conflict { color: #c27300; }\n[data-edrv-view] .edrv-svn-icon-added { color: #2465c9; }\n[data-edrv-view] .edrv-svn-icon-unversioned, [data-edrv-view] .edrv-svn-icon-ignored, [data-edrv-view] .edrv-svn-icon-external { color: #75808c; }\n[data-edrv-view] .edrv-svn-remote { position: absolute; right: -3px; top: -4px; color: #2465c9; background: var(--dsw-alias-bg-layer-1, #fff); border-radius: 3px; font-size: 11px; font-weight: 900; line-height: 12px; }\n[data-edrv-view] .edrv-rail-badge { position: absolute; top: 2px; right: 2px; min-width: 14px; height: 14px; padding: 0 3px; border-radius: 999px; background: var(--dsw-alias-brand-primary, #0f9d58); color: var(--dsw-alias-label-primary-inverted, #fff); font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; }\n[data-edrv-view] .edrv-side-body { flex: 1; min-width: 0; min-height: 0; display: flex; overflow: hidden; }\n[data-edrv-view] .edrv-side-panel { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; position: relative; }\n[data-edrv-view] .edrv-side-head { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px 0 12px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); flex-shrink: 0; }\n[data-edrv-view] .edrv-side-title { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-side-root { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-side-btn { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-side-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-tree { flex: 1; min-height: 0; overflow: auto; padding: 4px 6px 12px; font-size: 12px; }\n/* Virtual rows and placeholders share the same geometry as treeWindow.ts. @author ddj 2026年10月22号 */\n[data-edrv-view] .edrv-tree.edrv-tree-virtual { padding-top: 0; padding-bottom: 0; }\n[data-edrv-view] .edrv-tree-virtual .edrv-tree-row,\n[data-edrv-view] .edrv-tree-virtual .edrv-tree-loading { height: 24px; min-height: 24px; box-sizing: border-box; }\n[data-edrv-view] .edrv-tree-virtual .edrv-tree-loading { line-height: 24px; }\n[data-edrv-view] .edrv-large-text { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }\n[data-edrv-view] .edrv-large-text-toolbar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }\n[data-edrv-view] .edrv-large-text-content { margin: 0; flex: 1; min-height: 0; overflow: auto; white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); user-select: text; }\n[data-edrv-view] .edrv-tree-row { display: flex; align-items: center; gap: 4px; height: 22px; padding-right: 6px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-tree-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-active { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-context { box-shadow: inset 0 0 0 1px var(--dsw-alias-brand-primary, #0f9d58); background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); }\n/* 「在资源管理器视图中显示」的定位高亮(短时淡出,区别于常驻的 active 态) */\n[data-edrv-view] .edrv-tree-row.edrv-tree-reveal { box-shadow: inset 0 0 0 1px var(--dsw-alias-brand-primary, #0f9d58); background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.18)); animation: edrv-tree-reveal-fade 2s ease-out; }\n@keyframes edrv-tree-reveal-fade { from { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.32)); } to { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.18)); } }\n[data-edrv-view] .edrv-tree-chev { width: 12px; flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; text-align: center; }\n[data-edrv-view] .edrv-tree-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-tree-name.edrv-tree-dim { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-tree-badge { min-width: 14px; height: 14px; padding: 0 4px; border-radius: 999px; background: var(--dsw-alias-state-warn-primary, #d97706); color: var(--dsw-alias-label-primary-inverted, #fff); font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; flex-shrink: 0; }\n[data-edrv-view] .edrv-tree-loading { height: 22px; line-height: 22px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n/* SVN 状态独立占位:换行不覆盖文件树,长错误在栏内滚动。@author ddj 2026-10-12 */\n[data-edrv-view] .edrv-tree-status { flex: 0 0 auto; min-width: 0; max-height: 96px; overflow: auto; padding: 4px 8px; box-sizing: border-box; line-height: 18px; white-space: normal; overflow-wrap: anywhere; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-tree-error { display: flex; align-items: center; gap: 8px; margin: 4px 2px 6px; padding: 6px 8px; border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n/* 大纲面板:kind 字形 / detail / 行号 */\n[data-edrv-view] .edrv-outline-kind { display: inline-flex; align-items: center; justify-content: center; width: 14px; flex-shrink: 0; font-size: 12px; font-weight: 600; text-align: center; }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-func { color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-type { color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-data { color: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-ns { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-key { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-outline-detail { flex-shrink: 0; max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-outline-ln { flex-shrink: 0; padding-right: 2px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-side-resize { width: 5px; flex-shrink: 0; cursor: col-resize; background: transparent; }\n[data-edrv-view] .edrv-side-resize:hover { background: var(--dsw-alias-brand-primary, #0f9d58); opacity: .35; }\n[data-edrv-view] .edrv-chip-btn.edrv-chip-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); }\n\n/* ===== SVN 变更面板(工作副本状态列表;颜色分级对齐 TortoiseSVN 口径) @author ddj 2026-09-16 ===== */\n[data-edrv-view] .edrv-svn-toolbar { display: flex; align-items: center; gap: 12px; padding: 4px 10px 6px 12px; flex-shrink: 0; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-svn-check { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-svn-check input { margin: 0; cursor: pointer; }\n[data-edrv-view] .edrv-svn-bulk { display: flex; align-items: center; gap: 6px; padding: 0 10px 6px 12px; flex-shrink: 0; }\n[data-edrv-view] .edrv-svn-count { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n/* AI 助手 dock(12-panel-ai-bar 设计定稿):manifest 分带之 assistant 带,accent 左竖轨为唯一记忆点。\n AI 线程色三处同轨:竖轨 / dock 按钮描边 / 弹窗进度条(--dsw-alias-state-info-primary)。 @author ddj 2026-09-23 */\n[data-edrv-view] .edrv-svn-ai-bar { position: relative; display: flex; align-items: center; gap: 8px; padding: 6px 10px 6px 14px; background: var(--dsw-alias-bg-layer-2, #f7f9f9); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); flex-shrink: 0; }\n[data-edrv-view] .edrv-svn-ai-rail { position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--dsw-alias-state-info-primary, #2f6fed); }\n[data-edrv-view] .edrv-svn-ai-label { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-svn-ai-btn { border-color: var(--dsw-alias-state-info-primary, #2f6fed); color: var(--dsw-alias-state-info-primary, #2f6fed); }\n[data-edrv-view] .edrv-svn-ai-btn:disabled { border-color: var(--dsw-alias-border-l1, #e0e6e8); color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n/* 状态位定宽右对齐(就绪/分析中…/等待助手投递…互换不跳版) */\n[data-edrv-view] .edrv-svn-ai-status { flex-shrink: 0; min-width: 88px; text-align: right; font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; }\n/* 批量条语义分隔(导出簇 ┊ 动作簇之间 1px 竖线) */\n[data-edrv-view] .edrv-svn-sep { width: 1px; height: 14px; background: var(--dsw-alias-border-l1, #e0e6e8); flex-shrink: 0; }\n/* 头部图标组(检查远端/刷新成组,gap 收紧) */\n[data-edrv-view] .edrv-svn-head-icons { display: inline-flex; align-items: center; gap: 2px; flex-shrink: 0; }\n[data-edrv-view] .edrv-svn-list { flex: 1; min-height: 0; overflow: auto; padding: 2px 6px 12px; font-size: 12px; }\n[data-edrv-view] .edrv-svn-group { display: flex; align-items: center; gap: 6px; height: 22px; padding: 0 6px; margin: 2px 0; border-radius: 5px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); font-size: 10px; font-weight: 600; cursor: pointer; user-select: none; }\n[data-edrv-view] .edrv-svn-group:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-svn-fold-ch { width: 10px; flex-shrink: 0; text-align: center; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svn-group-n { margin-left: auto; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-weight: 400; }\n[data-edrv-view] .edrv-svn-row { display: flex; align-items: center; gap: 6px; height: 22px; padding: 0 6px; border-radius: 5px; cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-svn-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-svn-path { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-svn-acts { display: none; align-items: center; gap: 4px; flex-shrink: 0; }\n[data-edrv-view] .edrv-svn-row:hover .edrv-svn-acts { display: inline-flex; }\n[data-edrv-view] .edrv-svn-act { border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 5px; padding: 1px 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-secondary, #52606d); font-size: 10px; line-height: 14px; cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-svn-act:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-svn-act:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-svn-act-danger { color: var(--dsw-alias-state-error-primary, #d9534f); border-color: var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); }\n[data-edrv-view] .edrv-svn-ch { display: inline-flex; align-items: center; justify-content: center; width: 14px; flex-shrink: 0; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; font-weight: 700; }\n[data-edrv-view] .edrv-svn-ch-modified { color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svn-ch-added { color: var(--dsw-alias-state-warn-primary, #7c3aed); }\n[data-edrv-view] .edrv-svn-ch-deleted { color: var(--dsw-alias-label-secondary, #b91c1c); }\n[data-edrv-view] .edrv-svn-ch-conflict { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-svn-ch-plain { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n/* .edrv-svn-ch-* 动作词表补全:replaced(替换)= 琥珀;侧栏状态分级(SVN_STATUS_TONE)不受影响 @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svn-ch-replaced { color: var(--dsw-alias-state-warn-primary, #d97706); }\n/* 文件树行尾 SVN 状态徽标(与差异角标并列;方形字母,区别圆形差异计数) */\n[data-edrv-view] .edrv-tree-svn { min-width: 14px; height: 14px; padding: 0 4px; border-radius: 4px; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 9px; font-weight: 700; line-height: 14px; text-align: center; box-sizing: border-box; flex-shrink: 0; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-tree-svn.edrv-svn-ch-modified { color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-tree-svn.edrv-svn-ch-added { color: var(--dsw-alias-state-warn-primary, #7c3aed); }\n[data-edrv-view] .edrv-tree-svn.edrv-svn-ch-deleted { color: var(--dsw-alias-label-secondary, #b91c1c); }\n[data-edrv-view] .edrv-tree-svn.edrv-svn-ch-conflict { color: var(--dsw-alias-state-error-primary, #d9534f); }\n/* ===== SVN 基线差异视图(BASE 左 / 工作区右,只读并排) @author ddj 2026-09-16 ===== */\n[data-edrv-view] .edrv-svndiff { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-svndiff-bar { display: flex; align-items: center; gap: 8px; height: 30px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-svndiff-title { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-svndiff-tag { padding: 1px 6px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svndiff-host { flex: 1 1 auto; min-height: 0; }\n[data-edrv-view] .edrv-svndiff-empty { display: flex; flex: 1 1 auto; align-items: center; justify-content: center; padding: 24px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 12px; text-align: center; }\n/* 差异整行色块与行首 +/- 指示符:vendored Monaco(diffEditorWidget2)静态 CSS 无\n .line-insert/.line-delete 背景规则,整行色块靠主题键 + 此处固定色兜底保证可见。\n 注意不要用 --dsw-alias-interactive-bg-hover 类 token:实测为 92% 透明中性色,肉眼不可见。\n 固定色取自研差异 UI 同色相(绿 #0f9d58 / 红 #d9534f),alpha 提到肉眼可辨。 */\n[data-edrv-view] .monaco-editor .line-insert { background: rgba(15, 157, 88, 0.18); }\n[data-edrv-view] .monaco-editor .line-delete { background: rgba(217, 83, 79, 0.16); }\n[data-edrv-view] .monaco-editor .gutter-insert { background: rgba(15, 157, 88, 0.35); }\n[data-edrv-view] .monaco-editor .gutter-delete { background: rgba(217, 83, 79, 0.35); }\n[data-edrv-view] .monaco-editor .insert-sign { color: var(--dsw-alias-state-success-primary, #0f9d58); }\n[data-edrv-view] .monaco-editor .delete-sign { color: var(--dsw-alias-state-error-primary, #d9534f); }\n/* 差异条快速跳转簇(对齐自研审查条 ↑ i/N ↓ 的形态) */\n[data-edrv-view] .edrv-svndiff-nav { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }\n[data-edrv-view] .edrv-svndiff-nav-count { min-width: 36px; text-align: center; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); }\n/* W1-3 差异统计徽标(+新增/修改 −删除/修改;色相与行块绿红一致) @author ddj 2026-09-20 */\n[data-edrv-view] .edrv-svndiff-stat { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-svndiff-stat-add { color: var(--dsw-alias-state-success-primary, #0f9d58); }\n[data-edrv-view] .edrv-svndiff-stat-del { color: var(--dsw-alias-state-error-primary, #d9534f); }\n/* W1-1 差异视图选项条(右端开关组;复用 .edrv-svn-check 的 label 形态) */\n[data-edrv-view] .edrv-svndiff-opts { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }\n/* W1-2 变更面板名称过滤输入框(工具条行内,紧凑) @author ddj 2026-09-20 */\n[data-edrv-view] .edrv-svn-name-filter { width: 168px; padding: 1px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 5px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; line-height: 14px; }\n[data-edrv-view] .edrv-svn-name-filter:focus { outline: none; border-color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-svn-name-filter::placeholder { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n/* W2-3 远端更新提示条(工具条上方;warn = 失败降级) @author ddj 2026-09-20 */\n[data-edrv-view] .edrv-svn-hintbar { display: flex; align-items: center; gap: 6px; margin: 0 10px 4px 12px; padding: 3px 8px; border-radius: 5px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); font-size: 11px; flex-shrink: 0; }\n[data-edrv-view] .edrv-svn-hintbar-warn { background: rgba(217, 83, 79, 0.12); color: var(--dsw-alias-state-error-primary, #d9534f); }\n/* W2-5 疑似改名标记(行内 ⇄;hover 见 tooltip) */\n[data-edrv-view] .edrv-svn-pair-mark { flex-shrink: 0; color: var(--dsw-alias-state-warn-primary, #7c3aed); font-size: 11px; cursor: help; }\n/* W2-1 补丁对话框 / W2-2 目录对比对话框 @author ddj 2026-09-20 */\n[data-edrv-view] .edrv-svn-patch-dialog { max-width: calc(100vw - 48px); }\n[data-edrv-view] .edrv-svn-patch-head { display: flex; align-items: center; gap: 8px; padding-bottom: 8px; }\n[data-edrv-view] .edrv-svn-patch-notes { display: flex; flex-direction: column; gap: 4px; padding-bottom: 6px; }\n[data-edrv-view] .edrv-svn-patch-warn { color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n[data-edrv-view] .edrv-svn-patch-pre { max-height: 46vh; overflow: auto; margin: 0 0 8px; padding: 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f7f9f9); color: var(--dsw-alias-label-primary, #1f2933); font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; line-height: 1.5; white-space: pre; user-select: text; }\n[data-edrv-view] .edrv-svn-patch-foot { display: flex; align-items: center; gap: 8px; }\n/* AI 智能整理弹窗(11-ai-changelist-triage):预览三分组卡 + 执行进度 @author ddj 2026-09-23 */\n[data-edrv-view] .edrv-svnplan-dialog { max-width: calc(100vw - 48px); }\n[data-edrv-view] .edrv-svnplan-head { display: flex; align-items: center; gap: 8px; padding-bottom: 4px; }\n[data-edrv-view] .edrv-svnplan-meta { display: flex; gap: 10px; padding-bottom: 6px; }\n[data-edrv-view] .edrv-svnplan-note { color: var(--dsw-alias-state-warn-primary, #b7791f); font-size: 10px; }\n[data-edrv-view] .edrv-svnplan-sec { border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; padding: 6px 8px; margin-bottom: 8px; }\n[data-edrv-view] .edrv-svnplan-sechead { display: flex; align-items: center; gap: 6px; }\n[data-edrv-view] .edrv-svnplan-sectitle { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-svnplan-list { max-height: 26vh; overflow: auto; padding-left: 18px; }\n[data-edrv-view] .edrv-svnplan-card { border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); padding: 3px 0; }\n[data-edrv-view] .edrv-svnplan-row { display: flex; align-items: center; gap: 6px; }\n[data-edrv-view] .edrv-svnplan-item { display: flex; align-items: center; gap: 6px; padding-left: 18px; }\n[data-edrv-view] .edrv-svnplan-groupname { font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-svnplan-path { font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 46ch; }\n[data-edrv-view] .edrv-svnplan-reason { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 30ch; }\n[data-edrv-view] .edrv-svnplan-stage { display: flex; align-items: center; gap: 8px; padding-bottom: 6px; }\n[data-edrv-view] .edrv-svnplan-badge { border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 5px; padding: 1px 8px; font-size: 10px; color: var(--dsw-alias-label-secondary, #52606d); background: var(--dsw-alias-bg-layer-2, #f7f9f9); white-space: nowrap; }\n[data-edrv-view] .edrv-svnplan-badge-cur { font-family: var(--ds-font-family-code, ui-monospace, monospace); overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-svnplan-bar { height: 6px; border-radius: 3px; background: var(--dsw-alias-bg-layer-2, #f7f9f9); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); overflow: hidden; margin-bottom: 8px; }\n[data-edrv-view] .edrv-svnplan-bar-in { height: 100%; background: var(--dsw-alias-state-info-primary, #2f6fed); transition: width .2s; }\n[data-edrv-view] .edrv-svnplan-logwrap { max-height: 40vh; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; padding: 6px 8px; background: var(--dsw-alias-bg-layer-2, #f7f9f9); }\n[data-edrv-view] .edrv-svnplan-log { font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; line-height: 1.6; color: var(--dsw-alias-label-secondary, #52606d); user-select: text; }\n[data-edrv-view] .edrv-svnplan-log-bad { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-svnplan-foot { display: flex; align-items: center; gap: 8px; }\n[data-edrv-view] .edrv-svnplan-primary { font-weight: 600; border-color: var(--dsw-alias-state-info-primary, #2f6fed); }\n[data-edrv-view] .edrv-svn-sum-form { display: flex; align-items: center; gap: 10px; padding-bottom: 8px; }\n[data-edrv-view] .edrv-svn-sum-list { max-height: 44vh; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; padding: 2px; margin-bottom: 8px; }\n[data-edrv-view] .edrv-svnlog-path-kind { flex-shrink: 0; padding: 0 5px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; }\n\n/* ===== 通用弹窗外壳(ModalShell)+ SVN 日志弹窗 @author ddj 2026-09-16 ===== */\n/* z-index 190:落在既有阶梯内(命令栏 200 > 片段二级 197 > 片段一级 195 > 190),\n 不遮挡命令栏;既有浮层数值不受影响(见 tests/overlayZOrder.test.ts)。 */\n[data-edrv-view] .edrv-modal-mask { position: fixed; inset: 0; z-index: 190; display: flex; align-items: center; justify-content: center; padding: 24px; box-sizing: border-box; background: var(--dsw-alias-bg-mask-drop, #000000aa); }\n[data-edrv-view] .edrv-svnlog-dialog { position: relative; display: flex; flex-direction: column; gap: 8px; }\n[data-edrv-view] .edrv-svnlog-head { display: flex; align-items: center; gap: 10px; flex-wrap: nowrap; }\n[data-edrv-view] .edrv-svnlog-title { margin: 0; font-size: 16px; font-weight: 650; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n/* 头部目标路径 = 代码 chip(mono + 底色 + 全圆角;补丁/目录对比弹窗共用同视觉) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-target { font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); }\n/* 关闭按钮图标化(透明底,hover 显形;dialog 前缀提高特异性,压过底部按钮统一尺寸) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-dialog .edrv-svnlog-head .edrv-svn-act { border-color: transparent; background: transparent; width: 24px; height: 24px; padding: 0; font-size: 12px; line-height: 1; }\n[data-edrv-view] .edrv-svnlog-dialog .edrv-svnlog-head .edrv-svn-act:hover { border-color: var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.10)); }\n/* 过滤栏:浅色表面条 + 统一 24px 控件 + 焦点边框变色 + 日期组靠右 @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-filter { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; padding: 6px 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 8px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); }\n[data-edrv-view] .edrv-svnlog-field { flex: 0 0 auto; height: 24px; box-sizing: border-box; padding: 0 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-svnlog-search { min-width: 160px; flex: 1 1 220px; height: 24px; box-sizing: border-box; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-svnlog-field:focus, [data-edrv-view] .edrv-svnlog-search:focus, [data-edrv-view] .edrv-svnlog-date:focus { outline: none; border-color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-dates { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; }\n[data-edrv-view] .edrv-svnlog-date { height: 24px; box-sizing: border-box; padding: 0 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-svnlog-check { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); white-space: nowrap; cursor: pointer; }\n[data-edrv-view] .edrv-svnlog-check input[type=\"checkbox\"] { margin: 0; cursor: pointer; accent-color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-error { padding: 4px 8px; border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n/* P0-14 上中下三段全宽:body 纵向铺满弹窗(外框高度由拖拽受控) */\n[data-edrv-view] .edrv-svnlog-body { display: flex; flex-direction: column; gap: 6px; flex: 1 1 auto; min-height: 0; }\n/* 上段修订列表(13-log-ui-polish 变更台账):\n 1px 发丝边框 + 底色面 + 左侧时间脊线(每行 ::before 画节点圆点、行盒左缘 ::after 连线段,\n 行行相接即整列脊线;选中行节点实心着色 + 2px 左强调条)。列宽由 cell 类与 nth-child 对齐(P0-12/D30)。 */\n[data-edrv-view] .edrv-svnlog-list { position: relative; flex: 0 0 auto; display: flex; flex-direction: column; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 8px; padding: 0 4px 4px; background: var(--dsw-alias-bg-layer-1, #fff); }\n/* 列头 sticky:滚动常驻 + 实底防透;z-index 20 低于一切浮层(190 起),仅在列表滚动容器内生效 */\n[data-edrv-view] .edrv-svnlog-cols { position: sticky; top: 0; z-index: 20; display: flex; align-items: center; gap: 6px; padding: 6px 6px 6px 16px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, #fff); font-size: 10px; font-weight: 600; letter-spacing: .04em; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }\n[data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(1) { width: 52px; flex: none; }\n[data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(2) { width: 52px; flex: none; }\n[data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(3) { width: 88px; flex: none; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(4) { width: 82px; flex: none; }\n[data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(5) { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-svnlog-col-sortable { cursor: pointer; }\n[data-edrv-view] .edrv-svnlog-col-sortable:hover { color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-svnlog-col-on { color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-sort-reset { flex: none; }\n/* 行:position:relative 承载脊线节点(::before)与连线段(::after);选中 = 强调条 + 节点实心 @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-item { position: relative; display: flex; align-items: center; gap: 6px; padding: 3px 6px 3px 16px; border-radius: 6px; cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-svnlog-item::before { content: ''; position: absolute; left: 6px; top: 50%; transform: translateY(-50%); width: 7px; height: 7px; box-sizing: border-box; border-radius: 50%; border: 1.5px solid var(--dsw-alias-border-l2, #cbd2d9); background: var(--dsw-alias-bg-layer-1, #fff); z-index: 1; }\n[data-edrv-view] .edrv-svnlog-item::after { content: ''; position: absolute; left: 9px; top: 0; bottom: 0; width: 1px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-svnlog-item:last-child::after { bottom: 50%; }\n[data-edrv-view] .edrv-svnlog-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-svnlog-item.edrv-svnlog-item-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); box-shadow: inset 2px 0 0 var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-item.edrv-svnlog-item-on::before { border-color: var(--dsw-alias-brand-primary, #2563eb); background: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-item-copied .edrv-svnlog-cell-msg,\n[data-edrv-view] .edrv-svnlog-item-copied .edrv-svnlog-rev { color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-cell { overflow: hidden; text-overflow: ellipsis; font-size: 11px; color: var(--dsw-alias-label-primary, #1f2933); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-svnlog-cell-rev { width: 52px; flex: none; }\n[data-edrv-view] .edrv-svnlog-cell-act { width: 52px; flex: none; display: inline-flex; gap: 3px; }\n[data-edrv-view] .edrv-svnlog-cell-author { width: 88px; flex: none; }\n[data-edrv-view] .edrv-svnlog-cell-date { width: 82px; flex: none; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svnlog-cell-msg { flex: 1 1 auto; }\n[data-edrv-view] .edrv-svnlog-rev { font-family: var(--ds-font-family-code, ui-monospace, monospace); font-weight: 700; color: var(--dsw-alias-brand-primary, #2563eb); }\n[data-edrv-view] .edrv-svnlog-badge { display: inline-flex; align-items: center; justify-content: center; width: 14px; height: 14px; border-radius: 4px; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 9px; font-weight: 700; }\n/* 动作语义色(13-log-ui-polish 归位):A=success 绿 · D=error 红 · M=brand 蓝 · R=warn 琥珀 @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-badge-modified { color: var(--dsw-alias-state-info-primary, #2f6fed); background: rgba(47,111,237,.14); }\n[data-edrv-view] .edrv-svnlog-badge-added { color: var(--dsw-alias-state-success-primary, #0f9d58); background: rgba(15,157,88,.14); }\n[data-edrv-view] .edrv-svnlog-badge-deleted { color: var(--dsw-alias-state-error-primary, #d9534f); background: rgba(217,83,79,.14); }\n[data-edrv-view] .edrv-svnlog-badge-replaced { color: var(--dsw-alias-state-warn-primary, #d97706); background: rgba(217,119,6,.16); }\n[data-edrv-view] .edrv-svnlog-badge-plain { color: var(--dsw-alias-label-tertiary, #9aa5b1); background: var(--dsw-alias-bg-layer-2, #f0f4f4); }\n[data-edrv-view] .edrv-svnlog-more { padding: 6px 4px 2px; text-align: center; }\n/* P0-14 三段 pane 公共外观(13-log-ui-polish:2px 粗框 → 1px 发丝线 + 面板底色) */\n[data-edrv-view] .edrv-svnlog-pane { border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 8px; padding: 8px 10px; overflow: hidden; background: var(--dsw-alias-bg-layer-1, #fff); }\n[data-edrv-view] .edrv-svnlog-msgpane { flex: 0 0 auto; display: flex; flex-direction: column; }\n[data-edrv-view] .edrv-svnlog-pathspane { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }\n/* P0-13/P0-14:分段横向拖拽分隔条 + 变更路径独立滚动容器 */\n[data-edrv-view] .edrv-svnlog-hsplit { flex: 0 0 6px; cursor: row-resize; margin: 2px 0; border-radius: 3px; touch-action: none; }\n[data-edrv-view] .edrv-svnlog-hsplit:hover, [data-edrv-view] .edrv-svnlog-hsplit:active { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.28)); }\n[data-edrv-view] .edrv-svnlog-paths-wrap { flex: 1 1 auto; min-height: 0; overflow: auto; display: flex; flex-direction: column; }\n/* 提交信息 meta 行:rev/日期 chip 化,author 常规字重(13-log-ui-polish) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-meta { display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); flex-wrap: wrap; }\n[data-edrv-view] .edrv-svnlog-meta .edrv-svnlog-rev { padding: 1px 8px; border-radius: 999px; background: var(--dsw-alias-state-info-primary, #2f6fed); color: var(--dsw-alias-label-primary-inverted, #fff); font-size: 11px; font-weight: 700; }\n[data-edrv-view] .edrv-svnlog-meta .edrv-svnlog-date { height: auto; padding: 1px 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-svnlog-date { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n/* 正文块:左侧品牌强调线 +1.7 行高(长提交信息可读性) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-msgpane pre.edrv-svnlog-body { margin: 8px 0 0; padding: 8px 10px; border-radius: 0 6px 6px 0; border-left: 2px solid var(--dsw-alias-state-info-primary, #2f6fed); background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11.5px; line-height: 1.7; white-space: pre-wrap; word-break: break-word; overflow: auto; flex: 1 1 auto; min-height: 0; }\n/* P0-14 外框尺寸手柄(13-log-ui-polish 增量3 八向:n/s/w/e 四边 + nw/ne/sw/se 四角);\n 热区避开内边距带(边8px/角14px 落在卡片22/24px padding 内不遮内容),角手柄后渲染盖过相邻边2px overlap。\n chevron 角标四向各自朝向,hover 显形。 @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-grip { position: absolute; z-index: 2; touch-action: none; }\n[data-edrv-view] .edrv-svnlog-grip-n { top: 0; left: 12px; right: 12px; height: 8px; cursor: ns-resize; }\n[data-edrv-view] .edrv-svnlog-grip-s { bottom: 0; left: 12px; right: 12px; height: 8px; cursor: ns-resize; }\n[data-edrv-view] .edrv-svnlog-grip-w { left: 0; top: 12px; bottom: 12px; width: 8px; cursor: ew-resize; }\n[data-edrv-view] .edrv-svnlog-grip-e { right: 0; top: 12px; bottom: 12px; width: 8px; cursor: ew-resize; }\n[data-edrv-view] .edrv-svnlog-grip-nw { left: 0; top: 0; width: 14px; height: 14px; cursor: nwse-resize; }\n[data-edrv-view] .edrv-svnlog-grip-ne { right: 0; top: 0; width: 14px; height: 14px; cursor: nesw-resize; }\n[data-edrv-view] .edrv-svnlog-grip-sw { left: 0; bottom: 0; width: 14px; height: 14px; cursor: nesw-resize; }\n[data-edrv-view] .edrv-svnlog-grip-se { right: 0; bottom: 0; width: 14px; height: 14px; cursor: nwse-resize; background: transparent; }\n[data-edrv-view] .edrv-svnlog-grip-nw::after,\n[data-edrv-view] .edrv-svnlog-grip-ne::after,\n[data-edrv-view] .edrv-svnlog-grip-sw::after,\n[data-edrv-view] .edrv-svnlog-grip-se::after { content: ''; position: absolute; width: 6px; height: 6px; opacity: .5; border-color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svnlog-grip-se::after { right: 3px; bottom: 3px; border-right: 1.5px solid; border-bottom: 1.5px solid; }\n[data-edrv-view] .edrv-svnlog-grip-sw::after { left: 3px; bottom: 3px; border-left: 1.5px solid; border-bottom: 1.5px solid; }\n[data-edrv-view] .edrv-svnlog-grip-ne::after { right: 3px; top: 3px; border-right: 1.5px solid; border-top: 1.5px solid; }\n[data-edrv-view] .edrv-svnlog-grip-nw::after { left: 3px; top: 3px; border-left: 1.5px solid; border-top: 1.5px solid; }\n[data-edrv-view] .edrv-svnlog-grip:hover, [data-edrv-view] .edrv-svnlog-grip:active { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.35)); }\n[data-edrv-view] .edrv-svnlog-grip:hover::after, [data-edrv-view] .edrv-svnlog-grip:active::after { opacity: 1; border-color: var(--dsw-alias-label-primary, #1f2933); }\n/* 行反馈微动效(仅 hover/选中背景过渡;用户要求减少动效时关闭) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-item { transition: background-color .12s ease, box-shadow .12s ease; }\n@media (prefers-reduced-motion: reduce) {\n [data-edrv-view] .edrv-svnlog-item { transition: none; }\n}\n/*13-log-ui-polish 增量:行容器可滚动(pane 仍 overflow:hidden 裁圆角),列头不被压缩 @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-paths-head { display: flex; align-items: center; gap: 6px; flex: none; font-size: 10px; font-weight: 600; letter-spacing: .04em; color: var(--dsw-alias-label-tertiary, #9aa5b1); margin-bottom: 4px; padding-bottom: 4px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-svnlog-hint { font-weight: 400; letter-spacing: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svnlog-paths { display: flex; flex-direction: column; gap: 1px; flex: 1 1 auto; min-height: 0; overflow: auto; }\n[data-edrv-view] .edrv-svnlog-path { display: flex; align-items: center; gap: 6px; height: 22px; padding: 0 6px; border-radius: 5px; cursor: default; white-space: nowrap; }\n[data-edrv-view] .edrv-svnlog-path:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-svnlog-path-dim .edrv-svnlog-pathtext,\n[data-edrv-view] .edrv-svnlog-path-dim .edrv-svnlog-copyfrom { opacity: .45; }\n[data-edrv-view] .edrv-svnlog-pathtext { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; color: var(--dsw-alias-label-primary, #1f2933); }\n/* 路径两段着色:目录段弱化 + 文件名加重(13-log-ui-polish) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-pdir { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svnlog-pbase { color: var(--dsw-alias-label-primary, #1f2933); font-weight: 600; }\n[data-edrv-view] .edrv-svnlog-copyfrom { flex: 0 1 160px; overflow: hidden; text-overflow: ellipsis; font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-svnlog-kind { font-size: 10px; padding: 0 5px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-svnlog-acts { display: none; align-items: center; gap: 4px; flex-shrink: 0; }\n[data-edrv-view] .edrv-svnlog-path:hover .edrv-svnlog-acts { display: inline-flex; }\n/* 底部:状态行与按钮组分层 + 组间发丝线(13-log-ui-polish) @author ddj 2026年09月24号 */\n[data-edrv-view] .edrv-svnlog-footwrap { display: flex; flex-direction: column; gap: 6px; flex-shrink: 0; padding-top: 6px; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-svnlog-foot { display: flex; align-items: center; gap: 10px; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-svnlog-btns { display: flex; align-items: center; gap: 6px; }\n[data-edrv-view] .edrv-svnlog-btngroup { display: inline-flex; align-items: center; gap: 6px; }\n[data-edrv-view] .edrv-svnlog-btngroup + .edrv-svnlog-btngroup { margin-left: 6px; padding-left: 10px; border-left: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-svnlog-dialog .edrv-svn-act,\n[data-edrv-view] .edrv-svnlog-range .edrv-svn-act,\n[data-edrv-view] .edrv-svnlog-stats .edrv-svn-act { font-size: 11px; padding: 2px 9px; }\n[data-edrv-view] .edrv-svnlog-dialog .edrv-svn-act:focus-visible,\n[data-edrv-view] .edrv-svnlog-range .edrv-svn-act:focus-visible,\n[data-edrv-view] .edrv-svnlog-stats .edrv-svn-act:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #2563eb); outline-offset: 1px; }\n/* ===== SVN 日志统计窗(P1-10)@author ddj 2026-09-17 ===== */\n[data-edrv-view] .edrv-svnlog-stats { display: flex; flex-direction: column; max-height: 82vh; }\n[data-edrv-view] .edrv-svnlog-stats-head { display: flex; align-items: center; gap: 8px; padding: 12px 16px 0; }\n[data-edrv-view] .edrv-svnlog-stats-body { display: flex; flex-direction: column; gap: 14px; padding: 10px 16px 16px; overflow: auto; }\n[data-edrv-view] .edrv-svnlog-stat-line { display: flex; gap: 16px; flex-wrap: wrap; font-size: 12px; color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-svnlog-stat-block { display: flex; flex-direction: column; gap: 8px; }\n[data-edrv-view] .edrv-svnlog-stat-label { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-svnlog-stat-duo { display: flex; gap: 18px; align-items: flex-start; flex-wrap: wrap; }\n[data-edrv-view] .edrv-svnlog-stat-hist { flex: 1 1 260px; display: flex; flex-direction: column; gap: 4px; min-width: 220px; }\n[data-edrv-view] .edrv-svnlog-stat-row { display: flex; align-items: center; gap: 8px; font-size: 11px; }\n[data-edrv-view] .edrv-svnlog-stat-name { width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-svnlog-stat-barwrap { flex: 1; height: 12px; border-radius: 6px; background: rgba(127,127,127,.15); overflow: hidden; }\n[data-edrv-view] .edrv-svnlog-stat-bar { display: block; height: 100%; border-radius: 6px; }\n[data-edrv-view] .edrv-svnlog-stat-count { width: 36px; text-align: right; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-svnlog-stat-pie { width: 120px; height: 120px; flex-shrink: 0; border-radius: 50%; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-svnlog-stat-days { display: flex; align-items: flex-end; gap: 2px; height: 120px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); overflow-x: auto; }\n[data-edrv-view] .edrv-svnlog-stat-day { flex: 1 0 6px; min-width: 6px; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; }\n[data-edrv-view] .edrv-svnlog-stat-seg { display: block; width: 100%; }\n/* ===== SVN 日志区间弹窗(P1-6)@author ddj 2026-09-17 ===== */\n[data-edrv-view] .edrv-svnlog-range-body { display: flex; flex-direction: column; gap: 12px; padding: 14px 16px 16px; }\n[data-edrv-view] .edrv-svnlog-range-row { display: flex; gap: 14px; flex-wrap: wrap; }\n[data-edrv-view] .edrv-svnlog-range-input { width: 110px; padding: 4px 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-svnlog-range-btns { display: flex; gap: 8px; justify-content: flex-end; }\n/* SVN 日志 WC 版号行加粗(P1-9) */\n[data-edrv-view] .edrv-svnlog-item-wc { font-weight: 700; }\n/* SVN 日志 merged 行(P1-5):灰字 + 缩进(缩进量按层级内联),前置「↳」标记 */\n[data-edrv-view] .edrv-svnlog-item-merged { color: var(--dsw-alias-label-tertiary, #7b8794); }\n[data-edrv-view] .edrv-svnlog-item-merged .edrv-svnlog-merge-mark { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary, #7b8794); }\n[data-edrv-view] .edrv-svnlog-item-merged .edrv-svnlog-cell-msg { font-style: italic; }\n/* ===== 诊断日志弹窗(LogDialog)@author ddj 2026-09-17 ===== */\n[data-edrv-view] .edrv-dlog-dialog { display: flex; flex-direction: column; gap: 8px; max-height: 88vh; }\n[data-edrv-view] .edrv-dlog-head { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; }\n[data-edrv-view] .edrv-dlog-title { margin: 0; font-size: 15px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-dlog-file { max-width: 200px; padding: 4px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-dlog-level { padding: 4px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-dlog-search { min-width: 120px; flex: 0 1 200px; padding: 4px 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }\n[data-edrv-view] .edrv-dlog-auto { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); white-space: nowrap; cursor: pointer; }\n[data-edrv-view] .edrv-dlog-error { padding: 4px 8px; border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n[data-edrv-view] .edrv-dlog-scroll { flex: 1 1 auto; min-height: 240px; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 8px; padding: 4px 0; background: var(--dsw-alias-bg-layer-1, #fff); }\n[data-edrv-view] .edrv-dlog-row { display: flex; align-items: baseline; gap: 8px; padding: 1px 8px; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; line-height: 1.55; }\n[data-edrv-view] .edrv-dlog-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.06)); }\n[data-edrv-view] .edrv-dlog-at { flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-dlog-lvl { flex-shrink: 0; width: 26px; text-align: center; font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-dlog-text { flex: 1; min-width: 0; white-space: pre-wrap; word-break: break-word; color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-dlog-row-error .edrv-dlog-text { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-dlog-row-warn .edrv-dlog-text { color: var(--dsw-alias-state-warning-primary, #b45309); }\n[data-edrv-view] .edrv-dlog-row-debug .edrv-dlog-text { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-dlog-foot { display: flex; align-items: center; gap: 10px; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }\n[data-edrv-view] .edrv-dlog-path { font-family: var(--ds-font-family-code, ui-monospace, monospace); max-width: 46%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-dlog-stat { white-space: nowrap; }\n[data-edrv-view] .edrv-dlog-switch { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); white-space: nowrap; cursor: pointer; flex-shrink: 0; }\n/* 原生 SVN 更新窗口:逐条状态与路径是主信息;跟随 DSH 主题令牌。 */\n[data-edrv-view] .edrv-update-dialog { display: flex; flex-direction: column; padding: 0; border-radius: 8px; overflow: hidden; background: var(--dsw-alias-bg-overlay, #fff); }\n[data-edrv-view] .edrv-update-head { display: flex; align-items: center; gap: 12px; min-height: 48px; padding: 0 16px; border-bottom: 1px solid var(--dsw-alias-border-l1, #d9e0e4); }\n[data-edrv-view] .edrv-update-heading { display: flex; align-items: center; gap: 12px; min-width: 0; flex: 1; }\n[data-edrv-view] .edrv-update-heading h3 { margin: 0; flex: none; font-size: 15px; }\n[data-edrv-view] .edrv-update-target { min-width: 0; color: var(--dsw-alias-label-tertiary, #65737c); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; }\n[data-edrv-view] .edrv-update-phase { flex: none; font-size: 12px; color: var(--dsw-alias-label-secondary, #485762); }\n[data-edrv-view] .edrv-update-close { display: grid; place-items: center; flex: none; width: 28px; height: 28px; border: 0; border-radius: 4px; background: none; color: var(--dsw-alias-label-secondary, #485762); cursor: pointer; }\n[data-edrv-view] .edrv-update-close:disabled { opacity: .35; cursor: default; }\n[data-edrv-view] .edrv-update-close:not(:disabled):hover { background: var(--dsw-alias-interactive-bg-hover, #e7edf0); }\n[data-edrv-view] .edrv-update-table { display: flex; flex-direction: column; flex: 1; min-height: 0; margin: 12px 14px 0; border: 1px solid var(--dsw-alias-border-l1, #d9e0e4); border-radius: 5px; overflow: hidden; background: var(--dsw-alias-bg-layer-1, #fff); }\n[data-edrv-view] .edrv-update-cols, [data-edrv-view] .edrv-update-row { display: grid; grid-template-columns: 110px minmax(0, 1fr) 160px; align-items: center; }\n[data-edrv-view] .edrv-update-sort { width: 100%; padding: 0; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }\n[data-edrv-view] .edrv-update-sort:hover { color: var(--dsw-alias-brand-primary, #2869b4); }\n[data-edrv-view] .edrv-update-sort:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #2869b4); outline-offset: 2px; }\n[data-edrv-view] .edrv-update-jump { color: var(--dsw-alias-state-error-primary, #b93b43); }\n[data-edrv-view] .edrv-update-cols { flex: none; height: 30px; background: var(--dsw-alias-bg-layer-2, #f1f4f5); border-bottom: 1px solid var(--dsw-alias-border-l1, #d9e0e4); color: var(--dsw-alias-label-secondary, #485762); font-size: 11px; font-weight: 600; }\n[data-edrv-view] .edrv-update-cols > span, [data-edrv-view] .edrv-update-row > span { min-width: 0; padding: 0 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-update-cols > span + span, [data-edrv-view] .edrv-update-row > span + span { border-left: 1px solid var(--dsw-alias-border-l1, #d9e0e4); }\n[data-edrv-view] .edrv-update-list { position: relative; flex: 1; min-height: 0; overflow: auto; scrollbar-width: thin; }\n[data-edrv-view] .edrv-update-space { position: relative; }\n[data-edrv-view] .edrv-update-pos { position: absolute; left: 0; right: 0; }\n[data-edrv-view] .edrv-update-row { height: 100%; font-size: 11px; cursor: default; color: var(--dsw-alias-label-primary, #202932); }\n[data-edrv-view] .edrv-update-row:hover { background: var(--dsw-alias-interactive-bg-hover, #ecf3f9); }\n[data-edrv-view] .edrv-update-row.is-selected { background: var(--dsw-alias-interactive-bg-hover, #dceaf9); box-shadow: inset 2px 0 0 var(--dsw-alias-brand-primary, #2869b4); }\n[data-edrv-view] .edrv-update-action { font-weight: 600; }\n[data-edrv-view] .edrv-update-action.tone-added { color: var(--dsw-alias-state-success-primary, #23704e); }\n[data-edrv-view] .edrv-update-action.tone-deleted, [data-edrv-view] .edrv-update-action.tone-conflict { color: var(--dsw-alias-state-error-primary, #b93b43); }\n[data-edrv-view] .edrv-update-action.tone-merged { color: var(--dsw-alias-state-warn-primary, #a05d22); }\n[data-edrv-view] .edrv-update-action.tone-updated { color: var(--dsw-alias-state-info-primary, #2869b4); }\n[data-edrv-view] .edrv-update-path { font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-update-type { color: var(--dsw-alias-label-tertiary, #65737c); }\n[data-edrv-view] .edrv-update-empty { position: absolute; top: 50%; left: 0; right: 0; transform: translateY(-50%); text-align: center; font-size: 12px; color: var(--dsw-alias-label-tertiary, #65737c); pointer-events: none; }\n[data-edrv-view] .edrv-update-telemetry { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 18px; padding: 10px 15px; min-height: 43px; box-sizing: border-box; font-size: 11px; color: var(--dsw-alias-label-secondary, #485762); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-update-error { color: var(--dsw-alias-state-error-primary, #b93b43); overflow-wrap: anywhere; }\n[data-edrv-view] .edrv-update-warning { color: var(--dsw-alias-state-warn-primary, #a05d22); }\n[data-edrv-view] .edrv-update-locked { color: var(--dsw-alias-state-warn-primary, #a05d22); }\n[data-edrv-view] .edrv-update-foot .edrv-update-cleanup:not(:disabled) { color: var(--dsw-alias-state-warn-primary, #a05d22); }\n[data-edrv-view] .edrv-update-foot { display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-top: 1px solid var(--dsw-alias-border-l1, #d9e0e4); font-size: 11px; }\n[data-edrv-view] .edrv-update-counts { flex: 1; min-width: 0; color: var(--dsw-alias-label-secondary, #485762); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-update-foot .edrv-svn-act { flex: none; min-width: 74px; }\n[data-edrv-view] .edrv-update-foot .edrv-svn-act:focus-visible, [data-edrv-view] .edrv-update-close:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #2869b4); outline-offset: 2px; }\n[data-edrv-view] .edrv-update-foot .edrv-svn-act:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-update-raw { max-height: 120px; overflow: auto; padding: 0 14px 10px; font-size: 11px; }\n[data-edrv-view] .edrv-update-raw pre { white-space: pre-wrap; overflow-wrap: anywhere; margin: 5px 0; }\n@media (max-width: 620px) {\n [data-edrv-view] .edrv-update-head { gap: 6px; padding: 0 10px; }\n [data-edrv-view] .edrv-update-table { margin: 8px 8px 0; }\n [data-edrv-view] .edrv-update-cols, [data-edrv-view] .edrv-update-row { grid-template-columns: 76px minmax(0, 1fr); }\n [data-edrv-view] .edrv-update-cols > span:nth-child(3), [data-edrv-view] .edrv-update-row > span:nth-child(3) { display: none; }\n [data-edrv-view] .edrv-update-path { white-space: normal !important; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }\n [data-edrv-view] .edrv-update-foot { flex-wrap: wrap; padding: 8px; }\n [data-edrv-view] .edrv-update-counts { flex-basis: 100%; }\n}\n\n/* ===== 侧栏形态(betterSidebar 面板内编辑区) ===== */\n[data-edrv-view][data-edrv-layout='side'] { box-sizing: border-box; height: 100%; }\n\n/* ===== 侧边栏引导条(旧页签形态、未装 betterSidebar 时) ===== */\n[data-edrv-view] .edrv-side-hint { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; flex-wrap: wrap; padding: 6px 12px; font-size: 12px; color: var(--dsw-alias-label-secondary, #52606d); background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, #f0f4f4)); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-side-hint-text { white-space: nowrap; }\n[data-edrv-view] .edrv-side-hint-cmd { font-size: 11px; padding: 2px 6px; border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #ffffff); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-side-hint-close { margin-left: auto; border: none; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); cursor: pointer; font-size: 14px; line-height: 1; padding: 2px 6px; border-radius: 6px; flex-shrink: 0; }\n[data-edrv-view] .edrv-side-hint-close:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n\n/* ===== search panel (sidebar, VSCode search view) ===== */\n[data-edrv-view] .edrv-search-panel { display: flex; flex-direction: column; min-height: 0; flex: 1; padding: 6px; gap: 4px; }\n[data-edrv-view] .edrv-search-qrow { display: flex; gap: 4px; }\n[data-edrv-view] .edrv-search-input { flex: 1; min-width: 0; height: 26px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-input:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-opts { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-search-opt { min-width: 24px; height: 22px; padding: 0 6px; border: 1px solid transparent; border-radius: 5px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 11px; font-family: var(--ds-font-family-code, ui-monospace, monospace); cursor: pointer; }\n[data-edrv-view] .edrv-search-opt:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-opt.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); border-color: var(--dsw-alias-border-l2, #d5dde3); }\n[data-edrv-view] .edrv-search-spacer { flex: 1; }\n[data-edrv-view] .edrv-search-summary { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); padding: 2px 4px; }\n[data-edrv-view] .edrv-search-results { flex: 1; min-height: 0; overflow: auto; font-size: 12px; }\n[data-edrv-view] .edrv-search-group { margin-bottom: 2px; }\n[data-edrv-view] .edrv-search-file { display: flex; align-items: center; gap: 4px; height: 22px; padding: 0 4px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-search-file:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-chev { width: 12px; flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; text-align: center; }\n[data-edrv-view] .edrv-search-fname { font-weight: 600; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-search-fdir { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-search-fcount { flex-shrink: 0; min-width: 16px; height: 15px; padding: 0 4px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); font-size: 10px; line-height: 15px; text-align: center; }\n[data-edrv-view] .edrv-search-line { display: flex; gap: 6px; padding: 1px 4px 1px 16px; border-radius: 4px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; overflow: hidden; }\n[data-edrv-view] .edrv-search-line.alt { background: var(--dsw-alias-bg-layer-1, #ffffff); }\n[data-edrv-view] .edrv-search-line:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-ln { flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; min-width: 26px; text-align: right; user-select: none; }\n[data-edrv-view] .edrv-search-text { overflow: hidden; text-overflow: ellipsis; white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; }\n[data-edrv-view] .edrv-search-hit { background: var(--dsw-alias-state-warn-primary, #e5b93f); color: var(--dsw-alias-label-primary-inverted, #1f2933); border-radius: 2px; padding: 0 1px; }\n[data-edrv-view] .edrv-search-empty { padding: 8px 4px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-search-error { padding: 6px 8px; border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n[data-edrv-view] .edrv-search-warn { padding: 6px 8px; border: 1px solid var(--dsw-alias-state-warn-primary, rgba(240,173,78,.45)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover, rgba(240,173,78,.12)); color: var(--dsw-alias-state-warn-primary, #b8860b); font-size: 11px; word-break: break-all; }\n\n/* include/exclude filter rows (collapsible) */\n[data-edrv-view] .edrv-search-rows { display: flex; flex-direction: column; gap: 4px; padding: 2px 0 4px; }\n[data-edrv-view] .edrv-search-row { display: flex; align-items: center; gap: 4px; }\n[data-edrv-view] .edrv-search-field { flex: 1; min-width: 0; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 11px; outline: none; }\n[data-edrv-view] .edrv-search-field:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-field.dim { opacity: .45; }\n[data-edrv-view] .edrv-search-field::placeholder { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-search-toggle { min-width: 24px; height: 24px; padding: 0 5px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; line-height: 1; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-search-toggle:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-toggle.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); border-color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-search-toggle:disabled { opacity: .4; cursor: default; }\n\n/* ===== rules panel (sidebar, Codebuddy rules view) ===== */\n[data-edrv-view] .edrv-rules-panel { display: flex; flex-direction: column; min-width: 0; min-height: 0; flex: 1; padding: 6px; gap: 4px; }\n[data-edrv-view] .edrv-rules-tabs { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-tab { height: 24px; padding: 0 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; cursor: pointer; }\n[data-edrv-view] .edrv-rules-tab:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-tab.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); font-weight: 600; }\n[data-edrv-view] .edrv-rules-new { margin-left: auto; height: 24px; padding: 0 10px; border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-new:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-ws-line { font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); padding: 0 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 0 0 auto; user-select: none; }\n[data-edrv-view] .edrv-rules-hint { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); padding: 0 2px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-list { flex: 1; min-height: 0; overflow: auto; }\n[data-edrv-view] .edrv-rules-row { display: flex; align-items: center; gap: 4px; padding: 6px 4px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; }\n[data-edrv-view] .edrv-rules-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-row.off .edrv-rules-main { opacity: .5; }\n[data-edrv-view] .edrv-rules-main { flex: 1; min-width: 0; cursor: pointer; overflow: hidden; }\n[data-edrv-view] .edrv-rules-line1 { display: flex; align-items: center; gap: 6px; min-width: 0; }\n[data-edrv-view] .edrv-rules-sub { display: flex; align-items: center; gap: 5px; margin-top: 3px; min-width: 0; }\n[data-edrv-view] .edrv-rules-sublabel { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-file { font-weight: 600; font-size: 12px; color: var(--dsw-alias-label-primary, #1f2933); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-rules-path { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-rules-type { flex-shrink: 0; height: 15px; padding: 0 5px; border-radius: 999px; font-size: 10px; line-height: 15px; }\n[data-edrv-view] .edrv-rules-type-always { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.15)); color: var(--dsw-alias-state-success-primary, #0b7c46); }\n[data-edrv-view] .edrv-rules-type-auto { background: var(--dsw-alias-interactive-bg-hover-accent, rgba(79,140,255,.16)); color: var(--dsw-alias-link, #3367c9); }\n[data-edrv-view] .edrv-rules-type-manual { background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-rules-offtag { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-desc { font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 0 1 auto; }\n[data-edrv-view] .edrv-rules-actions { display: flex; align-items: center; gap: 2px; flex-shrink: 0; margin-left: auto; }\n[data-edrv-view] .edrv-rules-act { min-width: 22px; height: 22px; padding: 0 4px; border: none; border-radius: 5px; background: transparent; font-size: 12px; line-height: 1; cursor: pointer; color: var(--dsw-alias-label-secondary, #52606d); display: inline-flex; align-items: center; justify-content: center; }\n[data-edrv-view] .edrv-rules-act:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-switch { position: relative; width: 30px; height: 16px; border-radius: 999px; border: none; background: var(--dsw-alias-border-l2, #b9c4cc); cursor: pointer; transition: background .15s; flex-shrink: 0; padding: 0; }\n[data-edrv-view] .edrv-rules-switch.on { background: var(--dsw-alias-brand-primary, #7c5cff); }\n[data-edrv-view] .edrv-rules-knob { position: absolute; top: 2px; left: 2px; width: 12px; height: 12px; border-radius: 50%; background: var(--dsw-alias-bg-layer-1, #fff); transition: left .15s; }\n[data-edrv-view] .edrv-rules-switch.on .edrv-rules-knob { left: 16px; }\n[data-edrv-view] .edrv-rules-empty { padding: 10px 4px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); line-height: 1.6; }\n[data-edrv-view] .edrv-rules-err { padding: 6px 8px; border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; word-break: break-all; }\n[data-edrv-view] .edrv-rules-editor { display: flex; flex-direction: column; min-height: 0; flex: 1; gap: 6px; }\n[data-edrv-view] .edrv-rules-editor-head { display: flex; align-items: center; justify-content: space-between; gap: 6px; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-crumb { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-rules-editor-ctl { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-ctl-label { font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-select { height: 24px; padding: 0 4px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 11px; outline: none; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-select:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-rules-desc-input { flex: 1; min-width: 0; }\n[data-edrv-view] .edrv-rules-input { flex: 1; min-width: 0; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 11px; outline: none; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-rules-input:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-rules-body { flex: 1; min-height: 0; resize: none; padding: 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; line-height: 1.5; outline: none; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-rules-body:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-rules-editor-hint { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); line-height: 1.5; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-editor-foot { display: flex; justify-content: flex-end; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-save { height: 24px; padding: 0 14px; border: none; border-radius: 6px; background: var(--dsw-alias-brand-primary, #0f9d58); color: var(--dsw-alias-label-primary-inverted, #fff); font-size: 12px; cursor: pointer; }\n[data-edrv-view] .edrv-rules-save:disabled { opacity: .5; cursor: default; }\n\n/* 命令栏(Ctrl+Shift+P / F1):body 级 portal 浮层,故不依赖编辑区几何。\n ⚠️ 浮层 z-index 阶梯(自下而上;改动务必保持顺序,tests/overlayZOrder.test.ts 守约):\n 编辑器内浮层 60(.edrv-search-pop) / 71(.edrv-ctxmenu)\n < 190 命令栏遮罩 < 200 命令栏\n < 195 片段浮窗遮罩 < 197 片段二级弹窗遮罩\n 命令栏必须**高于**片段浮窗:曾因片段遮罩(192) 压过命令栏(191),导致片段浮窗打开时\n 命令栏被整体盖住,按 Ctrl+Shift+P 因「已打开即早退」而毫无反应(表现为快捷键失效)。 */\n[data-edrv-view] .edrv-palette-mask { position: fixed; inset: 0; z-index: 190; background: rgba(0, 0, 0, .28); }\n[data-edrv-view] .edrv-palette { position: fixed; top: 12vh; left: 50%; transform: translateX(-50%); z-index: 200; width: min(580px, calc(100vw - 32px)); max-height: 64vh; display: flex; flex-direction: column; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 12px 34px rgba(31, 41, 51, .24); overflow: hidden; }\n[data-edrv-view] .edrv-palette-input { height: 38px; flex: 0 0 auto; padding: 0 12px; border: none; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: transparent; color: var(--dsw-alias-label-primary, #1f2933); font-size: 13px; outline: none; }\n[data-edrv-view] .edrv-palette-list { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 4px; }\n[data-edrv-view] .edrv-palette-row { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-radius: 6px; font-size: 12px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; }\n[data-edrv-view] .edrv-palette-row.edrv-palette-sel { background: var(--dsw-alias-interactive-bg-hover, rgba(15, 157, 88, .10)); }\n[data-edrv-view] .edrv-palette-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-palette-cat { flex: 0 0 auto; font-size: 10px; padding: 1px 7px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-tertiary, #7b8794); }\n[data-edrv-view] .edrv-palette-key { flex: 0 0 auto; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 10px; padding: 1px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 5px; background: var(--dsw-alias-bg-layer-1, #f7f9fa); color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-palette-empty { padding: 14px 10px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #7b8794); text-align: center; }\n[data-edrv-view] .edrv-palette-notice { flex: 0 0 auto; padding: 6px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 11px; color: var(--dsw-alias-state-warn-primary, #b7791f); }\n\n/* 代码片段选择器(配置 / 插入):居中浮窗 + 新建二级弹窗。\n 卡片复用 mcp.css 的 .vsm-mcp-dialog(与「添加 MCP」「工作区选择」同一视觉语言),\n 此处只补遮罩、尺寸与列表布局。⚠️ 根节点须带 data-edrv-view,否则这些规则全部失配。 */\n[data-edrv-view] .edrv-snip-mask { position: fixed; inset: 0; z-index: 195; display: flex; align-items: center; justify-content: center; padding: 24px; box-sizing: border-box; background: var(--dsw-alias-bg-mask-drop, rgba(0, 0, 0, .55)); }\n/* 二级弹窗叠在一级浮窗之上(仍在命令栏之下,见上方阶梯说明) */\n[data-edrv-view] .edrv-snip-mask-top { z-index: 197; }\n[data-edrv-view] .edrv-snip-dialog { display: flex; flex-direction: column; overflow: hidden; width: min(620px, calc(100vw - 48px)); }\n[data-edrv-view] .edrv-snip-hint { margin: 0 0 12px; font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-snip-list { display: flex; flex-direction: column; gap: 1px; min-height: 0; max-height: 46vh; overflow: auto; }\n[data-edrv-view] .edrv-snip-group { padding: 12px 2px 4px; font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-secondary, #aaa); }\n[data-edrv-view] .edrv-snip-row { display: flex; align-items: center; gap: 4px; width: 100%; box-sizing: border-box; padding: 2px; border: none; border-radius: 8px; background: transparent; text-align: left; color: var(--dsw-alias-label-primary, #e6e6e6); font: inherit; }\n[data-edrv-view] .edrv-snip-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, .06)); }\n[data-edrv-view] button.edrv-snip-row { cursor: pointer; }\n[data-edrv-view] .edrv-snip-row-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; padding: 8px 10px; border: none; border-radius: 6px; background: transparent; text-align: left; cursor: pointer; color: inherit; font: inherit; }\n[data-edrv-view] .edrv-snip-label { font-size: 13px; font-weight: 600; color: var(--dsw-alias-label-primary, #e6e6e6); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-snip-desc { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-snip-src { flex: 0 0 auto; padding-right: 6px; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-snip-warn { flex: 0 0 auto; font-size: 11px; padding: 2px 7px; border-radius: 999px; background: var(--dsw-alias-state-warn-tertiary, rgba(229, 185, 63, .16)); color: var(--dsw-alias-state-warn-primary, #e5b93f); white-space: nowrap; }\n[data-edrv-view] .edrv-snip-act { flex: 0 0 auto; width: 26px; height: 26px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 13px; line-height: 1; cursor: pointer; }\n[data-edrv-view] .edrv-snip-act:hover { background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217, 83, 79, .16)); color: var(--dsw-alias-state-error-primary, #f07878); }\n[data-edrv-view] .edrv-snip-empty { padding: 12px 4px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); line-height: 1.6; }\n/* 当前文件语言提示条(片段按语言绑定,明确告知生效范围) */\n[data-edrv-view] .edrv-snip-langbar { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; margin: 0 0 10px; padding: 8px 10px; border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #2b2b2b); font-size: 12px; color: var(--dsw-alias-label-secondary, #aaa); }\n[data-edrv-view] .edrv-snip-langbar b { color: var(--dsw-alias-label-primary, #e6e6e6); }\n/* 二级弹窗的动作按钮(原生 .vsm-mcp-dialog-actions 已提供排版与按钮样式) */\n[data-edrv-view] .edrv-snip-dialog .vsm-mcp-dialog-actions { flex-wrap: wrap; }\n/* 删除图标按钮:.vsm-danger 的规则全部限定在 .vsm-mcp-actions button 之下,裸用无样式,此处补自有类 */\n[data-edrv-view] .edrv-snip-act.edrv-snip-danger:hover { background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217, 83, 79, .16)); color: var(--dsw-alias-state-error-primary, #f07878); }\n/* 浮窗内的原生空态(.vsm-mcp-empty 默认为整页尺寸)压缩为对话框尺寸 */\n[data-edrv-view] .edrv-snip-dialog .vsm-mcp-empty { padding: 26px 16px; border: 0; }\n\n\n\n\n\n/* 调试(DAP):断点圆点 / 停帧行 / 浮动调试工具条。\n 圆点挂在 Monaco glyphMargin(glyphMarginClassName),行背景为 isWholeLine className;\n 工具条绝对定位于编辑区右上(editorArea relative 由 .edrv-editor-area 自身定位)。 */\n/* 断点圆点:装饰类占满整行,圆点经 ::before 在本行盒子内居中(行归属明确、不跨行界) */\n[data-edrv-view] .edrv-dap-bp { cursor: pointer; }\n[data-edrv-view] .edrv-dap-bp::before { content: ''; display: block; width: 7px; height: 7px; margin: 6px auto 0; border-radius: 50%; background: var(--dsw-alias-state-error-primary, #e51400); }\n[data-edrv-view] .edrv-dap-bp-off { cursor: pointer; }\n[data-edrv-view] .edrv-dap-bp-off::before { content: ''; display: block; box-sizing: border-box; width: 7px; height: 7px; margin: 6px auto 0; border-radius: 50%; border: 1px solid var(--dsw-alias-label-tertiary, #9aa5b1); opacity: .6; }\n/* 未验证断点(适配器真实回执 verified:false):空心红圈,对齐 VS Code 空心圆语义 */\n[data-edrv-view] .edrv-dap-bp-unverified { cursor: pointer; }\n[data-edrv-view] .edrv-dap-bp-unverified::before { content: ''; display: block; box-sizing: border-box; width: 7px; height: 7px; margin: 6px auto 0; border-radius: 50%; border: 1px solid var(--dsw-alias-state-error-primary, #e51400); opacity: .75; }\n[data-edrv-view] .edrv-dap-paused-line { background: var(--dsw-alias-state-warn-tertiary, rgba(229, 185, 63, .22)); }\n[data-edrv-view] .edrv-dap-paused-glyph { cursor: pointer; }\n[data-edrv-view] .edrv-dap-paused-glyph::before { content: ''; display: block; width: 5px; height: 12px; margin: 4px auto 0; border-radius: 2px; background: var(--dsw-alias-state-warn-primary, #e5b93f); }\n[data-edrv-view] .edrv-editor-area { position: relative; }\n/* launch.json 右下角「添加配置」按钮(VSCode 同款形态):仅 launch.json 打开时渲染 */\n[data-edrv-view] .edrv-addcfg { position: absolute; right: 14px; bottom: 10px; z-index: 13; padding: 4px 12px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #f7f9fa); color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; line-height: 18px; cursor: pointer; box-shadow: 0 1px 4px rgba(0, 0, 0, .1); }\n[data-edrv-view] .edrv-addcfg:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .15)); color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-dapbar { position: absolute; top: 6px; right: 14px; z-index: 12; display: none; align-items: center; gap: 3px; padding: 3px 5px; border-radius: 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, #f7f9fa); box-shadow: 0 2px 8px rgba(0, 0, 0, .12); }\n[data-edrv-view] .edrv-dapbar.on { display: flex; border-color: var(--dsw-alias-border-l2, #cdd6da); }\n[data-edrv-view] .edrv-dapbar-grip { flex: 0 0 auto; width: 16px; height: 22px; display: inline-flex; align-items: center; justify-content: center; cursor: grab; user-select: none; color: var(--dsw-alias-label-tertiary, #7b8794); font-size: 12px; }\n[data-edrv-view] .edrv-dapbar-grip:active, [data-edrv-view] .edrv-dapbar.dragging .edrv-dapbar-grip { cursor: grabbing; }\n[data-edrv-view] .edrv-dapbar.dragging { user-select: none; }\n[data-edrv-view] .edrv-dapbar-cfg { max-width: 220px; height: 22px; font-size: 11px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); }\n[data-edrv-view] .edrv-dapbar-btn { min-width: 24px; height: 22px; padding: 0 6px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, inherit); font-size: 12px; line-height: 1; cursor: pointer; }\n[data-edrv-view] .edrv-dapbar-btn:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .15)); }\n[data-edrv-view] .edrv-dapbar-btn:disabled { opacity: .35; cursor: default; }\n[data-edrv-view] .edrv-dapbar-phase { padding: 0 4px; font-size: 11px; white-space: nowrap; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-dapbar-phase.paused { color: var(--dsw-alias-state-warn-primary, #b7791f); font-weight: 600; }\n[data-edrv-view] .edrv-dapbar-err { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; color: var(--dsw-alias-state-error-primary, #e05252); }\n\n/* 调试面板(活动栏「调试」页签):四段折叠区 + 断点列表 + REPL。 */\n[data-edrv-view] .edrv-dappanel { display: flex; flex-direction: column; gap: 10px; height: 100%; min-height: 0; overflow: auto; padding: 2px 2px 8px; }\n[data-edrv-view] .edrv-dappanel-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; row-gap: 4px; font-size: 12px; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-dappanel-phase { padding: 2px 8px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); }\n[data-edrv-view] .edrv-dappanel-phase.paused { background: var(--dsw-alias-state-warn-tertiary, rgba(229, 185, 63, .18)); color: var(--dsw-alias-state-warn-primary, #b7791f); font-weight: 600; }\n[data-edrv-view] .edrv-dappanel-cfg { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-dappanel-pid { font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; }\n[data-edrv-view] .edrv-dappanel-err { color: var(--dsw-alias-state-error-primary, #e05252); }\n[data-edrv-view] .edrv-dapseg { display: flex; flex-direction: column; gap: 2px; min-height: 0; }\n[data-edrv-view] .edrv-dapseg-body { min-height: 0; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; }\n/* 段间分隔条(splitview sash):拖动调整相邻两段高度占比;各段高度由 DebugPanel 内联接管。 */\n[data-edrv-view] .edrv-dapseg-sash { flex: 0 0 auto; height: 4px; margin: 2px 0; border-radius: 2px; background: transparent; cursor: row-resize; }\n[data-edrv-view] .edrv-dapseg-sash:hover, [data-edrv-view] .edrv-dapseg-sash.dragging { background: var(--dsw-alias-border-l2, #cdd6da); }\n/* 折叠段头:chevron + 标题 + 悬浮动作区(动作 hover 显隐;REPL 头不参与折叠)。 */\n[data-edrv-view] .edrv-dapseg-head { display: flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-tertiary, #7b8794); padding: 2px 2px; cursor: pointer; border-radius: 4px; }\n[data-edrv-view] .edrv-dapseg-head:hover { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-daprepl .edrv-dapseg-head { cursor: default; padding: 2px 0; }\n[data-edrv-view] .edrv-dapseg-chevron { flex: 0 0 auto; width: 14px; text-align: center; font-size: 10px; }\n[data-edrv-view] .edrv-dapseg-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-dapseg-act { flex: 0 0 auto; min-width: 20px; height: 18px; padding: 0 4px; border: none; border-radius: 4px; background: transparent; color: var(--dsw-alias-label-tertiary, #7b8794); font-size: 11px; line-height: 1; cursor: pointer; opacity: 0; }\n[data-edrv-view] .edrv-dapseg-head:hover .edrv-dapseg-act { opacity: 1; }\n[data-edrv-view] .edrv-dapseg-act:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .15)); color: var(--dsw-alias-label-primary, inherit); }\n[data-edrv-view] .edrv-dapempty { font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); padding: 4px 2px; }\n[data-edrv-view] .edrv-dapframe { display: flex; align-items: baseline; gap: 8px; padding: 3px 4px; border-radius: 6px; cursor: pointer; font-size: 12px; }\n[data-edrv-view] .edrv-dapframe:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12)); }\n[data-edrv-view] .edrv-dapframe-name { color: var(--dsw-alias-label-primary, inherit); font-weight: 600; }\n[data-edrv-view] .edrv-dapframe-loc { color: var(--dsw-alias-label-tertiary, #9aa5b1); font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-dapscope { font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-secondary, #52606d); padding: 4px 2px 1px; }\n[data-edrv-view] .edrv-dapvar { display: flex; align-items: baseline; gap: 6px; width: 100%; box-sizing: border-box; font-size: 12px; padding: 1px 2px; min-width: 0; }\n[data-edrv-view] .edrv-dapvar-tw { flex: 0 0 auto; width: 14px; border: none; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); cursor: pointer; font-size: 10px; padding: 0; }\n[data-edrv-view] span.edrv-dapvar-tw { cursor: default; }\n[data-edrv-view] .edrv-dapvar-name { flex: 0 1 34%; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-primary, inherit); font-family: var(--ds-font-family-code, ui-monospace, monospace); white-space: nowrap; }\n[data-edrv-view] .edrv-dapvar-val { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-dapvar-type { flex: 0 0 auto; font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-dapframe.selected { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .16)); outline: 1px solid var(--dsw-alias-border-l2, #cdd6da); }\n[data-edrv-view] .edrv-dapwatch { display: flex; align-items: baseline; gap: 6px; width: 100%; box-sizing: border-box; min-width: 0; font-size: 12px; padding: 1px 2px; }\n[data-edrv-view] .edrv-dapwatch-x { border: none; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); cursor: pointer; width: 14px; }\n[data-edrv-view] .edrv-dapwatch-x:hover { color: var(--dsw-alias-state-error-primary, #e05252); }\n[data-edrv-view] .edrv-dapwatch-add { padding: 2px 0; }\n[data-edrv-view] .edrv-dapbp { display: flex; align-items: center; gap: 6px; font-size: 12px; padding: 2px 2px; border-radius: 6px; cursor: pointer; }\n[data-edrv-view] .edrv-dapbp:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12)); }\n[data-edrv-view] .edrv-dapbp:active { background: var(--dsw-alias-interactive-bg-pressed, rgba(127, 127, 127, .2)); }\n[data-edrv-view] .edrv-dapbp-dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; background: var(--dsw-alias-state-error-primary, #e51400); }\n[data-edrv-view] .edrv-dapbp-dot.off { background: transparent; border: 1.5px solid var(--dsw-alias-label-tertiary, #9aa5b1); opacity: .6; }\n[data-edrv-view] .edrv-dapbp-file { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border: none; background: transparent; color: var(--dsw-alias-label-primary, inherit); cursor: pointer; padding: 0; font-size: 12px; }\n[data-edrv-view] .edrv-dapbp-dir { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; text-align: right; }\n[data-edrv-view] .edrv-dapbp-line { flex: 0 0 auto; font-size: 10px; padding: 1px 6px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-dapbp-x { flex: 0 0 auto; border: none; background: transparent; color: transparent; cursor: pointer; width: 14px; }\n[data-edrv-view] .edrv-dapbp:hover .edrv-dapbp-x { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-dapbp-x:hover { color: var(--dsw-alias-state-error-primary, #e05252) !important; }\n[data-edrv-view] .edrv-daprepl { display: flex; flex-direction: column; gap: 4px; }\n[data-edrv-view] .edrv-daprepl-log { display: flex; flex-direction: column; gap: 1px; max-height: 180px; overflow: auto; }\n[data-edrv-view] .edrv-daprepl-line { font-size: 11px; font-family: var(--ds-font-family-code, ui-monospace, monospace); color: var(--dsw-alias-label-secondary, #52606d); white-space: pre-wrap; word-break: break-all; display: flex; gap: 6px; }\n[data-edrv-view] .edrv-daprepl-line.out { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-daprepl-in { width: 100%; box-sizing: border-box; height: 26px; padding: 0 8px; font-size: 12px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); }\n[data-edrv-view] .edrv-dappanel-run { flex: 0 0 auto; width: 24px; height: 24px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, inherit); font-size: 13px; line-height: 1; cursor: pointer; }\n[data-edrv-view] .edrv-dappanel-run:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .15)); }\n[data-edrv-view] .edrv-dappanel-run:disabled { opacity: .35; cursor: default; background: transparent; }\n/* 面板顶部配置下拉(与编辑器浮动条 .edrv-dapbar-cfg 同视觉)。 */\n[data-edrv-view] .edrv-dappanel-cfg-select { max-width: 190px; height: 24px; padding: 0 4px; font-size: 11px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); }\n/* 断点行内编辑行(面板内展开编辑,控件复用 .edrv-bpwidget-* 视觉;左缩进对齐断点行文字列)。 */\n[data-edrv-view] .edrv-dapbp-edit { display: flex; align-items: center; gap: 6px; padding: 3px 2px 3px 20px; border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); }\n[data-edrv-view] .edrv-dapbp-edit .edrv-bpwidget-mode { height: 20px; }\n[data-edrv-view] .edrv-dapbp-edit .edrv-bpwidget-input { height: 20px; }\n/* 断点回执:未验证空心红圈(须排在 .off 之后,禁用态优先展示灰圈语义)。 */\n[data-edrv-view] .edrv-dapbp-dot.unverified { background: transparent; border: 1.5px solid var(--dsw-alias-state-error-primary, #e51400); opacity: .75; }\n/* 断点行内编辑浮层(Monaco content widget,锚定断点行上方;对齐 VS Code breakpointWidget 形态)。 */\n/* View Zone 浮层:宽度/左移由 BpWidget.syncWidth 按 layout 内联设置\n (Monaco .view-zones 仅内容区宽,需补偿 contentLeft 与滚动条),此处只管视觉。 */\n[data-edrv-view] .edrv-bpwidget { position: relative; box-sizing: border-box; display: flex; align-items: center; gap: 6px; padding: 3px 8px; border-top: 1px solid var(--dsw-alias-border-l2, #cdd6da); border-bottom: 1px solid var(--dsw-alias-border-l2, #cdd6da); background: var(--dsw-alias-bg-layer-1, #f7f9fa); }\n/* 断点编辑中的目标行高亮(浮层打开期间) */\n[data-edrv-view] .edrv-dap-editing-line { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12)); }\n[data-edrv-view] .edrv-bpwidget-mode { flex: 0 0 auto; height: 22px; padding: 0 4px; font-size: 12px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 3px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); cursor: pointer; }\n[data-edrv-view] .edrv-bpwidget-input { flex: 1 1 auto !important; min-width: 0; width: auto !important; box-sizing: border-box; height: 22px; padding: 0 6px; font-size: 12px; font-family: var(--ds-font-family-code, ui-monospace, monospace); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 3px; background: var(--dsw-alias-bg-base, transparent); color: var(--dsw-alias-label-primary, inherit); }\n[data-edrv-view] .edrv-bpwidget-input:focus { outline: 1px solid var(--dsw-alias-border-l2, #cdd6da); }\n\n/* 断点 hover 预览(glyph 区):半透明小红点 + pointer 光标(对齐 VS Code .codicon-debug-hint)。 */\n[data-edrv-view] .edrv-dap-bp-hint { cursor: pointer; }\n[data-edrv-view] .edrv-dap-bp-hint::before { content: ''; display: block; width: 7px; height: 7px; margin: 6px auto 0; border-radius: 50%; background: var(--dsw-alias-state-error-primary, #e51400); opacity: .4; }\n/* 行号区也给出可点提示(与 VS Code 一致:glyph 区与行号区都可点/可 hover);\n 不拦截折叠箭头等自有控件的光标。 */\n[data-edrv-view] .edrv-editor-row .monaco-editor .margin-view-overlays .line-numbers:hover { cursor: pointer; }\n\n/* 暂停态 hover 调试块 + 变量树(成员全量直显 + table 成员折叠展开)。\n ⚠️ 选择器一律用 data-*:本仓 vendored Monaco 的 markdown→DOM 管线会剥掉我们 HTML 里的\n class(supportHtml/isTrusted 都不豁免),只有 data-* 存活;用 class 会静默失效。 */\n[data-edrv-view] [data-edrv-dap] { display: block; min-width: 0; }\n[data-edrv-view] [data-edrv-head] { display: flex; align-items: baseline; gap: 2px; }\n[data-edrv-view] [data-edrv-head] [data-edrv-expr] { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] [data-edrv-head] [data-edrv-val] { font-family: var(--ds-font-family-code, ui-monospace, monospace); word-break: break-all; }\n[data-edrv-view] [data-edrv-type] { margin-top: 2px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #8a949b); }\n[data-edrv-view] [data-edrv-tree] { margin: 2px 0 0; padding: 0; list-style: none; font-variant-numeric: tabular-nums; }\n[data-edrv-view] [data-edrv-line] { display: flex; align-items: baseline; gap: 4px; padding: 1px 2px; border-radius: 3px; white-space: nowrap; }\n[data-edrv-view] [data-edrv-line]:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12)); }\n/* 折叠按钮(对齐 CodeBuddy 调试浮窗变量树的 twist):热区 14px + hover 反馈,未可展开时留空占位 */\n[data-edrv-view] [data-edrv-twist] { flex: 0 0 auto; box-sizing: border-box; width: 14px; height: 14px; line-height: 13px; text-align: center; border-radius: 3px; color: var(--dsw-alias-label-tertiary, #8a949b); }\n[data-edrv-view] [data-edrv-twist]:not(:empty) { cursor: pointer; user-select: none; }\n[data-edrv-view] [data-edrv-twist]:not(:empty):hover { background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .2)); color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] [data-edrv-name] { flex: 0 0 auto; color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] [data-edrv-line] [data-edrv-eq] { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary, #8a949b); }\n[data-edrv-view] [data-edrv-line] [data-edrv-val] { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] [data-edrv-note] { color: var(--dsw-alias-label-tertiary, #8a949b); font-style: italic; }\n/* Alt 提示层:由 hoverTree 注入到 .monaco-hover(滚动区之外)贴底常驻;容器预留同高内边距避免遮挡末行。\n 不再用 sticky / `:has()`:滚动链在 .monaco-hover-content 上,sticky 会被滚走。 */\n[data-edrv-view] .monaco-hover[data-edrv-hint-host=\"1\"] { padding-bottom: 22px; }\n[data-edrv-view] [data-edrv-hint=\"1\"] { position: absolute; left: 0; right: 0; bottom: 0; box-sizing: border-box; height: 22px; padding: 2px 8px; font-size: 11px; line-height: 18px; color: var(--dsw-alias-label-tertiary, #8a949b); background: var(--dsw-alias-bg-overlay, #ffffff); border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n\n/* Workbench panes: existing theme, action-driven drop previews. ddj 2026年10月14号 */\n[data-edrv-view] .edrv-group, [data-edrv-view] .edrv-group-body,\n[data-edrv-view] .edrv-full-slot, [data-edrv-view] .edrv-full-view,\n[data-edrv-view] .edrv-passive-view { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-group { border: 0; }\n[data-edrv-view] .edrv-group .edrv-tabs { flex: 0 0 auto; touch-action: none; }\n[data-edrv-view] .edrv-group-tab-name { overflow: hidden; text-overflow: ellipsis; max-width: 180px; }\n[data-edrv-view] .edrv-split { display: flex; flex: 1 1 auto; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-split-vertical { flex-direction: column; }\n[data-edrv-view] .edrv-split-child { display: flex; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-split-handle { flex: 0 0 5px; background: var(--dsw-alias-border-l1, #e0e6e8); cursor: col-resize; touch-action: none; }\n[data-edrv-view] .edrv-split-vertical > .edrv-split-handle { cursor: row-resize; }\n[data-edrv-view] .edrv-split-handle:hover, [data-edrv-view] .edrv-split-handle:focus-visible { background: var(--dsw-alias-state-info-primary, #4d6bfe); }\n[data-edrv-view].edrv-floating { position: fixed; display: flex; flex-direction: column; overflow: hidden; box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l2, #cdd6da); background: var(--dsw-alias-bg-base, #fff); color: var(--dsw-alias-label-primary, #1f2933); box-shadow: 0 8px 28px rgba(0,0,0,.22); }\n[data-edrv-view] .edrv-floating-title { display: flex; align-items: center; gap: 8px; flex: 0 0 32px; padding: 0 8px; background: var(--dsw-alias-bg-layer-1, #f7f9fa); cursor: move; touch-action: none; user-select: none; }\n[data-edrv-view] .edrv-floating-title > span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-floating-content { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; overflow: auto; box-sizing: border-box; }\n[data-edrv-view] .edrv-floating-content.edrv-svnlog-dialog { padding: 12px; overflow: auto; }\n[data-edrv-view] .edrv-floating-content.edrv-svn-patch-dialog,\n[data-edrv-view] .edrv-floating-content.edrv-svn-sum-dialog,\n[data-edrv-view] .edrv-floating-content.edrv-svnplan-dialog { padding: 12px; max-width: none; gap: 8px; }\n[data-edrv-view] .edrv-floating-content.edrv-svnlog-stats { max-height: none; }\n[data-edrv-view] .edrv-floating-content .edrv-svn-patch-pre,\n[data-edrv-view] .edrv-floating-content .edrv-svn-sum-list { flex: 1; min-height: 0; max-height: none; }\n[data-edrv-view] .edrv-floating-content .edrv-svnlog-filter,\n[data-edrv-view] .edrv-floating-content .edrv-svn-sum-form { flex-wrap: wrap; }\n[data-edrv-view] .edrv-floating-content > .edrv-svnlog-head,\n[data-edrv-view] .edrv-floating-content > .edrv-svnlog-filter,\n[data-edrv-view] .edrv-floating-content > .edrv-svnlog-foot { flex-shrink: 0; }\n[data-edrv-view] .edrv-floating-title > button { border: 0; background: transparent; color: inherit; cursor: pointer; }\n[data-edrv-view] .edrv-floating-grip { position: absolute; width: 16px; height: 16px; bottom: 0; right: 0; cursor: nwse-resize; touch-action: none; border-right: 3px solid var(--dsw-alias-border-l2, #cdd6da); border-bottom: 3px solid var(--dsw-alias-border-l2, #cdd6da); }\n.edrv-drag-overlay { position: fixed; z-index: 150; pointer-events: none; box-sizing: border-box; border: 0; background: color-mix(in srgb, var(--dsw-alias-state-info-primary, #4d6bfe) 25%, transparent); }\n.edrv-drop-label, .edrv-drag-ghost { position: fixed; z-index: 151; pointer-events: none; padding: 4px 8px; background: var(--dsw-alias-bg-overlay, #fff); color: var(--dsw-alias-label-primary, #1f2933); border: 1px solid var(--dsw-alias-border-l2, #cdd6da); font-size: 12px; white-space: nowrap; }\n.edrv-drop-label { position: absolute; left: 4px; top: 4px; }\n[data-edrv-view] .edrv-tab:focus-visible { outline: 2px solid var(--dsw-alias-state-info-primary, #4d6bfe); outline-offset: -2px; }\n@media (prefers-reduced-motion: reduce) { .edrv-drag-overlay { transition: none; } }\n";
59
+ const tagId$4 = "dsh-vscode-mode/editor.css";
60
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
61
+ const tag = document.createElement("style");
62
+ tag.dataset.plugin = "dsh-vscode-mode";
63
+ tag.dataset.pluginCss = tagId$4;
64
+ tag.textContent = css$4;
65
+ document.head.appendChild(tag);
66
+ }
67
+ //#endregion
68
+ //#region \0dsh-css:/home/runner/work/DSH_VsCodeMode/DSH_VsCodeMode/src/client/styles/quickmenu.css.mjs
69
+ const css$3 = "/* dsh-vscode-mode — 快捷菜单(悬浮按钮 + SAO 风格多列级联菜单 + 配置弹窗)\n * 作者 ddj 2026年10月30号\n *\n * 浮层约定:根节点带 data-edrv-view(editor.css 的浮层样式同款作用域),\n * 颜色全部走 DSH 设计令牌并带浅色兜底,避免深/浅主题下不可读。\n *\n * 设计取向:整个菜单只花一次「亮色」——当前行的 2px 强调条与悬浮按钮的强调色;\n * 其余全部收成中性灰阶与发丝线,靠层级、间距和一处滑入动效表达结奏,\n * 不做每行图标、不做渐变装饰、不做逐卡片阴影。\n */\n\n/* ── 令牌 ─────────────────────────────────────────────────── */\n/* 定义在 [data-edrv-view] 上:菜单(portal 到 body 的独立根)与 ModalShell 弹窗\n * 各自是不同根节点,令牌若只挂在菜单根上,弹窗里的按钮/芯片会取不到颜色。 */\n[data-edrv-view] {\n --qm-surface: var(--dsw-alias-bg-overlay, #ffffff);\n --qm-sunken: var(--dsw-alias-bg-layer-1, #f4f7fa);\n --qm-ink: var(--dsw-alias-label-primary, #1f2933);\n --qm-ink-2: var(--dsw-alias-label-secondary, #55616e);\n --qm-ink-3: var(--dsw-alias-label-tertiary, #8a95a1);\n --qm-line: var(--dsw-alias-border-l2, #d8dfe6);\n --qm-accent: var(--dsw-alias-brand-primary, #2f6fed);\n --qm-accent-soft: var(--dsw-alias-interactive-bg-hover, rgba(47, 111, 237, .1));\n --qm-hover: var(--dsw-alias-interactive-bg-hover, rgba(31, 41, 51, .06));\n --qm-warn: var(--dsw-alias-state-warn-primary, #cf8b1f);\n --qm-danger: var(--dsw-alias-state-error-primary, #d64545);\n --qm-radius: 10px;\n --qm-row: 34px;\n --qm-elev:\n 0 12px 30px -12px rgba(16, 24, 40, .3),\n 0 2px 6px -2px rgba(16, 24, 40, .12);\n}\n\n/* 根层:铺满浮层、默认点击穿透;z-index 要压过同槽位的常驻浮层(如音乐条),否则菜单被盖住 */\n[data-edrv-view].edrv-qm-root {\n position: absolute;\n inset: 0;\n pointer-events: none;\n z-index: 60;\n font-size: 12px;\n line-height: 1.4;\n}\n\n/* ── 悬浮按钮 ─────────────────────────────────────────────── */\n[data-edrv-view] .edrv-qm-launcher {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: 9px;\n border: 1px solid var(--qm-line);\n background: var(--qm-surface);\n color: var(--qm-accent);\n box-shadow: 0 1px 2px rgba(16, 24, 40, .12);\n pointer-events: auto;\n cursor: grab;\n touch-action: none;\n user-select: none;\n transition: transform .14s cubic-bezier(.2, .8, .3, 1), box-shadow .14s ease, border-color .14s ease;\n}\n\n[data-edrv-view] .edrv-qm-launcher:hover {\n transform: translateY(-1px);\n border-color: var(--qm-accent);\n box-shadow: 0 4px 14px -4px rgba(16, 24, 40, .3), 0 0 0 3px var(--qm-accent-soft);\n}\n\n[data-edrv-view] .edrv-qm-launcher:active {\n transform: scale(.96);\n}\n\n[data-edrv-view] .edrv-qm-launcher--drag {\n cursor: grabbing;\n transform: scale(1.04);\n box-shadow: 0 8px 20px -6px rgba(16, 24, 40, .38);\n}\n\n[data-edrv-view] .edrv-qm-launcher--open {\n border-color: var(--qm-accent);\n background: var(--qm-accent);\n color: #fff;\n box-shadow: 0 0 0 3px var(--qm-accent-soft);\n}\n\n/* 隐藏态:半透明小圆点(仍可拖动/点开,防止关掉后无处恢复) */\n[data-edrv-view] .edrv-qm-launcher--mini {\n width: 12px;\n height: 12px;\n border-radius: 50%;\n border-color: transparent;\n opacity: .4;\n box-shadow: none;\n transition: opacity .14s ease, transform .14s ease;\n}\n\n[data-edrv-view] .edrv-qm-launcher--mini:hover {\n opacity: .9;\n transform: scale(1.2);\n box-shadow: none;\n}\n\n[data-edrv-view] .edrv-qm-launcher svg {\n width: 19px;\n height: auto;\n display: block;\n pointer-events: none;\n}\n\n/* ── 菜单容器与列 ─────────────────────────────────────────── */\n[data-edrv-view] .edrv-qm-menu {\n position: absolute;\n pointer-events: auto;\n}\n\n/* 菜单本体(portal 到 body):铺满视口的穿透层,层级高于输入框卡片与其它浮层条目 */\n[data-edrv-view].edrv-qm-menu {\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 150;\n}\n\n[data-edrv-view] .edrv-qm-col {\n position: absolute;\n display: flex;\n flex-direction: column;\n width: 220px;\n box-sizing: border-box;\n pointer-events: auto;\n border-radius: var(--qm-radius);\n border: 1px solid var(--qm-line);\n background: var(--qm-surface);\n box-shadow: var(--qm-elev);\n overflow: hidden;\n animation: edrv-qm-col-in .14s cubic-bezier(.2, .8, .3, 1);\n}\n\n@keyframes edrv-qm-col-in {\n from { opacity: 0; transform: translateX(-6px); }\n to { opacity: 1; transform: none; }\n}\n\n[data-edrv-view] .edrv-qm-scroll {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n overflow-x: hidden;\n padding: 8px 7px;\n scrollbar-width: none;\n}\n\n/* 上下渐隐只在内容确实超出列高时启用(短列被遮掉会看不清) */\n[data-edrv-view] .edrv-qm-scroll--fade {\n mask-image: linear-gradient(to bottom, transparent 0, #000 36px, #000 calc(100% - 36px), transparent 100%);\n -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 36px, #000 calc(100% - 36px), transparent 100%);\n}\n\n[data-edrv-view] .edrv-qm-scroll::-webkit-scrollbar {\n width: 0;\n height: 0;\n}\n\n[data-edrv-view] .edrv-qm-list {\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n/* ── 行 ──────────────────────────────────────────────────── */\n[data-edrv-view] .edrv-qm-row {\n position: relative;\n display: flex;\n align-items: center;\n height: var(--qm-row);\n min-height: var(--qm-row);\n box-sizing: border-box;\n padding: 0 7px 0 10px;\n border-radius: 7px;\n color: var(--qm-ink);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n user-select: none;\n transform-origin: center center;\n transition: background .1s ease, color .1s ease;\n}\n\n[data-edrv-view] .edrv-qm-row:hover,\n[data-edrv-view] .edrv-qm-row--active {\n background: var(--qm-hover);\n}\n\n/* 唯一的强调色落点:当前行左缘的短竖条 */\n[data-edrv-view] .edrv-qm-row--active::before {\n content: '';\n position: absolute;\n left: 3px;\n top: 50%;\n width: 2px;\n height: 15px;\n margin-top: -7.5px;\n border-radius: 2px;\n background: var(--qm-accent);\n}\n\n[data-edrv-view] .edrv-qm-row:focus-visible,\n[data-edrv-view] .edrv-qm-launcher:focus-visible,\n[data-edrv-view] .edrv-qm-foot:focus-visible,\n[data-edrv-view] .edrv-qm-btn:focus-visible,\n[data-edrv-view] .edrv-qm-input:focus-visible,\n[data-edrv-view] .edrv-qm-textarea:focus-visible,\n[data-edrv-view] .edrv-qm-select:focus-visible,\n[data-edrv-view] .edrv-qm-chip:focus-visible {\n outline: 2px solid var(--qm-accent);\n outline-offset: 1px;\n}\n\n[data-edrv-view] .edrv-qm-row-label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* 组行只留一枚折角箭头,文字「组」属于冗余信息 */\n[data-edrv-view] .edrv-qm-row-chevron {\n flex: 0 0 auto;\n display: flex;\n align-items: center;\n color: var(--qm-ink-3);\n transition: color .1s ease, transform .12s ease;\n}\n\n[data-edrv-view] .edrv-qm-row-chevron svg {\n width: 12px;\n height: 12px;\n display: block;\n}\n\n[data-edrv-view] .edrv-qm-row--group:hover .edrv-qm-row-chevron,\n[data-edrv-view] .edrv-qm-row--group.edrv-qm-row--active .edrv-qm-row-chevron {\n color: var(--qm-accent);\n transform: translateX(1px);\n}\n\n[data-edrv-view] .edrv-qm-row-kind {\n flex: 0 0 auto;\n color: var(--qm-ink-3);\n font-size: 10px;\n font-weight: 400;\n}\n\n[data-edrv-view] .edrv-qm-row-badge {\n flex: 0 0 auto;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--qm-warn);\n}\n\n[data-edrv-view] .edrv-qm-row-badge--error {\n background: var(--qm-danger);\n}\n\n/* 行尾「⋯」:占位常驻,避免悬停时行内容左右跳动 */\n[data-edrv-view] .edrv-qm-row-more {\n flex: 0 0 auto;\n width: 18px;\n height: 18px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 0;\n border-radius: 5px;\n background: transparent;\n color: var(--qm-ink-3);\n cursor: pointer;\n font-size: 13px;\n line-height: 1;\n opacity: 0;\n transition: opacity .1s ease, background .1s ease, color .1s ease;\n}\n\n[data-edrv-view] .edrv-qm-row:hover .edrv-qm-row-more,\n[data-edrv-view] .edrv-qm-row--active .edrv-qm-row-more,\n[data-edrv-view] .edrv-qm-row-more:focus-visible {\n opacity: 1;\n}\n\n[data-edrv-view] .edrv-qm-row-more:hover {\n background: var(--qm-surface);\n color: var(--qm-accent);\n}\n\n[data-edrv-view] .edrv-qm-row--add {\n margin-top: 5px;\n border: 1px dashed var(--qm-line);\n color: var(--qm-ink-2);\n font-size: 11.5px;\n font-weight: 400;\n justify-content: center;\n}\n\n[data-edrv-view] .edrv-qm-row--add:hover {\n border-color: var(--qm-accent);\n color: var(--qm-accent);\n background: var(--qm-accent-soft);\n}\n\n/* ── 底部固定栏(菜单设置) ──────────────────────────────── */\n[data-edrv-view] .edrv-qm-foot {\n display: flex;\n align-items: center;\n gap: 6px;\n flex: 0 0 32px;\n padding: 0 10px;\n border-top: 1px solid var(--qm-line);\n background: var(--qm-sunken);\n color: var(--qm-ink-2);\n font-size: 11.5px;\n cursor: pointer;\n user-select: none;\n transition: color .1s ease, background .1s ease;\n}\n\n[data-edrv-view] .edrv-qm-foot:hover {\n color: var(--qm-accent);\n background: var(--qm-hover);\n}\n\n[data-edrv-view] .edrv-qm-foot-icon {\n display: flex;\n align-items: center;\n}\n\n[data-edrv-view] .edrv-qm-foot-icon svg {\n width: 13px;\n height: 13px;\n display: block;\n}\n\n/* ── 空态:邀请而不是气氛 ────────────────────────────────── */\n[data-edrv-view] .edrv-qm-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 3px;\n padding: 14px 10px 12px;\n text-align: center;\n color: var(--qm-ink-3);\n font-size: 11px;\n font-weight: 400;\n}\n\n[data-edrv-view] .edrv-qm-empty-title {\n color: var(--qm-ink-2);\n font-size: 12px;\n font-weight: 600;\n}\n\n[data-edrv-view] .edrv-qm-empty .edrv-qm-btn {\n margin-top: 7px;\n}\n\n/* ── 弹窗表单 ────────────────────────────────────────────── */\n[data-edrv-view] .edrv-qm-dialog {\n width: min(620px, calc(100vw - 48px));\n max-height: min(84vh, 720px);\n box-sizing: border-box;\n /* 复位 .vsm-mcp-dialog 的整卡内边距(本弹窗自带 头/体/脚 三段布局) */\n padding: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n/* 固定头:长表单滚动时仍知道在编辑哪一项 */\n[data-edrv-view] .edrv-qm-head {\n flex: 0 0 auto;\n padding: 14px 16px 12px;\n border-bottom: 1px solid var(--qm-line);\n}\n\n[data-edrv-view] .edrv-qm-head-title {\n color: var(--qm-ink);\n font-size: 14px;\n font-weight: 650;\n line-height: 1.35;\n}\n\n[data-edrv-view] .edrv-qm-head-sub {\n margin-top: 3px;\n color: var(--qm-ink-3);\n font-size: 11px;\n font-weight: 400;\n}\n\n[data-edrv-view] .edrv-qm-sep {\n height: 1px;\n margin: 2px 0;\n background: var(--qm-line);\n opacity: .75;\n}\n\n[data-edrv-view] .edrv-qm-dialog-body {\n flex: 1 1 auto;\n min-height: 0;\n padding: 14px 16px;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n[data-edrv-view] .edrv-qm-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n /* 抵消 .vsm-mcp-dialog label 的 12px 上下外边距(与 body 的 gap 重复) */\n margin: 0;\n}\n\n[data-edrv-view] .edrv-qm-field-label {\n color: var(--qm-ink-2);\n font-size: 11px;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n[data-edrv-view] .edrv-qm-field-hint {\n color: var(--qm-ink-3);\n font-size: 10px;\n font-weight: 400;\n}\n\n/* 标签行:标签在左、紧凑控件(分段)在右,避免控件挤成两行 */\n[data-edrv-view] .edrv-qm-field-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n min-height: 18px;\n}\n\n/* 分组区块:只用标题左侧的强调条分隔,不堆卡片、不加阴影 */\n[data-edrv-view] .edrv-qm-section {\n display: flex;\n flex-direction: column;\n gap: 9px;\n}\n\n[data-edrv-view] .edrv-qm-section-head {\n position: relative;\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding-left: 9px;\n}\n\n[data-edrv-view] .edrv-qm-section-head::before {\n content: '';\n position: absolute;\n left: 0;\n top: 3px;\n bottom: 3px;\n width: 3px;\n border-radius: 2px;\n background: var(--qm-accent);\n}\n\n[data-edrv-view] .edrv-qm-section-title {\n color: var(--qm-ink);\n font-size: 11.5px;\n font-weight: 650;\n}\n\n[data-edrv-view] .edrv-qm-section-hint {\n color: var(--qm-ink-3);\n font-size: 10px;\n font-weight: 400;\n}\n\n[data-edrv-view] .edrv-qm-section-body {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n/* 开关行:左侧标题与说明,右侧滑动开关 */\n[data-edrv-view] .edrv-qm-switch-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n[data-edrv-view] .edrv-qm-switch-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n[data-edrv-view] .edrv-qm-switch {\n position: relative;\n flex: 0 0 auto;\n box-sizing: border-box;\n width: 34px;\n height: 19px;\n padding: 0;\n border: 1px solid var(--qm-line);\n border-radius: 999px;\n background: var(--qm-sunken);\n cursor: pointer;\n transition: background .12s ease, border-color .12s ease;\n}\n\n[data-edrv-view] .edrv-qm-switch-knob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 13px;\n height: 13px;\n border-radius: 50%;\n background: var(--dsw-alias-bg-base, #fff);\n box-shadow: 0 1px 2px rgba(0, 0, 0, .18);\n transition: transform .12s ease;\n}\n\n[data-edrv-view] .edrv-qm-switch--on {\n border-color: var(--qm-accent);\n background: var(--qm-accent);\n}\n\n[data-edrv-view] .edrv-qm-switch--on .edrv-qm-switch-knob {\n transform: translateX(15px);\n}\n\n[data-edrv-view] .edrv-qm-switch:focus-visible {\n outline: 2px solid var(--qm-accent);\n outline-offset: 2px;\n}\n\n[data-edrv-view] .edrv-qm-switch:disabled {\n opacity: .5;\n cursor: not-allowed;\n}\n\n/* 字段里的按钮按内容宽度靠左,不拉满整行 */\n[data-edrv-view] .edrv-qm-field > .edrv-qm-btn {\n align-self: flex-start;\n}\n\n[data-edrv-view] .edrv-qm-input,\n[data-edrv-view] .edrv-qm-textarea,\n[data-edrv-view] .edrv-qm-select {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid var(--qm-line);\n border-radius: 7px;\n background: var(--dsw-alias-bg-base, #fff);\n color: var(--qm-ink);\n font: inherit;\n padding: 6px 9px;\n transition: border-color .1s ease, box-shadow .1s ease;\n}\n\n[data-edrv-view] .edrv-qm-input:focus,\n[data-edrv-view] .edrv-qm-textarea:focus,\n[data-edrv-view] .edrv-qm-select:focus {\n outline: none;\n border-color: var(--qm-accent);\n box-shadow: 0 0 0 3px var(--qm-accent-soft);\n}\n\n[data-edrv-view] .edrv-qm-textarea {\n min-height: 96px;\n resize: vertical;\n font-family: ui-monospace, SFMono-Regular, Consolas, monospace;\n font-size: 11.5px;\n line-height: 1.55;\n}\n\n[data-edrv-view] .edrv-qm-input:disabled,\n[data-edrv-view] .edrv-qm-select:disabled {\n background: var(--qm-sunken);\n color: var(--qm-ink-3);\n cursor: not-allowed;\n}\n\n[data-edrv-view] .edrv-qm-row2 {\n display: flex;\n gap: 10px;\n}\n\n[data-edrv-view] .edrv-qm-row2 > * {\n flex: 1;\n min-width: 0;\n}\n\n/* 分段控件:凹槽轨道 + 浮起选中块,比「整块填色」更克制 */\n[data-edrv-view] .edrv-qm-seg {\n display: flex;\n gap: 2px;\n width: 100%;\n box-sizing: border-box;\n padding: 2px;\n border: 1px solid var(--qm-line);\n border-radius: 7px;\n background: var(--qm-sunken);\n}\n\n[data-edrv-view] .edrv-qm-seg-btn {\n flex: 1 1 0;\n min-width: 0;\n border: 0;\n border-radius: 5px;\n background: transparent;\n color: var(--qm-ink-2);\n font: inherit;\n font-size: 11.5px;\n font-weight: 600;\n padding: 4px 8px;\n cursor: pointer;\n transition: background .1s ease, color .1s ease;\n}\n\n[data-edrv-view] .edrv-qm-seg-btn:hover {\n color: var(--qm-ink);\n background: var(--qm-hover);\n}\n\n[data-edrv-view] .edrv-qm-seg-btn--on {\n color: var(--qm-accent);\n background: var(--qm-surface);\n box-shadow: 0 1px 2px rgba(0, 0, 0, .08);\n}\n\n[data-edrv-view] .edrv-qm-seg-btn:focus-visible {\n outline: 2px solid var(--qm-accent);\n outline-offset: -2px;\n}\n\n[data-edrv-view] .edrv-qm-seg-btn:disabled {\n opacity: .5;\n cursor: not-allowed;\n}\n\n/* 放在标签行里的分段按内容宽度收起,不撑满整行 */\n[data-edrv-view] .edrv-qm-field-head .edrv-qm-seg {\n width: auto;\n flex: 0 0 auto;\n}\n\n[data-edrv-view] .edrv-qm-field-head .edrv-qm-seg-btn {\n flex: 0 0 auto;\n padding: 3px 9px;\n font-size: 11px;\n}\n\n/* ── 下拉选择(技能 / 规则 / 工作区) ────────────────────── */\n[data-edrv-view] .edrv-qm-pick {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n[data-edrv-view] .edrv-qm-pick-trigger {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n box-sizing: border-box;\n min-height: 30px;\n padding: 5px 9px;\n border: 1px solid var(--qm-line);\n border-radius: 7px;\n background: var(--dsw-alias-bg-base, #fff);\n color: var(--qm-ink);\n font: inherit;\n font-size: 11.5px;\n text-align: left;\n cursor: pointer;\n transition: border-color .1s ease, box-shadow .1s ease;\n}\n\n[data-edrv-view] .edrv-qm-pick-trigger:hover {\n border-color: var(--qm-accent);\n}\n\n[data-edrv-view] .edrv-qm-pick-trigger:focus-visible {\n outline: none;\n border-color: var(--qm-accent);\n box-shadow: 0 0 0 3px var(--qm-accent-soft);\n}\n\n[data-edrv-view] .edrv-qm-pick-trigger--open {\n border-color: var(--qm-accent);\n box-shadow: 0 0 0 3px var(--qm-accent-soft);\n}\n\n[data-edrv-view] .edrv-qm-pick-trigger:disabled {\n border-color: var(--qm-line);\n background: var(--qm-sunken);\n color: var(--qm-ink-3);\n cursor: not-allowed;\n}\n\n[data-edrv-view] .edrv-qm-pick-summary {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n[data-edrv-view] .edrv-qm-pick-badge {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n border-radius: 999px;\n background: var(--qm-warn);\n color: #fff;\n font-size: 10px;\n font-weight: 700;\n}\n\n[data-edrv-view] .edrv-qm-pick-caret {\n flex: 0 0 auto;\n width: 0;\n height: 0;\n border-left: 4px solid transparent;\n border-right: 4px solid transparent;\n border-top: 5px solid var(--qm-ink-3);\n transition: transform .12s ease;\n}\n\n[data-edrv-view] .edrv-qm-pick-trigger--open .edrv-qm-pick-caret {\n transform: rotate(180deg);\n}\n\n/* 弹层是 position: fixed + 视口坐标,不受弹窗滚动区裁剪 */\n[data-edrv-view] .edrv-qm-pick-panel {\n position: fixed;\n z-index: 260;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 5px;\n border: 1px solid var(--qm-line);\n border-radius: var(--qm-radius);\n background: var(--qm-surface);\n box-shadow: var(--qm-elev);\n overflow: hidden;\n}\n\n[data-edrv-view] .edrv-qm-pick-list {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-height: 0;\n overflow: auto;\n}\n\n[data-edrv-view] .edrv-qm-pick-group {\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n[data-edrv-view] .edrv-qm-pick-group-title {\n padding: 6px 7px 3px;\n color: var(--qm-ink-3);\n font-size: 10px;\n font-weight: 600;\n}\n\n[data-edrv-view] .edrv-qm-pick-option {\n display: flex;\n align-items: center;\n gap: 7px;\n width: 100%;\n box-sizing: border-box;\n padding: 5px 7px;\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: var(--qm-ink);\n font: inherit;\n font-size: 11.5px;\n text-align: left;\n cursor: pointer;\n transition: background .1s ease, color .1s ease;\n}\n\n[data-edrv-view] .edrv-qm-pick-option:hover {\n background: var(--qm-hover);\n}\n\n[data-edrv-view] .edrv-qm-pick-option:focus-visible {\n outline: 2px solid var(--qm-accent);\n outline-offset: -2px;\n}\n\n[data-edrv-view] .edrv-qm-pick-option--on {\n color: var(--qm-accent);\n background: var(--qm-accent-soft);\n}\n\n[data-edrv-view] .edrv-qm-pick-check {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 13px;\n height: 13px;\n border: 1px solid var(--qm-line);\n border-radius: 4px;\n background: var(--dsw-alias-bg-base, #fff);\n color: #fff;\n font-size: 9px;\n line-height: 1;\n}\n\n[data-edrv-view] .edrv-qm-pick-check--on {\n border-color: var(--qm-accent);\n background: var(--qm-accent);\n}\n\n[data-edrv-view] .edrv-qm-pick-name {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n[data-edrv-view] .edrv-qm-pick-hint {\n flex: 0 0 auto;\n max-width: 45%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--qm-ink-3);\n font-size: 10px;\n}\n\n[data-edrv-view] .edrv-qm-pick-dot {\n flex: 0 0 auto;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--qm-warn);\n}\n\n[data-edrv-view] .edrv-qm-pick-dot--error {\n background: var(--qm-danger);\n}\n\n[data-edrv-view] .edrv-qm-pick-empty {\n padding: 10px 8px;\n color: var(--qm-ink-3);\n font-size: 11px;\n text-align: center;\n}\n\n[data-edrv-view] .edrv-qm-pick-more {\n border: 1px dashed var(--qm-line);\n border-radius: 6px;\n background: transparent;\n color: var(--qm-ink-2);\n font: inherit;\n font-size: 11px;\n padding: 5px 7px;\n cursor: pointer;\n text-align: center;\n}\n\n[data-edrv-view] .edrv-qm-pick-more:hover {\n border-color: var(--qm-accent);\n color: var(--qm-accent);\n background: var(--qm-accent-soft);\n}\n\n[data-edrv-view] .edrv-qm-warn-list {\n margin: 0;\n padding-left: 16px;\n color: var(--qm-warn);\n font-size: 11px;\n}\n\n[data-edrv-view] .edrv-qm-error-list {\n margin: 0;\n padding-left: 16px;\n color: var(--qm-danger);\n font-size: 11px;\n}\n\n[data-edrv-view] .edrv-qm-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 11px 16px;\n border-top: 1px solid var(--qm-line);\n background: var(--qm-sunken);\n}\n\n[data-edrv-view] .edrv-qm-actions-grow {\n flex: 1;\n}\n\n/* 按钮统一盒模型:固定高度 + 内容居中,同一行里的按钮才能等高对齐 */\n[data-edrv-view] .edrv-qm-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 5px;\n box-sizing: border-box;\n height: 28px;\n padding: 0 12px;\n border: 1px solid var(--qm-line);\n border-radius: 7px;\n background: var(--dsw-alias-bg-base, #fff);\n color: var(--qm-ink);\n font: inherit;\n font-size: 11.5px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n user-select: none;\n cursor: pointer;\n transition: background .1s ease, border-color .1s ease, color .1s ease;\n}\n\n[data-edrv-view] .edrv-qm-btn--sm {\n height: 24px;\n padding: 0 9px;\n font-size: 11px;\n}\n\n[data-edrv-view] .edrv-qm-btn:hover {\n background: var(--qm-hover);\n}\n\n[data-edrv-view] .edrv-qm-btn--primary {\n border-color: var(--qm-accent);\n background: var(--qm-accent);\n color: #fff;\n}\n\n[data-edrv-view] .edrv-qm-btn--primary:hover {\n filter: brightness(1.06);\n background: var(--qm-accent);\n}\n\n[data-edrv-view] .edrv-qm-btn--danger {\n border-color: var(--qm-danger);\n color: var(--qm-danger);\n}\n\n[data-edrv-view] .edrv-qm-btn:disabled {\n opacity: .5;\n cursor: not-allowed;\n}\n\n[data-edrv-view] .edrv-qm-btn:focus-visible {\n outline: 2px solid var(--qm-accent);\n outline-offset: 2px;\n}\n\n/* 按钮组:按内容宽度排布,不再等分拉满(导出/导入/重置宽度本就不同) */\n[data-edrv-view] .edrv-qm-btn-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n}\n\n/* 同一行里的输入框与按钮 */\n[data-edrv-view] .edrv-qm-inline {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n[data-edrv-view] .edrv-qm-input--order {\n width: 74px;\n flex: 0 0 74px;\n}\n\n[data-edrv-view] .edrv-qm-row2 > .edrv-qm-field--narrow {\n flex: 0 0 136px;\n}\n\n/* 悬浮按钮右键小菜单 */\n[data-edrv-view] .edrv-qm-context {\n position: absolute;\n min-width: 148px;\n padding: 4px;\n border-radius: 9px;\n border: 1px solid var(--qm-line);\n background: var(--qm-surface);\n box-shadow: var(--qm-elev);\n pointer-events: auto;\n}\n\n[data-edrv-view] .edrv-qm-context-item {\n padding: 5px 9px;\n border-radius: 6px;\n cursor: pointer;\n color: var(--qm-ink);\n font-size: 12px;\n white-space: nowrap;\n transition: background .1s ease, color .1s ease;\n}\n\n[data-edrv-view] .edrv-qm-context-item:hover {\n background: var(--qm-hover);\n}\n\n[data-edrv-view] .edrv-qm-context-item--danger {\n color: var(--qm-danger);\n}\n\n[data-edrv-view] .edrv-qm-context-sep {\n height: 1px;\n margin: 4px 2px;\n background: var(--qm-line);\n}\n\n@media (prefers-reduced-motion: reduce) {\n [data-edrv-view] .edrv-qm-col { animation: none; }\n [data-edrv-view] .edrv-qm-launcher,\n [data-edrv-view] .edrv-qm-row,\n [data-edrv-view] .edrv-qm-row-chevron { transition: none; }\n [data-edrv-view] .edrv-qm-launcher:hover { transform: none; }\n}\n";
70
+ const tagId$3 = "dsh-vscode-mode/quickmenu.css";
60
71
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
61
72
  const tag = document.createElement("style");
62
73
  tag.dataset.plugin = "dsh-vscode-mode";
@@ -3544,29 +3555,23 @@ window.__ModuleLoader__.load({
3544
3555
  }
3545
3556
  //#endregion
3546
3557
  //#region src/client/reviewBatch.ts
3547
- /** @public @author ddj 2026年10月27号 @param port Authenticated RPC adapter. @returns Frozen complete pending set; aborts before any decision if pagination shifts. @throws Incomplete or changing pages. */
3558
+ /**
3559
+ * @public
3560
+ * @author ddj 2026年10月31号
3561
+ * @description: 取一次完整待审快照。分页计数只用于展示,批量写入必须一次取全,避免把页内数字当成总数。
3562
+ * @param port 已鉴权的 RPC 适配器
3563
+ * @returns 冻结的完整待审集;任何不一致都在首个写入前中止
3564
+ * @throws 快照重复、版本已变或数量不完整
3565
+ */
3548
3566
  async function loadPending(port) {
3549
- const items = [];
3567
+ const page = await port.snapshot();
3550
3568
  const seen = /* @__PURE__ */ new Set();
3551
- const cursors = /* @__PURE__ */ new Set();
3552
- let cursor;
3553
- let expected;
3554
- do {
3555
- const page = await port.list(cursor);
3556
- expected ??= page.pending;
3557
- if (page.pending !== expected) throw new Error("待审记录在分页期间变化,请刷新后重试");
3558
- for (const item of page.items) {
3559
- if (seen.has(item.id) || !item.current || item.decision !== "pending") throw new Error("待审分页发生重复或版本变化,请刷新后重试");
3560
- seen.add(item.id);
3561
- items.push(item);
3562
- }
3563
- if (page.nextCursor === null) break;
3564
- if (cursors.has(page.nextCursor) || page.items.length === 0) throw new Error("待审分页未完整返回,请刷新后重试");
3565
- cursor = page.nextCursor;
3566
- cursors.add(cursor);
3567
- } while (true);
3568
- if (items.length !== expected) throw new Error("待审记录数量不完整,未执行批量操作");
3569
- return items;
3569
+ for (const item of page.items) {
3570
+ if (seen.has(item.id) || !item.current || item.decision !== "pending") throw new Error("待审快照发生重复或版本变化,请刷新后重试");
3571
+ seen.add(item.id);
3572
+ }
3573
+ if (page.nextCursor !== null || page.items.length !== page.pending) throw new Error("待审记录数量不完整,未执行批量操作");
3574
+ return page.items;
3570
3575
  }
3571
3576
  /** @public @author ddj 2026年10月27号 @param port Guarded existing per-file decision adapter. @param reject Undo instead of Keep. @returns Precise partial result; Undo stops at first conflict, never claims atomic multi-file rollback. */
3572
3577
  async function runReviewBatch(port, reject) {
@@ -3753,6 +3758,39 @@ window.__ModuleLoader__.load({
3753
3758
  /** 待审条目在面板列表里的 id 前缀;客户端与 Host 共用这一份口径。 */
3754
3759
  const PENDING_PREFIX = "pending:";
3755
3760
  //#endregion
3761
+ //#region src/shared/pathKey.ts
3762
+ /**
3763
+ * 路径比较口径:Host 与客户端共用同一套「远端 / 盘符 / 相对路径」判定,只在平台折叠上分工。
3764
+ * @author ddj 2026年10月30号
3765
+ */
3766
+ /** 远端仓库身份:`scheme://...` 区分大小写,绝不能折叠。 */
3767
+ const REMOTE = /^[a-z]+:\/\//i;
3768
+ /** 盘符绝对路径:大小写不敏感与宿主平台无关,两边都必须折叠。 */
3769
+ const DRIVE = /^[a-z]:\//i;
3770
+ /**
3771
+ * @author ddj 2026年10月30号
3772
+ * @description: 统一分隔符为 `/`,其余字符原样保留。
3773
+ * @param path 原始路径
3774
+ * @returns 反斜杠已归一化的路径
3775
+ */
3776
+ function unifiedPath(path) {
3777
+ return path.replace(/\\/g, "/");
3778
+ }
3779
+ /**
3780
+ * @author ddj 2026年10月30号
3781
+ * @description: 生成路径比较键。盘符路径恒折叠大小写;其余路径是否折叠由调用方按运行平台决定
3782
+ * (Host 在 Windows 上折叠,客户端不在宿主进程里,取 false)。
3783
+ * @param path 原始路径
3784
+ * @param foldCase 非盘符路径是否折叠大小写
3785
+ * @returns 比较键
3786
+ */
3787
+ function pathKeyFor(path, foldCase) {
3788
+ const unified = unifiedPath(path);
3789
+ if (REMOTE.test(unified)) return unified;
3790
+ if (DRIVE.test(unified)) return unified.toLowerCase();
3791
+ return foldCase ? unified.toLowerCase() : unified;
3792
+ }
3793
+ //#endregion
3756
3794
  //#region src/shared/diff.ts
3757
3795
  /** 统一取对应 hunk;callHunk 只用于旧记录缺失 hunks 时兼容。 */
3758
3796
  function preciseHunk(record, idx) {
@@ -4050,11 +4088,15 @@ window.__ModuleLoader__.load({
4050
4088
  //#endregion
4051
4089
  //#region src/client/state/diffSummary.ts
4052
4090
  /** Source-aware metadata projection; official patches never become legacy hunks. @author ddj 2026年10月26号 */
4053
- /** @public @author ddj 2026年10月26号 @param path Functional local or remote identity. @returns Comparison key without rewriting remote case. */
4091
+ /**
4092
+ * @public
4093
+ * @author ddj 2026年10月26号
4094
+ * @description: 客户端路径比较口径:浏览器里没有宿主平台信息,非盘符路径不折叠大小写。
4095
+ * @param path 文件身份或相对路径
4096
+ * @returns 比较键
4097
+ */
4054
4098
  function diffPathKey(path) {
4055
- if (/^[a-z]+:\/\//i.test(path)) return path;
4056
- const normalized = path.replace(/\\/g, "/");
4057
- return /^[a-z]:\//i.test(normalized) ? normalized.toLowerCase() : normalized;
4099
+ return pathKeyFor(path, false);
4058
4100
  }
4059
4101
  /** @public @author ddj 2026年10月26号 @param path Current editor file. @param page Authoritative metadata, including decided paths. @returns Whether legacy actions must be hidden for this file. */
4060
4102
  function officialPath(path, page) {
@@ -87762,7 +87804,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
87762
87804
  bottom: "top",
87763
87805
  top: "bottom"
87764
87806
  };
87765
- function clamp$3(start, value, end) {
87807
+ function clamp$4(start, value, end) {
87766
87808
  return max$1(start, min$1(value, end));
87767
87809
  }
87768
87810
  function evaluate$1(value, param) {
@@ -88062,7 +88104,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
88062
88104
  const maxPadding = min$1(paddingObject[maxProp], largestPossiblePadding);
88063
88105
  const max = clientSize - arrowDimensions[length] - maxPadding;
88064
88106
  const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;
88065
- const offset = clamp$3(minPadding, center, max);
88107
+ const offset = clamp$4(minPadding, center, max);
88066
88108
  const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center !== offset && rects.reference[length] / 2 - (center < minPadding ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;
88067
88109
  const alignmentOffset = shouldAddOffset ? center < minPadding ? center - minPadding : center - max : 0;
88068
88110
  return {
@@ -88281,7 +88323,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
88281
88323
  const mainAxis = getOppositeAxis(crossAxis);
88282
88324
  let mainAxisCoord = coords[mainAxis];
88283
88325
  let crossAxisCoord = coords[crossAxis];
88284
- const clampCoord = (axis, coord) => clamp$3(coord + overflow[axis === "y" ? "top" : "left"], coord, coord - overflow[axis === "y" ? "bottom" : "right"]);
88326
+ const clampCoord = (axis, coord) => clamp$4(coord + overflow[axis === "y" ? "top" : "left"], coord, coord - overflow[axis === "y" ? "bottom" : "right"]);
88285
88327
  if (checkMainAxis) mainAxisCoord = clampCoord(mainAxis, mainAxisCoord);
88286
88328
  if (checkCrossAxis) crossAxisCoord = clampCoord(crossAxis, crossAxisCoord);
88287
88329
  const limitedCoords = limiter.fn({
@@ -94042,7 +94084,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
94042
94084
  const LINE_DELTA_PIXEL_SIZE = 16;
94043
94085
  const PAGE_DELTA_PIXEL_SIZE = 800;
94044
94086
  const ZOOM_RATIO_PRECISION = 100;
94045
- function clamp$1$3(value, min, max) {
94087
+ function clamp$1$2(value, min, max) {
94046
94088
  return Math.min(max, Math.max(min, value));
94047
94089
  }
94048
94090
  function normalizeWheelDelta(event) {
@@ -94057,10 +94099,10 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
94057
94099
  function getNextWheelZoomRatio(currentRatio, event) {
94058
94100
  const validCurrentRatio = Number.isFinite(currentRatio) ? currentRatio : 1;
94059
94101
  const normalizedDelta = normalizeWheelDelta(event);
94060
- if (normalizedDelta === 0) return clamp$1$3(validCurrentRatio, MIN_ZOOM_RATIO, MAX_ZOOM_RATIO);
94102
+ if (normalizedDelta === 0) return clamp$1$2(validCurrentRatio, MIN_ZOOM_RATIO, MAX_ZOOM_RATIO);
94061
94103
  const maxStep = validCurrentRatio < 1 ? MAX_WHEEL_ZOOM_STEP_BELOW_ONE : MAX_WHEEL_ZOOM_STEP;
94062
- const ratioDelta = clamp$1$3(-normalizedDelta * PIXEL_DELTA_TO_ZOOM_RATIO, -maxStep, maxStep);
94063
- return clamp$1$3(Math.round((validCurrentRatio + ratioDelta) * ZOOM_RATIO_PRECISION) / ZOOM_RATIO_PRECISION, MIN_ZOOM_RATIO, MAX_ZOOM_RATIO);
94104
+ const ratioDelta = clamp$1$2(-normalizedDelta * PIXEL_DELTA_TO_ZOOM_RATIO, -maxStep, maxStep);
94105
+ return clamp$1$2(Math.round((validCurrentRatio + ratioDelta) * ZOOM_RATIO_PRECISION) / ZOOM_RATIO_PRECISION, MIN_ZOOM_RATIO, MAX_ZOOM_RATIO);
94064
94106
  }
94065
94107
  const BASE_OBJECT_ARRAY = [
94066
94108
  "top",
@@ -94825,14 +94867,14 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
94825
94867
  }
94826
94868
  };
94827
94869
  const DRAWINGML_GLOW_BLUR_SCALE = .5;
94828
- function clamp$4(value, min, max) {
94870
+ function clamp$3(value, min, max) {
94829
94871
  return Math.max(min, Math.min(max, value));
94830
94872
  }
94831
94873
  function colorWithOpacity(color, opacity) {
94832
94874
  if (opacity === void 0) return color;
94833
94875
  const parsedColor = new ColorKit(color);
94834
94876
  if (!parsedColor.isValid) return color;
94835
- return parsedColor.setAlpha(parsedColor.getAlpha() * clamp$4(opacity, 0, 1)).toRgbString();
94877
+ return parsedColor.setAlpha(parsedColor.getAlpha() * clamp$3(opacity, 0, 1)).toRgbString();
94836
94878
  }
94837
94879
  function isTransparentColor(color) {
94838
94880
  const parsedColor = new ColorKit(color);
@@ -145676,10 +145718,10 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
145676
145718
  var require_useMergeRef = /* @__PURE__ */ __commonJSMin(((exports) => {
145677
145719
  Object.defineProperty(exports, "__esModule", { value: true });
145678
145720
  exports.useMergeRefs = void 0;
145679
- var React$65 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports)).__importStar(require("react"));
145721
+ var React$68 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports)).__importStar(require("react"));
145680
145722
  var assignRef_1 = require_assignRef();
145681
145723
  var useRef_1 = require_useRef();
145682
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React$65.useLayoutEffect : React$65.useEffect;
145724
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React$68.useLayoutEffect : React$68.useEffect;
145683
145725
  var currentValues = /* @__PURE__ */ new WeakMap();
145684
145726
  /**
145685
145727
  * Merges two or more refs together providing a single interface to set their value
@@ -145959,7 +146001,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
145959
146001
  Object.defineProperty(exports, "__esModule", { value: true });
145960
146002
  exports.sidecar = void 0;
145961
146003
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
145962
- var React$64 = tslib_1.__importStar(require("react"));
146004
+ var React$67 = tslib_1.__importStar(require("react"));
145963
146005
  var hook_1 = require_hook$1();
145964
146006
  function sidecar(importer, errorComponent) {
145965
146007
  var ErrorCase = function() {
@@ -145968,7 +146010,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
145968
146010
  return function Sidecar(props) {
145969
146011
  var _a = (0, hook_1.useSidecar)(importer, props.sideCar), Car = _a[0];
145970
146012
  if (_a[1] && errorComponent) return ErrorCase;
145971
- return Car ? React$64.createElement(Car, tslib_1.__assign({}, props)) : null;
146013
+ return Car ? React$67.createElement(Car, tslib_1.__assign({}, props)) : null;
145972
146014
  };
145973
146015
  }
145974
146016
  exports.sidecar = sidecar;
@@ -146083,7 +146125,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146083
146125
  Object.defineProperty(exports, "__esModule", { value: true });
146084
146126
  exports.renderCar = void 0;
146085
146127
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
146086
- var React$63 = tslib_1.__importStar(require("react"));
146128
+ var React$66 = tslib_1.__importStar(require("react"));
146087
146129
  var react_1 = require("react");
146088
146130
  function renderCar(WrappedComponent, defaults) {
146089
146131
  function State(_a) {
@@ -146096,9 +146138,9 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146096
146138
  });
146097
146139
  return null;
146098
146140
  }, []);
146099
- return React$63.createElement(WrappedComponent, tslib_1.__assign({}, props, { children: renderTarget }));
146141
+ return React$66.createElement(WrappedComponent, tslib_1.__assign({}, props, { children: renderTarget }));
146100
146142
  }
146101
- var Children = React$63.memo(function(_a) {
146143
+ var Children = React$66.memo(function(_a) {
146102
146144
  var stateRef = _a.stateRef, defaultState = _a.defaultState, children = _a.children;
146103
146145
  var _b = (0, react_1.useState)(defaultState.current), state = _b[0], setState = _b[1];
146104
146146
  (0, react_1.useEffect)(function() {
@@ -146109,14 +146151,14 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146109
146151
  return true;
146110
146152
  });
146111
146153
  return function Combiner(props) {
146112
- var defaultState = React$63.useRef(defaults(props));
146113
- var ref = React$63.useRef(function(state) {
146154
+ var defaultState = React$66.useRef(defaults(props));
146155
+ var ref = React$66.useRef(function(state) {
146114
146156
  return defaultState.current = state;
146115
146157
  });
146116
- return React$63.createElement(React$63.Fragment, null, React$63.createElement(State, {
146158
+ return React$66.createElement(React$66.Fragment, null, React$66.createElement(State, {
146117
146159
  stateRef: ref,
146118
146160
  props
146119
- }), React$63.createElement(Children, {
146161
+ }), React$66.createElement(Children, {
146120
146162
  stateRef: ref,
146121
146163
  defaultState,
146122
146164
  children: props.children
@@ -146131,13 +146173,13 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146131
146173
  Object.defineProperty(exports, "__esModule", { value: true });
146132
146174
  exports.exportSidecar = void 0;
146133
146175
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
146134
- var React$62 = tslib_1.__importStar(require("react"));
146176
+ var React$65 = tslib_1.__importStar(require("react"));
146135
146177
  var SideCar = function(_a) {
146136
146178
  var sideCar = _a.sideCar, rest = tslib_1.__rest(_a, ["sideCar"]);
146137
146179
  if (!sideCar) throw new Error("Sidecar: please provide `sideCar` property to import the right car");
146138
146180
  var Target = sideCar.read();
146139
146181
  if (!Target) throw new Error("Sidecar medium not found");
146140
- return React$62.createElement(Target, tslib_1.__assign({}, rest));
146182
+ return React$65.createElement(Target, tslib_1.__assign({}, rest));
146141
146183
  };
146142
146184
  SideCar.isSideCarExport = true;
146143
146185
  function exportSidecar(medium, exported) {
@@ -146213,7 +146255,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146213
146255
  Object.defineProperty(exports, "__esModule", { value: true });
146214
146256
  exports.RemoveScroll = void 0;
146215
146257
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
146216
- var React$61 = tslib_1.__importStar(require("react"));
146258
+ var React$64 = tslib_1.__importStar(require("react"));
146217
146259
  var constants_1 = require_constants();
146218
146260
  var use_callback_ref_1 = require_es5$5();
146219
146261
  var medium_1 = require_medium();
@@ -146221,9 +146263,9 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146221
146263
  /**
146222
146264
  * Removes scrollbar from the page and contain the scroll within the Lock
146223
146265
  */
146224
- var RemoveScroll = React$61.forwardRef(function(props, parentRef) {
146225
- var ref = React$61.useRef(null);
146226
- var _a = React$61.useState({
146266
+ var RemoveScroll = React$64.forwardRef(function(props, parentRef) {
146267
+ var ref = React$64.useRef(null);
146268
+ var _a = React$64.useState({
146227
146269
  onScrollCapture: nothing,
146228
146270
  onWheelCapture: nothing,
146229
146271
  onTouchMoveCapture: nothing
@@ -146246,7 +146288,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146246
146288
  var SideCar = sideCar;
146247
146289
  var containerRef = (0, use_callback_ref_1.useMergeRefs)([ref, parentRef]);
146248
146290
  var containerProps = tslib_1.__assign(tslib_1.__assign({}, rest), callbacks);
146249
- return React$61.createElement(React$61.Fragment, null, enabled && React$61.createElement(SideCar, {
146291
+ return React$64.createElement(React$64.Fragment, null, enabled && React$64.createElement(SideCar, {
146250
146292
  sideCar: medium_1.effectCar,
146251
146293
  removeScrollBar,
146252
146294
  shards,
@@ -146257,7 +146299,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146257
146299
  allowPinchZoom: !!allowPinchZoom,
146258
146300
  lockRef: ref,
146259
146301
  gapMode
146260
- }), forwardProps ? React$61.cloneElement(React$61.Children.only(children), tslib_1.__assign(tslib_1.__assign({}, containerProps), { ref: containerRef })) : React$61.createElement(Container, tslib_1.__assign({}, containerProps, {
146302
+ }), forwardProps ? React$64.cloneElement(React$64.Children.only(children), tslib_1.__assign(tslib_1.__assign({}, containerProps), { ref: containerRef })) : React$64.createElement(Container, tslib_1.__assign({}, containerProps, {
146261
146303
  className,
146262
146304
  ref: containerRef
146263
146305
  }), children));
@@ -146336,7 +146378,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146336
146378
  var require_hook = /* @__PURE__ */ __commonJSMin(((exports) => {
146337
146379
  Object.defineProperty(exports, "__esModule", { value: true });
146338
146380
  exports.styleHookSingleton = void 0;
146339
- var React$60 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports)).__importStar(require("react"));
146381
+ var React$63 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports)).__importStar(require("react"));
146340
146382
  var singleton_1 = require_singleton();
146341
146383
  /**
146342
146384
  * creates a hook to control style singleton
@@ -146350,7 +146392,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146350
146392
  var styleHookSingleton = function() {
146351
146393
  var sheet = (0, singleton_1.stylesheetSingleton)();
146352
146394
  return function(styles, isDynamic) {
146353
- React$60.useEffect(function() {
146395
+ React$63.useEffect(function() {
146354
146396
  sheet.add(styles);
146355
146397
  return function() {
146356
146398
  sheet.remove();
@@ -146455,7 +146497,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146455
146497
  var require_component = /* @__PURE__ */ __commonJSMin(((exports) => {
146456
146498
  Object.defineProperty(exports, "__esModule", { value: true });
146457
146499
  exports.RemoveScrollBar = exports.useLockAttribute = exports.lockAttribute = void 0;
146458
- var React$59 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports)).__importStar(require("react"));
146500
+ var React$62 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports)).__importStar(require("react"));
146459
146501
  var react_style_singleton_1 = require_es5$2();
146460
146502
  var constants_1 = require_constants();
146461
146503
  var utils_1 = require_utils();
@@ -146475,7 +146517,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146475
146517
  return isFinite(counter) ? counter : 0;
146476
146518
  };
146477
146519
  var useLockAttribute = function() {
146478
- React$59.useEffect(function() {
146520
+ React$62.useEffect(function() {
146479
146521
  document.body.setAttribute(exports.lockAttribute, (getCurrentUseCounter() + 1).toString());
146480
146522
  return function() {
146481
146523
  var newCounter = getCurrentUseCounter() - 1;
@@ -146491,10 +146533,10 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146491
146533
  var RemoveScrollBar = function(_a) {
146492
146534
  var noRelative = _a.noRelative, noImportant = _a.noImportant, _b = _a.gapMode, gapMode = _b === void 0 ? "margin" : _b;
146493
146535
  (0, exports.useLockAttribute)();
146494
- var gap = React$59.useMemo(function() {
146536
+ var gap = React$62.useMemo(function() {
146495
146537
  return (0, utils_1.getGapWidth)(gapMode);
146496
146538
  }, [gapMode]);
146497
- return React$59.createElement(Style, { styles: getStyles(gap, !noRelative, gapMode, !noImportant ? "!important" : "") });
146539
+ return React$62.createElement(Style, { styles: getStyles(gap, !noRelative, gapMode, !noImportant ? "!important" : "") });
146498
146540
  };
146499
146541
  exports.RemoveScrollBar = RemoveScrollBar;
146500
146542
  }));
@@ -146656,7 +146698,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146656
146698
  Object.defineProperty(exports, "__esModule", { value: true });
146657
146699
  exports.RemoveScrollSideCar = exports.getDeltaXY = exports.getTouchXY = void 0;
146658
146700
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
146659
- var React$58 = tslib_1.__importStar(require("react"));
146701
+ var React$61 = tslib_1.__importStar(require("react"));
146660
146702
  var react_remove_scroll_bar_1 = require_es5$1();
146661
146703
  var react_style_singleton_1 = require_es5$2();
146662
146704
  var aggresiveCapture_1 = require_aggresiveCapture();
@@ -146681,16 +146723,16 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146681
146723
  var idCounter = 0;
146682
146724
  var lockStack = [];
146683
146725
  function RemoveScrollSideCar(props) {
146684
- var shouldPreventQueue = React$58.useRef([]);
146685
- var touchStartRef = React$58.useRef([0, 0]);
146686
- var activeAxis = React$58.useRef();
146687
- var id = React$58.useState(idCounter++)[0];
146688
- var Style = React$58.useState(react_style_singleton_1.styleSingleton)[0];
146689
- var lastProps = React$58.useRef(props);
146690
- React$58.useEffect(function() {
146726
+ var shouldPreventQueue = React$61.useRef([]);
146727
+ var touchStartRef = React$61.useRef([0, 0]);
146728
+ var activeAxis = React$61.useRef();
146729
+ var id = React$61.useState(idCounter++)[0];
146730
+ var Style = React$61.useState(react_style_singleton_1.styleSingleton)[0];
146731
+ var lastProps = React$61.useRef(props);
146732
+ React$61.useEffect(function() {
146691
146733
  lastProps.current = props;
146692
146734
  }, [props]);
146693
- React$58.useEffect(function() {
146735
+ React$61.useEffect(function() {
146694
146736
  if (props.inert) {
146695
146737
  document.body.classList.add("block-interactivity-".concat(id));
146696
146738
  var allow_1 = tslib_1.__spreadArray([props.lockRef.current], (props.shards || []).map(extractRef), true).filter(Boolean);
@@ -146709,7 +146751,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146709
146751
  props.lockRef.current,
146710
146752
  props.shards
146711
146753
  ]);
146712
- var shouldCancelEvent = React$58.useCallback(function(event, parent) {
146754
+ var shouldCancelEvent = React$61.useCallback(function(event, parent) {
146713
146755
  if ("touches" in event && event.touches.length === 2 || event.type === "wheel" && event.ctrlKey) return !lastProps.current.allowPinchZoom;
146714
146756
  var touch = (0, exports.getTouchXY)(event);
146715
146757
  var touchStart = touchStartRef.current;
@@ -146735,7 +146777,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146735
146777
  var cancelingAxis = activeAxis.current || currentAxis;
146736
146778
  return (0, handleScroll_1.handleScroll)(cancelingAxis, parent, event, cancelingAxis === "h" ? deltaX : deltaY, true);
146737
146779
  }, []);
146738
- var shouldPrevent = React$58.useCallback(function(_event) {
146780
+ var shouldPrevent = React$61.useCallback(function(_event) {
146739
146781
  var event = _event;
146740
146782
  if (!lockStack.length || lockStack[lockStack.length - 1] !== Style) return;
146741
146783
  var delta = "deltaY" in event ? (0, exports.getDeltaXY)(event) : (0, exports.getTouchXY)(event);
@@ -146755,7 +146797,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146755
146797
  }
146756
146798
  }
146757
146799
  }, []);
146758
- var shouldCancel = React$58.useCallback(function(name, delta, target, should) {
146800
+ var shouldCancel = React$61.useCallback(function(name, delta, target, should) {
146759
146801
  var event = {
146760
146802
  name,
146761
146803
  delta,
@@ -146770,17 +146812,17 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146770
146812
  });
146771
146813
  }, 1);
146772
146814
  }, []);
146773
- var scrollTouchStart = React$58.useCallback(function(event) {
146815
+ var scrollTouchStart = React$61.useCallback(function(event) {
146774
146816
  touchStartRef.current = (0, exports.getTouchXY)(event);
146775
146817
  activeAxis.current = void 0;
146776
146818
  }, []);
146777
- var scrollWheel = React$58.useCallback(function(event) {
146819
+ var scrollWheel = React$61.useCallback(function(event) {
146778
146820
  shouldCancel(event.type, (0, exports.getDeltaXY)(event), event.target, shouldCancelEvent(event, props.lockRef.current));
146779
146821
  }, []);
146780
- var scrollTouchMove = React$58.useCallback(function(event) {
146822
+ var scrollTouchMove = React$61.useCallback(function(event) {
146781
146823
  shouldCancel(event.type, (0, exports.getTouchXY)(event), event.target, shouldCancelEvent(event, props.lockRef.current));
146782
146824
  }, []);
146783
- React$58.useEffect(function() {
146825
+ React$61.useEffect(function() {
146784
146826
  lockStack.push(Style);
146785
146827
  props.setCallbacks({
146786
146828
  onScrollCapture: scrollWheel,
@@ -146800,7 +146842,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146800
146842
  };
146801
146843
  }, []);
146802
146844
  var removeScrollBar = props.removeScrollBar, inert = props.inert;
146803
- return React$58.createElement(React$58.Fragment, null, inert ? React$58.createElement(Style, { styles: generateStyle(id) }) : null, removeScrollBar ? React$58.createElement(react_remove_scroll_bar_1.RemoveScrollBar, {
146845
+ return React$61.createElement(React$61.Fragment, null, inert ? React$61.createElement(Style, { styles: generateStyle(id) }) : null, removeScrollBar ? React$61.createElement(react_remove_scroll_bar_1.RemoveScrollBar, {
146804
146846
  noRelative: props.noRelative,
146805
146847
  gapMode: props.gapMode
146806
146848
  }) : null);
@@ -146832,11 +146874,11 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146832
146874
  var require_Combination = /* @__PURE__ */ __commonJSMin(((exports) => {
146833
146875
  Object.defineProperty(exports, "__esModule", { value: true });
146834
146876
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
146835
- var React$57 = tslib_1.__importStar(require("react"));
146877
+ var React$60 = tslib_1.__importStar(require("react"));
146836
146878
  var UI_1 = require_UI();
146837
146879
  var sidecar_1 = tslib_1.__importDefault(require_sidecar());
146838
- var ReactRemoveScroll = React$57.forwardRef(function(props, ref) {
146839
- return React$57.createElement(UI_1.RemoveScroll, tslib_1.__assign({}, props, {
146880
+ var ReactRemoveScroll = React$60.forwardRef(function(props, ref) {
146881
+ return React$60.createElement(UI_1.RemoveScroll, tslib_1.__assign({}, props, {
146840
146882
  ref,
146841
146883
  sideCar: sidecar_1.default
146842
146884
  }));
@@ -194844,9 +194886,9 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
194844
194886
  movedRange: params.sourceRange
194845
194887
  };
194846
194888
  const dataStreamLength = body.dataStream.length;
194847
- const startOffset = clamp$1(params.sourceRange.startOffset, 0, dataStreamLength);
194848
- const endOffset = clamp$1(params.sourceRange.endOffset, startOffset, dataStreamLength);
194849
- const targetOffset = clamp$1(params.targetOffset, 0, dataStreamLength);
194889
+ const startOffset = clamp$1$1(params.sourceRange.startOffset, 0, dataStreamLength);
194890
+ const endOffset = clamp$1$1(params.sourceRange.endOffset, startOffset, dataStreamLength);
194891
+ const targetOffset = clamp$1$1(params.targetOffset, 0, dataStreamLength);
194850
194892
  if (startOffset === endOffset || targetOffset >= startOffset && targetOffset <= endOffset) return {
194851
194893
  nextDocumentData,
194852
194894
  movedRange: {
@@ -194890,9 +194932,9 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
194890
194932
  }
194891
194933
  function buildMoveBodyTextAction(previousBody, nextBody, move) {
194892
194934
  const dataStreamLength = previousBody.dataStream.length;
194893
- const startOffset = clamp$1(move.sourceRange.startOffset, 0, dataStreamLength);
194894
- const endOffset = clamp$1(move.sourceRange.endOffset, startOffset, dataStreamLength);
194895
- const targetOffset = clamp$1(move.targetOffset, 0, dataStreamLength);
194935
+ const startOffset = clamp$1$1(move.sourceRange.startOffset, 0, dataStreamLength);
194936
+ const endOffset = clamp$1$1(move.sourceRange.endOffset, startOffset, dataStreamLength);
194937
+ const targetOffset = clamp$1$1(move.targetOffset, 0, dataStreamLength);
194896
194938
  const moveLength = endOffset - startOffset;
194897
194939
  if (moveLength <= 0 || targetOffset >= startOffset && targetOffset <= endOffset) return null;
194898
194940
  const textX = new TextX();
@@ -195050,7 +195092,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
195050
195092
  if (targetOffset > endOffset && index >= endOffset && index <= targetOffset) return index - moveLength;
195051
195093
  return remapIndexAfterMove(index, startOffset, endOffset, targetOffset, insertOffset, moveLength);
195052
195094
  }
195053
- function clamp$1(value, min, max) {
195095
+ function clamp$1$1(value, min, max) {
195054
195096
  return Math.min(Math.max(value, min), max);
195055
195097
  }
195056
195098
  const HorizontalLineCommand = {
@@ -219192,7 +219234,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
219192
219234
  function ceil$1(x) {
219193
219235
  return finalise(x = new this(x), x.e + 1, 2);
219194
219236
  }
219195
- function clamp(x, min, max) {
219237
+ function clamp$1(x, min, max) {
219196
219238
  return new this(x).clamp(min, max);
219197
219239
  }
219198
219240
  function config(obj) {
@@ -219352,7 +219394,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
219352
219394
  Decimal.atan2 = atan2;
219353
219395
  Decimal.cbrt = cbrt;
219354
219396
  Decimal.ceil = ceil$1;
219355
- Decimal.clamp = clamp;
219397
+ Decimal.clamp = clamp$1;
219356
219398
  Decimal.cos = cos;
219357
219399
  Decimal.cosh = cosh;
219358
219400
  Decimal.div = div;
@@ -344164,7 +344206,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
344164
344206
  };
344165
344207
  return nextTick;
344166
344208
  };
344167
- const TYPE_OPTIONS = [
344209
+ const TYPE_OPTIONS$1 = [
344168
344210
  {
344169
344211
  label: "sheets-numfmt-ui.general",
344170
344212
  component: GeneralPanel
@@ -344208,7 +344250,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
344208
344250
  const [type, setType] = (0, react.useState)(findDefaultType);
344209
344251
  const [key, setKey] = (0, react.useState)(() => `${row}_${col}_${defaultPattern}`);
344210
344252
  const { mark, userHabitCurrency } = useCurrencyOptions(() => setKey(`${row}_${col}_${defaultPattern}_userCurrency`));
344211
- const BusinessComponent = (_TYPE_OPTIONS$find = TYPE_OPTIONS.find((item) => item.label === type)) === null || _TYPE_OPTIONS$find === void 0 ? void 0 : _TYPE_OPTIONS$find.component;
344253
+ const BusinessComponent = (_TYPE_OPTIONS$find = TYPE_OPTIONS$1.find((item) => item.label === type)) === null || _TYPE_OPTIONS$find === void 0 ? void 0 : _TYPE_OPTIONS$find.component;
344212
344254
  function findDefaultType() {
344213
344255
  return [
344214
344256
  isGeneralPanel,
@@ -344216,9 +344258,9 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
344216
344258
  isCurrencyPanel,
344217
344259
  isDatePanel,
344218
344260
  isThousandthPercentilePanel
344219
- ].reduce((pre, curFn, index) => pre || (curFn(defaultPattern) ? TYPE_OPTIONS[index].label : ""), "") || "sheets-numfmt-ui.customFormat";
344261
+ ].reduce((pre, curFn, index) => pre || (curFn(defaultPattern) ? TYPE_OPTIONS$1[index].label : ""), "") || "sheets-numfmt-ui.customFormat";
344220
344262
  }
344221
- const selectOptions = TYPE_OPTIONS.map((option) => ({
344263
+ const selectOptions = TYPE_OPTIONS$1.map((option) => ({
344222
344264
  label: localeService.t(option.label),
344223
344265
  value: option.label
344224
344266
  }));
@@ -382993,7 +383035,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
382993
383035
  //#endregion
382994
383036
  //#region src/client/sessionScope.ts
382995
383037
  /** 对象窄化(非对象返回 undefined,供后续安全取字段)。 */
382996
- function asRecord(value) {
383038
+ function asRecord$1(value) {
382997
383039
  return value && typeof value === "object" ? value : void 0;
382998
383040
  }
382999
383041
  /** 非空字符串取值(其他类型一律视为缺失)。 */
@@ -383010,9 +383052,9 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
383010
383052
  * @returns sessionId,或 undefined
383011
383053
  */
383012
383054
  function uiSessionIdOf(snapshot) {
383013
- const binding = asRecord(snapshot);
383055
+ const binding = asRecord$1(snapshot);
383014
383056
  if (!binding) return void 0;
383015
- return strOf(binding.key) ?? strOf(asRecord(binding.value)?.key);
383057
+ return strOf(binding.key) ?? strOf(asRecord$1(binding.value)?.key);
383016
383058
  }
383017
383059
  /**
383018
383060
  * 从 `sessions.list` 快照读出 sessionId。
@@ -383023,14 +383065,14 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
383023
383065
  * @returns sessionId,或 undefined
383024
383066
  */
383025
383067
  function listSessionId(list) {
383026
- const snap = asRecord(list);
383068
+ const snap = asRecord$1(list);
383027
383069
  if (!snap) return void 0;
383028
383070
  const current = strOf(snap.current);
383029
383071
  if (current) return current;
383030
- const byId = asRecord(snap.byId);
383072
+ const byId = asRecord$1(snap.byId);
383031
383073
  if (!byId) return void 0;
383032
383074
  for (const [id, row] of Object.entries(byId)) {
383033
- const mainView = asRecord(asRecord(row)?.retainedBy)?.mainView;
383075
+ const mainView = asRecord$1(asRecord$1(row)?.retainedBy)?.mainView;
383034
383076
  if (typeof mainView === "number" && mainView > 0) return id;
383035
383077
  }
383036
383078
  }
@@ -383044,7 +383086,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
383044
383086
  function pickSessionScope(sources) {
383045
383087
  const sessionId = uiSessionIdOf(sources.uiSession) ?? listSessionId(sources.list) ?? strOf(sources.fallbackSessionId);
383046
383088
  if (!sessionId) return {};
383047
- const cwd = strOf(asRecord(asRecord(asRecord(sources.list)?.byId)?.[sessionId])?.cwd);
383089
+ const cwd = strOf(asRecord$1(asRecord$1(asRecord$1(sources.list)?.byId)?.[sessionId])?.cwd);
383048
383090
  return cwd ? {
383049
383091
  sessionId,
383050
383092
  cwd
@@ -383060,7 +383102,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
383060
383102
  }
383061
383103
  /** 取 `uiSession.current` 快照(服务或订阅面缺失时返回 undefined)。 */
383062
383104
  function uiCurrentSnapshot(ctx) {
383063
- const current = asRecord(asRecord(safeGet(ctx, "uiSession"))?.current);
383105
+ const current = asRecord$1(asRecord$1(safeGet(ctx, "uiSession"))?.current);
383064
383106
  const getSnapshot = current?.getSnapshot;
383065
383107
  if (typeof getSnapshot !== "function") return void 0;
383066
383108
  try {
@@ -383071,7 +383113,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
383071
383113
  }
383072
383114
  /** 取 `sessions.list` 快照(服务缺失时返回 undefined)。 */
383073
383115
  function listSnapshot(ctx) {
383074
- const list = asRecord(asRecord(safeGet(ctx, "sessions"))?.list);
383116
+ const list = asRecord$1(asRecord$1(safeGet(ctx, "sessions"))?.list);
383075
383117
  const getSnapshot = list?.getSnapshot;
383076
383118
  if (typeof getSnapshot !== "function") return void 0;
383077
383119
  try {
@@ -383106,15 +383148,15 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
383106
383148
  function subscribeScope(ctx, listener) {
383107
383149
  const disposers = [];
383108
383150
  const attach = (owner) => {
383109
- const subscribe = asRecord(owner)?.subscribe;
383151
+ const subscribe = asRecord$1(owner)?.subscribe;
383110
383152
  if (typeof subscribe !== "function") return;
383111
383153
  try {
383112
383154
  const disposer = subscribe.call(owner, listener);
383113
383155
  if (typeof disposer === "function") disposers.push(disposer);
383114
383156
  } catch {}
383115
383157
  };
383116
- attach(asRecord(asRecord(safeGet(ctx, "uiSession"))?.current));
383117
- attach(asRecord(asRecord(safeGet(ctx, "sessions"))?.list));
383158
+ attach(asRecord$1(asRecord$1(safeGet(ctx, "uiSession"))?.current));
383159
+ attach(asRecord$1(asRecord$1(safeGet(ctx, "sessions"))?.list));
383118
383160
  return () => {
383119
383161
  for (const disposer of disposers.splice(0)) try {
383120
383162
  disposer();
@@ -397018,10 +397060,10 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
397018
397060
  setStatus(reject ? "正在检查并 Undo 全部待审记录…" : "正在 Keep 全部待审记录…");
397019
397061
  try {
397020
397062
  const result = await runReviewBatch({
397021
- list: async (cursor) => {
397063
+ snapshot: async () => {
397022
397064
  const res = await rpc("edrv.review.list", {
397023
397065
  sessionId,
397024
- cursor
397066
+ all: true
397025
397067
  });
397026
397068
  if (!res.ok) throw new Error(res.error);
397027
397069
  return res.page;
@@ -403869,6 +403911,3080 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
403869
403911
  }
403870
403912
  return ClaimRouter;
403871
403913
  }
403914
+ /** 提示词长度上限。 */
403915
+ const QUICK_MENU_PROMPT_CAP = 8192;
403916
+ /** 单条规则内联正文上限。 */
403917
+ const QUICK_MENU_RULE_CAP = 8192;
403918
+ /** 技能名合法形态。 */
403919
+ const QUICK_MENU_SKILL_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
403920
+ /** 未命名项兜底名。 */
403921
+ const FALLBACK_NAME = "未命名";
403922
+ /**
403923
+ * 数值夹取(NaN/非有限回落到 fallback)。
403924
+ * @author ddj 2026年10月30号
403925
+ * @param value 原始值
403926
+ * @param min 下限
403927
+ * @param max 上限
403928
+ * @param fallback 非法值兜底
403929
+ * @returns 夹取后的数值
403930
+ */
403931
+ function clampNumber(value, min, max, fallback) {
403932
+ const number = typeof value === "number" && Number.isFinite(value) ? value : fallback;
403933
+ if (number < min) return min;
403934
+ if (number > max) return max;
403935
+ return number;
403936
+ }
403937
+ /**
403938
+ * 取对象形态(非对象/数组返回 null)。
403939
+ * @author ddj 2026年10月30号
403940
+ * @param value 原始值
403941
+ * @returns 普通对象或 null
403942
+ */
403943
+ function asRecord(value) {
403944
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
403945
+ return value;
403946
+ }
403947
+ /**
403948
+ * 取字符串(非字符串回落空串)。
403949
+ * @author ddj 2026年10月30号
403950
+ * @param value 原始值
403951
+ * @returns 字符串
403952
+ */
403953
+ function asString(value) {
403954
+ return typeof value === "string" ? value : "";
403955
+ }
403956
+ /**
403957
+ * 文本截断(超长补省略号)。
403958
+ * @author ddj 2026年10月30号
403959
+ * @param text 原文
403960
+ * @param cap 上限字符数
403961
+ * @returns 截断后文本
403962
+ */
403963
+ function cutText(text, cap) {
403964
+ if (text.length <= cap) return text;
403965
+ return text.slice(0, Math.max(0, cap - 1)) + "…";
403966
+ }
403967
+ /**
403968
+ * 归一化工作区路径(统一分隔符、去尾分隔符、win32 转小写),用于比较而非展示。
403969
+ * @author ddj 2026年10月30号
403970
+ * @param path 原始路径
403971
+ * @returns 归一化路径
403972
+ */
403973
+ function normalizePath(path) {
403974
+ const unified = path.trim().replace(/\//g, "\\").replace(/\\+$/, "");
403975
+ if (unified === "") return "";
403976
+ return unified.toLowerCase();
403977
+ }
403978
+ /**
403979
+ * 判断一条限定工作区列表是否覆盖当前工作区(含子目录,按路径边界匹配)。
403980
+ * @author ddj 2026年10月30号
403981
+ * @param paths 限定列表;null 表示全部工作区
403982
+ * @param cwd 当前工作区路径;空串表示未知(视为可见)
403983
+ * @returns 是否可见
403984
+ */
403985
+ function matchWorkspace(paths, cwd) {
403986
+ if (paths === null || paths === void 0) return true;
403987
+ if (paths.length === 0) return true;
403988
+ const normalizedCwd = normalizePath(cwd ?? "");
403989
+ if (normalizedCwd === "") return true;
403990
+ return paths.some((path) => {
403991
+ const normalized = normalizePath(path);
403992
+ if (normalized === "") return false;
403993
+ return normalizedCwd === normalized || normalizedCwd.startsWith(normalized + "\\");
403994
+ });
403995
+ }
403996
+ /**
403997
+ * 默认文档(首次打开菜单时的初始状态)。
403998
+ * @author ddj 2026年10月30号
403999
+ * @returns 默认文档
404000
+ */
404001
+ function defaultDoc() {
404002
+ return {
404003
+ version: 1,
404004
+ revision: 0,
404005
+ updatedAt: "",
404006
+ enabled: true,
404007
+ clickAction: "send",
404008
+ maxDepth: 3,
404009
+ launcher: {
404010
+ x: null,
404011
+ y: null
404012
+ },
404013
+ items: []
404014
+ };
404015
+ }
404016
+ /**
404017
+ * 生成新菜单项 id(类型前缀 + 时间 + 随机,跨会话不重复)。
404018
+ * @author ddj 2026年10月30号
404019
+ * @param type 项类型
404020
+ * @returns 新 id
404021
+ */
404022
+ function newQuickId(type) {
404023
+ const prefix = type === "group" ? "g" : "b";
404024
+ const random = Math.random().toString(36).slice(2, 8);
404025
+ return prefix + "_" + Date.now().toString(36) + random;
404026
+ }
404027
+ /**
404028
+ * 归一化限定工作区列表(去空、去重、归一化;空结果回落到 null=全部)。
404029
+ * @author ddj 2026年10月30号
404030
+ * @param raw 原始值
404031
+ * @returns 限定列表或 null
404032
+ */
404033
+ function normalizeWorkspaces(raw) {
404034
+ if (!Array.isArray(raw)) return null;
404035
+ const out = [];
404036
+ for (const entry of raw) {
404037
+ if (typeof entry !== "string") continue;
404038
+ const normalized = normalizePath(entry);
404039
+ if (normalized === "") continue;
404040
+ if (out.includes(normalized)) continue;
404041
+ out.push(normalized);
404042
+ }
404043
+ return out.length > 0 ? out : null;
404044
+ }
404045
+ /**
404046
+ * 绑定规则的稳定键(校验、去重、内联正文映射共用)。
404047
+ * @author ddj 2026年10月30号
404048
+ * @param binding 规则绑定
404049
+ * @returns 键
404050
+ */
404051
+ function bindingKey(binding) {
404052
+ const workspace = normalizePath(binding.workspacePath ?? "");
404053
+ return binding.scope + "|" + workspace + "|" + binding.file;
404054
+ }
404055
+ /**
404056
+ * 归一化单条规则绑定(scope/file 必填,项目规则必须带工作区)。
404057
+ * @author ddj 2026年10月30号
404058
+ * @param raw 原始值
404059
+ * @returns 绑定或 null
404060
+ */
404061
+ function normalizeRule(raw) {
404062
+ const record = asRecord(raw);
404063
+ if (!record) return null;
404064
+ const file = asString(record.file).trim();
404065
+ if (file === "") return null;
404066
+ const scope = record.scope === "project" ? "project" : "user";
404067
+ const binding = {
404068
+ scope,
404069
+ file
404070
+ };
404071
+ if (scope === "project") {
404072
+ const workspacePath = asString(record.workspacePath).trim();
404073
+ if (workspacePath === "") return null;
404074
+ binding.workspacePath = workspacePath;
404075
+ }
404076
+ if (record.origin === "current" || record.origin === "legacy") binding.origin = record.origin;
404077
+ return binding;
404078
+ }
404079
+ /**
404080
+ * 归一化规则绑定列表(去重 + 上限)。
404081
+ * @author ddj 2026年10月30号
404082
+ * @param raw 原始值
404083
+ * @returns 绑定列表
404084
+ */
404085
+ function normalizeRules(raw) {
404086
+ if (!Array.isArray(raw)) return [];
404087
+ const out = [];
404088
+ for (const entry of raw) {
404089
+ const binding = normalizeRule(entry);
404090
+ if (!binding) continue;
404091
+ if (out.some((existing) => bindingKey(existing) === bindingKey(binding))) continue;
404092
+ out.push(binding);
404093
+ }
404094
+ return out.slice(0, 24);
404095
+ }
404096
+ /**
404097
+ * 归一化技能名列表(只保留 kebab-case,去重 + 上限)。
404098
+ * @author ddj 2026年10月30号
404099
+ * @param raw 原始值
404100
+ * @returns 技能名列表
404101
+ */
404102
+ function normalizeSkills(raw) {
404103
+ if (!Array.isArray(raw)) return [];
404104
+ const out = [];
404105
+ for (const entry of raw) {
404106
+ if (typeof entry !== "string") continue;
404107
+ const name = entry.trim();
404108
+ if (name === "" || !QUICK_MENU_SKILL_RE.test(name)) continue;
404109
+ if (out.includes(name)) continue;
404110
+ out.push(name);
404111
+ }
404112
+ return out.slice(0, 24);
404113
+ }
404114
+ /**
404115
+ * 归一化单个菜单项(非法类型/缺 id 返回 null)。
404116
+ * @author ddj 2026年10月30号
404117
+ * @param raw 原始值
404118
+ * @returns 菜单项或 null
404119
+ */
404120
+ function normalizeItem(raw) {
404121
+ const record = asRecord(raw);
404122
+ if (!record) return null;
404123
+ const type = record.type === "group" ? "group" : "button";
404124
+ const id = asString(record.id).trim();
404125
+ if (id === "") return null;
404126
+ const name = cutText(asString(record.name).trim() || FALLBACK_NAME, 40);
404127
+ const order = clampNumber(record.order, 0, 999, 100);
404128
+ const rawParent = record.parentId;
404129
+ const base = {
404130
+ id,
404131
+ type,
404132
+ name,
404133
+ order,
404134
+ parentId: typeof rawParent === "string" && rawParent.trim() !== "" ? rawParent.trim() : null,
404135
+ workspaces: normalizeWorkspaces(record.workspaces)
404136
+ };
404137
+ if (type === "group") return {
404138
+ ...base,
404139
+ type: "group"
404140
+ };
404141
+ const button = {
404142
+ ...base,
404143
+ type: "button",
404144
+ prompt: cutText(asString(record.prompt), QUICK_MENU_PROMPT_CAP),
404145
+ skills: normalizeSkills(record.skills),
404146
+ rules: normalizeRules(record.rules),
404147
+ ruleMode: record.ruleMode === "inline" ? "inline" : "reference"
404148
+ };
404149
+ if (record.clickAction === "send" || record.clickAction === "fill") button.clickAction = record.clickAction;
404150
+ return button;
404151
+ }
404152
+ /**
404153
+ * 判断从起点上溯的父链是否会回到目标项(环检测;超深链也按非法处理)。
404154
+ * @author ddj 2026年10月30号
404155
+ * @param byId id → 项
404156
+ * @param startId 起点父 id
404157
+ * @param targetId 目标项 id
404158
+ * @returns 是否成环
404159
+ */
404160
+ function chainHas(byId, startId, targetId) {
404161
+ let current = startId;
404162
+ let guard = 0;
404163
+ while (current !== null) {
404164
+ if (current === targetId) return true;
404165
+ if (guard > 7) return true;
404166
+ const parent = byId.get(current);
404167
+ if (!parent) return false;
404168
+ current = parent.parentId;
404169
+ guard += 1;
404170
+ }
404171
+ return false;
404172
+ }
404173
+ /**
404174
+ * 修补父链问题:父不存在/父是按钮/自指/形成环时把子项上抬到最近合法祖组或顶层。
404175
+ * @author ddj 2026年10月30号
404176
+ * @param items 归一化后的项
404177
+ * @returns 修补后的项
404178
+ */
404179
+ function repairParents(items) {
404180
+ const byId = /* @__PURE__ */ new Map();
404181
+ for (const item of items) byId.set(item.id, item);
404182
+ for (const item of items) {
404183
+ let resolved = item.parentId;
404184
+ let guard = 0;
404185
+ while (resolved !== null && guard <= 7) {
404186
+ const parent = byId.get(resolved);
404187
+ if (!parent) {
404188
+ resolved = null;
404189
+ break;
404190
+ }
404191
+ if (parent.type === "group") break;
404192
+ resolved = parent.parentId;
404193
+ guard += 1;
404194
+ }
404195
+ if (resolved !== null && chainHas(byId, resolved, item.id)) resolved = null;
404196
+ item.parentId = resolved;
404197
+ }
404198
+ return items;
404199
+ }
404200
+ /**
404201
+ * 按父级分组重排优先级(order 归一为 10/20/30…,保证展示稳定且无并列)。
404202
+ * @author ddj 2026年10月30号
404203
+ * @param items 菜单项
404204
+ * @returns 同一数组(就地写入 order)
404205
+ */
404206
+ function normalizeOrder(items) {
404207
+ const groups = /* @__PURE__ */ new Map();
404208
+ for (const item of items) {
404209
+ const key = item.parentId ?? "";
404210
+ const bucket = groups.get(key);
404211
+ if (bucket) bucket.push(item);
404212
+ else groups.set(key, [item]);
404213
+ }
404214
+ for (const bucket of groups.values()) {
404215
+ bucket.sort((left, right) => {
404216
+ if (left.order !== right.order) return left.order - right.order;
404217
+ if (left.name !== right.name) return left.name < right.name ? -1 : 1;
404218
+ return left.id < right.id ? -1 : 1;
404219
+ });
404220
+ bucket.forEach((item, index) => {
404221
+ item.order = (index + 1) * 10;
404222
+ });
404223
+ }
404224
+ return items;
404225
+ }
404226
+ /**
404227
+ * 归一化整份文档(任何脏数据都收敛到可用形态,绝不抛错)。
404228
+ * @author ddj 2026年10月30号
404229
+ * @param raw 原始值(JSON.parse 结果或任意 unknown)
404230
+ * @returns 合法文档
404231
+ */
404232
+ function normalizeDoc(raw) {
404233
+ const fallback = defaultDoc();
404234
+ const record = asRecord(raw);
404235
+ if (!record) return fallback;
404236
+ const rawItems = Array.isArray(record.items) ? record.items : [];
404237
+ const seen = /* @__PURE__ */ new Set();
404238
+ const items = [];
404239
+ for (const entry of rawItems) {
404240
+ const item = normalizeItem(entry);
404241
+ if (!item || seen.has(item.id)) continue;
404242
+ seen.add(item.id);
404243
+ items.push(item);
404244
+ if (items.length >= 200) break;
404245
+ }
404246
+ const x = typeof record.launcher === "object" && record.launcher !== null ? asRecord(record.launcher) : null;
404247
+ return {
404248
+ version: 1,
404249
+ revision: Math.floor(clampNumber(record.revision, 0, Number.MAX_SAFE_INTEGER, 0)),
404250
+ updatedAt: cutText(asString(record.updatedAt), 40),
404251
+ enabled: typeof record.enabled === "boolean" ? record.enabled : true,
404252
+ clickAction: record.clickAction === "fill" ? "fill" : "send",
404253
+ maxDepth: Math.round(clampNumber(record.maxDepth, 1, 5, 3)),
404254
+ launcher: {
404255
+ x: x && typeof x.x === "number" && Number.isFinite(x.x) ? x.x : null,
404256
+ y: x && typeof x.y === "number" && Number.isFinite(x.y) ? x.y : null
404257
+ },
404258
+ items: normalizeOrder(repairParents(items))
404259
+ };
404260
+ }
404261
+ /**
404262
+ * 结构迁移入口(当前只有 v1;后续版本在此收敛旧结构)。
404263
+ * @author ddj 2026年10月30号
404264
+ * @param raw 原始值
404265
+ * @returns 合法文档
404266
+ */
404267
+ function migrateDoc(raw) {
404268
+ return normalizeDoc(raw);
404269
+ }
404270
+ /**
404271
+ * 序列化文档(host 落盘与导入导出共用)。
404272
+ * @author ddj 2026年10月30号
404273
+ * @param doc 文档
404274
+ * @returns JSON 文本
404275
+ */
404276
+ function serializeDoc(doc) {
404277
+ return JSON.stringify(normalizeDoc(doc), null, 2);
404278
+ }
404279
+ /**
404280
+ * 解析文档文本(解析失败返回默认文档并给出告警文案)。
404281
+ * @author ddj 2026年10月30号
404282
+ * @param text 文件内容
404283
+ * @returns 文档 + 可选告警
404284
+ */
404285
+ function parseDoc(text) {
404286
+ try {
404287
+ return { doc: migrateDoc(JSON.parse(text)) };
404288
+ } catch (error) {
404289
+ const reason = error instanceof Error ? error.message : String(error);
404290
+ return {
404291
+ doc: defaultDoc(),
404292
+ warning: "快捷菜单配置解析失败(" + reason + "),已回落默认配置"
404293
+ };
404294
+ }
404295
+ }
404296
+ /**
404297
+ * 计算项所在层级(root 的直接子项为第 1 层;孤儿按第 1 层)。
404298
+ * @author ddj 2026年10月30号
404299
+ * @param doc 文档
404300
+ * @param id 项 id
404301
+ * @returns 层级(找不到返回 0)
404302
+ */
404303
+ function depthOf(doc, id) {
404304
+ const byId = /* @__PURE__ */ new Map();
404305
+ for (const item of doc.items) byId.set(item.id, item);
404306
+ let current = byId.get(id);
404307
+ if (!current) return 0;
404308
+ let depth = 1;
404309
+ let guard = 0;
404310
+ while (current.parentId !== null && guard < 6) {
404311
+ const parent = byId.get(current.parentId);
404312
+ if (!parent) break;
404313
+ depth += 1;
404314
+ current = parent;
404315
+ guard += 1;
404316
+ }
404317
+ return depth;
404318
+ }
404319
+ /**
404320
+ * 判断能否在指定父级下新增子项(达到最大嵌套层数时禁止)。
404321
+ * @author ddj 2026年10月30号
404322
+ * @param doc 文档
404323
+ * @param parentId 父组 id(null 为顶层)
404324
+ * @returns 是否可新增
404325
+ */
404326
+ function canCreateAt(doc, parentId) {
404327
+ if (parentId === null) return doc.maxDepth >= 1;
404328
+ const depth = depthOf(doc, parentId);
404329
+ if (depth <= 0) return false;
404330
+ return depth + 1 <= doc.maxDepth;
404331
+ }
404332
+ /**
404333
+ * 取某组下可见的子项(按优先级排序;cwd 为空时不做工作区过滤)。
404334
+ * @author ddj 2026年10月30号
404335
+ * @param doc 文档
404336
+ * @param parentId 父组 id(null 为顶层)
404337
+ * @param cwd 当前工作区路径
404338
+ * @returns 可见子项
404339
+ */
404340
+ function childrenOf(doc, parentId, cwd) {
404341
+ return doc.items.filter((item) => item.parentId === parentId).filter((item) => itemVisibleIn(doc, item, cwd)).sort((left, right) => {
404342
+ if (left.order !== right.order) return left.order - right.order;
404343
+ return left.name < right.name ? -1 : 1;
404344
+ });
404345
+ }
404346
+ /**
404347
+ * 计算项的有效工作区限定(沿祖先链最近的显式限定生效)。
404348
+ * @author ddj 2026年10月30号
404349
+ * @param doc 文档
404350
+ * @param item 菜单项
404351
+ * @returns 限定列表或 null
404352
+ */
404353
+ function inheritWorkspaces(doc, item) {
404354
+ const byId = /* @__PURE__ */ new Map();
404355
+ for (const entry of doc.items) byId.set(entry.id, entry);
404356
+ let current = item;
404357
+ let guard = 0;
404358
+ while (current && guard < 7) {
404359
+ if (current.workspaces !== null) return current.workspaces;
404360
+ current = current.parentId === null ? void 0 : byId.get(current.parentId);
404361
+ guard += 1;
404362
+ }
404363
+ return null;
404364
+ }
404365
+ /**
404366
+ * 判断项在当前工作区是否可见(继承父组限定)。
404367
+ * @author ddj 2026年10月30号
404368
+ * @param doc 文档
404369
+ * @param item 菜单项
404370
+ * @param cwd 当前工作区路径
404371
+ * @returns 是否可见
404372
+ */
404373
+ function itemVisibleIn(doc, item, cwd) {
404374
+ return matchWorkspace(inheritWorkspaces(doc, item), cwd);
404375
+ }
404376
+ /**
404377
+ * 收集某组的所有后代 id(防止把组设为自己的子组)。
404378
+ * @author ddj 2026年10月30号
404379
+ * @param doc 文档
404380
+ * @param id 组 id
404381
+ * @returns 后代 id 列表(含自身)
404382
+ */
404383
+ function descendantIds(doc, id) {
404384
+ const out = [id];
404385
+ let index = 0;
404386
+ while (index < out.length) {
404387
+ const current = out[index];
404388
+ index += 1;
404389
+ for (const item of doc.items) {
404390
+ if (item.parentId !== current) continue;
404391
+ if (out.includes(item.id)) continue;
404392
+ out.push(item.id);
404393
+ }
404394
+ }
404395
+ return out;
404396
+ }
404397
+ /**
404398
+ * 在校验上下文里定位规则文件(用户规则按 origin 消歧、项目规则按工作区定位)。
404399
+ * @author ddj 2026年10月30号
404400
+ * @param binding 规则绑定
404401
+ * @param snapshot 规则列表快照
404402
+ * @returns 命中信息:规则元数据 / 工作区目录缺失 / 未找到
404403
+ */
404404
+ function lookupRule(binding, snapshot) {
404405
+ if (binding.scope === "user") {
404406
+ const hit = snapshot.user.find((rule) => {
404407
+ if (rule.file !== binding.file) return false;
404408
+ if (binding.origin && rule.origin !== binding.origin) return false;
404409
+ return true;
404410
+ });
404411
+ return hit ? { info: hit } : {};
404412
+ }
404413
+ const wanted = normalizePath(binding.workspacePath ?? "");
404414
+ const project = snapshot.projects.find((entry) => normalizePath(entry.workspacePath) === wanted);
404415
+ if (!project) return {};
404416
+ if (project.missingDir) return { missingDir: project.workspacePath };
404417
+ const hit = project.rules.find((rule) => rule.file === binding.file);
404418
+ return hit ? { info: hit } : {};
404419
+ }
404420
+ /**
404421
+ * 校验一条规则绑定(文件缺失/停用/自身报错/跨工作区)。
404422
+ * @author ddj 2026年10月30号
404423
+ * @param binding 规则绑定
404424
+ * @param ctx 校验上下文
404425
+ * @returns 绑定状态
404426
+ */
404427
+ function validateRule(binding, ctx) {
404428
+ if (!ctx.rules) return {
404429
+ state: "unknown",
404430
+ reason: "未校验(规则列表未加载)"
404431
+ };
404432
+ const found = lookupRule(binding, ctx.rules);
404433
+ if (found.missingDir) return {
404434
+ state: "error",
404435
+ reason: "工作区规则目录不存在:" + found.missingDir + "\\.dsh\\rules"
404436
+ };
404437
+ if (!found.info) return {
404438
+ state: "error",
404439
+ reason: "规则文件不存在或已移除:" + binding.file
404440
+ };
404441
+ if (found.info.error) return {
404442
+ state: "error",
404443
+ reason: "规则解析失败:" + found.info.error
404444
+ };
404445
+ if (!found.info.enabled) return {
404446
+ state: "warn",
404447
+ reason: "规则已停用,不会生效:" + binding.file
404448
+ };
404449
+ if (binding.scope === "project") {
404450
+ const wanted = normalizePath(binding.workspacePath ?? "");
404451
+ const current = normalizePath(ctx.cwd ?? "");
404452
+ if (current !== "" && wanted !== current) return {
404453
+ state: "warn",
404454
+ reason: "规则来自其他工作区,发送时会跳过:" + binding.file
404455
+ };
404456
+ }
404457
+ return { state: "ok" };
404458
+ }
404459
+ /**
404460
+ * 校验一条技能绑定(技能按会话枚举,不可见不等同于已删除)。
404461
+ * @author ddj 2026年10月30号
404462
+ * @param name 技能名
404463
+ * @param ctx 校验上下文
404464
+ * @returns 绑定状态
404465
+ */
404466
+ function validateSkill(name, ctx) {
404467
+ if (ctx.skillNames === void 0 || ctx.skillNames === null) return {
404468
+ state: "unknown",
404469
+ reason: "未校验(当前没有活跃会话)"
404470
+ };
404471
+ if (ctx.skillNames.includes(name)) return { state: "ok" };
404472
+ return {
404473
+ state: "warn",
404474
+ reason: "当前会话不可见(可能已移除或未被该会话加载):" + name
404475
+ };
404476
+ }
404477
+ /**
404478
+ * 聚合单个菜单项的绑定状态(组无绑定,恒为空)。
404479
+ * @author ddj 2026年10月30号
404480
+ * @param item 菜单项
404481
+ * @param ctx 校验上下文
404482
+ * @returns 失效与告警原因
404483
+ */
404484
+ function itemStatus(item, ctx) {
404485
+ const status = {
404486
+ errors: [],
404487
+ warnings: []
404488
+ };
404489
+ if (item.type !== "button") return status;
404490
+ if (item.prompt.trim() === "") status.warnings.push("提示词为空,只发送绑定的技能与规则");
404491
+ for (const binding of item.rules) {
404492
+ const result = validateRule(binding, ctx);
404493
+ if (result.state === "error") status.errors.push(result.reason ?? "规则失效");
404494
+ else if (result.state === "warn") status.warnings.push(result.reason ?? "规则需注意");
404495
+ }
404496
+ for (const name of item.skills) {
404497
+ const result = validateSkill(name, ctx);
404498
+ if (result.state === "error") status.errors.push(result.reason ?? "技能失效");
404499
+ else if (result.state === "warn") status.warnings.push(result.reason ?? "技能需注意");
404500
+ }
404501
+ return status;
404502
+ }
404503
+ /**
404504
+ * 校验编辑中的草稿(保存前拦截明显错误)。
404505
+ * @author ddj 2026年10月30号
404506
+ * @param draft 草稿
404507
+ * @param doc 当前文档(用于兄弟重名与父链判断)
404508
+ * @returns 错误与告警
404509
+ */
404510
+ function validateItem(draft, doc) {
404511
+ const status = {
404512
+ errors: [],
404513
+ warnings: []
404514
+ };
404515
+ const name = draft.name.trim();
404516
+ if (name === "") status.errors.push("名称不能为空");
404517
+ if (name.length > 40) status.errors.push("名称过长(上限 40 字)");
404518
+ if (draft.order < 0 || draft.order > 999) status.errors.push("优先级需在 0 到 999 之间");
404519
+ if (draft.parentId !== null) {
404520
+ const parent = doc.items.find((item) => item.id === draft.parentId);
404521
+ if (!parent) status.errors.push("归属组不存在");
404522
+ else if (parent.type !== "group") status.errors.push("只能归属到组");
404523
+ else if (descendantIds(doc, draft.id).includes(parent.id)) status.errors.push("不能把组归属到自己的子组");
404524
+ else if (depthOf(doc, parent.id) + 1 > doc.maxDepth) status.errors.push("已达最大嵌套层数(上限 " + doc.maxDepth + ")");
404525
+ }
404526
+ const siblings = doc.items.filter((item) => item.parentId === draft.parentId && item.id !== draft.id);
404527
+ if (name !== "" && siblings.some((item) => item.name === name)) status.warnings.push("同级已有同名项");
404528
+ if (draft.type === "button") {
404529
+ if (draft.prompt.length > 8192) status.errors.push("提示词过长(上限 8192 字符)");
404530
+ if (draft.prompt.trim() === "" && draft.skills.length === 0 && draft.rules.length === 0) status.errors.push("提示词与绑定都为空,点击后没有内容可发送");
404531
+ for (const name of draft.skills) if (!QUICK_MENU_SKILL_RE.test(name)) status.errors.push("技能名不合法:" + name);
404532
+ for (const binding of draft.rules) if (binding.scope === "project" && !binding.workspacePath) status.errors.push("项目规则缺少工作区:" + binding.file);
404533
+ }
404534
+ return status;
404535
+ }
404536
+ /**
404537
+ * 挑选可用技能(不可见技能不进消息,并留下告警)。
404538
+ * @author ddj 2026年10月30号
404539
+ * @param item 按钮
404540
+ * @param ctx 合成上下文
404541
+ * @param warnings 告警收集器
404542
+ * @returns 实际发送的技能名
404543
+ */
404544
+ function pickSkills(item, ctx, warnings) {
404545
+ const out = [];
404546
+ for (const name of item.skills) {
404547
+ const result = validateSkill(name, ctx);
404548
+ if (result.state === "error" || result.state === "warn") {
404549
+ warnings.push(result.reason ?? "技能不可用:" + name);
404550
+ continue;
404551
+ }
404552
+ out.push(name);
404553
+ }
404554
+ return out;
404555
+ }
404556
+ /**
404557
+ * 挑选可用规则(失效/停用/跨工作区规则不注入,并留下告警)。
404558
+ * @author ddj 2026年10月30号
404559
+ * @param item 按钮
404560
+ * @param ctx 合成上下文
404561
+ * @param warnings 告警收集器
404562
+ * @returns 发送的规则绑定与元数据
404563
+ */
404564
+ function pickRules(item, ctx, warnings) {
404565
+ const out = [];
404566
+ for (const binding of item.rules) {
404567
+ const result = validateRule(binding, ctx);
404568
+ if (result.state !== "ok") {
404569
+ warnings.push(result.reason ?? "规则不可用:" + binding.file);
404570
+ continue;
404571
+ }
404572
+ const found = ctx.rules ? lookupRule(binding, ctx.rules) : {};
404573
+ if (found.info) out.push({ info: found.info });
404574
+ }
404575
+ return out;
404576
+ }
404577
+ /**
404578
+ * 生成规则引用段(只给名字与绝对路径,由模型按需读取)。
404579
+ * @author ddj 2026年10月30号
404580
+ * @param rules 规则元数据
404581
+ * @returns 引用段落(无规则返回空串)
404582
+ */
404583
+ function buildRuleRefs(rules) {
404584
+ if (rules.length === 0) return "";
404585
+ return "---\n引用的规则:\n" + rules.map((rule) => "- " + rule.info.file + "(绝对路径:" + rule.info.absPath + ")").join("\n") + "\n(需要时按需读取上述规则文件)";
404586
+ }
404587
+ /**
404588
+ * 生成规则内联段(缺正文或超长时截断并告警)。
404589
+ * @author ddj 2026年10月30号
404590
+ * @param item 按钮
404591
+ * @param rules 规则元数据
404592
+ * @param ctx 合成上下文
404593
+ * @param warnings 告警收集器
404594
+ * @returns 内联段落(无内容返回空串)
404595
+ */
404596
+ function buildInline(item, rules, ctx, warnings) {
404597
+ if (rules.length === 0) return "";
404598
+ const contents = ctx.inlineContents ?? {};
404599
+ const blocks = [];
404600
+ for (const rule of rules) {
404601
+ const raw = contents[bindingKey({
404602
+ scope: rule.info.scope,
404603
+ file: rule.info.file,
404604
+ workspacePath: rule.info.scope === "project" ? ctx.cwd : void 0
404605
+ })];
404606
+ if (typeof raw !== "string" || raw.trim() === "") {
404607
+ warnings.push("未能读取规则正文,已跳过内联:" + rule.info.file);
404608
+ continue;
404609
+ }
404610
+ blocks.push("### " + rule.info.file + "\n" + cutText(raw, QUICK_MENU_RULE_CAP));
404611
+ }
404612
+ if (blocks.length === 0) return "";
404613
+ return "## 已绑定规则(节选)\n\n" + blocks.join("\n\n");
404614
+ }
404615
+ /**
404616
+ * 替换提示词占位符(未知占位符原样保留)。
404617
+ * @author ddj 2026年10月30号
404618
+ * @param prompt 原文
404619
+ * @param values 占位符取值
404620
+ * @returns 替换结果
404621
+ */
404622
+ function fillPrompt(prompt, values) {
404623
+ let out = prompt;
404624
+ for (const [key, value] of Object.entries(values)) out = out.split("{{" + key + "}}").join(value);
404625
+ return out.trim();
404626
+ }
404627
+ /**
404628
+ * 合成发送文本:提示词 + 技能 token + 规则引用/内联(自动剔失效绑定)。
404629
+ * @author ddj 2026年10月30号
404630
+ * @param item 按钮
404631
+ * @param ctx 合成上下文
404632
+ * @returns 文本与告警
404633
+ */
404634
+ function composePrompt(item, ctx) {
404635
+ const warnings = [];
404636
+ const skills = pickSkills(item, ctx, warnings);
404637
+ const rules = pickRules(item, ctx, warnings);
404638
+ const tokens = skills.map((name) => "/" + name).join(" ");
404639
+ const promptText = fillPrompt(item.prompt, {
404640
+ workspace: ctx.cwd ?? "",
404641
+ selection: ctx.selection ?? "",
404642
+ date: ctx.date ?? "",
404643
+ rules: rules.map((rule) => rule.info.file).join("、"),
404644
+ skills: tokens
404645
+ });
404646
+ const blocks = [];
404647
+ if (promptText !== "") blocks.push(promptText);
404648
+ if (tokens !== "") blocks.push(tokens);
404649
+ if (item.ruleMode === "reference") {
404650
+ const refs = buildRuleRefs(rules);
404651
+ if (refs !== "") blocks.push(refs);
404652
+ } else {
404653
+ const inline = buildInline(item, rules, ctx, warnings);
404654
+ if (inline !== "") blocks.push(inline);
404655
+ }
404656
+ return {
404657
+ text: blocks.join("\n\n").trim(),
404658
+ warnings
404659
+ };
404660
+ }
404661
+ /**
404662
+ * 计算悬浮按钮默认位置(输入框左上角上方让出一个按钮高度)。
404663
+ * @author ddj 2026年10月30号
404664
+ * @param seat 输入框锚点矩形(浮层坐标系;缺失时回落左上角)
404665
+ * @param layer 浮层矩形
404666
+ * @param size 按钮尺寸
404667
+ * @param margin 边距
404668
+ * @returns 位置
404669
+ */
404670
+ function anchorLauncher(seat, layer, size, margin = 8) {
404671
+ const maxLeft = Math.max(margin, layer.width - size.width - margin);
404672
+ const maxTop = Math.max(margin, layer.height - size.height - margin);
404673
+ if (!seat) return {
404674
+ left: clampNumber(margin, margin, maxLeft, margin),
404675
+ top: clampNumber(margin, margin, maxTop, margin)
404676
+ };
404677
+ const left = seat.left - layer.left;
404678
+ const top = seat.top - layer.top - size.height - margin;
404679
+ return {
404680
+ left: clampNumber(left, margin, maxLeft, margin),
404681
+ top: clampNumber(top, margin, maxTop, margin)
404682
+ };
404683
+ }
404684
+ /**
404685
+ * 计算级联子列位置(右侧放不下则翻到左侧,纵向夹取进视口)。
404686
+ * @author ddj 2026年10月30号
404687
+ * @param anchor 父列或父行矩形
404688
+ * @param size 子列尺寸
404689
+ * @param viewport 浮层尺寸
404690
+ * @param gap 列间距
404691
+ * @returns 子列矩形
404692
+ */
404693
+ function placeColumn(anchor, size, viewport, gap = 6) {
404694
+ const margin = 8;
404695
+ const maxLeft = Math.max(margin, viewport.width - size.width - margin);
404696
+ const maxTop = Math.max(margin, viewport.height - size.height - margin);
404697
+ const rightLeft = anchor.left + anchor.width + gap;
404698
+ const left = rightLeft + size.width > viewport.width - margin ? anchor.left - size.width - gap : rightLeft;
404699
+ const top = clampNumber(anchor.top, margin, maxTop, margin);
404700
+ return {
404701
+ left: clampNumber(left, margin, maxLeft, margin),
404702
+ top,
404703
+ width: size.width,
404704
+ height: size.height
404705
+ };
404706
+ }
404707
+ /**
404708
+ * 计算 SAO 风格居中突出缩放(离列中心越远越小越淡)。
404709
+ *
404710
+ * 幅度按「列内可用宽度」定:列 220px、左右内边距 7px + 1px 边框,行宽 204px,
404711
+ * 中心行放到 1.03(约 210px)刚好还在列内、不裁字;再大就会顶出滚动区。
404712
+ * 曲线用 smoothstep,让中间三四行连成一片「实」的区域,两端快速退淡,
404713
+ * 与 CSS 的上下虚化叠出「居中突出」的体积感。
404714
+ * @author ddj 2026年10月30号
404715
+ * @param index 行下标
404716
+ * @param count 行数
404717
+ * @returns 缩放与透明度
404718
+ */
404719
+ function centerScale(index, count) {
404720
+ if (count <= 1) return {
404721
+ scale: 1,
404722
+ opacity: 1
404723
+ };
404724
+ const center = (count - 1) / 2;
404725
+ const distance = Math.abs(index - center) / Math.max(center, .5);
404726
+ const ratio = Math.min(1, Math.max(0, distance));
404727
+ const eased = ratio * ratio * (3 - 2 * ratio);
404728
+ return {
404729
+ scale: 1.03 - .075 * eased,
404730
+ opacity: 1 - .35 * eased
404731
+ };
404732
+ }
404733
+ //#endregion
404734
+ //#region src/client/quickMenuStore.ts
404735
+ /**
404736
+ * dsh-vscode-mode client — 快捷菜单 store(文档 + 绑定快照 + 坐标防抖)。
404737
+ *
404738
+ * 为什么客户端要 store:菜单在多列里同时展示,多列共享同一份文档与绑定校验结果;
404739
+ * 用外部 store + useSyncExternalStore,避免把整棵树塞进一个组件的 state。
404740
+ * 写入一律「先改内存 → 整体提交 → host 归一后回传」;冲突(其他窗口改过)时重拉,
404741
+ * 不拿本地旧版本覆盖磁盘。
404742
+ * 作者 ddj 2026年10月30号
404743
+ */
404744
+ /** 绑定快照有效期(菜单打开/聚焦时刷新,期间复用)。 */
404745
+ const BINDINGS_TTL_MS = 6e4;
404746
+ /** 坐标落盘防抖。 */
404747
+ const LAUNCHER_DEBOUNCE_MS = 500;
404748
+ /**
404749
+ * 创建快捷菜单 store。
404750
+ * @author ddj 2026年10月30号
404751
+ * @param loaders 外部数据加载器
404752
+ * @returns store 实例
404753
+ */
404754
+ function createMenuStore(loaders) {
404755
+ let state = {
404756
+ status: "idle",
404757
+ doc: defaultDoc()
404758
+ };
404759
+ let version = 0;
404760
+ let mounted = false;
404761
+ const listeners = /* @__PURE__ */ new Set();
404762
+ /** 绑定快照(cwd/sessionId 变化或超期即重拉)。 */
404763
+ let bindings = { at: 0 };
404764
+ /** 待落盘坐标与定时器。 */
404765
+ let pending = null;
404766
+ let timer = null;
404767
+ /**
404768
+ * 写入状态并通知订阅者。
404769
+ * @author ddj 2026年10月30号
404770
+ * @param next 新状态
404771
+ */
404772
+ function publish(next) {
404773
+ state = next;
404774
+ version += 1;
404775
+ for (const listener of listeners) listener();
404776
+ }
404777
+ /**
404778
+ * 合并补丁到当前状态。
404779
+ * @author ddj 2026年10月30号
404780
+ * @param patch 局部更新
404781
+ */
404782
+ function patch(patchState) {
404783
+ publish({
404784
+ ...state,
404785
+ ...patchState
404786
+ });
404787
+ }
404788
+ /**
404789
+ * 读取规则与技能快照(失败保留旧值,不打断菜单)。
404790
+ * @author ddj 2026年10月30号
404791
+ * @param force 是否强制重拉
404792
+ */
404793
+ async function refreshBindings(force = false) {
404794
+ const scope = loaders.scope();
404795
+ if (!force && Date.now() - bindings.at < BINDINGS_TTL_MS && bindings.cwd === scope.cwd && bindings.sessionId === scope.sessionId) return;
404796
+ bindings = {
404797
+ ...bindings,
404798
+ at: Date.now(),
404799
+ cwd: scope.cwd,
404800
+ sessionId: scope.sessionId
404801
+ };
404802
+ try {
404803
+ bindings.rules = await loaders.rules();
404804
+ } catch {
404805
+ bindings.rules = void 0;
404806
+ }
404807
+ try {
404808
+ bindings.skillNames = scope.sessionId ? await loaders.skills(scope.sessionId) : null;
404809
+ } catch {
404810
+ bindings.skillNames = null;
404811
+ }
404812
+ version += 1;
404813
+ for (const listener of listeners) listener();
404814
+ }
404815
+ /**
404816
+ * 拉取文档(并顺带刷新绑定快照)。
404817
+ * @author ddj 2026年10月30号
404818
+ * @param force 是否强制重拉
404819
+ */
404820
+ async function load(force = false) {
404821
+ if (state.status === "loading") return;
404822
+ patch({ status: "loading" });
404823
+ try {
404824
+ const result = await rpc("quickMenu.get", {});
404825
+ if (result.ok) patch({
404826
+ status: "ready",
404827
+ doc: result.doc,
404828
+ warning: result.warning,
404829
+ error: void 0
404830
+ });
404831
+ else patch({
404832
+ status: "error",
404833
+ error: result.error
404834
+ });
404835
+ } catch (error) {
404836
+ patch({
404837
+ status: "error",
404838
+ error: "读取快捷菜单失败:" + String(error)
404839
+ });
404840
+ }
404841
+ await refreshBindings(force);
404842
+ }
404843
+ /**
404844
+ * 提交整份文档(host 归一后回传,冲突时重拉)。
404845
+ * @author ddj 2026年10月30号
404846
+ * @param doc 待提交文档
404847
+ * @returns 写结果
404848
+ */
404849
+ async function save(doc) {
404850
+ try {
404851
+ const result = await rpc("quickMenu.save", {
404852
+ doc,
404853
+ baseRevision: state.doc.revision
404854
+ });
404855
+ if (result.ok) {
404856
+ patch({
404857
+ status: "ready",
404858
+ doc: result.doc,
404859
+ error: void 0,
404860
+ warning: void 0
404861
+ });
404862
+ return { ok: true };
404863
+ }
404864
+ await load(true);
404865
+ return {
404866
+ ok: false,
404867
+ error: result.error
404868
+ };
404869
+ } catch (error) {
404870
+ return {
404871
+ ok: false,
404872
+ error: "保存快捷菜单失败:" + String(error)
404873
+ };
404874
+ }
404875
+ }
404876
+ /**
404877
+ * 新增或替换一项(按 id 去重)。
404878
+ * @author ddj 2026年10月30号
404879
+ * @param draft 菜单项
404880
+ * @returns 写结果
404881
+ */
404882
+ async function upsert(draft) {
404883
+ const items = state.doc.items.filter((item) => item.id !== draft.id);
404884
+ items.push(draft);
404885
+ return save(normalizeDoc({
404886
+ ...state.doc,
404887
+ items
404888
+ }));
404889
+ }
404890
+ /**
404891
+ * 删除一项及其全部子项。
404892
+ * @author ddj 2026年10月30号
404893
+ * @param id 项 id
404894
+ * @returns 写结果
404895
+ */
404896
+ async function removeItem(id) {
404897
+ const doomed = descendantIds(state.doc, id);
404898
+ const items = state.doc.items.filter((item) => !doomed.includes(item.id));
404899
+ return save(normalizeDoc({
404900
+ ...state.doc,
404901
+ items
404902
+ }));
404903
+ }
404904
+ /**
404905
+ * 定时把待写坐标落盘。
404906
+ * @author ddj 2026年10月30号
404907
+ */
404908
+ async function flushLauncher() {
404909
+ if (timer !== null) {
404910
+ clearTimeout(timer);
404911
+ timer = null;
404912
+ }
404913
+ const target = pending;
404914
+ pending = null;
404915
+ if (!target) return;
404916
+ try {
404917
+ const result = await rpc("quickMenu.launcher", target);
404918
+ if (result.ok) patch({ doc: result.doc });
404919
+ } catch {}
404920
+ }
404921
+ /**
404922
+ * 记录坐标并防抖落盘。
404923
+ * @author ddj 2026年10月30号
404924
+ * @param x 横坐标
404925
+ * @param y 纵坐标
404926
+ */
404927
+ function patchLauncher(x, y) {
404928
+ pending = {
404929
+ x,
404930
+ y
404931
+ };
404932
+ if (timer !== null) clearTimeout(timer);
404933
+ timer = setTimeout(() => {
404934
+ flushLauncher();
404935
+ }, LAUNCHER_DEBOUNCE_MS);
404936
+ }
404937
+ return {
404938
+ subscribe(listener) {
404939
+ listeners.add(listener);
404940
+ return () => {
404941
+ listeners.delete(listener);
404942
+ };
404943
+ },
404944
+ getVersion() {
404945
+ return version;
404946
+ },
404947
+ getState() {
404948
+ return state;
404949
+ },
404950
+ getContext() {
404951
+ return {
404952
+ cwd: loaders.scope().cwd,
404953
+ rules: bindings.rules,
404954
+ skillNames: bindings.skillNames ?? null
404955
+ };
404956
+ },
404957
+ load,
404958
+ save,
404959
+ createItem: upsert,
404960
+ updateItem: upsert,
404961
+ removeItem,
404962
+ patchLauncher,
404963
+ flushLauncher,
404964
+ refreshBindings,
404965
+ markMounted(value) {
404966
+ mounted = value;
404967
+ version += 1;
404968
+ for (const listener of listeners) listener();
404969
+ },
404970
+ isMounted() {
404971
+ return mounted;
404972
+ },
404973
+ nextId(type) {
404974
+ return newQuickId(type);
404975
+ }
404976
+ };
404977
+ }
404978
+ //#endregion
404979
+ //#region src/shared/quickPick.ts
404980
+ /**
404981
+ * dsh-vscode-mode 快捷菜单共享纯函数 — 下拉选择器的状态与摘要文案。
404982
+ *
404983
+ * 纯函数模块:禁 node/react 导入(与 shared/quickMenu.ts 同约束)。
404984
+ * 为什么抽:工作区/技能/规则三个下拉的「触发器上显示什么」「怎么切换一项」是同一类
404985
+ * 纯计算,放在 shared 里可被 vitest 直接覆盖,避免只能靠肉眼在弹窗里验。
404986
+ *
404987
+ * 作者 ddj 2026年10月30号
404988
+ */
404989
+ /**
404990
+ * 推导工作区下拉的摘要(null / 空数组都算「全部工作区」)。
404991
+ * @author ddj 2026年10月30号
404992
+ * @param paths 已选路径列表(null = 全部工作区)
404993
+ * @param cwd 当前工作区(缺失时无法判定「当前工作区」态)
404994
+ * @returns 摘要(mode + 文案 + 原始路径)
404995
+ */
404996
+ function workspaceLabel(paths, cwd) {
404997
+ const list = (paths ?? []).slice();
404998
+ if (list.length === 0) return {
404999
+ mode: "all",
405000
+ label: "全部工作区",
405001
+ paths: []
405002
+ };
405003
+ if (list.length === 1 && cwd !== void 0 && normalizePath(cwd) !== "" && normalizePath(list[0]) === normalizePath(cwd)) return {
405004
+ mode: "current",
405005
+ label: "当前工作区",
405006
+ paths: list
405007
+ };
405008
+ return {
405009
+ mode: "some",
405010
+ label: "已选 " + list.length + " 个工作区",
405011
+ paths: list
405012
+ };
405013
+ }
405014
+ /**
405015
+ * 多选摘要文案(0 项、1~2 项全写、更多折叠成「A、B 等 N 项」)。
405016
+ * @author ddj 2026年10月30号
405017
+ * @param labels 已选项文案
405018
+ * @param unit 折叠时的单位(如「项技能」「条规则」)
405019
+ * @returns 触发器文案
405020
+ */
405021
+ function selectionLabel(labels, unit) {
405022
+ if (labels.length === 0) return "未绑定";
405023
+ if (labels.length <= 2) return labels.join("、");
405024
+ return labels.slice(0, 2).join("、") + " 等 " + labels.length + " " + unit;
405025
+ }
405026
+ /**
405027
+ * 工作区集合切换(归一化比较去重;清空即回到「全部工作区」的 null 语义)。
405028
+ * @author ddj 2026年10月30号
405029
+ * @param paths 当前列表(null = 全部)
405030
+ * @param target 目标工作区路径
405031
+ * @returns 新列表;空集合返回 null
405032
+ */
405033
+ function toggleWorkspace(paths, target) {
405034
+ const list = (paths ?? []).slice();
405035
+ const key = normalizePath(target);
405036
+ const index = list.findIndex((path) => normalizePath(path) === key);
405037
+ if (index >= 0) list.splice(index, 1);
405038
+ else list.push(target);
405039
+ return list.length === 0 ? null : list;
405040
+ }
405041
+ //#endregion
405042
+ //#region src/client/ui/QuickMenuPickers.ts
405043
+ /**
405044
+ * dsh-vscode-mode client — 快捷菜单配置弹窗的通用选择控件。
405045
+ *
405046
+ * 提供三件控件:多选下拉(技能/规则)、限定工作区下拉(全部/当前/更多)、设置项开关与分段。
405047
+ * 弹层用 position: fixed 挂在 body 之上的坐标,因此不受弹窗 body 的 overflow 裁剪;
405048
+ * 组件仍用 React.createElement(项目内无 JSX),样式落在 quickmenu.css 的 edrv-qm-pick* 类上。
405049
+ * 作者 ddj 2026年10月30号
405050
+ */
405051
+ /** 弹层最小/最大高度(决定向上还是向下展开)。 */
405052
+ const PANEL_MIN = 180;
405053
+ const PANEL_MAX = 320;
405054
+ /** 弹层与触发器/视口边缘的间距。 */
405055
+ const PANEL_GAP = 6;
405056
+ const EDGE_GAP = 8;
405057
+ /**
405058
+ * 拼接 className,过滤假值。
405059
+ * @author ddj 2026年10月30号
405060
+ * @param names 候选类名(false / undefined 会被忽略)
405061
+ * @returns 空格分隔的 className
405062
+ */
405063
+ function classes(...names) {
405064
+ return names.filter(Boolean).join(" ");
405065
+ }
405066
+ /**
405067
+ * 按触发器位置计算弹层坐标,空间不足时向上展开。
405068
+ * @author ddj 2026年10月30号
405069
+ * @param trigger 触发器元素
405070
+ * @returns 弹层定位参数
405071
+ */
405072
+ function placePanel(trigger) {
405073
+ const rect = trigger.getBoundingClientRect();
405074
+ const width = Math.max(rect.width, 280);
405075
+ const below = window.innerHeight - rect.bottom - PANEL_GAP - EDGE_GAP;
405076
+ const above = rect.top - PANEL_GAP - EDGE_GAP;
405077
+ const useAbove = below < PANEL_MIN && above > below;
405078
+ const maxHeight = Math.max(PANEL_MIN, Math.min(PANEL_MAX, useAbove ? above : below));
405079
+ const maxLeft = Math.max(EDGE_GAP, window.innerWidth - width - EDGE_GAP);
405080
+ const left = Math.min(Math.max(EDGE_GAP, rect.left), maxLeft);
405081
+ if (useAbove) return {
405082
+ left,
405083
+ width,
405084
+ maxHeight,
405085
+ bottom: window.innerHeight - rect.top + PANEL_GAP
405086
+ };
405087
+ return {
405088
+ left,
405089
+ width,
405090
+ maxHeight,
405091
+ top: rect.bottom + PANEL_GAP
405092
+ };
405093
+ }
405094
+ /**
405095
+ * 受控弹层的位置与开合状态。
405096
+ *
405097
+ * Esc 的处理依赖「子组件 effect 先于父组件 effect 执行」:本 hook 在 ModalShell 内部的
405098
+ * 子组件里挂载,因此先注册 window 捕获监听,才能在弹层打开时拦下 Esc,避免连外层弹窗一起关掉。
405099
+ * @author ddj 2026年10月30号
405100
+ * @returns 弹层句柄
405101
+ */
405102
+ function useAnchored() {
405103
+ const triggerRef = react$1.default.useRef(null);
405104
+ const panelRef = react$1.default.useRef(null);
405105
+ const [open, setOpen] = react$1.default.useState(false);
405106
+ const [pos, setPos] = react$1.default.useState(null);
405107
+ const openRef = react$1.default.useRef(false);
405108
+ openRef.current = open;
405109
+ const close = react$1.default.useCallback(() => {
405110
+ setOpen(false);
405111
+ setPos(null);
405112
+ }, []);
405113
+ const toggle = react$1.default.useCallback(() => {
405114
+ if (openRef.current) {
405115
+ close();
405116
+ return;
405117
+ }
405118
+ const trigger = triggerRef.current;
405119
+ setPos(trigger ? placePanel(trigger) : null);
405120
+ setOpen(true);
405121
+ }, [close]);
405122
+ react$1.default.useEffect(() => {
405123
+ const onKey = (event) => {
405124
+ if (event.key !== "Escape" || !openRef.current) return;
405125
+ event.stopImmediatePropagation();
405126
+ event.preventDefault();
405127
+ close();
405128
+ };
405129
+ window.addEventListener("keydown", onKey, true);
405130
+ return () => window.removeEventListener("keydown", onKey, true);
405131
+ }, [close]);
405132
+ react$1.default.useEffect(() => {
405133
+ if (!open) return;
405134
+ const reposition = () => {
405135
+ const trigger = triggerRef.current;
405136
+ if (trigger) setPos(placePanel(trigger));
405137
+ };
405138
+ const onDown = (event) => {
405139
+ const target = event.target;
405140
+ if (!target) return;
405141
+ if (triggerRef.current?.contains(target)) return;
405142
+ if (panelRef.current?.contains(target)) return;
405143
+ close();
405144
+ };
405145
+ const onScroll = (event) => {
405146
+ const target = event.target;
405147
+ if (target && panelRef.current?.contains(target)) return;
405148
+ reposition();
405149
+ };
405150
+ window.addEventListener("mousedown", onDown, true);
405151
+ window.addEventListener("resize", reposition);
405152
+ window.addEventListener("scroll", onScroll, true);
405153
+ return () => {
405154
+ window.removeEventListener("mousedown", onDown, true);
405155
+ window.removeEventListener("resize", reposition);
405156
+ window.removeEventListener("scroll", onScroll, true);
405157
+ };
405158
+ }, [open, close]);
405159
+ return {
405160
+ open,
405161
+ style: pos ? {
405162
+ left: pos.left,
405163
+ top: pos.top,
405164
+ bottom: pos.bottom,
405165
+ width: pos.width,
405166
+ maxHeight: pos.maxHeight
405167
+ } : {},
405168
+ triggerRef,
405169
+ panelRef,
405170
+ toggle,
405171
+ close
405172
+ };
405173
+ }
405174
+ /**
405175
+ * 收集所有分组的选项键→标签映射。
405176
+ * @author ddj 2026年10月30号
405177
+ * @param groups 分组选项
405178
+ * @returns 选项键到标签的映射
405179
+ */
405180
+ function labelsOf(groups) {
405181
+ const labels = /* @__PURE__ */ new Map();
405182
+ for (const group of groups) for (const option of group.options) labels.set(option.key, option.label);
405183
+ return labels;
405184
+ }
405185
+ /**
405186
+ * 统计已选项里带警告/错误状态的数量。
405187
+ * @author ddj 2026年10月30号
405188
+ * @param groups 分组选项
405189
+ * @param selected 已选键
405190
+ * @returns 异常状态条目数
405191
+ */
405192
+ function issueCount(groups, selected) {
405193
+ const picked = new Set(selected);
405194
+ let count = 0;
405195
+ for (const group of groups) for (const option of group.options) if (picked.has(option.key) && option.state && option.state !== "ok") count += 1;
405196
+ return count;
405197
+ }
405198
+ /**
405199
+ * 下拉触发器:显示当前选择摘要与展开箭头。
405200
+ * @author ddj 2026年10月30号
405201
+ * @param props 触发器属性
405202
+ * @returns 触发器按钮
405203
+ */
405204
+ function PickTrigger(props) {
405205
+ return react$1.default.createElement("button", {
405206
+ ref: props.triggerRef,
405207
+ id: props.buttonId,
405208
+ type: "button",
405209
+ className: classes("edrv-qm-pick-trigger", props.open && "edrv-qm-pick-trigger--open"),
405210
+ disabled: props.disabled,
405211
+ title: props.hint,
405212
+ "aria-haspopup": "listbox",
405213
+ "aria-expanded": props.open,
405214
+ onClick: props.onClick
405215
+ }, react$1.default.createElement("span", { className: "edrv-qm-pick-summary" }, props.summary), props.issueCount ? react$1.default.createElement("span", { className: "edrv-qm-pick-badge" }, String(props.issueCount)) : null, react$1.default.createElement("span", {
405216
+ className: "edrv-qm-pick-caret",
405217
+ "aria-hidden": "true"
405218
+ }));
405219
+ }
405220
+ /**
405221
+ * 绝对定位的弹层容器。
405222
+ * @author ddj 2026年10月30号
405223
+ * @param props 弹层属性
405224
+ * @returns 弹层节点
405225
+ */
405226
+ function PickPanel(props) {
405227
+ return react$1.default.createElement("div", {
405228
+ ref: props.panelRef,
405229
+ className: "edrv-qm-pick-panel",
405230
+ style: props.style,
405231
+ role: "listbox",
405232
+ "aria-labelledby": props.labelledBy
405233
+ }, props.children);
405234
+ }
405235
+ /**
405236
+ * 可勾选的多选项行。
405237
+ * @author ddj 2026年10月30号
405238
+ * @param props 选项属性
405239
+ * @returns 选项按钮
405240
+ */
405241
+ function PickOptionRow(props) {
405242
+ const option = props.option;
405243
+ const state = option.state ?? "ok";
405244
+ return react$1.default.createElement("button", {
405245
+ type: "button",
405246
+ role: "option",
405247
+ className: classes("edrv-qm-pick-option", props.picked && "edrv-qm-pick-option--on"),
405248
+ "aria-selected": props.picked,
405249
+ title: option.reason ?? option.hint,
405250
+ onClick: () => props.onToggle(option.key)
405251
+ }, react$1.default.createElement("span", {
405252
+ className: classes("edrv-qm-pick-check", props.picked && "edrv-qm-pick-check--on"),
405253
+ "aria-hidden": "true"
405254
+ }, props.picked ? "✓" : ""), react$1.default.createElement("span", { className: "edrv-qm-pick-name" }, option.label), state === "ok" ? null : react$1.default.createElement("span", {
405255
+ className: classes("edrv-qm-pick-dot", "edrv-qm-pick-dot--" + state),
405256
+ "aria-hidden": "true"
405257
+ }), option.hint ? react$1.default.createElement("span", { className: "edrv-qm-pick-hint" }, option.hint) : null);
405258
+ }
405259
+ /**
405260
+ * 多选下拉:技能与规则共用,按来源分组展示。
405261
+ * @author ddj 2026年10月30号
405262
+ * @param props 多选下拉属性
405263
+ * @returns 多选下拉组件
405264
+ */
405265
+ function PickMulti(props) {
405266
+ const anchor = useAnchored();
405267
+ const labels = labelsOf(props.groups);
405268
+ const summary = selectionLabel(props.selected.map((key) => labels.get(key) ?? key), props.unit);
405269
+ const issues = issueCount(props.groups, props.selected);
405270
+ const rows = props.groups.filter((group) => group.options.length > 0).map((group) => react$1.default.createElement("div", {
405271
+ key: group.key,
405272
+ className: "edrv-qm-pick-group"
405273
+ }, react$1.default.createElement("div", { className: "edrv-qm-pick-group-title" }, group.label), group.options.map((option) => react$1.default.createElement(PickOptionRow, {
405274
+ key: option.key,
405275
+ option,
405276
+ picked: props.selected.includes(option.key),
405277
+ onToggle: props.onToggle
405278
+ }))));
405279
+ return react$1.default.createElement("div", { className: "edrv-qm-pick" }, react$1.default.createElement(PickTrigger, {
405280
+ triggerRef: anchor.triggerRef,
405281
+ buttonId: props.buttonId,
405282
+ summary,
405283
+ open: anchor.open,
405284
+ disabled: props.disabled === true,
405285
+ issueCount: issues,
405286
+ hint: props.disabled ? props.disabledHint : void 0,
405287
+ onClick: anchor.toggle
405288
+ }), anchor.open ? react$1.default.createElement(PickPanel, {
405289
+ panelRef: anchor.panelRef,
405290
+ style: anchor.style,
405291
+ labelledBy: props.buttonId
405292
+ }, rows.length > 0 ? react$1.default.createElement("div", { className: "edrv-qm-pick-list" }, rows) : react$1.default.createElement("div", { className: "edrv-qm-pick-empty" }, props.emptyText)) : null);
405293
+ }
405294
+ /**
405295
+ * 限定工作区下拉:全部 / 当前 / 更多(更多展开全部工作区多选)。
405296
+ * @author ddj 2026年10月30号
405297
+ * @param props 工作区下拉属性
405298
+ * @returns 工作区下拉组件
405299
+ */
405300
+ function PickWorkspace(props) {
405301
+ const anchor = useAnchored();
405302
+ const [more, setMore] = react$1.default.useState(false);
405303
+ const summary = workspaceLabel(props.selected, props.cwd);
405304
+ const picked = props.selected ?? [];
405305
+ const disabled = props.inherited === true;
405306
+ const currentPath = props.cwd;
405307
+ const isAll = picked.length === 0;
405308
+ const isCurrent = picked.length === 1 && !!currentPath && summary.mode === "current";
405309
+ const onPickAll = () => {
405310
+ props.onChange(null);
405311
+ anchor.close();
405312
+ };
405313
+ const onPickCurrent = () => {
405314
+ if (!currentPath) return;
405315
+ props.onChange([currentPath]);
405316
+ anchor.close();
405317
+ };
405318
+ const quick = [react$1.default.createElement(PickOptionRow, {
405319
+ key: "__all__",
405320
+ option: {
405321
+ key: "__all__",
405322
+ label: "全部工作区",
405323
+ hint: "菜单在所有工作区可见"
405324
+ },
405325
+ picked: isAll,
405326
+ onToggle: onPickAll
405327
+ }), react$1.default.createElement(PickOptionRow, {
405328
+ key: "__current__",
405329
+ option: {
405330
+ key: "__current__",
405331
+ label: "当前工作区",
405332
+ hint: currentPath || "当前没有活跃工作区"
405333
+ },
405334
+ picked: isCurrent,
405335
+ onToggle: onPickCurrent
405336
+ })];
405337
+ const rows = props.workspaces.map((workspace) => react$1.default.createElement(PickOptionRow, {
405338
+ key: workspace.path,
405339
+ option: {
405340
+ key: workspace.path,
405341
+ label: workspace.title,
405342
+ hint: workspace.path
405343
+ },
405344
+ picked: picked.some((path) => path === workspace.path),
405345
+ onToggle: (path) => props.onChange(toggleWorkspace(props.selected, path))
405346
+ }));
405347
+ const moreToggle = react$1.default.createElement("button", {
405348
+ type: "button",
405349
+ className: classes("edrv-qm-pick-more", more && "edrv-qm-pick-more--on"),
405350
+ "aria-expanded": more,
405351
+ onClick: () => setMore(!more)
405352
+ }, more ? "收起工作区列表" : "更多工作区(" + String(props.workspaces.length) + ")");
405353
+ return react$1.default.createElement("div", { className: "edrv-qm-pick" }, react$1.default.createElement(PickTrigger, {
405354
+ triggerRef: anchor.triggerRef,
405355
+ buttonId: props.buttonId,
405356
+ summary: summary.label,
405357
+ open: anchor.open,
405358
+ disabled,
405359
+ hint: disabled ? props.inheritedHint : "点开选择生效范围",
405360
+ onClick: anchor.toggle
405361
+ }), anchor.open ? react$1.default.createElement(PickPanel, {
405362
+ panelRef: anchor.panelRef,
405363
+ style: anchor.style,
405364
+ labelledBy: props.buttonId
405365
+ }, react$1.default.createElement("div", { className: "edrv-qm-pick-list" }, quick), moreToggle, more ? react$1.default.createElement("div", { className: "edrv-qm-pick-list" }, rows) : null) : null);
405366
+ }
405367
+ /**
405368
+ * 开关行:左侧标题与说明,右侧滑动开关。
405369
+ * @author ddj 2026年10月30号
405370
+ * @param props 开关属性
405371
+ * @returns 开关行
405372
+ */
405373
+ function PickSwitch(props) {
405374
+ return react$1.default.createElement("div", { className: "edrv-qm-switch-row" }, react$1.default.createElement("div", { className: "edrv-qm-switch-text" }, react$1.default.createElement("div", { className: "edrv-qm-pick-name" }, props.label), props.hint ? react$1.default.createElement("div", { className: "edrv-qm-pick-hint" }, props.hint) : null), react$1.default.createElement("button", {
405375
+ id: props.buttonId,
405376
+ type: "button",
405377
+ role: "switch",
405378
+ className: classes("edrv-qm-switch", props.checked && "edrv-qm-switch--on"),
405379
+ "aria-checked": props.checked,
405380
+ "aria-label": props.label,
405381
+ disabled: props.disabled === true,
405382
+ onClick: () => props.onChange(!props.checked)
405383
+ }, react$1.default.createElement("span", {
405384
+ className: "edrv-qm-switch-knob",
405385
+ "aria-hidden": "true"
405386
+ })));
405387
+ }
405388
+ /**
405389
+ * 分段控件:等宽按钮组,用于 1–5 层数这类小范围枚举。
405390
+ * @author ddj 2026年10月30号
405391
+ * @param props 分段控件属性
405392
+ * @returns 分段控件
405393
+ */
405394
+ function PickSeg(props) {
405395
+ return react$1.default.createElement("div", {
405396
+ className: "edrv-qm-seg",
405397
+ role: "group",
405398
+ "aria-label": props.label
405399
+ }, props.options.map((option) => react$1.default.createElement("button", {
405400
+ key: option.value,
405401
+ type: "button",
405402
+ className: classes("edrv-qm-seg-btn", option.value === props.value && "edrv-qm-seg-btn--on"),
405403
+ "aria-pressed": option.value === props.value,
405404
+ disabled: props.disabled === true,
405405
+ onClick: () => props.onChange(option.value)
405406
+ }, option.label)));
405407
+ }
405408
+ //#endregion
405409
+ //#region src/shared/skillCatalog.ts
405410
+ /** 来源分组标题文案。 */
405411
+ const SKILL_SOURCE_LABEL = {
405412
+ user: "全局技能",
405413
+ project: "当前工作区技能"
405414
+ };
405415
+ /**
405416
+ * 按来源分组(空组不出现;面板全空时由 UI 统一渲染空态)。
405417
+ * @author ddj 2026年10月30号
405418
+ * @param input 技能候选快照
405419
+ * @returns 非空分组(全局在前、工作区在后)
405420
+ */
405421
+ function groupSkills(input) {
405422
+ if (!input) return [];
405423
+ const groups = [];
405424
+ if (input.user.length > 0) groups.push({
405425
+ key: "user",
405426
+ label: SKILL_SOURCE_LABEL.user,
405427
+ skills: input.user
405428
+ });
405429
+ if (input.project.length > 0) groups.push({
405430
+ key: "project",
405431
+ label: SKILL_SOURCE_LABEL.project,
405432
+ skills: input.project
405433
+ });
405434
+ return groups;
405435
+ }
405436
+ //#endregion
405437
+ //#region src/client/ui/QuickMenuDialog.tsx
405438
+ /**
405439
+ * dsh-vscode-mode client — 快捷菜单的配置弹窗(新增/编辑项 + 全局设置)。
405440
+ *
405441
+ * 设计要点:配置不再有独立设置页,全部从菜单就地入口打开;
405442
+ * 表单按「类型」动态取舍字段(组只要名称/归属/优先级,按钮额外要提示词与绑定);
405443
+ * 工作区限定沿祖先链继承——父组已限定时本项只读展示继承值,避免出现互相矛盾的配置。
405444
+ * 技能与规则都走多选下拉(候选来自目录扫描,不再手输名字);分段与开关统一由 QuickMenuPickers 提供。
405445
+ * 作者 ddj 2026年10月30号
405446
+ */
405447
+ /** 类型分段的可选值。 */
405448
+ const TYPE_OPTIONS = [{
405449
+ value: "button",
405450
+ label: "按钮"
405451
+ }, {
405452
+ value: "group",
405453
+ label: "组"
405454
+ }];
405455
+ /** 规则注入方式分段的可选值。 */
405456
+ const MODE_OPTIONS = [{
405457
+ value: "reference",
405458
+ label: "引用"
405459
+ }, {
405460
+ value: "inline",
405461
+ label: "内联正文"
405462
+ }];
405463
+ /** 最大嵌套层数分段的可选值(1–5)。 */
405464
+ const DEPTH_OPTIONS = Array.from({ length: 5 }, (_, index) => {
405465
+ const depth = 1 + index;
405466
+ return {
405467
+ value: String(depth),
405468
+ label: String(depth)
405469
+ };
405470
+ });
405471
+ /** 技能描述在候选行里的最大长度。 */
405472
+ const SKILL_HINT_CAP = 28;
405473
+ /**
405474
+ * 截断技能描述,作为候选行的次要说明。
405475
+ * @author ddj 2026年10月30号
405476
+ * @param description 技能描述
405477
+ * @returns 单行摘要
405478
+ */
405479
+ function brief(description) {
405480
+ const text = description.trim().replace(/\s+/g, " ");
405481
+ if (text === "") return void 0;
405482
+ return text.length > SKILL_HINT_CAP ? text.slice(0, SKILL_HINT_CAP) + "…" : text;
405483
+ }
405484
+ /**
405485
+ * 单个技能候选(状态点来自会话可见性校验)。
405486
+ * @author ddj 2026年10月30号
405487
+ * @param name 技能名
405488
+ * @param ctx 校验上下文
405489
+ * @param hint 次要说明
405490
+ * @returns 候选项
405491
+ */
405492
+ function skillOption(name, ctx, hint) {
405493
+ const status = validateSkill(name, ctx);
405494
+ return {
405495
+ key: name,
405496
+ label: name,
405497
+ ...hint ? { hint } : {},
405498
+ ...status.state === "ok" ? {} : { state: status.state === "warn" ? "warn" : void 0 },
405499
+ ...status.reason ? { reason: status.reason } : {}
405500
+ };
405501
+ }
405502
+ /**
405503
+ * 技能候选分组:全局技能 / 当前工作区技能 / 已绑定但不在候选内。
405504
+ * @author ddj 2026年10月30号
405505
+ * @param catalog 目录扫描结果(读取失败为 null)
405506
+ * @param names 当前会话可见技能名(候选为空时的兜底来源)
405507
+ * @param selected 已绑定技能名
405508
+ * @param ctx 校验上下文
405509
+ * @returns 分组候选
405510
+ */
405511
+ function skillGroups(catalog, names, selected, ctx) {
405512
+ const groups = [];
405513
+ const known = /* @__PURE__ */ new Set();
405514
+ for (const group of groupSkills(catalog)) {
405515
+ const options = group.skills.map((info) => skillOption(info.name, ctx, brief(info.description)));
405516
+ for (const option of options) known.add(option.key);
405517
+ if (options.length > 0) groups.push({
405518
+ key: group.key,
405519
+ label: group.label,
405520
+ options
405521
+ });
405522
+ }
405523
+ if (groups.length === 0 && names && names.length > 0) {
405524
+ const options = names.map((name) => skillOption(name, ctx));
405525
+ for (const option of options) known.add(option.key);
405526
+ groups.push({
405527
+ key: "session",
405528
+ label: "当前会话技能",
405529
+ options
405530
+ });
405531
+ }
405532
+ const extra = selected.filter((name) => !known.has(name));
405533
+ if (extra.length > 0) groups.push({
405534
+ key: "bound",
405535
+ label: "已绑定(不在候选内)",
405536
+ options: extra.map((name) => skillOption(name, ctx, "候选目录里没有该技能,可取消绑定"))
405537
+ });
405538
+ return groups;
405539
+ }
405540
+ /**
405541
+ * 单个规则候选(状态点来自 validateRule)。
405542
+ * @author ddj 2026年10月30号
405543
+ * @param choice 规则候选
405544
+ * @param ctx 校验上下文
405545
+ * @returns 候选项
405546
+ */
405547
+ function ruleOption(choice, ctx) {
405548
+ const status = validateRule(choice.binding, ctx);
405549
+ const state = status.state === "warn" || status.state === "error" ? status.state : void 0;
405550
+ return {
405551
+ key: bindingKey(choice.binding),
405552
+ label: choice.label,
405553
+ hint: choice.hint,
405554
+ ...state ? { state } : {},
405555
+ ...status.reason ? { reason: status.reason } : {}
405556
+ };
405557
+ }
405558
+ /**
405559
+ * 规则候选分组:用户规则 / 当前工作区规则 / 已绑定但已失效。
405560
+ * @author ddj 2026年10月30号
405561
+ * @param choices 规则候选
405562
+ * @param selected 已绑定规则
405563
+ * @param ctx 校验上下文
405564
+ * @returns 分组候选
405565
+ */
405566
+ function ruleGroups(choices, selected, ctx) {
405567
+ const known = new Set(choices.map((choice) => bindingKey(choice.binding)));
405568
+ const user = choices.filter((choice) => choice.binding.scope === "user");
405569
+ const project = choices.filter((choice) => choice.binding.scope === "project");
405570
+ const groups = [];
405571
+ if (user.length > 0) groups.push({
405572
+ key: "user",
405573
+ label: "用户规则",
405574
+ options: user.map((choice) => ruleOption(choice, ctx))
405575
+ });
405576
+ if (project.length > 0) groups.push({
405577
+ key: "project",
405578
+ label: "当前工作区规则",
405579
+ options: project.map((choice) => ruleOption(choice, ctx))
405580
+ });
405581
+ const extra = selected.filter((binding) => !known.has(bindingKey(binding)));
405582
+ if (extra.length > 0) groups.push({
405583
+ key: "bound",
405584
+ label: "已绑定(规则已不存在)",
405585
+ options: extra.map((binding) => {
405586
+ const status = validateRule(binding, ctx);
405587
+ return {
405588
+ key: bindingKey(binding),
405589
+ label: binding.file,
405590
+ hint: binding.scope === "user" ? "用户规则" : "项目规则",
405591
+ state: status.state === "warn" ? "warn" : "error",
405592
+ reason: status.reason ?? "候选里没有该规则,可取消绑定"
405593
+ };
405594
+ })
405595
+ });
405596
+ return groups;
405597
+ }
405598
+ /**
405599
+ * 构造待保存草稿(编辑时沿用原 id,校验需要它判断自身子树)。
405600
+ * @author ddj 2026年10月30号
405601
+ * @param id 项 id
405602
+ * @param fields 表单字段
405603
+ * @returns 菜单项
405604
+ */
405605
+ function buildDraft(id, fields) {
405606
+ const base = {
405607
+ id,
405608
+ name: fields.name,
405609
+ order: fields.order,
405610
+ parentId: fields.parentId,
405611
+ workspaces: fields.workspaces
405612
+ };
405613
+ if (fields.type === "group") return {
405614
+ ...base,
405615
+ type: "group"
405616
+ };
405617
+ const button = {
405618
+ ...base,
405619
+ type: "button",
405620
+ prompt: fields.prompt,
405621
+ skills: fields.skills,
405622
+ rules: fields.rules,
405623
+ ruleMode: fields.ruleMode
405624
+ };
405625
+ if (fields.clickAction !== "inherit") button.clickAction = fields.clickAction;
405626
+ return button;
405627
+ }
405628
+ /**
405629
+ * 归属组候选(组自身与自身子树不能当父级,否则会成环)。
405630
+ * @author ddj 2026年10月30号
405631
+ * @param doc 文档
405632
+ * @param selfId 正在编辑的项 id
405633
+ * @returns 候选(顶层 + 可选组)
405634
+ */
405635
+ function groupChoices(doc, selfId) {
405636
+ const banned = selfId === "" ? [] : descendantIds(doc, selfId);
405637
+ const out = [{
405638
+ id: "",
405639
+ label: "顶层(root 组)"
405640
+ }];
405641
+ for (const item of doc.items) {
405642
+ if (item.type !== "group" || banned.includes(item.id)) continue;
405643
+ out.push({
405644
+ id: item.id,
405645
+ label: item.name + "(第 " + depthOf(doc, item.id) + " 层)"
405646
+ });
405647
+ }
405648
+ return out;
405649
+ }
405650
+ /**
405651
+ * 规则候选(用户规则 + 当前工作区的项目规则)。
405652
+ * @author ddj 2026年10月30号
405653
+ * @param snapshot 规则快照
405654
+ * @param cwd 当前工作区
405655
+ * @returns 候选列表
405656
+ */
405657
+ function ruleChoices(snapshot, cwd) {
405658
+ if (!snapshot) return [];
405659
+ const out = [];
405660
+ for (const info of snapshot.user) {
405661
+ const binding = {
405662
+ scope: "user",
405663
+ file: info.file
405664
+ };
405665
+ if (info.origin) binding.origin = info.origin;
405666
+ out.push({
405667
+ binding,
405668
+ label: info.file,
405669
+ hint: "用户规则 · " + (info.type === "always" ? "总是生效" : info.type === "auto" ? "按 globs" : "手动")
405670
+ });
405671
+ }
405672
+ const wanted = normalizePath(cwd ?? "");
405673
+ for (const project of snapshot.projects) {
405674
+ if (wanted !== "" && normalizePath(project.workspacePath) !== wanted) continue;
405675
+ for (const info of project.rules) out.push({
405676
+ binding: {
405677
+ scope: "project",
405678
+ workspacePath: project.workspacePath,
405679
+ file: info.file
405680
+ },
405681
+ label: info.file,
405682
+ hint: "项目规则 · " + project.title
405683
+ });
405684
+ }
405685
+ return out;
405686
+ }
405687
+ /**
405688
+ * 表单里的分组区块(标题 + 说明 + 内容),把长表单切成可扫读的几段。
405689
+ * @author ddj 2026年10月30号
405690
+ * @param props 区块属性
405691
+ * @returns 区块元素
405692
+ */
405693
+ function Section(props) {
405694
+ return react$1.default.createElement("div", { className: "edrv-qm-section" }, react$1.default.createElement("div", { className: "edrv-qm-section-head" }, react$1.default.createElement("span", { className: "edrv-qm-section-title" }, props.title), props.hint ? react$1.default.createElement("span", { className: "edrv-qm-section-hint" }, props.hint) : null), react$1.default.createElement("div", { className: "edrv-qm-section-body" }, props.children));
405695
+ }
405696
+ /**
405697
+ * 新增/编辑菜单项弹窗。
405698
+ * @author ddj 2026年10月30号
405699
+ * @param props host、待编辑项、父组、关闭回调
405700
+ * @returns 弹窗元素
405701
+ */
405702
+ function QuickItemDialog(props) {
405703
+ const { host, item, parentId } = props;
405704
+ const doc = host.store.getState().doc;
405705
+ const editing = item;
405706
+ const [type, setType] = react$1.default.useState(editing?.type ?? "button");
405707
+ const [name, setName] = react$1.default.useState(editing?.name ?? "");
405708
+ const [order, setOrder] = react$1.default.useState(editing?.order ?? 100);
405709
+ const [parent, setParent] = react$1.default.useState(editing?.parentId ?? parentId);
405710
+ const [workspaces, setWorkspaces] = react$1.default.useState(editing?.workspaces ?? null);
405711
+ const [prompt, setPrompt] = react$1.default.useState(editing?.type === "button" ? editing.prompt : "");
405712
+ const [skills, setSkills] = react$1.default.useState(editing?.type === "button" ? editing.skills.slice() : []);
405713
+ const [rules, setRules] = react$1.default.useState(editing?.type === "button" ? editing.rules.slice() : []);
405714
+ const [ruleMode, setRuleMode] = react$1.default.useState(editing?.type === "button" ? editing.ruleMode : "reference");
405715
+ const [clickAction, setClickAction] = react$1.default.useState(editing?.type === "button" ? editing.clickAction ?? "inherit" : "inherit");
405716
+ const [catalog, setCatalog] = react$1.default.useState(null);
405717
+ const [snapshot, setSnapshot] = react$1.default.useState(null);
405718
+ const [skillNames, setSkillNames] = react$1.default.useState(null);
405719
+ const [busy, setBusy] = react$1.default.useState(false);
405720
+ const [error, setError] = react$1.default.useState("");
405721
+ react$1.default.useEffect(() => {
405722
+ let alive = true;
405723
+ const cwd = host.store.getContext().cwd;
405724
+ (async () => {
405725
+ const [loadedRules, loadedSkills, loadedCatalog] = await Promise.all([
405726
+ host.rules().catch(() => null),
405727
+ host.skills().catch(() => null),
405728
+ host.skillCatalog(cwd).catch(() => null)
405729
+ ]);
405730
+ if (!alive) return;
405731
+ setSnapshot(loadedRules);
405732
+ setSkillNames(loadedSkills);
405733
+ setCatalog(loadedCatalog);
405734
+ })();
405735
+ return () => {
405736
+ alive = false;
405737
+ };
405738
+ }, [host]);
405739
+ const before = host.store.getContext();
405740
+ const ctx = {
405741
+ cwd: before.cwd,
405742
+ rules: snapshot ?? void 0,
405743
+ skillNames
405744
+ };
405745
+ const draftId = editing?.id ?? "";
405746
+ const status = validateItem(buildDraft(draftId === "" ? "draft" : draftId, {
405747
+ type,
405748
+ name,
405749
+ order,
405750
+ parentId: parent,
405751
+ workspaces,
405752
+ prompt,
405753
+ skills,
405754
+ rules,
405755
+ ruleMode,
405756
+ clickAction
405757
+ }), doc);
405758
+ const choices = ruleChoices(snapshot, before.cwd);
405759
+ const parentItem = parent === null ? null : doc.items.find((entry) => entry.id === parent);
405760
+ const inherited = parentItem ? inheritWorkspaces(doc, parentItem) : null;
405761
+ const workspaceList = host.workspaces();
405762
+ const skillPick = skillGroups(catalog, skillNames, skills, ctx);
405763
+ const rulePick = ruleGroups(choices, rules, ctx);
405764
+ const ruleByKey = new Map(choices.map((choice) => [bindingKey(choice.binding), choice.binding]));
405765
+ /**
405766
+ * 切换技能绑定。
405767
+ * @author ddj 2026年10月30号
405768
+ * @param target 技能名
405769
+ */
405770
+ function toggleSkill(target) {
405771
+ setSkills((current) => current.includes(target) ? current.filter((entry) => entry !== target) : current.concat(target));
405772
+ }
405773
+ /**
405774
+ * 切换规则绑定(键来自下拉,失效规则也能取消绑定)。
405775
+ * @author ddj 2026年10月30号
405776
+ * @param key bindingKey 结果
405777
+ */
405778
+ function toggleRuleKey(key) {
405779
+ const binding = ruleByKey.get(key);
405780
+ setRules((current) => {
405781
+ if (current.some((entry) => bindingKey(entry) === key)) return current.filter((entry) => bindingKey(entry) !== key);
405782
+ return binding ? current.concat(binding) : current;
405783
+ });
405784
+ }
405785
+ /**
405786
+ * 保存(新建或更新);冲突/校验失败时留在弹窗内提示。
405787
+ * @author ddj 2026年10月30号
405788
+ */
405789
+ async function onSave() {
405790
+ if (status.errors.length > 0) return;
405791
+ setBusy(true);
405792
+ const finalItem = buildDraft(editing?.id ?? host.store.nextId(type), {
405793
+ type,
405794
+ name,
405795
+ order,
405796
+ parentId: parent,
405797
+ workspaces,
405798
+ prompt,
405799
+ skills,
405800
+ rules,
405801
+ ruleMode,
405802
+ clickAction
405803
+ });
405804
+ const result = editing ? await host.store.updateItem(finalItem) : await host.store.createItem(finalItem);
405805
+ setBusy(false);
405806
+ if (!result.ok) {
405807
+ setError(result.error ?? "保存失败");
405808
+ return;
405809
+ }
405810
+ props.onClose();
405811
+ }
405812
+ /**
405813
+ * 删除该项及其子项。
405814
+ * @author ddj 2026年10月30号
405815
+ */
405816
+ async function onDelete() {
405817
+ if (!editing) return;
405818
+ const suffix = editing.type === "group" ? "及其全部子项" : "";
405819
+ if (!window.confirm("确认删除「" + editing.name + "」" + suffix + "?")) return;
405820
+ setBusy(true);
405821
+ const result = await host.store.removeItem(editing.id);
405822
+ setBusy(false);
405823
+ if (!result.ok) {
405824
+ setError(result.error ?? "删除失败");
405825
+ return;
405826
+ }
405827
+ props.onClose();
405828
+ }
405829
+ /**
405830
+ * 类型与名称。
405831
+ * @author ddj 2026年10月30号
405832
+ * @returns 表单行
405833
+ */
405834
+ function basicRow() {
405835
+ return react$1.default.createElement("div", { className: "edrv-qm-row2" }, react$1.default.createElement("div", { className: "edrv-qm-field edrv-qm-field--narrow" }, react$1.default.createElement("span", { className: "edrv-qm-field-label" }, "类型"), react$1.default.createElement(PickSeg, {
405836
+ options: TYPE_OPTIONS,
405837
+ value: type,
405838
+ label: "菜单项类型",
405839
+ onChange: (next) => setType(next)
405840
+ })), react$1.default.createElement("label", { className: "edrv-qm-field" }, react$1.default.createElement("span", { className: "edrv-qm-field-label" }, "名称"), react$1.default.createElement("input", {
405841
+ className: "edrv-qm-input",
405842
+ value: name,
405843
+ maxLength: 40,
405844
+ placeholder: type === "group" ? "例如:代码审查" : "例如:审查当前改动",
405845
+ onChange: (event) => setName(event.target.value)
405846
+ })));
405847
+ }
405848
+ /**
405849
+ * 归属组与同级优先级。
405850
+ * @author ddj 2026年10月30号
405851
+ * @returns 表单行
405852
+ */
405853
+ function placeRow() {
405854
+ return react$1.default.createElement("div", { className: "edrv-qm-row2" }, react$1.default.createElement("label", { className: "edrv-qm-field" }, react$1.default.createElement("span", { className: "edrv-qm-field-label" }, "归属组"), react$1.default.createElement("select", {
405855
+ className: "edrv-qm-select",
405856
+ value: parent ?? "",
405857
+ onChange: (event) => setParent(event.target.value === "" ? null : event.target.value)
405858
+ }, groupChoices(doc, draftId).map((choice) => react$1.default.createElement("option", {
405859
+ key: choice.id,
405860
+ value: choice.id
405861
+ }, choice.label)))), react$1.default.createElement("div", { className: "edrv-qm-field" }, react$1.default.createElement("span", { className: "edrv-qm-field-label" }, "同级优先级(越小越靠前)"), react$1.default.createElement("div", { className: "edrv-qm-inline" }, react$1.default.createElement("input", {
405862
+ className: "edrv-qm-input edrv-qm-input--order",
405863
+ type: "number",
405864
+ min: 0,
405865
+ max: 999,
405866
+ value: order,
405867
+ onChange: (event) => setOrder(Number(event.target.value) || 0)
405868
+ }), react$1.default.createElement("button", {
405869
+ type: "button",
405870
+ className: "edrv-qm-btn edrv-qm-btn--sm",
405871
+ onClick: () => setOrder((value) => Math.max(0, value - 10))
405872
+ }, "上移"), react$1.default.createElement("button", {
405873
+ type: "button",
405874
+ className: "edrv-qm-btn edrv-qm-btn--sm",
405875
+ onClick: () => setOrder((value) => Math.min(999, value + 10))
405876
+ }, "下移"))));
405877
+ }
405878
+ /**
405879
+ * 限定工作区(继承上级组时只读)。
405880
+ * @author ddj 2026年10月30号
405881
+ * @returns 表单行
405882
+ */
405883
+ function workspaceField() {
405884
+ return react$1.default.createElement("div", { className: "edrv-qm-field" }, react$1.default.createElement("div", { className: "edrv-qm-field-head" }, react$1.default.createElement("span", { className: "edrv-qm-field-label" }, "限定工作区"), react$1.default.createElement("span", { className: "edrv-qm-field-hint" }, inherited ? "遵循上级组:" + inherited.join("、") + "(此项不可编辑)" : "默认全部工作区可见,可切到当前工作区或展开更多")), react$1.default.createElement(PickWorkspace, {
405885
+ buttonId: "edrv-qm-pick-workspaces",
405886
+ workspaces: workspaceList,
405887
+ selected: inherited ?? workspaces,
405888
+ cwd: before.cwd,
405889
+ inherited: inherited !== null,
405890
+ inheritedHint: "遵循上级组,此项不可编辑",
405891
+ onChange: (next) => setWorkspaces(next)
405892
+ }));
405893
+ }
405894
+ /**
405895
+ * 提示词输入。
405896
+ * @author ddj 2026年10月30号
405897
+ * @returns 表单行
405898
+ */
405899
+ function promptField() {
405900
+ return react$1.default.createElement("div", { className: "edrv-qm-field" }, react$1.default.createElement("div", { className: "edrv-qm-field-head" }, react$1.default.createElement("span", { className: "edrv-qm-field-label" }, "预填提示词"), react$1.default.createElement("span", { className: "edrv-qm-field-hint" }, "占位符:{{workspace}} {{selection}} {{date}} {{rules}} {{skills}}")), react$1.default.createElement("textarea", {
405901
+ className: "edrv-qm-textarea",
405902
+ value: prompt,
405903
+ maxLength: QUICK_MENU_PROMPT_CAP,
405904
+ placeholder: "点击后发送给 AI 的内容…",
405905
+ onChange: (event) => setPrompt(event.target.value)
405906
+ }));
405907
+ }
405908
+ /**
405909
+ * 绑定技能(多选下拉,候选来自全局与工作区技能目录)。
405910
+ * @author ddj 2026年10月30号
405911
+ * @returns 表单行
405912
+ */
405913
+ function skillField() {
405914
+ return react$1.default.createElement("div", { className: "edrv-qm-field" }, react$1.default.createElement("div", { className: "edrv-qm-field-head" }, react$1.default.createElement("span", { className: "edrv-qm-field-label" }, "绑定技能"), react$1.default.createElement("span", { className: "edrv-qm-field-hint" }, "发送时写成 /技能名;黄点=当前会话不可见")), react$1.default.createElement(PickMulti, {
405915
+ buttonId: "edrv-qm-pick-skills",
405916
+ groups: skillPick,
405917
+ selected: skills,
405918
+ unit: "个技能",
405919
+ emptyText: "没有可选技能(全局技能目录与当前工作区 .dsh/skills 均为空)",
405920
+ onToggle: toggleSkill
405921
+ }));
405922
+ }
405923
+ /**
405924
+ * 绑定规则(多选下拉 + 引用/内联切换)。
405925
+ * @author ddj 2026年10月30号
405926
+ * @returns 表单行
405927
+ */
405928
+ function ruleField() {
405929
+ return react$1.default.createElement("div", { className: "edrv-qm-field" }, react$1.default.createElement("div", { className: "edrv-qm-field-head" }, react$1.default.createElement("span", { className: "edrv-qm-field-label" }, "绑定规则"), react$1.default.createElement(PickSeg, {
405930
+ options: MODE_OPTIONS,
405931
+ value: ruleMode,
405932
+ label: "规则注入方式",
405933
+ onChange: (next) => setRuleMode(next)
405934
+ })), react$1.default.createElement(PickMulti, {
405935
+ buttonId: "edrv-qm-pick-rules",
405936
+ groups: rulePick,
405937
+ selected: rules.map((binding) => bindingKey(binding)),
405938
+ unit: "条规则",
405939
+ emptyText: "没有可选规则(可在规则面板新建 .mdc 规则)",
405940
+ onToggle: toggleRuleKey
405941
+ }));
405942
+ }
405943
+ /**
405944
+ * 点击动作(跟随全局 / 直接发送 / 只填入)。
405945
+ * @author ddj 2026年10月30号
405946
+ * @returns 表单行
405947
+ */
405948
+ function clickField() {
405949
+ return react$1.default.createElement("label", { className: "edrv-qm-field" }, react$1.default.createElement("span", { className: "edrv-qm-field-label" }, "点击动作"), react$1.default.createElement("select", {
405950
+ className: "edrv-qm-select",
405951
+ value: clickAction,
405952
+ onChange: (event) => setClickAction(event.target.value)
405953
+ }, react$1.default.createElement("option", { value: "inherit" }, "跟随全局"), react$1.default.createElement("option", { value: "send" }, "直接发送"), react$1.default.createElement("option", { value: "fill" }, "只填入输入框")));
405954
+ }
405955
+ const parentName = parent === null ? null : doc.items.find((entry) => entry.id === parent)?.name ?? null;
405956
+ const typeLabel = type === "group" ? "组" : "按钮";
405957
+ const headTitle = editing ? "编辑「" + editing.name + "」" : parent === null ? "新建顶层项" : "新建子项";
405958
+ const headSub = (editing ? editing.type === "group" ? "组" : "按钮" : typeLabel) + (parentName ? " · 位于「" + parentName + "」" : " · 位于顶层");
405959
+ const header = react$1.default.createElement("div", { className: "edrv-qm-head" }, react$1.default.createElement("div", { className: "edrv-qm-head-title" }, headTitle), react$1.default.createElement("div", { className: "edrv-qm-head-sub" }, headSub));
405960
+ const body = react$1.default.createElement("div", { className: "edrv-qm-dialog-body" }, react$1.default.createElement(Section, { title: "基本信息" }, basicRow(), placeRow(), workspaceField()), type === "button" ? react$1.default.createElement(Section, {
405961
+ title: "提示词",
405962
+ hint: "点击后发给 AI 的内容"
405963
+ }, promptField()) : null, type === "button" ? react$1.default.createElement(Section, { title: "绑定" }, skillField(), ruleField()) : null, type === "button" ? react$1.default.createElement(Section, { title: "行为" }, clickField()) : null, error !== "" ? react$1.default.createElement("div", { className: "edrv-qm-error-list" }, error) : null, status.errors.length > 0 ? react$1.default.createElement("ul", { className: "edrv-qm-error-list" }, status.errors.map((text) => react$1.default.createElement("li", { key: text }, text))) : null, status.warnings.length > 0 ? react$1.default.createElement("ul", { className: "edrv-qm-warn-list" }, status.warnings.map((text) => react$1.default.createElement("li", { key: text }, text))) : null);
405964
+ const actions = react$1.default.createElement("div", { className: "edrv-qm-actions" }, editing ? react$1.default.createElement("button", {
405965
+ type: "button",
405966
+ className: "edrv-qm-btn edrv-qm-btn--danger",
405967
+ disabled: busy,
405968
+ onClick: () => {
405969
+ onDelete();
405970
+ }
405971
+ }, "删除") : null, react$1.default.createElement("span", { className: "edrv-qm-actions-grow" }), react$1.default.createElement("button", {
405972
+ type: "button",
405973
+ className: "edrv-qm-btn",
405974
+ onClick: props.onClose
405975
+ }, "取消"), react$1.default.createElement("button", {
405976
+ type: "button",
405977
+ className: "edrv-qm-btn edrv-qm-btn--primary",
405978
+ disabled: busy || status.errors.length > 0,
405979
+ onClick: () => {
405980
+ onSave();
405981
+ }
405982
+ }, busy ? "保存中…" : editing ? "保存" : "创建"));
405983
+ return react$1.default.createElement(ModalShell, {
405984
+ dialogClass: "edrv-qm-dialog",
405985
+ zIndex: 220,
405986
+ onClose: props.onClose,
405987
+ closeOnMask: false
405988
+ }, header, body, actions);
405989
+ }
405990
+ /**
405991
+ * 菜单全局设置(显隐、点击动作、最大嵌套层数、位置重置、导入导出、失效绑定清理)。
405992
+ * @author ddj 2026年10月30号
405993
+ * @param props host 与关闭回调
405994
+ * @returns 弹窗元素
405995
+ */
405996
+ function MenuGlobalDialog(props) {
405997
+ const { host } = props;
405998
+ const doc = host.store.getState().doc;
405999
+ const [enabled, setEnabled] = react$1.default.useState(doc.enabled);
406000
+ const [clickAction, setClickAction] = react$1.default.useState(doc.clickAction);
406001
+ const [maxDepth, setMaxDepth] = react$1.default.useState(doc.maxDepth || 3);
406002
+ const [busy, setBusy] = react$1.default.useState(false);
406003
+ const [error, setError] = react$1.default.useState("");
406004
+ const ctx = host.store.getContext();
406005
+ const broken = collectBroken(doc, ctx);
406006
+ const fileRef = react$1.default.useRef(null);
406007
+ /**
406008
+ * 保存全局设置。
406009
+ * @author ddj 2026年10月30号
406010
+ */
406011
+ async function onSave() {
406012
+ setBusy(true);
406013
+ const result = await host.store.save({
406014
+ ...doc,
406015
+ enabled,
406016
+ clickAction,
406017
+ maxDepth
406018
+ });
406019
+ setBusy(false);
406020
+ if (!result.ok) {
406021
+ setError(result.error ?? "保存失败");
406022
+ return;
406023
+ }
406024
+ props.onClose();
406025
+ }
406026
+ /**
406027
+ * 清理全部失效绑定(移除失效规则/技能,保留提示词)。
406028
+ * @author ddj 2026年10月30号
406029
+ */
406030
+ async function onClean() {
406031
+ setBusy(true);
406032
+ const items = doc.items.map((item) => {
406033
+ if (item.type !== "button") return item;
406034
+ const rules = item.rules.filter((binding) => validateRule(binding, ctx).state === "ok");
406035
+ const skills = item.skills.filter((name) => validateSkill(name, ctx).state === "ok");
406036
+ return {
406037
+ ...item,
406038
+ rules,
406039
+ skills
406040
+ };
406041
+ });
406042
+ const result = await host.store.save({
406043
+ ...doc,
406044
+ enabled,
406045
+ clickAction,
406046
+ maxDepth,
406047
+ items
406048
+ });
406049
+ setBusy(false);
406050
+ if (!result.ok) setError(result.error ?? "清理失败");
406051
+ else host.notify("已清理失效绑定");
406052
+ }
406053
+ /**
406054
+ * 配置备份与迁移(导出 / 导入 / 重置按钮位置)。
406055
+ * @author ddj 2026年10月30号
406056
+ * @returns 按钮行
406057
+ */
406058
+ function backupRow() {
406059
+ return react$1.default.createElement("div", { className: "edrv-qm-btn-row" }, react$1.default.createElement("input", {
406060
+ ref: fileRef,
406061
+ type: "file",
406062
+ accept: "application/json",
406063
+ style: { display: "none" },
406064
+ onChange: (event) => {
406065
+ onImport(event, host, setError);
406066
+ }
406067
+ }), react$1.default.createElement("button", {
406068
+ type: "button",
406069
+ className: "edrv-qm-btn",
406070
+ onClick: () => downloadDoc(doc)
406071
+ }, "导出 JSON"), react$1.default.createElement("button", {
406072
+ type: "button",
406073
+ className: "edrv-qm-btn",
406074
+ onClick: () => fileRef.current?.click()
406075
+ }, "导入 JSON"), react$1.default.createElement("button", {
406076
+ type: "button",
406077
+ className: "edrv-qm-btn",
406078
+ onClick: () => {
406079
+ host.store.patchLauncher(null, null);
406080
+ host.store.flushLauncher();
406081
+ host.notify("已重置悬浮按钮位置");
406082
+ }
406083
+ }, "重置按钮位置"));
406084
+ }
406085
+ const header = react$1.default.createElement("div", { className: "edrv-qm-head" }, react$1.default.createElement("div", { className: "edrv-qm-head-title" }, "菜单设置"), react$1.default.createElement("div", { className: "edrv-qm-head-sub" }, "悬浮按钮、交互行为与配置迁移"));
406086
+ const body = react$1.default.createElement("div", { className: "edrv-qm-dialog-body" }, react$1.default.createElement(Section, { title: "外观" }, react$1.default.createElement(PickSwitch, {
406087
+ buttonId: "edrv-qm-switch-enabled",
406088
+ label: "显示悬浮按钮",
406089
+ hint: "关闭后左侧不再显示快捷菜单按钮,配置会保留",
406090
+ checked: enabled,
406091
+ onChange: setEnabled
406092
+ })), react$1.default.createElement(Section, { title: "交互" }, react$1.default.createElement("label", { className: "edrv-qm-field" }, react$1.default.createElement("span", { className: "edrv-qm-field-label" }, "点击默认动作"), react$1.default.createElement("select", {
406093
+ className: "edrv-qm-select",
406094
+ value: clickAction,
406095
+ onChange: (event) => setClickAction(event.target.value)
406096
+ }, react$1.default.createElement("option", { value: "send" }, "直接发送给 AI"), react$1.default.createElement("option", { value: "fill" }, "只填入输入框"))), react$1.default.createElement("div", { className: "edrv-qm-field" }, react$1.default.createElement("div", { className: "edrv-qm-field-head" }, react$1.default.createElement("span", { className: "edrv-qm-field-label" }, "最大嵌套层数"), react$1.default.createElement("span", { className: "edrv-qm-field-hint" }, "root 列里的项是第 1 层;调小不删除已有深层项,只禁止继续新增")), react$1.default.createElement(PickSeg, {
406097
+ options: DEPTH_OPTIONS,
406098
+ value: String(maxDepth),
406099
+ label: "最大嵌套层数",
406100
+ onChange: (next) => setMaxDepth(Number(next) || 3)
406101
+ }))), react$1.default.createElement(Section, { title: "配置备份与迁移" }, backupRow()), react$1.default.createElement(Section, {
406102
+ title: "绑定状态",
406103
+ hint: broken.length === 0 ? "全部可用" : String(broken.length) + " 项失效"
406104
+ }, broken.length > 0 ? react$1.default.createElement("ul", { className: "edrv-qm-error-list" }, broken.map((entry) => react$1.default.createElement("li", { key: entry.key }, entry.label))) : null, broken.length > 0 ? react$1.default.createElement("div", { className: "edrv-qm-btn-row" }, react$1.default.createElement("button", {
406105
+ type: "button",
406106
+ className: "edrv-qm-btn",
406107
+ disabled: busy,
406108
+ onClick: () => {
406109
+ onClean();
406110
+ }
406111
+ }, "清理失效绑定")) : null), error !== "" ? react$1.default.createElement("div", { className: "edrv-qm-error-list" }, error) : null, react$1.default.createElement("div", { className: "edrv-qm-field-hint" }, "左侧按钮 / 菜单项" + (host.store.isMounted() ? "(当前界面已挂载)" : "(当前 DSH 未提供浮层槽,按钮不显示)")));
406112
+ return react$1.default.createElement(ModalShell, {
406113
+ dialogClass: "edrv-qm-dialog",
406114
+ zIndex: 220,
406115
+ onClose: props.onClose,
406116
+ closeOnMask: false
406117
+ }, header, body, react$1.default.createElement("div", { className: "edrv-qm-actions" }, react$1.default.createElement("span", { className: "edrv-qm-actions-grow" }), react$1.default.createElement("button", {
406118
+ type: "button",
406119
+ className: "edrv-qm-btn",
406120
+ onClick: props.onClose
406121
+ }, "取消"), react$1.default.createElement("button", {
406122
+ type: "button",
406123
+ className: "edrv-qm-btn edrv-qm-btn--primary",
406124
+ disabled: busy,
406125
+ onClick: () => {
406126
+ onSave();
406127
+ }
406128
+ }, busy ? "保存中…" : "保存")));
406129
+ }
406130
+ /**
406131
+ * 收集全菜单的失效绑定(设置弹窗顶部汇总)。
406132
+ * @author ddj 2026年10月30号
406133
+ * @param doc 文档
406134
+ * @param ctx 校验上下文
406135
+ * @returns 失效条目文案
406136
+ */
406137
+ function collectBroken(doc, ctx) {
406138
+ const out = [];
406139
+ for (const item of doc.items) {
406140
+ if (item.type !== "button") continue;
406141
+ const status = itemStatus(item, ctx);
406142
+ status.errors.concat(status.warnings).forEach((reason, index) => {
406143
+ out.push({
406144
+ key: item.id + ":" + index,
406145
+ label: item.name + " → " + reason
406146
+ });
406147
+ });
406148
+ }
406149
+ return out;
406150
+ }
406151
+ /**
406152
+ * 下载当前文档为 JSON(浏览器侧 Blob,不经 host)。
406153
+ * @author ddj 2026年10月30号
406154
+ * @param doc 文档
406155
+ */
406156
+ function downloadDoc(doc) {
406157
+ const blob = new Blob([serializeDoc(doc)], { type: "application/json" });
406158
+ const url = URL.createObjectURL(blob);
406159
+ const anchor = document.createElement("a");
406160
+ anchor.href = url;
406161
+ anchor.download = "quick-menu.json";
406162
+ anchor.click();
406163
+ setTimeout(() => URL.revokeObjectURL(url), 2e3);
406164
+ }
406165
+ /**
406166
+ * 导入 JSON(解析失败/校验不通过时给错误文案)。
406167
+ * @author ddj 2026年10月30号
406168
+ * @param event 文件输入事件
406169
+ * @param host 宿主
406170
+ * @param setError 错误回写
406171
+ */
406172
+ async function onImport(event, host, setError) {
406173
+ const file = event.target.files?.[0];
406174
+ event.target.value = "";
406175
+ if (!file) return;
406176
+ let text = "";
406177
+ try {
406178
+ text = await file.text();
406179
+ } catch (error) {
406180
+ setError("读取文件失败:" + String(error));
406181
+ return;
406182
+ }
406183
+ const parsed = parseDoc(text);
406184
+ if (parsed.warning) {
406185
+ setError(parsed.warning);
406186
+ return;
406187
+ }
406188
+ const result = await host.store.save(parsed.doc);
406189
+ if (!result.ok) setError(result.error ?? "导入失败");
406190
+ else host.notify("已导入快捷菜单配置");
406191
+ }
406192
+ //#endregion
406193
+ //#region src/client/ui/QuickMenuLauncher.tsx
406194
+ /**
406195
+ * dsh-vscode-mode client — 快捷菜单悬浮按钮与多列级联菜单(SAO Utils 2 风格)。
406196
+ *
406197
+ * 交互约定:
406198
+ * - 按钮可拖动(3px 阈值区分点击),松手落盘坐标;无保存坐标时锚定输入框左上角。
406199
+ * - 点开是纵向滑动列表:行靠近列中心放大、向两端递减透明度,上下各 48px 渐隐(CSS mask)。
406200
+ * - 每组列最后一行是「+ 新建」;悬停组 120ms 后在右侧展开它的一列(放不下自动翻左侧)。
406201
+ * - 行悬停浮出「⋯」:编辑(弹窗内可删)/ 复制 / 删除;root 列底部固定「菜单设置」。
406202
+ * 作者 ddj 2026年10月30号
406203
+ */
406204
+ /** 悬浮按钮尺寸。 */
406205
+ const LAUNCHER_SIZE = {
406206
+ width: 32,
406207
+ height: 32
406208
+ };
406209
+ /** 列宽。 */
406210
+ const COL_WIDTH = 220;
406211
+ /** 行高(与 CSS 的 --qm-row 一致,用于计算子列对齐位置)。 */
406212
+ const ROW_HEIGHT = 34;
406213
+ /** 列内上下内边距(与 CSS 一致)。 */
406214
+ const COL_PAD = 8;
406215
+ /** 列间距。 */
406216
+ const COL_GAP = 6;
406217
+ /** 列最大高度。 */
406218
+ const COL_MAX_HEIGHT = 520;
406219
+ /** root 列底部「⚙ 菜单设置」底栏高度(与 CSS 的 flex-basis 一致),会占掉滚动区高度。 */
406220
+ const FOOT_HEIGHT = 32;
406221
+ /** 行与行之间的间隔(与 CSS 的 .edrv-qm-list gap 一致)。 */
406222
+ const ROW_GAP = 1;
406223
+ /** 空态卡片的内容高度:标题 + 说明 + 主按钮,短于此会被裁掉说明文字。 */
406224
+ const EMPTY_HEIGHT = 132;
406225
+ /** 列的上下边框(border-box,需从内容高度里让出)。 */
406226
+ const COL_BORDER = 2;
406227
+ /** 拖动判定阈值(像素)。 */
406228
+ const DRAG_SLOP = 3;
406229
+ /** 悬停展开意图延迟。 */
406230
+ const HOVER_DELAY = 120;
406231
+ /**
406232
+ * 悬浮按钮的鲸鱼标记:取自 DSH 顶栏品牌鲸(同一条可视化轮廓),
406233
+ * 实心填充在 19px 下比描边更认得出是鲸鱼而不是一个气泡。
406234
+ */
406235
+ const WHALE_PATH = "M22.9168 1.43018C22.6713 1.31018 22.5658 1.53918 22.4223 1.65519C22.3733 1.69269 22.3318 1.74169 22.2903 1.78669C21.9317 2.1697 21.5127 2.42121 20.9657 2.39121C20.1657 2.34621 19.4827 2.59771 18.8787 3.20973C18.7502 2.45521 18.3236 2.0047 17.6746 1.71569C17.3351 1.56568 16.9916 1.41518 16.7536 1.08867C16.5876 0.856163 16.5421 0.597155 16.4591 0.341647C16.4061 0.187643 16.3536 0.0301382 16.1761 0.00363739C15.9836 -0.0263635 15.9081 0.135141 15.8326 0.270145C15.5306 0.822162 15.4136 1.43018 15.4251 2.0462C15.4516 3.43174 16.0366 4.53527 17.1991 5.3203C17.3311 5.4103 17.3651 5.5003 17.3236 5.63181C17.2441 5.90231 17.1501 6.16482 17.0671 6.43533C17.0141 6.60784 16.9351 6.64584 16.7501 6.57033C16.1121 6.30383 15.5611 5.90931 15.074 5.4328C14.2475 4.63328 13.5 3.75075 12.568 3.05973C12.349 2.89822 12.13 2.74822 11.9034 2.60522C10.9524 1.68169 12.028 0.923165 12.277 0.833162C12.5375 0.739159 12.3675 0.41615 11.5259 0.42015C10.6844 0.42365 9.91439 0.705658 8.93286 1.08117C8.78935 1.13767 8.63835 1.17867 8.48384 1.21267C7.59332 1.04367 6.66829 1.00617 5.70226 1.11517C3.88321 1.31768 2.43016 2.1777 1.36213 3.64575C0.0790928 5.4103 -0.222916 7.41536 0.146595 9.50642C0.535106 11.7105 1.66014 13.535 3.38869 14.9616C5.18125 16.4406 7.24581 17.1657 9.60138 17.0266C11.0319 16.9441 12.6245 16.7526 14.421 15.2321C14.874 15.4576 15.3496 15.5476 16.1381 15.6151C16.7456 15.6716 17.3306 15.5851 17.7836 15.4911C18.4931 15.3411 18.4441 14.6841 18.1876 14.5636C16.1081 13.595 16.5646 13.9891 16.1496 13.67C17.2061 12.42 18.8202 10.1979 19.3182 7.17235C19.3672 6.83834 19.4297 6.36783 19.4222 6.09732C19.4182 5.93231 19.4562 5.86831 19.6447 5.84931C20.1657 5.78931 20.6712 5.64681 21.1357 5.3913C22.4833 4.65528 23.0268 3.44624 23.1548 1.9972C23.1738 1.77569 23.1508 1.54668 22.9168 1.43018ZM11.1749 14.4736C9.15936 12.889 8.18184 12.3675 7.77832 12.39C7.40081 12.4125 7.46881 12.8445 7.55182 13.126C7.63882 13.404 7.75182 13.5955 7.91033 13.8396C8.01983 14.0011 8.09533 14.2411 7.80083 14.4216C7.15181 14.8231 6.02327 14.2866 5.97027 14.2601C4.65673 13.4865 3.5587 12.4655 2.78467 11.069C2.03715 9.72493 1.60314 8.28289 1.53164 6.74384C1.51264 6.37233 1.62214 6.24082 1.99215 6.17332C2.47916 6.08332 2.98118 6.06432 3.46769 6.13582C5.52476 6.43633 7.27581 7.35586 8.74385 8.8129C9.58188 9.64243 10.2159 10.634 10.8689 11.6025C11.5634 12.631 12.3105 13.611 13.262 14.4146C13.598 14.6961 13.866 14.9101 14.1225 15.0681C13.349 15.1546 12.058 15.1731 11.1749 14.4746L11.1749 14.4736ZM12.141 8.25988C12.141 8.09488 12.273 7.96338 12.439 7.96338C12.4765 7.96338 12.5105 7.97088 12.541 7.98188C12.5825 7.99688 12.6205 8.01938 12.6505 8.05338C12.7035 8.10588 12.7335 8.18088 12.7335 8.25988C12.7335 8.42489 12.6015 8.55639 12.4355 8.55639C12.2695 8.55639 12.141 8.42489 12.141 8.25988ZM15.1415 9.79893C14.949 9.87793 14.7565 9.94544 14.5715 9.95294C14.2845 9.96794 13.9715 9.85143 13.8015 9.70893C13.5375 9.48742 13.3485 9.36342 13.2695 8.97691C13.2355 8.8119 13.2545 8.55639 13.2845 8.40989C13.3525 8.09438 13.277 7.89187 13.0545 7.70787C12.8735 7.55786 12.643 7.51636 12.39 7.51636C12.2955 7.51636 12.209 7.47486 12.1445 7.44136C12.039 7.38886 11.9519 7.25735 12.035 7.09585C12.0615 7.04335 12.19 6.91584 12.22 6.89334C12.5635 6.69784 12.9595 6.76184 13.326 6.90834C13.6655 7.04735 13.9225 7.30236 14.292 7.66287C14.6695 8.09838 14.7375 8.21838 14.9525 8.54539C15.1225 8.8009 15.277 9.06341 15.3831 9.36392C15.4471 9.55142 15.3641 9.70493 15.1415 9.79893Z";
406236
+ /**
406237
+ * 数值夹取。
406238
+ * @author ddj 2026年10月30号
406239
+ * @param value 原始值
406240
+ * @param min 下限
406241
+ * @param max 上限
406242
+ * @returns 夹取结果
406243
+ */
406244
+ function clamp(value, min, max) {
406245
+ if (!Number.isFinite(value)) return min;
406246
+ return Math.min(max, Math.max(min, value));
406247
+ }
406248
+ /**
406249
+ * 两个矩形是否等价(测量轮询用,避免无意义重渲染)。
406250
+ * @author ddj 2026年10月30号
406251
+ * @param left 矩形 A
406252
+ * @param right 矩形 B
406253
+ * @returns 是否等价
406254
+ */
406255
+ function sameRect(left, right) {
406256
+ return left.left === right.left && left.top === right.top && left.width === right.width && left.height === right.height;
406257
+ }
406258
+ /**
406259
+ * 取当前可见的输入框锚点。
406260
+ * 页面里可能同时存在多个(隐藏会话的 seat 位于视口外,量到的 top 会超过浮层高度,
406261
+ * 把按钮夹到屏幕底边压住输入框),因此优先选真正落在视口内、面积最大的那个。
406262
+ * @author ddj 2026年10月30号
406263
+ * @param layerRect 浮层矩形(按钮坐标以它为基准)
406264
+ * @returns 相对浮层的输入框矩形;没有可用锚点时返回 null
406265
+ */
406266
+ function pickSeatRect(layerRect) {
406267
+ const seats = Array.from(document.querySelectorAll("[data-composer-seat]"));
406268
+ let best = null;
406269
+ for (const seat of seats) {
406270
+ const rect = seat.getBoundingClientRect();
406271
+ if (rect.width < 40 || rect.height < 20) continue;
406272
+ const visibleTop = Math.max(rect.top, 0);
406273
+ const visibleBottom = Math.min(rect.bottom, window.innerHeight);
406274
+ if (visibleBottom <= visibleTop) continue;
406275
+ const visibleArea = (visibleBottom - visibleTop) * rect.width;
406276
+ if (best && best.visibleArea >= visibleArea) continue;
406277
+ best = {
406278
+ visibleArea,
406279
+ rect: {
406280
+ left: rect.left - layerRect.left,
406281
+ top: rect.top - layerRect.top,
406282
+ width: rect.width,
406283
+ height: rect.height
406284
+ }
406285
+ };
406286
+ }
406287
+ return best ? best.rect : null;
406288
+ }
406289
+ /**
406290
+ * 今日日期文本({{date}} 占位符)。
406291
+ * @author ddj 2026年10月30号
406292
+ * @returns YYYY-MM-DD
406293
+ */
406294
+ function todayText() {
406295
+ const now = /* @__PURE__ */ new Date();
406296
+ const month = String(now.getMonth() + 1).padStart(2, "0");
406297
+ const day = String(now.getDate()).padStart(2, "0");
406298
+ return now.getFullYear() + "-" + month + "-" + day;
406299
+ }
406300
+ /**
406301
+ * 当前文档选择文本({{selection}} 占位符;拿不到就是空串)。
406302
+ * @author ddj 2026年10月30号
406303
+ * @returns 选中文本
406304
+ */
406305
+ function selectionText() {
406306
+ try {
406307
+ return window.getSelection()?.toString() ?? "";
406308
+ } catch {
406309
+ return "";
406310
+ }
406311
+ }
406312
+ /**
406313
+ * 悬浮按钮 + 多列级联菜单。
406314
+ * @author ddj 2026年10月30号
406315
+ * @param props host(store + 发送/规则/技能桥接)
406316
+ * @returns 菜单元素(不显示时为 null)
406317
+ */
406318
+ function QuickMenuLauncher(props) {
406319
+ const { host } = props;
406320
+ const store = host.store;
406321
+ react$1.default.useSyncExternalStore(store.subscribe, store.getVersion, store.getVersion);
406322
+ const state = store.getState();
406323
+ const doc = state.doc;
406324
+ const [layerSize, setLayerSize] = react$1.default.useState({
406325
+ width: 1280,
406326
+ height: 800
406327
+ });
406328
+ const [measured, setMeasured] = react$1.default.useState(false);
406329
+ const [seat, setSeat] = react$1.default.useState(null);
406330
+ const [open, setOpen] = react$1.default.useState(false);
406331
+ const [dragging, setDragging] = react$1.default.useState(false);
406332
+ const [dragPos, setDragPos] = react$1.default.useState(null);
406333
+ const [openPath, setOpenPath] = react$1.default.useState([]);
406334
+ const [hoverRow, setHoverRow] = react$1.default.useState({});
406335
+ const [scrolls, setScrolls] = react$1.default.useState({});
406336
+ const [rowMenu, setRowMenu] = react$1.default.useState(null);
406337
+ const [dialog, setDialog] = react$1.default.useState({ kind: "none" });
406338
+ const [active, setActive] = react$1.default.useState({
406339
+ depth: 0,
406340
+ index: 0
406341
+ });
406342
+ const hoverTimer = react$1.default.useRef(null);
406343
+ /**
406344
+ * 测量浮层与输入框锚点(浮层坐标系)。
406345
+ * 值不变时返回原状态,避免轮询测量引起无意义重渲染。
406346
+ * @author ddj 2026年10月30号
406347
+ */
406348
+ const measure = react$1.default.useCallback(() => {
406349
+ const layerEl = document.querySelector("[data-shell-overlay]");
406350
+ if (!layerEl) {
406351
+ setLayerSize((prev) => prev.width === window.innerWidth && prev.height === window.innerHeight ? prev : {
406352
+ width: window.innerWidth,
406353
+ height: window.innerHeight
406354
+ });
406355
+ setSeat(null);
406356
+ return;
406357
+ }
406358
+ const layerRect = layerEl.getBoundingClientRect();
406359
+ setLayerSize((prev) => prev.width === layerRect.width && prev.height === layerRect.height ? prev : {
406360
+ width: layerRect.width,
406361
+ height: layerRect.height
406362
+ });
406363
+ setMeasured((prev) => prev ? prev : true);
406364
+ const next = pickSeatRect(layerRect);
406365
+ setSeat((prev) => prev && next && sameRect(prev, next) ? prev : next);
406366
+ }, []);
406367
+ react$1.default.useEffect(() => {
406368
+ store.markMounted(true);
406369
+ measure();
406370
+ store.load();
406371
+ const onResize = () => measure();
406372
+ window.addEventListener("resize", onResize);
406373
+ const timers = [
406374
+ 80,
406375
+ 320,
406376
+ 900,
406377
+ 2e3
406378
+ ].map((delay) => setTimeout(measure, delay));
406379
+ const layerEl = document.querySelector("[data-shell-overlay]");
406380
+ const seatEl = document.querySelector("[data-composer-seat]");
406381
+ let observer = null;
406382
+ if (typeof ResizeObserver !== "undefined") {
406383
+ observer = new ResizeObserver(() => measure());
406384
+ if (layerEl) observer.observe(layerEl);
406385
+ if (seatEl) observer.observe(seatEl);
406386
+ }
406387
+ const poll = setInterval(measure, 1200);
406388
+ return () => {
406389
+ window.removeEventListener("resize", onResize);
406390
+ timers.forEach((timer) => clearTimeout(timer));
406391
+ clearInterval(poll);
406392
+ observer?.disconnect();
406393
+ store.markMounted(false);
406394
+ };
406395
+ }, [measure, store]);
406396
+ react$1.default.useEffect(() => {
406397
+ if (!open) return;
406398
+ measure();
406399
+ store.refreshBindings();
406400
+ }, [
406401
+ open,
406402
+ measure,
406403
+ store
406404
+ ]);
406405
+ const ctx = store.getContext();
406406
+ const basePos = react$1.default.useMemo(() => {
406407
+ const maxLeft = Math.max(8, layerSize.width - LAUNCHER_SIZE.width - 8);
406408
+ const maxTop = Math.max(8, layerSize.height - LAUNCHER_SIZE.height - 8);
406409
+ const savedX = doc.launcher.x;
406410
+ const savedY = doc.launcher.y;
406411
+ if (savedX !== null && savedY !== null) {
406412
+ if (savedX >= 8 && savedY >= 8 && savedX <= maxLeft && savedY <= maxTop || !measured) return {
406413
+ left: clamp(savedX, 8, maxLeft),
406414
+ top: clamp(savedY, 8, maxTop)
406415
+ };
406416
+ }
406417
+ const layer = {
406418
+ left: 0,
406419
+ top: 0,
406420
+ width: layerSize.width,
406421
+ height: layerSize.height
406422
+ };
406423
+ return anchorLauncher(seat, layer, LAUNCHER_SIZE, 8);
406424
+ }, [
406425
+ doc.launcher.x,
406426
+ doc.launcher.y,
406427
+ seat,
406428
+ layerSize,
406429
+ measured
406430
+ ]);
406431
+ const launcherPos = dragPos ?? basePos;
406432
+ /**
406433
+ * 计算每一列的矩形(独立定位,子列右展放不下则自动翻到左侧)。
406434
+ * @author ddj 2026年10月30号
406435
+ */
406436
+ const columnRects = react$1.default.useMemo(() => {
406437
+ const rects = [];
406438
+ const layerRect = {
406439
+ left: 0,
406440
+ top: 0,
406441
+ width: layerSize.width,
406442
+ height: layerSize.height
406443
+ };
406444
+ let anchor = {
406445
+ left: launcherPos.left,
406446
+ top: launcherPos.top,
406447
+ width: LAUNCHER_SIZE.width,
406448
+ height: LAUNCHER_SIZE.height
406449
+ };
406450
+ const depthCount = openPath.length + 1;
406451
+ for (let depth = 0; depth < depthCount; depth += 1) {
406452
+ const parentId = depth === 0 ? null : openPath[depth - 1];
406453
+ const items = childrenOf(doc, parentId, ctx.cwd);
406454
+ const extra = canCreateAt(doc, parentId) ? 1 : 0;
406455
+ const foot = depth === 0 ? FOOT_HEIGHT : 0;
406456
+ const rowCount = items.length + extra;
406457
+ const rowHeight = items.length === 0 ? EMPTY_HEIGHT : items.length * ROW_HEIGHT + (extra > 0 ? 39 : 0) + Math.max(0, rowCount - 1) * ROW_GAP + 16;
406458
+ const height = Math.min(COL_MAX_HEIGHT, Math.max(68 + foot, rowHeight + foot + COL_BORDER));
406459
+ let rect = { ...placeColumn(anchor, {
406460
+ width: COL_WIDTH,
406461
+ height
406462
+ }, layerSize, COL_GAP) };
406463
+ if (depth > 0) {
406464
+ const rowIndex = hoverRow[depth - 1] ?? 0;
406465
+ const wantedTop = anchor.top + COL_PAD + rowIndex * ROW_HEIGHT - (scrolls[depth - 1] ?? 0);
406466
+ rect = {
406467
+ ...rect,
406468
+ top: clamp(wantedTop, 8, Math.max(8, layerRect.height - height - 8))
406469
+ };
406470
+ }
406471
+ rects.push(rect);
406472
+ anchor = rect;
406473
+ }
406474
+ return rects;
406475
+ }, [
406476
+ doc,
406477
+ ctx.cwd,
406478
+ launcherPos.left,
406479
+ launcherPos.top,
406480
+ layerSize,
406481
+ openPath,
406482
+ hoverRow,
406483
+ scrolls
406484
+ ]);
406485
+ /**
406486
+ * 关闭菜单并清空级联状态。
406487
+ * @author ddj 2026年10月30号
406488
+ */
406489
+ const closeMenu = react$1.default.useCallback(() => {
406490
+ setOpen(false);
406491
+ setRowMenu(null);
406492
+ setOpenPath([]);
406493
+ setHoverRow({});
406494
+ setScrolls({});
406495
+ setActive({
406496
+ depth: 0,
406497
+ index: 0
406498
+ });
406499
+ }, []);
406500
+ react$1.default.useEffect(() => {
406501
+ if (!open) return;
406502
+ const onPointerDown = (event) => {
406503
+ const target = event.target;
406504
+ if (target && target.closest("[data-qm-root=\"1\"]")) return;
406505
+ closeMenu();
406506
+ };
406507
+ const onKeyDown = (event) => {
406508
+ if (event.key === "Escape") {
406509
+ event.stopPropagation();
406510
+ closeMenu();
406511
+ return;
406512
+ }
406513
+ const parentId = active.depth === 0 ? null : openPath[active.depth - 1];
406514
+ const items = childrenOf(doc, parentId, ctx.cwd);
406515
+ if (items.length === 0) return;
406516
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
406517
+ event.preventDefault();
406518
+ const delta = event.key === "ArrowDown" ? 1 : -1;
406519
+ const index = (active.index + delta + items.length) % items.length;
406520
+ setActive({
406521
+ depth: active.depth,
406522
+ index
406523
+ });
406524
+ setHoverRow((prev) => ({
406525
+ ...prev,
406526
+ [active.depth]: index
406527
+ }));
406528
+ return;
406529
+ }
406530
+ if (event.key === "ArrowRight") {
406531
+ const item = items[Math.min(active.index, items.length - 1)];
406532
+ if (item && item.type === "group") {
406533
+ event.preventDefault();
406534
+ setOpenPath((prev) => prev.slice(0, active.depth).concat(item.id));
406535
+ setActive({
406536
+ depth: active.depth + 1,
406537
+ index: 0
406538
+ });
406539
+ }
406540
+ return;
406541
+ }
406542
+ if (event.key === "ArrowLeft" && active.depth > 0) {
406543
+ event.preventDefault();
406544
+ setOpenPath((prev) => prev.slice(0, active.depth - 1));
406545
+ setActive({
406546
+ depth: active.depth - 1,
406547
+ index: 0
406548
+ });
406549
+ return;
406550
+ }
406551
+ if (event.key === "Enter") {
406552
+ const item = items[Math.min(active.index, items.length - 1)];
406553
+ if (item) activate(item);
406554
+ }
406555
+ };
406556
+ window.addEventListener("pointerdown", onPointerDown, true);
406557
+ window.addEventListener("keydown", onKeyDown, true);
406558
+ return () => {
406559
+ window.removeEventListener("pointerdown", onPointerDown, true);
406560
+ window.removeEventListener("keydown", onKeyDown, true);
406561
+ };
406562
+ });
406563
+ /**
406564
+ * 拖动与点击的分流:移动超过阈值才算拖拽,否则当作点击开关。
406565
+ * @author ddj 2026年10月30号
406566
+ * @param event 指针按下事件
406567
+ */
406568
+ function beginDrag(event) {
406569
+ if (event.button !== 0) return;
406570
+ event.preventDefault();
406571
+ event.stopPropagation();
406572
+ const start = {
406573
+ pointerId: event.pointerId,
406574
+ x: event.clientX,
406575
+ y: event.clientY,
406576
+ left: launcherPos.left,
406577
+ top: launcherPos.top,
406578
+ moved: false
406579
+ };
406580
+ let last = {
406581
+ left: launcherPos.left,
406582
+ top: launcherPos.top
406583
+ };
406584
+ const onMove = (moveEvent) => {
406585
+ if (moveEvent.pointerId !== start.pointerId) return;
406586
+ const dx = moveEvent.clientX - start.x;
406587
+ const dy = moveEvent.clientY - start.y;
406588
+ if (!start.moved && Math.abs(dx) + Math.abs(dy) < DRAG_SLOP) return;
406589
+ start.moved = true;
406590
+ setDragging(true);
406591
+ last = {
406592
+ left: clamp(start.left + dx, 8, Math.max(8, layerSize.width - LAUNCHER_SIZE.width - 8)),
406593
+ top: clamp(start.top + dy, 8, Math.max(8, layerSize.height - LAUNCHER_SIZE.height - 8))
406594
+ };
406595
+ setDragPos(last);
406596
+ };
406597
+ const finish = () => {
406598
+ window.removeEventListener("pointermove", onMove, true);
406599
+ window.removeEventListener("pointerup", onUp, true);
406600
+ window.removeEventListener("pointercancel", onUp, true);
406601
+ window.removeEventListener("blur", onUp, true);
406602
+ setDragging(false);
406603
+ setDragPos(null);
406604
+ if (start.moved) {
406605
+ store.patchLauncher(last.left, last.top);
406606
+ store.flushLauncher();
406607
+ return;
406608
+ }
406609
+ if (open) closeMenu();
406610
+ else setOpen(true);
406611
+ };
406612
+ const onUp = () => finish();
406613
+ window.addEventListener("pointermove", onMove, true);
406614
+ window.addEventListener("pointerup", onUp, true);
406615
+ window.addEventListener("pointercancel", onUp, true);
406616
+ window.addEventListener("blur", onUp, true);
406617
+ }
406618
+ /**
406619
+ * 悬停行:按钮只高亮;组延迟后在右侧展开(防抖动)。
406620
+ * @author ddj 2026年10月30号
406621
+ * @param depth 所在列序号
406622
+ * @param index 行序号
406623
+ * @param item 菜单项
406624
+ */
406625
+ function hoverItem(depth, index, item) {
406626
+ setHoverRow((prev) => ({
406627
+ ...prev,
406628
+ [depth]: index
406629
+ }));
406630
+ setActive({
406631
+ depth,
406632
+ index
406633
+ });
406634
+ if (hoverTimer.current !== null) clearTimeout(hoverTimer.current);
406635
+ if (item.type !== "group") {
406636
+ setOpenPath((prev) => prev.slice(0, depth));
406637
+ return;
406638
+ }
406639
+ hoverTimer.current = setTimeout(() => {
406640
+ setOpenPath((prev) => prev.slice(0, depth).concat(item.id));
406641
+ }, HOVER_DELAY);
406642
+ }
406643
+ /**
406644
+ * 执行一项:组则展开,按钮则合成文本后发送/填入。
406645
+ * @author ddj 2026年10月30号
406646
+ * @param item 菜单项
406647
+ */
406648
+ async function activate(item) {
406649
+ if (item.type === "group") return;
406650
+ const inlineContents = {};
406651
+ if (item.ruleMode === "inline") for (const binding of item.rules) {
406652
+ if (validateRule(binding, ctx).state !== "ok") continue;
406653
+ try {
406654
+ inlineContents[bindingKey(binding)] = await host.readRule(binding);
406655
+ } catch {}
406656
+ }
406657
+ const composed = composePrompt(item, {
406658
+ ...ctx,
406659
+ inlineContents,
406660
+ selection: selectionText(),
406661
+ date: todayText()
406662
+ });
406663
+ if (composed.text.trim() === "") {
406664
+ host.notify("「" + item.name + "」没有可发送的内容");
406665
+ return;
406666
+ }
406667
+ if (composed.warnings.length > 0) host.notify("已跳过 " + composed.warnings.length + " 项失效绑定:" + composed.warnings[0]);
406668
+ const action = item.clickAction ?? doc.clickAction;
406669
+ closeMenu();
406670
+ const result = action === "fill" ? await host.fill(composed.text) : await host.send(composed.text);
406671
+ if (!result.ok) host.notify(result.message ?? "操作失败");
406672
+ }
406673
+ /**
406674
+ * 复制一项(同组同级追加副本,名字加「副本」)。
406675
+ * @author ddj 2026年10月30号
406676
+ * @param item 源项
406677
+ */
406678
+ async function duplicate(item) {
406679
+ const copy = item.type === "group" ? {
406680
+ ...item,
406681
+ id: store.nextId("group"),
406682
+ name: item.name + " 副本",
406683
+ order: item.order + 1
406684
+ } : {
406685
+ ...item,
406686
+ id: store.nextId("button"),
406687
+ name: item.name + " 副本",
406688
+ order: item.order + 1
406689
+ };
406690
+ const result = await store.createItem(copy);
406691
+ if (!result.ok) host.notify(result.error ?? "复制失败");
406692
+ setRowMenu(null);
406693
+ }
406694
+ /**
406695
+ * 删除一项(组连带子项)。
406696
+ * @author ddj 2026年10月30号
406697
+ * @param item 目标项
406698
+ */
406699
+ async function removeItem(item) {
406700
+ setRowMenu(null);
406701
+ const suffix = item.type === "group" ? "及其全部子项" : "";
406702
+ if (!window.confirm("确认删除「" + item.name + "」" + suffix + "?")) return;
406703
+ const result = await store.removeItem(item.id);
406704
+ if (!result.ok) host.notify(result.error ?? "删除失败");
406705
+ setOpenPath([]);
406706
+ }
406707
+ const launcher = /* @__PURE__ */ react$1.default.createElement("div", {
406708
+ className: "edrv-qm-launcher" + (dragging ? " edrv-qm-launcher--drag" : "") + (open ? " edrv-qm-launcher--open" : ""),
406709
+ style: {
406710
+ left: launcherPos.left,
406711
+ top: launcherPos.top
406712
+ },
406713
+ title: "快捷菜单(拖动可移动,右键更多)",
406714
+ role: "button",
406715
+ tabIndex: 0,
406716
+ "aria-label": "快捷菜单",
406717
+ "aria-expanded": open,
406718
+ onPointerDown: beginDrag,
406719
+ onKeyDown: (event) => {
406720
+ if (event.key !== "Enter" && event.key !== " ") return;
406721
+ event.preventDefault();
406722
+ if (open) closeMenu();
406723
+ else setOpen(true);
406724
+ },
406725
+ onContextMenu: (event) => {
406726
+ event.preventDefault();
406727
+ setOpen(false);
406728
+ setDialog({ kind: "global" });
406729
+ }
406730
+ }, /* @__PURE__ */ react$1.default.createElement("svg", {
406731
+ viewBox: "0 0 23.16 17.04",
406732
+ "aria-hidden": "true"
406733
+ }, /* @__PURE__ */ react$1.default.createElement("path", {
406734
+ d: WHALE_PATH,
406735
+ fill: "currentColor"
406736
+ })));
406737
+ const miniDot = /* @__PURE__ */ react$1.default.createElement("div", {
406738
+ className: "edrv-qm-launcher edrv-qm-launcher--mini",
406739
+ style: {
406740
+ left: launcherPos.left,
406741
+ top: launcherPos.top
406742
+ },
406743
+ title: "快捷菜单已隐藏,点击展开(右键打开设置)",
406744
+ role: "button",
406745
+ tabIndex: 0,
406746
+ "aria-label": "快捷菜单(已隐藏)",
406747
+ onPointerDown: beginDrag,
406748
+ onKeyDown: (event) => {
406749
+ if (event.key !== "Enter" && event.key !== " ") return;
406750
+ event.preventDefault();
406751
+ if (open) closeMenu();
406752
+ else setOpen(true);
406753
+ },
406754
+ onContextMenu: (event) => {
406755
+ event.preventDefault();
406756
+ setOpen(false);
406757
+ setDialog({ kind: "global" });
406758
+ }
406759
+ });
406760
+ const panels = open ? columnRects.map((rect, depth) => {
406761
+ const parentId = depth === 0 ? null : openPath[depth - 1];
406762
+ return /* @__PURE__ */ react$1.default.createElement(QuickColumn, {
406763
+ key: depth,
406764
+ depth,
406765
+ rect,
406766
+ items: childrenOf(doc, parentId, ctx.cwd),
406767
+ canAdd: canCreateAt(doc, parentId),
406768
+ ctx,
406769
+ status: state.status,
406770
+ error: state.error,
406771
+ activeIndex: active.depth === depth ? active.index : -1,
406772
+ scrollTop: scrolls[depth] ?? 0,
406773
+ onScroll: (top) => setScrolls((prev) => ({
406774
+ ...prev,
406775
+ [depth]: top
406776
+ })),
406777
+ onHover: (index, item) => hoverItem(depth, index, item),
406778
+ onActivate: (item) => {
406779
+ activate(item);
406780
+ },
406781
+ onMore: (item, rowRect) => setRowMenu({
406782
+ item,
406783
+ rect: rowRect
406784
+ }),
406785
+ onAdd: () => setDialog({
406786
+ kind: "item",
406787
+ parentId
406788
+ }),
406789
+ onSettings: depth === 0 ? () => {
406790
+ setOpen(false);
406791
+ setDialog({ kind: "global" });
406792
+ } : void 0,
406793
+ onRetry: () => {
406794
+ store.load(true);
406795
+ },
406796
+ onEmptyHint: depth === 0 ? "把常做的提问连同技能、规则存成一个按钮,点一下就发出去。" : "组里还没有内容,新建一个按钮或子组。"
406797
+ });
406798
+ }) : null;
406799
+ return /* @__PURE__ */ react$1.default.createElement(react$1.default.Fragment, null, /* @__PURE__ */ react$1.default.createElement("div", {
406800
+ "data-edrv-view": "1",
406801
+ className: "edrv-qm-root",
406802
+ "data-qm-root": "1"
406803
+ }, doc.enabled ? launcher : miniDot), open ? (0, react_dom.createPortal)(/* @__PURE__ */ react$1.default.createElement("div", {
406804
+ "data-edrv-view": "1",
406805
+ className: "edrv-qm-menu",
406806
+ "data-qm-root": "1"
406807
+ }, panels, rowMenu ? /* @__PURE__ */ react$1.default.createElement(RowMenu, {
406808
+ state: rowMenu,
406809
+ onEdit: () => {
406810
+ setDialog({
406811
+ kind: "item",
406812
+ item: rowMenu.item
406813
+ });
406814
+ setRowMenu(null);
406815
+ },
406816
+ onDuplicate: () => {
406817
+ duplicate(rowMenu.item);
406818
+ },
406819
+ onDelete: () => {
406820
+ removeItem(rowMenu.item);
406821
+ }
406822
+ }) : null), document.body) : null, dialog.kind === "item" ? /* @__PURE__ */ react$1.default.createElement(QuickItemDialog, {
406823
+ host,
406824
+ item: dialog.item,
406825
+ parentId: dialog.item ? dialog.item.parentId : dialog.parentId ?? null,
406826
+ onClose: () => setDialog({ kind: "none" })
406827
+ }) : null, dialog.kind === "global" ? /* @__PURE__ */ react$1.default.createElement(MenuGlobalDialog, {
406828
+ host,
406829
+ onClose: () => setDialog({ kind: "none" })
406830
+ }) : null);
406831
+ }
406832
+ /**
406833
+ * 一列(纵向滑动列表 + 末行「+ 新建」 + root 底部设置)。
406834
+ * @author ddj 2026年10月30号
406835
+ * @param props 列数据与回调
406836
+ * @returns 列元素
406837
+ */
406838
+ function QuickColumn(props) {
406839
+ const scrollRef = react$1.default.useRef(null);
406840
+ const count = props.items.length + (props.canAdd ? 1 : 0);
406841
+ const isEmpty = props.items.length === 0 && props.status !== "loading" && props.status !== "error";
406842
+ const [overflow, setOverflow] = react$1.default.useState(false);
406843
+ react$1.default.useEffect(() => {
406844
+ props.onScroll(scrollRef.current?.scrollTop ?? 0);
406845
+ }, []);
406846
+ react$1.default.useEffect(() => {
406847
+ const viewport = scrollRef.current;
406848
+ if (!viewport) return;
406849
+ const measure = () => setOverflow(viewport.scrollHeight > viewport.clientHeight + 8);
406850
+ measure();
406851
+ const observer = new ResizeObserver(measure);
406852
+ observer.observe(viewport);
406853
+ return () => observer.disconnect();
406854
+ }, [
406855
+ props.items.length,
406856
+ props.canAdd,
406857
+ props.status
406858
+ ]);
406859
+ return /* @__PURE__ */ react$1.default.createElement("div", {
406860
+ className: "edrv-qm-col",
406861
+ style: {
406862
+ left: props.rect.left,
406863
+ top: props.rect.top,
406864
+ height: props.rect.height
406865
+ },
406866
+ role: "menu"
406867
+ }, /* @__PURE__ */ react$1.default.createElement("div", {
406868
+ className: "edrv-qm-scroll" + (overflow ? " edrv-qm-scroll--fade" : ""),
406869
+ ref: scrollRef,
406870
+ onScroll: (event) => props.onScroll(event.currentTarget.scrollTop)
406871
+ }, props.status === "loading" && props.items.length === 0 ? /* @__PURE__ */ react$1.default.createElement("div", { className: "edrv-qm-empty" }, "正在读取菜单…") : null, props.status === "error" && props.items.length === 0 ? /* @__PURE__ */ react$1.default.createElement("div", { className: "edrv-qm-empty" }, /* @__PURE__ */ react$1.default.createElement("div", { className: "edrv-qm-empty-title" }, "菜单没读出来"), /* @__PURE__ */ react$1.default.createElement("div", null, props.error ?? "读取失败"), /* @__PURE__ */ react$1.default.createElement("button", {
406872
+ type: "button",
406873
+ className: "edrv-qm-btn",
406874
+ onClick: props.onRetry
406875
+ }, "重试")) : null, isEmpty ? /* @__PURE__ */ react$1.default.createElement("div", { className: "edrv-qm-empty" }, /* @__PURE__ */ react$1.default.createElement("div", { className: "edrv-qm-empty-title" }, props.depth === 0 ? "还没有快捷项" : "这一组是空的"), /* @__PURE__ */ react$1.default.createElement("div", null, props.onEmptyHint), props.canAdd ? /* @__PURE__ */ react$1.default.createElement("button", {
406876
+ type: "button",
406877
+ className: "edrv-qm-btn edrv-qm-btn--primary",
406878
+ onClick: props.onAdd
406879
+ }, props.depth === 0 ? "新建第一个" : "新建一项") : null) : null, /* @__PURE__ */ react$1.default.createElement("div", { className: "edrv-qm-list" }, props.items.map((item, index) => {
406880
+ const scale = index === props.activeIndex ? {
406881
+ scale: 1,
406882
+ opacity: 1
406883
+ } : centerScale(index, count);
406884
+ const flags = itemStatus(item, props.ctx);
406885
+ const badge = flags.errors.length > 0 ? " edrv-qm-row-badge--error" : "";
406886
+ const tip = flags.errors.concat(flags.warnings).join("\n");
406887
+ return /* @__PURE__ */ react$1.default.createElement("div", {
406888
+ key: item.id,
406889
+ className: "edrv-qm-row" + (item.type === "group" ? " edrv-qm-row--group" : "") + (props.activeIndex === index ? " edrv-qm-row--active" : ""),
406890
+ style: {
406891
+ transform: "scale(" + scale.scale.toFixed(3) + ")",
406892
+ opacity: scale.opacity
406893
+ },
406894
+ title: tip,
406895
+ "data-qm-row": "1",
406896
+ role: "menuitem",
406897
+ tabIndex: -1,
406898
+ "aria-label": item.type === "group" ? item.name + "(组)" : item.name,
406899
+ onPointerEnter: () => props.onHover(index, item),
406900
+ onClick: () => props.onActivate(item)
406901
+ }, /* @__PURE__ */ react$1.default.createElement("span", { className: "edrv-qm-row-label" }, item.name), badge !== "" ? /* @__PURE__ */ react$1.default.createElement("span", { className: "edrv-qm-row-badge" + badge }) : null, item.type === "group" ? /* @__PURE__ */ react$1.default.createElement("span", {
406902
+ className: "edrv-qm-row-chevron",
406903
+ "aria-hidden": "true"
406904
+ }, /* @__PURE__ */ react$1.default.createElement("svg", {
406905
+ viewBox: "0 0 12 12",
406906
+ fill: "none",
406907
+ stroke: "currentColor",
406908
+ strokeWidth: "1.6",
406909
+ strokeLinecap: "round",
406910
+ strokeLinejoin: "round"
406911
+ }, /* @__PURE__ */ react$1.default.createElement("path", { d: "M4.5 2.5 8 6l-3.5 3.5" }))) : null, /* @__PURE__ */ react$1.default.createElement("button", {
406912
+ type: "button",
406913
+ className: "edrv-qm-row-more",
406914
+ title: "编辑 / 复制 / 删除",
406915
+ "aria-label": "「" + item.name + "」的更多操作",
406916
+ onClick: (event) => {
406917
+ event.stopPropagation();
406918
+ const rowRect = {
406919
+ left: props.rect.left,
406920
+ top: props.rect.top + COL_PAD + index * ROW_HEIGHT - props.scrollTop,
406921
+ width: COL_WIDTH,
406922
+ height: ROW_HEIGHT
406923
+ };
406924
+ props.onMore(item, rowRect);
406925
+ }
406926
+ }, "⋯"));
406927
+ }), props.canAdd && !isEmpty ? /* @__PURE__ */ react$1.default.createElement("div", {
406928
+ className: "edrv-qm-row edrv-qm-row--add",
406929
+ "data-qm-row": "1",
406930
+ role: "menuitem",
406931
+ "aria-label": "新建",
406932
+ onClick: props.onAdd
406933
+ }, "+ 新建") : null)), props.onSettings ? /* @__PURE__ */ react$1.default.createElement("div", {
406934
+ className: "edrv-qm-foot",
406935
+ onClick: props.onSettings,
406936
+ title: "显示、点击动作、最大嵌套层数、导入导出",
406937
+ role: "button",
406938
+ tabIndex: 0,
406939
+ onKeyDown: (event) => {
406940
+ if (event.key !== "Enter" && event.key !== " ") return;
406941
+ event.preventDefault();
406942
+ props.onSettings?.();
406943
+ }
406944
+ }, /* @__PURE__ */ react$1.default.createElement("span", {
406945
+ className: "edrv-qm-foot-icon",
406946
+ "aria-hidden": "true"
406947
+ }, /* @__PURE__ */ react$1.default.createElement("svg", {
406948
+ viewBox: "0 0 16 16",
406949
+ fill: "none",
406950
+ stroke: "currentColor",
406951
+ strokeWidth: "1.3",
406952
+ strokeLinecap: "round"
406953
+ }, /* @__PURE__ */ react$1.default.createElement("circle", {
406954
+ cx: "8",
406955
+ cy: "8",
406956
+ r: "2.3"
406957
+ }), /* @__PURE__ */ react$1.default.createElement("path", { d: "M8 1.9v1.7M8 12.4v1.7M1.9 8h1.7M12.4 8h1.7M3.7 3.7l1.2 1.2M11.1 11.1l1.2 1.2M12.3 3.7l-1.2 1.2M4.9 11.1l-1.2 1.2" }))), /* @__PURE__ */ react$1.default.createElement("span", null, "菜单设置")) : null);
406958
+ }
406959
+ /**
406960
+ * 行内小菜单(编辑 / 复制 / 删除)。
406961
+ * @author ddj 2026年10月30号
406962
+ * @param props 状态与动作
406963
+ * @returns 小菜单元素
406964
+ */
406965
+ function RowMenu(props) {
406966
+ return /* @__PURE__ */ react$1.default.createElement("div", {
406967
+ className: "edrv-qm-context",
406968
+ style: {
406969
+ left: props.state.rect.left + COL_WIDTH - 10,
406970
+ top: props.state.rect.top + 26
406971
+ },
406972
+ "data-qm-root": "1",
406973
+ role: "menu"
406974
+ }, /* @__PURE__ */ react$1.default.createElement("div", {
406975
+ className: "edrv-qm-context-item",
406976
+ role: "menuitem",
406977
+ onClick: props.onEdit
406978
+ }, "编辑"), /* @__PURE__ */ react$1.default.createElement("div", {
406979
+ className: "edrv-qm-context-item",
406980
+ role: "menuitem",
406981
+ onClick: props.onDuplicate
406982
+ }, "复制"), /* @__PURE__ */ react$1.default.createElement("div", { className: "edrv-qm-context-sep" }), /* @__PURE__ */ react$1.default.createElement("div", {
406983
+ className: "edrv-qm-context-item edrv-qm-context-item--danger",
406984
+ role: "menuitem",
406985
+ onClick: props.onDelete
406986
+ }, "删除"));
406987
+ }
403872
406988
  //#endregion
403873
406989
  //#region src/client/sidebar/registry.ts
403874
406990
  /** 校验面板定义并写入注册表(缺 id/render 抛 TypeError)。 */
@@ -409563,6 +412679,154 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
409563
412679
  /** 「添加到对话」动作集:编辑区/Tab 右键菜单注入文件引用与代码块(conversation 服务缺失时各动作安全降级)。 */
409564
412680
  const addToConversation = createAddToConversation(ctx);
409565
412681
  /**
412682
+ * 读取规则列表(rules.list;失败抛错,由快捷菜单按「未校验」降级)。
412683
+ * @author ddj 2026年10月30号
412684
+ * @returns 用户规则 + 项目规则
412685
+ */
412686
+ const loadRules = async () => {
412687
+ const result = await rpc("rules.list", {});
412688
+ if (!result.ok) throw new Error(result.error);
412689
+ return {
412690
+ user: result.user,
412691
+ projects: result.projects
412692
+ };
412693
+ };
412694
+ /**
412695
+ * 会话技能枚举服务(remote.skills 动态注入;旧版 DSH 无该服务时保持 null = 「未校验」)。
412696
+ * @author ddj 2026年10月30号
412697
+ */
412698
+ let skillEnumerator = null;
412699
+ /**
412700
+ * 读取会话可见技能名(服务不可用时返回 null,界面按「未校验」处理)。
412701
+ * @author ddj 2026年10月30号
412702
+ * @param sessionId 会话 id
412703
+ * @returns 技能名清单或 null
412704
+ */
412705
+ const loadSkillNames = async (sessionId) => {
412706
+ const list = skillEnumerator?.list;
412707
+ if (typeof list !== "function") return null;
412708
+ const result = await list.call(skillEnumerator, { sessionId });
412709
+ return ((result.value ?? result)?.skills ?? []).map((entry) => entry.name).filter((name) => typeof name === "string");
412710
+ };
412711
+ /**
412712
+ * 读取技能候选(全局技能 + 当前工作区技能;只读枚举,失败返回 null)。
412713
+ * @author ddj 2026年10月30号
412714
+ * @param cwd 目标工作区(缺省取当前会话作用域)
412715
+ * @returns 技能候选快照;host 无该 RPC 或读取失败时为 null
412716
+ */
412717
+ const loadSkillCatalog = async (cwd) => {
412718
+ const target = cwd ?? readSessionScope(ctx).cwd;
412719
+ try {
412720
+ const result = await rpc("skills.list", target ? { cwd: target } : {});
412721
+ if (!result.ok) return null;
412722
+ return {
412723
+ user: result.user,
412724
+ project: result.project,
412725
+ ...result.projectRoot ? { projectRoot: result.projectRoot } : {}
412726
+ };
412727
+ } catch {
412728
+ return null;
412729
+ }
412730
+ };
412731
+ /**
412732
+ * 确保存在可写入的会话(无会话时新建独立会话)。
412733
+ * @author ddj 2026年10月30号
412734
+ * @returns 会话 id 或错误文案
412735
+ */
412736
+ const ensureSession = async () => {
412737
+ const scope = readSessionScope(ctx);
412738
+ if (scope.sessionId) return { id: scope.sessionId };
412739
+ const draft = await addToConversation.startDraftSession(scope.cwd);
412740
+ if (!draft.ok || !draft.id) return { error: "当前没有可用会话,新建会话也失败了" };
412741
+ return { id: draft.id };
412742
+ };
412743
+ /**
412744
+ * 发送快捷菜单文本(优先发到当前会话,会话忙碌时如实回报)。
412745
+ * @author ddj 2026年10月30号
412746
+ * @param text 合成后的消息文本
412747
+ * @returns 是否发送成功
412748
+ */
412749
+ const sendQuickText = async (text) => {
412750
+ const session = await ensureSession();
412751
+ if ("error" in session) return {
412752
+ ok: false,
412753
+ message: session.error
412754
+ };
412755
+ return await addToConversation.sendTask(session.id, text) ? { ok: true } : {
412756
+ ok: false,
412757
+ message: "发送被拒绝(会话可能正忙,稍后再点)"
412758
+ };
412759
+ };
412760
+ /**
412761
+ * 只把快捷菜单文本填入输入框。
412762
+ * @author ddj 2026年10月30号
412763
+ * @param text 合成后的消息文本
412764
+ * @returns 是否填入成功
412765
+ */
412766
+ const fillQuickText = async (text) => {
412767
+ const session = await ensureSession();
412768
+ if ("error" in session) return {
412769
+ ok: false,
412770
+ message: session.error
412771
+ };
412772
+ const outcome = await addToConversation.appendText(session.id, text);
412773
+ if (outcome === "ok" || outcome === "busy") return { ok: true };
412774
+ return {
412775
+ ok: false,
412776
+ message: "写入输入框失败(" + outcome + "),请重试"
412777
+ };
412778
+ };
412779
+ /** 快捷菜单宿主桥接:菜单组件不直接摸 ctx,规则/技能/发送/工作区都从这里注入。 */
412780
+ const quickMenuHost = {
412781
+ store: createMenuStore({
412782
+ rules: loadRules,
412783
+ skills: loadSkillNames,
412784
+ scope: () => {
412785
+ const scope = readSessionScope(ctx);
412786
+ return {
412787
+ sessionId: scope.sessionId,
412788
+ cwd: scope.cwd
412789
+ };
412790
+ }
412791
+ }),
412792
+ scope: () => {
412793
+ const scope = readSessionScope(ctx);
412794
+ return {
412795
+ sessionId: scope.sessionId,
412796
+ cwd: scope.cwd
412797
+ };
412798
+ },
412799
+ send: sendQuickText,
412800
+ fill: fillQuickText,
412801
+ rules: loadRules,
412802
+ skills: async () => {
412803
+ const scope = readSessionScope(ctx);
412804
+ return scope.sessionId ? loadSkillNames(scope.sessionId) : null;
412805
+ },
412806
+ skillCatalog: loadSkillCatalog,
412807
+ readRule: async (binding) => {
412808
+ const result = await rpc("rules.read", {
412809
+ scope: binding.scope,
412810
+ file: binding.file,
412811
+ ...binding.workspacePath ? { workspacePath: binding.workspacePath } : {},
412812
+ ...binding.origin ? { origin: binding.origin } : {}
412813
+ });
412814
+ if (!result.ok) throw new Error(result.error);
412815
+ return result.content;
412816
+ },
412817
+ workspaces: () => (workspaces?.list.getSnapshot().items ?? []).map((item) => ({
412818
+ path: item.path,
412819
+ title: item.title
412820
+ })),
412821
+ notify: (message) => {
412822
+ toastDom(message);
412823
+ }
412824
+ };
412825
+ if (typeof ctx.inject === "function") ctx.inject(["remote.skills"], (scope) => {
412826
+ skillEnumerator = scope.remote?.skills ?? null;
412827
+ quickMenuHost.store.refreshBindings(true);
412828
+ });
412829
+ /**
409566
412830
  * Build applicable client diagnostics from live installation state.
409567
412831
  * @private @author ddj 2026年10月13号
409568
412832
  * @param version Running DSH version supplied by the compat RPC.
@@ -409999,6 +413263,21 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
409999
413263
  id: "edrv-plugin-status",
410000
413264
  order: 40
410001
413265
  }, (props) => react$1.default.createElement(PluginDetailSection, Object.assign({}, props)));
413266
+ ctx.effect(() => {
413267
+ const dispose = registerSlotSafely(ctx, {
413268
+ name: "shell.overlay",
413269
+ id: "edrv-quick-menu",
413270
+ order: 120,
413271
+ label: "快捷菜单"
413272
+ }, () => react$1.default.createElement(QuickMenuLauncher, { host: quickMenuHost }));
413273
+ return () => {
413274
+ dispose?.();
413275
+ quickMenuHost.store.flushLauncher();
413276
+ };
413277
+ }, "vscode-mode: quick menu launcher");
413278
+ ctx.effect(() => subscribeScope(ctx, () => {
413279
+ quickMenuHost.store.refreshBindings(true);
413280
+ }), "vscode-mode: quick menu scope refresh");
410002
413281
  ctx.effect(() => () => {
410003
413282
  setPanelImport();
410004
413283
  try {