dsh-vscode-mode 0.1.7 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -29,13 +29,13 @@ window.__ModuleLoader__.load({
29
29
  let react = require("react");
30
30
  react = __toESM(react, 1);
31
31
  //#region \0dsh-css:/home/runner/work/DSH_VsCodeMode/DSH_VsCodeMode/src/client/styles/editor.css.mjs
32
- const css = "/* dsh-vscode-mode 编辑区样式(原 ensureEdrvStyles 字符串,落盘为真 .css,由 tsdown CSS-inline 注入) */\n/* 浅色主题(对齐参考图:#f8f8f8 近白底 + 薄荷绿药丸 + 青绿文字 + 三文鱼 undo + 浅灰边框):\n 在 [data-edrv-view] 作用域内重定义 --dsw-alias-*,级联命中组件内联样式与 CSS。 */\n[data-edrv-view] { --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-path-input { flex: 1; min-width: 140px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-wrap { position: relative; flex-shrink: 0; display: flex; align-items: center; padding: 2px 8px 2px 4px; }\n[data-edrv-view] .edrv-search { width: 190px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-pop { position: absolute; top: calc(100% + 4px); right: 8px; width: min(460px, calc(100vw - 24px)); max-height: 300px; overflow: auto; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 10px; box-shadow: 0 6px 20px rgba(0,0,0,.4); z-index: 60; padding: 4px; }\n[data-edrv-view] .edrv-search-item { display: flex; flex-direction: column; gap: 1px; padding: 5px 8px; border-radius: 6px; cursor: pointer; }\n[data-edrv-view] .edrv-search-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.07)); }\n[data-edrv-view] .edrv-search-item .n { color: var(--dsw-alias-label-primary, #eee); font-size: 12px; word-break: break-all; }\n[data-edrv-view] .edrv-search-item .d { color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-search-empty { padding: 8px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #888); }\n[data-edrv-view] .edrv-btn-mini { font-size: 11px; padding: 2px 9px; border: none; border-radius: 999px; cursor: pointer; color: #fff; font-weight: 600; white-space: nowrap; }\n[data-edrv-view] .edrv-btn-keep { background: var(--dsw-alias-state-success-primary, #2e9e44); }\n[data-edrv-view] .edrv-btn-undo { background: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-btn-mini:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-statusbar { display: flex; align-items: center; gap: 10px; padding: 3px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #333); background: var(--dsw-alias-bg-layer-1, transparent); font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); flex-shrink: 0; white-space: nowrap; overflow: hidden; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-path { flex: 1; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-diffchip { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-state-warn-primary, #b7791f); cursor: pointer; white-space: nowrap; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn { font-size: 12px; padding: 2px 6px; border: none; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; border-radius: 4px; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-mn-add-line { background: rgba(15,157,88,.10); }\n[data-edrv-view] .edrv-mn-gutter-add { color: #0f9d58 !important; font-weight: 700; display: flex !important; align-items: center; justify-content: center; }\n[data-edrv-view] .edrv-mn-gutter-add::before { content: \"+\"; }\n[data-edrv-view] .edrv-del-zone { background: #fdeaea; border-top: 1px solid rgba(217,83,79,.35); border-bottom: 1px solid rgba(217,83,79,.35); box-shadow: inset 0 0 0 1px rgba(217,83,79,.12); }\n[data-edrv-view] .edrv-del-row { white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 12.5px; line-height: 20px; color: #c0392b; }\n[data-edrv-view] .edrv-del-text { display: inline-block; white-space: pre; }\n[data-edrv-view] .edrv-minus-overlay { position: absolute; left: 0; top: 0; pointer-events: none; z-index: 6; }\n[data-edrv-view] .edrv-minus-item { position: absolute; width: 16px; text-align: center; color: #d9534f; font-weight: 700; font-size: 12.5px; line-height: 20px; }\n[data-edrv-view] .edrv-monaco-host { flex: 1; min-height: 0; position: relative; }\n[data-edrv-view] .edrv-monaco-host > div { position: absolute; inset: 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: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 4px 16px rgba(31,41,51,.12); overflow: visible; display: flex; flex-direction: column; }\n[data-edrv-view] .edrv-diffbar-row { display: flex; align-items: center; gap: 6px; height: 34px; padding: 0 8px; 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[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, [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";
33
- const tagId = "dsh-vscode-mode/editor.css";
34
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
32
+ const css$1 = "/* dsh-vscode-mode 编辑区样式(原 ensureEdrvStyles 字符串,落盘为真 .css,由 tsdown CSS-inline 注入) */\n/* 浅色主题(对齐参考图:#f8f8f8 近白底 + 薄荷绿药丸 + 青绿文字 + 三文鱼 undo + 浅灰边框):\n 在 [data-edrv-view] 作用域内重定义 --dsw-alias-*,级联命中组件内联样式与 CSS。 */\n[data-edrv-view] { --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-path-input { flex: 1; min-width: 140px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search-wrap { position: relative; flex-shrink: 0; display: flex; align-items: center; padding: 2px 8px 2px 4px; }\n[data-edrv-view] .edrv-search { width: 190px; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; outline: none; }\n[data-edrv-view] .edrv-search:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }\n[data-edrv-view] .edrv-search-pop { position: absolute; top: calc(100% + 4px); right: 8px; width: min(460px, calc(100vw - 24px)); max-height: 300px; overflow: auto; background: var(--dsw-alias-bg-overlay, #1c1c1c); border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 10px; box-shadow: 0 6px 20px rgba(0,0,0,.4); z-index: 60; padding: 4px; }\n[data-edrv-view] .edrv-search-item { display: flex; flex-direction: column; gap: 1px; padding: 5px 8px; border-radius: 6px; cursor: pointer; }\n[data-edrv-view] .edrv-search-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.07)); }\n[data-edrv-view] .edrv-search-item .n { color: var(--dsw-alias-label-primary, #eee); font-size: 12px; word-break: break-all; }\n[data-edrv-view] .edrv-search-item .d { color: var(--dsw-alias-label-tertiary, #888); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n[data-edrv-view] .edrv-search-empty { padding: 8px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #888); }\n[data-edrv-view] .edrv-btn-mini { font-size: 11px; padding: 2px 9px; border: none; border-radius: 999px; cursor: pointer; color: #fff; font-weight: 600; white-space: nowrap; }\n[data-edrv-view] .edrv-btn-keep { background: var(--dsw-alias-state-success-primary, #2e9e44); }\n[data-edrv-view] .edrv-btn-undo { background: var(--dsw-alias-state-warn-primary, #d97706); }\n[data-edrv-view] .edrv-btn-mini:disabled { opacity: .45; cursor: default; }\n[data-edrv-view] .edrv-statusbar { display: flex; align-items: center; gap: 10px; padding: 3px 10px; border-top: 1px solid var(--dsw-alias-border-l1, #333); background: var(--dsw-alias-bg-layer-1, transparent); font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); flex-shrink: 0; white-space: nowrap; overflow: hidden; }\n[data-edrv-view] .edrv-statusbar .edrv-sp-path { flex: 1; overflow: hidden; text-overflow: ellipsis; }\n[data-edrv-view] .edrv-diffchip { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-state-warn-primary, #b7791f); cursor: pointer; white-space: nowrap; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn { font-size: 12px; padding: 2px 6px; border: none; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; border-radius: 4px; flex-shrink: 0; }\n[data-edrv-view] .edrv-chip-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }\n[data-edrv-view] .edrv-mn-add-line { background: rgba(15,157,88,.10); }\n[data-edrv-view] .edrv-mn-gutter-add { color: #0f9d58 !important; font-weight: 700; display: flex !important; align-items: center; justify-content: center; }\n[data-edrv-view] .edrv-mn-gutter-add::before { content: \"+\"; }\n[data-edrv-view] .edrv-del-zone { background: #fdeaea; border-top: 1px solid rgba(217,83,79,.35); border-bottom: 1px solid rgba(217,83,79,.35); box-shadow: inset 0 0 0 1px rgba(217,83,79,.12); }\n[data-edrv-view] .edrv-del-row { white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 12.5px; line-height: 20px; color: #c0392b; }\n[data-edrv-view] .edrv-del-text { display: inline-block; white-space: pre; }\n[data-edrv-view] .edrv-minus-overlay { position: absolute; left: 0; top: 0; width: 100%; height: 100%; max-width: 100%; overflow: visible; pointer-events: none; z-index: 6; }\n[data-edrv-view] .edrv-minus-item { position: absolute; box-sizing: border-box; max-width: 100%; overflow: hidden; }\n[data-edrv-view] .edrv-minus-item::before { content: ''; position: absolute; left: 4px; top: 50%; width: 8px; height: 2px; transform: translateY(-50%); border-radius: 2px; background: #d9534f; }\n[data-edrv-view] .edrv-monaco-host { flex: 1; min-height: 0; position: relative; }\n[data-edrv-view] .edrv-monaco-host > div { position: absolute; inset: 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: 10px; background: var(--dsw-alias-bg-overlay, #ffffff); border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); box-shadow: 0 4px 16px rgba(31,41,51,.12); overflow: visible; display: flex; flex-direction: column; }\n[data-edrv-view] .edrv-diffbar-row { display: flex; align-items: center; gap: 6px; height: 34px; padding: 0 8px; 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[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";
33
+ const tagId$1 = "dsh-vscode-mode/editor.css";
34
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
35
35
  const tag = document.createElement("style");
36
36
  tag.dataset.plugin = "dsh-vscode-mode";
37
- tag.dataset.pluginCss = tagId;
38
- tag.textContent = css;
37
+ tag.dataset.pluginCss = tagId$1;
38
+ tag.textContent = css$1;
39
39
  document.head.appendChild(tag);
40
40
  }
41
41
  //#endregion
@@ -114,6 +114,63 @@ window.__ModuleLoader__.load({
114
114
  */
115
115
  const MONACO_BASE = "/edrv/vendor/monaco/vs";
116
116
  let monacoPromise = null;
117
+ let monacoStage = {
118
+ phase: "idle",
119
+ progress: 0,
120
+ message: "准备加载 Monaco…"
121
+ };
122
+ const stageListeners = /* @__PURE__ */ new Set();
123
+ /**
124
+ * Monaco 加载阶段:这是阶段进度而非网络字节进度,避免误导用户。
125
+ * @author ddj 2026年08月22号
126
+ */
127
+ const MONACO_STAGES = {
128
+ loader: {
129
+ progress: 18,
130
+ message: "加载 Monaco 引导模块…"
131
+ },
132
+ core: {
133
+ progress: 72,
134
+ message: "加载编辑器核心模块…"
135
+ },
136
+ ready: {
137
+ progress: 100,
138
+ message: "Monaco 编辑器已就绪"
139
+ },
140
+ error: {
141
+ progress: 0,
142
+ message: "Monaco 编辑器加载失败"
143
+ }
144
+ };
145
+ /**
146
+ * 发布 Monaco 加载阶段,监听器异常不得影响编辑器加载。
147
+ * @author ddj 2026年08月22号
148
+ * @param phase 阶段名
149
+ * @param progress 阶段百分比
150
+ * @param message 用户可读状态
151
+ */
152
+ function publishStage(phase, progress, message) {
153
+ monacoStage = {
154
+ phase,
155
+ progress,
156
+ message
157
+ };
158
+ for (const listener of stageListeners) try {
159
+ listener(monacoStage);
160
+ } catch (error) {}
161
+ }
162
+ /**
163
+ * 订阅 Monaco 加载阶段。
164
+ * @author ddj 2026年08月22号
165
+ * @param listener 阶段回调
166
+ * @returns 取消订阅函数
167
+ */
168
+ function subscribeStage(listener) {
169
+ if (typeof listener !== "function") return () => {};
170
+ stageListeners.add(listener);
171
+ listener(monacoStage);
172
+ return () => stageListeners.delete(listener);
173
+ }
117
174
  /** 扩展名 → Monaco language id(常见语言;未知回退 plaintext)。 */
118
175
  const LANG_BY_EXT = {
119
176
  js: "javascript",
@@ -184,34 +241,40 @@ window.__ModuleLoader__.load({
184
241
  * @author ddj 2026年08月20号
185
242
  * @returns Promise<object> window.monaco
186
243
  */
187
- function loadMonaco() {
188
- if (!monacoPromise) monacoPromise = new Promise((resolve, reject) => {
189
- const boot = () => {
190
- try {
191
- window.require.config({ paths: { vs: MONACO_BASE } });
192
- window.require(["vs/editor/editor.main"], () => resolve(window.monaco), (err) => {
193
- monacoPromise = null;
194
- reject(/* @__PURE__ */ new Error("Monaco 模块加载失败:" + String(err)));
195
- });
196
- } catch (error) {
244
+ function loadMonaco(onProgress) {
245
+ const unsubscribe = subscribeStage(onProgress);
246
+ if (!monacoPromise) {
247
+ publishStage("loader", MONACO_STAGES.loader.progress, MONACO_STAGES.loader.message);
248
+ monacoPromise = new Promise((resolve, reject) => {
249
+ const fail = (error) => {
197
250
  monacoPromise = null;
251
+ publishStage("error", MONACO_STAGES.error.progress, MONACO_STAGES.error.message);
198
252
  reject(error);
199
- }
200
- };
201
- if (document.querySelector("script[data-edrv-monaco-loader]")) boot();
202
- else {
203
- const s = document.createElement("script");
204
- s.src = "/edrv/vendor/monaco/vs/loader.js";
205
- s.dataset.edrvMonacoLoader = "1";
206
- s.onload = boot;
207
- s.onerror = () => {
208
- monacoPromise = null;
209
- reject(/* @__PURE__ */ new Error("Monaco loader 加载失败"));
210
253
  };
211
- document.head.appendChild(s);
212
- }
213
- });
214
- return monacoPromise;
254
+ const boot = () => {
255
+ try {
256
+ window.require.config({ paths: { vs: MONACO_BASE } });
257
+ publishStage("core", MONACO_STAGES.core.progress, MONACO_STAGES.core.message);
258
+ window.require(["vs/editor/editor.main"], () => {
259
+ publishStage("ready", MONACO_STAGES.ready.progress, MONACO_STAGES.ready.message);
260
+ resolve(window.monaco);
261
+ }, (err) => fail(/* @__PURE__ */ new Error("Monaco 模块加载失败:" + String(err))));
262
+ } catch (error) {
263
+ fail(error);
264
+ }
265
+ };
266
+ if (document.querySelector("script[data-edrv-monaco-loader]")) boot();
267
+ else {
268
+ const s = document.createElement("script");
269
+ s.src = "/edrv/vendor/monaco/vs/loader.js";
270
+ s.dataset.edrvMonacoLoader = "1";
271
+ s.onload = boot;
272
+ s.onerror = () => fail(/* @__PURE__ */ new Error("Monaco loader 加载失败"));
273
+ document.head.appendChild(s);
274
+ }
275
+ });
276
+ }
277
+ return monacoPromise.finally(unsubscribe);
215
278
  }
216
279
  //#endregion
217
280
  //#region src/client/monaco/diffRender.ts
@@ -305,8 +368,10 @@ window.__ModuleLoader__.load({
305
368
  try {
306
369
  li = editor.getLayoutInfo();
307
370
  } catch (e) {}
308
- const left = li ? li.decorationsLeft : 0;
371
+ const rawLeft = li ? li.decorationsLeft : 0;
309
372
  const width = li && li.decorationsWidth > 0 ? li.decorationsWidth : 16;
373
+ const maxLeft = Math.max(0, root.clientWidth - width);
374
+ const left = Math.max(0, Math.min(maxLeft, rawLeft + 12));
310
375
  const lineCount = editor.getModel().getLineCount();
311
376
  let itemIdx = 0;
312
377
  let zi = 0;
@@ -318,7 +383,7 @@ window.__ModuleLoader__.load({
318
383
  for (let i = 0; i < z.n; i++) {
319
384
  const item = document.createElement("div");
320
385
  item.className = "edrv-minus-item";
321
- item.textContent = "-";
386
+ item.textContent = "";
322
387
  item.style.width = width + "px";
323
388
  item.style.height = lineH + "px";
324
389
  item.dataset.zone = String(zi);
@@ -346,7 +411,14 @@ window.__ModuleLoader__.load({
346
411
  const edRect = root.getBoundingClientRect();
347
412
  const st = editor.getScrollTop() || 0;
348
413
  const items = overlay.querySelectorAll(".edrv-minus-item");
414
+ let correctedLeft = left;
415
+ try {
416
+ const layout = editor.getLayoutInfo();
417
+ const markerWidth = layout.decorationsWidth > 0 ? layout.decorationsWidth : width;
418
+ correctedLeft = Math.max(0, Math.min(Math.max(0, root.clientWidth - markerWidth), (layout.decorationsLeft || 0) + 12));
419
+ } catch (e) {}
349
420
  for (const item of items) {
421
+ item.style.left = correctedLeft + "px";
350
422
  const zIndex = Number(item.dataset.zone);
351
423
  const rIndex = Number(item.dataset.row);
352
424
  const z = createdZones[zIndex];
@@ -378,6 +450,121 @@ window.__ModuleLoader__.load({
378
450
  };
379
451
  }
380
452
  //#endregion
453
+ //#region src/shared/diff.ts
454
+ /** 统一取对应 hunk;callHunk 只用于旧记录缺失 hunks 时兼容。 */
455
+ function preciseHunk(record, idx) {
456
+ return hunkOf(record, idx);
457
+ }
458
+ /** 取记录第 idx 个有效 hunk;只有记录缺 hunk 时才回退 callHunk。 */
459
+ function hunkOf(record, idx) {
460
+ const hunk = Array.isArray(record.hunks) ? record.hunks[idx] : void 0;
461
+ if (hunk && typeof hunk.newText === "string") return hunk;
462
+ if (idx === 0 && record.callHunk && typeof record.callHunk.newText === "string") return record.callHunk;
463
+ return null;
464
+ }
465
+ /** splitLines 的空文本语义:真实空文件没有一行变更内容。 */
466
+ function splitLines(text) {
467
+ return text.length ? text.split("\n") : [];
468
+ }
469
+ /** 计算 index 前的 0 基行数。 */
470
+ function lineBefore(text, index) {
471
+ let n = 0;
472
+ const end = Math.max(0, Math.min(index, text.length));
473
+ for (let i = 0; i < end; i++) if (text.charCodeAt(i) === 10) n++;
474
+ return n;
475
+ }
476
+ /** 找出文本的全部非重叠出现位置。 */
477
+ function allMatches(content, needle) {
478
+ if (!needle.length) return [];
479
+ const out = [];
480
+ let from = 0;
481
+ while (from <= content.length - needle.length) {
482
+ const at = content.indexOf(needle, from);
483
+ if (at < 0) break;
484
+ out.push({
485
+ start: at,
486
+ end: at + needle.length
487
+ });
488
+ from = at + Math.max(1, needle.length);
489
+ }
490
+ return out;
491
+ }
492
+ /**
493
+ * 从 after 快照定位所有 hunk,按 hunk 顺序优先选未占用且靠近前一块的候选。
494
+ * 若 hunk 带 afterStart/afterEnd,则优先验证快照坐标,避免重复文本错配。
495
+ * @author ddj 2026年08月24号
496
+ * @param content 当前文件内容
497
+ * @param hunks 待定位 hunk
498
+ * @returns 每个 hunk 一个结果,matched=false 表示冲突/stale
499
+ */
500
+ function locateHunks(content, hunks) {
501
+ const locations = [];
502
+ let cursor = 0;
503
+ const occupied = [];
504
+ for (let idx = 0; idx < hunks.length; idx++) {
505
+ const hunk = hunks[idx];
506
+ const newText = hunk.newText;
507
+ const snapshot = Number.isInteger(hunk.afterStart) && Number.isInteger(hunk.afterEnd) ? {
508
+ start: hunk.afterStart,
509
+ end: hunk.afterEnd
510
+ } : null;
511
+ const snapshotMatches = snapshot && snapshot.start >= 0 && snapshot.end >= snapshot.start && (newText.length === 0 || content.slice(snapshot.start, snapshot.end) === newText) ? snapshot : null;
512
+ const candidates = snapshotMatches ? [snapshotMatches] : allMatches(content, newText);
513
+ const candidate = candidates.find((range) => range.start >= cursor && !occupied.some((used) => overlaps(range, used))) ?? candidates.find((range) => !occupied.some((used) => overlaps(range, used)));
514
+ if (candidate) {
515
+ locations.push({
516
+ idx,
517
+ hunk,
518
+ ...candidate,
519
+ matched: true
520
+ });
521
+ occupied.push(candidate);
522
+ cursor = Math.max(cursor, candidate.end);
523
+ continue;
524
+ }
525
+ if (newText.length === 0 && hunk.afterStart === void 0 && hunk.afterEnd === void 0 && content.length === 0) {
526
+ locations.push({
527
+ idx,
528
+ hunk,
529
+ start: 0,
530
+ end: 0,
531
+ matched: true
532
+ });
533
+ continue;
534
+ }
535
+ if (newText.length === 0 && hunk.afterStart !== void 0 && hunk.afterEnd !== void 0 && hunk.afterStart === hunk.afterEnd && hunk.afterStart >= 0 && hunk.afterStart <= content.length) {
536
+ const point = {
537
+ start: hunk.afterStart,
538
+ end: hunk.afterEnd
539
+ };
540
+ if (!occupied.some((used) => overlaps(point, used))) {
541
+ locations.push({
542
+ idx,
543
+ hunk,
544
+ ...point,
545
+ matched: true
546
+ });
547
+ occupied.push(point);
548
+ cursor = Math.max(cursor, point.end);
549
+ continue;
550
+ }
551
+ }
552
+ locations.push({
553
+ idx,
554
+ hunk,
555
+ start: -1,
556
+ end: -1,
557
+ matched: false
558
+ });
559
+ }
560
+ return locations;
561
+ }
562
+ /** 判断两个半开区间是否重叠。零长度区间只与同一位置的零长度区间重叠。 */
563
+ function overlaps(left, right) {
564
+ if (left.start === left.end || right.start === right.end) return left.start === right.start && left.end === right.end;
565
+ return left.start < right.end && right.start < left.end;
566
+ }
567
+ //#endregion
381
568
  //#region src/client/state/records.ts
382
569
  /** 差异决策状态。 */
383
570
  const ST = {
@@ -398,14 +585,14 @@ window.__ModuleLoader__.load({
398
585
  /** hunk 是否为空差异(old===new,无实际内容变化,不可操作)。create 记录不视为空差异。 */
399
586
  function noopHunk(rec, h) {
400
587
  if (!rec || rec.create === true) return false;
401
- const precise = rec.toolName === "edit" && rec.callHunk ? rec.callHunk : h;
588
+ const precise = rec && h && rec.hunks.indexOf(h) >= 0 ? preciseHunk(rec, rec.hunks.indexOf(h)) : h;
402
589
  const oldText = precise?.oldText ?? null;
403
590
  const newText = precise?.newText ?? null;
404
591
  return oldText !== null && oldText === newText;
405
592
  }
406
593
  /** 记录是否仍有待处理差异(superseded / 全部已决策 = false)。 */
407
594
  function isRecPending(rec) {
408
- if (!rec || rec.superseded === true) return false;
595
+ if (!rec || rec.superseded === true || rec.conflict === true) return false;
409
596
  const perHunk = Array.isArray(rec.decisions?.perHunk) ? rec.decisions.perHunk : [];
410
597
  if (perHunk.length) {
411
598
  for (let i = 0; i < perHunk.length; i++) if (perHunk[i] !== "accepted" && perHunk[i] !== "rejected" && !noopHunk(rec, (rec.hunks || [])[i])) return true;
@@ -454,9 +641,7 @@ window.__ModuleLoader__.load({
454
641
  //#region src/client/state/regions.ts
455
642
  /** 统计 index 之前(不含)的换行数 → 0 基行号。 */
456
643
  function countLinesBefore(text, index) {
457
- let n = 0;
458
- for (let i = 0; i < index; i++) if (text.charCodeAt(i) === 10) n++;
459
- return n;
644
+ return lineBefore(text, index);
460
645
  }
461
646
  /**
462
647
  * 行级公共前缀/后缀裁剪:old/new 首尾相同的行视为未变化(上下文),只保留真正变更的中间段。
@@ -482,59 +667,71 @@ window.__ModuleLoader__.load({
482
667
  function diffRegions(records, content) {
483
668
  const regions = [];
484
669
  if (content === null) return regions;
485
- const lines = content.split("\n");
486
- for (const rec of records) for (let i = 0; i < rec.hunks.length; i++) {
487
- const h = rec.hunks[i];
488
- if (noopHunk(rec, h)) continue;
489
- const status = statusAt(rec, i);
670
+ const lines = splitLines(content);
671
+ for (const rec of records) {
490
672
  if (rec.create) {
491
- regions.push({
492
- callId: rec.callId,
673
+ for (let i = 0; i < rec.hunks.length; i++) {
674
+ const h = preciseHunk(rec, i);
675
+ if (!h || noopHunk(rec, h)) continue;
676
+ regions.push({
677
+ callId: rec.callId,
678
+ idx: i,
679
+ start: 1,
680
+ end: Math.max(1, lines.length),
681
+ oldLines: [],
682
+ newLines: lines.slice(),
683
+ whole: true,
684
+ status: statusAt(rec, i),
685
+ create: true,
686
+ rec,
687
+ superseded: rec.superseded === true
688
+ });
689
+ }
690
+ continue;
691
+ }
692
+ const entries = [];
693
+ for (let i = 0; i < rec.hunks.length; i++) {
694
+ const hunk = preciseHunk(rec, i);
695
+ if (hunk && !noopHunk(rec, hunk)) entries.push({
493
696
  idx: i,
494
- start: 1,
495
- end: lines.length,
496
- oldLines: [],
497
- newLines: lines.slice(),
498
- whole: true,
499
- status,
500
- create: true,
501
- rec,
502
- superseded: rec.superseded === true
697
+ hunk
503
698
  });
504
- continue;
505
699
  }
506
- const precise = rec.toolName === "edit" && rec.callHunk ? rec.callHunk : h;
507
- const newText = precise.newText ?? "";
508
- const at = content.indexOf(newText);
509
- if (at < 0) {
700
+ const locations = locateHunks(content, entries.map((entry) => entry.hunk));
701
+ for (let i = 0; i < entries.length; i++) {
702
+ const entry = entries[i];
703
+ const location = locations[i];
704
+ const status = statusAt(rec, entry.idx);
705
+ if (!location.matched) {
706
+ regions.push({
707
+ callId: rec.callId,
708
+ idx: entry.idx,
709
+ stale: true,
710
+ status,
711
+ create: false,
712
+ oldLines: entry.hunk.oldText === null ? [] : entry.hunk.oldText.split("\n"),
713
+ newLines: entry.hunk.newText ? entry.hunk.newText.split("\n") : [],
714
+ rec,
715
+ superseded: rec.superseded === true
716
+ });
717
+ continue;
718
+ }
719
+ const start = countLinesBefore(content, location.start) + 1;
720
+ const trimmed = trimCommonLines(entry.hunk.oldText === null ? [] : entry.hunk.oldText.split("\n"), entry.hunk.newText.split("\n"));
721
+ const regionStart = start + trimmed.shift;
510
722
  regions.push({
511
723
  callId: rec.callId,
512
- idx: i,
513
- stale: true,
724
+ idx: entry.idx,
725
+ start: regionStart,
726
+ end: regionStart + trimmed.newLines.length,
727
+ oldLines: trimmed.oldLines,
728
+ newLines: trimmed.newLines,
514
729
  status,
515
730
  create: false,
516
- oldLines: [],
517
- newLines: [],
518
731
  rec,
519
732
  superseded: rec.superseded === true
520
733
  });
521
- continue;
522
734
  }
523
- const start = countLinesBefore(content, at) + 1;
524
- const trimmed = trimCommonLines(precise.oldText === null ? [] : precise.oldText.split("\n"), newText.split("\n"));
525
- const regionStart = start + trimmed.shift;
526
- regions.push({
527
- callId: rec.callId,
528
- idx: i,
529
- start: regionStart,
530
- end: regionStart + trimmed.newLines.length,
531
- oldLines: trimmed.oldLines,
532
- newLines: trimmed.newLines,
533
- status,
534
- create: false,
535
- rec,
536
- superseded: rec.superseded === true
537
- });
538
735
  }
539
736
  regions.sort((a, b) => (a.start ?? Infinity) - (b.start ?? Infinity));
540
737
  return regions;
@@ -698,7 +895,7 @@ window.__ModuleLoader__.load({
698
895
  key: "stale" + callIdAttr(r.callId, r.idx),
699
896
  className: "edrv-diffrow edrv-diffrow-stale",
700
897
  title: "差异无法定位(文件可能已被手动修改)"
701
- }, react.default.createElement("span", { className: "edrv-diffrow-l" }, "L" + (r.start ?? "?") + " 无法定位"), react.default.createElement("span", { className: "edrv-diffrow-tag" }, badgeOf(r.status))));
898
+ }, react.default.createElement("span", { className: "edrv-diffrow-l" }, "L" + (r.start ?? "?") + " 无法定位"), react.default.createElement("span", { className: "edrv-diffrow-tag" }, "冲突/需刷新"), react.default.createElement("span", { className: "edrv-diffrow-tag" }, badgeOf(r.status))));
702
899
  let othersEl = null;
703
900
  if (otherFiles.length) othersEl = react.default.createElement("div", { className: "edrv-diffbox-others" }, react.default.createElement("span", { className: "edrv-diffrow-tag" }, "其他差异文件 (" + otherFiles.length + "):"), otherFiles.map((f) => react.default.createElement("button", {
704
901
  key: f.path,
@@ -1116,6 +1313,11 @@ window.__ModuleLoader__.load({
1116
1313
  const [content, setContent] = react.default.useState(null);
1117
1314
  const [status, setStatus] = react.default.useState("");
1118
1315
  const [error, setError] = react.default.useState(null);
1316
+ const [loadError, setLoadError] = react.default.useState(null);
1317
+ const [loadStage, setLoadStage] = react.default.useState({
1318
+ progress: 0,
1319
+ message: "准备加载编辑器…"
1320
+ });
1119
1321
  const [openInput, setOpenInput] = react.default.useState(false);
1120
1322
  const [pathDraft, setPathDraft] = react.default.useState("");
1121
1323
  const [cursor, setCursor] = react.default.useState("");
@@ -1190,6 +1392,11 @@ window.__ModuleLoader__.load({
1190
1392
  };
1191
1393
  const loadContent = (path, sid) => {
1192
1394
  const seq = ++loadSeqRef.current;
1395
+ setLoadError(null);
1396
+ setLoadStage({
1397
+ progress: monaco ? 72 : 12,
1398
+ message: "读取文件内容…"
1399
+ });
1193
1400
  rpc("edrv.read", {
1194
1401
  sessionId: sid,
1195
1402
  path
@@ -1197,16 +1404,23 @@ window.__ModuleLoader__.load({
1197
1404
  if (seq !== loadSeqRef.current || path !== active) return;
1198
1405
  if (res && res.ok) {
1199
1406
  setContent(res.content);
1407
+ setLoadStage((prev) => ({
1408
+ progress: Math.max(84, prev.progress),
1409
+ message: "文件已读取,准备创建编辑器…"
1410
+ }));
1200
1411
  setStatus("已加载");
1201
1412
  } else {
1202
- setError(res?.error ? String(res.error) : "读取失败");
1413
+ const message = res?.error ? String(res.error) : "读取失败";
1414
+ setLoadError(message);
1415
+ setError(message);
1203
1416
  setStatus("读取失败");
1204
1417
  }
1205
1418
  }).catch((e) => {
1206
- if (seq === loadSeqRef.current) {
1207
- setError("read异常:" + String(e));
1208
- setStatus("读取失败");
1209
- }
1419
+ if (seq !== loadSeqRef.current) return;
1420
+ const message = "read异常:" + String(e);
1421
+ setLoadError(message);
1422
+ setError(message);
1423
+ setStatus("读取失败");
1210
1424
  });
1211
1425
  };
1212
1426
  react.default.useEffect(() => {
@@ -1263,13 +1477,26 @@ window.__ModuleLoader__.load({
1263
1477
  react.default.useEffect(() => {
1264
1478
  if (!active) return;
1265
1479
  setContent(null);
1480
+ setLoadError(null);
1481
+ setLoadStage({
1482
+ progress: 10,
1483
+ message: "准备读取文件…"
1484
+ });
1266
1485
  setStatus("加载中…");
1267
1486
  loadContent(active, sessionId);
1268
1487
  }, [active, sessionId]);
1269
1488
  react.default.useEffect(() => {
1270
1489
  if (monaco || monacoErr) return;
1271
1490
  let alive = true;
1272
- loadMonaco().then((m) => {
1491
+ const onProgress = (stage) => {
1492
+ if (!alive) return;
1493
+ const progress = stage.phase === "ready" ? 70 : Math.round(10 + stage.progress * .6);
1494
+ setLoadStage((prev) => ({
1495
+ progress: Math.max(prev.progress, progress),
1496
+ message: stage.message
1497
+ }));
1498
+ };
1499
+ loadMonaco(onProgress).then((m) => {
1273
1500
  if (alive) setMonaco(m);
1274
1501
  }).catch((e) => {
1275
1502
  if (alive) {
@@ -1338,6 +1565,10 @@ window.__ModuleLoader__.load({
1338
1565
  const ed = editorRef.current;
1339
1566
  const model = getModel(active, content);
1340
1567
  if (ed.getModel() !== model) ed.setModel(model);
1568
+ setLoadStage((prev) => ({
1569
+ progress: Math.max(96, prev.progress),
1570
+ message: "创建编辑器视图…"
1571
+ }));
1341
1572
  }, [
1342
1573
  monaco,
1343
1574
  active,
@@ -1346,6 +1577,10 @@ window.__ModuleLoader__.load({
1346
1577
  react.default.useEffect(() => {
1347
1578
  if (!monaco || !editorRef.current || !active || content === null) return;
1348
1579
  diffRendererRef.current.render(monaco, editorRef.current, pendingRegions, sessionId);
1580
+ setLoadStage({
1581
+ progress: 100,
1582
+ message: "编辑器已就绪"
1583
+ });
1349
1584
  }, [
1350
1585
  monaco,
1351
1586
  active,
@@ -1713,11 +1948,54 @@ window.__ModuleLoader__.load({
1713
1948
  onClick: reloadFile
1714
1949
  }, "⟳"));
1715
1950
  const otherFiles = sum.pendingFiles.filter((f) => f.path !== active);
1951
+ /**
1952
+ * 渲染编辑器/文件加载进度面板。
1953
+ * @author ddj 2026年08月22号
1954
+ * @param message 当前加载阶段
1955
+ * @param progress 阶段进度
1956
+ * @param retry 可选重试回调
1957
+ * @returns 加载面板 React 元素
1958
+ */
1959
+ const loadingBody = (message, progress, retry) => react.default.createElement("div", {
1960
+ className: "edrv-empty edrv-loading",
1961
+ "aria-live": "polite"
1962
+ }, react.default.createElement("div", { className: "edrv-loading-title" }, message), react.default.createElement("div", {
1963
+ className: "edrv-progress",
1964
+ role: "progressbar",
1965
+ "aria-label": "编辑器加载进度",
1966
+ "aria-valuemin": 0,
1967
+ "aria-valuemax": 100,
1968
+ "aria-valuenow": progress
1969
+ }, react.default.createElement("div", {
1970
+ className: "edrv-progress-value",
1971
+ style: { width: Math.max(0, Math.min(100, progress)) + "%" }
1972
+ })), react.default.createElement("div", { className: "edrv-loading-percent" }, Math.round(progress) + "%"), retry ? react.default.createElement("button", {
1973
+ className: "edrv-pill edrv-pill-ghost",
1974
+ onClick: retry
1975
+ }, "重试") : null);
1716
1976
  let body;
1717
- if (!monaco && !monacoErr) body = react.default.createElement("div", { className: "edrv-empty" }, "正在加载 Monaco 编辑器…");
1718
- else if (monacoErr) body = react.default.createElement("div", { className: "edrv-empty" }, react.default.createElement("div", null, "Monaco 编辑器加载失败:" + String(monacoErr)), react.default.createElement("div", { style: { fontSize: 11 } }, "请确认插件包 assets/vendor/monaco 完整(/edrv/vendor 路由可达)"));
1977
+ if (!monaco && !monacoErr) body = loadingBody(loadStage.message, loadStage.progress);
1978
+ else if (monacoErr) body = react.default.createElement("div", { className: "edrv-empty" }, react.default.createElement("div", null, "Monaco 编辑器加载失败:" + String(monacoErr)), react.default.createElement("div", { style: { fontSize: 11 } }, "请确认插件包 assets/vendor/monaco 完整(/edrv/vendor 路由可达)"), react.default.createElement("button", {
1979
+ className: "edrv-pill edrv-pill-ghost",
1980
+ onClick: () => {
1981
+ setMonacoErr(null);
1982
+ setLoadStage({
1983
+ progress: 0,
1984
+ message: "准备重试 Monaco…"
1985
+ });
1986
+ }
1987
+ }, "重试"));
1719
1988
  else if (!active) body = react.default.createElement("div", { className: "edrv-empty" }, react.default.createElement("div", null, "暂无打开的文件"), react.default.createElement("div", { style: { fontSize: 12 } }, "使用右上搜索框 (Ctrl+P) 打开工作区文件;agent 修改文件后顶部会出现差异角标"));
1720
- else if (content === null) body = react.default.createElement("div", { className: "edrv-empty" }, "加载中…");
1989
+ else if (content === null && loadError) body = loadingBody("文件加载失败:" + loadError, 0, () => {
1990
+ setLoadError(null);
1991
+ setContent(null);
1992
+ setLoadStage({
1993
+ progress: 10,
1994
+ message: "重新读取文件…"
1995
+ });
1996
+ loadContent(active, sessionId);
1997
+ });
1998
+ else if (content === null) body = loadingBody(loadStage.message || "读取文件内容…", loadStage.progress);
1721
1999
  else body = react.default.createElement("div", { className: "edrv-monaco-host" }, react.default.createElement("div", { ref: ensureEditor }));
1722
2000
  const hoverEl = hoverAct && !launcherOpen ? react.default.createElement("div", {
1723
2001
  className: "edrv-hoveract",
@@ -1918,6 +2196,352 @@ window.__ModuleLoader__.load({
1918
2196
  } }, String(n)));
1919
2197
  }
1920
2198
  //#endregion
2199
+ //#region \0dsh-css:/home/runner/work/DSH_VsCodeMode/DSH_VsCodeMode/src/client/styles/mcp.css.mjs
2200
+ const css = "/* dsh-vscode-mode MCP 管理界面样式。\n 全部使用 DSH 真实主题变量(--dsw-alias-*),不再用不存在的 fg-* 变量。 */\n.vsm-mcp-page{display:flex;flex-direction:column;gap:18px;width:100%;max-width:920px;box-sizing:border-box;color:var(--dsw-alias-label-primary,#e6e6e6);padding-bottom:32px}\n\n/* 顶部:标题 + 副标题 + 添加按钮 */\n.vsm-mcp-header{display:flex;align-items:flex-start;justify-content:space-between;gap:20px}\n.vsm-mcp-header h2{margin:0 0 4px;font-size:20px;font-weight:600;line-height:1.35;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-mcp-header p{margin:0;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:13px;line-height:1.5}\n.vsm-primary{border:0;border-radius:6px;padding:8px 16px;background:var(--dsw-alias-button-primary-fill,#6d55ff);color:var(--dsw-alias-button-contrast-fill,#fff);font-size:13px;font-weight:500;cursor:pointer;white-space:nowrap}\n.vsm-primary:hover{background:var(--dsw-alias-button-primary-hover,#5a46e6)}\n.vsm-primary:disabled{opacity:.55;cursor:default}\n\n/* 子 Tab:我的 MCP / MCP 市场 */\n.vsm-mcp-tabs{display:flex;gap:24px;border-bottom:1px solid var(--dsw-alias-border-l2,#3a3a3a)}\n.vsm-mcp-tabs button{border:0;background:none;color:var(--dsw-alias-label-tertiary,#9aa5b1);padding:9px 2px 11px;cursor:pointer;font-size:14px;position:relative}\n.vsm-mcp-tabs button:hover{color:var(--dsw-alias-label-secondary,#c8c8c8)}\n.vsm-mcp-tabs button.active{color:var(--dsw-alias-label-primary,#fff);font-weight:500}\n.vsm-mcp-tabs button.active::after{content:\"\";position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--dsw-alias-brand-primary,#6d55ff);border-radius:2px}\n\n/* 服务卡片 */\n.vsm-mcp-card{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:10px;padding:14px 16px;margin-bottom:10px;background:var(--dsw-alias-bg-layer-2,transparent);transition:border-color .15s}\n.vsm-mcp-card:hover{border-color:var(--dsw-alias-border-l3,#555)}\n.vsm-mcp-card-head{display:flex;justify-content:space-between;align-items:center;gap:12px}\n.vsm-mcp-title{display:flex;align-items:center;gap:9px;font-weight:600;font-size:14px;color:var(--dsw-alias-label-primary,#e6e6e6);min-width:0}\n.vsm-mcp-source{font-size:11px;font-weight:400;padding:2px 7px;border-radius:4px;background:var(--dsw-alias-bg-mask-2,#ffffff22);color:var(--dsw-alias-label-tertiary,#9aa5b1);flex-shrink:0}\n.vsm-mcp-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;background:var(--dsw-alias-state-error-primary,#e05252)}\n.vsm-mcp-dot.connected{background:var(--dsw-alias-state-success-primary,#38bd75)}\n.vsm-mcp-dot.connecting{background:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-mcp-dot.disabled{background:var(--dsw-alias-label-tertiary,#9aa5b1)}\n.vsm-mcp-actions{display:flex;align-items:center;gap:4px;flex-shrink:0}\n.vsm-mcp-actions button{border:0;background:none;color:var(--dsw-alias-label-tertiary,#9aa5b1);cursor:pointer;font-size:15px;width:28px;height:28px;border-radius:6px;line-height:1}\n.vsm-mcp-actions button:hover{color:var(--dsw-alias-label-primary,#fff);background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-mcp-actions button.vsm-danger:hover{color:var(--dsw-alias-state-error-primary,#f07878);background:var(--dsw-alias-interactive-bg-hover-danger,#ff000014)}\n.vsm-switch{font-size:14px !important}\n.vsm-switch.on{color:var(--dsw-alias-state-success-primary,#38bd75)}\n.vsm-mcp-meta{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1);margin:9px 0}\n.vsm-mcp-error{color:var(--dsw-alias-state-error-primary,#f07878);font-size:12px;margin:8px 0}\n.vsm-mcp-banner{padding:9px 12px;border:1px solid var(--dsw-alias-state-error-secondary,#d9534f55);border-radius:6px;background:var(--dsw-alias-state-error-secondary,#d9534f1a);margin-bottom:10px}\n\n/* 工具 chips */\n.vsm-mcp-tools{display:flex;flex-wrap:wrap;gap:6px}\n.vsm-mcp-chip{font:12px var(--ds-font-family-code,ui-monospace,monospace);padding:4px 8px;border-radius:5px;background:var(--dsw-alias-bg-mask-2,#ffffff14);color:var(--dsw-alias-label-secondary,#c8c8c8);border:1px solid var(--dsw-alias-border-l1,#333)}\n\n/* 空态 / 加载态 */\n.vsm-mcp-empty{text-align:center;padding:56px 20px;color:var(--dsw-alias-label-tertiary,#9aa5b1);border:1px dashed var(--dsw-alias-border-l2,#3a3a3a);border-radius:8px;font-size:13px}\n\n/* 项目搜索下拉框 */\n.vsm-project-picker{position:relative;width:100%;z-index:4}\n.vsm-project-picker-trigger{width:100%;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:8px;background:var(--dsw-alias-bg-layer-1,transparent);color:var(--dsw-alias-label-primary,#e6e6e6);cursor:pointer;text-align:left}\n.vsm-project-picker-trigger:hover{border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-picker-label{display:flex;flex-direction:column;min-width:0;font-size:13px;font-weight:500}\n.vsm-project-picker-label small{margin-top:3px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font:11px var(--ds-font-family-code,ui-monospace,monospace);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.vsm-project-picker-arrow{color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:16px}\n.vsm-project-picker-menu{position:absolute;top:calc(100% + 6px);left:0;right:0;padding:8px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:9px;background:var(--dsw-alias-bg-overlay,#202020);box-shadow:0 12px 36px #0008}\n.vsm-project-picker-search{width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:var(--dsw-alias-bg-layer-1,#2b2b2b);color:var(--dsw-alias-label-primary,#eee);outline:none}\n.vsm-project-picker-search:focus{border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-picker-options{max-height:260px;overflow:auto;margin-top:6px}\n.vsm-project-option{display:flex;width:100%;flex-direction:column;gap:3px;padding:8px 9px;border:0;border-radius:6px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;text-align:left}\n.vsm-project-option:hover,.vsm-project-option.highlight{background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-project-option.selected{color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-option small{overflow:hidden;color:var(--dsw-alias-label-tertiary,#9aa5b1);font:11px var(--ds-font-family-code,ui-monospace,monospace);text-overflow:ellipsis;white-space:nowrap}\n.vsm-project-picker-empty{padding:18px 8px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px;text-align:center}\n\n/* 项目级 MCP 分组 */\n.vsm-project{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:12px;margin-bottom:14px;overflow:hidden;background:var(--dsw-alias-bg-layer-1,transparent)}\n.vsm-project-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l1,#333);background:var(--dsw-alias-bg-mask-1,#ffffff0a)}\n.vsm-project-title{display:flex;align-items:center;gap:10px;min-width:0}\n.vsm-project-icon{color:var(--dsw-alias-brand-primary,#6d55ff);font-size:15px;flex-shrink:0}\n.vsm-project-name{font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6);display:block}\n.vsm-project-path{font-size:11px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-family:var(--ds-font-family-code,ui-monospace,monospace);display:block;margin-top:2px;word-break:break-all}\n.vsm-primary.vsm-small{padding:5px 12px;font-size:12px}\n.vsm-project-empty{text-align:center;padding:26px 16px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px}\n.vsm-project .vsm-mcp-card{border:0;border-radius:0;margin:0;border-bottom:1px solid var(--dsw-alias-border-l1,#333);background:transparent}\n.vsm-project .vsm-mcp-card:last-child{border-bottom:0}\n.vsm-project .vsm-mcp-banner{border-radius:0;margin:0}\n\n/* 新增 MCP 弹窗 */\n.vsm-mcp-modal{position:fixed;inset:0;background:var(--dsw-alias-bg-mask-drop,#000000aa);display:flex;align-items:center;justify-content:center;z-index:50}\n.vsm-mcp-dialog{width:min(560px,calc(100vw - 40px));max-height:88vh;overflow:auto;padding:22px 24px;border-radius:12px;background:var(--dsw-alias-bg-overlay,#202020);border:1px solid var(--dsw-alias-border-l2,#444);box-shadow:0 16px 60px #000c;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-mcp-dialog h3{margin:0 0 14px;font-size:16px;font-weight:600}\n.vsm-mcp-dialog label{display:flex;flex-direction:column;gap:6px;margin:12px 0;font-size:12px;color:var(--dsw-alias-label-secondary,#aaa)}\n.vsm-mcp-dialog input,.vsm-mcp-dialog select,.vsm-mcp-dialog textarea{font:inherit;color:var(--dsw-alias-label-primary,#eee);background:var(--dsw-alias-bg-layer-1,#2b2b2b);border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;padding:8px 10px}\n.vsm-mcp-dialog input:focus,.vsm-mcp-dialog select:focus,.vsm-mcp-dialog textarea:focus{outline:none;border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-mcp-dialog textarea{min-height:64px;resize:vertical;font-family:var(--ds-font-family-code,ui-monospace,monospace)}\n.vsm-mcp-dialog-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:20px}\n.vsm-mcp-dialog-actions button{padding:8px 14px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;font-size:13px}\n.vsm-mcp-dialog-actions button:hover{background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n";
2201
+ const tagId = "dsh-vscode-mode/mcp.css";
2202
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
2203
+ const tag = document.createElement("style");
2204
+ tag.dataset.plugin = "dsh-vscode-mode";
2205
+ tag.dataset.pluginCss = tagId;
2206
+ tag.textContent = css;
2207
+ document.head.appendChild(tag);
2208
+ }
2209
+ //#endregion
2210
+ //#region src/client/ui/McpSettings.ts
2211
+ /**
2212
+ * dsh-vscode-mode client — VSCodeMode 设置区内的 MCP 管理。
2213
+ * 子 Tab:我的 MCP(profile 全局)/ 项目 MCP(各项目 .mcp.json)/ MCP 市场(占位)。
2214
+ * 作者 ddj 2026年08月22号
2215
+ */
2216
+ const EMPTY = {
2217
+ serverName: "",
2218
+ transport: "stdio",
2219
+ command: "",
2220
+ args: "",
2221
+ cwd: "",
2222
+ url: "",
2223
+ headers: ""
2224
+ };
2225
+ /** 将表单草稿转换为 Host MCP 配置。 */
2226
+ function configOf(draft) {
2227
+ const config = {
2228
+ serverName: draft.serverName.trim(),
2229
+ transport: draft.transport
2230
+ };
2231
+ if (draft.transport === "stdio") {
2232
+ config.command = draft.command.trim();
2233
+ config.args = draft.args.split(/\r?\n|\s+/).filter(Boolean);
2234
+ if (draft.cwd.trim()) config.cwd = draft.cwd.trim();
2235
+ } else {
2236
+ config.url = draft.url.trim();
2237
+ config.headers = parsePairs(draft.headers);
2238
+ }
2239
+ return config;
2240
+ }
2241
+ /** 解析每行 key=value 的表单字段。 */
2242
+ function parsePairs(text) {
2243
+ return Object.fromEntries(text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => {
2244
+ const idx = line.indexOf("=");
2245
+ return idx > 0 ? [line.slice(0, idx).trim(), line.slice(idx + 1).trim()] : [line, ""];
2246
+ }));
2247
+ }
2248
+ /** 服务状态文案。 */
2249
+ function statusOf(server) {
2250
+ return server.status === "connected" ? "在线" : server.status === "connecting" ? "连接中" : server.status === "disabled" ? "已禁用" : "错误";
2251
+ }
2252
+ /** MCP 服务卡片(全局与项目共用)。 */
2253
+ function ServerCard({ server, onRefresh, onToggle, onRemove }) {
2254
+ return react.default.createElement("article", { className: "vsm-mcp-card" }, react.default.createElement("div", { className: "vsm-mcp-card-head" }, react.default.createElement("div", { className: "vsm-mcp-title" }, react.default.createElement("span", { className: "vsm-mcp-dot " + (server.enabled ? server.status : "disabled") }), server.serverName), react.default.createElement("div", { className: "vsm-mcp-actions" }, react.default.createElement("button", {
2255
+ onClick: () => onRefresh(server.id),
2256
+ title: "刷新连接"
2257
+ }, "↻"), react.default.createElement("button", {
2258
+ className: "vsm-danger",
2259
+ onClick: () => onRemove(server.id),
2260
+ title: "删除 MCP"
2261
+ }, "⌫"), react.default.createElement("button", {
2262
+ className: "vsm-switch " + (server.enabled ? "on" : ""),
2263
+ onClick: () => onToggle(server),
2264
+ "aria-label": server.enabled ? "禁用" : "启用"
2265
+ }, server.enabled ? "●" : "○"))), react.default.createElement("div", { className: "vsm-mcp-meta" }, statusOf(server), " · ", server.toolCount, " 个工具 · ", server.transport), server.error && react.default.createElement("div", { className: "vsm-mcp-error" }, server.error), react.default.createElement("div", { className: "vsm-mcp-tools" }, server.tools.map((tool) => react.default.createElement("span", {
2266
+ key: tool.name,
2267
+ className: "vsm-mcp-chip",
2268
+ title: tool.description || tool.name
2269
+ }, tool.name))));
2270
+ }
2271
+ /** 按项目标题或路径过滤,大小写不敏感。 */
2272
+ function filterProjects(projects, query) {
2273
+ const needle = String(query ?? "").trim().toLowerCase();
2274
+ if (!needle) return projects;
2275
+ return projects.filter((project) => `${project.title} ${project.workspacePath}`.toLowerCase().includes(needle));
2276
+ }
2277
+ /** 带搜索的项目组合框。 */
2278
+ function ProjectPicker({ projects, value, onChange }) {
2279
+ const [open, setOpen] = react.default.useState(false);
2280
+ const [query, setQuery] = react.default.useState("");
2281
+ const [highlight, setHighlight] = react.default.useState(0);
2282
+ const rootRef = react.default.useRef(null);
2283
+ const selected = projects.find((project) => project.workspacePath === value);
2284
+ const filtered = react.default.useMemo(() => filterProjects(projects, query), [projects, query]);
2285
+ react.default.useEffect(() => {
2286
+ const close = (event) => {
2287
+ if (!rootRef.current?.contains(event.target)) setOpen(false);
2288
+ };
2289
+ document.addEventListener("mousedown", close);
2290
+ return () => document.removeEventListener("mousedown", close);
2291
+ }, []);
2292
+ const choose = (project) => {
2293
+ onChange(project.workspacePath);
2294
+ setQuery("");
2295
+ setOpen(false);
2296
+ };
2297
+ const move = (delta) => setHighlight((old) => Math.max(0, Math.min(Math.max(0, filtered.length - 1), old + delta)));
2298
+ const keyDown = (event) => {
2299
+ if (event.key === "ArrowDown") {
2300
+ event.preventDefault();
2301
+ move(1);
2302
+ } else if (event.key === "ArrowUp") {
2303
+ event.preventDefault();
2304
+ move(-1);
2305
+ } else if (event.key === "Enter" && filtered[highlight]) {
2306
+ event.preventDefault();
2307
+ choose(filtered[highlight]);
2308
+ } else if (event.key === "Escape") {
2309
+ event.preventDefault();
2310
+ setOpen(false);
2311
+ }
2312
+ };
2313
+ return react.default.createElement("div", {
2314
+ className: "vsm-project-picker",
2315
+ ref: rootRef
2316
+ }, react.default.createElement("button", {
2317
+ className: "vsm-project-picker-trigger",
2318
+ role: "combobox",
2319
+ "aria-expanded": open,
2320
+ onClick: () => setOpen((old) => !old)
2321
+ }, react.default.createElement("span", { className: "vsm-project-picker-label" }, selected?.title || "选择项目", selected && react.default.createElement("small", null, selected.workspacePath)), react.default.createElement("span", { className: "vsm-project-picker-arrow" }, open ? "⌃" : "⌄")), open && react.default.createElement("div", { className: "vsm-project-picker-menu" }, react.default.createElement("input", {
2322
+ autoFocus: true,
2323
+ className: "vsm-project-picker-search",
2324
+ value: query,
2325
+ placeholder: "搜索项目名称或路径…",
2326
+ onChange: (event) => {
2327
+ setQuery(event.target.value);
2328
+ setHighlight(0);
2329
+ },
2330
+ onKeyDown: keyDown
2331
+ }), react.default.createElement("div", {
2332
+ className: "vsm-project-picker-options",
2333
+ role: "listbox"
2334
+ }, filtered.length ? filtered.map((project, index) => react.default.createElement("button", {
2335
+ key: project.workspacePath,
2336
+ role: "option",
2337
+ "aria-selected": project.workspacePath === value,
2338
+ className: "vsm-project-option " + (index === highlight ? "highlight" : "") + (project.workspacePath === value ? " selected" : ""),
2339
+ onMouseEnter: () => setHighlight(index),
2340
+ onClick: () => choose(project)
2341
+ }, react.default.createElement("span", null, project.title), react.default.createElement("small", null, project.workspacePath, " · ", project.servers.length, " 个 MCP"))) : react.default.createElement("div", { className: "vsm-project-picker-empty" }, "没有匹配的项目"))));
2342
+ }
2343
+ /** 单个项目的 MCP 分组。 */
2344
+ function ProjectGroup({ project, busy, onAdd, onRefresh, onToggle, onRemove }) {
2345
+ const head = react.default.createElement("div", { className: "vsm-project-head" }, react.default.createElement("div", { className: "vsm-project-title" }, react.default.createElement("span", { className: "vsm-project-icon" }, "▣"), react.default.createElement("div", null, react.default.createElement("span", { className: "vsm-project-name" }, project.title), react.default.createElement("span", { className: "vsm-project-path" }, project.workspacePath))), react.default.createElement("button", {
2346
+ className: "vsm-primary vsm-small",
2347
+ onClick: () => onAdd(project)
2348
+ }, "+ 添加 MCP"));
2349
+ let body;
2350
+ if (project.missingDir) body = react.default.createElement("div", { className: "vsm-project-empty" }, "项目目录已不存在,无法管理 MCP");
2351
+ else if (project.fileError) body = react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, project.fileError);
2352
+ else if (!project.servers.length) body = react.default.createElement("div", { className: "vsm-project-empty" }, "此项目未配置 MCP");
2353
+ else body = project.servers.map((server) => react.default.createElement(ServerCard, {
2354
+ key: server.serverName,
2355
+ server,
2356
+ onRefresh: (id) => onRefresh(project, server, id),
2357
+ onToggle: () => onToggle(project, server),
2358
+ onRemove: (id) => onRemove(project, server, id)
2359
+ }));
2360
+ return react.default.createElement("section", { className: "vsm-project" }, head, body);
2361
+ }
2362
+ /** MCP 管理主面板。 */
2363
+ function McpSettings() {
2364
+ const [servers, setServers] = react.default.useState([]);
2365
+ const [projects, setProjects] = react.default.useState([]);
2366
+ const [loading, setLoading] = react.default.useState(true);
2367
+ const [busy, setBusy] = react.default.useState("");
2368
+ const [error, setError] = react.default.useState("");
2369
+ const [tab, setTab] = react.default.useState("mine");
2370
+ const [draft, setDraft] = react.default.useState(EMPTY);
2371
+ const [showForm, setShowForm] = react.default.useState(false);
2372
+ const [projectForm, setProjectForm] = react.default.useState(null);
2373
+ const [selectedPath, setSelectedPath] = react.default.useState("");
2374
+ react.default.useEffect(() => {
2375
+ if (!projects.length) {
2376
+ setSelectedPath("");
2377
+ return;
2378
+ }
2379
+ if (!projects.some((project) => project.workspacePath === selectedPath)) setSelectedPath(projects[0].workspacePath);
2380
+ }, [projects, selectedPath]);
2381
+ const loadAll = react.default.useCallback(() => {
2382
+ setLoading(true);
2383
+ setError("");
2384
+ Promise.all([rpc("mcp.list", {}), rpc("mcp.projects", {})]).then(([list, prj]) => {
2385
+ if (!list.ok) throw new Error(list.error);
2386
+ if (!prj.ok) throw new Error(prj.error);
2387
+ setServers(list.servers);
2388
+ setProjects(prj.projects);
2389
+ }).catch((e) => setError(String(e))).finally(() => setLoading(false));
2390
+ }, []);
2391
+ react.default.useEffect(loadAll, [loadAll]);
2392
+ const finish = (result) => {
2393
+ if (!result.ok) throw new Error(result.error);
2394
+ return result;
2395
+ };
2396
+ const action = (label, id, method, args, patch) => {
2397
+ setBusy(id);
2398
+ setError("");
2399
+ return rpc(method, args).then(finish).then((result) => {
2400
+ if (patch) patch(result);
2401
+ }).catch((e) => setError(String(e))).finally(() => setBusy("")).then(() => label);
2402
+ };
2403
+ const refreshGlobal = (id) => action("refresh", id, "mcp.refresh", { id }, (r) => setServers((old) => old.map((s) => s.id === id ? r.server : s)));
2404
+ const toggleGlobal = (s) => action("toggle", s.id, "mcp.toggle", {
2405
+ id: s.id,
2406
+ enabled: !s.enabled
2407
+ }, (r) => setServers((old) => old.map((x) => x.id === s.id ? r.server : x)));
2408
+ const removeGlobal = (s) => window.confirm("确认删除此 MCP 服务?") && action("remove", s.id, "mcp.remove", { id: s.id }, (r) => setServers((old) => old.filter((x) => x.id !== s.id)));
2409
+ const replaceProject = (workspacePath) => (result) => setProjects((old) => old.map((p) => p.workspacePath === workspacePath ? result.project : p));
2410
+ const projectAction = (workspacePath, serverName, method, args, patch) => {
2411
+ setBusy(workspacePath + ":" + serverName);
2412
+ setError("");
2413
+ return rpc(method, args).then(finish).then((result) => {
2414
+ if (patch) patch(result);
2415
+ }).catch((e) => setError(String(e))).finally(() => setBusy(""));
2416
+ };
2417
+ const refreshProject = (p, s) => projectAction(p.workspacePath, s.serverName, "mcp.projectRefresh", {
2418
+ workspacePath: p.workspacePath,
2419
+ serverName: s.serverName
2420
+ }, replaceProject(p.workspacePath));
2421
+ const toggleProject = (p, s) => projectAction(p.workspacePath, s.serverName, "mcp.projectToggle", {
2422
+ workspacePath: p.workspacePath,
2423
+ serverName: s.serverName,
2424
+ enabled: !s.enabled
2425
+ }, replaceProject(p.workspacePath));
2426
+ const removeProject = (p, s) => window.confirm("确认删除此项目的 MCP「" + s.serverName + "」?") && projectAction(p.workspacePath, s.serverName, "mcp.projectRemove", {
2427
+ workspacePath: p.workspacePath,
2428
+ serverName: s.serverName
2429
+ }, replaceProject(p.workspacePath));
2430
+ const saveGlobal = () => {
2431
+ setBusy("save");
2432
+ setError("");
2433
+ rpc("mcp.save", { config: configOf(draft) }).then(finish).then((result) => {
2434
+ setServers((old) => old.some((s) => s.id === result.server.id) ? old.map((s) => s.id === result.server.id ? result.server : s) : old.concat(result.server));
2435
+ setDraft(EMPTY);
2436
+ setShowForm(false);
2437
+ }).catch((e) => setError(String(e))).finally(() => setBusy(""));
2438
+ };
2439
+ const saveProject = () => {
2440
+ if (!projectForm) return;
2441
+ setBusy("project-save");
2442
+ setError("");
2443
+ const config = configOf(draft);
2444
+ rpc("mcp.projectSave", {
2445
+ workspacePath: projectForm.workspacePath,
2446
+ serverName: config.serverName,
2447
+ config
2448
+ }).then(finish).then((result) => {
2449
+ setProjects((old) => old.map((p) => p.workspacePath === projectForm.workspacePath ? result.project : p));
2450
+ setDraft(EMPTY);
2451
+ setProjectForm(null);
2452
+ }).catch((e) => setError(String(e))).finally(() => setBusy(""));
2453
+ };
2454
+ const edit = (key, value) => setDraft((old) => ({
2455
+ ...old,
2456
+ [key]: value
2457
+ }));
2458
+ const marketBody = react.default.createElement("div", { className: "vsm-mcp-empty" }, "MCP 市场暂未接入");
2459
+ const selectedProject = projects.find((project) => project.workspacePath === selectedPath);
2460
+ const projectBody = projects.length ? react.default.createElement(react.default.Fragment, null, react.default.createElement(ProjectPicker, {
2461
+ projects,
2462
+ value: selectedPath,
2463
+ onChange: setSelectedPath
2464
+ }), selectedProject ? react.default.createElement(ProjectGroup, {
2465
+ project: selectedProject,
2466
+ busy,
2467
+ onAdd: (project) => {
2468
+ setDraft(EMPTY);
2469
+ setProjectForm({
2470
+ workspacePath: project.workspacePath,
2471
+ title: project.title
2472
+ });
2473
+ },
2474
+ onRefresh: refreshProject,
2475
+ onToggle: toggleProject,
2476
+ onRemove: removeProject
2477
+ }) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "请选择项目")) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "还没有项目");
2478
+ let body;
2479
+ if (loading) body = react.default.createElement("div", { className: "vsm-mcp-empty" }, "正在读取 MCP 服务…");
2480
+ else if (tab === "mine") body = react.default.createElement("div", null, servers.length ? servers.map((s) => react.default.createElement(ServerCard, {
2481
+ key: s.id,
2482
+ server: s,
2483
+ onRefresh: refreshGlobal,
2484
+ onToggle: toggleGlobal,
2485
+ onRemove: removeGlobal
2486
+ })) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "还没有配置全局 MCP"));
2487
+ else if (tab === "projects") body = projectBody;
2488
+ else body = marketBody;
2489
+ const form = showForm ? react.default.createElement(McpForm, {
2490
+ title: "添加全局 MCP",
2491
+ draft,
2492
+ busy,
2493
+ edit,
2494
+ save: saveGlobal,
2495
+ close: () => setShowForm(false)
2496
+ }) : projectForm ? react.default.createElement(McpForm, {
2497
+ title: "添加项目 MCP · " + projectForm.title,
2498
+ draft,
2499
+ busy,
2500
+ edit,
2501
+ save: saveProject,
2502
+ close: () => setProjectForm(null)
2503
+ }) : null;
2504
+ return react.default.createElement("section", { className: "vsm-mcp-page" }, react.default.createElement("header", { className: "vsm-mcp-header" }, react.default.createElement("div", null, react.default.createElement("h2", null, "VSCodeMode"), react.default.createElement("p", null, "管理当前 profile 与各项目的 Model Context Protocol 服务。")), react.default.createElement("button", {
2505
+ className: "vsm-primary",
2506
+ onClick: () => {
2507
+ setDraft(EMPTY);
2508
+ setShowForm(true);
2509
+ }
2510
+ }, "+ 添加全局 MCP")), react.default.createElement("nav", { className: "vsm-mcp-tabs" }, react.default.createElement("button", {
2511
+ className: tab === "mine" ? "active" : "",
2512
+ onClick: () => setTab("mine")
2513
+ }, "我的 MCP"), react.default.createElement("button", {
2514
+ className: tab === "projects" ? "active" : "",
2515
+ onClick: () => setTab("projects")
2516
+ }, "项目 MCP"), react.default.createElement("button", {
2517
+ className: tab === "market" ? "active" : "",
2518
+ onClick: () => setTab("market")
2519
+ }, "MCP 市场")), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), body, form);
2520
+ }
2521
+ /** MCP 新增表单(全局/项目共用)。 */
2522
+ function McpForm({ title, draft, busy, edit, save, close }) {
2523
+ const input = (key, placeholder) => react.default.createElement("input", {
2524
+ value: draft[key],
2525
+ onChange: (e) => edit(key, e.target.value),
2526
+ placeholder
2527
+ });
2528
+ const fields = draft.transport === "stdio" ? react.default.createElement(react.default.Fragment, null, react.default.createElement("label", null, "命令", input("command", "npx")), react.default.createElement("label", null, "参数(空格或换行分隔)", react.default.createElement("textarea", {
2529
+ value: draft.args,
2530
+ onChange: (e) => edit("args", e.target.value)
2531
+ })), react.default.createElement("label", null, "工作目录(可选)", input("cwd", ""))) : react.default.createElement(react.default.Fragment, null, react.default.createElement("label", null, "URL", input("url", "http://localhost:3000/mcp")), react.default.createElement("label", null, "请求头(每行 key=value)", react.default.createElement("textarea", {
2532
+ value: draft.headers,
2533
+ onChange: (e) => edit("headers", e.target.value)
2534
+ })));
2535
+ return react.default.createElement("div", { className: "vsm-mcp-modal" }, react.default.createElement("div", { className: "vsm-mcp-dialog" }, react.default.createElement("h3", null, title), react.default.createElement("label", null, "名称", input("serverName", "例如 codegraph")), react.default.createElement("label", null, "传输方式", react.default.createElement("select", {
2536
+ value: draft.transport,
2537
+ onChange: (e) => edit("transport", e.target.value)
2538
+ }, react.default.createElement("option", { value: "stdio" }, "stdio"), react.default.createElement("option", { value: "streamable-http" }, "streamable-http"))), fields, react.default.createElement("div", { className: "vsm-mcp-dialog-actions" }, react.default.createElement("button", { onClick: close }, "取消"), react.default.createElement("button", {
2539
+ className: "vsm-primary",
2540
+ disabled: busy === "save" || busy === "project-save",
2541
+ onClick: save
2542
+ }, "保存并连接"))));
2543
+ }
2544
+ //#endregion
1921
2545
  //#region src/client/index.ts
1922
2546
  /**
1923
2547
  * dsh-vscode-mode client — 浏览器半入口:slot 注册 + 装配。
@@ -1932,7 +2556,13 @@ window.__ModuleLoader__.load({
1932
2556
  * 故跨版本兼容。slot 名未变:conversation.view / conversation.session.header.utilities。
1933
2557
  * 作者 ddj 2026-08-20
1934
2558
  */
1935
- const inject = ["slots", "timer"];
2559
+ const inject = [
2560
+ "slots",
2561
+ "timer",
2562
+ "locale",
2563
+ "connection",
2564
+ "remote"
2565
+ ];
1936
2566
  /**
1937
2567
  * 装配客户端:注册中央编辑区视图与 header 差异角标。
1938
2568
  * @author ddj 2026年08月20号
@@ -1953,6 +2583,12 @@ window.__ModuleLoader__.load({
1953
2583
  order: 90,
1954
2584
  label: "差异"
1955
2585
  }, (props) => react.default.createElement(DiffBadge, Object.assign({}, props))));
2586
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
2587
+ name: "settings.section",
2588
+ id: "vscode-mode",
2589
+ order: 30,
2590
+ label: "VSCodeMode"
2591
+ }, () => react.default.createElement(McpSettings)));
1956
2592
  }
1957
2593
  //#endregion
1958
2594
  exports.apply = apply;