dsh-vscode-mode 0.1.22 → 0.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -25
- package/lib/client.js +538 -98
- package/lib/client.js.map +1 -1
- package/lib/index.js +4 -1
- package/lib/index.js.map +1 -1
- package/package.json +5 -2
- package/src/client/events.ts +8 -4
- package/src/client/index.ts +36 -9
- package/src/client/monaco/diffRender.ts +3 -0
- package/src/client/sidebar/panels/FileExplorer.ts +12 -2
- package/src/client/sidebarBridge.ts +247 -0
- package/src/client/styles/editor.css +11 -0
- package/src/client/ui/ContextMenu.ts +37 -12
- package/src/client/ui/DiffBadge.ts +5 -3
- package/src/client/ui/EditorView.ts +108 -43
- package/src/client/ui/SideEditorTab.ts +48 -0
- package/src/client/ui/menuPosition.ts +63 -0
- package/src/rpc.ts +7 -1
package/lib/client.js
CHANGED
|
@@ -28,8 +28,9 @@ window.__ModuleLoader__.load({
|
|
|
28
28
|
//#endregion
|
|
29
29
|
let react = require("react");
|
|
30
30
|
react = __toESM(react, 1);
|
|
31
|
+
let react_dom = require("react-dom");
|
|
31
32
|
//#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-item:disabled,\n[data-edrv-view] .edrv-ctxmenu-item.edrv-ctxmenu-disabled { color: var(--dsw-alias-label-disabled, #b0b7c0); cursor: default; background: transparent; }\n[data-edrv-view] .edrv-ctxmenu-sep { height: 1px; margin: 3px 4px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-diffbar-body { max-height: 130px; max-width: min(560px, calc(100vw - 40px)); overflow: auto; padding: 4px 8px 8px; display: flex; flex-direction: column; gap: 4px; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-diffrow { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); cursor: pointer; flex-wrap: wrap; }\n[data-edrv-view] .edrv-diffrow:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-diffrow-l { font-size: 11px; color: var(--dsw-alias-label-primary, #1f2933); min-width: 74px; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-o { font-size: 11px; color: var(--dsw-alias-state-error-primary, #d9534f); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-n { font-size: 11px; color: var(--dsw-alias-state-success-primary, #0f9d58); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-tag { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-diffrow-stale { border-color: rgba(217,119,6,.5); cursor: default; }\n[data-edrv-view] .edrv-diffbox-others { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: 4px 2px 0; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); margin-top: 2px; }\n[data-edrv-view] .edrv-diffrow-file { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); cursor: pointer; white-space: nowrap; max-width: 200px; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-diffrow-file:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n/* 编辑区 hover 差异块的 Keep/Undo 浮层 */\n[data-edrv-view] .edrv-hoveract { position: absolute; z-index: 30; display: flex; align-items: center; gap: 4px; padding: 2px 6px; border-radius: 8px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 4px 14px rgba(31,41,51,.14); }\n[data-edrv-view] .edrv-launch { position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 40; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); box-shadow: 0 6px 24px rgba(0,0,0,.5); display: flex; flex-direction: column; max-height: 60%; overflow: hidden; }\n[data-edrv-view] .edrv-launch-head { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-bottom: 1px solid var(--dsw-alias-border-l1, #333); }\n[data-edrv-view] .edrv-launch-tab { font-size: 12px; padding: 3px 12px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; }\n[data-edrv-view] .edrv-launch-tab.on { background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-label-primary, #eee); font-weight: 600; }\n[data-edrv-view] .edrv-launch-body { flex: 1; min-height: 0; overflow: auto; padding: 6px 8px; display: flex; flex-direction: column; gap: 4px; }\n[data-edrv-view] .edrv-launch-row { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 6px; cursor: pointer; flex-wrap: wrap; }\n[data-edrv-view] .edrv-launch-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-launch-path { flex: 1; min-width: 0; font-size: 12px; color: var(--dsw-alias-label-primary, #eee); word-break: break-all; }\n[data-edrv-view] .edrv-launch-cnt { font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); white-space: nowrap; }\n[data-edrv-view] .edrv-launch-arch { display: flex; flex-direction: column; gap: 6px; }\n[data-edrv-view] .monaco-editor { overflow: hidden !important; }\n[data-edrv-view] .monaco-editor .sticky-widget { z-index: 30 !important; background: var(--dsw-alias-bg-base, #f8f8f8) !important; }\n[data-edrv-view] .monaco-editor .sticky-widget .sticky-line-content,\n[data-edrv-view] .monaco-editor .sticky-widget .sticky-line-number { background: var(--dsw-alias-bg-base, #f8f8f8) !important; }\n[data-edrv-view] .monaco-editor, [data-edrv-view] .monaco-editor .margin, [data-edrv-view] .monaco-editor-background { background: transparent !important; }\n[data-edrv-view] .monaco-editor .scrollbar .slider { background: rgba(128,128,128,.3) !important; }\n[data-edrv-view] .edrv-loading { gap: 8px; padding: 20px; }\n[data-edrv-view] .edrv-loading-title { color: var(--dsw-alias-label-primary, #1f2933); font-size: 13px; }\n[data-edrv-view] .edrv-progress { width: min(360px, 72%); height: 6px; overflow: hidden; border-radius: 999px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-progress-value { height: 100%; border-radius: inherit; background: var(--dsw-alias-brand-primary, #0f9d58); transition: width .24s ease; }\n[data-edrv-view] .edrv-loading-percent { color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; font-variant-numeric: tabular-nums; }\n@media (prefers-reduced-motion: reduce) {\n [data-edrv-view] .edrv-progress-value { transition: none; }\n}\n\n/* ===== 唯一会话级差异 dock(遵循 DSH todo/queue 原生布局) ===== */\n[data-edrv-diff-dock-shell] {\n box-sizing: border-box;\n width: calc(100% - var(--dsh-composer-side-clearance) - var(--dsh-composer-side-clearance) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));\n max-width: calc(var(--dsh-composer-card-max-width) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));\n margin: 0 auto;\n padding: 0;\n flex: none;\n border: 1px solid var(--dsw-alias-border-l1, #e0e6e8);\n border-radius: 12px;\n background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, transparent));\n box-shadow: none;\n overflow: hidden;\n}\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock {\n box-sizing: border-box;\n position: relative;\n left: auto;\n right: auto;\n bottom: auto;\n z-index: auto;\n width: 100%;\n max-width: none;\n margin: 0;\n display: flex;\n flex-direction: column;\n border: none;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n overflow: visible;\n}\n[data-edrv-diff-dock-shell] .edrv-diffbar-row { min-height: 36px; padding: 6px 12px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-row { min-height: 36px; padding: 6px 12px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-body { max-height: 180px; overflow: auto; padding: 6px 12px 10px; border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-button { box-sizing: border-box; display: flex; align-items: center; justify-content: center; width: 100%; height: 24px; border: none; border-radius: 8px; padding: 0 12px; background: transparent; color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 500; line-height: 24px; text-align: center; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-button:hover { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diffbar-count { display: inline-flex; align-items: center; color: var(--dsw-alias-label-primary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 500; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-file { display: inline-flex; align-items: center; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-editor-row { display: flex; align-items: center; height: 36px; min-height: 36px; padding: 4px 12px; overflow-x: auto; scrollbar-width: thin; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-editor-content { display: flex; align-items: center; gap: 10px; width: 100%; min-width: max-content; min-height: 28px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-nav,\n[data-edrv-diff-dock-shell] .edrv-diffbar-files,\n[data-edrv-diff-dock-shell] .edrv-diffbar-actions { display: flex; align-items: center; gap: 6px; flex: none; height: 28px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-actions { margin-left: auto; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-details-toggle { display: inline-flex; align-items: center; justify-content: center; width: 28px; min-width: 28px; padding: 0; font-size: 13px; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-details-toggle svg { width: 14px; height: 14px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-files { min-width: 0; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-files .edrv-diffbar-file { min-width: 72px; max-width: 240px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-progress { min-width: 0; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; font-variant-numeric: tabular-nums; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-summary { min-width: 0; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-pill { box-sizing: border-box; height: 28px; padding: 0 9px; border: none; border-radius: 999px; cursor: pointer; white-space: nowrap; font-size: 11px; line-height: 26px; font-weight: 600; }\n[data-edrv-diff-dock-shell] .edrv-pill-keep { background: var(--dsw-alias-state-success-tertiary); color: var(--dsw-alias-state-success-primary); border: 1px solid var(--dsw-alias-state-success-secondary); }\n[data-edrv-diff-dock-shell] .edrv-pill-undo { background: var(--dsw-alias-state-error-tertiary); color: var(--dsw-alias-state-error-primary); border: 1px solid var(--dsw-alias-state-error-secondary); }\n[data-edrv-diff-dock-shell] .edrv-pill-ghost { background: transparent; color: var(--dsw-alias-label-secondary); border: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-pill:hover:not(:disabled) { background-color: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-pill:focus-visible { outline: 2px solid var(--dsw-alias-label-tertiary); outline-offset: -2px; }\n[data-edrv-diff-dock-shell] .edrv-pill:disabled { opacity: .45; cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diffrow { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; background: transparent; cursor: pointer; flex-wrap: wrap; }\n[data-edrv-diff-dock-shell] .edrv-diffrow:hover { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-l { color: var(--dsw-alias-label-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-o { color: var(--dsw-alias-state-error-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-n { color: var(--dsw-alias-state-success-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-tag { color: var(--dsw-alias-label-tertiary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-stale { border-color: var(--dsw-alias-state-warn-secondary); cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diffbox-others { border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-file { border-color: var(--dsw-alias-border-l1); background: transparent; color: var(--dsw-alias-label-secondary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-file:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }\n[data-edrv-diff-dock-shell] .edrv-diff-details { display: flex; flex-direction: column; gap: 2px; padding: 6px 12px 8px; border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item { display: flex; align-items: center; width: 100%; min-height: 28px; gap: 8px; padding: 4px 8px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary); cursor: pointer; text-align: left; font-size: 12px; line-height: 20px; }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item.danger { color: var(--dsw-alias-state-error-primary); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item:disabled { opacity: .45; cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-sep { height: 1px; margin: 2px 4px; background: var(--dsw-alias-border-l1); }\n@media (max-width: 620px) {\n [data-edrv-diff-dock-shell] .edrv-diffbar-editor-content { width: max-content; }\n [data-edrv-diff-dock-shell] .edrv-diffbar-files .edrv-diffbar-file { max-width: 160px; }\n}\n\n/* ===== 侧边栏(活动栏 + 面板区 + 文件树) ===== */\n[data-edrv-view] .edrv-editor-row { display: flex; flex: 1 1 auto; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-main-col { box-sizing: border-box; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-main-col > .edrv-pathbar,\n[data-edrv-view] .edrv-main-col > .edrv-tabs { flex: 0 0 auto; }\n[data-edrv-view] .edrv-editor-area { position: relative; display: flex; flex: 1 1 auto; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-sidebar { display: flex; flex-direction: row; flex-shrink: 0; min-width: 0; height: 100%; background: var(--dsw-alias-bg-layer-1, #ffffff); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); overflow: hidden; }\n[data-edrv-view] .edrv-rail { display: flex; flex-direction: column; align-items: center; width: 44px; flex-shrink: 0; padding: 6px 0; background: var(--dsw-alias-bg-layer-2, #f0f4f4); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); gap: 4px; }\n[data-edrv-view] .edrv-rail-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: none; border-radius: 8px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rail-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rail-btn.edrv-rail-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); box-shadow: inset 2px 0 0 var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-rail-icon { font-size: 15px; line-height: 1; }\n[data-edrv-view] .edrv-rail-badge { position: absolute; top: 2px; right: 2px; min-width: 14px; height: 14px; padding: 0 3px; border-radius: 999px; background: var(--dsw-alias-brand-primary, #0f9d58); color: #fff; font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; }\n[data-edrv-view] .edrv-side-body { flex: 1; min-width: 0; min-height: 0; display: flex; overflow: hidden; }\n[data-edrv-view] .edrv-side-panel { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; position: relative; }\n[data-edrv-view] .edrv-side-head { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px 0 12px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); flex-shrink: 0; }\n[data-edrv-view] .edrv-side-title { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-side-root { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-side-btn { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-side-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-tree { flex: 1; min-height: 0; overflow: auto; padding: 4px 6px 12px; font-size: 12px; }\n[data-edrv-view] .edrv-tree-row { display: flex; align-items: center; gap: 4px; height: 22px; padding-right: 6px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-tree-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-active { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); }\n[data-edrv-view] .edrv-tree-chev { width: 12px; flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; text-align: center; }\n[data-edrv-view] .edrv-tree-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-tree-name.edrv-tree-dim { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-tree-badge { min-width: 14px; height: 14px; padding: 0 4px; border-radius: 999px; background: var(--dsw-alias-state-warn-primary, #d97706); color: #fff; font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; flex-shrink: 0; }\n[data-edrv-view] .edrv-tree-loading { height: 22px; line-height: 22px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-tree-error { display: flex; align-items: center; gap: 8px; margin: 4px 2px 6px; padding: 6px 8px; border: 1px solid rgba(217,83,79,.4); border-radius: 6px; background: #fdeaea; color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n/* 大纲面板:kind 字形 / detail / 行号 */\n[data-edrv-view] .edrv-outline-kind { display: inline-flex; align-items: center; justify-content: center; width: 14px; flex-shrink: 0; font-size: 12px; font-weight: 600; text-align: center; }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-func { color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-type { color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-data { color: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-ns { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-key { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-outline-detail { flex-shrink: 0; max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-outline-ln { flex-shrink: 0; padding-right: 2px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-side-resize { width: 5px; flex-shrink: 0; cursor: col-resize; background: transparent; }\n[data-edrv-view] .edrv-side-resize:hover { background: var(--dsw-alias-brand-primary, #0f9d58); opacity: .35; }\n[data-edrv-view] .edrv-chip-btn.edrv-chip-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); }\n\n";
|
|
33
|
+
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-row.edrv-tree-context { box-shadow: inset 0 0 0 1px var(--dsw-alias-brand-primary, #0f9d58); background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); }\n[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
34
|
const tagId$1 = "dsh-vscode-mode/editor.css";
|
|
34
35
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
35
36
|
const tag = document.createElement("style");
|
|
@@ -75,14 +76,251 @@ window.__ModuleLoader__.load({
|
|
|
75
76
|
} catch (e) {}
|
|
76
77
|
}
|
|
77
78
|
//#endregion
|
|
79
|
+
//#region src/client/sidebarBridge.ts
|
|
80
|
+
/**
|
|
81
|
+
* dsh-vscode-mode client — 侧边栏编辑区桥:可选探测 dsh-better-sidebar 的
|
|
82
|
+
* ctx.betterSidebar 服务,并注册「文件编辑」Tab(单实例)。
|
|
83
|
+
* 可选依赖模式:服务缺失时调用方回退到中央页签形态;本模块不注册任何东西。
|
|
84
|
+
* 纯逻辑(探测/初始打开解析/角标计数)可单测,DOM/事件仅存在于 install 路径。
|
|
85
|
+
* 作者 ddj 2026年08月25号
|
|
86
|
+
*/
|
|
87
|
+
/** betterSidebar 服务名(ctx.get 用;不要加进 inject——缺失会让插件停靠等待)。 */
|
|
88
|
+
const BETTER_SIDEBAR_SERVICE = "betterSidebar";
|
|
89
|
+
/** 本插件在侧边栏注册的 Tab 类型 id(亦是 SidebarTab.type)。 */
|
|
90
|
+
const SIDE_TAB_ID = "edrv-editor";
|
|
91
|
+
/** 侧边栏 Tab 标题。 */
|
|
92
|
+
const SIDE_TAB_TITLE = "文件编辑";
|
|
93
|
+
/** 推荐安装命令(提示条/兼容性报告共用)。 */
|
|
94
|
+
const SIDEBAR_INSTALL_CMD = "dsh plugin --profile web add dsh-better-sidebar";
|
|
95
|
+
/**
|
|
96
|
+
* 解析 Tab 挂载时的初始打开请求(创建路径读 tab.path/meta,聚焦路径走窗口事件)。
|
|
97
|
+
* @author ddj 2026年08月25号
|
|
98
|
+
* @param tab 侧边栏 Tab(path/meta 为可选字段)
|
|
99
|
+
* @returns 待打开路径与是否聚焦差异
|
|
100
|
+
*/
|
|
101
|
+
function resolveInitialOpen(tab) {
|
|
102
|
+
const meta = tab?.meta ?? {};
|
|
103
|
+
return {
|
|
104
|
+
path: (typeof tab?.path === "string" && tab.path ? tab.path : typeof meta?.openPath === "string" ? meta.openPath : null) || null,
|
|
105
|
+
focusDiff: meta?.focusDiff === true
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/** 会话级待处理差异文件数缓存(Tab 角标同步读取;EditorView 侧栏形态写入)。 */
|
|
109
|
+
const sidePending = /* @__PURE__ */ new Map();
|
|
110
|
+
/**
|
|
111
|
+
* 写入会话的待处理差异文件数(0 视为清除,不显示角标)。
|
|
112
|
+
* @author ddj 2026年08月25号
|
|
113
|
+
* @param sessionId 会话 id
|
|
114
|
+
* @param count 待处理差异文件数
|
|
115
|
+
*/
|
|
116
|
+
function setSidePending(sessionId, count) {
|
|
117
|
+
if (!sessionId) return;
|
|
118
|
+
const value = Number.isFinite(count) ? Math.max(0, Math.floor(count)) : 0;
|
|
119
|
+
if (value > 0) sidePending.set(sessionId, value);
|
|
120
|
+
else sidePending.delete(sessionId);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* 读取会话的待处理差异文件数(角标用)。
|
|
124
|
+
* @author ddj 2026年08月25号
|
|
125
|
+
* @param sessionId 会话 id
|
|
126
|
+
* @returns 非负计数
|
|
127
|
+
*/
|
|
128
|
+
function getSidePending(sessionId) {
|
|
129
|
+
if (!sessionId) return 0;
|
|
130
|
+
return sidePending.get(sessionId) ?? 0;
|
|
131
|
+
}
|
|
132
|
+
/** 模块级侧栏打开路由(events.ts 优先走它;未注册 = 旧页签形态)。 */
|
|
133
|
+
let ensureSideEditor = null;
|
|
134
|
+
/** 待消费的初始打开(Tab 组件尚未挂载时暂存;按会话匹配,挂载时取走)。 */
|
|
135
|
+
let pendingOpen = null;
|
|
136
|
+
/** Tab 组件当前是否有挂载实例(有则直接投递窗口事件,无则暂存待消费)。 */
|
|
137
|
+
let sideEditorMounted = false;
|
|
138
|
+
/**
|
|
139
|
+
* 注册/撤销侧栏打开路由。
|
|
140
|
+
* @author ddj 2026年08月25号
|
|
141
|
+
* @param fn 路由函数或 null
|
|
142
|
+
*/
|
|
143
|
+
function setEnsureSideEditor(fn) {
|
|
144
|
+
ensureSideEditor = fn;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* 标记侧栏编辑器组件挂载状态(SideEditorTab 挂载/卸载时调用)。
|
|
148
|
+
* @author ddj 2026年08月25号
|
|
149
|
+
* @param mounted 是否已挂载
|
|
150
|
+
*/
|
|
151
|
+
function setSideEditorMounted(mounted) {
|
|
152
|
+
sideEditorMounted = mounted;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* 暂存指定会话的待消费初始打开(Tab 组件未挂载时由 install 路径调用)。
|
|
156
|
+
* @author ddj 2026年08月25号
|
|
157
|
+
* @param sessionId 会话 id
|
|
158
|
+
* @param path 待打开路径(可空)
|
|
159
|
+
* @param focusDiff 是否聚焦首个差异
|
|
160
|
+
*/
|
|
161
|
+
function stagePendingSideOpen(sessionId, path, focusDiff) {
|
|
162
|
+
if (!sessionId) return;
|
|
163
|
+
pendingOpen = {
|
|
164
|
+
sessionId,
|
|
165
|
+
path: path || null,
|
|
166
|
+
focusDiff: focusDiff === true
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 取走指定会话的待消费初始打开(SideEditorTab 挂载时读取;不存在返回 null)。
|
|
171
|
+
* @author ddj 2026年08月25号
|
|
172
|
+
* @param sessionId 会话 id
|
|
173
|
+
* @returns 待打开请求或 null
|
|
174
|
+
*/
|
|
175
|
+
function takePendingSideOpen(sessionId) {
|
|
176
|
+
if (!pendingOpen || !sessionId || pendingOpen.sessionId !== sessionId) return null;
|
|
177
|
+
const value = {
|
|
178
|
+
path: pendingOpen.path,
|
|
179
|
+
focusDiff: pendingOpen.focusDiff
|
|
180
|
+
};
|
|
181
|
+
pendingOpen = null;
|
|
182
|
+
return value;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* 打开/聚焦侧栏编辑器(打开路径可选;返回是否已被侧栏形态接管)。
|
|
186
|
+
* @author ddj 2026年08月25号
|
|
187
|
+
* @param path 待打开路径(可空)
|
|
188
|
+
* @param focusDiff 是否聚焦首个差异
|
|
189
|
+
* @returns 是否路由成功
|
|
190
|
+
*/
|
|
191
|
+
function routeSideEditor(path, focusDiff) {
|
|
192
|
+
if (typeof ensureSideEditor !== "function") return false;
|
|
193
|
+
try {
|
|
194
|
+
return ensureSideEditor(path, focusDiff) === true;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.warn("[dsh-vscode-mode] 侧栏编辑器打开失败(" + String(error) + "),已回退");
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* 结构化探测 betterSidebar 服务(可选依赖:缺失/降级返回 undefined)。
|
|
202
|
+
* @author ddj 2026年08月25号
|
|
203
|
+
* @param ctx 客户端服务上下文
|
|
204
|
+
* @returns 服务结构面或 undefined
|
|
205
|
+
*/
|
|
206
|
+
function detectSidebarService(ctx) {
|
|
207
|
+
try {
|
|
208
|
+
const service = ctx.get(BETTER_SIDEBAR_SERVICE);
|
|
209
|
+
if (!service || typeof service.registerTab !== "function" || typeof service.openTab !== "function") return void 0;
|
|
210
|
+
return service;
|
|
211
|
+
} catch {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* 注册侧边栏「文件编辑」Tab 并接管打开路由。
|
|
217
|
+
* Tab 被用户在 better-sidebar 设置里禁用时,打开路由降级为回退回调(中央页签)。
|
|
218
|
+
* @author ddj 2026年08月25号
|
|
219
|
+
* @param options 装配参数(组件依赖 + 活动会话解析 + 回退注册)
|
|
220
|
+
* @returns 卸载器(反注册 Tab + 撤销路由)
|
|
221
|
+
*/
|
|
222
|
+
function installSideEditor(options) {
|
|
223
|
+
const { service, renderTab, activeSession, registerLegacyFallback } = options;
|
|
224
|
+
let fallbackInstalled = false;
|
|
225
|
+
const open = (path, focusDiff) => {
|
|
226
|
+
if (typeof service.isTabEnabled === "function" && !service.isTabEnabled("edrv-editor")) {
|
|
227
|
+
if (!fallbackInstalled) {
|
|
228
|
+
fallbackInstalled = true;
|
|
229
|
+
try {
|
|
230
|
+
registerLegacyFallback();
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.warn("[dsh-vscode-mode] 回退注册失败(" + String(error) + ")");
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
const scope = activeSession();
|
|
238
|
+
const sessionId = scope?.sessionId;
|
|
239
|
+
if (!sessionId) return false;
|
|
240
|
+
try {
|
|
241
|
+
service.openTab({
|
|
242
|
+
type: SIDE_TAB_ID,
|
|
243
|
+
title: SIDE_TAB_TITLE,
|
|
244
|
+
...path ? { path } : {},
|
|
245
|
+
meta: {
|
|
246
|
+
openPath: path || void 0,
|
|
247
|
+
focusDiff
|
|
248
|
+
}
|
|
249
|
+
}, {
|
|
250
|
+
sessionId,
|
|
251
|
+
cwd: scope?.cwd
|
|
252
|
+
});
|
|
253
|
+
} catch (error) {
|
|
254
|
+
console.warn("[dsh-vscode-mode] openTab 失败(" + String(error) + ")");
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
if (sideEditorMounted) window.dispatchEvent(new CustomEvent("edrv:open-editor", { detail: {
|
|
258
|
+
path,
|
|
259
|
+
focusDiff
|
|
260
|
+
} }));
|
|
261
|
+
else stagePendingSideOpen(sessionId, path, focusDiff);
|
|
262
|
+
return true;
|
|
263
|
+
};
|
|
264
|
+
let tabDisposer = null;
|
|
265
|
+
try {
|
|
266
|
+
tabDisposer = service.registerTab({
|
|
267
|
+
id: SIDE_TAB_ID,
|
|
268
|
+
title: SIDE_TAB_TITLE,
|
|
269
|
+
icon: codeIcon,
|
|
270
|
+
single: true,
|
|
271
|
+
badge: (_ctx, scope) => getSidePending(scope?.sessionId) || null,
|
|
272
|
+
component: renderTab
|
|
273
|
+
});
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.warn("[dsh-vscode-mode] 侧边栏 Tab 注册失败(" + String(error) + ")");
|
|
276
|
+
if (!fallbackInstalled) {
|
|
277
|
+
fallbackInstalled = true;
|
|
278
|
+
try {
|
|
279
|
+
registerLegacyFallback();
|
|
280
|
+
} catch (inner) {
|
|
281
|
+
console.warn("[dsh-vscode-mode] 回退注册失败(" + String(inner) + ")");
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return () => {};
|
|
285
|
+
}
|
|
286
|
+
setEnsureSideEditor(open);
|
|
287
|
+
return () => {
|
|
288
|
+
if (typeof tabDisposer === "function") tabDisposer();
|
|
289
|
+
setEnsureSideEditor(null);
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* 侧边栏 Tab 图标(内联 SVG 代码括号,不依赖图标包)。
|
|
294
|
+
* @author ddj 2026年08月25号
|
|
295
|
+
* @param size 边长(px)
|
|
296
|
+
* @returns 图标元素
|
|
297
|
+
*/
|
|
298
|
+
function codeIcon(size) {
|
|
299
|
+
return react.default.createElement("svg", {
|
|
300
|
+
width: size,
|
|
301
|
+
height: size,
|
|
302
|
+
viewBox: "0 0 16 16",
|
|
303
|
+
fill: "none",
|
|
304
|
+
"aria-hidden": true,
|
|
305
|
+
focusable: false
|
|
306
|
+
}, react.default.createElement("path", {
|
|
307
|
+
d: "M5.5 4.5 2.5 8l3 3.5M10.5 4.5 13.5 8l-3 3.5",
|
|
308
|
+
stroke: "currentColor",
|
|
309
|
+
strokeWidth: 1.4,
|
|
310
|
+
strokeLinecap: "round",
|
|
311
|
+
strokeLinejoin: "round"
|
|
312
|
+
}));
|
|
313
|
+
}
|
|
314
|
+
//#endregion
|
|
78
315
|
//#region src/client/events.ts
|
|
79
316
|
/**
|
|
80
317
|
* dsh-vscode-mode client — 跨组件/跨 slot 的窗口事件助手。
|
|
81
|
-
* 迁移自原 src/client/index.ts 的 openEditorView
|
|
318
|
+
* 迁移自原 src/client/index.ts 的 openEditorView 与事件派发,语义不改;
|
|
319
|
+
* 侧边栏形态(sidebarBridge 已注册路由)优先,否则回退 DOM 页签点击。
|
|
82
320
|
* 作者 ddj 2026-08-20
|
|
83
321
|
*/
|
|
84
322
|
/**
|
|
85
|
-
* 选择中央「文件编辑」页签(DOM 级,无需 store actions
|
|
323
|
+
* 选择中央「文件编辑」页签(DOM 级,无需 store actions)。仅旧页签形态使用。
|
|
86
324
|
* @author ddj 2026年08月26号
|
|
87
325
|
*/
|
|
88
326
|
function selectEditorTab() {
|
|
@@ -93,11 +331,12 @@ window.__ModuleLoader__.load({
|
|
|
93
331
|
}
|
|
94
332
|
}
|
|
95
333
|
/**
|
|
96
|
-
*
|
|
334
|
+
* 打开「文件编辑」:侧边栏形态路由优先,旧形态回退中央页签。
|
|
97
335
|
* @author ddj 2026年08月26号
|
|
98
336
|
* @param path 要打开的路径(可空)
|
|
99
337
|
*/
|
|
100
338
|
function openEditorView(path) {
|
|
339
|
+
if (routeSideEditor(path ?? null, false)) return;
|
|
101
340
|
selectEditorTab();
|
|
102
341
|
const target = path ?? null;
|
|
103
342
|
setTimeout(() => {
|
|
@@ -105,11 +344,12 @@ window.__ModuleLoader__.load({
|
|
|
105
344
|
}, 80);
|
|
106
345
|
}
|
|
107
346
|
/**
|
|
108
|
-
*
|
|
347
|
+
* 打开文件编辑页并聚焦指定文件的首个差异(侧边栏形态自动展开面板)。
|
|
109
348
|
* @author ddj 2026年08月26号
|
|
110
349
|
* @param path 待聚焦的差异文件路径
|
|
111
350
|
*/
|
|
112
351
|
function openDiffView(path) {
|
|
352
|
+
if (routeSideEditor(path, true)) return;
|
|
113
353
|
selectEditorTab();
|
|
114
354
|
setTimeout(() => {
|
|
115
355
|
window.dispatchEvent(new CustomEvent("edrv:open-editor", { detail: {
|
|
@@ -291,7 +531,7 @@ window.__ModuleLoader__.load({
|
|
|
291
531
|
if (document.querySelector("script[data-edrv-monaco-loader]")) boot();
|
|
292
532
|
else {
|
|
293
533
|
const s = document.createElement("script");
|
|
294
|
-
s.src = "/
|
|
534
|
+
s.src = MONACO_BASE + "/loader.js";
|
|
295
535
|
s.dataset.edrvMonacoLoader = "1";
|
|
296
536
|
s.onload = boot;
|
|
297
537
|
s.onerror = () => fail(/* @__PURE__ */ new Error("Monaco loader 加载失败"));
|
|
@@ -323,6 +563,7 @@ window.__ModuleLoader__.load({
|
|
|
323
563
|
* @param sessionId 会话 id
|
|
324
564
|
*/
|
|
325
565
|
const render = (monaco, editor, pendingRegions, sessionId) => {
|
|
566
|
+
if (!pendingRegions.length && viewZoneIds.length === 0 && decorations.length === 0) return;
|
|
326
567
|
const renderT0 = Date.now();
|
|
327
568
|
const callIdAttr = (callId, idx) => String(callId) + ":" + String(idx);
|
|
328
569
|
log(sessionId, "[diff-render] begin regs=" + pendingRegions.length + " " + pendingRegions.map((r) => callIdAttr(r.callId, r.idx) + "@" + (r.start ?? "?") + "-" + (r.end ?? "?") + "(-" + (r.oldLines ? r.oldLines.length : 0) + "/+" + (r.newLines ? r.newLines.length : 0) + ")").join("|"));
|
|
@@ -1374,11 +1615,15 @@ window.__ModuleLoader__.load({
|
|
|
1374
1615
|
* 底部差异条(DiffBox/空态)+ 全局差异下拉(DiffLauncher)+ 状态栏。
|
|
1375
1616
|
* @param props.sessionId 会话 id
|
|
1376
1617
|
* @param props.schedule 延时调度(ctx.timeout)
|
|
1618
|
+
* @param props.layout 布局形态:'tab'(中央页签,默认)| 'side'(侧边栏面板)
|
|
1619
|
+
* @param props.sideHint 旧页签形态下显示侧边栏引导(安装命令)
|
|
1377
1620
|
*/
|
|
1378
1621
|
function EditorView(props) {
|
|
1379
1622
|
const sessionId = props?.sessionId;
|
|
1380
1623
|
const schedule = props.schedule;
|
|
1381
1624
|
const addToConversation = props.addToConversation;
|
|
1625
|
+
const layout = props?.layout === "side" ? "side" : "tab";
|
|
1626
|
+
const sideHint = props?.sideHint;
|
|
1382
1627
|
const [monaco, setMonaco] = react.default.useState(null);
|
|
1383
1628
|
const [monacoErr, setMonacoErr] = react.default.useState(null);
|
|
1384
1629
|
const [records, setRecords] = react.default.useState({});
|
|
@@ -1403,10 +1648,17 @@ window.__ModuleLoader__.load({
|
|
|
1403
1648
|
const [diffIdx, setDiffIdx] = react.default.useState(0);
|
|
1404
1649
|
const [fileIdx, setFileIdx] = react.default.useState(0);
|
|
1405
1650
|
const [tabMenu, setTabMenu] = react.default.useState(null);
|
|
1406
|
-
const [sidebarOn, setSidebarOn] = react.default.useState(
|
|
1651
|
+
const [sidebarOn, setSidebarOn] = react.default.useState(layout !== "side");
|
|
1407
1652
|
const [sidebarW, setSidebarW] = react.default.useState(240);
|
|
1408
1653
|
const [activePanel, setActivePanel] = react.default.useState("explorer");
|
|
1409
1654
|
const [focusRequest, setFocusRequest] = react.default.useState(0);
|
|
1655
|
+
const [hintDismissed, setHintDismissed] = react.default.useState(() => {
|
|
1656
|
+
try {
|
|
1657
|
+
return localStorage.getItem("edrv.side-hint-dismissed") === "1";
|
|
1658
|
+
} catch {
|
|
1659
|
+
return false;
|
|
1660
|
+
}
|
|
1661
|
+
});
|
|
1410
1662
|
const editorRef = react.default.useRef(null);
|
|
1411
1663
|
const viewRootRef = react.default.useRef(null);
|
|
1412
1664
|
const monacoRef = react.default.useRef(null);
|
|
@@ -1427,6 +1679,8 @@ window.__ModuleLoader__.load({
|
|
|
1427
1679
|
const batchBusyRef = react.default.useRef(false);
|
|
1428
1680
|
const menuHandlersRef = react.default.useRef(null);
|
|
1429
1681
|
const diffRendererRef = react.default.useRef(null);
|
|
1682
|
+
const layoutRef = react.default.useRef(layout);
|
|
1683
|
+
layoutRef.current = layout;
|
|
1430
1684
|
if (!diffRendererRef.current) diffRendererRef.current = createDiffRenderer((sid, t) => dbg(sid, t));
|
|
1431
1685
|
const currentRecords = react.default.useMemo(() => {
|
|
1432
1686
|
const list = [];
|
|
@@ -1470,6 +1724,24 @@ window.__ModuleLoader__.load({
|
|
|
1470
1724
|
return next;
|
|
1471
1725
|
});
|
|
1472
1726
|
};
|
|
1727
|
+
/**
|
|
1728
|
+
* 轮询去重:前后记录内容一致时返回原引用,React 跳过重渲染,
|
|
1729
|
+
* 阻断 5s 轮询 → memo 链 → diff 重渲染/日志 的空转。
|
|
1730
|
+
* @author ddj 2026年08月26号
|
|
1731
|
+
* @param prev 当前 records
|
|
1732
|
+
* @param next 轮询新结果
|
|
1733
|
+
* @returns 内容一致返回 true
|
|
1734
|
+
*/
|
|
1735
|
+
const sameRecords = (prev, next) => {
|
|
1736
|
+
const pk = Object.keys(prev);
|
|
1737
|
+
const nk = Object.keys(next);
|
|
1738
|
+
if (pk.length !== nk.length) return false;
|
|
1739
|
+
for (const k of pk) {
|
|
1740
|
+
if (!(k in next)) return false;
|
|
1741
|
+
if (JSON.stringify(prev[k]) !== JSON.stringify(next[k])) return false;
|
|
1742
|
+
}
|
|
1743
|
+
return true;
|
|
1744
|
+
};
|
|
1473
1745
|
const refreshRecords = (skipStale) => {
|
|
1474
1746
|
if (!sessionId) return;
|
|
1475
1747
|
rpc("edrv.list", {
|
|
@@ -1482,7 +1754,7 @@ window.__ModuleLoader__.load({
|
|
|
1482
1754
|
if (r.archived === true) continue;
|
|
1483
1755
|
map[r.callId] = r;
|
|
1484
1756
|
}
|
|
1485
|
-
setRecords(map);
|
|
1757
|
+
setRecords((prev) => sameRecords(prev, map) ? prev : map);
|
|
1486
1758
|
}).catch((e) => setError("list异常:" + String(e)));
|
|
1487
1759
|
};
|
|
1488
1760
|
const loadContent = (path, sid) => {
|
|
@@ -1556,8 +1828,12 @@ window.__ModuleLoader__.load({
|
|
|
1556
1828
|
};
|
|
1557
1829
|
}, [sessionId]);
|
|
1558
1830
|
const dockSourceRef = react.default.useRef({});
|
|
1559
|
-
react.default.useEffect(() => () =>
|
|
1831
|
+
react.default.useEffect(() => () => {
|
|
1832
|
+
clearDiffDock(sessionId, dockSourceRef.current);
|
|
1833
|
+
if (layout === "side") setSidePending(sessionId, 0);
|
|
1834
|
+
}, [sessionId]);
|
|
1560
1835
|
react.default.useLayoutEffect(() => {
|
|
1836
|
+
if (layout === "side") return;
|
|
1561
1837
|
const root = viewRootRef.current;
|
|
1562
1838
|
const scroll = root?.closest?.("[data-conversation-scroll]");
|
|
1563
1839
|
if (!root || !scroll) return;
|
|
@@ -1606,7 +1882,7 @@ window.__ModuleLoader__.load({
|
|
|
1606
1882
|
window.removeEventListener("resize", scheduleUpdate);
|
|
1607
1883
|
root.style.removeProperty("--edrv-editor-height");
|
|
1608
1884
|
};
|
|
1609
|
-
}, [sessionId]);
|
|
1885
|
+
}, [sessionId, layout]);
|
|
1610
1886
|
react.default.useEffect(() => {
|
|
1611
1887
|
if (bootRef.current || !sessionId) return;
|
|
1612
1888
|
bootRef.current = true;
|
|
@@ -1634,10 +1910,11 @@ window.__ModuleLoader__.load({
|
|
|
1634
1910
|
active,
|
|
1635
1911
|
sessionId
|
|
1636
1912
|
]);
|
|
1913
|
+
const sidebarKey = "edrv.sidebar." + (layout === "side" ? "side." : "") + String(sessionId);
|
|
1637
1914
|
react.default.useEffect(() => {
|
|
1638
1915
|
if (!sessionId) return;
|
|
1639
1916
|
try {
|
|
1640
|
-
const raw = localStorage.getItem(
|
|
1917
|
+
const raw = localStorage.getItem(sidebarKey);
|
|
1641
1918
|
if (raw) {
|
|
1642
1919
|
const saved = JSON.parse(raw);
|
|
1643
1920
|
if (typeof saved.on === "boolean") setSidebarOn(saved.on);
|
|
@@ -1645,11 +1922,11 @@ window.__ModuleLoader__.load({
|
|
|
1645
1922
|
if (typeof saved.panel === "string") setActivePanel(saved.panel);
|
|
1646
1923
|
}
|
|
1647
1924
|
} catch (e) {}
|
|
1648
|
-
}, [sessionId]);
|
|
1925
|
+
}, [sessionId, sidebarKey]);
|
|
1649
1926
|
react.default.useEffect(() => {
|
|
1650
1927
|
if (!sessionId) return;
|
|
1651
1928
|
try {
|
|
1652
|
-
localStorage.setItem(
|
|
1929
|
+
localStorage.setItem(sidebarKey, JSON.stringify({
|
|
1653
1930
|
on: sidebarOn,
|
|
1654
1931
|
width: sidebarW,
|
|
1655
1932
|
panel: activePanel
|
|
@@ -1659,7 +1936,8 @@ window.__ModuleLoader__.load({
|
|
|
1659
1936
|
sidebarOn,
|
|
1660
1937
|
sidebarW,
|
|
1661
1938
|
activePanel,
|
|
1662
|
-
sessionId
|
|
1939
|
+
sessionId,
|
|
1940
|
+
sidebarKey
|
|
1663
1941
|
]);
|
|
1664
1942
|
react.default.useEffect(() => {
|
|
1665
1943
|
const onKey = (e) => {
|
|
@@ -1825,6 +2103,7 @@ window.__ModuleLoader__.load({
|
|
|
1825
2103
|
}
|
|
1826
2104
|
if (editorRef.current || !monacoRef.current) return;
|
|
1827
2105
|
const m = monacoRef.current;
|
|
2106
|
+
const side = layoutRef.current === "side";
|
|
1828
2107
|
const ed = m.editor.create(node, {
|
|
1829
2108
|
value: "",
|
|
1830
2109
|
language: "plaintext",
|
|
@@ -1833,7 +2112,7 @@ window.__ModuleLoader__.load({
|
|
|
1833
2112
|
fontSize: 13,
|
|
1834
2113
|
lineHeight: 20,
|
|
1835
2114
|
minimap: {
|
|
1836
|
-
enabled:
|
|
2115
|
+
enabled: !side,
|
|
1837
2116
|
scale: 1
|
|
1838
2117
|
},
|
|
1839
2118
|
glyphMargin: true,
|
|
@@ -1845,7 +2124,7 @@ window.__ModuleLoader__.load({
|
|
|
1845
2124
|
renderWhitespace: "selection",
|
|
1846
2125
|
smoothScrolling: true,
|
|
1847
2126
|
cursorBlinking: "smooth",
|
|
1848
|
-
padding: { top: 8 }
|
|
2127
|
+
padding: { top: side ? 6 : 8 }
|
|
1849
2128
|
});
|
|
1850
2129
|
ed.addCommand(m.KeyMod.CtrlCmd | m.KeyCode.KeyS, () => {
|
|
1851
2130
|
flushSave();
|
|
@@ -2434,39 +2713,42 @@ window.__ModuleLoader__.load({
|
|
|
2434
2713
|
actHunk(hoverAct.region, true);
|
|
2435
2714
|
}
|
|
2436
2715
|
}, "↩ Undo")) : null;
|
|
2716
|
+
/** 组装差异 dock 快照(发布给 conversation.input.dock 的唯一 DiffBox 实例)。 */
|
|
2717
|
+
const buildDockSnapshot = () => ({
|
|
2718
|
+
mode: editorDockMode(active),
|
|
2719
|
+
pendingRegions,
|
|
2720
|
+
staleRegions,
|
|
2721
|
+
onAct: actHunk,
|
|
2722
|
+
onAcceptFile: acceptFile,
|
|
2723
|
+
onUndoFile: undoFile,
|
|
2724
|
+
onAcceptAllFiles: acceptAllFiles,
|
|
2725
|
+
onUndoAllFiles: undoAllFiles,
|
|
2726
|
+
allPendingCount: allPending.length,
|
|
2727
|
+
onRollback: rollbackFile,
|
|
2728
|
+
onJump: jumpTo,
|
|
2729
|
+
otherFiles,
|
|
2730
|
+
onOpenOther: (path) => openFile(path, true),
|
|
2731
|
+
onOpenLauncher: (tab) => {
|
|
2732
|
+
setLauncherTab(tab || "pending");
|
|
2733
|
+
setLauncherOpen(true);
|
|
2734
|
+
},
|
|
2735
|
+
onRefresh: reloadFile,
|
|
2736
|
+
activePath: active,
|
|
2737
|
+
diffIdx: contentReady ? diffIdx : 0,
|
|
2738
|
+
diffTotal: displayDiffTotal(contentReady, pendingRegions.length, sum.files.find((file) => file.path === active)?.pending ?? 0),
|
|
2739
|
+
fileIdx,
|
|
2740
|
+
fileTotal: sum.pendingFiles.length,
|
|
2741
|
+
onPrevDiff: () => gotoDiff(-1),
|
|
2742
|
+
onNextDiff: () => gotoDiff(1),
|
|
2743
|
+
onPrevFile: () => gotoFile(-1),
|
|
2744
|
+
onNextFile: () => gotoFile(1),
|
|
2745
|
+
onOpenNextFile: openNextFile
|
|
2746
|
+
});
|
|
2437
2747
|
react.default.useEffect(() => {
|
|
2438
2748
|
if (!sessionId) return;
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
pendingRegions,
|
|
2443
|
-
staleRegions,
|
|
2444
|
-
onAct: actHunk,
|
|
2445
|
-
onAcceptFile: acceptFile,
|
|
2446
|
-
onUndoFile: undoFile,
|
|
2447
|
-
onAcceptAllFiles: acceptAllFiles,
|
|
2448
|
-
onUndoAllFiles: undoAllFiles,
|
|
2449
|
-
allPendingCount: allPending.length,
|
|
2450
|
-
onRollback: rollbackFile,
|
|
2451
|
-
onJump: jumpTo,
|
|
2452
|
-
otherFiles,
|
|
2453
|
-
onOpenOther: (path) => openFile(path, true),
|
|
2454
|
-
onOpenLauncher: (tab) => {
|
|
2455
|
-
setLauncherTab(tab || "pending");
|
|
2456
|
-
setLauncherOpen(true);
|
|
2457
|
-
},
|
|
2458
|
-
onRefresh: reloadFile,
|
|
2459
|
-
activePath: active,
|
|
2460
|
-
diffIdx: contentReady ? diffIdx : 0,
|
|
2461
|
-
diffTotal: displayDiffTotal(contentReady, pendingRegions.length, sum.files.find((file) => file.path === active)?.pending ?? 0),
|
|
2462
|
-
fileIdx,
|
|
2463
|
-
fileTotal: sum.pendingFiles.length,
|
|
2464
|
-
onPrevDiff: () => gotoDiff(-1),
|
|
2465
|
-
onNextDiff: () => gotoDiff(1),
|
|
2466
|
-
onPrevFile: () => gotoFile(-1),
|
|
2467
|
-
onNextFile: () => gotoFile(1),
|
|
2468
|
-
onOpenNextFile: openNextFile
|
|
2469
|
-
}, dockSourceRef.current);
|
|
2749
|
+
publishDiffDock(sessionId, buildDockSnapshot(), dockSourceRef.current);
|
|
2750
|
+
if (layout === "side") setSidePending(sessionId, sum.totalFiles);
|
|
2751
|
+
else setSidePending(sessionId, 0);
|
|
2470
2752
|
});
|
|
2471
2753
|
const overlay = launcherOpen ? react.default.createElement(react.default.Fragment, null, react.default.createElement("div", {
|
|
2472
2754
|
style: {
|
|
@@ -2538,6 +2820,25 @@ window.__ModuleLoader__.load({
|
|
|
2538
2820
|
notify: (message) => setStatus(message)
|
|
2539
2821
|
};
|
|
2540
2822
|
const editorArea = react.default.createElement("div", { className: "edrv-editor-area" }, body, hoverEl, overlay);
|
|
2823
|
+
const dismissHint = () => {
|
|
2824
|
+
setHintDismissed(true);
|
|
2825
|
+
try {
|
|
2826
|
+
localStorage.setItem("edrv.side-hint-dismissed", "1");
|
|
2827
|
+
} catch (e) {}
|
|
2828
|
+
};
|
|
2829
|
+
const copyInstallCmd = () => {
|
|
2830
|
+
if (navigator.clipboard?.writeText) navigator.clipboard.writeText(SIDEBAR_INSTALL_CMD).catch(() => {});
|
|
2831
|
+
};
|
|
2832
|
+
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", {
|
|
2833
|
+
className: "edrv-pill edrv-pill-ghost",
|
|
2834
|
+
title: "复制安装命令",
|
|
2835
|
+
onClick: copyInstallCmd
|
|
2836
|
+
}, "复制命令"), react.default.createElement("button", {
|
|
2837
|
+
className: "edrv-side-hint-close",
|
|
2838
|
+
title: "关闭提示",
|
|
2839
|
+
"aria-label": "关闭提示",
|
|
2840
|
+
onClick: dismissHint
|
|
2841
|
+
}, "×")) : null;
|
|
2541
2842
|
const mainCol = react.default.createElement("div", {
|
|
2542
2843
|
className: "edrv-main-col",
|
|
2543
2844
|
style: {
|
|
@@ -2548,7 +2849,7 @@ window.__ModuleLoader__.load({
|
|
|
2548
2849
|
flexDirection: "column",
|
|
2549
2850
|
overflow: "hidden"
|
|
2550
2851
|
}
|
|
2551
|
-
}, pathBar, tabRow, editorArea);
|
|
2852
|
+
}, pathBar, tabRow, sideHintEl, editorArea);
|
|
2552
2853
|
const editorRow = react.default.createElement("div", { className: "edrv-editor-row" }, sidebarOn && sidebarPanels ? react.default.createElement(SidebarView, {
|
|
2553
2854
|
registry: sidebarPanels,
|
|
2554
2855
|
ctx: sidebarCtx,
|
|
@@ -2558,18 +2859,26 @@ window.__ModuleLoader__.load({
|
|
|
2558
2859
|
onWidth: setSidebarW,
|
|
2559
2860
|
onHide: () => setSidebarOn(false)
|
|
2560
2861
|
}) : null, mainCol);
|
|
2561
|
-
|
|
2862
|
+
const baseStyle = {
|
|
2863
|
+
minHeight: 0,
|
|
2864
|
+
display: "flex",
|
|
2865
|
+
flexDirection: "column",
|
|
2866
|
+
background: "var(--dsw-alias-bg-base,transparent)",
|
|
2867
|
+
overflow: "hidden"
|
|
2868
|
+
};
|
|
2869
|
+
return layout === "side" ? react.default.createElement("div", {
|
|
2562
2870
|
ref: viewRootRef,
|
|
2563
2871
|
"data-edrv-view": "1",
|
|
2564
|
-
|
|
2872
|
+
"data-edrv-layout": "side",
|
|
2873
|
+
className: "edrv-view-side",
|
|
2874
|
+
style: Object.assign({}, baseStyle, { height: "100%" })
|
|
2875
|
+
}, editorRow, menuBackdrop, tabMenuEl) : react.default.createElement("div", {
|
|
2876
|
+
ref: viewRootRef,
|
|
2877
|
+
"data-edrv-view": "1",
|
|
2878
|
+
style: Object.assign({}, baseStyle, {
|
|
2565
2879
|
height: "var(--edrv-editor-height, 100%)",
|
|
2566
|
-
maxHeight: "var(--edrv-editor-height, 100%)"
|
|
2567
|
-
|
|
2568
|
-
display: "flex",
|
|
2569
|
-
flexDirection: "column",
|
|
2570
|
-
background: "var(--dsw-alias-bg-base,transparent)",
|
|
2571
|
-
overflow: "hidden"
|
|
2572
|
-
}
|
|
2880
|
+
maxHeight: "var(--edrv-editor-height, 100%)"
|
|
2881
|
+
})
|
|
2573
2882
|
}, editorRow, menuBackdrop, tabMenuEl);
|
|
2574
2883
|
}
|
|
2575
2884
|
//#endregion
|
|
@@ -2584,14 +2893,14 @@ window.__ModuleLoader__.load({
|
|
|
2584
2893
|
*/
|
|
2585
2894
|
function DiffBadge(props) {
|
|
2586
2895
|
const sessionId = props?.sessionId;
|
|
2587
|
-
const [
|
|
2896
|
+
const [summary, setSummary] = react.default.useState(null);
|
|
2588
2897
|
const seq = react.default.useRef(0);
|
|
2589
2898
|
const load = react.default.useCallback(() => {
|
|
2590
2899
|
if (!sessionId) return;
|
|
2591
2900
|
const s = ++seq.current;
|
|
2592
2901
|
rpc("edrv.list", { sessionId }).then((res) => {
|
|
2593
2902
|
if (s !== seq.current || !res || !res.ok || !Array.isArray(res.records)) return;
|
|
2594
|
-
|
|
2903
|
+
setSummary(summarize(res.records));
|
|
2595
2904
|
}).catch(() => {});
|
|
2596
2905
|
}, [sessionId]);
|
|
2597
2906
|
react.default.useEffect(() => {
|
|
@@ -2604,9 +2913,10 @@ window.__ModuleLoader__.load({
|
|
|
2604
2913
|
window.removeEventListener("edrv:refresh", onRefresh);
|
|
2605
2914
|
};
|
|
2606
2915
|
}, [load]);
|
|
2916
|
+
const n = summary?.totalFiles ?? 0;
|
|
2607
2917
|
if (!n) return null;
|
|
2608
2918
|
const click = () => {
|
|
2609
|
-
openEditorView(null);
|
|
2919
|
+
openEditorView(summary?.pendingFiles?.[0]?.path ?? null);
|
|
2610
2920
|
setTimeout(() => {
|
|
2611
2921
|
emitShowLauncher();
|
|
2612
2922
|
}, 120);
|
|
@@ -3597,6 +3907,51 @@ window.__ModuleLoader__.load({
|
|
|
3597
3907
|
return typeof value === "string" && value.trim() ? value.trim() : AUTO_OPEN_TOOL;
|
|
3598
3908
|
}
|
|
3599
3909
|
//#endregion
|
|
3910
|
+
//#region src/client/ui/SideEditorTab.ts
|
|
3911
|
+
/**
|
|
3912
|
+
* dsh-vscode-mode client — SideEditorTab:betterSidebar「文件编辑」Tab 的组件包装。
|
|
3913
|
+
* 把 TabComponentProps(scope/tab/visible)映射为 EditorView 侧栏形态 props,
|
|
3914
|
+
* 并按会话 key 保证切会话状态干净重建;创建种子的初始打开在挂载后投递一次。
|
|
3915
|
+
* 作者 ddj 2026年08月25号
|
|
3916
|
+
*/
|
|
3917
|
+
/**
|
|
3918
|
+
* 侧边栏 Tab 组件(TabDescriptor.component 装配)。
|
|
3919
|
+
* @param props TabComponentProps(scope.sessionId、tab.path/meta 等)
|
|
3920
|
+
* @returns EditorView 侧栏形态
|
|
3921
|
+
*/
|
|
3922
|
+
function SideEditorTab(props) {
|
|
3923
|
+
const sessionId = props?.scope?.sessionId;
|
|
3924
|
+
const initial = react.default.useMemo(() => resolveInitialOpen(props?.tab), [props?.tab]);
|
|
3925
|
+
const dispatchedRef = react.default.useRef(false);
|
|
3926
|
+
react.default.useEffect(() => {
|
|
3927
|
+
setSideEditorMounted(true);
|
|
3928
|
+
return () => setSideEditorMounted(false);
|
|
3929
|
+
}, []);
|
|
3930
|
+
react.default.useEffect(() => {
|
|
3931
|
+
if (dispatchedRef.current) return;
|
|
3932
|
+
dispatchedRef.current = true;
|
|
3933
|
+
const request = takePendingSideOpen(sessionId) ?? initial;
|
|
3934
|
+
window.dispatchEvent(new CustomEvent("edrv:open-editor", { detail: {
|
|
3935
|
+
path: request?.path ?? null,
|
|
3936
|
+
focusDiff: request?.focusDiff === true
|
|
3937
|
+
} }));
|
|
3938
|
+
}, [
|
|
3939
|
+
initial.path,
|
|
3940
|
+
initial.focusDiff,
|
|
3941
|
+
sessionId
|
|
3942
|
+
]);
|
|
3943
|
+
return react.default.createElement(EditorView, {
|
|
3944
|
+
key: sessionId,
|
|
3945
|
+
sessionId,
|
|
3946
|
+
layout: "side",
|
|
3947
|
+
schedule: props?.schedule,
|
|
3948
|
+
addToConversation: props?.addToConversation,
|
|
3949
|
+
sidebarPanels: props?.sidebarPanels,
|
|
3950
|
+
outlineSources: props?.outlineSources,
|
|
3951
|
+
fileMenuItems: props?.fileMenuItems
|
|
3952
|
+
});
|
|
3953
|
+
}
|
|
3954
|
+
//#endregion
|
|
3600
3955
|
//#region src/client/addToConversation.ts
|
|
3601
3956
|
/** DSH reference source 名(dsh-client-ui-reference 注册的 @file/@session 统一源)。 */
|
|
3602
3957
|
const REF_SOURCE = "reference";
|
|
@@ -3775,10 +4130,50 @@ window.__ModuleLoader__.load({
|
|
|
3775
4130
|
};
|
|
3776
4131
|
}
|
|
3777
4132
|
//#endregion
|
|
4133
|
+
//#region src/client/ui/menuPosition.ts
|
|
4134
|
+
/**
|
|
4135
|
+
* 将菜单坐标限制在 viewport 内。
|
|
4136
|
+
* @author ddj 2026年08月28号
|
|
4137
|
+
* @param x 期望的 viewport x 坐标
|
|
4138
|
+
* @param y 期望的 viewport y 坐标
|
|
4139
|
+
* @param viewportWidth viewport 宽度
|
|
4140
|
+
* @param viewportHeight viewport 高度
|
|
4141
|
+
* @param menuWidth 菜单估算宽度
|
|
4142
|
+
* @param menuHeight 菜单估算高度
|
|
4143
|
+
* @returns 限制后的菜单位置
|
|
4144
|
+
*/
|
|
4145
|
+
function clampMenuPosition(x, y, viewportWidth, viewportHeight, menuWidth, menuHeight) {
|
|
4146
|
+
const maxLeft = Math.max(4, viewportWidth - menuWidth);
|
|
4147
|
+
const maxTop = Math.max(4, viewportHeight - menuHeight);
|
|
4148
|
+
return {
|
|
4149
|
+
left: Math.max(4, Math.min(x, maxLeft)),
|
|
4150
|
+
top: Math.max(4, Math.min(y, maxTop))
|
|
4151
|
+
};
|
|
4152
|
+
}
|
|
4153
|
+
/**
|
|
4154
|
+
* 将文件树行右键菜单锚定到目标行右侧;无法测量时回退到鼠标坐标。
|
|
4155
|
+
* @author ddj 2026年08月28号
|
|
4156
|
+
* @param rect 目标行的布局矩形
|
|
4157
|
+
* @param fallbackX 鼠标 viewport x 坐标
|
|
4158
|
+
* @param fallbackY 鼠标 viewport y 坐标
|
|
4159
|
+
* @returns 菜单初始位置
|
|
4160
|
+
*/
|
|
4161
|
+
function rowMenuPosition(rect, fallbackX, fallbackY) {
|
|
4162
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) return {
|
|
4163
|
+
left: fallbackX,
|
|
4164
|
+
top: fallbackY
|
|
4165
|
+
};
|
|
4166
|
+
return {
|
|
4167
|
+
left: rect.right + 4,
|
|
4168
|
+
top: rect.top
|
|
4169
|
+
};
|
|
4170
|
+
}
|
|
4171
|
+
//#endregion
|
|
3778
4172
|
//#region src/client/ui/ContextMenu.ts
|
|
3779
4173
|
/**
|
|
3780
4174
|
* dsh-vscode-mode client — 通用浮动右键菜单(createElement 风格、类型化)。
|
|
3781
|
-
*
|
|
4175
|
+
* 非阻塞式关闭:window capture 监听(点击菜单外/右键菜单外/Esc 关闭),
|
|
4176
|
+
* 不拦截菜单外事件,允许同一事件内由发起方切换/重开菜单。
|
|
3782
4177
|
* 条目支持 danger/disabled/separator,复用既有 edrv-ctxmenu* 样式类。
|
|
3783
4178
|
* 作者 ddj 2026-08-27
|
|
3784
4179
|
*/
|
|
@@ -3794,15 +4189,30 @@ window.__ModuleLoader__.load({
|
|
|
3794
4189
|
*/
|
|
3795
4190
|
function ContextMenu(props) {
|
|
3796
4191
|
const { x, y, entries, onClose } = props;
|
|
4192
|
+
const menuRef = react.default.useRef(null);
|
|
3797
4193
|
react.default.useEffect(() => {
|
|
3798
4194
|
const onKey = (e) => {
|
|
3799
4195
|
if (e.key === "Escape") onClose();
|
|
3800
4196
|
};
|
|
4197
|
+
const onPointerDown = (e) => {
|
|
4198
|
+
if (e.button !== 0) return;
|
|
4199
|
+
if (menuRef.current && menuRef.current.contains(e.target)) return;
|
|
4200
|
+
onClose();
|
|
4201
|
+
};
|
|
4202
|
+
const onContextMenu = (e) => {
|
|
4203
|
+
if (menuRef.current && menuRef.current.contains(e.target)) e.preventDefault();
|
|
4204
|
+
onClose();
|
|
4205
|
+
};
|
|
3801
4206
|
window.addEventListener("keydown", onKey, true);
|
|
3802
|
-
|
|
4207
|
+
window.addEventListener("pointerdown", onPointerDown, true);
|
|
4208
|
+
window.addEventListener("contextmenu", onContextMenu, true);
|
|
4209
|
+
return () => {
|
|
4210
|
+
window.removeEventListener("keydown", onKey, true);
|
|
4211
|
+
window.removeEventListener("pointerdown", onPointerDown, true);
|
|
4212
|
+
window.removeEventListener("contextmenu", onContextMenu, true);
|
|
4213
|
+
};
|
|
3803
4214
|
}, [onClose]);
|
|
3804
|
-
const
|
|
3805
|
-
const top = Math.max(4, Math.min(y, (window.innerHeight || 600) - MENU_H));
|
|
4215
|
+
const position = clampMenuPosition(Number.isFinite(x) ? x : 4, Number.isFinite(y) ? y : 4, window.innerWidth || 800, window.innerHeight || 600, MENU_W, MENU_H);
|
|
3806
4216
|
const children = [];
|
|
3807
4217
|
for (const entry of entries) {
|
|
3808
4218
|
if (entry.separator) {
|
|
@@ -3825,25 +4235,12 @@ window.__ModuleLoader__.load({
|
|
|
3825
4235
|
}
|
|
3826
4236
|
}, entry.label));
|
|
3827
4237
|
}
|
|
3828
|
-
|
|
3829
|
-
className: "edrv-ctxmenu-backdrop",
|
|
3830
|
-
style: {
|
|
3831
|
-
position: "fixed",
|
|
3832
|
-
inset: 0,
|
|
3833
|
-
zIndex: 70
|
|
3834
|
-
},
|
|
3835
|
-
onClick: onClose,
|
|
3836
|
-
onContextMenu: (e) => {
|
|
3837
|
-
e.preventDefault();
|
|
3838
|
-
onClose();
|
|
3839
|
-
}
|
|
3840
|
-
}), react.default.createElement("div", {
|
|
4238
|
+
const overlay = react.default.createElement("div", { "data-edrv-view": "1" }, react.default.createElement("div", {
|
|
3841
4239
|
className: "edrv-ctxmenu",
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
top
|
|
3845
|
-
}
|
|
4240
|
+
ref: menuRef,
|
|
4241
|
+
style: position
|
|
3846
4242
|
}, ...children));
|
|
4243
|
+
return typeof document === "undefined" ? overlay : (0, react_dom.createPortal)(overlay, document.body);
|
|
3847
4244
|
}
|
|
3848
4245
|
//#endregion
|
|
3849
4246
|
//#region src/client/sidebar/contextMenu.ts
|
|
@@ -3966,6 +4363,7 @@ window.__ModuleLoader__.load({
|
|
|
3966
4363
|
const refresh = () => {
|
|
3967
4364
|
const keep = Object.keys(expandedRef.current).filter((k) => expandedRef.current[k] === true);
|
|
3968
4365
|
tokensRef.current = {};
|
|
4366
|
+
setMenu(null);
|
|
3969
4367
|
setDirs({});
|
|
3970
4368
|
setLoading({});
|
|
3971
4369
|
setError(null);
|
|
@@ -3983,6 +4381,7 @@ window.__ModuleLoader__.load({
|
|
|
3983
4381
|
setExpanded({});
|
|
3984
4382
|
setLoading({});
|
|
3985
4383
|
setError(null);
|
|
4384
|
+
setMenu(null);
|
|
3986
4385
|
setRoot(null);
|
|
3987
4386
|
loadDir("");
|
|
3988
4387
|
}, [sessionId]);
|
|
@@ -3995,10 +4394,11 @@ window.__ModuleLoader__.load({
|
|
|
3995
4394
|
const rowEl = (e, depth, isDir, isOpen) => {
|
|
3996
4395
|
const pending = isDir ? 0 : pendingByPath[e.path] ?? 0;
|
|
3997
4396
|
const active = !isDir && e.path === activePath;
|
|
4397
|
+
const contextTarget = Boolean(menu && menuEntries.length && menu.target.path === e.path);
|
|
3998
4398
|
const dim = e.type === "other";
|
|
3999
4399
|
return react.default.createElement("div", {
|
|
4000
4400
|
key: e.path,
|
|
4001
|
-
className: "edrv-tree-row" + (active ? " edrv-tree-active" : ""),
|
|
4401
|
+
className: "edrv-tree-row" + (active ? " edrv-tree-active" : "") + (contextTarget ? " edrv-tree-context" : ""),
|
|
4002
4402
|
title: e.path,
|
|
4003
4403
|
style: { paddingLeft: 6 + depth * 14 },
|
|
4004
4404
|
onClick: () => {
|
|
@@ -4008,9 +4408,11 @@ window.__ModuleLoader__.load({
|
|
|
4008
4408
|
onContextMenu: (ev) => {
|
|
4009
4409
|
ev.preventDefault();
|
|
4010
4410
|
ev.stopPropagation();
|
|
4411
|
+
const rect = ev.currentTarget?.getBoundingClientRect?.();
|
|
4412
|
+
const position = rowMenuPosition(rect, ev.clientX, ev.clientY);
|
|
4011
4413
|
setMenu({
|
|
4012
|
-
x:
|
|
4013
|
-
y:
|
|
4414
|
+
x: position.left,
|
|
4415
|
+
y: position.top,
|
|
4014
4416
|
target: {
|
|
4015
4417
|
path: e.path,
|
|
4016
4418
|
type: e.type
|
|
@@ -4044,6 +4446,8 @@ window.__ModuleLoader__.load({
|
|
|
4044
4446
|
};
|
|
4045
4447
|
const onPanelContext = (ev) => {
|
|
4046
4448
|
ev.preventDefault();
|
|
4449
|
+
ev.stopPropagation();
|
|
4450
|
+
if (!ev.currentTarget.contains(ev.target)) return;
|
|
4047
4451
|
setMenu({
|
|
4048
4452
|
x: ev.clientX,
|
|
4049
4453
|
y: ev.clientY,
|
|
@@ -4862,7 +5266,8 @@ window.__ModuleLoader__.load({
|
|
|
4862
5266
|
//#region src/client/index.ts
|
|
4863
5267
|
/**
|
|
4864
5268
|
* dsh-vscode-mode client — 浏览器半入口:slot 注册 + 装配。
|
|
4865
|
-
* 挂点:
|
|
5269
|
+
* 挂点:betterSidebar「文件编辑」Tab(侧边栏形态,对话+编辑同屏;未装 dsh-better-sidebar 时
|
|
5270
|
+
* 回退 conversation.view 中央页签)+ conversation.input.dock 差异条 + header 差异角标。
|
|
4866
5271
|
* 与 Host 通信:同源 fetch('/edrv/rpc')(shared/rpc 契约)。
|
|
4867
5272
|
*
|
|
4868
5273
|
* ⚠️ 跨版本 slot 装配(2026-08-21):新版 DSH 的 slots 系统要求 slot 必须由父 entry
|
|
@@ -4915,6 +5320,8 @@ window.__ModuleLoader__.load({
|
|
|
4915
5320
|
const binder = pickSettingsBinder(ctx);
|
|
4916
5321
|
const settings = binder.scope;
|
|
4917
5322
|
let selected = autoValue("auto");
|
|
5323
|
+
/** 可选探测 betterSidebar 服务(不进 inject:缺失会让插件停靠等待,杀死回退路径)。 */
|
|
5324
|
+
const sideService = detectSidebarService(ctx);
|
|
4918
5325
|
/** 「添加到对话」动作集:编辑区/Tab 右键菜单注入文件引用与代码块(conversation 服务缺失时各动作安全降级)。 */
|
|
4919
5326
|
const addToConversation = createAddToConversation(ctx);
|
|
4920
5327
|
/** 客户端侧兼容摘要(与 host 报告合并展示;惰性求值保证 HMR 后仍新鲜)。 */
|
|
@@ -4933,6 +5340,11 @@ window.__ModuleLoader__.load({
|
|
|
4933
5340
|
name: "侧边栏打开器(dsh-better-sidebar)",
|
|
4934
5341
|
active: registry.get(SIDEBAR_PLUGIN) !== void 0,
|
|
4935
5342
|
note: registry.get("dsh-better-sidebar") !== void 0 ? "已注册(优先级 80)" : "未检测到侧边栏打开能力"
|
|
5343
|
+
},
|
|
5344
|
+
{
|
|
5345
|
+
name: "侧边栏编辑区(dsh-better-sidebar)",
|
|
5346
|
+
active: sideService !== void 0,
|
|
5347
|
+
note: sideService !== void 0 ? "编辑区=侧边栏 Tab(对话+编辑同屏)" : "未检测到;安装 dsh-better-sidebar 后刷新启用侧边栏形态(" + SIDEBAR_INSTALL_CMD + ")"
|
|
4936
5348
|
}
|
|
4937
5349
|
];
|
|
4938
5350
|
ctx.provide("fileOpeners", registry);
|
|
@@ -4982,19 +5394,47 @@ window.__ModuleLoader__.load({
|
|
|
4982
5394
|
},
|
|
4983
5395
|
logger: (message) => console.warn("[dsh-vscode-mode] " + message)
|
|
4984
5396
|
}), "vscode-mode: file link routing");
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
5397
|
+
const registerLegacyTab = () => {
|
|
5398
|
+
registerSlotSafely(ctx, {
|
|
5399
|
+
name: "conversation.view",
|
|
5400
|
+
id: "edrv-editor",
|
|
5401
|
+
order: 5,
|
|
5402
|
+
label: "文件编辑",
|
|
5403
|
+
inject: (sessionId) => ({ sessionId })
|
|
5404
|
+
}, (props) => react.default.createElement(EditorView, Object.assign({}, props, {
|
|
5405
|
+
layout: "tab",
|
|
5406
|
+
sideHint: SIDEBAR_INSTALL_CMD,
|
|
5407
|
+
schedule,
|
|
5408
|
+
addToConversation,
|
|
5409
|
+
sidebarPanels,
|
|
5410
|
+
outlineSources,
|
|
5411
|
+
fileMenuItems
|
|
5412
|
+
})));
|
|
5413
|
+
};
|
|
5414
|
+
if (sideService) ctx.effect(() => installSideEditor({
|
|
5415
|
+
service: sideService,
|
|
5416
|
+
renderTab: (props) => react.default.createElement(SideEditorTab, Object.assign({}, props, {
|
|
5417
|
+
schedule,
|
|
5418
|
+
addToConversation,
|
|
5419
|
+
sidebarPanels,
|
|
5420
|
+
outlineSources,
|
|
5421
|
+
fileMenuItems
|
|
5422
|
+
})),
|
|
5423
|
+
activeSession: () => {
|
|
5424
|
+
const snapshot = sessions?.list?.getSnapshot?.();
|
|
5425
|
+
const sessionId = snapshot?.current;
|
|
5426
|
+
if (!sessionId) return void 0;
|
|
5427
|
+
return {
|
|
5428
|
+
sessionId,
|
|
5429
|
+
cwd: snapshot?.byId?.[sessionId]?.cwd
|
|
5430
|
+
};
|
|
5431
|
+
},
|
|
5432
|
+
registerLegacyFallback: registerLegacyTab
|
|
5433
|
+
}), "vscode-mode: sidebar editor tab");
|
|
5434
|
+
else {
|
|
5435
|
+
setEnsureSideEditor(null);
|
|
5436
|
+
registerLegacyTab();
|
|
5437
|
+
}
|
|
4998
5438
|
registerSlotSafely(ctx, {
|
|
4999
5439
|
name: "conversation.input.dock",
|
|
5000
5440
|
id: "edrv-diff-dock",
|