dsh-vscode-mode 0.1.20 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -29,7 +29,7 @@ window.__ModuleLoader__.load({
29
29
  let react = require("react");
30
30
  react = __toESM(react, 1);
31
31
  //#region \0dsh-css:/home/runner/work/DSH_VsCodeMode/DSH_VsCodeMode/src/client/styles/editor.css.mjs
32
- const css$1 = "/* dsh-vscode-mode 编辑区样式(原 ensureEdrvStyles 字符串,落盘为真 .css,由 tsdown CSS-inline 注入) */\n/* 浅色主题(对齐参考图:#f8f8f8 近白底 + 薄荷绿药丸 + 青绿文字 + 三文鱼 undo + 浅灰边框):\n 在 [data-edrv-view] 作用域内重定义 --dsw-alias-*,级联命中组件内联样式与 CSS。 */\n[data-edrv-view] { box-sizing: border-box; --edrv-tab-h: 34px; --dsw-alias-bg-base: #f8f8f8; --dsw-alias-bg-layer-1: #ffffff; --dsw-alias-bg-layer-2: #f0f4f4; --dsw-alias-bg-overlay: #ffffff; --dsw-alias-label-primary: #1f2933; --dsw-alias-label-secondary: #52606d; --dsw-alias-label-tertiary: #9aa5b1; --dsw-alias-border-l1: #e0e6e8; --dsw-alias-border-l2: #cbd2d9; --dsw-alias-brand-primary: #0f9d58; --dsw-alias-state-success-primary: #0f9d58; --dsw-alias-state-error-primary: #d9534f; --dsw-alias-state-warn-primary: #d97706; --dsw-alias-interactive-bg-hover: rgba(15,157,88,.08); }\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; scrollbar-width: thin; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab { 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-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dsw-alias-brand-primary, #4f8cff); flex-shrink: 0; }\n[data-edrv-view] .edrv-tab .edrv-tab-x { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: 4px; color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; }\n[data-edrv-view] .edrv-tab .edrv-tab-x:hover { background: rgba(255,255,255,.12); color: var(--dsw-alias-label-primary, #eee); }\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[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[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: #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-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-mn-add-line { background: rgba(15,157,88,.10); }\n[data-edrv-view] .edrv-mn-gutter-add { color: #0f9d58 !important; font-weight: 700; 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 { background: #fdeaea; border-top: 1px solid rgba(217,83,79,.35); border-bottom: 1px solid rgba(217,83,79,.35); box-shadow: inset 0 0 0 1px rgba(217,83,79,.12); }\n[data-edrv-view] .edrv-del-row { white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 12.5px; line-height: 20px; color: #c0392b; }\n[data-edrv-view] .edrv-del-text { display: inline-block; white-space: pre; }\n[data-edrv-view] .edrv-minus-overlay { position: absolute; left: 0; top: 0; width: 100%; height: 100%; max-width: 100%; overflow: visible; pointer-events: none; z-index: 6; }\n[data-edrv-view] .edrv-minus-item { position: absolute; box-sizing: border-box; max-width: 100%; overflow: hidden; }\n[data-edrv-view] .edrv-minus-item::before { content: ''; position: absolute; left: 4px; top: 50%; width: 8px; height: 2px; transform: translateY(-50%); border-radius: 2px; background: #d9534f; }\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-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: #e8f8e8; color: #0f7a45; border: 1px solid rgba(15,157,88,.4); }\n[data-edrv-view] .edrv-pill-undo { background: #fdeaea; color: #c0392b; border: 1px solid rgba(217,83,79,.4); }\n[data-edrv-view] .edrv-pill-ghost { background: #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[data-edrv-view] .edrv-ctxmenu { position: fixed; z-index: 71; min-width: 200px; max-width: 320px; 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-sep { height: 1px; margin: 3px 4px; background: var(--dsw-alias-border-l1, #e0e6e8); }\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: 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\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: 0; 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; }\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: #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; }\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-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: #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[data-edrv-view] .edrv-tree-error { display: flex; align-items: center; gap: 8px; margin: 4px 2px 6px; padding: 6px 8px; border: 1px solid rgba(217,83,79,.4); border-radius: 6px; background: #fdeaea; color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\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";
32
+ const css$1 = "/* dsh-vscode-mode 编辑区样式(原 ensureEdrvStyles 字符串,落盘为真 .css,由 tsdown CSS-inline 注入) */\n/* 浅色主题(对齐参考图:#f8f8f8 近白底 + 薄荷绿药丸 + 青绿文字 + 三文鱼 undo + 浅灰边框):\n 在 [data-edrv-view] 作用域内重定义 --dsw-alias-*,级联命中组件内联样式与 CSS。 */\n[data-edrv-view] { box-sizing: border-box; --edrv-tab-h: 34px; --dsw-alias-bg-base: #f8f8f8; --dsw-alias-bg-layer-1: #ffffff; --dsw-alias-bg-layer-2: #f0f4f4; --dsw-alias-bg-overlay: #ffffff; --dsw-alias-label-primary: #1f2933; --dsw-alias-label-secondary: #52606d; --dsw-alias-label-tertiary: #9aa5b1; --dsw-alias-border-l1: #e0e6e8; --dsw-alias-border-l2: #cbd2d9; --dsw-alias-brand-primary: #0f9d58; --dsw-alias-state-success-primary: #0f9d58; --dsw-alias-state-error-primary: #d9534f; --dsw-alias-state-warn-primary: #d97706; --dsw-alias-interactive-bg-hover: rgba(15,157,88,.08); }\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; scrollbar-width: thin; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab { 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-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dsw-alias-brand-primary, #4f8cff); flex-shrink: 0; }\n[data-edrv-view] .edrv-tab .edrv-tab-x { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: 4px; color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; }\n[data-edrv-view] .edrv-tab .edrv-tab-x:hover { background: rgba(255,255,255,.12); color: var(--dsw-alias-label-primary, #eee); }\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[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[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: #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-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-mn-add-line { background: rgba(15,157,88,.10); }\n[data-edrv-view] .edrv-mn-gutter-add { color: #0f9d58 !important; font-weight: 700; 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 { background: #fdeaea; border-top: 1px solid rgba(217,83,79,.35); border-bottom: 1px solid rgba(217,83,79,.35); box-shadow: inset 0 0 0 1px rgba(217,83,79,.12); }\n[data-edrv-view] .edrv-del-row { white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 12.5px; line-height: 20px; color: #c0392b; }\n[data-edrv-view] .edrv-del-text { display: inline-block; white-space: pre; }\n[data-edrv-view] .edrv-minus-overlay { position: absolute; left: 0; top: 0; width: 100%; height: 100%; max-width: 100%; overflow: visible; pointer-events: none; z-index: 6; }\n[data-edrv-view] .edrv-minus-item { position: absolute; box-sizing: border-box; max-width: 100%; overflow: hidden; }\n[data-edrv-view] .edrv-minus-item::before { content: ''; position: absolute; left: 4px; top: 50%; width: 8px; height: 2px; transform: translateY(-50%); border-radius: 2px; background: #d9534f; }\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-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: #e8f8e8; color: #0f7a45; border: 1px solid rgba(15,157,88,.4); }\n[data-edrv-view] .edrv-pill-undo { background: #fdeaea; color: #c0392b; border: 1px solid rgba(217,83,79,.4); }\n[data-edrv-view] .edrv-pill-ghost { background: #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[data-edrv-view] .edrv-ctxmenu { position: fixed; z-index: 71; min-width: 200px; max-width: 320px; 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[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: 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\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: 0; 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; }\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: #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-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: #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[data-edrv-view] .edrv-tree-error { display: flex; align-items: center; gap: 8px; margin: 4px 2px 6px; padding: 6px 8px; border: 1px solid rgba(217,83,79,.4); border-radius: 6px; background: #fdeaea; 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";
33
33
  const tagId$1 = "dsh-vscode-mode/editor.css";
34
34
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
35
35
  const tag = document.createElement("style");
@@ -1327,6 +1327,41 @@ window.__ModuleLoader__.load({
1327
1327
  return Math.max(0, Math.min(scroll, Number.isFinite(composerTop) ? composerTop : scroll) - root);
1328
1328
  }
1329
1329
  //#endregion
1330
+ //#region src/client/fileReveal.ts
1331
+ /**
1332
+ * dsh-vscode-mode client — 「在文件浏览器中打开」RPC 包装(树菜单与 Monaco 右键共用)。
1333
+ * 归一化为 { ok, error? },不抛异常;无会话/异常降级返回错误文案。
1334
+ * 作者 ddj 2026-08-27
1335
+ */
1336
+ /**
1337
+ * 请求 host 在 OS 文件浏览器中打开/定位路径。
1338
+ * @author ddj 2026年08月27号
1339
+ * @param sessionId 会话 id(可空)
1340
+ * @param path 工作区相对路径('' = 根目录)
1341
+ * @returns 成功或失败原因
1342
+ */
1343
+ async function revealInExplorer(sessionId, path) {
1344
+ if (!sessionId) return {
1345
+ ok: false,
1346
+ error: "无活动会话"
1347
+ };
1348
+ try {
1349
+ const res = await rpc("edrv.revealInExplorer", {
1350
+ sessionId,
1351
+ path
1352
+ });
1353
+ return res.ok ? { ok: true } : {
1354
+ ok: false,
1355
+ error: res.error
1356
+ };
1357
+ } catch (error) {
1358
+ return {
1359
+ ok: false,
1360
+ error: "打开异常:" + String(error)
1361
+ };
1362
+ }
1363
+ }
1364
+ //#endregion
1330
1365
  //#region src/client/ui/EditorView.ts
1331
1366
  /**
1332
1367
  * dsh-vscode-mode client — EditorView:中央 VSCode 式文件编辑器(编排层)。
@@ -1863,6 +1898,13 @@ window.__ModuleLoader__.load({
1863
1898
  });
1864
1899
  }
1865
1900
  });
1901
+ ed.addAction({
1902
+ id: "edrv.revealInExplorer",
1903
+ label: "在文件浏览器中打开",
1904
+ contextMenuGroupId: "1_edrv",
1905
+ precondition: "editorTextFocus",
1906
+ run: (edx) => menuHandlers()?.openInExplorer(pathOf(edx))
1907
+ });
1866
1908
  const hideSoon = () => {
1867
1909
  if (hoverEditorRef.current || hoverPanelRef.current || hideTimerRef.current) return;
1868
1910
  hideTimerRef.current = setTimeout(() => {
@@ -2167,7 +2209,30 @@ window.__ModuleLoader__.load({
2167
2209
  }
2168
2210
  addToConversation.appendReference(sessionId, path, range).then((o) => setStatus(statusOfAdd(o, "已添加文件引用")));
2169
2211
  };
2170
- menuHandlersRef.current = { addRefToChat };
2212
+ /**
2213
+ * 在 OS 文件浏览器中打开/定位路径(Monaco 右键菜单用,状态栏反馈)。
2214
+ * @author ddj 2026年08月27号
2215
+ * @param path 工作区相对路径(可能为 null)
2216
+ */
2217
+ const openInExplorer = (path) => {
2218
+ if (!path) {
2219
+ setStatus("无活动文件");
2220
+ return;
2221
+ }
2222
+ if (!sessionId) {
2223
+ setStatus("无活动会话");
2224
+ return;
2225
+ }
2226
+ setStatus("正在打开文件浏览器…");
2227
+ revealInExplorer(sessionId, path).then((outcome) => {
2228
+ setStatus(outcome.ok ? "已在文件浏览器中打开" : "打开失败");
2229
+ if (!outcome.ok && outcome.error) setError(outcome.error);
2230
+ });
2231
+ };
2232
+ menuHandlersRef.current = {
2233
+ addRefToChat,
2234
+ openInExplorer
2235
+ };
2171
2236
  const openFile = (path, focusDiff) => {
2172
2237
  if (!path) return;
2173
2238
  addTab(path, true);
@@ -2466,7 +2531,11 @@ window.__ModuleLoader__.load({
2466
2531
  totalFiles: sum.totalFiles,
2467
2532
  files: sum.files
2468
2533
  },
2469
- refreshRecords: () => refreshRecords()
2534
+ refreshRecords: () => refreshRecords(),
2535
+ editor: () => editorRef.current,
2536
+ outlineSources: props.outlineSources,
2537
+ fileMenuItems: props.fileMenuItems,
2538
+ notify: (message) => setStatus(message)
2470
2539
  };
2471
2540
  const editorArea = react.default.createElement("div", { className: "edrv-editor-area" }, body, hoverEl, overlay);
2472
2541
  const mainCol = react.default.createElement("div", {
@@ -3706,17 +3775,144 @@ window.__ModuleLoader__.load({
3706
3775
  };
3707
3776
  }
3708
3777
  //#endregion
3778
+ //#region src/client/ui/ContextMenu.ts
3779
+ /**
3780
+ * dsh-vscode-mode client — 通用浮动右键菜单(createElement 风格、类型化)。
3781
+ * 全屏 backdrop(点击/右键/Esc 关闭)+ 固定定位(viewport clamp 防越界),
3782
+ * 条目支持 danger/disabled/separator,复用既有 edrv-ctxmenu* 样式类。
3783
+ * 作者 ddj 2026-08-27
3784
+ */
3785
+ /** 菜单估算宽高(clamp 防越出视口,与 EditorView menuPos 常量对齐)。 */
3786
+ const MENU_W = 224;
3787
+ const MENU_H = 176;
3788
+ /**
3789
+ * 浮动右键菜单。
3790
+ * @param props.x 视口 x 坐标
3791
+ * @param props.y 视口 y 坐标
3792
+ * @param props.entries 菜单项列表
3793
+ * @param props.onClose 关闭回调(backdrop 点击/右键/Esc/点击项后触发)
3794
+ */
3795
+ function ContextMenu(props) {
3796
+ const { x, y, entries, onClose } = props;
3797
+ react.default.useEffect(() => {
3798
+ const onKey = (e) => {
3799
+ if (e.key === "Escape") onClose();
3800
+ };
3801
+ window.addEventListener("keydown", onKey, true);
3802
+ return () => window.removeEventListener("keydown", onKey, true);
3803
+ }, [onClose]);
3804
+ const left = Math.max(4, Math.min(x, (window.innerWidth || 800) - MENU_W));
3805
+ const top = Math.max(4, Math.min(y, (window.innerHeight || 600) - MENU_H));
3806
+ const children = [];
3807
+ for (const entry of entries) {
3808
+ if (entry.separator) {
3809
+ children.push(react.default.createElement("div", {
3810
+ key: "sep-" + entry.id,
3811
+ className: "edrv-ctxmenu-sep"
3812
+ }));
3813
+ continue;
3814
+ }
3815
+ const cls = "edrv-ctxmenu-item" + (entry.danger ? " edrv-ctxmenu-danger" : "") + (entry.disabled ? " edrv-ctxmenu-disabled" : "");
3816
+ children.push(react.default.createElement("button", {
3817
+ key: entry.id,
3818
+ className: cls,
3819
+ disabled: entry.disabled,
3820
+ onClick: () => {
3821
+ if (!entry.disabled) {
3822
+ entry.onClick?.();
3823
+ onClose();
3824
+ }
3825
+ }
3826
+ }, entry.label));
3827
+ }
3828
+ return react.default.createElement(react.default.Fragment, null, react.default.createElement("div", {
3829
+ className: "edrv-ctxmenu-backdrop",
3830
+ style: {
3831
+ position: "fixed",
3832
+ inset: 0,
3833
+ zIndex: 70
3834
+ },
3835
+ onClick: onClose,
3836
+ onContextMenu: (e) => {
3837
+ e.preventDefault();
3838
+ onClose();
3839
+ }
3840
+ }), react.default.createElement("div", {
3841
+ className: "edrv-ctxmenu",
3842
+ style: {
3843
+ left,
3844
+ top
3845
+ }
3846
+ }, ...children));
3847
+ }
3848
+ //#endregion
3849
+ //#region src/client/sidebar/contextMenu.ts
3850
+ /** 校验菜单项并写入注册表(缺 id/run/label 抛 TypeError)。 */
3851
+ function itemRegister(entries, notify, item) {
3852
+ if (!item.id || typeof item.run !== "function" || !item.label) throw new TypeError("文件右键菜单项必须提供 id、label 和 run");
3853
+ entries.set(item.id, item);
3854
+ notify();
3855
+ }
3856
+ /**
3857
+ * 创建生命周期独立的右键菜单项注册表。
3858
+ * @author ddj 2026年08月27号
3859
+ * @returns 菜单项注册表
3860
+ */
3861
+ function createTreeMenuRegistry() {
3862
+ const entries = /* @__PURE__ */ new Map();
3863
+ const listeners = /* @__PURE__ */ new Set();
3864
+ const notify = () => {
3865
+ for (const listener of listeners) listener();
3866
+ };
3867
+ const list = () => [...entries.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
3868
+ return {
3869
+ register(item) {
3870
+ itemRegister(entries, notify, item);
3871
+ return () => {
3872
+ if (entries.get(item.id) !== item) return;
3873
+ entries.delete(item.id);
3874
+ notify();
3875
+ };
3876
+ },
3877
+ list,
3878
+ subscribe(listener) {
3879
+ listeners.add(listener);
3880
+ return () => listeners.delete(listener);
3881
+ },
3882
+ get: (id) => entries.get(id)
3883
+ };
3884
+ }
3885
+ /**
3886
+ * 按目标构建当前可见菜单项(visible 过滤 + order 排序)。
3887
+ * @author ddj 2026年08月27号
3888
+ * @param registry 菜单项注册表
3889
+ * @param target 右键目标
3890
+ * @param ctx 面板共享上下文
3891
+ * @returns 可见菜单项(已排序)
3892
+ */
3893
+ function buildTreeMenu(registry, target, ctx) {
3894
+ if (!registry) return [];
3895
+ const out = [];
3896
+ for (const item of registry.list()) {
3897
+ if (typeof item.visible === "function" && !item.visible(target, ctx)) continue;
3898
+ out.push(item);
3899
+ }
3900
+ return out;
3901
+ }
3902
+ //#endregion
3709
3903
  //#region src/client/sidebar/panels/FileExplorer.ts
3710
3904
  /**
3711
3905
  * dsh-vscode-mode client — 侧边栏「文件管理」面板(懒加载目录树)。
3712
3906
  * 点目录展开/收起(首次经 edrv.listDir 拉取并缓存),点文件经 ctx.openFile 打开;
3713
3907
  * 差异角标取 pendingByPath,活动文件高亮;edrv:refresh 事件与手动刷新重载树。
3714
- * 作者 ddj 2026-08-26
3908
+ * 右键行/空白弹出菜单:项来自 ctx.fileMenuItems 注册表(可拓展),
3909
+ * 内置「在文件浏览器中打开」经 edrv.revealInExplorer 定位/打开。
3910
+ * 作者 ddj 2026-08-26 / 2026-08-27
3715
3911
  */
3716
3912
  const DIR_CAP = 4e3;
3717
3913
  /**
3718
3914
  * 目录树面板主体。
3719
- * @param props.ctx 面板共享上下文(sessionId/openFile/activePath/pendingByPath)
3915
+ * @param props.ctx 面板共享上下文(sessionId/openFile/activePath/pendingByPath/fileMenuItems/notify)
3720
3916
  */
3721
3917
  function FileExplorer(props) {
3722
3918
  const ctx = props?.ctx;
@@ -3729,6 +3925,7 @@ window.__ModuleLoader__.load({
3729
3925
  const [expanded, setExpanded] = react.default.useState({});
3730
3926
  const [loading, setLoading] = react.default.useState({});
3731
3927
  const [error, setError] = react.default.useState(null);
3928
+ const [menu, setMenu] = react.default.useState(null);
3732
3929
  const tokensRef = react.default.useRef({});
3733
3930
  const expandedRef = react.default.useRef({});
3734
3931
  expandedRef.current = expanded;
@@ -3807,6 +4004,18 @@ window.__ModuleLoader__.load({
3807
4004
  onClick: () => {
3808
4005
  if (isDir) toggle(e.path);
3809
4006
  else openFile(e.path);
4007
+ },
4008
+ onContextMenu: (ev) => {
4009
+ ev.preventDefault();
4010
+ ev.stopPropagation();
4011
+ setMenu({
4012
+ x: ev.clientX,
4013
+ y: ev.clientY,
4014
+ target: {
4015
+ path: e.path,
4016
+ type: e.type
4017
+ }
4018
+ });
3810
4019
  }
3811
4020
  }, react.default.createElement("span", { className: "edrv-tree-chev" }, isDir ? isOpen ? "▾" : "▸" : ""), react.default.createElement("span", { className: "edrv-tree-name" + (dim ? " edrv-tree-dim" : "") }, isDir ? isOpen ? "📂" : "📁" : "📄", " ", e.name), pending > 0 ? react.default.createElement("span", { className: "edrv-tree-badge" }, String(pending)) : null);
3812
4021
  };
@@ -3833,7 +4042,29 @@ window.__ModuleLoader__.load({
3833
4042
  }, "目录条目过多,仅显示前 4000 项"));
3834
4043
  return rows;
3835
4044
  };
3836
- return react.default.createElement("div", { className: "edrv-side-panel" }, react.default.createElement("div", { className: "edrv-side-head" }, react.default.createElement("span", { className: "edrv-side-title" }, "资源管理器"), react.default.createElement("span", {
4045
+ const onPanelContext = (ev) => {
4046
+ ev.preventDefault();
4047
+ setMenu({
4048
+ x: ev.clientX,
4049
+ y: ev.clientY,
4050
+ target: {
4051
+ path: "",
4052
+ type: "directory"
4053
+ }
4054
+ });
4055
+ };
4056
+ const menuEntries = menu ? buildTreeMenu(ctx?.fileMenuItems, menu.target, ctx).map((item) => ({
4057
+ id: item.id,
4058
+ label: item.label,
4059
+ danger: item.danger,
4060
+ disabled: item.disabled,
4061
+ separator: item.separator,
4062
+ onClick: () => item.run(menu.target, ctx)
4063
+ })) : [];
4064
+ return react.default.createElement("div", {
4065
+ className: "edrv-side-panel",
4066
+ onContextMenu: onPanelContext
4067
+ }, react.default.createElement("div", { className: "edrv-side-head" }, react.default.createElement("span", { className: "edrv-side-title" }, "资源管理器"), react.default.createElement("span", {
3837
4068
  className: "edrv-side-root",
3838
4069
  title: root || ""
3839
4070
  }, rootName), react.default.createElement("span", { style: { flex: 1 } }), react.default.createElement("button", {
@@ -3843,7 +4074,12 @@ window.__ModuleLoader__.load({
3843
4074
  }, "⟳")), react.default.createElement("div", { className: "edrv-tree" }, error ? react.default.createElement("div", { className: "edrv-tree-error" }, react.default.createElement("span", null, String(error)), react.default.createElement("button", {
3844
4075
  className: "edrv-side-btn",
3845
4076
  onClick: () => refresh()
3846
- }, "重试")) : null, rowsOf("", 0)));
4077
+ }, "重试")) : null, rowsOf("", 0)), menu && menuEntries.length ? react.default.createElement(ContextMenu, {
4078
+ x: menu.x,
4079
+ y: menu.y,
4080
+ entries: menuEntries,
4081
+ onClose: () => setMenu(null)
4082
+ }) : null);
3847
4083
  }
3848
4084
  //#endregion
3849
4085
  //#region src/client/sidebar/panels/index.ts
@@ -3867,6 +4103,762 @@ window.__ModuleLoader__.load({
3867
4103
  };
3868
4104
  }
3869
4105
  //#endregion
4106
+ //#region src/client/sidebar/menuItems.ts
4107
+ /**
4108
+ * dsh-vscode-mode client — 文件管理右键菜单内置项。
4109
+ * 首个内置项:「在文件浏览器中打开」(文件→OS Explorer 定位选中、目录→打开目录)。
4110
+ * 反馈统一走 ctx.notify(由 EditorView 提供,落到编辑区路径栏状态)。
4111
+ * 作者 ddj 2026-08-27
4112
+ */
4113
+ /**
4114
+ * 构造内置右键菜单项列表(后续内置项直接追加)。
4115
+ * @author ddj 2026年08月27号
4116
+ * @returns 内置菜单项数组
4117
+ */
4118
+ function createDefaultFileMenuItems() {
4119
+ return [{
4120
+ id: "reveal-in-explorer",
4121
+ label: "在文件浏览器中打开",
4122
+ order: 0,
4123
+ run: (target, ctx) => {
4124
+ revealInExplorer(ctx.sessionId, target.path).then((outcome) => {
4125
+ ctx.notify?.(outcome.ok ? "已在文件浏览器中打开" : "打开失败:" + (outcome.error ?? "未知错误"));
4126
+ });
4127
+ }
4128
+ }];
4129
+ }
4130
+ //#endregion
4131
+ //#region src/client/outline/parse.ts
4132
+ /** monaco SymbolKind 数值常量(与 LSP SymbolKind 一致,面板渲染按此分组)。 */
4133
+ const SK = {
4134
+ File: 0,
4135
+ Module: 1,
4136
+ Namespace: 2,
4137
+ Package: 3,
4138
+ Class: 4,
4139
+ Method: 5,
4140
+ Property: 6,
4141
+ Field: 7,
4142
+ Constructor: 8,
4143
+ Enum: 9,
4144
+ Interface: 10,
4145
+ Function: 11,
4146
+ Variable: 12,
4147
+ Constant: 13,
4148
+ String: 14,
4149
+ Number: 15,
4150
+ Boolean: 16,
4151
+ Array: 17,
4152
+ Object: 18,
4153
+ Key: 19,
4154
+ Null: 20,
4155
+ EnumMember: 21,
4156
+ Struct: 22,
4157
+ Event: 23,
4158
+ Operator: 24,
4159
+ TypeParameter: 25
4160
+ };
4161
+ /** 花括号语言的泛型函数名排除集(避免误把控制流/关键字当符号)。 */
4162
+ const BRACE_KW = /* @__PURE__ */ new Set([
4163
+ "if",
4164
+ "for",
4165
+ "while",
4166
+ "switch",
4167
+ "catch",
4168
+ "foreach",
4169
+ "using",
4170
+ "with",
4171
+ "when",
4172
+ "match",
4173
+ "return",
4174
+ "new",
4175
+ "in",
4176
+ "of",
4177
+ "do",
4178
+ "else",
4179
+ "elif",
4180
+ "then",
4181
+ "yield",
4182
+ "await",
4183
+ "typeof",
4184
+ "instanceof",
4185
+ "case",
4186
+ "default",
4187
+ "try",
4188
+ "finally",
4189
+ "throw"
4190
+ ]);
4191
+ /** 花括号语言的类型关键字 → SymbolKind。 */
4192
+ const BRACE_TYPE_KIND = {
4193
+ class: SK.Class,
4194
+ struct: SK.Struct,
4195
+ interface: SK.Interface,
4196
+ enum: SK.Enum,
4197
+ namespace: SK.Namespace,
4198
+ module: SK.Module,
4199
+ trait: SK.Interface,
4200
+ record: SK.Class
4201
+ };
4202
+ /** 按文档序(先序)补齐每个符号的 endLine:叶子取“下一个符号起行 - 1”,容器取末子 endLine。 */
4203
+ function computeEnds(symbols, lastLine) {
4204
+ const order = [];
4205
+ const collect = (list) => {
4206
+ for (const s of list) {
4207
+ order.push(s);
4208
+ if (s.children && s.children.length) collect(s.children);
4209
+ }
4210
+ };
4211
+ collect(symbols);
4212
+ for (let i = order.length - 1; i >= 0; i--) {
4213
+ const s = order[i];
4214
+ const kids = s.children ?? [];
4215
+ if (kids.length) s.endLine = Math.max(s.startLine, kids[kids.length - 1].endLine);
4216
+ else {
4217
+ const next = order[i + 1];
4218
+ const end = next ? Math.max(s.startLine, next.startLine - 1) : lastLine;
4219
+ s.endLine = Math.max(s.startLine, Math.min(end, lastLine));
4220
+ }
4221
+ }
4222
+ }
4223
+ /** 构造单行符号(起始行=跳转行=所在行;detail 为截断后的声明文本)。 */
4224
+ function mk(lineIndex, name, kind, raw) {
4225
+ const detail = raw.trim().replace(/\s+/g, " ").slice(0, 80);
4226
+ const line = lineIndex + 1;
4227
+ const out = {
4228
+ name,
4229
+ kind,
4230
+ startLine: line,
4231
+ endLine: line,
4232
+ selectLine: line
4233
+ };
4234
+ if (detail && detail !== name) out.detail = detail;
4235
+ return out;
4236
+ }
4237
+ /** Markdown/mdx:`#` 级标题,层级嵌套。 */
4238
+ function parseMarkdown(text) {
4239
+ const roots = [];
4240
+ const stack = [];
4241
+ const lines = text.split("\n");
4242
+ for (let i = 0; i < lines.length; i++) {
4243
+ const m = /^(\#{1,6})\s+(.+?)\s*#*\s*$/.exec(lines[i]);
4244
+ if (!m) continue;
4245
+ const item = {
4246
+ name: m[2].trim(),
4247
+ kind: SK.Namespace,
4248
+ startLine: i + 1,
4249
+ endLine: i + 1,
4250
+ selectLine: i + 1,
4251
+ children: []
4252
+ };
4253
+ while (stack.length && stack[stack.length - 1].level >= m[1].length) stack.pop();
4254
+ const parent = stack.length ? stack[stack.length - 1].item : null;
4255
+ (parent ? parent.children : roots).push(item);
4256
+ stack.push({
4257
+ level: m[1].length,
4258
+ item
4259
+ });
4260
+ }
4261
+ computeEnds(roots, lines.length);
4262
+ return roots;
4263
+ }
4264
+ /** Python:def/class 按缩进栈嵌套。 */
4265
+ function parsePython(text) {
4266
+ const roots = [];
4267
+ const stack = [];
4268
+ const lines = text.split("\n");
4269
+ for (let i = 0; i < lines.length; i++) {
4270
+ const m = /^([ \t]*)(?:class|def)\s+([A-Za-z_]\w*)\s*[(:]/.exec(lines[i]);
4271
+ if (!m) continue;
4272
+ const indent = m[1].replace(/\t/g, " ").length;
4273
+ const isClass = /class\s/.test(lines[i]);
4274
+ const item = {
4275
+ name: m[2],
4276
+ kind: isClass ? SK.Class : SK.Function,
4277
+ startLine: i + 1,
4278
+ endLine: i + 1,
4279
+ selectLine: i + 1,
4280
+ children: []
4281
+ };
4282
+ while (stack.length && stack[stack.length - 1].indent >= indent) stack.pop();
4283
+ const parent = stack.length ? stack[stack.length - 1].item : null;
4284
+ (parent ? parent.children : roots).push(item);
4285
+ stack.push({
4286
+ indent,
4287
+ item
4288
+ });
4289
+ }
4290
+ computeEnds(roots, lines.length);
4291
+ return roots;
4292
+ }
4293
+ /** Shell:`name() {` 函数。 */
4294
+ function parseShell(text) {
4295
+ const out = [];
4296
+ const lines = text.split("\n");
4297
+ for (let i = 0; i < lines.length; i++) {
4298
+ const m = /^[ \t]*([A-Za-z_][A-Za-z0-9_]*)[ \t]*\(\)[ \t]*\{/.exec(lines[i]);
4299
+ if (m) out.push(mk(i, m[1], SK.Function, lines[i]));
4300
+ }
4301
+ computeEnds(out, lines.length);
4302
+ return out;
4303
+ }
4304
+ /** PowerShell:`function Name {` / `filter Name {`。 */
4305
+ function parsePwsh(text) {
4306
+ const out = [];
4307
+ const lines = text.split("\n");
4308
+ for (let i = 0; i < lines.length; i++) {
4309
+ const m = /^[ \t]*(?:function|filter)[ \t]+([\w.-]+)/.exec(lines[i]);
4310
+ if (m) out.push(mk(i, m[1], SK.Function, lines[i]));
4311
+ }
4312
+ computeEnds(out, lines.length);
4313
+ return out;
4314
+ }
4315
+ /** Lua:`function Foo:bar` / `M.bar = class|function`。 */
4316
+ function parseLua(text) {
4317
+ const out = [];
4318
+ const lines = text.split("\n");
4319
+ for (let i = 0; i < lines.length; i++) {
4320
+ const line = lines[i];
4321
+ if (/^\s*--/.test(line)) continue;
4322
+ const fm = /^[ \t]*(?:local[ \t]+)?function[ \t]+([A-Za-z_][\w.:]*)/.exec(line);
4323
+ if (fm) {
4324
+ out.push(mk(i, fm[1], SK.Function, line));
4325
+ continue;
4326
+ }
4327
+ const cm = /^[ \t]*([A-Za-z_][\w.]*)[ \t]*=[ \t]*(?:class|function)\b/.exec(line);
4328
+ if (cm) out.push(mk(i, cm[1], /class\b/.test(line) ? SK.Class : SK.Function, line));
4329
+ }
4330
+ computeEnds(out, lines.length);
4331
+ return out;
4332
+ }
4333
+ /** Go:func(含 receiver)/ type。 */
4334
+ function parseGo(text) {
4335
+ const out = [];
4336
+ const lines = text.split("\n");
4337
+ for (let i = 0; i < lines.length; i++) {
4338
+ const fm = /^[ \t]*func[ \t]+(?:\([^)]*\)[ \t]*)?([A-Za-z_]\w*)/.exec(lines[i]);
4339
+ if (fm) {
4340
+ out.push(mk(i, fm[1], SK.Function, lines[i]));
4341
+ continue;
4342
+ }
4343
+ const tm = /^[ \t]*type[ \t]+([A-Za-z_]\w*)/.exec(lines[i]);
4344
+ if (tm) out.push(mk(i, tm[1], SK.Struct, lines[i]));
4345
+ }
4346
+ computeEnds(out, lines.length);
4347
+ return out;
4348
+ }
4349
+ /** Rust:fn/struct/enum/trait/impl/mod/type。 */
4350
+ function parseRust(text) {
4351
+ const out = [];
4352
+ const lines = text.split("\n");
4353
+ const kindOf = {
4354
+ fn: SK.Function,
4355
+ struct: SK.Struct,
4356
+ enum: SK.Enum,
4357
+ trait: SK.Interface,
4358
+ impl: SK.Module,
4359
+ mod: SK.Module,
4360
+ type: SK.TypeParameter
4361
+ };
4362
+ for (let i = 0; i < lines.length; i++) {
4363
+ const m = /^[ \t]*(?:pub(?:\([^)]*\))?[ \t]+)?(fn|struct|enum|trait|impl|mod|type)[ \t]+([A-Za-z_]\w*)/.exec(lines[i]);
4364
+ if (m && kindOf[m[1]]) out.push(mk(i, m[2], kindOf[m[1]], lines[i]));
4365
+ }
4366
+ computeEnds(out, lines.length);
4367
+ return out;
4368
+ }
4369
+ /** YAML:仅顶层键。 */
4370
+ function parseYaml(text) {
4371
+ const out = [];
4372
+ const lines = text.split("\n");
4373
+ for (let i = 0; i < lines.length; i++) {
4374
+ const line = lines[i];
4375
+ if (!line.trim() || /^\s/.test(line) || /^\s*[#-]/.test(line)) continue;
4376
+ const m = /^([A-Za-z0-9_.\-]+)\s*:/.exec(line);
4377
+ if (m) out.push(mk(i, m[1], SK.Key, line));
4378
+ }
4379
+ computeEnds(out, lines.length);
4380
+ return out;
4381
+ }
4382
+ /** INI/TOML:`[section]` + `key=value`。 */
4383
+ function parseIni(text) {
4384
+ const out = [];
4385
+ const lines = text.split("\n");
4386
+ for (let i = 0; i < lines.length; i++) {
4387
+ const line = lines[i];
4388
+ if (!line.trim() || /^\s*[;#]/.test(line)) continue;
4389
+ const sm = /^\[([^\]]+)\]/.exec(line);
4390
+ if (sm) {
4391
+ out.push(mk(i, sm[1], SK.Namespace, line));
4392
+ continue;
4393
+ }
4394
+ const km = /^([A-Za-z0-9_.\-]+)\s*[=:]/.exec(line.trim());
4395
+ if (km) out.push(mk(i, km[1], SK.Key, line));
4396
+ }
4397
+ computeEnds(out, lines.length);
4398
+ return out;
4399
+ }
4400
+ /** 花括号语言(C 族/JVM/.NET):类型声明 + 泛型函数扫描(扁平,best-effort)。 */
4401
+ function parseBrace(text) {
4402
+ const out = [];
4403
+ const lines = text.split("\n");
4404
+ const typeRe = /^[ \t]*(?:(?:public|private|protected|internal|static|final|abstract|sealed|readonly|export|declare|async|open|extern|virtual|override|const|global|pub|local)\s+)*(class|struct|interface|enum|namespace|module|trait|record)\s+([A-Za-z_]\w*)/;
4405
+ const funcRe = /^[ \t]*(?:[\w<>,*&\[\].:]+\s+)*([A-Za-z_]\w*)\s*(?:<[^>]*>)?\s*\(/;
4406
+ for (let i = 0; i < lines.length; i++) {
4407
+ const line = lines[i];
4408
+ const t = line.trim();
4409
+ if (!t || /^\s*\*/.test(line)) continue;
4410
+ const tm = typeRe.exec(line);
4411
+ if (tm) {
4412
+ out.push(mk(i, tm[2], BRACE_TYPE_KIND[tm[1]] ?? SK.Class, line));
4413
+ continue;
4414
+ }
4415
+ const fm = funcRe.exec(line);
4416
+ if (!fm) continue;
4417
+ if (BRACE_KW.has(fm[1]) || /;\s*$/.test(t)) continue;
4418
+ out.push(mk(i, fm[1], SK.Function, line));
4419
+ }
4420
+ computeEnds(out, lines.length);
4421
+ return out;
4422
+ }
4423
+ /**
4424
+ * 按 Monaco languageId 解析大纲符号(纯函数)。
4425
+ * @author ddj 2026年08月27号
4426
+ * @param languageId Monaco 语言 id
4427
+ * @param text 文件全文
4428
+ * @returns 归一化大纲符号树
4429
+ */
4430
+ function parseOutline(languageId, text) {
4431
+ switch (languageId) {
4432
+ case "markdown":
4433
+ case "mdx": return parseMarkdown(text);
4434
+ case "python": return parsePython(text);
4435
+ case "shell": return parseShell(text);
4436
+ case "powershell": return parsePwsh(text);
4437
+ case "lua": return parseLua(text);
4438
+ case "go": return parseGo(text);
4439
+ case "rust": return parseRust(text);
4440
+ case "yaml": return parseYaml(text);
4441
+ case "ini": return parseIni(text);
4442
+ case "c":
4443
+ case "cpp":
4444
+ case "csharp":
4445
+ case "java":
4446
+ case "php":
4447
+ case "ruby":
4448
+ case "kotlin":
4449
+ case "swift":
4450
+ case "dart": return parseBrace(text);
4451
+ default: return [];
4452
+ }
4453
+ }
4454
+ //#endregion
4455
+ //#region src/client/outline/sources.ts
4456
+ /** 无内置 provider、由 parse.ts 兜底的语言。 */
4457
+ const OUTLINE_FALLBACK_LANGS = /* @__PURE__ */ new Set([
4458
+ "markdown",
4459
+ "mdx",
4460
+ "python",
4461
+ "shell",
4462
+ "powershell",
4463
+ "lua",
4464
+ "go",
4465
+ "rust",
4466
+ "yaml",
4467
+ "ini",
4468
+ "c",
4469
+ "cpp",
4470
+ "csharp",
4471
+ "java",
4472
+ "php",
4473
+ "ruby",
4474
+ "kotlin",
4475
+ "swift",
4476
+ "dart"
4477
+ ]);
4478
+ /**
4479
+ * 创建生命周期独立的大纲源注册表。
4480
+ * @author ddj 2026年08月27号
4481
+ * @returns 大纲源注册表
4482
+ */
4483
+ function createOutlineSourceRegistry() {
4484
+ const entries = /* @__PURE__ */ new Map();
4485
+ const listeners = /* @__PURE__ */ new Set();
4486
+ const notify = () => {
4487
+ for (const listener of listeners) listener();
4488
+ };
4489
+ const list = () => [...entries.values()].sort((a, b) => b.priority - a.priority || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
4490
+ return {
4491
+ register(source) {
4492
+ if (!source || !source.id || typeof source.get !== "function") throw new TypeError("大纲源必须提供 id 与 get");
4493
+ entries.set(source.id, source);
4494
+ notify();
4495
+ return () => {
4496
+ if (entries.get(source.id) !== source) return;
4497
+ entries.delete(source.id);
4498
+ notify();
4499
+ };
4500
+ },
4501
+ list,
4502
+ subscribe(listener) {
4503
+ listeners.add(listener);
4504
+ return () => listeners.delete(listener);
4505
+ },
4506
+ get: (id) => entries.get(id)
4507
+ };
4508
+ }
4509
+ /** 归一化 Monaco OutlineElement(getTopLevelSymbols 产物)→ OutlineSymbol。 */
4510
+ function normalizeMonaco(el) {
4511
+ if (!el || typeof el.name !== "string") return null;
4512
+ const range = el.range || {};
4513
+ const sel = el.selectionRange || range;
4514
+ const startLine = Math.max(1, Number(sel.startLineNumber ?? range.startLineNumber ?? 1));
4515
+ const endLine = Math.max(1, Number(range.endLineNumber ?? startLine));
4516
+ const out = {
4517
+ name: el.name,
4518
+ kind: typeof el.kind === "number" ? el.kind : 0,
4519
+ startLine: Math.max(1, Number(range.startLineNumber ?? startLine)),
4520
+ endLine,
4521
+ selectLine: startLine
4522
+ };
4523
+ if (typeof el.detail === "string" && el.detail) out.detail = el.detail;
4524
+ if (Array.isArray(el.children) && el.children.length) {
4525
+ const kids = el.children.map(normalizeMonaco).filter(Boolean);
4526
+ if (kids.length) out.children = kids;
4527
+ }
4528
+ return out;
4529
+ }
4530
+ /**
4531
+ * 按优先级解析大纲符号:首个非空结果生效(出错源跳过,落入下一优先级)。
4532
+ * @author ddj 2026年08月27号
4533
+ * @param sources 大纲源注册表
4534
+ * @param input 当前快照(languageId/model/editor/monaco)
4535
+ * @returns 归一化符号树(无符号时为空数组)
4536
+ */
4537
+ async function resolveOutline(sources, input) {
4538
+ for (const source of sources.list()) {
4539
+ let supports = false;
4540
+ try {
4541
+ supports = source.provides(input.languageId);
4542
+ } catch {
4543
+ supports = false;
4544
+ }
4545
+ if (!supports) continue;
4546
+ let items = null;
4547
+ try {
4548
+ items = await source.get(input);
4549
+ } catch {
4550
+ items = null;
4551
+ }
4552
+ if (items && items.length) return items;
4553
+ }
4554
+ return [];
4555
+ }
4556
+ /**
4557
+ * 注册内置大纲源(monaco=50 / fallback=30)。
4558
+ * @author ddj 2026年08月27号
4559
+ * @param registry 目标注册表
4560
+ * @returns 注销函数(同时注销全部内置源)
4561
+ */
4562
+ function registerBuiltinOutlineSources(registry) {
4563
+ const disposers = [registry.register({
4564
+ id: "monaco",
4565
+ priority: 50,
4566
+ provides: () => true,
4567
+ async get(input) {
4568
+ const ed = input.editor;
4569
+ const model = input.model;
4570
+ const cmd = ed?._commandService;
4571
+ if (!cmd || typeof cmd.executeCommand !== "function") return [];
4572
+ const uri = model?.uri;
4573
+ if (!uri) return [];
4574
+ const list = await cmd.executeCommand("_executeDocumentSymbolProvider", uri);
4575
+ return Array.isArray(list) ? list.map(normalizeMonaco).filter(Boolean) : [];
4576
+ }
4577
+ }), registry.register({
4578
+ id: "fallback",
4579
+ priority: 30,
4580
+ provides: (languageId) => OUTLINE_FALLBACK_LANGS.has(languageId),
4581
+ async get(input) {
4582
+ const model = input.model;
4583
+ if (!model || typeof model.getValue !== "function") return [];
4584
+ return parseOutline(input.languageId, model.getValue());
4585
+ }
4586
+ })];
4587
+ return () => {
4588
+ for (const dispose of disposers) dispose();
4589
+ };
4590
+ }
4591
+ //#endregion
4592
+ //#region src/client/outline/OutlinePanel.tsx
4593
+ /**
4594
+ * dsh-vscode-mode client — 侧边栏「大纲」面板。
4595
+ * 数据源经 resolveOutline(源注册表,优先级降序、首个非空生效)解析当前活动文件符号:
4596
+ * monaco 源吃原生 document symbol provider,fallback 源兜底无内置提供方的语言。
4597
+ * 交互:点击符号跳转编辑器、▸/▾ 折叠、光标所在符号高亮、空/加载/错误态。
4598
+ * 作者 ddj 2026-08-27
4599
+ */
4600
+ /** 渲染符号数上限(防超大文件卡顿,超出显示截断提示)。 */
4601
+ const RENDER_CAP = 800;
4602
+ /** kind 分组元信息(字形 + 样式类)。 */
4603
+ const KIND_GROUPS = {
4604
+ func: {
4605
+ glyph: "ƒ",
4606
+ cls: "edrv-ol-func"
4607
+ },
4608
+ type: {
4609
+ glyph: "C",
4610
+ cls: "edrv-ol-type"
4611
+ },
4612
+ data: {
4613
+ glyph: "•",
4614
+ cls: "edrv-ol-data"
4615
+ },
4616
+ ns: {
4617
+ glyph: "▤",
4618
+ cls: "edrv-ol-ns"
4619
+ },
4620
+ key: {
4621
+ glyph: "·",
4622
+ cls: "edrv-ol-key"
4623
+ }
4624
+ };
4625
+ /** SymbolKind 数值 → 分组(File0..Package3=ns;Class4/Enum9/Interface10/Struct22/TypeParameter25=type;Method5/Constructor8/Function11=func;Key19/EnumMember21=key)。 */
4626
+ function kindMeta(kind) {
4627
+ const k = kind | 0;
4628
+ if (k <= 3) return KIND_GROUPS.ns;
4629
+ if (k === 4 || k === 9 || k === 10 || k === 22 || k === 25) return KIND_GROUPS.type;
4630
+ if (k === 5 || k === 8 || k === 11) return KIND_GROUPS.func;
4631
+ if (k === 19 || k === 21) return KIND_GROUPS.key;
4632
+ return KIND_GROUPS.data;
4633
+ }
4634
+ /**
4635
+ * 大纲面板主体。
4636
+ * @param props.ctx 面板共享上下文(editor/outlineSources/activePath)
4637
+ */
4638
+ function OutlinePanel(props) {
4639
+ const ctx = props?.ctx;
4640
+ const activePath = ctx?.activePath ?? null;
4641
+ const [symbols, setSymbols] = react.default.useState(null);
4642
+ const [error, setError] = react.default.useState(null);
4643
+ const [collapsed, setCollapsed] = react.default.useState({});
4644
+ const [cursorLine, setCursorLine] = react.default.useState(null);
4645
+ const seqRef = react.default.useRef(0);
4646
+ const ctxRef = react.default.useRef(ctx);
4647
+ ctxRef.current = ctx;
4648
+ const activeRef = react.default.useRef(activePath);
4649
+ activeRef.current = activePath;
4650
+ const sourcesRef = react.default.useRef(ctx?.outlineSources);
4651
+ sourcesRef.current = ctx?.outlineSources;
4652
+ const refresh = react.default.useCallback(() => {
4653
+ const ed = ctxRef.current?.editor?.();
4654
+ const model = ed?.getModel?.();
4655
+ const seq = ++seqRef.current;
4656
+ if (!ed || !model) {
4657
+ setSymbols(null);
4658
+ setError(null);
4659
+ return;
4660
+ }
4661
+ const uriPath = model?.uri?.path;
4662
+ const modelPath = uriPath ? decodeURIComponent(String(uriPath).replace(/^\//, "")) : null;
4663
+ const active = activeRef.current;
4664
+ if (active && modelPath !== active) {
4665
+ setSymbols(null);
4666
+ setError(null);
4667
+ return;
4668
+ }
4669
+ const sources = sourcesRef.current;
4670
+ if (!sources) {
4671
+ setSymbols([]);
4672
+ setError(null);
4673
+ return;
4674
+ }
4675
+ setError(null);
4676
+ resolveOutline(sources, {
4677
+ languageId: model.getLanguageId(),
4678
+ model,
4679
+ editor: ed,
4680
+ monaco: window.monaco
4681
+ }).then((list) => {
4682
+ if (seq === seqRef.current) setSymbols(list || []);
4683
+ }).catch((e) => {
4684
+ if (seq === seqRef.current) {
4685
+ setError(String(e?.message ?? e));
4686
+ setSymbols(null);
4687
+ }
4688
+ });
4689
+ }, []);
4690
+ react.default.useEffect(() => {
4691
+ const disposers = [];
4692
+ let debounceTimer = null;
4693
+ let waitTimer = null;
4694
+ const attach = () => {
4695
+ const ed = ctxRef.current?.editor?.();
4696
+ if (!ed) return false;
4697
+ let contentDisposable = null;
4698
+ const subContent = (model) => {
4699
+ if (contentDisposable) {
4700
+ contentDisposable.dispose();
4701
+ contentDisposable = null;
4702
+ }
4703
+ contentDisposable = model?.onDidChangeContent?.(() => {
4704
+ if (debounceTimer) clearTimeout(debounceTimer);
4705
+ debounceTimer = setTimeout(() => {
4706
+ debounceTimer = null;
4707
+ refresh();
4708
+ }, 300);
4709
+ }) ?? null;
4710
+ };
4711
+ const onModel = () => {
4712
+ subContent(ed.getModel?.());
4713
+ refresh();
4714
+ };
4715
+ const onCursor = (e) => setCursorLine(e?.position?.lineNumber ?? null);
4716
+ subContent(ed.getModel?.());
4717
+ const subs = [
4718
+ ed.onDidChangeModel?.(onModel),
4719
+ ed.onDidChangeCursorPosition?.(onCursor),
4720
+ { dispose: () => {
4721
+ if (contentDisposable) contentDisposable.dispose();
4722
+ } }
4723
+ ];
4724
+ for (const s of subs) if (s) disposers.push(s);
4725
+ refresh();
4726
+ return true;
4727
+ };
4728
+ const onRefresh = () => refresh();
4729
+ window.addEventListener("edrv:refresh", onRefresh);
4730
+ if (!attach()) waitTimer = setInterval(() => {
4731
+ if (attach()) {
4732
+ clearInterval(waitTimer);
4733
+ waitTimer = null;
4734
+ }
4735
+ }, 400);
4736
+ return () => {
4737
+ if (debounceTimer) clearTimeout(debounceTimer);
4738
+ if (waitTimer) clearInterval(waitTimer);
4739
+ for (const d of disposers) if (d?.dispose) d.dispose();
4740
+ window.removeEventListener("edrv:refresh", onRefresh);
4741
+ };
4742
+ }, [refresh]);
4743
+ react.default.useEffect(() => {
4744
+ refresh();
4745
+ }, [refresh, activePath]);
4746
+ const jump = (sym) => {
4747
+ const ed = ctxRef.current?.editor?.();
4748
+ if (!ed) return;
4749
+ const line = Math.max(1, sym?.selectLine ?? sym?.startLine ?? 1);
4750
+ ed.revealLineInCenter(line);
4751
+ ed.setPosition({
4752
+ lineNumber: line,
4753
+ column: 1
4754
+ });
4755
+ ed.focus();
4756
+ };
4757
+ const toggleCollapse = (key) => {
4758
+ setCollapsed((prev) => Object.assign({}, prev, { [key]: prev[key] === true ? false : true }));
4759
+ };
4760
+ const allKeys = react.default.useMemo(() => {
4761
+ const keys = [];
4762
+ const walk = (list, prefix) => {
4763
+ for (let i = 0; i < list.length; i++) {
4764
+ const key = prefix ? prefix + "/" + i : String(i);
4765
+ const sym = list[i];
4766
+ if (sym.children && sym.children.length) {
4767
+ keys.push(key);
4768
+ walk(sym.children, key);
4769
+ }
4770
+ }
4771
+ };
4772
+ walk(symbols || [], "");
4773
+ return keys;
4774
+ }, [symbols]);
4775
+ const setAll = (value) => {
4776
+ const next = {};
4777
+ for (const key of allKeys) next[key] = value;
4778
+ setCollapsed(next);
4779
+ };
4780
+ const renderTree = (list, depth, prefix) => {
4781
+ const rows = [];
4782
+ const cap = Math.min(list.length, RENDER_CAP);
4783
+ for (let i = 0; i < cap; i++) {
4784
+ const sym = list[i];
4785
+ const key = prefix ? prefix + "/" + i : String(i);
4786
+ const kids = sym.children && sym.children.length ? sym.children : null;
4787
+ const isCollapsed = collapsed[key] === true;
4788
+ const meta = kindMeta(sym.kind);
4789
+ const active = cursorLine != null && sym.startLine <= cursorLine && cursorLine <= sym.endLine;
4790
+ rows.push(react.default.createElement("div", {
4791
+ key,
4792
+ className: "edrv-tree-row" + (active ? " edrv-tree-active" : ""),
4793
+ title: sym.detail || sym.name,
4794
+ style: { paddingLeft: 6 + depth * 14 },
4795
+ onClick: () => jump(sym)
4796
+ }, react.default.createElement("span", {
4797
+ className: "edrv-tree-chev",
4798
+ onClick: (e) => {
4799
+ e.stopPropagation();
4800
+ toggleCollapse(key);
4801
+ }
4802
+ }, kids ? isCollapsed ? "▸" : "▾" : ""), react.default.createElement("span", { className: "edrv-outline-kind " + meta.cls }, meta.glyph), react.default.createElement("span", { className: "edrv-tree-name" }, sym.name), sym.detail ? react.default.createElement("span", { className: "edrv-outline-detail" }, sym.detail) : null, react.default.createElement("span", { className: "edrv-outline-ln" }, String(sym.selectLine ?? sym.startLine ?? ""))));
4803
+ if (kids && !isCollapsed) rows.push(...renderTree(kids, depth + 1, key));
4804
+ }
4805
+ if (list.length > RENDER_CAP) rows.push(react.default.createElement("div", {
4806
+ key: "cap",
4807
+ className: "edrv-tree-loading"
4808
+ }, "符号过多,仅显示前 800 个"));
4809
+ return rows;
4810
+ };
4811
+ const basename = activePath ? String(activePath).split(/[\\/]/).pop() || activePath : "";
4812
+ let body;
4813
+ if (error) body = react.default.createElement("div", { className: "edrv-tree" }, react.default.createElement("div", { className: "edrv-tree-error" }, react.default.createElement("span", null, String(error)), react.default.createElement("button", {
4814
+ className: "edrv-side-btn",
4815
+ onClick: refresh
4816
+ }, "重试")));
4817
+ else if (!activePath) body = react.default.createElement("div", { className: "edrv-tree" }, react.default.createElement("div", { className: "edrv-tree-loading" }, "未打开文件"));
4818
+ else if (symbols === null) body = react.default.createElement("div", { className: "edrv-tree" }, react.default.createElement("div", { className: "edrv-tree-loading" }, "加载中…"));
4819
+ else if (symbols.length === 0) body = react.default.createElement("div", { className: "edrv-tree" }, react.default.createElement("div", { className: "edrv-tree-loading" }, "该语言暂不支持大纲或文件无符号"));
4820
+ else body = react.default.createElement("div", { className: "edrv-tree" }, ...renderTree(symbols, 0, ""));
4821
+ return react.default.createElement("div", { className: "edrv-side-panel" }, react.default.createElement("div", { className: "edrv-side-head" }, react.default.createElement("span", { className: "edrv-side-title" }, "大纲"), react.default.createElement("span", {
4822
+ className: "edrv-side-root",
4823
+ title: activePath || ""
4824
+ }, basename), react.default.createElement("span", { style: { flex: 1 } }), react.default.createElement("button", {
4825
+ className: "edrv-side-btn",
4826
+ title: "折叠全部",
4827
+ disabled: !(symbols && symbols.length),
4828
+ onClick: () => setAll(true)
4829
+ }, "−"), react.default.createElement("button", {
4830
+ className: "edrv-side-btn",
4831
+ title: "展开全部",
4832
+ disabled: !(symbols && symbols.length),
4833
+ onClick: () => setAll(false)
4834
+ }, "+"), react.default.createElement("button", {
4835
+ className: "edrv-side-btn",
4836
+ title: "刷新大纲",
4837
+ onClick: refresh
4838
+ }, "⟳")), body);
4839
+ }
4840
+ //#endregion
4841
+ //#region src/client/outline/index.ts
4842
+ /**
4843
+ * dsh-vscode-mode client — 「大纲」面板定义(注册表一项)。
4844
+ * 数据源解析复用 outline/sources 的源注册表(ctx.outlineSources 注入)。
4845
+ * 作者 ddj 2026-08-27
4846
+ */
4847
+ /**
4848
+ * 构造大纲面板定义。
4849
+ * @author ddj 2026年08月27号
4850
+ * @returns 面板定义(无徽标;活动栏图标 📜)
4851
+ */
4852
+ function createOutlinePanel() {
4853
+ return {
4854
+ id: "outline",
4855
+ title: "大纲",
4856
+ icon: "📜",
4857
+ order: 20,
4858
+ render: (ctx) => react.default.createElement(OutlinePanel, { ctx })
4859
+ };
4860
+ }
4861
+ //#endregion
3870
4862
  //#region src/client/index.ts
3871
4863
  /**
3872
4864
  * dsh-vscode-mode client — 浏览器半入口:slot 注册 + 装配。
@@ -3948,6 +4940,13 @@ window.__ModuleLoader__.load({
3948
4940
  const sidebarPanels = createSidebarPanelRegistry();
3949
4941
  ctx.provide("edrvSidebarPanels", sidebarPanels);
3950
4942
  ctx.effect(() => sidebarPanels.register(createFilePanel()), "vscode-mode: sidebar panel");
4943
+ const fileMenuItems = createTreeMenuRegistry();
4944
+ ctx.provide("edrvFileContextMenuItems", fileMenuItems);
4945
+ for (const item of createDefaultFileMenuItems()) ctx.effect(() => fileMenuItems.register(item), "vscode-mode: file context menu item " + item.id);
4946
+ const outlineSources = createOutlineSourceRegistry();
4947
+ ctx.provide("edrvOutlineSources", outlineSources);
4948
+ ctx.effect(() => registerBuiltinOutlineSources(outlineSources), "vscode-mode: outline sources");
4949
+ ctx.effect(() => sidebarPanels.register(createOutlinePanel()), "vscode-mode: sidebar panel outline");
3951
4950
  ctx.effect(() => registry.register({
3952
4951
  id: "system",
3953
4952
  label: "系统默认应用",
@@ -3992,7 +4991,9 @@ window.__ModuleLoader__.load({
3992
4991
  }, (props) => react.default.createElement(EditorView, Object.assign({}, props, {
3993
4992
  schedule,
3994
4993
  addToConversation,
3995
- sidebarPanels
4994
+ sidebarPanels,
4995
+ outlineSources,
4996
+ fileMenuItems
3996
4997
  })));
3997
4998
  registerSlotSafely(ctx, {
3998
4999
  name: "conversation.input.dock",