dsh-vscode-mode 0.1.21 → 0.1.23

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/* 大纲面板: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";
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/* ===== 侧栏形态(betterSidebar 面板内编辑区) ===== */\n[data-edrv-view][data-edrv-layout='side'] { box-sizing: border-box; height: 100%; }\n\n/* ===== 侧边栏引导条(旧页签形态、未装 betterSidebar 时) ===== */\n[data-edrv-view] .edrv-side-hint { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; flex-wrap: wrap; padding: 6px 12px; font-size: 12px; color: var(--dsw-alias-label-secondary, #52606d); background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, #f0f4f4)); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-side-hint-text { white-space: nowrap; }\n[data-edrv-view] .edrv-side-hint-cmd { font-size: 11px; padding: 2px 6px; border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #ffffff); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-side-hint-close { margin-left: auto; border: none; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); cursor: pointer; font-size: 14px; line-height: 1; padding: 2px 6px; border-radius: 6px; flex-shrink: 0; }\n[data-edrv-view] .edrv-side-hint-close:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n\n";
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");
@@ -75,14 +75,251 @@ window.__ModuleLoader__.load({
75
75
  } catch (e) {}
76
76
  }
77
77
  //#endregion
78
+ //#region src/client/sidebarBridge.ts
79
+ /**
80
+ * dsh-vscode-mode client — 侧边栏编辑区桥:可选探测 dsh-better-sidebar 的
81
+ * ctx.betterSidebar 服务,并注册「文件编辑」Tab(单实例)。
82
+ * 可选依赖模式:服务缺失时调用方回退到中央页签形态;本模块不注册任何东西。
83
+ * 纯逻辑(探测/初始打开解析/角标计数)可单测,DOM/事件仅存在于 install 路径。
84
+ * 作者 ddj 2026年08月25号
85
+ */
86
+ /** betterSidebar 服务名(ctx.get 用;不要加进 inject——缺失会让插件停靠等待)。 */
87
+ const BETTER_SIDEBAR_SERVICE = "betterSidebar";
88
+ /** 本插件在侧边栏注册的 Tab 类型 id(亦是 SidebarTab.type)。 */
89
+ const SIDE_TAB_ID = "edrv-editor";
90
+ /** 侧边栏 Tab 标题。 */
91
+ const SIDE_TAB_TITLE = "文件编辑";
92
+ /** 推荐安装命令(提示条/兼容性报告共用)。 */
93
+ const SIDEBAR_INSTALL_CMD = "dsh plugin --profile web add dsh-better-sidebar";
94
+ /**
95
+ * 解析 Tab 挂载时的初始打开请求(创建路径读 tab.path/meta,聚焦路径走窗口事件)。
96
+ * @author ddj 2026年08月25号
97
+ * @param tab 侧边栏 Tab(path/meta 为可选字段)
98
+ * @returns 待打开路径与是否聚焦差异
99
+ */
100
+ function resolveInitialOpen(tab) {
101
+ const meta = tab?.meta ?? {};
102
+ return {
103
+ path: (typeof tab?.path === "string" && tab.path ? tab.path : typeof meta?.openPath === "string" ? meta.openPath : null) || null,
104
+ focusDiff: meta?.focusDiff === true
105
+ };
106
+ }
107
+ /** 会话级待处理差异文件数缓存(Tab 角标同步读取;EditorView 侧栏形态写入)。 */
108
+ const sidePending = /* @__PURE__ */ new Map();
109
+ /**
110
+ * 写入会话的待处理差异文件数(0 视为清除,不显示角标)。
111
+ * @author ddj 2026年08月25号
112
+ * @param sessionId 会话 id
113
+ * @param count 待处理差异文件数
114
+ */
115
+ function setSidePending(sessionId, count) {
116
+ if (!sessionId) return;
117
+ const value = Number.isFinite(count) ? Math.max(0, Math.floor(count)) : 0;
118
+ if (value > 0) sidePending.set(sessionId, value);
119
+ else sidePending.delete(sessionId);
120
+ }
121
+ /**
122
+ * 读取会话的待处理差异文件数(角标用)。
123
+ * @author ddj 2026年08月25号
124
+ * @param sessionId 会话 id
125
+ * @returns 非负计数
126
+ */
127
+ function getSidePending(sessionId) {
128
+ if (!sessionId) return 0;
129
+ return sidePending.get(sessionId) ?? 0;
130
+ }
131
+ /** 模块级侧栏打开路由(events.ts 优先走它;未注册 = 旧页签形态)。 */
132
+ let ensureSideEditor = null;
133
+ /** 待消费的初始打开(Tab 组件尚未挂载时暂存;按会话匹配,挂载时取走)。 */
134
+ let pendingOpen = null;
135
+ /** Tab 组件当前是否有挂载实例(有则直接投递窗口事件,无则暂存待消费)。 */
136
+ let sideEditorMounted = false;
137
+ /**
138
+ * 注册/撤销侧栏打开路由。
139
+ * @author ddj 2026年08月25号
140
+ * @param fn 路由函数或 null
141
+ */
142
+ function setEnsureSideEditor(fn) {
143
+ ensureSideEditor = fn;
144
+ }
145
+ /**
146
+ * 标记侧栏编辑器组件挂载状态(SideEditorTab 挂载/卸载时调用)。
147
+ * @author ddj 2026年08月25号
148
+ * @param mounted 是否已挂载
149
+ */
150
+ function setSideEditorMounted(mounted) {
151
+ sideEditorMounted = mounted;
152
+ }
153
+ /**
154
+ * 暂存指定会话的待消费初始打开(Tab 组件未挂载时由 install 路径调用)。
155
+ * @author ddj 2026年08月25号
156
+ * @param sessionId 会话 id
157
+ * @param path 待打开路径(可空)
158
+ * @param focusDiff 是否聚焦首个差异
159
+ */
160
+ function stagePendingSideOpen(sessionId, path, focusDiff) {
161
+ if (!sessionId) return;
162
+ pendingOpen = {
163
+ sessionId,
164
+ path: path || null,
165
+ focusDiff: focusDiff === true
166
+ };
167
+ }
168
+ /**
169
+ * 取走指定会话的待消费初始打开(SideEditorTab 挂载时读取;不存在返回 null)。
170
+ * @author ddj 2026年08月25号
171
+ * @param sessionId 会话 id
172
+ * @returns 待打开请求或 null
173
+ */
174
+ function takePendingSideOpen(sessionId) {
175
+ if (!pendingOpen || !sessionId || pendingOpen.sessionId !== sessionId) return null;
176
+ const value = {
177
+ path: pendingOpen.path,
178
+ focusDiff: pendingOpen.focusDiff
179
+ };
180
+ pendingOpen = null;
181
+ return value;
182
+ }
183
+ /**
184
+ * 打开/聚焦侧栏编辑器(打开路径可选;返回是否已被侧栏形态接管)。
185
+ * @author ddj 2026年08月25号
186
+ * @param path 待打开路径(可空)
187
+ * @param focusDiff 是否聚焦首个差异
188
+ * @returns 是否路由成功
189
+ */
190
+ function routeSideEditor(path, focusDiff) {
191
+ if (typeof ensureSideEditor !== "function") return false;
192
+ try {
193
+ return ensureSideEditor(path, focusDiff) === true;
194
+ } catch (error) {
195
+ console.warn("[dsh-vscode-mode] 侧栏编辑器打开失败(" + String(error) + "),已回退");
196
+ return false;
197
+ }
198
+ }
199
+ /**
200
+ * 结构化探测 betterSidebar 服务(可选依赖:缺失/降级返回 undefined)。
201
+ * @author ddj 2026年08月25号
202
+ * @param ctx 客户端服务上下文
203
+ * @returns 服务结构面或 undefined
204
+ */
205
+ function detectSidebarService(ctx) {
206
+ try {
207
+ const service = ctx.get(BETTER_SIDEBAR_SERVICE);
208
+ if (!service || typeof service.registerTab !== "function" || typeof service.openTab !== "function") return void 0;
209
+ return service;
210
+ } catch {
211
+ return;
212
+ }
213
+ }
214
+ /**
215
+ * 注册侧边栏「文件编辑」Tab 并接管打开路由。
216
+ * Tab 被用户在 better-sidebar 设置里禁用时,打开路由降级为回退回调(中央页签)。
217
+ * @author ddj 2026年08月25号
218
+ * @param options 装配参数(组件依赖 + 活动会话解析 + 回退注册)
219
+ * @returns 卸载器(反注册 Tab + 撤销路由)
220
+ */
221
+ function installSideEditor(options) {
222
+ const { service, renderTab, activeSession, registerLegacyFallback } = options;
223
+ let fallbackInstalled = false;
224
+ const open = (path, focusDiff) => {
225
+ if (typeof service.isTabEnabled === "function" && !service.isTabEnabled("edrv-editor")) {
226
+ if (!fallbackInstalled) {
227
+ fallbackInstalled = true;
228
+ try {
229
+ registerLegacyFallback();
230
+ } catch (error) {
231
+ console.warn("[dsh-vscode-mode] 回退注册失败(" + String(error) + ")");
232
+ }
233
+ }
234
+ return false;
235
+ }
236
+ const scope = activeSession();
237
+ const sessionId = scope?.sessionId;
238
+ if (!sessionId) return false;
239
+ try {
240
+ service.openTab({
241
+ type: SIDE_TAB_ID,
242
+ title: SIDE_TAB_TITLE,
243
+ ...path ? { path } : {},
244
+ meta: {
245
+ openPath: path || void 0,
246
+ focusDiff
247
+ }
248
+ }, {
249
+ sessionId,
250
+ cwd: scope?.cwd
251
+ });
252
+ } catch (error) {
253
+ console.warn("[dsh-vscode-mode] openTab 失败(" + String(error) + ")");
254
+ return false;
255
+ }
256
+ if (sideEditorMounted) window.dispatchEvent(new CustomEvent("edrv:open-editor", { detail: {
257
+ path,
258
+ focusDiff
259
+ } }));
260
+ else stagePendingSideOpen(sessionId, path, focusDiff);
261
+ return true;
262
+ };
263
+ let tabDisposer = null;
264
+ try {
265
+ tabDisposer = service.registerTab({
266
+ id: SIDE_TAB_ID,
267
+ title: SIDE_TAB_TITLE,
268
+ icon: codeIcon,
269
+ single: true,
270
+ badge: (_ctx, scope) => getSidePending(scope?.sessionId) || null,
271
+ component: renderTab
272
+ });
273
+ } catch (error) {
274
+ console.warn("[dsh-vscode-mode] 侧边栏 Tab 注册失败(" + String(error) + ")");
275
+ if (!fallbackInstalled) {
276
+ fallbackInstalled = true;
277
+ try {
278
+ registerLegacyFallback();
279
+ } catch (inner) {
280
+ console.warn("[dsh-vscode-mode] 回退注册失败(" + String(inner) + ")");
281
+ }
282
+ }
283
+ return () => {};
284
+ }
285
+ setEnsureSideEditor(open);
286
+ return () => {
287
+ if (typeof tabDisposer === "function") tabDisposer();
288
+ setEnsureSideEditor(null);
289
+ };
290
+ }
291
+ /**
292
+ * 侧边栏 Tab 图标(内联 SVG 代码括号,不依赖图标包)。
293
+ * @author ddj 2026年08月25号
294
+ * @param size 边长(px)
295
+ * @returns 图标元素
296
+ */
297
+ function codeIcon(size) {
298
+ return react.default.createElement("svg", {
299
+ width: size,
300
+ height: size,
301
+ viewBox: "0 0 16 16",
302
+ fill: "none",
303
+ "aria-hidden": true,
304
+ focusable: false
305
+ }, react.default.createElement("path", {
306
+ d: "M5.5 4.5 2.5 8l3 3.5M10.5 4.5 13.5 8l-3 3.5",
307
+ stroke: "currentColor",
308
+ strokeWidth: 1.4,
309
+ strokeLinecap: "round",
310
+ strokeLinejoin: "round"
311
+ }));
312
+ }
313
+ //#endregion
78
314
  //#region src/client/events.ts
79
315
  /**
80
316
  * dsh-vscode-mode client — 跨组件/跨 slot 的窗口事件助手。
81
- * 迁移自原 src/client/index.ts 的 openEditorView 与事件派发,语义不改。
317
+ * 迁移自原 src/client/index.ts 的 openEditorView 与事件派发,语义不改;
318
+ * 侧边栏形态(sidebarBridge 已注册路由)优先,否则回退 DOM 页签点击。
82
319
  * 作者 ddj 2026-08-20
83
320
  */
84
321
  /**
85
- * 选择中央「文件编辑」页签(DOM 级,无需 store actions)。
322
+ * 选择中央「文件编辑」页签(DOM 级,无需 store actions)。仅旧页签形态使用。
86
323
  * @author ddj 2026年08月26号
87
324
  */
88
325
  function selectEditorTab() {
@@ -93,11 +330,12 @@ window.__ModuleLoader__.load({
93
330
  }
94
331
  }
95
332
  /**
96
- * 打开中央「文件编辑」页签。
333
+ * 打开「文件编辑」:侧边栏形态路由优先,旧形态回退中央页签。
97
334
  * @author ddj 2026年08月26号
98
335
  * @param path 要打开的路径(可空)
99
336
  */
100
337
  function openEditorView(path) {
338
+ if (routeSideEditor(path ?? null, false)) return;
101
339
  selectEditorTab();
102
340
  const target = path ?? null;
103
341
  setTimeout(() => {
@@ -105,11 +343,12 @@ window.__ModuleLoader__.load({
105
343
  }, 80);
106
344
  }
107
345
  /**
108
- * 打开文件编辑页并聚焦指定文件的首个差异。
346
+ * 打开文件编辑页并聚焦指定文件的首个差异(侧边栏形态自动展开面板)。
109
347
  * @author ddj 2026年08月26号
110
348
  * @param path 待聚焦的差异文件路径
111
349
  */
112
350
  function openDiffView(path) {
351
+ if (routeSideEditor(path, true)) return;
113
352
  selectEditorTab();
114
353
  setTimeout(() => {
115
354
  window.dispatchEvent(new CustomEvent("edrv:open-editor", { detail: {
@@ -1327,6 +1566,41 @@ window.__ModuleLoader__.load({
1327
1566
  return Math.max(0, Math.min(scroll, Number.isFinite(composerTop) ? composerTop : scroll) - root);
1328
1567
  }
1329
1568
  //#endregion
1569
+ //#region src/client/fileReveal.ts
1570
+ /**
1571
+ * dsh-vscode-mode client — 「在文件浏览器中打开」RPC 包装(树菜单与 Monaco 右键共用)。
1572
+ * 归一化为 { ok, error? },不抛异常;无会话/异常降级返回错误文案。
1573
+ * 作者 ddj 2026-08-27
1574
+ */
1575
+ /**
1576
+ * 请求 host 在 OS 文件浏览器中打开/定位路径。
1577
+ * @author ddj 2026年08月27号
1578
+ * @param sessionId 会话 id(可空)
1579
+ * @param path 工作区相对路径('' = 根目录)
1580
+ * @returns 成功或失败原因
1581
+ */
1582
+ async function revealInExplorer(sessionId, path) {
1583
+ if (!sessionId) return {
1584
+ ok: false,
1585
+ error: "无活动会话"
1586
+ };
1587
+ try {
1588
+ const res = await rpc("edrv.revealInExplorer", {
1589
+ sessionId,
1590
+ path
1591
+ });
1592
+ return res.ok ? { ok: true } : {
1593
+ ok: false,
1594
+ error: res.error
1595
+ };
1596
+ } catch (error) {
1597
+ return {
1598
+ ok: false,
1599
+ error: "打开异常:" + String(error)
1600
+ };
1601
+ }
1602
+ }
1603
+ //#endregion
1330
1604
  //#region src/client/ui/EditorView.ts
1331
1605
  /**
1332
1606
  * dsh-vscode-mode client — EditorView:中央 VSCode 式文件编辑器(编排层)。
@@ -1339,11 +1613,15 @@ window.__ModuleLoader__.load({
1339
1613
  * 底部差异条(DiffBox/空态)+ 全局差异下拉(DiffLauncher)+ 状态栏。
1340
1614
  * @param props.sessionId 会话 id
1341
1615
  * @param props.schedule 延时调度(ctx.timeout)
1616
+ * @param props.layout 布局形态:'tab'(中央页签,默认)| 'side'(侧边栏面板)
1617
+ * @param props.sideHint 旧页签形态下显示侧边栏引导(安装命令)
1342
1618
  */
1343
1619
  function EditorView(props) {
1344
1620
  const sessionId = props?.sessionId;
1345
1621
  const schedule = props.schedule;
1346
1622
  const addToConversation = props.addToConversation;
1623
+ const layout = props?.layout === "side" ? "side" : "tab";
1624
+ const sideHint = props?.sideHint;
1347
1625
  const [monaco, setMonaco] = react.default.useState(null);
1348
1626
  const [monacoErr, setMonacoErr] = react.default.useState(null);
1349
1627
  const [records, setRecords] = react.default.useState({});
@@ -1368,10 +1646,17 @@ window.__ModuleLoader__.load({
1368
1646
  const [diffIdx, setDiffIdx] = react.default.useState(0);
1369
1647
  const [fileIdx, setFileIdx] = react.default.useState(0);
1370
1648
  const [tabMenu, setTabMenu] = react.default.useState(null);
1371
- const [sidebarOn, setSidebarOn] = react.default.useState(true);
1649
+ const [sidebarOn, setSidebarOn] = react.default.useState(layout !== "side");
1372
1650
  const [sidebarW, setSidebarW] = react.default.useState(240);
1373
1651
  const [activePanel, setActivePanel] = react.default.useState("explorer");
1374
1652
  const [focusRequest, setFocusRequest] = react.default.useState(0);
1653
+ const [hintDismissed, setHintDismissed] = react.default.useState(() => {
1654
+ try {
1655
+ return localStorage.getItem("edrv.side-hint-dismissed") === "1";
1656
+ } catch {
1657
+ return false;
1658
+ }
1659
+ });
1375
1660
  const editorRef = react.default.useRef(null);
1376
1661
  const viewRootRef = react.default.useRef(null);
1377
1662
  const monacoRef = react.default.useRef(null);
@@ -1392,6 +1677,8 @@ window.__ModuleLoader__.load({
1392
1677
  const batchBusyRef = react.default.useRef(false);
1393
1678
  const menuHandlersRef = react.default.useRef(null);
1394
1679
  const diffRendererRef = react.default.useRef(null);
1680
+ const layoutRef = react.default.useRef(layout);
1681
+ layoutRef.current = layout;
1395
1682
  if (!diffRendererRef.current) diffRendererRef.current = createDiffRenderer((sid, t) => dbg(sid, t));
1396
1683
  const currentRecords = react.default.useMemo(() => {
1397
1684
  const list = [];
@@ -1521,8 +1808,12 @@ window.__ModuleLoader__.load({
1521
1808
  };
1522
1809
  }, [sessionId]);
1523
1810
  const dockSourceRef = react.default.useRef({});
1524
- react.default.useEffect(() => () => clearDiffDock(sessionId, dockSourceRef.current), [sessionId]);
1811
+ react.default.useEffect(() => () => {
1812
+ clearDiffDock(sessionId, dockSourceRef.current);
1813
+ if (layout === "side") setSidePending(sessionId, 0);
1814
+ }, [sessionId]);
1525
1815
  react.default.useLayoutEffect(() => {
1816
+ if (layout === "side") return;
1526
1817
  const root = viewRootRef.current;
1527
1818
  const scroll = root?.closest?.("[data-conversation-scroll]");
1528
1819
  if (!root || !scroll) return;
@@ -1571,7 +1862,7 @@ window.__ModuleLoader__.load({
1571
1862
  window.removeEventListener("resize", scheduleUpdate);
1572
1863
  root.style.removeProperty("--edrv-editor-height");
1573
1864
  };
1574
- }, [sessionId]);
1865
+ }, [sessionId, layout]);
1575
1866
  react.default.useEffect(() => {
1576
1867
  if (bootRef.current || !sessionId) return;
1577
1868
  bootRef.current = true;
@@ -1599,10 +1890,11 @@ window.__ModuleLoader__.load({
1599
1890
  active,
1600
1891
  sessionId
1601
1892
  ]);
1893
+ const sidebarKey = "edrv.sidebar." + (layout === "side" ? "side." : "") + String(sessionId);
1602
1894
  react.default.useEffect(() => {
1603
1895
  if (!sessionId) return;
1604
1896
  try {
1605
- const raw = localStorage.getItem("edrv.sidebar." + String(sessionId));
1897
+ const raw = localStorage.getItem(sidebarKey);
1606
1898
  if (raw) {
1607
1899
  const saved = JSON.parse(raw);
1608
1900
  if (typeof saved.on === "boolean") setSidebarOn(saved.on);
@@ -1610,11 +1902,11 @@ window.__ModuleLoader__.load({
1610
1902
  if (typeof saved.panel === "string") setActivePanel(saved.panel);
1611
1903
  }
1612
1904
  } catch (e) {}
1613
- }, [sessionId]);
1905
+ }, [sessionId, sidebarKey]);
1614
1906
  react.default.useEffect(() => {
1615
1907
  if (!sessionId) return;
1616
1908
  try {
1617
- localStorage.setItem("edrv.sidebar." + String(sessionId), JSON.stringify({
1909
+ localStorage.setItem(sidebarKey, JSON.stringify({
1618
1910
  on: sidebarOn,
1619
1911
  width: sidebarW,
1620
1912
  panel: activePanel
@@ -1624,7 +1916,8 @@ window.__ModuleLoader__.load({
1624
1916
  sidebarOn,
1625
1917
  sidebarW,
1626
1918
  activePanel,
1627
- sessionId
1919
+ sessionId,
1920
+ sidebarKey
1628
1921
  ]);
1629
1922
  react.default.useEffect(() => {
1630
1923
  const onKey = (e) => {
@@ -1790,6 +2083,7 @@ window.__ModuleLoader__.load({
1790
2083
  }
1791
2084
  if (editorRef.current || !monacoRef.current) return;
1792
2085
  const m = monacoRef.current;
2086
+ const side = layoutRef.current === "side";
1793
2087
  const ed = m.editor.create(node, {
1794
2088
  value: "",
1795
2089
  language: "plaintext",
@@ -1798,7 +2092,7 @@ window.__ModuleLoader__.load({
1798
2092
  fontSize: 13,
1799
2093
  lineHeight: 20,
1800
2094
  minimap: {
1801
- enabled: true,
2095
+ enabled: !side,
1802
2096
  scale: 1
1803
2097
  },
1804
2098
  glyphMargin: true,
@@ -1810,7 +2104,7 @@ window.__ModuleLoader__.load({
1810
2104
  renderWhitespace: "selection",
1811
2105
  smoothScrolling: true,
1812
2106
  cursorBlinking: "smooth",
1813
- padding: { top: 8 }
2107
+ padding: { top: side ? 6 : 8 }
1814
2108
  });
1815
2109
  ed.addCommand(m.KeyMod.CtrlCmd | m.KeyCode.KeyS, () => {
1816
2110
  flushSave();
@@ -1863,6 +2157,13 @@ window.__ModuleLoader__.load({
1863
2157
  });
1864
2158
  }
1865
2159
  });
2160
+ ed.addAction({
2161
+ id: "edrv.revealInExplorer",
2162
+ label: "在文件浏览器中打开",
2163
+ contextMenuGroupId: "1_edrv",
2164
+ precondition: "editorTextFocus",
2165
+ run: (edx) => menuHandlers()?.openInExplorer(pathOf(edx))
2166
+ });
1866
2167
  const hideSoon = () => {
1867
2168
  if (hoverEditorRef.current || hoverPanelRef.current || hideTimerRef.current) return;
1868
2169
  hideTimerRef.current = setTimeout(() => {
@@ -2167,7 +2468,30 @@ window.__ModuleLoader__.load({
2167
2468
  }
2168
2469
  addToConversation.appendReference(sessionId, path, range).then((o) => setStatus(statusOfAdd(o, "已添加文件引用")));
2169
2470
  };
2170
- menuHandlersRef.current = { addRefToChat };
2471
+ /**
2472
+ * 在 OS 文件浏览器中打开/定位路径(Monaco 右键菜单用,状态栏反馈)。
2473
+ * @author ddj 2026年08月27号
2474
+ * @param path 工作区相对路径(可能为 null)
2475
+ */
2476
+ const openInExplorer = (path) => {
2477
+ if (!path) {
2478
+ setStatus("无活动文件");
2479
+ return;
2480
+ }
2481
+ if (!sessionId) {
2482
+ setStatus("无活动会话");
2483
+ return;
2484
+ }
2485
+ setStatus("正在打开文件浏览器…");
2486
+ revealInExplorer(sessionId, path).then((outcome) => {
2487
+ setStatus(outcome.ok ? "已在文件浏览器中打开" : "打开失败");
2488
+ if (!outcome.ok && outcome.error) setError(outcome.error);
2489
+ });
2490
+ };
2491
+ menuHandlersRef.current = {
2492
+ addRefToChat,
2493
+ openInExplorer
2494
+ };
2171
2495
  const openFile = (path, focusDiff) => {
2172
2496
  if (!path) return;
2173
2497
  addTab(path, true);
@@ -2369,39 +2693,42 @@ window.__ModuleLoader__.load({
2369
2693
  actHunk(hoverAct.region, true);
2370
2694
  }
2371
2695
  }, "↩ Undo")) : null;
2696
+ /** 组装差异 dock 快照(发布给 conversation.input.dock 的唯一 DiffBox 实例)。 */
2697
+ const buildDockSnapshot = () => ({
2698
+ mode: editorDockMode(active),
2699
+ pendingRegions,
2700
+ staleRegions,
2701
+ onAct: actHunk,
2702
+ onAcceptFile: acceptFile,
2703
+ onUndoFile: undoFile,
2704
+ onAcceptAllFiles: acceptAllFiles,
2705
+ onUndoAllFiles: undoAllFiles,
2706
+ allPendingCount: allPending.length,
2707
+ onRollback: rollbackFile,
2708
+ onJump: jumpTo,
2709
+ otherFiles,
2710
+ onOpenOther: (path) => openFile(path, true),
2711
+ onOpenLauncher: (tab) => {
2712
+ setLauncherTab(tab || "pending");
2713
+ setLauncherOpen(true);
2714
+ },
2715
+ onRefresh: reloadFile,
2716
+ activePath: active,
2717
+ diffIdx: contentReady ? diffIdx : 0,
2718
+ diffTotal: displayDiffTotal(contentReady, pendingRegions.length, sum.files.find((file) => file.path === active)?.pending ?? 0),
2719
+ fileIdx,
2720
+ fileTotal: sum.pendingFiles.length,
2721
+ onPrevDiff: () => gotoDiff(-1),
2722
+ onNextDiff: () => gotoDiff(1),
2723
+ onPrevFile: () => gotoFile(-1),
2724
+ onNextFile: () => gotoFile(1),
2725
+ onOpenNextFile: openNextFile
2726
+ });
2372
2727
  react.default.useEffect(() => {
2373
2728
  if (!sessionId) return;
2374
- const mode = editorDockMode(active);
2375
- publishDiffDock(sessionId, {
2376
- mode,
2377
- pendingRegions,
2378
- staleRegions,
2379
- onAct: actHunk,
2380
- onAcceptFile: acceptFile,
2381
- onUndoFile: undoFile,
2382
- onAcceptAllFiles: acceptAllFiles,
2383
- onUndoAllFiles: undoAllFiles,
2384
- allPendingCount: allPending.length,
2385
- onRollback: rollbackFile,
2386
- onJump: jumpTo,
2387
- otherFiles,
2388
- onOpenOther: (path) => openFile(path, true),
2389
- onOpenLauncher: (tab) => {
2390
- setLauncherTab(tab || "pending");
2391
- setLauncherOpen(true);
2392
- },
2393
- onRefresh: reloadFile,
2394
- activePath: active,
2395
- diffIdx: contentReady ? diffIdx : 0,
2396
- diffTotal: displayDiffTotal(contentReady, pendingRegions.length, sum.files.find((file) => file.path === active)?.pending ?? 0),
2397
- fileIdx,
2398
- fileTotal: sum.pendingFiles.length,
2399
- onPrevDiff: () => gotoDiff(-1),
2400
- onNextDiff: () => gotoDiff(1),
2401
- onPrevFile: () => gotoFile(-1),
2402
- onNextFile: () => gotoFile(1),
2403
- onOpenNextFile: openNextFile
2404
- }, dockSourceRef.current);
2729
+ publishDiffDock(sessionId, buildDockSnapshot(), dockSourceRef.current);
2730
+ if (layout === "side") setSidePending(sessionId, sum.totalFiles);
2731
+ else setSidePending(sessionId, 0);
2405
2732
  });
2406
2733
  const overlay = launcherOpen ? react.default.createElement(react.default.Fragment, null, react.default.createElement("div", {
2407
2734
  style: {
@@ -2468,9 +2795,30 @@ window.__ModuleLoader__.load({
2468
2795
  },
2469
2796
  refreshRecords: () => refreshRecords(),
2470
2797
  editor: () => editorRef.current,
2471
- outlineSources: props.outlineSources
2798
+ outlineSources: props.outlineSources,
2799
+ fileMenuItems: props.fileMenuItems,
2800
+ notify: (message) => setStatus(message)
2472
2801
  };
2473
2802
  const editorArea = react.default.createElement("div", { className: "edrv-editor-area" }, body, hoverEl, overlay);
2803
+ const dismissHint = () => {
2804
+ setHintDismissed(true);
2805
+ try {
2806
+ localStorage.setItem("edrv.side-hint-dismissed", "1");
2807
+ } catch (e) {}
2808
+ };
2809
+ const copyInstallCmd = () => {
2810
+ if (navigator.clipboard?.writeText) navigator.clipboard.writeText(SIDEBAR_INSTALL_CMD).catch(() => {});
2811
+ };
2812
+ const sideHintEl = layout === "tab" && sideHint && !hintDismissed ? react.default.createElement("div", { className: "edrv-side-hint" }, react.default.createElement("span", { className: "edrv-side-hint-text" }, "安装 dsh-better-sidebar 后可启用侧边栏编辑(对话+编辑同屏):"), react.default.createElement("code", { className: "edrv-side-hint-cmd" }, SIDEBAR_INSTALL_CMD), react.default.createElement("button", {
2813
+ className: "edrv-pill edrv-pill-ghost",
2814
+ title: "复制安装命令",
2815
+ onClick: copyInstallCmd
2816
+ }, "复制命令"), react.default.createElement("button", {
2817
+ className: "edrv-side-hint-close",
2818
+ title: "关闭提示",
2819
+ "aria-label": "关闭提示",
2820
+ onClick: dismissHint
2821
+ }, "×")) : null;
2474
2822
  const mainCol = react.default.createElement("div", {
2475
2823
  className: "edrv-main-col",
2476
2824
  style: {
@@ -2481,7 +2829,7 @@ window.__ModuleLoader__.load({
2481
2829
  flexDirection: "column",
2482
2830
  overflow: "hidden"
2483
2831
  }
2484
- }, pathBar, tabRow, editorArea);
2832
+ }, pathBar, tabRow, sideHintEl, editorArea);
2485
2833
  const editorRow = react.default.createElement("div", { className: "edrv-editor-row" }, sidebarOn && sidebarPanels ? react.default.createElement(SidebarView, {
2486
2834
  registry: sidebarPanels,
2487
2835
  ctx: sidebarCtx,
@@ -2491,18 +2839,26 @@ window.__ModuleLoader__.load({
2491
2839
  onWidth: setSidebarW,
2492
2840
  onHide: () => setSidebarOn(false)
2493
2841
  }) : null, mainCol);
2494
- return react.default.createElement("div", {
2842
+ const baseStyle = {
2843
+ minHeight: 0,
2844
+ display: "flex",
2845
+ flexDirection: "column",
2846
+ background: "var(--dsw-alias-bg-base,transparent)",
2847
+ overflow: "hidden"
2848
+ };
2849
+ return layout === "side" ? react.default.createElement("div", {
2495
2850
  ref: viewRootRef,
2496
2851
  "data-edrv-view": "1",
2497
- style: {
2852
+ "data-edrv-layout": "side",
2853
+ className: "edrv-view-side",
2854
+ style: Object.assign({}, baseStyle, { height: "100%" })
2855
+ }, editorRow, menuBackdrop, tabMenuEl) : react.default.createElement("div", {
2856
+ ref: viewRootRef,
2857
+ "data-edrv-view": "1",
2858
+ style: Object.assign({}, baseStyle, {
2498
2859
  height: "var(--edrv-editor-height, 100%)",
2499
- maxHeight: "var(--edrv-editor-height, 100%)",
2500
- minHeight: 0,
2501
- display: "flex",
2502
- flexDirection: "column",
2503
- background: "var(--dsw-alias-bg-base,transparent)",
2504
- overflow: "hidden"
2505
- }
2860
+ maxHeight: "var(--edrv-editor-height, 100%)"
2861
+ })
2506
2862
  }, editorRow, menuBackdrop, tabMenuEl);
2507
2863
  }
2508
2864
  //#endregion
@@ -2517,14 +2873,14 @@ window.__ModuleLoader__.load({
2517
2873
  */
2518
2874
  function DiffBadge(props) {
2519
2875
  const sessionId = props?.sessionId;
2520
- const [n, setN] = react.default.useState(0);
2876
+ const [summary, setSummary] = react.default.useState(null);
2521
2877
  const seq = react.default.useRef(0);
2522
2878
  const load = react.default.useCallback(() => {
2523
2879
  if (!sessionId) return;
2524
2880
  const s = ++seq.current;
2525
2881
  rpc("edrv.list", { sessionId }).then((res) => {
2526
2882
  if (s !== seq.current || !res || !res.ok || !Array.isArray(res.records)) return;
2527
- setN(summarize(res.records).totalFiles);
2883
+ setSummary(summarize(res.records));
2528
2884
  }).catch(() => {});
2529
2885
  }, [sessionId]);
2530
2886
  react.default.useEffect(() => {
@@ -2537,9 +2893,10 @@ window.__ModuleLoader__.load({
2537
2893
  window.removeEventListener("edrv:refresh", onRefresh);
2538
2894
  };
2539
2895
  }, [load]);
2896
+ const n = summary?.totalFiles ?? 0;
2540
2897
  if (!n) return null;
2541
2898
  const click = () => {
2542
- openEditorView(null);
2899
+ openEditorView(summary?.pendingFiles?.[0]?.path ?? null);
2543
2900
  setTimeout(() => {
2544
2901
  emitShowLauncher();
2545
2902
  }, 120);
@@ -3530,6 +3887,51 @@ window.__ModuleLoader__.load({
3530
3887
  return typeof value === "string" && value.trim() ? value.trim() : AUTO_OPEN_TOOL;
3531
3888
  }
3532
3889
  //#endregion
3890
+ //#region src/client/ui/SideEditorTab.ts
3891
+ /**
3892
+ * dsh-vscode-mode client — SideEditorTab:betterSidebar「文件编辑」Tab 的组件包装。
3893
+ * 把 TabComponentProps(scope/tab/visible)映射为 EditorView 侧栏形态 props,
3894
+ * 并按会话 key 保证切会话状态干净重建;创建种子的初始打开在挂载后投递一次。
3895
+ * 作者 ddj 2026年08月25号
3896
+ */
3897
+ /**
3898
+ * 侧边栏 Tab 组件(TabDescriptor.component 装配)。
3899
+ * @param props TabComponentProps(scope.sessionId、tab.path/meta 等)
3900
+ * @returns EditorView 侧栏形态
3901
+ */
3902
+ function SideEditorTab(props) {
3903
+ const sessionId = props?.scope?.sessionId;
3904
+ const initial = react.default.useMemo(() => resolveInitialOpen(props?.tab), [props?.tab]);
3905
+ const dispatchedRef = react.default.useRef(false);
3906
+ react.default.useEffect(() => {
3907
+ setSideEditorMounted(true);
3908
+ return () => setSideEditorMounted(false);
3909
+ }, []);
3910
+ react.default.useEffect(() => {
3911
+ if (dispatchedRef.current) return;
3912
+ dispatchedRef.current = true;
3913
+ const request = takePendingSideOpen(sessionId) ?? initial;
3914
+ window.dispatchEvent(new CustomEvent("edrv:open-editor", { detail: {
3915
+ path: request?.path ?? null,
3916
+ focusDiff: request?.focusDiff === true
3917
+ } }));
3918
+ }, [
3919
+ initial.path,
3920
+ initial.focusDiff,
3921
+ sessionId
3922
+ ]);
3923
+ return react.default.createElement(EditorView, {
3924
+ key: sessionId,
3925
+ sessionId,
3926
+ layout: "side",
3927
+ schedule: props?.schedule,
3928
+ addToConversation: props?.addToConversation,
3929
+ sidebarPanels: props?.sidebarPanels,
3930
+ outlineSources: props?.outlineSources,
3931
+ fileMenuItems: props?.fileMenuItems
3932
+ });
3933
+ }
3934
+ //#endregion
3533
3935
  //#region src/client/addToConversation.ts
3534
3936
  /** DSH reference source 名(dsh-client-ui-reference 注册的 @file/@session 统一源)。 */
3535
3937
  const REF_SOURCE = "reference";
@@ -3708,17 +4110,144 @@ window.__ModuleLoader__.load({
3708
4110
  };
3709
4111
  }
3710
4112
  //#endregion
4113
+ //#region src/client/ui/ContextMenu.ts
4114
+ /**
4115
+ * dsh-vscode-mode client — 通用浮动右键菜单(createElement 风格、类型化)。
4116
+ * 全屏 backdrop(点击/右键/Esc 关闭)+ 固定定位(viewport clamp 防越界),
4117
+ * 条目支持 danger/disabled/separator,复用既有 edrv-ctxmenu* 样式类。
4118
+ * 作者 ddj 2026-08-27
4119
+ */
4120
+ /** 菜单估算宽高(clamp 防越出视口,与 EditorView menuPos 常量对齐)。 */
4121
+ const MENU_W = 224;
4122
+ const MENU_H = 176;
4123
+ /**
4124
+ * 浮动右键菜单。
4125
+ * @param props.x 视口 x 坐标
4126
+ * @param props.y 视口 y 坐标
4127
+ * @param props.entries 菜单项列表
4128
+ * @param props.onClose 关闭回调(backdrop 点击/右键/Esc/点击项后触发)
4129
+ */
4130
+ function ContextMenu(props) {
4131
+ const { x, y, entries, onClose } = props;
4132
+ react.default.useEffect(() => {
4133
+ const onKey = (e) => {
4134
+ if (e.key === "Escape") onClose();
4135
+ };
4136
+ window.addEventListener("keydown", onKey, true);
4137
+ return () => window.removeEventListener("keydown", onKey, true);
4138
+ }, [onClose]);
4139
+ const left = Math.max(4, Math.min(x, (window.innerWidth || 800) - MENU_W));
4140
+ const top = Math.max(4, Math.min(y, (window.innerHeight || 600) - MENU_H));
4141
+ const children = [];
4142
+ for (const entry of entries) {
4143
+ if (entry.separator) {
4144
+ children.push(react.default.createElement("div", {
4145
+ key: "sep-" + entry.id,
4146
+ className: "edrv-ctxmenu-sep"
4147
+ }));
4148
+ continue;
4149
+ }
4150
+ const cls = "edrv-ctxmenu-item" + (entry.danger ? " edrv-ctxmenu-danger" : "") + (entry.disabled ? " edrv-ctxmenu-disabled" : "");
4151
+ children.push(react.default.createElement("button", {
4152
+ key: entry.id,
4153
+ className: cls,
4154
+ disabled: entry.disabled,
4155
+ onClick: () => {
4156
+ if (!entry.disabled) {
4157
+ entry.onClick?.();
4158
+ onClose();
4159
+ }
4160
+ }
4161
+ }, entry.label));
4162
+ }
4163
+ return react.default.createElement(react.default.Fragment, null, react.default.createElement("div", {
4164
+ className: "edrv-ctxmenu-backdrop",
4165
+ style: {
4166
+ position: "fixed",
4167
+ inset: 0,
4168
+ zIndex: 70
4169
+ },
4170
+ onClick: onClose,
4171
+ onContextMenu: (e) => {
4172
+ e.preventDefault();
4173
+ onClose();
4174
+ }
4175
+ }), react.default.createElement("div", {
4176
+ className: "edrv-ctxmenu",
4177
+ style: {
4178
+ left,
4179
+ top
4180
+ }
4181
+ }, ...children));
4182
+ }
4183
+ //#endregion
4184
+ //#region src/client/sidebar/contextMenu.ts
4185
+ /** 校验菜单项并写入注册表(缺 id/run/label 抛 TypeError)。 */
4186
+ function itemRegister(entries, notify, item) {
4187
+ if (!item.id || typeof item.run !== "function" || !item.label) throw new TypeError("文件右键菜单项必须提供 id、label 和 run");
4188
+ entries.set(item.id, item);
4189
+ notify();
4190
+ }
4191
+ /**
4192
+ * 创建生命周期独立的右键菜单项注册表。
4193
+ * @author ddj 2026年08月27号
4194
+ * @returns 菜单项注册表
4195
+ */
4196
+ function createTreeMenuRegistry() {
4197
+ const entries = /* @__PURE__ */ new Map();
4198
+ const listeners = /* @__PURE__ */ new Set();
4199
+ const notify = () => {
4200
+ for (const listener of listeners) listener();
4201
+ };
4202
+ const list = () => [...entries.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
4203
+ return {
4204
+ register(item) {
4205
+ itemRegister(entries, notify, item);
4206
+ return () => {
4207
+ if (entries.get(item.id) !== item) return;
4208
+ entries.delete(item.id);
4209
+ notify();
4210
+ };
4211
+ },
4212
+ list,
4213
+ subscribe(listener) {
4214
+ listeners.add(listener);
4215
+ return () => listeners.delete(listener);
4216
+ },
4217
+ get: (id) => entries.get(id)
4218
+ };
4219
+ }
4220
+ /**
4221
+ * 按目标构建当前可见菜单项(visible 过滤 + order 排序)。
4222
+ * @author ddj 2026年08月27号
4223
+ * @param registry 菜单项注册表
4224
+ * @param target 右键目标
4225
+ * @param ctx 面板共享上下文
4226
+ * @returns 可见菜单项(已排序)
4227
+ */
4228
+ function buildTreeMenu(registry, target, ctx) {
4229
+ if (!registry) return [];
4230
+ const out = [];
4231
+ for (const item of registry.list()) {
4232
+ if (typeof item.visible === "function" && !item.visible(target, ctx)) continue;
4233
+ out.push(item);
4234
+ }
4235
+ return out;
4236
+ }
4237
+ //#endregion
3711
4238
  //#region src/client/sidebar/panels/FileExplorer.ts
3712
4239
  /**
3713
4240
  * dsh-vscode-mode client — 侧边栏「文件管理」面板(懒加载目录树)。
3714
4241
  * 点目录展开/收起(首次经 edrv.listDir 拉取并缓存),点文件经 ctx.openFile 打开;
3715
4242
  * 差异角标取 pendingByPath,活动文件高亮;edrv:refresh 事件与手动刷新重载树。
3716
- * 作者 ddj 2026-08-26
4243
+ * 右键行/空白弹出菜单:项来自 ctx.fileMenuItems 注册表(可拓展),
4244
+ * 内置「在文件浏览器中打开」经 edrv.revealInExplorer 定位/打开。
4245
+ * 作者 ddj 2026-08-26 / 2026-08-27
3717
4246
  */
3718
4247
  const DIR_CAP = 4e3;
3719
4248
  /**
3720
4249
  * 目录树面板主体。
3721
- * @param props.ctx 面板共享上下文(sessionId/openFile/activePath/pendingByPath)
4250
+ * @param props.ctx 面板共享上下文(sessionId/openFile/activePath/pendingByPath/fileMenuItems/notify)
3722
4251
  */
3723
4252
  function FileExplorer(props) {
3724
4253
  const ctx = props?.ctx;
@@ -3731,6 +4260,7 @@ window.__ModuleLoader__.load({
3731
4260
  const [expanded, setExpanded] = react.default.useState({});
3732
4261
  const [loading, setLoading] = react.default.useState({});
3733
4262
  const [error, setError] = react.default.useState(null);
4263
+ const [menu, setMenu] = react.default.useState(null);
3734
4264
  const tokensRef = react.default.useRef({});
3735
4265
  const expandedRef = react.default.useRef({});
3736
4266
  expandedRef.current = expanded;
@@ -3809,6 +4339,18 @@ window.__ModuleLoader__.load({
3809
4339
  onClick: () => {
3810
4340
  if (isDir) toggle(e.path);
3811
4341
  else openFile(e.path);
4342
+ },
4343
+ onContextMenu: (ev) => {
4344
+ ev.preventDefault();
4345
+ ev.stopPropagation();
4346
+ setMenu({
4347
+ x: ev.clientX,
4348
+ y: ev.clientY,
4349
+ target: {
4350
+ path: e.path,
4351
+ type: e.type
4352
+ }
4353
+ });
3812
4354
  }
3813
4355
  }, 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);
3814
4356
  };
@@ -3835,7 +4377,29 @@ window.__ModuleLoader__.load({
3835
4377
  }, "目录条目过多,仅显示前 4000 项"));
3836
4378
  return rows;
3837
4379
  };
3838
- 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", {
4380
+ const onPanelContext = (ev) => {
4381
+ ev.preventDefault();
4382
+ setMenu({
4383
+ x: ev.clientX,
4384
+ y: ev.clientY,
4385
+ target: {
4386
+ path: "",
4387
+ type: "directory"
4388
+ }
4389
+ });
4390
+ };
4391
+ const menuEntries = menu ? buildTreeMenu(ctx?.fileMenuItems, menu.target, ctx).map((item) => ({
4392
+ id: item.id,
4393
+ label: item.label,
4394
+ danger: item.danger,
4395
+ disabled: item.disabled,
4396
+ separator: item.separator,
4397
+ onClick: () => item.run(menu.target, ctx)
4398
+ })) : [];
4399
+ return react.default.createElement("div", {
4400
+ className: "edrv-side-panel",
4401
+ onContextMenu: onPanelContext
4402
+ }, react.default.createElement("div", { className: "edrv-side-head" }, react.default.createElement("span", { className: "edrv-side-title" }, "资源管理器"), react.default.createElement("span", {
3839
4403
  className: "edrv-side-root",
3840
4404
  title: root || ""
3841
4405
  }, rootName), react.default.createElement("span", { style: { flex: 1 } }), react.default.createElement("button", {
@@ -3845,7 +4409,12 @@ window.__ModuleLoader__.load({
3845
4409
  }, "⟳")), 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", {
3846
4410
  className: "edrv-side-btn",
3847
4411
  onClick: () => refresh()
3848
- }, "重试")) : null, rowsOf("", 0)));
4412
+ }, "重试")) : null, rowsOf("", 0)), menu && menuEntries.length ? react.default.createElement(ContextMenu, {
4413
+ x: menu.x,
4414
+ y: menu.y,
4415
+ entries: menuEntries,
4416
+ onClose: () => setMenu(null)
4417
+ }) : null);
3849
4418
  }
3850
4419
  //#endregion
3851
4420
  //#region src/client/sidebar/panels/index.ts
@@ -3869,6 +4438,31 @@ window.__ModuleLoader__.load({
3869
4438
  };
3870
4439
  }
3871
4440
  //#endregion
4441
+ //#region src/client/sidebar/menuItems.ts
4442
+ /**
4443
+ * dsh-vscode-mode client — 文件管理右键菜单内置项。
4444
+ * 首个内置项:「在文件浏览器中打开」(文件→OS Explorer 定位选中、目录→打开目录)。
4445
+ * 反馈统一走 ctx.notify(由 EditorView 提供,落到编辑区路径栏状态)。
4446
+ * 作者 ddj 2026-08-27
4447
+ */
4448
+ /**
4449
+ * 构造内置右键菜单项列表(后续内置项直接追加)。
4450
+ * @author ddj 2026年08月27号
4451
+ * @returns 内置菜单项数组
4452
+ */
4453
+ function createDefaultFileMenuItems() {
4454
+ return [{
4455
+ id: "reveal-in-explorer",
4456
+ label: "在文件浏览器中打开",
4457
+ order: 0,
4458
+ run: (target, ctx) => {
4459
+ revealInExplorer(ctx.sessionId, target.path).then((outcome) => {
4460
+ ctx.notify?.(outcome.ok ? "已在文件浏览器中打开" : "打开失败:" + (outcome.error ?? "未知错误"));
4461
+ });
4462
+ }
4463
+ }];
4464
+ }
4465
+ //#endregion
3872
4466
  //#region src/client/outline/parse.ts
3873
4467
  /** monaco SymbolKind 数值常量(与 LSP SymbolKind 一致,面板渲染按此分组)。 */
3874
4468
  const SK = {
@@ -4603,7 +5197,8 @@ window.__ModuleLoader__.load({
4603
5197
  //#region src/client/index.ts
4604
5198
  /**
4605
5199
  * dsh-vscode-mode client — 浏览器半入口:slot 注册 + 装配。
4606
- * 挂点:conversation.view「文件编辑」页签(中央 Monaco 编辑器)+ conversation.input.dock 差异条 + header 差异角标。
5200
+ * 挂点:betterSidebar「文件编辑」Tab(侧边栏形态,对话+编辑同屏;未装 dsh-better-sidebar 时
5201
+ * 回退 conversation.view 中央页签)+ conversation.input.dock 差异条 + header 差异角标。
4607
5202
  * 与 Host 通信:同源 fetch('/edrv/rpc')(shared/rpc 契约)。
4608
5203
  *
4609
5204
  * ⚠️ 跨版本 slot 装配(2026-08-21):新版 DSH 的 slots 系统要求 slot 必须由父 entry
@@ -4656,6 +5251,8 @@ window.__ModuleLoader__.load({
4656
5251
  const binder = pickSettingsBinder(ctx);
4657
5252
  const settings = binder.scope;
4658
5253
  let selected = autoValue("auto");
5254
+ /** 可选探测 betterSidebar 服务(不进 inject:缺失会让插件停靠等待,杀死回退路径)。 */
5255
+ const sideService = detectSidebarService(ctx);
4659
5256
  /** 「添加到对话」动作集:编辑区/Tab 右键菜单注入文件引用与代码块(conversation 服务缺失时各动作安全降级)。 */
4660
5257
  const addToConversation = createAddToConversation(ctx);
4661
5258
  /** 客户端侧兼容摘要(与 host 报告合并展示;惰性求值保证 HMR 后仍新鲜)。 */
@@ -4674,6 +5271,11 @@ window.__ModuleLoader__.load({
4674
5271
  name: "侧边栏打开器(dsh-better-sidebar)",
4675
5272
  active: registry.get(SIDEBAR_PLUGIN) !== void 0,
4676
5273
  note: registry.get("dsh-better-sidebar") !== void 0 ? "已注册(优先级 80)" : "未检测到侧边栏打开能力"
5274
+ },
5275
+ {
5276
+ name: "侧边栏编辑区(dsh-better-sidebar)",
5277
+ active: sideService !== void 0,
5278
+ note: sideService !== void 0 ? "编辑区=侧边栏 Tab(对话+编辑同屏)" : "未检测到;安装 dsh-better-sidebar 后刷新启用侧边栏形态(dsh plugin --profile web add dsh-better-sidebar)"
4677
5279
  }
4678
5280
  ];
4679
5281
  ctx.provide("fileOpeners", registry);
@@ -4681,6 +5283,9 @@ window.__ModuleLoader__.load({
4681
5283
  const sidebarPanels = createSidebarPanelRegistry();
4682
5284
  ctx.provide("edrvSidebarPanels", sidebarPanels);
4683
5285
  ctx.effect(() => sidebarPanels.register(createFilePanel()), "vscode-mode: sidebar panel");
5286
+ const fileMenuItems = createTreeMenuRegistry();
5287
+ ctx.provide("edrvFileContextMenuItems", fileMenuItems);
5288
+ for (const item of createDefaultFileMenuItems()) ctx.effect(() => fileMenuItems.register(item), "vscode-mode: file context menu item " + item.id);
4684
5289
  const outlineSources = createOutlineSourceRegistry();
4685
5290
  ctx.provide("edrvOutlineSources", outlineSources);
4686
5291
  ctx.effect(() => registerBuiltinOutlineSources(outlineSources), "vscode-mode: outline sources");
@@ -4720,18 +5325,47 @@ window.__ModuleLoader__.load({
4720
5325
  },
4721
5326
  logger: (message) => console.warn("[dsh-vscode-mode] " + message)
4722
5327
  }), "vscode-mode: file link routing");
4723
- registerSlotSafely(ctx, {
4724
- name: "conversation.view",
4725
- id: "edrv-editor",
4726
- order: 5,
4727
- label: "文件编辑",
4728
- inject: (sessionId) => ({ sessionId })
4729
- }, (props) => react.default.createElement(EditorView, Object.assign({}, props, {
4730
- schedule,
4731
- addToConversation,
4732
- sidebarPanels,
4733
- outlineSources
4734
- })));
5328
+ const registerLegacyTab = () => {
5329
+ registerSlotSafely(ctx, {
5330
+ name: "conversation.view",
5331
+ id: "edrv-editor",
5332
+ order: 5,
5333
+ label: "文件编辑",
5334
+ inject: (sessionId) => ({ sessionId })
5335
+ }, (props) => react.default.createElement(EditorView, Object.assign({}, props, {
5336
+ layout: "tab",
5337
+ sideHint: SIDEBAR_INSTALL_CMD,
5338
+ schedule,
5339
+ addToConversation,
5340
+ sidebarPanels,
5341
+ outlineSources,
5342
+ fileMenuItems
5343
+ })));
5344
+ };
5345
+ if (sideService) ctx.effect(() => installSideEditor({
5346
+ service: sideService,
5347
+ renderTab: (props) => react.default.createElement(SideEditorTab, Object.assign({}, props, {
5348
+ schedule,
5349
+ addToConversation,
5350
+ sidebarPanels,
5351
+ outlineSources,
5352
+ fileMenuItems
5353
+ })),
5354
+ activeSession: () => {
5355
+ const snapshot = sessions?.list?.getSnapshot?.();
5356
+ const sessionId = snapshot?.current;
5357
+ if (!sessionId) return void 0;
5358
+ return {
5359
+ sessionId,
5360
+ cwd: snapshot?.byId?.[sessionId]?.cwd
5361
+ };
5362
+ },
5363
+ registerLegacyFallback: registerLegacyTab
5364
+ }), "vscode-mode: sidebar editor tab");
5365
+ else {
5366
+ setEnsureSideEditor(null);
5367
+ registerLegacyTab();
5368
+ }
4735
5369
  registerSlotSafely(ctx, {
4736
5370
  name: "conversation.input.dock",
4737
5371
  id: "edrv-diff-dock",