dsh-vscode-mode 0.22.0 → 0.23.0

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";
@@ -3753,6 +3764,39 @@ window.__ModuleLoader__.load({
3753
3764
  /** 待审条目在面板列表里的 id 前缀;客户端与 Host 共用这一份口径。 */
3754
3765
  const PENDING_PREFIX = "pending:";
3755
3766
  //#endregion
3767
+ //#region src/shared/pathKey.ts
3768
+ /**
3769
+ * 路径比较口径:Host 与客户端共用同一套「远端 / 盘符 / 相对路径」判定,只在平台折叠上分工。
3770
+ * @author ddj 2026年10月30号
3771
+ */
3772
+ /** 远端仓库身份:`scheme://...` 区分大小写,绝不能折叠。 */
3773
+ const REMOTE = /^[a-z]+:\/\//i;
3774
+ /** 盘符绝对路径:大小写不敏感与宿主平台无关,两边都必须折叠。 */
3775
+ const DRIVE = /^[a-z]:\//i;
3776
+ /**
3777
+ * @author ddj 2026年10月30号
3778
+ * @description: 统一分隔符为 `/`,其余字符原样保留。
3779
+ * @param path 原始路径
3780
+ * @returns 反斜杠已归一化的路径
3781
+ */
3782
+ function unifiedPath(path) {
3783
+ return path.replace(/\\/g, "/");
3784
+ }
3785
+ /**
3786
+ * @author ddj 2026年10月30号
3787
+ * @description: 生成路径比较键。盘符路径恒折叠大小写;其余路径是否折叠由调用方按运行平台决定
3788
+ * (Host 在 Windows 上折叠,客户端不在宿主进程里,取 false)。
3789
+ * @param path 原始路径
3790
+ * @param foldCase 非盘符路径是否折叠大小写
3791
+ * @returns 比较键
3792
+ */
3793
+ function pathKeyFor(path, foldCase) {
3794
+ const unified = unifiedPath(path);
3795
+ if (REMOTE.test(unified)) return unified;
3796
+ if (DRIVE.test(unified)) return unified.toLowerCase();
3797
+ return foldCase ? unified.toLowerCase() : unified;
3798
+ }
3799
+ //#endregion
3756
3800
  //#region src/shared/diff.ts
3757
3801
  /** 统一取对应 hunk;callHunk 只用于旧记录缺失 hunks 时兼容。 */
3758
3802
  function preciseHunk(record, idx) {
@@ -4050,11 +4094,15 @@ window.__ModuleLoader__.load({
4050
4094
  //#endregion
4051
4095
  //#region src/client/state/diffSummary.ts
4052
4096
  /** 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. */
4097
+ /**
4098
+ * @public
4099
+ * @author ddj 2026年10月26号
4100
+ * @description: 客户端路径比较口径:浏览器里没有宿主平台信息,非盘符路径不折叠大小写。
4101
+ * @param path 文件身份或相对路径
4102
+ * @returns 比较键
4103
+ */
4054
4104
  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;
4105
+ return pathKeyFor(path, false);
4058
4106
  }
4059
4107
  /** @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
4108
  function officialPath(path, page) {
@@ -87762,7 +87810,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
87762
87810
  bottom: "top",
87763
87811
  top: "bottom"
87764
87812
  };
87765
- function clamp$3(start, value, end) {
87813
+ function clamp$4(start, value, end) {
87766
87814
  return max$1(start, min$1(value, end));
87767
87815
  }
87768
87816
  function evaluate$1(value, param) {
@@ -88062,7 +88110,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
88062
88110
  const maxPadding = min$1(paddingObject[maxProp], largestPossiblePadding);
88063
88111
  const max = clientSize - arrowDimensions[length] - maxPadding;
88064
88112
  const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;
88065
- const offset = clamp$3(minPadding, center, max);
88113
+ const offset = clamp$4(minPadding, center, max);
88066
88114
  const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center !== offset && rects.reference[length] / 2 - (center < minPadding ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;
88067
88115
  const alignmentOffset = shouldAddOffset ? center < minPadding ? center - minPadding : center - max : 0;
88068
88116
  return {
@@ -88281,7 +88329,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
88281
88329
  const mainAxis = getOppositeAxis(crossAxis);
88282
88330
  let mainAxisCoord = coords[mainAxis];
88283
88331
  let crossAxisCoord = coords[crossAxis];
88284
- const clampCoord = (axis, coord) => clamp$3(coord + overflow[axis === "y" ? "top" : "left"], coord, coord - overflow[axis === "y" ? "bottom" : "right"]);
88332
+ const clampCoord = (axis, coord) => clamp$4(coord + overflow[axis === "y" ? "top" : "left"], coord, coord - overflow[axis === "y" ? "bottom" : "right"]);
88285
88333
  if (checkMainAxis) mainAxisCoord = clampCoord(mainAxis, mainAxisCoord);
88286
88334
  if (checkCrossAxis) crossAxisCoord = clampCoord(crossAxis, crossAxisCoord);
88287
88335
  const limitedCoords = limiter.fn({
@@ -94042,7 +94090,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
94042
94090
  const LINE_DELTA_PIXEL_SIZE = 16;
94043
94091
  const PAGE_DELTA_PIXEL_SIZE = 800;
94044
94092
  const ZOOM_RATIO_PRECISION = 100;
94045
- function clamp$1$3(value, min, max) {
94093
+ function clamp$1$2(value, min, max) {
94046
94094
  return Math.min(max, Math.max(min, value));
94047
94095
  }
94048
94096
  function normalizeWheelDelta(event) {
@@ -94057,10 +94105,10 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
94057
94105
  function getNextWheelZoomRatio(currentRatio, event) {
94058
94106
  const validCurrentRatio = Number.isFinite(currentRatio) ? currentRatio : 1;
94059
94107
  const normalizedDelta = normalizeWheelDelta(event);
94060
- if (normalizedDelta === 0) return clamp$1$3(validCurrentRatio, MIN_ZOOM_RATIO, MAX_ZOOM_RATIO);
94108
+ if (normalizedDelta === 0) return clamp$1$2(validCurrentRatio, MIN_ZOOM_RATIO, MAX_ZOOM_RATIO);
94061
94109
  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);
94110
+ const ratioDelta = clamp$1$2(-normalizedDelta * PIXEL_DELTA_TO_ZOOM_RATIO, -maxStep, maxStep);
94111
+ return clamp$1$2(Math.round((validCurrentRatio + ratioDelta) * ZOOM_RATIO_PRECISION) / ZOOM_RATIO_PRECISION, MIN_ZOOM_RATIO, MAX_ZOOM_RATIO);
94064
94112
  }
94065
94113
  const BASE_OBJECT_ARRAY = [
94066
94114
  "top",
@@ -94825,14 +94873,14 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
94825
94873
  }
94826
94874
  };
94827
94875
  const DRAWINGML_GLOW_BLUR_SCALE = .5;
94828
- function clamp$4(value, min, max) {
94876
+ function clamp$3(value, min, max) {
94829
94877
  return Math.max(min, Math.min(max, value));
94830
94878
  }
94831
94879
  function colorWithOpacity(color, opacity) {
94832
94880
  if (opacity === void 0) return color;
94833
94881
  const parsedColor = new ColorKit(color);
94834
94882
  if (!parsedColor.isValid) return color;
94835
- return parsedColor.setAlpha(parsedColor.getAlpha() * clamp$4(opacity, 0, 1)).toRgbString();
94883
+ return parsedColor.setAlpha(parsedColor.getAlpha() * clamp$3(opacity, 0, 1)).toRgbString();
94836
94884
  }
94837
94885
  function isTransparentColor(color) {
94838
94886
  const parsedColor = new ColorKit(color);
@@ -145676,10 +145724,10 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
145676
145724
  var require_useMergeRef = /* @__PURE__ */ __commonJSMin(((exports) => {
145677
145725
  Object.defineProperty(exports, "__esModule", { value: true });
145678
145726
  exports.useMergeRefs = void 0;
145679
- var React$65 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports)).__importStar(require("react"));
145727
+ var React$68 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports)).__importStar(require("react"));
145680
145728
  var assignRef_1 = require_assignRef();
145681
145729
  var useRef_1 = require_useRef();
145682
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React$65.useLayoutEffect : React$65.useEffect;
145730
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React$68.useLayoutEffect : React$68.useEffect;
145683
145731
  var currentValues = /* @__PURE__ */ new WeakMap();
145684
145732
  /**
145685
145733
  * Merges two or more refs together providing a single interface to set their value
@@ -145959,7 +146007,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
145959
146007
  Object.defineProperty(exports, "__esModule", { value: true });
145960
146008
  exports.sidecar = void 0;
145961
146009
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
145962
- var React$64 = tslib_1.__importStar(require("react"));
146010
+ var React$67 = tslib_1.__importStar(require("react"));
145963
146011
  var hook_1 = require_hook$1();
145964
146012
  function sidecar(importer, errorComponent) {
145965
146013
  var ErrorCase = function() {
@@ -145968,7 +146016,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
145968
146016
  return function Sidecar(props) {
145969
146017
  var _a = (0, hook_1.useSidecar)(importer, props.sideCar), Car = _a[0];
145970
146018
  if (_a[1] && errorComponent) return ErrorCase;
145971
- return Car ? React$64.createElement(Car, tslib_1.__assign({}, props)) : null;
146019
+ return Car ? React$67.createElement(Car, tslib_1.__assign({}, props)) : null;
145972
146020
  };
145973
146021
  }
145974
146022
  exports.sidecar = sidecar;
@@ -146083,7 +146131,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146083
146131
  Object.defineProperty(exports, "__esModule", { value: true });
146084
146132
  exports.renderCar = void 0;
146085
146133
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
146086
- var React$63 = tslib_1.__importStar(require("react"));
146134
+ var React$66 = tslib_1.__importStar(require("react"));
146087
146135
  var react_1 = require("react");
146088
146136
  function renderCar(WrappedComponent, defaults) {
146089
146137
  function State(_a) {
@@ -146096,9 +146144,9 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146096
146144
  });
146097
146145
  return null;
146098
146146
  }, []);
146099
- return React$63.createElement(WrappedComponent, tslib_1.__assign({}, props, { children: renderTarget }));
146147
+ return React$66.createElement(WrappedComponent, tslib_1.__assign({}, props, { children: renderTarget }));
146100
146148
  }
146101
- var Children = React$63.memo(function(_a) {
146149
+ var Children = React$66.memo(function(_a) {
146102
146150
  var stateRef = _a.stateRef, defaultState = _a.defaultState, children = _a.children;
146103
146151
  var _b = (0, react_1.useState)(defaultState.current), state = _b[0], setState = _b[1];
146104
146152
  (0, react_1.useEffect)(function() {
@@ -146109,14 +146157,14 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146109
146157
  return true;
146110
146158
  });
146111
146159
  return function Combiner(props) {
146112
- var defaultState = React$63.useRef(defaults(props));
146113
- var ref = React$63.useRef(function(state) {
146160
+ var defaultState = React$66.useRef(defaults(props));
146161
+ var ref = React$66.useRef(function(state) {
146114
146162
  return defaultState.current = state;
146115
146163
  });
146116
- return React$63.createElement(React$63.Fragment, null, React$63.createElement(State, {
146164
+ return React$66.createElement(React$66.Fragment, null, React$66.createElement(State, {
146117
146165
  stateRef: ref,
146118
146166
  props
146119
- }), React$63.createElement(Children, {
146167
+ }), React$66.createElement(Children, {
146120
146168
  stateRef: ref,
146121
146169
  defaultState,
146122
146170
  children: props.children
@@ -146131,13 +146179,13 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146131
146179
  Object.defineProperty(exports, "__esModule", { value: true });
146132
146180
  exports.exportSidecar = void 0;
146133
146181
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
146134
- var React$62 = tslib_1.__importStar(require("react"));
146182
+ var React$65 = tslib_1.__importStar(require("react"));
146135
146183
  var SideCar = function(_a) {
146136
146184
  var sideCar = _a.sideCar, rest = tslib_1.__rest(_a, ["sideCar"]);
146137
146185
  if (!sideCar) throw new Error("Sidecar: please provide `sideCar` property to import the right car");
146138
146186
  var Target = sideCar.read();
146139
146187
  if (!Target) throw new Error("Sidecar medium not found");
146140
- return React$62.createElement(Target, tslib_1.__assign({}, rest));
146188
+ return React$65.createElement(Target, tslib_1.__assign({}, rest));
146141
146189
  };
146142
146190
  SideCar.isSideCarExport = true;
146143
146191
  function exportSidecar(medium, exported) {
@@ -146213,7 +146261,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146213
146261
  Object.defineProperty(exports, "__esModule", { value: true });
146214
146262
  exports.RemoveScroll = void 0;
146215
146263
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
146216
- var React$61 = tslib_1.__importStar(require("react"));
146264
+ var React$64 = tslib_1.__importStar(require("react"));
146217
146265
  var constants_1 = require_constants();
146218
146266
  var use_callback_ref_1 = require_es5$5();
146219
146267
  var medium_1 = require_medium();
@@ -146221,9 +146269,9 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146221
146269
  /**
146222
146270
  * Removes scrollbar from the page and contain the scroll within the Lock
146223
146271
  */
146224
- var RemoveScroll = React$61.forwardRef(function(props, parentRef) {
146225
- var ref = React$61.useRef(null);
146226
- var _a = React$61.useState({
146272
+ var RemoveScroll = React$64.forwardRef(function(props, parentRef) {
146273
+ var ref = React$64.useRef(null);
146274
+ var _a = React$64.useState({
146227
146275
  onScrollCapture: nothing,
146228
146276
  onWheelCapture: nothing,
146229
146277
  onTouchMoveCapture: nothing
@@ -146246,7 +146294,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146246
146294
  var SideCar = sideCar;
146247
146295
  var containerRef = (0, use_callback_ref_1.useMergeRefs)([ref, parentRef]);
146248
146296
  var containerProps = tslib_1.__assign(tslib_1.__assign({}, rest), callbacks);
146249
- return React$61.createElement(React$61.Fragment, null, enabled && React$61.createElement(SideCar, {
146297
+ return React$64.createElement(React$64.Fragment, null, enabled && React$64.createElement(SideCar, {
146250
146298
  sideCar: medium_1.effectCar,
146251
146299
  removeScrollBar,
146252
146300
  shards,
@@ -146257,7 +146305,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146257
146305
  allowPinchZoom: !!allowPinchZoom,
146258
146306
  lockRef: ref,
146259
146307
  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, {
146308
+ }), 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
146309
  className,
146262
146310
  ref: containerRef
146263
146311
  }), children));
@@ -146336,7 +146384,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146336
146384
  var require_hook = /* @__PURE__ */ __commonJSMin(((exports) => {
146337
146385
  Object.defineProperty(exports, "__esModule", { value: true });
146338
146386
  exports.styleHookSingleton = void 0;
146339
- var React$60 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports)).__importStar(require("react"));
146387
+ var React$63 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports)).__importStar(require("react"));
146340
146388
  var singleton_1 = require_singleton();
146341
146389
  /**
146342
146390
  * creates a hook to control style singleton
@@ -146350,7 +146398,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146350
146398
  var styleHookSingleton = function() {
146351
146399
  var sheet = (0, singleton_1.stylesheetSingleton)();
146352
146400
  return function(styles, isDynamic) {
146353
- React$60.useEffect(function() {
146401
+ React$63.useEffect(function() {
146354
146402
  sheet.add(styles);
146355
146403
  return function() {
146356
146404
  sheet.remove();
@@ -146455,7 +146503,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146455
146503
  var require_component = /* @__PURE__ */ __commonJSMin(((exports) => {
146456
146504
  Object.defineProperty(exports, "__esModule", { value: true });
146457
146505
  exports.RemoveScrollBar = exports.useLockAttribute = exports.lockAttribute = void 0;
146458
- var React$59 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports)).__importStar(require("react"));
146506
+ var React$62 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports)).__importStar(require("react"));
146459
146507
  var react_style_singleton_1 = require_es5$2();
146460
146508
  var constants_1 = require_constants();
146461
146509
  var utils_1 = require_utils();
@@ -146475,7 +146523,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146475
146523
  return isFinite(counter) ? counter : 0;
146476
146524
  };
146477
146525
  var useLockAttribute = function() {
146478
- React$59.useEffect(function() {
146526
+ React$62.useEffect(function() {
146479
146527
  document.body.setAttribute(exports.lockAttribute, (getCurrentUseCounter() + 1).toString());
146480
146528
  return function() {
146481
146529
  var newCounter = getCurrentUseCounter() - 1;
@@ -146491,10 +146539,10 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146491
146539
  var RemoveScrollBar = function(_a) {
146492
146540
  var noRelative = _a.noRelative, noImportant = _a.noImportant, _b = _a.gapMode, gapMode = _b === void 0 ? "margin" : _b;
146493
146541
  (0, exports.useLockAttribute)();
146494
- var gap = React$59.useMemo(function() {
146542
+ var gap = React$62.useMemo(function() {
146495
146543
  return (0, utils_1.getGapWidth)(gapMode);
146496
146544
  }, [gapMode]);
146497
- return React$59.createElement(Style, { styles: getStyles(gap, !noRelative, gapMode, !noImportant ? "!important" : "") });
146545
+ return React$62.createElement(Style, { styles: getStyles(gap, !noRelative, gapMode, !noImportant ? "!important" : "") });
146498
146546
  };
146499
146547
  exports.RemoveScrollBar = RemoveScrollBar;
146500
146548
  }));
@@ -146656,7 +146704,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146656
146704
  Object.defineProperty(exports, "__esModule", { value: true });
146657
146705
  exports.RemoveScrollSideCar = exports.getDeltaXY = exports.getTouchXY = void 0;
146658
146706
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
146659
- var React$58 = tslib_1.__importStar(require("react"));
146707
+ var React$61 = tslib_1.__importStar(require("react"));
146660
146708
  var react_remove_scroll_bar_1 = require_es5$1();
146661
146709
  var react_style_singleton_1 = require_es5$2();
146662
146710
  var aggresiveCapture_1 = require_aggresiveCapture();
@@ -146681,16 +146729,16 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146681
146729
  var idCounter = 0;
146682
146730
  var lockStack = [];
146683
146731
  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() {
146732
+ var shouldPreventQueue = React$61.useRef([]);
146733
+ var touchStartRef = React$61.useRef([0, 0]);
146734
+ var activeAxis = React$61.useRef();
146735
+ var id = React$61.useState(idCounter++)[0];
146736
+ var Style = React$61.useState(react_style_singleton_1.styleSingleton)[0];
146737
+ var lastProps = React$61.useRef(props);
146738
+ React$61.useEffect(function() {
146691
146739
  lastProps.current = props;
146692
146740
  }, [props]);
146693
- React$58.useEffect(function() {
146741
+ React$61.useEffect(function() {
146694
146742
  if (props.inert) {
146695
146743
  document.body.classList.add("block-interactivity-".concat(id));
146696
146744
  var allow_1 = tslib_1.__spreadArray([props.lockRef.current], (props.shards || []).map(extractRef), true).filter(Boolean);
@@ -146709,7 +146757,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146709
146757
  props.lockRef.current,
146710
146758
  props.shards
146711
146759
  ]);
146712
- var shouldCancelEvent = React$58.useCallback(function(event, parent) {
146760
+ var shouldCancelEvent = React$61.useCallback(function(event, parent) {
146713
146761
  if ("touches" in event && event.touches.length === 2 || event.type === "wheel" && event.ctrlKey) return !lastProps.current.allowPinchZoom;
146714
146762
  var touch = (0, exports.getTouchXY)(event);
146715
146763
  var touchStart = touchStartRef.current;
@@ -146735,7 +146783,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146735
146783
  var cancelingAxis = activeAxis.current || currentAxis;
146736
146784
  return (0, handleScroll_1.handleScroll)(cancelingAxis, parent, event, cancelingAxis === "h" ? deltaX : deltaY, true);
146737
146785
  }, []);
146738
- var shouldPrevent = React$58.useCallback(function(_event) {
146786
+ var shouldPrevent = React$61.useCallback(function(_event) {
146739
146787
  var event = _event;
146740
146788
  if (!lockStack.length || lockStack[lockStack.length - 1] !== Style) return;
146741
146789
  var delta = "deltaY" in event ? (0, exports.getDeltaXY)(event) : (0, exports.getTouchXY)(event);
@@ -146755,7 +146803,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146755
146803
  }
146756
146804
  }
146757
146805
  }, []);
146758
- var shouldCancel = React$58.useCallback(function(name, delta, target, should) {
146806
+ var shouldCancel = React$61.useCallback(function(name, delta, target, should) {
146759
146807
  var event = {
146760
146808
  name,
146761
146809
  delta,
@@ -146770,17 +146818,17 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146770
146818
  });
146771
146819
  }, 1);
146772
146820
  }, []);
146773
- var scrollTouchStart = React$58.useCallback(function(event) {
146821
+ var scrollTouchStart = React$61.useCallback(function(event) {
146774
146822
  touchStartRef.current = (0, exports.getTouchXY)(event);
146775
146823
  activeAxis.current = void 0;
146776
146824
  }, []);
146777
- var scrollWheel = React$58.useCallback(function(event) {
146825
+ var scrollWheel = React$61.useCallback(function(event) {
146778
146826
  shouldCancel(event.type, (0, exports.getDeltaXY)(event), event.target, shouldCancelEvent(event, props.lockRef.current));
146779
146827
  }, []);
146780
- var scrollTouchMove = React$58.useCallback(function(event) {
146828
+ var scrollTouchMove = React$61.useCallback(function(event) {
146781
146829
  shouldCancel(event.type, (0, exports.getTouchXY)(event), event.target, shouldCancelEvent(event, props.lockRef.current));
146782
146830
  }, []);
146783
- React$58.useEffect(function() {
146831
+ React$61.useEffect(function() {
146784
146832
  lockStack.push(Style);
146785
146833
  props.setCallbacks({
146786
146834
  onScrollCapture: scrollWheel,
@@ -146800,7 +146848,7 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146800
146848
  };
146801
146849
  }, []);
146802
146850
  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, {
146851
+ 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
146852
  noRelative: props.noRelative,
146805
146853
  gapMode: props.gapMode
146806
146854
  }) : null);
@@ -146832,11 +146880,11 @@ For more info please visit our website: https://redi.wzhu.dev/en-US/docs/faq#imp
146832
146880
  var require_Combination = /* @__PURE__ */ __commonJSMin(((exports) => {
146833
146881
  Object.defineProperty(exports, "__esModule", { value: true });
146834
146882
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
146835
- var React$57 = tslib_1.__importStar(require("react"));
146883
+ var React$60 = tslib_1.__importStar(require("react"));
146836
146884
  var UI_1 = require_UI();
146837
146885
  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, {
146886
+ var ReactRemoveScroll = React$60.forwardRef(function(props, ref) {
146887
+ return React$60.createElement(UI_1.RemoveScroll, tslib_1.__assign({}, props, {
146840
146888
  ref,
146841
146889
  sideCar: sidecar_1.default
146842
146890
  }));
@@ -194844,9 +194892,9 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
194844
194892
  movedRange: params.sourceRange
194845
194893
  };
194846
194894
  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);
194895
+ const startOffset = clamp$1$1(params.sourceRange.startOffset, 0, dataStreamLength);
194896
+ const endOffset = clamp$1$1(params.sourceRange.endOffset, startOffset, dataStreamLength);
194897
+ const targetOffset = clamp$1$1(params.targetOffset, 0, dataStreamLength);
194850
194898
  if (startOffset === endOffset || targetOffset >= startOffset && targetOffset <= endOffset) return {
194851
194899
  nextDocumentData,
194852
194900
  movedRange: {
@@ -194890,9 +194938,9 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
194890
194938
  }
194891
194939
  function buildMoveBodyTextAction(previousBody, nextBody, move) {
194892
194940
  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);
194941
+ const startOffset = clamp$1$1(move.sourceRange.startOffset, 0, dataStreamLength);
194942
+ const endOffset = clamp$1$1(move.sourceRange.endOffset, startOffset, dataStreamLength);
194943
+ const targetOffset = clamp$1$1(move.targetOffset, 0, dataStreamLength);
194896
194944
  const moveLength = endOffset - startOffset;
194897
194945
  if (moveLength <= 0 || targetOffset >= startOffset && targetOffset <= endOffset) return null;
194898
194946
  const textX = new TextX();
@@ -195050,7 +195098,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
195050
195098
  if (targetOffset > endOffset && index >= endOffset && index <= targetOffset) return index - moveLength;
195051
195099
  return remapIndexAfterMove(index, startOffset, endOffset, targetOffset, insertOffset, moveLength);
195052
195100
  }
195053
- function clamp$1(value, min, max) {
195101
+ function clamp$1$1(value, min, max) {
195054
195102
  return Math.min(Math.max(value, min), max);
195055
195103
  }
195056
195104
  const HorizontalLineCommand = {
@@ -219192,7 +219240,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
219192
219240
  function ceil$1(x) {
219193
219241
  return finalise(x = new this(x), x.e + 1, 2);
219194
219242
  }
219195
- function clamp(x, min, max) {
219243
+ function clamp$1(x, min, max) {
219196
219244
  return new this(x).clamp(min, max);
219197
219245
  }
219198
219246
  function config(obj) {
@@ -219352,7 +219400,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
219352
219400
  Decimal.atan2 = atan2;
219353
219401
  Decimal.cbrt = cbrt;
219354
219402
  Decimal.ceil = ceil$1;
219355
- Decimal.clamp = clamp;
219403
+ Decimal.clamp = clamp$1;
219356
219404
  Decimal.cos = cos;
219357
219405
  Decimal.cosh = cosh;
219358
219406
  Decimal.div = div;
@@ -344164,7 +344212,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
344164
344212
  };
344165
344213
  return nextTick;
344166
344214
  };
344167
- const TYPE_OPTIONS = [
344215
+ const TYPE_OPTIONS$1 = [
344168
344216
  {
344169
344217
  label: "sheets-numfmt-ui.general",
344170
344218
  component: GeneralPanel
@@ -344208,7 +344256,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
344208
344256
  const [type, setType] = (0, react.useState)(findDefaultType);
344209
344257
  const [key, setKey] = (0, react.useState)(() => `${row}_${col}_${defaultPattern}`);
344210
344258
  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;
344259
+ 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
344260
  function findDefaultType() {
344213
344261
  return [
344214
344262
  isGeneralPanel,
@@ -344216,9 +344264,9 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
344216
344264
  isCurrencyPanel,
344217
344265
  isDatePanel,
344218
344266
  isThousandthPercentilePanel
344219
- ].reduce((pre, curFn, index) => pre || (curFn(defaultPattern) ? TYPE_OPTIONS[index].label : ""), "") || "sheets-numfmt-ui.customFormat";
344267
+ ].reduce((pre, curFn, index) => pre || (curFn(defaultPattern) ? TYPE_OPTIONS$1[index].label : ""), "") || "sheets-numfmt-ui.customFormat";
344220
344268
  }
344221
- const selectOptions = TYPE_OPTIONS.map((option) => ({
344269
+ const selectOptions = TYPE_OPTIONS$1.map((option) => ({
344222
344270
  label: localeService.t(option.label),
344223
344271
  value: option.label
344224
344272
  }));
@@ -382993,7 +383041,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
382993
383041
  //#endregion
382994
383042
  //#region src/client/sessionScope.ts
382995
383043
  /** 对象窄化(非对象返回 undefined,供后续安全取字段)。 */
382996
- function asRecord(value) {
383044
+ function asRecord$1(value) {
382997
383045
  return value && typeof value === "object" ? value : void 0;
382998
383046
  }
382999
383047
  /** 非空字符串取值(其他类型一律视为缺失)。 */
@@ -383010,9 +383058,9 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
383010
383058
  * @returns sessionId,或 undefined
383011
383059
  */
383012
383060
  function uiSessionIdOf(snapshot) {
383013
- const binding = asRecord(snapshot);
383061
+ const binding = asRecord$1(snapshot);
383014
383062
  if (!binding) return void 0;
383015
- return strOf(binding.key) ?? strOf(asRecord(binding.value)?.key);
383063
+ return strOf(binding.key) ?? strOf(asRecord$1(binding.value)?.key);
383016
383064
  }
383017
383065
  /**
383018
383066
  * 从 `sessions.list` 快照读出 sessionId。
@@ -383023,14 +383071,14 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
383023
383071
  * @returns sessionId,或 undefined
383024
383072
  */
383025
383073
  function listSessionId(list) {
383026
- const snap = asRecord(list);
383074
+ const snap = asRecord$1(list);
383027
383075
  if (!snap) return void 0;
383028
383076
  const current = strOf(snap.current);
383029
383077
  if (current) return current;
383030
- const byId = asRecord(snap.byId);
383078
+ const byId = asRecord$1(snap.byId);
383031
383079
  if (!byId) return void 0;
383032
383080
  for (const [id, row] of Object.entries(byId)) {
383033
- const mainView = asRecord(asRecord(row)?.retainedBy)?.mainView;
383081
+ const mainView = asRecord$1(asRecord$1(row)?.retainedBy)?.mainView;
383034
383082
  if (typeof mainView === "number" && mainView > 0) return id;
383035
383083
  }
383036
383084
  }
@@ -383044,7 +383092,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
383044
383092
  function pickSessionScope(sources) {
383045
383093
  const sessionId = uiSessionIdOf(sources.uiSession) ?? listSessionId(sources.list) ?? strOf(sources.fallbackSessionId);
383046
383094
  if (!sessionId) return {};
383047
- const cwd = strOf(asRecord(asRecord(asRecord(sources.list)?.byId)?.[sessionId])?.cwd);
383095
+ const cwd = strOf(asRecord$1(asRecord$1(asRecord$1(sources.list)?.byId)?.[sessionId])?.cwd);
383048
383096
  return cwd ? {
383049
383097
  sessionId,
383050
383098
  cwd
@@ -383060,7 +383108,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
383060
383108
  }
383061
383109
  /** 取 `uiSession.current` 快照(服务或订阅面缺失时返回 undefined)。 */
383062
383110
  function uiCurrentSnapshot(ctx) {
383063
- const current = asRecord(asRecord(safeGet(ctx, "uiSession"))?.current);
383111
+ const current = asRecord$1(asRecord$1(safeGet(ctx, "uiSession"))?.current);
383064
383112
  const getSnapshot = current?.getSnapshot;
383065
383113
  if (typeof getSnapshot !== "function") return void 0;
383066
383114
  try {
@@ -383071,7 +383119,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
383071
383119
  }
383072
383120
  /** 取 `sessions.list` 快照(服务缺失时返回 undefined)。 */
383073
383121
  function listSnapshot(ctx) {
383074
- const list = asRecord(asRecord(safeGet(ctx, "sessions"))?.list);
383122
+ const list = asRecord$1(asRecord$1(safeGet(ctx, "sessions"))?.list);
383075
383123
  const getSnapshot = list?.getSnapshot;
383076
383124
  if (typeof getSnapshot !== "function") return void 0;
383077
383125
  try {
@@ -383106,15 +383154,15 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
383106
383154
  function subscribeScope(ctx, listener) {
383107
383155
  const disposers = [];
383108
383156
  const attach = (owner) => {
383109
- const subscribe = asRecord(owner)?.subscribe;
383157
+ const subscribe = asRecord$1(owner)?.subscribe;
383110
383158
  if (typeof subscribe !== "function") return;
383111
383159
  try {
383112
383160
  const disposer = subscribe.call(owner, listener);
383113
383161
  if (typeof disposer === "function") disposers.push(disposer);
383114
383162
  } catch {}
383115
383163
  };
383116
- attach(asRecord(asRecord(safeGet(ctx, "uiSession"))?.current));
383117
- attach(asRecord(asRecord(safeGet(ctx, "sessions"))?.list));
383164
+ attach(asRecord$1(asRecord$1(safeGet(ctx, "uiSession"))?.current));
383165
+ attach(asRecord$1(asRecord$1(safeGet(ctx, "sessions"))?.list));
383118
383166
  return () => {
383119
383167
  for (const disposer of disposers.splice(0)) try {
383120
383168
  disposer();
@@ -403869,6 +403917,3080 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
403869
403917
  }
403870
403918
  return ClaimRouter;
403871
403919
  }
403920
+ /** 提示词长度上限。 */
403921
+ const QUICK_MENU_PROMPT_CAP = 8192;
403922
+ /** 单条规则内联正文上限。 */
403923
+ const QUICK_MENU_RULE_CAP = 8192;
403924
+ /** 技能名合法形态。 */
403925
+ const QUICK_MENU_SKILL_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
403926
+ /** 未命名项兜底名。 */
403927
+ const FALLBACK_NAME = "未命名";
403928
+ /**
403929
+ * 数值夹取(NaN/非有限回落到 fallback)。
403930
+ * @author ddj 2026年10月30号
403931
+ * @param value 原始值
403932
+ * @param min 下限
403933
+ * @param max 上限
403934
+ * @param fallback 非法值兜底
403935
+ * @returns 夹取后的数值
403936
+ */
403937
+ function clampNumber(value, min, max, fallback) {
403938
+ const number = typeof value === "number" && Number.isFinite(value) ? value : fallback;
403939
+ if (number < min) return min;
403940
+ if (number > max) return max;
403941
+ return number;
403942
+ }
403943
+ /**
403944
+ * 取对象形态(非对象/数组返回 null)。
403945
+ * @author ddj 2026年10月30号
403946
+ * @param value 原始值
403947
+ * @returns 普通对象或 null
403948
+ */
403949
+ function asRecord(value) {
403950
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
403951
+ return value;
403952
+ }
403953
+ /**
403954
+ * 取字符串(非字符串回落空串)。
403955
+ * @author ddj 2026年10月30号
403956
+ * @param value 原始值
403957
+ * @returns 字符串
403958
+ */
403959
+ function asString(value) {
403960
+ return typeof value === "string" ? value : "";
403961
+ }
403962
+ /**
403963
+ * 文本截断(超长补省略号)。
403964
+ * @author ddj 2026年10月30号
403965
+ * @param text 原文
403966
+ * @param cap 上限字符数
403967
+ * @returns 截断后文本
403968
+ */
403969
+ function cutText(text, cap) {
403970
+ if (text.length <= cap) return text;
403971
+ return text.slice(0, Math.max(0, cap - 1)) + "…";
403972
+ }
403973
+ /**
403974
+ * 归一化工作区路径(统一分隔符、去尾分隔符、win32 转小写),用于比较而非展示。
403975
+ * @author ddj 2026年10月30号
403976
+ * @param path 原始路径
403977
+ * @returns 归一化路径
403978
+ */
403979
+ function normalizePath(path) {
403980
+ const unified = path.trim().replace(/\//g, "\\").replace(/\\+$/, "");
403981
+ if (unified === "") return "";
403982
+ return unified.toLowerCase();
403983
+ }
403984
+ /**
403985
+ * 判断一条限定工作区列表是否覆盖当前工作区(含子目录,按路径边界匹配)。
403986
+ * @author ddj 2026年10月30号
403987
+ * @param paths 限定列表;null 表示全部工作区
403988
+ * @param cwd 当前工作区路径;空串表示未知(视为可见)
403989
+ * @returns 是否可见
403990
+ */
403991
+ function matchWorkspace(paths, cwd) {
403992
+ if (paths === null || paths === void 0) return true;
403993
+ if (paths.length === 0) return true;
403994
+ const normalizedCwd = normalizePath(cwd ?? "");
403995
+ if (normalizedCwd === "") return true;
403996
+ return paths.some((path) => {
403997
+ const normalized = normalizePath(path);
403998
+ if (normalized === "") return false;
403999
+ return normalizedCwd === normalized || normalizedCwd.startsWith(normalized + "\\");
404000
+ });
404001
+ }
404002
+ /**
404003
+ * 默认文档(首次打开菜单时的初始状态)。
404004
+ * @author ddj 2026年10月30号
404005
+ * @returns 默认文档
404006
+ */
404007
+ function defaultDoc() {
404008
+ return {
404009
+ version: 1,
404010
+ revision: 0,
404011
+ updatedAt: "",
404012
+ enabled: true,
404013
+ clickAction: "send",
404014
+ maxDepth: 3,
404015
+ launcher: {
404016
+ x: null,
404017
+ y: null
404018
+ },
404019
+ items: []
404020
+ };
404021
+ }
404022
+ /**
404023
+ * 生成新菜单项 id(类型前缀 + 时间 + 随机,跨会话不重复)。
404024
+ * @author ddj 2026年10月30号
404025
+ * @param type 项类型
404026
+ * @returns 新 id
404027
+ */
404028
+ function newQuickId(type) {
404029
+ const prefix = type === "group" ? "g" : "b";
404030
+ const random = Math.random().toString(36).slice(2, 8);
404031
+ return prefix + "_" + Date.now().toString(36) + random;
404032
+ }
404033
+ /**
404034
+ * 归一化限定工作区列表(去空、去重、归一化;空结果回落到 null=全部)。
404035
+ * @author ddj 2026年10月30号
404036
+ * @param raw 原始值
404037
+ * @returns 限定列表或 null
404038
+ */
404039
+ function normalizeWorkspaces(raw) {
404040
+ if (!Array.isArray(raw)) return null;
404041
+ const out = [];
404042
+ for (const entry of raw) {
404043
+ if (typeof entry !== "string") continue;
404044
+ const normalized = normalizePath(entry);
404045
+ if (normalized === "") continue;
404046
+ if (out.includes(normalized)) continue;
404047
+ out.push(normalized);
404048
+ }
404049
+ return out.length > 0 ? out : null;
404050
+ }
404051
+ /**
404052
+ * 绑定规则的稳定键(校验、去重、内联正文映射共用)。
404053
+ * @author ddj 2026年10月30号
404054
+ * @param binding 规则绑定
404055
+ * @returns 键
404056
+ */
404057
+ function bindingKey(binding) {
404058
+ const workspace = normalizePath(binding.workspacePath ?? "");
404059
+ return binding.scope + "|" + workspace + "|" + binding.file;
404060
+ }
404061
+ /**
404062
+ * 归一化单条规则绑定(scope/file 必填,项目规则必须带工作区)。
404063
+ * @author ddj 2026年10月30号
404064
+ * @param raw 原始值
404065
+ * @returns 绑定或 null
404066
+ */
404067
+ function normalizeRule(raw) {
404068
+ const record = asRecord(raw);
404069
+ if (!record) return null;
404070
+ const file = asString(record.file).trim();
404071
+ if (file === "") return null;
404072
+ const scope = record.scope === "project" ? "project" : "user";
404073
+ const binding = {
404074
+ scope,
404075
+ file
404076
+ };
404077
+ if (scope === "project") {
404078
+ const workspacePath = asString(record.workspacePath).trim();
404079
+ if (workspacePath === "") return null;
404080
+ binding.workspacePath = workspacePath;
404081
+ }
404082
+ if (record.origin === "current" || record.origin === "legacy") binding.origin = record.origin;
404083
+ return binding;
404084
+ }
404085
+ /**
404086
+ * 归一化规则绑定列表(去重 + 上限)。
404087
+ * @author ddj 2026年10月30号
404088
+ * @param raw 原始值
404089
+ * @returns 绑定列表
404090
+ */
404091
+ function normalizeRules(raw) {
404092
+ if (!Array.isArray(raw)) return [];
404093
+ const out = [];
404094
+ for (const entry of raw) {
404095
+ const binding = normalizeRule(entry);
404096
+ if (!binding) continue;
404097
+ if (out.some((existing) => bindingKey(existing) === bindingKey(binding))) continue;
404098
+ out.push(binding);
404099
+ }
404100
+ return out.slice(0, 24);
404101
+ }
404102
+ /**
404103
+ * 归一化技能名列表(只保留 kebab-case,去重 + 上限)。
404104
+ * @author ddj 2026年10月30号
404105
+ * @param raw 原始值
404106
+ * @returns 技能名列表
404107
+ */
404108
+ function normalizeSkills(raw) {
404109
+ if (!Array.isArray(raw)) return [];
404110
+ const out = [];
404111
+ for (const entry of raw) {
404112
+ if (typeof entry !== "string") continue;
404113
+ const name = entry.trim();
404114
+ if (name === "" || !QUICK_MENU_SKILL_RE.test(name)) continue;
404115
+ if (out.includes(name)) continue;
404116
+ out.push(name);
404117
+ }
404118
+ return out.slice(0, 24);
404119
+ }
404120
+ /**
404121
+ * 归一化单个菜单项(非法类型/缺 id 返回 null)。
404122
+ * @author ddj 2026年10月30号
404123
+ * @param raw 原始值
404124
+ * @returns 菜单项或 null
404125
+ */
404126
+ function normalizeItem(raw) {
404127
+ const record = asRecord(raw);
404128
+ if (!record) return null;
404129
+ const type = record.type === "group" ? "group" : "button";
404130
+ const id = asString(record.id).trim();
404131
+ if (id === "") return null;
404132
+ const name = cutText(asString(record.name).trim() || FALLBACK_NAME, 40);
404133
+ const order = clampNumber(record.order, 0, 999, 100);
404134
+ const rawParent = record.parentId;
404135
+ const base = {
404136
+ id,
404137
+ type,
404138
+ name,
404139
+ order,
404140
+ parentId: typeof rawParent === "string" && rawParent.trim() !== "" ? rawParent.trim() : null,
404141
+ workspaces: normalizeWorkspaces(record.workspaces)
404142
+ };
404143
+ if (type === "group") return {
404144
+ ...base,
404145
+ type: "group"
404146
+ };
404147
+ const button = {
404148
+ ...base,
404149
+ type: "button",
404150
+ prompt: cutText(asString(record.prompt), QUICK_MENU_PROMPT_CAP),
404151
+ skills: normalizeSkills(record.skills),
404152
+ rules: normalizeRules(record.rules),
404153
+ ruleMode: record.ruleMode === "inline" ? "inline" : "reference"
404154
+ };
404155
+ if (record.clickAction === "send" || record.clickAction === "fill") button.clickAction = record.clickAction;
404156
+ return button;
404157
+ }
404158
+ /**
404159
+ * 判断从起点上溯的父链是否会回到目标项(环检测;超深链也按非法处理)。
404160
+ * @author ddj 2026年10月30号
404161
+ * @param byId id → 项
404162
+ * @param startId 起点父 id
404163
+ * @param targetId 目标项 id
404164
+ * @returns 是否成环
404165
+ */
404166
+ function chainHas(byId, startId, targetId) {
404167
+ let current = startId;
404168
+ let guard = 0;
404169
+ while (current !== null) {
404170
+ if (current === targetId) return true;
404171
+ if (guard > 7) return true;
404172
+ const parent = byId.get(current);
404173
+ if (!parent) return false;
404174
+ current = parent.parentId;
404175
+ guard += 1;
404176
+ }
404177
+ return false;
404178
+ }
404179
+ /**
404180
+ * 修补父链问题:父不存在/父是按钮/自指/形成环时把子项上抬到最近合法祖组或顶层。
404181
+ * @author ddj 2026年10月30号
404182
+ * @param items 归一化后的项
404183
+ * @returns 修补后的项
404184
+ */
404185
+ function repairParents(items) {
404186
+ const byId = /* @__PURE__ */ new Map();
404187
+ for (const item of items) byId.set(item.id, item);
404188
+ for (const item of items) {
404189
+ let resolved = item.parentId;
404190
+ let guard = 0;
404191
+ while (resolved !== null && guard <= 7) {
404192
+ const parent = byId.get(resolved);
404193
+ if (!parent) {
404194
+ resolved = null;
404195
+ break;
404196
+ }
404197
+ if (parent.type === "group") break;
404198
+ resolved = parent.parentId;
404199
+ guard += 1;
404200
+ }
404201
+ if (resolved !== null && chainHas(byId, resolved, item.id)) resolved = null;
404202
+ item.parentId = resolved;
404203
+ }
404204
+ return items;
404205
+ }
404206
+ /**
404207
+ * 按父级分组重排优先级(order 归一为 10/20/30…,保证展示稳定且无并列)。
404208
+ * @author ddj 2026年10月30号
404209
+ * @param items 菜单项
404210
+ * @returns 同一数组(就地写入 order)
404211
+ */
404212
+ function normalizeOrder(items) {
404213
+ const groups = /* @__PURE__ */ new Map();
404214
+ for (const item of items) {
404215
+ const key = item.parentId ?? "";
404216
+ const bucket = groups.get(key);
404217
+ if (bucket) bucket.push(item);
404218
+ else groups.set(key, [item]);
404219
+ }
404220
+ for (const bucket of groups.values()) {
404221
+ bucket.sort((left, right) => {
404222
+ if (left.order !== right.order) return left.order - right.order;
404223
+ if (left.name !== right.name) return left.name < right.name ? -1 : 1;
404224
+ return left.id < right.id ? -1 : 1;
404225
+ });
404226
+ bucket.forEach((item, index) => {
404227
+ item.order = (index + 1) * 10;
404228
+ });
404229
+ }
404230
+ return items;
404231
+ }
404232
+ /**
404233
+ * 归一化整份文档(任何脏数据都收敛到可用形态,绝不抛错)。
404234
+ * @author ddj 2026年10月30号
404235
+ * @param raw 原始值(JSON.parse 结果或任意 unknown)
404236
+ * @returns 合法文档
404237
+ */
404238
+ function normalizeDoc(raw) {
404239
+ const fallback = defaultDoc();
404240
+ const record = asRecord(raw);
404241
+ if (!record) return fallback;
404242
+ const rawItems = Array.isArray(record.items) ? record.items : [];
404243
+ const seen = /* @__PURE__ */ new Set();
404244
+ const items = [];
404245
+ for (const entry of rawItems) {
404246
+ const item = normalizeItem(entry);
404247
+ if (!item || seen.has(item.id)) continue;
404248
+ seen.add(item.id);
404249
+ items.push(item);
404250
+ if (items.length >= 200) break;
404251
+ }
404252
+ const x = typeof record.launcher === "object" && record.launcher !== null ? asRecord(record.launcher) : null;
404253
+ return {
404254
+ version: 1,
404255
+ revision: Math.floor(clampNumber(record.revision, 0, Number.MAX_SAFE_INTEGER, 0)),
404256
+ updatedAt: cutText(asString(record.updatedAt), 40),
404257
+ enabled: typeof record.enabled === "boolean" ? record.enabled : true,
404258
+ clickAction: record.clickAction === "fill" ? "fill" : "send",
404259
+ maxDepth: Math.round(clampNumber(record.maxDepth, 1, 5, 3)),
404260
+ launcher: {
404261
+ x: x && typeof x.x === "number" && Number.isFinite(x.x) ? x.x : null,
404262
+ y: x && typeof x.y === "number" && Number.isFinite(x.y) ? x.y : null
404263
+ },
404264
+ items: normalizeOrder(repairParents(items))
404265
+ };
404266
+ }
404267
+ /**
404268
+ * 结构迁移入口(当前只有 v1;后续版本在此收敛旧结构)。
404269
+ * @author ddj 2026年10月30号
404270
+ * @param raw 原始值
404271
+ * @returns 合法文档
404272
+ */
404273
+ function migrateDoc(raw) {
404274
+ return normalizeDoc(raw);
404275
+ }
404276
+ /**
404277
+ * 序列化文档(host 落盘与导入导出共用)。
404278
+ * @author ddj 2026年10月30号
404279
+ * @param doc 文档
404280
+ * @returns JSON 文本
404281
+ */
404282
+ function serializeDoc(doc) {
404283
+ return JSON.stringify(normalizeDoc(doc), null, 2);
404284
+ }
404285
+ /**
404286
+ * 解析文档文本(解析失败返回默认文档并给出告警文案)。
404287
+ * @author ddj 2026年10月30号
404288
+ * @param text 文件内容
404289
+ * @returns 文档 + 可选告警
404290
+ */
404291
+ function parseDoc(text) {
404292
+ try {
404293
+ return { doc: migrateDoc(JSON.parse(text)) };
404294
+ } catch (error) {
404295
+ const reason = error instanceof Error ? error.message : String(error);
404296
+ return {
404297
+ doc: defaultDoc(),
404298
+ warning: "快捷菜单配置解析失败(" + reason + "),已回落默认配置"
404299
+ };
404300
+ }
404301
+ }
404302
+ /**
404303
+ * 计算项所在层级(root 的直接子项为第 1 层;孤儿按第 1 层)。
404304
+ * @author ddj 2026年10月30号
404305
+ * @param doc 文档
404306
+ * @param id 项 id
404307
+ * @returns 层级(找不到返回 0)
404308
+ */
404309
+ function depthOf(doc, id) {
404310
+ const byId = /* @__PURE__ */ new Map();
404311
+ for (const item of doc.items) byId.set(item.id, item);
404312
+ let current = byId.get(id);
404313
+ if (!current) return 0;
404314
+ let depth = 1;
404315
+ let guard = 0;
404316
+ while (current.parentId !== null && guard < 6) {
404317
+ const parent = byId.get(current.parentId);
404318
+ if (!parent) break;
404319
+ depth += 1;
404320
+ current = parent;
404321
+ guard += 1;
404322
+ }
404323
+ return depth;
404324
+ }
404325
+ /**
404326
+ * 判断能否在指定父级下新增子项(达到最大嵌套层数时禁止)。
404327
+ * @author ddj 2026年10月30号
404328
+ * @param doc 文档
404329
+ * @param parentId 父组 id(null 为顶层)
404330
+ * @returns 是否可新增
404331
+ */
404332
+ function canCreateAt(doc, parentId) {
404333
+ if (parentId === null) return doc.maxDepth >= 1;
404334
+ const depth = depthOf(doc, parentId);
404335
+ if (depth <= 0) return false;
404336
+ return depth + 1 <= doc.maxDepth;
404337
+ }
404338
+ /**
404339
+ * 取某组下可见的子项(按优先级排序;cwd 为空时不做工作区过滤)。
404340
+ * @author ddj 2026年10月30号
404341
+ * @param doc 文档
404342
+ * @param parentId 父组 id(null 为顶层)
404343
+ * @param cwd 当前工作区路径
404344
+ * @returns 可见子项
404345
+ */
404346
+ function childrenOf(doc, parentId, cwd) {
404347
+ return doc.items.filter((item) => item.parentId === parentId).filter((item) => itemVisibleIn(doc, item, cwd)).sort((left, right) => {
404348
+ if (left.order !== right.order) return left.order - right.order;
404349
+ return left.name < right.name ? -1 : 1;
404350
+ });
404351
+ }
404352
+ /**
404353
+ * 计算项的有效工作区限定(沿祖先链最近的显式限定生效)。
404354
+ * @author ddj 2026年10月30号
404355
+ * @param doc 文档
404356
+ * @param item 菜单项
404357
+ * @returns 限定列表或 null
404358
+ */
404359
+ function inheritWorkspaces(doc, item) {
404360
+ const byId = /* @__PURE__ */ new Map();
404361
+ for (const entry of doc.items) byId.set(entry.id, entry);
404362
+ let current = item;
404363
+ let guard = 0;
404364
+ while (current && guard < 7) {
404365
+ if (current.workspaces !== null) return current.workspaces;
404366
+ current = current.parentId === null ? void 0 : byId.get(current.parentId);
404367
+ guard += 1;
404368
+ }
404369
+ return null;
404370
+ }
404371
+ /**
404372
+ * 判断项在当前工作区是否可见(继承父组限定)。
404373
+ * @author ddj 2026年10月30号
404374
+ * @param doc 文档
404375
+ * @param item 菜单项
404376
+ * @param cwd 当前工作区路径
404377
+ * @returns 是否可见
404378
+ */
404379
+ function itemVisibleIn(doc, item, cwd) {
404380
+ return matchWorkspace(inheritWorkspaces(doc, item), cwd);
404381
+ }
404382
+ /**
404383
+ * 收集某组的所有后代 id(防止把组设为自己的子组)。
404384
+ * @author ddj 2026年10月30号
404385
+ * @param doc 文档
404386
+ * @param id 组 id
404387
+ * @returns 后代 id 列表(含自身)
404388
+ */
404389
+ function descendantIds(doc, id) {
404390
+ const out = [id];
404391
+ let index = 0;
404392
+ while (index < out.length) {
404393
+ const current = out[index];
404394
+ index += 1;
404395
+ for (const item of doc.items) {
404396
+ if (item.parentId !== current) continue;
404397
+ if (out.includes(item.id)) continue;
404398
+ out.push(item.id);
404399
+ }
404400
+ }
404401
+ return out;
404402
+ }
404403
+ /**
404404
+ * 在校验上下文里定位规则文件(用户规则按 origin 消歧、项目规则按工作区定位)。
404405
+ * @author ddj 2026年10月30号
404406
+ * @param binding 规则绑定
404407
+ * @param snapshot 规则列表快照
404408
+ * @returns 命中信息:规则元数据 / 工作区目录缺失 / 未找到
404409
+ */
404410
+ function lookupRule(binding, snapshot) {
404411
+ if (binding.scope === "user") {
404412
+ const hit = snapshot.user.find((rule) => {
404413
+ if (rule.file !== binding.file) return false;
404414
+ if (binding.origin && rule.origin !== binding.origin) return false;
404415
+ return true;
404416
+ });
404417
+ return hit ? { info: hit } : {};
404418
+ }
404419
+ const wanted = normalizePath(binding.workspacePath ?? "");
404420
+ const project = snapshot.projects.find((entry) => normalizePath(entry.workspacePath) === wanted);
404421
+ if (!project) return {};
404422
+ if (project.missingDir) return { missingDir: project.workspacePath };
404423
+ const hit = project.rules.find((rule) => rule.file === binding.file);
404424
+ return hit ? { info: hit } : {};
404425
+ }
404426
+ /**
404427
+ * 校验一条规则绑定(文件缺失/停用/自身报错/跨工作区)。
404428
+ * @author ddj 2026年10月30号
404429
+ * @param binding 规则绑定
404430
+ * @param ctx 校验上下文
404431
+ * @returns 绑定状态
404432
+ */
404433
+ function validateRule(binding, ctx) {
404434
+ if (!ctx.rules) return {
404435
+ state: "unknown",
404436
+ reason: "未校验(规则列表未加载)"
404437
+ };
404438
+ const found = lookupRule(binding, ctx.rules);
404439
+ if (found.missingDir) return {
404440
+ state: "error",
404441
+ reason: "工作区规则目录不存在:" + found.missingDir + "\\.dsh\\rules"
404442
+ };
404443
+ if (!found.info) return {
404444
+ state: "error",
404445
+ reason: "规则文件不存在或已移除:" + binding.file
404446
+ };
404447
+ if (found.info.error) return {
404448
+ state: "error",
404449
+ reason: "规则解析失败:" + found.info.error
404450
+ };
404451
+ if (!found.info.enabled) return {
404452
+ state: "warn",
404453
+ reason: "规则已停用,不会生效:" + binding.file
404454
+ };
404455
+ if (binding.scope === "project") {
404456
+ const wanted = normalizePath(binding.workspacePath ?? "");
404457
+ const current = normalizePath(ctx.cwd ?? "");
404458
+ if (current !== "" && wanted !== current) return {
404459
+ state: "warn",
404460
+ reason: "规则来自其他工作区,发送时会跳过:" + binding.file
404461
+ };
404462
+ }
404463
+ return { state: "ok" };
404464
+ }
404465
+ /**
404466
+ * 校验一条技能绑定(技能按会话枚举,不可见不等同于已删除)。
404467
+ * @author ddj 2026年10月30号
404468
+ * @param name 技能名
404469
+ * @param ctx 校验上下文
404470
+ * @returns 绑定状态
404471
+ */
404472
+ function validateSkill(name, ctx) {
404473
+ if (ctx.skillNames === void 0 || ctx.skillNames === null) return {
404474
+ state: "unknown",
404475
+ reason: "未校验(当前没有活跃会话)"
404476
+ };
404477
+ if (ctx.skillNames.includes(name)) return { state: "ok" };
404478
+ return {
404479
+ state: "warn",
404480
+ reason: "当前会话不可见(可能已移除或未被该会话加载):" + name
404481
+ };
404482
+ }
404483
+ /**
404484
+ * 聚合单个菜单项的绑定状态(组无绑定,恒为空)。
404485
+ * @author ddj 2026年10月30号
404486
+ * @param item 菜单项
404487
+ * @param ctx 校验上下文
404488
+ * @returns 失效与告警原因
404489
+ */
404490
+ function itemStatus(item, ctx) {
404491
+ const status = {
404492
+ errors: [],
404493
+ warnings: []
404494
+ };
404495
+ if (item.type !== "button") return status;
404496
+ if (item.prompt.trim() === "") status.warnings.push("提示词为空,只发送绑定的技能与规则");
404497
+ for (const binding of item.rules) {
404498
+ const result = validateRule(binding, ctx);
404499
+ if (result.state === "error") status.errors.push(result.reason ?? "规则失效");
404500
+ else if (result.state === "warn") status.warnings.push(result.reason ?? "规则需注意");
404501
+ }
404502
+ for (const name of item.skills) {
404503
+ const result = validateSkill(name, ctx);
404504
+ if (result.state === "error") status.errors.push(result.reason ?? "技能失效");
404505
+ else if (result.state === "warn") status.warnings.push(result.reason ?? "技能需注意");
404506
+ }
404507
+ return status;
404508
+ }
404509
+ /**
404510
+ * 校验编辑中的草稿(保存前拦截明显错误)。
404511
+ * @author ddj 2026年10月30号
404512
+ * @param draft 草稿
404513
+ * @param doc 当前文档(用于兄弟重名与父链判断)
404514
+ * @returns 错误与告警
404515
+ */
404516
+ function validateItem(draft, doc) {
404517
+ const status = {
404518
+ errors: [],
404519
+ warnings: []
404520
+ };
404521
+ const name = draft.name.trim();
404522
+ if (name === "") status.errors.push("名称不能为空");
404523
+ if (name.length > 40) status.errors.push("名称过长(上限 40 字)");
404524
+ if (draft.order < 0 || draft.order > 999) status.errors.push("优先级需在 0 到 999 之间");
404525
+ if (draft.parentId !== null) {
404526
+ const parent = doc.items.find((item) => item.id === draft.parentId);
404527
+ if (!parent) status.errors.push("归属组不存在");
404528
+ else if (parent.type !== "group") status.errors.push("只能归属到组");
404529
+ else if (descendantIds(doc, draft.id).includes(parent.id)) status.errors.push("不能把组归属到自己的子组");
404530
+ else if (depthOf(doc, parent.id) + 1 > doc.maxDepth) status.errors.push("已达最大嵌套层数(上限 " + doc.maxDepth + ")");
404531
+ }
404532
+ const siblings = doc.items.filter((item) => item.parentId === draft.parentId && item.id !== draft.id);
404533
+ if (name !== "" && siblings.some((item) => item.name === name)) status.warnings.push("同级已有同名项");
404534
+ if (draft.type === "button") {
404535
+ if (draft.prompt.length > 8192) status.errors.push("提示词过长(上限 8192 字符)");
404536
+ if (draft.prompt.trim() === "" && draft.skills.length === 0 && draft.rules.length === 0) status.errors.push("提示词与绑定都为空,点击后没有内容可发送");
404537
+ for (const name of draft.skills) if (!QUICK_MENU_SKILL_RE.test(name)) status.errors.push("技能名不合法:" + name);
404538
+ for (const binding of draft.rules) if (binding.scope === "project" && !binding.workspacePath) status.errors.push("项目规则缺少工作区:" + binding.file);
404539
+ }
404540
+ return status;
404541
+ }
404542
+ /**
404543
+ * 挑选可用技能(不可见技能不进消息,并留下告警)。
404544
+ * @author ddj 2026年10月30号
404545
+ * @param item 按钮
404546
+ * @param ctx 合成上下文
404547
+ * @param warnings 告警收集器
404548
+ * @returns 实际发送的技能名
404549
+ */
404550
+ function pickSkills(item, ctx, warnings) {
404551
+ const out = [];
404552
+ for (const name of item.skills) {
404553
+ const result = validateSkill(name, ctx);
404554
+ if (result.state === "error" || result.state === "warn") {
404555
+ warnings.push(result.reason ?? "技能不可用:" + name);
404556
+ continue;
404557
+ }
404558
+ out.push(name);
404559
+ }
404560
+ return out;
404561
+ }
404562
+ /**
404563
+ * 挑选可用规则(失效/停用/跨工作区规则不注入,并留下告警)。
404564
+ * @author ddj 2026年10月30号
404565
+ * @param item 按钮
404566
+ * @param ctx 合成上下文
404567
+ * @param warnings 告警收集器
404568
+ * @returns 发送的规则绑定与元数据
404569
+ */
404570
+ function pickRules(item, ctx, warnings) {
404571
+ const out = [];
404572
+ for (const binding of item.rules) {
404573
+ const result = validateRule(binding, ctx);
404574
+ if (result.state !== "ok") {
404575
+ warnings.push(result.reason ?? "规则不可用:" + binding.file);
404576
+ continue;
404577
+ }
404578
+ const found = ctx.rules ? lookupRule(binding, ctx.rules) : {};
404579
+ if (found.info) out.push({ info: found.info });
404580
+ }
404581
+ return out;
404582
+ }
404583
+ /**
404584
+ * 生成规则引用段(只给名字与绝对路径,由模型按需读取)。
404585
+ * @author ddj 2026年10月30号
404586
+ * @param rules 规则元数据
404587
+ * @returns 引用段落(无规则返回空串)
404588
+ */
404589
+ function buildRuleRefs(rules) {
404590
+ if (rules.length === 0) return "";
404591
+ return "---\n引用的规则:\n" + rules.map((rule) => "- " + rule.info.file + "(绝对路径:" + rule.info.absPath + ")").join("\n") + "\n(需要时按需读取上述规则文件)";
404592
+ }
404593
+ /**
404594
+ * 生成规则内联段(缺正文或超长时截断并告警)。
404595
+ * @author ddj 2026年10月30号
404596
+ * @param item 按钮
404597
+ * @param rules 规则元数据
404598
+ * @param ctx 合成上下文
404599
+ * @param warnings 告警收集器
404600
+ * @returns 内联段落(无内容返回空串)
404601
+ */
404602
+ function buildInline(item, rules, ctx, warnings) {
404603
+ if (rules.length === 0) return "";
404604
+ const contents = ctx.inlineContents ?? {};
404605
+ const blocks = [];
404606
+ for (const rule of rules) {
404607
+ const raw = contents[bindingKey({
404608
+ scope: rule.info.scope,
404609
+ file: rule.info.file,
404610
+ workspacePath: rule.info.scope === "project" ? ctx.cwd : void 0
404611
+ })];
404612
+ if (typeof raw !== "string" || raw.trim() === "") {
404613
+ warnings.push("未能读取规则正文,已跳过内联:" + rule.info.file);
404614
+ continue;
404615
+ }
404616
+ blocks.push("### " + rule.info.file + "\n" + cutText(raw, QUICK_MENU_RULE_CAP));
404617
+ }
404618
+ if (blocks.length === 0) return "";
404619
+ return "## 已绑定规则(节选)\n\n" + blocks.join("\n\n");
404620
+ }
404621
+ /**
404622
+ * 替换提示词占位符(未知占位符原样保留)。
404623
+ * @author ddj 2026年10月30号
404624
+ * @param prompt 原文
404625
+ * @param values 占位符取值
404626
+ * @returns 替换结果
404627
+ */
404628
+ function fillPrompt(prompt, values) {
404629
+ let out = prompt;
404630
+ for (const [key, value] of Object.entries(values)) out = out.split("{{" + key + "}}").join(value);
404631
+ return out.trim();
404632
+ }
404633
+ /**
404634
+ * 合成发送文本:提示词 + 技能 token + 规则引用/内联(自动剔失效绑定)。
404635
+ * @author ddj 2026年10月30号
404636
+ * @param item 按钮
404637
+ * @param ctx 合成上下文
404638
+ * @returns 文本与告警
404639
+ */
404640
+ function composePrompt(item, ctx) {
404641
+ const warnings = [];
404642
+ const skills = pickSkills(item, ctx, warnings);
404643
+ const rules = pickRules(item, ctx, warnings);
404644
+ const tokens = skills.map((name) => "/" + name).join(" ");
404645
+ const promptText = fillPrompt(item.prompt, {
404646
+ workspace: ctx.cwd ?? "",
404647
+ selection: ctx.selection ?? "",
404648
+ date: ctx.date ?? "",
404649
+ rules: rules.map((rule) => rule.info.file).join("、"),
404650
+ skills: tokens
404651
+ });
404652
+ const blocks = [];
404653
+ if (promptText !== "") blocks.push(promptText);
404654
+ if (tokens !== "") blocks.push(tokens);
404655
+ if (item.ruleMode === "reference") {
404656
+ const refs = buildRuleRefs(rules);
404657
+ if (refs !== "") blocks.push(refs);
404658
+ } else {
404659
+ const inline = buildInline(item, rules, ctx, warnings);
404660
+ if (inline !== "") blocks.push(inline);
404661
+ }
404662
+ return {
404663
+ text: blocks.join("\n\n").trim(),
404664
+ warnings
404665
+ };
404666
+ }
404667
+ /**
404668
+ * 计算悬浮按钮默认位置(输入框左上角上方让出一个按钮高度)。
404669
+ * @author ddj 2026年10月30号
404670
+ * @param seat 输入框锚点矩形(浮层坐标系;缺失时回落左上角)
404671
+ * @param layer 浮层矩形
404672
+ * @param size 按钮尺寸
404673
+ * @param margin 边距
404674
+ * @returns 位置
404675
+ */
404676
+ function anchorLauncher(seat, layer, size, margin = 8) {
404677
+ const maxLeft = Math.max(margin, layer.width - size.width - margin);
404678
+ const maxTop = Math.max(margin, layer.height - size.height - margin);
404679
+ if (!seat) return {
404680
+ left: clampNumber(margin, margin, maxLeft, margin),
404681
+ top: clampNumber(margin, margin, maxTop, margin)
404682
+ };
404683
+ const left = seat.left - layer.left;
404684
+ const top = seat.top - layer.top - size.height - margin;
404685
+ return {
404686
+ left: clampNumber(left, margin, maxLeft, margin),
404687
+ top: clampNumber(top, margin, maxTop, margin)
404688
+ };
404689
+ }
404690
+ /**
404691
+ * 计算级联子列位置(右侧放不下则翻到左侧,纵向夹取进视口)。
404692
+ * @author ddj 2026年10月30号
404693
+ * @param anchor 父列或父行矩形
404694
+ * @param size 子列尺寸
404695
+ * @param viewport 浮层尺寸
404696
+ * @param gap 列间距
404697
+ * @returns 子列矩形
404698
+ */
404699
+ function placeColumn(anchor, size, viewport, gap = 6) {
404700
+ const margin = 8;
404701
+ const maxLeft = Math.max(margin, viewport.width - size.width - margin);
404702
+ const maxTop = Math.max(margin, viewport.height - size.height - margin);
404703
+ const rightLeft = anchor.left + anchor.width + gap;
404704
+ const left = rightLeft + size.width > viewport.width - margin ? anchor.left - size.width - gap : rightLeft;
404705
+ const top = clampNumber(anchor.top, margin, maxTop, margin);
404706
+ return {
404707
+ left: clampNumber(left, margin, maxLeft, margin),
404708
+ top,
404709
+ width: size.width,
404710
+ height: size.height
404711
+ };
404712
+ }
404713
+ /**
404714
+ * 计算 SAO 风格居中突出缩放(离列中心越远越小越淡)。
404715
+ *
404716
+ * 幅度按「列内可用宽度」定:列 220px、左右内边距 7px + 1px 边框,行宽 204px,
404717
+ * 中心行放到 1.03(约 210px)刚好还在列内、不裁字;再大就会顶出滚动区。
404718
+ * 曲线用 smoothstep,让中间三四行连成一片「实」的区域,两端快速退淡,
404719
+ * 与 CSS 的上下虚化叠出「居中突出」的体积感。
404720
+ * @author ddj 2026年10月30号
404721
+ * @param index 行下标
404722
+ * @param count 行数
404723
+ * @returns 缩放与透明度
404724
+ */
404725
+ function centerScale(index, count) {
404726
+ if (count <= 1) return {
404727
+ scale: 1,
404728
+ opacity: 1
404729
+ };
404730
+ const center = (count - 1) / 2;
404731
+ const distance = Math.abs(index - center) / Math.max(center, .5);
404732
+ const ratio = Math.min(1, Math.max(0, distance));
404733
+ const eased = ratio * ratio * (3 - 2 * ratio);
404734
+ return {
404735
+ scale: 1.03 - .075 * eased,
404736
+ opacity: 1 - .35 * eased
404737
+ };
404738
+ }
404739
+ //#endregion
404740
+ //#region src/client/quickMenuStore.ts
404741
+ /**
404742
+ * dsh-vscode-mode client — 快捷菜单 store(文档 + 绑定快照 + 坐标防抖)。
404743
+ *
404744
+ * 为什么客户端要 store:菜单在多列里同时展示,多列共享同一份文档与绑定校验结果;
404745
+ * 用外部 store + useSyncExternalStore,避免把整棵树塞进一个组件的 state。
404746
+ * 写入一律「先改内存 → 整体提交 → host 归一后回传」;冲突(其他窗口改过)时重拉,
404747
+ * 不拿本地旧版本覆盖磁盘。
404748
+ * 作者 ddj 2026年10月30号
404749
+ */
404750
+ /** 绑定快照有效期(菜单打开/聚焦时刷新,期间复用)。 */
404751
+ const BINDINGS_TTL_MS = 6e4;
404752
+ /** 坐标落盘防抖。 */
404753
+ const LAUNCHER_DEBOUNCE_MS = 500;
404754
+ /**
404755
+ * 创建快捷菜单 store。
404756
+ * @author ddj 2026年10月30号
404757
+ * @param loaders 外部数据加载器
404758
+ * @returns store 实例
404759
+ */
404760
+ function createMenuStore(loaders) {
404761
+ let state = {
404762
+ status: "idle",
404763
+ doc: defaultDoc()
404764
+ };
404765
+ let version = 0;
404766
+ let mounted = false;
404767
+ const listeners = /* @__PURE__ */ new Set();
404768
+ /** 绑定快照(cwd/sessionId 变化或超期即重拉)。 */
404769
+ let bindings = { at: 0 };
404770
+ /** 待落盘坐标与定时器。 */
404771
+ let pending = null;
404772
+ let timer = null;
404773
+ /**
404774
+ * 写入状态并通知订阅者。
404775
+ * @author ddj 2026年10月30号
404776
+ * @param next 新状态
404777
+ */
404778
+ function publish(next) {
404779
+ state = next;
404780
+ version += 1;
404781
+ for (const listener of listeners) listener();
404782
+ }
404783
+ /**
404784
+ * 合并补丁到当前状态。
404785
+ * @author ddj 2026年10月30号
404786
+ * @param patch 局部更新
404787
+ */
404788
+ function patch(patchState) {
404789
+ publish({
404790
+ ...state,
404791
+ ...patchState
404792
+ });
404793
+ }
404794
+ /**
404795
+ * 读取规则与技能快照(失败保留旧值,不打断菜单)。
404796
+ * @author ddj 2026年10月30号
404797
+ * @param force 是否强制重拉
404798
+ */
404799
+ async function refreshBindings(force = false) {
404800
+ const scope = loaders.scope();
404801
+ if (!force && Date.now() - bindings.at < BINDINGS_TTL_MS && bindings.cwd === scope.cwd && bindings.sessionId === scope.sessionId) return;
404802
+ bindings = {
404803
+ ...bindings,
404804
+ at: Date.now(),
404805
+ cwd: scope.cwd,
404806
+ sessionId: scope.sessionId
404807
+ };
404808
+ try {
404809
+ bindings.rules = await loaders.rules();
404810
+ } catch {
404811
+ bindings.rules = void 0;
404812
+ }
404813
+ try {
404814
+ bindings.skillNames = scope.sessionId ? await loaders.skills(scope.sessionId) : null;
404815
+ } catch {
404816
+ bindings.skillNames = null;
404817
+ }
404818
+ version += 1;
404819
+ for (const listener of listeners) listener();
404820
+ }
404821
+ /**
404822
+ * 拉取文档(并顺带刷新绑定快照)。
404823
+ * @author ddj 2026年10月30号
404824
+ * @param force 是否强制重拉
404825
+ */
404826
+ async function load(force = false) {
404827
+ if (state.status === "loading") return;
404828
+ patch({ status: "loading" });
404829
+ try {
404830
+ const result = await rpc("quickMenu.get", {});
404831
+ if (result.ok) patch({
404832
+ status: "ready",
404833
+ doc: result.doc,
404834
+ warning: result.warning,
404835
+ error: void 0
404836
+ });
404837
+ else patch({
404838
+ status: "error",
404839
+ error: result.error
404840
+ });
404841
+ } catch (error) {
404842
+ patch({
404843
+ status: "error",
404844
+ error: "读取快捷菜单失败:" + String(error)
404845
+ });
404846
+ }
404847
+ await refreshBindings(force);
404848
+ }
404849
+ /**
404850
+ * 提交整份文档(host 归一后回传,冲突时重拉)。
404851
+ * @author ddj 2026年10月30号
404852
+ * @param doc 待提交文档
404853
+ * @returns 写结果
404854
+ */
404855
+ async function save(doc) {
404856
+ try {
404857
+ const result = await rpc("quickMenu.save", {
404858
+ doc,
404859
+ baseRevision: state.doc.revision
404860
+ });
404861
+ if (result.ok) {
404862
+ patch({
404863
+ status: "ready",
404864
+ doc: result.doc,
404865
+ error: void 0,
404866
+ warning: void 0
404867
+ });
404868
+ return { ok: true };
404869
+ }
404870
+ await load(true);
404871
+ return {
404872
+ ok: false,
404873
+ error: result.error
404874
+ };
404875
+ } catch (error) {
404876
+ return {
404877
+ ok: false,
404878
+ error: "保存快捷菜单失败:" + String(error)
404879
+ };
404880
+ }
404881
+ }
404882
+ /**
404883
+ * 新增或替换一项(按 id 去重)。
404884
+ * @author ddj 2026年10月30号
404885
+ * @param draft 菜单项
404886
+ * @returns 写结果
404887
+ */
404888
+ async function upsert(draft) {
404889
+ const items = state.doc.items.filter((item) => item.id !== draft.id);
404890
+ items.push(draft);
404891
+ return save(normalizeDoc({
404892
+ ...state.doc,
404893
+ items
404894
+ }));
404895
+ }
404896
+ /**
404897
+ * 删除一项及其全部子项。
404898
+ * @author ddj 2026年10月30号
404899
+ * @param id 项 id
404900
+ * @returns 写结果
404901
+ */
404902
+ async function removeItem(id) {
404903
+ const doomed = descendantIds(state.doc, id);
404904
+ const items = state.doc.items.filter((item) => !doomed.includes(item.id));
404905
+ return save(normalizeDoc({
404906
+ ...state.doc,
404907
+ items
404908
+ }));
404909
+ }
404910
+ /**
404911
+ * 定时把待写坐标落盘。
404912
+ * @author ddj 2026年10月30号
404913
+ */
404914
+ async function flushLauncher() {
404915
+ if (timer !== null) {
404916
+ clearTimeout(timer);
404917
+ timer = null;
404918
+ }
404919
+ const target = pending;
404920
+ pending = null;
404921
+ if (!target) return;
404922
+ try {
404923
+ const result = await rpc("quickMenu.launcher", target);
404924
+ if (result.ok) patch({ doc: result.doc });
404925
+ } catch {}
404926
+ }
404927
+ /**
404928
+ * 记录坐标并防抖落盘。
404929
+ * @author ddj 2026年10月30号
404930
+ * @param x 横坐标
404931
+ * @param y 纵坐标
404932
+ */
404933
+ function patchLauncher(x, y) {
404934
+ pending = {
404935
+ x,
404936
+ y
404937
+ };
404938
+ if (timer !== null) clearTimeout(timer);
404939
+ timer = setTimeout(() => {
404940
+ flushLauncher();
404941
+ }, LAUNCHER_DEBOUNCE_MS);
404942
+ }
404943
+ return {
404944
+ subscribe(listener) {
404945
+ listeners.add(listener);
404946
+ return () => {
404947
+ listeners.delete(listener);
404948
+ };
404949
+ },
404950
+ getVersion() {
404951
+ return version;
404952
+ },
404953
+ getState() {
404954
+ return state;
404955
+ },
404956
+ getContext() {
404957
+ return {
404958
+ cwd: loaders.scope().cwd,
404959
+ rules: bindings.rules,
404960
+ skillNames: bindings.skillNames ?? null
404961
+ };
404962
+ },
404963
+ load,
404964
+ save,
404965
+ createItem: upsert,
404966
+ updateItem: upsert,
404967
+ removeItem,
404968
+ patchLauncher,
404969
+ flushLauncher,
404970
+ refreshBindings,
404971
+ markMounted(value) {
404972
+ mounted = value;
404973
+ version += 1;
404974
+ for (const listener of listeners) listener();
404975
+ },
404976
+ isMounted() {
404977
+ return mounted;
404978
+ },
404979
+ nextId(type) {
404980
+ return newQuickId(type);
404981
+ }
404982
+ };
404983
+ }
404984
+ //#endregion
404985
+ //#region src/shared/quickPick.ts
404986
+ /**
404987
+ * dsh-vscode-mode 快捷菜单共享纯函数 — 下拉选择器的状态与摘要文案。
404988
+ *
404989
+ * 纯函数模块:禁 node/react 导入(与 shared/quickMenu.ts 同约束)。
404990
+ * 为什么抽:工作区/技能/规则三个下拉的「触发器上显示什么」「怎么切换一项」是同一类
404991
+ * 纯计算,放在 shared 里可被 vitest 直接覆盖,避免只能靠肉眼在弹窗里验。
404992
+ *
404993
+ * 作者 ddj 2026年10月30号
404994
+ */
404995
+ /**
404996
+ * 推导工作区下拉的摘要(null / 空数组都算「全部工作区」)。
404997
+ * @author ddj 2026年10月30号
404998
+ * @param paths 已选路径列表(null = 全部工作区)
404999
+ * @param cwd 当前工作区(缺失时无法判定「当前工作区」态)
405000
+ * @returns 摘要(mode + 文案 + 原始路径)
405001
+ */
405002
+ function workspaceLabel(paths, cwd) {
405003
+ const list = (paths ?? []).slice();
405004
+ if (list.length === 0) return {
405005
+ mode: "all",
405006
+ label: "全部工作区",
405007
+ paths: []
405008
+ };
405009
+ if (list.length === 1 && cwd !== void 0 && normalizePath(cwd) !== "" && normalizePath(list[0]) === normalizePath(cwd)) return {
405010
+ mode: "current",
405011
+ label: "当前工作区",
405012
+ paths: list
405013
+ };
405014
+ return {
405015
+ mode: "some",
405016
+ label: "已选 " + list.length + " 个工作区",
405017
+ paths: list
405018
+ };
405019
+ }
405020
+ /**
405021
+ * 多选摘要文案(0 项、1~2 项全写、更多折叠成「A、B 等 N 项」)。
405022
+ * @author ddj 2026年10月30号
405023
+ * @param labels 已选项文案
405024
+ * @param unit 折叠时的单位(如「项技能」「条规则」)
405025
+ * @returns 触发器文案
405026
+ */
405027
+ function selectionLabel(labels, unit) {
405028
+ if (labels.length === 0) return "未绑定";
405029
+ if (labels.length <= 2) return labels.join("、");
405030
+ return labels.slice(0, 2).join("、") + " 等 " + labels.length + " " + unit;
405031
+ }
405032
+ /**
405033
+ * 工作区集合切换(归一化比较去重;清空即回到「全部工作区」的 null 语义)。
405034
+ * @author ddj 2026年10月30号
405035
+ * @param paths 当前列表(null = 全部)
405036
+ * @param target 目标工作区路径
405037
+ * @returns 新列表;空集合返回 null
405038
+ */
405039
+ function toggleWorkspace(paths, target) {
405040
+ const list = (paths ?? []).slice();
405041
+ const key = normalizePath(target);
405042
+ const index = list.findIndex((path) => normalizePath(path) === key);
405043
+ if (index >= 0) list.splice(index, 1);
405044
+ else list.push(target);
405045
+ return list.length === 0 ? null : list;
405046
+ }
405047
+ //#endregion
405048
+ //#region src/client/ui/QuickMenuPickers.ts
405049
+ /**
405050
+ * dsh-vscode-mode client — 快捷菜单配置弹窗的通用选择控件。
405051
+ *
405052
+ * 提供三件控件:多选下拉(技能/规则)、限定工作区下拉(全部/当前/更多)、设置项开关与分段。
405053
+ * 弹层用 position: fixed 挂在 body 之上的坐标,因此不受弹窗 body 的 overflow 裁剪;
405054
+ * 组件仍用 React.createElement(项目内无 JSX),样式落在 quickmenu.css 的 edrv-qm-pick* 类上。
405055
+ * 作者 ddj 2026年10月30号
405056
+ */
405057
+ /** 弹层最小/最大高度(决定向上还是向下展开)。 */
405058
+ const PANEL_MIN = 180;
405059
+ const PANEL_MAX = 320;
405060
+ /** 弹层与触发器/视口边缘的间距。 */
405061
+ const PANEL_GAP = 6;
405062
+ const EDGE_GAP = 8;
405063
+ /**
405064
+ * 拼接 className,过滤假值。
405065
+ * @author ddj 2026年10月30号
405066
+ * @param names 候选类名(false / undefined 会被忽略)
405067
+ * @returns 空格分隔的 className
405068
+ */
405069
+ function classes(...names) {
405070
+ return names.filter(Boolean).join(" ");
405071
+ }
405072
+ /**
405073
+ * 按触发器位置计算弹层坐标,空间不足时向上展开。
405074
+ * @author ddj 2026年10月30号
405075
+ * @param trigger 触发器元素
405076
+ * @returns 弹层定位参数
405077
+ */
405078
+ function placePanel(trigger) {
405079
+ const rect = trigger.getBoundingClientRect();
405080
+ const width = Math.max(rect.width, 280);
405081
+ const below = window.innerHeight - rect.bottom - PANEL_GAP - EDGE_GAP;
405082
+ const above = rect.top - PANEL_GAP - EDGE_GAP;
405083
+ const useAbove = below < PANEL_MIN && above > below;
405084
+ const maxHeight = Math.max(PANEL_MIN, Math.min(PANEL_MAX, useAbove ? above : below));
405085
+ const maxLeft = Math.max(EDGE_GAP, window.innerWidth - width - EDGE_GAP);
405086
+ const left = Math.min(Math.max(EDGE_GAP, rect.left), maxLeft);
405087
+ if (useAbove) return {
405088
+ left,
405089
+ width,
405090
+ maxHeight,
405091
+ bottom: window.innerHeight - rect.top + PANEL_GAP
405092
+ };
405093
+ return {
405094
+ left,
405095
+ width,
405096
+ maxHeight,
405097
+ top: rect.bottom + PANEL_GAP
405098
+ };
405099
+ }
405100
+ /**
405101
+ * 受控弹层的位置与开合状态。
405102
+ *
405103
+ * Esc 的处理依赖「子组件 effect 先于父组件 effect 执行」:本 hook 在 ModalShell 内部的
405104
+ * 子组件里挂载,因此先注册 window 捕获监听,才能在弹层打开时拦下 Esc,避免连外层弹窗一起关掉。
405105
+ * @author ddj 2026年10月30号
405106
+ * @returns 弹层句柄
405107
+ */
405108
+ function useAnchored() {
405109
+ const triggerRef = react$1.default.useRef(null);
405110
+ const panelRef = react$1.default.useRef(null);
405111
+ const [open, setOpen] = react$1.default.useState(false);
405112
+ const [pos, setPos] = react$1.default.useState(null);
405113
+ const openRef = react$1.default.useRef(false);
405114
+ openRef.current = open;
405115
+ const close = react$1.default.useCallback(() => {
405116
+ setOpen(false);
405117
+ setPos(null);
405118
+ }, []);
405119
+ const toggle = react$1.default.useCallback(() => {
405120
+ if (openRef.current) {
405121
+ close();
405122
+ return;
405123
+ }
405124
+ const trigger = triggerRef.current;
405125
+ setPos(trigger ? placePanel(trigger) : null);
405126
+ setOpen(true);
405127
+ }, [close]);
405128
+ react$1.default.useEffect(() => {
405129
+ const onKey = (event) => {
405130
+ if (event.key !== "Escape" || !openRef.current) return;
405131
+ event.stopImmediatePropagation();
405132
+ event.preventDefault();
405133
+ close();
405134
+ };
405135
+ window.addEventListener("keydown", onKey, true);
405136
+ return () => window.removeEventListener("keydown", onKey, true);
405137
+ }, [close]);
405138
+ react$1.default.useEffect(() => {
405139
+ if (!open) return;
405140
+ const reposition = () => {
405141
+ const trigger = triggerRef.current;
405142
+ if (trigger) setPos(placePanel(trigger));
405143
+ };
405144
+ const onDown = (event) => {
405145
+ const target = event.target;
405146
+ if (!target) return;
405147
+ if (triggerRef.current?.contains(target)) return;
405148
+ if (panelRef.current?.contains(target)) return;
405149
+ close();
405150
+ };
405151
+ const onScroll = (event) => {
405152
+ const target = event.target;
405153
+ if (target && panelRef.current?.contains(target)) return;
405154
+ reposition();
405155
+ };
405156
+ window.addEventListener("mousedown", onDown, true);
405157
+ window.addEventListener("resize", reposition);
405158
+ window.addEventListener("scroll", onScroll, true);
405159
+ return () => {
405160
+ window.removeEventListener("mousedown", onDown, true);
405161
+ window.removeEventListener("resize", reposition);
405162
+ window.removeEventListener("scroll", onScroll, true);
405163
+ };
405164
+ }, [open, close]);
405165
+ return {
405166
+ open,
405167
+ style: pos ? {
405168
+ left: pos.left,
405169
+ top: pos.top,
405170
+ bottom: pos.bottom,
405171
+ width: pos.width,
405172
+ maxHeight: pos.maxHeight
405173
+ } : {},
405174
+ triggerRef,
405175
+ panelRef,
405176
+ toggle,
405177
+ close
405178
+ };
405179
+ }
405180
+ /**
405181
+ * 收集所有分组的选项键→标签映射。
405182
+ * @author ddj 2026年10月30号
405183
+ * @param groups 分组选项
405184
+ * @returns 选项键到标签的映射
405185
+ */
405186
+ function labelsOf(groups) {
405187
+ const labels = /* @__PURE__ */ new Map();
405188
+ for (const group of groups) for (const option of group.options) labels.set(option.key, option.label);
405189
+ return labels;
405190
+ }
405191
+ /**
405192
+ * 统计已选项里带警告/错误状态的数量。
405193
+ * @author ddj 2026年10月30号
405194
+ * @param groups 分组选项
405195
+ * @param selected 已选键
405196
+ * @returns 异常状态条目数
405197
+ */
405198
+ function issueCount(groups, selected) {
405199
+ const picked = new Set(selected);
405200
+ let count = 0;
405201
+ for (const group of groups) for (const option of group.options) if (picked.has(option.key) && option.state && option.state !== "ok") count += 1;
405202
+ return count;
405203
+ }
405204
+ /**
405205
+ * 下拉触发器:显示当前选择摘要与展开箭头。
405206
+ * @author ddj 2026年10月30号
405207
+ * @param props 触发器属性
405208
+ * @returns 触发器按钮
405209
+ */
405210
+ function PickTrigger(props) {
405211
+ return react$1.default.createElement("button", {
405212
+ ref: props.triggerRef,
405213
+ id: props.buttonId,
405214
+ type: "button",
405215
+ className: classes("edrv-qm-pick-trigger", props.open && "edrv-qm-pick-trigger--open"),
405216
+ disabled: props.disabled,
405217
+ title: props.hint,
405218
+ "aria-haspopup": "listbox",
405219
+ "aria-expanded": props.open,
405220
+ onClick: props.onClick
405221
+ }, 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", {
405222
+ className: "edrv-qm-pick-caret",
405223
+ "aria-hidden": "true"
405224
+ }));
405225
+ }
405226
+ /**
405227
+ * 绝对定位的弹层容器。
405228
+ * @author ddj 2026年10月30号
405229
+ * @param props 弹层属性
405230
+ * @returns 弹层节点
405231
+ */
405232
+ function PickPanel(props) {
405233
+ return react$1.default.createElement("div", {
405234
+ ref: props.panelRef,
405235
+ className: "edrv-qm-pick-panel",
405236
+ style: props.style,
405237
+ role: "listbox",
405238
+ "aria-labelledby": props.labelledBy
405239
+ }, props.children);
405240
+ }
405241
+ /**
405242
+ * 可勾选的多选项行。
405243
+ * @author ddj 2026年10月30号
405244
+ * @param props 选项属性
405245
+ * @returns 选项按钮
405246
+ */
405247
+ function PickOptionRow(props) {
405248
+ const option = props.option;
405249
+ const state = option.state ?? "ok";
405250
+ return react$1.default.createElement("button", {
405251
+ type: "button",
405252
+ role: "option",
405253
+ className: classes("edrv-qm-pick-option", props.picked && "edrv-qm-pick-option--on"),
405254
+ "aria-selected": props.picked,
405255
+ title: option.reason ?? option.hint,
405256
+ onClick: () => props.onToggle(option.key)
405257
+ }, react$1.default.createElement("span", {
405258
+ className: classes("edrv-qm-pick-check", props.picked && "edrv-qm-pick-check--on"),
405259
+ "aria-hidden": "true"
405260
+ }, props.picked ? "✓" : ""), react$1.default.createElement("span", { className: "edrv-qm-pick-name" }, option.label), state === "ok" ? null : react$1.default.createElement("span", {
405261
+ className: classes("edrv-qm-pick-dot", "edrv-qm-pick-dot--" + state),
405262
+ "aria-hidden": "true"
405263
+ }), option.hint ? react$1.default.createElement("span", { className: "edrv-qm-pick-hint" }, option.hint) : null);
405264
+ }
405265
+ /**
405266
+ * 多选下拉:技能与规则共用,按来源分组展示。
405267
+ * @author ddj 2026年10月30号
405268
+ * @param props 多选下拉属性
405269
+ * @returns 多选下拉组件
405270
+ */
405271
+ function PickMulti(props) {
405272
+ const anchor = useAnchored();
405273
+ const labels = labelsOf(props.groups);
405274
+ const summary = selectionLabel(props.selected.map((key) => labels.get(key) ?? key), props.unit);
405275
+ const issues = issueCount(props.groups, props.selected);
405276
+ const rows = props.groups.filter((group) => group.options.length > 0).map((group) => react$1.default.createElement("div", {
405277
+ key: group.key,
405278
+ className: "edrv-qm-pick-group"
405279
+ }, react$1.default.createElement("div", { className: "edrv-qm-pick-group-title" }, group.label), group.options.map((option) => react$1.default.createElement(PickOptionRow, {
405280
+ key: option.key,
405281
+ option,
405282
+ picked: props.selected.includes(option.key),
405283
+ onToggle: props.onToggle
405284
+ }))));
405285
+ return react$1.default.createElement("div", { className: "edrv-qm-pick" }, react$1.default.createElement(PickTrigger, {
405286
+ triggerRef: anchor.triggerRef,
405287
+ buttonId: props.buttonId,
405288
+ summary,
405289
+ open: anchor.open,
405290
+ disabled: props.disabled === true,
405291
+ issueCount: issues,
405292
+ hint: props.disabled ? props.disabledHint : void 0,
405293
+ onClick: anchor.toggle
405294
+ }), anchor.open ? react$1.default.createElement(PickPanel, {
405295
+ panelRef: anchor.panelRef,
405296
+ style: anchor.style,
405297
+ labelledBy: props.buttonId
405298
+ }, 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);
405299
+ }
405300
+ /**
405301
+ * 限定工作区下拉:全部 / 当前 / 更多(更多展开全部工作区多选)。
405302
+ * @author ddj 2026年10月30号
405303
+ * @param props 工作区下拉属性
405304
+ * @returns 工作区下拉组件
405305
+ */
405306
+ function PickWorkspace(props) {
405307
+ const anchor = useAnchored();
405308
+ const [more, setMore] = react$1.default.useState(false);
405309
+ const summary = workspaceLabel(props.selected, props.cwd);
405310
+ const picked = props.selected ?? [];
405311
+ const disabled = props.inherited === true;
405312
+ const currentPath = props.cwd;
405313
+ const isAll = picked.length === 0;
405314
+ const isCurrent = picked.length === 1 && !!currentPath && summary.mode === "current";
405315
+ const onPickAll = () => {
405316
+ props.onChange(null);
405317
+ anchor.close();
405318
+ };
405319
+ const onPickCurrent = () => {
405320
+ if (!currentPath) return;
405321
+ props.onChange([currentPath]);
405322
+ anchor.close();
405323
+ };
405324
+ const quick = [react$1.default.createElement(PickOptionRow, {
405325
+ key: "__all__",
405326
+ option: {
405327
+ key: "__all__",
405328
+ label: "全部工作区",
405329
+ hint: "菜单在所有工作区可见"
405330
+ },
405331
+ picked: isAll,
405332
+ onToggle: onPickAll
405333
+ }), react$1.default.createElement(PickOptionRow, {
405334
+ key: "__current__",
405335
+ option: {
405336
+ key: "__current__",
405337
+ label: "当前工作区",
405338
+ hint: currentPath || "当前没有活跃工作区"
405339
+ },
405340
+ picked: isCurrent,
405341
+ onToggle: onPickCurrent
405342
+ })];
405343
+ const rows = props.workspaces.map((workspace) => react$1.default.createElement(PickOptionRow, {
405344
+ key: workspace.path,
405345
+ option: {
405346
+ key: workspace.path,
405347
+ label: workspace.title,
405348
+ hint: workspace.path
405349
+ },
405350
+ picked: picked.some((path) => path === workspace.path),
405351
+ onToggle: (path) => props.onChange(toggleWorkspace(props.selected, path))
405352
+ }));
405353
+ const moreToggle = react$1.default.createElement("button", {
405354
+ type: "button",
405355
+ className: classes("edrv-qm-pick-more", more && "edrv-qm-pick-more--on"),
405356
+ "aria-expanded": more,
405357
+ onClick: () => setMore(!more)
405358
+ }, more ? "收起工作区列表" : "更多工作区(" + String(props.workspaces.length) + ")");
405359
+ return react$1.default.createElement("div", { className: "edrv-qm-pick" }, react$1.default.createElement(PickTrigger, {
405360
+ triggerRef: anchor.triggerRef,
405361
+ buttonId: props.buttonId,
405362
+ summary: summary.label,
405363
+ open: anchor.open,
405364
+ disabled,
405365
+ hint: disabled ? props.inheritedHint : "点开选择生效范围",
405366
+ onClick: anchor.toggle
405367
+ }), anchor.open ? react$1.default.createElement(PickPanel, {
405368
+ panelRef: anchor.panelRef,
405369
+ style: anchor.style,
405370
+ labelledBy: props.buttonId
405371
+ }, 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);
405372
+ }
405373
+ /**
405374
+ * 开关行:左侧标题与说明,右侧滑动开关。
405375
+ * @author ddj 2026年10月30号
405376
+ * @param props 开关属性
405377
+ * @returns 开关行
405378
+ */
405379
+ function PickSwitch(props) {
405380
+ 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", {
405381
+ id: props.buttonId,
405382
+ type: "button",
405383
+ role: "switch",
405384
+ className: classes("edrv-qm-switch", props.checked && "edrv-qm-switch--on"),
405385
+ "aria-checked": props.checked,
405386
+ "aria-label": props.label,
405387
+ disabled: props.disabled === true,
405388
+ onClick: () => props.onChange(!props.checked)
405389
+ }, react$1.default.createElement("span", {
405390
+ className: "edrv-qm-switch-knob",
405391
+ "aria-hidden": "true"
405392
+ })));
405393
+ }
405394
+ /**
405395
+ * 分段控件:等宽按钮组,用于 1–5 层数这类小范围枚举。
405396
+ * @author ddj 2026年10月30号
405397
+ * @param props 分段控件属性
405398
+ * @returns 分段控件
405399
+ */
405400
+ function PickSeg(props) {
405401
+ return react$1.default.createElement("div", {
405402
+ className: "edrv-qm-seg",
405403
+ role: "group",
405404
+ "aria-label": props.label
405405
+ }, props.options.map((option) => react$1.default.createElement("button", {
405406
+ key: option.value,
405407
+ type: "button",
405408
+ className: classes("edrv-qm-seg-btn", option.value === props.value && "edrv-qm-seg-btn--on"),
405409
+ "aria-pressed": option.value === props.value,
405410
+ disabled: props.disabled === true,
405411
+ onClick: () => props.onChange(option.value)
405412
+ }, option.label)));
405413
+ }
405414
+ //#endregion
405415
+ //#region src/shared/skillCatalog.ts
405416
+ /** 来源分组标题文案。 */
405417
+ const SKILL_SOURCE_LABEL = {
405418
+ user: "全局技能",
405419
+ project: "当前工作区技能"
405420
+ };
405421
+ /**
405422
+ * 按来源分组(空组不出现;面板全空时由 UI 统一渲染空态)。
405423
+ * @author ddj 2026年10月30号
405424
+ * @param input 技能候选快照
405425
+ * @returns 非空分组(全局在前、工作区在后)
405426
+ */
405427
+ function groupSkills(input) {
405428
+ if (!input) return [];
405429
+ const groups = [];
405430
+ if (input.user.length > 0) groups.push({
405431
+ key: "user",
405432
+ label: SKILL_SOURCE_LABEL.user,
405433
+ skills: input.user
405434
+ });
405435
+ if (input.project.length > 0) groups.push({
405436
+ key: "project",
405437
+ label: SKILL_SOURCE_LABEL.project,
405438
+ skills: input.project
405439
+ });
405440
+ return groups;
405441
+ }
405442
+ //#endregion
405443
+ //#region src/client/ui/QuickMenuDialog.tsx
405444
+ /**
405445
+ * dsh-vscode-mode client — 快捷菜单的配置弹窗(新增/编辑项 + 全局设置)。
405446
+ *
405447
+ * 设计要点:配置不再有独立设置页,全部从菜单就地入口打开;
405448
+ * 表单按「类型」动态取舍字段(组只要名称/归属/优先级,按钮额外要提示词与绑定);
405449
+ * 工作区限定沿祖先链继承——父组已限定时本项只读展示继承值,避免出现互相矛盾的配置。
405450
+ * 技能与规则都走多选下拉(候选来自目录扫描,不再手输名字);分段与开关统一由 QuickMenuPickers 提供。
405451
+ * 作者 ddj 2026年10月30号
405452
+ */
405453
+ /** 类型分段的可选值。 */
405454
+ const TYPE_OPTIONS = [{
405455
+ value: "button",
405456
+ label: "按钮"
405457
+ }, {
405458
+ value: "group",
405459
+ label: "组"
405460
+ }];
405461
+ /** 规则注入方式分段的可选值。 */
405462
+ const MODE_OPTIONS = [{
405463
+ value: "reference",
405464
+ label: "引用"
405465
+ }, {
405466
+ value: "inline",
405467
+ label: "内联正文"
405468
+ }];
405469
+ /** 最大嵌套层数分段的可选值(1–5)。 */
405470
+ const DEPTH_OPTIONS = Array.from({ length: 5 }, (_, index) => {
405471
+ const depth = 1 + index;
405472
+ return {
405473
+ value: String(depth),
405474
+ label: String(depth)
405475
+ };
405476
+ });
405477
+ /** 技能描述在候选行里的最大长度。 */
405478
+ const SKILL_HINT_CAP = 28;
405479
+ /**
405480
+ * 截断技能描述,作为候选行的次要说明。
405481
+ * @author ddj 2026年10月30号
405482
+ * @param description 技能描述
405483
+ * @returns 单行摘要
405484
+ */
405485
+ function brief(description) {
405486
+ const text = description.trim().replace(/\s+/g, " ");
405487
+ if (text === "") return void 0;
405488
+ return text.length > SKILL_HINT_CAP ? text.slice(0, SKILL_HINT_CAP) + "…" : text;
405489
+ }
405490
+ /**
405491
+ * 单个技能候选(状态点来自会话可见性校验)。
405492
+ * @author ddj 2026年10月30号
405493
+ * @param name 技能名
405494
+ * @param ctx 校验上下文
405495
+ * @param hint 次要说明
405496
+ * @returns 候选项
405497
+ */
405498
+ function skillOption(name, ctx, hint) {
405499
+ const status = validateSkill(name, ctx);
405500
+ return {
405501
+ key: name,
405502
+ label: name,
405503
+ ...hint ? { hint } : {},
405504
+ ...status.state === "ok" ? {} : { state: status.state === "warn" ? "warn" : void 0 },
405505
+ ...status.reason ? { reason: status.reason } : {}
405506
+ };
405507
+ }
405508
+ /**
405509
+ * 技能候选分组:全局技能 / 当前工作区技能 / 已绑定但不在候选内。
405510
+ * @author ddj 2026年10月30号
405511
+ * @param catalog 目录扫描结果(读取失败为 null)
405512
+ * @param names 当前会话可见技能名(候选为空时的兜底来源)
405513
+ * @param selected 已绑定技能名
405514
+ * @param ctx 校验上下文
405515
+ * @returns 分组候选
405516
+ */
405517
+ function skillGroups(catalog, names, selected, ctx) {
405518
+ const groups = [];
405519
+ const known = /* @__PURE__ */ new Set();
405520
+ for (const group of groupSkills(catalog)) {
405521
+ const options = group.skills.map((info) => skillOption(info.name, ctx, brief(info.description)));
405522
+ for (const option of options) known.add(option.key);
405523
+ if (options.length > 0) groups.push({
405524
+ key: group.key,
405525
+ label: group.label,
405526
+ options
405527
+ });
405528
+ }
405529
+ if (groups.length === 0 && names && names.length > 0) {
405530
+ const options = names.map((name) => skillOption(name, ctx));
405531
+ for (const option of options) known.add(option.key);
405532
+ groups.push({
405533
+ key: "session",
405534
+ label: "当前会话技能",
405535
+ options
405536
+ });
405537
+ }
405538
+ const extra = selected.filter((name) => !known.has(name));
405539
+ if (extra.length > 0) groups.push({
405540
+ key: "bound",
405541
+ label: "已绑定(不在候选内)",
405542
+ options: extra.map((name) => skillOption(name, ctx, "候选目录里没有该技能,可取消绑定"))
405543
+ });
405544
+ return groups;
405545
+ }
405546
+ /**
405547
+ * 单个规则候选(状态点来自 validateRule)。
405548
+ * @author ddj 2026年10月30号
405549
+ * @param choice 规则候选
405550
+ * @param ctx 校验上下文
405551
+ * @returns 候选项
405552
+ */
405553
+ function ruleOption(choice, ctx) {
405554
+ const status = validateRule(choice.binding, ctx);
405555
+ const state = status.state === "warn" || status.state === "error" ? status.state : void 0;
405556
+ return {
405557
+ key: bindingKey(choice.binding),
405558
+ label: choice.label,
405559
+ hint: choice.hint,
405560
+ ...state ? { state } : {},
405561
+ ...status.reason ? { reason: status.reason } : {}
405562
+ };
405563
+ }
405564
+ /**
405565
+ * 规则候选分组:用户规则 / 当前工作区规则 / 已绑定但已失效。
405566
+ * @author ddj 2026年10月30号
405567
+ * @param choices 规则候选
405568
+ * @param selected 已绑定规则
405569
+ * @param ctx 校验上下文
405570
+ * @returns 分组候选
405571
+ */
405572
+ function ruleGroups(choices, selected, ctx) {
405573
+ const known = new Set(choices.map((choice) => bindingKey(choice.binding)));
405574
+ const user = choices.filter((choice) => choice.binding.scope === "user");
405575
+ const project = choices.filter((choice) => choice.binding.scope === "project");
405576
+ const groups = [];
405577
+ if (user.length > 0) groups.push({
405578
+ key: "user",
405579
+ label: "用户规则",
405580
+ options: user.map((choice) => ruleOption(choice, ctx))
405581
+ });
405582
+ if (project.length > 0) groups.push({
405583
+ key: "project",
405584
+ label: "当前工作区规则",
405585
+ options: project.map((choice) => ruleOption(choice, ctx))
405586
+ });
405587
+ const extra = selected.filter((binding) => !known.has(bindingKey(binding)));
405588
+ if (extra.length > 0) groups.push({
405589
+ key: "bound",
405590
+ label: "已绑定(规则已不存在)",
405591
+ options: extra.map((binding) => {
405592
+ const status = validateRule(binding, ctx);
405593
+ return {
405594
+ key: bindingKey(binding),
405595
+ label: binding.file,
405596
+ hint: binding.scope === "user" ? "用户规则" : "项目规则",
405597
+ state: status.state === "warn" ? "warn" : "error",
405598
+ reason: status.reason ?? "候选里没有该规则,可取消绑定"
405599
+ };
405600
+ })
405601
+ });
405602
+ return groups;
405603
+ }
405604
+ /**
405605
+ * 构造待保存草稿(编辑时沿用原 id,校验需要它判断自身子树)。
405606
+ * @author ddj 2026年10月30号
405607
+ * @param id 项 id
405608
+ * @param fields 表单字段
405609
+ * @returns 菜单项
405610
+ */
405611
+ function buildDraft(id, fields) {
405612
+ const base = {
405613
+ id,
405614
+ name: fields.name,
405615
+ order: fields.order,
405616
+ parentId: fields.parentId,
405617
+ workspaces: fields.workspaces
405618
+ };
405619
+ if (fields.type === "group") return {
405620
+ ...base,
405621
+ type: "group"
405622
+ };
405623
+ const button = {
405624
+ ...base,
405625
+ type: "button",
405626
+ prompt: fields.prompt,
405627
+ skills: fields.skills,
405628
+ rules: fields.rules,
405629
+ ruleMode: fields.ruleMode
405630
+ };
405631
+ if (fields.clickAction !== "inherit") button.clickAction = fields.clickAction;
405632
+ return button;
405633
+ }
405634
+ /**
405635
+ * 归属组候选(组自身与自身子树不能当父级,否则会成环)。
405636
+ * @author ddj 2026年10月30号
405637
+ * @param doc 文档
405638
+ * @param selfId 正在编辑的项 id
405639
+ * @returns 候选(顶层 + 可选组)
405640
+ */
405641
+ function groupChoices(doc, selfId) {
405642
+ const banned = selfId === "" ? [] : descendantIds(doc, selfId);
405643
+ const out = [{
405644
+ id: "",
405645
+ label: "顶层(root 组)"
405646
+ }];
405647
+ for (const item of doc.items) {
405648
+ if (item.type !== "group" || banned.includes(item.id)) continue;
405649
+ out.push({
405650
+ id: item.id,
405651
+ label: item.name + "(第 " + depthOf(doc, item.id) + " 层)"
405652
+ });
405653
+ }
405654
+ return out;
405655
+ }
405656
+ /**
405657
+ * 规则候选(用户规则 + 当前工作区的项目规则)。
405658
+ * @author ddj 2026年10月30号
405659
+ * @param snapshot 规则快照
405660
+ * @param cwd 当前工作区
405661
+ * @returns 候选列表
405662
+ */
405663
+ function ruleChoices(snapshot, cwd) {
405664
+ if (!snapshot) return [];
405665
+ const out = [];
405666
+ for (const info of snapshot.user) {
405667
+ const binding = {
405668
+ scope: "user",
405669
+ file: info.file
405670
+ };
405671
+ if (info.origin) binding.origin = info.origin;
405672
+ out.push({
405673
+ binding,
405674
+ label: info.file,
405675
+ hint: "用户规则 · " + (info.type === "always" ? "总是生效" : info.type === "auto" ? "按 globs" : "手动")
405676
+ });
405677
+ }
405678
+ const wanted = normalizePath(cwd ?? "");
405679
+ for (const project of snapshot.projects) {
405680
+ if (wanted !== "" && normalizePath(project.workspacePath) !== wanted) continue;
405681
+ for (const info of project.rules) out.push({
405682
+ binding: {
405683
+ scope: "project",
405684
+ workspacePath: project.workspacePath,
405685
+ file: info.file
405686
+ },
405687
+ label: info.file,
405688
+ hint: "项目规则 · " + project.title
405689
+ });
405690
+ }
405691
+ return out;
405692
+ }
405693
+ /**
405694
+ * 表单里的分组区块(标题 + 说明 + 内容),把长表单切成可扫读的几段。
405695
+ * @author ddj 2026年10月30号
405696
+ * @param props 区块属性
405697
+ * @returns 区块元素
405698
+ */
405699
+ function Section(props) {
405700
+ 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));
405701
+ }
405702
+ /**
405703
+ * 新增/编辑菜单项弹窗。
405704
+ * @author ddj 2026年10月30号
405705
+ * @param props host、待编辑项、父组、关闭回调
405706
+ * @returns 弹窗元素
405707
+ */
405708
+ function QuickItemDialog(props) {
405709
+ const { host, item, parentId } = props;
405710
+ const doc = host.store.getState().doc;
405711
+ const editing = item;
405712
+ const [type, setType] = react$1.default.useState(editing?.type ?? "button");
405713
+ const [name, setName] = react$1.default.useState(editing?.name ?? "");
405714
+ const [order, setOrder] = react$1.default.useState(editing?.order ?? 100);
405715
+ const [parent, setParent] = react$1.default.useState(editing?.parentId ?? parentId);
405716
+ const [workspaces, setWorkspaces] = react$1.default.useState(editing?.workspaces ?? null);
405717
+ const [prompt, setPrompt] = react$1.default.useState(editing?.type === "button" ? editing.prompt : "");
405718
+ const [skills, setSkills] = react$1.default.useState(editing?.type === "button" ? editing.skills.slice() : []);
405719
+ const [rules, setRules] = react$1.default.useState(editing?.type === "button" ? editing.rules.slice() : []);
405720
+ const [ruleMode, setRuleMode] = react$1.default.useState(editing?.type === "button" ? editing.ruleMode : "reference");
405721
+ const [clickAction, setClickAction] = react$1.default.useState(editing?.type === "button" ? editing.clickAction ?? "inherit" : "inherit");
405722
+ const [catalog, setCatalog] = react$1.default.useState(null);
405723
+ const [snapshot, setSnapshot] = react$1.default.useState(null);
405724
+ const [skillNames, setSkillNames] = react$1.default.useState(null);
405725
+ const [busy, setBusy] = react$1.default.useState(false);
405726
+ const [error, setError] = react$1.default.useState("");
405727
+ react$1.default.useEffect(() => {
405728
+ let alive = true;
405729
+ const cwd = host.store.getContext().cwd;
405730
+ (async () => {
405731
+ const [loadedRules, loadedSkills, loadedCatalog] = await Promise.all([
405732
+ host.rules().catch(() => null),
405733
+ host.skills().catch(() => null),
405734
+ host.skillCatalog(cwd).catch(() => null)
405735
+ ]);
405736
+ if (!alive) return;
405737
+ setSnapshot(loadedRules);
405738
+ setSkillNames(loadedSkills);
405739
+ setCatalog(loadedCatalog);
405740
+ })();
405741
+ return () => {
405742
+ alive = false;
405743
+ };
405744
+ }, [host]);
405745
+ const before = host.store.getContext();
405746
+ const ctx = {
405747
+ cwd: before.cwd,
405748
+ rules: snapshot ?? void 0,
405749
+ skillNames
405750
+ };
405751
+ const draftId = editing?.id ?? "";
405752
+ const status = validateItem(buildDraft(draftId === "" ? "draft" : draftId, {
405753
+ type,
405754
+ name,
405755
+ order,
405756
+ parentId: parent,
405757
+ workspaces,
405758
+ prompt,
405759
+ skills,
405760
+ rules,
405761
+ ruleMode,
405762
+ clickAction
405763
+ }), doc);
405764
+ const choices = ruleChoices(snapshot, before.cwd);
405765
+ const parentItem = parent === null ? null : doc.items.find((entry) => entry.id === parent);
405766
+ const inherited = parentItem ? inheritWorkspaces(doc, parentItem) : null;
405767
+ const workspaceList = host.workspaces();
405768
+ const skillPick = skillGroups(catalog, skillNames, skills, ctx);
405769
+ const rulePick = ruleGroups(choices, rules, ctx);
405770
+ const ruleByKey = new Map(choices.map((choice) => [bindingKey(choice.binding), choice.binding]));
405771
+ /**
405772
+ * 切换技能绑定。
405773
+ * @author ddj 2026年10月30号
405774
+ * @param target 技能名
405775
+ */
405776
+ function toggleSkill(target) {
405777
+ setSkills((current) => current.includes(target) ? current.filter((entry) => entry !== target) : current.concat(target));
405778
+ }
405779
+ /**
405780
+ * 切换规则绑定(键来自下拉,失效规则也能取消绑定)。
405781
+ * @author ddj 2026年10月30号
405782
+ * @param key bindingKey 结果
405783
+ */
405784
+ function toggleRuleKey(key) {
405785
+ const binding = ruleByKey.get(key);
405786
+ setRules((current) => {
405787
+ if (current.some((entry) => bindingKey(entry) === key)) return current.filter((entry) => bindingKey(entry) !== key);
405788
+ return binding ? current.concat(binding) : current;
405789
+ });
405790
+ }
405791
+ /**
405792
+ * 保存(新建或更新);冲突/校验失败时留在弹窗内提示。
405793
+ * @author ddj 2026年10月30号
405794
+ */
405795
+ async function onSave() {
405796
+ if (status.errors.length > 0) return;
405797
+ setBusy(true);
405798
+ const finalItem = buildDraft(editing?.id ?? host.store.nextId(type), {
405799
+ type,
405800
+ name,
405801
+ order,
405802
+ parentId: parent,
405803
+ workspaces,
405804
+ prompt,
405805
+ skills,
405806
+ rules,
405807
+ ruleMode,
405808
+ clickAction
405809
+ });
405810
+ const result = editing ? await host.store.updateItem(finalItem) : await host.store.createItem(finalItem);
405811
+ setBusy(false);
405812
+ if (!result.ok) {
405813
+ setError(result.error ?? "保存失败");
405814
+ return;
405815
+ }
405816
+ props.onClose();
405817
+ }
405818
+ /**
405819
+ * 删除该项及其子项。
405820
+ * @author ddj 2026年10月30号
405821
+ */
405822
+ async function onDelete() {
405823
+ if (!editing) return;
405824
+ const suffix = editing.type === "group" ? "及其全部子项" : "";
405825
+ if (!window.confirm("确认删除「" + editing.name + "」" + suffix + "?")) return;
405826
+ setBusy(true);
405827
+ const result = await host.store.removeItem(editing.id);
405828
+ setBusy(false);
405829
+ if (!result.ok) {
405830
+ setError(result.error ?? "删除失败");
405831
+ return;
405832
+ }
405833
+ props.onClose();
405834
+ }
405835
+ /**
405836
+ * 类型与名称。
405837
+ * @author ddj 2026年10月30号
405838
+ * @returns 表单行
405839
+ */
405840
+ function basicRow() {
405841
+ 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, {
405842
+ options: TYPE_OPTIONS,
405843
+ value: type,
405844
+ label: "菜单项类型",
405845
+ onChange: (next) => setType(next)
405846
+ })), react$1.default.createElement("label", { className: "edrv-qm-field" }, react$1.default.createElement("span", { className: "edrv-qm-field-label" }, "名称"), react$1.default.createElement("input", {
405847
+ className: "edrv-qm-input",
405848
+ value: name,
405849
+ maxLength: 40,
405850
+ placeholder: type === "group" ? "例如:代码审查" : "例如:审查当前改动",
405851
+ onChange: (event) => setName(event.target.value)
405852
+ })));
405853
+ }
405854
+ /**
405855
+ * 归属组与同级优先级。
405856
+ * @author ddj 2026年10月30号
405857
+ * @returns 表单行
405858
+ */
405859
+ function placeRow() {
405860
+ 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", {
405861
+ className: "edrv-qm-select",
405862
+ value: parent ?? "",
405863
+ onChange: (event) => setParent(event.target.value === "" ? null : event.target.value)
405864
+ }, groupChoices(doc, draftId).map((choice) => react$1.default.createElement("option", {
405865
+ key: choice.id,
405866
+ value: choice.id
405867
+ }, 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", {
405868
+ className: "edrv-qm-input edrv-qm-input--order",
405869
+ type: "number",
405870
+ min: 0,
405871
+ max: 999,
405872
+ value: order,
405873
+ onChange: (event) => setOrder(Number(event.target.value) || 0)
405874
+ }), react$1.default.createElement("button", {
405875
+ type: "button",
405876
+ className: "edrv-qm-btn edrv-qm-btn--sm",
405877
+ onClick: () => setOrder((value) => Math.max(0, value - 10))
405878
+ }, "上移"), react$1.default.createElement("button", {
405879
+ type: "button",
405880
+ className: "edrv-qm-btn edrv-qm-btn--sm",
405881
+ onClick: () => setOrder((value) => Math.min(999, value + 10))
405882
+ }, "下移"))));
405883
+ }
405884
+ /**
405885
+ * 限定工作区(继承上级组时只读)。
405886
+ * @author ddj 2026年10月30号
405887
+ * @returns 表单行
405888
+ */
405889
+ function workspaceField() {
405890
+ 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, {
405891
+ buttonId: "edrv-qm-pick-workspaces",
405892
+ workspaces: workspaceList,
405893
+ selected: inherited ?? workspaces,
405894
+ cwd: before.cwd,
405895
+ inherited: inherited !== null,
405896
+ inheritedHint: "遵循上级组,此项不可编辑",
405897
+ onChange: (next) => setWorkspaces(next)
405898
+ }));
405899
+ }
405900
+ /**
405901
+ * 提示词输入。
405902
+ * @author ddj 2026年10月30号
405903
+ * @returns 表单行
405904
+ */
405905
+ function promptField() {
405906
+ 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", {
405907
+ className: "edrv-qm-textarea",
405908
+ value: prompt,
405909
+ maxLength: QUICK_MENU_PROMPT_CAP,
405910
+ placeholder: "点击后发送给 AI 的内容…",
405911
+ onChange: (event) => setPrompt(event.target.value)
405912
+ }));
405913
+ }
405914
+ /**
405915
+ * 绑定技能(多选下拉,候选来自全局与工作区技能目录)。
405916
+ * @author ddj 2026年10月30号
405917
+ * @returns 表单行
405918
+ */
405919
+ function skillField() {
405920
+ 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, {
405921
+ buttonId: "edrv-qm-pick-skills",
405922
+ groups: skillPick,
405923
+ selected: skills,
405924
+ unit: "个技能",
405925
+ emptyText: "没有可选技能(全局技能目录与当前工作区 .dsh/skills 均为空)",
405926
+ onToggle: toggleSkill
405927
+ }));
405928
+ }
405929
+ /**
405930
+ * 绑定规则(多选下拉 + 引用/内联切换)。
405931
+ * @author ddj 2026年10月30号
405932
+ * @returns 表单行
405933
+ */
405934
+ function ruleField() {
405935
+ 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, {
405936
+ options: MODE_OPTIONS,
405937
+ value: ruleMode,
405938
+ label: "规则注入方式",
405939
+ onChange: (next) => setRuleMode(next)
405940
+ })), react$1.default.createElement(PickMulti, {
405941
+ buttonId: "edrv-qm-pick-rules",
405942
+ groups: rulePick,
405943
+ selected: rules.map((binding) => bindingKey(binding)),
405944
+ unit: "条规则",
405945
+ emptyText: "没有可选规则(可在规则面板新建 .mdc 规则)",
405946
+ onToggle: toggleRuleKey
405947
+ }));
405948
+ }
405949
+ /**
405950
+ * 点击动作(跟随全局 / 直接发送 / 只填入)。
405951
+ * @author ddj 2026年10月30号
405952
+ * @returns 表单行
405953
+ */
405954
+ function clickField() {
405955
+ 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", {
405956
+ className: "edrv-qm-select",
405957
+ value: clickAction,
405958
+ onChange: (event) => setClickAction(event.target.value)
405959
+ }, react$1.default.createElement("option", { value: "inherit" }, "跟随全局"), react$1.default.createElement("option", { value: "send" }, "直接发送"), react$1.default.createElement("option", { value: "fill" }, "只填入输入框")));
405960
+ }
405961
+ const parentName = parent === null ? null : doc.items.find((entry) => entry.id === parent)?.name ?? null;
405962
+ const typeLabel = type === "group" ? "组" : "按钮";
405963
+ const headTitle = editing ? "编辑「" + editing.name + "」" : parent === null ? "新建顶层项" : "新建子项";
405964
+ const headSub = (editing ? editing.type === "group" ? "组" : "按钮" : typeLabel) + (parentName ? " · 位于「" + parentName + "」" : " · 位于顶层");
405965
+ 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));
405966
+ 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, {
405967
+ title: "提示词",
405968
+ hint: "点击后发给 AI 的内容"
405969
+ }, 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);
405970
+ const actions = react$1.default.createElement("div", { className: "edrv-qm-actions" }, editing ? react$1.default.createElement("button", {
405971
+ type: "button",
405972
+ className: "edrv-qm-btn edrv-qm-btn--danger",
405973
+ disabled: busy,
405974
+ onClick: () => {
405975
+ onDelete();
405976
+ }
405977
+ }, "删除") : null, react$1.default.createElement("span", { className: "edrv-qm-actions-grow" }), react$1.default.createElement("button", {
405978
+ type: "button",
405979
+ className: "edrv-qm-btn",
405980
+ onClick: props.onClose
405981
+ }, "取消"), react$1.default.createElement("button", {
405982
+ type: "button",
405983
+ className: "edrv-qm-btn edrv-qm-btn--primary",
405984
+ disabled: busy || status.errors.length > 0,
405985
+ onClick: () => {
405986
+ onSave();
405987
+ }
405988
+ }, busy ? "保存中…" : editing ? "保存" : "创建"));
405989
+ return react$1.default.createElement(ModalShell, {
405990
+ dialogClass: "edrv-qm-dialog",
405991
+ zIndex: 220,
405992
+ onClose: props.onClose,
405993
+ closeOnMask: false
405994
+ }, header, body, actions);
405995
+ }
405996
+ /**
405997
+ * 菜单全局设置(显隐、点击动作、最大嵌套层数、位置重置、导入导出、失效绑定清理)。
405998
+ * @author ddj 2026年10月30号
405999
+ * @param props host 与关闭回调
406000
+ * @returns 弹窗元素
406001
+ */
406002
+ function MenuGlobalDialog(props) {
406003
+ const { host } = props;
406004
+ const doc = host.store.getState().doc;
406005
+ const [enabled, setEnabled] = react$1.default.useState(doc.enabled);
406006
+ const [clickAction, setClickAction] = react$1.default.useState(doc.clickAction);
406007
+ const [maxDepth, setMaxDepth] = react$1.default.useState(doc.maxDepth || 3);
406008
+ const [busy, setBusy] = react$1.default.useState(false);
406009
+ const [error, setError] = react$1.default.useState("");
406010
+ const ctx = host.store.getContext();
406011
+ const broken = collectBroken(doc, ctx);
406012
+ const fileRef = react$1.default.useRef(null);
406013
+ /**
406014
+ * 保存全局设置。
406015
+ * @author ddj 2026年10月30号
406016
+ */
406017
+ async function onSave() {
406018
+ setBusy(true);
406019
+ const result = await host.store.save({
406020
+ ...doc,
406021
+ enabled,
406022
+ clickAction,
406023
+ maxDepth
406024
+ });
406025
+ setBusy(false);
406026
+ if (!result.ok) {
406027
+ setError(result.error ?? "保存失败");
406028
+ return;
406029
+ }
406030
+ props.onClose();
406031
+ }
406032
+ /**
406033
+ * 清理全部失效绑定(移除失效规则/技能,保留提示词)。
406034
+ * @author ddj 2026年10月30号
406035
+ */
406036
+ async function onClean() {
406037
+ setBusy(true);
406038
+ const items = doc.items.map((item) => {
406039
+ if (item.type !== "button") return item;
406040
+ const rules = item.rules.filter((binding) => validateRule(binding, ctx).state === "ok");
406041
+ const skills = item.skills.filter((name) => validateSkill(name, ctx).state === "ok");
406042
+ return {
406043
+ ...item,
406044
+ rules,
406045
+ skills
406046
+ };
406047
+ });
406048
+ const result = await host.store.save({
406049
+ ...doc,
406050
+ enabled,
406051
+ clickAction,
406052
+ maxDepth,
406053
+ items
406054
+ });
406055
+ setBusy(false);
406056
+ if (!result.ok) setError(result.error ?? "清理失败");
406057
+ else host.notify("已清理失效绑定");
406058
+ }
406059
+ /**
406060
+ * 配置备份与迁移(导出 / 导入 / 重置按钮位置)。
406061
+ * @author ddj 2026年10月30号
406062
+ * @returns 按钮行
406063
+ */
406064
+ function backupRow() {
406065
+ return react$1.default.createElement("div", { className: "edrv-qm-btn-row" }, react$1.default.createElement("input", {
406066
+ ref: fileRef,
406067
+ type: "file",
406068
+ accept: "application/json",
406069
+ style: { display: "none" },
406070
+ onChange: (event) => {
406071
+ onImport(event, host, setError);
406072
+ }
406073
+ }), react$1.default.createElement("button", {
406074
+ type: "button",
406075
+ className: "edrv-qm-btn",
406076
+ onClick: () => downloadDoc(doc)
406077
+ }, "导出 JSON"), react$1.default.createElement("button", {
406078
+ type: "button",
406079
+ className: "edrv-qm-btn",
406080
+ onClick: () => fileRef.current?.click()
406081
+ }, "导入 JSON"), react$1.default.createElement("button", {
406082
+ type: "button",
406083
+ className: "edrv-qm-btn",
406084
+ onClick: () => {
406085
+ host.store.patchLauncher(null, null);
406086
+ host.store.flushLauncher();
406087
+ host.notify("已重置悬浮按钮位置");
406088
+ }
406089
+ }, "重置按钮位置"));
406090
+ }
406091
+ 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" }, "悬浮按钮、交互行为与配置迁移"));
406092
+ const body = react$1.default.createElement("div", { className: "edrv-qm-dialog-body" }, react$1.default.createElement(Section, { title: "外观" }, react$1.default.createElement(PickSwitch, {
406093
+ buttonId: "edrv-qm-switch-enabled",
406094
+ label: "显示悬浮按钮",
406095
+ hint: "关闭后左侧不再显示快捷菜单按钮,配置会保留",
406096
+ checked: enabled,
406097
+ onChange: setEnabled
406098
+ })), 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", {
406099
+ className: "edrv-qm-select",
406100
+ value: clickAction,
406101
+ onChange: (event) => setClickAction(event.target.value)
406102
+ }, 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, {
406103
+ options: DEPTH_OPTIONS,
406104
+ value: String(maxDepth),
406105
+ label: "最大嵌套层数",
406106
+ onChange: (next) => setMaxDepth(Number(next) || 3)
406107
+ }))), react$1.default.createElement(Section, { title: "配置备份与迁移" }, backupRow()), react$1.default.createElement(Section, {
406108
+ title: "绑定状态",
406109
+ hint: broken.length === 0 ? "全部可用" : String(broken.length) + " 项失效"
406110
+ }, 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", {
406111
+ type: "button",
406112
+ className: "edrv-qm-btn",
406113
+ disabled: busy,
406114
+ onClick: () => {
406115
+ onClean();
406116
+ }
406117
+ }, "清理失效绑定")) : 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 未提供浮层槽,按钮不显示)")));
406118
+ return react$1.default.createElement(ModalShell, {
406119
+ dialogClass: "edrv-qm-dialog",
406120
+ zIndex: 220,
406121
+ onClose: props.onClose,
406122
+ closeOnMask: false
406123
+ }, 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", {
406124
+ type: "button",
406125
+ className: "edrv-qm-btn",
406126
+ onClick: props.onClose
406127
+ }, "取消"), react$1.default.createElement("button", {
406128
+ type: "button",
406129
+ className: "edrv-qm-btn edrv-qm-btn--primary",
406130
+ disabled: busy,
406131
+ onClick: () => {
406132
+ onSave();
406133
+ }
406134
+ }, busy ? "保存中…" : "保存")));
406135
+ }
406136
+ /**
406137
+ * 收集全菜单的失效绑定(设置弹窗顶部汇总)。
406138
+ * @author ddj 2026年10月30号
406139
+ * @param doc 文档
406140
+ * @param ctx 校验上下文
406141
+ * @returns 失效条目文案
406142
+ */
406143
+ function collectBroken(doc, ctx) {
406144
+ const out = [];
406145
+ for (const item of doc.items) {
406146
+ if (item.type !== "button") continue;
406147
+ const status = itemStatus(item, ctx);
406148
+ status.errors.concat(status.warnings).forEach((reason, index) => {
406149
+ out.push({
406150
+ key: item.id + ":" + index,
406151
+ label: item.name + " → " + reason
406152
+ });
406153
+ });
406154
+ }
406155
+ return out;
406156
+ }
406157
+ /**
406158
+ * 下载当前文档为 JSON(浏览器侧 Blob,不经 host)。
406159
+ * @author ddj 2026年10月30号
406160
+ * @param doc 文档
406161
+ */
406162
+ function downloadDoc(doc) {
406163
+ const blob = new Blob([serializeDoc(doc)], { type: "application/json" });
406164
+ const url = URL.createObjectURL(blob);
406165
+ const anchor = document.createElement("a");
406166
+ anchor.href = url;
406167
+ anchor.download = "quick-menu.json";
406168
+ anchor.click();
406169
+ setTimeout(() => URL.revokeObjectURL(url), 2e3);
406170
+ }
406171
+ /**
406172
+ * 导入 JSON(解析失败/校验不通过时给错误文案)。
406173
+ * @author ddj 2026年10月30号
406174
+ * @param event 文件输入事件
406175
+ * @param host 宿主
406176
+ * @param setError 错误回写
406177
+ */
406178
+ async function onImport(event, host, setError) {
406179
+ const file = event.target.files?.[0];
406180
+ event.target.value = "";
406181
+ if (!file) return;
406182
+ let text = "";
406183
+ try {
406184
+ text = await file.text();
406185
+ } catch (error) {
406186
+ setError("读取文件失败:" + String(error));
406187
+ return;
406188
+ }
406189
+ const parsed = parseDoc(text);
406190
+ if (parsed.warning) {
406191
+ setError(parsed.warning);
406192
+ return;
406193
+ }
406194
+ const result = await host.store.save(parsed.doc);
406195
+ if (!result.ok) setError(result.error ?? "导入失败");
406196
+ else host.notify("已导入快捷菜单配置");
406197
+ }
406198
+ //#endregion
406199
+ //#region src/client/ui/QuickMenuLauncher.tsx
406200
+ /**
406201
+ * dsh-vscode-mode client — 快捷菜单悬浮按钮与多列级联菜单(SAO Utils 2 风格)。
406202
+ *
406203
+ * 交互约定:
406204
+ * - 按钮可拖动(3px 阈值区分点击),松手落盘坐标;无保存坐标时锚定输入框左上角。
406205
+ * - 点开是纵向滑动列表:行靠近列中心放大、向两端递减透明度,上下各 48px 渐隐(CSS mask)。
406206
+ * - 每组列最后一行是「+ 新建」;悬停组 120ms 后在右侧展开它的一列(放不下自动翻左侧)。
406207
+ * - 行悬停浮出「⋯」:编辑(弹窗内可删)/ 复制 / 删除;root 列底部固定「菜单设置」。
406208
+ * 作者 ddj 2026年10月30号
406209
+ */
406210
+ /** 悬浮按钮尺寸。 */
406211
+ const LAUNCHER_SIZE = {
406212
+ width: 32,
406213
+ height: 32
406214
+ };
406215
+ /** 列宽。 */
406216
+ const COL_WIDTH = 220;
406217
+ /** 行高(与 CSS 的 --qm-row 一致,用于计算子列对齐位置)。 */
406218
+ const ROW_HEIGHT = 34;
406219
+ /** 列内上下内边距(与 CSS 一致)。 */
406220
+ const COL_PAD = 8;
406221
+ /** 列间距。 */
406222
+ const COL_GAP = 6;
406223
+ /** 列最大高度。 */
406224
+ const COL_MAX_HEIGHT = 520;
406225
+ /** root 列底部「⚙ 菜单设置」底栏高度(与 CSS 的 flex-basis 一致),会占掉滚动区高度。 */
406226
+ const FOOT_HEIGHT = 32;
406227
+ /** 行与行之间的间隔(与 CSS 的 .edrv-qm-list gap 一致)。 */
406228
+ const ROW_GAP = 1;
406229
+ /** 空态卡片的内容高度:标题 + 说明 + 主按钮,短于此会被裁掉说明文字。 */
406230
+ const EMPTY_HEIGHT = 132;
406231
+ /** 列的上下边框(border-box,需从内容高度里让出)。 */
406232
+ const COL_BORDER = 2;
406233
+ /** 拖动判定阈值(像素)。 */
406234
+ const DRAG_SLOP = 3;
406235
+ /** 悬停展开意图延迟。 */
406236
+ const HOVER_DELAY = 120;
406237
+ /**
406238
+ * 悬浮按钮的鲸鱼标记:取自 DSH 顶栏品牌鲸(同一条可视化轮廓),
406239
+ * 实心填充在 19px 下比描边更认得出是鲸鱼而不是一个气泡。
406240
+ */
406241
+ 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";
406242
+ /**
406243
+ * 数值夹取。
406244
+ * @author ddj 2026年10月30号
406245
+ * @param value 原始值
406246
+ * @param min 下限
406247
+ * @param max 上限
406248
+ * @returns 夹取结果
406249
+ */
406250
+ function clamp(value, min, max) {
406251
+ if (!Number.isFinite(value)) return min;
406252
+ return Math.min(max, Math.max(min, value));
406253
+ }
406254
+ /**
406255
+ * 两个矩形是否等价(测量轮询用,避免无意义重渲染)。
406256
+ * @author ddj 2026年10月30号
406257
+ * @param left 矩形 A
406258
+ * @param right 矩形 B
406259
+ * @returns 是否等价
406260
+ */
406261
+ function sameRect(left, right) {
406262
+ return left.left === right.left && left.top === right.top && left.width === right.width && left.height === right.height;
406263
+ }
406264
+ /**
406265
+ * 取当前可见的输入框锚点。
406266
+ * 页面里可能同时存在多个(隐藏会话的 seat 位于视口外,量到的 top 会超过浮层高度,
406267
+ * 把按钮夹到屏幕底边压住输入框),因此优先选真正落在视口内、面积最大的那个。
406268
+ * @author ddj 2026年10月30号
406269
+ * @param layerRect 浮层矩形(按钮坐标以它为基准)
406270
+ * @returns 相对浮层的输入框矩形;没有可用锚点时返回 null
406271
+ */
406272
+ function pickSeatRect(layerRect) {
406273
+ const seats = Array.from(document.querySelectorAll("[data-composer-seat]"));
406274
+ let best = null;
406275
+ for (const seat of seats) {
406276
+ const rect = seat.getBoundingClientRect();
406277
+ if (rect.width < 40 || rect.height < 20) continue;
406278
+ const visibleTop = Math.max(rect.top, 0);
406279
+ const visibleBottom = Math.min(rect.bottom, window.innerHeight);
406280
+ if (visibleBottom <= visibleTop) continue;
406281
+ const visibleArea = (visibleBottom - visibleTop) * rect.width;
406282
+ if (best && best.visibleArea >= visibleArea) continue;
406283
+ best = {
406284
+ visibleArea,
406285
+ rect: {
406286
+ left: rect.left - layerRect.left,
406287
+ top: rect.top - layerRect.top,
406288
+ width: rect.width,
406289
+ height: rect.height
406290
+ }
406291
+ };
406292
+ }
406293
+ return best ? best.rect : null;
406294
+ }
406295
+ /**
406296
+ * 今日日期文本({{date}} 占位符)。
406297
+ * @author ddj 2026年10月30号
406298
+ * @returns YYYY-MM-DD
406299
+ */
406300
+ function todayText() {
406301
+ const now = /* @__PURE__ */ new Date();
406302
+ const month = String(now.getMonth() + 1).padStart(2, "0");
406303
+ const day = String(now.getDate()).padStart(2, "0");
406304
+ return now.getFullYear() + "-" + month + "-" + day;
406305
+ }
406306
+ /**
406307
+ * 当前文档选择文本({{selection}} 占位符;拿不到就是空串)。
406308
+ * @author ddj 2026年10月30号
406309
+ * @returns 选中文本
406310
+ */
406311
+ function selectionText() {
406312
+ try {
406313
+ return window.getSelection()?.toString() ?? "";
406314
+ } catch {
406315
+ return "";
406316
+ }
406317
+ }
406318
+ /**
406319
+ * 悬浮按钮 + 多列级联菜单。
406320
+ * @author ddj 2026年10月30号
406321
+ * @param props host(store + 发送/规则/技能桥接)
406322
+ * @returns 菜单元素(不显示时为 null)
406323
+ */
406324
+ function QuickMenuLauncher(props) {
406325
+ const { host } = props;
406326
+ const store = host.store;
406327
+ react$1.default.useSyncExternalStore(store.subscribe, store.getVersion, store.getVersion);
406328
+ const state = store.getState();
406329
+ const doc = state.doc;
406330
+ const [layerSize, setLayerSize] = react$1.default.useState({
406331
+ width: 1280,
406332
+ height: 800
406333
+ });
406334
+ const [measured, setMeasured] = react$1.default.useState(false);
406335
+ const [seat, setSeat] = react$1.default.useState(null);
406336
+ const [open, setOpen] = react$1.default.useState(false);
406337
+ const [dragging, setDragging] = react$1.default.useState(false);
406338
+ const [dragPos, setDragPos] = react$1.default.useState(null);
406339
+ const [openPath, setOpenPath] = react$1.default.useState([]);
406340
+ const [hoverRow, setHoverRow] = react$1.default.useState({});
406341
+ const [scrolls, setScrolls] = react$1.default.useState({});
406342
+ const [rowMenu, setRowMenu] = react$1.default.useState(null);
406343
+ const [dialog, setDialog] = react$1.default.useState({ kind: "none" });
406344
+ const [active, setActive] = react$1.default.useState({
406345
+ depth: 0,
406346
+ index: 0
406347
+ });
406348
+ const hoverTimer = react$1.default.useRef(null);
406349
+ /**
406350
+ * 测量浮层与输入框锚点(浮层坐标系)。
406351
+ * 值不变时返回原状态,避免轮询测量引起无意义重渲染。
406352
+ * @author ddj 2026年10月30号
406353
+ */
406354
+ const measure = react$1.default.useCallback(() => {
406355
+ const layerEl = document.querySelector("[data-shell-overlay]");
406356
+ if (!layerEl) {
406357
+ setLayerSize((prev) => prev.width === window.innerWidth && prev.height === window.innerHeight ? prev : {
406358
+ width: window.innerWidth,
406359
+ height: window.innerHeight
406360
+ });
406361
+ setSeat(null);
406362
+ return;
406363
+ }
406364
+ const layerRect = layerEl.getBoundingClientRect();
406365
+ setLayerSize((prev) => prev.width === layerRect.width && prev.height === layerRect.height ? prev : {
406366
+ width: layerRect.width,
406367
+ height: layerRect.height
406368
+ });
406369
+ setMeasured((prev) => prev ? prev : true);
406370
+ const next = pickSeatRect(layerRect);
406371
+ setSeat((prev) => prev && next && sameRect(prev, next) ? prev : next);
406372
+ }, []);
406373
+ react$1.default.useEffect(() => {
406374
+ store.markMounted(true);
406375
+ measure();
406376
+ store.load();
406377
+ const onResize = () => measure();
406378
+ window.addEventListener("resize", onResize);
406379
+ const timers = [
406380
+ 80,
406381
+ 320,
406382
+ 900,
406383
+ 2e3
406384
+ ].map((delay) => setTimeout(measure, delay));
406385
+ const layerEl = document.querySelector("[data-shell-overlay]");
406386
+ const seatEl = document.querySelector("[data-composer-seat]");
406387
+ let observer = null;
406388
+ if (typeof ResizeObserver !== "undefined") {
406389
+ observer = new ResizeObserver(() => measure());
406390
+ if (layerEl) observer.observe(layerEl);
406391
+ if (seatEl) observer.observe(seatEl);
406392
+ }
406393
+ const poll = setInterval(measure, 1200);
406394
+ return () => {
406395
+ window.removeEventListener("resize", onResize);
406396
+ timers.forEach((timer) => clearTimeout(timer));
406397
+ clearInterval(poll);
406398
+ observer?.disconnect();
406399
+ store.markMounted(false);
406400
+ };
406401
+ }, [measure, store]);
406402
+ react$1.default.useEffect(() => {
406403
+ if (!open) return;
406404
+ measure();
406405
+ store.refreshBindings();
406406
+ }, [
406407
+ open,
406408
+ measure,
406409
+ store
406410
+ ]);
406411
+ const ctx = store.getContext();
406412
+ const basePos = react$1.default.useMemo(() => {
406413
+ const maxLeft = Math.max(8, layerSize.width - LAUNCHER_SIZE.width - 8);
406414
+ const maxTop = Math.max(8, layerSize.height - LAUNCHER_SIZE.height - 8);
406415
+ const savedX = doc.launcher.x;
406416
+ const savedY = doc.launcher.y;
406417
+ if (savedX !== null && savedY !== null) {
406418
+ if (savedX >= 8 && savedY >= 8 && savedX <= maxLeft && savedY <= maxTop || !measured) return {
406419
+ left: clamp(savedX, 8, maxLeft),
406420
+ top: clamp(savedY, 8, maxTop)
406421
+ };
406422
+ }
406423
+ const layer = {
406424
+ left: 0,
406425
+ top: 0,
406426
+ width: layerSize.width,
406427
+ height: layerSize.height
406428
+ };
406429
+ return anchorLauncher(seat, layer, LAUNCHER_SIZE, 8);
406430
+ }, [
406431
+ doc.launcher.x,
406432
+ doc.launcher.y,
406433
+ seat,
406434
+ layerSize,
406435
+ measured
406436
+ ]);
406437
+ const launcherPos = dragPos ?? basePos;
406438
+ /**
406439
+ * 计算每一列的矩形(独立定位,子列右展放不下则自动翻到左侧)。
406440
+ * @author ddj 2026年10月30号
406441
+ */
406442
+ const columnRects = react$1.default.useMemo(() => {
406443
+ const rects = [];
406444
+ const layerRect = {
406445
+ left: 0,
406446
+ top: 0,
406447
+ width: layerSize.width,
406448
+ height: layerSize.height
406449
+ };
406450
+ let anchor = {
406451
+ left: launcherPos.left,
406452
+ top: launcherPos.top,
406453
+ width: LAUNCHER_SIZE.width,
406454
+ height: LAUNCHER_SIZE.height
406455
+ };
406456
+ const depthCount = openPath.length + 1;
406457
+ for (let depth = 0; depth < depthCount; depth += 1) {
406458
+ const parentId = depth === 0 ? null : openPath[depth - 1];
406459
+ const items = childrenOf(doc, parentId, ctx.cwd);
406460
+ const extra = canCreateAt(doc, parentId) ? 1 : 0;
406461
+ const foot = depth === 0 ? FOOT_HEIGHT : 0;
406462
+ const rowCount = items.length + extra;
406463
+ const rowHeight = items.length === 0 ? EMPTY_HEIGHT : items.length * ROW_HEIGHT + (extra > 0 ? 39 : 0) + Math.max(0, rowCount - 1) * ROW_GAP + 16;
406464
+ const height = Math.min(COL_MAX_HEIGHT, Math.max(68 + foot, rowHeight + foot + COL_BORDER));
406465
+ let rect = { ...placeColumn(anchor, {
406466
+ width: COL_WIDTH,
406467
+ height
406468
+ }, layerSize, COL_GAP) };
406469
+ if (depth > 0) {
406470
+ const rowIndex = hoverRow[depth - 1] ?? 0;
406471
+ const wantedTop = anchor.top + COL_PAD + rowIndex * ROW_HEIGHT - (scrolls[depth - 1] ?? 0);
406472
+ rect = {
406473
+ ...rect,
406474
+ top: clamp(wantedTop, 8, Math.max(8, layerRect.height - height - 8))
406475
+ };
406476
+ }
406477
+ rects.push(rect);
406478
+ anchor = rect;
406479
+ }
406480
+ return rects;
406481
+ }, [
406482
+ doc,
406483
+ ctx.cwd,
406484
+ launcherPos.left,
406485
+ launcherPos.top,
406486
+ layerSize,
406487
+ openPath,
406488
+ hoverRow,
406489
+ scrolls
406490
+ ]);
406491
+ /**
406492
+ * 关闭菜单并清空级联状态。
406493
+ * @author ddj 2026年10月30号
406494
+ */
406495
+ const closeMenu = react$1.default.useCallback(() => {
406496
+ setOpen(false);
406497
+ setRowMenu(null);
406498
+ setOpenPath([]);
406499
+ setHoverRow({});
406500
+ setScrolls({});
406501
+ setActive({
406502
+ depth: 0,
406503
+ index: 0
406504
+ });
406505
+ }, []);
406506
+ react$1.default.useEffect(() => {
406507
+ if (!open) return;
406508
+ const onPointerDown = (event) => {
406509
+ const target = event.target;
406510
+ if (target && target.closest("[data-qm-root=\"1\"]")) return;
406511
+ closeMenu();
406512
+ };
406513
+ const onKeyDown = (event) => {
406514
+ if (event.key === "Escape") {
406515
+ event.stopPropagation();
406516
+ closeMenu();
406517
+ return;
406518
+ }
406519
+ const parentId = active.depth === 0 ? null : openPath[active.depth - 1];
406520
+ const items = childrenOf(doc, parentId, ctx.cwd);
406521
+ if (items.length === 0) return;
406522
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
406523
+ event.preventDefault();
406524
+ const delta = event.key === "ArrowDown" ? 1 : -1;
406525
+ const index = (active.index + delta + items.length) % items.length;
406526
+ setActive({
406527
+ depth: active.depth,
406528
+ index
406529
+ });
406530
+ setHoverRow((prev) => ({
406531
+ ...prev,
406532
+ [active.depth]: index
406533
+ }));
406534
+ return;
406535
+ }
406536
+ if (event.key === "ArrowRight") {
406537
+ const item = items[Math.min(active.index, items.length - 1)];
406538
+ if (item && item.type === "group") {
406539
+ event.preventDefault();
406540
+ setOpenPath((prev) => prev.slice(0, active.depth).concat(item.id));
406541
+ setActive({
406542
+ depth: active.depth + 1,
406543
+ index: 0
406544
+ });
406545
+ }
406546
+ return;
406547
+ }
406548
+ if (event.key === "ArrowLeft" && active.depth > 0) {
406549
+ event.preventDefault();
406550
+ setOpenPath((prev) => prev.slice(0, active.depth - 1));
406551
+ setActive({
406552
+ depth: active.depth - 1,
406553
+ index: 0
406554
+ });
406555
+ return;
406556
+ }
406557
+ if (event.key === "Enter") {
406558
+ const item = items[Math.min(active.index, items.length - 1)];
406559
+ if (item) activate(item);
406560
+ }
406561
+ };
406562
+ window.addEventListener("pointerdown", onPointerDown, true);
406563
+ window.addEventListener("keydown", onKeyDown, true);
406564
+ return () => {
406565
+ window.removeEventListener("pointerdown", onPointerDown, true);
406566
+ window.removeEventListener("keydown", onKeyDown, true);
406567
+ };
406568
+ });
406569
+ /**
406570
+ * 拖动与点击的分流:移动超过阈值才算拖拽,否则当作点击开关。
406571
+ * @author ddj 2026年10月30号
406572
+ * @param event 指针按下事件
406573
+ */
406574
+ function beginDrag(event) {
406575
+ if (event.button !== 0) return;
406576
+ event.preventDefault();
406577
+ event.stopPropagation();
406578
+ const start = {
406579
+ pointerId: event.pointerId,
406580
+ x: event.clientX,
406581
+ y: event.clientY,
406582
+ left: launcherPos.left,
406583
+ top: launcherPos.top,
406584
+ moved: false
406585
+ };
406586
+ let last = {
406587
+ left: launcherPos.left,
406588
+ top: launcherPos.top
406589
+ };
406590
+ const onMove = (moveEvent) => {
406591
+ if (moveEvent.pointerId !== start.pointerId) return;
406592
+ const dx = moveEvent.clientX - start.x;
406593
+ const dy = moveEvent.clientY - start.y;
406594
+ if (!start.moved && Math.abs(dx) + Math.abs(dy) < DRAG_SLOP) return;
406595
+ start.moved = true;
406596
+ setDragging(true);
406597
+ last = {
406598
+ left: clamp(start.left + dx, 8, Math.max(8, layerSize.width - LAUNCHER_SIZE.width - 8)),
406599
+ top: clamp(start.top + dy, 8, Math.max(8, layerSize.height - LAUNCHER_SIZE.height - 8))
406600
+ };
406601
+ setDragPos(last);
406602
+ };
406603
+ const finish = () => {
406604
+ window.removeEventListener("pointermove", onMove, true);
406605
+ window.removeEventListener("pointerup", onUp, true);
406606
+ window.removeEventListener("pointercancel", onUp, true);
406607
+ window.removeEventListener("blur", onUp, true);
406608
+ setDragging(false);
406609
+ setDragPos(null);
406610
+ if (start.moved) {
406611
+ store.patchLauncher(last.left, last.top);
406612
+ store.flushLauncher();
406613
+ return;
406614
+ }
406615
+ if (open) closeMenu();
406616
+ else setOpen(true);
406617
+ };
406618
+ const onUp = () => finish();
406619
+ window.addEventListener("pointermove", onMove, true);
406620
+ window.addEventListener("pointerup", onUp, true);
406621
+ window.addEventListener("pointercancel", onUp, true);
406622
+ window.addEventListener("blur", onUp, true);
406623
+ }
406624
+ /**
406625
+ * 悬停行:按钮只高亮;组延迟后在右侧展开(防抖动)。
406626
+ * @author ddj 2026年10月30号
406627
+ * @param depth 所在列序号
406628
+ * @param index 行序号
406629
+ * @param item 菜单项
406630
+ */
406631
+ function hoverItem(depth, index, item) {
406632
+ setHoverRow((prev) => ({
406633
+ ...prev,
406634
+ [depth]: index
406635
+ }));
406636
+ setActive({
406637
+ depth,
406638
+ index
406639
+ });
406640
+ if (hoverTimer.current !== null) clearTimeout(hoverTimer.current);
406641
+ if (item.type !== "group") {
406642
+ setOpenPath((prev) => prev.slice(0, depth));
406643
+ return;
406644
+ }
406645
+ hoverTimer.current = setTimeout(() => {
406646
+ setOpenPath((prev) => prev.slice(0, depth).concat(item.id));
406647
+ }, HOVER_DELAY);
406648
+ }
406649
+ /**
406650
+ * 执行一项:组则展开,按钮则合成文本后发送/填入。
406651
+ * @author ddj 2026年10月30号
406652
+ * @param item 菜单项
406653
+ */
406654
+ async function activate(item) {
406655
+ if (item.type === "group") return;
406656
+ const inlineContents = {};
406657
+ if (item.ruleMode === "inline") for (const binding of item.rules) {
406658
+ if (validateRule(binding, ctx).state !== "ok") continue;
406659
+ try {
406660
+ inlineContents[bindingKey(binding)] = await host.readRule(binding);
406661
+ } catch {}
406662
+ }
406663
+ const composed = composePrompt(item, {
406664
+ ...ctx,
406665
+ inlineContents,
406666
+ selection: selectionText(),
406667
+ date: todayText()
406668
+ });
406669
+ if (composed.text.trim() === "") {
406670
+ host.notify("「" + item.name + "」没有可发送的内容");
406671
+ return;
406672
+ }
406673
+ if (composed.warnings.length > 0) host.notify("已跳过 " + composed.warnings.length + " 项失效绑定:" + composed.warnings[0]);
406674
+ const action = item.clickAction ?? doc.clickAction;
406675
+ closeMenu();
406676
+ const result = action === "fill" ? await host.fill(composed.text) : await host.send(composed.text);
406677
+ if (!result.ok) host.notify(result.message ?? "操作失败");
406678
+ }
406679
+ /**
406680
+ * 复制一项(同组同级追加副本,名字加「副本」)。
406681
+ * @author ddj 2026年10月30号
406682
+ * @param item 源项
406683
+ */
406684
+ async function duplicate(item) {
406685
+ const copy = item.type === "group" ? {
406686
+ ...item,
406687
+ id: store.nextId("group"),
406688
+ name: item.name + " 副本",
406689
+ order: item.order + 1
406690
+ } : {
406691
+ ...item,
406692
+ id: store.nextId("button"),
406693
+ name: item.name + " 副本",
406694
+ order: item.order + 1
406695
+ };
406696
+ const result = await store.createItem(copy);
406697
+ if (!result.ok) host.notify(result.error ?? "复制失败");
406698
+ setRowMenu(null);
406699
+ }
406700
+ /**
406701
+ * 删除一项(组连带子项)。
406702
+ * @author ddj 2026年10月30号
406703
+ * @param item 目标项
406704
+ */
406705
+ async function removeItem(item) {
406706
+ setRowMenu(null);
406707
+ const suffix = item.type === "group" ? "及其全部子项" : "";
406708
+ if (!window.confirm("确认删除「" + item.name + "」" + suffix + "?")) return;
406709
+ const result = await store.removeItem(item.id);
406710
+ if (!result.ok) host.notify(result.error ?? "删除失败");
406711
+ setOpenPath([]);
406712
+ }
406713
+ const launcher = /* @__PURE__ */ react$1.default.createElement("div", {
406714
+ className: "edrv-qm-launcher" + (dragging ? " edrv-qm-launcher--drag" : "") + (open ? " edrv-qm-launcher--open" : ""),
406715
+ style: {
406716
+ left: launcherPos.left,
406717
+ top: launcherPos.top
406718
+ },
406719
+ title: "快捷菜单(拖动可移动,右键更多)",
406720
+ role: "button",
406721
+ tabIndex: 0,
406722
+ "aria-label": "快捷菜单",
406723
+ "aria-expanded": open,
406724
+ onPointerDown: beginDrag,
406725
+ onKeyDown: (event) => {
406726
+ if (event.key !== "Enter" && event.key !== " ") return;
406727
+ event.preventDefault();
406728
+ if (open) closeMenu();
406729
+ else setOpen(true);
406730
+ },
406731
+ onContextMenu: (event) => {
406732
+ event.preventDefault();
406733
+ setOpen(false);
406734
+ setDialog({ kind: "global" });
406735
+ }
406736
+ }, /* @__PURE__ */ react$1.default.createElement("svg", {
406737
+ viewBox: "0 0 23.16 17.04",
406738
+ "aria-hidden": "true"
406739
+ }, /* @__PURE__ */ react$1.default.createElement("path", {
406740
+ d: WHALE_PATH,
406741
+ fill: "currentColor"
406742
+ })));
406743
+ const miniDot = /* @__PURE__ */ react$1.default.createElement("div", {
406744
+ className: "edrv-qm-launcher edrv-qm-launcher--mini",
406745
+ style: {
406746
+ left: launcherPos.left,
406747
+ top: launcherPos.top
406748
+ },
406749
+ title: "快捷菜单已隐藏,点击展开(右键打开设置)",
406750
+ role: "button",
406751
+ tabIndex: 0,
406752
+ "aria-label": "快捷菜单(已隐藏)",
406753
+ onPointerDown: beginDrag,
406754
+ onKeyDown: (event) => {
406755
+ if (event.key !== "Enter" && event.key !== " ") return;
406756
+ event.preventDefault();
406757
+ if (open) closeMenu();
406758
+ else setOpen(true);
406759
+ },
406760
+ onContextMenu: (event) => {
406761
+ event.preventDefault();
406762
+ setOpen(false);
406763
+ setDialog({ kind: "global" });
406764
+ }
406765
+ });
406766
+ const panels = open ? columnRects.map((rect, depth) => {
406767
+ const parentId = depth === 0 ? null : openPath[depth - 1];
406768
+ return /* @__PURE__ */ react$1.default.createElement(QuickColumn, {
406769
+ key: depth,
406770
+ depth,
406771
+ rect,
406772
+ items: childrenOf(doc, parentId, ctx.cwd),
406773
+ canAdd: canCreateAt(doc, parentId),
406774
+ ctx,
406775
+ status: state.status,
406776
+ error: state.error,
406777
+ activeIndex: active.depth === depth ? active.index : -1,
406778
+ scrollTop: scrolls[depth] ?? 0,
406779
+ onScroll: (top) => setScrolls((prev) => ({
406780
+ ...prev,
406781
+ [depth]: top
406782
+ })),
406783
+ onHover: (index, item) => hoverItem(depth, index, item),
406784
+ onActivate: (item) => {
406785
+ activate(item);
406786
+ },
406787
+ onMore: (item, rowRect) => setRowMenu({
406788
+ item,
406789
+ rect: rowRect
406790
+ }),
406791
+ onAdd: () => setDialog({
406792
+ kind: "item",
406793
+ parentId
406794
+ }),
406795
+ onSettings: depth === 0 ? () => {
406796
+ setOpen(false);
406797
+ setDialog({ kind: "global" });
406798
+ } : void 0,
406799
+ onRetry: () => {
406800
+ store.load(true);
406801
+ },
406802
+ onEmptyHint: depth === 0 ? "把常做的提问连同技能、规则存成一个按钮,点一下就发出去。" : "组里还没有内容,新建一个按钮或子组。"
406803
+ });
406804
+ }) : null;
406805
+ return /* @__PURE__ */ react$1.default.createElement(react$1.default.Fragment, null, /* @__PURE__ */ react$1.default.createElement("div", {
406806
+ "data-edrv-view": "1",
406807
+ className: "edrv-qm-root",
406808
+ "data-qm-root": "1"
406809
+ }, doc.enabled ? launcher : miniDot), open ? (0, react_dom.createPortal)(/* @__PURE__ */ react$1.default.createElement("div", {
406810
+ "data-edrv-view": "1",
406811
+ className: "edrv-qm-menu",
406812
+ "data-qm-root": "1"
406813
+ }, panels, rowMenu ? /* @__PURE__ */ react$1.default.createElement(RowMenu, {
406814
+ state: rowMenu,
406815
+ onEdit: () => {
406816
+ setDialog({
406817
+ kind: "item",
406818
+ item: rowMenu.item
406819
+ });
406820
+ setRowMenu(null);
406821
+ },
406822
+ onDuplicate: () => {
406823
+ duplicate(rowMenu.item);
406824
+ },
406825
+ onDelete: () => {
406826
+ removeItem(rowMenu.item);
406827
+ }
406828
+ }) : null), document.body) : null, dialog.kind === "item" ? /* @__PURE__ */ react$1.default.createElement(QuickItemDialog, {
406829
+ host,
406830
+ item: dialog.item,
406831
+ parentId: dialog.item ? dialog.item.parentId : dialog.parentId ?? null,
406832
+ onClose: () => setDialog({ kind: "none" })
406833
+ }) : null, dialog.kind === "global" ? /* @__PURE__ */ react$1.default.createElement(MenuGlobalDialog, {
406834
+ host,
406835
+ onClose: () => setDialog({ kind: "none" })
406836
+ }) : null);
406837
+ }
406838
+ /**
406839
+ * 一列(纵向滑动列表 + 末行「+ 新建」 + root 底部设置)。
406840
+ * @author ddj 2026年10月30号
406841
+ * @param props 列数据与回调
406842
+ * @returns 列元素
406843
+ */
406844
+ function QuickColumn(props) {
406845
+ const scrollRef = react$1.default.useRef(null);
406846
+ const count = props.items.length + (props.canAdd ? 1 : 0);
406847
+ const isEmpty = props.items.length === 0 && props.status !== "loading" && props.status !== "error";
406848
+ const [overflow, setOverflow] = react$1.default.useState(false);
406849
+ react$1.default.useEffect(() => {
406850
+ props.onScroll(scrollRef.current?.scrollTop ?? 0);
406851
+ }, []);
406852
+ react$1.default.useEffect(() => {
406853
+ const viewport = scrollRef.current;
406854
+ if (!viewport) return;
406855
+ const measure = () => setOverflow(viewport.scrollHeight > viewport.clientHeight + 8);
406856
+ measure();
406857
+ const observer = new ResizeObserver(measure);
406858
+ observer.observe(viewport);
406859
+ return () => observer.disconnect();
406860
+ }, [
406861
+ props.items.length,
406862
+ props.canAdd,
406863
+ props.status
406864
+ ]);
406865
+ return /* @__PURE__ */ react$1.default.createElement("div", {
406866
+ className: "edrv-qm-col",
406867
+ style: {
406868
+ left: props.rect.left,
406869
+ top: props.rect.top,
406870
+ height: props.rect.height
406871
+ },
406872
+ role: "menu"
406873
+ }, /* @__PURE__ */ react$1.default.createElement("div", {
406874
+ className: "edrv-qm-scroll" + (overflow ? " edrv-qm-scroll--fade" : ""),
406875
+ ref: scrollRef,
406876
+ onScroll: (event) => props.onScroll(event.currentTarget.scrollTop)
406877
+ }, 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", {
406878
+ type: "button",
406879
+ className: "edrv-qm-btn",
406880
+ onClick: props.onRetry
406881
+ }, "重试")) : 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", {
406882
+ type: "button",
406883
+ className: "edrv-qm-btn edrv-qm-btn--primary",
406884
+ onClick: props.onAdd
406885
+ }, props.depth === 0 ? "新建第一个" : "新建一项") : null) : null, /* @__PURE__ */ react$1.default.createElement("div", { className: "edrv-qm-list" }, props.items.map((item, index) => {
406886
+ const scale = index === props.activeIndex ? {
406887
+ scale: 1,
406888
+ opacity: 1
406889
+ } : centerScale(index, count);
406890
+ const flags = itemStatus(item, props.ctx);
406891
+ const badge = flags.errors.length > 0 ? " edrv-qm-row-badge--error" : "";
406892
+ const tip = flags.errors.concat(flags.warnings).join("\n");
406893
+ return /* @__PURE__ */ react$1.default.createElement("div", {
406894
+ key: item.id,
406895
+ className: "edrv-qm-row" + (item.type === "group" ? " edrv-qm-row--group" : "") + (props.activeIndex === index ? " edrv-qm-row--active" : ""),
406896
+ style: {
406897
+ transform: "scale(" + scale.scale.toFixed(3) + ")",
406898
+ opacity: scale.opacity
406899
+ },
406900
+ title: tip,
406901
+ "data-qm-row": "1",
406902
+ role: "menuitem",
406903
+ tabIndex: -1,
406904
+ "aria-label": item.type === "group" ? item.name + "(组)" : item.name,
406905
+ onPointerEnter: () => props.onHover(index, item),
406906
+ onClick: () => props.onActivate(item)
406907
+ }, /* @__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", {
406908
+ className: "edrv-qm-row-chevron",
406909
+ "aria-hidden": "true"
406910
+ }, /* @__PURE__ */ react$1.default.createElement("svg", {
406911
+ viewBox: "0 0 12 12",
406912
+ fill: "none",
406913
+ stroke: "currentColor",
406914
+ strokeWidth: "1.6",
406915
+ strokeLinecap: "round",
406916
+ strokeLinejoin: "round"
406917
+ }, /* @__PURE__ */ react$1.default.createElement("path", { d: "M4.5 2.5 8 6l-3.5 3.5" }))) : null, /* @__PURE__ */ react$1.default.createElement("button", {
406918
+ type: "button",
406919
+ className: "edrv-qm-row-more",
406920
+ title: "编辑 / 复制 / 删除",
406921
+ "aria-label": "「" + item.name + "」的更多操作",
406922
+ onClick: (event) => {
406923
+ event.stopPropagation();
406924
+ const rowRect = {
406925
+ left: props.rect.left,
406926
+ top: props.rect.top + COL_PAD + index * ROW_HEIGHT - props.scrollTop,
406927
+ width: COL_WIDTH,
406928
+ height: ROW_HEIGHT
406929
+ };
406930
+ props.onMore(item, rowRect);
406931
+ }
406932
+ }, "⋯"));
406933
+ }), props.canAdd && !isEmpty ? /* @__PURE__ */ react$1.default.createElement("div", {
406934
+ className: "edrv-qm-row edrv-qm-row--add",
406935
+ "data-qm-row": "1",
406936
+ role: "menuitem",
406937
+ "aria-label": "新建",
406938
+ onClick: props.onAdd
406939
+ }, "+ 新建") : null)), props.onSettings ? /* @__PURE__ */ react$1.default.createElement("div", {
406940
+ className: "edrv-qm-foot",
406941
+ onClick: props.onSettings,
406942
+ title: "显示、点击动作、最大嵌套层数、导入导出",
406943
+ role: "button",
406944
+ tabIndex: 0,
406945
+ onKeyDown: (event) => {
406946
+ if (event.key !== "Enter" && event.key !== " ") return;
406947
+ event.preventDefault();
406948
+ props.onSettings?.();
406949
+ }
406950
+ }, /* @__PURE__ */ react$1.default.createElement("span", {
406951
+ className: "edrv-qm-foot-icon",
406952
+ "aria-hidden": "true"
406953
+ }, /* @__PURE__ */ react$1.default.createElement("svg", {
406954
+ viewBox: "0 0 16 16",
406955
+ fill: "none",
406956
+ stroke: "currentColor",
406957
+ strokeWidth: "1.3",
406958
+ strokeLinecap: "round"
406959
+ }, /* @__PURE__ */ react$1.default.createElement("circle", {
406960
+ cx: "8",
406961
+ cy: "8",
406962
+ r: "2.3"
406963
+ }), /* @__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);
406964
+ }
406965
+ /**
406966
+ * 行内小菜单(编辑 / 复制 / 删除)。
406967
+ * @author ddj 2026年10月30号
406968
+ * @param props 状态与动作
406969
+ * @returns 小菜单元素
406970
+ */
406971
+ function RowMenu(props) {
406972
+ return /* @__PURE__ */ react$1.default.createElement("div", {
406973
+ className: "edrv-qm-context",
406974
+ style: {
406975
+ left: props.state.rect.left + COL_WIDTH - 10,
406976
+ top: props.state.rect.top + 26
406977
+ },
406978
+ "data-qm-root": "1",
406979
+ role: "menu"
406980
+ }, /* @__PURE__ */ react$1.default.createElement("div", {
406981
+ className: "edrv-qm-context-item",
406982
+ role: "menuitem",
406983
+ onClick: props.onEdit
406984
+ }, "编辑"), /* @__PURE__ */ react$1.default.createElement("div", {
406985
+ className: "edrv-qm-context-item",
406986
+ role: "menuitem",
406987
+ onClick: props.onDuplicate
406988
+ }, "复制"), /* @__PURE__ */ react$1.default.createElement("div", { className: "edrv-qm-context-sep" }), /* @__PURE__ */ react$1.default.createElement("div", {
406989
+ className: "edrv-qm-context-item edrv-qm-context-item--danger",
406990
+ role: "menuitem",
406991
+ onClick: props.onDelete
406992
+ }, "删除"));
406993
+ }
403872
406994
  //#endregion
403873
406995
  //#region src/client/sidebar/registry.ts
403874
406996
  /** 校验面板定义并写入注册表(缺 id/render 抛 TypeError)。 */
@@ -409563,6 +412685,154 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
409563
412685
  /** 「添加到对话」动作集:编辑区/Tab 右键菜单注入文件引用与代码块(conversation 服务缺失时各动作安全降级)。 */
409564
412686
  const addToConversation = createAddToConversation(ctx);
409565
412687
  /**
412688
+ * 读取规则列表(rules.list;失败抛错,由快捷菜单按「未校验」降级)。
412689
+ * @author ddj 2026年10月30号
412690
+ * @returns 用户规则 + 项目规则
412691
+ */
412692
+ const loadRules = async () => {
412693
+ const result = await rpc("rules.list", {});
412694
+ if (!result.ok) throw new Error(result.error);
412695
+ return {
412696
+ user: result.user,
412697
+ projects: result.projects
412698
+ };
412699
+ };
412700
+ /**
412701
+ * 会话技能枚举服务(remote.skills 动态注入;旧版 DSH 无该服务时保持 null = 「未校验」)。
412702
+ * @author ddj 2026年10月30号
412703
+ */
412704
+ let skillEnumerator = null;
412705
+ /**
412706
+ * 读取会话可见技能名(服务不可用时返回 null,界面按「未校验」处理)。
412707
+ * @author ddj 2026年10月30号
412708
+ * @param sessionId 会话 id
412709
+ * @returns 技能名清单或 null
412710
+ */
412711
+ const loadSkillNames = async (sessionId) => {
412712
+ const list = skillEnumerator?.list;
412713
+ if (typeof list !== "function") return null;
412714
+ const result = await list.call(skillEnumerator, { sessionId });
412715
+ return ((result.value ?? result)?.skills ?? []).map((entry) => entry.name).filter((name) => typeof name === "string");
412716
+ };
412717
+ /**
412718
+ * 读取技能候选(全局技能 + 当前工作区技能;只读枚举,失败返回 null)。
412719
+ * @author ddj 2026年10月30号
412720
+ * @param cwd 目标工作区(缺省取当前会话作用域)
412721
+ * @returns 技能候选快照;host 无该 RPC 或读取失败时为 null
412722
+ */
412723
+ const loadSkillCatalog = async (cwd) => {
412724
+ const target = cwd ?? readSessionScope(ctx).cwd;
412725
+ try {
412726
+ const result = await rpc("skills.list", target ? { cwd: target } : {});
412727
+ if (!result.ok) return null;
412728
+ return {
412729
+ user: result.user,
412730
+ project: result.project,
412731
+ ...result.projectRoot ? { projectRoot: result.projectRoot } : {}
412732
+ };
412733
+ } catch {
412734
+ return null;
412735
+ }
412736
+ };
412737
+ /**
412738
+ * 确保存在可写入的会话(无会话时新建独立会话)。
412739
+ * @author ddj 2026年10月30号
412740
+ * @returns 会话 id 或错误文案
412741
+ */
412742
+ const ensureSession = async () => {
412743
+ const scope = readSessionScope(ctx);
412744
+ if (scope.sessionId) return { id: scope.sessionId };
412745
+ const draft = await addToConversation.startDraftSession(scope.cwd);
412746
+ if (!draft.ok || !draft.id) return { error: "当前没有可用会话,新建会话也失败了" };
412747
+ return { id: draft.id };
412748
+ };
412749
+ /**
412750
+ * 发送快捷菜单文本(优先发到当前会话,会话忙碌时如实回报)。
412751
+ * @author ddj 2026年10月30号
412752
+ * @param text 合成后的消息文本
412753
+ * @returns 是否发送成功
412754
+ */
412755
+ const sendQuickText = async (text) => {
412756
+ const session = await ensureSession();
412757
+ if ("error" in session) return {
412758
+ ok: false,
412759
+ message: session.error
412760
+ };
412761
+ return await addToConversation.sendTask(session.id, text) ? { ok: true } : {
412762
+ ok: false,
412763
+ message: "发送被拒绝(会话可能正忙,稍后再点)"
412764
+ };
412765
+ };
412766
+ /**
412767
+ * 只把快捷菜单文本填入输入框。
412768
+ * @author ddj 2026年10月30号
412769
+ * @param text 合成后的消息文本
412770
+ * @returns 是否填入成功
412771
+ */
412772
+ const fillQuickText = async (text) => {
412773
+ const session = await ensureSession();
412774
+ if ("error" in session) return {
412775
+ ok: false,
412776
+ message: session.error
412777
+ };
412778
+ const outcome = await addToConversation.appendText(session.id, text);
412779
+ if (outcome === "ok" || outcome === "busy") return { ok: true };
412780
+ return {
412781
+ ok: false,
412782
+ message: "写入输入框失败(" + outcome + "),请重试"
412783
+ };
412784
+ };
412785
+ /** 快捷菜单宿主桥接:菜单组件不直接摸 ctx,规则/技能/发送/工作区都从这里注入。 */
412786
+ const quickMenuHost = {
412787
+ store: createMenuStore({
412788
+ rules: loadRules,
412789
+ skills: loadSkillNames,
412790
+ scope: () => {
412791
+ const scope = readSessionScope(ctx);
412792
+ return {
412793
+ sessionId: scope.sessionId,
412794
+ cwd: scope.cwd
412795
+ };
412796
+ }
412797
+ }),
412798
+ scope: () => {
412799
+ const scope = readSessionScope(ctx);
412800
+ return {
412801
+ sessionId: scope.sessionId,
412802
+ cwd: scope.cwd
412803
+ };
412804
+ },
412805
+ send: sendQuickText,
412806
+ fill: fillQuickText,
412807
+ rules: loadRules,
412808
+ skills: async () => {
412809
+ const scope = readSessionScope(ctx);
412810
+ return scope.sessionId ? loadSkillNames(scope.sessionId) : null;
412811
+ },
412812
+ skillCatalog: loadSkillCatalog,
412813
+ readRule: async (binding) => {
412814
+ const result = await rpc("rules.read", {
412815
+ scope: binding.scope,
412816
+ file: binding.file,
412817
+ ...binding.workspacePath ? { workspacePath: binding.workspacePath } : {},
412818
+ ...binding.origin ? { origin: binding.origin } : {}
412819
+ });
412820
+ if (!result.ok) throw new Error(result.error);
412821
+ return result.content;
412822
+ },
412823
+ workspaces: () => (workspaces?.list.getSnapshot().items ?? []).map((item) => ({
412824
+ path: item.path,
412825
+ title: item.title
412826
+ })),
412827
+ notify: (message) => {
412828
+ toastDom(message);
412829
+ }
412830
+ };
412831
+ if (typeof ctx.inject === "function") ctx.inject(["remote.skills"], (scope) => {
412832
+ skillEnumerator = scope.remote?.skills ?? null;
412833
+ quickMenuHost.store.refreshBindings(true);
412834
+ });
412835
+ /**
409566
412836
  * Build applicable client diagnostics from live installation state.
409567
412837
  * @private @author ddj 2026年10月13号
409568
412838
  * @param version Running DSH version supplied by the compat RPC.
@@ -409999,6 +413269,21 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
409999
413269
  id: "edrv-plugin-status",
410000
413270
  order: 40
410001
413271
  }, (props) => react$1.default.createElement(PluginDetailSection, Object.assign({}, props)));
413272
+ ctx.effect(() => {
413273
+ const dispose = registerSlotSafely(ctx, {
413274
+ name: "shell.overlay",
413275
+ id: "edrv-quick-menu",
413276
+ order: 120,
413277
+ label: "快捷菜单"
413278
+ }, () => react$1.default.createElement(QuickMenuLauncher, { host: quickMenuHost }));
413279
+ return () => {
413280
+ dispose?.();
413281
+ quickMenuHost.store.flushLauncher();
413282
+ };
413283
+ }, "vscode-mode: quick menu launcher");
413284
+ ctx.effect(() => subscribeScope(ctx, () => {
413285
+ quickMenuHost.store.refreshBindings(true);
413286
+ }), "vscode-mode: quick menu scope refresh");
410002
413287
  ctx.effect(() => () => {
410003
413288
  setPanelImport();
410004
413289
  try {