dsh-vscode-mode 0.1.52 → 0.1.54
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 +71 -0
- package/assets/shell/dsh-open.cs +182 -0
- package/assets/shell/dsh-open.ps1 +52 -0
- package/assets/shell/dsh-open.sh +70 -0
- package/assets/shell/dsh-whale.ico +0 -0
- package/lib/client.js +1038 -13
- package/lib/client.js.map +1 -1
- package/lib/index.js +1130 -7
- package/lib/index.js.map +1 -1
- package/package.json +2 -1
- package/src/client/chooseDialog.ts +70 -0
- package/src/client/compat.ts +31 -0
- package/src/client/externalOpen.ts +130 -0
- package/src/client/imagePreview.ts +45 -0
- package/src/client/index.ts +50 -8
- package/src/client/openFlow.ts +330 -0
- package/src/client/remoteOpenRouter.ts +78 -0
- package/src/client/styles/editor.css +6 -0
- package/src/client/ui/EditorView.ts +116 -1
- package/src/client/ui/IntegrationSettings.ts +243 -0
- package/src/client/ui/McpSettings.ts +2 -0
- package/src/compat.ts +1 -1
- package/src/dshVersion.ts +1 -0
- package/src/externalHandoff.ts +70 -0
- package/src/fileOpenSettings.ts +10 -2
- package/src/index.ts +3 -0
- package/src/integrate.ts +576 -0
- package/src/rpc.ts +97 -3
- package/src/shared/externalOpen.ts +62 -0
- package/src/shared/integration.ts +73 -0
- package/src/shared/rpc.ts +58 -5
- package/src/unityBridge.ts +234 -0
- package/unity/com.dsh.editor/Editor/DshCodeEditor.cs +267 -0
- package/unity/com.dsh.editor/Editor/DshCodeEditor.cs.meta +11 -0
- package/unity/com.dsh.editor/Editor/com.dsh.editor.asmdef +15 -0
- package/unity/com.dsh.editor/Editor/com.dsh.editor.asmdef.meta +7 -0
- package/unity/com.dsh.editor/Editor.meta +8 -0
- package/unity/com.dsh.editor/README.md +33 -0
- package/unity/com.dsh.editor/README.md.meta +7 -0
- package/unity/com.dsh.editor/package.json +7 -0
- package/unity/com.dsh.editor/package.json.meta +7 -0
package/lib/client.js
CHANGED
|
@@ -28,9 +28,10 @@ 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");
|
|
31
32
|
let react_dom = require("react-dom");
|
|
32
33
|
//#region \0dsh-css:/home/runner/work/DSH_VsCodeMode/DSH_VsCodeMode/src/client/styles/editor.css.mjs
|
|
33
|
-
const css$1 = "/* dsh-vscode-mode 编辑区样式(原 ensureEdrvStyles 字符串,落盘为真 .css,由 tsdown CSS-inline 注入) */\n/* 浅色主题(对齐参考图:#f8f8f8 近白底 + 薄荷绿药丸 + 青绿文字 + 三文鱼 undo + 浅灰边框):\n 在 [data-edrv-view] 作用域内重定义 --dsw-alias-*,级联命中组件内联样式与 CSS。 */\n[data-edrv-view] { box-sizing: border-box; --edrv-tab-h: 34px; --dsw-alias-bg-base: #f8f8f8; --dsw-alias-bg-layer-1: #ffffff; --dsw-alias-bg-layer-2: #f0f4f4; --dsw-alias-bg-overlay: #ffffff; --dsw-alias-label-primary: #1f2933; --dsw-alias-label-secondary: #52606d; --dsw-alias-label-tertiary: #9aa5b1; --dsw-alias-border-l1: #e0e6e8; --dsw-alias-border-l2: #cbd2d9; --dsw-alias-brand-primary: #0f9d58; --dsw-alias-state-success-primary: #0f9d58; --dsw-alias-state-error-primary: #d9534f; --dsw-alias-state-warn-primary: #d97706; --dsw-alias-interactive-bg-hover: rgba(15,157,88,.08); }\n\n[data-edrv-view] .edrv-tabs { display: flex; align-items: center; gap: 2px; padding: 2px 8px 0; background: var(--dsw-alias-bg-layer-1, transparent); overflow-x: auto; scrollbar-width: thin; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 8px 0 12px; border: none; border-radius: 6px 6px 0 0; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 12px; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-tab.edrv-tab-active { background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); box-shadow: inset 0 -2px 0 var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-tab .edrv-tab-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dsw-alias-brand-primary, #4f8cff); flex-shrink: 0; }\n[data-edrv-view] .edrv-tab .edrv-tab-x { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: 4px; color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; }\n[data-edrv-view] .edrv-tab .edrv-tab-x:hover { background: rgba(255,255,255,.12); color: var(--dsw-alias-label-primary, #eee); }\n[data-edrv-view] .edrv-tab-add { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; margin-left: 2px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 14px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab-add:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-pathbar { display: flex; align-items: center; gap: 8px; height: 26px; padding: 0 10px; background: var(--dsw-alias-bg-layer-1, #ffffff); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 12px; flex-shrink: 0; overflow: hidden; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-name { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-full { flex: 1; min-width: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; direction: rtl; text-align: left; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-meta { flex-shrink: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-status { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-path-input { flex: 1; min-width: 140px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-wrap { position: relative; flex-shrink: 0; display: flex; align-items: center; padding: 2px 8px 2px 4px; }\n[data-edrv-view] .edrv-search { width: 190px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-pop { position: absolute; top: calc(100% + 4px); right: 8px; width: min(460px, calc(100vw - 24px)); max-height: 300px; overflow: auto; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 10px; box-shadow: 0 6px 20px rgba(0,0,0,.4); z-index: 60; padding: 4px; }\n[data-edrv-view] .edrv-search-item { display: flex; flex-direction: column; gap: 1px; padding: 5px 8px; border-radius: 6px; cursor: pointer; }\n[data-edrv-view] .edrv-search-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.07)); }\n[data-edrv-view] .edrv-search-item .n { color: var(--dsw-alias-label-primary, #eee); font-size: 12px; word-break: break-all; }\n[data-edrv-view] .edrv-search-item .d { color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-search-empty { padding: 8px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #888); }\n[data-edrv-view] .edrv-btn-mini { font-size: 11px; padding: 2px 9px; border: none; border-radius: 999px; cursor: pointer; color: #fff; font-weight: 600; white-space: nowrap; }\n[data-edrv-view] .edrv-btn-keep { background: var(--dsw-alias-state-success-primary, #2e9e44); }\n[data-edrv-view] .edrv-btn-undo { background: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-btn-mini:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-statusbar { display: flex; align-items: center; gap: 10px; padding: 3px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #333); background: var(--dsw-alias-bg-layer-1, transparent); font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); flex-shrink: 0; white-space: nowrap; overflow: hidden; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-path { flex: 1; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-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-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";
|
|
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-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dsw-alias-brand-primary, #4f8cff); flex-shrink: 0; }\n[data-edrv-view] .edrv-tab .edrv-tab-x { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: 4px; color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; }\n[data-edrv-view] .edrv-tab .edrv-tab-x:hover { background: rgba(255,255,255,.12); color: var(--dsw-alias-label-primary, #eee); }\n[data-edrv-view] .edrv-tab-add { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; margin-left: 2px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 14px; cursor: pointer; flex-shrink: 0; }\n[data-edrv-view] .edrv-tab-add:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-pathbar { display: flex; align-items: center; gap: 8px; height: 26px; padding: 0 10px; background: var(--dsw-alias-bg-layer-1, #ffffff); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 12px; flex-shrink: 0; overflow: hidden; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-name { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-full { flex: 1; min-width: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; direction: rtl; text-align: left; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-meta { flex-shrink: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-variant-numeric: tabular-nums; }\n[data-edrv-view] .edrv-pathbar .edrv-pb-status { color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-path-input { flex: 1; min-width: 140px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-wrap { position: relative; flex-shrink: 0; display: flex; align-items: center; padding: 2px 8px 2px 4px; }\n[data-edrv-view] .edrv-search { width: 190px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-pop { position: absolute; top: calc(100% + 4px); right: 8px; width: min(460px, calc(100vw - 24px)); max-height: 300px; overflow: auto; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 10px; box-shadow: 0 6px 20px rgba(0,0,0,.4); z-index: 60; padding: 4px; }\n[data-edrv-view] .edrv-search-item { display: flex; flex-direction: column; gap: 1px; padding: 5px 8px; border-radius: 6px; cursor: pointer; }\n[data-edrv-view] .edrv-search-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.07)); }\n[data-edrv-view] .edrv-search-item .n { color: var(--dsw-alias-label-primary, #eee); font-size: 12px; word-break: break-all; }\n[data-edrv-view] .edrv-search-item .d { color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-search-empty { padding: 8px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #888); }\n[data-edrv-view] .edrv-btn-mini { font-size: 11px; padding: 2px 9px; border: none; border-radius: 999px; cursor: pointer; color: #fff; font-weight: 600; white-space: nowrap; }\n[data-edrv-view] .edrv-btn-keep { background: var(--dsw-alias-state-success-primary, #2e9e44); }\n[data-edrv-view] .edrv-btn-undo { background: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-btn-mini:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-statusbar { display: flex; align-items: center; gap: 10px; padding: 3px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #333); background: var(--dsw-alias-bg-layer-1, transparent); font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); flex-shrink: 0; white-space: nowrap; overflow: hidden; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-path { flex: 1; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-lsp { flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-progress-message { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-statusbar .edrv-sp-progress { flex: 0 0 auto; min-width: 36px; text-align: right; font-variant-numeric: tabular-nums; color: var(--dsw-alias-label-secondary, #52606d); }\n[data-edrv-view] .edrv-lsp-status-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; background: var(--dsw-alias-label-tertiary, #9aa5b1); }\n[data-edrv-view] .edrv-lsp-status-dot.ready { background: var(--dsw-alias-state-success-primary, #0f9d58); }\n[data-edrv-view] .edrv-lsp-status-dot.busy { background: var(--dsw-alias-state-warn-primary, #d97706); animation: edrv-lsp-pulse 1.2s ease-in-out infinite; }\n@keyframes edrv-lsp-pulse { 50% { opacity: .45; } }\n/* 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-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";
|
|
34
35
|
const tagId$1 = "dsh-vscode-mode/editor.css";
|
|
35
36
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
36
37
|
const tag = document.createElement("style");
|
|
@@ -43,6 +44,22 @@ window.__ModuleLoader__.load({
|
|
|
43
44
|
//#region src/shared/rpc.ts
|
|
44
45
|
/** webServer 精确路由。 */
|
|
45
46
|
const RPC_PATH = "/edrv/rpc";
|
|
47
|
+
/**
|
|
48
|
+
* 图片扩展名(小写、无点)→ MIME。edrv.read base64 响应与 client 图片预览共用,
|
|
49
|
+
* 收敛在此避免 host/client 两处漂移。
|
|
50
|
+
* @author ddj 2026年09月08号
|
|
51
|
+
*/
|
|
52
|
+
const IMAGE_MIME = {
|
|
53
|
+
png: "image/png",
|
|
54
|
+
jpg: "image/jpeg",
|
|
55
|
+
jpeg: "image/jpeg",
|
|
56
|
+
gif: "image/gif",
|
|
57
|
+
webp: "image/webp",
|
|
58
|
+
bmp: "image/bmp",
|
|
59
|
+
ico: "image/x-icon",
|
|
60
|
+
avif: "image/avif",
|
|
61
|
+
svg: "image/svg+xml"
|
|
62
|
+
};
|
|
46
63
|
//#endregion
|
|
47
64
|
//#region src/client/rpc.ts
|
|
48
65
|
/**
|
|
@@ -377,6 +394,20 @@ window.__ModuleLoader__.load({
|
|
|
377
394
|
window.dispatchEvent(new CustomEvent("edrv:refresh"));
|
|
378
395
|
}
|
|
379
396
|
/**
|
|
397
|
+
* 打开指定路径并定位到行列(LSP 跳转目标)。
|
|
398
|
+
* @author ddj 2026年08月27号
|
|
399
|
+
* @param path 目标路径
|
|
400
|
+
* @param line 目标行(1-based,可空)
|
|
401
|
+
* @param column 目标列(1-based,可空)
|
|
402
|
+
*/
|
|
403
|
+
function emitOpenAt(path, line, column) {
|
|
404
|
+
window.dispatchEvent(new CustomEvent("edrv:open-editor", { detail: {
|
|
405
|
+
path,
|
|
406
|
+
line,
|
|
407
|
+
column
|
|
408
|
+
} }));
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
380
411
|
* 拉起 DiffLauncher。
|
|
381
412
|
* @author ddj 2026年08月26号
|
|
382
413
|
* @param tab 可选的目标页签
|
|
@@ -1090,6 +1121,48 @@ window.__ModuleLoader__.load({
|
|
|
1090
1121
|
return monacoPromise.finally(unsubscribe);
|
|
1091
1122
|
}
|
|
1092
1123
|
//#endregion
|
|
1124
|
+
//#region src/client/imagePreview.ts
|
|
1125
|
+
/**
|
|
1126
|
+
* dsh-vscode-mode client — 图片文件判定与 data URL 组装(编辑区图片预览面板用)。
|
|
1127
|
+
* 纯函数、无 DOM 依赖,便于单测;MIME 表收敛在 shared/rpc.ts(host base64 响应同源)。
|
|
1128
|
+
* @author ddj 2026年09月08号
|
|
1129
|
+
*/
|
|
1130
|
+
/** 可预览的图片扩展名集合(小写、无点)。 */
|
|
1131
|
+
const IMAGE_EXT_SET = new Set(Object.keys(IMAGE_MIME));
|
|
1132
|
+
/**
|
|
1133
|
+
* 判断路径是否为可预览的图片文件(取 basename 扩展名,大小写不敏感)。
|
|
1134
|
+
* @author ddj 2026年09月08号
|
|
1135
|
+
* @param path 文件路径(`/` 或 `\` 分隔均可)
|
|
1136
|
+
* @returns 是否图片文件
|
|
1137
|
+
*/
|
|
1138
|
+
function isImagePath(path) {
|
|
1139
|
+
const base = String(path || "").split(/[\\/]/).pop() || "";
|
|
1140
|
+
const dot = base.lastIndexOf(".");
|
|
1141
|
+
if (dot <= 0) return false;
|
|
1142
|
+
return IMAGE_EXT_SET.has(base.slice(dot + 1).toLowerCase());
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* 判断路径是否为 SVG 文件(唯一提供「以文本打开」切换的图片格式)。
|
|
1146
|
+
* @author ddj 2026年09月08号
|
|
1147
|
+
* @param path 文件路径(`/` 或 `\` 分隔均可)
|
|
1148
|
+
* @returns 是否 SVG
|
|
1149
|
+
*/
|
|
1150
|
+
function isSvgPath(path) {
|
|
1151
|
+
const base = String(path || "").split(/[\\/]/).pop() || "";
|
|
1152
|
+
const dot = base.lastIndexOf(".");
|
|
1153
|
+
return dot > 0 && base.slice(dot + 1).toLowerCase() === "svg";
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* base64 文本 → 可直接赋给 `<img src>` 的 data URL。
|
|
1157
|
+
* @author ddj 2026年09月08号
|
|
1158
|
+
* @param base64 base64 文本(不含 data: 前缀)
|
|
1159
|
+
* @param mime MIME 类型(host edrv.read base64 响应携带)
|
|
1160
|
+
* @returns data URL 字符串
|
|
1161
|
+
*/
|
|
1162
|
+
function dataUrlOf(base64, mime) {
|
|
1163
|
+
return "data:" + (mime || "application/octet-stream") + ";base64," + base64;
|
|
1164
|
+
}
|
|
1165
|
+
//#endregion
|
|
1093
1166
|
//#region src/client/monaco/diffRender.ts
|
|
1094
1167
|
/**
|
|
1095
1168
|
* dsh-vscode-mode client — 行内差异自绘(新增行绿底 + glyph margin '+';删除块 view zone 红底)。
|
|
@@ -3601,6 +3674,10 @@ window.__ModuleLoader__.load({
|
|
|
3601
3674
|
const [dirtyMap, setDirtyMap] = react.default.useState({});
|
|
3602
3675
|
const [content, setContent] = react.default.useState(null);
|
|
3603
3676
|
const [contentPath, setContentPath] = react.default.useState(null);
|
|
3677
|
+
const [imageSrc, setImageSrc] = react.default.useState(null);
|
|
3678
|
+
const [imgSize, setImgSize] = react.default.useState(null);
|
|
3679
|
+
const [imgBroken, setImgBroken] = react.default.useState(false);
|
|
3680
|
+
const svgTextRef = react.default.useRef(/* @__PURE__ */ new Set());
|
|
3604
3681
|
const [status, setStatus] = react.default.useState("");
|
|
3605
3682
|
const [lspServers, setLspServers] = react.default.useState([]);
|
|
3606
3683
|
const [error, setError] = react.default.useState(null);
|
|
@@ -3668,6 +3745,7 @@ window.__ModuleLoader__.load({
|
|
|
3668
3745
|
return list;
|
|
3669
3746
|
}, [records, active]);
|
|
3670
3747
|
const contentReady = content !== null && contentPath === active;
|
|
3748
|
+
const isImageActive = !!active && isImagePath(active) && !svgTextRef.current.has(active);
|
|
3671
3749
|
const regions = react.default.useMemo(() => diffRegions(currentRecords, contentReady ? content : null).filter((r) => !r.superseded), [
|
|
3672
3750
|
currentRecords,
|
|
3673
3751
|
content,
|
|
@@ -3873,6 +3951,10 @@ window.__ModuleLoader__.load({
|
|
|
3873
3951
|
*/
|
|
3874
3952
|
const loadContent = (path, sid, force) => {
|
|
3875
3953
|
const seq = ++loadSeqRef.current;
|
|
3954
|
+
if (isImagePath(path) && !svgTextRef.current.has(path)) {
|
|
3955
|
+
loadImage(path, sid, seq);
|
|
3956
|
+
return;
|
|
3957
|
+
}
|
|
3876
3958
|
const cachedModel = force ? null : modelsRef.current.get(path);
|
|
3877
3959
|
if (cachedModel) {
|
|
3878
3960
|
setContent(cachedModel.getValue());
|
|
@@ -3928,6 +4010,55 @@ window.__ModuleLoader__.load({
|
|
|
3928
4010
|
setStatus("读取失败");
|
|
3929
4011
|
});
|
|
3930
4012
|
};
|
|
4013
|
+
/**
|
|
4014
|
+
* 加载图片文件为 data URL(编辑区只读预览);失败走 loadError 面板(重试经 loadContent 分派回此)。
|
|
4015
|
+
* @author ddj 2026年09月08号
|
|
4016
|
+
* @param path 图片文件路径
|
|
4017
|
+
* @param sid 会话 id
|
|
4018
|
+
* @param seq 加载序号(过期响应丢弃)
|
|
4019
|
+
*/
|
|
4020
|
+
const loadImage = (path, sid, seq) => {
|
|
4021
|
+
setImageSrc(null);
|
|
4022
|
+
setImgSize(null);
|
|
4023
|
+
setImgBroken(false);
|
|
4024
|
+
setLoadStage({
|
|
4025
|
+
progress: monaco ? 72 : 12,
|
|
4026
|
+
message: "读取图片…"
|
|
4027
|
+
});
|
|
4028
|
+
rpc("edrv.read", {
|
|
4029
|
+
sessionId: sid,
|
|
4030
|
+
path,
|
|
4031
|
+
encoding: "base64"
|
|
4032
|
+
}).then((res) => {
|
|
4033
|
+
if (seq !== loadSeqRef.current || path !== active) return;
|
|
4034
|
+
if (res && res.ok && res.encoding === "base64") {
|
|
4035
|
+
if (!res.mime) {
|
|
4036
|
+
setLoadError("host 版本过旧:图片响应缺少 mime");
|
|
4037
|
+
setError("host 版本过旧:图片响应缺少 mime");
|
|
4038
|
+
setStatus("读取失败");
|
|
4039
|
+
return;
|
|
4040
|
+
}
|
|
4041
|
+
setImageSrc(dataUrlOf(res.content, res.mime));
|
|
4042
|
+
setLoadStage({
|
|
4043
|
+
progress: 100,
|
|
4044
|
+
message: "图片已就绪"
|
|
4045
|
+
});
|
|
4046
|
+
setStatus("已加载");
|
|
4047
|
+
return;
|
|
4048
|
+
}
|
|
4049
|
+
const base = res?.error ? String(res.error) : "读取失败";
|
|
4050
|
+
const message = res?.resolvedPath ? base + ":" + String(res.resolvedPath) : base;
|
|
4051
|
+
setLoadError(message);
|
|
4052
|
+
setError(message);
|
|
4053
|
+
setStatus("读取失败");
|
|
4054
|
+
}).catch((e) => {
|
|
4055
|
+
if (seq !== loadSeqRef.current) return;
|
|
4056
|
+
const message = "read异常:" + String(e);
|
|
4057
|
+
setLoadError(message);
|
|
4058
|
+
setError(message);
|
|
4059
|
+
setStatus("读取失败");
|
|
4060
|
+
});
|
|
4061
|
+
};
|
|
3931
4062
|
react.default.useEffect(() => {
|
|
3932
4063
|
if (!sessionId) return;
|
|
3933
4064
|
refreshRecords();
|
|
@@ -4203,6 +4334,9 @@ window.__ModuleLoader__.load({
|
|
|
4203
4334
|
if (!active) return;
|
|
4204
4335
|
setContent(null);
|
|
4205
4336
|
setContentPath(null);
|
|
4337
|
+
setImageSrc(null);
|
|
4338
|
+
setImgSize(null);
|
|
4339
|
+
setImgBroken(false);
|
|
4206
4340
|
setLoadError(null);
|
|
4207
4341
|
setLoadStage({
|
|
4208
4342
|
progress: 10,
|
|
@@ -4279,6 +4413,10 @@ window.__ModuleLoader__.load({
|
|
|
4279
4413
|
const doSave = (silent) => {
|
|
4280
4414
|
const ed = editorRef.current;
|
|
4281
4415
|
if (!active || !ed) return;
|
|
4416
|
+
if (isImageActive) {
|
|
4417
|
+
setStatus("图片只读预览");
|
|
4418
|
+
return;
|
|
4419
|
+
}
|
|
4282
4420
|
const text = ed.getValue();
|
|
4283
4421
|
if (!silent) setStatus("保存中…");
|
|
4284
4422
|
rpc("edrv.save", {
|
|
@@ -4609,6 +4747,53 @@ window.__ModuleLoader__.load({
|
|
|
4609
4747
|
refreshRecords(skipStale === true);
|
|
4610
4748
|
};
|
|
4611
4749
|
/**
|
|
4750
|
+
* SVG 在图片预览与文本编辑间切换(按路径记忆;重载分派随集合状态自动路由)。
|
|
4751
|
+
* @author ddj 2026年09月08号
|
|
4752
|
+
* @param path SVG 文件路径
|
|
4753
|
+
*/
|
|
4754
|
+
const toggleSvgText = (path) => {
|
|
4755
|
+
const next = new Set(svgTextRef.current);
|
|
4756
|
+
if (next.has(path)) next.delete(path);
|
|
4757
|
+
else next.add(path);
|
|
4758
|
+
svgTextRef.current = next;
|
|
4759
|
+
setContent(null);
|
|
4760
|
+
setContentPath(null);
|
|
4761
|
+
setImageSrc(null);
|
|
4762
|
+
setImgSize(null);
|
|
4763
|
+
setImgBroken(false);
|
|
4764
|
+
setLoadError(null);
|
|
4765
|
+
setStatus(next.has(path) ? "已切换为文本模式" : "已切换为图片预览");
|
|
4766
|
+
loadContent(path, sessionId, true);
|
|
4767
|
+
};
|
|
4768
|
+
/**
|
|
4769
|
+
* 图片预览面板:工具条(尺寸 meta / SVG 文本切换 / 刷新)+ 棋盘底自适应图片。
|
|
4770
|
+
* @author ddj 2026年09月08号
|
|
4771
|
+
*/
|
|
4772
|
+
const imagePanel = () => react.default.createElement("div", { className: "edrv-imgview" }, react.default.createElement("div", { className: "edrv-imgview-bar" }, react.default.createElement("span", { className: "edrv-imgview-meta" }, imgBroken ? "图片无法显示" : imgSize ? imgSize.w + "×" + imgSize.h : "…"), react.default.createElement("span", { style: { flex: 1 } }), isSvgPath(active) ? react.default.createElement("button", {
|
|
4773
|
+
className: "edrv-pill edrv-pill-ghost",
|
|
4774
|
+
title: "以文本方式查看/编辑(SVG 为文本格式)",
|
|
4775
|
+
onClick: () => toggleSvgText(active)
|
|
4776
|
+
}, "以文本打开") : null, react.default.createElement("button", {
|
|
4777
|
+
className: "edrv-pill edrv-pill-ghost",
|
|
4778
|
+
title: "重新加载图片",
|
|
4779
|
+
onClick: () => reloadFile()
|
|
4780
|
+
}, "⟳ 刷新")), react.default.createElement("div", { className: "edrv-imgview-stage" }, imgBroken ? react.default.createElement("div", { className: "edrv-imgview-broken" }, react.default.createElement("div", null, "图片解码失败,文件可能已损坏"), react.default.createElement("button", {
|
|
4781
|
+
className: "edrv-pill edrv-pill-ghost",
|
|
4782
|
+
onClick: () => reloadFile()
|
|
4783
|
+
}, "重试")) : react.default.createElement("img", {
|
|
4784
|
+
className: "edrv-imgview-img",
|
|
4785
|
+
src: imageSrc,
|
|
4786
|
+
alt: active || "",
|
|
4787
|
+
onLoad: (e) => {
|
|
4788
|
+
setImgBroken(false);
|
|
4789
|
+
setImgSize({
|
|
4790
|
+
w: e.target.naturalWidth,
|
|
4791
|
+
h: e.target.naturalHeight
|
|
4792
|
+
});
|
|
4793
|
+
},
|
|
4794
|
+
onError: () => setImgBroken(true)
|
|
4795
|
+
})));
|
|
4796
|
+
/**
|
|
4612
4797
|
* 关闭当前差异浮窗并清理 hover 锚点。
|
|
4613
4798
|
* @author ddj 2026年08月21号
|
|
4614
4799
|
*/
|
|
@@ -4860,6 +5045,14 @@ window.__ModuleLoader__.load({
|
|
|
4860
5045
|
const openPath = () => {
|
|
4861
5046
|
const p = (pathDraft || "").trim();
|
|
4862
5047
|
if (!p) return;
|
|
5048
|
+
if (isImagePath(p)) {
|
|
5049
|
+
openFile(p, false);
|
|
5050
|
+
setOpenInput(false);
|
|
5051
|
+
setPathDraft("");
|
|
5052
|
+
setStatus("已打开");
|
|
5053
|
+
setError(null);
|
|
5054
|
+
return;
|
|
5055
|
+
}
|
|
4863
5056
|
setStatus("打开中…");
|
|
4864
5057
|
rpc("edrv.read", {
|
|
4865
5058
|
sessionId,
|
|
@@ -4935,7 +5128,7 @@ window.__ModuleLoader__.load({
|
|
|
4935
5128
|
const pathBar = react.default.createElement("div", {
|
|
4936
5129
|
className: "edrv-pathbar",
|
|
4937
5130
|
title: active || ""
|
|
4938
|
-
}, react.default.createElement("span", { className: "edrv-pb-name" }, active ? String(active).split(/[\\/]/).pop() : "未打开文件"), react.default.createElement("span", { className: "edrv-pb-full" }, active || "使用右上搜索框 (" + (chordOf("edrv.quickOpen") ?? "Ctrl+P") + ") 打开文件"), active && langOf(active) ? react.default.createElement("span", { className: "edrv-pb-meta" }, langOf(active)) : null, cursor ? react.default.createElement("span", { className: "edrv-pb-meta" }, cursor) : null, status ? react.default.createElement("span", { className: "edrv-pb-meta edrv-pb-status" }, status) : null);
|
|
5131
|
+
}, react.default.createElement("span", { className: "edrv-pb-name" }, active ? String(active).split(/[\\/]/).pop() : "未打开文件"), react.default.createElement("span", { className: "edrv-pb-full" }, active || "使用右上搜索框 (" + (chordOf("edrv.quickOpen") ?? "Ctrl+P") + ") 打开文件"), active && !isImageActive && langOf(active) ? react.default.createElement("span", { className: "edrv-pb-meta" }, langOf(active)) : null, isImageActive && imgSize ? react.default.createElement("span", { className: "edrv-pb-meta" }, imgSize.w + "×" + imgSize.h) : null, cursor ? react.default.createElement("span", { className: "edrv-pb-meta" }, cursor) : null, status ? react.default.createElement("span", { className: "edrv-pb-meta edrv-pb-status" }, status) : null);
|
|
4939
5132
|
const lspLanguage = active ? langOf(active) : "";
|
|
4940
5133
|
const lspServer = lspServers.find((item) => item.languageId === lspLanguage);
|
|
4941
5134
|
const lspLabel = lspServer ? "LSP " + lspLanguage + " · " + ({
|
|
@@ -5041,6 +5234,8 @@ window.__ModuleLoader__.load({
|
|
|
5041
5234
|
});
|
|
5042
5235
|
loadContent(active, sessionId);
|
|
5043
5236
|
});
|
|
5237
|
+
else if (isImageActive && imageSrc) body = imagePanel();
|
|
5238
|
+
else if (isImageActive) body = loadingBody(loadStage.message || "读取图片…", loadStage.progress);
|
|
5044
5239
|
else if (content === null) body = loadingBody(loadStage.message || "读取文件内容…", loadStage.progress);
|
|
5045
5240
|
else body = react.default.createElement("div", { className: "edrv-monaco-host" }, react.default.createElement("div", { ref: ensureEditor }));
|
|
5046
5241
|
const hoverEl = hoverAct && !launcherOpen ? react.default.createElement("div", {
|
|
@@ -5715,6 +5910,36 @@ window.__ModuleLoader__.load({
|
|
|
5715
5910
|
if (owner[key] === patched) owner[key] = original;
|
|
5716
5911
|
};
|
|
5717
5912
|
}
|
|
5913
|
+
/**
|
|
5914
|
+
* accessor 感知的链式补丁:DSH 0.1.3+ 的 remote 命名空间方法(如
|
|
5915
|
+
* remote.session.openWorkspacePath)是 getter-only accessor(Object.defineProperty
|
|
5916
|
+
* 仅定义 get,直接赋值在严格模式下抛 TypeError),须整体替换属性描述符。
|
|
5917
|
+
* 新 getter 每次访问现场调用原 getter 取最新一次性函数作 original,天然跟随
|
|
5918
|
+
* DSH 内部 methods 表变化;data 属性降级走 patchMethod;属性缺失返回 null。
|
|
5919
|
+
* @author ddj 2026年09月08号
|
|
5920
|
+
* @param owner 目标对象
|
|
5921
|
+
* @param key 属性名
|
|
5922
|
+
* @param wrapper 包装实现(original 为补丁前的实时实现)
|
|
5923
|
+
* @returns 恢复函数(幂等、带归属校验);属性缺失时返回 null
|
|
5924
|
+
*/
|
|
5925
|
+
function patchAccessor(owner, key, wrapper) {
|
|
5926
|
+
const descriptor = Object.getOwnPropertyDescriptor(owner, key);
|
|
5927
|
+
if (!descriptor) return null;
|
|
5928
|
+
if (typeof descriptor.get !== "function") return patchMethod(owner, key, wrapper);
|
|
5929
|
+
const rawGet = descriptor.get;
|
|
5930
|
+
const patchedGet = function() {
|
|
5931
|
+
const original = (...args) => rawGet.call(owner)(...args);
|
|
5932
|
+
return (...args) => wrapper(original, ...args);
|
|
5933
|
+
};
|
|
5934
|
+
Object.defineProperty(owner, key, {
|
|
5935
|
+
configurable: true,
|
|
5936
|
+
enumerable: descriptor.enumerable,
|
|
5937
|
+
get: patchedGet
|
|
5938
|
+
});
|
|
5939
|
+
return () => {
|
|
5940
|
+
if (Object.getOwnPropertyDescriptor(owner, key)?.get === patchedGet) Object.defineProperty(owner, key, descriptor);
|
|
5941
|
+
};
|
|
5942
|
+
}
|
|
5718
5943
|
//#endregion
|
|
5719
5944
|
//#region src/client/fileOpeners.ts
|
|
5720
5945
|
/**
|
|
@@ -6892,6 +7117,219 @@ window.__ModuleLoader__.load({
|
|
|
6892
7117
|
}, "⟳ 刷新")), workspacePager, workspaceBlocks)), movedBody, configBody, guideBody, confirmBody);
|
|
6893
7118
|
}
|
|
6894
7119
|
//#endregion
|
|
7120
|
+
//#region src/client/ui/IntegrationSettings.ts
|
|
7121
|
+
/**
|
|
7122
|
+
* dsh-vscode-mode client — 「系统集成」设置面板(跨平台)。
|
|
7123
|
+
* 右键菜单:Windows=HKCU 三类键(reg)/ Linux=Nautilus 脚本 + Dolphin 服务菜单(文件写入)/
|
|
7124
|
+
* macOS=Automator 快速操作(一键复制配方手动创建,自动检测/移除)。
|
|
7125
|
+
* 深链基址来自设置 integrationBaseUrl(settings.set 持久化)。
|
|
7126
|
+
* Unity 集成:登记项目根 + 一键安装/更新内嵌包到 <项目>/Packages/com.dsh.editor。
|
|
7127
|
+
* 作者 ddj 2026-09-07
|
|
7128
|
+
*/
|
|
7129
|
+
/** launcher 状态文案。 */
|
|
7130
|
+
function launcherText(launcher) {
|
|
7131
|
+
if (!launcher?.present) return "未安装";
|
|
7132
|
+
return launcher.compiled ? "已编译 exe(无闪窗)" : "已安装";
|
|
7133
|
+
}
|
|
7134
|
+
/** 注册条目状态文案(跨平台 entries)。 */
|
|
7135
|
+
function entriesText(entries) {
|
|
7136
|
+
if (!entries?.length) return "本平台无自动注册入口";
|
|
7137
|
+
return entries.map((entry) => entry.label + (entry.registered ? " ✓" : " ✗")).join(" · ");
|
|
7138
|
+
}
|
|
7139
|
+
/** macOS Automator 快速操作配方(复制到剪贴板,用户按步骤创建)。 */
|
|
7140
|
+
function automatorRecipe(launcherPath) {
|
|
7141
|
+
return [
|
|
7142
|
+
"macOS Finder 快速操作(Automator)配置步骤:",
|
|
7143
|
+
"1. Automator → 新建 → 快速操作",
|
|
7144
|
+
"2. 「工作流程接收当前」选:文件或文件夹;位于:任何应用程序",
|
|
7145
|
+
"3. 添加动作「运行 Shell 脚本」,Shell 选 /bin/bash,传递输入选「作为参数」",
|
|
7146
|
+
"4. 脚本内容替换为:",
|
|
7147
|
+
" exec \"" + launcherPath + "\" \"$@\"",
|
|
7148
|
+
"5. 保存为:在 DSH 文件编辑中打开",
|
|
7149
|
+
"完成后在 Finder 右键 → 快速操作/服务 中出现「在 DSH 文件编辑中打开」。"
|
|
7150
|
+
].join("\n");
|
|
7151
|
+
}
|
|
7152
|
+
/** 单个 Unity 项目行。 */
|
|
7153
|
+
function ProjectRow({ project, busy, onInstall, onRemove }) {
|
|
7154
|
+
const versionText = project.missingDir ? "目录不存在" : project.error ? project.error : project.installedVersion ? "已装 v" + project.installedVersion + (project.upToDate ? "(最新)" : "(可更新)") : "未安装";
|
|
7155
|
+
const installable = !project.missingDir && !project.error;
|
|
7156
|
+
return react.default.createElement("div", {
|
|
7157
|
+
className: "vsm-general-row",
|
|
7158
|
+
key: project.path
|
|
7159
|
+
}, react.default.createElement("span", null, project.title, react.default.createElement("small", { className: "vsm-devform-note" }, " " + project.path + " · " + versionText)), react.default.createElement("div", { className: "vsm-devform-actions" }, react.default.createElement("button", {
|
|
7160
|
+
className: "vsm-primary vsm-small",
|
|
7161
|
+
disabled: !installable || busy === "install:" + project.path,
|
|
7162
|
+
title: project.upToDate ? "重新安装(整目录替换)" : "安装/更新到该 Unity 项目",
|
|
7163
|
+
onClick: () => onInstall(project.path)
|
|
7164
|
+
}, busy === "install:" + project.path ? "安装中…" : project.installedVersion ? project.upToDate ? "重装" : "更新" : "安装"), react.default.createElement("button", {
|
|
7165
|
+
disabled: busy === "remove:" + project.path,
|
|
7166
|
+
onClick: () => onRemove(project.path)
|
|
7167
|
+
}, "移除登记")));
|
|
7168
|
+
}
|
|
7169
|
+
/** 平台提示条(注册语义差异说明)。 */
|
|
7170
|
+
function PlatformNote({ platform, registered }) {
|
|
7171
|
+
if (platform === "win32") return null;
|
|
7172
|
+
const text = platform === "linux" ? "Linux 注册覆盖 GNOME Files(Nautilus)与 KDE Dolphin 两个主流文件管理器(文件写入即注册,无需管理员);其他文件管理器暂未支持。" : "macOS:点「复制 Automator 配方」按提示创建快速操作(约 1 分钟),创建后此处自动显示 ✓;「移除注册」仅删除引用本插件 launcher 的快速操作。";
|
|
7173
|
+
return react.default.createElement("small", { className: "vsm-devform-msg" }, text);
|
|
7174
|
+
}
|
|
7175
|
+
/**
|
|
7176
|
+
* 系统集成面板(挂在 VSCodeMode 设置「通用」页底部)。
|
|
7177
|
+
* @author ddj 2026年09月07号
|
|
7178
|
+
*/
|
|
7179
|
+
function IntegrationSettings() {
|
|
7180
|
+
const settings = react.default.useContext(SettingsContext);
|
|
7181
|
+
const [status, setStatus] = react.default.useState(null);
|
|
7182
|
+
const [statusError, setStatusError] = react.default.useState("");
|
|
7183
|
+
const [unity, setUnity] = react.default.useState(null);
|
|
7184
|
+
const [error, setError] = react.default.useState("");
|
|
7185
|
+
const [message, setMessage] = react.default.useState("");
|
|
7186
|
+
const [busy, setBusy] = react.default.useState("");
|
|
7187
|
+
const [baseUrl, setBaseUrl] = react.default.useState("");
|
|
7188
|
+
const [baseDraft, setBaseDraft] = react.default.useState(null);
|
|
7189
|
+
const [rootDraft, setRootDraft] = react.default.useState("");
|
|
7190
|
+
react.default.useEffect(() => {
|
|
7191
|
+
let alive = true;
|
|
7192
|
+
Promise.all([rpc("edrv.integration.status", {}), rpc("edrv.unity.list", {})]).then(([st, ul]) => {
|
|
7193
|
+
if (!alive) return;
|
|
7194
|
+
if (st.ok) {
|
|
7195
|
+
setStatus(st);
|
|
7196
|
+
setBaseUrl(st.baseUrl);
|
|
7197
|
+
setStatusError("");
|
|
7198
|
+
} else setStatusError(String(st.error));
|
|
7199
|
+
if (ul.ok) setUnity(ul);
|
|
7200
|
+
}).catch((e) => {
|
|
7201
|
+
if (alive) setError(String(e));
|
|
7202
|
+
});
|
|
7203
|
+
return () => {
|
|
7204
|
+
alive = false;
|
|
7205
|
+
};
|
|
7206
|
+
}, []);
|
|
7207
|
+
const platform = status?.platform ?? "";
|
|
7208
|
+
const onWin = platform === "win32";
|
|
7209
|
+
const onLinux = platform === "linux";
|
|
7210
|
+
const onMac = platform === "darwin";
|
|
7211
|
+
const anyRegistered = (status?.entries ?? []).some((entry) => entry.registered && !entry.manual);
|
|
7212
|
+
const refreshUnity = () => rpc("edrv.unity.list", {}).then((result) => {
|
|
7213
|
+
if (result.ok) setUnity(result);
|
|
7214
|
+
});
|
|
7215
|
+
/** 注册/移除右键菜单(Windows/Linux;macOS 注册按钮替换为配方复制)。 */
|
|
7216
|
+
const runMenu = (key, method, done) => {
|
|
7217
|
+
setBusy(key);
|
|
7218
|
+
setError("");
|
|
7219
|
+
setMessage("");
|
|
7220
|
+
rpc(method, {}).then((result) => {
|
|
7221
|
+
if (!result.ok) throw new Error(result.error);
|
|
7222
|
+
setStatus(result);
|
|
7223
|
+
setMessage(done);
|
|
7224
|
+
}).catch((e) => setError(String(e?.message ?? e))).finally(() => setBusy(""));
|
|
7225
|
+
};
|
|
7226
|
+
const register = () => runMenu("register", "edrv.integration.register", onLinux ? "已注册:在 Nautilus / Dolphin 中右键文件或文件夹即可看到「在 DSH 文件编辑中打开」(首次出现可能需重启文件管理器)。" : "右键菜单已注册:右键文件/文件夹 →「在 DSH 文件编辑中打开」。");
|
|
7227
|
+
const unregister = () => runMenu("unregister", "edrv.integration.unregister", "右键菜单注册已移除。");
|
|
7228
|
+
/** 复制 macOS Automator 配方(含 launcher 实际路径)。 */
|
|
7229
|
+
const copyRecipe = () => {
|
|
7230
|
+
const text = automatorRecipe(status?.launcher?.path ?? "$HOME/.dsh/dsh-vscode-mode/shell/dsh-open.sh");
|
|
7231
|
+
navigator.clipboard?.writeText(text).then(() => setMessage("配方已复制,粘贴到备忘录按步骤操作即可。")).catch(() => setMessage(text));
|
|
7232
|
+
};
|
|
7233
|
+
/** 保存深链基址(settings.set 持久化;已注册时提示重新注册刷新 launcher ini)。 */
|
|
7234
|
+
const saveBase = () => {
|
|
7235
|
+
const next = String(baseDraft ?? "").trim();
|
|
7236
|
+
if (!next) return;
|
|
7237
|
+
if (!settings?.set) {
|
|
7238
|
+
setError("设置服务不可用");
|
|
7239
|
+
return;
|
|
7240
|
+
}
|
|
7241
|
+
setBusy("base");
|
|
7242
|
+
setError("");
|
|
7243
|
+
setMessage("");
|
|
7244
|
+
settings.set("integrationBaseUrl", next).then(() => {
|
|
7245
|
+
setBaseUrl(next);
|
|
7246
|
+
setMessage("深链基址已保存;如已注册右键菜单,请点「注册」刷新 launcher 配置。");
|
|
7247
|
+
}).catch((e) => setError(String(e))).finally(() => setBusy(""));
|
|
7248
|
+
};
|
|
7249
|
+
/** 登记 Unity 项目(校验 Assets/ProjectSettings)。 */
|
|
7250
|
+
const addProject = () => {
|
|
7251
|
+
setBusy("add");
|
|
7252
|
+
setError("");
|
|
7253
|
+
setMessage("");
|
|
7254
|
+
rpc("edrv.unity.add", { path: rootDraft.trim() }).then((result) => {
|
|
7255
|
+
if (!result.ok) throw new Error(result.error);
|
|
7256
|
+
setRootDraft("");
|
|
7257
|
+
setMessage("项目已登记,点「安装」把包复制进项目 Packages/ 目录。");
|
|
7258
|
+
return refreshUnity();
|
|
7259
|
+
}).catch((e) => setError(String(e?.message ?? e))).finally(() => setBusy(""));
|
|
7260
|
+
};
|
|
7261
|
+
/** 一键安装/更新(整目录替换内嵌包)。 */
|
|
7262
|
+
const installProject = (path) => {
|
|
7263
|
+
setBusy("install:" + path);
|
|
7264
|
+
setError("");
|
|
7265
|
+
setMessage("");
|
|
7266
|
+
rpc("edrv.unity.install", { path }).then((result) => {
|
|
7267
|
+
if (!result.ok) throw new Error(result.error);
|
|
7268
|
+
const project = result.project;
|
|
7269
|
+
setMessage("已安装/更新:" + project.title + "(v" + project.installedVersion + ")。Unity 已打开时切回窗口即生效。");
|
|
7270
|
+
return refreshUnity();
|
|
7271
|
+
}).catch((e) => setError(String(e?.message ?? e))).finally(() => setBusy(""));
|
|
7272
|
+
};
|
|
7273
|
+
/** 移除登记(不删除已安装的包目录)。 */
|
|
7274
|
+
const removeProject = (path) => {
|
|
7275
|
+
if (!window.confirm("移除该项目的登记?(不删除已安装的包目录)")) return;
|
|
7276
|
+
setBusy("remove:" + path);
|
|
7277
|
+
setError("");
|
|
7278
|
+
setMessage("");
|
|
7279
|
+
rpc("edrv.unity.remove", { path }).then(refreshUnity).catch((e) => setError(String(e))).finally(() => setBusy(""));
|
|
7280
|
+
};
|
|
7281
|
+
/** 复制包源路径(手动 Add package from disk 兜底)。 */
|
|
7282
|
+
const copySource = () => {
|
|
7283
|
+
const text = unity?.sourcePath ?? "";
|
|
7284
|
+
if (!text) return;
|
|
7285
|
+
navigator.clipboard?.writeText(text).then(() => setMessage("已复制包源路径:" + text)).catch(() => setMessage("包源路径:" + text));
|
|
7286
|
+
};
|
|
7287
|
+
return 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("p", { style: { margin: "0 0 8px" } }, "把 DSH 编辑器接入系统入口:文件管理器右键菜单(Windows/Linux/macOS)+ Unity 外部脚本编辑器(UPM 内嵌包,自动发现,无需改 manifest.json)。"), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), message && react.default.createElement("small", { className: "vsm-devform-msg" }, message), status && !onWin && react.default.createElement(PlatformNote, {
|
|
7288
|
+
platform,
|
|
7289
|
+
registered: anyRegistered
|
|
7290
|
+
}), react.default.createElement("label", { className: "vsm-general-row" }, react.default.createElement("span", null, "DSH 服务地址"), react.default.createElement("input", {
|
|
7291
|
+
value: baseDraft ?? baseUrl,
|
|
7292
|
+
placeholder: "http://127.0.0.1:3080",
|
|
7293
|
+
onChange: (event) => setBaseDraft(event.target.value),
|
|
7294
|
+
onKeyDown: (event) => {
|
|
7295
|
+
if (event.key === "Enter") saveBase();
|
|
7296
|
+
}
|
|
7297
|
+
}), react.default.createElement("button", {
|
|
7298
|
+
disabled: busy === "base" || baseDraft === null,
|
|
7299
|
+
onClick: saveBase
|
|
7300
|
+
}, "保存")), react.default.createElement("label", { className: "vsm-general-row" }, react.default.createElement("span", null, "右键菜单 ", react.default.createElement("small", { className: "vsm-devform-note" }, status ? entriesText(status.entries) + " · launcher:" + launcherText(status.launcher) : statusError ? "状态获取失败:" + statusError : "读取中…")), react.default.createElement("div", { className: "vsm-devform-actions" }, (onWin || onLinux) && react.default.createElement("button", {
|
|
7301
|
+
className: "vsm-primary vsm-small",
|
|
7302
|
+
disabled: busy === "register",
|
|
7303
|
+
onClick: register
|
|
7304
|
+
}, busy === "register" ? "注册中…" : "注册"), onMac && react.default.createElement("button", {
|
|
7305
|
+
className: "vsm-primary vsm-small",
|
|
7306
|
+
onClick: copyRecipe
|
|
7307
|
+
}, "复制 Automator 配方"), status && react.default.createElement("button", {
|
|
7308
|
+
disabled: busy === "unregister" || !anyRegistered && !onMac,
|
|
7309
|
+
onClick: unregister
|
|
7310
|
+
}, "移除注册"))), react.default.createElement("div", {
|
|
7311
|
+
className: "vsm-general-row",
|
|
7312
|
+
style: { alignItems: "flex-start" }
|
|
7313
|
+
}, react.default.createElement("span", null, "Unity 一键安装 ", react.default.createElement("small", { className: "vsm-devform-note" }, unity ? "包 v" + (unity.sourceVersion ?? "?") + " · " + unity.sourcePath : "读取中…")), react.default.createElement("div", { className: "vsm-devform-actions" }, react.default.createElement("button", { onClick: copySource }, "复制包源路径"))), (unity?.projects ?? []).map((project) => react.default.createElement(ProjectRow, {
|
|
7314
|
+
key: project.path,
|
|
7315
|
+
project,
|
|
7316
|
+
busy,
|
|
7317
|
+
onInstall: installProject,
|
|
7318
|
+
onRemove: removeProject
|
|
7319
|
+
})), react.default.createElement("label", { className: "vsm-general-row" }, react.default.createElement("span", null, "添加 Unity 项目"), react.default.createElement("input", {
|
|
7320
|
+
value: rootDraft,
|
|
7321
|
+
placeholder: "项目根目录(含 Assets 与 ProjectSettings)",
|
|
7322
|
+
onChange: (event) => setRootDraft(event.target.value),
|
|
7323
|
+
onKeyDown: (event) => {
|
|
7324
|
+
if (event.key === "Enter") addProject();
|
|
7325
|
+
}
|
|
7326
|
+
}), react.default.createElement("button", {
|
|
7327
|
+
className: "vsm-primary vsm-small",
|
|
7328
|
+
disabled: busy === "add" || !rootDraft.trim(),
|
|
7329
|
+
onClick: addProject
|
|
7330
|
+
}, busy === "add" ? "校验中…" : "添加")), react.default.createElement("small", null, "安装 = 复制到 <项目>/Packages/com.dsh.editor(内嵌包);更新 = 整目录替换;卸载 = 删除该目录。Unity 打开时切回窗口自动刷新生效。插件卸载/重载时会自动清理并恢复右键菜单注册(「移除注册」后不再恢复)。")));
|
|
7331
|
+
}
|
|
7332
|
+
//#endregion
|
|
6895
7333
|
//#region src/client/ui/McpSettings.ts
|
|
6896
7334
|
/**
|
|
6897
7335
|
* dsh-vscode-mode client — VSCodeMode 设置区:通用 / 快捷键 / MCP 管理 / 语言服务器 / 性能优化 / 兼容性。
|
|
@@ -7152,7 +7590,7 @@ window.__ModuleLoader__.load({
|
|
|
7152
7590
|
onKeyDown: (event) => {
|
|
7153
7591
|
if (event.key === "Enter") commitMinW();
|
|
7154
7592
|
}
|
|
7155
|
-
}), react.default.createElement("small", null, "180–560 px;拖拽低于该宽度自动隐藏")))));
|
|
7593
|
+
}), react.default.createElement("small", null, "180–560 px;拖拽低于该宽度自动隐藏")))), react.default.createElement(IntegrationSettings, null));
|
|
7156
7594
|
}
|
|
7157
7595
|
/** 兼容性小节:host 报告(RPC compat)+ 客户端本地适配项。 */
|
|
7158
7596
|
function CompatSection({ getSummary }) {
|
|
@@ -7498,6 +7936,551 @@ window.__ModuleLoader__.load({
|
|
|
7498
7936
|
return typeof value === "string" && value.trim() ? value.trim() : AUTO_OPEN_TOOL;
|
|
7499
7937
|
}
|
|
7500
7938
|
//#endregion
|
|
7939
|
+
//#region src/client/remoteOpenRouter.ts
|
|
7940
|
+
/**
|
|
7941
|
+
* dsh-vscode-mode client — DSH 对话文件链接的 0.1.3+ 路由(remote.session 面)。
|
|
7942
|
+
* 0.1.3 起 dsh-client-ui-chat 的 openFile 直接调 ctx.remote.session.openWorkspacePath
|
|
7943
|
+
* (浏览器→host RPC session/openWorkspacePath),不再经过 workspaces.openPath,
|
|
7944
|
+
* 旧路由补丁因此落空;该方法是 getter-only accessor,补丁经 compat.patchAccessor
|
|
7945
|
+
* 换描述符安装。行为与 openPathRouter 对齐:插件打开器优先,"."(打开工作区文件夹)、
|
|
7946
|
+
* 无可用打开器或打开失败时透传原实现(系统默认应用打开)。
|
|
7947
|
+
* 纯逻辑,node 环境可单测(不依赖 DOM / events 模块)。
|
|
7948
|
+
* 作者 ddj 2026年09月08号
|
|
7949
|
+
*/
|
|
7950
|
+
/** 插件打开器接管成功时返回给调用方(chat 只看 ok,产物区读 value)。 */
|
|
7951
|
+
const OPENED_RESULT = {
|
|
7952
|
+
ok: true,
|
|
7953
|
+
value: { opened: true }
|
|
7954
|
+
};
|
|
7955
|
+
/**
|
|
7956
|
+
* 结构化探测 remote.session 命名空间服务及其 openWorkspacePath 方法。
|
|
7957
|
+
* 旧版 DSH 无该 namespace、或 api-remotes 尚未挂载时返回 undefined。
|
|
7958
|
+
* @author ddj 2026年09月08号
|
|
7959
|
+
* @param ctx 客户端服务上下文
|
|
7960
|
+
* @returns 命名空间服务实例;不可用返回 undefined
|
|
7961
|
+
*/
|
|
7962
|
+
function probeRemoteOpen(ctx) {
|
|
7963
|
+
try {
|
|
7964
|
+
const service = ctx.get("remote.session");
|
|
7965
|
+
if (!service || typeof service.openWorkspacePath !== "function") return void 0;
|
|
7966
|
+
return service;
|
|
7967
|
+
} catch {
|
|
7968
|
+
return;
|
|
7969
|
+
}
|
|
7970
|
+
}
|
|
7971
|
+
/**
|
|
7972
|
+
* 安装可卸载的 remote.session.openWorkspacePath 路由(0.1.3+ 会话文件链接主路径)。
|
|
7973
|
+
* @author ddj 2026年09月08号
|
|
7974
|
+
* @param service probeRemoteOpen 命中的命名空间服务
|
|
7975
|
+
* @param options 路由依赖
|
|
7976
|
+
* @returns 恢复函数;目标属性缺失时返回 null(调用方停止安装并告警)
|
|
7977
|
+
*/
|
|
7978
|
+
function patchRemoteOpen(service, options) {
|
|
7979
|
+
return patchAccessor(service, "openWorkspacePath", async (original, ...args) => {
|
|
7980
|
+
const fallback = original;
|
|
7981
|
+
const request = args[0] ?? {};
|
|
7982
|
+
const path = typeof request.path === "string" ? request.path : "";
|
|
7983
|
+
if (!path || path === ".") return fallback(request);
|
|
7984
|
+
const opener = selectOpener(options.registry, options.selected());
|
|
7985
|
+
if (!opener || !isAvailable(opener)) return fallback(request);
|
|
7986
|
+
try {
|
|
7987
|
+
await opener.open(path, options.context());
|
|
7988
|
+
return OPENED_RESULT;
|
|
7989
|
+
} catch (error) {
|
|
7990
|
+
options.logger?.("文件打开器 " + opener.id + " 失败,回退系统打开: " + String(error));
|
|
7991
|
+
return fallback(request);
|
|
7992
|
+
}
|
|
7993
|
+
});
|
|
7994
|
+
}
|
|
7995
|
+
//#endregion
|
|
7996
|
+
//#region src/shared/externalOpen.ts
|
|
7997
|
+
/**
|
|
7998
|
+
* dsh-vscode-mode shared — 外部深链 URL 契约(Windows launcher / Unity 包 / client 共用)。
|
|
7999
|
+
* 形态:?<FLAG>=1&<PATHS_KEY>=<enc1>[,<enc2>…] [&<LINE_KEY>=N] [&<COLUMN_KEY>=M]
|
|
8000
|
+
* 每个路径独立 percent-encode(encodeURIComponent / Uri.EscapeDataString)后以 `,` 连接:
|
|
8001
|
+
* 编码结果不含裸逗号(Windows 路径本身不允许逗号,编码兜底其余字符),URLSearchParams
|
|
8002
|
+
* 取值做一次标准解码即得 `enc1,enc2`,按 `,` split 即路径,无需二次 decode。
|
|
8003
|
+
* Purity rule: no node/react imports(URLSearchParams 为标准全局)。
|
|
8004
|
+
* 作者 ddj 2026-09-07
|
|
8005
|
+
*/
|
|
8006
|
+
/** 深链开关参数名(值恒为 '1')。 */
|
|
8007
|
+
const EDRV_OPEN_FLAG = "edrvOpen";
|
|
8008
|
+
/** 路径列表参数名(逗号分隔的已编码路径)。 */
|
|
8009
|
+
const EDRV_PATHS_KEY = "edrvPaths";
|
|
8010
|
+
/** 目标行参数名(1-based,可选,作用于首个路径)。 */
|
|
8011
|
+
const EDRV_LINE_KEY = "edrvLine";
|
|
8012
|
+
/** 目标列参数名(1-based,可选,作用于首个路径)。 */
|
|
8013
|
+
const EDRV_COLUMN_KEY = "edrvColumn";
|
|
8014
|
+
/** 深链参数键全集(URL 清理用)。 */
|
|
8015
|
+
const EDRV_PARAM_KEYS = [
|
|
8016
|
+
EDRV_OPEN_FLAG,
|
|
8017
|
+
EDRV_PATHS_KEY,
|
|
8018
|
+
EDRV_LINE_KEY,
|
|
8019
|
+
EDRV_COLUMN_KEY
|
|
8020
|
+
];
|
|
8021
|
+
/**
|
|
8022
|
+
* 解析深链查询串:非深链/路径为空 → null;行列仅接受正整数,非法忽略。
|
|
8023
|
+
* @author ddj 2026年09月07号
|
|
8024
|
+
* @param search location.search(含或不含前导 ? 均可)
|
|
8025
|
+
* @returns 深链参数或 null
|
|
8026
|
+
*/
|
|
8027
|
+
function parseOpenParams(search) {
|
|
8028
|
+
const params = new URLSearchParams(search ?? "");
|
|
8029
|
+
if (params.get("edrvOpen") !== "1") return null;
|
|
8030
|
+
const paths = (params.get("edrvPaths") ?? "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
8031
|
+
if (!paths.length) return null;
|
|
8032
|
+
return {
|
|
8033
|
+
paths,
|
|
8034
|
+
line: positiveIntOf(params.get(EDRV_LINE_KEY)),
|
|
8035
|
+
column: positiveIntOf(params.get(EDRV_COLUMN_KEY))
|
|
8036
|
+
};
|
|
8037
|
+
}
|
|
8038
|
+
/**
|
|
8039
|
+
* 正整数解析(非法/非正 → undefined)。
|
|
8040
|
+
* @author ddj 2026年09月07号
|
|
8041
|
+
* @param raw 参数原文(可空)
|
|
8042
|
+
* @returns 正整数或 undefined
|
|
8043
|
+
*/
|
|
8044
|
+
function positiveIntOf(raw) {
|
|
8045
|
+
const value = Number(raw);
|
|
8046
|
+
return Number.isInteger(value) && value > 0 ? value : void 0;
|
|
8047
|
+
}
|
|
8048
|
+
//#endregion
|
|
8049
|
+
//#region src/client/chooseDialog.ts
|
|
8050
|
+
/**
|
|
8051
|
+
* dsh-vscode-mode client — 工作区选择弹窗(深链打开规则 1)。
|
|
8052
|
+
* 复用与「添加 MCP」一致的 vsm-mcp-modal / vsm-mcp-dialog 模态(页面居中卡片 + 遮罩 + 主题
|
|
8053
|
+
* token),React portal 挂 body;Esc / 遮罩点击 / 取消 → null(中止流程不改任何状态)。
|
|
8054
|
+
* 作者 ddj 2026-09-07
|
|
8055
|
+
*/
|
|
8056
|
+
/** 选择弹窗(vsm-mcp-modal 遮罩 + vsm-mcp-dialog 卡片,Esc/遮罩点击取消)。 */
|
|
8057
|
+
function ChoiceModal({ title, folder, done }) {
|
|
8058
|
+
react.default.useEffect(() => {
|
|
8059
|
+
const onKey = (event) => {
|
|
8060
|
+
if (event.key === "Escape") done(null);
|
|
8061
|
+
};
|
|
8062
|
+
window.addEventListener("keydown", onKey);
|
|
8063
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
8064
|
+
}, []);
|
|
8065
|
+
return react.default.createElement("div", {
|
|
8066
|
+
className: "vsm-mcp-modal",
|
|
8067
|
+
onMouseDown: (event) => {
|
|
8068
|
+
if (event.target === event.currentTarget) done(null);
|
|
8069
|
+
}
|
|
8070
|
+
}, react.default.createElement("div", { className: "vsm-mcp-dialog" }, react.default.createElement("h3", null, "在 DSH 中打开"), react.default.createElement("label", null, "文件夹 " + folder + " 位于已注册的工作区「" + title + "」内,选择打开方式:"), react.default.createElement("div", { className: "vsm-mcp-dialog-actions" }, react.default.createElement("button", { onClick: () => done(null) }, "取消"), react.default.createElement("button", { onClick: () => done("create") }, "新建工作区"), react.default.createElement("button", {
|
|
8071
|
+
className: "vsm-primary",
|
|
8072
|
+
onClick: () => done("recent")
|
|
8073
|
+
}, "使用最近的工作区"))));
|
|
8074
|
+
}
|
|
8075
|
+
/**
|
|
8076
|
+
* 弹出工作区选择弹窗(深链打开规则 1)。
|
|
8077
|
+
* @author ddj 2026年09月07号
|
|
8078
|
+
* @param title 匹配到的工作区标题
|
|
8079
|
+
* @param folder 文件夹路径
|
|
8080
|
+
* @returns 用户选择;null = 取消/中止
|
|
8081
|
+
*/
|
|
8082
|
+
function chooseWorkspace(title, folder) {
|
|
8083
|
+
return new Promise((resolve) => {
|
|
8084
|
+
const host = document.createElement("div");
|
|
8085
|
+
document.body.appendChild(host);
|
|
8086
|
+
const root = (0, react_dom_client.createRoot)(host);
|
|
8087
|
+
const done = (choice) => {
|
|
8088
|
+
root.unmount();
|
|
8089
|
+
host.remove();
|
|
8090
|
+
resolve(choice);
|
|
8091
|
+
};
|
|
8092
|
+
root.render(react.default.createElement(ChoiceModal, {
|
|
8093
|
+
title,
|
|
8094
|
+
folder,
|
|
8095
|
+
done
|
|
8096
|
+
}));
|
|
8097
|
+
});
|
|
8098
|
+
}
|
|
8099
|
+
//#endregion
|
|
8100
|
+
//#region src/client/openFlow.ts
|
|
8101
|
+
/**
|
|
8102
|
+
* dsh-vscode-mode client — 深链打开规则引擎(资源管理器右键 / Unity 外部编辑器落地)。
|
|
8103
|
+
* 规则:文件夹 ⊂ 某已注册工作区 → 页面内弹窗二选一(使用最近的工作区 / 新建工作区);
|
|
8104
|
+
* 文件夹不在任何工作区 → 直达最近对话所在工作区新增;文件 → 最近对话 + 编辑器展开
|
|
8105
|
+
* (无任何会话 → 以文件父目录注册工作区引导创建)。首路径驱动规则,其余路径:文件进
|
|
8106
|
+
* 编辑器、文件夹补引用。纯函数可单测;编排依赖经 OpenDeps 注入(mock 可测)。
|
|
8107
|
+
* 作者 ddj 2026-09-07
|
|
8108
|
+
*/
|
|
8109
|
+
/** 等列表/输入就绪的节奏。 */
|
|
8110
|
+
const READY_TIMEOUT_MS = 15e3;
|
|
8111
|
+
const READY_TICK_MS = 300;
|
|
8112
|
+
const REF_RETRY_MAX = 20;
|
|
8113
|
+
const REF_RETRY_MS = 300;
|
|
8114
|
+
const REST_STEP_MS = 150;
|
|
8115
|
+
const LINE_AT_DELAY_MS = 200;
|
|
8116
|
+
/**
|
|
8117
|
+
* 路径规范化:`\→/`、小写、去尾分隔符(Windows 大小写不敏感前缀匹配用)。
|
|
8118
|
+
* @author ddj 2026年09月07号
|
|
8119
|
+
*/
|
|
8120
|
+
function normPath(p) {
|
|
8121
|
+
return String(p ?? "").replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
8122
|
+
}
|
|
8123
|
+
/**
|
|
8124
|
+
* 目录包含判定:child === root 或 child 位于 root 之下。
|
|
8125
|
+
* @author ddj 2026年09月07号
|
|
8126
|
+
*/
|
|
8127
|
+
function containsPath(root, child) {
|
|
8128
|
+
const r = normPath(root);
|
|
8129
|
+
const c = normPath(child);
|
|
8130
|
+
return c === r || c.startsWith(r + "/");
|
|
8131
|
+
}
|
|
8132
|
+
/**
|
|
8133
|
+
* 父目录(文件引导创建工作区用;根路径回退自身)。
|
|
8134
|
+
* @author ddj 2026年09月07号
|
|
8135
|
+
*/
|
|
8136
|
+
function dirnameOf(p) {
|
|
8137
|
+
const norm = String(p ?? "").replace(/[\\/]+$/, "");
|
|
8138
|
+
const cut = Math.max(norm.lastIndexOf("/"), norm.lastIndexOf("\\"));
|
|
8139
|
+
return cut > 0 ? norm.slice(0, cut) : norm;
|
|
8140
|
+
}
|
|
8141
|
+
/**
|
|
8142
|
+
* 最近一次对话:排除子代理行,updatedAt 最新优先。
|
|
8143
|
+
* @author ddj 2026年09月07号
|
|
8144
|
+
*/
|
|
8145
|
+
function recentSession(byId, ids) {
|
|
8146
|
+
return (ids ?? Object.keys(byId)).map((id) => byId[id]).filter((row) => row && row.origin !== "subagent").sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0))[0];
|
|
8147
|
+
}
|
|
8148
|
+
/**
|
|
8149
|
+
* 文件夹命中的已注册工作区(前缀包含),updatedAt 新者优先。
|
|
8150
|
+
* @author ddj 2026年09月07号
|
|
8151
|
+
*/
|
|
8152
|
+
function matchWorkspaces(items, folder) {
|
|
8153
|
+
return items.filter((ws) => containsPath(ws.path, folder));
|
|
8154
|
+
}
|
|
8155
|
+
/**
|
|
8156
|
+
* 规则分派:目录+命中工作区 → choose;目录未命中 → folderNew(以该文件夹为根注册新工作区,
|
|
8157
|
+
* 用户确认:一律取右键的文件夹本身);文件有会话 → fileRecent;文件无会话 → bootstrap;
|
|
8158
|
+
* 路径缺失 → abort。
|
|
8159
|
+
* @author ddj 2026年09月08号
|
|
8160
|
+
*/
|
|
8161
|
+
function pickRule(kind, matched, hasSession) {
|
|
8162
|
+
if (kind === "missing") return "abort";
|
|
8163
|
+
if (kind === "directory") return matched ? "choose" : "folderNew";
|
|
8164
|
+
return hasSession ? "fileRecent" : "bootstrap";
|
|
8165
|
+
}
|
|
8166
|
+
/**
|
|
8167
|
+
* 深链打开编排入口:等列表就绪 → 判型 → 规则分派 → 编排 create/open/引用/编辑器。
|
|
8168
|
+
* @author ddj 2026年09月07号
|
|
8169
|
+
* @param ctx 客户端根上下文(sessions/workspaces 服务)
|
|
8170
|
+
* @param params 深链参数(首路径驱动规则)
|
|
8171
|
+
* @param overrides 依赖覆盖(测试注入)
|
|
8172
|
+
*/
|
|
8173
|
+
async function openDeepLink(ctx, params, overrides = {}) {
|
|
8174
|
+
const deps = realDeps(ctx, overrides);
|
|
8175
|
+
if (!await waitListsReady(deps)) {
|
|
8176
|
+
deps.notify("DSH 会话列表未就绪,请稍后重试");
|
|
8177
|
+
return;
|
|
8178
|
+
}
|
|
8179
|
+
const primary = params.paths[0];
|
|
8180
|
+
const kind = await deps.stat(primary).catch(() => "missing");
|
|
8181
|
+
const sess = deps.sessions();
|
|
8182
|
+
const rule = pickRule(kind, matchWorkspaces(deps.workspaces().items, primary).length > 0, Boolean(recentSession(sess.byId, sess.ids)));
|
|
8183
|
+
try {
|
|
8184
|
+
if (rule === "abort") {
|
|
8185
|
+
deps.notify("路径不存在:" + primary);
|
|
8186
|
+
return;
|
|
8187
|
+
}
|
|
8188
|
+
if (rule === "choose") return await runChoose(deps, primary, params);
|
|
8189
|
+
if (rule === "folderNew") return await runFolderNew(deps, primary, params);
|
|
8190
|
+
if (rule === "fileRecent") return await runFileRecent(deps, primary, params);
|
|
8191
|
+
return await runFileBootstrap(deps, primary, params);
|
|
8192
|
+
} catch (error) {
|
|
8193
|
+
deps.notify("打开失败:" + String(error?.message ?? error));
|
|
8194
|
+
}
|
|
8195
|
+
}
|
|
8196
|
+
/** 等待会话与工作区列表就绪(上限 15s;超时按当前态判定)。 */
|
|
8197
|
+
async function waitListsReady(deps) {
|
|
8198
|
+
for (let waited = 0; waited < READY_TIMEOUT_MS; waited += READY_TICK_MS) {
|
|
8199
|
+
if (deps.sessions().ready && deps.workspaces().ready) return true;
|
|
8200
|
+
await sleep(deps, READY_TICK_MS);
|
|
8201
|
+
}
|
|
8202
|
+
return deps.sessions().ready && deps.workspaces().ready;
|
|
8203
|
+
}
|
|
8204
|
+
/** 规则 1:命中工作区 → 弹窗分派(取消即中止)。 */
|
|
8205
|
+
async function runChoose(deps, folder, params) {
|
|
8206
|
+
const best = bestMatch(deps, matchWorkspaces(deps.workspaces().items, folder));
|
|
8207
|
+
const choice = await deps.choose(best?.title ?? "工作区", folder);
|
|
8208
|
+
if (choice === null) return;
|
|
8209
|
+
if (choice === "create") return runFolderNew(deps, folder, params);
|
|
8210
|
+
const sessionId = await spawnInWorkspace(deps, (best ?? deps.workspaces().items[0]).workspaceId, void 0);
|
|
8211
|
+
await settleRef(deps, sessionId, folder, "folder");
|
|
8212
|
+
deps.openEditor(null);
|
|
8213
|
+
await openRest(deps, sessionId, params, 1);
|
|
8214
|
+
}
|
|
8215
|
+
/** 规则 3a:文件 → 所属工作区优先(有会话打开其最近会话;无会话新建 + 文件引用);无匹配 → 最近对话。 */
|
|
8216
|
+
async function runFileRecent(deps, file, params) {
|
|
8217
|
+
const ws = pickFileWorkspace(deps, file);
|
|
8218
|
+
if (ws) {
|
|
8219
|
+
const anchor = recentSessionIn(deps, ws);
|
|
8220
|
+
if (anchor) {
|
|
8221
|
+
deps.openSession(anchor.id);
|
|
8222
|
+
deps.openEditor(file, params.line, params.column);
|
|
8223
|
+
await openRest(deps, anchor.id, params, 1);
|
|
8224
|
+
return;
|
|
8225
|
+
}
|
|
8226
|
+
const sessionId = await spawnInWorkspace(deps, ws.workspaceId, void 0);
|
|
8227
|
+
await settleRef(deps, sessionId, file, "file");
|
|
8228
|
+
deps.openEditor(file, params.line, params.column);
|
|
8229
|
+
await openRest(deps, sessionId, params, 1);
|
|
8230
|
+
return;
|
|
8231
|
+
}
|
|
8232
|
+
const anchor = recentSession(deps.sessions().byId, deps.sessions().ids);
|
|
8233
|
+
if (!anchor) return runFileBootstrap(deps, file, params);
|
|
8234
|
+
deps.openSession(anchor.id);
|
|
8235
|
+
deps.openEditor(file, params.line, params.column);
|
|
8236
|
+
await openRest(deps, anchor.id, params, 1);
|
|
8237
|
+
}
|
|
8238
|
+
/**
|
|
8239
|
+
* 文件所属工作区:前缀命中的工作区里,优先取有会话且最近活跃的;都无会话取路径最具体的。
|
|
8240
|
+
* @author ddj 2026年09月08号
|
|
8241
|
+
*/
|
|
8242
|
+
function pickFileWorkspace(deps, file) {
|
|
8243
|
+
const matches = matchWorkspaces(deps.workspaces().items, file);
|
|
8244
|
+
if (!matches.length) return void 0;
|
|
8245
|
+
const withSessions = matches.filter((ws) => (ws.sessionIds ?? []).some((id) => Boolean(deps.sessions().byId[id])));
|
|
8246
|
+
if (!withSessions.length) return [...matches].sort((a, b) => normPath(b.path).length - normPath(a.path).length)[0];
|
|
8247
|
+
return withSessions.sort((a, b) => latestSessionAt(deps, b) - latestSessionAt(deps, a))[0];
|
|
8248
|
+
}
|
|
8249
|
+
/** 工作区内会话的最近活跃时间(无成员 → 0)。 */
|
|
8250
|
+
function latestSessionAt(deps, ws) {
|
|
8251
|
+
const byId = deps.sessions().byId;
|
|
8252
|
+
return (ws.sessionIds ?? []).reduce((max, id) => Math.max(max, byId[id]?.updatedAt ?? 0), 0);
|
|
8253
|
+
}
|
|
8254
|
+
/** 工作区内最近一次会话(排除子代理与不在列表中的陈旧行)。 */
|
|
8255
|
+
function recentSessionIn(deps, ws) {
|
|
8256
|
+
const byId = deps.sessions().byId;
|
|
8257
|
+
return (ws.sessionIds ?? []).map((id) => byId[id]).filter((row) => row && row.origin !== "subagent").sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0))[0];
|
|
8258
|
+
}
|
|
8259
|
+
/** 规则 3b:文件 + 无任何会话 → 父目录注册工作区 + 新对话 + 文件引用 + 编辑器打开。 */
|
|
8260
|
+
async function runFileBootstrap(deps, file, params) {
|
|
8261
|
+
const sessionId = await spawnInWorkspace(deps, (await deps.createWorkspace(dirnameOf(file))).workspaceId, void 0);
|
|
8262
|
+
await settleRef(deps, sessionId, file, "file");
|
|
8263
|
+
deps.openEditor(file, params.line, params.column);
|
|
8264
|
+
await openRest(deps, sessionId, params, 1);
|
|
8265
|
+
}
|
|
8266
|
+
/** 规则 1.2:以文件夹注册新工作区 + 新对话 + 文件夹引用 + 编辑页。 */
|
|
8267
|
+
async function runFolderNew(deps, folder, params) {
|
|
8268
|
+
const sessionId = await spawnInWorkspace(deps, (await deps.createWorkspace(folder)).workspaceId, void 0);
|
|
8269
|
+
await settleRef(deps, sessionId, folder, "folder");
|
|
8270
|
+
deps.openEditor(null);
|
|
8271
|
+
await openRest(deps, sessionId, params, 1);
|
|
8272
|
+
}
|
|
8273
|
+
/** 在指定工作区(或回退 cwd)新建对话并置为当前;返回新会话 id。 */
|
|
8274
|
+
async function spawnInWorkspace(deps, workspaceId, fallbackCwd) {
|
|
8275
|
+
const sessionId = await deps.createSession(workspaceId ? { workspaceId } : fallbackCwd ? { cwd: fallbackCwd } : {});
|
|
8276
|
+
deps.openSession(sessionId);
|
|
8277
|
+
return sessionId;
|
|
8278
|
+
}
|
|
8279
|
+
/** 命中工作区集中「最近」的一个(其成员会话 updatedAt 最新者优先)。 */
|
|
8280
|
+
function bestMatch(deps, matches) {
|
|
8281
|
+
const byId = deps.sessions().byId;
|
|
8282
|
+
const score = (ws) => {
|
|
8283
|
+
const sessionLatest = (ws.sessionIds ?? []).reduce((max, id) => Math.max(max, byId[id]?.updatedAt ?? 0), 0);
|
|
8284
|
+
return Math.max(sessionLatest, Date.parse(ws.updatedAt ?? "") || 0);
|
|
8285
|
+
};
|
|
8286
|
+
return [...matches].sort((a, b) => score(b) - score(a))[0];
|
|
8287
|
+
}
|
|
8288
|
+
/** 引用插入轮询:输入门面就绪前每 300ms 重试(appendReference 忙态已降级视为成功)。 */
|
|
8289
|
+
async function settleRef(deps, sessionId, path, appearance) {
|
|
8290
|
+
for (let attempt = 0; attempt < REF_RETRY_MAX; attempt++) {
|
|
8291
|
+
if (await deps.reference(sessionId, path, appearance)) return;
|
|
8292
|
+
await sleep(deps, REF_RETRY_MS);
|
|
8293
|
+
}
|
|
8294
|
+
}
|
|
8295
|
+
/** 其余路径:文件进编辑器(步进),文件夹补文件夹引用。 */
|
|
8296
|
+
async function openRest(deps, sessionId, params, from) {
|
|
8297
|
+
const rest = params.paths.slice(from);
|
|
8298
|
+
for (const [index, path] of rest.entries()) {
|
|
8299
|
+
const kind = await deps.stat(path).catch(() => "missing");
|
|
8300
|
+
if (kind === "file") deps.schedule(() => deps.openEditor(path), REST_STEP_MS * (index + 1));
|
|
8301
|
+
else if (kind === "directory" && sessionId) await settleRef(deps, sessionId, path, "folder");
|
|
8302
|
+
}
|
|
8303
|
+
}
|
|
8304
|
+
/** 可注入 sleep。 */
|
|
8305
|
+
function sleep(deps, ms) {
|
|
8306
|
+
return new Promise((resolve) => deps.schedule(resolve, ms));
|
|
8307
|
+
}
|
|
8308
|
+
/** 真实依赖装配(全部来自 ctx 注入服务 + 既有 RPC/弹窗/编辑器入口)。 */
|
|
8309
|
+
function realDeps(ctx, overrides = {}) {
|
|
8310
|
+
const service = (name) => {
|
|
8311
|
+
const get = ctx?.get;
|
|
8312
|
+
return typeof get === "function" ? get.call(ctx, name) : void 0;
|
|
8313
|
+
};
|
|
8314
|
+
const add = createAddToConversation(ctx);
|
|
8315
|
+
return {
|
|
8316
|
+
stat: async (path) => {
|
|
8317
|
+
const result = await rpc("edrv.externalStat", { path });
|
|
8318
|
+
return result.ok ? result.kind : "missing";
|
|
8319
|
+
},
|
|
8320
|
+
workspaces: () => {
|
|
8321
|
+
const snap = service("workspaces")?.list?.getSnapshot?.() ?? {};
|
|
8322
|
+
return {
|
|
8323
|
+
items: snap.items ?? [],
|
|
8324
|
+
ready: snap.phase === "ready"
|
|
8325
|
+
};
|
|
8326
|
+
},
|
|
8327
|
+
sessions: () => {
|
|
8328
|
+
const snap = service("sessions")?.list?.getSnapshot?.() ?? {};
|
|
8329
|
+
return {
|
|
8330
|
+
ids: snap.ids ?? [],
|
|
8331
|
+
byId: snap.byId ?? {},
|
|
8332
|
+
ready: snap.phase !== "pending"
|
|
8333
|
+
};
|
|
8334
|
+
},
|
|
8335
|
+
createSession: async (opts) => await service("sessions").create(opts),
|
|
8336
|
+
createWorkspace: async (path) => {
|
|
8337
|
+
const ws = await service("workspaces").create({ path });
|
|
8338
|
+
return {
|
|
8339
|
+
workspaceId: ws.workspaceId,
|
|
8340
|
+
title: ws.title
|
|
8341
|
+
};
|
|
8342
|
+
},
|
|
8343
|
+
openSession: (id) => service("sessions").open(id),
|
|
8344
|
+
reference: async (sessionId, path, appearance) => {
|
|
8345
|
+
return await add.appendReference(sessionId, path, void 0, appearance) !== "unavailable";
|
|
8346
|
+
},
|
|
8347
|
+
choose: (title, folder) => chooseWorkspace(title, folder),
|
|
8348
|
+
openEditor: (path, line, column) => {
|
|
8349
|
+
if (path == null) {
|
|
8350
|
+
openEditorView(null);
|
|
8351
|
+
return;
|
|
8352
|
+
}
|
|
8353
|
+
openEditorView(path);
|
|
8354
|
+
if (line != null) setTimeout(() => emitOpenAt(path, line, column), LINE_AT_DELAY_MS);
|
|
8355
|
+
},
|
|
8356
|
+
schedule: (fn, ms) => setTimeout(fn, ms),
|
|
8357
|
+
notify: (text) => toastDom(text),
|
|
8358
|
+
...overrides
|
|
8359
|
+
};
|
|
8360
|
+
}
|
|
8361
|
+
//#endregion
|
|
8362
|
+
//#region src/client/externalOpen.ts
|
|
8363
|
+
/**
|
|
8364
|
+
* dsh-vscode-mode client — 外部深链落地(Windows 右键菜单 / Unity 外部编辑器)。
|
|
8365
|
+
* 启动时解析 location.search 的 edrvOpen 深链:跨源 referrer 守卫防外部网页诱导;
|
|
8366
|
+
* 处理完剥离 URL 参数防刷新重开;执行委托 openFlow 打开规则引擎(工作区/对话智能路由)。
|
|
8367
|
+
* 另挂 3s 移交轮询(兼活跃心跳):launcher 投递的打开请求由已打开页面就地执行,避免重复开新页。
|
|
8368
|
+
* 作者 ddj 2026-09-07 / 2026-09-08
|
|
8369
|
+
*/
|
|
8370
|
+
/** toast 自隐时长。 */
|
|
8371
|
+
const TOAST_MS = 5e3;
|
|
8372
|
+
/** 深链移交轮询周期(兼活跃心跳)。 */
|
|
8373
|
+
const HANDOFF_POLL_MS = 3e3;
|
|
8374
|
+
/**
|
|
8375
|
+
* 装配外部深链:深链参数缺失时仅保持移交轮询(零额外开销)。
|
|
8376
|
+
* @author ddj 2026年09月07号
|
|
8377
|
+
* @param ctx 客户端根上下文(sessions/workspaces 服务)
|
|
8378
|
+
* @param options 可注入选项
|
|
8379
|
+
*/
|
|
8380
|
+
function setupExtOpen(ctx, options = {}) {
|
|
8381
|
+
startPresencePoll(ctx);
|
|
8382
|
+
const params = parseOpenParams(options.search ?? (typeof location !== "undefined" ? location.search : ""));
|
|
8383
|
+
if (!params) return;
|
|
8384
|
+
const referrer = options.referrer ?? (typeof document !== "undefined" ? document.referrer : "");
|
|
8385
|
+
if (!referrerAllowed(referrer, options.origin ?? (typeof location !== "undefined" ? location.origin : ""))) {
|
|
8386
|
+
console.warn("[dsh-vscode-mode] 已忽略跨源深链请求(referrer=" + referrer + ")");
|
|
8387
|
+
return;
|
|
8388
|
+
}
|
|
8389
|
+
stripCurrentUrl();
|
|
8390
|
+
openDeepLink(ctx, params).catch((error) => {
|
|
8391
|
+
console.warn("[dsh-vscode-mode] 深链打开失败:" + String(error));
|
|
8392
|
+
toastDom("深链打开失败:" + String(error?.message ?? error));
|
|
8393
|
+
});
|
|
8394
|
+
}
|
|
8395
|
+
/**
|
|
8396
|
+
* 深链移交轮询:每 3s 向 host 领取待打开请求(兼活跃心跳,launcher 据此决定是否开新页)。
|
|
8397
|
+
* 页面级单例(window 标记防 HMR 重复);领取到即执行打开规则。
|
|
8398
|
+
* @author ddj 2026年09月08号
|
|
8399
|
+
* @param ctx 客户端根上下文
|
|
8400
|
+
*/
|
|
8401
|
+
function startPresencePoll(ctx) {
|
|
8402
|
+
if (typeof window === "undefined") return;
|
|
8403
|
+
const marker = window;
|
|
8404
|
+
if (marker.__edrvExtPoll) return;
|
|
8405
|
+
marker.__edrvExtPoll = true;
|
|
8406
|
+
let executing = false;
|
|
8407
|
+
window.setInterval(() => {
|
|
8408
|
+
if (executing) return;
|
|
8409
|
+
executing = true;
|
|
8410
|
+
rpc("edrv.external.pending", {}).then(async (result) => {
|
|
8411
|
+
if (!result.ok || !result.open) return;
|
|
8412
|
+
await openDeepLink(ctx, {
|
|
8413
|
+
paths: result.open.paths,
|
|
8414
|
+
line: result.open.line,
|
|
8415
|
+
column: result.open.column
|
|
8416
|
+
});
|
|
8417
|
+
}).catch(() => {}).finally(() => {
|
|
8418
|
+
executing = false;
|
|
8419
|
+
});
|
|
8420
|
+
}, HANDOFF_POLL_MS);
|
|
8421
|
+
}
|
|
8422
|
+
/**
|
|
8423
|
+
* 跨源守卫:referrer 为空(launcher/Unity 直开)或同源 → 允许。
|
|
8424
|
+
* @author ddj 2026年09月07号
|
|
8425
|
+
* @param referrer 文档 referrer
|
|
8426
|
+
* @param origin 当前页面源
|
|
8427
|
+
* @returns 是否允许处理深链
|
|
8428
|
+
*/
|
|
8429
|
+
function referrerAllowed(referrer, origin) {
|
|
8430
|
+
if (!referrer) return true;
|
|
8431
|
+
try {
|
|
8432
|
+
return new URL(referrer).origin === origin;
|
|
8433
|
+
} catch {
|
|
8434
|
+
return false;
|
|
8435
|
+
}
|
|
8436
|
+
}
|
|
8437
|
+
/**
|
|
8438
|
+
* 剥离 URL 中的深链参数(保留其余参数与 hash,防刷新重开)。
|
|
8439
|
+
* @author ddj 2026年09月07号
|
|
8440
|
+
* @param href 完整 URL
|
|
8441
|
+
* @returns 清理后的 URL(pathname+search+hash)
|
|
8442
|
+
*/
|
|
8443
|
+
function stripOpenHref(href) {
|
|
8444
|
+
const url = new URL(href);
|
|
8445
|
+
for (const key of EDRV_PARAM_KEYS) url.searchParams.delete(key);
|
|
8446
|
+
return url.pathname + url.search + url.hash;
|
|
8447
|
+
}
|
|
8448
|
+
/** 清理当前页面 URL(best-effort,失败不阻塞打开)。 */
|
|
8449
|
+
function stripCurrentUrl() {
|
|
8450
|
+
try {
|
|
8451
|
+
if (typeof location === "undefined" || typeof history === "undefined") return;
|
|
8452
|
+
const next = stripOpenHref(location.href);
|
|
8453
|
+
if (next !== location.pathname + location.search + location.hash) history.replaceState(null, "", next);
|
|
8454
|
+
} catch {}
|
|
8455
|
+
}
|
|
8456
|
+
/**
|
|
8457
|
+
* 轻量 toast(编辑器未挂载也能提示;5s 自隐;openFlow 通知与错误呈现共用)。
|
|
8458
|
+
* @author ddj 2026年09月07号
|
|
8459
|
+
* @param text 提示文本
|
|
8460
|
+
*/
|
|
8461
|
+
function toastDom(text) {
|
|
8462
|
+
if (typeof document === "undefined") return;
|
|
8463
|
+
const toast = document.createElement("div");
|
|
8464
|
+
toast.textContent = text;
|
|
8465
|
+
toast.setAttribute("style", [
|
|
8466
|
+
"position:fixed",
|
|
8467
|
+
"left:50%",
|
|
8468
|
+
"bottom:28px",
|
|
8469
|
+
"transform:translateX(-50%)",
|
|
8470
|
+
"z-index:99999",
|
|
8471
|
+
"padding:8px 14px",
|
|
8472
|
+
"border-radius:8px",
|
|
8473
|
+
"max-width:80vw",
|
|
8474
|
+
"background:#2a2a2a",
|
|
8475
|
+
"color:#ddd",
|
|
8476
|
+
"border:1px solid #444",
|
|
8477
|
+
"font-size:12px",
|
|
8478
|
+
"box-shadow:0 4px 16px rgba(0,0,0,.35)"
|
|
8479
|
+
].join(";"));
|
|
8480
|
+
document.body?.appendChild(toast);
|
|
8481
|
+
setTimeout(() => toast.remove(), TOAST_MS);
|
|
8482
|
+
}
|
|
8483
|
+
//#endregion
|
|
7501
8484
|
//#region src/client/ui/SideEditorTab.ts
|
|
7502
8485
|
/**
|
|
7503
8486
|
* dsh-vscode-mode client — SideEditorTab:betterSidebar「文件编辑」Tab 的组件包装。
|
|
@@ -10232,6 +11215,7 @@ window.__ModuleLoader__.load({
|
|
|
10232
11215
|
const registry = createFileOpenerRegistry();
|
|
10233
11216
|
const workspaces = ctx.get("workspaces");
|
|
10234
11217
|
const sessions = ctx.get("sessions");
|
|
11218
|
+
setupExtOpen(ctx);
|
|
10235
11219
|
const monacoList = sessions?.list;
|
|
10236
11220
|
if (typeof window !== "undefined" && monacoList && typeof monacoList.subscribe === "function") {
|
|
10237
11221
|
const list = monacoList;
|
|
@@ -10259,6 +11243,19 @@ window.__ModuleLoader__.load({
|
|
|
10259
11243
|
const binder = pickSettingsBinder(ctx);
|
|
10260
11244
|
const settings = binder.scope;
|
|
10261
11245
|
let selected = autoValue("auto");
|
|
11246
|
+
/** 0.1.3+ 会话文件链接路由(remote.session.openWorkspacePath)是否已安装(compatSummary 展示用)。 */
|
|
11247
|
+
let remoteOpenInstalled = false;
|
|
11248
|
+
/** 两条文件链接路由共用的路由日志(openPathRouter / remoteOpenRouter)。 */
|
|
11249
|
+
const routeLogger = (message) => console.warn("[dsh-vscode-mode] " + message);
|
|
11250
|
+
/** 两条路由共用的 FileOpenContext:当前会话 id 与工作区 cwd。 */
|
|
11251
|
+
const openContext = () => {
|
|
11252
|
+
const current = sessions?.list?.getSnapshot?.();
|
|
11253
|
+
const sessionId = current?.current;
|
|
11254
|
+
return {
|
|
11255
|
+
sessionId,
|
|
11256
|
+
cwd: (sessionId ? current?.byId?.[sessionId] : void 0)?.cwd
|
|
11257
|
+
};
|
|
11258
|
+
};
|
|
10262
11259
|
/** 可选探测 betterSidebar 服务(不进 inject:缺失会让插件停靠等待,杀死回退路径)。 */
|
|
10263
11260
|
let sideService = detectSidebarService(ctx);
|
|
10264
11261
|
/** 「添加到对话」动作集:编辑区/Tab 右键菜单注入文件引用与代码块(conversation 服务缺失时各动作安全降级)。 */
|
|
@@ -10273,7 +11270,12 @@ window.__ModuleLoader__.load({
|
|
|
10273
11270
|
{
|
|
10274
11271
|
name: "文件打开路由(workspaces.openPath)",
|
|
10275
11272
|
active: Boolean(workspaces?.openPath),
|
|
10276
|
-
note: "vscode
|
|
11273
|
+
note: "vscode 打开器优先,失败回退系统打开(0.1.2 及更早 DSH 的对话文件链接主路径)"
|
|
11274
|
+
},
|
|
11275
|
+
{
|
|
11276
|
+
name: "会话文件链接路由(remote.session.openWorkspacePath)",
|
|
11277
|
+
active: remoteOpenInstalled,
|
|
11278
|
+
note: "0.1.3+ 对话文件引用/产物打开优先走插件打开器,失败回退系统打开"
|
|
10277
11279
|
},
|
|
10278
11280
|
{
|
|
10279
11281
|
name: "侧边栏打开器(dsh-better-sidebar)",
|
|
@@ -10338,16 +11340,39 @@ window.__ModuleLoader__.load({
|
|
|
10338
11340
|
workspaces,
|
|
10339
11341
|
registry,
|
|
10340
11342
|
selected: () => selected,
|
|
10341
|
-
context:
|
|
10342
|
-
|
|
10343
|
-
const sessionId = current?.current;
|
|
10344
|
-
return {
|
|
10345
|
-
sessionId,
|
|
10346
|
-
cwd: (sessionId ? current?.byId?.[sessionId] : void 0)?.cwd
|
|
10347
|
-
};
|
|
10348
|
-
},
|
|
10349
|
-
logger: (message) => console.warn("[dsh-vscode-mode] " + message)
|
|
11343
|
+
context: openContext,
|
|
11344
|
+
logger: routeLogger
|
|
10350
11345
|
}), "vscode-mode: file link routing");
|
|
11346
|
+
let remoteRetries = 0;
|
|
11347
|
+
const retryRemoteOpen = () => {
|
|
11348
|
+
if (remoteOpenInstalled || remoteRetries >= 15) {
|
|
11349
|
+
if (!remoteOpenInstalled) console.warn("[dsh-vscode-mode] 未探测到 remote.session.openWorkspacePath,0.1.3+ 会话文件链接路由未安装");
|
|
11350
|
+
return;
|
|
11351
|
+
}
|
|
11352
|
+
remoteRetries += 1;
|
|
11353
|
+
schedule(() => {
|
|
11354
|
+
if (remoteOpenInstalled) return;
|
|
11355
|
+
const service = probeRemoteOpen(ctx);
|
|
11356
|
+
if (!service) {
|
|
11357
|
+
retryRemoteOpen();
|
|
11358
|
+
return;
|
|
11359
|
+
}
|
|
11360
|
+
const disposer = patchRemoteOpen(service, {
|
|
11361
|
+
registry,
|
|
11362
|
+
selected: () => selected,
|
|
11363
|
+
context: openContext,
|
|
11364
|
+
logger: routeLogger
|
|
11365
|
+
});
|
|
11366
|
+
if (!disposer) {
|
|
11367
|
+
console.warn("[dsh-vscode-mode] remote.session 存在但 openWorkspacePath 不可补丁,会话文件链接路由未安装");
|
|
11368
|
+
return;
|
|
11369
|
+
}
|
|
11370
|
+
remoteOpenInstalled = true;
|
|
11371
|
+
ctx.effect(() => disposer, "vscode-mode: remote file link routing");
|
|
11372
|
+
console.info("[dsh-vscode-mode] 已安装 0.1.3+ 会话文件链接路由(remote.session.openWorkspacePath)");
|
|
11373
|
+
}, 2e3);
|
|
11374
|
+
};
|
|
11375
|
+
retryRemoteOpen();
|
|
10351
11376
|
const registerLegacyTab = () => {
|
|
10352
11377
|
return registerSlotSafely(ctx, {
|
|
10353
11378
|
name: "conversation.view",
|