dsh-vscode-mode 0.1.28 → 0.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +859 -27
- package/lib/client.js.map +1 -1
- package/lib/index.js +513 -41
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/compat.ts +1 -1
- package/src/client/index.ts +15 -0
- package/src/client/keybindings.ts +169 -0
- package/src/client/monaco/diffRender.ts +7 -0
- package/src/client/rpc.ts +13 -2
- package/src/client/sidebar/panels/SearchPanel.ts +294 -0
- package/src/client/sidebar/panels/index.ts +18 -2
- package/src/client/sidebar/types.ts +2 -0
- package/src/client/styles/editor.css +40 -0
- package/src/client/styles/mcp.css +18 -0
- package/src/client/ui/EditorView.ts +76 -15
- package/src/client/ui/KeybindingsPanel.ts +187 -0
- package/src/client/ui/McpSettings.ts +7 -4
- package/src/client/ui/QuickOpen.ts +6 -3
- package/src/fileOpenSettings.ts +20 -4
- package/src/index.ts +4 -1
- package/src/rpc.ts +106 -22
- package/src/search/content.ts +319 -0
- package/src/search/fallback.ts +1 -1
- package/src/search/orchestrator.ts +17 -10
- package/src/search/ripgrep.ts +17 -2
- package/src/search/types.ts +40 -0
- package/src/shared/keybindings.ts +39 -0
- package/src/shared/rpc.ts +11 -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";
|
|
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\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";
|
|
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");
|
|
@@ -50,6 +50,19 @@ window.__ModuleLoader__.load({
|
|
|
50
50
|
* 迁移自原 src/client/index.ts 的 rpc/dbg,语义不改。
|
|
51
51
|
* 作者 ddj 2026-08-20
|
|
52
52
|
*/
|
|
53
|
+
/**
|
|
54
|
+
* 诊断日志开关(默认关):diff 渲染热路径每条日志 = 1 次 RPC 往返 +
|
|
55
|
+
* host 端 debug 文件全量读改写(上限 512KB),高频渲染下开销可观。
|
|
56
|
+
* 排查问题时在浏览器控制台执行 localStorage.setItem('edrv.debug','1') 后刷新开启。
|
|
57
|
+
* @author ddj 2026年08月26号
|
|
58
|
+
*/
|
|
59
|
+
const DEBUG = (() => {
|
|
60
|
+
try {
|
|
61
|
+
return localStorage.getItem("edrv.debug") === "1";
|
|
62
|
+
} catch (e) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
})();
|
|
53
66
|
/** 同源 RPC 调用(host /edrv/rpc 精确路由)。 */
|
|
54
67
|
function rpc(method, args) {
|
|
55
68
|
return fetch(RPC_PATH, {
|
|
@@ -62,12 +75,13 @@ window.__ModuleLoader__.load({
|
|
|
62
75
|
}).then((res) => res.json());
|
|
63
76
|
}
|
|
64
77
|
/**
|
|
65
|
-
*
|
|
66
|
-
* @author ddj 2026年08月
|
|
78
|
+
* 诊断日志:开关关闭时直接返回(零 RPC 零落盘);开启时走 host edrv.debug 落盘。
|
|
79
|
+
* @author ddj 2026年08月26号
|
|
67
80
|
* @param sessionId 会话 id
|
|
68
81
|
* @param text 日志文本
|
|
69
82
|
*/
|
|
70
83
|
function dbg(sessionId, text) {
|
|
84
|
+
if (!DEBUG) return;
|
|
71
85
|
try {
|
|
72
86
|
rpc("edrv.debug", {
|
|
73
87
|
sessionId,
|
|
@@ -555,6 +569,8 @@ window.__ModuleLoader__.load({
|
|
|
555
569
|
function createDiffRenderer(log) {
|
|
556
570
|
let viewZoneIds = [];
|
|
557
571
|
let decorations = [];
|
|
572
|
+
/** 上次已渲染的差异指纹(callId:idx@start-end:oldLen/newLen 串):内容未变时跳过重建。 */
|
|
573
|
+
let lastKey = null;
|
|
558
574
|
/**
|
|
559
575
|
* 渲染当前 pending 差异:绿底 decoration + 删除块 view zones + '-' 号 overlay(滚动同步)。
|
|
560
576
|
* @param monaco window.monaco
|
|
@@ -566,6 +582,9 @@ window.__ModuleLoader__.load({
|
|
|
566
582
|
if (!pendingRegions.length && viewZoneIds.length === 0 && decorations.length === 0) return;
|
|
567
583
|
const renderT0 = Date.now();
|
|
568
584
|
const callIdAttr = (callId, idx) => String(callId) + ":" + String(idx);
|
|
585
|
+
const key = pendingRegions.map((r) => callIdAttr(r.callId, r.idx) + "@" + (r.start ?? "?") + "-" + (r.end ?? "?") + ":" + (r.oldLines ? r.oldLines.length : 0) + "/" + (r.newLines ? r.newLines.length : 0)).join("|");
|
|
586
|
+
if (key === lastKey) return;
|
|
587
|
+
lastKey = key;
|
|
569
588
|
log(sessionId, "[diff-render] begin regs=" + pendingRegions.length + " " + pendingRegions.map((r) => callIdAttr(r.callId, r.idx) + "@" + (r.start ?? "?") + "-" + (r.end ?? "?") + "(-" + (r.oldLines ? r.oldLines.length : 0) + "/+" + (r.newLines ? r.newLines.length : 0) + ")").join("|"));
|
|
570
589
|
const decos = [];
|
|
571
590
|
for (const r of pendingRegions) {
|
|
@@ -1003,11 +1022,226 @@ window.__ModuleLoader__.load({
|
|
|
1003
1022
|
return regions;
|
|
1004
1023
|
}
|
|
1005
1024
|
//#endregion
|
|
1025
|
+
//#region src/shared/keybindings.ts
|
|
1026
|
+
/**
|
|
1027
|
+
* dsh-vscode-mode shared — 快捷键命令目录与默认键位(双面契约)。
|
|
1028
|
+
* 纯数据模块:host(settings schema 默认值)与 client(执行匹配/设置页)共用。
|
|
1029
|
+
* 键位格式:修饰符 + 主键,`+` 连接,如 `Ctrl+Shift+F`;空串 = 未绑定。
|
|
1030
|
+
* 作者 ddj 2026年08月26号
|
|
1031
|
+
*/
|
|
1032
|
+
/** 命令 id → 默认键位。命令目录以此为准,新增命令只需加一项。 */
|
|
1033
|
+
const KEYBINDING_DEFAULTS = {
|
|
1034
|
+
"edrv.save": "Ctrl+S",
|
|
1035
|
+
"edrv.quickOpen": "Ctrl+P",
|
|
1036
|
+
"edrv.toggleSidebar": "Ctrl+B",
|
|
1037
|
+
"edrv.searchInFiles": "Ctrl+Shift+F"
|
|
1038
|
+
};
|
|
1039
|
+
/**
|
|
1040
|
+
* 返回默认键位的独立副本(调用方修改不污染目录)。
|
|
1041
|
+
* @author ddj 2026年08月26号
|
|
1042
|
+
* @returns 默认键位映射
|
|
1043
|
+
*/
|
|
1044
|
+
function defaultKeybindings() {
|
|
1045
|
+
return { ...KEYBINDING_DEFAULTS };
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* 规整外部键位数据:只保留已知命令 id 且为字符串的值,未知 id 丢弃。
|
|
1049
|
+
* 防止用户文档/旧版本写入的未知命令污染执行匹配。
|
|
1050
|
+
* @author ddj 2026年08月26号
|
|
1051
|
+
* @param raw 原始设置值
|
|
1052
|
+
* @returns 规整后的键位映射(缺省项不补默认值,由调用方按需合并)
|
|
1053
|
+
*/
|
|
1054
|
+
function normalizeKeybindings(raw) {
|
|
1055
|
+
const out = {};
|
|
1056
|
+
if (!raw || typeof raw !== "object") return out;
|
|
1057
|
+
for (const [id, chord] of Object.entries(raw)) if (id in KEYBINDING_DEFAULTS && typeof chord === "string") out[id] = chord;
|
|
1058
|
+
return out;
|
|
1059
|
+
}
|
|
1060
|
+
//#endregion
|
|
1061
|
+
//#region src/client/keybindings.ts
|
|
1062
|
+
/**
|
|
1063
|
+
* dsh-vscode-mode client — 快捷键配置模块(解析/匹配/状态同步)。
|
|
1064
|
+
* 纯逻辑(parseChord/formatChord/matchEvent/chordFromEvent/normalizeKey)不依赖 DOM,可单测;
|
|
1065
|
+
* 模块状态由 settings 订阅驱动(client/index.ts 调 keybindingsApply)。
|
|
1066
|
+
* 键位语义:Ctrl 与 Cmd 互认(延续现有 Ctrl+P/Ctrl+B 捕获行为)。
|
|
1067
|
+
* 作者 ddj 2026年08月26号
|
|
1068
|
+
*/
|
|
1069
|
+
/** 命令目录(设置页展示标签;执行按目录序先匹配先执行,冲突键位确定性)。 */
|
|
1070
|
+
const COMMANDS = [
|
|
1071
|
+
{
|
|
1072
|
+
id: "edrv.save",
|
|
1073
|
+
label: "保存文件"
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
id: "edrv.quickOpen",
|
|
1077
|
+
label: "快速打开文件"
|
|
1078
|
+
},
|
|
1079
|
+
{
|
|
1080
|
+
id: "edrv.toggleSidebar",
|
|
1081
|
+
label: "切换侧边栏"
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
id: "edrv.searchInFiles",
|
|
1085
|
+
label: "在工作区中搜索"
|
|
1086
|
+
}
|
|
1087
|
+
];
|
|
1088
|
+
const MODIFIERS = {
|
|
1089
|
+
ctrl: "ctrl",
|
|
1090
|
+
cmd: "meta",
|
|
1091
|
+
meta: "meta",
|
|
1092
|
+
shift: "shift",
|
|
1093
|
+
alt: "alt"
|
|
1094
|
+
};
|
|
1095
|
+
let current = { ...KEYBINDING_DEFAULTS };
|
|
1096
|
+
let parsed = {};
|
|
1097
|
+
const listeners$1 = /* @__PURE__ */ new Set();
|
|
1098
|
+
/**
|
|
1099
|
+
* 应用设置快照(与默认值合并;未知 id 丢弃;空对象 = 全部默认)。
|
|
1100
|
+
* @author ddj 2026年08月26号
|
|
1101
|
+
* @param raw 设置 scope 的 keybindings 字段
|
|
1102
|
+
*/
|
|
1103
|
+
function keybindingsApply(raw) {
|
|
1104
|
+
current = {
|
|
1105
|
+
...KEYBINDING_DEFAULTS,
|
|
1106
|
+
...normalizeKeybindings(raw)
|
|
1107
|
+
};
|
|
1108
|
+
parsed = {};
|
|
1109
|
+
for (const id of Object.keys(current)) parsed[id] = parseChord(current[id]);
|
|
1110
|
+
for (const listener of listeners$1) try {
|
|
1111
|
+
listener();
|
|
1112
|
+
} catch {}
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* 订阅键位配置变化。
|
|
1116
|
+
* @author ddj 2026年08月26号
|
|
1117
|
+
* @param listener 变化回调
|
|
1118
|
+
* @returns 取消订阅函数
|
|
1119
|
+
*/
|
|
1120
|
+
function subscribeKeybindings(listener) {
|
|
1121
|
+
listeners$1.add(listener);
|
|
1122
|
+
return () => listeners$1.delete(listener);
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* 当前键位弦(未绑定/空 → null)。
|
|
1126
|
+
* @author ddj 2026年08月26号
|
|
1127
|
+
* @param id 命令 id
|
|
1128
|
+
* @returns 键位弦或 null
|
|
1129
|
+
*/
|
|
1130
|
+
function chordOf(id) {
|
|
1131
|
+
const chord = current[id];
|
|
1132
|
+
return typeof chord === "string" && chord.trim() !== "" ? chord : null;
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* 当前命令的解析键位(未绑定/非法 → null)。
|
|
1136
|
+
* @author ddj 2026年08月26号
|
|
1137
|
+
* @param id 命令 id
|
|
1138
|
+
* @returns 解析键位或 null
|
|
1139
|
+
*/
|
|
1140
|
+
function bindingOf(id) {
|
|
1141
|
+
return parsed[id] ?? null;
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* 主键规范化:小写字母大写、空格归一为 Space,其余原样(与事件 e.key 对照)。
|
|
1145
|
+
* @author ddj 2026年08月26号
|
|
1146
|
+
* @param key 事件主键
|
|
1147
|
+
* @returns 规范化主键
|
|
1148
|
+
*/
|
|
1149
|
+
function normalizeKey(key) {
|
|
1150
|
+
const value = String(key ?? "");
|
|
1151
|
+
if (value === " ") return "Space";
|
|
1152
|
+
if (value.length === 1 && value >= "a" && value <= "z") return value.toUpperCase();
|
|
1153
|
+
return value;
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* 解析键位弦(如 `Ctrl+Shift+F`;纯修饰键/重复主键/空 → null)。
|
|
1157
|
+
* @author ddj 2026年08月26号
|
|
1158
|
+
* @param chord 键位弦
|
|
1159
|
+
* @returns 解析键位或 null
|
|
1160
|
+
*/
|
|
1161
|
+
function parseChord(chord) {
|
|
1162
|
+
const parts = String(chord ?? "").split("+").map((part) => part.trim()).filter(Boolean);
|
|
1163
|
+
if (!parts.length) return null;
|
|
1164
|
+
const binding = {
|
|
1165
|
+
ctrl: false,
|
|
1166
|
+
shift: false,
|
|
1167
|
+
alt: false,
|
|
1168
|
+
meta: false,
|
|
1169
|
+
key: ""
|
|
1170
|
+
};
|
|
1171
|
+
for (const part of parts) {
|
|
1172
|
+
const modifier = MODIFIERS[part.toLocaleLowerCase("en-US")];
|
|
1173
|
+
if (modifier) {
|
|
1174
|
+
binding[modifier] = true;
|
|
1175
|
+
continue;
|
|
1176
|
+
}
|
|
1177
|
+
if (binding.key) return null;
|
|
1178
|
+
binding.key = normalizeKey(part);
|
|
1179
|
+
}
|
|
1180
|
+
return binding.key ? binding : null;
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* 格式化键位(Ctrl/Cmd/Shift/Alt + 主键;meta 显示 Cmd)。
|
|
1184
|
+
* @author ddj 2026年08月26号
|
|
1185
|
+
* @param binding 解析键位
|
|
1186
|
+
* @returns 键位弦
|
|
1187
|
+
*/
|
|
1188
|
+
function formatChord(binding) {
|
|
1189
|
+
const parts = [];
|
|
1190
|
+
if (binding.ctrl) parts.push("Ctrl");
|
|
1191
|
+
if (binding.meta) parts.push("Cmd");
|
|
1192
|
+
if (binding.shift) parts.push("Shift");
|
|
1193
|
+
if (binding.alt) parts.push("Alt");
|
|
1194
|
+
parts.push(binding.key);
|
|
1195
|
+
return parts.join("+");
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* 事件是否命中键位(Ctrl 与 Cmd 互认)。
|
|
1199
|
+
* @author ddj 2026年08月26号
|
|
1200
|
+
* @param e 键盘事件(鸭子类型,便于单测)
|
|
1201
|
+
* @param binding 解析键位(null = 未绑定,永不命中)
|
|
1202
|
+
* @returns 是否命中
|
|
1203
|
+
*/
|
|
1204
|
+
function matchEvent(e, binding) {
|
|
1205
|
+
if (!binding) return false;
|
|
1206
|
+
if ((e.ctrlKey || e.metaKey) !== (binding.ctrl || binding.meta)) return false;
|
|
1207
|
+
if (Boolean(e.shiftKey) !== binding.shift) return false;
|
|
1208
|
+
if (Boolean(e.altKey) !== binding.alt) return false;
|
|
1209
|
+
return normalizeKey(e.key ?? "") === binding.key;
|
|
1210
|
+
}
|
|
1211
|
+
/**
|
|
1212
|
+
* 从键盘事件构造键位弦(纯修饰键 → null,供设置页录制)。
|
|
1213
|
+
* @author ddj 2026年08月26号
|
|
1214
|
+
* @param e 键盘事件
|
|
1215
|
+
* @returns 键位弦或 null
|
|
1216
|
+
*/
|
|
1217
|
+
function chordFromEvent(e) {
|
|
1218
|
+
const key = normalizeKey(e.key ?? "");
|
|
1219
|
+
if (!key || key === "Control" || key === "Shift" || key === "Alt" || key === "Meta") return null;
|
|
1220
|
+
return formatChord({
|
|
1221
|
+
ctrl: Boolean(e.ctrlKey),
|
|
1222
|
+
shift: Boolean(e.shiftKey),
|
|
1223
|
+
alt: Boolean(e.altKey),
|
|
1224
|
+
meta: Boolean(e.metaKey),
|
|
1225
|
+
key
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* 键位版本 hook:配置变化时返回新版本(tooltip/占位文案随键位刷新)。
|
|
1230
|
+
* @author ddj 2026年08月26号
|
|
1231
|
+
* @returns 当前版本号
|
|
1232
|
+
*/
|
|
1233
|
+
function useKeybindingsVersion() {
|
|
1234
|
+
const [version, setVersion] = react.default.useState(0);
|
|
1235
|
+
react.default.useEffect(() => subscribeKeybindings(() => setVersion((v) => v + 1)), []);
|
|
1236
|
+
return version;
|
|
1237
|
+
}
|
|
1238
|
+
//#endregion
|
|
1006
1239
|
//#region src/client/ui/QuickOpen.ts
|
|
1007
1240
|
/**
|
|
1008
1241
|
* dsh-vscode-mode client — QuickOpen:顶部搜索框(Ctrl+P 打开文件)。
|
|
1242
|
+
* 快捷键随配置(edrv.quickOpen),占位文案同步当前键位。
|
|
1009
1243
|
* 迁移自原 src/client/index.ts 的 QuickOpen,语义不改。
|
|
1010
|
-
* 作者 ddj 2026-08-20
|
|
1244
|
+
* 作者 ddj 2026-08-20 / 2026-08-26
|
|
1011
1245
|
*/
|
|
1012
1246
|
/**
|
|
1013
1247
|
* 快速打开搜索框:Ctrl+P 聚焦,输入 ≥2 字符经 edrv.searchFiles 搜索,
|
|
@@ -1024,6 +1258,7 @@ window.__ModuleLoader__.load({
|
|
|
1024
1258
|
const inputRef = react.default.useRef(null);
|
|
1025
1259
|
const seq = react.default.useRef(0);
|
|
1026
1260
|
const timer = react.default.useRef(null);
|
|
1261
|
+
useKeybindingsVersion();
|
|
1027
1262
|
const search = (query) => {
|
|
1028
1263
|
const s = ++seq.current;
|
|
1029
1264
|
const clean = String(query || "").trim();
|
|
@@ -1056,7 +1291,7 @@ window.__ModuleLoader__.load({
|
|
|
1056
1291
|
};
|
|
1057
1292
|
react.default.useEffect(() => {
|
|
1058
1293
|
const onKey = (e) => {
|
|
1059
|
-
if ((e
|
|
1294
|
+
if (matchEvent(e, bindingOf("edrv.quickOpen"))) {
|
|
1060
1295
|
e.preventDefault();
|
|
1061
1296
|
e.stopPropagation();
|
|
1062
1297
|
inputRef.current?.focus?.();
|
|
@@ -1087,7 +1322,7 @@ window.__ModuleLoader__.load({
|
|
|
1087
1322
|
return react.default.createElement("div", { className: "edrv-search-wrap" }, react.default.createElement("input", {
|
|
1088
1323
|
ref: inputRef,
|
|
1089
1324
|
className: "edrv-search",
|
|
1090
|
-
placeholder: "搜索文件 (Ctrl+P)",
|
|
1325
|
+
placeholder: "搜索文件 (" + (chordOf("edrv.quickOpen") ?? "Ctrl+P") + ")",
|
|
1091
1326
|
value: q,
|
|
1092
1327
|
onChange: (e) => onChange(e.target.value),
|
|
1093
1328
|
onFocus: () => {
|
|
@@ -1755,6 +1990,7 @@ window.__ModuleLoader__.load({
|
|
|
1755
1990
|
const [sidebarW, setSidebarW] = react.default.useState(240);
|
|
1756
1991
|
const [activePanel, setActivePanel] = react.default.useState("explorer");
|
|
1757
1992
|
const [focusRequest, setFocusRequest] = react.default.useState(0);
|
|
1993
|
+
useKeybindingsVersion();
|
|
1758
1994
|
const [hintDismissed, setHintDismissed] = react.default.useState(() => {
|
|
1759
1995
|
try {
|
|
1760
1996
|
return localStorage.getItem("edrv.side-hint-dismissed") === "1";
|
|
@@ -1782,6 +2018,7 @@ window.__ModuleLoader__.load({
|
|
|
1782
2018
|
const hoverPanelRef = react.default.useRef(false);
|
|
1783
2019
|
const batchBusyRef = react.default.useRef(false);
|
|
1784
2020
|
const menuHandlersRef = react.default.useRef(null);
|
|
2021
|
+
const doSaveRef = react.default.useRef(null);
|
|
1785
2022
|
const diffRendererRef = react.default.useRef(null);
|
|
1786
2023
|
const layoutRef = react.default.useRef(layout);
|
|
1787
2024
|
layoutRef.current = layout;
|
|
@@ -2110,11 +2347,34 @@ window.__ModuleLoader__.load({
|
|
|
2110
2347
|
]);
|
|
2111
2348
|
react.default.useEffect(() => {
|
|
2112
2349
|
const onKey = (e) => {
|
|
2113
|
-
if ((e
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2350
|
+
if (!matchEvent(e, bindingOf("edrv.toggleSidebar"))) return;
|
|
2351
|
+
e.preventDefault();
|
|
2352
|
+
e.stopPropagation();
|
|
2353
|
+
setSidebarOn((v) => !v);
|
|
2354
|
+
};
|
|
2355
|
+
window.addEventListener("keydown", onKey, true);
|
|
2356
|
+
return () => window.removeEventListener("keydown", onKey, true);
|
|
2357
|
+
}, []);
|
|
2358
|
+
react.default.useEffect(() => {
|
|
2359
|
+
const onKey = (e) => {
|
|
2360
|
+
if (!matchEvent(e, bindingOf("edrv.save"))) return;
|
|
2361
|
+
if (!editorRef.current?.getModel?.()) return;
|
|
2362
|
+
e.preventDefault();
|
|
2363
|
+
e.stopPropagation();
|
|
2364
|
+
flushSave();
|
|
2365
|
+
doSaveRef.current(false);
|
|
2366
|
+
};
|
|
2367
|
+
window.addEventListener("keydown", onKey, true);
|
|
2368
|
+
return () => window.removeEventListener("keydown", onKey, true);
|
|
2369
|
+
}, []);
|
|
2370
|
+
react.default.useEffect(() => {
|
|
2371
|
+
const onKey = (e) => {
|
|
2372
|
+
if (!matchEvent(e, bindingOf("edrv.searchInFiles"))) return;
|
|
2373
|
+
e.preventDefault();
|
|
2374
|
+
e.stopPropagation();
|
|
2375
|
+
setSidebarOn(true);
|
|
2376
|
+
setActivePanel("search");
|
|
2377
|
+
setTimeout(() => window.dispatchEvent(new CustomEvent("edrv:search-focus")), 0);
|
|
2118
2378
|
};
|
|
2119
2379
|
window.addEventListener("keydown", onKey, true);
|
|
2120
2380
|
return () => window.removeEventListener("keydown", onKey, true);
|
|
@@ -2213,6 +2473,7 @@ window.__ModuleLoader__.load({
|
|
|
2213
2473
|
setError("保存异常:" + String(e));
|
|
2214
2474
|
});
|
|
2215
2475
|
};
|
|
2476
|
+
doSaveRef.current = doSave;
|
|
2216
2477
|
const onEdit = () => {
|
|
2217
2478
|
if (!editorRef.current || !active) return;
|
|
2218
2479
|
setDirtyMap((d) => Object.assign({}, d, { [active]: true }));
|
|
@@ -2297,10 +2558,6 @@ window.__ModuleLoader__.load({
|
|
|
2297
2558
|
cursorBlinking: "smooth",
|
|
2298
2559
|
padding: { top: side ? 6 : 8 }
|
|
2299
2560
|
});
|
|
2300
|
-
ed.addCommand(m.KeyMod.CtrlCmd | m.KeyCode.KeyS, () => {
|
|
2301
|
-
flushSave();
|
|
2302
|
-
doSave(false);
|
|
2303
|
-
});
|
|
2304
2561
|
ed.onDidChangeModelContent(() => {
|
|
2305
2562
|
if (!ed.getModel() || programmaticRef.current) return;
|
|
2306
2563
|
onEdit();
|
|
@@ -2404,9 +2661,20 @@ window.__ModuleLoader__.load({
|
|
|
2404
2661
|
react.default.useEffect(() => {
|
|
2405
2662
|
const pf = pendingFocusRef.current;
|
|
2406
2663
|
if (!pf || pf.path !== active || content === null) return;
|
|
2407
|
-
const target = pf.region || pendingRegions[0];
|
|
2408
2664
|
const ed = editorRef.current;
|
|
2409
|
-
if (!
|
|
2665
|
+
if (!ed) return;
|
|
2666
|
+
if (pf.line != null) {
|
|
2667
|
+
pendingFocusRef.current = null;
|
|
2668
|
+
ed.revealLineInCenter(Math.max(1, pf.line));
|
|
2669
|
+
ed.setPosition({
|
|
2670
|
+
lineNumber: Math.max(1, pf.line),
|
|
2671
|
+
column: Math.max(1, pf.column ?? 1)
|
|
2672
|
+
});
|
|
2673
|
+
ed.focus();
|
|
2674
|
+
return;
|
|
2675
|
+
}
|
|
2676
|
+
const target = pf.region || pendingRegions[0];
|
|
2677
|
+
if (!target) return;
|
|
2410
2678
|
pendingFocusRef.current = null;
|
|
2411
2679
|
ed.revealLineInCenter(Math.max(1, target.start ?? 1));
|
|
2412
2680
|
ed.setPosition({
|
|
@@ -2541,7 +2809,7 @@ window.__ModuleLoader__.load({
|
|
|
2541
2809
|
ok++;
|
|
2542
2810
|
if (item.record) next[item.callId] = item.record;
|
|
2543
2811
|
} else fail++;
|
|
2544
|
-
setRecords((prev) => Object.assign({}, prev, next));
|
|
2812
|
+
if (Object.keys(next).length) setRecords((prev) => Object.assign({}, prev, next));
|
|
2545
2813
|
return {
|
|
2546
2814
|
ok,
|
|
2547
2815
|
fail
|
|
@@ -2568,19 +2836,29 @@ window.__ModuleLoader__.load({
|
|
|
2568
2836
|
decision: reject ? "rejected" : "accepted"
|
|
2569
2837
|
});
|
|
2570
2838
|
const acceptFile = () => {
|
|
2839
|
+
if (batchBusyRef.current || !pendingRegions.length) return;
|
|
2840
|
+
batchBusyRef.current = true;
|
|
2571
2841
|
actMany(pendingRegions.map((r) => itemOf(r, false))).then(({ ok, fail }) => {
|
|
2842
|
+
batchBusyRef.current = false;
|
|
2572
2843
|
reloadFile(true);
|
|
2573
2844
|
emitRefresh();
|
|
2574
2845
|
setStatus("已采纳 " + ok + " 处差异" + (fail ? "," + fail + " 处失败" : ""));
|
|
2575
2846
|
if (fail) setError(fail + " 处差异处理失败(可能已被后续修改影响),可刷新后重试");
|
|
2847
|
+
}).catch(() => {
|
|
2848
|
+
batchBusyRef.current = false;
|
|
2576
2849
|
});
|
|
2577
2850
|
};
|
|
2578
2851
|
const undoFile = () => {
|
|
2852
|
+
if (batchBusyRef.current || !pendingRegions.length) return;
|
|
2853
|
+
batchBusyRef.current = true;
|
|
2579
2854
|
actMany([...pendingRegions].reverse().map((r) => itemOf(r, true))).then(({ ok, fail }) => {
|
|
2855
|
+
batchBusyRef.current = false;
|
|
2580
2856
|
reloadFile(true);
|
|
2581
2857
|
emitRefresh();
|
|
2582
2858
|
setStatus("已不采纳 " + ok + " 处差异" + (fail ? "," + fail + " 处失败" : ""));
|
|
2583
2859
|
if (fail) setError(fail + " 处差异处理失败(可能已被后续修改影响),可刷新后重试");
|
|
2860
|
+
}).catch(() => {
|
|
2861
|
+
batchBusyRef.current = false;
|
|
2584
2862
|
});
|
|
2585
2863
|
};
|
|
2586
2864
|
const allPending = react.default.useMemo(() => {
|
|
@@ -2691,6 +2969,24 @@ window.__ModuleLoader__.load({
|
|
|
2691
2969
|
region: null
|
|
2692
2970
|
};
|
|
2693
2971
|
};
|
|
2972
|
+
/**
|
|
2973
|
+
* 打开文件并跳转到指定行列(搜索面板命中跳转)。
|
|
2974
|
+
* @author ddj 2026年08月26号
|
|
2975
|
+
* @param path 文件路径
|
|
2976
|
+
* @param line 目标行(缺省仅打开不跳转)
|
|
2977
|
+
* @param column 目标列(缺省 1)
|
|
2978
|
+
*/
|
|
2979
|
+
const openFileAt = (path, line, column) => {
|
|
2980
|
+
if (!path) return;
|
|
2981
|
+
addTab(path, true);
|
|
2982
|
+
pendingFocusRef.current = {
|
|
2983
|
+
path,
|
|
2984
|
+
region: null,
|
|
2985
|
+
line: line ?? null,
|
|
2986
|
+
column: column ?? 1
|
|
2987
|
+
};
|
|
2988
|
+
setFocusRequest((value) => value + 1);
|
|
2989
|
+
};
|
|
2694
2990
|
const openPath = () => {
|
|
2695
2991
|
const p = (pathDraft || "").trim();
|
|
2696
2992
|
if (!p) return;
|
|
@@ -2768,7 +3064,7 @@ window.__ModuleLoader__.load({
|
|
|
2768
3064
|
const pathBar = react.default.createElement("div", {
|
|
2769
3065
|
className: "edrv-pathbar",
|
|
2770
3066
|
title: active || ""
|
|
2771
|
-
}, react.default.createElement("span", { className: "edrv-pb-name" }, active ? String(active).split(/[\\/]/).pop() : "未打开文件"), react.default.createElement("span", { className: "edrv-pb-full" }, active || "使用右上搜索框 (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);
|
|
3067
|
+
}, 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);
|
|
2772
3068
|
const tabRow = react.default.createElement("div", { style: {
|
|
2773
3069
|
display: "flex",
|
|
2774
3070
|
alignItems: "center",
|
|
@@ -2780,7 +3076,7 @@ window.__ModuleLoader__.load({
|
|
|
2780
3076
|
onClick: () => setLauncherOpen((o) => !o)
|
|
2781
3077
|
}, "⚠ 差异 " + sum.totalFiles + " 文件") : null, react.default.createElement("button", {
|
|
2782
3078
|
className: "edrv-chip-btn" + (sidebarOn ? " edrv-chip-on" : ""),
|
|
2783
|
-
title: "切换侧边栏 (Ctrl+B)",
|
|
3079
|
+
title: "切换侧边栏 (" + (chordOf("edrv.toggleSidebar") ?? "Ctrl+B") + ")",
|
|
2784
3080
|
onClick: () => setSidebarOn((v) => !v)
|
|
2785
3081
|
}, "☰"), react.default.createElement("button", {
|
|
2786
3082
|
className: "edrv-chip-btn",
|
|
@@ -2828,7 +3124,7 @@ window.__ModuleLoader__.load({
|
|
|
2828
3124
|
});
|
|
2829
3125
|
}
|
|
2830
3126
|
}, "重试"));
|
|
2831
|
-
else if (!active) body = react.default.createElement("div", { className: "edrv-empty" }, react.default.createElement("div", null, "暂无打开的文件"), react.default.createElement("div", { style: { fontSize: 12 } }, "使用右上搜索框 (Ctrl+P) 打开工作区文件;agent 修改文件后顶部会出现差异角标"));
|
|
3127
|
+
else if (!active) body = react.default.createElement("div", { className: "edrv-empty" }, react.default.createElement("div", null, "暂无打开的文件"), react.default.createElement("div", { style: { fontSize: 12 } }, "使用右上搜索框 (" + (chordOf("edrv.quickOpen") ?? "Ctrl+P") + ") 打开工作区文件;agent 修改文件后顶部会出现差异角标"));
|
|
2832
3128
|
else if (content === null && loadError) body = loadingBody("文件加载失败:" + loadError, 0, () => {
|
|
2833
3129
|
setLoadError(null);
|
|
2834
3130
|
setContent(null);
|
|
@@ -2979,6 +3275,7 @@ window.__ModuleLoader__.load({
|
|
|
2979
3275
|
const sidebarCtx = {
|
|
2980
3276
|
sessionId,
|
|
2981
3277
|
openFile: (p) => openFile(p, false),
|
|
3278
|
+
openFileAt,
|
|
2982
3279
|
activePath: active,
|
|
2983
3280
|
pendingByPath,
|
|
2984
3281
|
sum: {
|
|
@@ -3360,7 +3657,7 @@ window.__ModuleLoader__.load({
|
|
|
3360
3657
|
}
|
|
3361
3658
|
//#endregion
|
|
3362
3659
|
//#region \0dsh-css:/home/runner/work/DSH_VsCodeMode/DSH_VsCodeMode/src/client/styles/mcp.css.mjs
|
|
3363
|
-
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";
|
|
3660
|
+
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";
|
|
3364
3661
|
const tagId = "dsh-vscode-mode/mcp.css";
|
|
3365
3662
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
3366
3663
|
const tag = document.createElement("style");
|
|
@@ -3585,11 +3882,185 @@ window.__ModuleLoader__.load({
|
|
|
3585
3882
|
*/
|
|
3586
3883
|
const SettingsContext = react.default.createContext(void 0);
|
|
3587
3884
|
//#endregion
|
|
3885
|
+
//#region src/client/ui/KeybindingsPanel.ts
|
|
3886
|
+
/**
|
|
3887
|
+
* dsh-vscode-mode client — VSCodeMode 设置区「快捷键」管理页。
|
|
3888
|
+
* 命令目录来自 client/keybindings 的 COMMANDS;当前键位读 SettingsContext scope;
|
|
3889
|
+
* 编辑:行内录制(捕获下一次组合键),Esc 取消、Backspace/Delete 清空(未绑定);
|
|
3890
|
+
* 草稿 + 显式保存(scope.set('keybindings', draft));行级恢复 + 页级「恢复默认」;
|
|
3891
|
+
* 冲突键位(两命令同键位)标红警告;scope 外部变更且无未保存修改时回同步草稿。
|
|
3892
|
+
* 作者 ddj 2026-08-26
|
|
3893
|
+
*/
|
|
3894
|
+
/**
|
|
3895
|
+
* 草稿化:默认值 + 设置值合并,空串归一为 null(未绑定)。
|
|
3896
|
+
* @param raw 设置 scope 的 keybindings 字段
|
|
3897
|
+
* @returns 草稿映射(id → 键位弦或 null)
|
|
3898
|
+
*/
|
|
3899
|
+
function draftOf(raw) {
|
|
3900
|
+
const merged = {
|
|
3901
|
+
...defaultKeybindings(),
|
|
3902
|
+
...normalizeKeybindings(raw)
|
|
3903
|
+
};
|
|
3904
|
+
const out = {};
|
|
3905
|
+
for (const [id, chord] of Object.entries(merged)) out[id] = chord === "" ? null : chord;
|
|
3906
|
+
return out;
|
|
3907
|
+
}
|
|
3908
|
+
/**
|
|
3909
|
+
* 存库化:null(未绑定)转空串。
|
|
3910
|
+
* @param draft 草稿映射
|
|
3911
|
+
* @returns 可存储的键位映射
|
|
3912
|
+
*/
|
|
3913
|
+
function storeOf(draft) {
|
|
3914
|
+
const out = {};
|
|
3915
|
+
for (const [id, chord] of Object.entries(draft)) out[id] = chord === null ? "" : chord;
|
|
3916
|
+
return out;
|
|
3917
|
+
}
|
|
3918
|
+
/**
|
|
3919
|
+
* 冲突检测:同一键位被多个命令占用 → 返回冲突命令 id 集合。
|
|
3920
|
+
* @param draft 草稿映射
|
|
3921
|
+
* @returns 冲突命令 id 集合
|
|
3922
|
+
*/
|
|
3923
|
+
function conflictsOf(draft) {
|
|
3924
|
+
const byChord = /* @__PURE__ */ new Map();
|
|
3925
|
+
for (const [id, chord] of Object.entries(draft)) {
|
|
3926
|
+
if (!chord) continue;
|
|
3927
|
+
const list = byChord.get(chord) ?? [];
|
|
3928
|
+
list.push(id);
|
|
3929
|
+
byChord.set(chord, list);
|
|
3930
|
+
}
|
|
3931
|
+
const out = /* @__PURE__ */ new Set();
|
|
3932
|
+
for (const list of byChord.values()) if (list.length > 1) for (const id of list) out.add(id);
|
|
3933
|
+
return out;
|
|
3934
|
+
}
|
|
3935
|
+
/**
|
|
3936
|
+
* 快捷键管理页主体。
|
|
3937
|
+
*/
|
|
3938
|
+
function KeybindingsPanel() {
|
|
3939
|
+
const settings = react.default.useContext(SettingsContext);
|
|
3940
|
+
const snapshot = settings?.getSnapshot?.();
|
|
3941
|
+
const unavailable = !settings || snapshot?.status === "unavailable";
|
|
3942
|
+
const notReady = snapshot?.status !== "ready";
|
|
3943
|
+
const [draft, setDraft] = react.default.useState(() => draftOf(snapshot?.value?.keybindings));
|
|
3944
|
+
const [recording, setRecording] = react.default.useState(null);
|
|
3945
|
+
const [dirty, setDirty] = react.default.useState(false);
|
|
3946
|
+
const [busy, setBusy] = react.default.useState(false);
|
|
3947
|
+
const [error, setError] = react.default.useState("");
|
|
3948
|
+
const [message, setMessage] = react.default.useState("");
|
|
3949
|
+
const dirtyRef = react.default.useRef(false);
|
|
3950
|
+
dirtyRef.current = dirty;
|
|
3951
|
+
const lastKeyRef = react.default.useRef(JSON.stringify(draft));
|
|
3952
|
+
const snapshotValue = snapshot?.value?.keybindings;
|
|
3953
|
+
react.default.useEffect(() => {
|
|
3954
|
+
if (dirtyRef.current) return;
|
|
3955
|
+
const next = draftOf(snapshotValue);
|
|
3956
|
+
const key = JSON.stringify(next);
|
|
3957
|
+
if (key === lastKeyRef.current) return;
|
|
3958
|
+
lastKeyRef.current = key;
|
|
3959
|
+
setDraft(next);
|
|
3960
|
+
}, [snapshotValue]);
|
|
3961
|
+
react.default.useEffect(() => {
|
|
3962
|
+
if (!recording) return;
|
|
3963
|
+
const onKey = (e) => {
|
|
3964
|
+
e.preventDefault();
|
|
3965
|
+
e.stopPropagation();
|
|
3966
|
+
if (e.key === "Escape") {
|
|
3967
|
+
setRecording(null);
|
|
3968
|
+
return;
|
|
3969
|
+
}
|
|
3970
|
+
if (e.key === "Backspace" || e.key === "Delete") {
|
|
3971
|
+
setDraft((prev) => Object.assign({}, prev, { [recording]: null }));
|
|
3972
|
+
setRecording(null);
|
|
3973
|
+
setDirty(true);
|
|
3974
|
+
return;
|
|
3975
|
+
}
|
|
3976
|
+
const chord = chordFromEvent(e);
|
|
3977
|
+
if (!chord) return;
|
|
3978
|
+
setDraft((prev) => Object.assign({}, prev, { [recording]: chord }));
|
|
3979
|
+
setRecording(null);
|
|
3980
|
+
setDirty(true);
|
|
3981
|
+
};
|
|
3982
|
+
window.addEventListener("keydown", onKey, true);
|
|
3983
|
+
return () => window.removeEventListener("keydown", onKey, true);
|
|
3984
|
+
}, [recording]);
|
|
3985
|
+
const conflicts = conflictsOf(draft);
|
|
3986
|
+
const save = () => {
|
|
3987
|
+
if (!settings?.set) {
|
|
3988
|
+
setError("设置服务不可用");
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
3991
|
+
setBusy(true);
|
|
3992
|
+
setError("");
|
|
3993
|
+
setMessage("");
|
|
3994
|
+
settings.set("keybindings", storeOf(draft)).then(() => {
|
|
3995
|
+
setDirty(false);
|
|
3996
|
+
lastKeyRef.current = JSON.stringify(draft);
|
|
3997
|
+
setMessage("已保存");
|
|
3998
|
+
}).catch((e) => setError(String(e))).finally(() => setBusy(false));
|
|
3999
|
+
};
|
|
4000
|
+
/**
|
|
4001
|
+
* 恢复全部默认(确认后直接保存,一步到位)。
|
|
4002
|
+
*/
|
|
4003
|
+
const resetAll = () => {
|
|
4004
|
+
if (!settings?.set) {
|
|
4005
|
+
setError("设置服务不可用");
|
|
4006
|
+
return;
|
|
4007
|
+
}
|
|
4008
|
+
if (!window.confirm("恢复全部快捷键为默认值?")) return;
|
|
4009
|
+
setBusy(true);
|
|
4010
|
+
setError("");
|
|
4011
|
+
setMessage("");
|
|
4012
|
+
settings.set("keybindings", { ...defaultKeybindings() }).then(() => {
|
|
4013
|
+
const next = draftOf({});
|
|
4014
|
+
setDraft(next);
|
|
4015
|
+
lastKeyRef.current = JSON.stringify(next);
|
|
4016
|
+
setDirty(false);
|
|
4017
|
+
setMessage("已恢复默认");
|
|
4018
|
+
}).catch((e) => setError(String(e))).finally(() => setBusy(false));
|
|
4019
|
+
};
|
|
4020
|
+
/** 行级恢复该项默认值(进草稿,随保存提交)。 */
|
|
4021
|
+
const resetRow = (id) => {
|
|
4022
|
+
setDraft((prev) => Object.assign({}, prev, { [id]: KEYBINDING_DEFAULTS[id] ?? null }));
|
|
4023
|
+
setDirty(true);
|
|
4024
|
+
};
|
|
4025
|
+
const rows = COMMANDS.map((command) => {
|
|
4026
|
+
const chord = draft[command.id] ?? null;
|
|
4027
|
+
const conflict = chord !== null && conflicts.has(command.id);
|
|
4028
|
+
const invalid = chord !== null && parseChord(chord) === null;
|
|
4029
|
+
const recordingRow = recording === command.id;
|
|
4030
|
+
let display;
|
|
4031
|
+
if (recordingRow) display = react.default.createElement("span", { className: "vsm-kb-rec" }, "按下组合键…(Esc 取消 / Backspace 清除)");
|
|
4032
|
+
else display = react.default.createElement("kbd", { className: "vsm-kb-key" + (conflict ? " warn" : "") + (invalid ? " invalid" : "") }, chord ?? "未绑定");
|
|
4033
|
+
return react.default.createElement("div", {
|
|
4034
|
+
key: command.id,
|
|
4035
|
+
className: "vsm-kb-row" + (recordingRow ? " rec" : "")
|
|
4036
|
+
}, react.default.createElement("div", { className: "vsm-kb-label" }, react.default.createElement("span", null, command.label), conflict && react.default.createElement("small", { className: "vsm-kb-warn" }, "与另一命令冲突"), invalid && react.default.createElement("small", { className: "vsm-kb-warn" }, "键位格式无效")), display, react.default.createElement("div", { className: "vsm-kb-actions" }, recordingRow ? react.default.createElement("button", { onClick: () => setRecording(null) }, "取消") : react.default.createElement(react.default.Fragment, null, react.default.createElement("button", {
|
|
4037
|
+
disabled: unavailable || notReady || busy || snapshot?.writable === false,
|
|
4038
|
+
onClick: () => {
|
|
4039
|
+
setRecording(command.id);
|
|
4040
|
+
setMessage("");
|
|
4041
|
+
}
|
|
4042
|
+
}, "编辑"), react.default.createElement("button", {
|
|
4043
|
+
disabled: busy,
|
|
4044
|
+
onClick: () => resetRow(command.id)
|
|
4045
|
+
}, "恢复"))));
|
|
4046
|
+
});
|
|
4047
|
+
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", {
|
|
4048
|
+
className: "vsm-primary vsm-small",
|
|
4049
|
+
disabled: unavailable || notReady || busy || snapshot?.writable === false || !dirty,
|
|
4050
|
+
onClick: save
|
|
4051
|
+
}, busy ? "保存中…" : "保存"), react.default.createElement("button", {
|
|
4052
|
+
className: "vsm-small",
|
|
4053
|
+
disabled: unavailable || notReady || busy || snapshot?.writable === false,
|
|
4054
|
+
onClick: resetAll
|
|
4055
|
+
}, "恢复默认")));
|
|
4056
|
+
}
|
|
4057
|
+
//#endregion
|
|
3588
4058
|
//#region src/client/ui/McpSettings.ts
|
|
3589
4059
|
/**
|
|
3590
|
-
* dsh-vscode-mode client — VSCodeMode
|
|
3591
|
-
* 子 Tab
|
|
3592
|
-
*
|
|
4060
|
+
* dsh-vscode-mode client — VSCodeMode 设置区:通用 / 快捷键 / MCP 管理。
|
|
4061
|
+
* 子 Tab:通用(文件打开工具/开发形态)/ 快捷键(编辑器键位配置,可重置)/
|
|
4062
|
+
* 我的 MCP(profile 全局)/ 项目 MCP(各项目 .mcp.json)/ MCP 市场(占位)/ 兼容性。
|
|
4063
|
+
* 作者 ddj 2026年08月22号 / 2026年08月26号
|
|
3593
4064
|
*/
|
|
3594
4065
|
const EMPTY = {
|
|
3595
4066
|
serverName: "",
|
|
@@ -3960,6 +4431,7 @@ window.__ModuleLoader__.load({
|
|
|
3960
4431
|
let body;
|
|
3961
4432
|
if (loading) body = react.default.createElement("div", { className: "vsm-mcp-empty" }, "正在读取 MCP 服务…");
|
|
3962
4433
|
else if (tab === "general") body = react.default.createElement(GeneralSettings, { registry: openerRegistry });
|
|
4434
|
+
else if (tab === "keys") body = react.default.createElement(KeybindingsPanel, null);
|
|
3963
4435
|
else if (tab === "mine") body = react.default.createElement("div", null, servers.length ? servers.map((s) => react.default.createElement(ServerCard, {
|
|
3964
4436
|
key: s.id,
|
|
3965
4437
|
server: s,
|
|
@@ -3995,6 +4467,9 @@ window.__ModuleLoader__.load({
|
|
|
3995
4467
|
className: tab === "general" ? "active" : "",
|
|
3996
4468
|
onClick: () => setTab("general")
|
|
3997
4469
|
}, "通用"), react.default.createElement("button", {
|
|
4470
|
+
className: tab === "keys" ? "active" : "",
|
|
4471
|
+
onClick: () => setTab("keys")
|
|
4472
|
+
}, "快捷键"), react.default.createElement("button", {
|
|
3998
4473
|
className: tab === "mine" ? "active" : "",
|
|
3999
4474
|
onClick: () => setTab("mine")
|
|
4000
4475
|
}, "我的 MCP"), react.default.createElement("button", {
|
|
@@ -4799,11 +5274,343 @@ window.__ModuleLoader__.load({
|
|
|
4799
5274
|
}) : null);
|
|
4800
5275
|
}
|
|
4801
5276
|
//#endregion
|
|
4802
|
-
//#region src/client/sidebar/panels/
|
|
5277
|
+
//#region src/client/sidebar/panels/SearchPanel.ts
|
|
4803
5278
|
/**
|
|
4804
|
-
* dsh-vscode-mode client —
|
|
5279
|
+
* dsh-vscode-mode client — 侧边栏「搜索」面板(VSCode 搜索视图形态)。
|
|
5280
|
+
* 内容搜索:edrv.searchContent RPC;结果按文件分组展示命中行(行号 + 高亮 + 文件计数),
|
|
5281
|
+
* 点击经 ctx.openFileAt 打开文件并跳转行列。
|
|
5282
|
+
* 选项:大小写/全词/正则;折叠区(⚙ 齿轮展开):包含的文件(📖 toggle = 仅当前打开文件)
|
|
5283
|
+
* 与排除的文件(⚙ toggle = 启用排除),模式为逗号分隔 rg glob;无搜索按钮,防抖 + 回车触发。
|
|
4805
5284
|
* 作者 ddj 2026-08-26
|
|
4806
5285
|
*/
|
|
5286
|
+
const DEBOUNCE_MS = 250;
|
|
5287
|
+
const INCLUDE_PLACEHOLDER = "例如 *.ts, src/**/include";
|
|
5288
|
+
/**
|
|
5289
|
+
* 逗号切分 glob 列表(trim + 去空)。
|
|
5290
|
+
* @param text 用户输入的逗号分隔模式
|
|
5291
|
+
* @returns glob 数组
|
|
5292
|
+
*/
|
|
5293
|
+
function splitGlobs(text) {
|
|
5294
|
+
return String(text ?? "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
5295
|
+
}
|
|
5296
|
+
/**
|
|
5297
|
+
* 命中行文本三段切分(before/命中/after,列为 1-based UTF-16)。
|
|
5298
|
+
* @param text 行文本
|
|
5299
|
+
* @param start 起始列
|
|
5300
|
+
* @param end 结束列
|
|
5301
|
+
* @returns 三段文本
|
|
5302
|
+
*/
|
|
5303
|
+
function splitHit(text, start, end) {
|
|
5304
|
+
const s = Math.max(0, (start | 0) - 1);
|
|
5305
|
+
const e = Math.max(s, end | 0);
|
|
5306
|
+
return {
|
|
5307
|
+
before: String(text).slice(0, s),
|
|
5308
|
+
hit: String(text).slice(s, e),
|
|
5309
|
+
after: String(text).slice(e)
|
|
5310
|
+
};
|
|
5311
|
+
}
|
|
5312
|
+
/**
|
|
5313
|
+
* 搜索面板主体。
|
|
5314
|
+
* @param props.ctx 面板共享上下文(sessionId/openFileAt/activePath)
|
|
5315
|
+
*/
|
|
5316
|
+
function SearchPanel(props) {
|
|
5317
|
+
const ctx = props?.ctx;
|
|
5318
|
+
const sessionId = ctx?.sessionId;
|
|
5319
|
+
const openFileAt = ctx?.openFileAt ?? (() => {});
|
|
5320
|
+
const activePath = ctx?.activePath ?? null;
|
|
5321
|
+
const [query, setQuery] = react.default.useState("");
|
|
5322
|
+
const [matchCase, setMatchCase] = react.default.useState(false);
|
|
5323
|
+
const [wholeWord, setWholeWord] = react.default.useState(false);
|
|
5324
|
+
const [regex, setRegex] = react.default.useState(false);
|
|
5325
|
+
const [includeText, setIncludeText] = react.default.useState("");
|
|
5326
|
+
const [excludeText, setExcludeText] = react.default.useState("");
|
|
5327
|
+
const [onlyActive, setOnlyActive] = react.default.useState(false);
|
|
5328
|
+
const [excludeOn, setExcludeOn] = react.default.useState(false);
|
|
5329
|
+
const [sectionOpen, setSectionOpen] = react.default.useState(false);
|
|
5330
|
+
const [status, setStatus] = react.default.useState("idle");
|
|
5331
|
+
const [error, setError] = react.default.useState("");
|
|
5332
|
+
const [matches, setMatches] = react.default.useState(null);
|
|
5333
|
+
const [truncated, setTruncated] = react.default.useState(false);
|
|
5334
|
+
const [collapsed, setCollapsed] = react.default.useState({});
|
|
5335
|
+
const seqRef = react.default.useRef(0);
|
|
5336
|
+
const timerRef = react.default.useRef(null);
|
|
5337
|
+
const inputRef = react.default.useRef(null);
|
|
5338
|
+
const queryRef = react.default.useRef("");
|
|
5339
|
+
queryRef.current = query;
|
|
5340
|
+
const activePathRef = react.default.useRef(activePath);
|
|
5341
|
+
activePathRef.current = activePath;
|
|
5342
|
+
const requestRef = react.default.useRef({
|
|
5343
|
+
matchCase,
|
|
5344
|
+
wholeWord,
|
|
5345
|
+
regex,
|
|
5346
|
+
include: [],
|
|
5347
|
+
exclude: []
|
|
5348
|
+
});
|
|
5349
|
+
requestRef.current = {
|
|
5350
|
+
matchCase,
|
|
5351
|
+
wholeWord,
|
|
5352
|
+
regex,
|
|
5353
|
+
include: onlyActive ? activePath ? [activePath] : [] : splitGlobs(includeText),
|
|
5354
|
+
exclude: excludeOn ? splitGlobs(excludeText) : []
|
|
5355
|
+
};
|
|
5356
|
+
react.default.useEffect(() => {
|
|
5357
|
+
if (!sessionId) return;
|
|
5358
|
+
try {
|
|
5359
|
+
const saved = JSON.parse(localStorage.getItem("edrv.search.v1." + String(sessionId)) || "null");
|
|
5360
|
+
if (saved && typeof saved === "object") {
|
|
5361
|
+
if (typeof saved.query === "string") setQuery(saved.query);
|
|
5362
|
+
if (typeof saved.matchCase === "boolean") setMatchCase(saved.matchCase);
|
|
5363
|
+
if (typeof saved.wholeWord === "boolean") setWholeWord(saved.wholeWord);
|
|
5364
|
+
if (typeof saved.regex === "boolean") setRegex(saved.regex);
|
|
5365
|
+
if (typeof saved.includeText === "string") setIncludeText(saved.includeText);
|
|
5366
|
+
if (typeof saved.excludeText === "string") setExcludeText(saved.excludeText);
|
|
5367
|
+
if (typeof saved.onlyActive === "boolean") setOnlyActive(saved.onlyActive);
|
|
5368
|
+
if (typeof saved.excludeOn === "boolean") setExcludeOn(saved.excludeOn);
|
|
5369
|
+
if (typeof saved.sectionOpen === "boolean") setSectionOpen(saved.sectionOpen);
|
|
5370
|
+
}
|
|
5371
|
+
} catch (e) {}
|
|
5372
|
+
}, [sessionId]);
|
|
5373
|
+
react.default.useEffect(() => {
|
|
5374
|
+
if (!sessionId) return;
|
|
5375
|
+
try {
|
|
5376
|
+
localStorage.setItem("edrv.search.v1." + String(sessionId), JSON.stringify({
|
|
5377
|
+
query,
|
|
5378
|
+
matchCase,
|
|
5379
|
+
wholeWord,
|
|
5380
|
+
regex,
|
|
5381
|
+
includeText,
|
|
5382
|
+
excludeText,
|
|
5383
|
+
onlyActive,
|
|
5384
|
+
excludeOn,
|
|
5385
|
+
sectionOpen
|
|
5386
|
+
}));
|
|
5387
|
+
} catch (e) {}
|
|
5388
|
+
}, [
|
|
5389
|
+
sessionId,
|
|
5390
|
+
query,
|
|
5391
|
+
matchCase,
|
|
5392
|
+
wholeWord,
|
|
5393
|
+
regex,
|
|
5394
|
+
includeText,
|
|
5395
|
+
excludeText,
|
|
5396
|
+
onlyActive,
|
|
5397
|
+
excludeOn,
|
|
5398
|
+
sectionOpen
|
|
5399
|
+
]);
|
|
5400
|
+
/**
|
|
5401
|
+
* 执行内容搜索(请求参数从 ref 读最新值)。
|
|
5402
|
+
* @param text 搜索词
|
|
5403
|
+
*/
|
|
5404
|
+
const runSearch = (text) => {
|
|
5405
|
+
const s = ++seqRef.current;
|
|
5406
|
+
const clean = String(text ?? "").trim();
|
|
5407
|
+
if (clean.length < 2) {
|
|
5408
|
+
setMatches(null);
|
|
5409
|
+
setStatus("idle");
|
|
5410
|
+
return;
|
|
5411
|
+
}
|
|
5412
|
+
const req = requestRef.current;
|
|
5413
|
+
setStatus("searching");
|
|
5414
|
+
setError("");
|
|
5415
|
+
rpc("edrv.searchContent", {
|
|
5416
|
+
sessionId,
|
|
5417
|
+
query: clean,
|
|
5418
|
+
matchCase: req.matchCase,
|
|
5419
|
+
wholeWord: req.wholeWord,
|
|
5420
|
+
regex: req.regex,
|
|
5421
|
+
include: req.include,
|
|
5422
|
+
exclude: req.exclude
|
|
5423
|
+
}).then((res) => {
|
|
5424
|
+
if (s !== seqRef.current) return;
|
|
5425
|
+
if (res && res.ok) {
|
|
5426
|
+
setMatches(Array.isArray(res.matches) ? res.matches : []);
|
|
5427
|
+
setTruncated(res.truncated === true);
|
|
5428
|
+
setStatus("done");
|
|
5429
|
+
} else {
|
|
5430
|
+
setError(res?.error ? String(res.error) : "搜索失败");
|
|
5431
|
+
setMatches(null);
|
|
5432
|
+
setStatus("error");
|
|
5433
|
+
}
|
|
5434
|
+
}).catch((e) => {
|
|
5435
|
+
if (s !== seqRef.current) return;
|
|
5436
|
+
setError("搜索异常:" + String(e));
|
|
5437
|
+
setMatches(null);
|
|
5438
|
+
setStatus("error");
|
|
5439
|
+
});
|
|
5440
|
+
};
|
|
5441
|
+
/**
|
|
5442
|
+
* 输入变化:防抖触发搜索。
|
|
5443
|
+
* @param value 新输入
|
|
5444
|
+
*/
|
|
5445
|
+
const onChange = (value) => {
|
|
5446
|
+
setQuery(value);
|
|
5447
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
5448
|
+
timerRef.current = setTimeout(() => runSearch(value), DEBOUNCE_MS);
|
|
5449
|
+
};
|
|
5450
|
+
/**
|
|
5451
|
+
* 输入框通用防抖重搜(包含/排除模式输入)。
|
|
5452
|
+
* @param setter 状态写入函数
|
|
5453
|
+
* @param value 新值
|
|
5454
|
+
*/
|
|
5455
|
+
const onChangeDebounced = (setter, value) => {
|
|
5456
|
+
setter(value);
|
|
5457
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
5458
|
+
timerRef.current = setTimeout(() => runSearch(queryRef.current), DEBOUNCE_MS);
|
|
5459
|
+
};
|
|
5460
|
+
/**
|
|
5461
|
+
* 立即重搜(查询 ≥2 字符时);先同步 ref 避免本次调用读到旧请求参数。
|
|
5462
|
+
* @param next 新的请求参数字段
|
|
5463
|
+
*/
|
|
5464
|
+
const rerun = (next) => {
|
|
5465
|
+
requestRef.current = Object.assign({}, requestRef.current, next);
|
|
5466
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
5467
|
+
if (String(queryRef.current).trim().length >= 2) runSearch(queryRef.current);
|
|
5468
|
+
};
|
|
5469
|
+
/** 选项开关切换(大小写/全词/正则/书本/设置):更新状态 + 立即重搜。 */
|
|
5470
|
+
const toggleOpt = (key, value) => {
|
|
5471
|
+
if (key === "matchCase") setMatchCase(value);
|
|
5472
|
+
if (key === "wholeWord") setWholeWord(value);
|
|
5473
|
+
if (key === "regex") setRegex(value);
|
|
5474
|
+
if (key === "onlyActive") setOnlyActive(value);
|
|
5475
|
+
if (key === "excludeOn") setExcludeOn(value);
|
|
5476
|
+
const base = {
|
|
5477
|
+
matchCase,
|
|
5478
|
+
wholeWord,
|
|
5479
|
+
regex,
|
|
5480
|
+
onlyActive,
|
|
5481
|
+
excludeOn
|
|
5482
|
+
};
|
|
5483
|
+
base[key] = value;
|
|
5484
|
+
rerun({
|
|
5485
|
+
matchCase: base.matchCase,
|
|
5486
|
+
wholeWord: base.wholeWord,
|
|
5487
|
+
regex: base.regex,
|
|
5488
|
+
include: base.onlyActive ? activePathRef.current ? [activePathRef.current] : [] : splitGlobs(includeText),
|
|
5489
|
+
exclude: base.excludeOn ? splitGlobs(excludeText) : []
|
|
5490
|
+
});
|
|
5491
|
+
};
|
|
5492
|
+
react.default.useEffect(() => {
|
|
5493
|
+
const onFocus = () => inputRef.current?.focus?.();
|
|
5494
|
+
window.addEventListener("edrv:search-focus", onFocus);
|
|
5495
|
+
return () => window.removeEventListener("edrv:search-focus", onFocus);
|
|
5496
|
+
}, []);
|
|
5497
|
+
const groups = react.default.useMemo(() => {
|
|
5498
|
+
if (!Array.isArray(matches)) return [];
|
|
5499
|
+
const map = /* @__PURE__ */ new Map();
|
|
5500
|
+
for (const m of matches) {
|
|
5501
|
+
let list = map.get(m.path);
|
|
5502
|
+
if (!list) {
|
|
5503
|
+
list = [];
|
|
5504
|
+
map.set(m.path, list);
|
|
5505
|
+
}
|
|
5506
|
+
list.push(m);
|
|
5507
|
+
}
|
|
5508
|
+
return [...map.entries()].map(([path, list]) => ({
|
|
5509
|
+
path,
|
|
5510
|
+
count: list.length,
|
|
5511
|
+
matches: list
|
|
5512
|
+
}));
|
|
5513
|
+
}, [matches]);
|
|
5514
|
+
const totalFiles = groups.length;
|
|
5515
|
+
const totalMatches = Array.isArray(matches) ? matches.length : 0;
|
|
5516
|
+
const toggleGroup = (path) => setCollapsed((prev) => Object.assign({}, prev, { [path]: prev[path] === true ? false : true }));
|
|
5517
|
+
const optsBar = react.default.createElement("div", { className: "edrv-search-opts" }, react.default.createElement("button", {
|
|
5518
|
+
className: "edrv-search-opt" + (matchCase ? " on" : ""),
|
|
5519
|
+
title: "区分大小写",
|
|
5520
|
+
"aria-pressed": matchCase,
|
|
5521
|
+
onClick: () => toggleOpt("matchCase", !matchCase)
|
|
5522
|
+
}, "Aa"), react.default.createElement("button", {
|
|
5523
|
+
className: "edrv-search-opt" + (wholeWord ? " on" : ""),
|
|
5524
|
+
title: "全字匹配",
|
|
5525
|
+
"aria-pressed": wholeWord,
|
|
5526
|
+
onClick: () => toggleOpt("wholeWord", !wholeWord)
|
|
5527
|
+
}, "Ab"), react.default.createElement("button", {
|
|
5528
|
+
className: "edrv-search-opt" + (regex ? " on" : ""),
|
|
5529
|
+
title: "使用正则表达式",
|
|
5530
|
+
"aria-pressed": regex,
|
|
5531
|
+
onClick: () => toggleOpt("regex", !regex)
|
|
5532
|
+
}, ".*"), react.default.createElement("span", { className: "edrv-search-spacer" }), react.default.createElement("button", {
|
|
5533
|
+
className: "edrv-search-opt" + (sectionOpen ? " on" : ""),
|
|
5534
|
+
title: sectionOpen ? "收起文件过滤" : "文件过滤(包含/排除)",
|
|
5535
|
+
"aria-pressed": sectionOpen,
|
|
5536
|
+
onClick: () => setSectionOpen((v) => !v)
|
|
5537
|
+
}, "⚙"));
|
|
5538
|
+
const filterRows = sectionOpen ? react.default.createElement("div", { className: "edrv-search-rows" }, react.default.createElement("div", { className: "edrv-search-row" }, react.default.createElement("input", {
|
|
5539
|
+
className: "edrv-search-field" + (onlyActive ? " dim" : ""),
|
|
5540
|
+
placeholder: INCLUDE_PLACEHOLDER,
|
|
5541
|
+
value: includeText,
|
|
5542
|
+
disabled: onlyActive,
|
|
5543
|
+
title: onlyActive ? "已启用「仅当前打开文件」,忽略此输入" : "",
|
|
5544
|
+
onChange: (e) => onChangeDebounced(setIncludeText, e.target.value)
|
|
5545
|
+
}), react.default.createElement("button", {
|
|
5546
|
+
className: "edrv-search-toggle" + (onlyActive ? " on" : ""),
|
|
5547
|
+
title: activePath ? "仅在当前打开的文件中搜索" : "无打开的文件(需先打开文件)",
|
|
5548
|
+
"aria-pressed": onlyActive,
|
|
5549
|
+
disabled: !activePath,
|
|
5550
|
+
onClick: () => toggleOpt("onlyActive", !onlyActive)
|
|
5551
|
+
}, "📖")), react.default.createElement("div", { className: "edrv-search-row" }, react.default.createElement("input", {
|
|
5552
|
+
className: "edrv-search-field" + (excludeOn ? "" : " dim"),
|
|
5553
|
+
placeholder: INCLUDE_PLACEHOLDER,
|
|
5554
|
+
value: excludeText,
|
|
5555
|
+
disabled: !excludeOn,
|
|
5556
|
+
title: excludeOn ? "" : "排除功能未启用,先点击右侧按钮开启",
|
|
5557
|
+
onChange: (e) => onChangeDebounced(setExcludeText, e.target.value)
|
|
5558
|
+
}), react.default.createElement("button", {
|
|
5559
|
+
className: "edrv-search-toggle" + (excludeOn ? " on" : ""),
|
|
5560
|
+
title: "启用排除文件(忽略下方模式的命中)",
|
|
5561
|
+
"aria-pressed": excludeOn,
|
|
5562
|
+
onClick: () => toggleOpt("excludeOn", !excludeOn)
|
|
5563
|
+
}, "⚙"))) : null;
|
|
5564
|
+
const summary = status === "done" ? react.default.createElement("div", { className: "edrv-search-summary" }, totalFiles > 0 ? totalFiles + " 个文件 · " + totalMatches + " 处匹配" + (truncated ? "(已截断)" : "") : "无匹配结果") : null;
|
|
5565
|
+
const body = (() => {
|
|
5566
|
+
if (status === "searching") return react.default.createElement("div", { className: "edrv-search-empty" }, "搜索中…");
|
|
5567
|
+
if (status === "error") return react.default.createElement("div", { className: "edrv-search-error" }, error);
|
|
5568
|
+
if (status === "done" && !groups.length) return react.default.createElement("div", { className: "edrv-search-empty" }, "无匹配结果");
|
|
5569
|
+
if (status !== "done") return react.default.createElement("div", { className: "edrv-search-empty" }, "输入至少 2 个字符开始搜索");
|
|
5570
|
+
return react.default.createElement("div", { className: "edrv-search-results" }, groups.map((group) => {
|
|
5571
|
+
const open = collapsed[group.path] !== true;
|
|
5572
|
+
return react.default.createElement("div", {
|
|
5573
|
+
key: group.path,
|
|
5574
|
+
className: "edrv-search-group"
|
|
5575
|
+
}, react.default.createElement("div", {
|
|
5576
|
+
className: "edrv-search-file",
|
|
5577
|
+
title: group.path,
|
|
5578
|
+
onClick: () => toggleGroup(group.path)
|
|
5579
|
+
}, react.default.createElement("span", { className: "edrv-search-chev" }, open ? "▾" : "▸"), react.default.createElement("span", { className: "edrv-search-fname" }, String(group.path).split(/[\\/]/).pop() || group.path), react.default.createElement("span", { className: "edrv-search-fdir" }, String(group.path).split(/[\\/]/).slice(0, -1).join("/")), react.default.createElement("span", { className: "edrv-search-fcount" }, group.count)), open ? group.matches.map((m, i) => {
|
|
5580
|
+
const parts = splitHit(m.text, m.startColumn, m.endColumn);
|
|
5581
|
+
return react.default.createElement("div", {
|
|
5582
|
+
key: m.path + ":" + m.line + ":" + i,
|
|
5583
|
+
className: "edrv-search-line" + (i % 2 ? " alt" : ""),
|
|
5584
|
+
title: group.path + ":" + m.line,
|
|
5585
|
+
onClick: () => openFileAt(m.path, m.line, m.startColumn)
|
|
5586
|
+
}, react.default.createElement("span", { className: "edrv-search-ln" }, m.line), react.default.createElement("span", { className: "edrv-search-text" }, react.default.createElement("span", null, parts.before), react.default.createElement("mark", { className: "edrv-search-hit" }, parts.hit), react.default.createElement("span", null, parts.after)));
|
|
5587
|
+
}) : null);
|
|
5588
|
+
}));
|
|
5589
|
+
})();
|
|
5590
|
+
return react.default.createElement("div", { className: "edrv-search-panel" }, react.default.createElement("div", { className: "edrv-search-qrow" }, react.default.createElement("input", {
|
|
5591
|
+
ref: inputRef,
|
|
5592
|
+
className: "edrv-search-input",
|
|
5593
|
+
autoFocus: true,
|
|
5594
|
+
autoComplete: "off",
|
|
5595
|
+
spellCheck: false,
|
|
5596
|
+
placeholder: "搜索",
|
|
5597
|
+
value: query,
|
|
5598
|
+
onChange: (e) => onChange(e.target.value),
|
|
5599
|
+
onKeyDown: (e) => {
|
|
5600
|
+
if (e.key === "Enter") {
|
|
5601
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
5602
|
+
runSearch(queryRef.current);
|
|
5603
|
+
}
|
|
5604
|
+
if (e.key === "Escape") inputRef.current?.blur?.();
|
|
5605
|
+
}
|
|
5606
|
+
})), optsBar, filterRows, summary, body);
|
|
5607
|
+
}
|
|
5608
|
+
//#endregion
|
|
5609
|
+
//#region src/client/sidebar/panels/index.ts
|
|
5610
|
+
/**
|
|
5611
|
+
* dsh-vscode-mode client — 侧边栏面板定义(文件管理 + 搜索)。
|
|
5612
|
+
* 作者 ddj 2026-08-26 / 2026-08-26
|
|
5613
|
+
*/
|
|
4807
5614
|
/**
|
|
4808
5615
|
* 构造文件管理面板定义。
|
|
4809
5616
|
* @author ddj 2026年08月26号
|
|
@@ -4819,6 +5626,20 @@ window.__ModuleLoader__.load({
|
|
|
4819
5626
|
render: (ctx) => react.default.createElement(FileExplorer, { ctx })
|
|
4820
5627
|
};
|
|
4821
5628
|
}
|
|
5629
|
+
/**
|
|
5630
|
+
* 构造搜索面板定义。
|
|
5631
|
+
* @author ddj 2026年08月30号
|
|
5632
|
+
* @returns 面板定义(无徽标;活动栏图标 🔍)
|
|
5633
|
+
*/
|
|
5634
|
+
function createSearchPanel() {
|
|
5635
|
+
return {
|
|
5636
|
+
id: "search",
|
|
5637
|
+
title: "搜索",
|
|
5638
|
+
icon: "🔍",
|
|
5639
|
+
order: 15,
|
|
5640
|
+
render: (ctx) => react.default.createElement(SearchPanel, { ctx })
|
|
5641
|
+
};
|
|
5642
|
+
}
|
|
4822
5643
|
//#endregion
|
|
4823
5644
|
//#region src/client/sidebar/menuItems.ts
|
|
4824
5645
|
/**
|
|
@@ -5664,6 +6485,7 @@ window.__ModuleLoader__.load({
|
|
|
5664
6485
|
const sidebarPanels = createSidebarPanelRegistry();
|
|
5665
6486
|
ctx.provide("edrvSidebarPanels", sidebarPanels);
|
|
5666
6487
|
ctx.effect(() => sidebarPanels.register(createFilePanel()), "vscode-mode: sidebar panel");
|
|
6488
|
+
ctx.effect(() => sidebarPanels.register(createSearchPanel()), "vscode-mode: sidebar panel search");
|
|
5667
6489
|
const fileMenuItems = createTreeMenuRegistry();
|
|
5668
6490
|
ctx.provide("edrvFileContextMenuItems", fileMenuItems);
|
|
5669
6491
|
for (const item of createDefaultFileMenuItems()) ctx.effect(() => fileMenuItems.register(item), "vscode-mode: file context menu item " + item.id);
|
|
@@ -5692,6 +6514,16 @@ window.__ModuleLoader__.load({
|
|
|
5692
6514
|
sync();
|
|
5693
6515
|
return settings.subscribe(sync);
|
|
5694
6516
|
}, "vscode-mode: file opener setting sync");
|
|
6517
|
+
ctx.effect(() => {
|
|
6518
|
+
if (!settings) return void 0;
|
|
6519
|
+
const sync = () => {
|
|
6520
|
+
const snapshot = settings.getSnapshot();
|
|
6521
|
+
if (snapshot.status === "loading") return;
|
|
6522
|
+
keybindingsApply(snapshot.value?.keybindings);
|
|
6523
|
+
};
|
|
6524
|
+
sync();
|
|
6525
|
+
return settings.subscribe(sync);
|
|
6526
|
+
}, "vscode-mode: keybindings setting sync");
|
|
5695
6527
|
if (workspaces?.openPath) ctx.effect(() => installOpenPathRouter({
|
|
5696
6528
|
workspaces,
|
|
5697
6529
|
registry,
|