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