dsh-vscode-mode 0.1.32 → 0.1.36
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/LICENSE +21 -0
- package/lib/client.js +1229 -124
- package/lib/client.js.map +1 -1
- package/lib/index.js +1959 -52
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/client/events.ts +11 -0
- package/src/client/index.ts +15 -2
- package/src/client/monaco/lsp/index.ts +30 -0
- package/src/client/monaco/lsp/lspClient.ts +192 -0
- package/src/client/monaco/lsp/providers.ts +229 -0
- package/src/client/outline/OutlinePanel.tsx +1 -1
- package/src/client/outline/lspSource.ts +49 -0
- package/src/client/outline/parse.ts +31 -1
- package/src/client/outline/types.ts +2 -0
- package/src/client/paths.ts +2 -0
- package/src/client/state/workspaceFoldCache.ts +95 -0
- package/src/client/styles/editor.css +15 -0
- package/src/client/styles/mcp.css +50 -12
- package/src/client/ui/EditorView.ts +55 -1
- package/src/client/ui/KeybindingsPanel.ts +8 -5
- package/src/client/ui/LspSettings.ts +274 -0
- package/src/client/ui/McpSettings.ts +112 -49
- package/src/client/ui/PerfSettings.ts +145 -34
- package/src/fileOpenSettings.ts +1 -0
- package/src/index.ts +20 -2
- package/src/lsp/client.ts +205 -0
- package/src/lsp/config.ts +107 -0
- package/src/lsp/extmgr.ts +208 -0
- package/src/lsp/jsonrpc.ts +86 -0
- package/src/lsp/manager.ts +133 -0
- package/src/lsp/providers.ts +235 -0
- package/src/lsp/rpc.ts +334 -0
- package/src/lsp/server.ts +346 -0
- package/src/lsp/settings.ts +44 -0
- package/src/lsp/transport.ts +143 -0
- package/src/lsp/uri.ts +62 -0
- package/src/lsp/zip.ts +82 -0
- package/src/rpc.ts +11 -2
- package/src/shared/lsp.ts +138 -0
- package/src/shared/rpc.ts +33 -0
package/lib/client.js
CHANGED
|
@@ -30,7 +30,7 @@ window.__ModuleLoader__.load({
|
|
|
30
30
|
react = __toESM(react, 1);
|
|
31
31
|
let react_dom = require("react-dom");
|
|
32
32
|
//#region \0dsh-css:/home/runner/work/DSH_VsCodeMode/DSH_VsCodeMode/src/client/styles/editor.css.mjs
|
|
33
|
-
const css$1 = "/* dsh-vscode-mode 编辑区样式(原 ensureEdrvStyles 字符串,落盘为真 .css,由 tsdown CSS-inline 注入) */\n/* 浅色主题(对齐参考图:#f8f8f8 近白底 + 薄荷绿药丸 + 青绿文字 + 三文鱼 undo + 浅灰边框):\n 在 [data-edrv-view] 作用域内重定义 --dsw-alias-*,级联命中组件内联样式与 CSS。 */\n[data-edrv-view] { box-sizing: border-box; --edrv-tab-h: 34px; --dsw-alias-bg-base: #f8f8f8; --dsw-alias-bg-layer-1: #ffffff; --dsw-alias-bg-layer-2: #f0f4f4; --dsw-alias-bg-overlay: #ffffff; --dsw-alias-label-primary: #1f2933; --dsw-alias-label-secondary: #52606d; --dsw-alias-label-tertiary: #9aa5b1; --dsw-alias-border-l1: #e0e6e8; --dsw-alias-border-l2: #cbd2d9; --dsw-alias-brand-primary: #0f9d58; --dsw-alias-state-success-primary: #0f9d58; --dsw-alias-state-error-primary: #d9534f; --dsw-alias-state-warn-primary: #d97706; --dsw-alias-interactive-bg-hover: rgba(15,157,88,.08); }\n\n[data-edrv-view] .edrv-tabs { display: flex; align-items: center; gap: 2px; padding: 2px 8px 0; background: var(--dsw-alias-bg-layer-1, transparent); overflow-x: auto; scrollbar-width: thin; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 8px 0 12px; border: none; border-radius: 6px 6px 0 0; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 12px; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-tab.edrv-tab-active { background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); box-shadow: inset 0 -2px 0 var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-tab .edrv-tab-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dsw-alias-brand-primary, #4f8cff); flex-shrink: 0; }\n[data-edrv-view] .edrv-tab .edrv-tab-x { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: 4px; color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; }\n[data-edrv-view] .edrv-tab .edrv-tab-x:hover { background: rgba(255,255,255,.12); color: var(--dsw-alias-label-primary, #eee); }\n[data-edrv-view] .edrv-tab-add { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; margin-left: 2px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 14px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab-add:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-pathbar { display: flex; align-items: center; gap: 8px; height: 26px; padding: 0 10px; background: var(--dsw-alias-bg-layer-1, #ffffff); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 12px; flex-shrink: 0; overflow: hidden; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-name { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-full { flex: 1; min-width: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; direction: rtl; text-align: left; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-meta { flex-shrink: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-status { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-path-input { flex: 1; min-width: 140px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-wrap { position: relative; flex-shrink: 0; display: flex; align-items: center; padding: 2px 8px 2px 4px; }\n[data-edrv-view] .edrv-search { width: 190px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-pop { position: absolute; top: calc(100% + 4px); right: 8px; width: min(460px, calc(100vw - 24px)); max-height: 300px; overflow: auto; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 10px; box-shadow: 0 6px 20px rgba(0,0,0,.4); z-index: 60; padding: 4px; }\n[data-edrv-view] .edrv-search-item { display: flex; flex-direction: column; gap: 1px; padding: 5px 8px; border-radius: 6px; cursor: pointer; }\n[data-edrv-view] .edrv-search-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.07)); }\n[data-edrv-view] .edrv-search-item .n { color: var(--dsw-alias-label-primary, #eee); font-size: 12px; word-break: break-all; }\n[data-edrv-view] .edrv-search-item .d { color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-search-empty { padding: 8px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #888); }\n[data-edrv-view] .edrv-btn-mini { font-size: 11px; padding: 2px 9px; border: none; border-radius: 999px; cursor: pointer; color: #fff; font-weight: 600; white-space: nowrap; }\n[data-edrv-view] .edrv-btn-keep { background: var(--dsw-alias-state-success-primary, #2e9e44); }\n[data-edrv-view] .edrv-btn-undo { background: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-btn-mini:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-statusbar { display: flex; align-items: center; gap: 10px; padding: 3px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #333); background: var(--dsw-alias-bg-layer-1, transparent); font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); flex-shrink: 0; white-space: nowrap; overflow: hidden; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-path { flex: 1; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-diffchip { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-state-warn-primary, #b7791f); cursor: pointer; white-space: nowrap; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn { font-size: 12px; padding: 2px 6px; border: none; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; border-radius: 4px; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-mn-add-line { background: rgba(15,157,88,.10); }\n[data-edrv-view] .edrv-mn-gutter-add { color: #0f9d58 !important; font-weight: 700; display: flex !important; align-items: center; justify-content: center; }\n[data-edrv-view] .edrv-mn-gutter-add::before { content: \"+\"; }\n[data-edrv-view] .edrv-del-zone { background: #fdeaea; border-top: 1px solid rgba(217,83,79,.35); border-bottom: 1px solid rgba(217,83,79,.35); box-shadow: inset 0 0 0 1px rgba(217,83,79,.12); }\n[data-edrv-view] .edrv-del-row { white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 12.5px; line-height: 20px; color: #c0392b; }\n[data-edrv-view] .edrv-del-text { display: inline-block; white-space: pre; }\n[data-edrv-view] .edrv-minus-overlay { position: absolute; left: 0; top: 0; width: 100%; height: 100%; max-width: 100%; overflow: visible; pointer-events: none; z-index: 6; }\n[data-edrv-view] .edrv-minus-item { position: absolute; box-sizing: border-box; max-width: 100%; overflow: hidden; }\n[data-edrv-view] .edrv-minus-item::before { content: ''; position: absolute; left: 4px; top: 50%; width: 8px; height: 2px; transform: translateY(-50%); border-radius: 2px; background: #d9534f; }\n[data-edrv-view] .edrv-monaco-host { flex: 1 1 auto; min-width: 0; min-height: 0; position: relative; overflow: hidden; }\n[data-edrv-view] .edrv-monaco-host > div { position: absolute; inset: 0; min-width: 0; min-height: 0; }\n[data-edrv-view] .edrv-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; height: 100%; color: var(--dsw-alias-label-tertiary, #777); font-size: 13px; }\n[data-edrv-view] .edrv-diffbar { position: absolute; left: 0; right: 0; bottom: 10px; margin: 0 auto; z-index: 25; width: fit-content; max-width: calc(100% - 20px); border-radius: 12px; background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, #f0f4f4)); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); box-shadow: none; overflow: visible; display: flex; flex-direction: column; }\n[data-edrv-view] .edrv-editor-area > .edrv-diffbar { max-height: calc(100% - 20px); }\n[data-edrv-view] .edrv-diffbar-row { display: flex; align-items: center; gap: 6px; height: 36px; padding: 4px 5px 4px 12px; flex-wrap: nowrap; }\n[data-edrv-view] .edrv-diffbar-count { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); min-width: 42px; text-align: center; white-space: nowrap; cursor: pointer; }\n[data-edrv-view] .edrv-diffbar-count.edrv-count-file { min-width: 64px; cursor: default; }\n[data-edrv-view] .edrv-diffbar-file { flex: 1; min-width: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-pill { font-size: 11px; padding: 3px 10px; border: none; border-radius: 999px; cursor: pointer; white-space: nowrap; font-weight: 600; }\n[data-edrv-view] .edrv-pill-keep { background: #e8f8e8; color: #0f7a45; border: 1px solid rgba(15,157,88,.4); }\n[data-edrv-view] .edrv-pill-undo { background: #fdeaea; color: #c0392b; border: 1px solid rgba(217,83,79,.4); }\n[data-edrv-view] .edrv-pill-ghost { background: #ffffff; color: var(--dsw-alias-label-secondary, #52606d); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-pill:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-diffbar-menu { position: absolute; right: 8px; bottom: calc(100% + 6px); z-index: 45; min-width: 160px; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 6px 20px rgba(31,41,51,.16); padding: 4px; display: flex; flex-direction: column; gap: 2px; }\n[data-edrv-view] .edrv-diffmenu-item { display: flex; align-items: center; gap: 8px; font-size: 12px; padding: 6px 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; text-align: left; }\n[data-edrv-view] .edrv-diffmenu-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-diffmenu-item.danger { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-diffmenu-sep { height: 1px; margin: 2px 4px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n/* 编辑区 / Tab 右键菜单(添加文件/选中内容到对话) */\n[data-edrv-view] .edrv-ctxmenu { position: fixed; z-index: 71; min-width: 200px; max-width: 320px; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 8px 26px rgba(31,41,51,.2); padding: 4px; display: flex; flex-direction: column; gap: 1px; }\n[data-edrv-view] .edrv-ctxmenu-item { display: flex; align-items: center; gap: 8px; font-size: 12px; line-height: 18px; padding: 7px 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-ctxmenu-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-ctxmenu-item.edrv-ctxmenu-danger { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-ctxmenu-item:disabled,\n[data-edrv-view] .edrv-ctxmenu-item.edrv-ctxmenu-disabled { color: var(--dsw-alias-label-disabled, #b0b7c0); cursor: default; background: transparent; }\n[data-edrv-view] .edrv-ctxmenu-sep { height: 1px; margin: 3px 4px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-diffbar-body { max-height: 130px; max-width: min(560px, calc(100vw - 40px)); overflow: auto; padding: 4px 8px 8px; display: flex; flex-direction: column; gap: 4px; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-diffrow { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); cursor: pointer; flex-wrap: wrap; }\n[data-edrv-view] .edrv-diffrow:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-diffrow-l { font-size: 11px; color: var(--dsw-alias-label-primary, #1f2933); min-width: 74px; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-o { font-size: 11px; color: var(--dsw-alias-state-error-primary, #d9534f); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-n { font-size: 11px; color: var(--dsw-alias-state-success-primary, #0f9d58); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-tag { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-diffrow-stale { border-color: rgba(217,119,6,.5); cursor: default; }\n[data-edrv-view] .edrv-diffbox-others { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: 4px 2px 0; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); margin-top: 2px; }\n[data-edrv-view] .edrv-diffrow-file { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); cursor: pointer; white-space: nowrap; max-width: 200px; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-diffrow-file:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n/* 编辑区 hover 差异块的 Keep/Undo 浮层 */\n[data-edrv-view] .edrv-hoveract { position: absolute; z-index: 30; display: flex; align-items: center; gap: 4px; padding: 2px 6px; border-radius: 8px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 4px 14px rgba(31,41,51,.14); }\n[data-edrv-view] .edrv-launch { position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 40; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); box-shadow: 0 6px 24px rgba(0,0,0,.5); display: flex; flex-direction: column; max-height: 60%; overflow: hidden; }\n[data-edrv-view] .edrv-launch-head { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-bottom: 1px solid var(--dsw-alias-border-l1, #333); }\n[data-edrv-view] .edrv-launch-tab { font-size: 12px; padding: 3px 12px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; }\n[data-edrv-view] .edrv-launch-tab.on { background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-label-primary, #eee); font-weight: 600; }\n[data-edrv-view] .edrv-launch-body { flex: 1; min-height: 0; overflow: auto; padding: 6px 8px; display: flex; flex-direction: column; gap: 4px; }\n[data-edrv-view] .edrv-launch-row { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 6px; cursor: pointer; flex-wrap: wrap; }\n[data-edrv-view] .edrv-launch-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-launch-path { flex: 1; min-width: 0; font-size: 12px; color: var(--dsw-alias-label-primary, #eee); word-break: break-all; }\n[data-edrv-view] .edrv-launch-cnt { font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); white-space: nowrap; }\n[data-edrv-view] .edrv-launch-arch { display: flex; flex-direction: column; gap: 6px; }\n[data-edrv-view] .monaco-editor { overflow: hidden !important; }\n[data-edrv-view] .monaco-editor .sticky-widget { z-index: 30 !important; background: var(--dsw-alias-bg-base, #f8f8f8) !important; }\n[data-edrv-view] .monaco-editor .sticky-widget .sticky-line-content,\n[data-edrv-view] .monaco-editor .sticky-widget .sticky-line-number { background: var(--dsw-alias-bg-base, #f8f8f8) !important; }\n[data-edrv-view] .monaco-editor, [data-edrv-view] .monaco-editor .margin, [data-edrv-view] .monaco-editor-background { background: transparent !important; }\n[data-edrv-view] .monaco-editor .scrollbar .slider { background: rgba(128,128,128,.3) !important; }\n[data-edrv-view] .edrv-loading { gap: 8px; padding: 20px; }\n[data-edrv-view] .edrv-loading-title { color: var(--dsw-alias-label-primary, #1f2933); font-size: 13px; }\n[data-edrv-view] .edrv-progress { width: min(360px, 72%); height: 6px; overflow: hidden; border-radius: 999px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-progress-value { height: 100%; border-radius: inherit; background: var(--dsw-alias-brand-primary, #0f9d58); transition: width .24s ease; }\n[data-edrv-view] .edrv-loading-percent { color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; font-variant-numeric: tabular-nums; }\n@media (prefers-reduced-motion: reduce) {\n [data-edrv-view] .edrv-progress-value { transition: none; }\n}\n\n/* ===== 唯一会话级差异 dock(遵循 DSH todo/queue 原生布局) ===== */\n[data-edrv-diff-dock-shell] {\n box-sizing: border-box;\n width: calc(100% - var(--dsh-composer-side-clearance) - var(--dsh-composer-side-clearance) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));\n max-width: calc(var(--dsh-composer-card-max-width) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));\n margin: 0 auto;\n padding: 0;\n flex: none;\n border: 1px solid var(--dsw-alias-border-l1, #e0e6e8);\n border-radius: 12px;\n background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, transparent));\n box-shadow: none;\n overflow: hidden;\n}\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock {\n box-sizing: border-box;\n position: relative;\n left: auto;\n right: auto;\n bottom: auto;\n z-index: auto;\n width: 100%;\n max-width: none;\n margin: 0;\n display: flex;\n flex-direction: column;\n border: none;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n overflow: visible;\n}\n[data-edrv-diff-dock-shell] .edrv-diffbar-row { min-height: 36px; padding: 6px 12px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-row { min-height: 36px; padding: 6px 12px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-body { max-height: 180px; overflow: auto; padding: 6px 12px 10px; border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-button { box-sizing: border-box; display: flex; align-items: center; justify-content: center; width: 100%; height: 24px; border: none; border-radius: 8px; padding: 0 12px; background: transparent; color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 500; line-height: 24px; text-align: center; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-button:hover { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diffbar-count { display: inline-flex; align-items: center; color: var(--dsw-alias-label-primary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 500; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-file { display: inline-flex; align-items: center; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-editor-row { display: flex; align-items: center; height: 36px; min-height: 36px; padding: 4px 12px; overflow-x: auto; scrollbar-width: thin; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-editor-content { display: flex; align-items: center; gap: 10px; width: 100%; min-width: max-content; min-height: 28px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-nav,\n[data-edrv-diff-dock-shell] .edrv-diffbar-files,\n[data-edrv-diff-dock-shell] .edrv-diffbar-actions { display: flex; align-items: center; gap: 6px; flex: none; height: 28px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-actions { margin-left: auto; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-details-toggle { display: inline-flex; align-items: center; justify-content: center; width: 28px; min-width: 28px; padding: 0; font-size: 13px; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-details-toggle svg { width: 14px; height: 14px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-files { min-width: 0; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-files .edrv-diffbar-file { min-width: 72px; max-width: 240px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-progress { min-width: 0; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; font-variant-numeric: tabular-nums; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-summary { min-width: 0; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-pill { box-sizing: border-box; height: 28px; padding: 0 9px; border: none; border-radius: 999px; cursor: pointer; white-space: nowrap; font-size: 11px; line-height: 26px; font-weight: 600; }\n[data-edrv-diff-dock-shell] .edrv-pill-keep { background: var(--dsw-alias-state-success-tertiary); color: var(--dsw-alias-state-success-primary); border: 1px solid var(--dsw-alias-state-success-secondary); }\n[data-edrv-diff-dock-shell] .edrv-pill-undo { background: var(--dsw-alias-state-error-tertiary); color: var(--dsw-alias-state-error-primary); border: 1px solid var(--dsw-alias-state-error-secondary); }\n[data-edrv-diff-dock-shell] .edrv-pill-ghost { background: transparent; color: var(--dsw-alias-label-secondary); border: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-pill:hover:not(:disabled) { background-color: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-pill:focus-visible { outline: 2px solid var(--dsw-alias-label-tertiary); outline-offset: -2px; }\n[data-edrv-diff-dock-shell] .edrv-pill:disabled { opacity: .45; cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diffrow { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; background: transparent; cursor: pointer; flex-wrap: wrap; }\n[data-edrv-diff-dock-shell] .edrv-diffrow:hover { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-l { color: var(--dsw-alias-label-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-o { color: var(--dsw-alias-state-error-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-n { color: var(--dsw-alias-state-success-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-tag { color: var(--dsw-alias-label-tertiary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-stale { border-color: var(--dsw-alias-state-warn-secondary); cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diffbox-others { border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-file { border-color: var(--dsw-alias-border-l1); background: transparent; color: var(--dsw-alias-label-secondary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-file:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }\n[data-edrv-diff-dock-shell] .edrv-diff-details { display: flex; flex-direction: column; gap: 2px; padding: 6px 12px 8px; border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item { display: flex; align-items: center; width: 100%; min-height: 28px; gap: 8px; padding: 4px 8px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary); cursor: pointer; text-align: left; font-size: 12px; line-height: 20px; }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item.danger { color: var(--dsw-alias-state-error-primary); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item:disabled { opacity: .45; cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-sep { height: 1px; margin: 2px 4px; background: var(--dsw-alias-border-l1); }\n@media (max-width: 620px) {\n [data-edrv-diff-dock-shell] .edrv-diffbar-editor-content { width: max-content; }\n [data-edrv-diff-dock-shell] .edrv-diffbar-files .edrv-diffbar-file { max-width: 160px; }\n}\n\n/* ===== 侧边栏(活动栏 + 面板区 + 文件树) ===== */\n[data-edrv-view] .edrv-editor-row { display: flex; flex: 1 1 auto; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-main-col { box-sizing: border-box; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-main-col > .edrv-pathbar,\n[data-edrv-view] .edrv-main-col > .edrv-tabs { flex: 0 0 auto; }\n[data-edrv-view] .edrv-editor-area { position: relative; display: flex; flex: 1 1 auto; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-sidebar { display: flex; flex-direction: row; flex-shrink: 0; min-width: 0; height: 100%; background: var(--dsw-alias-bg-layer-1, #ffffff); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); overflow: hidden; }\n[data-edrv-view] .edrv-rail { display: flex; flex-direction: column; align-items: center; width: 44px; flex-shrink: 0; padding: 6px 0; background: var(--dsw-alias-bg-layer-2, #f0f4f4); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); gap: 4px; }\n[data-edrv-view] .edrv-rail-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: none; border-radius: 8px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rail-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rail-btn.edrv-rail-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); box-shadow: inset 2px 0 0 var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-rail-icon { font-size: 15px; line-height: 1; }\n[data-edrv-view] .edrv-rail-badge { position: absolute; top: 2px; right: 2px; min-width: 14px; height: 14px; padding: 0 3px; border-radius: 999px; background: var(--dsw-alias-brand-primary, #0f9d58); color: #fff; font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; }\n[data-edrv-view] .edrv-side-body { flex: 1; min-width: 0; min-height: 0; display: flex; overflow: hidden; }\n[data-edrv-view] .edrv-side-panel { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; position: relative; }\n[data-edrv-view] .edrv-side-head { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px 0 12px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); flex-shrink: 0; }\n[data-edrv-view] .edrv-side-title { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-side-root { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-side-btn { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-side-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-tree { flex: 1; min-height: 0; overflow: auto; padding: 4px 6px 12px; font-size: 12px; }\n[data-edrv-view] .edrv-tree-row { display: flex; align-items: center; gap: 4px; height: 22px; padding-right: 6px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-tree-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-active { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-context { box-shadow: inset 0 0 0 1px var(--dsw-alias-brand-primary, #0f9d58); background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); }\n[data-edrv-view] .edrv-tree-chev { width: 12px; flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; text-align: center; }\n[data-edrv-view] .edrv-tree-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-tree-name.edrv-tree-dim { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-tree-badge { min-width: 14px; height: 14px; padding: 0 4px; border-radius: 999px; background: var(--dsw-alias-state-warn-primary, #d97706); color: #fff; font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; flex-shrink: 0; }\n[data-edrv-view] .edrv-tree-loading { height: 22px; line-height: 22px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-tree-error { display: flex; align-items: center; gap: 8px; margin: 4px 2px 6px; padding: 6px 8px; border: 1px solid rgba(217,83,79,.4); border-radius: 6px; background: #fdeaea; color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n/* 大纲面板:kind 字形 / detail / 行号 */\n[data-edrv-view] .edrv-outline-kind { display: inline-flex; align-items: center; justify-content: center; width: 14px; flex-shrink: 0; font-size: 12px; font-weight: 600; text-align: center; }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-func { color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-type { color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-data { color: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-ns { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-key { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-outline-detail { flex-shrink: 0; max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-outline-ln { flex-shrink: 0; padding-right: 2px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-side-resize { width: 5px; flex-shrink: 0; cursor: col-resize; background: transparent; }\n[data-edrv-view] .edrv-side-resize:hover { background: var(--dsw-alias-brand-primary, #0f9d58); opacity: .35; }\n[data-edrv-view] .edrv-chip-btn.edrv-chip-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); }\n\n/* ===== 侧栏形态(betterSidebar 面板内编辑区) ===== */\n[data-edrv-view][data-edrv-layout='side'] { box-sizing: border-box; height: 100%; }\n\n/* ===== 侧边栏引导条(旧页签形态、未装 betterSidebar 时) ===== */\n[data-edrv-view] .edrv-side-hint { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; flex-wrap: wrap; padding: 6px 12px; font-size: 12px; color: var(--dsw-alias-label-secondary, #52606d); background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, #f0f4f4)); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-side-hint-text { white-space: nowrap; }\n[data-edrv-view] .edrv-side-hint-cmd { font-size: 11px; padding: 2px 6px; border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #ffffff); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-side-hint-close { margin-left: auto; border: none; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); cursor: pointer; font-size: 14px; line-height: 1; padding: 2px 6px; border-radius: 6px; flex-shrink: 0; }\n[data-edrv-view] .edrv-side-hint-close:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n\n/* ===== search panel (sidebar, VSCode search view) ===== */\n[data-edrv-view] .edrv-search-panel { display: flex; flex-direction: column; min-height: 0; flex: 1; padding: 6px; gap: 4px; }\n[data-edrv-view] .edrv-search-qrow { display: flex; gap: 4px; }\n[data-edrv-view] .edrv-search-input { flex: 1; min-width: 0; height: 26px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-input:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-opts { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-search-opt { min-width: 24px; height: 22px; padding: 0 6px; border: 1px solid transparent; border-radius: 5px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 11px; font-family: var(--ds-font-family-code, ui-monospace, monospace); cursor: pointer; }\n[data-edrv-view] .edrv-search-opt:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-opt.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); border-color: var(--dsw-alias-border-l2, #d5dde3); }\n[data-edrv-view] .edrv-search-spacer { flex: 1; }\n[data-edrv-view] .edrv-search-summary { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); padding: 2px 4px; }\n[data-edrv-view] .edrv-search-results { flex: 1; min-height: 0; overflow: auto; font-size: 12px; }\n[data-edrv-view] .edrv-search-group { margin-bottom: 2px; }\n[data-edrv-view] .edrv-search-file { display: flex; align-items: center; gap: 4px; height: 22px; padding: 0 4px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-search-file:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-chev { width: 12px; flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; text-align: center; }\n[data-edrv-view] .edrv-search-fname { font-weight: 600; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-search-fdir { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-search-fcount { flex-shrink: 0; min-width: 16px; height: 15px; padding: 0 4px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); font-size: 10px; line-height: 15px; text-align: center; }\n[data-edrv-view] .edrv-search-line { display: flex; gap: 6px; padding: 1px 4px 1px 16px; border-radius: 4px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; overflow: hidden; }\n[data-edrv-view] .edrv-search-line.alt { background: var(--dsw-alias-bg-layer-1, #ffffff); }\n[data-edrv-view] .edrv-search-line:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-ln { flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; min-width: 26px; text-align: right; user-select: none; }\n[data-edrv-view] .edrv-search-text { overflow: hidden; text-overflow: ellipsis; white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; }\n[data-edrv-view] .edrv-search-hit { background: var(--dsw-alias-state-warn-primary, #e5b93f); color: #1f2933; border-radius: 2px; padding: 0 1px; }\n[data-edrv-view] .edrv-search-empty { padding: 8px 4px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-search-error { padding: 6px 8px; border: 1px solid 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-search-warn { padding: 6px 8px; border: 1px solid rgba(240,173,78,.45); border-radius: 6px; background: #fdf6e3; color: #b8860b; font-size: 11px; word-break: break-all; }\n\n/* include/exclude filter rows (collapsible) */\n[data-edrv-view] .edrv-search-rows { display: flex; flex-direction: column; gap: 4px; padding: 2px 0 4px; }\n[data-edrv-view] .edrv-search-row { display: flex; align-items: center; gap: 4px; }\n[data-edrv-view] .edrv-search-field { flex: 1; min-width: 0; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 11px; outline: none; }\n[data-edrv-view] .edrv-search-field:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-field.dim { opacity: .45; }\n[data-edrv-view] .edrv-search-field::placeholder { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-search-toggle { min-width: 24px; height: 24px; padding: 0 5px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; line-height: 1; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-search-toggle:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-toggle.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); border-color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-search-toggle:disabled { opacity: .4; cursor: default; }\n\n";
|
|
33
|
+
const css$1 = "/* dsh-vscode-mode 编辑区样式(原 ensureEdrvStyles 字符串,落盘为真 .css,由 tsdown CSS-inline 注入) */\n/* 浅色主题(对齐参考图:#f8f8f8 近白底 + 薄荷绿药丸 + 青绿文字 + 三文鱼 undo + 浅灰边框):\n 在 [data-edrv-view] 作用域内重定义 --dsw-alias-*,级联命中组件内联样式与 CSS。 */\n[data-edrv-view] { box-sizing: border-box; --edrv-tab-h: 34px; --dsw-alias-bg-base: #f8f8f8; --dsw-alias-bg-layer-1: #ffffff; --dsw-alias-bg-layer-2: #f0f4f4; --dsw-alias-bg-overlay: #ffffff; --dsw-alias-label-primary: #1f2933; --dsw-alias-label-secondary: #52606d; --dsw-alias-label-tertiary: #9aa5b1; --dsw-alias-border-l1: #e0e6e8; --dsw-alias-border-l2: #cbd2d9; --dsw-alias-brand-primary: #0f9d58; --dsw-alias-state-success-primary: #0f9d58; --dsw-alias-state-error-primary: #d9534f; --dsw-alias-state-warn-primary: #d97706; --dsw-alias-interactive-bg-hover: rgba(15,157,88,.08); }\n\n[data-edrv-view] .edrv-tabs { display: flex; align-items: center; gap: 2px; padding: 2px 8px 0; background: var(--dsw-alias-bg-layer-1, transparent); overflow-x: auto; scrollbar-width: thin; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 8px 0 12px; border: none; border-radius: 6px 6px 0 0; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 12px; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-tab.edrv-tab-active { background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); box-shadow: inset 0 -2px 0 var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-tab .edrv-tab-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dsw-alias-brand-primary, #4f8cff); flex-shrink: 0; }\n[data-edrv-view] .edrv-tab .edrv-tab-x { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: 4px; color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; }\n[data-edrv-view] .edrv-tab .edrv-tab-x:hover { background: rgba(255,255,255,.12); color: var(--dsw-alias-label-primary, #eee); }\n[data-edrv-view] .edrv-tab-add { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; margin-left: 2px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 14px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab-add:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-pathbar { display: flex; align-items: center; gap: 8px; height: 26px; padding: 0 10px; background: var(--dsw-alias-bg-layer-1, #ffffff); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 12px; flex-shrink: 0; overflow: hidden; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-name { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-full { flex: 1; min-width: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; direction: rtl; text-align: left; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-meta { flex-shrink: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-status { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-path-input { flex: 1; min-width: 140px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-wrap { position: relative; flex-shrink: 0; display: flex; align-items: center; padding: 2px 8px 2px 4px; }\n[data-edrv-view] .edrv-search { width: 190px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-pop { position: absolute; top: calc(100% + 4px); right: 8px; width: min(460px, calc(100vw - 24px)); max-height: 300px; overflow: auto; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 10px; box-shadow: 0 6px 20px rgba(0,0,0,.4); z-index: 60; padding: 4px; }\n[data-edrv-view] .edrv-search-item { display: flex; flex-direction: column; gap: 1px; padding: 5px 8px; border-radius: 6px; cursor: pointer; }\n[data-edrv-view] .edrv-search-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.07)); }\n[data-edrv-view] .edrv-search-item .n { color: var(--dsw-alias-label-primary, #eee); font-size: 12px; word-break: break-all; }\n[data-edrv-view] .edrv-search-item .d { color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-search-empty { padding: 8px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #888); }\n[data-edrv-view] .edrv-btn-mini { font-size: 11px; padding: 2px 9px; border: none; border-radius: 999px; cursor: pointer; color: #fff; font-weight: 600; white-space: nowrap; }\n[data-edrv-view] .edrv-btn-keep { background: var(--dsw-alias-state-success-primary, #2e9e44); }\n[data-edrv-view] .edrv-btn-undo { background: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-btn-mini:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-statusbar { display: flex; align-items: center; gap: 10px; padding: 3px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #333); background: var(--dsw-alias-bg-layer-1, transparent); font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); flex-shrink: 0; white-space: nowrap; overflow: hidden; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-path { flex: 1; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-lsp { flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-progress-message { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-statusbar .edrv-sp-progress { flex: 0 0 auto; min-width: 36px; text-align: right; font-variant-numeric: tabular-nums; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-lsp-status-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; background: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-lsp-status-dot.ready { background: var(--dsw-alias-state-success-primary, #0f9d58); }\n[data-edrv-view] .edrv-lsp-status-dot.busy { background: var(--dsw-alias-state-warn-primary, #d97706); animation: edrv-lsp-pulse 1.2s ease-in-out infinite; }\n@keyframes edrv-lsp-pulse { 50% { opacity: .45; } }\n[data-edrv-view] .edrv-diffchip { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-state-warn-primary, #b7791f); cursor: pointer; white-space: nowrap; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn { font-size: 12px; padding: 2px 6px; border: none; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; border-radius: 4px; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-mn-add-line { background: rgba(15,157,88,.10); }\n[data-edrv-view] .edrv-mn-gutter-add { color: #0f9d58 !important; font-weight: 700; display: flex !important; align-items: center; justify-content: center; }\n[data-edrv-view] .edrv-mn-gutter-add::before { content: \"+\"; }\n[data-edrv-view] .edrv-del-zone { background: #fdeaea; border-top: 1px solid rgba(217,83,79,.35); border-bottom: 1px solid rgba(217,83,79,.35); box-shadow: inset 0 0 0 1px rgba(217,83,79,.12); }\n[data-edrv-view] .edrv-del-row { white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 12.5px; line-height: 20px; color: #c0392b; }\n[data-edrv-view] .edrv-del-text { display: inline-block; white-space: pre; }\n[data-edrv-view] .edrv-minus-overlay { position: absolute; left: 0; top: 0; width: 100%; height: 100%; max-width: 100%; overflow: visible; pointer-events: none; z-index: 6; }\n[data-edrv-view] .edrv-minus-item { position: absolute; box-sizing: border-box; max-width: 100%; overflow: hidden; }\n[data-edrv-view] .edrv-minus-item::before { content: ''; position: absolute; left: 4px; top: 50%; width: 8px; height: 2px; transform: translateY(-50%); border-radius: 2px; background: #d9534f; }\n[data-edrv-view] .edrv-monaco-host { flex: 1 1 auto; min-width: 0; min-height: 0; position: relative; overflow: hidden; }\n[data-edrv-view] .edrv-monaco-host > div { position: absolute; inset: 0; min-width: 0; min-height: 0; }\n[data-edrv-view] .edrv-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; height: 100%; color: var(--dsw-alias-label-tertiary, #777); font-size: 13px; }\n[data-edrv-view] .edrv-diffbar { position: absolute; left: 0; right: 0; bottom: 10px; margin: 0 auto; z-index: 25; width: fit-content; max-width: calc(100% - 20px); border-radius: 12px; background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, #f0f4f4)); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); box-shadow: none; overflow: visible; display: flex; flex-direction: column; }\n[data-edrv-view] .edrv-editor-area > .edrv-diffbar { max-height: calc(100% - 20px); }\n[data-edrv-view] .edrv-diffbar-row { display: flex; align-items: center; gap: 6px; height: 36px; padding: 4px 5px 4px 12px; flex-wrap: nowrap; }\n[data-edrv-view] .edrv-diffbar-count { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); min-width: 42px; text-align: center; white-space: nowrap; cursor: pointer; }\n[data-edrv-view] .edrv-diffbar-count.edrv-count-file { min-width: 64px; cursor: default; }\n[data-edrv-view] .edrv-diffbar-file { flex: 1; min-width: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-pill { font-size: 11px; padding: 3px 10px; border: none; border-radius: 999px; cursor: pointer; white-space: nowrap; font-weight: 600; }\n[data-edrv-view] .edrv-pill-keep { background: #e8f8e8; color: #0f7a45; border: 1px solid rgba(15,157,88,.4); }\n[data-edrv-view] .edrv-pill-undo { background: #fdeaea; color: #c0392b; border: 1px solid rgba(217,83,79,.4); }\n[data-edrv-view] .edrv-pill-ghost { background: #ffffff; color: var(--dsw-alias-label-secondary, #52606d); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-pill:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-diffbar-menu { position: absolute; right: 8px; bottom: calc(100% + 6px); z-index: 45; min-width: 160px; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 6px 20px rgba(31,41,51,.16); padding: 4px; display: flex; flex-direction: column; gap: 2px; }\n[data-edrv-view] .edrv-diffmenu-item { display: flex; align-items: center; gap: 8px; font-size: 12px; padding: 6px 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; text-align: left; }\n[data-edrv-view] .edrv-diffmenu-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-diffmenu-item.danger { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-diffmenu-sep { height: 1px; margin: 2px 4px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n/* 编辑区 / Tab 右键菜单(添加文件/选中内容到对话) */\n[data-edrv-view] .edrv-ctxmenu { position: fixed; z-index: 71; min-width: 200px; max-width: 320px; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 8px 26px rgba(31,41,51,.2); padding: 4px; display: flex; flex-direction: column; gap: 1px; }\n[data-edrv-view] .edrv-ctxmenu-item { display: flex; align-items: center; gap: 8px; font-size: 12px; line-height: 18px; padding: 7px 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-ctxmenu-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-ctxmenu-item.edrv-ctxmenu-danger { color: var(--dsw-alias-state-error-primary, #d9534f); }\n[data-edrv-view] .edrv-ctxmenu-item:disabled,\n[data-edrv-view] .edrv-ctxmenu-item.edrv-ctxmenu-disabled { color: var(--dsw-alias-label-disabled, #b0b7c0); cursor: default; background: transparent; }\n[data-edrv-view] .edrv-ctxmenu-sep { height: 1px; margin: 3px 4px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-diffbar-body { max-height: 130px; max-width: min(560px, calc(100vw - 40px)); overflow: auto; padding: 4px 8px 8px; display: flex; flex-direction: column; gap: 4px; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-diffrow { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); cursor: pointer; flex-wrap: wrap; }\n[data-edrv-view] .edrv-diffrow:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-diffrow-l { font-size: 11px; color: var(--dsw-alias-label-primary, #1f2933); min-width: 74px; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-o { font-size: 11px; color: var(--dsw-alias-state-error-primary, #d9534f); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-n { font-size: 11px; color: var(--dsw-alias-state-success-primary, #0f9d58); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-diffrow-tag { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-diffrow-stale { border-color: rgba(217,119,6,.5); cursor: default; }\n[data-edrv-view] .edrv-diffbox-others { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: 4px 2px 0; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); margin-top: 2px; }\n[data-edrv-view] .edrv-diffrow-file { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); cursor: pointer; white-space: nowrap; max-width: 200px; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-diffrow-file:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n/* 编辑区 hover 差异块的 Keep/Undo 浮层 */\n[data-edrv-view] .edrv-hoveract { position: absolute; z-index: 30; display: flex; align-items: center; gap: 4px; padding: 2px 6px; border-radius: 8px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 4px 14px rgba(31,41,51,.14); }\n[data-edrv-view] .edrv-launch { position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 40; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); box-shadow: 0 6px 24px rgba(0,0,0,.5); display: flex; flex-direction: column; max-height: 60%; overflow: hidden; }\n[data-edrv-view] .edrv-launch-head { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-bottom: 1px solid var(--dsw-alias-border-l1, #333); }\n[data-edrv-view] .edrv-launch-tab { font-size: 12px; padding: 3px 12px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; }\n[data-edrv-view] .edrv-launch-tab.on { background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-label-primary, #eee); font-weight: 600; }\n[data-edrv-view] .edrv-launch-body { flex: 1; min-height: 0; overflow: auto; padding: 6px 8px; display: flex; flex-direction: column; gap: 4px; }\n[data-edrv-view] .edrv-launch-row { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 6px; cursor: pointer; flex-wrap: wrap; }\n[data-edrv-view] .edrv-launch-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-launch-path { flex: 1; min-width: 0; font-size: 12px; color: var(--dsw-alias-label-primary, #eee); word-break: break-all; }\n[data-edrv-view] .edrv-launch-cnt { font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); white-space: nowrap; }\n[data-edrv-view] .edrv-launch-arch { display: flex; flex-direction: column; gap: 6px; }\n[data-edrv-view] .monaco-editor { overflow: hidden !important; }\n[data-edrv-view] .monaco-editor .sticky-widget { z-index: 30 !important; background: var(--dsw-alias-bg-base, #f8f8f8) !important; }\n[data-edrv-view] .monaco-editor .sticky-widget .sticky-line-content,\n[data-edrv-view] .monaco-editor .sticky-widget .sticky-line-number { background: var(--dsw-alias-bg-base, #f8f8f8) !important; }\n[data-edrv-view] .monaco-editor, [data-edrv-view] .monaco-editor .margin, [data-edrv-view] .monaco-editor-background { background: transparent !important; }\n[data-edrv-view] .monaco-editor .scrollbar .slider { background: rgba(128,128,128,.3) !important; }\n[data-edrv-view] .edrv-loading { gap: 8px; padding: 20px; }\n[data-edrv-view] .edrv-loading-title { color: var(--dsw-alias-label-primary, #1f2933); font-size: 13px; }\n[data-edrv-view] .edrv-progress { width: min(360px, 72%); height: 6px; overflow: hidden; border-radius: 999px; background: var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-progress-value { height: 100%; border-radius: inherit; background: var(--dsw-alias-brand-primary, #0f9d58); transition: width .24s ease; }\n[data-edrv-view] .edrv-loading-percent { color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; font-variant-numeric: tabular-nums; }\n@media (prefers-reduced-motion: reduce) {\n [data-edrv-view] .edrv-progress-value { transition: none; }\n}\n\n/* ===== 唯一会话级差异 dock(遵循 DSH todo/queue 原生布局) ===== */\n[data-edrv-diff-dock-shell] {\n box-sizing: border-box;\n width: calc(100% - var(--dsh-composer-side-clearance) - var(--dsh-composer-side-clearance) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));\n max-width: calc(var(--dsh-composer-card-max-width) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));\n margin: 0 auto;\n padding: 0;\n flex: none;\n border: 1px solid var(--dsw-alias-border-l1, #e0e6e8);\n border-radius: 12px;\n background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, transparent));\n box-shadow: none;\n overflow: hidden;\n}\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock {\n box-sizing: border-box;\n position: relative;\n left: auto;\n right: auto;\n bottom: auto;\n z-index: auto;\n width: 100%;\n max-width: none;\n margin: 0;\n display: flex;\n flex-direction: column;\n border: none;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n overflow: visible;\n}\n[data-edrv-diff-dock-shell] .edrv-diffbar-row { min-height: 36px; padding: 6px 12px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-row { min-height: 36px; padding: 6px 12px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-body { max-height: 180px; overflow: auto; padding: 6px 12px 10px; border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-button { box-sizing: border-box; display: flex; align-items: center; justify-content: center; width: 100%; height: 24px; border: none; border-radius: 8px; padding: 0 12px; background: transparent; color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 500; line-height: 24px; text-align: center; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-dock-button:hover { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diffbar-count { display: inline-flex; align-items: center; color: var(--dsw-alias-label-primary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 500; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-file { display: inline-flex; align-items: center; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-editor-row { display: flex; align-items: center; height: 36px; min-height: 36px; padding: 4px 12px; overflow-x: auto; scrollbar-width: thin; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-editor-content { display: flex; align-items: center; gap: 10px; width: 100%; min-width: max-content; min-height: 28px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-nav,\n[data-edrv-diff-dock-shell] .edrv-diffbar-files,\n[data-edrv-diff-dock-shell] .edrv-diffbar-actions { display: flex; align-items: center; gap: 6px; flex: none; height: 28px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-actions { margin-left: auto; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-details-toggle { display: inline-flex; align-items: center; justify-content: center; width: 28px; min-width: 28px; padding: 0; font-size: 13px; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-details-toggle svg { width: 14px; height: 14px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-files { min-width: 0; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-files .edrv-diffbar-file { min-width: 72px; max-width: 240px; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-progress { min-width: 0; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; font-variant-numeric: tabular-nums; }\n[data-edrv-diff-dock-shell] .edrv-diffbar-summary { min-width: 0; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; }\n[data-edrv-diff-dock-shell] .edrv-pill { box-sizing: border-box; height: 28px; padding: 0 9px; border: none; border-radius: 999px; cursor: pointer; white-space: nowrap; font-size: 11px; line-height: 26px; font-weight: 600; }\n[data-edrv-diff-dock-shell] .edrv-pill-keep { background: var(--dsw-alias-state-success-tertiary); color: var(--dsw-alias-state-success-primary); border: 1px solid var(--dsw-alias-state-success-secondary); }\n[data-edrv-diff-dock-shell] .edrv-pill-undo { background: var(--dsw-alias-state-error-tertiary); color: var(--dsw-alias-state-error-primary); border: 1px solid var(--dsw-alias-state-error-secondary); }\n[data-edrv-diff-dock-shell] .edrv-pill-ghost { background: transparent; color: var(--dsw-alias-label-secondary); border: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-pill:hover:not(:disabled) { background-color: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-pill:focus-visible { outline: 2px solid var(--dsw-alias-label-tertiary); outline-offset: -2px; }\n[data-edrv-diff-dock-shell] .edrv-pill:disabled { opacity: .45; cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diffrow { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; background: transparent; cursor: pointer; flex-wrap: wrap; }\n[data-edrv-diff-dock-shell] .edrv-diffrow:hover { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-l { color: var(--dsw-alias-label-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-o { color: var(--dsw-alias-state-error-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-n { color: var(--dsw-alias-state-success-primary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-tag { color: var(--dsw-alias-label-tertiary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-stale { border-color: var(--dsw-alias-state-warn-secondary); cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diffbox-others { border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-file { border-color: var(--dsw-alias-border-l1); background: transparent; color: var(--dsw-alias-label-secondary); }\n[data-edrv-diff-dock-shell] .edrv-diffrow-file:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }\n[data-edrv-diff-dock-shell] .edrv-diff-details { display: flex; flex-direction: column; gap: 2px; padding: 6px 12px 8px; border-top: 1px solid var(--dsw-alias-border-l1); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item { display: flex; align-items: center; width: 100%; min-height: 28px; gap: 8px; padding: 4px 8px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary); cursor: pointer; text-align: left; font-size: 12px; line-height: 20px; }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item.danger { color: var(--dsw-alias-state-error-primary); }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item:disabled { opacity: .45; cursor: default; }\n[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-sep { height: 1px; margin: 2px 4px; background: var(--dsw-alias-border-l1); }\n@media (max-width: 620px) {\n [data-edrv-diff-dock-shell] .edrv-diffbar-editor-content { width: max-content; }\n [data-edrv-diff-dock-shell] .edrv-diffbar-files .edrv-diffbar-file { max-width: 160px; }\n}\n\n/* ===== 侧边栏(活动栏 + 面板区 + 文件树) ===== */\n[data-edrv-view] .edrv-editor-row { display: flex; flex: 1 1 auto; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-main-col { box-sizing: border-box; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-main-col > .edrv-pathbar,\n[data-edrv-view] .edrv-main-col > .edrv-tabs { flex: 0 0 auto; }\n[data-edrv-view] .edrv-editor-area { position: relative; display: flex; flex: 1 1 auto; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; }\n[data-edrv-view] .edrv-sidebar { display: flex; flex-direction: row; flex-shrink: 0; min-width: 0; height: 100%; background: var(--dsw-alias-bg-layer-1, #ffffff); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); overflow: hidden; }\n[data-edrv-view] .edrv-rail { display: flex; flex-direction: column; align-items: center; width: 44px; flex-shrink: 0; padding: 6px 0; background: var(--dsw-alias-bg-layer-2, #f0f4f4); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); gap: 4px; }\n[data-edrv-view] .edrv-rail-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: none; border-radius: 8px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rail-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rail-btn.edrv-rail-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); box-shadow: inset 2px 0 0 var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-rail-icon { font-size: 15px; line-height: 1; }\n[data-edrv-view] .edrv-rail-badge { position: absolute; top: 2px; right: 2px; min-width: 14px; height: 14px; padding: 0 3px; border-radius: 999px; background: var(--dsw-alias-brand-primary, #0f9d58); color: #fff; font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; }\n[data-edrv-view] .edrv-side-body { flex: 1; min-width: 0; min-height: 0; display: flex; overflow: hidden; }\n[data-edrv-view] .edrv-side-panel { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; position: relative; }\n[data-edrv-view] .edrv-side-head { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px 0 12px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); flex-shrink: 0; }\n[data-edrv-view] .edrv-side-title { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-side-root { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-side-btn { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-side-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-tree { flex: 1; min-height: 0; overflow: auto; padding: 4px 6px 12px; font-size: 12px; }\n[data-edrv-view] .edrv-tree-row { display: flex; align-items: center; gap: 4px; height: 22px; padding-right: 6px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-tree-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-active { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-context { box-shadow: inset 0 0 0 1px var(--dsw-alias-brand-primary, #0f9d58); background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); }\n[data-edrv-view] .edrv-tree-chev { width: 12px; flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; text-align: center; }\n[data-edrv-view] .edrv-tree-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-tree-name.edrv-tree-dim { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-tree-badge { min-width: 14px; height: 14px; padding: 0 4px; border-radius: 999px; background: var(--dsw-alias-state-warn-primary, #d97706); color: #fff; font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; flex-shrink: 0; }\n[data-edrv-view] .edrv-tree-loading { height: 22px; line-height: 22px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-tree-error { display: flex; align-items: center; gap: 8px; margin: 4px 2px 6px; padding: 6px 8px; border: 1px solid rgba(217,83,79,.4); border-radius: 6px; background: #fdeaea; color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n/* 大纲面板:kind 字形 / detail / 行号 */\n[data-edrv-view] .edrv-outline-kind { display: inline-flex; align-items: center; justify-content: center; width: 14px; flex-shrink: 0; font-size: 12px; font-weight: 600; text-align: center; }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-func { color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-type { color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-data { color: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-ns { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-key { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-outline-detail { flex-shrink: 0; max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-outline-ln { flex-shrink: 0; padding-right: 2px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-side-resize { width: 5px; flex-shrink: 0; cursor: col-resize; background: transparent; }\n[data-edrv-view] .edrv-side-resize:hover { background: var(--dsw-alias-brand-primary, #0f9d58); opacity: .35; }\n[data-edrv-view] .edrv-chip-btn.edrv-chip-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); }\n\n/* ===== 侧栏形态(betterSidebar 面板内编辑区) ===== */\n[data-edrv-view][data-edrv-layout='side'] { box-sizing: border-box; height: 100%; }\n\n/* ===== 侧边栏引导条(旧页签形态、未装 betterSidebar 时) ===== */\n[data-edrv-view] .edrv-side-hint { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; flex-wrap: wrap; padding: 6px 12px; font-size: 12px; color: var(--dsw-alias-label-secondary, #52606d); background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, #f0f4f4)); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-side-hint-text { white-space: nowrap; }\n[data-edrv-view] .edrv-side-hint-cmd { font-size: 11px; padding: 2px 6px; border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #ffffff); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-side-hint-close { margin-left: auto; border: none; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); cursor: pointer; font-size: 14px; line-height: 1; padding: 2px 6px; border-radius: 6px; flex-shrink: 0; }\n[data-edrv-view] .edrv-side-hint-close:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n\n/* ===== search panel (sidebar, VSCode search view) ===== */\n[data-edrv-view] .edrv-search-panel { display: flex; flex-direction: column; min-height: 0; flex: 1; padding: 6px; gap: 4px; }\n[data-edrv-view] .edrv-search-qrow { display: flex; gap: 4px; }\n[data-edrv-view] .edrv-search-input { flex: 1; min-width: 0; height: 26px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-input:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-opts { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-search-opt { min-width: 24px; height: 22px; padding: 0 6px; border: 1px solid transparent; border-radius: 5px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 11px; font-family: var(--ds-font-family-code, ui-monospace, monospace); cursor: pointer; }\n[data-edrv-view] .edrv-search-opt:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-opt.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); border-color: var(--dsw-alias-border-l2, #d5dde3); }\n[data-edrv-view] .edrv-search-spacer { flex: 1; }\n[data-edrv-view] .edrv-search-summary { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); padding: 2px 4px; }\n[data-edrv-view] .edrv-search-results { flex: 1; min-height: 0; overflow: auto; font-size: 12px; }\n[data-edrv-view] .edrv-search-group { margin-bottom: 2px; }\n[data-edrv-view] .edrv-search-file { display: flex; align-items: center; gap: 4px; height: 22px; padding: 0 4px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-search-file:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-chev { width: 12px; flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; text-align: center; }\n[data-edrv-view] .edrv-search-fname { font-weight: 600; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-search-fdir { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-search-fcount { flex-shrink: 0; min-width: 16px; height: 15px; padding: 0 4px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); font-size: 10px; line-height: 15px; text-align: center; }\n[data-edrv-view] .edrv-search-line { display: flex; gap: 6px; padding: 1px 4px 1px 16px; border-radius: 4px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; overflow: hidden; }\n[data-edrv-view] .edrv-search-line.alt { background: var(--dsw-alias-bg-layer-1, #ffffff); }\n[data-edrv-view] .edrv-search-line:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-ln { flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; min-width: 26px; text-align: right; user-select: none; }\n[data-edrv-view] .edrv-search-text { overflow: hidden; text-overflow: ellipsis; white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; }\n[data-edrv-view] .edrv-search-hit { background: var(--dsw-alias-state-warn-primary, #e5b93f); color: #1f2933; border-radius: 2px; padding: 0 1px; }\n[data-edrv-view] .edrv-search-empty { padding: 8px 4px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-search-error { padding: 6px 8px; border: 1px solid 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-search-warn { padding: 6px 8px; border: 1px solid rgba(240,173,78,.45); border-radius: 6px; background: #fdf6e3; color: #b8860b; font-size: 11px; word-break: break-all; }\n\n/* include/exclude filter rows (collapsible) */\n[data-edrv-view] .edrv-search-rows { display: flex; flex-direction: column; gap: 4px; padding: 2px 0 4px; }\n[data-edrv-view] .edrv-search-row { display: flex; align-items: center; gap: 4px; }\n[data-edrv-view] .edrv-search-field { flex: 1; min-width: 0; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 11px; outline: none; }\n[data-edrv-view] .edrv-search-field:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-field.dim { opacity: .45; }\n[data-edrv-view] .edrv-search-field::placeholder { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-search-toggle { min-width: 24px; height: 24px; padding: 0 5px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; line-height: 1; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-search-toggle:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-toggle.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); border-color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-search-toggle:disabled { opacity: .4; cursor: default; }\n\n/* LSP 引用结果浮动面板(Shift+F12 / 右键「查找所有引用」) */\n[data-edrv-view] .edrv-refs { position: absolute; right: 12px; top: 40px; z-index: 60; width: 360px; max-height: 60%; display: flex; flex-direction: column; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 8px; background: var(--dsw-alias-bg-panel, #161b22); box-shadow: 0 6px 24px rgba(0,0,0,.4); overflow: hidden; font-size: 12px; }\n[data-edrv-view] .edrv-refs-head { padding: 6px 10px; font-weight: 600; color: var(--dsw-alias-label-primary, #eee); border-bottom: 1px solid var(--dsw-alias-border-l2, #333); background: var(--dsw-alias-bg-layer-1, transparent); }\n[data-edrv-view] .edrv-refs-list { overflow-y: auto; }\n[data-edrv-view] .edrv-refs-row { padding: 4px 10px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-secondary, #c0c8d0); font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-refs-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.10)); color: var(--dsw-alias-label-primary, #fff); }\n\n\n";
|
|
34
34
|
const tagId$1 = "dsh-vscode-mode/editor.css";
|
|
35
35
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
36
36
|
const tag = document.createElement("style");
|
|
@@ -1861,7 +1861,9 @@ window.__ModuleLoader__.load({
|
|
|
1861
1861
|
/** 搜索面板条件(SearchPanel v1)。 */
|
|
1862
1862
|
search: "edrv.search.v1.",
|
|
1863
1863
|
/** 侧边栏提示已关闭标记(全局,无会话)。 */
|
|
1864
|
-
sideHint: "edrv.side-hint-dismissed"
|
|
1864
|
+
sideHint: "edrv.side-hint-dismissed",
|
|
1865
|
+
/** 「性能优化」页工作区栏目展开状态(workspaceFoldCache v1,全局不按会话)。 */
|
|
1866
|
+
workspaceFold: "edrv.ws-fold.v1."
|
|
1865
1867
|
};
|
|
1866
1868
|
//#endregion
|
|
1867
1869
|
//#region src/client/state/viewStateCache.ts
|
|
@@ -1968,6 +1970,418 @@ window.__ModuleLoader__.load({
|
|
|
1968
1970
|
} catch (error) {}
|
|
1969
1971
|
}
|
|
1970
1972
|
//#endregion
|
|
1973
|
+
//#region src/shared/lsp.ts
|
|
1974
|
+
/**
|
|
1975
|
+
* Monaco 位置(1-based lineNumber/column)→ LSP Position(0-based)。
|
|
1976
|
+
* @author ddj 2026年08月27号
|
|
1977
|
+
* @param lineNumber Monaco 行(≥1)
|
|
1978
|
+
* @param column Monaco 列(≥1)
|
|
1979
|
+
* @returns LSP 0-based position
|
|
1980
|
+
*/
|
|
1981
|
+
function monoToLsp(lineNumber, column) {
|
|
1982
|
+
return {
|
|
1983
|
+
line: Math.max(0, lineNumber - 1),
|
|
1984
|
+
character: Math.max(0, column - 1)
|
|
1985
|
+
};
|
|
1986
|
+
}
|
|
1987
|
+
//#endregion
|
|
1988
|
+
//#region src/client/monaco/lsp/lspClient.ts
|
|
1989
|
+
/**
|
|
1990
|
+
* dsh-vscode-mode client — LSP 会话与文档同步(模块级单例)。
|
|
1991
|
+
* 跟踪当前会话 + Monaco 打开文档(model 事件驱动),去抖推送 edrv.lsp.sync;
|
|
1992
|
+
* 并提供 definition/references/documentSymbol/hover/status 的 RPC 薄封装。
|
|
1993
|
+
* 作者 ddj 2026-08-27
|
|
1994
|
+
*/
|
|
1995
|
+
/** edrv:// 模型 URI → 工作区相对/绝对路径(与 host 侧 path 对齐)。 */
|
|
1996
|
+
function pathOfModel(model) {
|
|
1997
|
+
if (!model || !model.uri) return null;
|
|
1998
|
+
const uri = model.uri;
|
|
1999
|
+
if (uri.scheme !== "edrv") return null;
|
|
2000
|
+
const raw = uri.path ?? "";
|
|
2001
|
+
try {
|
|
2002
|
+
return decodeURIComponent(String(raw).replace(/^\//, "")) || null;
|
|
2003
|
+
} catch (error) {
|
|
2004
|
+
return String(raw).replace(/^\//, "") || null;
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
const SYNC_DEBOUNCE_MS = 250;
|
|
2008
|
+
let sessionId;
|
|
2009
|
+
const syncTimers = /* @__PURE__ */ new Map();
|
|
2010
|
+
let statusCache = {
|
|
2011
|
+
servers: [],
|
|
2012
|
+
at: 0
|
|
2013
|
+
};
|
|
2014
|
+
let progressListeners = /* @__PURE__ */ new Set();
|
|
2015
|
+
/** 设置当前会话(切换会话时清理旧文档跟踪由 host 端 session/disposed 兜底)。 */
|
|
2016
|
+
function setLspSession(id) {
|
|
2017
|
+
if (sessionId === id) return;
|
|
2018
|
+
sessionId = id;
|
|
2019
|
+
refreshStatus();
|
|
2020
|
+
}
|
|
2021
|
+
/** 订阅 LSP 状态变化(服务器进度/启动状态)。 */
|
|
2022
|
+
function onLspProgress(listener) {
|
|
2023
|
+
if (typeof listener !== "function") return () => {};
|
|
2024
|
+
progressListeners.add(listener);
|
|
2025
|
+
return () => progressListeners.delete(listener);
|
|
2026
|
+
}
|
|
2027
|
+
/** 发布当前 server 状态给编辑器底部状态区。 */
|
|
2028
|
+
function publishProgress() {
|
|
2029
|
+
for (const listener of progressListeners) try {
|
|
2030
|
+
listener(statusCache.servers);
|
|
2031
|
+
} catch (error) {}
|
|
2032
|
+
}
|
|
2033
|
+
/** 立即同步某文档到 host(首次打开/查询前保证服务器已认识该文档)。 */
|
|
2034
|
+
async function syncDoc(path, text, immediate = false) {
|
|
2035
|
+
if (!path || typeof text !== "string") return;
|
|
2036
|
+
if (immediate) {
|
|
2037
|
+
clearPending(path);
|
|
2038
|
+
await rpc("edrv.lsp.sync", {
|
|
2039
|
+
sessionId,
|
|
2040
|
+
path,
|
|
2041
|
+
text,
|
|
2042
|
+
version: Date.now()
|
|
2043
|
+
}).catch(() => {});
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
clearPending(path);
|
|
2047
|
+
syncTimers.set(path, setTimeout(() => {
|
|
2048
|
+
syncTimers.delete(path);
|
|
2049
|
+
rpc("edrv.lsp.sync", {
|
|
2050
|
+
sessionId,
|
|
2051
|
+
path,
|
|
2052
|
+
text,
|
|
2053
|
+
version: Date.now()
|
|
2054
|
+
}).catch(() => {});
|
|
2055
|
+
}, SYNC_DEBOUNCE_MS));
|
|
2056
|
+
}
|
|
2057
|
+
function clearPending(path) {
|
|
2058
|
+
const timer = syncTimers.get(path);
|
|
2059
|
+
if (timer) {
|
|
2060
|
+
clearTimeout(timer);
|
|
2061
|
+
syncTimers.delete(path);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
/** 文档关闭:停止去抖并通知 host。 */
|
|
2065
|
+
function closeDoc(path) {
|
|
2066
|
+
clearPending(path);
|
|
2067
|
+
rpc("edrv.lsp.close", {
|
|
2068
|
+
sessionId,
|
|
2069
|
+
path
|
|
2070
|
+
}).catch(() => {});
|
|
2071
|
+
}
|
|
2072
|
+
/** 立即推送 + 查询定义;返回 [{ uri, range }](uri 已转 edrv://)。 */
|
|
2073
|
+
async function findDefinition(path, text, position) {
|
|
2074
|
+
await syncDoc(path, text, true);
|
|
2075
|
+
const res = await rpc("edrv.lsp.definition", {
|
|
2076
|
+
sessionId,
|
|
2077
|
+
path,
|
|
2078
|
+
position: monoToLsp(position.lineNumber, position.column)
|
|
2079
|
+
}).catch(() => null);
|
|
2080
|
+
if (!res || !res.ok || !Array.isArray(res.locations)) return [];
|
|
2081
|
+
return res.locations.map(toClientLocation);
|
|
2082
|
+
}
|
|
2083
|
+
/** 立即推送 + 查询引用。 */
|
|
2084
|
+
async function findReferences(path, text, position, includeDeclaration = false) {
|
|
2085
|
+
await syncDoc(path, text, true);
|
|
2086
|
+
const res = await rpc("edrv.lsp.references", {
|
|
2087
|
+
sessionId,
|
|
2088
|
+
path,
|
|
2089
|
+
position: monoToLsp(position.lineNumber, position.column),
|
|
2090
|
+
includeDeclaration
|
|
2091
|
+
}).catch(() => null);
|
|
2092
|
+
if (!res || !res.ok || !Array.isArray(res.locations)) return [];
|
|
2093
|
+
return res.locations.map(toClientLocation);
|
|
2094
|
+
}
|
|
2095
|
+
/** 查询大纲符号(documentSymbol)。 */
|
|
2096
|
+
async function fetchDocumentSymbols(path, text) {
|
|
2097
|
+
await syncDoc(path, text, true);
|
|
2098
|
+
const res = await rpc("edrv.lsp.documentSymbol", {
|
|
2099
|
+
sessionId,
|
|
2100
|
+
path
|
|
2101
|
+
}).catch(() => null);
|
|
2102
|
+
if (!res || !res.ok || !Array.isArray(res.symbols)) return [];
|
|
2103
|
+
return res.symbols;
|
|
2104
|
+
}
|
|
2105
|
+
/** 查询 hover 内容。 */
|
|
2106
|
+
async function fetchHover(path, text, position) {
|
|
2107
|
+
await syncDoc(path, text, true);
|
|
2108
|
+
const res = await rpc("edrv.lsp.hover", {
|
|
2109
|
+
sessionId,
|
|
2110
|
+
path,
|
|
2111
|
+
position: monoToLsp(position.lineNumber, position.column)
|
|
2112
|
+
}).catch(() => null);
|
|
2113
|
+
if (!res || !res.ok) return null;
|
|
2114
|
+
return res.hover || null;
|
|
2115
|
+
}
|
|
2116
|
+
/** 拉取服务器状态(带 5s 缓存)。 */
|
|
2117
|
+
async function refreshStatus(force = false) {
|
|
2118
|
+
if (!force && Date.now() - statusCache.at < 1500) return statusCache.servers;
|
|
2119
|
+
const res = await rpc("edrv.lsp.status", { sessionId }).catch(() => null);
|
|
2120
|
+
const servers = res && res.ok && Array.isArray(res.servers) ? res.servers : [];
|
|
2121
|
+
statusCache = {
|
|
2122
|
+
servers,
|
|
2123
|
+
at: Date.now()
|
|
2124
|
+
};
|
|
2125
|
+
publishProgress();
|
|
2126
|
+
return servers;
|
|
2127
|
+
}
|
|
2128
|
+
/** file:// 或已转 edrv:// 的 LSP location → Monaco Location(edrv:// uri + 1-based range)。 */
|
|
2129
|
+
function toClientLocation(loc) {
|
|
2130
|
+
const range = loc.range || {};
|
|
2131
|
+
const start = range.start || {};
|
|
2132
|
+
const end = range.end || {};
|
|
2133
|
+
return {
|
|
2134
|
+
uri: window.monaco.Uri.parse(toEdrvUri(loc.uri, loc.root)),
|
|
2135
|
+
range: new window.monaco.Range(Math.max(1, (start.line ?? 0) + 1), Math.max(1, (start.character ?? 0) + 1), Math.max(1, (end.line ?? 0) + 1), Math.max(1, (end.character ?? 0) + 1))
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
/** file:// URI(或裸路径)→ edrv:// 相对路径(去掉盘符/根,与编辑器模型路径一致)。 */
|
|
2139
|
+
function toEdrvUri(uri, root) {
|
|
2140
|
+
if (!uri) return "";
|
|
2141
|
+
if (uri.startsWith("edrv://")) return uri;
|
|
2142
|
+
const path = relativeLspPath(uri, root);
|
|
2143
|
+
return "edrv:///" + encodeURI(path);
|
|
2144
|
+
}
|
|
2145
|
+
/** 将 LSP 目标解析为当前工作区相对路径(跨文件跳转与 model URI 对齐)。 */
|
|
2146
|
+
function targetOpenPath(loc) {
|
|
2147
|
+
return relativeLspPath(loc?.uri, loc?.root);
|
|
2148
|
+
}
|
|
2149
|
+
/** file URI/裸路径 → root 下的 / 分隔相对路径;root 不匹配时保留规范化绝对路径。 */
|
|
2150
|
+
function relativeLspPath(uri, root) {
|
|
2151
|
+
if (!uri) return "";
|
|
2152
|
+
let path = String(uri);
|
|
2153
|
+
if (path.startsWith("file://")) try {
|
|
2154
|
+
path = decodeURIComponent(path.replace(/^file:\/\//, ""));
|
|
2155
|
+
} catch (error) {
|
|
2156
|
+
path = path.replace(/^file:\/\//, "");
|
|
2157
|
+
}
|
|
2158
|
+
path = path.replace(/\\/g, "/").replace(/^\/([A-Za-z]:)/, "$1");
|
|
2159
|
+
const normalizedRoot = root ? String(root).replace(/\\/g, "/").replace(/^\/([A-Za-z]:)/, "$1").replace(/\/+$/, "") : "";
|
|
2160
|
+
const lowerPath = path.toLowerCase();
|
|
2161
|
+
const lowerRoot = normalizedRoot.toLowerCase();
|
|
2162
|
+
if (normalizedRoot && (lowerPath === lowerRoot || lowerPath.startsWith(lowerRoot + "/"))) return path.slice(normalizedRoot.length).replace(/^\/+/, "");
|
|
2163
|
+
return path.replace(/^\/+/, "");
|
|
2164
|
+
}
|
|
2165
|
+
/** 目标行/列(0-based LSP → 1-based Monaco)。 */
|
|
2166
|
+
function targetMonoPosition(loc) {
|
|
2167
|
+
const start = (loc.range || {}).start || {};
|
|
2168
|
+
return {
|
|
2169
|
+
line: Math.max(1, (start.line ?? 0) + 1),
|
|
2170
|
+
column: Math.max(1, (start.character ?? 0) + 1)
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
//#endregion
|
|
2174
|
+
//#region src/client/monaco/lsp/providers.ts
|
|
2175
|
+
/**
|
|
2176
|
+
* dsh-vscode-mode client — Monaco LSP provider 注册 + 跳转/引用命令。
|
|
2177
|
+
* 数据层:Definition/Reference/DocumentSymbol/Hover provider(同一文件内跳转 Monaco 原生可用)。
|
|
2178
|
+
* 交互层:edrv.goToDefinition(F12)/ edrv.findReferences(Shift+F12 / 右键),跨文件跳转自研
|
|
2179
|
+
* (target 经 openFileAt 打开并定位),引用结果用轻量浮动面板展示(点击跳转)。
|
|
2180
|
+
* 作者 ddj 2026-08-27
|
|
2181
|
+
*/
|
|
2182
|
+
const LSP_LANGS = ["lua", "csharp"];
|
|
2183
|
+
let registered = false;
|
|
2184
|
+
const disposables = [];
|
|
2185
|
+
let overlayEl = null;
|
|
2186
|
+
/** 目标文件打开并定位(复用现有 openFileAt 的 edrv:open-editor 事件通道)。 */
|
|
2187
|
+
function openAt(path, line, column) {
|
|
2188
|
+
window.dispatchEvent(new CustomEvent("edrv:open-editor", { detail: {
|
|
2189
|
+
path,
|
|
2190
|
+
line,
|
|
2191
|
+
column
|
|
2192
|
+
} }));
|
|
2193
|
+
}
|
|
2194
|
+
function jumpToLocation(loc) {
|
|
2195
|
+
const path = targetOpenPath(loc);
|
|
2196
|
+
const pos = targetMonoPosition(loc);
|
|
2197
|
+
openAt(path, pos.line, pos.column);
|
|
2198
|
+
}
|
|
2199
|
+
/** 注册全部 Monaco LSP provider 与文档跟踪(幂等)。 */
|
|
2200
|
+
function registerLspProviders(monaco) {
|
|
2201
|
+
if (registered) return;
|
|
2202
|
+
registered = true;
|
|
2203
|
+
const attachModel = (model) => {
|
|
2204
|
+
if (!model || model.uri.scheme !== "edrv") return;
|
|
2205
|
+
const path = pathOfModel(model);
|
|
2206
|
+
if (!path) return;
|
|
2207
|
+
const contentDisposer = model.onDidChangeContent(() => syncDoc(path, model.getValue()));
|
|
2208
|
+
const disposeDisposer = model.onWillDispose(() => {
|
|
2209
|
+
closeDoc(path);
|
|
2210
|
+
contentDisposer.dispose();
|
|
2211
|
+
});
|
|
2212
|
+
disposables.push(contentDisposer, disposeDisposer);
|
|
2213
|
+
syncDoc(path, model.getValue(), true);
|
|
2214
|
+
};
|
|
2215
|
+
disposables.push(monaco.editor.onDidCreateModel(attachModel));
|
|
2216
|
+
for (const model of monaco.editor.getModels()) attachModel(model);
|
|
2217
|
+
disposables.push(monaco.languages.registerDefinitionProvider(LSP_LANGS, { provideDefinition: (model, position, token) => {
|
|
2218
|
+
const path = pathOfModel(model);
|
|
2219
|
+
if (!path) return [];
|
|
2220
|
+
return findDefinition(path, model.getValue(), position);
|
|
2221
|
+
} }), monaco.languages.registerReferenceProvider(LSP_LANGS, { provideReferences: (model, position, context, token) => {
|
|
2222
|
+
const path = pathOfModel(model);
|
|
2223
|
+
if (!path) return [];
|
|
2224
|
+
return findReferences(path, model.getValue(), position, Boolean(context?.includeDeclaration));
|
|
2225
|
+
} }), monaco.languages.registerDocumentSymbolProvider(LSP_LANGS, { provideDocumentSymbols: async (model, token) => {
|
|
2226
|
+
const path = pathOfModel(model);
|
|
2227
|
+
if (!path) return [];
|
|
2228
|
+
return (await fetchDocumentSymbols(path, model.getValue())).map(toMonacoSymbol);
|
|
2229
|
+
} }), monaco.languages.registerHoverProvider(LSP_LANGS, { provideHover: async (model, position, token) => {
|
|
2230
|
+
const path = pathOfModel(model);
|
|
2231
|
+
if (!path) return null;
|
|
2232
|
+
const hover = await fetchHover(path, model.getValue(), position);
|
|
2233
|
+
if (!hover || !hover.contents || !hover.contents.length) return null;
|
|
2234
|
+
const range = hover.range ? new monaco.Range(hover.range.start.line + 1, hover.range.start.character + 1, hover.range.end.line + 1, hover.range.end.character + 1) : null;
|
|
2235
|
+
return {
|
|
2236
|
+
contents: hover.contents.map((text) => ({ value: text })),
|
|
2237
|
+
range: range || void 0
|
|
2238
|
+
};
|
|
2239
|
+
} }));
|
|
2240
|
+
}
|
|
2241
|
+
/** LSP SymbolInfo(host 归一化后)→ Monaco DocumentSymbol。 */
|
|
2242
|
+
function toMonacoSymbol(symbol) {
|
|
2243
|
+
const range = symbol.range || {};
|
|
2244
|
+
const sel = symbol.selectionRange || range;
|
|
2245
|
+
const s = (p) => ({
|
|
2246
|
+
lineNumber: Math.max(1, (p.line ?? 0) + 1),
|
|
2247
|
+
column: Math.max(1, (p.character ?? 0) + 1)
|
|
2248
|
+
});
|
|
2249
|
+
const out = {
|
|
2250
|
+
name: symbol.name,
|
|
2251
|
+
detail: symbol.detail || "",
|
|
2252
|
+
kind: symbol.kind ?? 0,
|
|
2253
|
+
tags: [],
|
|
2254
|
+
range: new window.monaco.Range(s(range.start).lineNumber, s(range.start).column, s(range.end).lineNumber, s(range.end).column),
|
|
2255
|
+
selectionRange: new window.monaco.Range(s(sel.start).lineNumber, s(sel.start).column, s(sel.end).lineNumber, s(sel.end).column)
|
|
2256
|
+
};
|
|
2257
|
+
if (Array.isArray(symbol.children) && symbol.children.length) out.children = symbol.children.map(toMonacoSymbol);
|
|
2258
|
+
return out;
|
|
2259
|
+
}
|
|
2260
|
+
/** 定义查询并跳转;nativeReveal 仅供 F12 保留 Monaco 原生 reveal。 */
|
|
2261
|
+
async function goToDefinition(ed, position, nativeReveal = false) {
|
|
2262
|
+
const model = ed.getModel();
|
|
2263
|
+
const path = pathOfModel(model);
|
|
2264
|
+
if (!path || !position) return;
|
|
2265
|
+
const locations = await findDefinition(path, model.getValue(), position);
|
|
2266
|
+
if (!locations.length) {
|
|
2267
|
+
setStatus("未找到定义");
|
|
2268
|
+
return;
|
|
2269
|
+
}
|
|
2270
|
+
const first = locations[0];
|
|
2271
|
+
if (nativeReveal && targetOpenPath(first) === path) {
|
|
2272
|
+
const action = ed.getAction("editor.action.revealDefinition");
|
|
2273
|
+
if (action) {
|
|
2274
|
+
await action.run();
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
jumpToLocation(first);
|
|
2279
|
+
}
|
|
2280
|
+
/** F12 / 右键「转到定义」:查询定义并跳转(首个目标)。 */
|
|
2281
|
+
async function runGoToDefinition(ed) {
|
|
2282
|
+
await goToDefinition(ed, ed.getPosition(), true);
|
|
2283
|
+
}
|
|
2284
|
+
/** Ctrl+鼠标点击定义:使用点击位置查询,避免依赖当前光标位置。 */
|
|
2285
|
+
async function gotoDefinitionAt(ed, position) {
|
|
2286
|
+
await goToDefinition(ed, position);
|
|
2287
|
+
}
|
|
2288
|
+
/** 给一个 Monaco 编辑器绑定 Ctrl/Cmd+鼠标定义跳转(幂等)。 */
|
|
2289
|
+
function bindLspEditor(ed) {
|
|
2290
|
+
if (!ed || ed.__edrvLspClick) return;
|
|
2291
|
+
ed.__edrvLspClick = true;
|
|
2292
|
+
const disposable = ed.onMouseDown((event) => {
|
|
2293
|
+
const native = event?.event;
|
|
2294
|
+
const position = event?.target?.position;
|
|
2295
|
+
const button = native?.button ?? event?.button;
|
|
2296
|
+
const ctrl = native?.ctrlKey ?? event?.ctrlKey;
|
|
2297
|
+
const meta = native?.metaKey ?? event?.metaKey;
|
|
2298
|
+
const isLeftButton = button === 0 || native?.leftButton === true;
|
|
2299
|
+
if (!native || !position || !isLeftButton || !ctrl && !meta) return;
|
|
2300
|
+
native.preventDefault?.();
|
|
2301
|
+
native.stopPropagation?.();
|
|
2302
|
+
gotoDefinitionAt(ed, position);
|
|
2303
|
+
});
|
|
2304
|
+
disposables.push(disposable);
|
|
2305
|
+
}
|
|
2306
|
+
/** Shift+F12 / 右键「查找所有引用」:查询引用并展示浮动面板。 */
|
|
2307
|
+
async function runFindReferences(ed) {
|
|
2308
|
+
const model = ed.getModel();
|
|
2309
|
+
const path = pathOfModel(model);
|
|
2310
|
+
const position = ed.getPosition();
|
|
2311
|
+
if (!path || !position) return;
|
|
2312
|
+
showReferencesOverlay(ed, path, position, await findReferences(path, model.getValue(), position, false));
|
|
2313
|
+
}
|
|
2314
|
+
/** 引用结果浮动面板(轻量 DOM,点击跳转;Esc/失焦关闭)。 */
|
|
2315
|
+
function showReferencesOverlay(ed, path, position, locations) {
|
|
2316
|
+
hideReferencesOverlay();
|
|
2317
|
+
if (!locations.length) {
|
|
2318
|
+
setStatus("未找到引用");
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
const root = ed.getDomNode();
|
|
2322
|
+
if (!root) return;
|
|
2323
|
+
const el = document.createElement("div");
|
|
2324
|
+
el.className = "edrv-refs";
|
|
2325
|
+
const header = document.createElement("div");
|
|
2326
|
+
header.className = "edrv-refs-head";
|
|
2327
|
+
header.textContent = "引用 " + locations.length + " 处";
|
|
2328
|
+
el.appendChild(header);
|
|
2329
|
+
const list = document.createElement("div");
|
|
2330
|
+
list.className = "edrv-refs-list";
|
|
2331
|
+
for (const loc of locations) {
|
|
2332
|
+
const row = document.createElement("div");
|
|
2333
|
+
row.className = "edrv-refs-row";
|
|
2334
|
+
const pos = targetMonoPosition(loc);
|
|
2335
|
+
const rel = targetOpenPath(loc);
|
|
2336
|
+
row.textContent = (String(rel).split("/").pop() || rel) + ":" + pos.line + ":" + pos.column;
|
|
2337
|
+
row.title = rel + ":" + pos.line;
|
|
2338
|
+
row.addEventListener("click", () => {
|
|
2339
|
+
hideReferencesOverlay();
|
|
2340
|
+
jumpToLocation(loc);
|
|
2341
|
+
});
|
|
2342
|
+
list.appendChild(row);
|
|
2343
|
+
}
|
|
2344
|
+
el.appendChild(list);
|
|
2345
|
+
root.appendChild(el);
|
|
2346
|
+
overlayEl = el;
|
|
2347
|
+
const onKey = (e) => {
|
|
2348
|
+
if (e.key === "Escape") hideReferencesOverlay();
|
|
2349
|
+
};
|
|
2350
|
+
window.addEventListener("keydown", onKey);
|
|
2351
|
+
el.addEventListener("click", (e) => {
|
|
2352
|
+
if (e.target === el) hideReferencesOverlay();
|
|
2353
|
+
});
|
|
2354
|
+
el._onKey = onKey;
|
|
2355
|
+
}
|
|
2356
|
+
function hideReferencesOverlay() {
|
|
2357
|
+
if (overlayEl) {
|
|
2358
|
+
window.removeEventListener("keydown", overlayEl._onKey);
|
|
2359
|
+
overlayEl.remove();
|
|
2360
|
+
overlayEl = null;
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
/** 编辑器状态栏提示(复用 EditorView 的 status 通道)。 */
|
|
2364
|
+
function setStatus(text) {
|
|
2365
|
+
window.dispatchEvent(new CustomEvent("edrv:status", { detail: { text } }));
|
|
2366
|
+
}
|
|
2367
|
+
//#endregion
|
|
2368
|
+
//#region src/client/monaco/lsp/index.ts
|
|
2369
|
+
/**
|
|
2370
|
+
* dsh-vscode-mode client — LSP 装配编排。
|
|
2371
|
+
* setupLsp(monaco):注册 Monaco provider 与文档跟踪(幂等);调用方在 Monaco 加载后调用。
|
|
2372
|
+
* setSession(id):会话切换时同步(provider 与文档同步共用)。
|
|
2373
|
+
* 作者 ddj 2026-08-27
|
|
2374
|
+
*/
|
|
2375
|
+
/** Monaco 加载后装配(幂等;重复调用仅刷新会话)。 */
|
|
2376
|
+
function setupLsp(monaco) {
|
|
2377
|
+
registerLspProviders(monaco);
|
|
2378
|
+
}
|
|
2379
|
+
/** 会话切换:更新 host 侧文档归属 + 刷新状态。 */
|
|
2380
|
+
function setSession(sessionId) {
|
|
2381
|
+
setLspSession(sessionId);
|
|
2382
|
+
refreshStatus(true);
|
|
2383
|
+
}
|
|
2384
|
+
//#endregion
|
|
1971
2385
|
//#region src/client/ui/EditorView.ts
|
|
1972
2386
|
/**
|
|
1973
2387
|
* dsh-vscode-mode client — EditorView:中央 VSCode 式文件编辑器(编排层)。
|
|
@@ -1998,6 +2412,7 @@ window.__ModuleLoader__.load({
|
|
|
1998
2412
|
const [content, setContent] = react.default.useState(null);
|
|
1999
2413
|
const [contentPath, setContentPath] = react.default.useState(null);
|
|
2000
2414
|
const [status, setStatus] = react.default.useState("");
|
|
2415
|
+
const [lspServers, setLspServers] = react.default.useState([]);
|
|
2001
2416
|
const [error, setError] = react.default.useState(null);
|
|
2002
2417
|
const [loadError, setLoadError] = react.default.useState(null);
|
|
2003
2418
|
const [loadStage, setLoadStage] = react.default.useState({
|
|
@@ -2234,6 +2649,18 @@ window.__ModuleLoader__.load({
|
|
|
2234
2649
|
window.removeEventListener("edrv:refresh", onRefresh);
|
|
2235
2650
|
};
|
|
2236
2651
|
}, [sessionId]);
|
|
2652
|
+
react.default.useEffect(() => {
|
|
2653
|
+
const publishLsp = (servers) => setLspServers(Array.isArray(servers) ? servers : []);
|
|
2654
|
+
const unsubscribe = onLspProgress(publishLsp);
|
|
2655
|
+
refreshStatus(true).then(publishLsp);
|
|
2656
|
+
const timer = setInterval(() => {
|
|
2657
|
+
refreshStatus(true).then(publishLsp);
|
|
2658
|
+
}, 1500);
|
|
2659
|
+
return () => {
|
|
2660
|
+
unsubscribe();
|
|
2661
|
+
clearInterval(timer);
|
|
2662
|
+
};
|
|
2663
|
+
}, [sessionId]);
|
|
2237
2664
|
react.default.useEffect(() => {
|
|
2238
2665
|
const onOpen = (e) => {
|
|
2239
2666
|
const p = e?.detail?.path;
|
|
@@ -2244,6 +2671,12 @@ window.__ModuleLoader__.load({
|
|
|
2244
2671
|
region: null
|
|
2245
2672
|
};
|
|
2246
2673
|
setFocusRequest((value) => value + 1);
|
|
2674
|
+
addTab(p, true);
|
|
2675
|
+
return;
|
|
2676
|
+
}
|
|
2677
|
+
if (e?.detail?.line != null) {
|
|
2678
|
+
openFileAt(p, e?.detail?.line, e?.detail?.column);
|
|
2679
|
+
return;
|
|
2247
2680
|
}
|
|
2248
2681
|
addTab(p, true);
|
|
2249
2682
|
};
|
|
@@ -2254,9 +2687,14 @@ window.__ModuleLoader__.load({
|
|
|
2254
2687
|
};
|
|
2255
2688
|
window.addEventListener("edrv:open-editor", onOpen);
|
|
2256
2689
|
window.addEventListener("edrv:show-launcher", onShowLauncher);
|
|
2690
|
+
const onStatus = (e) => {
|
|
2691
|
+
if (e?.detail?.text) setStatus(e.detail.text);
|
|
2692
|
+
};
|
|
2693
|
+
window.addEventListener("edrv:status", onStatus);
|
|
2257
2694
|
return () => {
|
|
2258
2695
|
window.removeEventListener("edrv:open-editor", onOpen);
|
|
2259
2696
|
window.removeEventListener("edrv:show-launcher", onShowLauncher);
|
|
2697
|
+
window.removeEventListener("edrv:status", onStatus);
|
|
2260
2698
|
};
|
|
2261
2699
|
}, [sessionId]);
|
|
2262
2700
|
const dockSourceRef = react.default.useRef({});
|
|
@@ -2540,6 +2978,7 @@ window.__ModuleLoader__.load({
|
|
|
2540
2978
|
flushSave();
|
|
2541
2979
|
saveViewState(active);
|
|
2542
2980
|
diffRendererRef.current?.dispose?.();
|
|
2981
|
+
hideReferencesOverlay();
|
|
2543
2982
|
if (editorRef.current) {
|
|
2544
2983
|
editorRef.current.dispose();
|
|
2545
2984
|
editorRef.current = null;
|
|
@@ -2639,6 +3078,30 @@ window.__ModuleLoader__.load({
|
|
|
2639
3078
|
precondition: "editorTextFocus",
|
|
2640
3079
|
run: (edx) => menuHandlers()?.openInExplorer(pathOf(edx))
|
|
2641
3080
|
});
|
|
3081
|
+
ed.addAction({
|
|
3082
|
+
id: "edrv.goToDefinition",
|
|
3083
|
+
label: "转到定义",
|
|
3084
|
+
contextMenuGroupId: "1_edrv",
|
|
3085
|
+
keybindings: m.KeyMod.CtrlCmd | m.KeyCode.F12,
|
|
3086
|
+
precondition: "editorTextFocus",
|
|
3087
|
+
run: (edx) => {
|
|
3088
|
+
runGoToDefinition(edx);
|
|
3089
|
+
}
|
|
3090
|
+
});
|
|
3091
|
+
ed.addAction({
|
|
3092
|
+
id: "edrv.findReferences",
|
|
3093
|
+
label: "查找所有引用",
|
|
3094
|
+
contextMenuGroupId: "1_edrv",
|
|
3095
|
+
keybindings: m.KeyMod.Shift | m.KeyCode.F12,
|
|
3096
|
+
precondition: "editorTextFocus",
|
|
3097
|
+
run: (edx) => {
|
|
3098
|
+
runFindReferences(edx);
|
|
3099
|
+
}
|
|
3100
|
+
});
|
|
3101
|
+
ed.addCommand(m.KeyCode.F12, () => {
|
|
3102
|
+
runGoToDefinition(ed);
|
|
3103
|
+
});
|
|
3104
|
+
bindLspEditor(ed);
|
|
2642
3105
|
const hideSoon = () => {
|
|
2643
3106
|
if (hoverEditorRef.current || hoverPanelRef.current || hideTimerRef.current) return;
|
|
2644
3107
|
hideTimerRef.current = setTimeout(() => {
|
|
@@ -3092,6 +3555,26 @@ window.__ModuleLoader__.load({
|
|
|
3092
3555
|
className: "edrv-pathbar",
|
|
3093
3556
|
title: active || ""
|
|
3094
3557
|
}, react.default.createElement("span", { className: "edrv-pb-name" }, active ? String(active).split(/[\\/]/).pop() : "未打开文件"), react.default.createElement("span", { className: "edrv-pb-full" }, active || "使用右上搜索框 (" + (chordOf("edrv.quickOpen") ?? "Ctrl+P") + ") 打开文件"), active && langOf(active) ? react.default.createElement("span", { className: "edrv-pb-meta" }, langOf(active)) : null, cursor ? react.default.createElement("span", { className: "edrv-pb-meta" }, cursor) : null, status ? react.default.createElement("span", { className: "edrv-pb-meta edrv-pb-status" }, status) : null);
|
|
3558
|
+
const lspLanguage = active ? langOf(active) : "";
|
|
3559
|
+
const lspServer = lspServers.find((item) => item.languageId === lspLanguage);
|
|
3560
|
+
const lspLabel = lspServer ? "LSP " + lspLanguage + " · " + ({
|
|
3561
|
+
idle: "未启动",
|
|
3562
|
+
starting: "启动中",
|
|
3563
|
+
ready: "已就绪",
|
|
3564
|
+
indexing: "解析中",
|
|
3565
|
+
unavailable: "不可用",
|
|
3566
|
+
stopped: "已停止"
|
|
3567
|
+
}[lspServer.phase] ?? lspServer.phase) + " · " + ({
|
|
3568
|
+
extension: "扩展",
|
|
3569
|
+
discover: "自动发现",
|
|
3570
|
+
manual: "手动配置",
|
|
3571
|
+
none: "未配置"
|
|
3572
|
+
}[lspServer.source] ?? lspServer.source) : "LSP " + lspLanguage + " · 未启动";
|
|
3573
|
+
const lspProgress = typeof lspServer?.progress === "number" ? Math.round(lspServer.progress) : null;
|
|
3574
|
+
const lspFooter = lspLanguage === "lua" || lspLanguage === "csharp" ? react.default.createElement("div", {
|
|
3575
|
+
className: "edrv-statusbar",
|
|
3576
|
+
title: lspServer?.progressMessage || lspLabel
|
|
3577
|
+
}, react.default.createElement("span", { className: "edrv-lsp-status-dot " + (lspServer?.phase === "ready" ? "ready" : lspServer?.phase === "indexing" || lspServer?.phase === "starting" ? "busy" : "idle") }), react.default.createElement("span", { className: "edrv-sp-lsp" }, lspLabel), lspServer?.progressMessage ? react.default.createElement("span", { className: "edrv-sp-progress-message" }, lspServer.progressMessage) : null, lspProgress !== null ? react.default.createElement("span", { className: "edrv-sp-progress" }, lspProgress + "%") : null) : null;
|
|
3095
3578
|
const tabRow = react.default.createElement("div", { style: {
|
|
3096
3579
|
display: "flex",
|
|
3097
3580
|
alignItems: "center",
|
|
@@ -3345,7 +3828,7 @@ window.__ModuleLoader__.load({
|
|
|
3345
3828
|
flexDirection: "column",
|
|
3346
3829
|
overflow: "hidden"
|
|
3347
3830
|
}
|
|
3348
|
-
}, pathBar, tabRow, sideHintEl, editorArea);
|
|
3831
|
+
}, pathBar, tabRow, sideHintEl, editorArea, lspFooter);
|
|
3349
3832
|
const editorRow = react.default.createElement("div", { className: "edrv-editor-row" }, sidebarOn && sidebarPanels ? react.default.createElement(SidebarView, {
|
|
3350
3833
|
registry: sidebarPanels,
|
|
3351
3834
|
ctx: sidebarCtx,
|
|
@@ -3749,7 +4232,7 @@ window.__ModuleLoader__.load({
|
|
|
3749
4232
|
}
|
|
3750
4233
|
//#endregion
|
|
3751
4234
|
//#region \0dsh-css:/home/runner/work/DSH_VsCodeMode/DSH_VsCodeMode/src/client/styles/mcp.css.mjs
|
|
3752
|
-
const css = "/* dsh-vscode-mode MCP 管理界面样式。\n 全部使用 DSH 真实主题变量(--dsw-alias-*),不再用不存在的 fg-* 变量。 */\n.vsm-mcp-page{display:flex;flex-direction:column;gap:18px;width:100%;max-width:920px;box-sizing:border-box;color:var(--dsw-alias-label-primary,#e6e6e6);padding-bottom:32px}\n\n/* 顶部:标题 + 副标题 + 添加按钮 */\n.vsm-mcp-header{display:flex;align-items:flex-start;justify-content:space-between;gap:20px}\n.vsm-mcp-header h2{margin:0 0 4px;font-size:20px;font-weight:600;line-height:1.35;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-mcp-header p{margin:0;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:13px;line-height:1.5}\n.vsm-primary{border:0;border-radius:6px;padding:8px 16px;background:var(--dsw-alias-button-primary-fill,#6d55ff);color:var(--dsw-alias-button-contrast-fill,#fff);font-size:13px;font-weight:500;cursor:pointer;white-space:nowrap}\n.vsm-primary:hover{background:var(--dsw-alias-button-primary-hover,#5a46e6)}\n.vsm-primary:disabled{opacity:.55;cursor:default}\n\n/* 子 Tab:我的 MCP / MCP 市场 */\n.vsm-mcp-tabs{display:flex;gap:24px;border-bottom:1px solid var(--dsw-alias-border-l2,#3a3a3a)}\n.vsm-mcp-tabs button{border:0;background:none;color:var(--dsw-alias-label-tertiary,#9aa5b1);padding:9px 2px 11px;cursor:pointer;font-size:14px;position:relative}\n.vsm-mcp-tabs button:hover{color:var(--dsw-alias-label-secondary,#c8c8c8)}\n.vsm-mcp-tabs button.active{color:var(--dsw-alias-label-primary,#fff);font-weight:500}\n.vsm-mcp-tabs button.active::after{content:\"\";position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--dsw-alias-brand-primary,#6d55ff);border-radius:2px}\n\n/* 通用设置 */\n.vsm-general-page{display:flex;flex-direction:column;gap:10px;max-width:720px}\n.vsm-general-page h2{margin:0;font-size:20px;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-general-page p{margin:0 0 8px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:13px}\n.vsm-general-row{display:flex;align-items:center;justify-content:space-between;gap:20px;padding:14px 0;border-bottom:1px solid var(--dsw-alias-border-l1,#333);color:var(--dsw-alias-label-primary,#eee);font-size:14px}\n.vsm-general-row select{min-width:220px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:var(--dsw-alias-bg-layer-1,#2b2b2b);color:var(--dsw-alias-label-primary,#eee);font:inherit}\n.vsm-general-row small{color:var(--dsw-alias-label-tertiary,#9aa5b1)}\n\n/* 服务卡片 */\n.vsm-mcp-card{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:10px;padding:14px 16px;margin-bottom:10px;background:var(--dsw-alias-bg-layer-2,transparent);transition:border-color .15s}\n.vsm-mcp-card:hover{border-color:var(--dsw-alias-border-l3,#555)}\n.vsm-mcp-card-head{display:flex;justify-content:space-between;align-items:center;gap:12px}\n.vsm-mcp-title{display:flex;align-items:center;gap:9px;font-weight:600;font-size:14px;color:var(--dsw-alias-label-primary,#e6e6e6);min-width:0}\n.vsm-mcp-source{font-size:11px;font-weight:400;padding:2px 7px;border-radius:4px;background:var(--dsw-alias-bg-mask-2,#ffffff22);color:var(--dsw-alias-label-tertiary,#9aa5b1);flex-shrink:0}\n.vsm-mcp-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;background:var(--dsw-alias-state-error-primary,#e05252)}\n.vsm-mcp-dot.connected{background:var(--dsw-alias-state-success-primary,#38bd75)}\n.vsm-mcp-dot.connecting{background:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-mcp-dot.disabled{background:var(--dsw-alias-label-tertiary,#9aa5b1)}\n.vsm-mcp-actions{display:flex;align-items:center;gap:4px;flex-shrink:0}\n.vsm-mcp-actions button{border:0;background:none;color:var(--dsw-alias-label-tertiary,#9aa5b1);cursor:pointer;font-size:15px;width:28px;height:28px;border-radius:6px;line-height:1}\n.vsm-mcp-actions button:hover{color:var(--dsw-alias-label-primary,#fff);background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-mcp-actions button.vsm-danger:hover{color:var(--dsw-alias-state-error-primary,#f07878);background:var(--dsw-alias-interactive-bg-hover-danger,#ff000014)}\n.vsm-switch{font-size:14px !important}\n.vsm-switch.on{color:var(--dsw-alias-state-success-primary,#38bd75)}\n.vsm-mcp-meta{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1);margin:9px 0}\n.vsm-mcp-error{color:var(--dsw-alias-state-error-primary,#f07878);font-size:12px;margin:8px 0}\n.vsm-mcp-banner{padding:9px 12px;border:1px solid var(--dsw-alias-state-error-secondary,#d9534f55);border-radius:6px;background:var(--dsw-alias-state-error-secondary,#d9534f1a);margin-bottom:10px}\n\n/* 工具 chips */\n.vsm-mcp-tools{display:flex;flex-wrap:wrap;gap:6px}\n.vsm-mcp-chip{font:12px var(--ds-font-family-code,ui-monospace,monospace);padding:4px 8px;border-radius:5px;background:var(--dsw-alias-bg-mask-2,#ffffff14);color:var(--dsw-alias-label-secondary,#c8c8c8);border:1px solid var(--dsw-alias-border-l1,#333)}\n\n/* 空态 / 加载态 */\n.vsm-mcp-empty{text-align:center;padding:56px 20px;color:var(--dsw-alias-label-tertiary,#9aa5b1);border:1px dashed var(--dsw-alias-border-l2,#3a3a3a);border-radius:8px;font-size:13px}\n\n/* 项目搜索下拉框 */\n.vsm-project-picker{position:relative;width:100%;z-index:4}\n.vsm-project-picker-trigger{width:100%;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:8px;background:var(--dsw-alias-bg-layer-1,transparent);color:var(--dsw-alias-label-primary,#e6e6e6);cursor:pointer;text-align:left}\n.vsm-project-picker-trigger:hover{border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-picker-label{display:flex;flex-direction:column;min-width:0;font-size:13px;font-weight:500}\n.vsm-project-picker-label small{margin-top:3px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font:11px var(--ds-font-family-code,ui-monospace,monospace);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.vsm-project-picker-arrow{color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:16px}\n.vsm-project-picker-menu{position:absolute;top:calc(100% + 6px);left:0;right:0;padding:8px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:9px;background:var(--dsw-alias-bg-overlay,#202020);box-shadow:0 12px 36px #0008}\n.vsm-project-picker-search{width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:var(--dsw-alias-bg-layer-1,#2b2b2b);color:var(--dsw-alias-label-primary,#eee);outline:none}\n.vsm-project-picker-search:focus{border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-picker-options{max-height:260px;overflow:auto;margin-top:6px}\n.vsm-project-option{display:flex;width:100%;flex-direction:column;gap:3px;padding:8px 9px;border:0;border-radius:6px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;text-align:left}\n.vsm-project-option:hover,.vsm-project-option.highlight{background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-project-option.selected{color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-option small{overflow:hidden;color:var(--dsw-alias-label-tertiary,#9aa5b1);font:11px var(--ds-font-family-code,ui-monospace,monospace);text-overflow:ellipsis;white-space:nowrap}\n.vsm-project-picker-empty{padding:18px 8px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px;text-align:center}\n\n/* 项目级 MCP 分组 */\n.vsm-project{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:12px;margin-bottom:14px;overflow:hidden;background:var(--dsw-alias-bg-layer-1,transparent)}\n.vsm-project-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l1,#333);background:var(--dsw-alias-bg-mask-1,#ffffff0a)}\n.vsm-project-title{display:flex;align-items:center;gap:10px;min-width:0}\n.vsm-project-icon{color:var(--dsw-alias-brand-primary,#6d55ff);font-size:15px;flex-shrink:0}\n.vsm-project-name{font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6);display:block}\n.vsm-project-path{font-size:11px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-family:var(--ds-font-family-code,ui-monospace,monospace);display:block;margin-top:2px;word-break:break-all}\n.vsm-primary.vsm-small{padding:5px 12px;font-size:12px}\n.vsm-project-empty{text-align:center;padding:26px 16px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px}\n.vsm-project .vsm-mcp-card{border:0;border-radius:0;margin:0;border-bottom:1px solid var(--dsw-alias-border-l1,#333);background:transparent}\n.vsm-project .vsm-mcp-card:last-child{border-bottom:0}\n.vsm-project .vsm-mcp-banner{border-radius:0;margin:0}\n\n/* 新增 MCP 弹窗 */\n.vsm-mcp-modal{position:fixed;inset:0;background:var(--dsw-alias-bg-mask-drop,#000000aa);display:flex;align-items:center;justify-content:center;z-index:50}\n.vsm-mcp-dialog{width:min(560px,calc(100vw - 40px));max-height:88vh;overflow:auto;padding:22px 24px;border-radius:12px;background:var(--dsw-alias-bg-overlay,#202020);border:1px solid var(--dsw-alias-border-l2,#444);box-shadow:0 16px 60px #000c;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-mcp-dialog h3{margin:0 0 14px;font-size:16px;font-weight:600}\n.vsm-mcp-dialog label{display:flex;flex-direction:column;gap:6px;margin:12px 0;font-size:12px;color:var(--dsw-alias-label-secondary,#aaa)}\n.vsm-mcp-dialog input,.vsm-mcp-dialog select,.vsm-mcp-dialog textarea{font:inherit;color:var(--dsw-alias-label-primary,#eee);background:var(--dsw-alias-bg-layer-1,#2b2b2b);border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;padding:8px 10px}\n.vsm-mcp-dialog input:focus,.vsm-mcp-dialog select:focus,.vsm-mcp-dialog textarea:focus{outline:none;border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-mcp-dialog textarea{min-height:64px;resize:vertical;font-family:var(--ds-font-family-code,ui-monospace,monospace)}\n.vsm-mcp-dialog-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:20px}\n.vsm-mcp-dialog-actions button{padding:8px 14px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;font-size:13px}\n.vsm-mcp-dialog-actions button:hover{background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n\n/* 兼容性小节 */\n.vsm-compat-h3{margin:16px 0 2px;font-size:14px;font-weight:600;color:var(--dsw-alias-label-secondary,#c8c8c8)}\n.vsm-compat-item{display:flex;align-items:baseline;justify-content:space-between;gap:16px;padding:10px 0;border-bottom:1px solid var(--dsw-alias-border-l1,#333);font-size:13px;color:var(--dsw-alias-label-primary,#eee)}\n.vsm-compat-item.ok .vsm-compat-name::before{content:\"● \";color:var(--dsw-alias-state-success-primary,#38bd75)}\n.vsm-compat-item.warn .vsm-compat-name::before{content:\"● \";color:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-compat-name{font-weight:500;min-width:0}\n.vsm-compat-note{color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px;text-align:right;min-width:0}\n\n/* 开发形态开关 */\n.vsm-devform-note{display:block;margin-top:4px;font-size:11px;color:var(--dsw-alias-label-tertiary,#9aa5b1);word-break:break-all}\n.vsm-devform-actions{display:flex;flex-direction:column;align-items:flex-end;gap:6px;min-width:0}\n.vsm-devform-msg{color:var(--dsw-alias-state-warn-primary,#e5b93f);font-size:11px;text-align:right}\n.vsm-general-row button{border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;padding:6px 12px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;font-size:12px;white-space:nowrap}\n.vsm-general-row button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-general-row button:disabled{opacity:.55;cursor:default}\n.vsm-general-row button.vsm-devform-close{color:var(--dsw-alias-state-error-primary,#f07878);border-color:var(--dsw-alias-state-error-secondary,#d9534f55)}\n\n/* keyboard shortcuts page */\n.vsm-kb-list{display:flex;flex-direction:column;gap:6px;margin-top:8px}\n.vsm-kb-row{display:flex;align-items:center;gap:10px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:8px;background:var(--dsw-alias-bg-layer-1,#161616)}\n.vsm-kb-row.rec{border-color:var(--dsw-alias-brand-primary,#4f8cff)}\n.vsm-kb-label{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;color:var(--dsw-alias-label-primary,#eee);font-size:13px}\n.vsm-kb-warn{color:var(--dsw-alias-state-warn-primary,#e5b93f);font-size:11px}\n.vsm-kb-key{min-width:96px;text-align:center;padding:3px 10px;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;font-family:var(--ds-font-family-code,ui-monospace,monospace)}\n.vsm-kb-key.warn{border-color:var(--dsw-alias-state-warn-primary,#e5b93f);color:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-kb-key.invalid{border-color:var(--dsw-alias-state-error-primary,#d9534f);color:var(--dsw-alias-state-error-primary,#d9534f)}\n.vsm-kb-rec{font-size:12px;color:var(--dsw-alias-label-secondary,#9aa5b1)}\n.vsm-kb-actions{display:flex;gap:6px}\n.vsm-kb-actions button{border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;padding:4px 10px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;font-size:12px;white-space:nowrap}\n.vsm-kb-actions button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-kb-actions button:disabled{opacity:.55;cursor:default}\n.vsm-kb-actionsbar{display:flex;align-items:center;gap:10px;margin-top:12px}\n.vsm-kb-dirty{flex:1;font-size:12px;color:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-kb-message{font-size:12px;color:var(--dsw-alias-brand-primary,#4f8cff);margin-top:8px}\n\n/* 会话性能(PerfSettings 子 Tab) */\n.vsm-perf-page{display:flex;flex-direction:column;gap:14px;max-width:920px}\n.vsm-perf-page h2{margin:0;font-size:20px;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-perf-page p{margin:0;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:13px;line-height:1.5}\n.vsm-perf-cards{display:flex;gap:12px;flex-wrap:wrap}\n.vsm-perf-card{display:flex;flex-direction:column;gap:4px;min-width:120px;padding:12px 16px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:10px;background:var(--dsw-alias-bg-layer-2,transparent)}\n.vsm-perf-card span{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1)}\n.vsm-perf-card b{font-size:20px;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-perf-card b.big{color:var(--dsw-alias-state-error-primary,#e05252)}\n.vsm-perf-toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.vsm-perf-selcount{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1);margin:0 4px}\n.vsm-perf-ws{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:10px;overflow:hidden}\n.vsm-perf-ws-head{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:10px 14px;background:var(--dsw-alias-bg-layer-1,#262626);border-bottom:1px solid var(--dsw-alias-border-l2,#3a3a3a)}\n.vsm-perf-ws-name{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6);font-family:var(--ds-font-family-code,ui-monospace,monospace);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.vsm-perf-ws-meta{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1);flex-shrink:0}\n.vsm-perf-rows{display:flex;flex-direction:column}\n.vsm-perf-row{display:flex;align-items:center;gap:10px;padding:7px 14px;border-bottom:1px solid var(--dsw-alias-border-l1,#333);font-size:12px;color:var(--dsw-alias-label-secondary,#c8c8c8)}\n.vsm-perf-row:last-child{border-bottom:0}\n.vsm-perf-row:hover{background:var(--dsw-alias-interactive-bg-hover,#ffffff0d)}\n.vsm-perf-row input[type=checkbox]{accent-color:var(--dsw-alias-brand-primary,#6d55ff);cursor:pointer}\n.vsm-perf-cell{min-width:0}\n.vsm-perf-id{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--ds-font-family-code,ui-monospace,monospace);color:var(--dsw-alias-label-primary,#eee)}\n.vsm-perf-size{width:70px;text-align:right;flex-shrink:0;color:var(--dsw-alias-label-secondary,#c8c8c8)}\n.vsm-perf-size.big{color:var(--dsw-alias-state-error-primary,#e05252);font-weight:600}\n.vsm-perf-mtime{width:44px;flex-shrink:0;color:var(--dsw-alias-label-tertiary,#9aa5b1)}\n.vsm-perf-active{width:44px;flex-shrink:0;font-size:11px;color:var(--dsw-alias-state-warn-primary,#e5b93f);text-align:right}\n.vsm-perf-moved{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:10px;padding:12px 14px;background:var(--dsw-alias-bg-layer-2,transparent)}\n.vsm-perf-moved h3{margin:0 0 8px;font-size:14px;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-perf-config{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:10px;padding:12px 14px;background:var(--dsw-alias-bg-layer-2,transparent)}\n.vsm-perf-config h3{margin:0 0 8px;font-size:14px;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-perf-config-state{display:flex;align-items:center;gap:12px;margin:10px 0}\n.vsm-perf-note{font-size:12px;color:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-perf-block{margin:8px 0 0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:8px;background:var(--dsw-alias-bg-base,#111);color:var(--dsw-alias-label-secondary,#c8c8c8);font-size:11px;line-height:1.5;font-family:var(--ds-font-family-code,ui-monospace,monospace);white-space:pre-wrap;overflow-x:auto;max-width:100%}\n.vsm-perf-guide{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:10px;padding:12px 14px;background:var(--dsw-alias-bg-layer-2,transparent)}\n.vsm-perf-guide h3{margin:0 0 8px;font-size:14px;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-perf-modal{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:var(--dsw-alias-bg-mask-1,#000000aa);z-index:1000;padding:20px}\n.vsm-perf-modal-card{width:min(560px,92vw);max-height:72vh;display:flex;flex-direction:column;gap:12px;padding:18px 20px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:12px;background:var(--dsw-alias-bg-layer-2,#1f1f1f);color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-perf-modal-card h3{margin:0;font-size:16px}\n.vsm-perf-modal-note{font-size:12px}\n.vsm-perf-modal-list{list-style:none;margin:0;padding:0;overflow-y:auto;font-size:12px;color:var(--dsw-alias-label-secondary,#c8c8c8);display:flex;flex-direction:column;gap:4px}\n.vsm-perf-modal-list li{padding:4px 6px;border-radius:6px;background:var(--dsw-alias-bg-layer-1,#262626);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.vsm-perf-modal-more{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1)}\n.vsm-perf-modal-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:4px}\n.vsm-secondary{border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;padding:8px 16px;background:none;color:var(--dsw-alias-label-primary,#eee);font-size:13px;cursor:pointer;white-space:nowrap}\n.vsm-secondary:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-secondary:disabled{opacity:.55;cursor:default}\n.vsm-small{padding:4px 10px;font-size:12px}\n";
|
|
4235
|
+
const css = "/* dsh-vscode-mode MCP 管理界面样式。\n 全部使用 DSH 真实主题变量(--dsw-alias-*),不再用不存在的 fg-* 变量。 */\n.vsm-mcp-page{display:flex;flex-direction:column;gap:18px;width:100%;max-width:920px;box-sizing:border-box;color:var(--dsw-alias-label-primary,#e6e6e6);padding-bottom:32px}\n\n/* 顶部:标题 + 副标题 + 添加按钮 */\n.vsm-mcp-header{display:flex;align-items:flex-start;justify-content:space-between;gap:20px}\n.vsm-mcp-header h2{margin:0 0 4px;font-size:20px;font-weight:600;line-height:1.35;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-mcp-header p{margin:0;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:13px;line-height:1.5}\n.vsm-primary{border:0;border-radius:6px;padding:8px 16px;background:var(--dsw-alias-button-primary-fill,#6d55ff);color:var(--dsw-alias-button-contrast-fill,#fff);font-size:13px;font-weight:500;cursor:pointer;white-space:nowrap}\n.vsm-primary:hover{background:var(--dsw-alias-button-primary-hover,#5a46e6)}\n.vsm-primary:disabled{opacity:.55;cursor:default}\n\n/* MCP 管理下的二级子页签:我的 MCP / 项目 MCP / MCP 市场 */\n.vsm-mcp-subhead{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-top:-4px}\n.vsm-mcp-subtabs{display:flex;gap:18px;border-bottom:1px solid var(--dsw-alias-border-l1,#2e2e2e);flex:1}\n.vsm-mcp-subtabs button{border:0;background:none;color:var(--dsw-alias-label-tertiary,#9aa5b1);padding:7px 2px 9px;cursor:pointer;font-size:13px;position:relative}\n.vsm-mcp-subtabs button:hover{color:var(--dsw-alias-label-secondary,#c8c8c8)}\n.vsm-mcp-subtabs button.active{color:var(--dsw-alias-label-primary,#fff);font-weight:500}\n.vsm-mcp-subtabs button.active::after{content:\"\";position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--dsw-alias-brand-primary,#6d55ff);border-radius:2px}\n\n/* 顶层 Tab:通用 / 快捷键 / MCP 管理 / 语言服务器 / 性能优化 / 兼容性 */\n.vsm-mcp-tabs{display:flex;gap:24px;border-bottom:1px solid var(--dsw-alias-border-l2,#3a3a3a)}\n.vsm-mcp-tabs button{border:0;background:none;color:var(--dsw-alias-label-tertiary,#9aa5b1);padding:9px 2px 11px;cursor:pointer;font-size:14px;position:relative}\n.vsm-mcp-tabs button:hover{color:var(--dsw-alias-label-secondary,#c8c8c8)}\n.vsm-mcp-tabs button.active{color:var(--dsw-alias-label-primary,#fff);font-weight:500}\n.vsm-mcp-tabs button.active::after{content:\"\";position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--dsw-alias-brand-primary,#6d55ff);border-radius:2px}\n\n/* 统一线框面板:圆角矩形边框圈住各设置分区,提升可读性。\n 用法:<section class=\"vsm-panel\"> + 可选 <h3 class=\"vsm-panel-title\">。 */\n.vsm-panel{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:12px;background:var(--dsw-alias-bg-layer-2,transparent);overflow:hidden}\n.vsm-panel-pad{padding:14px 16px}\n.vsm-panel-title{margin:0;padding:10px 16px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-secondary,#c8c8c8);background:var(--dsw-alias-bg-mask-1,#ffffff0a);border-bottom:1px solid var(--dsw-alias-border-l1,#333)}\n.vsm-panel-body{padding:6px 16px}\n.vsm-panel-body>*:first-child{margin-top:8px}\n.vsm-panel-body>*:last-child{margin-bottom:10px}\n\n/* 通用设置 */\n.vsm-general-page{display:flex;flex-direction:column;gap:14px;max-width:720px}\n.vsm-general-page h2{margin:0;font-size:20px;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-general-page p{margin:0;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:13px}\n.vsm-general-row{display:flex;align-items:center;justify-content:space-between;gap:20px;padding:14px 0;border-bottom:1px solid var(--dsw-alias-border-l1,#333);color:var(--dsw-alias-label-primary,#eee);font-size:14px}\n.vsm-general-row:last-child{border-bottom:0}\n.vsm-general-row select{min-width:220px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:var(--dsw-alias-bg-layer-1,#2b2b2b);color:var(--dsw-alias-label-primary,#eee);font:inherit}\n.vsm-general-row small{color:var(--dsw-alias-label-tertiary,#9aa5b1)}\n\n/* 服务卡片 */\n.vsm-mcp-card{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:10px;padding:14px 16px;margin-bottom:10px;background:var(--dsw-alias-bg-layer-2,transparent);transition:border-color .15s}\n.vsm-mcp-card:hover{border-color:var(--dsw-alias-border-l3,#555)}\n.vsm-mcp-card-head{display:flex;justify-content:space-between;align-items:center;gap:12px}\n.vsm-mcp-title{display:flex;align-items:center;gap:9px;font-weight:600;font-size:14px;color:var(--dsw-alias-label-primary,#e6e6e6);min-width:0}\n.vsm-mcp-source{font-size:11px;font-weight:400;padding:2px 7px;border-radius:4px;background:var(--dsw-alias-bg-mask-2,#ffffff22);color:var(--dsw-alias-label-tertiary,#9aa5b1);flex-shrink:0}\n.vsm-mcp-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;background:var(--dsw-alias-state-error-primary,#e05252)}\n.vsm-mcp-dot.connected{background:var(--dsw-alias-state-success-primary,#38bd75)}\n.vsm-mcp-dot.connecting{background:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-mcp-dot.disabled{background:var(--dsw-alias-label-tertiary,#9aa5b1)}\n.vsm-mcp-actions{display:flex;align-items:center;gap:4px;flex-shrink:0}\n.vsm-mcp-actions button{border:0;background:none;color:var(--dsw-alias-label-tertiary,#9aa5b1);cursor:pointer;font-size:15px;width:28px;height:28px;border-radius:6px;line-height:1}\n.vsm-mcp-actions button:hover{color:var(--dsw-alias-label-primary,#fff);background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-mcp-actions button.vsm-danger:hover{color:var(--dsw-alias-state-error-primary,#f07878);background:var(--dsw-alias-interactive-bg-hover-danger,#ff000014)}\n.vsm-switch{font-size:14px !important}\n.vsm-switch.on{color:var(--dsw-alias-state-success-primary,#38bd75)}\n.vsm-mcp-meta{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1);margin:9px 0}\n.vsm-mcp-error{color:var(--dsw-alias-state-error-primary,#f07878);font-size:12px;margin:8px 0}\n.vsm-mcp-banner{padding:9px 12px;border:1px solid var(--dsw-alias-state-error-secondary,#d9534f55);border-radius:6px;background:var(--dsw-alias-state-error-secondary,#d9534f1a);margin-bottom:10px}\n\n/* 工具 chips */\n.vsm-mcp-tools{display:flex;flex-wrap:wrap;gap:6px}\n.vsm-mcp-chip{font:12px var(--ds-font-family-code,ui-monospace,monospace);padding:4px 8px;border-radius:5px;background:var(--dsw-alias-bg-mask-2,#ffffff14);color:var(--dsw-alias-label-secondary,#c8c8c8);border:1px solid var(--dsw-alias-border-l1,#333)}\n\n/* 空态 / 加载态 */\n.vsm-mcp-empty{text-align:center;padding:56px 20px;color:var(--dsw-alias-label-tertiary,#9aa5b1);border:1px dashed var(--dsw-alias-border-l2,#3a3a3a);border-radius:8px;font-size:13px}\n\n/* 项目搜索下拉框 */\n.vsm-project-picker{position:relative;width:100%;z-index:4}\n.vsm-project-picker-trigger{width:100%;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:8px;background:var(--dsw-alias-bg-layer-1,transparent);color:var(--dsw-alias-label-primary,#e6e6e6);cursor:pointer;text-align:left}\n.vsm-project-picker-trigger:hover{border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-picker-label{display:flex;flex-direction:column;min-width:0;font-size:13px;font-weight:500}\n.vsm-project-picker-label small{margin-top:3px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font:11px var(--ds-font-family-code,ui-monospace,monospace);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.vsm-project-picker-arrow{color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:16px}\n.vsm-project-picker-menu{position:absolute;top:calc(100% + 6px);left:0;right:0;padding:8px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:9px;background:var(--dsw-alias-bg-overlay,#202020);box-shadow:0 12px 36px #0008}\n.vsm-project-picker-search{width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:var(--dsw-alias-bg-layer-1,#2b2b2b);color:var(--dsw-alias-label-primary,#eee);outline:none}\n.vsm-project-picker-search:focus{border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-picker-options{max-height:260px;overflow:auto;margin-top:6px}\n.vsm-project-option{display:flex;width:100%;flex-direction:column;gap:3px;padding:8px 9px;border:0;border-radius:6px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;text-align:left}\n.vsm-project-option:hover,.vsm-project-option.highlight{background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-project-option.selected{color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-option small{overflow:hidden;color:var(--dsw-alias-label-tertiary,#9aa5b1);font:11px var(--ds-font-family-code,ui-monospace,monospace);text-overflow:ellipsis;white-space:nowrap}\n.vsm-project-picker-empty{padding:18px 8px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px;text-align:center}\n\n/* 项目级 MCP 分组 */\n.vsm-project{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:12px;margin-bottom:14px;overflow:hidden;background:var(--dsw-alias-bg-layer-1,transparent)}\n.vsm-project-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l1,#333);background:var(--dsw-alias-bg-mask-1,#ffffff0a)}\n.vsm-project-title{display:flex;align-items:center;gap:10px;min-width:0}\n.vsm-project-icon{color:var(--dsw-alias-brand-primary,#6d55ff);font-size:15px;flex-shrink:0}\n.vsm-project-name{font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6);display:block}\n.vsm-project-path{font-size:11px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-family:var(--ds-font-family-code,ui-monospace,monospace);display:block;margin-top:2px;word-break:break-all}\n.vsm-primary.vsm-small{padding:5px 12px;font-size:12px}\n.vsm-project-empty{text-align:center;padding:26px 16px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px}\n.vsm-project .vsm-mcp-card{border:0;border-radius:0;margin:0;border-bottom:1px solid var(--dsw-alias-border-l1,#333);background:transparent}\n.vsm-project .vsm-mcp-card:last-child{border-bottom:0}\n.vsm-project .vsm-mcp-banner{border-radius:0;margin:0}\n\n/* 新增 MCP 弹窗 */\n.vsm-mcp-modal{position:fixed;inset:0;background:var(--dsw-alias-bg-mask-drop,#000000aa);display:flex;align-items:center;justify-content:center;z-index:50}\n.vsm-mcp-dialog{width:min(560px,calc(100vw - 40px));max-height:88vh;overflow:auto;padding:22px 24px;border-radius:12px;background:var(--dsw-alias-bg-overlay,#202020);border:1px solid var(--dsw-alias-border-l2,#444);box-shadow:0 16px 60px #000c;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-mcp-dialog h3{margin:0 0 14px;font-size:16px;font-weight:600}\n.vsm-mcp-dialog label{display:flex;flex-direction:column;gap:6px;margin:12px 0;font-size:12px;color:var(--dsw-alias-label-secondary,#aaa)}\n.vsm-mcp-dialog input,.vsm-mcp-dialog select,.vsm-mcp-dialog textarea{font:inherit;color:var(--dsw-alias-label-primary,#eee);background:var(--dsw-alias-bg-layer-1,#2b2b2b);border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;padding:8px 10px}\n.vsm-mcp-dialog input:focus,.vsm-mcp-dialog select:focus,.vsm-mcp-dialog textarea:focus{outline:none;border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-mcp-dialog textarea{min-height:64px;resize:vertical;font-family:var(--ds-font-family-code,ui-monospace,monospace)}\n.vsm-mcp-dialog-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:20px}\n.vsm-mcp-dialog-actions button{padding:8px 14px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;font-size:13px}\n.vsm-mcp-dialog-actions button:hover{background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n\n/* 兼容性条目(面板标题改用 .vsm-panel-title) */\n.vsm-compat-item{display:flex;align-items:baseline;justify-content:space-between;gap:16px;padding:10px 0;border-bottom:1px solid var(--dsw-alias-border-l1,#333);font-size:13px;color:var(--dsw-alias-label-primary,#eee)}\n.vsm-compat-item.ok .vsm-compat-name::before{content:\"● \";color:var(--dsw-alias-state-success-primary,#38bd75)}\n.vsm-compat-item.warn .vsm-compat-name::before{content:\"● \";color:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-compat-name{font-weight:500;min-width:0}\n.vsm-compat-note{color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px;text-align:right;min-width:0}\n\n/* 开发形态开关 */\n.vsm-devform-note{display:block;margin-top:4px;font-size:11px;color:var(--dsw-alias-label-tertiary,#9aa5b1);word-break:break-all}\n.vsm-devform-actions{display:flex;flex-direction:column;align-items:flex-end;gap:6px;min-width:0}\n.vsm-devform-msg{color:var(--dsw-alias-state-warn-primary,#e5b93f);font-size:11px;text-align:right}\n.vsm-general-row button{border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;padding:6px 12px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;font-size:12px;white-space:nowrap}\n.vsm-general-row button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-general-row button:disabled{opacity:.55;cursor:default}\n.vsm-general-row button.vsm-devform-close{color:var(--dsw-alias-state-error-primary,#f07878);border-color:var(--dsw-alias-state-error-secondary,#d9534f55)}\n\n/* keyboard shortcuts page */\n.vsm-kb-list{display:flex;flex-direction:column;gap:6px;margin-top:8px}\n.vsm-kb-row{display:flex;align-items:center;gap:10px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:8px;background:var(--dsw-alias-bg-layer-1,#161616)}\n.vsm-kb-row.rec{border-color:var(--dsw-alias-brand-primary,#4f8cff)}\n.vsm-kb-label{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;color:var(--dsw-alias-label-primary,#eee);font-size:13px}\n.vsm-kb-warn{color:var(--dsw-alias-state-warn-primary,#e5b93f);font-size:11px}\n.vsm-kb-key{min-width:96px;text-align:center;padding:3px 10px;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;font-family:var(--ds-font-family-code,ui-monospace,monospace)}\n.vsm-kb-key.warn{border-color:var(--dsw-alias-state-warn-primary,#e5b93f);color:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-kb-key.invalid{border-color:var(--dsw-alias-state-error-primary,#d9534f);color:var(--dsw-alias-state-error-primary,#d9534f)}\n.vsm-kb-rec{font-size:12px;color:var(--dsw-alias-label-secondary,#9aa5b1)}\n.vsm-kb-actions{display:flex;gap:6px}\n.vsm-kb-actions button{border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;padding:4px 10px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;font-size:12px;white-space:nowrap}\n.vsm-kb-actions button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-kb-actions button:disabled{opacity:.55;cursor:default}\n.vsm-kb-actionsbar{display:flex;align-items:center;gap:10px;margin-top:12px}\n.vsm-kb-dirty{flex:1;font-size:12px;color:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-kb-message{font-size:12px;color:var(--dsw-alias-brand-primary,#4f8cff);margin-top:8px}\n\n/* 语言服务器(LSP)设置子 Tab */\n.vsm-lsp-form{display:flex;flex-direction:column;gap:6px;margin-top:8px}\n.vsm-lsp-form label{display:flex;flex-direction:column;gap:4px;font-size:12px;color:var(--dsw-alias-label-secondary,#9aa5b1)}\n.vsm-lsp-form input{height:26px;padding:0 8px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:var(--dsw-alias-bg-base,#111);color:var(--dsw-alias-label-primary,#eee);font-size:12px;outline:none}\n.vsm-lsp-form input:focus{border-color:var(--dsw-alias-brand-primary,#4f8cff)}\n.vsm-lsp-form button{align-self:flex-start;margin-top:2px}\n.vsm-lsp-hint{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1);margin-top:10px;line-height:1.6}\n\n/* 会话性能(PerfSettings 子 Tab) */\n.vsm-perf-page{display:flex;flex-direction:column;gap:14px;max-width:920px}\n.vsm-perf-page>.vsm-panel-body>*:first-child{margin-top:0}\n.vsm-perf-page h2{margin:0;font-size:20px;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-perf-page p{margin:0;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:13px;line-height:1.5}\n.vsm-perf-cards{display:flex;gap:12px;flex-wrap:wrap}\n.vsm-perf-card{display:flex;flex-direction:column;gap:4px;min-width:120px;padding:12px 16px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:10px;background:var(--dsw-alias-bg-layer-2,transparent)}\n.vsm-perf-card span{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1)}\n.vsm-perf-card b{font-size:20px;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-perf-card b.big{color:var(--dsw-alias-state-error-primary,#e05252)}\n.vsm-perf-toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.vsm-perf-selcount{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1);margin:0 4px}\n/* 工作区列表分页器 */\n.vsm-perf-pager{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-top:4px}\n.vsm-perf-pager-text{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1)}\n.vsm-perf-pager-actions{display:flex;gap:8px}\n.vsm-perf-ws-list{display:flex;flex-direction:column;gap:8px}\n.vsm-perf-ws{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:10px;overflow:hidden}\n.vsm-perf-ws-head{display:flex;align-items:center;gap:10px;padding:10px 14px;background:var(--dsw-alias-bg-layer-1,#262626);cursor:pointer;user-select:none}\n.vsm-perf-ws-head:hover{background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-perf-ws-head:focus-visible{outline:1px solid var(--dsw-alias-brand-primary,#6d55ff);outline-offset:-1px}\n.vsm-perf-ws.expanded .vsm-perf-ws-head{border-bottom:1px solid var(--dsw-alias-border-l2,#3a3a3a)}\n.vsm-perf-ws-caret{color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px;flex-shrink:0;transition:transform .15s;display:inline-block}\n.vsm-perf-ws-caret.open{transform:rotate(90deg)}\n.vsm-perf-ws .vsm-perf-ws-name{flex:1}\n.vsm-perf-ws-name{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6);font-family:var(--ds-font-family-code,ui-monospace,monospace);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.vsm-perf-ws-meta{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1);flex-shrink:0}\n.vsm-perf-rows{display:flex;flex-direction:column}\n.vsm-perf-row{display:flex;align-items:center;gap:10px;padding:7px 14px;border-bottom:1px solid var(--dsw-alias-border-l1,#333);font-size:12px;color:var(--dsw-alias-label-secondary,#c8c8c8)}\n.vsm-perf-row:last-child{border-bottom:0}\n.vsm-perf-row:hover{background:var(--dsw-alias-interactive-bg-hover,#ffffff0d)}\n.vsm-perf-row input[type=checkbox]{accent-color:var(--dsw-alias-brand-primary,#6d55ff);cursor:pointer}\n.vsm-perf-cell{min-width:0}\n.vsm-perf-id{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--ds-font-family-code,ui-monospace,monospace);color:var(--dsw-alias-label-primary,#eee)}\n.vsm-perf-size{width:70px;text-align:right;flex-shrink:0;color:var(--dsw-alias-label-secondary,#c8c8c8)}\n.vsm-perf-size.big{color:var(--dsw-alias-state-error-primary,#e05252);font-weight:600}\n.vsm-perf-mtime{width:44px;flex-shrink:0;color:var(--dsw-alias-label-tertiary,#9aa5b1)}\n.vsm-perf-active{width:44px;flex-shrink:0;font-size:11px;color:var(--dsw-alias-state-warn-primary,#e5b93f);text-align:right}\n.vsm-perf-config-state{display:flex;align-items:center;gap:12px;margin:10px 0}\n.vsm-perf-note{font-size:12px;color:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-perf-block{margin:8px 0 0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:8px;background:var(--dsw-alias-bg-base,#111);color:var(--dsw-alias-label-secondary,#c8c8c8);font-size:11px;line-height:1.5;font-family:var(--ds-font-family-code,ui-monospace,monospace);white-space:pre-wrap;overflow-x:auto;max-width:100%}\n.vsm-perf-modal{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:var(--dsw-alias-bg-mask-1,#000000aa);z-index:1000;padding:20px}\n.vsm-perf-modal-card{width:min(560px,92vw);max-height:72vh;display:flex;flex-direction:column;gap:12px;padding:18px 20px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:12px;background:var(--dsw-alias-bg-layer-2,#1f1f1f);color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-perf-modal-card h3{margin:0;font-size:16px}\n.vsm-perf-modal-note{font-size:12px}\n.vsm-perf-modal-list{list-style:none;margin:0;padding:0;overflow-y:auto;font-size:12px;color:var(--dsw-alias-label-secondary,#c8c8c8);display:flex;flex-direction:column;gap:4px}\n.vsm-perf-modal-list li{padding:4px 6px;border-radius:6px;background:var(--dsw-alias-bg-layer-1,#262626);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.vsm-perf-modal-more{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1)}\n.vsm-perf-modal-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:4px}\n.vsm-secondary{border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;padding:8px 16px;background:none;color:var(--dsw-alias-label-primary,#eee);font-size:13px;cursor:pointer;white-space:nowrap}\n.vsm-secondary:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-secondary:disabled{opacity:.55;cursor:default}\n.vsm-small{padding:4px 10px;font-size:12px}\n\n/* 扩展管理(LSP 语言服务器) */\n.vsm-mcp-actions button.vsm-primary,\n.vsm-mcp-actions button.vsm-danger{width:auto;height:auto;padding:5px 12px;font-size:12px;border-radius:6px;line-height:1.4}\n.vsm-mcp-actions button.vsm-danger{color:var(--dsw-alias-state-error-primary,#f07878);background:none}\n.vsm-mcp-actions button.vsm-danger:hover{background:var(--dsw-alias-interactive-bg-hover-danger,#ff000014)}\n.vsm-ext-ver{font-weight:400;font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n";
|
|
3753
4236
|
const tagId = "dsh-vscode-mode/mcp.css";
|
|
3754
4237
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
3755
4238
|
const tag = document.createElement("style");
|
|
@@ -4136,7 +4619,7 @@ window.__ModuleLoader__.load({
|
|
|
4136
4619
|
onClick: () => resetRow(command.id)
|
|
4137
4620
|
}, "恢复"))));
|
|
4138
4621
|
});
|
|
4139
|
-
return react.default.createElement("section", { className: "vsm-general-page" }, react.default.createElement("h2", null, "快捷键"), react.default.createElement("p", null, "配置编辑器视图的快捷键(保存 / 快速打开 / 侧边栏 / 搜索)。修改即时写入配置,重新聚焦编辑器后生效。"), unavailable && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, "设置服务暂不可用,当前使用默认键位。"), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), message && react.default.createElement("div", { className: "vsm-kb-message" }, message), react.default.createElement("div", { className: "vsm-kb-list" }, rows), react.default.createElement("div", { className: "vsm-kb-actionsbar" }, react.default.createElement("span", { className: "vsm-kb-dirty" }, dirty ? "有未保存的修改" : ""), react.default.createElement("button", {
|
|
4622
|
+
return react.default.createElement("section", { className: "vsm-general-page" }, react.default.createElement("h2", null, "快捷键"), react.default.createElement("p", null, "配置编辑器视图的快捷键(保存 / 快速打开 / 侧边栏 / 搜索)。修改即时写入配置,重新聚焦编辑器后生效。"), unavailable && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, "设置服务暂不可用,当前使用默认键位。"), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), message && react.default.createElement("div", { className: "vsm-kb-message" }, message), react.default.createElement("section", { className: "vsm-panel" }, react.default.createElement("h3", { className: "vsm-panel-title" }, "编辑器键位"), react.default.createElement("div", { className: "vsm-panel-body" }, react.default.createElement("div", { className: "vsm-kb-list" }, rows)), react.default.createElement("div", { className: "vsm-panel-body vsm-kb-actionsbar" }, react.default.createElement("span", { className: "vsm-kb-dirty" }, dirty ? "有未保存的修改" : ""), react.default.createElement("button", {
|
|
4140
4623
|
className: "vsm-primary vsm-small",
|
|
4141
4624
|
disabled: unavailable || notReady || busy || snapshot?.writable === false || !dirty,
|
|
4142
4625
|
onClick: save
|
|
@@ -4144,7 +4627,386 @@ window.__ModuleLoader__.load({
|
|
|
4144
4627
|
className: "vsm-small",
|
|
4145
4628
|
disabled: unavailable || notReady || busy || snapshot?.writable === false,
|
|
4146
4629
|
onClick: resetAll
|
|
4147
|
-
}, "恢复默认")));
|
|
4630
|
+
}, "恢复默认"))));
|
|
4631
|
+
}
|
|
4632
|
+
//#endregion
|
|
4633
|
+
//#region src/client/ui/LspSettings.ts
|
|
4634
|
+
/**
|
|
4635
|
+
* dsh-vscode-mode client — 「语言服务器」设置子 Tab。
|
|
4636
|
+
* 四视图:服务器(状态 + 每语言启用/命令/路径)|已安装(卸载/更新)|市场(Open VSX 搜索/安装 + 本地 vsix)|更新。
|
|
4637
|
+
* 状态经 edrv.lsp.status,配置经 edrv.lsp.configGet/Update,扩展经 edrv.lsp.ext.*。
|
|
4638
|
+
* 作者 ddj 2026-08-27
|
|
4639
|
+
*/
|
|
4640
|
+
const LANGUAGES = [{
|
|
4641
|
+
id: "lua",
|
|
4642
|
+
label: "Lua(LuaLS)",
|
|
4643
|
+
hint: "lua-language-server,EmmyLua 注解原生支持"
|
|
4644
|
+
}, {
|
|
4645
|
+
id: "csharp",
|
|
4646
|
+
label: "C#(Roslyn / OmniSharp)",
|
|
4647
|
+
hint: "需 dotnet + ms-dotnettools.csharp 的 .roslyn 服务器,或手动指定"
|
|
4648
|
+
}];
|
|
4649
|
+
const PHASE_LABEL = {
|
|
4650
|
+
idle: "未启动",
|
|
4651
|
+
starting: "启动中",
|
|
4652
|
+
ready: "就绪",
|
|
4653
|
+
indexing: "索引中",
|
|
4654
|
+
unavailable: "不可用",
|
|
4655
|
+
stopped: "已停止"
|
|
4656
|
+
};
|
|
4657
|
+
const SOURCE_LABEL = {
|
|
4658
|
+
extension: "扩展",
|
|
4659
|
+
discover: "自动发现",
|
|
4660
|
+
manual: "手动配置",
|
|
4661
|
+
none: "未配置"
|
|
4662
|
+
};
|
|
4663
|
+
const TABS = [
|
|
4664
|
+
{
|
|
4665
|
+
id: "servers",
|
|
4666
|
+
label: "服务器"
|
|
4667
|
+
},
|
|
4668
|
+
{
|
|
4669
|
+
id: "installed",
|
|
4670
|
+
label: "已安装"
|
|
4671
|
+
},
|
|
4672
|
+
{
|
|
4673
|
+
id: "market",
|
|
4674
|
+
label: "市场"
|
|
4675
|
+
},
|
|
4676
|
+
{
|
|
4677
|
+
id: "updates",
|
|
4678
|
+
label: "更新"
|
|
4679
|
+
}
|
|
4680
|
+
];
|
|
4681
|
+
/** 语言卡片:状态 + 启用开关 + 命令/路径配置。 */
|
|
4682
|
+
function LangCard({ lang, config, status, busy, onToggle, onSave }) {
|
|
4683
|
+
const [pathDraft, setPathDraft] = react.default.useState(config?.path ?? "");
|
|
4684
|
+
const [commandDraft, setCommandDraft] = react.default.useState(config?.command ?? "");
|
|
4685
|
+
react.default.useEffect(() => {
|
|
4686
|
+
setPathDraft(config?.path ?? "");
|
|
4687
|
+
setCommandDraft(config?.command ?? "");
|
|
4688
|
+
}, [config?.path, config?.command]);
|
|
4689
|
+
const phase = status?.phase ?? "idle";
|
|
4690
|
+
return react.default.createElement("article", { className: "vsm-mcp-card" }, react.default.createElement("div", { className: "vsm-mcp-card-head" }, react.default.createElement("div", { className: "vsm-mcp-title" }, react.default.createElement("span", { className: "vsm-mcp-dot " + (phase === "ready" || phase === "indexing" ? "connected" : phase === "unavailable" ? "error" : "disabled") }), lang.label), react.default.createElement("div", { className: "vsm-mcp-actions" }, react.default.createElement("button", {
|
|
4691
|
+
className: "vsm-switch " + (config?.enabled !== false ? "on" : ""),
|
|
4692
|
+
onClick: () => onToggle(lang.id, config?.enabled !== false ? false : true),
|
|
4693
|
+
"aria-label": "启用/禁用"
|
|
4694
|
+
}, config?.enabled !== false ? "●" : "○"))), react.default.createElement("div", { className: "vsm-mcp-meta" }, PHASE_LABEL[phase] ?? phase, " · ", SOURCE_LABEL[status?.source] ?? status?.source, phase === "ready" && status?.root ? " · " + String(status.root).split(/[\\/]/).pop() : ""), status?.reason ? react.default.createElement("div", { className: "vsm-mcp-error" }, status.reason) : null, react.default.createElement("div", { className: "vsm-lsp-form" }, react.default.createElement("label", null, react.default.createElement("span", null, "可执行文件路径(绝对路径,优先)"), react.default.createElement("input", {
|
|
4695
|
+
value: pathDraft,
|
|
4696
|
+
placeholder: "如 C:/.../lua-language-server.exe",
|
|
4697
|
+
onChange: (e) => setPathDraft(e.target.value)
|
|
4698
|
+
})), react.default.createElement("label", null, react.default.createElement("span", null, "命令名(PATH 内查找)"), react.default.createElement("input", {
|
|
4699
|
+
value: commandDraft,
|
|
4700
|
+
placeholder: "如 lua-language-server",
|
|
4701
|
+
onChange: (e) => setCommandDraft(e.target.value)
|
|
4702
|
+
})), react.default.createElement("button", {
|
|
4703
|
+
className: "vsm-primary",
|
|
4704
|
+
disabled: busy === lang.id,
|
|
4705
|
+
onClick: () => onSave(lang.id, pathDraft, commandDraft)
|
|
4706
|
+
}, busy === lang.id ? "保存中…" : "保存配置")), react.default.createElement("div", { className: "vsm-lsp-hint" }, lang.hint));
|
|
4707
|
+
}
|
|
4708
|
+
/** 已装扩展卡片:清单 + 卸载/更新。 */
|
|
4709
|
+
function InstalledCard({ ext, busy, onUninstall, onUpdate }) {
|
|
4710
|
+
return react.default.createElement("article", { className: "vsm-mcp-card" }, react.default.createElement("div", { className: "vsm-mcp-card-head" }, react.default.createElement("div", { className: "vsm-mcp-title" }, react.default.createElement("span", { className: "vsm-mcp-dot connected" }), react.default.createElement("span", null, ext.displayName || ext.name), react.default.createElement("span", { className: "vsm-ext-ver" }, ext.id + " · v" + ext.version)), react.default.createElement("div", { className: "vsm-mcp-actions" }, react.default.createElement("button", {
|
|
4711
|
+
className: "vsm-primary vsm-small",
|
|
4712
|
+
disabled: busy === ext.id,
|
|
4713
|
+
onClick: () => onUpdate(ext)
|
|
4714
|
+
}, "更新"), react.default.createElement("button", {
|
|
4715
|
+
className: "vsm-danger vsm-small",
|
|
4716
|
+
disabled: busy === ext.id,
|
|
4717
|
+
onClick: () => onUninstall(ext)
|
|
4718
|
+
}, "卸载"))), ext.description ? react.default.createElement("div", { className: "vsm-lsp-hint" }, ext.description) : null);
|
|
4719
|
+
}
|
|
4720
|
+
/** 市场条目卡片:安装/已装态。 */
|
|
4721
|
+
function MarketCard({ item, installed, busy, onInstall }) {
|
|
4722
|
+
const has = installed.some((e) => e.id === item.id);
|
|
4723
|
+
return react.default.createElement("article", { className: "vsm-mcp-card" }, react.default.createElement("div", { className: "vsm-mcp-card-head" }, react.default.createElement("div", { className: "vsm-mcp-title" }, react.default.createElement("span", { className: "vsm-mcp-dot " + (has ? "connected" : "disabled") }), react.default.createElement("span", null, item.displayName || item.name), react.default.createElement("span", { className: "vsm-ext-ver" }, item.id + " · v" + item.version)), react.default.createElement("div", { className: "vsm-mcp-actions" }, react.default.createElement("button", {
|
|
4724
|
+
className: "vsm-primary vsm-small",
|
|
4725
|
+
disabled: has || busy === item.id,
|
|
4726
|
+
onClick: () => onInstall(item)
|
|
4727
|
+
}, has ? "已安装" : "安装"))), item.description ? react.default.createElement("div", { className: "vsm-lsp-hint" }, item.description) : null);
|
|
4728
|
+
}
|
|
4729
|
+
/**
|
|
4730
|
+
* 语言服务器设置页:服务器状态/配置 + VSIX 扩展(安装/更新/卸载三视图)。
|
|
4731
|
+
* @author ddj 2026年08月27号
|
|
4732
|
+
*/
|
|
4733
|
+
function LspSettings() {
|
|
4734
|
+
const [tab, setTab] = react.default.useState("servers");
|
|
4735
|
+
const [config, setConfig] = react.default.useState({});
|
|
4736
|
+
const [servers, setServers] = react.default.useState([]);
|
|
4737
|
+
const [installed, setInstalled] = react.default.useState([]);
|
|
4738
|
+
const [updates, setUpdates] = react.default.useState([]);
|
|
4739
|
+
const [market, setMarket] = react.default.useState([]);
|
|
4740
|
+
const [query, setQuery] = react.default.useState("");
|
|
4741
|
+
const [vsixPath, setVsixPath] = react.default.useState("");
|
|
4742
|
+
const [busy, setBusy] = react.default.useState("");
|
|
4743
|
+
const [error, setError] = react.default.useState("");
|
|
4744
|
+
const [loading, setLoading] = react.default.useState(true);
|
|
4745
|
+
const refreshServers = react.default.useCallback(() => {
|
|
4746
|
+
Promise.all([rpc("edrv.lsp.configGet", {}), rpc("edrv.lsp.status", {})]).then(([cfg, st]) => {
|
|
4747
|
+
if (cfg?.ok) setConfig(cfg.config ?? {});
|
|
4748
|
+
if (st?.ok) setServers(st.servers ?? []);
|
|
4749
|
+
setError("");
|
|
4750
|
+
}).catch((e) => setError(String(e)));
|
|
4751
|
+
}, []);
|
|
4752
|
+
const refreshExt = react.default.useCallback(() => {
|
|
4753
|
+
Promise.all([rpc("edrv.lsp.ext.list", {}), rpc("edrv.lsp.ext.updates", {})]).then(([l, u]) => {
|
|
4754
|
+
if (l?.ok) setInstalled(l.extensions ?? []);
|
|
4755
|
+
if (u?.ok) setUpdates(u.updates ?? []);
|
|
4756
|
+
setError("");
|
|
4757
|
+
}).catch((e) => setError(String(e)));
|
|
4758
|
+
}, []);
|
|
4759
|
+
const refresh = react.default.useCallback(() => {
|
|
4760
|
+
setLoading(true);
|
|
4761
|
+
Promise.all([refreshServers(), refreshExt()]).finally(() => setLoading(false));
|
|
4762
|
+
}, [refreshServers, refreshExt]);
|
|
4763
|
+
react.default.useEffect(() => {
|
|
4764
|
+
refresh();
|
|
4765
|
+
}, [refresh]);
|
|
4766
|
+
const switchTab = (next) => {
|
|
4767
|
+
setTab(next);
|
|
4768
|
+
setError("");
|
|
4769
|
+
if (next === "market" && !query) setQuery("lua language server");
|
|
4770
|
+
if (next === "installed" || next === "updates") refreshExt();
|
|
4771
|
+
if (next === "servers") refreshServers();
|
|
4772
|
+
};
|
|
4773
|
+
const saveLang = (languageId, path, command) => {
|
|
4774
|
+
setBusy(languageId);
|
|
4775
|
+
setError("");
|
|
4776
|
+
rpc("edrv.lsp.configUpdate", {
|
|
4777
|
+
languageId,
|
|
4778
|
+
path,
|
|
4779
|
+
command
|
|
4780
|
+
}).then((res) => {
|
|
4781
|
+
if (!res?.ok) {
|
|
4782
|
+
setError(res?.error ?? "保存失败");
|
|
4783
|
+
return;
|
|
4784
|
+
}
|
|
4785
|
+
setConfig(res.config ?? {});
|
|
4786
|
+
}).catch((e) => setError(String(e))).finally(() => setBusy(""));
|
|
4787
|
+
};
|
|
4788
|
+
const toggleLang = (languageId, enabled) => {
|
|
4789
|
+
setBusy(languageId);
|
|
4790
|
+
rpc("edrv.lsp.configUpdate", {
|
|
4791
|
+
languageId,
|
|
4792
|
+
enabled
|
|
4793
|
+
}).then((res) => {
|
|
4794
|
+
if (res?.ok) {
|
|
4795
|
+
setConfig(res.config ?? {});
|
|
4796
|
+
refreshServers();
|
|
4797
|
+
} else setError(res?.error ?? "切换失败");
|
|
4798
|
+
}).catch((e) => setError(String(e))).finally(() => setBusy(""));
|
|
4799
|
+
};
|
|
4800
|
+
const searchMarket = () => {
|
|
4801
|
+
if (!query.trim()) return;
|
|
4802
|
+
setBusy("__market");
|
|
4803
|
+
setError("");
|
|
4804
|
+
rpc("edrv.lsp.ext.market", {
|
|
4805
|
+
query: query.trim(),
|
|
4806
|
+
size: 12
|
|
4807
|
+
}).then((res) => {
|
|
4808
|
+
if (res?.ok) setMarket(res.extensions ?? []);
|
|
4809
|
+
else setError(res?.error ?? "搜索失败");
|
|
4810
|
+
}).catch((e) => setError(String(e))).finally(() => setBusy(""));
|
|
4811
|
+
};
|
|
4812
|
+
const installMarket = (item) => {
|
|
4813
|
+
setBusy(item.id);
|
|
4814
|
+
setError("");
|
|
4815
|
+
rpc("edrv.lsp.ext.install", {
|
|
4816
|
+
namespace: item.namespace,
|
|
4817
|
+
name: item.name
|
|
4818
|
+
}).then((res) => {
|
|
4819
|
+
if (!res?.ok) {
|
|
4820
|
+
setError(res?.error ?? "安装失败");
|
|
4821
|
+
return;
|
|
4822
|
+
}
|
|
4823
|
+
refreshExt();
|
|
4824
|
+
}).catch((e) => setError(String(e))).finally(() => setBusy(""));
|
|
4825
|
+
};
|
|
4826
|
+
const installLocal = () => {
|
|
4827
|
+
if (!vsixPath.trim()) return;
|
|
4828
|
+
setBusy("__vsix");
|
|
4829
|
+
setError("");
|
|
4830
|
+
rpc("edrv.lsp.ext.install", { vsixPath: vsixPath.trim() }).then((res) => {
|
|
4831
|
+
if (!res?.ok) {
|
|
4832
|
+
setError(res?.error ?? "安装失败");
|
|
4833
|
+
return;
|
|
4834
|
+
}
|
|
4835
|
+
setVsixPath("");
|
|
4836
|
+
refreshExt();
|
|
4837
|
+
}).catch((e) => setError(String(e))).finally(() => setBusy(""));
|
|
4838
|
+
};
|
|
4839
|
+
const uninstallExt = (ext) => {
|
|
4840
|
+
if (!window.confirm("卸载扩展 " + ext.id + "?")) return;
|
|
4841
|
+
setBusy(ext.id);
|
|
4842
|
+
setError("");
|
|
4843
|
+
rpc("edrv.lsp.ext.uninstall", { id: ext.id }).then((res) => {
|
|
4844
|
+
if (!res?.ok) setError(res?.error ?? "卸载失败");
|
|
4845
|
+
refreshExt();
|
|
4846
|
+
}).catch((e) => setError(String(e))).finally(() => setBusy(""));
|
|
4847
|
+
};
|
|
4848
|
+
const updateExt = (ext) => {
|
|
4849
|
+
setBusy(ext.id);
|
|
4850
|
+
setError("");
|
|
4851
|
+
rpc("edrv.lsp.ext.update", { id: ext.id }).then((res) => {
|
|
4852
|
+
if (!res?.ok) setError(res?.error ?? "更新失败");
|
|
4853
|
+
refreshExt();
|
|
4854
|
+
}).catch((e) => setError(String(e))).finally(() => setBusy(""));
|
|
4855
|
+
};
|
|
4856
|
+
if (loading) return react.default.createElement("div", { className: "vsm-mcp-empty" }, "正在读取语言服务器状态…");
|
|
4857
|
+
const nav = react.default.createElement("nav", { className: "vsm-mcp-tabs" }, TABS.map((t) => react.default.createElement("button", {
|
|
4858
|
+
key: t.id,
|
|
4859
|
+
className: tab === t.id ? "active" : "",
|
|
4860
|
+
onClick: () => switchTab(t.id)
|
|
4861
|
+
}, t.label)));
|
|
4862
|
+
const header = react.default.createElement("div", { className: "vsm-mcp-header" }, react.default.createElement("div", null, react.default.createElement("h2", null, "语言服务器(LSP)"), react.default.createElement("p", null, "代码索引 / 引用查找 / 动态跳转;可安装 VSIX 语言服务器扩展(Open VSX 市场 / 本地 .vsix)。")), react.default.createElement("button", {
|
|
4863
|
+
className: "vsm-primary",
|
|
4864
|
+
onClick: refresh
|
|
4865
|
+
}, "↻ 刷新"));
|
|
4866
|
+
const banner = error ? react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error) : null;
|
|
4867
|
+
/** 线框面板容器:标题 + 内容体。 */
|
|
4868
|
+
const panel = (title, children) => react.default.createElement("section", { className: "vsm-panel" }, react.default.createElement("h3", { className: "vsm-panel-title" }, title), react.default.createElement("div", { className: "vsm-panel-body" }, children));
|
|
4869
|
+
let body = null;
|
|
4870
|
+
if (tab === "servers") body = panel("语言服务器", react.default.createElement(react.default.Fragment, null, LANGUAGES.map((lang) => react.default.createElement(LangCard, {
|
|
4871
|
+
key: lang.id,
|
|
4872
|
+
lang,
|
|
4873
|
+
config: config[lang.id] ?? {},
|
|
4874
|
+
status: servers.find((s) => s.languageId === lang.id),
|
|
4875
|
+
busy,
|
|
4876
|
+
onToggle: toggleLang,
|
|
4877
|
+
onSave: saveLang
|
|
4878
|
+
})), react.default.createElement("div", { className: "vsm-lsp-hint" }, "提示:打开 Lua/C# 文件即自动(惰性)启动对应语言服务器;未配置时编辑器功能不受影响,大纲回退内置解析。")));
|
|
4879
|
+
else if (tab === "installed") body = panel("已安装扩展", react.default.createElement(react.default.Fragment, null, react.default.createElement("div", { className: "vsm-lsp-hint" }, "已安装的语言服务器扩展(存于 ~/.dsh/dsh-vscode-mode/extensions/)。语言服务器被自动发现为「扩展」源。"), installed.length ? installed.map((ext) => react.default.createElement(InstalledCard, {
|
|
4880
|
+
key: ext.id,
|
|
4881
|
+
ext,
|
|
4882
|
+
busy,
|
|
4883
|
+
onUninstall: uninstallExt,
|
|
4884
|
+
onUpdate: updateExt
|
|
4885
|
+
})) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "还没有安装扩展(去「市场」或指定本地 .vsix)")));
|
|
4886
|
+
else if (tab === "market") body = panel("扩展市场", react.default.createElement(react.default.Fragment, null, react.default.createElement("div", { className: "vsm-lsp-form" }, react.default.createElement("label", null, react.default.createElement("span", null, "搜索 Open VSX 市场"), react.default.createElement("input", {
|
|
4887
|
+
value: query,
|
|
4888
|
+
placeholder: "如 sumneko.lua / lua language server",
|
|
4889
|
+
onChange: (e) => setQuery(e.target.value),
|
|
4890
|
+
onKeyDown: (e) => {
|
|
4891
|
+
if (e.key === "Enter") searchMarket();
|
|
4892
|
+
}
|
|
4893
|
+
})), react.default.createElement("button", {
|
|
4894
|
+
className: "vsm-primary",
|
|
4895
|
+
disabled: busy === "__market",
|
|
4896
|
+
onClick: searchMarket
|
|
4897
|
+
}, busy === "__market" ? "搜索中…" : "搜索")), react.default.createElement("div", { className: "vsm-lsp-form" }, react.default.createElement("label", null, react.default.createElement("span", null, "安装本地 .vsix 文件(服务器绝对路径)"), react.default.createElement("input", {
|
|
4898
|
+
value: vsixPath,
|
|
4899
|
+
placeholder: "如 C:/Downloads/sumneko.lua-3.19.1.vsix",
|
|
4900
|
+
onChange: (e) => setVsixPath(e.target.value)
|
|
4901
|
+
})), react.default.createElement("button", {
|
|
4902
|
+
className: "vsm-primary",
|
|
4903
|
+
disabled: busy === "__vsix",
|
|
4904
|
+
onClick: installLocal
|
|
4905
|
+
}, busy === "__vsix" ? "安装中…" : "安装本地 .vsix")), react.default.createElement("div", { className: "vsm-lsp-hint" }, "市场安装会下载并解包 VSIX 到扩展目录;含语言服务器(如 sumneko.lua)的扩展会被自动发现。"), market.map((item) => react.default.createElement(MarketCard, {
|
|
4906
|
+
key: item.id,
|
|
4907
|
+
item,
|
|
4908
|
+
installed,
|
|
4909
|
+
busy,
|
|
4910
|
+
onInstall: installMarket
|
|
4911
|
+
}))));
|
|
4912
|
+
else body = panel("可用更新", react.default.createElement(react.default.Fragment, null, react.default.createElement("div", { className: "vsm-lsp-hint" }, "已装扩展与 Open VSX 最新版的差异。"), updates.length ? updates.map((u) => react.default.createElement("article", {
|
|
4913
|
+
key: u.id,
|
|
4914
|
+
className: "vsm-mcp-card"
|
|
4915
|
+
}, react.default.createElement("div", { className: "vsm-mcp-card-head" }, react.default.createElement("div", { className: "vsm-mcp-title" }, react.default.createElement("span", { className: "vsm-mcp-dot error" }), react.default.createElement("span", null, u.displayName || u.id), react.default.createElement("span", { className: "vsm-ext-ver" }, "v" + u.current + " → v" + u.latest)), react.default.createElement("div", { className: "vsm-mcp-actions" }, react.default.createElement("button", {
|
|
4916
|
+
className: "vsm-primary vsm-small",
|
|
4917
|
+
disabled: busy === u.id,
|
|
4918
|
+
onClick: () => updateExt(u)
|
|
4919
|
+
}, busy === u.id ? "更新中…" : "更新"))))) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "全部已是最新")));
|
|
4920
|
+
return react.default.createElement("div", null, header, nav, banner, body);
|
|
4921
|
+
}
|
|
4922
|
+
//#endregion
|
|
4923
|
+
//#region src/client/state/workspaceFoldCache.ts
|
|
4924
|
+
/**
|
|
4925
|
+
* dsh-vscode-mode client — 「性能优化」页工作区栏目展开状态持久化(纯函数,可单测)。
|
|
4926
|
+
* 只持久化「已展开」的工作区键集合(默认全部折叠),对齐 explorerCache 的做法:
|
|
4927
|
+
* 不缓存会话条目本身,条目由 edrv.perf.inventory 实时返回。
|
|
4928
|
+
* 键前缀统一走 paths.ts PathConst(CACHE_KEY.workspaceFold)。
|
|
4929
|
+
* 作者 ddj 2026-09-02
|
|
4930
|
+
*/
|
|
4931
|
+
/** localStorage 键(全局,不按会话隔离)。 */
|
|
4932
|
+
const STORE_KEY = CACHE_KEY.workspaceFold;
|
|
4933
|
+
/**
|
|
4934
|
+
* 归一化工作区键:仅去首尾空白(工作区键非文件系统路径,不做斜杠转换)。
|
|
4935
|
+
* @author ddj 2026年09月02号
|
|
4936
|
+
* @param key 工作区标识
|
|
4937
|
+
* @returns 归一化键,空值返回 ''
|
|
4938
|
+
*/
|
|
4939
|
+
function normalizeFoldKey(key) {
|
|
4940
|
+
return String(key ?? "").trim();
|
|
4941
|
+
}
|
|
4942
|
+
/**
|
|
4943
|
+
* 裁剪展开键集:去重、丢弃空值、超容量丢弃尾部(保持传入顺序)。
|
|
4944
|
+
* @author ddj 2026年09月02号
|
|
4945
|
+
* @param expanded 原始展开键集合
|
|
4946
|
+
* @returns 规范化后的键数组
|
|
4947
|
+
*/
|
|
4948
|
+
function sanitizeFolded(expanded) {
|
|
4949
|
+
if (!Array.isArray(expanded)) return [];
|
|
4950
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4951
|
+
const out = [];
|
|
4952
|
+
for (const item of expanded) {
|
|
4953
|
+
const key = normalizeFoldKey(typeof item === "string" ? item : "");
|
|
4954
|
+
if (!key || seen.has(key)) continue;
|
|
4955
|
+
seen.add(key);
|
|
4956
|
+
out.push(key);
|
|
4957
|
+
if (out.length >= 300) break;
|
|
4958
|
+
}
|
|
4959
|
+
return out;
|
|
4960
|
+
}
|
|
4961
|
+
/**
|
|
4962
|
+
* 序列化展开状态(JSON,带 schema 版本号)。
|
|
4963
|
+
* @author ddj 2026年09月02号
|
|
4964
|
+
* @param expanded 已展开的项目键集合
|
|
4965
|
+
* @returns JSON 字符串
|
|
4966
|
+
*/
|
|
4967
|
+
function serializeFold(expanded) {
|
|
4968
|
+
return JSON.stringify({
|
|
4969
|
+
v: 1,
|
|
4970
|
+
expanded: sanitizeFolded(expanded)
|
|
4971
|
+
});
|
|
4972
|
+
}
|
|
4973
|
+
/**
|
|
4974
|
+
* 解析缓存文本;损坏/格式不符 → 空集合。
|
|
4975
|
+
* @author ddj 2026年09月02号
|
|
4976
|
+
* @param text localStorage 原文
|
|
4977
|
+
* @returns 已展开键集合
|
|
4978
|
+
*/
|
|
4979
|
+
function parseFold(text) {
|
|
4980
|
+
if (!text) return [];
|
|
4981
|
+
try {
|
|
4982
|
+
const data = JSON.parse(text);
|
|
4983
|
+
if (!data || typeof data !== "object") return [];
|
|
4984
|
+
return sanitizeFolded(data.expanded);
|
|
4985
|
+
} catch (error) {
|
|
4986
|
+
return [];
|
|
4987
|
+
}
|
|
4988
|
+
}
|
|
4989
|
+
/**
|
|
4990
|
+
* 读取展开状态(localStorage 不可用 → 空集合)。
|
|
4991
|
+
* @author ddj 2026年09月02号
|
|
4992
|
+
* @returns 已展开键集合
|
|
4993
|
+
*/
|
|
4994
|
+
function foldLoad() {
|
|
4995
|
+
try {
|
|
4996
|
+
return parseFold(window.localStorage.getItem(STORE_KEY));
|
|
4997
|
+
} catch (error) {
|
|
4998
|
+
return [];
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
/**
|
|
5002
|
+
* 写入展开状态(配额满/隐私模式失败静默)。
|
|
5003
|
+
* @author ddj 2026年09月02号
|
|
5004
|
+
* @param expanded 已展开键集合
|
|
5005
|
+
*/
|
|
5006
|
+
function foldSave(expanded) {
|
|
5007
|
+
try {
|
|
5008
|
+
window.localStorage.setItem(STORE_KEY, serializeFold(expanded));
|
|
5009
|
+
} catch (error) {}
|
|
4148
5010
|
}
|
|
4149
5011
|
//#endregion
|
|
4150
5012
|
//#region src/client/ui/PerfSettings.ts
|
|
@@ -4166,6 +5028,43 @@ window.__ModuleLoader__.load({
|
|
|
4166
5028
|
}
|
|
4167
5029
|
/** 会话行 key:workspaceKey \u0000 sessionId。 */
|
|
4168
5030
|
const keyOf = (ws, id) => ws + "\0" + id;
|
|
5031
|
+
/** 工作区列表每页条数。 */
|
|
5032
|
+
const PAGE_SIZE = 7;
|
|
5033
|
+
/**
|
|
5034
|
+
* 计算分页(纯函数,可单测)。
|
|
5035
|
+
* 作者 ddj 2026年09月02号
|
|
5036
|
+
* @param total 总条数
|
|
5037
|
+
* @param pageSize 每页条数
|
|
5038
|
+
* @param page 当前页码(1 起)
|
|
5039
|
+
* @returns 归一化后的分页信息(page 被夹到有效范围)
|
|
5040
|
+
*/
|
|
5041
|
+
function paginate(total, pageSize, page) {
|
|
5042
|
+
const size = Math.max(1, Math.floor(pageSize) || 1);
|
|
5043
|
+
const pages = Math.max(1, Math.ceil(total / size));
|
|
5044
|
+
const current = Math.min(Math.max(1, Math.floor(page) || 1), pages);
|
|
5045
|
+
const start = (current - 1) * size;
|
|
5046
|
+
return {
|
|
5047
|
+
page: current,
|
|
5048
|
+
pages,
|
|
5049
|
+
pageSize: size,
|
|
5050
|
+
start,
|
|
5051
|
+
end: Math.min(start + size, total),
|
|
5052
|
+
total
|
|
5053
|
+
};
|
|
5054
|
+
}
|
|
5055
|
+
/**
|
|
5056
|
+
* 取当前页切片(纯函数,可单测)。
|
|
5057
|
+
* 作者 ddj 2026年09月02号
|
|
5058
|
+
* @param items 全量列表
|
|
5059
|
+
* @param page 当前页码(1 起)
|
|
5060
|
+
* @param pageSize 每页条数
|
|
5061
|
+
* @returns 当前页子数组
|
|
5062
|
+
*/
|
|
5063
|
+
function pageSlice(items, page, pageSize) {
|
|
5064
|
+
const list = items ?? [];
|
|
5065
|
+
const { start, end } = paginate(list.length, pageSize, page);
|
|
5066
|
+
return list.slice(start, end);
|
|
5067
|
+
}
|
|
4169
5068
|
/** 单行会话。 */
|
|
4170
5069
|
function SessionRow({ row, checked, disabled, onToggle }) {
|
|
4171
5070
|
const age = Math.max(0, (Date.now() - row.mtime) / 864e5);
|
|
@@ -4184,6 +5083,63 @@ window.__ModuleLoader__.load({
|
|
|
4184
5083
|
}, when), row.active ? react.default.createElement("span", { className: "vsm-perf-cell vsm-perf-active" }, "活跃") : null);
|
|
4185
5084
|
}
|
|
4186
5085
|
/**
|
|
5086
|
+
* 工作区列表分页器:顶部文案 + 上一页/下一页交互。
|
|
5087
|
+
* 作者 ddj 2026年09月02号
|
|
5088
|
+
* @param {object} info paginate() 的分页信息
|
|
5089
|
+
* @param {Function} onPrev 上一页回调
|
|
5090
|
+
* @param {Function} onNext 下一页回调
|
|
5091
|
+
*/
|
|
5092
|
+
function WorkspacePager({ info, onPrev, onNext }) {
|
|
5093
|
+
const { page, pages, start, end, total } = info;
|
|
5094
|
+
return react.default.createElement("div", { className: "vsm-perf-pager" }, react.default.createElement("span", { className: "vsm-perf-pager-text" }, total ? `第 ${start + 1}–${end} 个 / 共 ${total} 个工作区(第 ${page}/${pages} 页)` : "暂无工作区"), react.default.createElement("div", { className: "vsm-perf-pager-actions" }, react.default.createElement("button", {
|
|
5095
|
+
className: "vsm-secondary vsm-small",
|
|
5096
|
+
onClick: onPrev,
|
|
5097
|
+
disabled: page <= 1
|
|
5098
|
+
}, "‹ 上一页"), react.default.createElement("button", {
|
|
5099
|
+
className: "vsm-secondary vsm-small",
|
|
5100
|
+
onClick: onNext,
|
|
5101
|
+
disabled: page >= pages
|
|
5102
|
+
}, "下一页 ›")));
|
|
5103
|
+
}
|
|
5104
|
+
/**
|
|
5105
|
+
* 工作区栏目(可展开/折叠),内含该工作区的会话行。
|
|
5106
|
+
* 作者 ddj 2026年09月02号
|
|
5107
|
+
* @param {object} ws 工作区汇总(workspaceKey / totalBytes)
|
|
5108
|
+
* @param {object[]} rows 该工作区的会话行
|
|
5109
|
+
* @param {boolean} expanded 是否展开
|
|
5110
|
+
* @param {Function} onToggleFold 展开/折叠切换回调
|
|
5111
|
+
* @param {object} selected 已选会话键集合
|
|
5112
|
+
* @param {boolean} locked 会话勾选是否禁用(移出执行中)
|
|
5113
|
+
* @param {Function} onToggleRow 单行勾选回调
|
|
5114
|
+
*/
|
|
5115
|
+
function WorkspaceBlock({ ws, rows, expanded, onToggleFold, selected, locked, onToggleRow }) {
|
|
5116
|
+
const head = react.default.createElement("div", {
|
|
5117
|
+
className: "vsm-perf-ws-head",
|
|
5118
|
+
role: "button",
|
|
5119
|
+
tabIndex: 0,
|
|
5120
|
+
"aria-expanded": expanded,
|
|
5121
|
+
onClick: onToggleFold,
|
|
5122
|
+
onKeyDown: (event) => {
|
|
5123
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
5124
|
+
event.preventDefault();
|
|
5125
|
+
onToggleFold();
|
|
5126
|
+
}
|
|
5127
|
+
}
|
|
5128
|
+
}, react.default.createElement("span", { className: "vsm-perf-ws-caret " + (expanded ? "open" : "") }, "▸"), react.default.createElement("span", {
|
|
5129
|
+
className: "vsm-perf-ws-name",
|
|
5130
|
+
title: ws.workspaceKey
|
|
5131
|
+
}, ws.workspaceKey), react.default.createElement("span", { className: "vsm-perf-ws-meta" }, rows.length + " 会话 · " + formatBytes(ws.totalBytes)));
|
|
5132
|
+
if (!expanded) return react.default.createElement("div", { className: "vsm-perf-ws" }, head);
|
|
5133
|
+
const body = rows.length ? rows.map((row) => react.default.createElement(SessionRow, {
|
|
5134
|
+
key: keyOf(row.workspaceKey, row.sessionId),
|
|
5135
|
+
row,
|
|
5136
|
+
checked: !!selected[keyOf(row.workspaceKey, row.sessionId)],
|
|
5137
|
+
disabled: row.active || locked,
|
|
5138
|
+
onToggle: () => onToggleRow(keyOf(row.workspaceKey, row.sessionId))
|
|
5139
|
+
})) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "无会话");
|
|
5140
|
+
return react.default.createElement("div", { className: "vsm-perf-ws expanded" }, head, react.default.createElement("div", { className: "vsm-perf-rows" }, body));
|
|
5141
|
+
}
|
|
5142
|
+
/**
|
|
4187
5143
|
* 会话卫生 + 压缩调优设置子页。
|
|
4188
5144
|
* @author ddj 2026年09月02号
|
|
4189
5145
|
*/
|
|
@@ -4195,6 +5151,26 @@ window.__ModuleLoader__.load({
|
|
|
4195
5151
|
const [confirm, setConfirm] = react.default.useState(null);
|
|
4196
5152
|
const [moved, setMoved] = react.default.useState(null);
|
|
4197
5153
|
const [config, setConfig] = react.default.useState(null);
|
|
5154
|
+
const [expanded, setExpanded] = react.default.useState(() => foldLoad());
|
|
5155
|
+
const [page, setPage] = react.default.useState(1);
|
|
5156
|
+
react.default.useEffect(() => {
|
|
5157
|
+
foldSave(expanded);
|
|
5158
|
+
}, [expanded]);
|
|
5159
|
+
/** 数据变化后把越界页码夹回有效范围。 */
|
|
5160
|
+
const workspaces = inv?.workspaces ?? [];
|
|
5161
|
+
const pageInfo = paginate(workspaces.length, PAGE_SIZE, page);
|
|
5162
|
+
react.default.useEffect(() => {
|
|
5163
|
+
if (page !== pageInfo.page) setPage(pageInfo.page);
|
|
5164
|
+
}, [page, pageInfo.page]);
|
|
5165
|
+
/** 切换某工作区栏目的展开/折叠(默认全折叠)。 */
|
|
5166
|
+
const toggleFold = (key) => setExpanded((old) => old.includes(key) ? old.filter((item) => item !== key) : old.concat(key));
|
|
5167
|
+
/** 切换单会话勾选。 */
|
|
5168
|
+
const toggleRow = (k) => setSelected((old) => {
|
|
5169
|
+
const next = { ...old };
|
|
5170
|
+
if (next[k]) delete next[k];
|
|
5171
|
+
else next[k] = true;
|
|
5172
|
+
return next;
|
|
5173
|
+
});
|
|
4198
5174
|
const load = react.default.useCallback(() => {
|
|
4199
5175
|
setError("");
|
|
4200
5176
|
Promise.all([rpc("edrv.perf.inventory", {}), rpc("edrv.perf.configGet", {})]).then(([i, c]) => {
|
|
@@ -4297,30 +5273,25 @@ window.__ModuleLoader__.load({
|
|
|
4297
5273
|
};
|
|
4298
5274
|
const totals = inv?.totals;
|
|
4299
5275
|
const summaryCards = totals ? react.default.createElement("div", { className: "vsm-perf-cards" }, react.default.createElement("div", { className: "vsm-perf-card" }, react.default.createElement("span", null, "工作区"), react.default.createElement("b", null, String(totals.workspaces))), react.default.createElement("div", { className: "vsm-perf-card" }, react.default.createElement("span", null, "会话数"), react.default.createElement("b", null, String(totals.sessions))), react.default.createElement("div", { className: "vsm-perf-card" }, react.default.createElement("span", null, "压缩态总体积"), react.default.createElement("b", { className: totals.totalBytes >= 52428800 ? "big" : "" }, formatBytes(totals.totalBytes)))) : null;
|
|
4300
|
-
const
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
}, react.default.createElement("div", { className: "vsm-perf-ws-head" }, react.default.createElement("span", {
|
|
4306
|
-
className: "vsm-perf-ws-name",
|
|
4307
|
-
title: ws.workspaceKey
|
|
4308
|
-
}, ws.workspaceKey), react.default.createElement("span", { className: "vsm-perf-ws-meta" }, rows.length + " 会话 · " + formatBytes(ws.totalBytes))), react.default.createElement("div", { className: "vsm-perf-rows" }, rows.length ? rows.map((row) => react.default.createElement(SessionRow, {
|
|
4309
|
-
key: keyOf(row.workspaceKey, row.sessionId),
|
|
4310
|
-
row,
|
|
4311
|
-
checked: !!selected[keyOf(row.workspaceKey, row.sessionId)],
|
|
4312
|
-
disabled: row.active || busy === "move",
|
|
4313
|
-
onToggle: () => {
|
|
4314
|
-
const k = keyOf(row.workspaceKey, row.sessionId);
|
|
4315
|
-
setSelected((old) => {
|
|
4316
|
-
const next = { ...old };
|
|
4317
|
-
if (next[k]) delete next[k];
|
|
4318
|
-
else next[k] = true;
|
|
4319
|
-
return next;
|
|
4320
|
-
});
|
|
4321
|
-
}
|
|
4322
|
-
})) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "无会话")));
|
|
5276
|
+
const visible = pageSlice(workspaces, pageInfo.page, PAGE_SIZE);
|
|
5277
|
+
const workspacePager = workspaces.length > PAGE_SIZE ? react.default.createElement(WorkspacePager, {
|
|
5278
|
+
info: pageInfo,
|
|
5279
|
+
onPrev: () => setPage((old) => Math.max(1, old - 1)),
|
|
5280
|
+
onNext: () => setPage((old) => Math.min(pageInfo.pages, old + 1))
|
|
4323
5281
|
}) : null;
|
|
5282
|
+
const workspaceBlocks = workspaces.length ? react.default.createElement("div", { className: "vsm-perf-ws-list" }, visible.map((ws) => {
|
|
5283
|
+
const key = normalizeFoldKey(ws.workspaceKey);
|
|
5284
|
+
return react.default.createElement(WorkspaceBlock, {
|
|
5285
|
+
key: ws.workspaceKey,
|
|
5286
|
+
ws,
|
|
5287
|
+
rows: inv.sessions.filter((s) => s.workspaceKey === ws.workspaceKey),
|
|
5288
|
+
expanded: expanded.includes(key),
|
|
5289
|
+
onToggleFold: () => toggleFold(key),
|
|
5290
|
+
selected,
|
|
5291
|
+
locked: busy === "move",
|
|
5292
|
+
onToggleRow: toggleRow
|
|
5293
|
+
});
|
|
5294
|
+
})) : null;
|
|
4324
5295
|
const selCount = Object.keys(selected).length;
|
|
4325
5296
|
const confirmBody = confirm ? react.default.createElement("div", { className: "vsm-perf-modal" }, react.default.createElement("div", { className: "vsm-perf-modal-card" }, react.default.createElement("h3", null, "确认移出 " + confirm.items.length + " 个会话?"), react.default.createElement("p", { className: "vsm-perf-modal-note" }, "将从 ~/.dsh/sessions 移出到 ~/.dsh/sessions-archive(可逆,重启后不再回放,释放 " + formatBytes(confirm.reclaimedBytes) + ")。活跃会话已排除。"), react.default.createElement("ul", { className: "vsm-perf-modal-list" }, confirm.items.slice(0, 20).map((item) => react.default.createElement("li", { key: item.workspaceKey + item.sessionId }, item.workspaceKey + " / " + item.sessionId + "(" + formatBytes(item.bytes) + ")"))), confirm.items.length > 20 ? react.default.createElement("div", { className: "vsm-perf-modal-more" }, "…等 " + confirm.items.length + " 个") : null, react.default.createElement("div", { className: "vsm-perf-modal-actions" }, react.default.createElement("button", {
|
|
4326
5297
|
className: "vsm-primary",
|
|
@@ -4330,15 +5301,15 @@ window.__ModuleLoader__.load({
|
|
|
4330
5301
|
className: "vsm-secondary",
|
|
4331
5302
|
onClick: () => setConfirm(null)
|
|
4332
5303
|
}, "取消")))) : null;
|
|
4333
|
-
const movedBody = moved?.moved?.length ? react.default.createElement("
|
|
5304
|
+
const movedBody = moved?.moved?.length ? react.default.createElement("section", { className: "vsm-panel" }, react.default.createElement("h3", { className: "vsm-panel-title" }, "已移出 " + moved.moved.length + " 个会话(释放 " + formatBytes(moved.reclaimedBytes) + ")"), react.default.createElement("div", { className: "vsm-panel-body" }, react.default.createElement("div", { className: "vsm-perf-rows" }, moved.moved.map((item) => react.default.createElement("div", {
|
|
4334
5305
|
key: item.workspaceKey + item.sessionId,
|
|
4335
5306
|
className: "vsm-perf-row"
|
|
4336
5307
|
}, react.default.createElement("span", { className: "vsm-perf-cell vsm-perf-id" }, item.sessionId), react.default.createElement("span", { className: "vsm-perf-cell vsm-perf-size" }, formatBytes(item.bytes)), react.default.createElement("button", {
|
|
4337
5308
|
className: "vsm-secondary vsm-small",
|
|
4338
5309
|
onClick: () => doRestore(item),
|
|
4339
5310
|
disabled: busy === "restore"
|
|
4340
|
-
}, "恢复")))), moved.failures?.length ? react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, "失败:" + moved.failures.join(";")) : null) : null;
|
|
4341
|
-
const configBody = config ? react.default.createElement("
|
|
5311
|
+
}, "恢复")))), moved.failures?.length ? react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, "失败:" + moved.failures.join(";")) : null)) : null;
|
|
5312
|
+
const configBody = config ? react.default.createElement("section", { className: "vsm-panel" }, react.default.createElement("h3", { className: "vsm-panel-title" }, "DSH 内置压缩调优"), react.default.createElement("div", { className: "vsm-panel-body" }, react.default.createElement("p", null, "调低 compaction-basic / tool-result-pruner 触发阈值,让长循环会话更早压缩,降低模型可见上下文压力(减少 token 与防 context overflow)。"), react.default.createElement("p", { className: "vsm-perf-note" }, "注意:压缩只追加 compaction 事件,不重写已持久化会话日志(append-only),因此不会缩小已存在的会话文件——存量瘦身请用上方「移出到归档」。packChunks 已默认开启,无需配置。"), react.default.createElement("div", { className: "vsm-perf-config-state" }, react.default.createElement("span", { className: "vsm-perf-cell" }, "状态:" + (config.applied ? "已应用" : "未应用")), config.applied ? react.default.createElement("button", {
|
|
4342
5313
|
className: "vsm-secondary vsm-small",
|
|
4343
5314
|
onClick: undoConfig,
|
|
4344
5315
|
disabled: busy === "undo"
|
|
@@ -4346,9 +5317,9 @@ window.__ModuleLoader__.load({
|
|
|
4346
5317
|
className: "vsm-primary vsm-small",
|
|
4347
5318
|
onClick: applyConfig,
|
|
4348
5319
|
disabled: busy === "apply"
|
|
4349
|
-
}, "应用建议配置(需重启)")), react.default.createElement("pre", { className: "vsm-perf-block" }, config.block)) : null;
|
|
4350
|
-
const guideBody = react.default.createElement("
|
|
4351
|
-
return react.default.createElement("section", { className: "vsm-perf-page" }, react.default.createElement("h2", null, "会话性能"), react.default.createElement("p", null, "DSH 启动会回放 ~/.dsh/sessions 全部会话(V8 展开约 10×),体积越大内存越吃紧。定期把巨型/旧会话移出到归档可显著降低启动内存。"), error ? react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error) : null, summaryCards, react.default.createElement("div", { className: "vsm-perf-toolbar" }, react.default.createElement("button", {
|
|
5320
|
+
}, "应用建议配置(需重启)")), react.default.createElement("pre", { className: "vsm-perf-block" }, config.block))) : null;
|
|
5321
|
+
const guideBody = react.default.createElement("section", { className: "vsm-panel" }, react.default.createElement("h3", { className: "vsm-panel-title" }, "侧车摘要(agent/脚本取关键字段,不整份读 sidecar)"), react.default.createElement("div", { className: "vsm-panel-body" }, react.default.createElement("p", null, "工作区根目录的 .dsh-edit-review.json(可能数 MB)只应通过摘要接口读取,禁止 agent 整份 read/write 进对话:"), react.default.createElement("pre", { className: "vsm-perf-block" }, "curl -s -X POST http://127.0.0.1:3080/edrv/rpc -H 'content-type: application/json' -d '{\"method\":\"edrv.perf.sidecarSummary\",\"args\":{}}'\n# 差异记录列表(含每文件 pending 摘要):edrv.list\n# 历史归档:edrv.archiveList / edrv.archiveRead")));
|
|
5322
|
+
return react.default.createElement("section", { className: "vsm-perf-page" }, react.default.createElement("h2", null, "会话性能"), react.default.createElement("p", null, "DSH 启动会回放 ~/.dsh/sessions 全部会话(V8 展开约 10×),体积越大内存越吃紧。定期把巨型/旧会话移出到归档可显著降低启动内存。"), error ? react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error) : null, summaryCards, react.default.createElement("section", { className: "vsm-panel" }, react.default.createElement("h3", { className: "vsm-panel-title" }, "会话盘点"), react.default.createElement("div", { className: "vsm-panel-body" }, react.default.createElement("div", { className: "vsm-perf-toolbar" }, react.default.createElement("button", {
|
|
4352
5323
|
className: "vsm-secondary vsm-small",
|
|
4353
5324
|
onClick: selectBig
|
|
4354
5325
|
}, "圈选 ≥1MB 非活跃"), react.default.createElement("button", {
|
|
@@ -4367,15 +5338,14 @@ window.__ModuleLoader__.load({
|
|
|
4367
5338
|
className: "vsm-secondary vsm-small",
|
|
4368
5339
|
onClick: load,
|
|
4369
5340
|
disabled: busy === "load"
|
|
4370
|
-
}, "⟳ 刷新")), workspaceBlocks, movedBody, configBody, guideBody, confirmBody);
|
|
5341
|
+
}, "⟳ 刷新")), workspacePager, workspaceBlocks)), movedBody, configBody, guideBody, confirmBody);
|
|
4371
5342
|
}
|
|
4372
5343
|
//#endregion
|
|
4373
5344
|
//#region src/client/ui/McpSettings.ts
|
|
4374
5345
|
/**
|
|
4375
|
-
* dsh-vscode-mode client — VSCodeMode 设置区:通用 / 快捷键 / MCP
|
|
4376
|
-
*
|
|
4377
|
-
*
|
|
4378
|
-
* 作者 ddj 2026年08月22号 / 2026年08月26号
|
|
5346
|
+
* dsh-vscode-mode client — VSCodeMode 设置区:通用 / 快捷键 / MCP 管理 / 语言服务器 / 性能优化 / 兼容性。
|
|
5347
|
+
* MCP 管理 Tab 下含三个子页签:我的 MCP(profile 全局)/ 项目 MCP(各项目 .mcp.json)/ MCP 市场(占位)。
|
|
5348
|
+
* 作者 ddj 2026年08月22号 / 2026年08月26号 / 2026年08月27号
|
|
4379
5349
|
*/
|
|
4380
5350
|
const EMPTY = {
|
|
4381
5351
|
serverName: "",
|
|
@@ -4588,14 +5558,14 @@ window.__ModuleLoader__.load({
|
|
|
4588
5558
|
disabled: devBusy,
|
|
4589
5559
|
onClick: closeDevForm
|
|
4590
5560
|
}, devBusy ? "切换中…" : "关闭开发形态"))) : null;
|
|
4591
|
-
return react.default.createElement("section", { className: "vsm-general-page" }, react.default.createElement("h2", null, "通用"), react.default.createElement("p", null, "配置 DSH 对话中文件链接的打开工具。工具由当前运行时自动扫描。"), unavailable && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, "设置服务暂不可用,当前使用自动选择。"), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), react.default.createElement("label", { className: "vsm-general-row" }, react.default.createElement("span", null, "文件链接使用工具"), react.default.createElement("select", {
|
|
5561
|
+
return react.default.createElement("section", { className: "vsm-general-page" }, react.default.createElement("h2", null, "通用"), react.default.createElement("p", null, "配置 DSH 对话中文件链接的打开工具。工具由当前运行时自动扫描。"), unavailable && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, "设置服务暂不可用,当前使用自动选择。"), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), react.default.createElement("section", { className: "vsm-panel" }, react.default.createElement("h3", { className: "vsm-panel-title" }, "文件与安装"), react.default.createElement("div", { className: "vsm-panel-body" }, react.default.createElement("label", { className: "vsm-general-row" }, react.default.createElement("span", null, "文件链接使用工具"), react.default.createElement("select", {
|
|
4592
5562
|
value: currentOption,
|
|
4593
5563
|
disabled: loading || unavailable || notReady || busy || snapshot?.writable === false,
|
|
4594
5564
|
onChange: (event) => save(event.target.value)
|
|
4595
5565
|
}, options.map((item) => react.default.createElement("option", {
|
|
4596
5566
|
key: item.id,
|
|
4597
5567
|
value: item.id
|
|
4598
|
-
}, item.label))), loading && react.default.createElement("small", null, "读取中…")), devFormRow);
|
|
5568
|
+
}, item.label))), loading && react.default.createElement("small", null, "读取中…")), devFormRow)));
|
|
4599
5569
|
}
|
|
4600
5570
|
/** 兼容性小节:host 报告(RPC compat)+ 客户端本地适配项。 */
|
|
4601
5571
|
function CompatSection({ getSummary }) {
|
|
@@ -4618,12 +5588,111 @@ window.__ModuleLoader__.load({
|
|
|
4618
5588
|
key: item.name,
|
|
4619
5589
|
className: "vsm-compat-item " + (item.active ? "ok" : "warn")
|
|
4620
5590
|
}, react.default.createElement("span", { className: "vsm-compat-name" }, item.name), react.default.createElement("span", { className: "vsm-compat-note" }, item.note ?? (item.active ? "正常" : "未生效"))));
|
|
5591
|
+
/** 兼容性分组面板:标题 + 线框内容区。 */
|
|
5592
|
+
const group = (title, children) => react.default.createElement("section", { className: "vsm-panel" }, react.default.createElement("h3", { className: "vsm-panel-title" }, title), react.default.createElement("div", { className: "vsm-panel-body" }, children));
|
|
4621
5593
|
const warnings = report?.warnings ?? [];
|
|
4622
5594
|
const devForm = report?.devForm;
|
|
4623
|
-
return react.default.createElement("section", { className: "vsm-general-page" }, react.default.createElement("h2", null, "兼容性"), react.default.createElement("p", null, "检测与其他插件 / DSH 版本的适配状态与已知问题。" + (report?.pluginVersion ? "(插件版本 " + report.pluginVersion + ")" : "")), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), devForm?.enabled &&
|
|
5595
|
+
return react.default.createElement("section", { className: "vsm-general-page" }, react.default.createElement("h2", null, "兼容性"), react.default.createElement("p", null, "检测与其他插件 / DSH 版本的适配状态与已知问题。" + (report?.pluginVersion ? "(插件版本 " + report.pluginVersion + ")" : "")), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), devForm?.enabled && group("开发形态", react.default.createElement("div", { className: "vsm-compat-item ok" }, react.default.createElement("span", { className: "vsm-compat-name" }, "开发形态开启(工作区链接安装)"), react.default.createElement("span", { className: "vsm-compat-note" }, devForm.path))), group("外部插件与服务", renderItems(report?.external)), group("客户端适配", renderItems(getSummary?.() ?? [])), group("护栏", renderItems(report?.guards)), group("警告", warnings.length ? warnings.map((w, index) => react.default.createElement("div", {
|
|
4624
5596
|
key: index,
|
|
4625
5597
|
className: "vsm-mcp-error vsm-mcp-banner"
|
|
4626
|
-
}, w)) : react.default.createElement("div", { className: "vsm-compat-item ok" }, react.default.createElement("span", { className: "vsm-compat-name" }, "未检测到兼容性问题"), react.default.createElement("span", { className: "vsm-compat-note" }, "")));
|
|
5598
|
+
}, w)) : react.default.createElement("div", { className: "vsm-compat-item ok" }, react.default.createElement("span", { className: "vsm-compat-name" }, "未检测到兼容性问题"), react.default.createElement("span", { className: "vsm-compat-note" }, ""))));
|
|
5599
|
+
}
|
|
5600
|
+
/** MCP 管理子页签定义(我的 / 项目 / 市场)。 */
|
|
5601
|
+
const MCP_TABS = [
|
|
5602
|
+
{
|
|
5603
|
+
id: "mine",
|
|
5604
|
+
label: "我的 MCP"
|
|
5605
|
+
},
|
|
5606
|
+
{
|
|
5607
|
+
id: "projects",
|
|
5608
|
+
label: "项目 MCP"
|
|
5609
|
+
},
|
|
5610
|
+
{
|
|
5611
|
+
id: "market",
|
|
5612
|
+
label: "MCP 市场"
|
|
5613
|
+
}
|
|
5614
|
+
];
|
|
5615
|
+
/**
|
|
5616
|
+
* MCP 管理面板:顶层「MCP 管理」Tab 下的三个子页签容器。
|
|
5617
|
+
* 作者 ddj 2026年08月27号
|
|
5618
|
+
* @param {object[]} servers 全局 MCP 服务列表
|
|
5619
|
+
* @param {object[]} projects 项目及其项目级 MCP 列表
|
|
5620
|
+
* @param {string} busy 正在执行的操作标识
|
|
5621
|
+
* @param {object} draft 新增表单草稿
|
|
5622
|
+
* @param {Function} edit 草稿字段变更回调
|
|
5623
|
+
* @param {Function} resetDraft 清空草稿回调
|
|
5624
|
+
*/
|
|
5625
|
+
function McpManagePanel({ servers, projects, busy, draft, edit, resetDraft, refreshGlobal, toggleGlobal, removeGlobal, refreshProject, toggleProject, removeProject, saveGlobal, saveProject }) {
|
|
5626
|
+
const [mcpTab, setMcpTab] = react.default.useState("mine");
|
|
5627
|
+
const [showForm, setShowForm] = react.default.useState(false);
|
|
5628
|
+
const [projectForm, setProjectForm] = react.default.useState(null);
|
|
5629
|
+
const [selectedPath, setSelectedPath] = react.default.useState("");
|
|
5630
|
+
react.default.useEffect(() => {
|
|
5631
|
+
if (!projects.length) {
|
|
5632
|
+
setSelectedPath("");
|
|
5633
|
+
return;
|
|
5634
|
+
}
|
|
5635
|
+
if (!projects.some((project) => project.workspacePath === selectedPath)) setSelectedPath(projects[0].workspacePath);
|
|
5636
|
+
}, [projects, selectedPath]);
|
|
5637
|
+
const closeGlobalForm = () => setShowForm(false);
|
|
5638
|
+
const closeProjectForm = () => setProjectForm(null);
|
|
5639
|
+
const addProject = (project) => {
|
|
5640
|
+
resetDraft?.();
|
|
5641
|
+
setProjectForm({
|
|
5642
|
+
workspacePath: project.workspacePath,
|
|
5643
|
+
title: project.title
|
|
5644
|
+
});
|
|
5645
|
+
};
|
|
5646
|
+
const selectedProject = projects.find((project) => project.workspacePath === selectedPath);
|
|
5647
|
+
const mineBody = servers.length ? servers.map((s) => react.default.createElement(ServerCard, {
|
|
5648
|
+
key: s.id,
|
|
5649
|
+
server: s,
|
|
5650
|
+
onRefresh: refreshGlobal,
|
|
5651
|
+
onToggle: toggleGlobal,
|
|
5652
|
+
onRemove: removeGlobal
|
|
5653
|
+
})) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "还没有配置全局 MCP");
|
|
5654
|
+
const projectBody = projects.length ? react.default.createElement(react.default.Fragment, null, react.default.createElement(ProjectPicker, {
|
|
5655
|
+
projects,
|
|
5656
|
+
value: selectedPath,
|
|
5657
|
+
onChange: setSelectedPath
|
|
5658
|
+
}), selectedProject ? react.default.createElement(ProjectGroup, {
|
|
5659
|
+
project: selectedProject,
|
|
5660
|
+
busy,
|
|
5661
|
+
onAdd: addProject,
|
|
5662
|
+
onRefresh: refreshProject,
|
|
5663
|
+
onToggle: toggleProject,
|
|
5664
|
+
onRemove: removeProject
|
|
5665
|
+
}) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "请选择项目")) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "还没有项目");
|
|
5666
|
+
let body;
|
|
5667
|
+
if (mcpTab === "projects") body = projectBody;
|
|
5668
|
+
else if (mcpTab === "market") body = react.default.createElement("div", { className: "vsm-mcp-empty" }, "MCP 市场暂未接入");
|
|
5669
|
+
else body = mineBody;
|
|
5670
|
+
const form = showForm ? react.default.createElement(McpForm, {
|
|
5671
|
+
title: "添加全局 MCP",
|
|
5672
|
+
draft,
|
|
5673
|
+
busy,
|
|
5674
|
+
edit,
|
|
5675
|
+
save: () => saveGlobal(closeGlobalForm),
|
|
5676
|
+
close: closeGlobalForm
|
|
5677
|
+
}) : projectForm ? react.default.createElement(McpForm, {
|
|
5678
|
+
title: "添加项目 MCP · " + projectForm.title,
|
|
5679
|
+
draft,
|
|
5680
|
+
busy,
|
|
5681
|
+
edit,
|
|
5682
|
+
save: () => saveProject(projectForm.workspacePath, closeProjectForm),
|
|
5683
|
+
close: closeProjectForm
|
|
5684
|
+
}) : null;
|
|
5685
|
+
return react.default.createElement(react.default.Fragment, null, react.default.createElement("div", { className: "vsm-mcp-subhead" }, react.default.createElement("nav", { className: "vsm-mcp-subtabs" }, MCP_TABS.map((item) => react.default.createElement("button", {
|
|
5686
|
+
key: item.id,
|
|
5687
|
+
className: mcpTab === item.id ? "active" : "",
|
|
5688
|
+
onClick: () => setMcpTab(item.id)
|
|
5689
|
+
}, item.label))), react.default.createElement("button", {
|
|
5690
|
+
className: "vsm-primary vsm-small",
|
|
5691
|
+
onClick: () => {
|
|
5692
|
+
resetDraft?.();
|
|
5693
|
+
setShowForm(true);
|
|
5694
|
+
}
|
|
5695
|
+
}, "+ 添加全局 MCP")), body, form);
|
|
4627
5696
|
}
|
|
4628
5697
|
/** MCP 管理主面板。 */
|
|
4629
5698
|
function McpSettings(props) {
|
|
@@ -4636,16 +5705,6 @@ window.__ModuleLoader__.load({
|
|
|
4636
5705
|
const [error, setError] = react.default.useState("");
|
|
4637
5706
|
const [tab, setTab] = react.default.useState("general");
|
|
4638
5707
|
const [draft, setDraft] = react.default.useState(EMPTY);
|
|
4639
|
-
const [showForm, setShowForm] = react.default.useState(false);
|
|
4640
|
-
const [projectForm, setProjectForm] = react.default.useState(null);
|
|
4641
|
-
const [selectedPath, setSelectedPath] = react.default.useState("");
|
|
4642
|
-
react.default.useEffect(() => {
|
|
4643
|
-
if (!projects.length) {
|
|
4644
|
-
setSelectedPath("");
|
|
4645
|
-
return;
|
|
4646
|
-
}
|
|
4647
|
-
if (!projects.some((project) => project.workspacePath === selectedPath)) setSelectedPath(projects[0].workspacePath);
|
|
4648
|
-
}, [projects, selectedPath]);
|
|
4649
5708
|
const loadAll = react.default.useCallback(() => {
|
|
4650
5709
|
setLoading(true);
|
|
4651
5710
|
setError("");
|
|
@@ -4695,112 +5754,87 @@ window.__ModuleLoader__.load({
|
|
|
4695
5754
|
workspacePath: p.workspacePath,
|
|
4696
5755
|
serverName: s.serverName
|
|
4697
5756
|
}, replaceProject(p.workspacePath));
|
|
4698
|
-
|
|
5757
|
+
/**
|
|
5758
|
+
* 保存全局 MCP:成功后由回调关闭子面板。
|
|
5759
|
+
* 作者 ddj 2026年08月27号
|
|
5760
|
+
* @param {Function} close 关闭新增表单回调
|
|
5761
|
+
*/
|
|
5762
|
+
const saveGlobal = (close) => {
|
|
4699
5763
|
setBusy("save");
|
|
4700
5764
|
setError("");
|
|
4701
5765
|
rpc("mcp.save", { config: configOf(draft) }).then(finish).then((result) => {
|
|
4702
5766
|
setServers((old) => old.some((s) => s.id === result.server.id) ? old.map((s) => s.id === result.server.id ? result.server : s) : old.concat(result.server));
|
|
4703
5767
|
setDraft(EMPTY);
|
|
4704
|
-
|
|
5768
|
+
close();
|
|
4705
5769
|
}).catch((e) => setError(String(e))).finally(() => setBusy(""));
|
|
4706
5770
|
};
|
|
4707
|
-
|
|
4708
|
-
|
|
5771
|
+
/**
|
|
5772
|
+
* 保存项目 MCP:成功后由回调关闭子面板。
|
|
5773
|
+
* 作者 ddj 2026年08月27号
|
|
5774
|
+
* @param {string} workspacePath 目标项目路径
|
|
5775
|
+
* @param {Function} close 关闭新增表单回调
|
|
5776
|
+
*/
|
|
5777
|
+
const saveProject = (workspacePath, close) => {
|
|
4709
5778
|
setBusy("project-save");
|
|
4710
5779
|
setError("");
|
|
4711
5780
|
const config = configOf(draft);
|
|
4712
5781
|
rpc("mcp.projectSave", {
|
|
4713
|
-
workspacePath
|
|
5782
|
+
workspacePath,
|
|
4714
5783
|
serverName: config.serverName,
|
|
4715
5784
|
config
|
|
4716
5785
|
}).then(finish).then((result) => {
|
|
4717
|
-
setProjects((old) => old.map((p) => p.workspacePath ===
|
|
5786
|
+
setProjects((old) => old.map((p) => p.workspacePath === workspacePath ? result.project : p));
|
|
4718
5787
|
setDraft(EMPTY);
|
|
4719
|
-
|
|
5788
|
+
close();
|
|
4720
5789
|
}).catch((e) => setError(String(e))).finally(() => setBusy(""));
|
|
4721
5790
|
};
|
|
4722
5791
|
const edit = (key, value) => setDraft((old) => ({
|
|
4723
5792
|
...old,
|
|
4724
5793
|
[key]: value
|
|
4725
5794
|
}));
|
|
4726
|
-
const
|
|
4727
|
-
const selectedProject = projects.find((project) => project.workspacePath === selectedPath);
|
|
4728
|
-
const projectBody = projects.length ? react.default.createElement(react.default.Fragment, null, react.default.createElement(ProjectPicker, {
|
|
4729
|
-
projects,
|
|
4730
|
-
value: selectedPath,
|
|
4731
|
-
onChange: setSelectedPath
|
|
4732
|
-
}), selectedProject ? react.default.createElement(ProjectGroup, {
|
|
4733
|
-
project: selectedProject,
|
|
4734
|
-
busy,
|
|
4735
|
-
onAdd: (project) => {
|
|
4736
|
-
setDraft(EMPTY);
|
|
4737
|
-
setProjectForm({
|
|
4738
|
-
workspacePath: project.workspacePath,
|
|
4739
|
-
title: project.title
|
|
4740
|
-
});
|
|
4741
|
-
},
|
|
4742
|
-
onRefresh: refreshProject,
|
|
4743
|
-
onToggle: toggleProject,
|
|
4744
|
-
onRemove: removeProject
|
|
4745
|
-
}) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "请选择项目")) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "还没有项目");
|
|
5795
|
+
const resetDraft = () => setDraft(EMPTY);
|
|
4746
5796
|
let body;
|
|
4747
5797
|
if (loading) body = react.default.createElement("div", { className: "vsm-mcp-empty" }, "正在读取 MCP 服务…");
|
|
4748
5798
|
else if (tab === "general") body = react.default.createElement(GeneralSettings, { registry: openerRegistry });
|
|
4749
5799
|
else if (tab === "keys") body = react.default.createElement(KeybindingsPanel, null);
|
|
4750
|
-
else if (tab === "
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
onRefresh: refreshGlobal,
|
|
4754
|
-
onToggle: toggleGlobal,
|
|
4755
|
-
onRemove: removeGlobal
|
|
4756
|
-
})) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "还没有配置全局 MCP"));
|
|
4757
|
-
else if (tab === "projects") body = projectBody;
|
|
4758
|
-
else if (tab === "compat") body = react.default.createElement(CompatSection, { getSummary: compatSummary });
|
|
4759
|
-
else if (tab === "perf") body = react.default.createElement(PerfSettings, null);
|
|
4760
|
-
else body = marketBody;
|
|
4761
|
-
const form = showForm ? react.default.createElement(McpForm, {
|
|
4762
|
-
title: "添加全局 MCP",
|
|
4763
|
-
draft,
|
|
5800
|
+
else if (tab === "mcp") body = react.default.createElement(McpManagePanel, {
|
|
5801
|
+
servers,
|
|
5802
|
+
projects,
|
|
4764
5803
|
busy,
|
|
4765
|
-
edit,
|
|
4766
|
-
save: saveGlobal,
|
|
4767
|
-
close: () => setShowForm(false)
|
|
4768
|
-
}) : projectForm ? react.default.createElement(McpForm, {
|
|
4769
|
-
title: "添加项目 MCP · " + projectForm.title,
|
|
4770
5804
|
draft,
|
|
4771
|
-
busy,
|
|
4772
5805
|
edit,
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
}
|
|
5806
|
+
resetDraft,
|
|
5807
|
+
saveGlobal: (close) => saveGlobal(close),
|
|
5808
|
+
saveProject: (workspacePath, close) => saveProject(workspacePath, close),
|
|
5809
|
+
refreshGlobal,
|
|
5810
|
+
toggleGlobal,
|
|
5811
|
+
removeGlobal,
|
|
5812
|
+
refreshProject,
|
|
5813
|
+
toggleProject,
|
|
5814
|
+
removeProject
|
|
5815
|
+
});
|
|
5816
|
+
else if (tab === "compat") body = react.default.createElement(CompatSection, { getSummary: compatSummary });
|
|
5817
|
+
else if (tab === "lsp") body = react.default.createElement(LspSettings, null);
|
|
5818
|
+
else body = react.default.createElement(PerfSettings, null);
|
|
5819
|
+
return react.default.createElement("section", { className: "vsm-mcp-page" }, react.default.createElement("header", { className: "vsm-mcp-header" }, react.default.createElement("div", null, react.default.createElement("h2", null, "VSCodeMode"), react.default.createElement("p", null, "管理当前 profile 与各项目的 Model Context Protocol 服务。"))), react.default.createElement("nav", { className: "vsm-mcp-tabs" }, react.default.createElement("button", {
|
|
4783
5820
|
className: tab === "general" ? "active" : "",
|
|
4784
5821
|
onClick: () => setTab("general")
|
|
4785
5822
|
}, "通用"), react.default.createElement("button", {
|
|
4786
5823
|
className: tab === "keys" ? "active" : "",
|
|
4787
5824
|
onClick: () => setTab("keys")
|
|
4788
5825
|
}, "快捷键"), react.default.createElement("button", {
|
|
4789
|
-
className: tab === "
|
|
4790
|
-
onClick: () => setTab("
|
|
4791
|
-
}, "
|
|
4792
|
-
className: tab === "
|
|
4793
|
-
onClick: () => setTab("
|
|
4794
|
-
}, "
|
|
5826
|
+
className: tab === "mcp" ? "active" : "",
|
|
5827
|
+
onClick: () => setTab("mcp")
|
|
5828
|
+
}, "MCP 管理"), react.default.createElement("button", {
|
|
5829
|
+
className: tab === "lsp" ? "active" : "",
|
|
5830
|
+
onClick: () => setTab("lsp")
|
|
5831
|
+
}, "语言服务器"), react.default.createElement("button", {
|
|
4795
5832
|
className: tab === "perf" ? "active" : "",
|
|
4796
5833
|
onClick: () => setTab("perf")
|
|
4797
5834
|
}, "性能优化"), react.default.createElement("button", {
|
|
4798
|
-
className: tab === "market" ? "active" : "",
|
|
4799
|
-
onClick: () => setTab("market")
|
|
4800
|
-
}, "MCP 市场"), react.default.createElement("button", {
|
|
4801
5835
|
className: tab === "compat" ? "active" : "",
|
|
4802
5836
|
onClick: () => setTab("compat")
|
|
4803
|
-
}, "兼容性")), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), body
|
|
5837
|
+
}, "兼容性")), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), body);
|
|
4804
5838
|
}
|
|
4805
5839
|
/** MCP 新增表单(全局/项目共用)。 */
|
|
4806
5840
|
function McpForm({ title, draft, busy, edit, save, close }) {
|
|
@@ -6393,12 +7427,36 @@ window.__ModuleLoader__.load({
|
|
|
6393
7427
|
computeEnds(out, lines.length);
|
|
6394
7428
|
return out;
|
|
6395
7429
|
}
|
|
6396
|
-
/** Lua:`function Foo:bar` / `M.bar = class|function
|
|
7430
|
+
/** Lua:`function Foo:bar` / `M.bar = class|function` + EmmyLua 注解(---@class/@alias/@enum/@field)。 */
|
|
6397
7431
|
function parseLua(text) {
|
|
6398
7432
|
const out = [];
|
|
6399
7433
|
const lines = text.split("\n");
|
|
7434
|
+
/** 当前注解类(收拢紧随的 ---@field 为字段子节点)。 */
|
|
7435
|
+
let pendingClass = null;
|
|
6400
7436
|
for (let i = 0; i < lines.length; i++) {
|
|
6401
7437
|
const line = lines[i];
|
|
7438
|
+
const am = /^[ \t]*---@(class|alias|enum|field|type)\s+([^\r\n]+)/.exec(line);
|
|
7439
|
+
if (am) {
|
|
7440
|
+
const tag = am[1];
|
|
7441
|
+
const name = (am[2].trim().match(/^([A-Za-z_][\w]*)/) || [])[1] ?? "";
|
|
7442
|
+
if (tag === "class") {
|
|
7443
|
+
if (name) {
|
|
7444
|
+
const item = mk(i, name, SK.Class, line);
|
|
7445
|
+
item.children = [];
|
|
7446
|
+
out.push(item);
|
|
7447
|
+
pendingClass = item;
|
|
7448
|
+
} else pendingClass = null;
|
|
7449
|
+
} else if (tag === "alias") {
|
|
7450
|
+
if (name) out.push(mk(i, name, SK.TypeParameter, line));
|
|
7451
|
+
pendingClass = null;
|
|
7452
|
+
} else if (tag === "enum") {
|
|
7453
|
+
if (name) out.push(mk(i, name, SK.Enum, line));
|
|
7454
|
+
pendingClass = null;
|
|
7455
|
+
} else if (tag === "field") {
|
|
7456
|
+
if (name && pendingClass) pendingClass.children.push(mk(i, name, SK.Field, line));
|
|
7457
|
+
} else if (tag === "type") pendingClass = null;
|
|
7458
|
+
continue;
|
|
7459
|
+
}
|
|
6402
7460
|
if (/^\s*--/.test(line)) continue;
|
|
6403
7461
|
const fm = /^[ \t]*(?:local[ \t]+)?function[ \t]+([A-Za-z_][\w.:]*)/.exec(line);
|
|
6404
7462
|
if (fm) {
|
|
@@ -6756,6 +7814,7 @@ window.__ModuleLoader__.load({
|
|
|
6756
7814
|
setError(null);
|
|
6757
7815
|
resolveOutline(sources, {
|
|
6758
7816
|
languageId: model.getLanguageId(),
|
|
7817
|
+
path: modelPath,
|
|
6759
7818
|
model,
|
|
6760
7819
|
editor: ed,
|
|
6761
7820
|
monaco: window.monaco
|
|
@@ -6939,6 +7998,43 @@ window.__ModuleLoader__.load({
|
|
|
6939
7998
|
render: (ctx) => react.default.createElement(OutlinePanel, { ctx })
|
|
6940
7999
|
};
|
|
6941
8000
|
}
|
|
8001
|
+
/** LSP 大纲源:lua/csharp 等有 host 服务器能力的语言。 */
|
|
8002
|
+
function createLspOutlineSource() {
|
|
8003
|
+
return {
|
|
8004
|
+
id: "lsp",
|
|
8005
|
+
priority: 60,
|
|
8006
|
+
provides: (languageId) => languageId === "lua" || languageId === "csharp",
|
|
8007
|
+
async get(input) {
|
|
8008
|
+
const model = input.model;
|
|
8009
|
+
if (!model || typeof model.getValue !== "function") return [];
|
|
8010
|
+
const path = input.path || (model.uri && model.uri.path ? decodeURIComponent(String(model.uri.path).replace(/^\//, "")) : null);
|
|
8011
|
+
if (!path) return [];
|
|
8012
|
+
const symbols = await fetchDocumentSymbols(path, model.getValue());
|
|
8013
|
+
return (Array.isArray(symbols) ? symbols : []).map(toOutline).filter(Boolean);
|
|
8014
|
+
}
|
|
8015
|
+
};
|
|
8016
|
+
}
|
|
8017
|
+
/** LSP SymbolInfo(host 归一化,0-based range)→ 大纲 OutlineSymbol(1-based)。 */
|
|
8018
|
+
function toOutline(symbol) {
|
|
8019
|
+
if (!symbol || typeof symbol.name !== "string") return null;
|
|
8020
|
+
const range = symbol.range || {};
|
|
8021
|
+
const sel = symbol.selectionRange || range;
|
|
8022
|
+
const start = range.start || {};
|
|
8023
|
+
const select = sel.start || {};
|
|
8024
|
+
const out = {
|
|
8025
|
+
name: symbol.name,
|
|
8026
|
+
kind: typeof symbol.kind === "number" ? symbol.kind : 0,
|
|
8027
|
+
startLine: Math.max(1, (start.line ?? 0) + 1),
|
|
8028
|
+
endLine: Math.max(1, (range.end?.line ?? start.line ?? 0) + 1),
|
|
8029
|
+
selectLine: Math.max(1, (select.line ?? start.line ?? 0) + 1)
|
|
8030
|
+
};
|
|
8031
|
+
if (typeof symbol.detail === "string" && symbol.detail) out.detail = symbol.detail;
|
|
8032
|
+
if (Array.isArray(symbol.children) && symbol.children.length) {
|
|
8033
|
+
const kids = symbol.children.map(toOutline).filter(Boolean);
|
|
8034
|
+
if (kids.length) out.children = kids;
|
|
8035
|
+
}
|
|
8036
|
+
return out;
|
|
8037
|
+
}
|
|
6942
8038
|
//#endregion
|
|
6943
8039
|
//#region src/client/index.ts
|
|
6944
8040
|
/**
|
|
@@ -6981,17 +8077,25 @@ window.__ModuleLoader__.load({
|
|
|
6981
8077
|
if (typeof window !== "undefined" && monacoList && typeof monacoList.subscribe === "function") {
|
|
6982
8078
|
const list = monacoList;
|
|
6983
8079
|
const schedulePreload = () => {
|
|
6984
|
-
const preload = () => loadMonaco(() => {}).catch(() => {});
|
|
8080
|
+
const preload = () => loadMonaco(() => {}).then((m) => setupLsp(m)).catch(() => {});
|
|
6985
8081
|
if (typeof window.requestIdleCallback === "function") window.requestIdleCallback(preload, { timeout: 2e3 });
|
|
6986
8082
|
else schedule(preload, 300);
|
|
6987
8083
|
};
|
|
6988
8084
|
const onSessionEnter = () => {
|
|
6989
|
-
|
|
8085
|
+
const current = list.getSnapshot()?.current;
|
|
8086
|
+
if (!current) return;
|
|
8087
|
+
setSession(current);
|
|
6990
8088
|
schedulePreload();
|
|
6991
8089
|
};
|
|
6992
8090
|
ctx.effect(() => list.subscribe(onSessionEnter), "vscode-mode: monaco session trigger");
|
|
6993
8091
|
onSessionEnter();
|
|
6994
8092
|
}
|
|
8093
|
+
if (monacoList && typeof monacoList.subscribe === "function") {
|
|
8094
|
+
const list = monacoList;
|
|
8095
|
+
ctx.effect(() => list.subscribe(() => {
|
|
8096
|
+
setSession(list.getSnapshot()?.current);
|
|
8097
|
+
}), "vscode-mode: lsp session sync");
|
|
8098
|
+
}
|
|
6995
8099
|
const originalOpenPath = workspaces?.openPath;
|
|
6996
8100
|
const binder = pickSettingsBinder(ctx);
|
|
6997
8101
|
const settings = binder.scope;
|
|
@@ -7035,6 +8139,7 @@ window.__ModuleLoader__.load({
|
|
|
7035
8139
|
const outlineSources = createOutlineSourceRegistry();
|
|
7036
8140
|
ctx.provide("edrvOutlineSources", outlineSources);
|
|
7037
8141
|
ctx.effect(() => registerBuiltinOutlineSources(outlineSources), "vscode-mode: outline sources");
|
|
8142
|
+
ctx.effect(() => outlineSources.register(createLspOutlineSource()), "vscode-mode: outline lsp source");
|
|
7038
8143
|
ctx.effect(() => sidebarPanels.register(createOutlinePanel()), "vscode-mode: sidebar panel outline");
|
|
7039
8144
|
ctx.effect(() => registry.register({
|
|
7040
8145
|
id: "system",
|