dsh-vscode-mode 0.1.15 → 0.1.17
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 +30 -3
- package/lib/client.js +169 -35
- package/lib/client.js.map +1 -1
- package/lib/index.js +550 -197
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/compat.ts +91 -0
- package/src/client/fileOpeners.ts +2 -1
- package/src/client/index.ts +18 -10
- package/src/client/openPathRouter.ts +5 -5
- package/src/client/settingsContext.ts +7 -17
- package/src/client/styles/mcp.css +17 -0
- package/src/client/ui/McpSettings.ts +68 -2
- package/src/compat.ts +147 -0
- package/src/devForm.ts +133 -0
- package/src/fileOpenSettings.ts +58 -6
- package/src/index.ts +33 -5
- package/src/mcp.ts +8 -9
- package/src/mcpIsolation.ts +6 -12
- package/src/routes.ts +26 -7
- package/src/rpc.ts +13 -0
- package/src/shared/compat.ts +27 -0
- package/src/shared/rpc.ts +7 -0
package/README.md
CHANGED
|
@@ -84,23 +84,50 @@ npm pack # 产物 tgz(含 lib/assets/src/cordis.patch.yml)
|
|
|
84
84
|
|
|
85
85
|
```
|
|
86
86
|
src/
|
|
87
|
-
├── index.ts Host 入口:name/inject/apply
|
|
87
|
+
├── index.ts Host 入口:name/inject/apply(薄装配,含兼容性报告启动日志)
|
|
88
88
|
├── shared/ ★ 双面契约(禁 node/react):types.ts(记录/归档/摘要)+ rpc.ts(11 个方法类型化)
|
|
89
|
+
│ + compat.ts(兼容性报告形状)+ mcp.ts(MCP 契约)
|
|
90
|
+
├── compat.ts ★ Host 兼容层:身份常量、外部插件探测、路由/重复装配护栏、依赖守卫、兼容性报告
|
|
91
|
+
├── devForm.ts Host 开发形态管理:link:/junction 安装切换(RPC vscode.devForm* + 设置页开关)
|
|
89
92
|
├── model.ts Host 纯域逻辑(可单测):normalize/markDecision/resolved/summary/reconstruct/批次/归档
|
|
90
93
|
├── store.ts Host 存储层:sidecar 读写合并 + 归档持久化 + stale 检测
|
|
91
94
|
├── workspace.ts Host 工作区文件扫描(TTL 缓存)+ 快速打开搜索
|
|
92
95
|
├── revert.ts Host 回滚/删除(fs + subprocess,fs.contains 边界校验)
|
|
93
96
|
├── registry.ts Host 每工作区记录桶注册表
|
|
94
|
-
├── rpc.ts Host RPC 分发表(类型化 handler 表替代巨型 switch)
|
|
95
|
-
├── routes.ts Host webServer 路由(/edrv/rpc、/edrv/assets/*、/edrv/vendor
|
|
97
|
+
├── rpc.ts Host RPC 分发表(类型化 handler 表替代巨型 switch,含 compat)
|
|
98
|
+
├── routes.ts Host webServer 路由(/edrv/rpc、/edrv/assets/*、/edrv/vendor/*,带冲突护栏)
|
|
96
99
|
└── client/
|
|
97
100
|
├── index.ts Client 入口:slot 注册(inject=['slots','timer'])
|
|
101
|
+
├── compat.ts ★ Client 兼容层:设置桥三级降级(webUiSettings→settingsScope)、slot 安全注册、openPath 链式补丁、外部插件常量
|
|
98
102
|
├── rpc.ts Client 类型化 fetch 包装 + 诊断日志
|
|
99
103
|
├── events.ts 窗口事件助手(edrv:refresh/open-editor/show-launcher)
|
|
100
104
|
├── state/ records.ts(摘要/计数/空差异)+ regions.ts(差异区域/行裁剪)纯函数
|
|
101
105
|
├── monaco/ loader.ts(AMD 加载/语言映射)+ diffRender.ts(差异自绘渲染器)
|
|
102
106
|
├── styles/editor.css 编辑区样式(tsdown CSS-inline 注入)
|
|
103
107
|
└── ui/ EditorView(编排)/ QuickOpen / DiffBox / DiffBarEmpty / DiffLauncher / DiffBadge
|
|
108
|
+
/ McpSettings(含「兼容性」子 Tab)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**兼容层(`src/compat.ts` + `src/client/compat.ts`)**:集中处理与其他插件 / DSH 版本的适配——
|
|
112
|
+
运行时探测 `@deepseek-ai/dsh-mcp-client`、设置桥(`webUiSettings` → `settingsScope`)等外部依赖,
|
|
113
|
+
护栏检测 `/edrv` 路由前缀冲突与本插件重复装配(duplicate loader entry),
|
|
114
|
+
`@deepseek-ai/dsh-settings` / `schemastery` 以动态加载引入(缺失时插件仍可加载,设置持久化降级)。
|
|
115
|
+
「VSCodeMode」设置页 →「兼容性」子 Tab 或 RPC `edrv.compat` 可查看完整报告。
|
|
116
|
+
|
|
117
|
+
**开发形态(`src/devForm.ts`)**:开发 = profile 中以 `link:` 依赖 + junction 指向工作区的安装。
|
|
118
|
+
「VSCodeMode」设置页 → 通用 里在开发形态开启时显示「关闭开发形态」开关(切回正式版安装,重启生效);
|
|
119
|
+
AI 后续开发时可通过 RPC 自动开启/关闭:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# 读取当前形态(compat 报告亦含 devForm 字段)
|
|
123
|
+
curl -s -X POST http://127.0.0.1:3080/edrv/rpc -H 'content-type: application/json' \
|
|
124
|
+
-d '{"method":"vscode.devFormGet","args":{}}'
|
|
125
|
+
# 开启开发形态(link 到工作区;path 必填,重启后生效)
|
|
126
|
+
curl -s -X POST http://127.0.0.1:3080/edrv/rpc -H 'content-type: application/json' \
|
|
127
|
+
-d '{"method":"vscode.devFormSet","args":{"enabled":true,"path":"D:/Work/ToolsDev/DeepSeekHarnessPlugin/packages/dsh-edit-review"}}'
|
|
128
|
+
# 关闭开发形态(改回 ^<version> + 删 junction + pnpm install,重启后生效)
|
|
129
|
+
curl -s -X POST http://127.0.0.1:3080/edrv/rpc -H 'content-type: application/json' \
|
|
130
|
+
-d '{"method":"vscode.devFormSet","args":{"enabled":false}}'
|
|
104
131
|
```
|
|
105
132
|
|
|
106
133
|
**扩展缝(VSCode 化后续迭代)**:新能力 = `shared/rpc.ts` 加方法 + `src/rpc.ts` 加 handler +
|
package/lib/client.js
CHANGED
|
@@ -2200,7 +2200,7 @@ window.__ModuleLoader__.load({
|
|
|
2200
2200
|
}
|
|
2201
2201
|
//#endregion
|
|
2202
2202
|
//#region \0dsh-css:/home/runner/work/DSH_VsCodeMode/DSH_VsCodeMode/src/client/styles/mcp.css.mjs
|
|
2203
|
-
const css = "/* dsh-vscode-mode MCP 管理界面样式。\n 全部使用 DSH 真实主题变量(--dsw-alias-*),不再用不存在的 fg-* 变量。 */\n.vsm-mcp-page{display:flex;flex-direction:column;gap:18px;width:100%;max-width:920px;box-sizing:border-box;color:var(--dsw-alias-label-primary,#e6e6e6);padding-bottom:32px}\n\n/* 顶部:标题 + 副标题 + 添加按钮 */\n.vsm-mcp-header{display:flex;align-items:flex-start;justify-content:space-between;gap:20px}\n.vsm-mcp-header h2{margin:0 0 4px;font-size:20px;font-weight:600;line-height:1.35;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-mcp-header p{margin:0;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:13px;line-height:1.5}\n.vsm-primary{border:0;border-radius:6px;padding:8px 16px;background:var(--dsw-alias-button-primary-fill,#6d55ff);color:var(--dsw-alias-button-contrast-fill,#fff);font-size:13px;font-weight:500;cursor:pointer;white-space:nowrap}\n.vsm-primary:hover{background:var(--dsw-alias-button-primary-hover,#5a46e6)}\n.vsm-primary:disabled{opacity:.55;cursor:default}\n\n/* 子 Tab:我的 MCP / MCP 市场 */\n.vsm-mcp-tabs{display:flex;gap:24px;border-bottom:1px solid var(--dsw-alias-border-l2,#3a3a3a)}\n.vsm-mcp-tabs button{border:0;background:none;color:var(--dsw-alias-label-tertiary,#9aa5b1);padding:9px 2px 11px;cursor:pointer;font-size:14px;position:relative}\n.vsm-mcp-tabs button:hover{color:var(--dsw-alias-label-secondary,#c8c8c8)}\n.vsm-mcp-tabs button.active{color:var(--dsw-alias-label-primary,#fff);font-weight:500}\n.vsm-mcp-tabs button.active::after{content:\"\";position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--dsw-alias-brand-primary,#6d55ff);border-radius:2px}\n\n/* 通用设置 */\n.vsm-general-page{display:flex;flex-direction:column;gap:10px;max-width:720px}\n.vsm-general-page h2{margin:0;font-size:20px;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-general-page p{margin:0 0 8px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:13px}\n.vsm-general-row{display:flex;align-items:center;justify-content:space-between;gap:20px;padding:14px 0;border-bottom:1px solid var(--dsw-alias-border-l1,#333);color:var(--dsw-alias-label-primary,#eee);font-size:14px}\n.vsm-general-row select{min-width:220px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:var(--dsw-alias-bg-layer-1,#2b2b2b);color:var(--dsw-alias-label-primary,#eee);font:inherit}\n.vsm-general-row small{color:var(--dsw-alias-label-tertiary,#9aa5b1)}\n\n/* 服务卡片 */\n.vsm-mcp-card{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:10px;padding:14px 16px;margin-bottom:10px;background:var(--dsw-alias-bg-layer-2,transparent);transition:border-color .15s}\n.vsm-mcp-card:hover{border-color:var(--dsw-alias-border-l3,#555)}\n.vsm-mcp-card-head{display:flex;justify-content:space-between;align-items:center;gap:12px}\n.vsm-mcp-title{display:flex;align-items:center;gap:9px;font-weight:600;font-size:14px;color:var(--dsw-alias-label-primary,#e6e6e6);min-width:0}\n.vsm-mcp-source{font-size:11px;font-weight:400;padding:2px 7px;border-radius:4px;background:var(--dsw-alias-bg-mask-2,#ffffff22);color:var(--dsw-alias-label-tertiary,#9aa5b1);flex-shrink:0}\n.vsm-mcp-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;background:var(--dsw-alias-state-error-primary,#e05252)}\n.vsm-mcp-dot.connected{background:var(--dsw-alias-state-success-primary,#38bd75)}\n.vsm-mcp-dot.connecting{background:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-mcp-dot.disabled{background:var(--dsw-alias-label-tertiary,#9aa5b1)}\n.vsm-mcp-actions{display:flex;align-items:center;gap:4px;flex-shrink:0}\n.vsm-mcp-actions button{border:0;background:none;color:var(--dsw-alias-label-tertiary,#9aa5b1);cursor:pointer;font-size:15px;width:28px;height:28px;border-radius:6px;line-height:1}\n.vsm-mcp-actions button:hover{color:var(--dsw-alias-label-primary,#fff);background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-mcp-actions button.vsm-danger:hover{color:var(--dsw-alias-state-error-primary,#f07878);background:var(--dsw-alias-interactive-bg-hover-danger,#ff000014)}\n.vsm-switch{font-size:14px !important}\n.vsm-switch.on{color:var(--dsw-alias-state-success-primary,#38bd75)}\n.vsm-mcp-meta{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1);margin:9px 0}\n.vsm-mcp-error{color:var(--dsw-alias-state-error-primary,#f07878);font-size:12px;margin:8px 0}\n.vsm-mcp-banner{padding:9px 12px;border:1px solid var(--dsw-alias-state-error-secondary,#d9534f55);border-radius:6px;background:var(--dsw-alias-state-error-secondary,#d9534f1a);margin-bottom:10px}\n\n/* 工具 chips */\n.vsm-mcp-tools{display:flex;flex-wrap:wrap;gap:6px}\n.vsm-mcp-chip{font:12px var(--ds-font-family-code,ui-monospace,monospace);padding:4px 8px;border-radius:5px;background:var(--dsw-alias-bg-mask-2,#ffffff14);color:var(--dsw-alias-label-secondary,#c8c8c8);border:1px solid var(--dsw-alias-border-l1,#333)}\n\n/* 空态 / 加载态 */\n.vsm-mcp-empty{text-align:center;padding:56px 20px;color:var(--dsw-alias-label-tertiary,#9aa5b1);border:1px dashed var(--dsw-alias-border-l2,#3a3a3a);border-radius:8px;font-size:13px}\n\n/* 项目搜索下拉框 */\n.vsm-project-picker{position:relative;width:100%;z-index:4}\n.vsm-project-picker-trigger{width:100%;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:8px;background:var(--dsw-alias-bg-layer-1,transparent);color:var(--dsw-alias-label-primary,#e6e6e6);cursor:pointer;text-align:left}\n.vsm-project-picker-trigger:hover{border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-picker-label{display:flex;flex-direction:column;min-width:0;font-size:13px;font-weight:500}\n.vsm-project-picker-label small{margin-top:3px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font:11px var(--ds-font-family-code,ui-monospace,monospace);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.vsm-project-picker-arrow{color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:16px}\n.vsm-project-picker-menu{position:absolute;top:calc(100% + 6px);left:0;right:0;padding:8px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:9px;background:var(--dsw-alias-bg-overlay,#202020);box-shadow:0 12px 36px #0008}\n.vsm-project-picker-search{width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:var(--dsw-alias-bg-layer-1,#2b2b2b);color:var(--dsw-alias-label-primary,#eee);outline:none}\n.vsm-project-picker-search:focus{border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-picker-options{max-height:260px;overflow:auto;margin-top:6px}\n.vsm-project-option{display:flex;width:100%;flex-direction:column;gap:3px;padding:8px 9px;border:0;border-radius:6px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;text-align:left}\n.vsm-project-option:hover,.vsm-project-option.highlight{background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-project-option.selected{color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-option small{overflow:hidden;color:var(--dsw-alias-label-tertiary,#9aa5b1);font:11px var(--ds-font-family-code,ui-monospace,monospace);text-overflow:ellipsis;white-space:nowrap}\n.vsm-project-picker-empty{padding:18px 8px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px;text-align:center}\n\n/* 项目级 MCP 分组 */\n.vsm-project{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:12px;margin-bottom:14px;overflow:hidden;background:var(--dsw-alias-bg-layer-1,transparent)}\n.vsm-project-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l1,#333);background:var(--dsw-alias-bg-mask-1,#ffffff0a)}\n.vsm-project-title{display:flex;align-items:center;gap:10px;min-width:0}\n.vsm-project-icon{color:var(--dsw-alias-brand-primary,#6d55ff);font-size:15px;flex-shrink:0}\n.vsm-project-name{font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6);display:block}\n.vsm-project-path{font-size:11px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-family:var(--ds-font-family-code,ui-monospace,monospace);display:block;margin-top:2px;word-break:break-all}\n.vsm-primary.vsm-small{padding:5px 12px;font-size:12px}\n.vsm-project-empty{text-align:center;padding:26px 16px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px}\n.vsm-project .vsm-mcp-card{border:0;border-radius:0;margin:0;border-bottom:1px solid var(--dsw-alias-border-l1,#333);background:transparent}\n.vsm-project .vsm-mcp-card:last-child{border-bottom:0}\n.vsm-project .vsm-mcp-banner{border-radius:0;margin:0}\n\n/* 新增 MCP 弹窗 */\n.vsm-mcp-modal{position:fixed;inset:0;background:var(--dsw-alias-bg-mask-drop,#000000aa);display:flex;align-items:center;justify-content:center;z-index:50}\n.vsm-mcp-dialog{width:min(560px,calc(100vw - 40px));max-height:88vh;overflow:auto;padding:22px 24px;border-radius:12px;background:var(--dsw-alias-bg-overlay,#202020);border:1px solid var(--dsw-alias-border-l2,#444);box-shadow:0 16px 60px #000c;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-mcp-dialog h3{margin:0 0 14px;font-size:16px;font-weight:600}\n.vsm-mcp-dialog label{display:flex;flex-direction:column;gap:6px;margin:12px 0;font-size:12px;color:var(--dsw-alias-label-secondary,#aaa)}\n.vsm-mcp-dialog input,.vsm-mcp-dialog select,.vsm-mcp-dialog textarea{font:inherit;color:var(--dsw-alias-label-primary,#eee);background:var(--dsw-alias-bg-layer-1,#2b2b2b);border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;padding:8px 10px}\n.vsm-mcp-dialog input:focus,.vsm-mcp-dialog select:focus,.vsm-mcp-dialog textarea:focus{outline:none;border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-mcp-dialog textarea{min-height:64px;resize:vertical;font-family:var(--ds-font-family-code,ui-monospace,monospace)}\n.vsm-mcp-dialog-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:20px}\n.vsm-mcp-dialog-actions button{padding:8px 14px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;font-size:13px}\n.vsm-mcp-dialog-actions button:hover{background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n";
|
|
2203
|
+
const css = "/* dsh-vscode-mode MCP 管理界面样式。\n 全部使用 DSH 真实主题变量(--dsw-alias-*),不再用不存在的 fg-* 变量。 */\n.vsm-mcp-page{display:flex;flex-direction:column;gap:18px;width:100%;max-width:920px;box-sizing:border-box;color:var(--dsw-alias-label-primary,#e6e6e6);padding-bottom:32px}\n\n/* 顶部:标题 + 副标题 + 添加按钮 */\n.vsm-mcp-header{display:flex;align-items:flex-start;justify-content:space-between;gap:20px}\n.vsm-mcp-header h2{margin:0 0 4px;font-size:20px;font-weight:600;line-height:1.35;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-mcp-header p{margin:0;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:13px;line-height:1.5}\n.vsm-primary{border:0;border-radius:6px;padding:8px 16px;background:var(--dsw-alias-button-primary-fill,#6d55ff);color:var(--dsw-alias-button-contrast-fill,#fff);font-size:13px;font-weight:500;cursor:pointer;white-space:nowrap}\n.vsm-primary:hover{background:var(--dsw-alias-button-primary-hover,#5a46e6)}\n.vsm-primary:disabled{opacity:.55;cursor:default}\n\n/* 子 Tab:我的 MCP / MCP 市场 */\n.vsm-mcp-tabs{display:flex;gap:24px;border-bottom:1px solid var(--dsw-alias-border-l2,#3a3a3a)}\n.vsm-mcp-tabs button{border:0;background:none;color:var(--dsw-alias-label-tertiary,#9aa5b1);padding:9px 2px 11px;cursor:pointer;font-size:14px;position:relative}\n.vsm-mcp-tabs button:hover{color:var(--dsw-alias-label-secondary,#c8c8c8)}\n.vsm-mcp-tabs button.active{color:var(--dsw-alias-label-primary,#fff);font-weight:500}\n.vsm-mcp-tabs button.active::after{content:\"\";position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--dsw-alias-brand-primary,#6d55ff);border-radius:2px}\n\n/* 通用设置 */\n.vsm-general-page{display:flex;flex-direction:column;gap:10px;max-width:720px}\n.vsm-general-page h2{margin:0;font-size:20px;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-general-page p{margin:0 0 8px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:13px}\n.vsm-general-row{display:flex;align-items:center;justify-content:space-between;gap:20px;padding:14px 0;border-bottom:1px solid var(--dsw-alias-border-l1,#333);color:var(--dsw-alias-label-primary,#eee);font-size:14px}\n.vsm-general-row select{min-width:220px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:var(--dsw-alias-bg-layer-1,#2b2b2b);color:var(--dsw-alias-label-primary,#eee);font:inherit}\n.vsm-general-row small{color:var(--dsw-alias-label-tertiary,#9aa5b1)}\n\n/* 服务卡片 */\n.vsm-mcp-card{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:10px;padding:14px 16px;margin-bottom:10px;background:var(--dsw-alias-bg-layer-2,transparent);transition:border-color .15s}\n.vsm-mcp-card:hover{border-color:var(--dsw-alias-border-l3,#555)}\n.vsm-mcp-card-head{display:flex;justify-content:space-between;align-items:center;gap:12px}\n.vsm-mcp-title{display:flex;align-items:center;gap:9px;font-weight:600;font-size:14px;color:var(--dsw-alias-label-primary,#e6e6e6);min-width:0}\n.vsm-mcp-source{font-size:11px;font-weight:400;padding:2px 7px;border-radius:4px;background:var(--dsw-alias-bg-mask-2,#ffffff22);color:var(--dsw-alias-label-tertiary,#9aa5b1);flex-shrink:0}\n.vsm-mcp-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;background:var(--dsw-alias-state-error-primary,#e05252)}\n.vsm-mcp-dot.connected{background:var(--dsw-alias-state-success-primary,#38bd75)}\n.vsm-mcp-dot.connecting{background:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-mcp-dot.disabled{background:var(--dsw-alias-label-tertiary,#9aa5b1)}\n.vsm-mcp-actions{display:flex;align-items:center;gap:4px;flex-shrink:0}\n.vsm-mcp-actions button{border:0;background:none;color:var(--dsw-alias-label-tertiary,#9aa5b1);cursor:pointer;font-size:15px;width:28px;height:28px;border-radius:6px;line-height:1}\n.vsm-mcp-actions button:hover{color:var(--dsw-alias-label-primary,#fff);background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-mcp-actions button.vsm-danger:hover{color:var(--dsw-alias-state-error-primary,#f07878);background:var(--dsw-alias-interactive-bg-hover-danger,#ff000014)}\n.vsm-switch{font-size:14px !important}\n.vsm-switch.on{color:var(--dsw-alias-state-success-primary,#38bd75)}\n.vsm-mcp-meta{font-size:12px;color:var(--dsw-alias-label-tertiary,#9aa5b1);margin:9px 0}\n.vsm-mcp-error{color:var(--dsw-alias-state-error-primary,#f07878);font-size:12px;margin:8px 0}\n.vsm-mcp-banner{padding:9px 12px;border:1px solid var(--dsw-alias-state-error-secondary,#d9534f55);border-radius:6px;background:var(--dsw-alias-state-error-secondary,#d9534f1a);margin-bottom:10px}\n\n/* 工具 chips */\n.vsm-mcp-tools{display:flex;flex-wrap:wrap;gap:6px}\n.vsm-mcp-chip{font:12px var(--ds-font-family-code,ui-monospace,monospace);padding:4px 8px;border-radius:5px;background:var(--dsw-alias-bg-mask-2,#ffffff14);color:var(--dsw-alias-label-secondary,#c8c8c8);border:1px solid var(--dsw-alias-border-l1,#333)}\n\n/* 空态 / 加载态 */\n.vsm-mcp-empty{text-align:center;padding:56px 20px;color:var(--dsw-alias-label-tertiary,#9aa5b1);border:1px dashed var(--dsw-alias-border-l2,#3a3a3a);border-radius:8px;font-size:13px}\n\n/* 项目搜索下拉框 */\n.vsm-project-picker{position:relative;width:100%;z-index:4}\n.vsm-project-picker-trigger{width:100%;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:8px;background:var(--dsw-alias-bg-layer-1,transparent);color:var(--dsw-alias-label-primary,#e6e6e6);cursor:pointer;text-align:left}\n.vsm-project-picker-trigger:hover{border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-picker-label{display:flex;flex-direction:column;min-width:0;font-size:13px;font-weight:500}\n.vsm-project-picker-label small{margin-top:3px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font:11px var(--ds-font-family-code,ui-monospace,monospace);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.vsm-project-picker-arrow{color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:16px}\n.vsm-project-picker-menu{position:absolute;top:calc(100% + 6px);left:0;right:0;padding:8px;border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:9px;background:var(--dsw-alias-bg-overlay,#202020);box-shadow:0 12px 36px #0008}\n.vsm-project-picker-search{width:100%;box-sizing:border-box;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:var(--dsw-alias-bg-layer-1,#2b2b2b);color:var(--dsw-alias-label-primary,#eee);outline:none}\n.vsm-project-picker-search:focus{border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-picker-options{max-height:260px;overflow:auto;margin-top:6px}\n.vsm-project-option{display:flex;width:100%;flex-direction:column;gap:3px;padding:8px 9px;border:0;border-radius:6px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;text-align:left}\n.vsm-project-option:hover,.vsm-project-option.highlight{background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-project-option.selected{color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-project-option small{overflow:hidden;color:var(--dsw-alias-label-tertiary,#9aa5b1);font:11px var(--ds-font-family-code,ui-monospace,monospace);text-overflow:ellipsis;white-space:nowrap}\n.vsm-project-picker-empty{padding:18px 8px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px;text-align:center}\n\n/* 项目级 MCP 分组 */\n.vsm-project{border:1px solid var(--dsw-alias-border-l2,#3a3a3a);border-radius:12px;margin-bottom:14px;overflow:hidden;background:var(--dsw-alias-bg-layer-1,transparent)}\n.vsm-project-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l1,#333);background:var(--dsw-alias-bg-mask-1,#ffffff0a)}\n.vsm-project-title{display:flex;align-items:center;gap:10px;min-width:0}\n.vsm-project-icon{color:var(--dsw-alias-brand-primary,#6d55ff);font-size:15px;flex-shrink:0}\n.vsm-project-name{font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6);display:block}\n.vsm-project-path{font-size:11px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-family:var(--ds-font-family-code,ui-monospace,monospace);display:block;margin-top:2px;word-break:break-all}\n.vsm-primary.vsm-small{padding:5px 12px;font-size:12px}\n.vsm-project-empty{text-align:center;padding:26px 16px;color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px}\n.vsm-project .vsm-mcp-card{border:0;border-radius:0;margin:0;border-bottom:1px solid var(--dsw-alias-border-l1,#333);background:transparent}\n.vsm-project .vsm-mcp-card:last-child{border-bottom:0}\n.vsm-project .vsm-mcp-banner{border-radius:0;margin:0}\n\n/* 新增 MCP 弹窗 */\n.vsm-mcp-modal{position:fixed;inset:0;background:var(--dsw-alias-bg-mask-drop,#000000aa);display:flex;align-items:center;justify-content:center;z-index:50}\n.vsm-mcp-dialog{width:min(560px,calc(100vw - 40px));max-height:88vh;overflow:auto;padding:22px 24px;border-radius:12px;background:var(--dsw-alias-bg-overlay,#202020);border:1px solid var(--dsw-alias-border-l2,#444);box-shadow:0 16px 60px #000c;color:var(--dsw-alias-label-primary,#e6e6e6)}\n.vsm-mcp-dialog h3{margin:0 0 14px;font-size:16px;font-weight:600}\n.vsm-mcp-dialog label{display:flex;flex-direction:column;gap:6px;margin:12px 0;font-size:12px;color:var(--dsw-alias-label-secondary,#aaa)}\n.vsm-mcp-dialog input,.vsm-mcp-dialog select,.vsm-mcp-dialog textarea{font:inherit;color:var(--dsw-alias-label-primary,#eee);background:var(--dsw-alias-bg-layer-1,#2b2b2b);border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;padding:8px 10px}\n.vsm-mcp-dialog input:focus,.vsm-mcp-dialog select:focus,.vsm-mcp-dialog textarea:focus{outline:none;border-color:var(--dsw-alias-brand-primary,#6d55ff)}\n.vsm-mcp-dialog textarea{min-height:64px;resize:vertical;font-family:var(--ds-font-family-code,ui-monospace,monospace)}\n.vsm-mcp-dialog-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:20px}\n.vsm-mcp-dialog-actions button{padding:8px 14px;border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;font-size:13px}\n.vsm-mcp-dialog-actions button:hover{background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n\n/* 兼容性小节 */\n.vsm-compat-h3{margin:16px 0 2px;font-size:14px;font-weight:600;color:var(--dsw-alias-label-secondary,#c8c8c8)}\n.vsm-compat-item{display:flex;align-items:baseline;justify-content:space-between;gap:16px;padding:10px 0;border-bottom:1px solid var(--dsw-alias-border-l1,#333);font-size:13px;color:var(--dsw-alias-label-primary,#eee)}\n.vsm-compat-item.ok .vsm-compat-name::before{content:\"● \";color:var(--dsw-alias-state-success-primary,#38bd75)}\n.vsm-compat-item.warn .vsm-compat-name::before{content:\"● \";color:var(--dsw-alias-state-warn-primary,#e5b93f)}\n.vsm-compat-name{font-weight:500;min-width:0}\n.vsm-compat-note{color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px;text-align:right;min-width:0}\n\n/* 开发形态开关 */\n.vsm-devform-note{display:block;margin-top:4px;font-size:11px;color:var(--dsw-alias-label-tertiary,#9aa5b1);word-break:break-all}\n.vsm-devform-actions{display:flex;flex-direction:column;align-items:flex-end;gap:6px;min-width:0}\n.vsm-devform-msg{color:var(--dsw-alias-state-warn-primary,#e5b93f);font-size:11px;text-align:right}\n.vsm-general-row button{border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;padding:6px 12px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;font-size:12px;white-space:nowrap}\n.vsm-general-row button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}\n.vsm-general-row button:disabled{opacity:.55;cursor:default}\n.vsm-general-row button.vsm-devform-close{color:var(--dsw-alias-state-error-primary,#f07878);border-color:var(--dsw-alias-state-error-secondary,#d9534f55)}\n";
|
|
2204
2204
|
const tagId = "dsh-vscode-mode/mcp.css";
|
|
2205
2205
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
2206
2206
|
const tag = document.createElement("style");
|
|
@@ -2210,6 +2210,76 @@ window.__ModuleLoader__.load({
|
|
|
2210
2210
|
document.head.appendChild(tag);
|
|
2211
2211
|
}
|
|
2212
2212
|
//#endregion
|
|
2213
|
+
//#region src/client/compat.ts
|
|
2214
|
+
/**
|
|
2215
|
+
* dsh-vscode-mode client — 兼容层:设置桥选择 / slot 安全注册 / openPath 链式补丁 / 外部插件常量。
|
|
2216
|
+
* 纯逻辑与薄 ctx 胶水,node 环境可单测(不依赖 DOM)。
|
|
2217
|
+
* 作者 ddj 2026年08月24号
|
|
2218
|
+
*/
|
|
2219
|
+
/** 本插件包名(与 host compat.PLUGIN_NAME 对应)。 */
|
|
2220
|
+
const PLUGIN_NAME = "dsh-vscode-mode";
|
|
2221
|
+
/** 外部侧栏插件名(dsh-web-ui-all 家族的文件打开能力提供方)。 */
|
|
2222
|
+
const SIDEBAR_PLUGIN = "dsh-better-sidebar";
|
|
2223
|
+
/**
|
|
2224
|
+
* 设置桥三级优先:webUiSettings(linxin666 兼容桥)→ settingsScope(官方)→ 无。
|
|
2225
|
+
* 桥绑定抛错时降级到下一级,不让单个桥异常拖垮设置装配。
|
|
2226
|
+
* @author ddj 2026年08月24号
|
|
2227
|
+
* @param ctx 客户端服务上下文
|
|
2228
|
+
* @returns 激活的桥名称与绑定 scope
|
|
2229
|
+
*/
|
|
2230
|
+
function pickSettingsBinder(ctx) {
|
|
2231
|
+
for (const service of ["webUiSettings", "settingsScope"]) try {
|
|
2232
|
+
const scope = ctx.get(service)?.bind?.({ namespace: PLUGIN_NAME });
|
|
2233
|
+
if (scope) return {
|
|
2234
|
+
service,
|
|
2235
|
+
scope
|
|
2236
|
+
};
|
|
2237
|
+
} catch {}
|
|
2238
|
+
return {
|
|
2239
|
+
service: "none",
|
|
2240
|
+
scope: void 0
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2243
|
+
/**
|
|
2244
|
+
* 跨版本 slot 安全注册:封装 ctx.slots.inject 等待声明模式(新老 DSH 通用),
|
|
2245
|
+
* 服务缺失或注册抛错时降级为警告,不让单条 slot 拖垮装配。
|
|
2246
|
+
* 注意:方法必须挂在服务对象上调用(slots.register 是依赖 this.records 的类方法,
|
|
2247
|
+
* 解构后 this 丢失会静默失败),故禁止解构。
|
|
2248
|
+
* @author ddj 2026年08月24号
|
|
2249
|
+
* @param ctx 客户端上下文(slots 服务)
|
|
2250
|
+
* @param spec slot 描述
|
|
2251
|
+
* @param render 渲染函数
|
|
2252
|
+
*/
|
|
2253
|
+
function registerSlotSafely(ctx, spec, render) {
|
|
2254
|
+
const slots = ctx?.slots;
|
|
2255
|
+
if (!slots || typeof slots.inject !== "function" || typeof slots.register !== "function") {
|
|
2256
|
+
console.warn("[dsh-vscode-mode] slots 服务不可用,跳过 slot " + spec.name);
|
|
2257
|
+
return;
|
|
2258
|
+
}
|
|
2259
|
+
try {
|
|
2260
|
+
slots.inject(spec.name, () => slots.register(spec, render));
|
|
2261
|
+
} catch (error) {
|
|
2262
|
+
console.warn("[dsh-vscode-mode] slot " + spec.name + " 注册失败(" + String(error) + "),已跳过");
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
/**
|
|
2266
|
+
* 链式补丁对象方法:保存原实现、装入包装实现,dispose 恢复。
|
|
2267
|
+
* 恢复带归属校验(仅当当前实现仍是本包装时还原),多插件补丁按栈序互不踩踏。
|
|
2268
|
+
* @author ddj 2026年08月24号
|
|
2269
|
+
* @param owner 目标对象
|
|
2270
|
+
* @param key 方法名
|
|
2271
|
+
* @param wrapper 包装实现(original 为补丁前的实现)
|
|
2272
|
+
* @returns 恢复函数(幂等)
|
|
2273
|
+
*/
|
|
2274
|
+
function patchMethod(owner, key, wrapper) {
|
|
2275
|
+
const original = owner[key];
|
|
2276
|
+
const patched = ((...args) => wrapper(original, ...args));
|
|
2277
|
+
owner[key] = patched;
|
|
2278
|
+
return () => {
|
|
2279
|
+
if (owner[key] === patched) owner[key] = original;
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
//#endregion
|
|
2213
2283
|
//#region src/client/fileOpeners.ts
|
|
2214
2284
|
/**
|
|
2215
2285
|
* dsh-vscode-mode client — 文件打开器注册表与自动选择。
|
|
@@ -2217,7 +2287,7 @@ window.__ModuleLoader__.load({
|
|
|
2217
2287
|
*/
|
|
2218
2288
|
const AUTO_OPEN_TOOL = "auto";
|
|
2219
2289
|
const VSCODE_OPEN_TOOL = "dsh-vscode-mode";
|
|
2220
|
-
const SIDEBAR_OPEN_TOOL =
|
|
2290
|
+
const SIDEBAR_OPEN_TOOL = SIDEBAR_PLUGIN;
|
|
2221
2291
|
/**
|
|
2222
2292
|
* 写入外部打开器并触发注册表通知。
|
|
2223
2293
|
* @author ddj 2026年08月24号
|
|
@@ -2347,24 +2417,10 @@ window.__ModuleLoader__.load({
|
|
|
2347
2417
|
//#region src/client/settingsContext.ts
|
|
2348
2418
|
/**
|
|
2349
2419
|
* dsh-vscode-mode client — 设置 scope 的跨组件上下文。
|
|
2350
|
-
*
|
|
2420
|
+
* 绑定逻辑(webUiSettings 兼容桥 → 官方 settingsScope 降级)统一由 compat 层提供。
|
|
2421
|
+
* 作者 ddj 2026年08月24号
|
|
2351
2422
|
*/
|
|
2352
2423
|
const SettingsContext = react.default.createContext(void 0);
|
|
2353
|
-
/**
|
|
2354
|
-
* 从 Cordis 服务绑定 VSCodeMode 设置 scope。
|
|
2355
|
-
* @author ddj 2026年08月24号
|
|
2356
|
-
* @param ctx 客户端上下文
|
|
2357
|
-
* @returns 设置 scope 或 undefined
|
|
2358
|
-
*/
|
|
2359
|
-
/**
|
|
2360
|
-
* 绑定官方设置 scope,优先使用 web-ui 兼容桥。
|
|
2361
|
-
* @author ddj 2026年08月24号
|
|
2362
|
-
* @param ctx 客户端服务上下文
|
|
2363
|
-
* @returns 设置 scope;未安装设置服务时返回 undefined
|
|
2364
|
-
*/
|
|
2365
|
-
function getSettingsScope(ctx) {
|
|
2366
|
-
return (ctx.get("webUiSettings") ?? ctx.get("settingsScope"))?.bind?.({ namespace: "dsh-vscode-mode" });
|
|
2367
|
-
}
|
|
2368
2424
|
//#endregion
|
|
2369
2425
|
//#region src/client/ui/McpSettings.ts
|
|
2370
2426
|
/**
|
|
@@ -2518,11 +2574,14 @@ window.__ModuleLoader__.load({
|
|
|
2518
2574
|
}));
|
|
2519
2575
|
return react.default.createElement("section", { className: "vsm-project" }, head, body);
|
|
2520
2576
|
}
|
|
2521
|
-
/**
|
|
2577
|
+
/** 通用设置:选择当前对话文件链接的打开器 + 开发形态开关。 */
|
|
2522
2578
|
function GeneralSettings({ registry }) {
|
|
2523
2579
|
const [tool, setTool] = react.default.useState(AUTO_OPEN_TOOL);
|
|
2524
2580
|
const [busy, setBusy] = react.default.useState(false);
|
|
2525
2581
|
const [error, setError] = react.default.useState("");
|
|
2582
|
+
const [devForm, setDevForm] = react.default.useState(null);
|
|
2583
|
+
const [devBusy, setDevBusy] = react.default.useState(false);
|
|
2584
|
+
const [devMessage, setDevMessage] = react.default.useState("");
|
|
2526
2585
|
const settings = react.default.useContext(SettingsContext);
|
|
2527
2586
|
const snapshot = settings?.getSnapshot?.();
|
|
2528
2587
|
const loading = !snapshot || snapshot.status === "loading";
|
|
@@ -2536,6 +2595,11 @@ window.__ModuleLoader__.load({
|
|
|
2536
2595
|
onChange();
|
|
2537
2596
|
return settings?.subscribe?.(onChange);
|
|
2538
2597
|
}, [settings]);
|
|
2598
|
+
react.default.useEffect(() => {
|
|
2599
|
+
rpc("vscode.devFormGet", {}).then((result) => {
|
|
2600
|
+
if (result.ok) setDevForm(result.devForm);
|
|
2601
|
+
}).catch(() => {});
|
|
2602
|
+
}, []);
|
|
2539
2603
|
const save = (value) => {
|
|
2540
2604
|
setTool(value);
|
|
2541
2605
|
setBusy(true);
|
|
@@ -2547,6 +2611,19 @@ window.__ModuleLoader__.load({
|
|
|
2547
2611
|
}
|
|
2548
2612
|
settings.set("fileOpenTool", value).catch((e) => setError(String(e))).finally(() => setBusy(false));
|
|
2549
2613
|
};
|
|
2614
|
+
const closeDevForm = () => {
|
|
2615
|
+
if (!window.confirm("关闭开发形态:插件将切换为正式版安装(版本依赖 + 删除工作区链接),pnpm 装配后需重启 DSH 生效。确认关闭?")) return;
|
|
2616
|
+
setDevBusy(true);
|
|
2617
|
+
setDevMessage("");
|
|
2618
|
+
rpc("vscode.devFormSet", { enabled: false }).then((result) => {
|
|
2619
|
+
if (!result.ok) {
|
|
2620
|
+
setDevMessage("关闭失败:" + String(result.error));
|
|
2621
|
+
return;
|
|
2622
|
+
}
|
|
2623
|
+
setDevForm(result.devForm);
|
|
2624
|
+
setDevMessage(result.restart ? "已切换为正式版,重启 DSH 后生效。" : "已关闭开发形态。");
|
|
2625
|
+
}).catch((e) => setDevMessage("关闭失败:" + String(e))).finally(() => setDevBusy(false));
|
|
2626
|
+
};
|
|
2550
2627
|
const options = [{
|
|
2551
2628
|
id: AUTO_OPEN_TOOL,
|
|
2552
2629
|
label: "自动选择",
|
|
@@ -2557,6 +2634,11 @@ window.__ModuleLoader__.load({
|
|
|
2557
2634
|
description: item.description
|
|
2558
2635
|
}))];
|
|
2559
2636
|
const currentOption = options.some((item) => item.id === tool) ? tool : AUTO_OPEN_TOOL;
|
|
2637
|
+
const devFormRow = devForm?.enabled ? react.default.createElement("div", { className: "vsm-general-row" }, react.default.createElement("span", null, "开发形态", react.default.createElement("small", { className: "vsm-devform-note" }, "工作区链接安装:" + devForm.path)), react.default.createElement("div", { className: "vsm-devform-actions" }, devMessage && react.default.createElement("small", { className: "vsm-devform-msg" }, devMessage), react.default.createElement("button", {
|
|
2638
|
+
className: "vsm-devform-close",
|
|
2639
|
+
disabled: devBusy,
|
|
2640
|
+
onClick: closeDevForm
|
|
2641
|
+
}, devBusy ? "切换中…" : "关闭开发形态"))) : null;
|
|
2560
2642
|
return react.default.createElement("section", { className: "vsm-general-page" }, react.default.createElement("h2", null, "通用"), react.default.createElement("p", null, "配置 DSH 对话中文件链接的打开工具。工具由当前运行时自动扫描。"), unavailable && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, "设置服务暂不可用,当前使用自动选择。"), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), react.default.createElement("label", { className: "vsm-general-row" }, react.default.createElement("span", null, "文件链接使用工具"), react.default.createElement("select", {
|
|
2561
2643
|
value: currentOption,
|
|
2562
2644
|
disabled: loading || unavailable || notReady || busy || snapshot?.writable === false,
|
|
@@ -2564,11 +2646,40 @@ window.__ModuleLoader__.load({
|
|
|
2564
2646
|
}, options.map((item) => react.default.createElement("option", {
|
|
2565
2647
|
key: item.id,
|
|
2566
2648
|
value: item.id
|
|
2567
|
-
}, item.label))), loading && react.default.createElement("small", null, "读取中…")));
|
|
2649
|
+
}, item.label))), loading && react.default.createElement("small", null, "读取中…")), devFormRow);
|
|
2650
|
+
}
|
|
2651
|
+
/** 兼容性小节:host 报告(RPC compat)+ 客户端本地适配项。 */
|
|
2652
|
+
function CompatSection({ getSummary }) {
|
|
2653
|
+
const [report, setReport] = react.default.useState(null);
|
|
2654
|
+
const [error, setError] = react.default.useState("");
|
|
2655
|
+
react.default.useEffect(() => {
|
|
2656
|
+
let alive = true;
|
|
2657
|
+
rpc("compat", {}).then((result) => {
|
|
2658
|
+
if (!alive) return;
|
|
2659
|
+
if (!result.ok) setError(String(result.error));
|
|
2660
|
+
else setReport(result.report);
|
|
2661
|
+
}).catch((e) => {
|
|
2662
|
+
if (alive) setError(String(e));
|
|
2663
|
+
});
|
|
2664
|
+
return () => {
|
|
2665
|
+
alive = false;
|
|
2666
|
+
};
|
|
2667
|
+
}, []);
|
|
2668
|
+
const renderItems = (list) => (list ?? []).map((item) => react.default.createElement("div", {
|
|
2669
|
+
key: item.name,
|
|
2670
|
+
className: "vsm-compat-item " + (item.active ? "ok" : "warn")
|
|
2671
|
+
}, react.default.createElement("span", { className: "vsm-compat-name" }, item.name), react.default.createElement("span", { className: "vsm-compat-note" }, item.note ?? (item.active ? "正常" : "未生效"))));
|
|
2672
|
+
const warnings = report?.warnings ?? [];
|
|
2673
|
+
const devForm = report?.devForm;
|
|
2674
|
+
return react.default.createElement("section", { className: "vsm-general-page" }, react.default.createElement("h2", null, "兼容性"), react.default.createElement("p", null, "检测与其他插件 / DSH 版本的适配状态与已知问题。" + (report?.pluginVersion ? "(插件版本 " + report.pluginVersion + ")" : "")), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), devForm?.enabled && react.default.createElement(react.default.Fragment, null, react.default.createElement("h3", { className: "vsm-compat-h3" }, "开发形态"), react.default.createElement("div", { className: "vsm-compat-item ok" }, react.default.createElement("span", { className: "vsm-compat-name" }, "开发形态开启(工作区链接安装)"), react.default.createElement("span", { className: "vsm-compat-note" }, devForm.path))), react.default.createElement("h3", { className: "vsm-compat-h3" }, "外部插件与服务"), renderItems(report?.external), react.default.createElement("h3", { className: "vsm-compat-h3" }, "客户端适配"), renderItems(getSummary?.() ?? []), react.default.createElement("h3", { className: "vsm-compat-h3" }, "护栏"), renderItems(report?.guards), react.default.createElement("h3", { className: "vsm-compat-h3" }, "警告"), warnings.length ? warnings.map((w, index) => react.default.createElement("div", {
|
|
2675
|
+
key: index,
|
|
2676
|
+
className: "vsm-mcp-error vsm-mcp-banner"
|
|
2677
|
+
}, w)) : react.default.createElement("div", { className: "vsm-compat-item ok" }, react.default.createElement("span", { className: "vsm-compat-name" }, "未检测到兼容性问题"), react.default.createElement("span", { className: "vsm-compat-note" }, "")));
|
|
2568
2678
|
}
|
|
2569
2679
|
/** MCP 管理主面板。 */
|
|
2570
2680
|
function McpSettings(props) {
|
|
2571
2681
|
const openerRegistry = props?.openerRegistry ?? { list: () => [] };
|
|
2682
|
+
const compatSummary = props?.compatSummary ?? (() => []);
|
|
2572
2683
|
const [servers, setServers] = react.default.useState([]);
|
|
2573
2684
|
const [projects, setProjects] = react.default.useState([]);
|
|
2574
2685
|
const [loading, setLoading] = react.default.useState(true);
|
|
@@ -2694,6 +2805,7 @@ window.__ModuleLoader__.load({
|
|
|
2694
2805
|
onRemove: removeGlobal
|
|
2695
2806
|
})) : react.default.createElement("div", { className: "vsm-mcp-empty" }, "还没有配置全局 MCP"));
|
|
2696
2807
|
else if (tab === "projects") body = projectBody;
|
|
2808
|
+
else if (tab === "compat") body = react.default.createElement(CompatSection, { getSummary: compatSummary });
|
|
2697
2809
|
else body = marketBody;
|
|
2698
2810
|
const form = showForm ? react.default.createElement(McpForm, {
|
|
2699
2811
|
title: "添加全局 MCP",
|
|
@@ -2728,7 +2840,10 @@ window.__ModuleLoader__.load({
|
|
|
2728
2840
|
}, "项目 MCP"), react.default.createElement("button", {
|
|
2729
2841
|
className: tab === "market" ? "active" : "",
|
|
2730
2842
|
onClick: () => setTab("market")
|
|
2731
|
-
}, "MCP 市场")
|
|
2843
|
+
}, "MCP 市场"), react.default.createElement("button", {
|
|
2844
|
+
className: tab === "compat" ? "active" : "",
|
|
2845
|
+
onClick: () => setTab("compat")
|
|
2846
|
+
}, "兼容性")), error && react.default.createElement("div", { className: "vsm-mcp-error vsm-mcp-banner" }, error), body, form);
|
|
2732
2847
|
}
|
|
2733
2848
|
/** MCP 新增表单(全局/项目共用)。 */
|
|
2734
2849
|
function McpForm({ title, draft, busy, edit, save, close }) {
|
|
@@ -2757,7 +2872,8 @@ window.__ModuleLoader__.load({
|
|
|
2757
2872
|
//#region src/client/openPathRouter.ts
|
|
2758
2873
|
/**
|
|
2759
2874
|
* dsh-vscode-mode client — DSH 对话文件链接的统一打开路由。
|
|
2760
|
-
*
|
|
2875
|
+
* openPath 补丁经 compat.patchMethod 安装(链式恢复,多插件补丁互不踩踏)。
|
|
2876
|
+
* 作者 ddj 2026年08月24号
|
|
2761
2877
|
*/
|
|
2762
2878
|
/**
|
|
2763
2879
|
* 安装可卸载的 workspaces.openPath 路由。
|
|
@@ -2766,8 +2882,7 @@ window.__ModuleLoader__.load({
|
|
|
2766
2882
|
* @returns 恢复原方法的 disposer
|
|
2767
2883
|
*/
|
|
2768
2884
|
function installOpenPathRouter(options) {
|
|
2769
|
-
const
|
|
2770
|
-
const open = async (path) => {
|
|
2885
|
+
const open = async (original, path) => {
|
|
2771
2886
|
const context = options.context();
|
|
2772
2887
|
const opener = selectOpener(options.registry, options.selected());
|
|
2773
2888
|
if (!opener || !isAvailable(opener)) return original.call(options.workspaces, path);
|
|
@@ -2779,10 +2894,7 @@ window.__ModuleLoader__.load({
|
|
|
2779
2894
|
else throw error;
|
|
2780
2895
|
}
|
|
2781
2896
|
};
|
|
2782
|
-
options.workspaces
|
|
2783
|
-
return () => {
|
|
2784
|
-
options.workspaces.openPath = original;
|
|
2785
|
-
};
|
|
2897
|
+
return patchMethod(options.workspaces, "openPath", open);
|
|
2786
2898
|
}
|
|
2787
2899
|
/**
|
|
2788
2900
|
* 创建 VSCodeMode 内置文件打开器。
|
|
@@ -2843,8 +2955,27 @@ window.__ModuleLoader__.load({
|
|
|
2843
2955
|
const workspaces = ctx.get("workspaces");
|
|
2844
2956
|
const sessions = ctx.get("sessions");
|
|
2845
2957
|
const originalOpenPath = workspaces?.openPath;
|
|
2846
|
-
const
|
|
2958
|
+
const binder = pickSettingsBinder(ctx);
|
|
2959
|
+
const settings = binder.scope;
|
|
2847
2960
|
let selected = autoValue("auto");
|
|
2961
|
+
/** 客户端侧兼容摘要(与 host 报告合并展示;惰性求值保证 HMR 后仍新鲜)。 */
|
|
2962
|
+
const compatSummary = () => [
|
|
2963
|
+
{
|
|
2964
|
+
name: "设置桥",
|
|
2965
|
+
active: settings !== void 0,
|
|
2966
|
+
note: settings !== void 0 ? "使用 " + binder.service + " 桥" : "未绑定设置服务(fileOpenTool 持久化不可用)"
|
|
2967
|
+
},
|
|
2968
|
+
{
|
|
2969
|
+
name: "文件打开路由(workspaces.openPath)",
|
|
2970
|
+
active: Boolean(workspaces?.openPath),
|
|
2971
|
+
note: "vscode 打开器优先,失败回退系统打开"
|
|
2972
|
+
},
|
|
2973
|
+
{
|
|
2974
|
+
name: "侧边栏打开器(dsh-better-sidebar)",
|
|
2975
|
+
active: registry.get(SIDEBAR_PLUGIN) !== void 0,
|
|
2976
|
+
note: registry.get("dsh-better-sidebar") !== void 0 ? "已注册(优先级 80)" : "未检测到侧边栏打开能力"
|
|
2977
|
+
}
|
|
2978
|
+
];
|
|
2848
2979
|
ctx.provide("fileOpeners", registry);
|
|
2849
2980
|
ctx.effect(() => registry.register(vscodeOpener()), "vscode-mode: file opener");
|
|
2850
2981
|
ctx.effect(() => registry.register({
|
|
@@ -2882,25 +3013,28 @@ window.__ModuleLoader__.load({
|
|
|
2882
3013
|
},
|
|
2883
3014
|
logger: (message) => console.warn("[dsh-vscode-mode] " + message)
|
|
2884
3015
|
}), "vscode-mode: file link routing");
|
|
2885
|
-
ctx
|
|
3016
|
+
registerSlotSafely(ctx, {
|
|
2886
3017
|
name: "conversation.view",
|
|
2887
3018
|
id: "edrv-editor",
|
|
2888
3019
|
order: 5,
|
|
2889
3020
|
label: "文件编辑",
|
|
2890
3021
|
inject: (sessionId) => ({ sessionId })
|
|
2891
|
-
}, (props) => react.default.createElement(EditorView, Object.assign({}, props, { schedule })))
|
|
2892
|
-
ctx
|
|
3022
|
+
}, (props) => react.default.createElement(EditorView, Object.assign({}, props, { schedule })));
|
|
3023
|
+
registerSlotSafely(ctx, {
|
|
2893
3024
|
name: "conversation.session.header.utilities",
|
|
2894
3025
|
id: "edrv-diff-badge",
|
|
2895
3026
|
order: 90,
|
|
2896
3027
|
label: "差异"
|
|
2897
|
-
}, (props) => react.default.createElement(DiffBadge, Object.assign({}, props)))
|
|
2898
|
-
ctx
|
|
3028
|
+
}, (props) => react.default.createElement(DiffBadge, Object.assign({}, props)));
|
|
3029
|
+
registerSlotSafely(ctx, {
|
|
2899
3030
|
name: "settings.section",
|
|
2900
3031
|
id: "vscode-mode",
|
|
2901
3032
|
order: 30,
|
|
2902
3033
|
label: "VSCodeMode"
|
|
2903
|
-
}, () => react.default.createElement(SettingsContext.Provider, { value: settings }, react.default.createElement(McpSettings, {
|
|
3034
|
+
}, () => react.default.createElement(SettingsContext.Provider, { value: settings }, react.default.createElement(McpSettings, {
|
|
3035
|
+
openerRegistry: registry,
|
|
3036
|
+
compatSummary
|
|
3037
|
+
})));
|
|
2904
3038
|
}
|
|
2905
3039
|
//#endregion
|
|
2906
3040
|
exports.apply = apply;
|