dsh-vscode-mode 0.1.62 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/lib/client.js +1554 -87
- package/lib/client.js.map +1 -1
- package/lib/index.js +9 -2
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/client/commandBridge.ts +83 -0
- package/src/client/commandGlobals.ts +13 -0
- package/src/client/commandPaletteStore.ts +165 -0
- package/src/client/commandRegistry.ts +144 -0
- package/src/client/commandSearch.ts +89 -0
- package/src/client/editorModelState.ts +30 -0
- package/src/client/index.ts +55 -4
- package/src/client/keybindings.ts +71 -15
- package/src/client/monaco/loader.ts +3 -3
- package/src/client/monaco/theme.ts +313 -7
- package/src/client/officialSidebar.ts +45 -0
- package/src/client/outline/index.ts +4 -3
- package/src/client/sidebar/SidebarView.ts +16 -1
- package/src/client/sidebar/panels/FileExplorer.ts +58 -4
- package/src/client/sidebar/panels/index.ts +12 -10
- package/src/client/sidebar/types.ts +2 -2
- package/src/client/styles/editor.css +47 -28
- package/src/client/ui/ClaimRouter.ts +96 -0
- package/src/client/ui/CommandPalette.ts +158 -0
- package/src/client/ui/ConversationDiffDock.ts +17 -9
- package/src/client/ui/EditorView.ts +153 -15
- package/src/client/ui/KeybindingsPanel.ts +1 -1
- package/src/client/ui/OfficialSideTab.ts +8 -9
- package/src/client/ui/QuickOpen.ts +11 -3
- package/src/client/ui/commandCatalog.ts +179 -0
- package/src/client-primitives.d.ts +37 -0
- package/src/shared/keybindings.ts +10 -1
package/lib/client.js
CHANGED
|
@@ -28,10 +28,11 @@ window.__ModuleLoader__.load({
|
|
|
28
28
|
//#endregion
|
|
29
29
|
let react = require("react");
|
|
30
30
|
react = __toESM(react, 1);
|
|
31
|
-
let react_dom_client = require("react-dom/client");
|
|
32
31
|
let react_dom = require("react-dom");
|
|
32
|
+
let react_dom_client = require("react-dom/client");
|
|
33
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
33
34
|
//#region \0dsh-css:/home/runner/work/DSH_VsCodeMode/DSH_VsCodeMode/src/client/styles/editor.css.mjs
|
|
34
|
-
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-star { color: var(--dsw-alias-brand-primary, #4f8cff); font-size: 13px; font-weight: 700; line-height: 1; flex-shrink: 0; } /* 未保存修改星号 @author ddj 2026年09月09号 */\n[data-edrv-view] .edrv-tab .edrv-tab-x { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: 4px; color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; }\n[data-edrv-view] .edrv-tab .edrv-tab-x:hover { background: rgba(255,255,255,.12); color: var(--dsw-alias-label-primary, #eee); }\n[data-edrv-view] .edrv-tab-add { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; margin-left: 2px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 14px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab-add:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-pathbar { display: flex; align-items: center; gap: 8px; height: 26px; padding: 0 10px; background: var(--dsw-alias-bg-layer-1, #ffffff); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 12px; flex-shrink: 0; overflow: hidden; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-name { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-full { flex: 1; min-width: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; direction: rtl; text-align: left; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-meta { flex-shrink: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-status { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-path-input { flex: 1; min-width: 140px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-wrap { position: relative; flex-shrink: 0; display: flex; align-items: center; padding: 2px 8px 2px 4px; }\n[data-edrv-view] .edrv-search { width: 190px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-pop { position: absolute; top: calc(100% + 4px); right: 8px; width: min(460px, calc(100vw - 24px)); max-height: 300px; overflow: auto; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 10px; box-shadow: 0 6px 20px rgba(0,0,0,.4); z-index: 60; padding: 4px; }\n[data-edrv-view] .edrv-search-item { display: flex; flex-direction: column; gap: 1px; padding: 5px 8px; border-radius: 6px; cursor: pointer; }\n[data-edrv-view] .edrv-search-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.07)); }\n[data-edrv-view] .edrv-search-item .n { color: var(--dsw-alias-label-primary, #eee); font-size: 12px; word-break: break-all; }\n[data-edrv-view] .edrv-search-item .d { color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-search-empty { padding: 8px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #888); }\n[data-edrv-view] .edrv-btn-mini { font-size: 11px; padding: 2px 9px; border: none; border-radius: 999px; cursor: pointer; color: #fff; font-weight: 600; white-space: nowrap; }\n[data-edrv-view] .edrv-btn-keep { background: var(--dsw-alias-state-success-primary, #2e9e44); }\n[data-edrv-view] .edrv-btn-undo { background: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-btn-mini:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-statusbar { display: flex; align-items: center; gap: 10px; padding: 3px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #333); background: var(--dsw-alias-bg-layer-1, transparent); font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); flex-shrink: 0; white-space: nowrap; overflow: hidden; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-path { flex: 1; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-lsp { flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-progress-message { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-statusbar .edrv-sp-progress { flex: 0 0 auto; min-width: 36px; text-align: right; font-variant-numeric: tabular-nums; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-lsp-status-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; background: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-lsp-status-dot.ready { background: var(--dsw-alias-state-success-primary, #0f9d58); }\n[data-edrv-view] .edrv-lsp-status-dot.busy { background: var(--dsw-alias-state-warn-primary, #d97706); animation: edrv-lsp-pulse 1.2s ease-in-out infinite; }\n@keyframes edrv-lsp-pulse { 50% { opacity: .45; } }\n/* Ctrl+hover 可导航标识符下划线提示(绑定 Monaco inlineClassName 装饰) */\n[data-edrv-view] .edrv-nav-underline { text-decoration: underline; text-decoration-color: var(--dsw-alias-interactive-primary, #4fc1ff); text-underline-offset: 1px; cursor: pointer; }\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-chip-btn:disabled { opacity: .35; cursor: default; }\n[data-edrv-view] .edrv-chip-btn:disabled:hover { background: transparent; }\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-imgview { flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }\n[data-edrv-view] .edrv-imgview-bar { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, transparent); }\n[data-edrv-view] .edrv-imgview-meta { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; }\n[data-edrv-view] .edrv-imgview-stage { flex: 1 1 auto; min-height: 0; display: flex; align-items: center; justify-content: center; overflow: auto; padding: 14px; background-color: var(--dsw-alias-bg-layer-1, #f0f4f4); background-image: conic-gradient(rgba(128,128,128,.14) 25%, transparent 0 50%, rgba(128,128,128,.14) 0 75%, transparent 0); background-size: 16px 16px; }\n[data-edrv-view] .edrv-imgview-img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 4px; box-shadow: 0 2px 12px rgba(31,41,51,.18); }\n[data-edrv-view] .edrv-imgview-broken { display: flex; flex-direction: column; align-items: center; gap: 10px; font-size: 13px; color: var(--dsw-alias-label-tertiary, #777); }\n[data-edrv-view] .edrv-pdf-host { flex: 1 1 auto; min-width: 0; min-height: 0; position: relative; overflow: hidden; }\n[data-edrv-view] .edrv-pdf-host > .edrv-pdf { position: absolute; inset: 0; }\n[data-edrv-view] .edrv-pdf { min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }\n[data-edrv-view] .edrv-pdf-bar { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, transparent); }\n[data-edrv-view] .edrv-pdf-page { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; min-width: 52px; text-align: center; }\n[data-edrv-view] .edrv-pdf-zoom { min-width: 26px; text-align: center; padding: 3px 7px; }\n[data-edrv-view] .edrv-pdf-sep { width: 1px; height: 16px; background: var(--dsw-alias-border-l1, #e0e6e8); margin: 0 2px; }\n[data-edrv-view] .edrv-pdf-mode-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-brand-primary, #0f9d58); border-color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-pdf-dirty { color: var(--dsw-alias-state-warn-primary, #d97706); border-color: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-pdf-stage { position: absolute; top: 34px; left: 0; right: 0; bottom: 0; overflow: auto; }\n[data-edrv-view] .edrv-pdf-stage .edrv-pdf-viewer { margin: 0 auto; }\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: 1; min-width: 0; height: 100%; background: var(--dsw-alias-bg-layer-1, #ffffff); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); overflow: hidden; }\n[data-edrv-view] .edrv-rail { display: flex; flex-direction: column; align-items: center; width: 44px; flex-shrink: 0; padding: 6px 0; background: var(--dsw-alias-bg-layer-2, #f0f4f4); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); gap: 4px; }\n[data-edrv-view] .edrv-rail-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: none; border-radius: 8px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rail-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rail-btn.edrv-rail-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); box-shadow: inset 2px 0 0 var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-rail-icon { font-size: 15px; line-height: 1; }\n[data-edrv-view] .edrv-rail-badge { position: absolute; top: 2px; right: 2px; min-width: 14px; height: 14px; padding: 0 3px; border-radius: 999px; background: var(--dsw-alias-brand-primary, #0f9d58); color: #fff; font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; }\n[data-edrv-view] .edrv-side-body { flex: 1; min-width: 0; min-height: 0; display: flex; overflow: hidden; }\n[data-edrv-view] .edrv-side-panel { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; position: relative; }\n[data-edrv-view] .edrv-side-head { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px 0 12px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); flex-shrink: 0; }\n[data-edrv-view] .edrv-side-title { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-side-root { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-side-btn { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-side-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-tree { flex: 1; min-height: 0; overflow: auto; padding: 4px 6px 12px; font-size: 12px; }\n[data-edrv-view] .edrv-tree-row { display: flex; align-items: center; gap: 4px; height: 22px; padding-right: 6px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-tree-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-active { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-context { box-shadow: inset 0 0 0 1px var(--dsw-alias-brand-primary, #0f9d58); background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); }\n[data-edrv-view] .edrv-tree-chev { width: 12px; flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; text-align: center; }\n[data-edrv-view] .edrv-tree-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-tree-name.edrv-tree-dim { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-tree-badge { min-width: 14px; height: 14px; padding: 0 4px; border-radius: 999px; background: var(--dsw-alias-state-warn-primary, #d97706); color: #fff; font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; flex-shrink: 0; }\n[data-edrv-view] .edrv-tree-loading { height: 22px; line-height: 22px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-tree-error { display: flex; align-items: center; gap: 8px; margin: 4px 2px 6px; padding: 6px 8px; border: 1px solid rgba(217,83,79,.4); border-radius: 6px; background: #fdeaea; color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n/* 大纲面板:kind 字形 / detail / 行号 */\n[data-edrv-view] .edrv-outline-kind { display: inline-flex; align-items: center; justify-content: center; width: 14px; flex-shrink: 0; font-size: 12px; font-weight: 600; text-align: center; }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-func { color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-type { color: var(--dsw-alias-label-primary, #1f2933); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-data { color: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-ns { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-outline-kind.edrv-ol-key { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-outline-detail { flex-shrink: 0; max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-outline-ln { flex-shrink: 0; padding-right: 2px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-side-resize { width: 5px; flex-shrink: 0; cursor: col-resize; background: transparent; }\n[data-edrv-view] .edrv-side-resize:hover { background: var(--dsw-alias-brand-primary, #0f9d58); opacity: .35; }\n[data-edrv-view] .edrv-chip-btn.edrv-chip-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); }\n\n/* ===== 侧栏形态(betterSidebar 面板内编辑区) ===== */\n[data-edrv-view][data-edrv-layout='side'] { box-sizing: border-box; height: 100%; }\n\n/* ===== 侧边栏引导条(旧页签形态、未装 betterSidebar 时) ===== */\n[data-edrv-view] .edrv-side-hint { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; flex-wrap: wrap; padding: 6px 12px; font-size: 12px; color: var(--dsw-alias-label-secondary, #52606d); background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, #f0f4f4)); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }\n[data-edrv-view] .edrv-side-hint-text { white-space: nowrap; }\n[data-edrv-view] .edrv-side-hint-cmd { font-size: 11px; padding: 2px 6px; border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #ffffff); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-side-hint-close { margin-left: auto; border: none; background: transparent; color: var(--dsw-alias-label-tertiary, #9aa5b1); cursor: pointer; font-size: 14px; line-height: 1; padding: 2px 6px; border-radius: 6px; flex-shrink: 0; }\n[data-edrv-view] .edrv-side-hint-close:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }\n\n/* ===== search panel (sidebar, VSCode search view) ===== */\n[data-edrv-view] .edrv-search-panel { display: flex; flex-direction: column; min-height: 0; flex: 1; padding: 6px; gap: 4px; }\n[data-edrv-view] .edrv-search-qrow { display: flex; gap: 4px; }\n[data-edrv-view] .edrv-search-input { flex: 1; min-width: 0; height: 26px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-input:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-opts { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-search-opt { min-width: 24px; height: 22px; padding: 0 6px; border: 1px solid transparent; border-radius: 5px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 11px; font-family: var(--ds-font-family-code, ui-monospace, monospace); cursor: pointer; }\n[data-edrv-view] .edrv-search-opt:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-opt.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); border-color: var(--dsw-alias-border-l2, #d5dde3); }\n[data-edrv-view] .edrv-search-spacer { flex: 1; }\n[data-edrv-view] .edrv-search-summary { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); padding: 2px 4px; }\n[data-edrv-view] .edrv-search-results { flex: 1; min-height: 0; overflow: auto; font-size: 12px; }\n[data-edrv-view] .edrv-search-group { margin-bottom: 2px; }\n[data-edrv-view] .edrv-search-file { display: flex; align-items: center; gap: 4px; height: 22px; padding: 0 4px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }\n[data-edrv-view] .edrv-search-file:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-chev { width: 12px; flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; text-align: center; }\n[data-edrv-view] .edrv-search-fname { font-weight: 600; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-search-fdir { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }\n[data-edrv-view] .edrv-search-fcount { flex-shrink: 0; min-width: 16px; height: 15px; padding: 0 4px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); font-size: 10px; line-height: 15px; text-align: center; }\n[data-edrv-view] .edrv-search-line { display: flex; gap: 6px; padding: 1px 4px 1px 16px; border-radius: 4px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; overflow: hidden; }\n[data-edrv-view] .edrv-search-line.alt { background: var(--dsw-alias-bg-layer-1, #ffffff); }\n[data-edrv-view] .edrv-search-line:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-ln { flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; min-width: 26px; text-align: right; user-select: none; }\n[data-edrv-view] .edrv-search-text { overflow: hidden; text-overflow: ellipsis; white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; }\n[data-edrv-view] .edrv-search-hit { background: var(--dsw-alias-state-warn-primary, #e5b93f); color: #1f2933; border-radius: 2px; padding: 0 1px; }\n[data-edrv-view] .edrv-search-empty { padding: 8px 4px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-search-error { padding: 6px 8px; border: 1px solid rgba(217,83,79,.4); border-radius: 6px; background: #fdeaea; color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n[data-edrv-view] .edrv-search-warn { padding: 6px 8px; border: 1px solid rgba(240,173,78,.45); border-radius: 6px; background: #fdf6e3; color: #b8860b; font-size: 11px; word-break: break-all; }\n\n/* include/exclude filter rows (collapsible) */\n[data-edrv-view] .edrv-search-rows { display: flex; flex-direction: column; gap: 4px; padding: 2px 0 4px; }\n[data-edrv-view] .edrv-search-row { display: flex; align-items: center; gap: 4px; }\n[data-edrv-view] .edrv-search-field { flex: 1; min-width: 0; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 11px; outline: none; }\n[data-edrv-view] .edrv-search-field:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-field.dim { opacity: .45; }\n[data-edrv-view] .edrv-search-field::placeholder { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-search-toggle { min-width: 24px; height: 24px; padding: 0 5px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; line-height: 1; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-search-toggle:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-toggle.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); border-color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-search-toggle:disabled { opacity: .4; cursor: default; }\n\n/* ===== rules panel (sidebar, Codebuddy rules view) ===== */\n[data-edrv-view] .edrv-rules-panel { display: flex; flex-direction: column; min-width: 0; min-height: 0; flex: 1; padding: 6px; gap: 4px; }\n[data-edrv-view] .edrv-rules-tabs { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-tab { height: 24px; padding: 0 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; cursor: pointer; }\n[data-edrv-view] .edrv-rules-tab:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-tab.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); font-weight: 600; }\n[data-edrv-view] .edrv-rules-new { margin-left: auto; height: 24px; padding: 0 10px; border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-new:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-ws-line { font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); padding: 0 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 0 0 auto; user-select: none; }\n[data-edrv-view] .edrv-rules-hint { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); padding: 0 2px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-list { flex: 1; min-height: 0; overflow: auto; }\n[data-edrv-view] .edrv-rules-row { display: flex; align-items: center; gap: 4px; padding: 6px 4px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; }\n[data-edrv-view] .edrv-rules-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-row.off .edrv-rules-main { opacity: .5; }\n[data-edrv-view] .edrv-rules-main { flex: 1; min-width: 0; cursor: pointer; overflow: hidden; }\n[data-edrv-view] .edrv-rules-line1 { display: flex; align-items: center; gap: 6px; min-width: 0; }\n[data-edrv-view] .edrv-rules-sub { display: flex; align-items: center; gap: 5px; margin-top: 3px; min-width: 0; }\n[data-edrv-view] .edrv-rules-sublabel { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-file { font-weight: 600; font-size: 12px; color: var(--dsw-alias-label-primary, #1f2933); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-rules-path { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-rules-type { flex-shrink: 0; height: 15px; padding: 0 5px; border-radius: 999px; font-size: 10px; line-height: 15px; }\n[data-edrv-view] .edrv-rules-type-always { background: rgba(15,157,88,.15); color: #0b7c46; }\n[data-edrv-view] .edrv-rules-type-auto { background: rgba(79,140,255,.16); color: #3367c9; }\n[data-edrv-view] .edrv-rules-type-manual { background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-rules-offtag { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-desc { font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 0 1 auto; }\n[data-edrv-view] .edrv-rules-actions { display: flex; align-items: center; gap: 2px; flex-shrink: 0; margin-left: auto; }\n[data-edrv-view] .edrv-rules-act { min-width: 22px; height: 22px; padding: 0 4px; border: none; border-radius: 5px; background: transparent; font-size: 12px; line-height: 1; cursor: pointer; color: var(--dsw-alias-label-secondary, #52606d); display: inline-flex; align-items: center; justify-content: center; }\n[data-edrv-view] .edrv-rules-act:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-switch { position: relative; width: 30px; height: 16px; border-radius: 999px; border: none; background: #b9c4cc; cursor: pointer; transition: background .15s; flex-shrink: 0; padding: 0; }\n[data-edrv-view] .edrv-rules-switch.on { background: #7c5cff; }\n[data-edrv-view] .edrv-rules-knob { position: absolute; top: 2px; left: 2px; width: 12px; height: 12px; border-radius: 50%; background: #fff; transition: left .15s; }\n[data-edrv-view] .edrv-rules-switch.on .edrv-rules-knob { left: 16px; }\n[data-edrv-view] .edrv-rules-empty { padding: 10px 4px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); line-height: 1.6; }\n[data-edrv-view] .edrv-rules-err { 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; word-break: break-all; }\n[data-edrv-view] .edrv-rules-editor { display: flex; flex-direction: column; min-height: 0; flex: 1; gap: 6px; }\n[data-edrv-view] .edrv-rules-editor-head { display: flex; align-items: center; justify-content: space-between; gap: 6px; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-crumb { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-rules-editor-ctl { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-ctl-label { font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-select { height: 24px; padding: 0 4px; 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; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-select:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-rules-desc-input { flex: 1; min-width: 0; }\n[data-edrv-view] .edrv-rules-input { 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; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-rules-input:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-rules-body { flex: 1; min-height: 0; resize: none; padding: 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; line-height: 1.5; outline: none; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-rules-body:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-rules-editor-hint { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); line-height: 1.5; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-editor-foot { display: flex; justify-content: flex-end; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-save { height: 24px; padding: 0 14px; border: none; border-radius: 6px; background: var(--dsw-alias-brand-primary, #0f9d58); color: #fff; font-size: 12px; cursor: pointer; }\n[data-edrv-view] .edrv-rules-save:disabled { opacity: .5; cursor: default; }\n\n\n\n";
|
|
35
|
+
const css$1 = "/* dsh-vscode-mode 编辑区样式(原 ensureEdrvStyles 字符串,落盘为真 .css,由 tsdown CSS-inline 注入) */\n/* 主题:跟随官方 --dsw-alias-* 令牌(DSH 明暗与第三方皮肤生效)。\n 本作用域不再重定义官方令牌(旧实现曾在此硬覆盖浅色盘,导致插件不跟随 DSH 主题);\n 旧环境/缺令牌场景由每条声明的 var(--dsw-alias-*, 回落值) 兜底。\n @author ddj 2026-09-10 */\n[data-edrv-view] { box-sizing: border-box; --edrv-tab-h: 34px; }\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-star { color: var(--dsw-alias-brand-primary, #4f8cff); font-size: 13px; font-weight: 700; line-height: 1; flex-shrink: 0; } /* 未保存修改星号 @author ddj 2026年09月09号 */\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: var(--dsw-alias-interactive-bg-hover, 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: var(--dsw-alias-label-primary-inverted, #fff); font-weight: 600; white-space: nowrap; }\n[data-edrv-view] .edrv-btn-keep { background: var(--dsw-alias-state-success-primary, #2e9e44); }\n[data-edrv-view] .edrv-btn-undo { background: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-btn-mini:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-statusbar { display: flex; align-items: center; gap: 10px; padding: 3px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #333); background: var(--dsw-alias-bg-layer-1, transparent); font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); flex-shrink: 0; white-space: nowrap; overflow: hidden; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-path { flex: 1; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-lsp { flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-progress-message { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-statusbar .edrv-sp-progress { flex: 0 0 auto; min-width: 36px; text-align: right; font-variant-numeric: tabular-nums; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-lsp-status-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; background: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-lsp-status-dot.ready { background: var(--dsw-alias-state-success-primary, #0f9d58); }\n[data-edrv-view] .edrv-lsp-status-dot.busy { background: var(--dsw-alias-state-warn-primary, #d97706); animation: edrv-lsp-pulse 1.2s ease-in-out infinite; }\n@keyframes edrv-lsp-pulse { 50% { opacity: .45; } }\n/* Ctrl+hover 可导航标识符下划线提示(绑定 Monaco inlineClassName 装饰) */\n[data-edrv-view] .edrv-nav-underline { text-decoration: underline; text-decoration-color: var(--dsw-alias-interactive-primary, #4fc1ff); text-underline-offset: 1px; cursor: pointer; }\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-chip-btn:disabled { opacity: .35; cursor: default; }\n[data-edrv-view] .edrv-chip-btn:disabled:hover { background: transparent; }\n[data-edrv-view] .edrv-mn-add-line { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.10)); }\n[data-edrv-view] .edrv-mn-gutter-add { color: var(--dsw-alias-state-success-primary, #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: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.14)); border-top: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.35)); border-bottom: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.35)); box-shadow: inset 0 0 0 1px var(--dsw-alias-interactive-bg-hover-danger, 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: var(--dsw-alias-state-error-primary, #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: var(--dsw-alias-state-error-primary, #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-imgview { flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }\n[data-edrv-view] .edrv-imgview-bar { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, transparent); }\n[data-edrv-view] .edrv-imgview-meta { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; }\n[data-edrv-view] .edrv-imgview-stage { flex: 1 1 auto; min-height: 0; display: flex; align-items: center; justify-content: center; overflow: auto; padding: 14px; background-color: var(--dsw-alias-bg-layer-1, #f0f4f4); background-image: conic-gradient(rgba(128,128,128,.14) 25%, transparent 0 50%, rgba(128,128,128,.14) 0 75%, transparent 0); background-size: 16px 16px; }\n[data-edrv-view] .edrv-imgview-img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 4px; box-shadow: 0 2px 12px rgba(31,41,51,.18); }\n[data-edrv-view] .edrv-imgview-broken { display: flex; flex-direction: column; align-items: center; gap: 10px; font-size: 13px; color: var(--dsw-alias-label-tertiary, #777); }\n[data-edrv-view] .edrv-pdf-host { flex: 1 1 auto; min-width: 0; min-height: 0; position: relative; overflow: hidden; }\n[data-edrv-view] .edrv-pdf-host > .edrv-pdf { position: absolute; inset: 0; }\n[data-edrv-view] .edrv-pdf { min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }\n[data-edrv-view] .edrv-pdf-bar { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, transparent); }\n[data-edrv-view] .edrv-pdf-page { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; min-width: 52px; text-align: center; }\n[data-edrv-view] .edrv-pdf-zoom { min-width: 26px; text-align: center; padding: 3px 7px; }\n[data-edrv-view] .edrv-pdf-sep { width: 1px; height: 16px; background: var(--dsw-alias-border-l1, #e0e6e8); margin: 0 2px; }\n[data-edrv-view] .edrv-pdf-mode-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-brand-primary, #0f9d58); border-color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-pdf-dirty { color: var(--dsw-alias-state-warn-primary, #d97706); border-color: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-pdf-stage { position: absolute; top: 34px; left: 0; right: 0; bottom: 0; overflow: auto; }\n[data-edrv-view] .edrv-pdf-stage .edrv-pdf-viewer { margin: 0 auto; }\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: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-state-success-primary, #0f7a45); border: 1px solid var(--dsw-alias-state-success-primary, rgba(15,157,88,.4)); }\n[data-edrv-view] .edrv-pill-undo { background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.14)); color: var(--dsw-alias-state-error-primary, #c0392b); border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); }\n[data-edrv-view] .edrv-pill-ghost { background: var(--dsw-alias-bg-layer-1, #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: var(--dsw-alias-state-warn-primary, 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: 1; 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; display: inline-flex; align-items: center; justify-content: center; }\n/* 树行图标:官方原语(文件夹/文件类型),尺寸与行高对齐 @author ddj 2026-09-10 */\n[data-edrv-view] .edrv-tree-icon { flex-shrink: 0; display: inline-flex; align-items: center; color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-tree-row.edrv-tree-active .edrv-tree-icon { color: var(--dsw-alias-label-secondary, #52606d); }\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: var(--dsw-alias-label-primary-inverted, #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: var(--dsw-alias-label-primary-inverted, #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 var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); 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: var(--dsw-alias-label-primary-inverted, #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 var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }\n[data-edrv-view] .edrv-search-warn { padding: 6px 8px; border: 1px solid var(--dsw-alias-state-warn-primary, rgba(240,173,78,.45)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover, rgba(240,173,78,.12)); color: var(--dsw-alias-state-warn-primary, #b8860b); font-size: 11px; word-break: break-all; }\n\n/* include/exclude filter rows (collapsible) */\n[data-edrv-view] .edrv-search-rows { display: flex; flex-direction: column; gap: 4px; padding: 2px 0 4px; }\n[data-edrv-view] .edrv-search-row { display: flex; align-items: center; gap: 4px; }\n[data-edrv-view] .edrv-search-field { flex: 1; min-width: 0; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 11px; outline: none; }\n[data-edrv-view] .edrv-search-field:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-field.dim { opacity: .45; }\n[data-edrv-view] .edrv-search-field::placeholder { color: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-search-toggle { min-width: 24px; height: 24px; padding: 0 5px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; line-height: 1; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-search-toggle:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-search-toggle.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); border-color: var(--dsw-alias-brand-primary, #0f9d58); }\n[data-edrv-view] .edrv-search-toggle:disabled { opacity: .4; cursor: default; }\n\n/* ===== rules panel (sidebar, Codebuddy rules view) ===== */\n[data-edrv-view] .edrv-rules-panel { display: flex; flex-direction: column; min-width: 0; min-height: 0; flex: 1; padding: 6px; gap: 4px; }\n[data-edrv-view] .edrv-rules-tabs { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-tab { height: 24px; padding: 0 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; cursor: pointer; }\n[data-edrv-view] .edrv-rules-tab:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-tab.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); font-weight: 600; }\n[data-edrv-view] .edrv-rules-new { margin-left: auto; height: 24px; padding: 0 10px; border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-new:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-ws-line { font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); padding: 0 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 0 0 auto; user-select: none; }\n[data-edrv-view] .edrv-rules-hint { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); padding: 0 2px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-list { flex: 1; min-height: 0; overflow: auto; }\n[data-edrv-view] .edrv-rules-row { display: flex; align-items: center; gap: 4px; padding: 6px 4px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; }\n[data-edrv-view] .edrv-rules-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-row.off .edrv-rules-main { opacity: .5; }\n[data-edrv-view] .edrv-rules-main { flex: 1; min-width: 0; cursor: pointer; overflow: hidden; }\n[data-edrv-view] .edrv-rules-line1 { display: flex; align-items: center; gap: 6px; min-width: 0; }\n[data-edrv-view] .edrv-rules-sub { display: flex; align-items: center; gap: 5px; margin-top: 3px; min-width: 0; }\n[data-edrv-view] .edrv-rules-sublabel { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-file { font-weight: 600; font-size: 12px; color: var(--dsw-alias-label-primary, #1f2933); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-rules-path { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-rules-type { flex-shrink: 0; height: 15px; padding: 0 5px; border-radius: 999px; font-size: 10px; line-height: 15px; }\n[data-edrv-view] .edrv-rules-type-always { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.15)); color: var(--dsw-alias-state-success-primary, #0b7c46); }\n[data-edrv-view] .edrv-rules-type-auto { background: var(--dsw-alias-interactive-bg-hover-accent, rgba(79,140,255,.16)); color: var(--dsw-alias-link, #3367c9); }\n[data-edrv-view] .edrv-rules-type-manual { background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-rules-offtag { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-desc { font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 0 1 auto; }\n[data-edrv-view] .edrv-rules-actions { display: flex; align-items: center; gap: 2px; flex-shrink: 0; margin-left: auto; }\n[data-edrv-view] .edrv-rules-act { min-width: 22px; height: 22px; padding: 0 4px; border: none; border-radius: 5px; background: transparent; font-size: 12px; line-height: 1; cursor: pointer; color: var(--dsw-alias-label-secondary, #52606d); display: inline-flex; align-items: center; justify-content: center; }\n[data-edrv-view] .edrv-rules-act:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }\n[data-edrv-view] .edrv-rules-switch { position: relative; width: 30px; height: 16px; border-radius: 999px; border: none; background: var(--dsw-alias-border-l2, #b9c4cc); cursor: pointer; transition: background .15s; flex-shrink: 0; padding: 0; }\n[data-edrv-view] .edrv-rules-switch.on { background: var(--dsw-alias-brand-primary, #7c5cff); }\n[data-edrv-view] .edrv-rules-knob { position: absolute; top: 2px; left: 2px; width: 12px; height: 12px; border-radius: 50%; background: var(--dsw-alias-bg-layer-1, #fff); transition: left .15s; }\n[data-edrv-view] .edrv-rules-switch.on .edrv-rules-knob { left: 16px; }\n[data-edrv-view] .edrv-rules-empty { padding: 10px 4px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); line-height: 1.6; }\n[data-edrv-view] .edrv-rules-err { padding: 6px 8px; border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; word-break: break-all; }\n[data-edrv-view] .edrv-rules-editor { display: flex; flex-direction: column; min-height: 0; flex: 1; gap: 6px; }\n[data-edrv-view] .edrv-rules-editor-head { display: flex; align-items: center; justify-content: space-between; gap: 6px; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-crumb { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-rules-editor-ctl { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-ctl-label { font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-select { height: 24px; padding: 0 4px; 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; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-rules-select:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-rules-desc-input { flex: 1; min-width: 0; }\n[data-edrv-view] .edrv-rules-input { 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; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-rules-input:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-rules-body { flex: 1; min-height: 0; resize: none; padding: 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; line-height: 1.5; outline: none; font-family: var(--ds-font-family-code, ui-monospace, monospace); }\n[data-edrv-view] .edrv-rules-body:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-rules-editor-hint { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); line-height: 1.5; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-editor-foot { display: flex; justify-content: flex-end; flex: 0 0 auto; }\n[data-edrv-view] .edrv-rules-save { height: 24px; padding: 0 14px; border: none; border-radius: 6px; background: var(--dsw-alias-brand-primary, #0f9d58); color: var(--dsw-alias-label-primary-inverted, #fff); font-size: 12px; cursor: pointer; }\n[data-edrv-view] .edrv-rules-save:disabled { opacity: .5; cursor: default; }\n\n/* 命令栏(Ctrl+Shift+P / F1):body 级 portal 浮层,故不依赖编辑区几何 */\n[data-edrv-view] .edrv-palette-mask { position: fixed; inset: 0; z-index: 190; background: rgba(0, 0, 0, .28); }\n[data-edrv-view] .edrv-palette { position: fixed; top: 12vh; left: 50%; transform: translateX(-50%); z-index: 191; width: min(580px, calc(100vw - 32px)); max-height: 64vh; display: flex; flex-direction: column; border-radius: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 12px 34px rgba(31, 41, 51, .24); overflow: hidden; }\n[data-edrv-view] .edrv-palette-input { height: 38px; flex: 0 0 auto; padding: 0 12px; border: none; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: transparent; color: var(--dsw-alias-label-primary, #1f2933); font-size: 13px; outline: none; }\n[data-edrv-view] .edrv-palette-list { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 4px; }\n[data-edrv-view] .edrv-palette-row { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-radius: 6px; font-size: 12px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; }\n[data-edrv-view] .edrv-palette-row.edrv-palette-sel { background: var(--dsw-alias-interactive-bg-hover, rgba(15, 157, 88, .10)); }\n[data-edrv-view] .edrv-palette-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-palette-cat { flex: 0 0 auto; font-size: 10px; padding: 1px 7px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-tertiary, #7b8794); }\n[data-edrv-view] .edrv-palette-key { flex: 0 0 auto; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 10px; padding: 1px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 5px; background: var(--dsw-alias-bg-layer-1, #f7f9fa); color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-palette-empty { padding: 14px 10px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #7b8794); text-align: center; }\n[data-edrv-view] .edrv-palette-notice { flex: 0 0 auto; padding: 6px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 11px; color: var(--dsw-alias-state-warn-primary, #b7791f); }\n\n\n\n\n";
|
|
35
36
|
const tagId$1 = "dsh-vscode-mode/editor.css";
|
|
36
37
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
37
38
|
const tag = document.createElement("style");
|
|
@@ -504,11 +505,11 @@ window.__ModuleLoader__.load({
|
|
|
504
505
|
//#endregion
|
|
505
506
|
//#region src/client/monaco/theme.ts
|
|
506
507
|
/**
|
|
507
|
-
* dsh-vscode-mode client — Monaco
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
511
|
-
* 作者 ddj 2026-08-28
|
|
508
|
+
* dsh-vscode-mode client — Monaco 主题:官方令牌跟随 + 现役双套回落。
|
|
509
|
+
* 优先级:官方代码/UI 令牌(--shiki-* / --dsw-alias-*,DSH 0.1.5+ 主题体系,
|
|
510
|
+
* 含第三方皮肤)→ edrv-dark / edrv-light 两套内置配色(旧版 DSH 或令牌缺失)。
|
|
511
|
+
* 明暗判定:body[data-ds-dark-theme](官方)→ 根 data-theme(旧皮肤)→ prefers-color-scheme。
|
|
512
|
+
* 作者 ddj 2026-08-28 / 2026-09-10
|
|
512
513
|
*/
|
|
513
514
|
const EDRV_DARK = "edrv-dark";
|
|
514
515
|
const EDRV_LIGHT = "edrv-light";
|
|
@@ -985,12 +986,14 @@ window.__ModuleLoader__.load({
|
|
|
985
986
|
} catch (error) {}
|
|
986
987
|
}
|
|
987
988
|
/**
|
|
988
|
-
* 探测当前 DSH
|
|
989
|
-
* @author ddj 2026年08月28号
|
|
989
|
+
* 探测当前 DSH 明暗(官方 data-ds-dark-theme → 旧皮肤 data-theme → prefers-color-scheme → 暗)。
|
|
990
|
+
* @author ddj 2026年08月28号 / 2026年09月10号
|
|
990
991
|
* @returns 'dark' | 'light'
|
|
991
992
|
*/
|
|
992
993
|
function detectColorScheme() {
|
|
993
994
|
try {
|
|
995
|
+
const official = document.body?.getAttribute?.("data-ds-dark-theme");
|
|
996
|
+
if (official !== null && official !== void 0 && official !== "false") return "dark";
|
|
994
997
|
const attr = document.documentElement?.getAttribute?.("data-theme") ?? document.body?.getAttribute?.("data-theme");
|
|
995
998
|
const value = String(attr ?? "").toLowerCase();
|
|
996
999
|
if (value.includes("light") || value.includes("catppuccin-latte")) return "light";
|
|
@@ -1018,6 +1021,337 @@ window.__ModuleLoader__.load({
|
|
|
1018
1021
|
monaco.editor.setTheme(themeNameOf());
|
|
1019
1022
|
} catch (error) {}
|
|
1020
1023
|
}
|
|
1024
|
+
/** 官方代码配色令牌 → Monaco 规则 token 前缀(粗粒度;未覆盖的规则保留现役语义分层)。 */
|
|
1025
|
+
const RULE_TOKEN_MAP = [
|
|
1026
|
+
["--shiki-token-comment", ["comment"]],
|
|
1027
|
+
["--shiki-token-keyword", ["keyword"]],
|
|
1028
|
+
["--shiki-token-string", ["string"]],
|
|
1029
|
+
["--shiki-token-string-expression", ["string.escape"]],
|
|
1030
|
+
["--shiki-token-constant", ["number", "constant"]],
|
|
1031
|
+
["--shiki-token-function", [
|
|
1032
|
+
"function",
|
|
1033
|
+
"method",
|
|
1034
|
+
"member"
|
|
1035
|
+
]],
|
|
1036
|
+
["--shiki-token-parameter", ["parameter", "variable.parameter"]],
|
|
1037
|
+
["--shiki-token-punctuation", [
|
|
1038
|
+
"delimiter",
|
|
1039
|
+
"operator",
|
|
1040
|
+
"punctuation"
|
|
1041
|
+
]],
|
|
1042
|
+
["--shiki-token-link", ["annotation", "metatag"]]
|
|
1043
|
+
];
|
|
1044
|
+
/**
|
|
1045
|
+
* 官方 UI 令牌 → Monaco 颜色键(每键给候选令牌,按序取首个可解析值)。
|
|
1046
|
+
* 依据运行态实测:--dsw-alias-* 定义在 body 上;--shiki-background/-foreground 未定义,
|
|
1047
|
+
* 故代码面底色用 markdown-code-block 近似;selection 不覆盖(沿用现役可见选框)。
|
|
1048
|
+
*/
|
|
1049
|
+
const COLOR_TOKEN_MAP = [
|
|
1050
|
+
[["--dsw-alias-markdown-code-block", "--dsw-alias-bg-layer-2"], [
|
|
1051
|
+
"editor.background",
|
|
1052
|
+
"editorGutter.background",
|
|
1053
|
+
"minimap.background"
|
|
1054
|
+
]],
|
|
1055
|
+
[["--dsw-alias-label-primary", "--shiki-foreground"], ["editor.foreground"]],
|
|
1056
|
+
[["--dsw-alias-bg-layer-1", "--dsw-alias-bg-layer-3"], ["editorWidget.background", "editorHoverWidget.background"]],
|
|
1057
|
+
[["--dsw-alias-label-tertiary"], ["editorLineNumber.foreground"]],
|
|
1058
|
+
[["--dsw-alias-label-secondary"], ["editorLineNumber.activeForeground"]],
|
|
1059
|
+
[["--dsw-alias-interactive-bg-hover"], ["editor.lineHighlightBackground", "editor.inactiveSelectionBackground"]]
|
|
1060
|
+
];
|
|
1061
|
+
/** 已应用的官方主题序号(模块级计数,保证主题 id 每次唯一)。 */
|
|
1062
|
+
let officialSeq = 0;
|
|
1063
|
+
/**
|
|
1064
|
+
* 读官方 CSS 变量(先 body 后文档根;未定义/空值/未解析 var() 返回 undefined)。
|
|
1065
|
+
* 依据运行态实测:DSH 的 --dsw-alias-* 定义在 body 上,仅 --shiki-token-* 在根。
|
|
1066
|
+
* @author ddj 2026年09月10号
|
|
1067
|
+
* @param name CSS 变量名(含 -- 前缀)
|
|
1068
|
+
* @returns 计算样式值,或 undefined
|
|
1069
|
+
*/
|
|
1070
|
+
function readCssVar(name) {
|
|
1071
|
+
try {
|
|
1072
|
+
if (typeof window?.getComputedStyle !== "function") return void 0;
|
|
1073
|
+
const hosts = [document?.body, document?.documentElement];
|
|
1074
|
+
for (const host of hosts) {
|
|
1075
|
+
if (!host) continue;
|
|
1076
|
+
const raw = window.getComputedStyle(host).getPropertyValue(name);
|
|
1077
|
+
const value = String(raw ?? "").trim();
|
|
1078
|
+
if (value && !value.startsWith("var(")) return value;
|
|
1079
|
+
}
|
|
1080
|
+
return;
|
|
1081
|
+
} catch (error) {
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* 数值 → 两位十六进制(0-255 夹取)。
|
|
1087
|
+
* @author ddj 2026年09月10号
|
|
1088
|
+
* @param value 通道值
|
|
1089
|
+
* @returns 两位小写十六进制
|
|
1090
|
+
*/
|
|
1091
|
+
function hex2(value) {
|
|
1092
|
+
const text = Math.max(0, Math.min(255, Math.round(Number(value) || 0))).toString(16);
|
|
1093
|
+
return text.length === 1 ? "0" + text : text;
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* CSS 颜色归一为 Monaco 可用值(#rgb/#rrggbb/#rrggbbaa 原样,rgb()/hsl() 转 hex,其余 undefined)。
|
|
1097
|
+
* @author ddj 2026年09月10号
|
|
1098
|
+
* @param value CSS 计算样式颜色值
|
|
1099
|
+
* @returns Monaco 颜色字符串,或 undefined(不可识别)
|
|
1100
|
+
*/
|
|
1101
|
+
function toMonacoColor(value) {
|
|
1102
|
+
const text = String(value ?? "").trim().toLowerCase();
|
|
1103
|
+
if (!text || text.startsWith("var(")) return void 0;
|
|
1104
|
+
if (/^#[0-9a-f]{3}$/.test(text)) return "#" + text.slice(1).split("").map((c) => c + c).join("");
|
|
1105
|
+
if (/^#[0-9a-f]{6}$/.test(text) || /^#[0-9a-f]{8}$/.test(text)) return text;
|
|
1106
|
+
return channelColor(text);
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* rgb()/rgba()/hsl()/hsla() → Monaco 颜色(兼容逗号与空格/斜杠两种参数语法)。
|
|
1110
|
+
* @author ddj 2026年09月10号
|
|
1111
|
+
* @param text 小写颜色文本
|
|
1112
|
+
* @returns Monaco 颜色,或 undefined(不可识别)
|
|
1113
|
+
*/
|
|
1114
|
+
function channelColor(text) {
|
|
1115
|
+
const body = /^(?:rgba?|hsla?)\(([^)]+)\)$/.exec(text)?.[1];
|
|
1116
|
+
if (!body) return void 0;
|
|
1117
|
+
const parts = body.split(/[\s,/]+/).filter(Boolean);
|
|
1118
|
+
if (parts.length < 3) return void 0;
|
|
1119
|
+
let channels;
|
|
1120
|
+
if (text.startsWith("hsl")) {
|
|
1121
|
+
const h = Number(parts[0]);
|
|
1122
|
+
const s = percentOf(parts[1]);
|
|
1123
|
+
const l = percentOf(parts[2]);
|
|
1124
|
+
if (![
|
|
1125
|
+
h,
|
|
1126
|
+
s,
|
|
1127
|
+
l
|
|
1128
|
+
].every(Number.isFinite)) return void 0;
|
|
1129
|
+
channels = hslToRgb(h, s, l);
|
|
1130
|
+
} else channels = parts.slice(0, 3).map(Number);
|
|
1131
|
+
if (channels.some((n) => !Number.isFinite(n))) return void 0;
|
|
1132
|
+
const alpha = parts.length > 3 ? alphaOf(parts[3]) : 1;
|
|
1133
|
+
const tail = Number.isFinite(alpha) && alpha < 1 ? hex2(alpha * 255) : "";
|
|
1134
|
+
return "#" + channels.map(hex2).join("") + tail;
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* 百分数文本 → 数值('5%' → 5,'0.5' → 0.5)。
|
|
1138
|
+
* @author ddj 2026年09月10号
|
|
1139
|
+
* @param value 参数文本
|
|
1140
|
+
* @returns 数值(非数字为 NaN)
|
|
1141
|
+
*/
|
|
1142
|
+
function percentOf(value) {
|
|
1143
|
+
const text = String(value ?? "");
|
|
1144
|
+
return text.endsWith("%") ? Number(text.slice(0, -1)) : Number(text);
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* alpha 参数归一('5%' → 0.05,'0.5' → 0.5)。
|
|
1148
|
+
* @author ddj 2026年09月10号
|
|
1149
|
+
* @param value 参数文本
|
|
1150
|
+
* @returns 0-1 的 alpha(非数字为 NaN)
|
|
1151
|
+
*/
|
|
1152
|
+
function alphaOf(value) {
|
|
1153
|
+
const text = String(value ?? "");
|
|
1154
|
+
return text.endsWith("%") ? Number(text.slice(0, -1)) / 100 : Number(text);
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* HSL → RGB 通道(h 度、s/l 为 0-100 百分数)。
|
|
1158
|
+
* @author ddj 2026年09月10号
|
|
1159
|
+
* @param h 色相(度)
|
|
1160
|
+
* @param s 饱和度(0-100)
|
|
1161
|
+
* @param l 亮度(0-100)
|
|
1162
|
+
* @returns [r, g, b](0-255)
|
|
1163
|
+
*/
|
|
1164
|
+
function hslToRgb(h, s, l) {
|
|
1165
|
+
const hue = (h % 360 + 360) % 360;
|
|
1166
|
+
const sat = Math.max(0, Math.min(100, s)) / 100;
|
|
1167
|
+
const lum = Math.max(0, Math.min(100, l)) / 100;
|
|
1168
|
+
const c = (1 - Math.abs(2 * lum - 1)) * sat;
|
|
1169
|
+
const x = c * (1 - Math.abs(hue / 60 % 2 - 1));
|
|
1170
|
+
const m = lum - c / 2;
|
|
1171
|
+
return [
|
|
1172
|
+
[
|
|
1173
|
+
c,
|
|
1174
|
+
x,
|
|
1175
|
+
0
|
|
1176
|
+
],
|
|
1177
|
+
[
|
|
1178
|
+
x,
|
|
1179
|
+
c,
|
|
1180
|
+
0
|
|
1181
|
+
],
|
|
1182
|
+
[
|
|
1183
|
+
0,
|
|
1184
|
+
c,
|
|
1185
|
+
x
|
|
1186
|
+
],
|
|
1187
|
+
[
|
|
1188
|
+
0,
|
|
1189
|
+
x,
|
|
1190
|
+
c
|
|
1191
|
+
],
|
|
1192
|
+
[
|
|
1193
|
+
x,
|
|
1194
|
+
0,
|
|
1195
|
+
c
|
|
1196
|
+
],
|
|
1197
|
+
[
|
|
1198
|
+
c,
|
|
1199
|
+
0,
|
|
1200
|
+
x
|
|
1201
|
+
]
|
|
1202
|
+
][Math.min(5, Math.floor(hue / 60))].map((v) => (v + m) * 255);
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* 官方令牌 → Monaco 颜色键覆盖表(候选令牌按序取首个可解析值;全缺则不写入,保持现役回落)。
|
|
1206
|
+
* @author ddj 2026年09月10号
|
|
1207
|
+
* @returns 颜色键 → 颜色值
|
|
1208
|
+
*/
|
|
1209
|
+
function tokenColors() {
|
|
1210
|
+
const out = {};
|
|
1211
|
+
for (const entry of COLOR_TOKEN_MAP) {
|
|
1212
|
+
const color = firstColor(entry[0]);
|
|
1213
|
+
if (color === void 0) continue;
|
|
1214
|
+
for (const key of entry[1]) out[key] = color;
|
|
1215
|
+
}
|
|
1216
|
+
return out;
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* 候选令牌中首个可解析为 Monaco 颜色的值。
|
|
1220
|
+
* @author ddj 2026年09月10号
|
|
1221
|
+
* @param names CSS 变量名候选(按优先级)
|
|
1222
|
+
* @returns Monaco 颜色,或 undefined(全部缺失/不可解析)
|
|
1223
|
+
*/
|
|
1224
|
+
function firstColor(names) {
|
|
1225
|
+
for (const name of names) {
|
|
1226
|
+
const color = toMonacoColor(readCssVar(name));
|
|
1227
|
+
if (color !== void 0) return color;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* 官方令牌 → Monaco 规则前缀覆盖表(值去掉 # 前缀,Monaco rules 约定)。
|
|
1232
|
+
* @author ddj 2026年09月10号
|
|
1233
|
+
* @returns token 前缀 → 十六进制色(无 #)
|
|
1234
|
+
*/
|
|
1235
|
+
function tokenRules() {
|
|
1236
|
+
const out = {};
|
|
1237
|
+
for (const entry of RULE_TOKEN_MAP) {
|
|
1238
|
+
const color = toMonacoColor(readCssVar(entry[0]));
|
|
1239
|
+
if (color === void 0) continue;
|
|
1240
|
+
for (const prefix of entry[1]) out[prefix] = color.replace("#", "");
|
|
1241
|
+
}
|
|
1242
|
+
return out;
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* 现役规则叠加令牌覆盖(前缀命中即换色,未命中保持语义分层)。
|
|
1246
|
+
* @author ddj 2026年09月10号
|
|
1247
|
+
* @param baseRules 现役规则表(edrv-dark / edrv-light)
|
|
1248
|
+
* @param overrides token 前缀 → 颜色
|
|
1249
|
+
* @returns 覆盖后的规则表(无覆盖时原样返回)
|
|
1250
|
+
*/
|
|
1251
|
+
function withOverrides(baseRules, overrides) {
|
|
1252
|
+
const prefixes = Object.keys(overrides);
|
|
1253
|
+
if (!prefixes.length) return baseRules;
|
|
1254
|
+
return baseRules.map((rule) => {
|
|
1255
|
+
const hit = prefixes.find((p) => rule.token === p || rule.token.startsWith(p + "."));
|
|
1256
|
+
return hit === void 0 ? rule : Object.assign({}, rule, { foreground: overrides[hit] });
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* 基础规则(token 为空串)前景色跟随官方 editor.foreground。
|
|
1261
|
+
* Monaco 的 '' 规则匹配所有未被更具体规则命中的 token,不改它则内置 #1e1e1e 会盖住
|
|
1262
|
+
* editor.foreground 令牌色(实测 .mtk1 仍为内置值的原因)。
|
|
1263
|
+
* @author ddj 2026年09月10号
|
|
1264
|
+
* @param rules 现役规则表
|
|
1265
|
+
* @param color 已解析的 editor.foreground(含 #,缺省则不覆盖)
|
|
1266
|
+
* @returns 基础规则换色后的规则表
|
|
1267
|
+
*/
|
|
1268
|
+
function withBaseForeground(rules, color) {
|
|
1269
|
+
if (!color) return rules;
|
|
1270
|
+
const foreground = String(color).replace("#", "");
|
|
1271
|
+
return rules.map((rule) => rule.token === "" ? Object.assign({}, rule, { foreground }) : rule);
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* 构建跟随官方的 Monaco 主题(令牌全缺时 hasTokens=false,调用方回落现役双套)。
|
|
1275
|
+
* @author ddj 2026年09月10号
|
|
1276
|
+
* @param scheme 明暗('light' 之外一律按暗色)
|
|
1277
|
+
* @returns 主题定义(base/rules/colors/hasTokens)
|
|
1278
|
+
*/
|
|
1279
|
+
function officialThemeOf(scheme) {
|
|
1280
|
+
const dark = scheme !== "light";
|
|
1281
|
+
const colors = tokenColors();
|
|
1282
|
+
const rules = tokenRules();
|
|
1283
|
+
const layered = withOverrides(dark ? DARK_RULES : LIGHT_RULES, rules);
|
|
1284
|
+
return {
|
|
1285
|
+
base: dark ? "vs-dark" : "vs",
|
|
1286
|
+
rules: withBaseForeground(layered, colors["editor.foreground"]),
|
|
1287
|
+
colors: Object.assign({}, dark ? DARK_COLORS : LIGHT_COLORS, colors),
|
|
1288
|
+
hasTokens: Object.keys(colors).length > 0 || Object.keys(rules).length > 0
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* 应用跟随官方的主题;令牌读不到或定义失败时回落现役双套。
|
|
1293
|
+
* @author ddj 2026年09月10号
|
|
1294
|
+
* @param monaco Monaco 实例
|
|
1295
|
+
* @returns 实际应用的 Monaco 主题 id
|
|
1296
|
+
*/
|
|
1297
|
+
function applyOfficial(monaco) {
|
|
1298
|
+
if (!monaco?.editor?.setTheme || !monaco?.editor?.defineTheme) return themeNameOf();
|
|
1299
|
+
const theme = officialThemeOf(detectColorScheme());
|
|
1300
|
+
if (!theme.hasTokens) {
|
|
1301
|
+
applyTheme(monaco);
|
|
1302
|
+
return themeNameOf();
|
|
1303
|
+
}
|
|
1304
|
+
officialSeq += 1;
|
|
1305
|
+
const id = "edrv-official-" + officialSeq;
|
|
1306
|
+
try {
|
|
1307
|
+
monaco.editor.defineTheme(id, {
|
|
1308
|
+
base: theme.base,
|
|
1309
|
+
inherit: true,
|
|
1310
|
+
rules: theme.rules,
|
|
1311
|
+
colors: theme.colors
|
|
1312
|
+
});
|
|
1313
|
+
monaco.editor.setTheme(id);
|
|
1314
|
+
return id;
|
|
1315
|
+
} catch (error) {
|
|
1316
|
+
applyTheme(monaco);
|
|
1317
|
+
return themeNameOf();
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
/**
|
|
1321
|
+
* 观察官方明暗标记变化(body / 根节点的 data-ds-dark-theme 与 data-theme)。
|
|
1322
|
+
* @author ddj 2026年09月10号
|
|
1323
|
+
* @param onChange 标记变化回调
|
|
1324
|
+
* @returns 停止观察函数(非浏览器环境返回空函数)
|
|
1325
|
+
*/
|
|
1326
|
+
function observeScheme(onChange) {
|
|
1327
|
+
try {
|
|
1328
|
+
if (typeof MutationObserver !== "function" || !document?.body) return () => {};
|
|
1329
|
+
const observer = new MutationObserver(() => onChange());
|
|
1330
|
+
const filter = ["data-ds-dark-theme", "data-theme"];
|
|
1331
|
+
observer.observe(document.body, {
|
|
1332
|
+
attributes: true,
|
|
1333
|
+
attributeFilter: filter
|
|
1334
|
+
});
|
|
1335
|
+
if (document.documentElement) observer.observe(document.documentElement, {
|
|
1336
|
+
attributes: true,
|
|
1337
|
+
attributeFilter: filter
|
|
1338
|
+
});
|
|
1339
|
+
return () => observer.disconnect();
|
|
1340
|
+
} catch (error) {
|
|
1341
|
+
return () => {};
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* 官方主题快照 → 明暗(ThemeRuntime.getTheme() 快照;字段缺失/未知返回 undefined)。
|
|
1346
|
+
* @author ddj 2026年09月10号
|
|
1347
|
+
* @param snapshot 官方主题快照
|
|
1348
|
+
* @returns 'light' | 'dark' | undefined
|
|
1349
|
+
*/
|
|
1350
|
+
function schemeOfSnapshot(snapshot) {
|
|
1351
|
+
const value = snapshot ?? {};
|
|
1352
|
+
const scheme = value.active?.colorScheme ?? value.colorScheme;
|
|
1353
|
+
return scheme === "light" || scheme === "dark" ? scheme : void 0;
|
|
1354
|
+
}
|
|
1021
1355
|
//#endregion
|
|
1022
1356
|
//#region src/client/monaco/loader.ts
|
|
1023
1357
|
/**
|
|
@@ -1185,7 +1519,7 @@ window.__ModuleLoader__.load({
|
|
|
1185
1519
|
publishStage("ready", MONACO_STAGES.ready.progress, MONACO_STAGES.ready.message);
|
|
1186
1520
|
try {
|
|
1187
1521
|
registerThemes(window.monaco);
|
|
1188
|
-
|
|
1522
|
+
applyOfficial(window.monaco);
|
|
1189
1523
|
} catch (error) {}
|
|
1190
1524
|
resolve(window.monaco);
|
|
1191
1525
|
}, (err) => fail(/* @__PURE__ */ new Error("Monaco 模块加载失败:" + String(err))));
|
|
@@ -2153,7 +2487,9 @@ window.__ModuleLoader__.load({
|
|
|
2153
2487
|
* 纯数据模块:host(settings schema 默认值)与 client(执行匹配/设置页)共用。
|
|
2154
2488
|
* 键位格式:修饰符 + 主键,`+` 连接,如 `Ctrl+Shift+F`;空串 = 未绑定;
|
|
2155
2489
|
* `|` 连接多个候选(任一命中即触发),如 `Alt+ArrowLeft|Ctrl+Alt+-`。
|
|
2156
|
-
*
|
|
2490
|
+
* 约束:本模块被 host 与 client 两半共用,**禁止 import client 侧模块**(React/浏览器 API)。
|
|
2491
|
+
* 与 client/ui/commandCatalog 的一致性由 tests/commands.test.ts 断言兜底。
|
|
2492
|
+
* 作者 ddj 2026年08月26号 / 2026年09月10号
|
|
2157
2493
|
*/
|
|
2158
2494
|
/** 命令 id → 默认键位(可为多候选)。命令目录以此为准,新增命令只需加一项。 */
|
|
2159
2495
|
const KEYBINDING_DEFAULTS = {
|
|
@@ -2162,7 +2498,12 @@ window.__ModuleLoader__.load({
|
|
|
2162
2498
|
"edrv.toggleSidebar": "Ctrl+B",
|
|
2163
2499
|
"edrv.searchInFiles": "Ctrl+Shift+F",
|
|
2164
2500
|
"edrv.navigateBack": "Alt+ArrowLeft|Ctrl+Alt+-",
|
|
2165
|
-
"edrv.navigateForward": "Alt+ArrowRight|Ctrl+Shift+-"
|
|
2501
|
+
"edrv.navigateForward": "Alt+ArrowRight|Ctrl+Shift+-",
|
|
2502
|
+
"edrv.nextTab": "Ctrl+Alt+ArrowRight|Ctrl+PageDown",
|
|
2503
|
+
"edrv.prevTab": "Ctrl+Alt+ArrowLeft|Ctrl+PageUp",
|
|
2504
|
+
"edrv.showCommands": "Ctrl+Shift+P|F1",
|
|
2505
|
+
"edrv.nextEditorRow": "Ctrl+Alt+ArrowDown",
|
|
2506
|
+
"edrv.prevEditorRow": "Ctrl+Alt+ArrowUp"
|
|
2166
2507
|
};
|
|
2167
2508
|
/**
|
|
2168
2509
|
* 返回默认键位的独立副本(调用方修改不污染目录)。
|
|
@@ -2186,41 +2527,261 @@ window.__ModuleLoader__.load({
|
|
|
2186
2527
|
return out;
|
|
2187
2528
|
}
|
|
2188
2529
|
//#endregion
|
|
2189
|
-
//#region src/client/
|
|
2530
|
+
//#region src/client/editorModelState.ts
|
|
2190
2531
|
/**
|
|
2191
|
-
*
|
|
2192
|
-
*
|
|
2193
|
-
*
|
|
2194
|
-
* 键位语义:Ctrl 与 Cmd 互认(延续现有 Ctrl+P/Ctrl+B 捕获行为)。
|
|
2195
|
-
* 作者 ddj 2026年08月26号
|
|
2532
|
+
* 当前文档是否已有 Monaco 活动模型(编辑器打开着文件而非空态)。
|
|
2533
|
+
* @author ddj 2026年09月10号
|
|
2534
|
+
* @returns 是否存在活动模型
|
|
2196
2535
|
*/
|
|
2197
|
-
|
|
2198
|
-
|
|
2536
|
+
function hasEditorModel() {
|
|
2537
|
+
if (typeof document === "undefined") return false;
|
|
2538
|
+
return document.querySelector(".monaco-editor textarea.inputarea") !== null;
|
|
2539
|
+
}
|
|
2540
|
+
//#endregion
|
|
2541
|
+
//#region src/client/ui/commandCatalog.ts
|
|
2542
|
+
/**
|
|
2543
|
+
* dsh-vscode-mode client — 编辑器指令目录(纯元数据)。
|
|
2544
|
+
* 「一条命令 = 一条注册数据」:命令栏、快捷键设置页、快捷键监听与 Monaco 右键菜单
|
|
2545
|
+
* 全部从本表读取;新增一条能力只需在 EDITOR_COMMANDS 追加一项(可选 keybinding)。
|
|
2546
|
+
* run 只派发 `edrv.command.*` 窗口事件,不直接触碰 React/Monaco(`edrv.command.` 前缀
|
|
2547
|
+
* 与既有 `edrv:` 刷新/主题事件分属不同命名空间,互不干扰)。
|
|
2548
|
+
* 作者 ddj 2026年09月10号
|
|
2549
|
+
*/
|
|
2550
|
+
/**
|
|
2551
|
+
* 派发编辑器命令事件(`edrv.command.<action>`)。
|
|
2552
|
+
* 无 window 的运行环境(纯 Node 单测)静默跳过,命令本身不因此失败。
|
|
2553
|
+
* @author ddj 2026年09月10号
|
|
2554
|
+
* @param action 动作名(不含前缀)
|
|
2555
|
+
*/
|
|
2556
|
+
function emit(action) {
|
|
2557
|
+
if (typeof window === "undefined") return;
|
|
2558
|
+
window.dispatchEvent(new CustomEvent("edrv.command." + action));
|
|
2559
|
+
}
|
|
2560
|
+
/** 需要活动编辑器模型才可用的命令(命令栏隐藏并拒绝执行)。 */
|
|
2561
|
+
function needsModel() {
|
|
2562
|
+
return hasEditorModel();
|
|
2563
|
+
}
|
|
2564
|
+
/** 面板区显隐切换始终可用(未挂载编辑器时 run 为空操作)。 */
|
|
2565
|
+
function alwaysAvailable() {
|
|
2566
|
+
return true;
|
|
2567
|
+
}
|
|
2568
|
+
/**
|
|
2569
|
+
* 光标整行下移(编辑行导航)。
|
|
2570
|
+
* @author ddj 2026年09月10号
|
|
2571
|
+
* @returns 命令定义
|
|
2572
|
+
*/
|
|
2573
|
+
function nextEditorRowDef() {
|
|
2574
|
+
return {
|
|
2575
|
+
id: "edrv.nextEditorRow",
|
|
2576
|
+
label: "下一编辑行(光标整行下移)",
|
|
2577
|
+
category: "导航",
|
|
2578
|
+
order: 50,
|
|
2579
|
+
keybinding: "Ctrl+Alt+ArrowDown",
|
|
2580
|
+
available: needsModel,
|
|
2581
|
+
run: () => emit("nextEditorRow")
|
|
2582
|
+
};
|
|
2583
|
+
}
|
|
2584
|
+
/**
|
|
2585
|
+
* 光标整行上移(编辑行导航)。
|
|
2586
|
+
* @author ddj 2026年09月10号
|
|
2587
|
+
* @returns 命令定义
|
|
2588
|
+
*/
|
|
2589
|
+
function prevEditorRowDef() {
|
|
2590
|
+
return {
|
|
2591
|
+
id: "edrv.prevEditorRow",
|
|
2592
|
+
label: "上一编辑行(光标整行上移)",
|
|
2593
|
+
category: "导航",
|
|
2594
|
+
order: 60,
|
|
2595
|
+
keybinding: "Ctrl+Alt+ArrowUp",
|
|
2596
|
+
available: needsModel,
|
|
2597
|
+
run: () => emit("prevEditorRow")
|
|
2598
|
+
};
|
|
2599
|
+
}
|
|
2600
|
+
/**
|
|
2601
|
+
* 编辑器内置指令目录(顺序 = 快捷键设置页展示顺序)。
|
|
2602
|
+
* 前置 8 条的键位由 EditorView / QuickOpen 自行 capture 监听(历史实现),
|
|
2603
|
+
* 故不进 BRIDGE_COMMANDS,避免同一按键双执行。
|
|
2604
|
+
* @author ddj 2026年09月10号
|
|
2605
|
+
*/
|
|
2606
|
+
const EDITOR_COMMANDS = [
|
|
2199
2607
|
{
|
|
2200
2608
|
id: "edrv.save",
|
|
2201
|
-
label: "保存文件"
|
|
2609
|
+
label: "保存文件",
|
|
2610
|
+
category: "文件",
|
|
2611
|
+
order: 10,
|
|
2612
|
+
keybinding: "Ctrl+S",
|
|
2613
|
+
available: needsModel,
|
|
2614
|
+
run: () => emit("save")
|
|
2202
2615
|
},
|
|
2203
2616
|
{
|
|
2204
2617
|
id: "edrv.quickOpen",
|
|
2205
|
-
label: "快速打开文件"
|
|
2618
|
+
label: "快速打开文件",
|
|
2619
|
+
category: "文件",
|
|
2620
|
+
order: 20,
|
|
2621
|
+
keybinding: "Ctrl+P",
|
|
2622
|
+
available: alwaysAvailable,
|
|
2623
|
+
run: () => emit("quickOpen")
|
|
2206
2624
|
},
|
|
2207
2625
|
{
|
|
2208
2626
|
id: "edrv.toggleSidebar",
|
|
2209
|
-
label: "切换侧边栏"
|
|
2627
|
+
label: "切换侧边栏",
|
|
2628
|
+
category: "视图",
|
|
2629
|
+
order: 10,
|
|
2630
|
+
keybinding: "Ctrl+B",
|
|
2631
|
+
available: alwaysAvailable,
|
|
2632
|
+
run: () => emit("toggleSidebar")
|
|
2210
2633
|
},
|
|
2211
2634
|
{
|
|
2212
2635
|
id: "edrv.searchInFiles",
|
|
2213
|
-
label: "在工作区中搜索"
|
|
2636
|
+
label: "在工作区中搜索",
|
|
2637
|
+
category: "视图",
|
|
2638
|
+
order: 20,
|
|
2639
|
+
keybinding: "Ctrl+Shift+F",
|
|
2640
|
+
available: alwaysAvailable,
|
|
2641
|
+
run: () => emit("searchInFiles")
|
|
2214
2642
|
},
|
|
2215
2643
|
{
|
|
2216
2644
|
id: "edrv.navigateBack",
|
|
2217
|
-
label: "后退(导航历史)"
|
|
2645
|
+
label: "后退(导航历史)",
|
|
2646
|
+
category: "导航",
|
|
2647
|
+
order: 10,
|
|
2648
|
+
keybinding: "Alt+ArrowLeft|Ctrl+Alt+-",
|
|
2649
|
+
available: needsModel,
|
|
2650
|
+
run: () => emit("navigateBack")
|
|
2218
2651
|
},
|
|
2219
2652
|
{
|
|
2220
2653
|
id: "edrv.navigateForward",
|
|
2221
|
-
label: "前进(导航历史)"
|
|
2654
|
+
label: "前进(导航历史)",
|
|
2655
|
+
category: "导航",
|
|
2656
|
+
order: 20,
|
|
2657
|
+
keybinding: "Alt+ArrowRight|Ctrl+Shift+-",
|
|
2658
|
+
available: needsModel,
|
|
2659
|
+
run: () => emit("navigateForward")
|
|
2660
|
+
},
|
|
2661
|
+
{
|
|
2662
|
+
id: "edrv.nextTab",
|
|
2663
|
+
label: "下一个页签",
|
|
2664
|
+
category: "导航",
|
|
2665
|
+
order: 30,
|
|
2666
|
+
keybinding: "Ctrl+Alt+ArrowRight|Ctrl+PageDown",
|
|
2667
|
+
available: needsModel,
|
|
2668
|
+
run: () => emit("nextTab")
|
|
2669
|
+
},
|
|
2670
|
+
{
|
|
2671
|
+
id: "edrv.prevTab",
|
|
2672
|
+
label: "上一个页签",
|
|
2673
|
+
category: "导航",
|
|
2674
|
+
order: 40,
|
|
2675
|
+
keybinding: "Ctrl+Alt+ArrowLeft|Ctrl+PageUp",
|
|
2676
|
+
available: needsModel,
|
|
2677
|
+
run: () => emit("prevTab")
|
|
2678
|
+
},
|
|
2679
|
+
{
|
|
2680
|
+
id: "edrv.goToDefinition",
|
|
2681
|
+
label: "转到定义",
|
|
2682
|
+
category: "语言智能",
|
|
2683
|
+
order: 10,
|
|
2684
|
+
available: needsModel,
|
|
2685
|
+
run: () => emit("goToDefinition")
|
|
2686
|
+
},
|
|
2687
|
+
{
|
|
2688
|
+
id: "edrv.findReferences",
|
|
2689
|
+
label: "查找所有引用",
|
|
2690
|
+
category: "语言智能",
|
|
2691
|
+
order: 20,
|
|
2692
|
+
available: needsModel,
|
|
2693
|
+
run: () => emit("findReferences")
|
|
2694
|
+
},
|
|
2695
|
+
{
|
|
2696
|
+
id: "edrv.triggerAi",
|
|
2697
|
+
label: "触发 AI 内联补全",
|
|
2698
|
+
category: "语言智能",
|
|
2699
|
+
order: 30,
|
|
2700
|
+
available: needsModel,
|
|
2701
|
+
run: () => emit("triggerAi")
|
|
2702
|
+
},
|
|
2703
|
+
{
|
|
2704
|
+
id: "edrv.openInExplorer",
|
|
2705
|
+
label: "在文件浏览器中打开",
|
|
2706
|
+
category: "文件",
|
|
2707
|
+
order: 30,
|
|
2708
|
+
available: needsModel,
|
|
2709
|
+
run: () => emit("openInExplorer")
|
|
2222
2710
|
}
|
|
2223
2711
|
];
|
|
2712
|
+
/**
|
|
2713
|
+
* 桥接派发指令(无原生监听,键位由 commandBridge 统一 capture 处理)。
|
|
2714
|
+
* 新增「只填目录、不写监听」的编辑器指令一律放这里:键位、命令栏、设置页自动可用。
|
|
2715
|
+
* @author ddj 2026年09月10号
|
|
2716
|
+
*/
|
|
2717
|
+
const BRIDGE_COMMANDS = [nextEditorRowDef(), prevEditorRowDef()];
|
|
2718
|
+
/**
|
|
2719
|
+
* 命令栏自身的命令(避免循环依赖,由 commandBridge 注入 run 后注册)。
|
|
2720
|
+
* @author ddj 2026年09月10号
|
|
2721
|
+
* @param run 打开命令栏
|
|
2722
|
+
* @returns 命令定义
|
|
2723
|
+
*/
|
|
2724
|
+
function showCommandsDef(run) {
|
|
2725
|
+
return {
|
|
2726
|
+
id: "edrv.showCommands",
|
|
2727
|
+
label: "显示所有命令",
|
|
2728
|
+
category: "视图",
|
|
2729
|
+
order: 1,
|
|
2730
|
+
keybinding: "Ctrl+Shift+P|F1",
|
|
2731
|
+
available: alwaysAvailable,
|
|
2732
|
+
run
|
|
2733
|
+
};
|
|
2734
|
+
}
|
|
2735
|
+
/**
|
|
2736
|
+
* 需要全局键位派发的指令(桥接类 + 命令栏自身;不含 EditorView/QuickOpen 原生监听的那些)。
|
|
2737
|
+
* @author ddj 2026年09月10号
|
|
2738
|
+
* @param showCommands 命令栏命令定义
|
|
2739
|
+
* @returns 派发集合
|
|
2740
|
+
*/
|
|
2741
|
+
function dispatchedCommands(showCommands) {
|
|
2742
|
+
return [...BRIDGE_COMMANDS, showCommands];
|
|
2743
|
+
}
|
|
2744
|
+
//#endregion
|
|
2745
|
+
//#region src/client/keybindings.ts
|
|
2746
|
+
/**
|
|
2747
|
+
* dsh-vscode-mode client — 快捷键配置模块(解析/匹配/状态同步)。
|
|
2748
|
+
* 纯逻辑(parseChord/parseChords/formatChord/matchEvent/chordFromEvent/normalizeKey)不依赖 DOM,可单测;
|
|
2749
|
+
* 模块状态由 settings 订阅驱动(client/index.ts 调 keybindingsApply)。
|
|
2750
|
+
* 键位语义:Ctrl 与 Cmd 互认(延续 Ctrl+P/Ctrl+B 捕获行为)。
|
|
2751
|
+
* 命令目录(COMMANDS)派生自指令目录 commandCatalog(指令 = 单一数据源,设置页自动跟随);
|
|
2752
|
+
* 另支持运行时键位表(第三方命令 register 时声明,不落设置 schema,注销即失效)。
|
|
2753
|
+
* 作者 ddj 2026年08月26号 / 2026年09月10号
|
|
2754
|
+
*/
|
|
2755
|
+
/** 命令目录(设置页展示标签;执行按目录序先匹配先执行,冲突键位确定性)。 */
|
|
2756
|
+
const COMMANDS = [
|
|
2757
|
+
...EDITOR_COMMANDS.map((command) => ({
|
|
2758
|
+
id: command.id,
|
|
2759
|
+
label: command.label
|
|
2760
|
+
})),
|
|
2761
|
+
{
|
|
2762
|
+
id: "edrv.showCommands",
|
|
2763
|
+
label: "显示所有命令"
|
|
2764
|
+
},
|
|
2765
|
+
...BRIDGE_COMMANDS.map((command) => ({
|
|
2766
|
+
id: command.id,
|
|
2767
|
+
label: command.label
|
|
2768
|
+
}))
|
|
2769
|
+
];
|
|
2770
|
+
/** 命令栏命令定义(键位自检与目录展示共用;run 不参与键位逻辑)。 */
|
|
2771
|
+
function paletteCommand() {
|
|
2772
|
+
return showCommandsDef(() => {});
|
|
2773
|
+
}
|
|
2774
|
+
/**
|
|
2775
|
+
* 目录与默认键位表一致性自检(仅告警不中断):新指令漏写共享表时第一时间可见。
|
|
2776
|
+
* @author ddj 2026年09月10号
|
|
2777
|
+
*/
|
|
2778
|
+
function checkDefaultsDrift() {
|
|
2779
|
+
for (const command of [...EDITOR_COMMANDS, paletteCommand()]) {
|
|
2780
|
+
if (!command.keybinding) continue;
|
|
2781
|
+
if (KEYBINDING_DEFAULTS[command.id] === command.keybinding) continue;
|
|
2782
|
+
log.warn("指令默认键位与共享表不一致:" + command.id + " 目录=" + command.keybinding + " 共享表=" + String(KEYBINDING_DEFAULTS[command.id]));
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2224
2785
|
const MODIFIERS = {
|
|
2225
2786
|
ctrl: "ctrl",
|
|
2226
2787
|
cmd: "meta",
|
|
@@ -2229,8 +2790,9 @@ window.__ModuleLoader__.load({
|
|
|
2229
2790
|
alt: "alt"
|
|
2230
2791
|
};
|
|
2231
2792
|
let current = { ...KEYBINDING_DEFAULTS };
|
|
2232
|
-
|
|
2233
|
-
const listeners$
|
|
2793
|
+
const runtime = /* @__PURE__ */ new Map();
|
|
2794
|
+
const listeners$2 = /* @__PURE__ */ new Set();
|
|
2795
|
+
checkDefaultsDrift();
|
|
2234
2796
|
/**
|
|
2235
2797
|
* 应用设置快照(与默认值合并;未知 id 丢弃;空对象 = 全部默认)。
|
|
2236
2798
|
* 每个命令可含多候选键位(`|` 分隔),任一命中即触发。
|
|
@@ -2242,9 +2804,7 @@ window.__ModuleLoader__.load({
|
|
|
2242
2804
|
...KEYBINDING_DEFAULTS,
|
|
2243
2805
|
...normalizeKeybindings(raw)
|
|
2244
2806
|
};
|
|
2245
|
-
|
|
2246
|
-
for (const id of Object.keys(current)) parsed[id] = parseChords(current[id]);
|
|
2247
|
-
for (const listener of listeners$1) try {
|
|
2807
|
+
for (const listener of listeners$2) try {
|
|
2248
2808
|
listener();
|
|
2249
2809
|
} catch {}
|
|
2250
2810
|
}
|
|
@@ -2255,27 +2815,34 @@ window.__ModuleLoader__.load({
|
|
|
2255
2815
|
* @returns 取消订阅函数
|
|
2256
2816
|
*/
|
|
2257
2817
|
function subscribeKeybindings(listener) {
|
|
2258
|
-
listeners$
|
|
2259
|
-
return () => listeners$
|
|
2818
|
+
listeners$2.add(listener);
|
|
2819
|
+
return () => listeners$2.delete(listener);
|
|
2260
2820
|
}
|
|
2261
2821
|
/**
|
|
2262
|
-
*
|
|
2263
|
-
* @author ddj 2026年08月26号
|
|
2822
|
+
* 当前命令的键位弦(运行时键位优先;未绑定/空 → null)。
|
|
2823
|
+
* @author ddj 2026年08月26号 / 2026年09月10号
|
|
2264
2824
|
* @param id 命令 id
|
|
2265
2825
|
* @returns 键位弦或 null
|
|
2266
2826
|
*/
|
|
2267
2827
|
function chordOf(id) {
|
|
2828
|
+
if (runtime.has(id)) {
|
|
2829
|
+
const chords = runtime.get(id) ?? [];
|
|
2830
|
+
return chords.length ? chords.map(formatChord).join("|") : null;
|
|
2831
|
+
}
|
|
2268
2832
|
const chord = current[id];
|
|
2269
2833
|
return typeof chord === "string" && chord.trim() !== "" ? chord : null;
|
|
2270
2834
|
}
|
|
2271
2835
|
/**
|
|
2272
2836
|
* 当前命令的解析键位集合(未绑定/非法 → 空数组;含多候选)。
|
|
2273
|
-
* @author ddj 2026年08月26号
|
|
2837
|
+
* @author ddj 2026年08月26号 / 2026年09月10号
|
|
2274
2838
|
* @param id 命令 id
|
|
2275
2839
|
* @returns 解析键位数组(可能为空)
|
|
2276
2840
|
*/
|
|
2277
2841
|
function bindingsOf(id) {
|
|
2278
|
-
|
|
2842
|
+
const override = runtime.get(id);
|
|
2843
|
+
if (override) return override;
|
|
2844
|
+
const chord = current[id];
|
|
2845
|
+
return typeof chord === "string" ? parseChords(chord) : [];
|
|
2279
2846
|
}
|
|
2280
2847
|
/**
|
|
2281
2848
|
* 主键规范化:小写字母大写、空格归一为 Space,其余原样(与事件 e.key 对照)。
|
|
@@ -2449,19 +3016,26 @@ window.__ModuleLoader__.load({
|
|
|
2449
3016
|
inputRef.current?.blur?.();
|
|
2450
3017
|
};
|
|
2451
3018
|
react.default.useEffect(() => {
|
|
3019
|
+
const openBox = () => {
|
|
3020
|
+
inputRef.current?.focus?.();
|
|
3021
|
+
setOpen(true);
|
|
3022
|
+
};
|
|
2452
3023
|
const onKey = (e) => {
|
|
2453
3024
|
if (matchEvent(e, bindingsOf("edrv.quickOpen"))) {
|
|
2454
3025
|
e.preventDefault();
|
|
2455
3026
|
e.stopPropagation();
|
|
2456
|
-
|
|
2457
|
-
setOpen(true);
|
|
3027
|
+
openBox();
|
|
2458
3028
|
} else if (e.key === "Escape") {
|
|
2459
3029
|
setOpen(false);
|
|
2460
3030
|
inputRef.current?.blur?.();
|
|
2461
3031
|
}
|
|
2462
3032
|
};
|
|
2463
3033
|
window.addEventListener("keydown", onKey, true);
|
|
2464
|
-
|
|
3034
|
+
window.addEventListener("edrv.command.quickOpen", openBox);
|
|
3035
|
+
return () => {
|
|
3036
|
+
window.removeEventListener("keydown", onKey, true);
|
|
3037
|
+
window.removeEventListener("edrv.command.quickOpen", openBox);
|
|
3038
|
+
};
|
|
2465
3039
|
}, []);
|
|
2466
3040
|
const dirText = (p) => {
|
|
2467
3041
|
const base = String(p).split(/[\\/]/).pop() || String(p);
|
|
@@ -2494,6 +3068,460 @@ window.__ModuleLoader__.load({
|
|
|
2494
3068
|
}), pop);
|
|
2495
3069
|
}
|
|
2496
3070
|
//#endregion
|
|
3071
|
+
//#region src/client/commandGlobals.ts
|
|
3072
|
+
/**
|
|
3073
|
+
* dsh-vscode-mode client — 指令系统的全局挂点名约定。
|
|
3074
|
+
* 单独成模块:装配层(client/index.ts)与读取方(命令栏/第三方)都要用,
|
|
3075
|
+
* 放这里可避免 CommandPalette ↔ index 的循环依赖。
|
|
3076
|
+
*
|
|
3077
|
+
* ⚠️ 必须挂在 window 自身(双下划线 + edrv 前缀,与既有 __edrvExtPoll 同风格)。
|
|
3078
|
+
* 旧实现写 window.dsh.edrvCommands——DSH 从不创建 window.dsh 命名空间(全仓库无赋值),
|
|
3079
|
+
* 该路径恒为死代码并导致命令栏读到空表;不要再改回气泡式挂点。
|
|
3080
|
+
* 作者 ddj 2026年09月10号
|
|
3081
|
+
*/
|
|
3082
|
+
/** 指令注册表的全局挂点名:window[REGISTRY_GLOBAL](第三方注册与调试读取;命令栏首选模块引用)。 */
|
|
3083
|
+
const REGISTRY_GLOBAL = "__edrvCommands__";
|
|
3084
|
+
//#endregion
|
|
3085
|
+
//#region src/client/commandSearch.ts
|
|
3086
|
+
/** 排序用基础序(未声明 order 的命令排在已声明之后)。 */
|
|
3087
|
+
const DEFAULT_ORDER = 100;
|
|
3088
|
+
/** 命中字段权重(数值越小越优先)。 */
|
|
3089
|
+
const FIELD_WEIGHT = {
|
|
3090
|
+
label: 0,
|
|
3091
|
+
id: 1,
|
|
3092
|
+
category: 2
|
|
3093
|
+
};
|
|
3094
|
+
/** 小写化(非字符串按空串处理,容错外部脏数据)。 */
|
|
3095
|
+
function lower(text) {
|
|
3096
|
+
return typeof text === "string" ? text.toLocaleLowerCase("en-US") : "";
|
|
3097
|
+
}
|
|
3098
|
+
/** 建立一条命令的索引文本。 */
|
|
3099
|
+
function indexOf(command) {
|
|
3100
|
+
return {
|
|
3101
|
+
label: lower(command.label),
|
|
3102
|
+
id: lower(command.id),
|
|
3103
|
+
category: lower(command.category)
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
/**
|
|
3107
|
+
* 命令是否命中全部 token;命中时返回最优(最小)字段权重。
|
|
3108
|
+
* @author ddj 2026年09月10号
|
|
3109
|
+
* @param fields 命令索引文本
|
|
3110
|
+
* @param tokens 小写查询 token
|
|
3111
|
+
* @returns 字段权重;未命中返回 null
|
|
3112
|
+
*/
|
|
3113
|
+
function hitWeight(fields, tokens) {
|
|
3114
|
+
let best = null;
|
|
3115
|
+
for (const token of tokens) {
|
|
3116
|
+
let tokenWeight = null;
|
|
3117
|
+
for (const field of [
|
|
3118
|
+
"label",
|
|
3119
|
+
"id",
|
|
3120
|
+
"category"
|
|
3121
|
+
]) {
|
|
3122
|
+
if (!fields[field].includes(token)) continue;
|
|
3123
|
+
const weight = FIELD_WEIGHT[field];
|
|
3124
|
+
if (tokenWeight === null || weight < tokenWeight) tokenWeight = weight;
|
|
3125
|
+
if (weight === 0) break;
|
|
3126
|
+
}
|
|
3127
|
+
if (tokenWeight === null) return null;
|
|
3128
|
+
if (best === null || tokenWeight < best) best = tokenWeight;
|
|
3129
|
+
}
|
|
3130
|
+
return best;
|
|
3131
|
+
}
|
|
3132
|
+
/** 目录序(order 缺省 100;非有限值同样按 100)。 */
|
|
3133
|
+
function orderOf$1(command) {
|
|
3134
|
+
const order = command.order;
|
|
3135
|
+
return typeof order === "number" && Number.isFinite(order) ? order : DEFAULT_ORDER;
|
|
3136
|
+
}
|
|
3137
|
+
/**
|
|
3138
|
+
* 过滤并按相关度排序命令(空查询返回全部,仅按目录序排序)。
|
|
3139
|
+
* @author ddj 2026年09月10号
|
|
3140
|
+
* @param commands 候选命令(调用方通常已按可用性过滤)
|
|
3141
|
+
* @param query 用户输入
|
|
3142
|
+
* @returns 命中命令数组
|
|
3143
|
+
*/
|
|
3144
|
+
function filterCommands(commands, query) {
|
|
3145
|
+
const tokens = lower(query).split(/\s+/).filter(Boolean);
|
|
3146
|
+
const hits = [];
|
|
3147
|
+
for (const command of commands) {
|
|
3148
|
+
const weight = tokens.length ? hitWeight(indexOf(command), tokens) : 0;
|
|
3149
|
+
if (weight !== null) hits.push({
|
|
3150
|
+
command,
|
|
3151
|
+
weight
|
|
3152
|
+
});
|
|
3153
|
+
}
|
|
3154
|
+
return hits.map((hit, index) => ({
|
|
3155
|
+
hit,
|
|
3156
|
+
index
|
|
3157
|
+
})).sort((a, b) => a.hit.weight - b.hit.weight || orderOf$1(a.hit.command) - orderOf$1(b.hit.command) || a.index - b.index).map((entry) => entry.hit.command);
|
|
3158
|
+
}
|
|
3159
|
+
//#endregion
|
|
3160
|
+
//#region src/client/commandRegistry.ts
|
|
3161
|
+
/**
|
|
3162
|
+
* dsh-vscode-mode client — 指令注册表(命令栏与快捷键系统的唯一数据源)。
|
|
3163
|
+
* 镜像 sidebar/registry.ts 的注册表模式:register 返回注销器、list/subscribe 可订阅;
|
|
3164
|
+
* 额外提供 run(id):先做可用性判定再执行,运行体异常一律捕获上报,绝不让命令抛出到事件循环。
|
|
3165
|
+
* 重复 id 按「后注册者生效」覆盖,旧注销器带身份校验(不会误删新实例,见 releaseRegistry)。
|
|
3166
|
+
* 作者 ddj 2026年09月10号
|
|
3167
|
+
*/
|
|
3168
|
+
const registryLog = log.child("commands");
|
|
3169
|
+
/** 校验命令定义(缺 id/label/run 抛 TypeError,早失败优于静默无效)。 */
|
|
3170
|
+
function assertCommand(command) {
|
|
3171
|
+
if (!command || typeof command.id !== "string" || !command.id) throw new TypeError("命令必须提供 id");
|
|
3172
|
+
if (typeof command.label !== "string" || !command.label) throw new TypeError("命令必须提供 label:" + command.id);
|
|
3173
|
+
if (typeof command.run !== "function") throw new TypeError("命令必须提供 run:" + command.id);
|
|
3174
|
+
}
|
|
3175
|
+
/** 目录序(order 缺省 100;非有限值同样按 100)。 */
|
|
3176
|
+
function orderOf(command) {
|
|
3177
|
+
return typeof command.order === "number" && Number.isFinite(command.order) ? command.order : 100;
|
|
3178
|
+
}
|
|
3179
|
+
/** 命令是否可用(available 缺省视为可用;判定异常按不可用处理)。 */
|
|
3180
|
+
function isAvailable$1(command) {
|
|
3181
|
+
if (typeof command.available !== "function") return true;
|
|
3182
|
+
try {
|
|
3183
|
+
return command.available() === true;
|
|
3184
|
+
} catch (error) {
|
|
3185
|
+
registryLog.warn("可用性判定异常(按不可用处理):" + command.id + " · " + String(error));
|
|
3186
|
+
return false;
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
/**
|
|
3190
|
+
* 注销命令(仅当表中仍是本次注册的定义时移除,避免旧实例误删新实例)。
|
|
3191
|
+
* @author ddj 2026年09月10号
|
|
3192
|
+
* @param entries 命令表
|
|
3193
|
+
* @param notify 变化通知
|
|
3194
|
+
* @param command 注册时的定义
|
|
3195
|
+
* @returns 注销函数
|
|
3196
|
+
*/
|
|
3197
|
+
function releaseRegistry(entries, notify, command) {
|
|
3198
|
+
let released = false;
|
|
3199
|
+
return () => {
|
|
3200
|
+
if (released) return;
|
|
3201
|
+
released = true;
|
|
3202
|
+
if (entries.get(command.id) !== command) return;
|
|
3203
|
+
entries.delete(command.id);
|
|
3204
|
+
notify();
|
|
3205
|
+
};
|
|
3206
|
+
}
|
|
3207
|
+
/**
|
|
3208
|
+
* 执行已注册命令(可用性判定 → 运行体 → 异常上报)。
|
|
3209
|
+
* @author ddj 2026年09月10号
|
|
3210
|
+
* @param command 命令定义
|
|
3211
|
+
* @returns 是否真正执行
|
|
3212
|
+
*/
|
|
3213
|
+
function runCommand(command) {
|
|
3214
|
+
if (!isAvailable$1(command)) {
|
|
3215
|
+
registryLog.debug("命令不可用,已跳过:" + command.id);
|
|
3216
|
+
return false;
|
|
3217
|
+
}
|
|
3218
|
+
try {
|
|
3219
|
+
command.run();
|
|
3220
|
+
registryLog.debug("命令已执行:" + command.id);
|
|
3221
|
+
return true;
|
|
3222
|
+
} catch (error) {
|
|
3223
|
+
registryLog.error("命令执行失败:" + command.id + " · " + String(error));
|
|
3224
|
+
return false;
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
/**
|
|
3228
|
+
* 创建指令注册表。
|
|
3229
|
+
* @author ddj 2026年09月10号
|
|
3230
|
+
* @returns 指令注册表
|
|
3231
|
+
*/
|
|
3232
|
+
function createCommandRegistry() {
|
|
3233
|
+
const entries = /* @__PURE__ */ new Map();
|
|
3234
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
3235
|
+
const notify = () => {
|
|
3236
|
+
for (const listener of listeners) try {
|
|
3237
|
+
listener();
|
|
3238
|
+
} catch (error) {
|
|
3239
|
+
registryLog.warn("注册表订阅回调异常:" + String(error));
|
|
3240
|
+
}
|
|
3241
|
+
};
|
|
3242
|
+
const list = () => [...entries.values()].sort((a, b) => orderOf(a) - orderOf(b));
|
|
3243
|
+
const available = () => list().filter(isAvailable$1);
|
|
3244
|
+
return {
|
|
3245
|
+
register(command) {
|
|
3246
|
+
assertCommand(command);
|
|
3247
|
+
if (entries.get(command.id) !== void 0) registryLog.warn("命令 id 重复注册(后者生效):" + command.id);
|
|
3248
|
+
entries.set(command.id, command);
|
|
3249
|
+
notify();
|
|
3250
|
+
return releaseRegistry(entries, notify, command);
|
|
3251
|
+
},
|
|
3252
|
+
has: (id) => entries.has(id),
|
|
3253
|
+
get: (id) => entries.get(id),
|
|
3254
|
+
list,
|
|
3255
|
+
available,
|
|
3256
|
+
match: (query) => filterCommands(available(), query),
|
|
3257
|
+
run(id) {
|
|
3258
|
+
const command = entries.get(id);
|
|
3259
|
+
if (!command) {
|
|
3260
|
+
registryLog.warn("命令未注册:" + id);
|
|
3261
|
+
return false;
|
|
3262
|
+
}
|
|
3263
|
+
return runCommand(command);
|
|
3264
|
+
},
|
|
3265
|
+
subscribe(listener) {
|
|
3266
|
+
listeners.add(listener);
|
|
3267
|
+
return () => listeners.delete(listener);
|
|
3268
|
+
}
|
|
3269
|
+
};
|
|
3270
|
+
}
|
|
3271
|
+
/** 装配期存入的指令注册表(命令栏读取候选的唯一真源)。 */
|
|
3272
|
+
let registryTable = null;
|
|
3273
|
+
/** 命令栏是否展开。 */
|
|
3274
|
+
let opened = false;
|
|
3275
|
+
/** 当前真正渲染浮层的宿主令牌(null = 无宿主)。 */
|
|
3276
|
+
let hostToken = null;
|
|
3277
|
+
/** 注入的命令执行器(commandBridge 装配时写入)。 */
|
|
3278
|
+
let runner = null;
|
|
3279
|
+
/** 打开命令栏前的焦点元素(关闭后归还焦点,键盘操作不中断)。 */
|
|
3280
|
+
let focusBack = null;
|
|
3281
|
+
const listeners$1 = /* @__PURE__ */ new Set();
|
|
3282
|
+
/** 通知全部订阅者(回调异常不影响其他订阅)。 */
|
|
3283
|
+
function notifyPalette() {
|
|
3284
|
+
for (const listener of listeners$1) try {
|
|
3285
|
+
listener();
|
|
3286
|
+
} catch {}
|
|
3287
|
+
}
|
|
3288
|
+
/**
|
|
3289
|
+
* 注入命令执行器(命令栏执行选中命令时调用)。
|
|
3290
|
+
* @author ddj 2026年09月10号
|
|
3291
|
+
* @param next 执行器(返回是否执行成功)
|
|
3292
|
+
*/
|
|
3293
|
+
function setPaletteRunner(next) {
|
|
3294
|
+
runner = next;
|
|
3295
|
+
}
|
|
3296
|
+
/**
|
|
3297
|
+
* 执行命令栏选中的命令。
|
|
3298
|
+
* @author ddj 2026年09月10号
|
|
3299
|
+
* @param id 命令 id
|
|
3300
|
+
* @returns 是否执行成功(执行器未注入/命令不可用/抛异常均为 false)
|
|
3301
|
+
*/
|
|
3302
|
+
function runPaletteCommand(id) {
|
|
3303
|
+
if (!runner) return false;
|
|
3304
|
+
return runner(id) === true;
|
|
3305
|
+
}
|
|
3306
|
+
/**
|
|
3307
|
+
* 存入指令注册表(commandBridge 装配期调用;命令栏据此读取候选)。
|
|
3308
|
+
* @author ddj 2026年09月10号
|
|
3309
|
+
* @param next 指令注册表(null = 卸载)
|
|
3310
|
+
*/
|
|
3311
|
+
function setRegistryRef(next) {
|
|
3312
|
+
registryTable = next;
|
|
3313
|
+
}
|
|
3314
|
+
/**
|
|
3315
|
+
* 读取指令注册表(未装配返回 null;浮层据此回退全局/空表)。
|
|
3316
|
+
* @author ddj 2026年09月10号
|
|
3317
|
+
* @returns 指令注册表或 null
|
|
3318
|
+
*/
|
|
3319
|
+
function registryRef() {
|
|
3320
|
+
return registryTable;
|
|
3321
|
+
}
|
|
3322
|
+
/**
|
|
3323
|
+
* 订阅命令栏开关变化。
|
|
3324
|
+
* @author ddj 2026年09月10号
|
|
3325
|
+
* @param listener 变化回调
|
|
3326
|
+
* @returns 取消订阅函数
|
|
3327
|
+
*/
|
|
3328
|
+
function subscribePalette(listener) {
|
|
3329
|
+
listeners$1.add(listener);
|
|
3330
|
+
return () => listeners$1.delete(listener);
|
|
3331
|
+
}
|
|
3332
|
+
/**
|
|
3333
|
+
* 命令栏是否展开(useSyncExternalStore 快照)。
|
|
3334
|
+
* @author ddj 2026年09月10号
|
|
3335
|
+
* @returns 是否展开
|
|
3336
|
+
*/
|
|
3337
|
+
function isPaletteOpen() {
|
|
3338
|
+
return opened;
|
|
3339
|
+
}
|
|
3340
|
+
/** 记录打开前的焦点元素(只在真正需要归还时记录)。 */
|
|
3341
|
+
function rememberFocus() {
|
|
3342
|
+
if (typeof document === "undefined") return;
|
|
3343
|
+
const active = document.activeElement;
|
|
3344
|
+
focusBack = active instanceof HTMLElement ? active : null;
|
|
3345
|
+
}
|
|
3346
|
+
/**
|
|
3347
|
+
* 打开命令栏(已打开时为空操作;记录当前焦点供关闭后归还)。
|
|
3348
|
+
* @author ddj 2026年09月10号
|
|
3349
|
+
* @param reason 唤起来源(键盘/命令/外部 API)
|
|
3350
|
+
*/
|
|
3351
|
+
function openCommandPalette(reason = "keybinding") {
|
|
3352
|
+
if (opened) return;
|
|
3353
|
+
rememberFocus();
|
|
3354
|
+
opened = true;
|
|
3355
|
+
notifyPalette();
|
|
3356
|
+
}
|
|
3357
|
+
/**
|
|
3358
|
+
* 关闭命令栏并归还焦点。
|
|
3359
|
+
* @author ddj 2026年09月10号
|
|
3360
|
+
*/
|
|
3361
|
+
function closeCommandPalette() {
|
|
3362
|
+
if (!opened) return;
|
|
3363
|
+
opened = false;
|
|
3364
|
+
notifyPalette();
|
|
3365
|
+
const target = focusBack;
|
|
3366
|
+
focusBack = null;
|
|
3367
|
+
if (!target || typeof target.focus !== "function") return;
|
|
3368
|
+
try {
|
|
3369
|
+
target.focus();
|
|
3370
|
+
} catch {}
|
|
3371
|
+
}
|
|
3372
|
+
/**
|
|
3373
|
+
* 认领命令栏宿主(返回 true 表示由本宿主渲染浮层)。
|
|
3374
|
+
* @author ddj 2026年09月10号
|
|
3375
|
+
* @returns 是否为当前宿主
|
|
3376
|
+
*/
|
|
3377
|
+
function claimPaletteHost() {
|
|
3378
|
+
if (hostToken !== null) return false;
|
|
3379
|
+
hostToken = {};
|
|
3380
|
+
return true;
|
|
3381
|
+
}
|
|
3382
|
+
/**
|
|
3383
|
+
* 释放宿主认领(仅持有者可释放,卸载顺序错乱不会顶掉新宿主)。
|
|
3384
|
+
* @author ddj 2026年09月10号
|
|
3385
|
+
* @param token 认领令牌(null 表示本宿主未认领)
|
|
3386
|
+
*/
|
|
3387
|
+
function releasePaletteHost(token) {
|
|
3388
|
+
if (token === null || hostToken !== token) return;
|
|
3389
|
+
hostToken = null;
|
|
3390
|
+
}
|
|
3391
|
+
//#endregion
|
|
3392
|
+
//#region src/client/ui/CommandPalette.ts
|
|
3393
|
+
/**
|
|
3394
|
+
* dsh-vscode-mode client — 命令栏浮层(Ctrl+Shift+P / F1)。
|
|
3395
|
+
* 订阅 commandPaletteStore 的开关状态;候选来自注入的指令注册表(window.dsh.edrvCommands),
|
|
3396
|
+
* 过滤/排序复用 commandSearch 纯函数;↑↓ 选择、Enter 执行、Esc 或点击遮罩关闭。
|
|
3397
|
+
* 浮层经 createPortal 渲染到 body,故不受编辑区三种形态(官方侧栏/better-sidebar/中央页签)影响;
|
|
3398
|
+
* 宿主单实例由 store 的 claimPaletteHost 保证(另一个宿主自动返回 null)。
|
|
3399
|
+
* 作者 ddj 2026年09月10号
|
|
3400
|
+
*/
|
|
3401
|
+
/** 空注册表(宿主未注入时的安全降级:命令栏可开但无候选)。 */
|
|
3402
|
+
let fallbackRegistry = null;
|
|
3403
|
+
/**
|
|
3404
|
+
* 读取指令注册表:① 装配期存入的模块引用(首选,不依赖全局)
|
|
3405
|
+
* ② window[REGISTRY_GLOBAL] 镜像(第三方/调试场景)③ 空表降级(仅装配异常时)。
|
|
3406
|
+
* @author ddj 2026年09月10号
|
|
3407
|
+
* @returns 指令注册表
|
|
3408
|
+
*/
|
|
3409
|
+
function useRegistry() {
|
|
3410
|
+
const injected = registryRef();
|
|
3411
|
+
if (injected) return injected;
|
|
3412
|
+
const mirrored = typeof window === "undefined" ? void 0 : window[REGISTRY_GLOBAL];
|
|
3413
|
+
if (mirrored) return mirrored;
|
|
3414
|
+
if (!fallbackRegistry) fallbackRegistry = createCommandRegistry();
|
|
3415
|
+
return fallbackRegistry;
|
|
3416
|
+
}
|
|
3417
|
+
/**
|
|
3418
|
+
* 组装候选行(先按可用性过滤,再按查询排序)。
|
|
3419
|
+
* @author ddj 2026年09月10号
|
|
3420
|
+
* @param registry 指令注册表
|
|
3421
|
+
* @param query 用户输入
|
|
3422
|
+
* @returns 候选行
|
|
3423
|
+
*/
|
|
3424
|
+
function rowsOf(registry, query) {
|
|
3425
|
+
return filterCommands(registry.available(), query).map((command) => ({
|
|
3426
|
+
command,
|
|
3427
|
+
chord: chordOf(command.id)
|
|
3428
|
+
}));
|
|
3429
|
+
}
|
|
3430
|
+
/**
|
|
3431
|
+
* 命令栏浮层(未展开/非宿主时渲染 null)。
|
|
3432
|
+
* @author ddj 2026年09月10号
|
|
3433
|
+
* @returns 浮层 React 元素或 null
|
|
3434
|
+
*/
|
|
3435
|
+
function CommandPalette() {
|
|
3436
|
+
const open = react.default.useSyncExternalStore(subscribePalette, isPaletteOpen);
|
|
3437
|
+
const registry = useRegistry();
|
|
3438
|
+
const [token] = react.default.useState(() => claimPaletteHost() ? {} : null);
|
|
3439
|
+
const [query, setQuery] = react.default.useState("");
|
|
3440
|
+
const [selected, setSelected] = react.default.useState(0);
|
|
3441
|
+
const [notice, setNotice] = react.default.useState("");
|
|
3442
|
+
const inputRef = react.default.useRef(null);
|
|
3443
|
+
const host = token !== null;
|
|
3444
|
+
react.default.useEffect(() => () => releasePaletteHost(token), [token]);
|
|
3445
|
+
react.default.useEffect(() => {
|
|
3446
|
+
if (!open) return void 0;
|
|
3447
|
+
setQuery("");
|
|
3448
|
+
setSelected(0);
|
|
3449
|
+
setNotice("");
|
|
3450
|
+
const timer = setTimeout(() => inputRef.current?.focus(), 0);
|
|
3451
|
+
return () => clearTimeout(timer);
|
|
3452
|
+
}, [open]);
|
|
3453
|
+
const rows = react.default.useMemo(() => open && host ? rowsOf(registry, query) : [], [
|
|
3454
|
+
open,
|
|
3455
|
+
host,
|
|
3456
|
+
registry,
|
|
3457
|
+
query
|
|
3458
|
+
]);
|
|
3459
|
+
const active = rows.length ? Math.min(selected, rows.length - 1) : 0;
|
|
3460
|
+
react.default.useEffect(() => {
|
|
3461
|
+
if (!open) return;
|
|
3462
|
+
const node = document.querySelector(".edrv-palette-row.edrv-palette-sel");
|
|
3463
|
+
const scroll = node?.scrollIntoView;
|
|
3464
|
+
if (typeof scroll === "function") scroll.call(node, { block: "nearest" });
|
|
3465
|
+
}, [
|
|
3466
|
+
open,
|
|
3467
|
+
active,
|
|
3468
|
+
rows.length
|
|
3469
|
+
]);
|
|
3470
|
+
/** 执行候选行(执行器内部会做可用性校验与异常上报)。 */
|
|
3471
|
+
const execute = (row) => {
|
|
3472
|
+
if (!row) return;
|
|
3473
|
+
closeCommandPalette();
|
|
3474
|
+
if (!runPaletteCommand(row.command.id)) setNotice("命令未执行:" + row.command.label);
|
|
3475
|
+
};
|
|
3476
|
+
const onKeyDown = (event) => {
|
|
3477
|
+
event.stopPropagation();
|
|
3478
|
+
if (event.key === "Escape") {
|
|
3479
|
+
event.preventDefault();
|
|
3480
|
+
closeCommandPalette();
|
|
3481
|
+
return;
|
|
3482
|
+
}
|
|
3483
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
3484
|
+
event.preventDefault();
|
|
3485
|
+
if (!rows.length) return;
|
|
3486
|
+
const step = event.key === "ArrowDown" ? 1 : rows.length - 1;
|
|
3487
|
+
setSelected((value) => (Math.min(value, rows.length - 1) + step) % rows.length);
|
|
3488
|
+
return;
|
|
3489
|
+
}
|
|
3490
|
+
if (event.key === "Enter") {
|
|
3491
|
+
event.preventDefault();
|
|
3492
|
+
execute(rows[active]);
|
|
3493
|
+
}
|
|
3494
|
+
};
|
|
3495
|
+
if (!open || !host || typeof document === "undefined") return null;
|
|
3496
|
+
const list = rows.length ? rows.map((row, index) => react.default.createElement("div", {
|
|
3497
|
+
key: row.command.id,
|
|
3498
|
+
className: "edrv-palette-row" + (index === active ? " edrv-palette-sel" : ""),
|
|
3499
|
+
onMouseEnter: () => setSelected(index),
|
|
3500
|
+
onClick: () => execute(row)
|
|
3501
|
+
}, react.default.createElement("span", { className: "edrv-palette-label" }, row.command.label), react.default.createElement("span", { className: "edrv-palette-cat" }, row.command.category), row.chord ? react.default.createElement("kbd", { className: "edrv-palette-key" }, row.chord) : null)) : react.default.createElement("div", { className: "edrv-palette-empty" }, "无匹配命令");
|
|
3502
|
+
const panel = react.default.createElement("div", {
|
|
3503
|
+
className: "edrv-palette",
|
|
3504
|
+
"data-edrv-palette": "1",
|
|
3505
|
+
onKeyDown
|
|
3506
|
+
}, react.default.createElement("input", {
|
|
3507
|
+
ref: inputRef,
|
|
3508
|
+
className: "edrv-palette-input",
|
|
3509
|
+
value: query,
|
|
3510
|
+
placeholder: "输入命令名称…(↑↓ 选择 · Enter 执行 · Esc 关闭)",
|
|
3511
|
+
spellCheck: false,
|
|
3512
|
+
onChange: (event) => {
|
|
3513
|
+
setQuery(event.target.value);
|
|
3514
|
+
setSelected(0);
|
|
3515
|
+
setNotice("");
|
|
3516
|
+
}
|
|
3517
|
+
}), react.default.createElement("div", { className: "edrv-palette-list" }, list), notice ? react.default.createElement("div", { className: "edrv-palette-notice" }, notice) : null);
|
|
3518
|
+
const overlay = react.default.createElement("div", { "data-edrv-view": "1" }, react.default.createElement("div", {
|
|
3519
|
+
className: "edrv-palette-mask",
|
|
3520
|
+
onClick: () => closeCommandPalette()
|
|
3521
|
+
}), panel);
|
|
3522
|
+
return (0, react_dom.createPortal)(overlay, document.body);
|
|
3523
|
+
}
|
|
3524
|
+
//#endregion
|
|
2497
3525
|
//#region src/client/ui/shared.ts
|
|
2498
3526
|
/**
|
|
2499
3527
|
* dsh-vscode-mode client — UI 共用小工具(按钮样式/状态文案/等宽字体)。
|
|
@@ -2768,6 +3796,20 @@ window.__ModuleLoader__.load({
|
|
|
2768
3796
|
/** 隐藏态拖出面板区的触发阈值(px,拖过即展开并继续实时调宽)。 */
|
|
2769
3797
|
const PULL_OUT_THRESHOLD = 12;
|
|
2770
3798
|
/**
|
|
3799
|
+
* 活动栏图标内容:面板可给官方图标组件(跟随官方主题),也可给文本/emoji(回落)。
|
|
3800
|
+
* @author ddj 2026年09月10号
|
|
3801
|
+
* @param icon 面板定义的 icon 字段(组件或文本)
|
|
3802
|
+
* @returns 图标元素或文本;组件渲染异常返回 null
|
|
3803
|
+
*/
|
|
3804
|
+
function railIconEl(icon) {
|
|
3805
|
+
if (typeof icon !== "function") return icon;
|
|
3806
|
+
try {
|
|
3807
|
+
return react.default.createElement(icon, { size: 16 });
|
|
3808
|
+
} catch (error) {
|
|
3809
|
+
return null;
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
/**
|
|
2771
3813
|
* 侧边栏容器。
|
|
2772
3814
|
* @param props.registry 面板注册表(list() 提供面板顺序)
|
|
2773
3815
|
* @param props.ctx 面板共享上下文
|
|
@@ -2856,7 +3898,7 @@ window.__ModuleLoader__.load({
|
|
|
2856
3898
|
onActive(p.id);
|
|
2857
3899
|
if (!visible && typeof onShow === "function") onShow();
|
|
2858
3900
|
}
|
|
2859
|
-
}, react.default.createElement("span", { className: "edrv-rail-icon" }, p.icon), count > 0 ? react.default.createElement("span", { className: "edrv-rail-badge" }, String(count)) : null);
|
|
3901
|
+
}, react.default.createElement("span", { className: "edrv-rail-icon" }, railIconEl(p.icon)), count > 0 ? react.default.createElement("span", { className: "edrv-rail-badge" }, String(count)) : null);
|
|
2860
3902
|
})), react.default.createElement("button", {
|
|
2861
3903
|
className: "edrv-rail-btn",
|
|
2862
3904
|
title: (visible ? "隐藏面板区" : "展开面板区") + " (Ctrl+B)",
|
|
@@ -4658,6 +5700,12 @@ window.__ModuleLoader__.load({
|
|
|
4658
5700
|
const navCursorTimerRef = react.default.useRef(null);
|
|
4659
5701
|
const navBackRef = react.default.useRef(null);
|
|
4660
5702
|
const navForwardRef = react.default.useRef(null);
|
|
5703
|
+
const cycleTabRef = react.default.useRef(null);
|
|
5704
|
+
const rowNavColRef = react.default.useRef(null);
|
|
5705
|
+
const rowNavMoveRef = react.default.useRef(false);
|
|
5706
|
+
const activeRef = react.default.useRef(null);
|
|
5707
|
+
activeRef.current = active;
|
|
5708
|
+
const tabsHostRef = react.default.useRef(null);
|
|
4661
5709
|
const [navTick, setNavTick] = react.default.useState(0);
|
|
4662
5710
|
const hoverRegionsRef = react.default.useRef([]);
|
|
4663
5711
|
const lineRegionMapRef = react.default.useRef(/* @__PURE__ */ new Map());
|
|
@@ -4854,6 +5902,22 @@ window.__ModuleLoader__.load({
|
|
|
4854
5902
|
});
|
|
4855
5903
|
};
|
|
4856
5904
|
/**
|
|
5905
|
+
* 在已打开页签间循环切换(文件分页归编辑器自带页签栏)。
|
|
5906
|
+
* @author ddj 2026年09月10号
|
|
5907
|
+
* @param step 步进(+1 下一个 / -1 上一个)
|
|
5908
|
+
*/
|
|
5909
|
+
const cycleTab = (step) => {
|
|
5910
|
+
if (tabs.length < 2) return;
|
|
5911
|
+
const idx = tabs.findIndex((t) => t.path === active);
|
|
5912
|
+
const next = tabs[(idx + step + tabs.length) % tabs.length];
|
|
5913
|
+
if (!next || next.path === active) return;
|
|
5914
|
+
flushSave();
|
|
5915
|
+
saveViewState(active);
|
|
5916
|
+
recordNav();
|
|
5917
|
+
setActive(next.path);
|
|
5918
|
+
};
|
|
5919
|
+
cycleTabRef.current = cycleTab;
|
|
5920
|
+
/**
|
|
4857
5921
|
* 轮询去重:前后记录内容一致时返回原引用,React 跳过重渲染,
|
|
4858
5922
|
* 阻断 5s 轮询 → memo 链 → diff 重渲染/日志 的空转。
|
|
4859
5923
|
* @author ddj 2026年08月26号
|
|
@@ -5347,6 +6411,104 @@ window.__ModuleLoader__.load({
|
|
|
5347
6411
|
window.addEventListener("keydown", onKey, true);
|
|
5348
6412
|
return () => window.removeEventListener("keydown", onKey, true);
|
|
5349
6413
|
}, []);
|
|
6414
|
+
react.default.useEffect(() => {
|
|
6415
|
+
const onKey = (e) => {
|
|
6416
|
+
const step = matchEvent(e, bindingsOf("edrv.nextTab")) ? 1 : matchEvent(e, bindingsOf("edrv.prevTab")) ? -1 : 0;
|
|
6417
|
+
if (step === 0) return;
|
|
6418
|
+
e.preventDefault();
|
|
6419
|
+
e.stopPropagation();
|
|
6420
|
+
if (cycleTabRef.current) cycleTabRef.current(step);
|
|
6421
|
+
};
|
|
6422
|
+
window.addEventListener("keydown", onKey, true);
|
|
6423
|
+
return () => window.removeEventListener("keydown", onKey, true);
|
|
6424
|
+
}, []);
|
|
6425
|
+
/**
|
|
6426
|
+
* 指令系统接线:命令栏/键位/第三方派发的 `edrv.command.*` 事件落到编辑器动作。
|
|
6427
|
+
* 全部动作经最新闭包执行(与窗口级键位监听同源);事件名逐条字面书写,
|
|
6428
|
+
* 便于与指令目录(ui/commandCatalog)静态对照(tests/commands.test.ts 有断言)。
|
|
6429
|
+
* @author ddj 2026年09月10号
|
|
6430
|
+
*/
|
|
6431
|
+
react.default.useEffect(() => {
|
|
6432
|
+
/** 整行上下移动:Monaco 内置 cursorUp/Down 会丢列位,故按期望列自行定位(连续移动保持同列)。 */
|
|
6433
|
+
const moveRow = (step) => {
|
|
6434
|
+
const ed = editorRef.current;
|
|
6435
|
+
const model = ed?.getModel?.();
|
|
6436
|
+
const pos = ed?.getPosition?.();
|
|
6437
|
+
if (!ed || !model || !pos) return;
|
|
6438
|
+
const col = rowNavColRef.current ?? pos.column;
|
|
6439
|
+
const line = Math.max(1, Math.min(model.getLineCount(), pos.lineNumber + step));
|
|
6440
|
+
if (line === pos.lineNumber) return;
|
|
6441
|
+
const maxCol = model.getLineMaxColumn(line);
|
|
6442
|
+
rowNavColRef.current = col;
|
|
6443
|
+
rowNavMoveRef.current = true;
|
|
6444
|
+
ed.setPosition({
|
|
6445
|
+
lineNumber: line,
|
|
6446
|
+
column: Math.max(1, Math.min(col, maxCol))
|
|
6447
|
+
});
|
|
6448
|
+
ed.revealLineInCenterIfOutsideViewport?.(line);
|
|
6449
|
+
};
|
|
6450
|
+
const handlers = [
|
|
6451
|
+
["edrv.command.save", () => {
|
|
6452
|
+
if (editorRef.current?.getModel?.()) {
|
|
6453
|
+
flushSave();
|
|
6454
|
+
doSaveRef.current?.(false);
|
|
6455
|
+
}
|
|
6456
|
+
}],
|
|
6457
|
+
["edrv.command.toggleSidebar", () => setSidebarOn((v) => !v)],
|
|
6458
|
+
["edrv.command.searchInFiles", () => {
|
|
6459
|
+
setSidebarOn(true);
|
|
6460
|
+
setActivePanel("search");
|
|
6461
|
+
setTimeout(() => window.dispatchEvent(new CustomEvent("edrv:search-focus")), 0);
|
|
6462
|
+
}],
|
|
6463
|
+
["edrv.command.navigateBack", () => navBackRef.current?.()],
|
|
6464
|
+
["edrv.command.navigateForward", () => navForwardRef.current?.()],
|
|
6465
|
+
["edrv.command.nextTab", () => cycleTabRef.current?.(1)],
|
|
6466
|
+
["edrv.command.prevTab", () => cycleTabRef.current?.(-1)],
|
|
6467
|
+
["edrv.command.nextEditorRow", () => moveRow(1)],
|
|
6468
|
+
["edrv.command.prevEditorRow", () => moveRow(-1)],
|
|
6469
|
+
["edrv.command.goToDefinition", () => {
|
|
6470
|
+
const ed = editorRef.current;
|
|
6471
|
+
if (ed) runGoToDefinition(ed);
|
|
6472
|
+
}],
|
|
6473
|
+
["edrv.command.findReferences", () => {
|
|
6474
|
+
const ed = editorRef.current;
|
|
6475
|
+
if (ed) runFindReferences(ed);
|
|
6476
|
+
}],
|
|
6477
|
+
["edrv.command.triggerAi", () => editorRef.current?.trigger?.("edrv-ai", "editor.action.inlineSuggest.trigger", null)],
|
|
6478
|
+
["edrv.command.openInExplorer", () => {
|
|
6479
|
+
const path = activeRef.current;
|
|
6480
|
+
if (!path) {
|
|
6481
|
+
setStatus("无活动文件");
|
|
6482
|
+
return;
|
|
6483
|
+
}
|
|
6484
|
+
menuHandlersRef.current?.openInExplorer?.(path);
|
|
6485
|
+
}]
|
|
6486
|
+
];
|
|
6487
|
+
const byName = new Map(handlers);
|
|
6488
|
+
const onCommand = (event) => {
|
|
6489
|
+
const action = byName.get(event.type);
|
|
6490
|
+
if (!action) return;
|
|
6491
|
+
try {
|
|
6492
|
+
action();
|
|
6493
|
+
} catch (error) {
|
|
6494
|
+
dbg(sessionId, "指令 " + event.type + " 失败:" + String(error));
|
|
6495
|
+
}
|
|
6496
|
+
};
|
|
6497
|
+
for (const [eventName] of handlers) window.addEventListener(eventName, onCommand);
|
|
6498
|
+
return () => {
|
|
6499
|
+
for (const [eventName] of handlers) window.removeEventListener(eventName, onCommand);
|
|
6500
|
+
};
|
|
6501
|
+
}, [sessionId]);
|
|
6502
|
+
react.default.useEffect(() => {
|
|
6503
|
+
const host = tabsHostRef.current;
|
|
6504
|
+
if (!host) return;
|
|
6505
|
+
const el = host.querySelector?.(".edrv-tab-active");
|
|
6506
|
+
if (!el) return;
|
|
6507
|
+
const left = el.offsetLeft;
|
|
6508
|
+
const right = left + el.offsetWidth;
|
|
6509
|
+
if (left < host.scrollLeft) host.scrollLeft = left;
|
|
6510
|
+
else if (right > host.scrollLeft + host.clientWidth) host.scrollLeft = right - host.clientWidth;
|
|
6511
|
+
}, [active, tabs.length]);
|
|
5350
6512
|
react.default.useEffect(() => {
|
|
5351
6513
|
const onPointer = (e) => {
|
|
5352
6514
|
if (e?.button !== 3 && e?.button !== 4) return;
|
|
@@ -5424,16 +6586,20 @@ window.__ModuleLoader__.load({
|
|
|
5424
6586
|
react.default.useEffect(() => {
|
|
5425
6587
|
if (!monaco) return;
|
|
5426
6588
|
registerThemes(monaco);
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
const
|
|
5431
|
-
if (
|
|
5432
|
-
query.addEventListener("change",
|
|
5433
|
-
|
|
5434
|
-
}
|
|
5435
|
-
|
|
5436
|
-
|
|
6589
|
+
const apply = () => applyOfficial(monaco);
|
|
6590
|
+
apply();
|
|
6591
|
+
window.addEventListener("edrv:theme-change", apply);
|
|
6592
|
+
const query = typeof window.matchMedia === "function" ? window.matchMedia("(prefers-color-scheme: dark)") : null;
|
|
6593
|
+
if (query) {
|
|
6594
|
+
if (typeof query.addEventListener === "function") query.addEventListener("change", apply);
|
|
6595
|
+
else query.addListener(apply);
|
|
6596
|
+
}
|
|
6597
|
+
return () => {
|
|
6598
|
+
window.removeEventListener("edrv:theme-change", apply);
|
|
6599
|
+
if (!query) return;
|
|
6600
|
+
if (typeof query.removeEventListener === "function") query.removeEventListener("change", apply);
|
|
6601
|
+
else query.removeListener(apply);
|
|
6602
|
+
};
|
|
5437
6603
|
}, [monaco]);
|
|
5438
6604
|
monacoRef.current = monaco;
|
|
5439
6605
|
const getModel = (path, text) => {
|
|
@@ -5620,6 +6786,8 @@ window.__ModuleLoader__.load({
|
|
|
5620
6786
|
});
|
|
5621
6787
|
ed.onDidChangeCursorPosition((e) => {
|
|
5622
6788
|
setCursor("Ln " + e.position.lineNumber + ", Col " + e.position.column);
|
|
6789
|
+
if (rowNavMoveRef.current) rowNavMoveRef.current = false;
|
|
6790
|
+
else rowNavColRef.current = null;
|
|
5623
6791
|
if (navCursorTimerRef.current) clearTimeout(navCursorTimerRef.current);
|
|
5624
6792
|
navCursorTimerRef.current = setTimeout(() => {
|
|
5625
6793
|
navCursorTimerRef.current = null;
|
|
@@ -5696,6 +6864,24 @@ window.__ModuleLoader__.load({
|
|
|
5696
6864
|
ed.addCommand(m.KeyCode.F12, () => {
|
|
5697
6865
|
runGoToDefinition(ed);
|
|
5698
6866
|
});
|
|
6867
|
+
ed.addAction({
|
|
6868
|
+
id: "edrv.prevEditorRow",
|
|
6869
|
+
label: "上一编辑行",
|
|
6870
|
+
contextMenuGroupId: "1_edrv",
|
|
6871
|
+
run: () => window.dispatchEvent(new CustomEvent("edrv.command.prevEditorRow"))
|
|
6872
|
+
});
|
|
6873
|
+
ed.addAction({
|
|
6874
|
+
id: "edrv.nextEditorRow",
|
|
6875
|
+
label: "下一编辑行",
|
|
6876
|
+
contextMenuGroupId: "1_edrv",
|
|
6877
|
+
run: () => window.dispatchEvent(new CustomEvent("edrv.command.nextEditorRow"))
|
|
6878
|
+
});
|
|
6879
|
+
ed.addAction({
|
|
6880
|
+
id: "edrv.showCommands",
|
|
6881
|
+
label: "显示所有命令",
|
|
6882
|
+
contextMenuGroupId: "1_edrv",
|
|
6883
|
+
run: () => window.dispatchEvent(new CustomEvent("edrv.command.showCommands"))
|
|
6884
|
+
});
|
|
5699
6885
|
bindLspEditor(ed);
|
|
5700
6886
|
bindLspUnderline(ed, m);
|
|
5701
6887
|
trackAiEditor(ed);
|
|
@@ -6168,6 +7354,7 @@ window.__ModuleLoader__.load({
|
|
|
6168
7354
|
};
|
|
6169
7355
|
const tabsEl = react.default.createElement("div", {
|
|
6170
7356
|
className: "edrv-tabs",
|
|
7357
|
+
ref: tabsHostRef,
|
|
6171
7358
|
style: {
|
|
6172
7359
|
flex: "1 1 auto",
|
|
6173
7360
|
minWidth: 0
|
|
@@ -6344,7 +7531,7 @@ window.__ModuleLoader__.load({
|
|
|
6344
7531
|
});
|
|
6345
7532
|
}
|
|
6346
7533
|
}, "重试"));
|
|
6347
|
-
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 修改文件后顶部会出现差异角标"));
|
|
7534
|
+
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") + ") 打开工作区文件;" + (chordOf("edrv.showCommands") ?? "Ctrl+Shift+P") + " 打开命令栏;agent 修改文件后顶部会出现差异角标"));
|
|
6348
7535
|
else if (content === null && loadError) body = loadingBody("文件加载失败:" + loadError, 0, () => {
|
|
6349
7536
|
setLoadError(null);
|
|
6350
7537
|
setContent(null);
|
|
@@ -6569,20 +7756,24 @@ window.__ModuleLoader__.load({
|
|
|
6569
7756
|
background: "var(--dsw-alias-bg-base,transparent)",
|
|
6570
7757
|
overflow: "hidden"
|
|
6571
7758
|
};
|
|
7759
|
+
const paletteEl = react.default.createElement(CommandPalette, {
|
|
7760
|
+
key: "edrv-palette",
|
|
7761
|
+
sessionId
|
|
7762
|
+
});
|
|
6572
7763
|
return layout === "side" ? react.default.createElement("div", {
|
|
6573
7764
|
ref: viewRootRef,
|
|
6574
7765
|
"data-edrv-view": "1",
|
|
6575
7766
|
"data-edrv-layout": "side",
|
|
6576
7767
|
className: "edrv-view-side",
|
|
6577
7768
|
style: Object.assign({}, baseStyle, { height: "100%" })
|
|
6578
|
-
}, editorRow, menuBackdrop, tabMenuEl) : react.default.createElement("div", {
|
|
7769
|
+
}, editorRow, menuBackdrop, tabMenuEl, paletteEl) : react.default.createElement("div", {
|
|
6579
7770
|
ref: viewRootRef,
|
|
6580
7771
|
"data-edrv-view": "1",
|
|
6581
7772
|
style: Object.assign({}, baseStyle, {
|
|
6582
7773
|
height: "var(--edrv-editor-height, 100%)",
|
|
6583
7774
|
maxHeight: "var(--edrv-editor-height, 100%)"
|
|
6584
7775
|
})
|
|
6585
|
-
}, editorRow, menuBackdrop, tabMenuEl);
|
|
7776
|
+
}, editorRow, menuBackdrop, tabMenuEl, paletteEl);
|
|
6586
7777
|
}
|
|
6587
7778
|
//#endregion
|
|
6588
7779
|
//#region src/client/ui/DiffBadge.ts
|
|
@@ -6935,11 +8126,15 @@ window.__ModuleLoader__.load({
|
|
|
6935
8126
|
"data-edrv-diff-dock-shell": "1"
|
|
6936
8127
|
}, content);
|
|
6937
8128
|
const editorSnapshot = readDiffDock(sessionId);
|
|
8129
|
+
const palette = react.default.createElement(CommandPalette, {
|
|
8130
|
+
key: "edrv-palette",
|
|
8131
|
+
sessionId
|
|
8132
|
+
});
|
|
6938
8133
|
if (editorSnapshot) {
|
|
6939
|
-
if (editorSnapshot.mode === "editor-empty" && !editorSnapshot.fileTotal) return
|
|
6940
|
-
return renderDock(react.default.createElement(DiffBox, Object.assign({}, editorSnapshot, { dock: true })), sessionId);
|
|
8134
|
+
if (editorSnapshot.mode === "editor-empty" && !editorSnapshot.fileTotal) return palette;
|
|
8135
|
+
return react.default.createElement(react.default.Fragment, null, renderDock(react.default.createElement(DiffBox, Object.assign({}, editorSnapshot, { dock: true })), sessionId), palette);
|
|
6941
8136
|
}
|
|
6942
|
-
if (!sessionId || !summary?.pendingFiles?.length) return
|
|
8137
|
+
if (!sessionId || !summary?.pendingFiles?.length) return palette;
|
|
6943
8138
|
const paths = summary.pendingFiles.map((file) => file.path);
|
|
6944
8139
|
const click = () => {
|
|
6945
8140
|
const current = nextIndexBySession.get(sessionId) ?? 0;
|
|
@@ -6947,12 +8142,12 @@ window.__ModuleLoader__.load({
|
|
|
6947
8142
|
nextIndexBySession.set(sessionId, next.index);
|
|
6948
8143
|
if (next.path) openDiffView(next.path);
|
|
6949
8144
|
};
|
|
6950
|
-
return renderDock(react.default.createElement(DiffBox, {
|
|
8145
|
+
return react.default.createElement(react.default.Fragment, null, renderDock(react.default.createElement(DiffBox, {
|
|
6951
8146
|
mode: "chat",
|
|
6952
8147
|
dock: true,
|
|
6953
8148
|
fileTotal: paths.length,
|
|
6954
8149
|
onOpenNextFile: click
|
|
6955
|
-
}), sessionId || "chat");
|
|
8150
|
+
}), sessionId || "chat"), palette);
|
|
6956
8151
|
}
|
|
6957
8152
|
//#endregion
|
|
6958
8153
|
//#region \0dsh-css:/home/runner/work/DSH_VsCodeMode/DSH_VsCodeMode/src/client/styles/mcp.css.mjs
|
|
@@ -7132,6 +8327,17 @@ window.__ModuleLoader__.load({
|
|
|
7132
8327
|
focusDiff: value?.focusDiff === true
|
|
7133
8328
|
};
|
|
7134
8329
|
}
|
|
8330
|
+
/**
|
|
8331
|
+
* 从官方导航参数安全解析行号(缺字段/坏类型/非正数一律视为未指定)。
|
|
8332
|
+
* @author ddj 2026年09月10号
|
|
8333
|
+
* @param params openTab/openResource 传入的 params(正文经 navigation.params 读回)
|
|
8334
|
+
* @returns 正整数行号,或 undefined
|
|
8335
|
+
*/
|
|
8336
|
+
function resolveNavLine(params) {
|
|
8337
|
+
const line = (params ?? {})?.line;
|
|
8338
|
+
if (typeof line !== "number" || !Number.isFinite(line) || line <= 0) return void 0;
|
|
8339
|
+
return Math.floor(line);
|
|
8340
|
+
}
|
|
7135
8341
|
/** 逐段 component 解码(失败返回 null,对齐官方 parseFileAddress 的容错语义)。 */
|
|
7136
8342
|
function decodeSegment(raw) {
|
|
7137
8343
|
if (raw === void 0) return null;
|
|
@@ -7211,6 +8417,27 @@ window.__ModuleLoader__.load({
|
|
|
7211
8417
|
return "dsh-resource://file/session/" + encodeSegment(sessionId) + "/" + relative.split("/").map(encodeSegment).join("/");
|
|
7212
8418
|
}
|
|
7213
8419
|
/**
|
|
8420
|
+
* 把 file 打开请求转发进单一编辑器页签(文件分页由编辑器自带页签栏接管,
|
|
8421
|
+
* 官方侧栏不再按文件分裂出多套编辑器实例)。
|
|
8422
|
+
* @author ddj 2026年09月10号
|
|
8423
|
+
* @param service 官方 sidebarRight 服务(缺失/缺 openTab 返回 false)
|
|
8424
|
+
* @param path 目标文件路径(空值返回 false)
|
|
8425
|
+
* @param line 可选行号(透传给编辑器页签导航参数)
|
|
8426
|
+
* @returns 转发是否成功(openTab 抛错返回 false,由调用方决定重试或兜底)
|
|
8427
|
+
*/
|
|
8428
|
+
function forwardToEditor(service, path, line) {
|
|
8429
|
+
if (!service || typeof service.openTab !== "function" || !path) return false;
|
|
8430
|
+
const params = { openPath: path };
|
|
8431
|
+
if (line !== void 0) params.line = line;
|
|
8432
|
+
try {
|
|
8433
|
+
service.openTab(OFFICIAL_TAB_KIND, { params });
|
|
8434
|
+
return true;
|
|
8435
|
+
} catch (error) {
|
|
8436
|
+
log.warn("file 认领转发进编辑器页签失败(" + String(error) + ")");
|
|
8437
|
+
return false;
|
|
8438
|
+
}
|
|
8439
|
+
}
|
|
8440
|
+
/**
|
|
7214
8441
|
* 注册官方 Tab 正文(keyed slot,key=定义 id;slots.inject 等待 rightbar seat 声明)。
|
|
7215
8442
|
* @author ddj 2026年09月09号
|
|
7216
8443
|
* @param slots slots 服务
|
|
@@ -7696,7 +8923,7 @@ window.__ModuleLoader__.load({
|
|
|
7696
8923
|
onClick: () => resetRow(command.id)
|
|
7697
8924
|
}, "恢复"))));
|
|
7698
8925
|
});
|
|
7699
|
-
return react.default.createElement("section", { className: "vsm-general-page" }, react.default.createElement("h2", null, "快捷键"), react.default.createElement("p", null, "
|
|
8926
|
+
return react.default.createElement("section", { className: "vsm-general-page" }, react.default.createElement("h2", null, "快捷键"), react.default.createElement("p", null, "配置编辑器视图的快捷键(命令栏 / 保存 / 快速打开 / 侧边栏 / 搜索 / 后退前进 / 页签与编辑行导航)。修改即时写入配置,重新聚焦编辑器后生效;命令栏(默认 Ctrl+Shift+P 或 F1)可搜索并执行下列全部命令。"), unavailable && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, "设置服务暂不可用,当前使用默认键位。"), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), message && react.default.createElement("div", { className: "vsm-kb-message" }, message), react.default.createElement("section", { className: "vsm-panel" }, react.default.createElement("h3", { className: "vsm-panel-title" }, "编辑器键位"), react.default.createElement("div", { className: "vsm-panel-body" }, react.default.createElement("div", { className: "vsm-kb-list" }, rows)), react.default.createElement("div", { className: "vsm-panel-body vsm-kb-actionsbar" }, react.default.createElement("span", { className: "vsm-kb-dirty" }, dirty ? "有未保存的修改" : ""), react.default.createElement("button", {
|
|
7700
8927
|
className: "vsm-primary vsm-small",
|
|
7701
8928
|
disabled: unavailable || notReady || busy || snapshot?.writable === false || !dirty,
|
|
7702
8929
|
onClick: save
|
|
@@ -10089,13 +11316,12 @@ window.__ModuleLoader__.load({
|
|
|
10089
11316
|
//#endregion
|
|
10090
11317
|
//#region src/client/ui/OfficialSideTab.ts
|
|
10091
11318
|
/**
|
|
10092
|
-
* dsh-vscode-mode client — OfficialSideTab:DSH 官方右侧 Sidebar
|
|
10093
|
-
*
|
|
10094
|
-
*
|
|
10095
|
-
*
|
|
10096
|
-
* EditorView 打开流;按会话 key 保证切会话干净重建。
|
|
11319
|
+
* dsh-vscode-mode client — OfficialSideTab:DSH 官方右侧 Sidebar「文件编辑」页签正文包装。
|
|
11320
|
+
* 用途:页类型 Tab(edrvEditor)经 navigation.params(openPath/focusDiff/line)打开;
|
|
11321
|
+
* 兼作 file 认领转发(ClaimRouter)失败时的兜底正文(就地挂编辑器,旧行为)。
|
|
11322
|
+
* 派发既有 edrv:open-editor 事件复用 EditorView 打开流;按会话 key 保证切会话干净重建。
|
|
10097
11323
|
* Tab 正文切换后重挂载时按最新导航恢复打开(对齐 better-sidebar 形态的种子行为)。
|
|
10098
|
-
* 作者 ddj 2026年09月09号
|
|
11324
|
+
* 作者 ddj 2026年09月09号 / 2026年09月10号
|
|
10099
11325
|
*/
|
|
10100
11326
|
/**
|
|
10101
11327
|
* 官方侧边栏 Tab 正文组件(keyed slot sidebar.right.pane.tab 装配)。
|
|
@@ -10148,7 +11374,8 @@ window.__ModuleLoader__.load({
|
|
|
10148
11374
|
const request = resolveNavOpen(navigation?.params);
|
|
10149
11375
|
window.dispatchEvent(new CustomEvent("edrv:open-editor", { detail: {
|
|
10150
11376
|
path: request.path,
|
|
10151
|
-
focusDiff: request.focusDiff
|
|
11377
|
+
focusDiff: request.focusDiff,
|
|
11378
|
+
line: resolveNavLine(navigation?.params)
|
|
10152
11379
|
} }));
|
|
10153
11380
|
});
|
|
10154
11381
|
return react.default.createElement(EditorView, {
|
|
@@ -10164,6 +11391,93 @@ window.__ModuleLoader__.load({
|
|
|
10164
11391
|
});
|
|
10165
11392
|
}
|
|
10166
11393
|
//#endregion
|
|
11394
|
+
//#region src/client/ui/ClaimRouter.ts
|
|
11395
|
+
/**
|
|
11396
|
+
* dsh-vscode-mode client — ClaimRouter:官方 file 认领页签的中转正文。
|
|
11397
|
+
* 官方为每个 `dsh-resource://file/**` 地址开一个认领页签;本组件只做转发:
|
|
11398
|
+
* 解析地址 → 送进单一编辑器页签(文件分页由编辑器自带页签栏接管)→ 关闭本中转页签。
|
|
11399
|
+
* 转发失败(seat 未就绪等瞬时态)按 3 次 ×150ms 重试;仍失败则就地渲染兜底正文
|
|
11400
|
+
* (旧行为:每文件一套编辑器),保证退化路径不白屏。本组件自身不创建 Monaco 实例。
|
|
11401
|
+
* 作者 ddj 2026年09月10号
|
|
11402
|
+
*/
|
|
11403
|
+
/** 转发尝试次数(含首次)与重试间隔。 */
|
|
11404
|
+
const FORWARD_ATTEMPTS = 3;
|
|
11405
|
+
const FORWARD_DELAY_MS = 150;
|
|
11406
|
+
/**
|
|
11407
|
+
* 读 slot 框架注入的 tab 记录(hook 缺失或抛错时返回 undefined)。
|
|
11408
|
+
* @author ddj 2026年09月10号
|
|
11409
|
+
* @param props slot 框架注入的组件 props
|
|
11410
|
+
* @returns tab 记录(含 navigation 与 actions),或 undefined
|
|
11411
|
+
*/
|
|
11412
|
+
function readTab(props) {
|
|
11413
|
+
const useTabInfo = props?.useTabInfo;
|
|
11414
|
+
if (typeof useTabInfo !== "function") return void 0;
|
|
11415
|
+
try {
|
|
11416
|
+
return useTabInfo()?.tab;
|
|
11417
|
+
} catch {
|
|
11418
|
+
return;
|
|
11419
|
+
}
|
|
11420
|
+
}
|
|
11421
|
+
/**
|
|
11422
|
+
* 关闭本中转页签(失败仅告警:目标文件已在编辑器打开,残留页签不阻塞功能)。
|
|
11423
|
+
* @author ddj 2026年09月10号
|
|
11424
|
+
* @param tab slot 框架注入的 tab 记录
|
|
11425
|
+
*/
|
|
11426
|
+
function closeTab(tab) {
|
|
11427
|
+
try {
|
|
11428
|
+
tab?.actions?.close?.();
|
|
11429
|
+
} catch (error) {
|
|
11430
|
+
log.warn("中转页签关闭失败(" + String(error) + ")");
|
|
11431
|
+
}
|
|
11432
|
+
}
|
|
11433
|
+
/**
|
|
11434
|
+
* 组装 file 认领转发正文。
|
|
11435
|
+
* @author ddj 2026年09月10号
|
|
11436
|
+
* @param spec 装配依赖(service 官方导航服务 / schedule 延时调度 / fallback 兜底正文)
|
|
11437
|
+
* @returns 正文组件(注册进 keyed slot sidebar.right.pane.tab)
|
|
11438
|
+
*/
|
|
11439
|
+
function createClaimRouter(spec) {
|
|
11440
|
+
const service = spec?.service;
|
|
11441
|
+
const schedule = spec?.schedule;
|
|
11442
|
+
const fallback = spec?.fallback;
|
|
11443
|
+
/**
|
|
11444
|
+
* 中转正文:转发成功即关闭自身;重试耗尽后渲染兜底正文。
|
|
11445
|
+
* @param props slot 框架注入(useTabInfo)+ 装配依赖
|
|
11446
|
+
* @returns 兜底正文元素,或 null(转发中 / 已转发)
|
|
11447
|
+
*/
|
|
11448
|
+
function ClaimRouter(props) {
|
|
11449
|
+
const tab = readTab(props);
|
|
11450
|
+
const navigation = tab?.navigation;
|
|
11451
|
+
const revision = typeof navigation?.revision === "number" ? navigation.revision : -1;
|
|
11452
|
+
const [failed, setFailed] = react.default.useState(false);
|
|
11453
|
+
const doneRef = react.default.useRef(-1);
|
|
11454
|
+
react.default.useEffect(() => {
|
|
11455
|
+
if (revision === doneRef.current) return;
|
|
11456
|
+
doneRef.current = revision;
|
|
11457
|
+
const parsed = parseOfficialFileAddress(navigation?.address);
|
|
11458
|
+
if (!parsed) {
|
|
11459
|
+
setFailed(true);
|
|
11460
|
+
return;
|
|
11461
|
+
}
|
|
11462
|
+
const line = resolveNavLine(navigation?.params);
|
|
11463
|
+
let left = FORWARD_ATTEMPTS;
|
|
11464
|
+
const attempt = () => {
|
|
11465
|
+
if (forwardToEditor(service, parsed.path, line)) {
|
|
11466
|
+
closeTab(tab);
|
|
11467
|
+
return;
|
|
11468
|
+
}
|
|
11469
|
+
left -= 1;
|
|
11470
|
+
if (left > 0 && typeof schedule === "function") schedule(attempt, FORWARD_DELAY_MS);
|
|
11471
|
+
else setFailed(true);
|
|
11472
|
+
};
|
|
11473
|
+
attempt();
|
|
11474
|
+
}, [revision]);
|
|
11475
|
+
if (!failed || typeof fallback !== "function") return null;
|
|
11476
|
+
return react.default.createElement(fallback, props);
|
|
11477
|
+
}
|
|
11478
|
+
return ClaimRouter;
|
|
11479
|
+
}
|
|
11480
|
+
//#endregion
|
|
10167
11481
|
//#region src/client/sidebar/registry.ts
|
|
10168
11482
|
/** 校验面板定义并写入注册表(缺 id/render 抛 TypeError)。 */
|
|
10169
11483
|
function panelRegister(entries, notify, panel) {
|
|
@@ -10611,8 +11925,8 @@ window.__ModuleLoader__.load({
|
|
|
10611
11925
|
* host 树索引,命中近乎零成本);挂载/切会话从缓存即时恢复展开树;⟳/edrv:refresh
|
|
10612
11926
|
* 保留旧条目强制后台重列(force);每次用户发起加载后预取 ≤4 个子目录(排除重型
|
|
10613
11927
|
* 目录、缓存新鲜跳过、不级联);10s 轻量跟随已展开目录(命中索引,RPC 近零成本)。
|
|
10614
|
-
*
|
|
10615
|
-
* 作者 ddj 2026-08-26 / 2026-08-27 / 2026-08-31
|
|
11928
|
+
* 差异角标/右键菜单/展开状态持久化行为不变;行图标用官方原语(文件夹图标 + 文件类型图标)。
|
|
11929
|
+
* 作者 ddj 2026-08-26 / 2026-08-27 / 2026-08-31 / 2026-09-10
|
|
10616
11930
|
*/
|
|
10617
11931
|
const DIR_CAP = 4e3;
|
|
10618
11932
|
const SAVE_DEBOUNCE_MS = 300;
|
|
@@ -10627,6 +11941,58 @@ window.__ModuleLoader__.load({
|
|
|
10627
11941
|
".pnpm-store"
|
|
10628
11942
|
]);
|
|
10629
11943
|
/**
|
|
11944
|
+
* 目录图标元素(官方 IconFolderOpen16/Close16)。
|
|
11945
|
+
* @author ddj 2026年09月10号
|
|
11946
|
+
* @param isOpen 目录是否展开
|
|
11947
|
+
* @returns 图标元素;原语缺失返回 null
|
|
11948
|
+
*/
|
|
11949
|
+
function dirIconEl(isOpen) {
|
|
11950
|
+
const Icon = isOpen ? _deepseek_ai_dsh_client_ui_primitives.IconFolderOpen16 : _deepseek_ai_dsh_client_ui_primitives.IconFolderClose16;
|
|
11951
|
+
if (typeof Icon !== "function") return null;
|
|
11952
|
+
return react.default.createElement(Icon, {
|
|
11953
|
+
size: 16,
|
|
11954
|
+
className: "edrv-tree-icon"
|
|
11955
|
+
});
|
|
11956
|
+
}
|
|
11957
|
+
/**
|
|
11958
|
+
* 文件图标元素(官方 FileTypeIcon + classifyFileType 分类,未知类型为 other)。
|
|
11959
|
+
* @author ddj 2026年09月10号
|
|
11960
|
+
* @param name 文件名(含扩展名)
|
|
11961
|
+
* @returns 图标元素;原语缺失或分类异常返回 null
|
|
11962
|
+
*/
|
|
11963
|
+
function fileIconEl(name) {
|
|
11964
|
+
if (typeof _deepseek_ai_dsh_client_ui_primitives.FileTypeIcon !== "function" || typeof _deepseek_ai_dsh_client_ui_primitives.classifyFileType !== "function") return null;
|
|
11965
|
+
try {
|
|
11966
|
+
return react.default.createElement(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
|
|
11967
|
+
kind: (0, _deepseek_ai_dsh_client_ui_primitives.classifyFileType)(String(name ?? "")),
|
|
11968
|
+
size: 16,
|
|
11969
|
+
className: "edrv-tree-icon"
|
|
11970
|
+
});
|
|
11971
|
+
} catch (error) {
|
|
11972
|
+
return null;
|
|
11973
|
+
}
|
|
11974
|
+
}
|
|
11975
|
+
/**
|
|
11976
|
+
* 行图标分派:目录 → 文件夹图标,文件 → 官方类型图标。
|
|
11977
|
+
* @author ddj 2026年09月10号
|
|
11978
|
+
* @param e 目录条目(name/type)
|
|
11979
|
+
* @param isDir 是否目录
|
|
11980
|
+
* @param isOpen 目录是否展开
|
|
11981
|
+
* @returns 图标元素或 null
|
|
11982
|
+
*/
|
|
11983
|
+
function rowIconEl(e, isDir, isOpen) {
|
|
11984
|
+
return isDir ? dirIconEl(isOpen) : fileIconEl(e?.name);
|
|
11985
|
+
}
|
|
11986
|
+
/**
|
|
11987
|
+
* 刷新按钮图标(官方 IconRefreshOutline16;原语缺失回落文本 ⟳)。
|
|
11988
|
+
* @author ddj 2026年09月10号
|
|
11989
|
+
* @returns 图标元素或回落文本
|
|
11990
|
+
*/
|
|
11991
|
+
function refreshIconEl() {
|
|
11992
|
+
if (typeof _deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline16 !== "function") return "⟳";
|
|
11993
|
+
return react.default.createElement(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline16, { size: 14 });
|
|
11994
|
+
}
|
|
11995
|
+
/**
|
|
10630
11996
|
* 目录树面板主体(SWR:有缓存先渲染,无缓存才显示加载态;加载总在后台)。
|
|
10631
11997
|
* @param props.ctx 面板共享上下文(sessionId/openFile/activePath/pendingByPath/fileMenuItems/notify)
|
|
10632
11998
|
*/
|
|
@@ -10824,7 +12190,7 @@ window.__ModuleLoader__.load({
|
|
|
10824
12190
|
}
|
|
10825
12191
|
});
|
|
10826
12192
|
}
|
|
10827
|
-
}, react.default.createElement("span", { className: "edrv-tree-chev" }, isDir ? isOpen ? "▾" : "▸" : ""), react.default.createElement("span", { className: "edrv-tree-name" + (dim ? " edrv-tree-dim" : "") },
|
|
12193
|
+
}, react.default.createElement("span", { className: "edrv-tree-chev" }, isDir ? isOpen ? "▾" : "▸" : ""), rowIconEl(e, isDir, isOpen), react.default.createElement("span", { className: "edrv-tree-name" + (dim ? " edrv-tree-dim" : "") }, e.name), pending > 0 ? react.default.createElement("span", { className: "edrv-tree-badge" }, String(pending)) : null);
|
|
10828
12194
|
};
|
|
10829
12195
|
const rowsOf = (rel, depth) => {
|
|
10830
12196
|
const entries = entriesOf(rel);
|
|
@@ -10882,7 +12248,7 @@ window.__ModuleLoader__.load({
|
|
|
10882
12248
|
className: "edrv-side-btn",
|
|
10883
12249
|
title: "刷新目录树",
|
|
10884
12250
|
onClick: refresh
|
|
10885
|
-
},
|
|
12251
|
+
}, refreshIconEl())), react.default.createElement("div", { className: "edrv-tree" }, errorText ? react.default.createElement("div", { className: "edrv-tree-error" }, react.default.createElement("span", null, String(errorText)), react.default.createElement("button", {
|
|
10886
12252
|
className: "edrv-side-btn",
|
|
10887
12253
|
onClick: () => refresh()
|
|
10888
12254
|
}, "重试")) : null, rowsOf("", 0)), menu && menuEntries.length ? react.default.createElement(ContextMenu, {
|
|
@@ -11935,18 +13301,19 @@ window.__ModuleLoader__.load({
|
|
|
11935
13301
|
//#region src/client/sidebar/panels/index.ts
|
|
11936
13302
|
/**
|
|
11937
13303
|
* dsh-vscode-mode client — 侧边栏面板定义(文件管理 + 搜索 + 规则)。
|
|
11938
|
-
*
|
|
13304
|
+
* 活动栏图标用官方 UI 原语(跟随 DSH 主题与皮肤);原语缺失时由 SidebarView 回落文本渲染。
|
|
13305
|
+
* 作者 ddj 2026-08-26 / 2026-09-03 / 2026-09-10
|
|
11939
13306
|
*/
|
|
11940
13307
|
/**
|
|
11941
13308
|
* 构造文件管理面板定义。
|
|
11942
|
-
* @author ddj 2026年08月26号
|
|
11943
|
-
* @returns 面板定义(含活动栏徽标 =
|
|
13309
|
+
* @author ddj 2026年08月26号 / 2026年09月10号
|
|
13310
|
+
* @returns 面板定义(含活动栏徽标 = 差异文件总数;图标 = 官方 IconFolderOpenOutline16)
|
|
11944
13311
|
*/
|
|
11945
13312
|
function createFilePanel() {
|
|
11946
13313
|
return {
|
|
11947
13314
|
id: "explorer",
|
|
11948
13315
|
title: "文件管理",
|
|
11949
|
-
icon:
|
|
13316
|
+
icon: _deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16,
|
|
11950
13317
|
order: 10,
|
|
11951
13318
|
badge: (ctx) => ctx.sum && ctx.sum.totalFiles > 0 ? ctx.sum.totalFiles : null,
|
|
11952
13319
|
render: (ctx) => react.default.createElement(FileExplorer, { ctx })
|
|
@@ -11954,28 +13321,28 @@ window.__ModuleLoader__.load({
|
|
|
11954
13321
|
}
|
|
11955
13322
|
/**
|
|
11956
13323
|
* 构造搜索面板定义。
|
|
11957
|
-
* @author ddj 2026年08月30号
|
|
11958
|
-
* @returns
|
|
13324
|
+
* @author ddj 2026年08月30号 / 2026年09月10号
|
|
13325
|
+
* @returns 面板定义(无徽标;图标 = 官方 IconSearchOutline16)
|
|
11959
13326
|
*/
|
|
11960
13327
|
function createSearchPanel() {
|
|
11961
13328
|
return {
|
|
11962
13329
|
id: "search",
|
|
11963
13330
|
title: "搜索",
|
|
11964
|
-
icon:
|
|
13331
|
+
icon: _deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16,
|
|
11965
13332
|
order: 15,
|
|
11966
13333
|
render: (ctx) => react.default.createElement(SearchPanel, { ctx })
|
|
11967
13334
|
};
|
|
11968
13335
|
}
|
|
11969
13336
|
/**
|
|
11970
13337
|
* 构造规则管理面板定义(Codebuddy 规则形态:用户规则/项目规则双 Tab + 开关)。
|
|
11971
|
-
* @author ddj 2026年09月03号
|
|
11972
|
-
* @returns
|
|
13338
|
+
* @author ddj 2026年09月03号 / 2026年09月10号
|
|
13339
|
+
* @returns 面板定义(无徽标;图标 = 官方 IconListPenOutline16)
|
|
11973
13340
|
*/
|
|
11974
13341
|
function createRulesPanel() {
|
|
11975
13342
|
return {
|
|
11976
13343
|
id: "rules",
|
|
11977
13344
|
title: "规则",
|
|
11978
|
-
icon:
|
|
13345
|
+
icon: _deepseek_ai_dsh_client_ui_primitives.IconListPenOutline16,
|
|
11979
13346
|
order: 20,
|
|
11980
13347
|
render: (ctx) => react.default.createElement(RulesPanel, { ctx })
|
|
11981
13348
|
};
|
|
@@ -12767,14 +14134,14 @@ window.__ModuleLoader__.load({
|
|
|
12767
14134
|
*/
|
|
12768
14135
|
/**
|
|
12769
14136
|
* 构造大纲面板定义。
|
|
12770
|
-
* @author ddj 2026年08月27号
|
|
12771
|
-
* @returns 面板定义(无徽标;活动栏图标
|
|
14137
|
+
* @author ddj 2026年08月27号 / 2026年09月10号
|
|
14138
|
+
* @returns 面板定义(无徽标;活动栏图标 = 官方 IconCodeOutline16)
|
|
12772
14139
|
*/
|
|
12773
14140
|
function createOutlinePanel() {
|
|
12774
14141
|
return {
|
|
12775
14142
|
id: "outline",
|
|
12776
14143
|
title: "大纲",
|
|
12777
|
-
icon:
|
|
14144
|
+
icon: _deepseek_ai_dsh_client_ui_primitives.IconCodeOutline16,
|
|
12778
14145
|
order: 20,
|
|
12779
14146
|
render: (ctx) => react.default.createElement(OutlinePanel, { ctx })
|
|
12780
14147
|
};
|
|
@@ -12817,6 +14184,64 @@ window.__ModuleLoader__.load({
|
|
|
12817
14184
|
return out;
|
|
12818
14185
|
}
|
|
12819
14186
|
//#endregion
|
|
14187
|
+
//#region src/client/commandBridge.ts
|
|
14188
|
+
/**
|
|
14189
|
+
* dsh-vscode-mode client — 指令执行桥(装配层)。
|
|
14190
|
+
* 职责三件:① 把内置指令目录注册进注册表;② 为「桥接派发」类指令挂窗口 capture 键位监听
|
|
14191
|
+
* (EditorView 原生监听的 8 条不进这里,避免双执行);③ 注入命令栏执行器。
|
|
14192
|
+
* 编辑器动作本身由 EditorView / QuickOpen 监听 `edrv.command.<action>` 窗口事件完成——桥不持有
|
|
14193
|
+
* React 状态,因此指令系统可在编辑器挂载前装配、卸载后仍安全。
|
|
14194
|
+
* 作者 ddj 2026年09月10号
|
|
14195
|
+
*/
|
|
14196
|
+
const bridgeLog = log.child("commands");
|
|
14197
|
+
/** 解析事件目标(缺省 window;无 window 返回 null)。 */
|
|
14198
|
+
function resolveTarget(options) {
|
|
14199
|
+
if (options.target) return options.target;
|
|
14200
|
+
if (typeof window === "undefined") return null;
|
|
14201
|
+
return window;
|
|
14202
|
+
}
|
|
14203
|
+
/**
|
|
14204
|
+
* 创建指令执行桥:注册内置指令 + 桥接类指令的全局键位监听。
|
|
14205
|
+
* @author ddj 2026年09月10号
|
|
14206
|
+
* @param options 装配选项(事件目标注入)
|
|
14207
|
+
* @returns 指令执行桥句柄
|
|
14208
|
+
*/
|
|
14209
|
+
function createCommandBridge(options = {}) {
|
|
14210
|
+
const registry = createCommandRegistry();
|
|
14211
|
+
const disposers = [];
|
|
14212
|
+
const palette = showCommandsDef(() => openCommandPalette("command"));
|
|
14213
|
+
for (const command of [...EDITOR_COMMANDS, ...BRIDGE_COMMANDS]) disposers.push(registry.register(command));
|
|
14214
|
+
disposers.push(registry.register(palette));
|
|
14215
|
+
setPaletteRunner((id) => registry.run(id));
|
|
14216
|
+
setRegistryRef(registry);
|
|
14217
|
+
const dispatched = dispatchedCommands(palette);
|
|
14218
|
+
const onKey = (event) => {
|
|
14219
|
+
for (const command of dispatched) {
|
|
14220
|
+
if (!matchEvent(event, bindingsOf(command.id))) continue;
|
|
14221
|
+
event.preventDefault();
|
|
14222
|
+
event.stopPropagation();
|
|
14223
|
+
registry.run(command.id);
|
|
14224
|
+
return;
|
|
14225
|
+
}
|
|
14226
|
+
};
|
|
14227
|
+
const target = resolveTarget(options);
|
|
14228
|
+
if (target) target.addEventListener("keydown", onKey, true);
|
|
14229
|
+
const dispose = () => {
|
|
14230
|
+
if (target) target.removeEventListener("keydown", onKey, true);
|
|
14231
|
+
while (disposers.length) {
|
|
14232
|
+
const release = disposers.pop();
|
|
14233
|
+
if (release) release();
|
|
14234
|
+
}
|
|
14235
|
+
setPaletteRunner(null);
|
|
14236
|
+
setRegistryRef(null);
|
|
14237
|
+
};
|
|
14238
|
+
bridgeLog.info("指令注册表已装配(" + registry.list().length + " 条指令,桥接键位 " + dispatched.length + " 条)");
|
|
14239
|
+
return {
|
|
14240
|
+
registry,
|
|
14241
|
+
dispose
|
|
14242
|
+
};
|
|
14243
|
+
}
|
|
14244
|
+
//#endregion
|
|
12820
14245
|
//#region src/client/index.ts
|
|
12821
14246
|
/**
|
|
12822
14247
|
* dsh-vscode-mode client — 浏览器半入口:slot 注册 + 装配。
|
|
@@ -12844,6 +14269,30 @@ window.__ModuleLoader__.load({
|
|
|
12844
14269
|
"conversation",
|
|
12845
14270
|
"settingsScope"
|
|
12846
14271
|
];
|
|
14272
|
+
/** 指令桥装配幂等标记(同一 document 重复 apply 只装配一次,避免重复键位监听)。 */
|
|
14273
|
+
let commandsMounted = false;
|
|
14274
|
+
/**
|
|
14275
|
+
* 装配指令注册表(指令桥 + 命令栏)。
|
|
14276
|
+
* 指令桥不依赖已挂载编辑器(run 只派发窗口事件),故可在启动期装配;
|
|
14277
|
+
* 命令栏浮层的宿主由 CommandPalette 自己在编辑区树内认领。
|
|
14278
|
+
* @author ddj 2026年09月10号
|
|
14279
|
+
* @param ctx 客户端根上下文
|
|
14280
|
+
* @returns void
|
|
14281
|
+
*/
|
|
14282
|
+
function setupCommands(ctx) {
|
|
14283
|
+
if (commandsMounted) return;
|
|
14284
|
+
commandsMounted = true;
|
|
14285
|
+
const bridge = createCommandBridge();
|
|
14286
|
+
ctx.provide(REGISTRY_GLOBAL, bridge.registry);
|
|
14287
|
+
const host = window;
|
|
14288
|
+
host[REGISTRY_GLOBAL] = bridge.registry;
|
|
14289
|
+
ctx.effect(() => () => {
|
|
14290
|
+
bridge.dispose();
|
|
14291
|
+
commandsMounted = false;
|
|
14292
|
+
if (host["__edrvCommands__"] === bridge.registry) delete host[REGISTRY_GLOBAL];
|
|
14293
|
+
}, "vscode-mode: command registry");
|
|
14294
|
+
log.info("指令系统已装配:" + bridge.registry.list().length + " 条指令,Ctrl+Shift+P 打开命令栏");
|
|
14295
|
+
}
|
|
12847
14296
|
/**
|
|
12848
14297
|
* 装配客户端:注册中央编辑区视图与 header 差异角标。
|
|
12849
14298
|
* @author ddj 2026年08月20号
|
|
@@ -12851,6 +14300,7 @@ window.__ModuleLoader__.load({
|
|
|
12851
14300
|
*/
|
|
12852
14301
|
function apply(ctx) {
|
|
12853
14302
|
const schedule = (fn, ms) => ctx.timeout(fn, ms);
|
|
14303
|
+
setupCommands(ctx);
|
|
12854
14304
|
const registry = createFileOpenerRegistry();
|
|
12855
14305
|
const workspaces = ctx.get("workspaces");
|
|
12856
14306
|
const sessions = ctx.get("sessions");
|
|
@@ -12955,7 +14405,7 @@ window.__ModuleLoader__.load({
|
|
|
12955
14405
|
{
|
|
12956
14406
|
name: "文件链接官方认领(dsh-resource://file)",
|
|
12957
14407
|
active: claimDisposer !== null,
|
|
12958
|
-
note: claimDisposer !== null ? "
|
|
14408
|
+
note: claimDisposer !== null ? "聊天文件链接转发进单一编辑器页签(文件分页归编辑器自带页签栏)" : "链接走官方查看器或旧版路由(未认领)"
|
|
12959
14409
|
}
|
|
12960
14410
|
];
|
|
12961
14411
|
ctx.provide("fileOpeners", registry);
|
|
@@ -12991,15 +14441,20 @@ window.__ModuleLoader__.load({
|
|
|
12991
14441
|
fileMenuItems,
|
|
12992
14442
|
sessions
|
|
12993
14443
|
}));
|
|
12994
|
-
/** 官方 file 地址认领同步:自动/VSCodeMode
|
|
14444
|
+
/** 官方 file 地址认领同步:自动/VSCodeMode 档认领(转发进单一编辑器页签),其余交官方查看器。 */
|
|
12995
14445
|
const syncFileClaim = () => {
|
|
12996
14446
|
const official = officialService;
|
|
12997
14447
|
const want = official !== void 0 && shouldClaimFiles(selected);
|
|
12998
14448
|
if (want && claimDisposer === null && official) {
|
|
14449
|
+
const renderTab = createClaimRouter({
|
|
14450
|
+
service: official.service,
|
|
14451
|
+
schedule,
|
|
14452
|
+
fallback: officialRenderTab
|
|
14453
|
+
});
|
|
12999
14454
|
const disposer = registerOfficialFileClaim({
|
|
13000
14455
|
tabs: official.tabs,
|
|
13001
14456
|
slots: ctx.slots,
|
|
13002
|
-
renderTab
|
|
14457
|
+
renderTab
|
|
13003
14458
|
});
|
|
13004
14459
|
if (disposer !== null) claimDisposer = ctx.effect(() => disposer, "vscode-mode: official file claim");
|
|
13005
14460
|
return;
|
|
@@ -13070,6 +14525,18 @@ window.__ModuleLoader__.load({
|
|
|
13070
14525
|
}, 2e3);
|
|
13071
14526
|
};
|
|
13072
14527
|
retryRemoteOpen();
|
|
14528
|
+
const themeService = ctx.get("theme");
|
|
14529
|
+
const emitTheme = () => {
|
|
14530
|
+
const snapshot = typeof themeService?.getTheme === "function" ? themeService.getTheme() : void 0;
|
|
14531
|
+
window.dispatchEvent(new CustomEvent("edrv:theme-change", { detail: { scheme: schemeOfSnapshot(snapshot) } }));
|
|
14532
|
+
};
|
|
14533
|
+
ctx.effect(() => {
|
|
14534
|
+
if (!themeService || typeof ctx.on !== "function") return void 0;
|
|
14535
|
+
const disposer = ctx.on("theme/change", emitTheme);
|
|
14536
|
+
return typeof disposer === "function" ? disposer : void 0;
|
|
14537
|
+
}, "vscode-mode: official theme event");
|
|
14538
|
+
ctx.effect(() => observeScheme(emitTheme), "vscode-mode: theme attribute watch");
|
|
14539
|
+
emitTheme();
|
|
13073
14540
|
const registerLegacyTab = () => {
|
|
13074
14541
|
return registerSlotSafely(ctx, {
|
|
13075
14542
|
name: "conversation.view",
|