dsh-vscode-mode 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -6
- package/assets/icon.svg +7 -0
- package/cordis.patch.yml +16 -0
- package/lib/client.js +2804 -300
- package/lib/client.js.map +1 -1
- package/lib/index.js +1975 -191
- package/lib/index.js.map +1 -1
- package/locale/en.json +4 -0
- package/locale/zh.json +4 -0
- package/package.json +4 -2
- package/src/ai/inline.ts +19 -2
- package/src/ai/svnTriage.ts +150 -0
- package/src/capture.ts +225 -51
- package/src/client/addToConversation.ts +203 -1
- package/src/client/compat.ts +134 -4
- package/src/client/imagePreview.ts +76 -2
- package/src/client/index.ts +65 -36
- package/src/client/monaco/lsp/lspClient.ts +46 -0
- package/src/client/monaco/lsp/providers.ts +245 -1
- package/src/client/nativeOpenStore.ts +91 -0
- package/src/client/officialSidebar.ts +11 -45
- package/src/client/outline/index.ts +1 -1
- package/src/client/pdf/pdfPanel.ts +18 -0
- package/src/client/pendingNav.ts +141 -0
- package/src/client/settingsContext.ts +6 -4
- package/src/client/sidebar/panels/FileExplorer.ts +2 -15
- package/src/client/sidebar/panels/SvnPanel.ts +682 -72
- package/src/client/sidebar/panels/index.ts +1 -1
- package/src/client/sidebar/panels/svnListBudget.ts +58 -0
- package/src/client/state/records.ts +7 -1
- package/src/client/styles/editor.css +47 -5
- package/src/client/svnStatus.ts +206 -1
- package/src/client/svnStore.ts +6 -1
- package/src/client/ui/AiSettings.ts +47 -14
- package/src/client/ui/DiffBox.ts +1 -1
- package/src/client/ui/DiffLauncher.ts +6 -4
- package/src/client/ui/EditorView.ts +149 -22
- package/src/client/ui/McpSettings.ts +30 -0
- package/src/client/ui/PerfSettings.ts +4 -1
- package/src/client/ui/SvnAiPlanDialog.ts +293 -0
- package/src/client/ui/icons.ts +91 -0
- package/src/compat.ts +26 -5
- package/src/dshVersion.ts +2 -1
- package/src/fileOpenSettings.ts +274 -49
- package/src/index.ts +18 -1
- package/src/lsp/client.ts +24 -0
- package/src/lsp/rpc.ts +57 -0
- package/src/lsp/server.ts +272 -2
- package/src/model.ts +2 -1
- package/src/perf.ts +90 -4
- package/src/remoteWorkspace.ts +177 -0
- package/src/routes.ts +13 -1
- package/src/rpc.ts +115 -39
- package/src/shared/ai.ts +12 -0
- package/src/shared/lsp.ts +79 -0
- package/src/shared/nativeOpen.ts +91 -0
- package/src/shared/rpc.ts +162 -2
- package/src/shared/svn.ts +296 -10
- package/src/shared/types.ts +5 -1
- package/src/svn.ts +235 -13
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* dsh-vscode-mode client — AI 智能整理弹窗(11-ai-changelist-triage)。
|
|
4
|
+
*
|
|
5
|
+
* 双阶段单弹窗:
|
|
6
|
+
* - 预览(run = null):按**整理功能**划分三个分组卡(建议分组 changelist / 建议还原 revert /
|
|
7
|
+
* 建议忽略 svn:ignore),每段段级全选 + 逐项勾选确认;默认「分组勾选(可逆无副作用)、
|
|
8
|
+
* 还原/忽略不勾(有副作用)」,未勾选项一律不动。
|
|
9
|
+
* - 执行(run 非空):弹窗不关闭,切换为进度视图——阶段徽标 + 步骤计数 + 总进度条 +
|
|
10
|
+
* 逐步日志(✓/✗,失败红字);「暂停」在当前 RPC 返回后的步骤边界生效(svn 子进程不可
|
|
11
|
+
* 安全中断,如实标注「等待当前步骤完成…」),「继续」从断点续跑,「取消剩余」终止未执行
|
|
12
|
+
* 步骤(已执行不回滚)。
|
|
13
|
+
* Esc/遮罩仅在预览阶段生效(= 取消零副作用);执行阶段防误关。
|
|
14
|
+
* 作者 ddj 2026年09月23号
|
|
15
|
+
*/
|
|
16
|
+
import React from 'react'
|
|
17
|
+
import { ModalShell } from './ModalShell.js'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 预览勾选初值:分组默认全勾(changelist 可逆),还原/忽略默认不勾(有副作用)。
|
|
21
|
+
* @author ddj 2026年09月23号
|
|
22
|
+
* @param plan 三段方案
|
|
23
|
+
* @returns 勾选态(组级 + 逐项)
|
|
24
|
+
*/
|
|
25
|
+
function initPlanSel(plan) {
|
|
26
|
+
return {
|
|
27
|
+
groups: (plan.groups || []).map((group) => ({
|
|
28
|
+
name: group.name,
|
|
29
|
+
reason: group.reason || '',
|
|
30
|
+
checked: true,
|
|
31
|
+
paths: (group.paths || []).map((path) => ({ path, checked: true })),
|
|
32
|
+
})),
|
|
33
|
+
reverts: (plan.reverts || []).map((item) => ({ path: item.path, reason: item.reason || '', checked: false })),
|
|
34
|
+
ignores: (plan.ignores || []).map((item) => ({ path: item.path, reason: item.reason || '', checked: false })),
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 勾选态 → 执行载荷(空段/空组剔除;保持勾选顺序)。
|
|
40
|
+
* @author ddj 2026年09月23号
|
|
41
|
+
* @param sel 勾选态
|
|
42
|
+
* @returns 执行载荷 { groups, reverts, ignores }
|
|
43
|
+
*/
|
|
44
|
+
function selectedOf(sel) {
|
|
45
|
+
const groups = []
|
|
46
|
+
for (const group of sel.groups) {
|
|
47
|
+
const paths = group.paths.filter((p) => p.checked).map((p) => p.path)
|
|
48
|
+
if (group.checked && paths.length) groups.push({ name: group.name, paths })
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
groups,
|
|
52
|
+
reverts: sel.reverts.filter((item) => item.checked).map((item) => item.path),
|
|
53
|
+
ignores: sel.ignores.filter((item) => item.checked).map((item) => item.path),
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** 执行载荷 → 已选动作总数(「执行所选」禁用判定)。 */
|
|
58
|
+
function selectedCount(selected) {
|
|
59
|
+
let n = selected.reverts.length + selected.ignores.length
|
|
60
|
+
for (const group of selected.groups) n += group.paths.length
|
|
61
|
+
return n
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** 复选框元素(原生 input;仓库 UI 原语无 Checkbox,此处沿用补丁对话框的原生控件口径)。 */
|
|
65
|
+
function checkBoxEl(checked, onToggle, title) {
|
|
66
|
+
return React.createElement('input', {
|
|
67
|
+
type: 'checkbox',
|
|
68
|
+
checked,
|
|
69
|
+
title,
|
|
70
|
+
onChange: () => onToggle(),
|
|
71
|
+
onClick: (event) => event.stopPropagation(),
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** 段头(全选框 + 标题 + 计数 + 副标题)。 */
|
|
76
|
+
function sectionHeadEl(title, note, allChecked, onToggleAll, countText) {
|
|
77
|
+
return React.createElement('div', { className: 'edrv-svnplan-sechead' },
|
|
78
|
+
checkBoxEl(allChecked, onToggleAll, '全选/全不选'),
|
|
79
|
+
React.createElement('span', { className: 'edrv-svnplan-sectitle' }, title),
|
|
80
|
+
React.createElement('span', { className: 'edrv-svn-count' }, countText),
|
|
81
|
+
(note ? React.createElement('span', { className: 'edrv-svnplan-note' }, note) : null))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 阶段一 · 预览确认(按整理功能三分组)。
|
|
86
|
+
* @author ddj 2026年09月23号
|
|
87
|
+
* @param sel 勾选态
|
|
88
|
+
* @param setSel 勾选态更新
|
|
89
|
+
* @param open 组卡展开集合(组名)
|
|
90
|
+
* @param toggleOpen 组卡展开切换
|
|
91
|
+
* @returns 预览主体元素
|
|
92
|
+
*/
|
|
93
|
+
function previewBodyEl(sel, setSel, open, toggleOpen) {
|
|
94
|
+
/** 通用段更新(reverts/ignores 共用)。 */
|
|
95
|
+
const patchItems = (key, index, checked) => setSel((old) => ({
|
|
96
|
+
...old,
|
|
97
|
+
[key]: old[key].map((item, i) => (i === index ? { ...item, checked } : item)),
|
|
98
|
+
}))
|
|
99
|
+
const toggleItemsAll = (key, checked) => setSel((old) => ({
|
|
100
|
+
...old,
|
|
101
|
+
[key]: old[key].map((item) => ({ ...item, checked })),
|
|
102
|
+
}))
|
|
103
|
+
/** 组级/组内更新。 */
|
|
104
|
+
const patchGroup = (index, patch) => setSel((old) => ({
|
|
105
|
+
...old,
|
|
106
|
+
groups: old.groups.map((group, i) => (i === index ? { ...group, ...patch } : group)),
|
|
107
|
+
}))
|
|
108
|
+
const toggleGroupAll = (index, checked) => setSel((old) => ({
|
|
109
|
+
...old,
|
|
110
|
+
groups: old.groups.map((group, i) => (i === index
|
|
111
|
+
? { ...group, checked, paths: group.paths.map((p) => ({ ...p, checked })) }
|
|
112
|
+
: group)),
|
|
113
|
+
}))
|
|
114
|
+
|
|
115
|
+
const groupCards = sel.groups.map((group, index) => {
|
|
116
|
+
const done = group.paths.filter((p) => p.checked).length
|
|
117
|
+
const isOpen = open.has(group.name)
|
|
118
|
+
const fileRows = isOpen
|
|
119
|
+
? group.paths.map((item, pi) => React.createElement('div', { key: item.path, className: 'edrv-svnplan-item' },
|
|
120
|
+
checkBoxEl(item.checked, () => patchGroup(index, {
|
|
121
|
+
paths: group.paths.map((p, j) => (j === pi ? { ...p, checked: !p.checked } : p)),
|
|
122
|
+
}), item.path),
|
|
123
|
+
React.createElement('span', { className: 'edrv-svnplan-path', title: item.path }, item.path)))
|
|
124
|
+
: null
|
|
125
|
+
return React.createElement('div', { key: group.name, className: 'edrv-svnplan-card' },
|
|
126
|
+
React.createElement('div', { className: 'edrv-svnplan-row' },
|
|
127
|
+
checkBoxEl(group.checked, () => toggleGroupAll(index, !group.checked), '全选该组'),
|
|
128
|
+
React.createElement('span', {
|
|
129
|
+
className: 'edrv-svnplan-groupname',
|
|
130
|
+
title: '点击展开/收起成员',
|
|
131
|
+
onClick: () => toggleOpen(group.name),
|
|
132
|
+
}, (isOpen ? '▾ ' : '▸ ') + group.name),
|
|
133
|
+
React.createElement('span', { className: 'edrv-svn-count' }, done + '/' + group.paths.length + ' 项'),
|
|
134
|
+
(group.reason ? React.createElement('span', { className: 'edrv-svnplan-reason', title: group.reason }, group.reason) : null)),
|
|
135
|
+
fileRows)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
const itemRowsOf = (key) => sel[key].map((item, index) => React.createElement('div', { key: item.path, className: 'edrv-svnplan-item' },
|
|
139
|
+
checkBoxEl(item.checked, () => patchItems(key, index, !item.checked), item.path),
|
|
140
|
+
React.createElement('span', { className: 'edrv-svnplan-path', title: item.path }, item.path),
|
|
141
|
+
(item.reason ? React.createElement('span', { className: 'edrv-svnplan-reason', title: item.reason }, item.reason) : null)))
|
|
142
|
+
|
|
143
|
+
// 段级全选:取反当前全选态(checkBoxEl 的 onToggle 不回传状态,须闭包捕获)
|
|
144
|
+
const groupsAll = sel.groups.length > 0 && sel.groups.every((g) => g.checked && g.paths.every((p) => p.checked))
|
|
145
|
+
const revertsAll = sel.reverts.length > 0 && sel.reverts.every((i) => i.checked)
|
|
146
|
+
const ignoresAll = sel.ignores.length > 0 && sel.ignores.every((i) => i.checked)
|
|
147
|
+
|
|
148
|
+
return React.createElement(React.Fragment, null,
|
|
149
|
+
React.createElement('div', { className: 'edrv-svnplan-sec' },
|
|
150
|
+
sectionHeadEl('建议分组(changelist)', '无副作用、可逆', groupsAll,
|
|
151
|
+
() => setSel((old) => ({
|
|
152
|
+
...old,
|
|
153
|
+
groups: old.groups.map((group) => ({ ...group, checked: !groupsAll, paths: group.paths.map((p) => ({ ...p, checked: !groupsAll })) })),
|
|
154
|
+
})), sel.groups.length + ' 组'),
|
|
155
|
+
React.createElement('div', { className: 'edrv-svnplan-list' }, groupCards)),
|
|
156
|
+
React.createElement('div', { className: 'edrv-svnplan-sec' },
|
|
157
|
+
sectionHeadEl('建议还原(revert)', '放弃本地改动,默认不勾', revertsAll,
|
|
158
|
+
() => toggleItemsAll('reverts', !revertsAll), sel.reverts.length + ' 项'),
|
|
159
|
+
React.createElement('div', { className: 'edrv-svnplan-list' }, itemRowsOf('reverts'))),
|
|
160
|
+
React.createElement('div', { className: 'edrv-svnplan-sec' },
|
|
161
|
+
sectionHeadEl('建议忽略(svn:ignore)', '将写入 svn:ignore 属性,随提交进仓库,默认不勾', ignoresAll,
|
|
162
|
+
() => toggleItemsAll('ignores', !ignoresAll), sel.ignores.length + ' 项'),
|
|
163
|
+
React.createElement('div', { className: 'edrv-svnplan-list' }, itemRowsOf('ignores'))))
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 阶段二 · 执行进度(阶段徽标 + 计数 + 进度条 + 逐步日志)。
|
|
168
|
+
* @author ddj 2026年09月23号
|
|
169
|
+
* @param run 执行状态
|
|
170
|
+
* @returns 进度主体元素
|
|
171
|
+
*/
|
|
172
|
+
function progressBodyEl(run) {
|
|
173
|
+
const pct = run.total > 0 ? Math.floor((run.index / run.total) * 100) : 0
|
|
174
|
+
const statusText = run.status === 'pausing' ? '等待当前步骤完成…(暂停在步骤边界生效)'
|
|
175
|
+
: run.status === 'paused' ? '已暂停'
|
|
176
|
+
: run.status === 'cancelling' ? '正在取消剩余步骤…'
|
|
177
|
+
: run.status === 'done' ? (run.cancelled ? '已取消剩余步骤' : '执行完毕')
|
|
178
|
+
: '执行中…'
|
|
179
|
+
const logRows = run.log.map((entry, index) => React.createElement('div', {
|
|
180
|
+
key: index,
|
|
181
|
+
className: 'edrv-svnplan-log' + (entry.ok ? '' : ' edrv-svnplan-log-bad'),
|
|
182
|
+
}, (entry.ok ? '✓ ' : '✗ ') + entry.text))
|
|
183
|
+
return React.createElement(React.Fragment, null,
|
|
184
|
+
React.createElement('div', { className: 'edrv-svnplan-stage' },
|
|
185
|
+
React.createElement('span', { className: 'edrv-svnplan-badge' }, statusText),
|
|
186
|
+
(run.cur ? React.createElement('span', { className: 'edrv-svnplan-badge edrv-svnplan-badge-cur' }, run.cur) : null),
|
|
187
|
+
React.createElement('span', { className: 'edrv-svn-count' }, run.index + '/' + run.total + ' 步')),
|
|
188
|
+
React.createElement('div', { className: 'edrv-svnplan-bar' },
|
|
189
|
+
React.createElement('div', { className: 'edrv-svnplan-bar-in', style: { width: pct + '%' } })),
|
|
190
|
+
React.createElement('div', { className: 'edrv-svnplan-logwrap' }, logRows))
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* AI 智能整理弹窗(预览 + 进度双阶段)。
|
|
195
|
+
* @author ddj 2026年09月23号
|
|
196
|
+
* @param props.plan 三段方案(host 已归一)
|
|
197
|
+
* @param props.meta 分析元信息 { entriesCount, diffIncluded, dropped, model, source? }(source='agent' = 混合通道)
|
|
198
|
+
* @param props.run 执行状态;null = 预览阶段(shape 见 SvnPanel runPlanSteps)
|
|
199
|
+
* @param props.onExecute 预览确认(选中载荷 → 启动执行引擎)
|
|
200
|
+
* @param props.onPause 暂停(步骤边界生效)
|
|
201
|
+
* @param props.onResume 继续
|
|
202
|
+
* @param props.onCancelRest 取消剩余
|
|
203
|
+
* @param props.onClose 关闭(预览 = 取消零副作用;执行阶段按钮仅在结束后可用)
|
|
204
|
+
* @returns 弹窗元素
|
|
205
|
+
*/
|
|
206
|
+
export function SvnAiPlanDialog(props) {
|
|
207
|
+
const { plan, meta, run, onExecute, onPause, onResume, onCancelRest, onClose } = props
|
|
208
|
+
const [sel, setSel] = React.useState(() => initPlanSel(plan))
|
|
209
|
+
const [open, setOpen] = React.useState(() => new Set())
|
|
210
|
+
const selected = selectedOf(sel)
|
|
211
|
+
const count = selectedCount(selected)
|
|
212
|
+
|
|
213
|
+
/** 组卡展开切换(纯 UI 态)。 */
|
|
214
|
+
const toggleOpen = (name) => {
|
|
215
|
+
const next = new Set(open)
|
|
216
|
+
if (next.has(name)) next.delete(name)
|
|
217
|
+
else next.add(name)
|
|
218
|
+
setOpen(next)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const running = run !== null && run.status !== 'done'
|
|
222
|
+
const header = React.createElement('div', { className: 'edrv-svnplan-head' },
|
|
223
|
+
React.createElement('h3', { className: 'edrv-svnlog-title' }, 'AI 智能整理 SVN 变更'),
|
|
224
|
+
// 混合通道来源标注(agent 深度分析 vs 快速通道直调)
|
|
225
|
+
(meta?.source === 'agent'
|
|
226
|
+
? React.createElement('span', { className: 'edrv-svnplan-badge', title: '会话 AI 助手深度分析(codegraph/读文件)产出' }, 'AI 助手深度分析')
|
|
227
|
+
: null),
|
|
228
|
+
(meta?.model ? React.createElement('span', { className: 'edrv-svn-count', title: '分析模型' }, meta.model) : null),
|
|
229
|
+
React.createElement('span', { style: { flex: 1 } }),
|
|
230
|
+
React.createElement('button', {
|
|
231
|
+
className: 'edrv-svn-act',
|
|
232
|
+
title: running ? '执行中不可关闭(可取消剩余)' : '关闭',
|
|
233
|
+
disabled: running,
|
|
234
|
+
onClick: onClose,
|
|
235
|
+
}, '✕'))
|
|
236
|
+
|
|
237
|
+
const metaEl = React.createElement('div', { className: 'edrv-svnplan-meta' },
|
|
238
|
+
React.createElement('span', { className: 'edrv-svn-count' }, '分析条目 ' + (meta?.entriesCount ?? 0) + ' 项'),
|
|
239
|
+
((meta?.dropped ?? 0) > 0 ? React.createElement('span', { className: 'edrv-svn-count' }, '已丢弃 AI 无效建议 ' + meta.dropped + ' 项') : null),
|
|
240
|
+
(meta?.diffIncluded === false
|
|
241
|
+
? React.createElement('span', { className: 'edrv-svnplan-note' }, '未含 diff 上下文,分析精度有限')
|
|
242
|
+
: null))
|
|
243
|
+
|
|
244
|
+
let body = null
|
|
245
|
+
let footer = null
|
|
246
|
+
if (run === null) {
|
|
247
|
+
body = previewBodyEl(sel, setSel, open, toggleOpen)
|
|
248
|
+
footer = React.createElement('div', { className: 'edrv-svnplan-foot' },
|
|
249
|
+
React.createElement('span', { className: 'edrv-svn-count' }, '已选 ' + count + ' 项'),
|
|
250
|
+
React.createElement('span', { style: { flex: 1 } }),
|
|
251
|
+
React.createElement('button', { className: 'edrv-svn-act', title: '不执行任何动作', onClick: onClose }, '取消'),
|
|
252
|
+
React.createElement('button', {
|
|
253
|
+
className: 'edrv-svn-act edrv-svnplan-primary',
|
|
254
|
+
title: '执行全部勾选项(未勾选不动)',
|
|
255
|
+
disabled: count === 0,
|
|
256
|
+
onClick: () => onExecute(selected),
|
|
257
|
+
}, '执行所选(' + count + ' 项)'))
|
|
258
|
+
} else {
|
|
259
|
+
body = progressBodyEl(run)
|
|
260
|
+
footer = React.createElement('div', { className: 'edrv-svnplan-foot' },
|
|
261
|
+
React.createElement('span', { style: { flex: 1 } }),
|
|
262
|
+
(run.status === 'done'
|
|
263
|
+
? React.createElement('button', {
|
|
264
|
+
className: 'edrv-svn-act edrv-svnplan-primary',
|
|
265
|
+
title: '关闭弹窗并刷新变更列表',
|
|
266
|
+
onClick: onClose,
|
|
267
|
+
}, '关闭并刷新')
|
|
268
|
+
: React.createElement(React.Fragment, null,
|
|
269
|
+
React.createElement('button', {
|
|
270
|
+
className: 'edrv-svn-act',
|
|
271
|
+
title: run.status === 'running' || run.status === 'pausing'
|
|
272
|
+
? '暂停(当前步骤完成后生效)'
|
|
273
|
+
: '从断点继续执行',
|
|
274
|
+
disabled: run.status === 'pausing' || run.status === 'cancelling',
|
|
275
|
+
onClick: run.status === 'paused' ? onResume : onPause,
|
|
276
|
+
}, run.status === 'paused' ? '继续' : '暂停'),
|
|
277
|
+
React.createElement('button', {
|
|
278
|
+
className: 'edrv-svn-act edrv-svn-act-danger',
|
|
279
|
+
title: '终止未执行步骤(已执行部分不回滚)',
|
|
280
|
+
disabled: run.status === 'cancelling',
|
|
281
|
+
onClick: onCancelRest,
|
|
282
|
+
}, '取消剩余'))))
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return React.createElement(ModalShell, {
|
|
286
|
+
key: 'edrv-svn-plan',
|
|
287
|
+
dialogClass: 'edrv-svnplan-dialog',
|
|
288
|
+
width: 'min(680px, calc(100vw - 48px))',
|
|
289
|
+
closeOnMask: !running,
|
|
290
|
+
closeOnEsc: !running,
|
|
291
|
+
onClose,
|
|
292
|
+
}, header, metaEl, body, footer)
|
|
293
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-vscode-mode client — UI 原语图标跨版本出口(消费方导出名保持 ≤0.1.6 旧名)。
|
|
3
|
+
* 背景:DSH 0.1.7 图标命名重构——`Icon<Name>16`(0.1.6 及更旧)→
|
|
4
|
+
* `Icon<Name>Regular/Medium`(0.1.7+,size 参数化 + 双笔画档),旧名在 0.1.7
|
|
5
|
+
* 全树已不存在(整树 grep 实证)。解析策略:namespace import 属性探测,旧名优先
|
|
6
|
+
* (现网 0.1.6)→ 新名次之(0.1.7+)→ 皆缺走通用占位(第三态不炸渲染);
|
|
7
|
+
* 打包期 external 不校验成员存在性,两代运行时均稳(能力探测,不做版本判定)。
|
|
8
|
+
* 调用点零语义变化:导出名与旧垫片一致,仅改 import 来源。
|
|
9
|
+
* @author ddj 2026年09月22号
|
|
10
|
+
*/
|
|
11
|
+
import React from 'react'
|
|
12
|
+
import * as primitives from '@deepseek-ai/dsh-client-ui-primitives'
|
|
13
|
+
import { classifyFileType, FileTypeIcon } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
14
|
+
import type { EdrvIconProps } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
15
|
+
|
|
16
|
+
/** 图标组件形状(与垫片 EdrvIconProps 兼容)。 */
|
|
17
|
+
type IconComponent = (props: EdrvIconProps) => unknown
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 通用占位图标(两代皆缺的第三态兜底:空态方框,不抛错不白屏)。
|
|
21
|
+
* @author ddj 2026年09月22号
|
|
22
|
+
* @param props 图标 props(size/className 透传)
|
|
23
|
+
* @returns React 元素
|
|
24
|
+
*/
|
|
25
|
+
function fallbackIcon(props: EdrvIconProps): unknown {
|
|
26
|
+
return React.createElement('svg', {
|
|
27
|
+
width: props.size ?? 16,
|
|
28
|
+
height: props.size ?? 16,
|
|
29
|
+
viewBox: '0 0 16 16',
|
|
30
|
+
className: props.className,
|
|
31
|
+
'aria-hidden': true,
|
|
32
|
+
focusable: false,
|
|
33
|
+
}, React.createElement('rect', {
|
|
34
|
+
x: 3.5, y: 3.5, width: 9, height: 9, rx: 2,
|
|
35
|
+
fill: 'none', stroke: 'currentColor', strokeWidth: 1.2, opacity: 0.45,
|
|
36
|
+
}))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 按候选名序列解析图标组件(首个存在的函数命中;皆缺返回占位)。
|
|
41
|
+
* @author ddj 2026年09月22号
|
|
42
|
+
* @param names 候选导出名(旧名在前、新名在后)
|
|
43
|
+
* @returns 图标组件
|
|
44
|
+
*/
|
|
45
|
+
function iconOf(...names: string[]): IconComponent {
|
|
46
|
+
const table = primitives as unknown as Record<string, unknown>
|
|
47
|
+
for (const name of names) {
|
|
48
|
+
const hit = table[name]
|
|
49
|
+
if (typeof hit === 'function') return hit as IconComponent
|
|
50
|
+
}
|
|
51
|
+
return fallbackIcon
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 两代语义等价的图标对(旧名 → 0.1.7 Regular 档;Medium 档不用于本插件 16px 场景)
|
|
55
|
+
/** 目录闭合图标(官方文件树同款)。 */
|
|
56
|
+
export const IconFolderClose16 = iconOf('IconFolderClose16', 'IconFolderCloseRegular')
|
|
57
|
+
/** 目录展开图标(官方文件树同款)。 */
|
|
58
|
+
export const IconFolderOpen16 = iconOf('IconFolderOpen16', 'IconFolderOpenRegular')
|
|
59
|
+
/** 目录轮廓图标(活动栏「文件管理」)。 */
|
|
60
|
+
export const IconFolderOpenOutline16 = iconOf('IconFolderOpenOutline16', 'IconFolderOpenOutlineRegular')
|
|
61
|
+
/** 刷新图标(面板工具条)。 */
|
|
62
|
+
export const IconRefreshOutline16 = iconOf('IconRefreshOutline16', 'IconRefreshOutlineRegular')
|
|
63
|
+
/** 搜索图标(活动栏「搜索」)。 */
|
|
64
|
+
export const IconSearchOutline16 = iconOf('IconSearchOutline16', 'IconSearchOutlineRegular')
|
|
65
|
+
/** 列表+笔图标(活动栏「规则」)。 */
|
|
66
|
+
export const IconListPenOutline16 = iconOf('IconListPenOutline16', 'IconListPenOutlineRegular')
|
|
67
|
+
/** 代码图标(活动栏「大纲」)。 */
|
|
68
|
+
export const IconCodeOutline16 = iconOf('IconCodeOutline16', 'IconCodeOutlineRegular')
|
|
69
|
+
/** 分支图标(SVN 面板)。 */
|
|
70
|
+
export const IconBranchOutline16 = iconOf('IconBranchOutline16', 'IconBranchOutlineRegular')
|
|
71
|
+
|
|
72
|
+
// 两代同名存活的原语:直通再导出(0.1.6/0.1.7 实证均在)
|
|
73
|
+
export { FileTypeIcon, classifyFileType } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 文件图标元素(官方 FileTypeIcon + classifyFileType 分类,未知类型为 other)。
|
|
77
|
+
* 文件树与 SVN 变更行共用(原为 FileExplorer 私有副本,上移共享防两处漂移)。
|
|
78
|
+
* @author ddj 2026年09月23号
|
|
79
|
+
* @param name 文件名(含扩展名;目录由调用方走目录图标)
|
|
80
|
+
* @returns 图标元素;原语缺失或分类异常返回 null
|
|
81
|
+
*/
|
|
82
|
+
export function fileIconEl(name: string): React.ReactElement | null {
|
|
83
|
+
if (typeof FileTypeIcon !== 'function' || typeof classifyFileType !== 'function') return null
|
|
84
|
+
try {
|
|
85
|
+
// FileTypeIcon 签名为 (props) => ReactNode(非 ReactElement),createElement 重载不认——
|
|
86
|
+
// 纯函数组件直接调用并收窄返回类型(与 FileExplorer 原 createElement 等效)。
|
|
87
|
+
return FileTypeIcon({ kind: classifyFileType(String(name ?? '')), size: 16, className: 'edrv-tree-icon' }) as React.ReactElement
|
|
88
|
+
} catch (error) {
|
|
89
|
+
return null
|
|
90
|
+
}
|
|
91
|
+
}
|
package/src/compat.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { readFileSync } from 'node:fs'
|
|
|
10
10
|
import { dirname, join } from 'node:path'
|
|
11
11
|
import { fileURLToPath } from 'node:url'
|
|
12
12
|
import { entriesOf } from './mcp.js'
|
|
13
|
-
import { loadSettingsDeps, schemaLibName, settingsInstallNote, settingsInstallStrategy } from './fileOpenSettings.js'
|
|
13
|
+
import { loadSettingsDeps, schemaLibName, settingsInstallNote, settingsInstallStrategy, configVolatileState } from './fileOpenSettings.js'
|
|
14
14
|
import { readDevForm } from './devForm.js'
|
|
15
15
|
import { compareDshVersions, detectDshVersion, familyLabel, parseDshVersion } from './dshVersion.js'
|
|
16
16
|
import { SKILL_PREFIXES, skillGroupState } from './skills.js'
|
|
@@ -133,9 +133,12 @@ export function detectGuards(ctx: Ctx): CompatAdapter[] {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
/** 已实测覆盖的最高 DSH 版本(适配矩阵上界,超过则提示,见 buildReport)。 */
|
|
136
|
-
const TESTED_DSH_MAX = '0.1.
|
|
136
|
+
const TESTED_DSH_MAX = '0.1.7-alpha.2'
|
|
137
137
|
|
|
138
|
-
/**
|
|
138
|
+
/** Config volatile 标记起效的版本线(此前设置走 section 安装,与 volatile 无关)。 */
|
|
139
|
+
const CONFIG_VOLATILE_MIN = '0.1.7-alpha.1'
|
|
140
|
+
|
|
141
|
+
/** 版本适配机制状态行:DSH 版本探测 + 设置 section 安装策略 + Config volatile 标记。 */
|
|
139
142
|
export function versionAdapters(dshVersion: string): CompatAdapter[] {
|
|
140
143
|
const adapters: CompatAdapter[] = []
|
|
141
144
|
const parsed = parseDshVersion(dshVersion)
|
|
@@ -149,15 +152,27 @@ export function versionAdapters(dshVersion: string): CompatAdapter[] {
|
|
|
149
152
|
const strategy = settingsInstallStrategy()
|
|
150
153
|
adapters.push({
|
|
151
154
|
name: '设置 section 安装(版本适配)',
|
|
152
|
-
active: strategy === 'legacy' || strategy === 'service',
|
|
155
|
+
active: strategy === 'legacy' || strategy === 'service' || strategy === 'forms',
|
|
153
156
|
note: settingsInstallNote(),
|
|
154
157
|
})
|
|
158
|
+
// 追加在末尾(既有下标有断言);仅 Config 按 volatile 语义构建过才展示该行
|
|
159
|
+
const volatileState = configVolatileState()
|
|
160
|
+
if (volatileState.requested) {
|
|
161
|
+
const volatileOk = volatileState.total > 0 && volatileState.marked === volatileState.total
|
|
162
|
+
adapters.push({
|
|
163
|
+
name: 'Config volatile 字段(0.1.7 设置页)',
|
|
164
|
+
active: volatileOk,
|
|
165
|
+
note: volatileOk
|
|
166
|
+
? volatileState.marked + '/' + volatileState.total + ' 已标记(SettingsForms 可下发本插件 ns)'
|
|
167
|
+
: volatileState.marked + '/' + volatileState.total + ' 已标记:schemastery 依赖过旧无 .volatile(),0.1.7 设置页不可用',
|
|
168
|
+
})
|
|
169
|
+
}
|
|
155
170
|
return adapters
|
|
156
171
|
}
|
|
157
172
|
|
|
158
173
|
/**
|
|
159
174
|
* 构建完整兼容性报告(RPC 与启动日志共用)。
|
|
160
|
-
* @author ddj 2026年08月24号 / 2026年09月02号
|
|
175
|
+
* @author ddj 2026年08月24号 / 2026年09月02号 / 2026年09月23号
|
|
161
176
|
* @param ctx DSH host 上下文
|
|
162
177
|
* @param options 测试注入:depsAvailable 跳过动态导入、version 固定插件版本、dshVersion 固定 DSH 版本
|
|
163
178
|
* @returns 兼容性报告
|
|
@@ -180,6 +195,12 @@ export async function buildReport(
|
|
|
180
195
|
if (parsed && testedMax && compareDshVersions(parsed, testedMax) > 0) {
|
|
181
196
|
warnings.push('DSH ' + dshVersion + ' 高于已实测版本(' + TESTED_DSH_MAX + '):设置 API 按能力探测运行,异常时请回报适配矩阵')
|
|
182
197
|
}
|
|
198
|
+
// 0.1.7 线上 Config 未完成 volatile 标记 = 设置页不可用(ns 不下发),显式告警而非静默
|
|
199
|
+
const volatileState = configVolatileState()
|
|
200
|
+
const volatileMin = parseDshVersion(CONFIG_VOLATILE_MIN)
|
|
201
|
+
if (volatileState.requested && volatileState.marked < volatileState.total && parsed && volatileMin && compareDshVersions(parsed, volatileMin) >= 0) {
|
|
202
|
+
warnings.push('插件 Config volatile 标记不完整(' + volatileState.marked + '/' + volatileState.total + '):@deepseek-ai/schemastery 依赖过旧,0.1.7 设置页不可用(重装插件依赖后重启 DSH)')
|
|
203
|
+
}
|
|
183
204
|
for (const guard of guards) {
|
|
184
205
|
if (!guard.active && guard.note) warnings.push(guard.note)
|
|
185
206
|
}
|
package/src/dshVersion.ts
CHANGED
|
@@ -85,13 +85,14 @@ export function inDshRange(version: DshVersion | null | undefined, range: DshRan
|
|
|
85
85
|
/**
|
|
86
86
|
* 版本线标签:报告与文档展示版本归属;不可解析返回 '未知'。
|
|
87
87
|
* 逐线自新到旧匹配,先命中先返回(区间无上界,故顺序即优先级)。
|
|
88
|
-
* @author ddj 2026年09月02号 / 2026年09月18号
|
|
88
|
+
* @author ddj 2026年09月02号 / 2026年09月18号 / 2026年09月22号
|
|
89
89
|
* @param input 版本串(如 '0.1.6-alpha.2')
|
|
90
90
|
* @returns 版本线标签
|
|
91
91
|
*/
|
|
92
92
|
export function familyLabel(input: string): string {
|
|
93
93
|
const version = parseDshVersion(input)
|
|
94
94
|
if (!version) return '未知'
|
|
95
|
+
if (inDshRange(version, { from: '0.1.7-alpha.1' })) return '0.1.7-alpha.1 及更新(设置=profile Config + configForms 客户端面 + 会话 V4)'
|
|
95
96
|
if (inDshRange(version, { from: '0.1.6-alpha.2' })) return '0.1.6-alpha.2 及更新(会话多实例共存 + 回合改动卡片 + Office 侧栏预览 + 侧栏浏览器)'
|
|
96
97
|
if (inDshRange(version, { from: '0.1.6-alpha.1' })) return '0.1.6-alpha 及更新(MCP SDK v2 + Web 侧边栏终端 + 文件链接默认侧栏预览)'
|
|
97
98
|
if (inDshRange(version, { from: '0.1.5-alpha.1' })) return '0.1.5-alpha 及更新(官方右侧 Sidebar 编辑区 + sidebar.panellist)'
|