dsh-vscode-mode 0.18.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/lib/client.debug.js +934 -0
- package/lib/client.debug.js.map +1 -0
- package/lib/client.js +990 -1705
- package/lib/client.js.map +1 -1
- package/lib/client.rules.js +793 -0
- package/lib/client.rules.js.map +1 -0
- package/lib/index.js +236 -42
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/externalOpen.ts +7 -1
- package/src/client/index.ts +9 -0
- package/src/client/lazyPanel.ts +68 -0
- package/src/client/monaco/loader.ts +2 -0
- package/src/client/monaco/theme.ts +11 -0
- package/src/client/openReceipt.ts +4 -1
- package/src/client/requestId.ts +20 -0
- package/src/client/sidebar/PanelBoundary.ts +57 -0
- package/src/client/sidebar/SidebarView.ts +58 -21
- package/src/client/sidebar/panels/FileExplorer.ts +80 -27
- package/src/client/sidebar/panels/SvnPanel.ts +24 -8
- package/src/client/sidebar/panels/index.ts +3 -4
- package/src/client/sidebar/panels/treeWindow.ts +106 -0
- package/src/client/sidebar/panels/useExplorerReveal.ts +5 -2
- package/src/client/sidebar/registry.ts +4 -1
- package/src/client/state/uiStatePersist.ts +84 -23
- package/src/client/styles/editor.css +8 -0
- package/src/client/textPages.ts +135 -0
- package/src/client/ui/EditorView.ts +74 -16
- package/src/client/ui/LargeTextView.ts +94 -0
- package/src/rpc.ts +29 -7
- package/src/shared/rpc.ts +3 -1
- package/src/shared/snippets.ts +1 -1
- package/src/shared/textPages.ts +28 -0
- package/src/textPages.ts +149 -0
- package/src/treeIndex.ts +62 -37
package/lib/client.js
CHANGED
|
@@ -29,11 +29,11 @@ window.__ModuleLoader__.load({
|
|
|
29
29
|
let react = require("react");
|
|
30
30
|
react = __toESM(react, 1);
|
|
31
31
|
let react_dom = require("react-dom");
|
|
32
|
+
let react_dom_client = require("react-dom/client");
|
|
32
33
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
33
34
|
_deepseek_ai_dsh_client_ui_primitives = __toESM(_deepseek_ai_dsh_client_ui_primitives, 1);
|
|
34
|
-
let react_dom_client = require("react-dom/client");
|
|
35
35
|
//#region \0dsh-css:/home/runner/work/DSH_VsCodeMode/DSH_VsCodeMode/src/client/styles/editor.css.mjs
|
|
36
|
-
const css$1 = "/* 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/* ===== 侧边栏(活动栏 + 面板区 + 文件树) ===== */\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[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";
|
|
36
|
+
const css$1 = "/* 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/* ===== 侧边栏(活动栏 + 面板区 + 文件树) ===== */\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";
|
|
37
37
|
const tagId$1 = "dsh-vscode-mode/editor.css";
|
|
38
38
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
39
39
|
const tag = document.createElement("style");
|
|
@@ -199,7 +199,7 @@ window.__ModuleLoader__.load({
|
|
|
199
199
|
* 作者 ddj 2026-08-28
|
|
200
200
|
*/
|
|
201
201
|
/** localStorage 键前缀(按工作区作用域隔离,无 cwd 会话回退会话键)。 */
|
|
202
|
-
const KEY_PREFIX$
|
|
202
|
+
const KEY_PREFIX$4 = CACHE_KEY.viewstate;
|
|
203
203
|
/**
|
|
204
204
|
* 判断对象是否为合法的 Monaco view state(保守形状校验,非深校验)。
|
|
205
205
|
* @author ddj 2026年08月28号
|
|
@@ -277,7 +277,7 @@ window.__ModuleLoader__.load({
|
|
|
277
277
|
*/
|
|
278
278
|
function viewStatesLoad(scope) {
|
|
279
279
|
try {
|
|
280
|
-
return parseViewStates(window.localStorage.getItem(KEY_PREFIX$
|
|
280
|
+
return parseViewStates(window.localStorage.getItem(KEY_PREFIX$4 + String(scope))) ?? {};
|
|
281
281
|
} catch (error) {
|
|
282
282
|
return {};
|
|
283
283
|
}
|
|
@@ -290,7 +290,7 @@ window.__ModuleLoader__.load({
|
|
|
290
290
|
*/
|
|
291
291
|
function viewStatesSave(scope, states) {
|
|
292
292
|
try {
|
|
293
|
-
window.localStorage.setItem(KEY_PREFIX$
|
|
293
|
+
window.localStorage.setItem(KEY_PREFIX$4 + String(scope), serializeViewStates(states));
|
|
294
294
|
} catch (error) {}
|
|
295
295
|
}
|
|
296
296
|
//#endregion
|
|
@@ -1505,6 +1505,236 @@ window.__ModuleLoader__.load({
|
|
|
1505
1505
|
actions
|
|
1506
1506
|
}, props.children);
|
|
1507
1507
|
}
|
|
1508
|
+
const TEXT_VIEW_BYTES = 8388608;
|
|
1509
|
+
//#endregion
|
|
1510
|
+
//#region src/client/textPages.ts
|
|
1511
|
+
/**
|
|
1512
|
+
* Own one preview identity and one in-flight request; no polling or automatic continuation.
|
|
1513
|
+
* @public @author ddj 2026年10月22号
|
|
1514
|
+
* @param load Authorized RPC loader. @param changed Snapshot observer.
|
|
1515
|
+
* @returns Pager lifecycle and explicit load controls.
|
|
1516
|
+
*/
|
|
1517
|
+
function createTextPager(load, changed) {
|
|
1518
|
+
let generation = 0;
|
|
1519
|
+
let disposed = false;
|
|
1520
|
+
let target = null;
|
|
1521
|
+
let cursor;
|
|
1522
|
+
let version;
|
|
1523
|
+
let inFlight = false;
|
|
1524
|
+
let retainedBytes = 0;
|
|
1525
|
+
let state = emptyState();
|
|
1526
|
+
/** @private @author ddj 2026年10月22号 @returns Publish immutable snapshot. */
|
|
1527
|
+
function publish() {
|
|
1528
|
+
changed({ ...state });
|
|
1529
|
+
}
|
|
1530
|
+
/** @public @author ddj 2026年10月22号 @param args New file/session identity. @returns Reset state; old responses are ignored. */
|
|
1531
|
+
function reset(args) {
|
|
1532
|
+
generation++;
|
|
1533
|
+
target = {
|
|
1534
|
+
sessionId: args.sessionId,
|
|
1535
|
+
path: args.path
|
|
1536
|
+
};
|
|
1537
|
+
cursor = void 0;
|
|
1538
|
+
version = void 0;
|
|
1539
|
+
retainedBytes = 0;
|
|
1540
|
+
state = emptyState();
|
|
1541
|
+
state.loading = inFlight;
|
|
1542
|
+
if (!disposed) publish();
|
|
1543
|
+
}
|
|
1544
|
+
/** @public @author ddj 2026年10月22号 @returns Explicitly load one page, or no-op when busy/finished. */
|
|
1545
|
+
async function loadNext() {
|
|
1546
|
+
if (disposed || !target || inFlight || state.eof || state.capped) return;
|
|
1547
|
+
state.error = null;
|
|
1548
|
+
const requestGeneration = generation;
|
|
1549
|
+
const request = {
|
|
1550
|
+
...target,
|
|
1551
|
+
cursor
|
|
1552
|
+
};
|
|
1553
|
+
inFlight = true;
|
|
1554
|
+
state.loading = true;
|
|
1555
|
+
publish();
|
|
1556
|
+
try {
|
|
1557
|
+
const page = await load(request);
|
|
1558
|
+
if (disposed || requestGeneration !== generation) return;
|
|
1559
|
+
if (!page.ok) {
|
|
1560
|
+
state.error = page.error;
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
if (!validPage(page, state.offset, version)) {
|
|
1564
|
+
state.error = "Invalid or stale text page";
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
appendPage(page);
|
|
1568
|
+
} catch (error) {
|
|
1569
|
+
if (!disposed && requestGeneration === generation) state.error = String(error);
|
|
1570
|
+
} finally {
|
|
1571
|
+
inFlight = false;
|
|
1572
|
+
if (!disposed) {
|
|
1573
|
+
state.loading = false;
|
|
1574
|
+
publish();
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
/** @private @author ddj 2026年10月22号 @param page Verified page. @returns Append only within byte/line limits. */
|
|
1579
|
+
function appendPage(page) {
|
|
1580
|
+
let text = page.text;
|
|
1581
|
+
const encoded = new TextEncoder().encode(text);
|
|
1582
|
+
const remaining = TEXT_VIEW_BYTES - retainedBytes;
|
|
1583
|
+
if (encoded.length > remaining) {
|
|
1584
|
+
let end = remaining;
|
|
1585
|
+
while (end > 0 && (encoded[end] & 192) === 128) end--;
|
|
1586
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(encoded.subarray(0, end));
|
|
1587
|
+
state.capped = true;
|
|
1588
|
+
}
|
|
1589
|
+
let lines = state.lines;
|
|
1590
|
+
for (let index = 0; index < text.length; index++) {
|
|
1591
|
+
if (text[index] !== "\n" && (text[index] !== "\r" || text[index + 1] === "\n")) continue;
|
|
1592
|
+
if (lines >= 5e4) {
|
|
1593
|
+
const end = text[index] === "\n" && text[index - 1] === "\r" ? index - 1 : index;
|
|
1594
|
+
text = text.slice(0, end);
|
|
1595
|
+
state.capped = true;
|
|
1596
|
+
break;
|
|
1597
|
+
}
|
|
1598
|
+
lines++;
|
|
1599
|
+
}
|
|
1600
|
+
retainedBytes += new TextEncoder().encode(text).length;
|
|
1601
|
+
state.text += text;
|
|
1602
|
+
state.lines = lines;
|
|
1603
|
+
state.offset = page.offset;
|
|
1604
|
+
state.size = page.size;
|
|
1605
|
+
state.eof = page.eof;
|
|
1606
|
+
state.partialLine = page.partialLine || state.capped;
|
|
1607
|
+
state.capped ||= page.offset >= 8388608 || retainedBytes >= 8388608 || lines >= 5e4;
|
|
1608
|
+
version = page.version;
|
|
1609
|
+
cursor = page.nextCursor ?? void 0;
|
|
1610
|
+
}
|
|
1611
|
+
/** @public @author ddj 2026年10月22号 @returns Invalidate identity while retaining the transport lock. */
|
|
1612
|
+
function invalidate() {
|
|
1613
|
+
generation++;
|
|
1614
|
+
target = null;
|
|
1615
|
+
state = emptyState();
|
|
1616
|
+
}
|
|
1617
|
+
/** @public @author ddj 2026年10月22号 @returns Invalidate pending responses and release preview content. */
|
|
1618
|
+
function dispose() {
|
|
1619
|
+
disposed = true;
|
|
1620
|
+
invalidate();
|
|
1621
|
+
}
|
|
1622
|
+
return {
|
|
1623
|
+
reset,
|
|
1624
|
+
loadNext,
|
|
1625
|
+
invalidate,
|
|
1626
|
+
dispose,
|
|
1627
|
+
getState: () => ({ ...state })
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
/** @private @author ddj 2026年10月22号 @returns Empty bounded preview state. */
|
|
1631
|
+
function emptyState() {
|
|
1632
|
+
return {
|
|
1633
|
+
text: "",
|
|
1634
|
+
loading: false,
|
|
1635
|
+
eof: false,
|
|
1636
|
+
capped: false,
|
|
1637
|
+
error: null,
|
|
1638
|
+
offset: 0,
|
|
1639
|
+
size: 0,
|
|
1640
|
+
lines: 1,
|
|
1641
|
+
partialLine: false
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
/**
|
|
1645
|
+
* Validate the RPC envelope before retaining any content.
|
|
1646
|
+
* @private @author ddj 2026年10月22号
|
|
1647
|
+
* @param page Server page. @param offset Previous byte position. @param version Bound version.
|
|
1648
|
+
* @returns Whether bounds and continuation invariants hold.
|
|
1649
|
+
*/
|
|
1650
|
+
function validPage(page, offset, version) {
|
|
1651
|
+
return typeof page.text === "string" && typeof page.version === "string" && (version === void 0 || version === page.version) && Number.isSafeInteger(page.offset) && Number.isSafeInteger(page.size) && page.size >= 0 && page.offset >= offset && page.offset <= page.size && (page.offset > offset || page.eof) && page.offset - offset <= 262144 && new TextEncoder().encode(page.text).length <= 262145 && page.encoding === "utf-8" && typeof page.partialLine === "boolean" && typeof page.eof === "boolean" && (page.eof ? page.nextCursor === null : typeof page.nextCursor === "string" && page.nextCursor.length > 0) && page.eof === (page.offset === page.size);
|
|
1652
|
+
}
|
|
1653
|
+
//#endregion
|
|
1654
|
+
//#region src/client/ui/LargeTextView.ts
|
|
1655
|
+
const Button$1 = _deepseek_ai_dsh_client_ui_primitives.Button;
|
|
1656
|
+
/**
|
|
1657
|
+
* Read-only paged text surface: never creates a Monaco model or exposes a save action.
|
|
1658
|
+
* @public @author ddj 2026年10月22号
|
|
1659
|
+
* @param props Exact session/file identity and authorized page loader.
|
|
1660
|
+
* @returns Bounded selectable text with explicit continuation and status.
|
|
1661
|
+
*/
|
|
1662
|
+
function LargeTextView(props) {
|
|
1663
|
+
const [snapshot, setSnapshot] = react.default.useState(null);
|
|
1664
|
+
const [identity, setIdentity] = react.default.useState("");
|
|
1665
|
+
const pagerRef = react.default.useRef(null);
|
|
1666
|
+
const loaderRef = react.default.useRef(props.loadPage);
|
|
1667
|
+
const mountedRef = react.default.useRef(false);
|
|
1668
|
+
const firstPending = react.default.useRef(false);
|
|
1669
|
+
loaderRef.current = props.loadPage;
|
|
1670
|
+
const currentIdentity = JSON.stringify([props.sessionId, props.path]);
|
|
1671
|
+
/** @private @author ddj 2026年10月22号 @param args Request identity. @returns Current injected loader response. */
|
|
1672
|
+
function loadPage(args) {
|
|
1673
|
+
return loaderRef.current(args);
|
|
1674
|
+
}
|
|
1675
|
+
/** @private @author ddj 2026年10月22号 @param state Latest pager snapshot. @returns Publish only while mounted. */
|
|
1676
|
+
function onSnapshot(state) {
|
|
1677
|
+
if (!mountedRef.current) return;
|
|
1678
|
+
setSnapshot(state);
|
|
1679
|
+
if (!state.loading && firstPending.current) queueMicrotask(loadFirst);
|
|
1680
|
+
}
|
|
1681
|
+
/** @private @author ddj 2026年10月22号 @description Start only the new identity's deferred first page, never automatic continuation. */
|
|
1682
|
+
function loadFirst() {
|
|
1683
|
+
const pager = pagerRef.current;
|
|
1684
|
+
if (!mountedRef.current || !firstPending.current || !pager || pager.getState().loading || typeof document !== "undefined" && document.hidden) return;
|
|
1685
|
+
firstPending.current = false;
|
|
1686
|
+
pager.loadNext();
|
|
1687
|
+
}
|
|
1688
|
+
/** @private @author ddj 2026年10月22号 @returns Bind identity without replacing the single transport lock. */
|
|
1689
|
+
function mountPager() {
|
|
1690
|
+
mountedRef.current = true;
|
|
1691
|
+
if (!pagerRef.current) pagerRef.current = createTextPager(loadPage, onSnapshot);
|
|
1692
|
+
const pager = pagerRef.current;
|
|
1693
|
+
setIdentity(currentIdentity);
|
|
1694
|
+
firstPending.current = true;
|
|
1695
|
+
pager.reset({
|
|
1696
|
+
sessionId: props.sessionId,
|
|
1697
|
+
path: props.path
|
|
1698
|
+
});
|
|
1699
|
+
loadFirst();
|
|
1700
|
+
if (typeof document !== "undefined") document.addEventListener("visibilitychange", loadFirst);
|
|
1701
|
+
/** @private @author ddj 2026年10月22号 @returns Release retained text and ignore pending responses. */
|
|
1702
|
+
function unmountPager() {
|
|
1703
|
+
mountedRef.current = false;
|
|
1704
|
+
firstPending.current = false;
|
|
1705
|
+
if (typeof document !== "undefined") document.removeEventListener("visibilitychange", loadFirst);
|
|
1706
|
+
pager.invalidate();
|
|
1707
|
+
}
|
|
1708
|
+
return unmountPager;
|
|
1709
|
+
}
|
|
1710
|
+
react.default.useEffect(mountPager, [props.sessionId, props.path]);
|
|
1711
|
+
/** @private @author ddj 2026年10月22号 @returns Load exactly one additional page on user intent. */
|
|
1712
|
+
function loadNext() {
|
|
1713
|
+
if (typeof document !== "undefined" && document.hidden) return;
|
|
1714
|
+
if (firstPending.current) {
|
|
1715
|
+
loadFirst();
|
|
1716
|
+
return;
|
|
1717
|
+
}
|
|
1718
|
+
pagerRef.current?.loadNext();
|
|
1719
|
+
}
|
|
1720
|
+
const state = identity === currentIdentity ? snapshot : null;
|
|
1721
|
+
let status = "正在加载只读文本…";
|
|
1722
|
+
if (state?.error) status = state.error;
|
|
1723
|
+
else if (state?.capped) status = "已达预览上限(8 MiB / 50,000 行),请使用外部编辑器查看完整文件";
|
|
1724
|
+
else if (state?.eof) status = "文件末尾 · 只读";
|
|
1725
|
+
else if (state) status = `已读取 ${state.offset.toLocaleString()} / ${state.size.toLocaleString()} 字节 · 只读`;
|
|
1726
|
+
if (state?.partialLine) status += " · 末行尚未完整";
|
|
1727
|
+
return react.default.createElement("section", {
|
|
1728
|
+
className: "edrv-large-text",
|
|
1729
|
+
"aria-label": "大文件只读预览"
|
|
1730
|
+
}, react.default.createElement("div", { className: "edrv-large-text-toolbar" }, react.default.createElement("span", { role: "status" }, status), react.default.createElement(Button$1, {
|
|
1731
|
+
onClick: loadNext,
|
|
1732
|
+
disabled: !state || state.loading || state.eof || state.capped
|
|
1733
|
+
}, state?.loading ? "加载中…" : state?.error ? "重试本段" : "加载下一段")), react.default.createElement("pre", {
|
|
1734
|
+
className: "edrv-large-text-content",
|
|
1735
|
+
tabIndex: 0
|
|
1736
|
+
}, state?.text ?? ""));
|
|
1737
|
+
}
|
|
1508
1738
|
//#endregion
|
|
1509
1739
|
//#region src/client/tabDrag.ts
|
|
1510
1740
|
/** Internal pointer drag: never converts external text into file paths. @author ddj 2026年10月14号 */
|
|
@@ -2300,6 +2530,27 @@ window.__ModuleLoader__.load({
|
|
|
2300
2530
|
window.dispatchEvent(new CustomEvent("edrv:show-launcher", { detail: { tab } }));
|
|
2301
2531
|
}
|
|
2302
2532
|
//#endregion
|
|
2533
|
+
//#region src/client/requestId.ts
|
|
2534
|
+
/** @public @author ddj 2026年10月23号
|
|
2535
|
+
* Generate an unpredictable UUID, including on insecure LAN HTTP origins.
|
|
2536
|
+
* @returns UUID v4, or undefined when secure randomness is unavailable.
|
|
2537
|
+
*/
|
|
2538
|
+
function createRequestId() {
|
|
2539
|
+
try {
|
|
2540
|
+
if (typeof globalThis.crypto?.randomUUID === "function") return globalThis.crypto.randomUUID();
|
|
2541
|
+
} catch {}
|
|
2542
|
+
try {
|
|
2543
|
+
const bytes = /* @__PURE__ */ new Uint8Array(16);
|
|
2544
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
2545
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
2546
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
2547
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2548
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
2549
|
+
} catch {
|
|
2550
|
+
return;
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
//#endregion
|
|
2303
2554
|
//#region src/client/openReceipt.ts
|
|
2304
2555
|
const waiters = /* @__PURE__ */ new Map();
|
|
2305
2556
|
/** @public @author ddj 2026年09月28号
|
|
@@ -2334,8 +2585,10 @@ window.__ModuleLoader__.load({
|
|
|
2334
2585
|
*/
|
|
2335
2586
|
function requestOpen(path, line, column, sessionId = "", options = {}) {
|
|
2336
2587
|
if (typeof window === "undefined" || !sessionId || options.signal?.aborted) return Promise.resolve(false);
|
|
2588
|
+
const requestId = createRequestId();
|
|
2589
|
+
if (!requestId) return Promise.resolve(false);
|
|
2337
2590
|
const request = {
|
|
2338
|
-
requestId
|
|
2591
|
+
requestId,
|
|
2339
2592
|
sessionId,
|
|
2340
2593
|
path,
|
|
2341
2594
|
line,
|
|
@@ -3774,6 +4027,19 @@ window.__ModuleLoader__.load({
|
|
|
3774
4027
|
];
|
|
3775
4028
|
/** 已应用的官方主题序号(模块级计数,保证主题 id 每次唯一)。 */
|
|
3776
4029
|
let officialSeq = 0;
|
|
4030
|
+
/** @public @author ddj 2026年10月23号
|
|
4031
|
+
* Read the host content font on editor creation, preserving the original line density.
|
|
4032
|
+
* @returns Monaco font options; invalid or unavailable CSS falls back to 16px.
|
|
4033
|
+
*/
|
|
4034
|
+
function editorFont() {
|
|
4035
|
+
const raw = readCssVar("--dsh-content-font-size") ?? "";
|
|
4036
|
+
const parsed = /^\d+(?:\.\d+)?px$/i.test(raw) ? Number(raw.slice(0, -2)) : NaN;
|
|
4037
|
+
const fontSize = Number.isFinite(parsed) && parsed > 0 ? parsed : 16;
|
|
4038
|
+
return {
|
|
4039
|
+
fontSize,
|
|
4040
|
+
lineHeight: Math.round(fontSize * 21 / 16)
|
|
4041
|
+
};
|
|
4042
|
+
}
|
|
3777
4043
|
/**
|
|
3778
4044
|
* 读官方 CSS 变量(先 body 后文档根;未定义/空值/未解析 var() 返回 undefined)。
|
|
3779
4045
|
* 依据运行态实测:DSH 的 --dsw-alias-* 定义在 body 上,仅 --shiki-token-* 在根。
|
|
@@ -4189,6 +4455,14 @@ window.__ModuleLoader__.load({
|
|
|
4189
4455
|
plist: "xml",
|
|
4190
4456
|
lua51: "lua",
|
|
4191
4457
|
luac: "lua",
|
|
4458
|
+
v: "verilog",
|
|
4459
|
+
vh: "verilog",
|
|
4460
|
+
sv: "systemverilog",
|
|
4461
|
+
svh: "systemverilog",
|
|
4462
|
+
tcl: "tcl",
|
|
4463
|
+
xdc: "tcl",
|
|
4464
|
+
sdc: "tcl",
|
|
4465
|
+
ucf: "tcl",
|
|
4192
4466
|
gitattributes: "ini",
|
|
4193
4467
|
editorconfig: "ini",
|
|
4194
4468
|
env: "ini",
|
|
@@ -8433,7 +8707,7 @@ window.__ModuleLoader__.load({
|
|
|
8433
8707
|
//#endregion
|
|
8434
8708
|
//#region src/client/dap/breakpoints.ts
|
|
8435
8709
|
/** localStorage key 前缀。 */
|
|
8436
|
-
const KEY_PREFIX$
|
|
8710
|
+
const KEY_PREFIX$3 = "edrv.dap.bp.";
|
|
8437
8711
|
/**
|
|
8438
8712
|
* 读断点表(缺失/损坏返回空表)。
|
|
8439
8713
|
* @author ddj 2026年09月29号
|
|
@@ -8441,7 +8715,7 @@ window.__ModuleLoader__.load({
|
|
|
8441
8715
|
*/
|
|
8442
8716
|
function loadBpMap(scope) {
|
|
8443
8717
|
try {
|
|
8444
|
-
const raw = localStorage.getItem(KEY_PREFIX$
|
|
8718
|
+
const raw = localStorage.getItem(KEY_PREFIX$3 + scope);
|
|
8445
8719
|
if (!raw) return {};
|
|
8446
8720
|
const data = JSON.parse(raw);
|
|
8447
8721
|
if (!data || typeof data !== "object" || Array.isArray(data)) return {};
|
|
@@ -8477,8 +8751,8 @@ window.__ModuleLoader__.load({
|
|
|
8477
8751
|
*/
|
|
8478
8752
|
function saveBpMap(scope, map) {
|
|
8479
8753
|
try {
|
|
8480
|
-
if (!Object.keys(map).length) localStorage.removeItem(KEY_PREFIX$
|
|
8481
|
-
else localStorage.setItem(KEY_PREFIX$
|
|
8754
|
+
if (!Object.keys(map).length) localStorage.removeItem(KEY_PREFIX$3 + scope);
|
|
8755
|
+
else localStorage.setItem(KEY_PREFIX$3 + scope, JSON.stringify(map));
|
|
8482
8756
|
} catch {}
|
|
8483
8757
|
}
|
|
8484
8758
|
/**
|
|
@@ -8585,23 +8859,6 @@ window.__ModuleLoader__.load({
|
|
|
8585
8859
|
[path]: list
|
|
8586
8860
|
};
|
|
8587
8861
|
}
|
|
8588
|
-
/**
|
|
8589
|
-
* 推导断点行的跳转目标(调试面板点击断点项 → 打开文件并定位到断点行)。
|
|
8590
|
-
* 路径缺失或行号非 ≥1 的有限数时返回 null(提前返回,不发起无效导航)。
|
|
8591
|
-
* @author ddj 2026年09月21号
|
|
8592
|
-
* @param row 面板行视图(文件路径 + 断点条目)
|
|
8593
|
-
* @returns 跳转目标;入参不合法时返回 null
|
|
8594
|
-
*/
|
|
8595
|
-
function bpJumpTargetOf(row) {
|
|
8596
|
-
const path = typeof row?.path === "string" ? row.path : "";
|
|
8597
|
-
const line = row?.entry?.line;
|
|
8598
|
-
if (!path || typeof line !== "number" || !Number.isFinite(line) || line < 1) return null;
|
|
8599
|
-
return {
|
|
8600
|
-
path,
|
|
8601
|
-
line: Math.floor(line),
|
|
8602
|
-
column: 1
|
|
8603
|
-
};
|
|
8604
|
-
}
|
|
8605
8862
|
//#endregion
|
|
8606
8863
|
//#region src/client/dap/pidPick.ts
|
|
8607
8864
|
/**
|
|
@@ -10913,6 +11170,82 @@ window.__ModuleLoader__.load({
|
|
|
10913
11170
|
}, "✕ 关闭")), react.default.createElement("div", { className: "edrv-launch-body" }, body));
|
|
10914
11171
|
}
|
|
10915
11172
|
//#endregion
|
|
11173
|
+
//#region src/client/sidebar/PanelBoundary.ts
|
|
11174
|
+
/** Panel-local callback and React subtree isolation, not a security sandbox. @author ddj 2026年10月22号 */
|
|
11175
|
+
const panelIds = /* @__PURE__ */ new WeakMap();
|
|
11176
|
+
let nextPanelId = 0;
|
|
11177
|
+
/** @private @author ddj 2026年10月22号 @param props Subtree ownership. @returns Stable identity that changes on registration, scope or session replacement. */
|
|
11178
|
+
function ownerKey(props) {
|
|
11179
|
+
if (!panelIds.has(props.panel)) panelIds.set(props.panel, ++nextPanelId);
|
|
11180
|
+
return JSON.stringify([
|
|
11181
|
+
panelIds.get(props.panel),
|
|
11182
|
+
props.scope,
|
|
11183
|
+
props.sessionId
|
|
11184
|
+
]);
|
|
11185
|
+
}
|
|
11186
|
+
/** @public @author ddj 2026年10月22号 @param panel Registered definition. @param ctx Current scope. @returns Hidden marker or isolated visible/badge result; failed guards stay reachable for retry. */
|
|
11187
|
+
function inspectPanel(panel, ctx) {
|
|
11188
|
+
try {
|
|
11189
|
+
if (panel.visible && panel.visible(ctx) !== true) return null;
|
|
11190
|
+
return {
|
|
11191
|
+
panel,
|
|
11192
|
+
badge: panel.badge?.(ctx) ?? null
|
|
11193
|
+
};
|
|
11194
|
+
} catch (error) {
|
|
11195
|
+
return {
|
|
11196
|
+
panel,
|
|
11197
|
+
badge: null,
|
|
11198
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
11199
|
+
};
|
|
11200
|
+
}
|
|
11201
|
+
}
|
|
11202
|
+
/** @public @author ddj 2026年10月22号 @description Reset a failed subtree when registration or scope ownership changes. */
|
|
11203
|
+
var PanelBoundary = class extends react.default.Component {
|
|
11204
|
+
state = {
|
|
11205
|
+
panel: this.props.panel,
|
|
11206
|
+
scope: this.props.scope,
|
|
11207
|
+
sessionId: this.props.sessionId,
|
|
11208
|
+
error: null
|
|
11209
|
+
};
|
|
11210
|
+
/** @public @author ddj 2026年10月22号 @param props Current ownership. @param state Previous ownership/error. @returns Reset only when the owner changes. */
|
|
11211
|
+
static getDerivedStateFromProps(props, state) {
|
|
11212
|
+
if (state.panel === props.panel && state.scope === props.scope && state.sessionId === props.sessionId) return null;
|
|
11213
|
+
return {
|
|
11214
|
+
panel: props.panel,
|
|
11215
|
+
scope: props.scope,
|
|
11216
|
+
sessionId: props.sessionId,
|
|
11217
|
+
error: null
|
|
11218
|
+
};
|
|
11219
|
+
}
|
|
11220
|
+
/** @public @author ddj 2026年10月22号 @param error React render/lifecycle error. @returns Local fallback state. */
|
|
11221
|
+
static getDerivedStateFromError(error) {
|
|
11222
|
+
return { error };
|
|
11223
|
+
}
|
|
11224
|
+
/** @private @author ddj 2026年10月22号 @description Remount only this failed subtree and reevaluate its callbacks. */
|
|
11225
|
+
retry = () => {
|
|
11226
|
+
this.setState({ error: null });
|
|
11227
|
+
};
|
|
11228
|
+
/** @public @author ddj 2026年10月22号 @returns Original subtree or panel-local retry UI. */
|
|
11229
|
+
render() {
|
|
11230
|
+
if (!this.state.error) return react.default.createElement(react.default.Fragment, { key: ownerKey(this.props) }, this.props.children);
|
|
11231
|
+
if (this.props.compact) return react.default.createElement("span", {
|
|
11232
|
+
role: "img",
|
|
11233
|
+
title: "图标加载失败",
|
|
11234
|
+
"aria-label": "图标加载失败"
|
|
11235
|
+
}, "!");
|
|
11236
|
+
return react.default.createElement("div", {
|
|
11237
|
+
className: "edrv-empty",
|
|
11238
|
+
role: "alert"
|
|
11239
|
+
}, this.props.panel.title + ":" + String(this.state.error.message), react.default.createElement("button", { onClick: this.retry }, "重试"));
|
|
11240
|
+
}
|
|
11241
|
+
};
|
|
11242
|
+
/** @public @author ddj 2026年10月22号 @param props Isolated panel invocation below the boundary. @returns Panel subtree. @throws Extension callback error for the nearest boundary. */
|
|
11243
|
+
function PanelContent(props) {
|
|
11244
|
+
const checked = inspectPanel(props.panel, props.ctx);
|
|
11245
|
+
if (checked?.error) throw checked.error;
|
|
11246
|
+
return checked ? props.panel.render(props.ctx) : null;
|
|
11247
|
+
}
|
|
11248
|
+
//#endregion
|
|
10916
11249
|
//#region src/client/sidebar/SidebarView.ts
|
|
10917
11250
|
/**
|
|
10918
11251
|
* dsh-vscode-mode client — 侧边栏容器(活动栏常显 + 可隐藏面板区 + 拖拽调宽)。
|
|
@@ -10955,7 +11288,8 @@ window.__ModuleLoader__.load({
|
|
|
10955
11288
|
*/
|
|
10956
11289
|
function SidebarView(props) {
|
|
10957
11290
|
const ctx = props.ctx;
|
|
10958
|
-
const
|
|
11291
|
+
const checked = (props.registry?.list?.() ?? []).map((panel) => inspectPanel(panel, ctx)).filter(Boolean);
|
|
11292
|
+
const panels = checked.map((entry) => entry.panel);
|
|
10959
11293
|
const activePanel = props.activePanel;
|
|
10960
11294
|
const onActive = props.onActive;
|
|
10961
11295
|
const visible = props.visible !== false;
|
|
@@ -10965,26 +11299,71 @@ window.__ModuleLoader__.load({
|
|
|
10965
11299
|
const onHide = props.onHide;
|
|
10966
11300
|
const wMin = Math.max(120, Math.min(W_MAX, Math.round(Number(props.minWidth) || 300)));
|
|
10967
11301
|
const active = panels.find((p) => p.id === activePanel) || panels[0];
|
|
11302
|
+
const dragCleanup = react.default.useRef(null);
|
|
11303
|
+
/** @private @author ddj 2026年10月22号 @description End the active drag; safe on repeated teardown. */
|
|
11304
|
+
function stopDrag() {
|
|
11305
|
+
dragCleanup.current?.();
|
|
11306
|
+
}
|
|
11307
|
+
const seamOpening = react.default.useRef(false);
|
|
11308
|
+
const lastVisible = react.default.useRef(visible);
|
|
11309
|
+
/** @private @author ddj 2026年10月22号 @description Always release listeners on unmount, including a pending seam expansion. */
|
|
11310
|
+
function dragEffect() {
|
|
11311
|
+
return stopDrag;
|
|
11312
|
+
}
|
|
11313
|
+
react.default.useEffect(dragEffect, []);
|
|
11314
|
+
/** @private @author ddj 2026年10月22号 @description Preserve only the seam's own expansion; external visibility changes cancel ownership. */
|
|
11315
|
+
function visibilityEffect() {
|
|
11316
|
+
if (lastVisible.current !== visible) {
|
|
11317
|
+
if (!(visible && seamOpening.current)) stopDrag();
|
|
11318
|
+
seamOpening.current = false;
|
|
11319
|
+
lastVisible.current = visible;
|
|
11320
|
+
}
|
|
11321
|
+
}
|
|
11322
|
+
react.default.useEffect(visibilityEffect, [visible]);
|
|
11323
|
+
/** @private @author ddj 2026年10月22号 @param pointerId Owning pointer. @param move Pointer movement callback. @description Replace the previous drag and own every exit path. */
|
|
11324
|
+
function bindDrag(pointerId, move) {
|
|
11325
|
+
stopDrag();
|
|
11326
|
+
/** @author ddj 2026年10月22号 @param event Pointer event. @description Ignore another pointer's movement. */
|
|
11327
|
+
function onMove(event) {
|
|
11328
|
+
if (event.pointerId === pointerId) move(event);
|
|
11329
|
+
}
|
|
11330
|
+
/** @author ddj 2026年10月22号 @param event Pointer completion. @description Only the owning pointer ends the drag. */
|
|
11331
|
+
function onEnd(event) {
|
|
11332
|
+
if (event.pointerId === pointerId) cleanup();
|
|
11333
|
+
}
|
|
11334
|
+
/** @author ddj 2026年10月22号 @description Remove all drag listeners and release ownership. */
|
|
11335
|
+
function cleanup() {
|
|
11336
|
+
window.removeEventListener("pointermove", onMove);
|
|
11337
|
+
window.removeEventListener("pointerup", onEnd);
|
|
11338
|
+
window.removeEventListener("pointercancel", onEnd);
|
|
11339
|
+
window.removeEventListener("blur", cleanup);
|
|
11340
|
+
if (dragCleanup.current === cleanup) {
|
|
11341
|
+
dragCleanup.current = null;
|
|
11342
|
+
seamOpening.current = false;
|
|
11343
|
+
}
|
|
11344
|
+
}
|
|
11345
|
+
dragCleanup.current = cleanup;
|
|
11346
|
+
window.addEventListener("pointermove", onMove);
|
|
11347
|
+
window.addEventListener("pointerup", onEnd);
|
|
11348
|
+
window.addEventListener("pointercancel", onEnd);
|
|
11349
|
+
window.addEventListener("blur", cleanup);
|
|
11350
|
+
}
|
|
11351
|
+
/** @private @author ddj 2026年10月22号 @param e Starting pointer. @description Resize the visible panel until completion or collapse. */
|
|
10968
11352
|
const onPointerDown = (e) => {
|
|
10969
11353
|
e.preventDefault();
|
|
10970
11354
|
const startX = e.clientX;
|
|
10971
11355
|
const startW = width;
|
|
10972
|
-
|
|
10973
|
-
window.removeEventListener("pointermove", onMove);
|
|
10974
|
-
window.removeEventListener("pointerup", onUp);
|
|
10975
|
-
};
|
|
10976
|
-
const onUp = () => cleanup();
|
|
11356
|
+
/** @author ddj 2026年10月22号 @param ev Owning pointer movement. @description Apply this drag's width calculation. */
|
|
10977
11357
|
const onMove = (ev) => {
|
|
10978
11358
|
const raw = startW + (ev.clientX - startX);
|
|
10979
11359
|
if (raw < wMin) {
|
|
10980
|
-
|
|
11360
|
+
stopDrag();
|
|
10981
11361
|
onHide();
|
|
10982
11362
|
return;
|
|
10983
11363
|
}
|
|
10984
11364
|
onWidth(Math.min(W_MAX, raw));
|
|
10985
11365
|
};
|
|
10986
|
-
|
|
10987
|
-
window.addEventListener("pointerup", onUp);
|
|
11366
|
+
bindDrag(e.pointerId, onMove);
|
|
10988
11367
|
};
|
|
10989
11368
|
/**
|
|
10990
11369
|
* 隐藏态接缝拖拽:向右拖过阈值把面板区拉出来(以记忆宽度展开),继续拖动实时调宽。
|
|
@@ -10996,23 +11375,19 @@ window.__ModuleLoader__.load({
|
|
|
10996
11375
|
const baseW = Math.max(wMin, Math.min(W_MAX, Math.round(Number(width) || wMin)));
|
|
10997
11376
|
let expanded = false;
|
|
10998
11377
|
let expandStartX = 0;
|
|
10999
|
-
|
|
11000
|
-
window.removeEventListener("pointermove", onMove);
|
|
11001
|
-
window.removeEventListener("pointerup", onUp);
|
|
11002
|
-
};
|
|
11003
|
-
const onUp = () => cleanup();
|
|
11378
|
+
/** @author ddj 2026年10月22号 @param ev Owning pointer movement. @description Apply this drag's width calculation. */
|
|
11004
11379
|
const onMove = (ev) => {
|
|
11005
11380
|
if (!expanded) {
|
|
11006
11381
|
if (ev.clientX - startX < PULL_OUT_THRESHOLD) return;
|
|
11007
11382
|
expanded = true;
|
|
11008
11383
|
expandStartX = ev.clientX;
|
|
11384
|
+
seamOpening.current = true;
|
|
11009
11385
|
onShow();
|
|
11010
11386
|
return;
|
|
11011
11387
|
}
|
|
11012
11388
|
onWidth(Math.max(wMin, Math.min(W_MAX, baseW + (ev.clientX - expandStartX))));
|
|
11013
11389
|
};
|
|
11014
|
-
|
|
11015
|
-
window.addEventListener("pointerup", onUp);
|
|
11390
|
+
bindDrag(e.pointerId, onMove);
|
|
11016
11391
|
};
|
|
11017
11392
|
const rail = react.default.createElement("div", { className: "edrv-rail" }, react.default.createElement("div", { style: {
|
|
11018
11393
|
flex: 1,
|
|
@@ -11020,7 +11395,7 @@ window.__ModuleLoader__.load({
|
|
|
11020
11395
|
flexDirection: "column",
|
|
11021
11396
|
gap: 2
|
|
11022
11397
|
} }, panels.map((p) => {
|
|
11023
|
-
const count =
|
|
11398
|
+
const count = checked.find((entry) => entry.panel === p)?.badge;
|
|
11024
11399
|
return react.default.createElement("button", {
|
|
11025
11400
|
key: p.id,
|
|
11026
11401
|
className: "edrv-rail-btn" + (visible && active && p.id === active.id ? " edrv-rail-on" : ""),
|
|
@@ -11029,7 +11404,12 @@ window.__ModuleLoader__.load({
|
|
|
11029
11404
|
onActive(p.id);
|
|
11030
11405
|
if (!visible && typeof onShow === "function") onShow();
|
|
11031
11406
|
}
|
|
11032
|
-
}, react.default.createElement("span", { className: "edrv-rail-icon" },
|
|
11407
|
+
}, react.default.createElement("span", { className: "edrv-rail-icon" }, react.default.createElement(PanelBoundary, {
|
|
11408
|
+
panel: p,
|
|
11409
|
+
scope: ctx?.scope,
|
|
11410
|
+
sessionId: ctx?.sessionId,
|
|
11411
|
+
compact: true
|
|
11412
|
+
}, railIconEl(p.icon))), count > 0 ? react.default.createElement("span", { className: "edrv-rail-badge" }, String(count)) : null);
|
|
11033
11413
|
})), react.default.createElement("button", {
|
|
11034
11414
|
className: "edrv-rail-btn",
|
|
11035
11415
|
title: (visible ? "隐藏面板区" : "展开面板区") + " (Ctrl+B)",
|
|
@@ -11041,7 +11421,14 @@ window.__ModuleLoader__.load({
|
|
|
11041
11421
|
width: visible ? width : 49,
|
|
11042
11422
|
maxWidth: "100%"
|
|
11043
11423
|
}
|
|
11044
|
-
}, rail, visible ? react.default.createElement("div", { className: "edrv-side-body" }, active ?
|
|
11424
|
+
}, rail, visible ? react.default.createElement("div", { className: "edrv-side-body" }, active ? react.default.createElement(PanelBoundary, {
|
|
11425
|
+
panel: active,
|
|
11426
|
+
scope: ctx?.scope,
|
|
11427
|
+
sessionId: ctx?.sessionId
|
|
11428
|
+
}, react.default.createElement(PanelContent, {
|
|
11429
|
+
panel: active,
|
|
11430
|
+
ctx
|
|
11431
|
+
})) : null) : null, visible ? react.default.createElement("div", {
|
|
11045
11432
|
className: "edrv-side-resize",
|
|
11046
11433
|
onPointerDown
|
|
11047
11434
|
}) : react.default.createElement("div", {
|
|
@@ -11705,20 +12092,29 @@ window.__ModuleLoader__.load({
|
|
|
11705
12092
|
let suppressed = 0;
|
|
11706
12093
|
let done = false;
|
|
11707
12094
|
let installed = false;
|
|
12095
|
+
let disposed = false;
|
|
12096
|
+
let restoreStorage;
|
|
11708
12097
|
let lastError = "";
|
|
11709
12098
|
/** 水合尝试次数(失败重试;超过 `HYDRATE_RETRY_MAX` 放弃并解禁)。 */
|
|
11710
12099
|
let attempts = 0;
|
|
12100
|
+
/** 重试定时器(与水合无关的 flush 去抖计时器分开,避免互相取消)。 */
|
|
12101
|
+
let retryTimer = null;
|
|
11711
12102
|
let settleDone = false;
|
|
11712
12103
|
/** 记入待推送批次并安排去抖上送(水合期间或非镜像键 → 跳过)。 */
|
|
11713
12104
|
function queue(key, value) {
|
|
11714
|
-
if (suppressed > 0 || !isMirroredKey(key)) return;
|
|
12105
|
+
if (disposed || suppressed > 0 || !isMirroredKey(key)) return;
|
|
11715
12106
|
const bucket = isGlobalKey(key) ? pending.globals : pending.keys;
|
|
11716
12107
|
bucket[key] = value;
|
|
11717
12108
|
if (timer !== null) cancel(timer);
|
|
11718
|
-
|
|
12109
|
+
try {
|
|
12110
|
+
timer = schedule(() => {
|
|
12111
|
+
timer = null;
|
|
12112
|
+
if (!disposed) flush();
|
|
12113
|
+
}, 400);
|
|
12114
|
+
} catch (error) {
|
|
11719
12115
|
timer = null;
|
|
11720
|
-
|
|
11721
|
-
}
|
|
12116
|
+
log("界面状态调度失败: " + String(error));
|
|
12117
|
+
}
|
|
11722
12118
|
}
|
|
11723
12119
|
/** 上送一批补丁(失败重试一次)。 */
|
|
11724
12120
|
async function pushBatch(payload) {
|
|
@@ -11771,7 +12167,7 @@ window.__ModuleLoader__.load({
|
|
|
11771
12167
|
}
|
|
11772
12168
|
/** @private @author ddj 2026年10月12号 @returns Durable refresh snapshot. @throws When state is unreadable, unsettled or rejected by Host. @sideEffects Resends current local state after waiting for inflight writes. */
|
|
11773
12169
|
async function flushStrict() {
|
|
11774
|
-
if (!settleDone || !installed) throw new Error("界面状态镜像尚未就绪");
|
|
12170
|
+
if (disposed || !settleDone || !installed) throw new Error("界面状态镜像尚未就绪");
|
|
11775
12171
|
if (inflight) await inflight;
|
|
11776
12172
|
for (let index = 0; index < storage.length; index++) {
|
|
11777
12173
|
const key = storage.key(index);
|
|
@@ -11833,7 +12229,7 @@ window.__ModuleLoader__.load({
|
|
|
11833
12229
|
* 未结束前界面不得写存档,否则空初值会覆盖 host 上的真实状态。
|
|
11834
12230
|
*/
|
|
11835
12231
|
function settle() {
|
|
11836
|
-
if (settleDone) return;
|
|
12232
|
+
if (disposed || settleDone) return;
|
|
11837
12233
|
settleDone = true;
|
|
11838
12234
|
emitReady();
|
|
11839
12235
|
report("水合阶段结束(就绪;" + (done ? "已回填" : "未回填,退回纯 localStorage") + ")");
|
|
@@ -11843,9 +12239,11 @@ window.__ModuleLoader__.load({
|
|
|
11843
12239
|
* @returns 本次回填键数(失败/重试中为 0)
|
|
11844
12240
|
*/
|
|
11845
12241
|
async function hydrate() {
|
|
12242
|
+
if (disposed) return 0;
|
|
11846
12243
|
try {
|
|
11847
12244
|
report("水合开始(origin=" + originOf() + ",第 " + (attempts + 1) + " 次)");
|
|
11848
12245
|
const snapshot = await deps.fetch(deps.sessionIdOf?.());
|
|
12246
|
+
if (disposed) return 0;
|
|
11849
12247
|
const seeded = seedLocal();
|
|
11850
12248
|
const filled = applyHydration(snapshot);
|
|
11851
12249
|
const stored = Object.keys({
|
|
@@ -11857,44 +12255,81 @@ window.__ModuleLoader__.load({
|
|
|
11857
12255
|
if (seeded > 0) {
|
|
11858
12256
|
const lost = await flush();
|
|
11859
12257
|
report("首次搬运输送完成:丢弃 " + lost + " 键");
|
|
11860
|
-
if (lost === 0) markSeeded();
|
|
12258
|
+
if (!disposed && lost === 0) markSeeded();
|
|
11861
12259
|
}
|
|
11862
12260
|
settle();
|
|
11863
12261
|
return filled;
|
|
11864
12262
|
} catch (error) {
|
|
12263
|
+
if (disposed) return 0;
|
|
11865
12264
|
attempts++;
|
|
11866
12265
|
if (attempts < 8) {
|
|
11867
12266
|
report("水合失败(第 " + attempts + " 次,800ms 后重试): " + String(error), "warn");
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
11871
|
-
|
|
12267
|
+
try {
|
|
12268
|
+
retryTimer = schedule(() => {
|
|
12269
|
+
retryTimer = null;
|
|
12270
|
+
hydrate();
|
|
12271
|
+
}, 800);
|
|
12272
|
+
return 0;
|
|
12273
|
+
} catch (scheduleError) {
|
|
12274
|
+
report("水合重试调度失败: " + String(scheduleError), "warn");
|
|
12275
|
+
}
|
|
11872
12276
|
}
|
|
11873
12277
|
report("水合放弃(重试 " + attempts + " 次仍失败): " + String(error), "warn");
|
|
11874
12278
|
settle();
|
|
11875
12279
|
return 0;
|
|
11876
12280
|
}
|
|
11877
12281
|
}
|
|
12282
|
+
/** @private @author ddj 2026年10月23号 Release timers and only this instance's storage hooks. */
|
|
12283
|
+
function dispose() {
|
|
12284
|
+
if (disposed) return;
|
|
12285
|
+
disposed = true;
|
|
12286
|
+
if (timer !== null) cancel(timer);
|
|
12287
|
+
if (retryTimer !== null) cancel(retryTimer);
|
|
12288
|
+
timer = null;
|
|
12289
|
+
retryTimer = null;
|
|
12290
|
+
restoreStorage?.();
|
|
12291
|
+
installed = false;
|
|
12292
|
+
}
|
|
11878
12293
|
return {
|
|
12294
|
+
dispose,
|
|
11879
12295
|
install() {
|
|
12296
|
+
if (disposed) return false;
|
|
11880
12297
|
if (installed) return true;
|
|
11881
12298
|
try {
|
|
11882
12299
|
const proto = Object.getPrototypeOf(storage);
|
|
11883
|
-
const rawSet = proto?.setItem;
|
|
11884
|
-
const rawRemove = proto?.removeItem;
|
|
12300
|
+
const rawSet = storage.setItem ?? proto?.setItem;
|
|
12301
|
+
const rawRemove = storage.removeItem ?? proto?.removeItem;
|
|
11885
12302
|
if (typeof rawSet !== "function" || typeof rawRemove !== "function") return false;
|
|
11886
|
-
|
|
12303
|
+
const setDescriptor = Object.getOwnPropertyDescriptor(storage, "setItem");
|
|
12304
|
+
const removeDescriptor = Object.getOwnPropertyDescriptor(storage, "removeItem");
|
|
12305
|
+
/** @private @author ddj 2026年10月23号 @param key Storage key. @param value Stored text. @sideEffects Queues a mirror update. */
|
|
12306
|
+
function wrappedSet(key, value) {
|
|
11887
12307
|
rawSet.call(storage, key, value);
|
|
11888
12308
|
queue(String(key), String(value));
|
|
11889
|
-
}
|
|
11890
|
-
|
|
12309
|
+
}
|
|
12310
|
+
/** @private @author ddj 2026年10月23号 @param key Storage key. @sideEffects Queues a mirror deletion. */
|
|
12311
|
+
function wrappedRemove(key) {
|
|
11891
12312
|
rawRemove.call(storage, key);
|
|
11892
12313
|
queue(String(key), null);
|
|
12314
|
+
}
|
|
12315
|
+
restoreStorage = () => {
|
|
12316
|
+
if (storage.setItem === wrappedSet) {
|
|
12317
|
+
if (setDescriptor) Object.defineProperty(storage, "setItem", setDescriptor);
|
|
12318
|
+
else delete storage.setItem;
|
|
12319
|
+
}
|
|
12320
|
+
if (storage.removeItem === wrappedRemove) {
|
|
12321
|
+
if (removeDescriptor) Object.defineProperty(storage, "removeItem", removeDescriptor);
|
|
12322
|
+
else delete storage.removeItem;
|
|
12323
|
+
}
|
|
11893
12324
|
};
|
|
12325
|
+
storage.setItem = wrappedSet;
|
|
12326
|
+
storage.removeItem = wrappedRemove;
|
|
11894
12327
|
installed = true;
|
|
11895
12328
|
report("镜像已包装 localStorage 写口");
|
|
11896
12329
|
return true;
|
|
11897
12330
|
} catch (error) {
|
|
12331
|
+
restoreStorage?.();
|
|
12332
|
+
installed = false;
|
|
11898
12333
|
report("界面状态镜像包装失败(退回纯 localStorage): " + String(error), "warn");
|
|
11899
12334
|
return false;
|
|
11900
12335
|
}
|
|
@@ -12004,6 +12439,9 @@ window.__ModuleLoader__.load({
|
|
|
12004
12439
|
fetch: fetchViaRpc,
|
|
12005
12440
|
sessionIdOf,
|
|
12006
12441
|
schedule,
|
|
12442
|
+
cancel: (handle) => {
|
|
12443
|
+
if (typeof handle === "function") handle();
|
|
12444
|
+
},
|
|
12007
12445
|
report: (text, level) => reportUiStateEvent(text, level),
|
|
12008
12446
|
log: (text) => {
|
|
12009
12447
|
try {
|
|
@@ -12016,17 +12454,29 @@ window.__ModuleLoader__.load({
|
|
|
12016
12454
|
return false;
|
|
12017
12455
|
}
|
|
12018
12456
|
singleton = mirror;
|
|
12019
|
-
|
|
12020
|
-
|
|
12021
|
-
|
|
12022
|
-
|
|
12023
|
-
}
|
|
12457
|
+
const pageHost = typeof window === "undefined" ? void 0 : window;
|
|
12458
|
+
/** @private @author ddj 2026年10月23号 Persist pending changes when the page is hidden. */
|
|
12459
|
+
function onPageHide() {
|
|
12460
|
+
mirror.flush();
|
|
12461
|
+
}
|
|
12462
|
+
ctx.effect(() => {
|
|
12463
|
+
pageHost?.addEventListener("pagehide", onPageHide);
|
|
12464
|
+
return () => {
|
|
12465
|
+
pageHost?.removeEventListener("pagehide", onPageHide);
|
|
12466
|
+
mirror.dispose();
|
|
12467
|
+
if (singleton !== mirror) return;
|
|
12468
|
+
singleton = null;
|
|
12469
|
+
hydrating = null;
|
|
12470
|
+
uiReady = false;
|
|
12471
|
+
reporter = null;
|
|
12472
|
+
};
|
|
12473
|
+
}, "vscode-mode: ui state lifecycle");
|
|
12024
12474
|
hydrating = mirror.hydrate().then((filled) => {
|
|
12025
|
-
if (filled > 0 || mirror.settled()) uiReady = true;
|
|
12475
|
+
if (singleton === mirror && (filled > 0 || mirror.settled())) uiReady = true;
|
|
12026
12476
|
return filled;
|
|
12027
12477
|
});
|
|
12028
12478
|
hydrating.finally(() => {
|
|
12029
|
-
if (mirror.settled()) {
|
|
12479
|
+
if (singleton === mirror && mirror.settled()) {
|
|
12030
12480
|
uiReady = true;
|
|
12031
12481
|
hydrating = null;
|
|
12032
12482
|
}
|
|
@@ -16095,7 +16545,10 @@ window.__ModuleLoader__.load({
|
|
|
16095
16545
|
"shell",
|
|
16096
16546
|
"sql",
|
|
16097
16547
|
"swift",
|
|
16548
|
+
"systemverilog",
|
|
16549
|
+
"tcl",
|
|
16098
16550
|
"typescript",
|
|
16551
|
+
"verilog",
|
|
16099
16552
|
"xml",
|
|
16100
16553
|
"yaml"
|
|
16101
16554
|
];
|
|
@@ -22346,7 +22799,7 @@ window.__ModuleLoader__.load({
|
|
|
22346
22799
|
}
|
|
22347
22800
|
//#endregion
|
|
22348
22801
|
//#region src/client/dap/toolbarDrag.ts
|
|
22349
|
-
const KEY_PREFIX$
|
|
22802
|
+
const KEY_PREFIX$2 = "edrv.dap.toolbar.";
|
|
22350
22803
|
/** 将工具条位置限制在编辑区内。 */
|
|
22351
22804
|
function clampToolbarPosition(point, bounds) {
|
|
22352
22805
|
const margin = bounds.margin ?? 6;
|
|
@@ -22361,7 +22814,7 @@ window.__ModuleLoader__.load({
|
|
|
22361
22814
|
function loadToolbarPosition(scope) {
|
|
22362
22815
|
if (!scope) return null;
|
|
22363
22816
|
try {
|
|
22364
|
-
const raw = localStorage.getItem(KEY_PREFIX$
|
|
22817
|
+
const raw = localStorage.getItem(KEY_PREFIX$2 + scope);
|
|
22365
22818
|
if (!raw) return null;
|
|
22366
22819
|
const value = JSON.parse(raw);
|
|
22367
22820
|
if (!value || !Number.isFinite(value.left) || !Number.isFinite(value.top)) return null;
|
|
@@ -22377,7 +22830,7 @@ window.__ModuleLoader__.load({
|
|
|
22377
22830
|
function saveToolbarPosition(scope, point) {
|
|
22378
22831
|
if (!scope) return;
|
|
22379
22832
|
try {
|
|
22380
|
-
localStorage.setItem(KEY_PREFIX$
|
|
22833
|
+
localStorage.setItem(KEY_PREFIX$2 + scope, JSON.stringify(point));
|
|
22381
22834
|
} catch {}
|
|
22382
22835
|
}
|
|
22383
22836
|
//#endregion
|
|
@@ -22627,6 +23080,15 @@ window.__ModuleLoader__.load({
|
|
|
22627
23080
|
const aiStatusTimer = react.default.useRef(null);
|
|
22628
23081
|
const [error, setError] = react.default.useState(null);
|
|
22629
23082
|
const [loadError, setLoadError] = react.default.useState(null);
|
|
23083
|
+
const [largeCandidate, setLargeCandidate] = react.default.useState(null);
|
|
23084
|
+
const [pagedFile, setPagedFile] = react.default.useState(null);
|
|
23085
|
+
const [pageChanged, setPageChanged] = react.default.useState(null);
|
|
23086
|
+
const pageIdentity = JSON.stringify([
|
|
23087
|
+
scope,
|
|
23088
|
+
sessionId,
|
|
23089
|
+
active
|
|
23090
|
+
]);
|
|
23091
|
+
const paging = pagedFile === pageIdentity;
|
|
22630
23092
|
const [loadStage, setLoadStage] = react.default.useState({
|
|
22631
23093
|
progress: 0,
|
|
22632
23094
|
message: "准备加载编辑器…"
|
|
@@ -23339,6 +23801,9 @@ window.__ModuleLoader__.load({
|
|
|
23339
23801
|
status: "loading"
|
|
23340
23802
|
});
|
|
23341
23803
|
setLoadError(null);
|
|
23804
|
+
setLargeCandidate(null);
|
|
23805
|
+
setPagedFile(null);
|
|
23806
|
+
setPageChanged(null);
|
|
23342
23807
|
/**
|
|
23343
23808
|
* 记下磁盘版本基线;markRead=true 表示本次真的从磁盘读了内容
|
|
23344
23809
|
* (已读版本台账随之失效,下一轮轮询按新版本重新比对)。
|
|
@@ -23395,6 +23860,11 @@ window.__ModuleLoader__.load({
|
|
|
23395
23860
|
}));
|
|
23396
23861
|
setStatus("已加载");
|
|
23397
23862
|
} else {
|
|
23863
|
+
if (res?.code === "file-too-large") setLargeCandidate(JSON.stringify([
|
|
23864
|
+
scope,
|
|
23865
|
+
sid,
|
|
23866
|
+
path
|
|
23867
|
+
]));
|
|
23398
23868
|
const base = res?.error ? String(res.error) : "读取失败";
|
|
23399
23869
|
dapTrace("read-failed", {
|
|
23400
23870
|
path,
|
|
@@ -23575,7 +24045,7 @@ window.__ModuleLoader__.load({
|
|
|
23575
24045
|
* @author ddj 2026年09月15号
|
|
23576
24046
|
* @param change 轮询判定出的外部变更
|
|
23577
24047
|
*/
|
|
23578
|
-
|
|
24048
|
+
function onDiskChange(change) {
|
|
23579
24049
|
if (!change || !change.path) return;
|
|
23580
24050
|
const eventId = emitFileChanged(change.path, {
|
|
23581
24051
|
scope,
|
|
@@ -23604,6 +24074,18 @@ window.__ModuleLoader__.load({
|
|
|
23604
24074
|
}).catch(() => {});
|
|
23605
24075
|
return;
|
|
23606
24076
|
}
|
|
24077
|
+
if (change.kind === "modified" && (paging || pageChanged === pageIdentity) && change.path === activeRef.current) {
|
|
24078
|
+
loadContent(change.path, sessionId, true);
|
|
24079
|
+
setPageChanged(pageIdentity);
|
|
24080
|
+
setStatus("文件已变化,请重新读取");
|
|
24081
|
+
emitRefresh({
|
|
24082
|
+
scope,
|
|
24083
|
+
paths: [change.path],
|
|
24084
|
+
kind: "text",
|
|
24085
|
+
eventId
|
|
24086
|
+
});
|
|
24087
|
+
return;
|
|
24088
|
+
}
|
|
23607
24089
|
if (change.kind === "modified") {
|
|
23608
24090
|
loadContent(change.path, sessionId, true);
|
|
23609
24091
|
clearSync(scope, change.path);
|
|
@@ -23624,7 +24106,7 @@ window.__ModuleLoader__.load({
|
|
|
23624
24106
|
at: Date.now()
|
|
23625
24107
|
});
|
|
23626
24108
|
setStatus(kind === "deleted" ? "文件已被外部删除" : "外部已修改(未保存的编辑保留)");
|
|
23627
|
-
}
|
|
24109
|
+
}
|
|
23628
24110
|
diskChangeRef.current = onDiskChange;
|
|
23629
24111
|
/**
|
|
23630
24112
|
* 版本变化时读磁盘并与缓冲比对:内容相同 → 只推进基线(不做无意义重载);
|
|
@@ -23633,7 +24115,8 @@ window.__ModuleLoader__.load({
|
|
|
23633
24115
|
* @param path 文件路径
|
|
23634
24116
|
* @returns 比对结果;模型不可用/读失败 → null(按「需要重载」处理)
|
|
23635
24117
|
*/
|
|
23636
|
-
|
|
24118
|
+
function readDiskCompare(path) {
|
|
24119
|
+
if (path === activeRef.current && (paging || pageChanged === pageIdentity)) return Promise.resolve({ equal: false });
|
|
23637
24120
|
const model = modelsRef.current?.get(path);
|
|
23638
24121
|
if (!model || typeof model.getValue !== "function") return Promise.resolve(null);
|
|
23639
24122
|
return rpc("edrv.read", {
|
|
@@ -23643,7 +24126,7 @@ window.__ModuleLoader__.load({
|
|
|
23643
24126
|
if (!res || !res.ok) return null;
|
|
23644
24127
|
return { equal: res.content === model.getValue() };
|
|
23645
24128
|
}).catch(() => null);
|
|
23646
|
-
}
|
|
24129
|
+
}
|
|
23647
24130
|
useFileWatch({
|
|
23648
24131
|
sessionId,
|
|
23649
24132
|
scope,
|
|
@@ -24801,8 +25284,7 @@ window.__ModuleLoader__.load({
|
|
|
24801
25284
|
language: "plaintext",
|
|
24802
25285
|
theme: themeNameOf(),
|
|
24803
25286
|
fontFamily: "var(--ds-font-family-code, ui-monospace, monospace)",
|
|
24804
|
-
|
|
24805
|
-
lineHeight: 21,
|
|
25287
|
+
...editorFont(),
|
|
24806
25288
|
minimap: {
|
|
24807
25289
|
enabled: !side,
|
|
24808
25290
|
scale: 1
|
|
@@ -26353,7 +26835,8 @@ window.__ModuleLoader__.load({
|
|
|
26353
26835
|
window.removeEventListener("edrv.command.debugStop", onStop);
|
|
26354
26836
|
};
|
|
26355
26837
|
}, []);
|
|
26356
|
-
|
|
26838
|
+
/** @private @author ddj 2026年10月22号 @description Probe a typed path; let oversized files enter the existing read-only paging flow. @returns Probe completion. */
|
|
26839
|
+
function openPath() {
|
|
26357
26840
|
const p = (pathDraft || "").trim();
|
|
26358
26841
|
if (!p) return;
|
|
26359
26842
|
if (isImagePath(p) || isPdfPath(p)) {
|
|
@@ -26365,11 +26848,11 @@ window.__ModuleLoader__.load({
|
|
|
26365
26848
|
return;
|
|
26366
26849
|
}
|
|
26367
26850
|
setStatus("打开中…");
|
|
26368
|
-
rpc("edrv.read", {
|
|
26851
|
+
return rpc("edrv.read", {
|
|
26369
26852
|
sessionId,
|
|
26370
26853
|
path: p
|
|
26371
26854
|
}).then((res) => {
|
|
26372
|
-
if (res && res.ok) {
|
|
26855
|
+
if (res && (res.ok || res.code === "file-too-large")) {
|
|
26373
26856
|
openFile(p, false);
|
|
26374
26857
|
setOpenInput(false);
|
|
26375
26858
|
setPathDraft("");
|
|
@@ -26383,7 +26866,7 @@ window.__ModuleLoader__.load({
|
|
|
26383
26866
|
setStatus("打开失败");
|
|
26384
26867
|
setError("打开异常:" + String(e));
|
|
26385
26868
|
});
|
|
26386
|
-
}
|
|
26869
|
+
}
|
|
26387
26870
|
/** @private @author ddj 2026年10月14号 @param group Group id. @param path Optional selection. Route global actions only to this group. */
|
|
26388
26871
|
function selectGroup(group, path) {
|
|
26389
26872
|
saveViewState();
|
|
@@ -26565,9 +27048,21 @@ window.__ModuleLoader__.load({
|
|
|
26565
27048
|
host.removeEventListener("contextmenu", onCtx);
|
|
26566
27049
|
};
|
|
26567
27050
|
}
|
|
27051
|
+
/** @private @author ddj 2026年10月22号 @param args Read-only page request. @returns Current Host capability result. */
|
|
27052
|
+
function loadTextPage(args) {
|
|
27053
|
+
return rpc("edrv.textPage", args);
|
|
27054
|
+
}
|
|
27055
|
+
/** @private @author ddj 2026年10月22号 @description Never replace a dirty or saving document with partial text. */
|
|
27056
|
+
function openTextPages() {
|
|
27057
|
+
if (largeCandidate !== pageIdentity || dirtyRef.current[active] || savingRef.current.has(active)) return;
|
|
27058
|
+
flushSave();
|
|
27059
|
+
if (dirtyRef.current[active] || savingRef.current.has(active)) return;
|
|
27060
|
+
setPageChanged(null);
|
|
27061
|
+
setPagedFile(pageIdentity);
|
|
27062
|
+
}
|
|
26568
27063
|
/** @private @author ddj 2026年10月16号 @param host Passive body. @param path Canonical file. @param groupId Owning group id(find 快照按组隔离). @returns View-only cleanup, models remain workbench-owned. */
|
|
26569
27064
|
function mountPassive(host, path, groupId) {
|
|
26570
|
-
let disposed = false, keeper = null, subscription = null, observer = null;
|
|
27065
|
+
let disposed = false, keeper = null, subscription = null, observer = null, pageRoot = null;
|
|
26571
27066
|
const focus = () => {
|
|
26572
27067
|
const owner = ownerOf(benchRef.current, path);
|
|
26573
27068
|
if (owner) selectGroup(owner.id, path);
|
|
@@ -26594,6 +27089,27 @@ window.__ModuleLoader__.load({
|
|
|
26594
27089
|
host.textContent = "";
|
|
26595
27090
|
if (!result?.ok) {
|
|
26596
27091
|
host.textContent = result?.error || "文件加载失败";
|
|
27092
|
+
if (result?.code === "file-too-large" && !binary) {
|
|
27093
|
+
const button = document.createElement("button");
|
|
27094
|
+
button.textContent = "只读分段查看";
|
|
27095
|
+
/** @author ddj 2026年10月22号 @param event Pointer event. @description Do not focus and unmount the passive group before click. */
|
|
27096
|
+
button.onpointerdown = (event) => event.stopPropagation();
|
|
27097
|
+
/** @author ddj 2026年10月22号 @description Keep partial text out of passive models, autosave and editing commands. */
|
|
27098
|
+
button.onclick = () => {
|
|
27099
|
+
if (disposed || dirtyRef.current[path] || savingRef.current.has(path)) return;
|
|
27100
|
+
flushSave();
|
|
27101
|
+
if (dirtyRef.current[path] || savingRef.current.has(path)) return;
|
|
27102
|
+
host.textContent = "";
|
|
27103
|
+
host.removeEventListener("pointerdown", focus);
|
|
27104
|
+
pageRoot = (0, react_dom_client.createRoot)(host);
|
|
27105
|
+
pageRoot.render(react.default.createElement(LargeTextView, {
|
|
27106
|
+
sessionId,
|
|
27107
|
+
path,
|
|
27108
|
+
loadPage: loadTextPage
|
|
27109
|
+
}));
|
|
27110
|
+
};
|
|
27111
|
+
host.appendChild(button);
|
|
27112
|
+
}
|
|
26597
27113
|
return;
|
|
26598
27114
|
}
|
|
26599
27115
|
if (binary) {
|
|
@@ -26623,8 +27139,7 @@ window.__ModuleLoader__.load({
|
|
|
26623
27139
|
scrollBeyondLastLine: false,
|
|
26624
27140
|
glyphMargin: true,
|
|
26625
27141
|
fontFamily: "var(--ds-font-family-code, ui-monospace, monospace)",
|
|
26626
|
-
|
|
26627
|
-
lineHeight: 21,
|
|
27142
|
+
...editorFont(),
|
|
26628
27143
|
lineDecorationsWidth: 16,
|
|
26629
27144
|
padding: { top: layoutRef.current === "side" ? 6 : 8 }
|
|
26630
27145
|
});
|
|
@@ -26644,6 +27159,7 @@ window.__ModuleLoader__.load({
|
|
|
26644
27159
|
});
|
|
26645
27160
|
return () => {
|
|
26646
27161
|
disposed = true;
|
|
27162
|
+
pageRoot?.unmount();
|
|
26647
27163
|
host.removeEventListener("pointerdown", focus);
|
|
26648
27164
|
observer?.disconnect();
|
|
26649
27165
|
subscription?.dispose();
|
|
@@ -26705,7 +27221,11 @@ window.__ModuleLoader__.load({
|
|
|
26705
27221
|
path: group.active,
|
|
26706
27222
|
onFocus: () => selectGroup(id)
|
|
26707
27223
|
}), react.default.createElement(PassiveEditor, {
|
|
26708
|
-
key:
|
|
27224
|
+
key: JSON.stringify([
|
|
27225
|
+
scope,
|
|
27226
|
+
sessionId,
|
|
27227
|
+
group.active
|
|
27228
|
+
]),
|
|
26709
27229
|
path: group.active,
|
|
26710
27230
|
groupId: id,
|
|
26711
27231
|
mount: passiveMount
|
|
@@ -26931,7 +27451,20 @@ window.__ModuleLoader__.load({
|
|
|
26931
27451
|
ref: planHostRef,
|
|
26932
27452
|
"data-plan-address": activePlan.address
|
|
26933
27453
|
}, react.default.createElement("div", { className: "edrv-mdview-stage" }, react.default.createElement("div", { className: "edrv-mdview-doc" }, render)));
|
|
26934
|
-
} else if (
|
|
27454
|
+
} else if (pageChanged === pageIdentity && largeCandidate === pageIdentity) body = react.default.createElement("div", {
|
|
27455
|
+
className: "edrv-empty",
|
|
27456
|
+
role: "status"
|
|
27457
|
+
}, react.default.createElement("div", null, "文件已变化,请重新读取"), react.default.createElement("button", {
|
|
27458
|
+
className: "edrv-pill edrv-pill-ghost",
|
|
27459
|
+
onClick: openTextPages
|
|
27460
|
+
}, "重新读取首段"));
|
|
27461
|
+
else if (paging) body = react.default.createElement(LargeTextView, {
|
|
27462
|
+
key: pageIdentity,
|
|
27463
|
+
sessionId,
|
|
27464
|
+
path: active,
|
|
27465
|
+
loadPage: loadTextPage
|
|
27466
|
+
});
|
|
27467
|
+
else if (!monaco && !monacoErr) body = loadingBody(loadStage.message, loadStage.progress);
|
|
26935
27468
|
else if (monacoErr) body = react.default.createElement("div", { className: "edrv-empty" }, react.default.createElement("div", null, "Monaco 编辑器加载失败:" + String(monacoErr)), react.default.createElement("div", { style: { fontSize: 11 } }, "请确认插件包 assets/vendor/monaco 完整(/edrv/vendor 路由可达)"), react.default.createElement("button", {
|
|
26936
27469
|
className: "edrv-pill edrv-pill-ghost",
|
|
26937
27470
|
onClick: () => {
|
|
@@ -26963,7 +27496,7 @@ window.__ModuleLoader__.load({
|
|
|
26963
27496
|
loadContent(active, sessionId);
|
|
26964
27497
|
}
|
|
26965
27498
|
}, "重试"));
|
|
26966
|
-
else if (content === null && loadError) body = loadingBody("文件加载失败:" + loadError, 0, () => {
|
|
27499
|
+
else if (content === null && loadError) body = react.default.createElement(react.default.Fragment, null, loadingBody("文件加载失败:" + loadError, 0, () => {
|
|
26967
27500
|
setLoadError(null);
|
|
26968
27501
|
setContent(null);
|
|
26969
27502
|
setContentPath(null);
|
|
@@ -26972,7 +27505,10 @@ window.__ModuleLoader__.load({
|
|
|
26972
27505
|
message: "重新读取文件…"
|
|
26973
27506
|
});
|
|
26974
27507
|
loadContent(active, sessionId);
|
|
26975
|
-
})
|
|
27508
|
+
}), largeCandidate === pageIdentity && react.default.createElement("button", {
|
|
27509
|
+
className: "edrv-pill edrv-pill-ghost",
|
|
27510
|
+
onClick: openTextPages
|
|
27511
|
+
}, "只读分段查看"));
|
|
26976
27512
|
else if (isImageActive && imageSrc) body = imagePanel();
|
|
26977
27513
|
else if (isImageActive) body = loadingBody(loadStage.message || "读取图片…", loadStage.progress);
|
|
26978
27514
|
else if (isPdfActive && pdfBytes) body = react.default.createElement("div", {
|
|
@@ -28927,7 +29463,7 @@ window.__ModuleLoader__.load({
|
|
|
28927
29463
|
if (hasStoredConfig(config[status.languageId])) return true;
|
|
28928
29464
|
return Array.isArray(status.missingEnv) && status.missingEnv.length > 0;
|
|
28929
29465
|
}
|
|
28930
|
-
const PHASE_LABEL
|
|
29466
|
+
const PHASE_LABEL = {
|
|
28931
29467
|
idle: "未启动",
|
|
28932
29468
|
starting: "启动中",
|
|
28933
29469
|
ready: "就绪",
|
|
@@ -28981,7 +29517,7 @@ window.__ModuleLoader__.load({
|
|
|
28981
29517
|
className: "vsm-switch " + (config?.enabled !== false ? "on" : ""),
|
|
28982
29518
|
onClick: () => onToggle(lang.id, config?.enabled !== false ? false : true),
|
|
28983
29519
|
"aria-label": "启用/禁用"
|
|
28984
|
-
}, config?.enabled !== false ? "●" : "○"))), react.default.createElement("div", { className: "vsm-mcp-meta" }, PHASE_LABEL
|
|
29520
|
+
}, config?.enabled !== false ? "●" : "○"))), react.default.createElement("div", { className: "vsm-mcp-meta" }, PHASE_LABEL[phase] ?? phase, " · ", SOURCE_LABEL[status?.source] ?? status?.source, status?.providerName ? " · " + status.providerName : "", status?.version ? " · v" + status.version : "", phase === "ready" && status?.root ? " · " + String(status.root).split(/[\\/]/).pop() : ""), status?.reason ? react.default.createElement("div", { className: "vsm-mcp-error" }, status.reason) : null, Array.isArray(status?.candidates) && status.candidates.length > 1 ? react.default.createElement("div", { className: "vsm-lsp-hint" }, "候选(" + status.candidates.length + "):" + status.candidates.map((c) => c.name + (c.version ? " v" + c.version : "") + (c.chosen ? "(当前)" : "")).join(" · ")) : null, Array.isArray(status?.missingEnv) && status.missingEnv.length ? react.default.createElement("div", { className: "vsm-lsp-hint" }, "缺少运行环境(一键安装后自动生效):", status.missingEnv.map((req) => react.default.createElement("div", {
|
|
28985
29521
|
key: req.id,
|
|
28986
29522
|
className: "vsm-mcp-actions"
|
|
28987
29523
|
}, react.default.createElement("span", null, req.label, req.detail ? "(" + req.detail + ")" : ""), req.installable ? react.default.createElement("button", {
|
|
@@ -31887,7 +32423,12 @@ window.__ModuleLoader__.load({
|
|
|
31887
32423
|
if (typeof window === "undefined") return () => {};
|
|
31888
32424
|
const marker = window;
|
|
31889
32425
|
if (typeof marker.__edrvExtPoll === "function") marker.__edrvExtPoll();
|
|
31890
|
-
const
|
|
32426
|
+
const generatedId = createRequestId();
|
|
32427
|
+
if (!generatedId) {
|
|
32428
|
+
log$1.warn("安全随机源不可用,已停用外部打开心跳");
|
|
32429
|
+
return () => {};
|
|
32430
|
+
}
|
|
32431
|
+
const clientId = generatedId;
|
|
31891
32432
|
let disposed = false;
|
|
31892
32433
|
let polling = false;
|
|
31893
32434
|
let executing = false;
|
|
@@ -32315,8 +32856,11 @@ window.__ModuleLoader__.load({
|
|
|
32315
32856
|
function createSidebarPanelRegistry() {
|
|
32316
32857
|
const entries = /* @__PURE__ */ new Map();
|
|
32317
32858
|
const listeners = /* @__PURE__ */ new Set();
|
|
32859
|
+
/** @private @author ddj 2026年10月22号 @description Isolate subscribers and snapshot notification membership against reentrant changes. */
|
|
32318
32860
|
const notify = () => {
|
|
32319
|
-
for (const listener of listeners)
|
|
32861
|
+
for (const listener of [...listeners]) try {
|
|
32862
|
+
listener();
|
|
32863
|
+
} catch {}
|
|
32320
32864
|
};
|
|
32321
32865
|
const list = () => [...entries.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
|
|
32322
32866
|
return {
|
|
@@ -32432,7 +32976,7 @@ window.__ModuleLoader__.load({
|
|
|
32432
32976
|
* 作者 ddj 2026-08-28
|
|
32433
32977
|
*/
|
|
32434
32978
|
/** localStorage 键前缀(按工作区作用域隔离,无 cwd 会话回退会话键)。 */
|
|
32435
|
-
const KEY_PREFIX$
|
|
32979
|
+
const KEY_PREFIX$1 = CACHE_KEY.expanded;
|
|
32436
32980
|
/**
|
|
32437
32981
|
* 归一化展开路径:去空白、反斜杠→斜杠、去首尾斜杠,`..` 段拒绝。
|
|
32438
32982
|
* @author ddj 2026年08月28号
|
|
@@ -32510,7 +33054,7 @@ window.__ModuleLoader__.load({
|
|
|
32510
33054
|
*/
|
|
32511
33055
|
function explorerLoad(scope) {
|
|
32512
33056
|
try {
|
|
32513
|
-
return parseExplorer(window.localStorage.getItem(KEY_PREFIX$
|
|
33057
|
+
return parseExplorer(window.localStorage.getItem(KEY_PREFIX$1 + String(scope)));
|
|
32514
33058
|
} catch (error) {
|
|
32515
33059
|
return null;
|
|
32516
33060
|
}
|
|
@@ -32523,12 +33067,12 @@ window.__ModuleLoader__.load({
|
|
|
32523
33067
|
*/
|
|
32524
33068
|
function explorerSave(scope, data) {
|
|
32525
33069
|
try {
|
|
32526
|
-
window.localStorage.setItem(KEY_PREFIX$
|
|
33070
|
+
window.localStorage.setItem(KEY_PREFIX$1 + String(scope), serializeExplorer(data));
|
|
32527
33071
|
} catch (error) {}
|
|
32528
33072
|
}
|
|
32529
33073
|
//#endregion
|
|
32530
33074
|
//#region src/client/state/explorerEntriesCache.ts
|
|
32531
|
-
const KEY_PREFIX
|
|
33075
|
+
const KEY_PREFIX = CACHE_KEY.entries;
|
|
32532
33076
|
/** 裁剪到上限(目录按 ts 逐出最旧;单目录条目截断)。 */
|
|
32533
33077
|
function entriesTrim(data, maxDirs = 300, maxEntries = 500) {
|
|
32534
33078
|
const dirs = {};
|
|
@@ -32602,7 +33146,7 @@ window.__ModuleLoader__.load({
|
|
|
32602
33146
|
/** localStorage 读取(不可用 → null)。 */
|
|
32603
33147
|
function readRaw(scope) {
|
|
32604
33148
|
try {
|
|
32605
|
-
return window.localStorage.getItem(KEY_PREFIX
|
|
33149
|
+
return window.localStorage.getItem(KEY_PREFIX + scope);
|
|
32606
33150
|
} catch (error) {
|
|
32607
33151
|
return null;
|
|
32608
33152
|
}
|
|
@@ -32610,7 +33154,7 @@ window.__ModuleLoader__.load({
|
|
|
32610
33154
|
/** localStorage 写入(配额/隐私模式失败静默)。 */
|
|
32611
33155
|
function writeRaw(scope, text) {
|
|
32612
33156
|
try {
|
|
32613
|
-
window.localStorage.setItem(KEY_PREFIX
|
|
33157
|
+
window.localStorage.setItem(KEY_PREFIX + scope, text);
|
|
32614
33158
|
} catch (error) {}
|
|
32615
33159
|
}
|
|
32616
33160
|
/** 取(或建)作用域缓存镜像。 */
|
|
@@ -33230,12 +33774,12 @@ window.__ModuleLoader__.load({
|
|
|
33230
33774
|
setTarget(next);
|
|
33231
33775
|
if (next) current.prepare(next.path);
|
|
33232
33776
|
}
|
|
33233
|
-
/** @private @author ddj 2026年10月
|
|
33777
|
+
/** @private @author ddj 2026年10月22号 Attempts virtual data-index location or the original mounted-row fallback after commit/resize; consumes only a successful current request. */
|
|
33234
33778
|
function tryReveal() {
|
|
33235
33779
|
const next = pending.current;
|
|
33236
33780
|
const current = latest.current;
|
|
33237
33781
|
if (!next || next.scope !== current.scope || next.active !== current.activePath) return;
|
|
33238
|
-
if (!revealTreeRow(current.treeRef.current, next.path)) return;
|
|
33782
|
+
if (!(current.locate ? current.locate(next.path) : revealTreeRow(current.treeRef.current, next.path))) return;
|
|
33239
33783
|
pending.current = null;
|
|
33240
33784
|
setRevealPath(next.path);
|
|
33241
33785
|
clearHighlight();
|
|
@@ -33278,6 +33822,106 @@ window.__ModuleLoader__.load({
|
|
|
33278
33822
|
requestReveal
|
|
33279
33823
|
};
|
|
33280
33824
|
}
|
|
33825
|
+
/**
|
|
33826
|
+
* @public @author ddj 2026年10月22号
|
|
33827
|
+
* @description Preserve the first visible path when rows above it expand or collapse.
|
|
33828
|
+
* @param previous Previous complete model.
|
|
33829
|
+
* @param rows New complete model.
|
|
33830
|
+
* @param top Previous scroll offset.
|
|
33831
|
+
* @returns Anchored offset, or original offset for subsequent clamping if path disappeared.
|
|
33832
|
+
*/
|
|
33833
|
+
function treeAnchorTop(previous, rows, top) {
|
|
33834
|
+
const anchor = previous[Math.floor(Math.max(0, top) / 24)]?.key;
|
|
33835
|
+
if (!anchor) return top;
|
|
33836
|
+
const nextIndex = rows.findIndex((row) => row.key === anchor);
|
|
33837
|
+
if (nextIndex < 0) return top;
|
|
33838
|
+
return nextIndex * 24 + top % 24;
|
|
33839
|
+
}
|
|
33840
|
+
/**
|
|
33841
|
+
* @public @author ddj 2026年10月22号
|
|
33842
|
+
* @description Flatten all expanded entries in their supplied order, including metadata files.
|
|
33843
|
+
* @param entriesOf Directory snapshot lookup; null means unavailable.
|
|
33844
|
+
* @param expanded Current directory expansion flags.
|
|
33845
|
+
* @param loading Current loading flags.
|
|
33846
|
+
* @returns Complete row model, independent of viewport mounting.
|
|
33847
|
+
*/
|
|
33848
|
+
function flattenTree(entriesOf, expanded, loading) {
|
|
33849
|
+
const rows = [];
|
|
33850
|
+
const pending = [{
|
|
33851
|
+
rel: "",
|
|
33852
|
+
depth: 0
|
|
33853
|
+
}];
|
|
33854
|
+
while (pending.length) {
|
|
33855
|
+
const current = pending.pop();
|
|
33856
|
+
const entry = current.entry;
|
|
33857
|
+
if (entry) {
|
|
33858
|
+
const isDir = entry.type === "directory";
|
|
33859
|
+
const isOpen = expanded[entry.path] === true;
|
|
33860
|
+
rows.push({
|
|
33861
|
+
key: entry.path,
|
|
33862
|
+
e: entry,
|
|
33863
|
+
depth: current.depth,
|
|
33864
|
+
isDir,
|
|
33865
|
+
isOpen
|
|
33866
|
+
});
|
|
33867
|
+
if (isDir && isOpen) pending.push({
|
|
33868
|
+
rel: entry.path,
|
|
33869
|
+
depth: current.depth + 1
|
|
33870
|
+
});
|
|
33871
|
+
continue;
|
|
33872
|
+
}
|
|
33873
|
+
const entries = entriesOf(current.rel);
|
|
33874
|
+
if (!entries) {
|
|
33875
|
+
rows.push({
|
|
33876
|
+
key: current.rel + ":loading",
|
|
33877
|
+
depth: current.depth,
|
|
33878
|
+
text: loading[current.rel] ? "加载中…" : "无法加载"
|
|
33879
|
+
});
|
|
33880
|
+
continue;
|
|
33881
|
+
}
|
|
33882
|
+
for (let index = entries.length - 1; index >= 0; index--) pending.push({
|
|
33883
|
+
rel: current.rel,
|
|
33884
|
+
depth: current.depth,
|
|
33885
|
+
entry: entries[index]
|
|
33886
|
+
});
|
|
33887
|
+
}
|
|
33888
|
+
return rows;
|
|
33889
|
+
}
|
|
33890
|
+
/**
|
|
33891
|
+
* @public @author ddj 2026年10月22号
|
|
33892
|
+
* @description Clamp scroll state before reusing the existing fixed-height range calculation.
|
|
33893
|
+
* @param count Complete row count.
|
|
33894
|
+
* @param viewport Latest scroll position and measured viewport height.
|
|
33895
|
+
* @returns Clamped scroll offset and overscanned exclusive row range.
|
|
33896
|
+
*/
|
|
33897
|
+
function treeWindow(count, viewport) {
|
|
33898
|
+
const height = Math.max(0, viewport.height);
|
|
33899
|
+
const top = Math.min(Math.max(0, viewport.top), Math.max(0, count * 24 - height));
|
|
33900
|
+
return {
|
|
33901
|
+
top,
|
|
33902
|
+
...updateRange(count, top, height, 24)
|
|
33903
|
+
};
|
|
33904
|
+
}
|
|
33905
|
+
/**
|
|
33906
|
+
* @public @author ddj 2026年10月22号
|
|
33907
|
+
* @description Locate an unmounted row using model indices, without querying row DOM.
|
|
33908
|
+
* @param rows Complete flattened model.
|
|
33909
|
+
* @param path Requested relative file path.
|
|
33910
|
+
* @param viewport Current viewport geometry.
|
|
33911
|
+
* @returns New scroll offset, or null while ancestors/viewport are unavailable.
|
|
33912
|
+
*/
|
|
33913
|
+
function treeRevealTop(rows, path, viewport) {
|
|
33914
|
+
if (viewport.height <= 0) return null;
|
|
33915
|
+
const index = rows.findIndex((row) => row.e?.path === path);
|
|
33916
|
+
if (index < 0) return null;
|
|
33917
|
+
const current = treeWindow(rows.length, viewport).top;
|
|
33918
|
+
const rowTop = index * 24;
|
|
33919
|
+
if (rowTop >= current && rowTop + 24 <= current + viewport.height) return current;
|
|
33920
|
+
return treeWindow(rows.length, {
|
|
33921
|
+
top: rowTop - (viewport.height - 24) / 2,
|
|
33922
|
+
height: viewport.height
|
|
33923
|
+
}).top;
|
|
33924
|
+
}
|
|
33281
33925
|
//#endregion
|
|
33282
33926
|
//#region src/client/sidebar/panels/FileExplorer.ts
|
|
33283
33927
|
/**
|
|
@@ -33291,7 +33935,6 @@ window.__ModuleLoader__.load({
|
|
|
33291
33935
|
* 远端更新箭头由显式只读检查提供,与本地状态并存,不执行更新。
|
|
33292
33936
|
* 作者 ddj 2026-08-26 / 2026-08-27 / 2026-08-31 / 2026-09-10 / 2026-09-16 / 2026-10-09
|
|
33293
33937
|
*/
|
|
33294
|
-
const DIR_CAP = 4e3;
|
|
33295
33938
|
const SAVE_DEBOUNCE_MS = 300;
|
|
33296
33939
|
const FOLLOW_INTERVAL_MS = 1e4;
|
|
33297
33940
|
const PREFETCH_MAX = 4;
|
|
@@ -33390,6 +34033,11 @@ window.__ModuleLoader__.load({
|
|
|
33390
34033
|
const loadDirRef = react.default.useRef(null);
|
|
33391
34034
|
const refreshRef = react.default.useRef(null);
|
|
33392
34035
|
const treeRef = react.default.useRef(null);
|
|
34036
|
+
const [viewport, setViewport] = react.default.useState({
|
|
34037
|
+
scope,
|
|
34038
|
+
top: 0,
|
|
34039
|
+
height: 0
|
|
34040
|
+
});
|
|
33393
34041
|
const reloadDirRef = react.default.useRef(null);
|
|
33394
34042
|
const changedTimerRef = react.default.useRef(null);
|
|
33395
34043
|
const changedRelRef = react.default.useRef(/* @__PURE__ */ new Set());
|
|
@@ -33596,12 +34244,6 @@ window.__ModuleLoader__.load({
|
|
|
33596
34244
|
window.addEventListener("edrv:refresh", onRefresh);
|
|
33597
34245
|
return () => window.removeEventListener("edrv:refresh", onRefresh);
|
|
33598
34246
|
}, []);
|
|
33599
|
-
const { revealPath } = useExplorerReveal({
|
|
33600
|
-
scope,
|
|
33601
|
-
activePath,
|
|
33602
|
-
treeRef,
|
|
33603
|
-
prepare: prepareReveal
|
|
33604
|
-
});
|
|
33605
34247
|
const activeTreePath = treeRevealPath(activePath);
|
|
33606
34248
|
react.default.useEffect(() => {
|
|
33607
34249
|
if (!scope) return;
|
|
@@ -33613,39 +34255,9 @@ window.__ModuleLoader__.load({
|
|
|
33613
34255
|
}, FOLLOW_INTERVAL_MS);
|
|
33614
34256
|
return () => clearInterval(timer);
|
|
33615
34257
|
}, [scope]);
|
|
33616
|
-
/** @private @author ddj 2026年10月
|
|
34258
|
+
/** @private @author ddj 2026年10月22号 @returns Complete expanded model; icon-only updates do not flatten again. */
|
|
33617
34259
|
function flatten() {
|
|
33618
|
-
|
|
33619
|
-
/** @private @author ddj 2026年10月10号 @param rel Directory. @param depth Indent level. Appends only expanded rows and placeholders. */
|
|
33620
|
-
function visit(rel, depth) {
|
|
33621
|
-
const entries = dirs[rel] ?? entriesCacheGet(scope, rel);
|
|
33622
|
-
if (!entries) {
|
|
33623
|
-
result.push({
|
|
33624
|
-
key: rel + ":loading",
|
|
33625
|
-
depth,
|
|
33626
|
-
text: loading[rel] ? "加载中…" : "无法加载"
|
|
33627
|
-
});
|
|
33628
|
-
return;
|
|
33629
|
-
}
|
|
33630
|
-
for (const e of entries) {
|
|
33631
|
-
const isDir = e.type === "directory", isOpen = expanded[e.path] === true;
|
|
33632
|
-
result.push({
|
|
33633
|
-
key: e.path,
|
|
33634
|
-
e,
|
|
33635
|
-
depth,
|
|
33636
|
-
isDir,
|
|
33637
|
-
isOpen
|
|
33638
|
-
});
|
|
33639
|
-
if (isDir && isOpen) visit(e.path, depth + 1);
|
|
33640
|
-
}
|
|
33641
|
-
if (entries.length >= DIR_CAP) result.push({
|
|
33642
|
-
key: rel + ":cap",
|
|
33643
|
-
depth,
|
|
33644
|
-
text: "目录条目过多,仅显示前 4000 项"
|
|
33645
|
-
});
|
|
33646
|
-
}
|
|
33647
|
-
visit("", 0);
|
|
33648
|
-
return result;
|
|
34260
|
+
return flattenTree(entriesOf, expanded, loading);
|
|
33649
34261
|
}
|
|
33650
34262
|
const treeRows = react.default.useMemo(flatten, [
|
|
33651
34263
|
dirs,
|
|
@@ -33653,6 +34265,99 @@ window.__ModuleLoader__.load({
|
|
|
33653
34265
|
loading,
|
|
33654
34266
|
scope
|
|
33655
34267
|
]);
|
|
34268
|
+
const previousRows = react.default.useRef({
|
|
34269
|
+
scope,
|
|
34270
|
+
rows: treeRows
|
|
34271
|
+
});
|
|
34272
|
+
let anchoredTop = 0;
|
|
34273
|
+
if (viewport.scope === scope) {
|
|
34274
|
+
anchoredTop = viewport.top;
|
|
34275
|
+
if (previousRows.current.scope === scope && previousRows.current.rows !== treeRows) anchoredTop = treeAnchorTop(previousRows.current.rows, treeRows, viewport.top);
|
|
34276
|
+
}
|
|
34277
|
+
const currentViewport = {
|
|
34278
|
+
top: anchoredTop,
|
|
34279
|
+
height: viewport.height
|
|
34280
|
+
};
|
|
34281
|
+
const windowRows = treeWindow(treeRows.length, currentViewport);
|
|
34282
|
+
/** @private @author ddj 2026年10月22号 Read current geometry after scroll, resize or scope restoration. */
|
|
34283
|
+
function measureTree() {
|
|
34284
|
+
const host = treeRef.current;
|
|
34285
|
+
if (!host || host.clientHeight <= 0) return;
|
|
34286
|
+
setViewport({
|
|
34287
|
+
scope,
|
|
34288
|
+
top: host.scrollTop,
|
|
34289
|
+
height: host.clientHeight
|
|
34290
|
+
});
|
|
34291
|
+
}
|
|
34292
|
+
const scrollFrame = react.default.useRef(null);
|
|
34293
|
+
/** @private @author ddj 2026年10月22号 Coalesce high-frequency scrolling into one measured render per frame. */
|
|
34294
|
+
function scrollTree() {
|
|
34295
|
+
if (scrollFrame.current !== null) return;
|
|
34296
|
+
scrollFrame.current = requestAnimationFrame(readScroll);
|
|
34297
|
+
}
|
|
34298
|
+
/** @private @author ddj 2026年10月22号 Read only after the pending animation frame fires. */
|
|
34299
|
+
function readScroll() {
|
|
34300
|
+
scrollFrame.current = null;
|
|
34301
|
+
measureTree();
|
|
34302
|
+
}
|
|
34303
|
+
/** @private @author ddj 2026年10月22号 @returns Resize observer and pending frame cleanup. Keep hidden trees measurable when shown. */
|
|
34304
|
+
function watchTree() {
|
|
34305
|
+
const observer = typeof ResizeObserver === "function" ? new ResizeObserver(measureTree) : null;
|
|
34306
|
+
if (treeRef.current) observer?.observe(treeRef.current);
|
|
34307
|
+
measureTree();
|
|
34308
|
+
/** @private @author ddj 2026年10月22号 Stop all viewport callbacks owned by this scope. */
|
|
34309
|
+
function cleanup() {
|
|
34310
|
+
observer?.disconnect();
|
|
34311
|
+
if (scrollFrame.current !== null) cancelAnimationFrame(scrollFrame.current);
|
|
34312
|
+
scrollFrame.current = null;
|
|
34313
|
+
}
|
|
34314
|
+
return cleanup;
|
|
34315
|
+
}
|
|
34316
|
+
react.default.useLayoutEffect(watchTree, [scope]);
|
|
34317
|
+
/** @private @author ddj 2026年10月22号 Clamp actual scroll after collapse, filtering, or workspace switch. */
|
|
34318
|
+
function clampTree() {
|
|
34319
|
+
const host = treeRef.current;
|
|
34320
|
+
previousRows.current = {
|
|
34321
|
+
scope,
|
|
34322
|
+
rows: treeRows
|
|
34323
|
+
};
|
|
34324
|
+
if (!host || host.clientHeight <= 0) return;
|
|
34325
|
+
if (host.scrollTop !== windowRows.top) host.scrollTop = windowRows.top;
|
|
34326
|
+
if (viewport.scope !== scope || viewport.top !== windowRows.top) setViewport({
|
|
34327
|
+
scope,
|
|
34328
|
+
top: windowRows.top,
|
|
34329
|
+
height: host.clientHeight
|
|
34330
|
+
});
|
|
34331
|
+
}
|
|
34332
|
+
react.default.useLayoutEffect(clampTree, [
|
|
34333
|
+
scope,
|
|
34334
|
+
windowRows.top,
|
|
34335
|
+
treeRows
|
|
34336
|
+
]);
|
|
34337
|
+
/** @private @author ddj 2026年10月22号 @param path Requested file. @returns Whether its complete model row is available. */
|
|
34338
|
+
function locateTree(path) {
|
|
34339
|
+
const host = treeRef.current;
|
|
34340
|
+
if (!host) return false;
|
|
34341
|
+
const top = treeRevealTop(treeRows, path, {
|
|
34342
|
+
top: host.scrollTop,
|
|
34343
|
+
height: host.clientHeight
|
|
34344
|
+
});
|
|
34345
|
+
if (top === null) return false;
|
|
34346
|
+
host.scrollTop = top;
|
|
34347
|
+
setViewport({
|
|
34348
|
+
scope,
|
|
34349
|
+
top,
|
|
34350
|
+
height: host.clientHeight
|
|
34351
|
+
});
|
|
34352
|
+
return true;
|
|
34353
|
+
}
|
|
34354
|
+
const { revealPath } = useExplorerReveal({
|
|
34355
|
+
scope,
|
|
34356
|
+
activePath,
|
|
34357
|
+
treeRef,
|
|
34358
|
+
prepare: prepareReveal,
|
|
34359
|
+
locate: locateTree
|
|
34360
|
+
});
|
|
33656
34361
|
/** @private @author ddj 2026年10月10号 @returns Stable RPC row projection, independent of icon-only renders. */
|
|
33657
34362
|
function projectRows() {
|
|
33658
34363
|
return treeRows.filter((row) => row.e).map((row) => ({
|
|
@@ -33673,6 +34378,7 @@ window.__ModuleLoader__.load({
|
|
|
33673
34378
|
event.preventDefault();
|
|
33674
34379
|
event.stopPropagation();
|
|
33675
34380
|
setMenu({
|
|
34381
|
+
scope,
|
|
33676
34382
|
x: event.clientX,
|
|
33677
34383
|
y: event.clientY,
|
|
33678
34384
|
target: {
|
|
@@ -33682,7 +34388,7 @@ window.__ModuleLoader__.load({
|
|
|
33682
34388
|
});
|
|
33683
34389
|
}
|
|
33684
34390
|
const onSelect = react.default.useCallback((path, isDir) => selectRef.current(path, isDir), []);
|
|
33685
|
-
const onContext = react.default.useCallback(contextRow, []);
|
|
34391
|
+
const onContext = react.default.useCallback(contextRow, [scope]);
|
|
33686
34392
|
const rootName = root ? String(root).split(/[\\/]/).pop() || root : "";
|
|
33687
34393
|
/** @private @author ddj 2026年10月10号 @param row Flat descriptor. @returns Memo row; unaffected status objects retain identity. */
|
|
33688
34394
|
function renderRow(row) {
|
|
@@ -33715,6 +34421,7 @@ window.__ModuleLoader__.load({
|
|
|
33715
34421
|
ev.stopPropagation();
|
|
33716
34422
|
if (!ev.currentTarget.contains(ev.target)) return;
|
|
33717
34423
|
setMenu({
|
|
34424
|
+
scope,
|
|
33718
34425
|
x: ev.clientX,
|
|
33719
34426
|
y: ev.clientY,
|
|
33720
34427
|
target: {
|
|
@@ -33723,7 +34430,13 @@ window.__ModuleLoader__.load({
|
|
|
33723
34430
|
}
|
|
33724
34431
|
});
|
|
33725
34432
|
};
|
|
33726
|
-
const
|
|
34433
|
+
const menuValid = menu?.scope === scope && (menu.target.path === "" || treeRows.some((row) => row.e?.path === menu.target.path));
|
|
34434
|
+
/** @private @author ddj 2026年10月22号 Close stale path menus instead of rebinding recycled rows. */
|
|
34435
|
+
function closeLostMenu() {
|
|
34436
|
+
if (menu && !menuValid) setMenu(null);
|
|
34437
|
+
}
|
|
34438
|
+
react.default.useEffect(closeLostMenu, [menu, menuValid]);
|
|
34439
|
+
const menuEntries = menuValid ? buildTreeMenu(ctx?.fileMenuItems, menu.target, ctx).map((item) => ({
|
|
33727
34440
|
id: item.id,
|
|
33728
34441
|
label: item.label,
|
|
33729
34442
|
danger: item.danger,
|
|
@@ -33763,16 +34476,23 @@ window.__ModuleLoader__.load({
|
|
|
33763
34476
|
}, refreshIconEl$1())), svn.message ? react.default.createElement("div", {
|
|
33764
34477
|
className: "edrv-tree-status",
|
|
33765
34478
|
role: "status"
|
|
33766
|
-
}, svn.message) : null, react.default.createElement("div", {
|
|
33767
|
-
className: "edrv-tree",
|
|
33768
|
-
ref: treeRef
|
|
33769
|
-
}, errorText ? react.default.createElement("div", { className: "edrv-tree-error" }, react.default.createElement("span", null, String(errorText)), react.default.createElement("button", {
|
|
34479
|
+
}, svn.message) : null, errorText ? react.default.createElement("div", { className: "edrv-tree-error" }, react.default.createElement("span", null, String(errorText)), react.default.createElement("button", {
|
|
33770
34480
|
className: "edrv-side-btn",
|
|
33771
34481
|
onClick: () => emitRefresh({
|
|
33772
34482
|
scope,
|
|
33773
34483
|
kind: "unknown"
|
|
33774
34484
|
})
|
|
33775
|
-
}, "重试")) : null,
|
|
34485
|
+
}, "重试")) : null, react.default.createElement("div", {
|
|
34486
|
+
className: "edrv-tree edrv-tree-virtual",
|
|
34487
|
+
ref: treeRef,
|
|
34488
|
+
onScroll: scrollTree
|
|
34489
|
+
}, react.default.createElement("div", {
|
|
34490
|
+
"aria-hidden": true,
|
|
34491
|
+
style: { height: windowRows.start * 24 }
|
|
34492
|
+
}), treeRows.slice(windowRows.start, windowRows.end).map(renderRow), react.default.createElement("div", {
|
|
34493
|
+
"aria-hidden": true,
|
|
34494
|
+
style: { height: (treeRows.length - windowRows.end) * 24 }
|
|
34495
|
+
})), menu && menuEntries.length ? react.default.createElement(ContextMenu, {
|
|
33776
34496
|
x: menu.x,
|
|
33777
34497
|
y: menu.y,
|
|
33778
34498
|
entries: menuEntries,
|
|
@@ -34267,713 +34987,79 @@ window.__ModuleLoader__.load({
|
|
|
34267
34987
|
})), optsBar, filterRows, summary, warnBanner, body);
|
|
34268
34988
|
}
|
|
34269
34989
|
//#endregion
|
|
34270
|
-
//#region src/client/
|
|
34271
|
-
/**
|
|
34272
|
-
|
|
34273
|
-
|
|
34274
|
-
|
|
34275
|
-
* @returns 去引号后的值
|
|
34276
|
-
*/
|
|
34277
|
-
function stripQuotes(raw) {
|
|
34278
|
-
const value = raw.trim();
|
|
34279
|
-
if (value.length >= 2 && (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'"))) return value.slice(1, -1);
|
|
34280
|
-
return value;
|
|
34281
|
-
}
|
|
34282
|
-
/**
|
|
34283
|
-
* 解析 .mdc 全文:首行 --- 且 100 行内有闭合 --- 才视为 frontmatter;
|
|
34284
|
-
* 已知键 description/alwaysApply/globs/enabled,其余原行进 fmExtra;永不抛错。
|
|
34285
|
-
* @author ddj 2026年09月07号
|
|
34286
|
-
* @param text 文件全文
|
|
34287
|
-
* @returns 解析结果
|
|
34288
|
-
*/
|
|
34289
|
-
function parseRuleFm(text) {
|
|
34290
|
-
const bom = text.startsWith("");
|
|
34291
|
-
const lines = text.replace(/^\uFEFF/, "").split(/\r?\n/);
|
|
34292
|
-
const none = {
|
|
34293
|
-
type: "manual",
|
|
34294
|
-
description: "",
|
|
34295
|
-
alwaysApply: false,
|
|
34296
|
-
globs: [],
|
|
34297
|
-
enabled: true,
|
|
34298
|
-
hasFm: false,
|
|
34299
|
-
body: text.replace(/^\uFEFF/, ""),
|
|
34300
|
-
fmExtra: [],
|
|
34301
|
-
bom,
|
|
34302
|
-
hasEnabledLine: false
|
|
34303
|
-
};
|
|
34304
|
-
if (lines[0]?.trim() !== "---") return none;
|
|
34305
|
-
let close = -1;
|
|
34306
|
-
for (let i = 1; i < lines.length && i <= 101; i++) if (lines[i].trim() === "---") {
|
|
34307
|
-
close = i;
|
|
34308
|
-
break;
|
|
34309
|
-
}
|
|
34310
|
-
if (close < 0) return none;
|
|
34311
|
-
const parsed = {
|
|
34312
|
-
type: "manual",
|
|
34313
|
-
description: "",
|
|
34314
|
-
alwaysApply: false,
|
|
34315
|
-
globs: [],
|
|
34316
|
-
enabled: true,
|
|
34317
|
-
hasFm: true,
|
|
34318
|
-
body: lines.slice(close + 1).join("\n"),
|
|
34319
|
-
fmExtra: [],
|
|
34320
|
-
bom,
|
|
34321
|
-
hasEnabledLine: false
|
|
34322
|
-
};
|
|
34323
|
-
for (let i = 1; i < close; i++) {
|
|
34324
|
-
const match = /^([A-Za-z_-]+)[ \t]*:[ \t]?(.*)$/.exec(lines[i]);
|
|
34325
|
-
if (!match) {
|
|
34326
|
-
parsed.fmExtra.push(lines[i]);
|
|
34327
|
-
continue;
|
|
34328
|
-
}
|
|
34329
|
-
const [, key, raw] = match;
|
|
34330
|
-
if (key === "description") parsed.description = stripQuotes(raw);
|
|
34331
|
-
else if (key === "alwaysApply") parsed.alwaysApply = stripQuotes(raw).toLowerCase() === "true";
|
|
34332
|
-
else if (key === "enabled") {
|
|
34333
|
-
parsed.enabled = stripQuotes(raw).toLowerCase() !== "false";
|
|
34334
|
-
parsed.hasEnabledLine = true;
|
|
34335
|
-
} else if (key === "globs") parsed.globs = parseGlobsValue(raw, lines, i, close);
|
|
34336
|
-
else parsed.fmExtra.push(lines[i]);
|
|
34337
|
-
}
|
|
34338
|
-
parsed.type = fmTypeOf(parsed);
|
|
34339
|
-
return parsed;
|
|
34340
|
-
}
|
|
34341
|
-
/**
|
|
34342
|
-
* 解析 globs 值:内联逗号分隔串,或空值后接 YAML 短横列表(与 host parseGlobs 一致)。
|
|
34343
|
-
* @author ddj 2026年09月07号
|
|
34344
|
-
* @param value 内联值
|
|
34345
|
-
* @param lines 全文行
|
|
34346
|
-
* @param start globs 行下标
|
|
34347
|
-
* @param close 闭合 --- 下标
|
|
34348
|
-
* @returns glob 数组
|
|
34349
|
-
*/
|
|
34350
|
-
function parseGlobsValue(value, lines, start, close) {
|
|
34351
|
-
const inline = stripQuotes(value);
|
|
34352
|
-
if (inline) return inline.split(",").map((item) => stripQuotes(item)).filter(Boolean);
|
|
34353
|
-
const list = [];
|
|
34354
|
-
for (let j = start + 1; j < close; j++) {
|
|
34355
|
-
const item = /^[ \t]*-[ \t]*(.+)$/.exec(lines[j]);
|
|
34356
|
-
if (!item) break;
|
|
34357
|
-
list.push(stripQuotes(item[1]));
|
|
34358
|
-
}
|
|
34359
|
-
return list;
|
|
34360
|
-
}
|
|
34361
|
-
/**
|
|
34362
|
-
* 由 frontmatter 推导类型:alwaysApply=true → 总是;有 globs → 自动;否则手动。
|
|
34363
|
-
* @author ddj 2026年09月07号
|
|
34364
|
-
* @param parsed 解析结果
|
|
34365
|
-
* @returns 规则类型
|
|
34366
|
-
*/
|
|
34367
|
-
function fmTypeOf(parsed) {
|
|
34368
|
-
if (parsed.alwaysApply) return "always";
|
|
34369
|
-
if (parsed.globs.length) return "auto";
|
|
34370
|
-
return "manual";
|
|
34371
|
-
}
|
|
34372
|
-
/**
|
|
34373
|
-
* 按最新控件值重写 frontmatter:known 键固定顺序 + fmExtra + 闭合 --- + 原 body;
|
|
34374
|
-
* 无 frontmatter 且目标为手动时:有描述则新建仅含描述的 frontmatter,否则原文返回。
|
|
34375
|
-
* @author ddj 2026年09月07号
|
|
34376
|
-
* @param parsed parseRuleFm 的结果(必须来自同一文本)
|
|
34377
|
-
* @param next 控件最新值
|
|
34378
|
-
* @returns 重写后的全文
|
|
34379
|
-
*/
|
|
34380
|
-
function applyRuleMeta(parsed, next) {
|
|
34381
|
-
if (!parsed.hasFm && next.type === "manual") {
|
|
34382
|
-
if (!next.description.trim()) return (parsed.bom ? "" : "") + parsed.body;
|
|
34383
|
-
return (parsed.bom ? "" : "") + [
|
|
34384
|
-
"---",
|
|
34385
|
-
"description: " + next.description.trim(),
|
|
34386
|
-
"---"
|
|
34387
|
-
].join("\n") + "\n" + parsed.body;
|
|
34388
|
-
}
|
|
34389
|
-
const lines = ["---"];
|
|
34390
|
-
lines.push("description: " + next.description.trim());
|
|
34391
|
-
if (next.type === "always") lines.push("alwaysApply: true");
|
|
34392
|
-
if (next.type === "auto") lines.push("globs: " + next.globs.join(", "));
|
|
34393
|
-
if (parsed.hasEnabledLine) lines.push("enabled: " + (parsed.enabled ? "true" : "false"));
|
|
34394
|
-
lines.push(...parsed.fmExtra);
|
|
34395
|
-
lines.push("---");
|
|
34396
|
-
return (parsed.bom ? "" : "") + lines.join("\n") + "\n" + parsed.body;
|
|
34397
|
-
}
|
|
34398
|
-
//#endregion
|
|
34399
|
-
//#region src/client/sidebar/panels/RulesPanel.ts
|
|
34400
|
-
/**
|
|
34401
|
-
* dsh-vscode-mode client — 侧边栏「规则」面板(Codebuddy 规则管理形态)。
|
|
34402
|
-
* 双 Tab:用户规则(~/.dsh/rules/)与项目规则(<工作区>/.dsh/rules/);
|
|
34403
|
-
* 列表行 = 文件名 + 相对提示,第二行类型徽标与描述,右侧 编辑/删除 图标 + 滑动启用开关;
|
|
34404
|
-
* 新建/编辑 = 顶部面包屑 + 规则类型下拉(总是/自动/手动)+ 描述/globs 控件,与原始 .mdc
|
|
34405
|
-
* 文本域双向同步(控件改写 frontmatter,文本域回读控件),保存走 rules.save。
|
|
34406
|
-
* 作者 ddj 2026年09月03号(2026年09月07号 交互性调整)
|
|
34407
|
-
*/
|
|
34408
|
-
/** 新建规则模板(frontmatter 字段与 host parseRuleMdc 对齐)。 */
|
|
34409
|
-
const NEW_TEMPLATE = "---\ndescription: \nalwaysApply: true\n---\n\n";
|
|
34410
|
-
const TYPE_LABEL = {
|
|
34411
|
-
always: "总是",
|
|
34412
|
-
auto: "自动",
|
|
34413
|
-
manual: "手动"
|
|
34990
|
+
//#region src/client/lazyPanel.ts
|
|
34991
|
+
/** Package-local CJS panel loading; transport, revision URLs and HMR belong to DSH. @author ddj 2026年10月22号 */
|
|
34992
|
+
const FILES = {
|
|
34993
|
+
rules: "./client.rules.js",
|
|
34994
|
+
debug: "./client.debug.js"
|
|
34414
34995
|
};
|
|
34415
|
-
|
|
34416
|
-
|
|
34417
|
-
|
|
34418
|
-
|
|
34419
|
-
|
|
34420
|
-
|
|
34421
|
-
|
|
34422
|
-
|
|
34423
|
-
|
|
34424
|
-
|
|
34425
|
-
|
|
34426
|
-
|
|
34427
|
-
|
|
34428
|
-
|
|
34429
|
-
|
|
34430
|
-
|
|
34431
|
-
|
|
34432
|
-
|
|
34433
|
-
|
|
34434
|
-
|
|
34435
|
-
|
|
34436
|
-
}
|
|
34437
|
-
|
|
34438
|
-
|
|
34439
|
-
|
|
34440
|
-
|
|
34441
|
-
|
|
34442
|
-
|
|
34443
|
-
|
|
34444
|
-
|
|
34445
|
-
|
|
34446
|
-
localStorage.setItem(key, value);
|
|
34447
|
-
} catch (e) {}
|
|
34448
|
-
}
|
|
34449
|
-
/**
|
|
34450
|
-
* 类型徽标(总是=绿 / 自动=蓝 / 手动=灰)。
|
|
34451
|
-
* @author ddj 2026年09月03号
|
|
34452
|
-
* @param type 规则类型
|
|
34453
|
-
* @returns 徽标元素
|
|
34454
|
-
*/
|
|
34455
|
-
function typeBadge(type) {
|
|
34456
|
-
return react.default.createElement("span", { className: "edrv-rules-type edrv-rules-type-" + type }, TYPE_LABEL[type] ?? type);
|
|
34457
|
-
}
|
|
34458
|
-
/**
|
|
34459
|
-
* 按字符上限截断列表行描述,超出以省略号收尾(上限随面板宽度动态变化;
|
|
34460
|
-
* 窄面板下 CSS 省略号仍会按像素提前截断,二者取更小)。
|
|
34461
|
-
* @author ddj 2026年09月07号
|
|
34462
|
-
* @param text 完整描述
|
|
34463
|
-
* @param cap 本帧字符上限
|
|
34464
|
-
* @returns 截断后的展示文本
|
|
34465
|
-
*/
|
|
34466
|
-
function clampDesc(text, cap) {
|
|
34467
|
-
const chars = Array.from(String(text ?? ""));
|
|
34468
|
-
const limit = Math.max(4, Number(cap) || DESC_MAX);
|
|
34469
|
-
return chars.length > limit ? chars.slice(0, limit).join("") + "…" : chars.join("");
|
|
34470
|
-
}
|
|
34471
|
-
/**
|
|
34472
|
-
* 由面板宽度估算描述可用字符数:随宽度动态变化,并与基础上限取小、极窄时保底。
|
|
34473
|
-
* @author ddj 2026年09月07号
|
|
34474
|
-
* @param panelWidth 面板内容宽度(px)
|
|
34475
|
-
* @returns 本帧描述字符上限
|
|
34476
|
-
*/
|
|
34477
|
-
function descCapOf(panelWidth) {
|
|
34478
|
-
const byWidth = Math.floor((Number(panelWidth) - DESC_RESERVE_PX) / DESC_CHAR_PX);
|
|
34479
|
-
return Math.max(DESC_CAP_MIN, Math.min(DESC_MAX, byWidth));
|
|
34480
|
-
}
|
|
34481
|
-
/**
|
|
34482
|
-
* 编辑图标(线性铅笔,与 DiffBox chevron 同风格:currentColor 描边装饰图)。
|
|
34483
|
-
* @author ddj 2026年09月07号
|
|
34484
|
-
* @returns 13×13 装饰性 SVG
|
|
34485
|
-
*/
|
|
34486
|
-
function pencilIcon() {
|
|
34487
|
-
return react.default.createElement("svg", {
|
|
34488
|
-
width: 13,
|
|
34489
|
-
height: 13,
|
|
34490
|
-
viewBox: "0 0 24 24",
|
|
34491
|
-
fill: "none",
|
|
34492
|
-
"aria-hidden": true,
|
|
34493
|
-
focusable: false
|
|
34494
|
-
}, react.default.createElement("path", {
|
|
34495
|
-
d: "M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z",
|
|
34496
|
-
stroke: "currentColor",
|
|
34497
|
-
strokeWidth: 2,
|
|
34498
|
-
strokeLinecap: "round",
|
|
34499
|
-
strokeLinejoin: "round"
|
|
34500
|
-
}));
|
|
34501
|
-
}
|
|
34502
|
-
/**
|
|
34503
|
-
* 删除图标(线性垃圾桶,风格同 pencilIcon)。
|
|
34504
|
-
* @author ddj 2026年09月07号
|
|
34505
|
-
* @returns 13×13 装饰性 SVG
|
|
34506
|
-
*/
|
|
34507
|
-
function trashIcon() {
|
|
34508
|
-
return react.default.createElement("svg", {
|
|
34509
|
-
width: 13,
|
|
34510
|
-
height: 13,
|
|
34511
|
-
viewBox: "0 0 24 24",
|
|
34512
|
-
fill: "none",
|
|
34513
|
-
"aria-hidden": true,
|
|
34514
|
-
focusable: false
|
|
34515
|
-
}, react.default.createElement("path", {
|
|
34516
|
-
d: "M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14zM10 11v6M14 11v6",
|
|
34517
|
-
stroke: "currentColor",
|
|
34518
|
-
strokeWidth: 2,
|
|
34519
|
-
strokeLinecap: "round",
|
|
34520
|
-
strokeLinejoin: "round"
|
|
34521
|
-
}));
|
|
34522
|
-
}
|
|
34523
|
-
/**
|
|
34524
|
-
* 逗号分隔的 globs 输入串 → 数组(去空项)。
|
|
34525
|
-
* @author ddj 2026年09月07号
|
|
34526
|
-
* @param text 输入串
|
|
34527
|
-
* @returns glob 数组
|
|
34528
|
-
*/
|
|
34529
|
-
function globList(text) {
|
|
34530
|
-
return String(text ?? "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
34531
|
-
}
|
|
34532
|
-
/**
|
|
34533
|
-
* 由控件值重建 .mdc 全文:frontmatter 按类型/描述/globs 重写,正文与其余键不动。
|
|
34534
|
-
* @author ddj 2026年09月07号
|
|
34535
|
-
* @param base 当前全文
|
|
34536
|
-
* @param fmType 目标类型(always/auto/manual)
|
|
34537
|
-
* @param desc 描述文本
|
|
34538
|
-
* @param globsText globs 输入串
|
|
34539
|
-
* @returns 重写后的全文
|
|
34540
|
-
*/
|
|
34541
|
-
function rebuildContent(base, fmType, desc, globsText) {
|
|
34542
|
-
return applyRuleMeta(parseRuleFm(base), {
|
|
34543
|
-
type: fmType,
|
|
34544
|
-
description: desc,
|
|
34545
|
-
globs: globList(globsText)
|
|
34996
|
+
const EXPORTS = {
|
|
34997
|
+
rules: "RulesPanel",
|
|
34998
|
+
debug: "DebugPanel"
|
|
34999
|
+
};
|
|
35000
|
+
const loads = /* @__PURE__ */ new Map();
|
|
35001
|
+
let generation = 0;
|
|
35002
|
+
let importer;
|
|
35003
|
+
/** @public @author ddj 2026年10月22号 @param transport Injected ModuleLoader require.async, never a global require. @description Start a new plugin generation and discard older results. */
|
|
35004
|
+
function setPanelImport(transport) {
|
|
35005
|
+
generation++;
|
|
35006
|
+
loads.clear();
|
|
35007
|
+
importer = transport;
|
|
35008
|
+
}
|
|
35009
|
+
/** @public @author ddj 2026年10月22号 @param key Fixed built-in panel key. @returns Shared pending load or cached component. @throws Unsupported loader or invalid chunk export. */
|
|
35010
|
+
function loadPanel(key) {
|
|
35011
|
+
const cached = loads.get(key);
|
|
35012
|
+
if (cached?.component) return Promise.resolve(cached.component);
|
|
35013
|
+
if (cached?.pending) return cached.pending;
|
|
35014
|
+
if (!importer) return Promise.reject(/* @__PURE__ */ new Error("当前 DSH 不支持包内面板加载,请升级宿主"));
|
|
35015
|
+
const epoch = generation;
|
|
35016
|
+
const transport = importer;
|
|
35017
|
+
const state = {};
|
|
35018
|
+
const pending = Promise.resolve().then(() => transport(FILES[key])).then((exports) => {
|
|
35019
|
+
const component = exports[EXPORTS[key]];
|
|
35020
|
+
if (typeof component !== "function") throw new Error("面板脚本缺少组件导出");
|
|
35021
|
+
if (epoch !== generation) throw new Error("面板加载已失效,请重试");
|
|
35022
|
+
state.component = component;
|
|
35023
|
+
return state.component;
|
|
35024
|
+
}).catch((error) => {
|
|
35025
|
+
if (loads.get(key) === state) loads.delete(key);
|
|
35026
|
+
throw error;
|
|
34546
35027
|
});
|
|
35028
|
+
state.pending = pending;
|
|
35029
|
+
loads.set(key, state);
|
|
35030
|
+
return pending;
|
|
34547
35031
|
}
|
|
34548
|
-
/**
|
|
34549
|
-
|
|
34550
|
-
|
|
34551
|
-
|
|
34552
|
-
|
|
34553
|
-
|
|
34554
|
-
|
|
34555
|
-
|
|
34556
|
-
|
|
34557
|
-
|
|
34558
|
-
|
|
34559
|
-
|
|
34560
|
-
|
|
34561
|
-
|
|
34562
|
-
|
|
34563
|
-
|
|
34564
|
-
|
|
34565
|
-
className: "edrv-rules-path",
|
|
34566
|
-
title: rule.absPath
|
|
34567
|
-
}, "旧目录") : null, !rule.enabled ? react.default.createElement("span", { className: "edrv-rules-offtag" }, "已停用") : null), react.default.createElement("div", { className: "edrv-rules-sub" }, react.default.createElement("span", { className: "edrv-rules-sublabel" }, "类型:"), typeBadge(rule.type), desc ? react.default.createElement("span", {
|
|
34568
|
-
className: "edrv-rules-desc",
|
|
34569
|
-
title: desc
|
|
34570
|
-
}, clampDesc(desc, props.descCap)) : null)), react.default.createElement("div", { className: "edrv-rules-actions" }, react.default.createElement("button", {
|
|
34571
|
-
className: "edrv-rules-act",
|
|
34572
|
-
title: "编辑",
|
|
34573
|
-
onClick: () => props.onEdit(rule)
|
|
34574
|
-
}, pencilIcon()), react.default.createElement("button", {
|
|
34575
|
-
className: "edrv-rules-act",
|
|
34576
|
-
title: "删除",
|
|
34577
|
-
onClick: () => props.onRemove(rule)
|
|
34578
|
-
}, trashIcon()), react.default.createElement("button", {
|
|
34579
|
-
className: "edrv-rules-switch" + (rule.enabled ? " on" : ""),
|
|
34580
|
-
role: "switch",
|
|
34581
|
-
"aria-checked": rule.enabled,
|
|
34582
|
-
title: rule.enabled ? "已启用(点击停用)" : "已停用(点击启用)",
|
|
34583
|
-
onClick: () => props.onToggle(rule)
|
|
34584
|
-
}, react.default.createElement("span", { className: "edrv-rules-knob" }))));
|
|
34585
|
-
}
|
|
34586
|
-
/**
|
|
34587
|
-
* 新建/编辑表单:面包屑头部 + 规则类型下拉/描述/globs 控件行 + 原始 .mdc 文本域 + 保存/取消。
|
|
34588
|
-
* @author ddj 2026年09月03号
|
|
34589
|
-
* @param props.form 表单状态(mode/scope/workspacePath/relHint/file/content/fmType/desc/globs/error/saving)
|
|
34590
|
-
* @param props.onType 类型下拉变更回调
|
|
34591
|
-
* @param props.onDesc 描述输入回调
|
|
34592
|
-
* @param props.onGlobs globs 输入回调
|
|
34593
|
-
* @param props.onBody 文本域编辑回调
|
|
34594
|
-
* @param props.onSave 保存回调
|
|
34595
|
-
* @param props.onCancel 取消回调
|
|
34596
|
-
* @returns 表单元素
|
|
34597
|
-
*/
|
|
34598
|
-
function RuleEditor(props) {
|
|
34599
|
-
const form = props.form;
|
|
34600
|
-
const isNew = form.mode === "new";
|
|
34601
|
-
const crumb = (form.scope === "project" ? "项目规则" : "用户规则") + " > " + form.relHint + " > " + (form.file || "新建规则.mdc");
|
|
34602
|
-
return react.default.createElement("div", { className: "edrv-rules-editor" }, react.default.createElement("div", { className: "edrv-rules-editor-head" }, react.default.createElement("span", {
|
|
34603
|
-
className: "edrv-rules-crumb",
|
|
34604
|
-
title: crumb
|
|
34605
|
-
}, crumb), react.default.createElement("button", {
|
|
34606
|
-
className: "edrv-rules-act",
|
|
34607
|
-
title: "取消",
|
|
34608
|
-
onClick: props.onCancel
|
|
34609
|
-
}, "✕")), react.default.createElement("div", { className: "edrv-rules-editor-ctl" }, react.default.createElement("label", { className: "edrv-rules-ctl-label" }, "规则类型"), react.default.createElement("select", {
|
|
34610
|
-
className: "edrv-rules-select",
|
|
34611
|
-
value: form.fmType,
|
|
34612
|
-
onChange: props.onType
|
|
34613
|
-
}, Object.keys(TYPE_LABEL).map((key) => react.default.createElement("option", {
|
|
34614
|
-
key,
|
|
34615
|
-
value: key
|
|
34616
|
-
}, TYPE_LABEL[key]))), react.default.createElement("label", { className: "edrv-rules-ctl-label" }, "描述"), react.default.createElement("input", {
|
|
34617
|
-
className: "edrv-rules-input edrv-rules-desc-input",
|
|
34618
|
-
value: form.desc,
|
|
34619
|
-
spellCheck: false,
|
|
34620
|
-
placeholder: isNew ? "规则用途说明(写入 frontmatter description)" : "规则用途说明",
|
|
34621
|
-
onChange: props.onDesc
|
|
34622
|
-
})), form.fmType === "auto" ? react.default.createElement("div", { className: "edrv-rules-editor-ctl" }, react.default.createElement("label", { className: "edrv-rules-ctl-label" }, "globs"), react.default.createElement("input", {
|
|
34623
|
-
className: "edrv-rules-input",
|
|
34624
|
-
value: form.globs,
|
|
34625
|
-
spellCheck: false,
|
|
34626
|
-
placeholder: "src/**/*.ts, *.md(逗号分隔,命中才注入)",
|
|
34627
|
-
onChange: props.onGlobs
|
|
34628
|
-
})) : null, react.default.createElement("textarea", {
|
|
34629
|
-
className: "edrv-rules-body",
|
|
34630
|
-
spellCheck: false,
|
|
34631
|
-
value: form.content,
|
|
34632
|
-
onChange: props.onBody
|
|
34633
|
-
}), react.default.createElement("div", { className: "edrv-rules-editor-hint" }, "类型/描述/globs 与 frontmatter 双向同步;enabled 与其余键原样保留;无 frontmatter 的纯 markdown 视为「手动」规则"), form.error ? react.default.createElement("div", { className: "edrv-rules-err" }, form.error) : null, react.default.createElement("div", { className: "edrv-rules-editor-foot" }, react.default.createElement("button", {
|
|
34634
|
-
className: "edrv-rules-save",
|
|
34635
|
-
disabled: form.saving,
|
|
34636
|
-
onClick: props.onSave
|
|
34637
|
-
}, form.saving ? "保存中…" : "保存")));
|
|
34638
|
-
}
|
|
34639
|
-
/**
|
|
34640
|
-
* 规则面板主体。
|
|
34641
|
-
* @param props.ctx 面板共享上下文(notify 可选)
|
|
34642
|
-
*/
|
|
34643
|
-
function RulesPanel(props) {
|
|
34644
|
-
const ctx = props?.ctx;
|
|
34645
|
-
const notify = ctx?.notify ?? ((message) => {});
|
|
34646
|
-
const cwd = ctx?.cwd ?? null;
|
|
34647
|
-
const [tab, setTab] = react.default.useState(() => readLocal(CACHE_KEY.rules + "tab") === "project" ? "project" : "user");
|
|
34648
|
-
const [data, setData] = react.default.useState(null);
|
|
34649
|
-
const [error, setError] = react.default.useState("");
|
|
34650
|
-
const panelRef = react.default.useRef(null);
|
|
34651
|
-
const [panelW, setPanelW] = react.default.useState(0);
|
|
34652
|
-
const [form, setForm] = react.default.useState(null);
|
|
34653
|
-
const mountedRef = react.default.useRef(true);
|
|
34654
|
-
/**
|
|
34655
|
-
* 当前工作区对应的项目聚合(按 ctx.cwd 自动匹配;无会话/未注册返回 null)。
|
|
34656
|
-
* @returns RuleProject 或 null
|
|
34657
|
-
*/
|
|
34658
|
-
const activeProject = () => {
|
|
34659
|
-
if (!cwd) return null;
|
|
34660
|
-
return (data?.projects ?? []).find((p) => p.workspacePath === cwd) ?? null;
|
|
34661
|
-
};
|
|
34662
|
-
/** 拉取规则总列表(rules.list)。 */
|
|
34663
|
-
const load = () => {
|
|
34664
|
-
rpc("rules.list", {}).then((res) => {
|
|
34665
|
-
if (!mountedRef.current) return;
|
|
34666
|
-
if (res && res.ok) {
|
|
34667
|
-
setData(res);
|
|
34668
|
-
setError("");
|
|
34669
|
-
} else setError(res?.error ?? "读取规则失败");
|
|
34670
|
-
}).catch((e) => {
|
|
34671
|
-
if (mountedRef.current) setError("读取规则异常: " + String(e));
|
|
34672
|
-
});
|
|
34673
|
-
};
|
|
34674
|
-
react.default.useEffect(() => {
|
|
34675
|
-
mountedRef.current = true;
|
|
34676
|
-
load();
|
|
35032
|
+
/** @public @author ddj 2026年10月22号 @param props Fixed panel and current context. @returns Loading, explicit retry, or the loaded panel subtree. */
|
|
35033
|
+
function LazyPanel(props) {
|
|
35034
|
+
const [attempt, retry] = react.default.useState(0);
|
|
35035
|
+
const [view, setView] = react.default.useState({});
|
|
35036
|
+
/** @private @author ddj 2026年10月22号 @returns Cleanup invalidating callbacks after switch or unmount. */
|
|
35037
|
+
function loadEffect() {
|
|
35038
|
+
let active = true;
|
|
35039
|
+
setView({});
|
|
35040
|
+
/** @author ddj 2026年10月22号 @param component Loaded panel. */
|
|
35041
|
+
function ready(component) {
|
|
35042
|
+
if (active) setView({ component });
|
|
35043
|
+
}
|
|
35044
|
+
/** @author ddj 2026年10月22号 @param error Loader failure. */
|
|
35045
|
+
function failed(error) {
|
|
35046
|
+
if (active) setView({ error: String(error) });
|
|
35047
|
+
}
|
|
35048
|
+
loadPanel(props.panel).then(ready, failed);
|
|
34677
35049
|
return () => {
|
|
34678
|
-
|
|
34679
|
-
};
|
|
34680
|
-
}, []);
|
|
34681
|
-
react.default.useEffect(() => {
|
|
34682
|
-
const el = panelRef.current;
|
|
34683
|
-
if (!el || typeof ResizeObserver === "undefined") return void 0;
|
|
34684
|
-
const observer = new ResizeObserver(() => setPanelW(el.clientWidth));
|
|
34685
|
-
observer.observe(el);
|
|
34686
|
-
setPanelW(el.clientWidth);
|
|
34687
|
-
return () => observer.disconnect();
|
|
34688
|
-
}, []);
|
|
34689
|
-
const descCap = panelW > 0 ? descCapOf(panelW) : DESC_MAX;
|
|
34690
|
-
const narrow = panelW > 0 && panelW < NARROW_W;
|
|
34691
|
-
/**
|
|
34692
|
-
* 切换 Tab(记忆到 localStorage)。
|
|
34693
|
-
* @param next 目标 Tab
|
|
34694
|
-
*/
|
|
34695
|
-
const switchTab = (next) => {
|
|
34696
|
-
setTab(next);
|
|
34697
|
-
writeLocal(CACHE_KEY.rules + "tab", next);
|
|
34698
|
-
};
|
|
34699
|
-
/**
|
|
34700
|
-
* 打开新建表单(按当前 Tab 决定作用域;项目规则跟随当前会话工作区)。
|
|
34701
|
-
*/
|
|
34702
|
-
const openNew = () => {
|
|
34703
|
-
const scope = tab === "project" ? "project" : "user";
|
|
34704
|
-
if (scope === "project" && !cwd) {
|
|
34705
|
-
notify("当前会话没有工作区,无法新建项目规则");
|
|
34706
|
-
return;
|
|
34707
|
-
}
|
|
34708
|
-
const parsed = parseRuleFm(NEW_TEMPLATE);
|
|
34709
|
-
setForm({
|
|
34710
|
-
mode: "new",
|
|
34711
|
-
scope,
|
|
34712
|
-
origin: scope === "user" ? "current" : void 0,
|
|
34713
|
-
workspacePath: scope === "project" ? cwd : void 0,
|
|
34714
|
-
relHint: scope === "project" ? ".dsh/rules/" : "rules/",
|
|
34715
|
-
file: "",
|
|
34716
|
-
content: NEW_TEMPLATE,
|
|
34717
|
-
fmType: parsed.type,
|
|
34718
|
-
desc: parsed.description,
|
|
34719
|
-
globs: parsed.globs.join(", "),
|
|
34720
|
-
error: "",
|
|
34721
|
-
saving: false
|
|
34722
|
-
});
|
|
34723
|
-
};
|
|
34724
|
-
/**
|
|
34725
|
-
* 打开编辑表单:先拉全文,再解析 frontmatter 回填类型/描述/globs 控件。
|
|
34726
|
-
* @param rule 目标规则
|
|
34727
|
-
*/
|
|
34728
|
-
const openEdit = (rule) => {
|
|
34729
|
-
rpc("rules.read", {
|
|
34730
|
-
scope: rule.scope,
|
|
34731
|
-
origin: rule.origin,
|
|
34732
|
-
workspacePath: rule.scope === "project" ? rule.workspacePath : void 0,
|
|
34733
|
-
file: rule.file
|
|
34734
|
-
}).then((res) => {
|
|
34735
|
-
if (!res || !res.ok) {
|
|
34736
|
-
notify(res?.error ?? "读取规则失败");
|
|
34737
|
-
return;
|
|
34738
|
-
}
|
|
34739
|
-
const parsed = parseRuleFm(res.content);
|
|
34740
|
-
setForm({
|
|
34741
|
-
mode: "edit",
|
|
34742
|
-
scope: rule.scope,
|
|
34743
|
-
origin: rule.origin,
|
|
34744
|
-
workspacePath: rule.workspacePath,
|
|
34745
|
-
relHint: rule.relHint,
|
|
34746
|
-
file: rule.file,
|
|
34747
|
-
content: res.content,
|
|
34748
|
-
fmType: parsed.type,
|
|
34749
|
-
desc: parsed.description,
|
|
34750
|
-
globs: parsed.globs.join(", "),
|
|
34751
|
-
error: "",
|
|
34752
|
-
saving: false
|
|
34753
|
-
});
|
|
34754
|
-
}).catch((e) => notify("读取规则异常: " + String(e)));
|
|
34755
|
-
};
|
|
34756
|
-
/** 类型下拉变更:frontmatter 按新类型重写,控件同步。 */
|
|
34757
|
-
const onTypeChange = (e) => {
|
|
34758
|
-
const nextType = e.target.value;
|
|
34759
|
-
setForm((prev) => ({
|
|
34760
|
-
...prev,
|
|
34761
|
-
fmType: nextType,
|
|
34762
|
-
content: rebuildContent(prev.content, nextType, prev.desc, prev.globs),
|
|
34763
|
-
error: ""
|
|
34764
|
-
}));
|
|
34765
|
-
};
|
|
34766
|
-
/** 描述输入变更:同步 frontmatter description 行。 */
|
|
34767
|
-
const onDescChange = (e) => {
|
|
34768
|
-
const nextDesc = e.target.value;
|
|
34769
|
-
setForm((prev) => ({
|
|
34770
|
-
...prev,
|
|
34771
|
-
desc: nextDesc,
|
|
34772
|
-
content: rebuildContent(prev.content, prev.fmType, nextDesc, prev.globs),
|
|
34773
|
-
error: ""
|
|
34774
|
-
}));
|
|
34775
|
-
};
|
|
34776
|
-
/** globs 输入变更(自动规则):同步 frontmatter globs 行。 */
|
|
34777
|
-
const onGlobsChange = (e) => {
|
|
34778
|
-
const nextGlobs = e.target.value;
|
|
34779
|
-
setForm((prev) => ({
|
|
34780
|
-
...prev,
|
|
34781
|
-
globs: nextGlobs,
|
|
34782
|
-
content: rebuildContent(prev.content, prev.fmType, prev.desc, nextGlobs),
|
|
34783
|
-
error: ""
|
|
34784
|
-
}));
|
|
34785
|
-
};
|
|
34786
|
-
/** 文本域直接编辑:回读 frontmatter,同步类型/描述/globs 控件(文本域是唯一真源)。 */
|
|
34787
|
-
const onBodyChange = (e) => {
|
|
34788
|
-
const parsed = parseRuleFm(e.target.value);
|
|
34789
|
-
setForm((prev) => ({
|
|
34790
|
-
...prev,
|
|
34791
|
-
content: e.target.value,
|
|
34792
|
-
fmType: parsed.type,
|
|
34793
|
-
desc: parsed.description,
|
|
34794
|
-
globs: parsed.globs.join(", ")
|
|
34795
|
-
}));
|
|
34796
|
-
};
|
|
34797
|
-
/** 保存表单(rules.save;文件名非空 + 自动规则必须有 globs)。 */
|
|
34798
|
-
const saveForm = () => {
|
|
34799
|
-
const next = {
|
|
34800
|
-
...form,
|
|
34801
|
-
saving: true,
|
|
34802
|
-
error: ""
|
|
34803
|
-
};
|
|
34804
|
-
const file = String(form.file ?? "").trim();
|
|
34805
|
-
if (!file) {
|
|
34806
|
-
setForm({
|
|
34807
|
-
...next,
|
|
34808
|
-
saving: false,
|
|
34809
|
-
error: "文件名不能为空"
|
|
34810
|
-
});
|
|
34811
|
-
return;
|
|
34812
|
-
}
|
|
34813
|
-
if (form.fmType === "auto" && globList(form.globs).length === 0) {
|
|
34814
|
-
setForm({
|
|
34815
|
-
...next,
|
|
34816
|
-
saving: false,
|
|
34817
|
-
error: "自动规则需要至少一个 glob(逗号分隔)"
|
|
34818
|
-
});
|
|
34819
|
-
return;
|
|
34820
|
-
}
|
|
34821
|
-
setForm(next);
|
|
34822
|
-
rpc("rules.save", {
|
|
34823
|
-
scope: form.scope,
|
|
34824
|
-
origin: form.origin,
|
|
34825
|
-
workspacePath: form.workspacePath,
|
|
34826
|
-
file,
|
|
34827
|
-
content: form.content
|
|
34828
|
-
}).then((res) => {
|
|
34829
|
-
if (res && res.ok) {
|
|
34830
|
-
setForm(null);
|
|
34831
|
-
notify("规则已保存:" + file);
|
|
34832
|
-
load();
|
|
34833
|
-
} else setForm((prev) => ({
|
|
34834
|
-
...prev,
|
|
34835
|
-
saving: false,
|
|
34836
|
-
error: res?.error ?? "保存失败"
|
|
34837
|
-
}));
|
|
34838
|
-
}).catch((e) => setForm((prev) => ({
|
|
34839
|
-
...prev,
|
|
34840
|
-
saving: false,
|
|
34841
|
-
error: "保存异常: " + String(e)
|
|
34842
|
-
})));
|
|
34843
|
-
};
|
|
34844
|
-
/**
|
|
34845
|
-
* 删除规则(confirm 后走 rules.remove)。
|
|
34846
|
-
* @param rule 目标规则
|
|
34847
|
-
*/
|
|
34848
|
-
const removeRule = (rule) => {
|
|
34849
|
-
const reveal = rule.scope === "user" && rule.origin !== "legacy" ? "\n若旧目录有同名规则,删除后它将重新生效。" : "";
|
|
34850
|
-
if (!window.confirm("删除规则 " + rule.absPath + "?(不可恢复)" + reveal)) return;
|
|
34851
|
-
rpc("rules.remove", {
|
|
34852
|
-
scope: rule.scope,
|
|
34853
|
-
origin: rule.origin,
|
|
34854
|
-
workspacePath: rule.workspacePath,
|
|
34855
|
-
file: rule.file
|
|
34856
|
-
}).then((res) => {
|
|
34857
|
-
if (res && res.ok) {
|
|
34858
|
-
notify("已删除:" + rule.file);
|
|
34859
|
-
load();
|
|
34860
|
-
} else notify(res?.error ?? "删除失败");
|
|
34861
|
-
}).catch((e) => notify("删除异常: " + String(e)));
|
|
34862
|
-
};
|
|
34863
|
-
/**
|
|
34864
|
-
* 切换启用开关:本地乐观翻转(按 workspacePath+file 定位,失败回滚)。
|
|
34865
|
-
* @param rule 目标规则(project 已注入 workspacePath)
|
|
34866
|
-
*/
|
|
34867
|
-
const toggleRule = (rule) => {
|
|
34868
|
-
const enabled = !rule.enabled;
|
|
34869
|
-
const applyLocal = (on) => setData((prev) => prev ? {
|
|
34870
|
-
...prev,
|
|
34871
|
-
user: prev.user.map((r) => rule.scope === "user" && r.file === rule.file && (r.origin ?? "current") === (rule.origin ?? "current") ? {
|
|
34872
|
-
...r,
|
|
34873
|
-
enabled: on
|
|
34874
|
-
} : r),
|
|
34875
|
-
projects: prev.projects.map((p) => p.workspacePath === rule.workspacePath ? {
|
|
34876
|
-
...p,
|
|
34877
|
-
rules: p.rules.map((r) => r.file === rule.file ? {
|
|
34878
|
-
...r,
|
|
34879
|
-
enabled: on
|
|
34880
|
-
} : r)
|
|
34881
|
-
} : p)
|
|
34882
|
-
} : prev);
|
|
34883
|
-
applyLocal(enabled);
|
|
34884
|
-
rpc("rules.toggle", {
|
|
34885
|
-
scope: rule.scope,
|
|
34886
|
-
origin: rule.origin,
|
|
34887
|
-
workspacePath: rule.workspacePath,
|
|
34888
|
-
file: rule.file,
|
|
34889
|
-
enabled
|
|
34890
|
-
}).then((res) => {
|
|
34891
|
-
if (res && res.ok) {
|
|
34892
|
-
applyLocal(res.rule?.enabled === true);
|
|
34893
|
-
notify(res.rule?.enabled === false ? "已停用:" + rule.file : "已启用:" + rule.file);
|
|
34894
|
-
} else {
|
|
34895
|
-
applyLocal(!enabled);
|
|
34896
|
-
notify(res?.error ?? "切换失败");
|
|
34897
|
-
}
|
|
34898
|
-
}).catch((e) => {
|
|
34899
|
-
applyLocal(!enabled);
|
|
34900
|
-
notify("切换异常: " + String(e));
|
|
34901
|
-
});
|
|
34902
|
-
};
|
|
34903
|
-
/** 当前列表数据(按 Tab 取用户规则或当前工作区的项目规则)。 */
|
|
34904
|
-
const rowsOf = () => {
|
|
34905
|
-
if (!data) return [];
|
|
34906
|
-
if (tab === "user") return data.user;
|
|
34907
|
-
return activeProject()?.rules ?? [];
|
|
34908
|
-
};
|
|
34909
|
-
const renderTabs = () => react.default.createElement("div", { className: "edrv-rules-tabs" }, react.default.createElement("button", {
|
|
34910
|
-
className: "edrv-rules-tab" + (tab === "user" ? " on" : ""),
|
|
34911
|
-
onClick: () => switchTab("user")
|
|
34912
|
-
}, "用户规则"), react.default.createElement("button", {
|
|
34913
|
-
className: "edrv-rules-tab" + (tab === "project" ? " on" : ""),
|
|
34914
|
-
onClick: () => switchTab("project")
|
|
34915
|
-
}, "项目规则"), react.default.createElement("button", {
|
|
34916
|
-
className: "edrv-rules-new",
|
|
34917
|
-
title: "新建规则",
|
|
34918
|
-
onClick: openNew
|
|
34919
|
-
}, "+ 新建规则"));
|
|
34920
|
-
/** 项目 Tab 的工作区展示(不可编辑,自动跟随当前会话)。 */
|
|
34921
|
-
const renderWsLine = () => {
|
|
34922
|
-
if (tab !== "project") return null;
|
|
34923
|
-
return react.default.createElement("div", {
|
|
34924
|
-
className: "edrv-rules-ws-line",
|
|
34925
|
-
title: cwd ?? ""
|
|
34926
|
-
}, "工作区:" + (cwd ?? "当前会话未打开工作区"));
|
|
34927
|
-
};
|
|
34928
|
-
const renderEmpty = () => {
|
|
34929
|
-
if (tab === "project") {
|
|
34930
|
-
if (!cwd) return "当前会话没有工作区,项目规则不可用(项目规则跟随会话所在工作区)";
|
|
34931
|
-
if (!data?.projects?.length) return "当前 DSH 没有已注册的工作区;在目标工作区打开一次会话后再来管理";
|
|
34932
|
-
if (!activeProject()) return "当前工作区未注册为 DSH workspace,暂不能管理其项目规则";
|
|
34933
|
-
return "<工作区>/.dsh/rules/ 还没有规则,点击「+ 新建规则」创建";
|
|
34934
|
-
}
|
|
34935
|
-
return "还没有用户规则(当前 DSH_HOME/rules/ 或旧目录 ~/.dsh/rules/),点击「+ 新建规则」创建";
|
|
34936
|
-
};
|
|
34937
|
-
const renderBody = () => {
|
|
34938
|
-
if (form) return react.default.createElement(RuleEditor, {
|
|
34939
|
-
form,
|
|
34940
|
-
onType: onTypeChange,
|
|
34941
|
-
onDesc: onDescChange,
|
|
34942
|
-
onGlobs: onGlobsChange,
|
|
34943
|
-
onBody: onBodyChange,
|
|
34944
|
-
onSave: saveForm,
|
|
34945
|
-
onCancel: () => setForm(null)
|
|
34946
|
-
});
|
|
34947
|
-
if (!data) return react.default.createElement("div", { className: "edrv-rules-empty" }, "加载中…");
|
|
34948
|
-
if (error) return react.default.createElement("div", { className: "edrv-rules-err" }, error);
|
|
34949
|
-
const rows = rowsOf();
|
|
34950
|
-
if (!rows.length) return react.default.createElement("div", { className: "edrv-rules-empty" }, renderEmpty());
|
|
34951
|
-
const common = {
|
|
34952
|
-
descCap,
|
|
34953
|
-
narrow,
|
|
34954
|
-
onEdit: (rule) => openEdit({
|
|
34955
|
-
...rule,
|
|
34956
|
-
workspacePath: rule.scope === "project" ? cwd : void 0
|
|
34957
|
-
}),
|
|
34958
|
-
onRemove: (rule) => removeRule({
|
|
34959
|
-
...rule,
|
|
34960
|
-
workspacePath: rule.scope === "project" ? cwd : void 0
|
|
34961
|
-
}),
|
|
34962
|
-
onToggle: (rule) => toggleRule({
|
|
34963
|
-
...rule,
|
|
34964
|
-
workspacePath: rule.scope === "project" ? cwd : void 0
|
|
34965
|
-
})
|
|
35050
|
+
active = false;
|
|
34966
35051
|
};
|
|
34967
|
-
|
|
34968
|
-
|
|
34969
|
-
|
|
34970
|
-
|
|
34971
|
-
|
|
34972
|
-
}
|
|
35052
|
+
}
|
|
35053
|
+
react.default.useEffect(loadEffect, [props.panel, attempt]);
|
|
35054
|
+
/** @private @author ddj 2026年10月22号 @description Explicit retry; failures never loop automatically. */
|
|
35055
|
+
function onRetry() {
|
|
35056
|
+
retry(attempt + 1);
|
|
35057
|
+
}
|
|
35058
|
+
if (view.component) return react.default.createElement(view.component, { ctx: props.ctx });
|
|
34973
35059
|
return react.default.createElement("div", {
|
|
34974
|
-
className: "edrv-
|
|
34975
|
-
|
|
34976
|
-
},
|
|
35060
|
+
className: "edrv-empty",
|
|
35061
|
+
role: view.error ? "alert" : "status"
|
|
35062
|
+
}, view.error ?? "正在加载面板…", view.error && react.default.createElement("button", { onClick: onRetry }, "重试"));
|
|
34977
35063
|
}
|
|
34978
35064
|
//#endregion
|
|
34979
35065
|
//#region src/client/ui/SvnAiPlanDialog.ts
|
|
@@ -35703,7 +35789,7 @@ window.__ModuleLoader__.load({
|
|
|
35703
35789
|
const [busy, setBusy] = react.default.useState(false);
|
|
35704
35790
|
const [remote, setRemote] = react.default.useState(null);
|
|
35705
35791
|
const [remoteBusy, setRemoteBusy] = react.default.useState(false);
|
|
35706
|
-
const [
|
|
35792
|
+
const [sizeSnapshot, setSizes] = react.default.useState(null);
|
|
35707
35793
|
const [renderLimit, setRenderLimit] = react.default.useState(RENDER_STEP);
|
|
35708
35794
|
const ctxRef = react.default.useRef(ctx);
|
|
35709
35795
|
ctxRef.current = ctx;
|
|
@@ -35804,17 +35890,39 @@ window.__ModuleLoader__.load({
|
|
|
35804
35890
|
const missingEntries = react.default.useMemo(() => (entries ?? []).filter((entry) => entry.status === "missing"), [entries]);
|
|
35805
35891
|
const unversionedEntries = react.default.useMemo(() => (entries ?? []).filter((entry) => entry.status === "unversioned"), [entries]);
|
|
35806
35892
|
const requested = react.default.useMemo(() => [...missingEntries, ...unversionedEntries].slice(0, 200).map((entry) => entry.path), [missingEntries, unversionedEntries]);
|
|
35807
|
-
const candidateKey =
|
|
35808
|
-
const
|
|
35809
|
-
|
|
35810
|
-
|
|
35811
|
-
if (pairsKnown || !requested.length) return;
|
|
35812
|
-
svnFileSizes(sessionId, requested).then((table) => setSizes(table));
|
|
35813
|
-
}, [
|
|
35814
|
-
pairsKnown,
|
|
35893
|
+
const candidateKey = JSON.stringify(requested);
|
|
35894
|
+
const sizeOwner = JSON.stringify([
|
|
35895
|
+
scope,
|
|
35896
|
+
sessionId,
|
|
35815
35897
|
candidateKey,
|
|
35816
|
-
|
|
35898
|
+
managed
|
|
35817
35899
|
]);
|
|
35900
|
+
const sizeOwnerRef = react.default.useRef(sizeOwner);
|
|
35901
|
+
sizeOwnerRef.current = sizeOwner;
|
|
35902
|
+
const sizes = sizeSnapshot?.owner === sizeOwner ? sizeSnapshot.table : {};
|
|
35903
|
+
const pairsNeeded = managed && missingEntries.length > 0 && unversionedEntries.length > 0;
|
|
35904
|
+
const pairsKnown = pairsNeeded && requested.every((path) => path in sizes);
|
|
35905
|
+
/** @private @author ddj 2026年10月22号 @returns Cleanup invalidating this request. @description Keep sizes scoped to this candidate generation; failures do not retry in a render loop. */
|
|
35906
|
+
function loadPairSizes() {
|
|
35907
|
+
if (!pairsNeeded || !requested.length) return;
|
|
35908
|
+
let disposed = false;
|
|
35909
|
+
/** @author ddj 2026年10月22号 @param table Candidate sizes. @description Publish only to the current request owner. */
|
|
35910
|
+
function acceptSizes(table) {
|
|
35911
|
+
if (!disposed && sizeOwnerRef.current === sizeOwner) setSizes({
|
|
35912
|
+
owner: sizeOwner,
|
|
35913
|
+
table
|
|
35914
|
+
});
|
|
35915
|
+
}
|
|
35916
|
+
/** @author ddj 2026年10月22号 @description Failed reads leave pairing unknown without breaking the changes list. */
|
|
35917
|
+
function ignoreFailure() {}
|
|
35918
|
+
/** @author ddj 2026年10月22号 @description Revoke this request on dependency change or unmount. */
|
|
35919
|
+
function cancelSizes() {
|
|
35920
|
+
disposed = true;
|
|
35921
|
+
}
|
|
35922
|
+
svnFileSizes(sessionId, requested).then(acceptSizes, ignoreFailure);
|
|
35923
|
+
return cancelSizes;
|
|
35924
|
+
}
|
|
35925
|
+
react.default.useEffect(loadPairSizes, [sizeOwner, pairsNeeded]);
|
|
35818
35926
|
const pairNoteOf = react.default.useMemo(() => {
|
|
35819
35927
|
if (!pairsKnown) return {};
|
|
35820
35928
|
const notes = {};
|
|
@@ -36286,851 +36394,6 @@ window.__ModuleLoader__.load({
|
|
|
36286
36394
|
}), remoteEl, pairCapEl, ...toolbar, react.default.createElement("div", { className: "edrv-svn-list" }, svnListBody(listState, handlers)), menuEl, aiPlanEl);
|
|
36287
36395
|
}
|
|
36288
36396
|
//#endregion
|
|
36289
|
-
//#region src/client/dap/variableTree.ts
|
|
36290
|
-
/**
|
|
36291
|
-
* 将变量树展平为 sibling rows;循环 ref 只展开一次,避免异常适配器数据造成递归。
|
|
36292
|
-
* @author ddj 2026年09月29号
|
|
36293
|
-
* @param roots 根变量
|
|
36294
|
-
* @param expanded 已展开的 variablesReference
|
|
36295
|
-
* @param cache ref → 子变量缓存
|
|
36296
|
-
* @param options 行数/深度上限
|
|
36297
|
-
*/
|
|
36298
|
-
function flattenVariableRows(roots, expanded, cache, options = {}) {
|
|
36299
|
-
const rows = [];
|
|
36300
|
-
const maxRows = options.maxRows ?? 1e3;
|
|
36301
|
-
const maxDepth = options.maxDepth ?? 32;
|
|
36302
|
-
const walk = (items, depth, ancestry, prefix) => {
|
|
36303
|
-
if (depth > maxDepth || rows.length >= maxRows) return;
|
|
36304
|
-
for (let index = 0; index < items.length && rows.length < maxRows; index += 1) {
|
|
36305
|
-
const variable = items[index];
|
|
36306
|
-
const key = prefix + "/" + index + ":" + variable.name;
|
|
36307
|
-
rows.push({
|
|
36308
|
-
key,
|
|
36309
|
-
variable,
|
|
36310
|
-
depth
|
|
36311
|
-
});
|
|
36312
|
-
if (variable.ref <= 0 || !expanded.has(variable.ref) || ancestry.has(variable.ref)) continue;
|
|
36313
|
-
const children = cache.get(variable.ref);
|
|
36314
|
-
if (!children?.length) continue;
|
|
36315
|
-
const nextAncestry = new Set(ancestry);
|
|
36316
|
-
nextAncestry.add(variable.ref);
|
|
36317
|
-
walk(children, depth + 1, nextAncestry, key);
|
|
36318
|
-
}
|
|
36319
|
-
};
|
|
36320
|
-
walk(roots, 0, /* @__PURE__ */ new Set(), "root");
|
|
36321
|
-
return rows;
|
|
36322
|
-
}
|
|
36323
|
-
//#endregion
|
|
36324
|
-
//#region src/client/dap/bpRowMenu.ts
|
|
36325
|
-
/**
|
|
36326
|
-
* 构造调试面板断点行右键菜单条目。
|
|
36327
|
-
* @author ddj 2026年09月21号
|
|
36328
|
-
* @param state 行内断点状态
|
|
36329
|
-
* @returns 菜单条目列表(顺序即展示顺序)
|
|
36330
|
-
*/
|
|
36331
|
-
function buildBpRowMenu(state) {
|
|
36332
|
-
return [
|
|
36333
|
-
{
|
|
36334
|
-
id: "edit",
|
|
36335
|
-
label: "编辑断点…"
|
|
36336
|
-
},
|
|
36337
|
-
{
|
|
36338
|
-
id: "edit-condition",
|
|
36339
|
-
label: "编辑条件…"
|
|
36340
|
-
},
|
|
36341
|
-
{
|
|
36342
|
-
id: "edit-hit-count",
|
|
36343
|
-
label: "编辑命中次数…"
|
|
36344
|
-
},
|
|
36345
|
-
{
|
|
36346
|
-
id: "edit-logpoint",
|
|
36347
|
-
label: "编辑记录点…"
|
|
36348
|
-
},
|
|
36349
|
-
state.enabled !== false ? {
|
|
36350
|
-
id: "disable",
|
|
36351
|
-
label: "禁用断点",
|
|
36352
|
-
separator: true
|
|
36353
|
-
} : {
|
|
36354
|
-
id: "enable",
|
|
36355
|
-
label: "启用断点",
|
|
36356
|
-
separator: true
|
|
36357
|
-
},
|
|
36358
|
-
{
|
|
36359
|
-
id: "remove",
|
|
36360
|
-
label: "删除断点"
|
|
36361
|
-
}
|
|
36362
|
-
];
|
|
36363
|
-
}
|
|
36364
|
-
//#endregion
|
|
36365
|
-
//#region src/client/dap/panelSplit.ts
|
|
36366
|
-
/**
|
|
36367
|
-
* dsh-vscode-mode client — 调试面板四段分栏高度(纯函数 + localStorage 持久)。
|
|
36368
|
-
* 对齐 VS Code/CodeBuddy splitview 语义:段间 sash 拖动时相邻两段高度重分配
|
|
36369
|
-
* (拖下 = 上段增高、下段减矮;任一段触到夹取界时拖动量被"吸收",分隔条停在可行位置)。
|
|
36370
|
-
* 存储:`edrv.dap.split.<scope>` → { variables, watch, stack, breakpoints }。
|
|
36371
|
-
* 作者 ddj 2026年09月21号
|
|
36372
|
-
*/
|
|
36373
|
-
/** 分栏段 key(与 DebugPanel section key 一致)。 */
|
|
36374
|
-
const SPLIT_KEYS = [
|
|
36375
|
-
"variables",
|
|
36376
|
-
"watch",
|
|
36377
|
-
"stack",
|
|
36378
|
-
"breakpoints"
|
|
36379
|
-
];
|
|
36380
|
-
/** 默认高度(沿用重构前各段 max-height 值,改前改后视觉一致)。 */
|
|
36381
|
-
const DEFAULT_HEIGHTS = {
|
|
36382
|
-
variables: 360,
|
|
36383
|
-
watch: 220,
|
|
36384
|
-
stack: 180,
|
|
36385
|
-
breakpoints: 260
|
|
36386
|
-
};
|
|
36387
|
-
/** 单段最大高度(防无限拖爆面板)。 */
|
|
36388
|
-
const MAX_H = 1200;
|
|
36389
|
-
/** localStorage key 前缀。 */
|
|
36390
|
-
const KEY_PREFIX = "edrv.dap.split.";
|
|
36391
|
-
/**
|
|
36392
|
-
* 夹取单段高度到 [MIN_H, MAX_H](非有限数回退最小值)。
|
|
36393
|
-
* @author ddj 2026年09月21号
|
|
36394
|
-
* @param px 目标高度
|
|
36395
|
-
*/
|
|
36396
|
-
function clampSectionHeight(px) {
|
|
36397
|
-
if (!Number.isFinite(px)) return 64;
|
|
36398
|
-
return Math.min(MAX_H, Math.max(64, Math.round(px)));
|
|
36399
|
-
}
|
|
36400
|
-
/**
|
|
36401
|
-
* 归一化高度表:非对象/缺 key 回退默认,非法值丢弃(损坏安全)。
|
|
36402
|
-
* @author ddj 2026年09月21号
|
|
36403
|
-
* @param input localStorage 解析结果
|
|
36404
|
-
*/
|
|
36405
|
-
function sanitizeHeights(input) {
|
|
36406
|
-
const out = { ...DEFAULT_HEIGHTS };
|
|
36407
|
-
if (!input || typeof input !== "object" || Array.isArray(input)) return out;
|
|
36408
|
-
const data = input;
|
|
36409
|
-
for (const key of SPLIT_KEYS) {
|
|
36410
|
-
const value = data[key];
|
|
36411
|
-
if (typeof value === "number" && Number.isFinite(value)) out[key] = clampSectionHeight(value);
|
|
36412
|
-
}
|
|
36413
|
-
return out;
|
|
36414
|
-
}
|
|
36415
|
-
/**
|
|
36416
|
-
* 相邻两段高度重分配:above += dy、below −= dy,各自夹取;
|
|
36417
|
-
* 一段被夹取吸收的量补给另一段(分隔条停在可行位置,拖动量尽量生效)。
|
|
36418
|
-
* @author ddj 2026年09月21号
|
|
36419
|
-
* @param heights 拖动起点高度表(不修改入参)
|
|
36420
|
-
* @param above 上邻段 key
|
|
36421
|
-
* @param below 下邻段 key
|
|
36422
|
-
* @param dy 拖动位移(>0 向下拖 = 上段增高)
|
|
36423
|
-
* @returns 新高度表
|
|
36424
|
-
*/
|
|
36425
|
-
function redistribute(heights, above, below, dy) {
|
|
36426
|
-
const clean = Math.round(Number.isFinite(dy) ? dy : 0);
|
|
36427
|
-
const next = { ...heights };
|
|
36428
|
-
const startAbove = heights[above] ?? 64;
|
|
36429
|
-
const startBelow = heights[below] ?? 64;
|
|
36430
|
-
const targetAbove = clampSectionHeight(startAbove + clean);
|
|
36431
|
-
const targetBelow = clampSectionHeight(startBelow - clean);
|
|
36432
|
-
next[above] = targetAbove;
|
|
36433
|
-
next[below] = targetBelow;
|
|
36434
|
-
const lostAbove = startAbove + clean - targetAbove;
|
|
36435
|
-
const lostBelow = startBelow - clean - targetBelow;
|
|
36436
|
-
if (lostAbove > 0) next[below] = clampSectionHeight(targetBelow + lostAbove);
|
|
36437
|
-
else if (lostBelow > 0) next[above] = clampSectionHeight(targetAbove + lostBelow);
|
|
36438
|
-
return next;
|
|
36439
|
-
}
|
|
36440
|
-
/**
|
|
36441
|
-
* 读高度表(缺失/损坏回退默认值)。
|
|
36442
|
-
* @author ddj 2026年09月21号
|
|
36443
|
-
* @param scope 状态作用域
|
|
36444
|
-
*/
|
|
36445
|
-
function loadSectionHeights(scope) {
|
|
36446
|
-
if (!scope) return { ...DEFAULT_HEIGHTS };
|
|
36447
|
-
try {
|
|
36448
|
-
const raw = localStorage.getItem(KEY_PREFIX + scope);
|
|
36449
|
-
return sanitizeHeights(raw ? JSON.parse(raw) : null);
|
|
36450
|
-
} catch {
|
|
36451
|
-
return { ...DEFAULT_HEIGHTS };
|
|
36452
|
-
}
|
|
36453
|
-
}
|
|
36454
|
-
/**
|
|
36455
|
-
* 写高度表(写失败静默;写入前归一化)。
|
|
36456
|
-
* @author ddj 2026年09月21号
|
|
36457
|
-
* @param scope 状态作用域
|
|
36458
|
-
* @param heights 高度表
|
|
36459
|
-
*/
|
|
36460
|
-
function saveSectionHeights(scope, heights) {
|
|
36461
|
-
try {
|
|
36462
|
-
localStorage.setItem(KEY_PREFIX + scope, JSON.stringify(sanitizeHeights(heights)));
|
|
36463
|
-
} catch {}
|
|
36464
|
-
}
|
|
36465
|
-
//#endregion
|
|
36466
|
-
//#region src/client/sidebar/panels/DebugPanel.ts
|
|
36467
|
-
/**
|
|
36468
|
-
* dsh-vscode-mode client — 侧边栏「调试」面板(对齐 CodeBuddy「运行和调试」视图)。
|
|
36469
|
-
* 顶部工具条:配置下拉 + 启动/继续 + 暂停 + 单步组 + 停止(禁用逻辑镜像编辑器浮动条)。
|
|
36470
|
-
* 四段折叠区(变量 / 监视 / 调用堆栈 / 断点,图2 顺序)+ 底部调试控制台(REPL);
|
|
36471
|
-
* 每段头部可折叠(状态按 scope 持久化)且带悬浮动作按钮。
|
|
36472
|
-
* 断点多方式管理(对齐 CodeBuddy 断点区):段头「新建断点」菜单(条件断点/记录点,
|
|
36473
|
-
* 落在活动编辑器光标行)、禁用/启用所有、删除所有;断点行右键菜单(编辑条件/命中次数/
|
|
36474
|
-
* 记录点/启停/删除)→ 行内展开编辑器(Enter 提交 / Escape 取消,字段语义同 BpWidget);
|
|
36475
|
-
* 行内点展示适配器回执(未验证 = 空心点 + tooltip)。
|
|
36476
|
-
* 数据自取:paused 时经 dapStore 拉栈/作用域/变量/监视求值;断点读写 dapStore 断点表;
|
|
36477
|
-
* 输出流 = host poll 事件缓存(outputs)。host 不可达或未暂停时各段空态降级。
|
|
36478
|
-
* 断点行整行可点:除启停 checkbox 与删除 × 外,点击任一处即打开并定位到该断点行。
|
|
36479
|
-
* 作者 ddj 2026年09月29号 / 2026年09月21号
|
|
36480
|
-
*/
|
|
36481
|
-
/** 变量展开缓存上限(ref → 子级;防长会话累积)。 */
|
|
36482
|
-
const VAR_CACHE_CAP = 200;
|
|
36483
|
-
/** REPL 历史行上限。 */
|
|
36484
|
-
const REPL_CAP = 200;
|
|
36485
|
-
/** 监视持久化 key 前缀(CACHE_KEY 无调试段,自管命名空间)。 */
|
|
36486
|
-
const WATCH_KEY = "edrv.dap.watch.";
|
|
36487
|
-
/** 段折叠持久化 key 前缀(存折叠段名数组;损坏安全,默认全展开)。 */
|
|
36488
|
-
const FOLD_KEY = "edrv.dap.fold.";
|
|
36489
|
-
/** 调试配置文件(工作区相对路径;⚙ 按钮跳转/新建目标;与 VS Code 的 .vscode/launch.json 互不干扰)。 */
|
|
36490
|
-
const LAUNCH_REL = DAP_LAUNCH_REL;
|
|
36491
|
-
/** 新建调试配置模板(JSONC;示例按本机已装的 EmmyLua 适配器写)。 */
|
|
36492
|
-
const LAUNCH_TEMPLATE = `{
|
|
36493
|
-
// DSH 调试配置(插件专属 .dsh/launch.json,支持注释;改完整页刷新生效)
|
|
36494
|
-
// type 来自本机已装扩展的 contributes.debuggers(EmmyLua:emmylua_attach / emmylua_launch / emmylua_new)
|
|
36495
|
-
"version": "0.2.0",
|
|
36496
|
-
"configurations": [
|
|
36497
|
-
{
|
|
36498
|
-
"name": "附加 Unity (xLua)",
|
|
36499
|
-
"type": "emmylua_attach",
|
|
36500
|
-
"request": "attach",
|
|
36501
|
-
"processName": "Unity",
|
|
36502
|
-
"captureLog": true
|
|
36503
|
-
}
|
|
36504
|
-
]
|
|
36505
|
-
}`;
|
|
36506
|
-
/** 相位徽标文案。 */
|
|
36507
|
-
const PHASE_LABEL = {
|
|
36508
|
-
idle: "未启动",
|
|
36509
|
-
starting: "启动中…",
|
|
36510
|
-
waiting: "等待目标…",
|
|
36511
|
-
running: "运行中",
|
|
36512
|
-
paused: "已暂停",
|
|
36513
|
-
terminated: "已结束"
|
|
36514
|
-
};
|
|
36515
|
-
/** 读取监视表达式列表(损坏安全)。 */
|
|
36516
|
-
function loadWatches(scope) {
|
|
36517
|
-
if (!scope) return [];
|
|
36518
|
-
try {
|
|
36519
|
-
const raw = window.localStorage.getItem(WATCH_KEY + scope);
|
|
36520
|
-
const list = raw ? JSON.parse(raw) : [];
|
|
36521
|
-
return Array.isArray(list) ? list.filter((item) => typeof item === "string" && item.trim()) : [];
|
|
36522
|
-
} catch {
|
|
36523
|
-
return [];
|
|
36524
|
-
}
|
|
36525
|
-
}
|
|
36526
|
-
/** 写监视表达式列表。 */
|
|
36527
|
-
function saveWatches(scope, list) {
|
|
36528
|
-
try {
|
|
36529
|
-
window.localStorage.setItem(WATCH_KEY + scope, JSON.stringify(list));
|
|
36530
|
-
} catch {}
|
|
36531
|
-
}
|
|
36532
|
-
/** 读折叠段名列表(损坏安全;非字符串项丢弃)。 */
|
|
36533
|
-
function loadFolds(scope) {
|
|
36534
|
-
if (!scope) return [];
|
|
36535
|
-
try {
|
|
36536
|
-
const raw = window.localStorage.getItem(FOLD_KEY + scope);
|
|
36537
|
-
const list = raw ? JSON.parse(raw) : [];
|
|
36538
|
-
return Array.isArray(list) ? list.filter((item) => typeof item === "string" && item) : [];
|
|
36539
|
-
} catch {
|
|
36540
|
-
return [];
|
|
36541
|
-
}
|
|
36542
|
-
}
|
|
36543
|
-
/** 写折叠段名列表(写失败静默)。 */
|
|
36544
|
-
function saveFolds(scope, list) {
|
|
36545
|
-
try {
|
|
36546
|
-
window.localStorage.setItem(FOLD_KEY + scope, JSON.stringify(list));
|
|
36547
|
-
} catch {}
|
|
36548
|
-
}
|
|
36549
|
-
/**
|
|
36550
|
-
* 「调试」面板组件。
|
|
36551
|
-
* @param ctx 侧栏上下文(openFileAt 跳转 / scope 持久化 / cwd / editor 光标)
|
|
36552
|
-
*/
|
|
36553
|
-
function DebugPanel({ ctx }) {
|
|
36554
|
-
const scope = ctx?.scope || "";
|
|
36555
|
-
const [, setTick] = react.default.useState(0);
|
|
36556
|
-
react.default.useEffect(() => dapStore.subscribe(() => setTick((t) => t + 1)), []);
|
|
36557
|
-
const snap = dapStore.getSnapshot();
|
|
36558
|
-
const paused = snap.phase === "paused";
|
|
36559
|
-
const dapActive = snap.phase !== "idle" && snap.phase !== "terminated";
|
|
36560
|
-
const [stacks, setStacks] = react.default.useState([]);
|
|
36561
|
-
const [scopes, setScopes] = react.default.useState([]);
|
|
36562
|
-
const [varCache, setVarCache] = react.default.useState(() => /* @__PURE__ */ new Map());
|
|
36563
|
-
const [expanded, setExpanded] = react.default.useState(() => /* @__PURE__ */ new Set());
|
|
36564
|
-
const [watches, setWatches] = react.default.useState(() => loadWatches(scope));
|
|
36565
|
-
const [watchHits, setWatchHits] = react.default.useState({});
|
|
36566
|
-
const [watchDraft, setWatchDraft] = react.default.useState("");
|
|
36567
|
-
const [replLog, setReplLog] = react.default.useState([]);
|
|
36568
|
-
const [replDraft, setReplDraft] = react.default.useState("");
|
|
36569
|
-
const [folds, setFolds] = react.default.useState(() => new Set(loadFolds(scope)));
|
|
36570
|
-
const [heights, setHeights] = react.default.useState(() => loadSectionHeights(scope));
|
|
36571
|
-
const [splitDragging, setSplitDragging] = react.default.useState(false);
|
|
36572
|
-
const [bpMenu, setBpMenu] = react.default.useState(null);
|
|
36573
|
-
const [newBpMenu, setNewBpMenu] = react.default.useState(null);
|
|
36574
|
-
const [bpEdit, setBpEdit] = react.default.useState(null);
|
|
36575
|
-
const [pickPid, setPickPid] = react.default.useState(0);
|
|
36576
|
-
const watchInputRef = react.default.useRef(null);
|
|
36577
|
-
const stackTopKey = paused && snap.topFrame ? snap.topFrame.file + ":" + snap.topFrame.line : "";
|
|
36578
|
-
react.default.useEffect(() => {
|
|
36579
|
-
if (!ctx?.cwd) return;
|
|
36580
|
-
dapStore.setWorkspace(ctx.cwd);
|
|
36581
|
-
dapStore.ensureConfigs();
|
|
36582
|
-
}, [ctx?.cwd]);
|
|
36583
|
-
const [reloadTick, setReloadTick] = react.default.useState(0);
|
|
36584
|
-
react.default.useEffect(() => {
|
|
36585
|
-
if (!paused) {
|
|
36586
|
-
setStacks([]);
|
|
36587
|
-
setScopes([]);
|
|
36588
|
-
setVarCache(/* @__PURE__ */ new Map());
|
|
36589
|
-
setExpanded(/* @__PURE__ */ new Set());
|
|
36590
|
-
setWatchHits({});
|
|
36591
|
-
return;
|
|
36592
|
-
}
|
|
36593
|
-
let alive = true;
|
|
36594
|
-
const load = async () => {
|
|
36595
|
-
const frames = await dapStore.stackTrace();
|
|
36596
|
-
if (!alive) return;
|
|
36597
|
-
setStacks(frames);
|
|
36598
|
-
const list = await dapStore.scopes(snap.selectedFrameId);
|
|
36599
|
-
if (!alive) return;
|
|
36600
|
-
setScopes(list);
|
|
36601
|
-
const first = list[0];
|
|
36602
|
-
if (first?.ref) {
|
|
36603
|
-
const vars = await dapStore.variables(first.ref);
|
|
36604
|
-
if (!alive) return;
|
|
36605
|
-
setVarCache((prev) => new Map(prev).set(first.ref, vars));
|
|
36606
|
-
setExpanded((prev) => new Set(prev).add(first.ref));
|
|
36607
|
-
}
|
|
36608
|
-
const hits = {};
|
|
36609
|
-
for (const expr of watches) hits[expr] = await dapStore.evaluate(expr, snap.selectedFrameId);
|
|
36610
|
-
if (alive) setWatchHits(hits);
|
|
36611
|
-
};
|
|
36612
|
-
load();
|
|
36613
|
-
return () => {
|
|
36614
|
-
alive = false;
|
|
36615
|
-
};
|
|
36616
|
-
}, [
|
|
36617
|
-
paused,
|
|
36618
|
-
stackTopKey,
|
|
36619
|
-
snap.selectedFrameId,
|
|
36620
|
-
watches,
|
|
36621
|
-
reloadTick
|
|
36622
|
-
]);
|
|
36623
|
-
const expandVar = async (ref) => {
|
|
36624
|
-
setExpanded((prev) => {
|
|
36625
|
-
const next = new Set(prev);
|
|
36626
|
-
if (next.has(ref)) next.delete(ref);
|
|
36627
|
-
else next.add(ref);
|
|
36628
|
-
return next;
|
|
36629
|
-
});
|
|
36630
|
-
if (!varCache.has(ref)) {
|
|
36631
|
-
const vars = await dapStore.variables(ref);
|
|
36632
|
-
setVarCache((prev) => {
|
|
36633
|
-
const next = new Map(prev);
|
|
36634
|
-
next.set(ref, vars);
|
|
36635
|
-
if (next.size > VAR_CACHE_CAP) next.delete(next.keys().next().value);
|
|
36636
|
-
return next;
|
|
36637
|
-
});
|
|
36638
|
-
}
|
|
36639
|
-
};
|
|
36640
|
-
const addWatch = () => {
|
|
36641
|
-
const expr = watchDraft.trim();
|
|
36642
|
-
if (!expr || watches.includes(expr)) {
|
|
36643
|
-
setWatchDraft("");
|
|
36644
|
-
return;
|
|
36645
|
-
}
|
|
36646
|
-
const next = [...watches, expr];
|
|
36647
|
-
setWatches(next);
|
|
36648
|
-
saveWatches(scope, next);
|
|
36649
|
-
setWatchDraft("");
|
|
36650
|
-
if (paused) dapStore.evaluate(expr, snap.selectedFrameId).then((r) => setWatchHits((prev) => ({
|
|
36651
|
-
...prev,
|
|
36652
|
-
[expr]: r
|
|
36653
|
-
})));
|
|
36654
|
-
};
|
|
36655
|
-
const removeWatch = (expr) => {
|
|
36656
|
-
const next = watches.filter((item) => item !== expr);
|
|
36657
|
-
setWatches(next);
|
|
36658
|
-
saveWatches(scope, next);
|
|
36659
|
-
};
|
|
36660
|
-
/** 移除全部监视(段头动作;持久化与命中缓存同步清)。 */
|
|
36661
|
-
const clearWatches = () => {
|
|
36662
|
-
setWatches([]);
|
|
36663
|
-
saveWatches(scope, []);
|
|
36664
|
-
setWatchHits({});
|
|
36665
|
-
};
|
|
36666
|
-
/** 聚焦监视新增输入(段折叠时先展开,二次点击聚焦)。 */
|
|
36667
|
-
const focusWatchInput = () => {
|
|
36668
|
-
if (folds.has("watch")) {
|
|
36669
|
-
toggleFold("watch");
|
|
36670
|
-
return;
|
|
36671
|
-
}
|
|
36672
|
-
watchInputRef.current?.focus?.();
|
|
36673
|
-
};
|
|
36674
|
-
const runRepl = async () => {
|
|
36675
|
-
const expr = replDraft.trim();
|
|
36676
|
-
if (!expr) return;
|
|
36677
|
-
setReplDraft("");
|
|
36678
|
-
const r = await dapStore.evaluate(expr, snap.selectedFrameId);
|
|
36679
|
-
setReplLog((prev) => {
|
|
36680
|
-
const next = [...prev, {
|
|
36681
|
-
expr,
|
|
36682
|
-
result: r.result,
|
|
36683
|
-
type: r.type
|
|
36684
|
-
}];
|
|
36685
|
-
return next.length > REPL_CAP ? next.slice(next.length - REPL_CAP) : next;
|
|
36686
|
-
});
|
|
36687
|
-
};
|
|
36688
|
-
/** 切换段折叠(持久化到 localStorage)。 */
|
|
36689
|
-
const toggleFold = (key) => {
|
|
36690
|
-
setFolds((prev) => {
|
|
36691
|
-
const next = new Set(prev);
|
|
36692
|
-
if (next.has(key)) next.delete(key);
|
|
36693
|
-
else next.add(key);
|
|
36694
|
-
saveFolds(scope, [...next]);
|
|
36695
|
-
return next;
|
|
36696
|
-
});
|
|
36697
|
-
};
|
|
36698
|
-
const splitCleanupRef = react.default.useRef(null);
|
|
36699
|
-
react.default.useEffect(() => () => {
|
|
36700
|
-
const cleanup = splitCleanupRef.current;
|
|
36701
|
-
if (cleanup) cleanup();
|
|
36702
|
-
}, []);
|
|
36703
|
-
/** 段间 sash 拖动:以起点高度为基准按 dy 相邻重分配;pointerup 持久化(镜像 dapbar 拖动模式)。 */
|
|
36704
|
-
const startSplitDrag = (above, below) => (e) => {
|
|
36705
|
-
e.preventDefault();
|
|
36706
|
-
const startY = e.clientY;
|
|
36707
|
-
const startHeights = { ...heights };
|
|
36708
|
-
const onMove = (ev) => {
|
|
36709
|
-
setHeights(redistribute(startHeights, above, below, ev.clientY - startY));
|
|
36710
|
-
};
|
|
36711
|
-
const detach = () => {
|
|
36712
|
-
window.removeEventListener("pointermove", onMove);
|
|
36713
|
-
window.removeEventListener("pointerup", onUp);
|
|
36714
|
-
splitCleanupRef.current = null;
|
|
36715
|
-
setSplitDragging(false);
|
|
36716
|
-
};
|
|
36717
|
-
const onUp = () => {
|
|
36718
|
-
detach();
|
|
36719
|
-
setHeights((prev) => {
|
|
36720
|
-
saveSectionHeights(scope, prev);
|
|
36721
|
-
return prev;
|
|
36722
|
-
});
|
|
36723
|
-
};
|
|
36724
|
-
splitCleanupRef.current = detach;
|
|
36725
|
-
window.addEventListener("pointermove", onMove);
|
|
36726
|
-
window.addEventListener("pointerup", onUp);
|
|
36727
|
-
setSplitDragging(true);
|
|
36728
|
-
};
|
|
36729
|
-
const cwd = ctx?.cwd || dapStore.workspace();
|
|
36730
|
-
const bpMap = dapStore.bpMapOf(scope);
|
|
36731
|
-
const bpRows = Object.entries(bpMap).flatMap(([path, list]) => list.map((entry) => ({
|
|
36732
|
-
path,
|
|
36733
|
-
entry
|
|
36734
|
-
}))).sort((a, b) => a.path.localeCompare(b.path) || a.entry.line - b.entry.line);
|
|
36735
|
-
const allBpEnabled = bpRows.length > 0 && bpRows.every((row) => row.entry.enabled);
|
|
36736
|
-
const toggleBpEnabled = (row) => dapStore.setBpEnabled(scope, cwd, row.path, row.entry.line, !row.entry.enabled);
|
|
36737
|
-
const removeBp = (row) => dapStore.toggleAt(scope, cwd, row.path, row.entry.line);
|
|
36738
|
-
/** 删除所有断点(confirm 守卫;逐文件同步空载荷清空 Monaco 装饰与 host 断点)。 */
|
|
36739
|
-
const removeAllBps = () => {
|
|
36740
|
-
if (ctx?.confirm && !ctx.confirm("删除所有断点?")) return;
|
|
36741
|
-
dapStore.removeAllBps(scope, cwd);
|
|
36742
|
-
setBpEdit(null);
|
|
36743
|
-
};
|
|
36744
|
-
const jumpBp = (row) => {
|
|
36745
|
-
const target = bpJumpTargetOf(row);
|
|
36746
|
-
if (target) ctx?.openFileAt?.(target.path, target.line, target.column);
|
|
36747
|
-
};
|
|
36748
|
-
const autoBpModeOf = (entry) => entry?.logMessage ? BP_MODE.LOG_MESSAGE : entry?.hitCondition ? BP_MODE.HIT_COUNT : BP_MODE.EXPRESSION;
|
|
36749
|
-
const openBpRowEditor = (path, line, entry, mode) => {
|
|
36750
|
-
setBpEdit({
|
|
36751
|
-
path,
|
|
36752
|
-
line,
|
|
36753
|
-
mode: typeof mode === "number" ? mode : autoBpModeOf(entry),
|
|
36754
|
-
drafts: {
|
|
36755
|
-
condition: entry?.condition || "",
|
|
36756
|
-
hitCondition: entry?.hitCondition || "",
|
|
36757
|
-
logMessage: entry?.logMessage || ""
|
|
36758
|
-
}
|
|
36759
|
-
});
|
|
36760
|
-
};
|
|
36761
|
-
const commitBpEdit = () => {
|
|
36762
|
-
if (!bpEdit) return;
|
|
36763
|
-
dapStore.applyBpFields(scope, cwd, bpEdit.path, bpEdit.line, { ...bpEdit.drafts });
|
|
36764
|
-
setBpEdit(null);
|
|
36765
|
-
ctx?.notify?.("断点已更新");
|
|
36766
|
-
};
|
|
36767
|
-
/** 断点行右键菜单动作分发(菜单数据由 buildBpRowMenu 产出)。 */
|
|
36768
|
-
const runBpRowAction = (action, row) => {
|
|
36769
|
-
if (action === "remove") {
|
|
36770
|
-
removeBp(row);
|
|
36771
|
-
return;
|
|
36772
|
-
}
|
|
36773
|
-
if (action === "enable" || action === "disable") {
|
|
36774
|
-
dapStore.setBpEnabled(scope, cwd, row.path, row.entry.line, action === "enable");
|
|
36775
|
-
return;
|
|
36776
|
-
}
|
|
36777
|
-
const mode = action === "edit-condition" ? BP_MODE.EXPRESSION : action === "edit-hit-count" ? BP_MODE.HIT_COUNT : action === "edit-logpoint" ? BP_MODE.LOG_MESSAGE : void 0;
|
|
36778
|
-
openBpRowEditor(row.path, row.entry.line, row.entry, mode);
|
|
36779
|
-
};
|
|
36780
|
-
/** 段头「新建断点」:条件断点/记录点落在活动编辑器光标行(已有断点则直接编辑不翻转)。 */
|
|
36781
|
-
const addBpAtCursor = (mode) => {
|
|
36782
|
-
const ed = ctx?.editor?.();
|
|
36783
|
-
const path = editorModelPathOf(ed);
|
|
36784
|
-
const line = ed?.getPosition?.()?.lineNumber;
|
|
36785
|
-
if (!path || !line || !Number.isFinite(line)) {
|
|
36786
|
-
ctx?.notify?.("无活动文件,无法添加断点");
|
|
36787
|
-
return;
|
|
36788
|
-
}
|
|
36789
|
-
if (!(dapStore.bpMapOf(scope)[path] ?? []).find((it) => it.line === line)) dapStore.toggleAt(scope, cwd, path, line);
|
|
36790
|
-
const entry = (dapStore.bpMapOf(scope)[path] ?? []).find((it) => it.line === line);
|
|
36791
|
-
if (entry) openBpRowEditor(path, line, entry, mode);
|
|
36792
|
-
};
|
|
36793
|
-
/** ⚙ 打开/新建调试配置:存在即跳转;缺失经确认后写模板(edrv.save 无 rev 仅用于新建,绝不覆盖已有文件)。 */
|
|
36794
|
-
const openLaunchConfig = async () => {
|
|
36795
|
-
if (!ctx?.cwd) {
|
|
36796
|
-
ctx?.notify?.("无工作区,无法定位 " + LAUNCH_REL);
|
|
36797
|
-
return;
|
|
36798
|
-
}
|
|
36799
|
-
try {
|
|
36800
|
-
if ((await rpc("edrv.read", {
|
|
36801
|
-
sessionId: ctx?.sessionId,
|
|
36802
|
-
path: LAUNCH_REL
|
|
36803
|
-
}).catch(() => null))?.ok) {
|
|
36804
|
-
ctx?.openFile?.(LAUNCH_REL);
|
|
36805
|
-
return;
|
|
36806
|
-
}
|
|
36807
|
-
} catch {}
|
|
36808
|
-
if (ctx?.confirm && !ctx.confirm("未找到 " + LAUNCH_REL + ",是否创建调试配置模板?")) return;
|
|
36809
|
-
try {
|
|
36810
|
-
const saved = await rpc("edrv.save", {
|
|
36811
|
-
sessionId: ctx?.sessionId,
|
|
36812
|
-
path: LAUNCH_REL,
|
|
36813
|
-
content: LAUNCH_TEMPLATE
|
|
36814
|
-
});
|
|
36815
|
-
if (!saved?.ok) {
|
|
36816
|
-
ctx?.notify?.(saved?.error || "创建 " + LAUNCH_REL + " 失败");
|
|
36817
|
-
return;
|
|
36818
|
-
}
|
|
36819
|
-
dapStore.ensureConfigs(true);
|
|
36820
|
-
ctx?.openFile?.(LAUNCH_REL);
|
|
36821
|
-
ctx?.notify?.("已创建 " + LAUNCH_REL);
|
|
36822
|
-
} catch (error) {
|
|
36823
|
-
ctx?.notify?.("创建 " + LAUNCH_REL + " 失败:" + String(error));
|
|
36824
|
-
}
|
|
36825
|
-
};
|
|
36826
|
-
/** 段头动作按钮(hover 显隐,样式见 .edrv-dapseg-act)。 */
|
|
36827
|
-
const segAct = (key, label, title, onClick) => react.default.createElement("button", {
|
|
36828
|
-
key,
|
|
36829
|
-
className: "edrv-dapseg-act",
|
|
36830
|
-
title,
|
|
36831
|
-
onClick: (e) => {
|
|
36832
|
-
e.stopPropagation();
|
|
36833
|
-
onClick(e);
|
|
36834
|
-
}
|
|
36835
|
-
}, label);
|
|
36836
|
-
/** 折叠段壳:chevron + 标题 + 悬浮动作区 + 可折叠主体(高度由分栏 state 内联接管)。 */
|
|
36837
|
-
const section = (title, key, variant, actions, children) => {
|
|
36838
|
-
const folded = folds.has(key);
|
|
36839
|
-
return react.default.createElement("div", {
|
|
36840
|
-
className: "edrv-dapseg edrv-dapseg-" + variant,
|
|
36841
|
-
key
|
|
36842
|
-
}, react.default.createElement("div", {
|
|
36843
|
-
className: "edrv-dapseg-head",
|
|
36844
|
-
onClick: () => toggleFold(key)
|
|
36845
|
-
}, react.default.createElement("span", {
|
|
36846
|
-
className: "edrv-dapseg-chevron",
|
|
36847
|
-
title: folded ? "展开" : "折叠"
|
|
36848
|
-
}, folded ? "▸" : "▾"), react.default.createElement("span", { className: "edrv-dapseg-title" }, title), react.default.createElement("span", { style: { flex: 1 } }), ...folded ? [] : actions || []), ...folded ? [] : [react.default.createElement("div", {
|
|
36849
|
-
className: "edrv-dapseg-body",
|
|
36850
|
-
style: { height: (heights[key] ?? 200) + "px" }
|
|
36851
|
-
}, ...children)]);
|
|
36852
|
-
};
|
|
36853
|
-
/** 段间分隔条(两侧段都展开时才渲染;拖动调整相邻两段高度占比)。 */
|
|
36854
|
-
const sash = (above, below) => folds.has(above) || folds.has(below) ? null : react.default.createElement("div", {
|
|
36855
|
-
key: "sash-" + above + "-" + below,
|
|
36856
|
-
className: "edrv-dapseg-sash" + (splitDragging ? " dragging" : ""),
|
|
36857
|
-
title: "拖动调整上下两段高度占比",
|
|
36858
|
-
onPointerDown: startSplitDrag(above, below)
|
|
36859
|
-
});
|
|
36860
|
-
const renderVarRow = (row) => {
|
|
36861
|
-
const v = row.variable;
|
|
36862
|
-
const depth = row.depth + 1;
|
|
36863
|
-
return react.default.createElement("div", {
|
|
36864
|
-
key: row.key,
|
|
36865
|
-
className: "edrv-dapvar",
|
|
36866
|
-
style: { paddingLeft: 8 + depth * 12 }
|
|
36867
|
-
}, v.ref > 0 ? react.default.createElement("button", {
|
|
36868
|
-
className: "edrv-dapvar-tw",
|
|
36869
|
-
title: expanded.has(v.ref) ? "折叠" : "展开",
|
|
36870
|
-
onClick: () => {
|
|
36871
|
-
expandVar(v.ref);
|
|
36872
|
-
}
|
|
36873
|
-
}, expanded.has(v.ref) ? "▾" : "▸") : react.default.createElement("span", { className: "edrv-dapvar-tw" }, ""), react.default.createElement("span", {
|
|
36874
|
-
className: "edrv-dapvar-name",
|
|
36875
|
-
title: v.name
|
|
36876
|
-
}, v.name), react.default.createElement("span", {
|
|
36877
|
-
className: "edrv-dapvar-val",
|
|
36878
|
-
title: v.value
|
|
36879
|
-
}, v.value), v.type ? react.default.createElement("span", { className: "edrv-dapvar-type" }, v.type) : null);
|
|
36880
|
-
};
|
|
36881
|
-
const stackRows = stacks.length ? stacks.map((f) => react.default.createElement("div", {
|
|
36882
|
-
key: f.id,
|
|
36883
|
-
className: "edrv-dapframe" + (f.id === snap.selectedFrameId ? " selected" : ""),
|
|
36884
|
-
title: f.rawFile,
|
|
36885
|
-
onClick: () => {
|
|
36886
|
-
dapStore.selectFrame(f.id);
|
|
36887
|
-
const target = f.file || f.rawFile;
|
|
36888
|
-
if (target) ctx?.openFileAt?.(target, f.line, 1);
|
|
36889
|
-
}
|
|
36890
|
-
}, react.default.createElement("span", { className: "edrv-dapframe-name" }, f.name || "(anonymous)"), react.default.createElement("span", { className: "edrv-dapframe-loc" }, (f.file || f.rawFile) + ":" + f.line))) : [react.default.createElement("div", {
|
|
36891
|
-
key: "empty",
|
|
36892
|
-
className: "edrv-dapempty"
|
|
36893
|
-
}, paused ? "(无调用栈)" : "暂停时显示调用堆栈")];
|
|
36894
|
-
const scopeRows = scopes.flatMap((s) => [react.default.createElement("div", {
|
|
36895
|
-
key: "scope" + s.name,
|
|
36896
|
-
className: "edrv-dapscope"
|
|
36897
|
-
}, s.name), ...flattenVariableRows(varCache.get(s.ref) ?? [], expanded, varCache, { maxRows: 800 }).map(renderVarRow)]);
|
|
36898
|
-
const varRows = scopes.length ? scopeRows : [react.default.createElement("div", {
|
|
36899
|
-
key: "e",
|
|
36900
|
-
className: "edrv-dapempty"
|
|
36901
|
-
}, "暂停时显示变量")];
|
|
36902
|
-
const watchRows = watches.flatMap((expr) => {
|
|
36903
|
-
const hit = watchHits[expr];
|
|
36904
|
-
const rows = [react.default.createElement("div", {
|
|
36905
|
-
key: expr,
|
|
36906
|
-
className: "edrv-dapwatch"
|
|
36907
|
-
}, react.default.createElement("button", {
|
|
36908
|
-
className: "edrv-dapwatch-x",
|
|
36909
|
-
title: "移除监视",
|
|
36910
|
-
onClick: () => removeWatch(expr)
|
|
36911
|
-
}, "×"), hit?.ref > 0 ? react.default.createElement("button", {
|
|
36912
|
-
className: "edrv-dapvar-tw",
|
|
36913
|
-
title: expanded.has(hit.ref) ? "折叠 table" : "展开 table",
|
|
36914
|
-
onClick: () => {
|
|
36915
|
-
expandVar(hit.ref);
|
|
36916
|
-
}
|
|
36917
|
-
}, expanded.has(hit.ref) ? "▾" : "▸") : react.default.createElement("span", { className: "edrv-dapvar-tw" }, ""), react.default.createElement("span", {
|
|
36918
|
-
className: "edrv-dapvar-name",
|
|
36919
|
-
title: expr
|
|
36920
|
-
}, expr), react.default.createElement("span", {
|
|
36921
|
-
className: "edrv-dapvar-val",
|
|
36922
|
-
title: hit?.result || ""
|
|
36923
|
-
}, hit ? "= " + hit.result : "= 不可用"), hit?.type ? react.default.createElement("span", { className: "edrv-dapvar-type" }, hit.type) : null)];
|
|
36924
|
-
if (hit?.ref > 0 && expanded.has(hit.ref)) rows.push(...flattenVariableRows(varCache.get(hit.ref) ?? [], expanded, varCache, { maxRows: 500 }).map(renderVarRow));
|
|
36925
|
-
return rows;
|
|
36926
|
-
});
|
|
36927
|
-
const watchSection = section("监视", "watch", "watch", [segAct("watch-add", "+", "新增监视表达式", focusWatchInput), segAct("watch-clear", "🗑", "移除全部监视", clearWatches)], [...watchRows, react.default.createElement("div", {
|
|
36928
|
-
key: "add",
|
|
36929
|
-
className: "edrv-dapwatch-add"
|
|
36930
|
-
}, react.default.createElement("input", {
|
|
36931
|
-
ref: watchInputRef,
|
|
36932
|
-
className: "edrv-daprepl-in",
|
|
36933
|
-
placeholder: "新增监视表达式,回车确认",
|
|
36934
|
-
value: watchDraft,
|
|
36935
|
-
onChange: (e) => setWatchDraft(e.target.value),
|
|
36936
|
-
onKeyDown: (e) => {
|
|
36937
|
-
if (e.key === "Enter") addWatch();
|
|
36938
|
-
}
|
|
36939
|
-
}))]);
|
|
36940
|
-
const bpSectionChildren = bpRows.length ? bpRows.flatMap((row) => {
|
|
36941
|
-
const base = row.path.split("/").pop() || row.path;
|
|
36942
|
-
const dir = row.path.includes("/") ? row.path.slice(0, row.path.lastIndexOf("/")) : "";
|
|
36943
|
-
const ack = dapStore.ackOf(row.path, row.entry.line);
|
|
36944
|
-
const unverified = Boolean(ack && !ack.verified);
|
|
36945
|
-
const editing = bpEdit && bpEdit.path === row.path && bpEdit.line === row.entry.line;
|
|
36946
|
-
const rows = [react.default.createElement("div", {
|
|
36947
|
-
key: row.path + ":" + row.entry.line,
|
|
36948
|
-
className: "edrv-dapbp",
|
|
36949
|
-
title: "跳转到 " + row.path + ":" + row.entry.line + (unverified && ack.message ? "(" + ack.message + ")" : ""),
|
|
36950
|
-
onClick: () => jumpBp(row),
|
|
36951
|
-
onContextMenu: (e) => {
|
|
36952
|
-
e.preventDefault();
|
|
36953
|
-
setBpMenu({
|
|
36954
|
-
x: e.clientX,
|
|
36955
|
-
y: e.clientY,
|
|
36956
|
-
row
|
|
36957
|
-
});
|
|
36958
|
-
}
|
|
36959
|
-
}, react.default.createElement("input", {
|
|
36960
|
-
type: "checkbox",
|
|
36961
|
-
checked: row.entry.enabled,
|
|
36962
|
-
title: row.entry.enabled ? "禁用断点" : "启用断点",
|
|
36963
|
-
onChange: () => toggleBpEnabled(row),
|
|
36964
|
-
onClick: (e) => e.stopPropagation()
|
|
36965
|
-
}), react.default.createElement("span", {
|
|
36966
|
-
className: "edrv-dapbp-dot" + (row.entry.enabled ? "" : " off") + (unverified ? " unverified" : ""),
|
|
36967
|
-
title: unverified && ack.message ? ack.message : void 0
|
|
36968
|
-
}), react.default.createElement("button", {
|
|
36969
|
-
className: "edrv-dapbp-file",
|
|
36970
|
-
title: row.path
|
|
36971
|
-
}, base), react.default.createElement("span", {
|
|
36972
|
-
className: "edrv-dapbp-dir",
|
|
36973
|
-
title: dir
|
|
36974
|
-
}, dir), react.default.createElement("span", { className: "edrv-dapbp-line" }, row.entry.line), react.default.createElement("button", {
|
|
36975
|
-
className: "edrv-dapbp-x",
|
|
36976
|
-
title: "删除断点",
|
|
36977
|
-
onClick: (e) => {
|
|
36978
|
-
e.stopPropagation();
|
|
36979
|
-
removeBp(row);
|
|
36980
|
-
}
|
|
36981
|
-
}, "×"))];
|
|
36982
|
-
if (editing) {
|
|
36983
|
-
const fieldKey = fieldOfMode(bpEdit.mode);
|
|
36984
|
-
const modeItem = MODES.find((m) => m.mode === bpEdit.mode) ?? MODES[0];
|
|
36985
|
-
rows.push(react.default.createElement("div", {
|
|
36986
|
-
key: row.path + ":" + row.entry.line + ":edit",
|
|
36987
|
-
className: "edrv-dapbp-edit"
|
|
36988
|
-
}, react.default.createElement("select", {
|
|
36989
|
-
className: "edrv-bpwidget-mode",
|
|
36990
|
-
title: "断点类型",
|
|
36991
|
-
value: String(bpEdit.mode),
|
|
36992
|
-
onChange: (e) => setBpEdit({
|
|
36993
|
-
...bpEdit,
|
|
36994
|
-
mode: Number(e.target.value)
|
|
36995
|
-
})
|
|
36996
|
-
}, MODES.filter((m) => m.mode !== BP_MODE.WAIT).map((m) => react.default.createElement("option", {
|
|
36997
|
-
key: m.mode,
|
|
36998
|
-
value: String(m.mode)
|
|
36999
|
-
}, m.label))), react.default.createElement("input", {
|
|
37000
|
-
className: "edrv-bpwidget-input",
|
|
37001
|
-
autoFocus: true,
|
|
37002
|
-
spellCheck: false,
|
|
37003
|
-
placeholder: modeItem?.placeholder ?? "",
|
|
37004
|
-
hidden: fieldKey === null,
|
|
37005
|
-
value: fieldKey ? bpEdit.drafts[fieldKey] || "" : "",
|
|
37006
|
-
onChange: (e) => setBpEdit({
|
|
37007
|
-
...bpEdit,
|
|
37008
|
-
drafts: {
|
|
37009
|
-
...bpEdit.drafts,
|
|
37010
|
-
[fieldKey]: e.target.value
|
|
37011
|
-
}
|
|
37012
|
-
}),
|
|
37013
|
-
onKeyDown: (e) => {
|
|
37014
|
-
if (e.key === "Enter") {
|
|
37015
|
-
e.preventDefault();
|
|
37016
|
-
commitBpEdit();
|
|
37017
|
-
return;
|
|
37018
|
-
}
|
|
37019
|
-
if (e.key === "Escape") {
|
|
37020
|
-
e.preventDefault();
|
|
37021
|
-
setBpEdit(null);
|
|
37022
|
-
}
|
|
37023
|
-
}
|
|
37024
|
-
})));
|
|
37025
|
-
}
|
|
37026
|
-
return rows;
|
|
37027
|
-
}) : [react.default.createElement("div", {
|
|
37028
|
-
key: "e",
|
|
37029
|
-
className: "edrv-dapempty"
|
|
37030
|
-
}, "在编辑器行首点击或 F9 添加断点")];
|
|
37031
|
-
const bpSection = section("断点", "breakpoints", "breakpoints", [
|
|
37032
|
-
segAct("bp-new", "新建断点 ▾", "添加条件断点 / 记录点(落在当前编辑器光标行)", (e) => setNewBpMenu({
|
|
37033
|
-
x: e.clientX,
|
|
37034
|
-
y: e.clientY
|
|
37035
|
-
})),
|
|
37036
|
-
bpRows.length ? segAct("bp-toggle", allBpEnabled ? "⊘" : "◉", allBpEnabled ? "禁用所有断点" : "启用所有断点", () => dapStore.setAllBpEnabled(scope, cwd, !allBpEnabled)) : null,
|
|
37037
|
-
bpRows.length ? segAct("bp-clear", "🗑", "删除所有断点", removeAllBps) : null
|
|
37038
|
-
], bpSectionChildren);
|
|
37039
|
-
const outputRows = dapStore.outputs().slice(-60).map((ev, idx) => react.default.createElement("div", {
|
|
37040
|
-
key: ev.seq + ":" + idx,
|
|
37041
|
-
className: "edrv-daprepl-line out"
|
|
37042
|
-
}, ev.text?.trimEnd() || ""));
|
|
37043
|
-
const configReady = snap.configs.some((c) => snap.adapters.length === 0 || snap.adapters.find((a) => a.type === c.type)?.available === true);
|
|
37044
|
-
const toolbarBtn = (key, label, title, disabled, onClick) => react.default.createElement("button", {
|
|
37045
|
-
key,
|
|
37046
|
-
className: "edrv-dappanel-run",
|
|
37047
|
-
title,
|
|
37048
|
-
disabled: disabled === true,
|
|
37049
|
-
onClick
|
|
37050
|
-
}, label);
|
|
37051
|
-
const bpMenuEl = bpMenu ? react.default.createElement(ContextMenu, {
|
|
37052
|
-
x: bpMenu.x,
|
|
37053
|
-
y: bpMenu.y,
|
|
37054
|
-
onClose: () => setBpMenu(null),
|
|
37055
|
-
entries: buildBpRowMenu({ enabled: bpMenu.row.entry.enabled }).map((item) => ({
|
|
37056
|
-
id: item.id,
|
|
37057
|
-
label: item.label,
|
|
37058
|
-
separator: item.separator,
|
|
37059
|
-
onClick: () => runBpRowAction(item.id, bpMenu.row)
|
|
37060
|
-
}))
|
|
37061
|
-
}) : null;
|
|
37062
|
-
const newBpMenuEl = newBpMenu ? react.default.createElement(ContextMenu, {
|
|
37063
|
-
x: newBpMenu.x,
|
|
37064
|
-
y: newBpMenu.y,
|
|
37065
|
-
onClose: () => setNewBpMenu(null),
|
|
37066
|
-
entries: [{
|
|
37067
|
-
id: "add-conditional",
|
|
37068
|
-
label: "添加条件断点...",
|
|
37069
|
-
onClick: () => addBpAtCursor(BP_MODE.EXPRESSION)
|
|
37070
|
-
}, {
|
|
37071
|
-
id: "add-logpoint",
|
|
37072
|
-
label: "添加记录点...",
|
|
37073
|
-
onClick: () => addBpAtCursor(BP_MODE.LOG_MESSAGE)
|
|
37074
|
-
}]
|
|
37075
|
-
}) : null;
|
|
37076
|
-
return react.default.createElement("div", { className: "edrv-dappanel" }, react.default.createElement("div", { className: "edrv-dappanel-head" }, ...snap.candidates ? [
|
|
37077
|
-
react.default.createElement("select", {
|
|
37078
|
-
key: "pick",
|
|
37079
|
-
className: "edrv-dappanel-cfg-select",
|
|
37080
|
-
title: "选择附加目标进程(processName 命中多个候选)",
|
|
37081
|
-
value: String(pickPid || snap.candidates[0]?.pid || ""),
|
|
37082
|
-
onChange: (e) => setPickPid(Number(e.target.value))
|
|
37083
|
-
}, snap.candidates.map((it) => react.default.createElement("option", {
|
|
37084
|
-
key: it.pid,
|
|
37085
|
-
value: String(it.pid)
|
|
37086
|
-
}, it.pid + " : " + it.name + (it.title ? " — " + it.title.slice(0, 44) : "")))),
|
|
37087
|
-
toolbarBtn("pick-ok", "✓", "附加到选中进程", false, () => dapStore.confirmPick(pickPid || snap.candidates[0]?.pid || 0)),
|
|
37088
|
-
toolbarBtn("pick-cancel", "✕", "取消", false, () => dapStore.cancelPick())
|
|
37089
|
-
] : [react.default.createElement("select", {
|
|
37090
|
-
key: "cfg",
|
|
37091
|
-
className: "edrv-dappanel-cfg-select",
|
|
37092
|
-
title: "调试配置(读取工作区 .dsh/launch.json;适配器由扩展清单 contributes.debuggers 提供)",
|
|
37093
|
-
value: snap.selectedConfig ?? "",
|
|
37094
|
-
onChange: (e) => dapStore.selectConfig(e.target.value)
|
|
37095
|
-
}, snap.configs.length ? snap.configs.map((c) => {
|
|
37096
|
-
const adapter = snap.adapters.find((a) => a.type === c.type);
|
|
37097
|
-
const ready = snap.adapters.length === 0 || adapter?.available === true;
|
|
37098
|
-
const suffix = ready ? "" : "(" + (adapter?.reason || "未发现该类型适配器") + ")";
|
|
37099
|
-
return react.default.createElement("option", {
|
|
37100
|
-
key: c.name,
|
|
37101
|
-
value: c.name,
|
|
37102
|
-
disabled: !ready
|
|
37103
|
-
}, c.name + suffix);
|
|
37104
|
-
}) : react.default.createElement("option", { value: "" }, "无可用调试配置"))], toolbarBtn("run", "▶", paused ? "继续(F5)" : "启动调试(F5)", !paused && (!configReady || dapActive), () => dapStore.startOrContinue()), toolbarBtn("cfg-gear", "⚙", "打开/新建调试配置(" + LAUNCH_REL + ")", !ctx?.cwd, () => {
|
|
37105
|
-
openLaunchConfig();
|
|
37106
|
-
}), toolbarBtn("pause", "⏸", "暂停", !dapActive, () => {
|
|
37107
|
-
dapStore.action("pause");
|
|
37108
|
-
}), toolbarBtn("next", "⤵", "单步跳过(F10)", !paused, () => {
|
|
37109
|
-
dapStore.action("next");
|
|
37110
|
-
}), toolbarBtn("stepin", "↓", "单步步入(F11)", !paused, () => {
|
|
37111
|
-
dapStore.action("stepIn");
|
|
37112
|
-
}), toolbarBtn("stepout", "↑", "单步步出(Shift+F11)", !paused, () => {
|
|
37113
|
-
dapStore.action("stepOut");
|
|
37114
|
-
}), toolbarBtn("stop", "⏹", "停止(Shift+F5)", !dapActive, () => {
|
|
37115
|
-
dapStore.stop();
|
|
37116
|
-
}), react.default.createElement("span", { className: "edrv-dappanel-phase" + (paused ? " paused" : "") }, PHASE_LABEL[snap.phase] || snap.phase), snap.pid ? react.default.createElement("span", { className: "edrv-dappanel-pid" }, "pid " + snap.pid) : null, react.default.createElement("span", { style: { flex: 1 } }), snap.error ? react.default.createElement("span", {
|
|
37117
|
-
className: "edrv-dappanel-err",
|
|
37118
|
-
title: snap.error
|
|
37119
|
-
}, "⚠") : null), section("变量", "variables", "variables", [segAct("var-reload", "⟳", "刷新变量", () => setReloadTick((t) => t + 1))], varRows), sash("variables", "watch"), watchSection, sash("watch", "stack"), section("调用堆栈", "stack", "stack", [], stackRows), sash("stack", "breakpoints"), bpSection, react.default.createElement("div", { className: "edrv-daprepl" }, react.default.createElement("div", { className: "edrv-dapseg-head" }, "调试控制台"), react.default.createElement("div", { className: "edrv-daprepl-log" }, ...replLog.map((row, idx) => react.default.createElement("div", {
|
|
37120
|
-
key: idx,
|
|
37121
|
-
className: "edrv-daprepl-line"
|
|
37122
|
-
}, react.default.createElement("span", { className: "edrv-dapvar-name" }, row.expr), react.default.createElement("span", { className: "edrv-dapvar-val" }, "= " + row.result))), ...outputRows), react.default.createElement("input", {
|
|
37123
|
-
className: "edrv-daprepl-in",
|
|
37124
|
-
placeholder: paused ? "输入表达式求值,回车执行(如 self.itemId)" : "暂停时才可求值",
|
|
37125
|
-
disabled: !paused,
|
|
37126
|
-
value: replDraft,
|
|
37127
|
-
onChange: (e) => setReplDraft(e.target.value),
|
|
37128
|
-
onKeyDown: (e) => {
|
|
37129
|
-
if (e.key === "Enter") runRepl();
|
|
37130
|
-
}
|
|
37131
|
-
})), bpMenuEl, newBpMenuEl);
|
|
37132
|
-
}
|
|
37133
|
-
//#endregion
|
|
37134
36397
|
//#region src/client/sidebar/panels/index.ts
|
|
37135
36398
|
/**
|
|
37136
36399
|
* dsh-vscode-mode client — 侧边栏面板定义(文件管理 + 搜索 + 规则 + SVN 变更)。
|
|
@@ -37178,7 +36441,11 @@ window.__ModuleLoader__.load({
|
|
|
37178
36441
|
title: "规则",
|
|
37179
36442
|
icon: IconListPenOutline16,
|
|
37180
36443
|
order: 20,
|
|
37181
|
-
render: (ctx) => react.default.createElement(
|
|
36444
|
+
render: (ctx) => react.default.createElement(LazyPanel, {
|
|
36445
|
+
key: "rules",
|
|
36446
|
+
panel: "rules",
|
|
36447
|
+
ctx
|
|
36448
|
+
})
|
|
37182
36449
|
};
|
|
37183
36450
|
}
|
|
37184
36451
|
/**
|
|
@@ -37231,7 +36498,11 @@ window.__ModuleLoader__.load({
|
|
|
37231
36498
|
icon: "🐞",
|
|
37232
36499
|
order: 22,
|
|
37233
36500
|
badge: () => dapStore.getSnapshot().phase === "paused" ? 1 : null,
|
|
37234
|
-
render: (ctx) => react.default.createElement(
|
|
36501
|
+
render: (ctx) => react.default.createElement(LazyPanel, {
|
|
36502
|
+
key: "debug",
|
|
36503
|
+
panel: "debug",
|
|
36504
|
+
ctx
|
|
36505
|
+
})
|
|
37235
36506
|
};
|
|
37236
36507
|
}
|
|
37237
36508
|
//#endregion
|
|
@@ -39017,6 +38288,16 @@ window.__ModuleLoader__.load({
|
|
|
39017
38288
|
* conversation.input.dock。
|
|
39018
38289
|
* 作者 ddj 2026-08-20
|
|
39019
38290
|
*/
|
|
38291
|
+
/** Root-owned live objects shared with independent panel chunks; no copied stores or transport state. */
|
|
38292
|
+
const __lazyShared = {
|
|
38293
|
+
rpc,
|
|
38294
|
+
dbg,
|
|
38295
|
+
dapStore,
|
|
38296
|
+
BP_MODE,
|
|
38297
|
+
MODES,
|
|
38298
|
+
fieldOfMode,
|
|
38299
|
+
ContextMenu
|
|
38300
|
+
};
|
|
39020
38301
|
const inject = [
|
|
39021
38302
|
"slots",
|
|
39022
38303
|
"timer",
|
|
@@ -39633,6 +38914,7 @@ window.__ModuleLoader__.load({
|
|
|
39633
38914
|
order: 40
|
|
39634
38915
|
}, (props) => react.default.createElement(PluginDetailSection, Object.assign({}, props)));
|
|
39635
38916
|
ctx.effect(() => () => {
|
|
38917
|
+
setPanelImport();
|
|
39636
38918
|
try {
|
|
39637
38919
|
disposeSnippets();
|
|
39638
38920
|
} catch {}
|
|
@@ -39660,8 +38942,11 @@ window.__ModuleLoader__.load({
|
|
|
39660
38942
|
reportUiStateEvent("客户端装配完成:水合" + (uiStateSettled() ? "已结束" : "仍在进行(界面稍后重跑恢复)"));
|
|
39661
38943
|
}
|
|
39662
38944
|
//#endregion
|
|
38945
|
+
exports.__lazyShared = __lazyShared;
|
|
38946
|
+
exports.__setPanelImport = setPanelImport;
|
|
39663
38947
|
exports.apply = apply;
|
|
39664
38948
|
exports.inject = inject;
|
|
38949
|
+
module.exports.__setPanelImport(require.async?.bind(require));
|
|
39665
38950
|
return module.exports;
|
|
39666
38951
|
}
|
|
39667
38952
|
});
|