dsh-config-manager 0.1.76 → 0.1.78
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/adapters/plugin-data-declared.d.ts +12 -0
- package/lib/adapters/plugin-data-declared.js +57 -0
- package/lib/adapters/plugin-data.d.ts +4 -6
- package/lib/adapters/plugin-data.js +8 -36
- package/lib/adapters/plugins.d.ts +16 -0
- package/lib/adapters/plugins.js +223 -16
- package/lib/client.d.ts +104 -9
- package/lib/client.js +1800 -718
- package/lib/core/analyzer.js +3 -1
- package/lib/core/backup.d.ts +9 -0
- package/lib/core/backup.js +16 -1
- package/lib/core/messages.d.ts +11 -0
- package/lib/core/messages.js +22 -0
- package/lib/core/plugin-cli.d.ts +26 -0
- package/lib/core/plugin-cli.js +62 -2
- package/lib/core/rollback-attribution.d.ts +81 -0
- package/lib/core/rollback-attribution.js +236 -0
- package/lib/core/rollback-merge.d.ts +22 -0
- package/lib/core/rollback-merge.js +203 -0
- package/lib/core/rollback-point.d.ts +114 -0
- package/lib/core/rollback-point.js +534 -17
- package/lib/core/types.d.ts +28 -0
- package/lib/index.d.ts +26 -3
- package/lib/index.js +129 -16
- package/lib/routes/recovery.js +11 -0
- package/lib/schema/section-registry.js +5 -5
- package/lib/schema/types.d.ts +14 -0
- package/lib/ui/badge-tone.d.ts +16 -0
- package/lib/ui/badge-tone.js +20 -0
- package/lib/ui/i18n.d.ts +4 -4
- package/lib/ui/i18n.js +8 -8
- package/lib/ui/rollback-view.d.ts +95 -0
- package/lib/ui/rollback-view.js +55 -0
- package/lib/ui/selection-model.d.ts +140 -1
- package/lib/ui/selection-model.js +241 -9
- package/package.json +1 -1
- package/src/adapters/plugin-data-declared.ts +60 -0
- package/src/adapters/plugin-data.ts +7 -33
- package/src/adapters/plugins.test.ts +162 -0
- package/src/adapters/plugins.ts +236 -17
- package/src/adapters/units.test.ts +26 -0
- package/src/client/chat/ChatEntryView.tsx +147 -46
- package/src/client/chat/chat-entry-view.test.ts +277 -1
- package/src/client/chat/chat-entry-view.ts +98 -4
- package/src/client/chat/chat-node-registration.ts +71 -0
- package/src/client/chat/chat-slots.ts +30 -12
- package/src/client/common/ContentPicker.tsx +581 -175
- package/src/client/common/host-primitives.ts +64 -1
- package/src/client/common/ui.tsx +17 -3
- package/src/client/config-manager.module.css +630 -397
- package/src/client/css-scale-guard.test.ts +41 -3
- package/src/client/css-token-guard.test.ts +10 -4
- package/src/client/export/ExportView.tsx +2 -0
- package/src/client/index.ts +15 -3
- package/src/client/locales.ts +54 -10
- package/src/client/recovery/RollbackCard.tsx +56 -2
- package/src/client/recovery/RollbackDiffPane.tsx +5 -0
- package/src/client/recovery/RollbackFileChanges.tsx +46 -2
- package/src/client/recovery/recovery-locales.ts +22 -0
- package/src/client/recovery/rollback-file-changes.test.ts +1 -1
- package/src/core/analyzer.ts +3 -1
- package/src/core/backup.ts +16 -1
- package/src/core/messages.ts +22 -0
- package/src/core/plugin-cli.fs.test.ts +65 -3
- package/src/core/plugin-cli.ts +55 -2
- package/src/core/rollback-attribution.test.ts +120 -0
- package/src/core/rollback-attribution.ts +267 -0
- package/src/core/rollback-merge.test.ts +133 -0
- package/src/core/rollback-merge.ts +213 -0
- package/src/core/rollback-point.test.ts +412 -0
- package/src/core/rollback-point.ts +560 -20
- package/src/core/types.ts +29 -0
- package/src/index.facade.test.ts +60 -0
- package/src/index.ts +133 -19
- package/src/routes/recovery.ts +11 -0
- package/src/schema/section-registry.ts +5 -5
- package/src/schema/types.ts +14 -0
- package/src/ui/badge-tone.test.ts +23 -0
- package/src/ui/badge-tone.ts +27 -0
- package/src/ui/i18n.ts +8 -8
- package/src/ui/rollback-view.test.ts +27 -0
- package/src/ui/rollback-view.ts +139 -0
- package/src/ui/selection-model.test.ts +240 -2
- package/src/ui/selection-model.ts +349 -1
|
@@ -191,6 +191,56 @@ export type ChatEntryAttachment =
|
|
|
191
191
|
| { kind: 'image'; image: ChatEntryImage }
|
|
192
192
|
| { kind: 'file'; file: ChatEntryFile }
|
|
193
193
|
|
|
194
|
+
/* ------------------------------------------------------------ 宿主 content factory(0.2.1+ 委托路径) */
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 宿主 `conversation.chat.user.content`(Factory 位)的名字。
|
|
198
|
+
*
|
|
199
|
+
* 0.2.1-alpha.3 起用户气泡的正文/附件/引用**整体换轨**到这条 factory:图片位
|
|
200
|
+
* `conversation.chat.user.images` 是它的 children(single/session),旧的 `conversation.message.images`
|
|
201
|
+
* 与节点 props 上的 `renderMessageImages` 同时删除(本机解包 0.2.1-rc.2 实测:后者 0 次出现)。
|
|
202
|
+
* 因此「接管 user 之后自己复刻气泡」这条路在 0.2.1 上拿不到图片位(issue #92)——正确做法是把正文
|
|
203
|
+
* **委托回宿主自己的 content factory**,图片由 ui-attachment 在该 factory 的渲染上下文里 renderSlot。
|
|
204
|
+
*
|
|
205
|
+
* 老宿主(≤0.2.0)没有这条 factory:`FactoryOutlet` 在定义缺失时回落调用方给的 `options.fallback`
|
|
206
|
+
* ⇒ 渲染层把**自绘气泡**当 fallback 传进去,0.1.x/0.2.0 的路径逐字不变(一个版本双栈兼容)。
|
|
207
|
+
*/
|
|
208
|
+
export const CHAT_USER_CONTENT_FACTORY = 'conversation.chat.user.content'
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* 宿主 content factory 的附件项(逐字保留宿主 content 里的原始 attachment 载荷)。
|
|
212
|
+
*
|
|
213
|
+
* 形状逐字取自宿主 `contentParts`:`{ type:'image', image:{ attachment } }` /
|
|
214
|
+
* `{ type:'file', file: block.attachment }` —— `file` 那一支是我们的 ChatEntryFile **拼不回来**的
|
|
215
|
+
* `FileAttachmentRef`,所以必须原样留一份。
|
|
216
|
+
*/
|
|
217
|
+
export type HostUserContentAttachment =
|
|
218
|
+
| { type: 'image'; image: { attachment: unknown } }
|
|
219
|
+
| { type: 'file'; file: unknown }
|
|
220
|
+
|
|
221
|
+
/** 传给宿主 content factory 的正文载荷(= 宿主 UserStyleBubble 的 `prepared`)。 */
|
|
222
|
+
export interface HostUserContentData {
|
|
223
|
+
/** 拼接后的**原文**(未投影;宿主 content factory 内部自己投影会话引用/技能 chip)。 */
|
|
224
|
+
text: string
|
|
225
|
+
attachments: HostUserContentAttachment[]
|
|
226
|
+
/** 未知 / rest 块(宿主交给 JsonBlock,**必须**原样带过去)。 */
|
|
227
|
+
extraBlocks: unknown[]
|
|
228
|
+
/** 仅当节点 data 真的带了该字段时出现(宿主用「字段是否存在」区分「没带」与「空」)。 */
|
|
229
|
+
referenceLabels?: string[]
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** 传给宿主 content factory 的文本投影事实(= 宿主 UserStyleBubble 的 `facts`)。 */
|
|
233
|
+
export interface HostUserContentFacts {
|
|
234
|
+
sessionLabels?: string[]
|
|
235
|
+
skillNames?: string[]
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** 一次 `renderFactorySlot(CHAT_USER_CONTENT_FACTORY, …)` 的入参(`loadImage`/`interactive` 由渲染层补)。 */
|
|
239
|
+
export interface HostUserContentInput {
|
|
240
|
+
content: HostUserContentData
|
|
241
|
+
facts: HostUserContentFacts
|
|
242
|
+
}
|
|
243
|
+
|
|
194
244
|
export interface ChatEntryBubbleView {
|
|
195
245
|
/** 文本块拼接结果(宿主 `texts.join('')`,逐字保留换行与空白;会话引用的线格式已投影成 label)。 */
|
|
196
246
|
text: string
|
|
@@ -201,6 +251,12 @@ export interface ChatEntryBubbleView {
|
|
|
201
251
|
rawText: string
|
|
202
252
|
/** **有序附件列表**:渲染层必须按这个顺序输出(宿主不区分「先图片后文件」)。 */
|
|
203
253
|
attachments: ChatEntryAttachment[]
|
|
254
|
+
/**
|
|
255
|
+
* **宿主形状**的有序附件列表(0.2.1+ 委托路径专用):与 `attachments` 同一趟走查、同一顺序,
|
|
256
|
+
* 但保留宿主 content 里的**原始 attachment 载荷**(我们的 ChatEntryFile 只留了三个展示字段,
|
|
257
|
+
* 拼不回宿主 `FileAttachmentRef`)。逐字对齐宿主 `contentParts` 的 `attachments.push`。
|
|
258
|
+
*/
|
|
259
|
+
hostAttachments: HostUserContentAttachment[]
|
|
204
260
|
/** 图片附件(= `attachments` 里的 image 项,顺序一致;用于计数与「无渲染位」提示)。 */
|
|
205
261
|
images: ChatEntryImage[]
|
|
206
262
|
/** 文件附件(= `attachments` 里的 file 项,顺序一致)。 */
|
|
@@ -414,14 +470,15 @@ export function fileSizeTextOf(bytes: number | null): string | null {
|
|
|
414
470
|
export function buildChatEntryBubble(content: unknown): ChatEntryBubbleView {
|
|
415
471
|
if (typeof content === 'string') {
|
|
416
472
|
const text = projectChatEntryText(content)
|
|
417
|
-
return { text, rawText: content, attachments: [], images: [], files: [], raw: [], showBubble: text !== '' }
|
|
473
|
+
return { text, rawText: content, attachments: [], hostAttachments: [], images: [], files: [], raw: [], showBubble: text !== '' }
|
|
418
474
|
}
|
|
419
475
|
if (!Array.isArray(content)) {
|
|
420
|
-
return { text: '', rawText: '', attachments: [], images: [], files: [], raw: content === null || content === undefined ? [] : [{ id: '0', payload: content }], showBubble: content !== null && content !== undefined }
|
|
476
|
+
return { text: '', rawText: '', attachments: [], hostAttachments: [], images: [], files: [], raw: content === null || content === undefined ? [] : [{ id: '0', payload: content }], showBubble: content !== null && content !== undefined }
|
|
421
477
|
}
|
|
422
478
|
const texts: string[] = []
|
|
423
479
|
const rawTexts: string[] = []
|
|
424
480
|
const attachments: ChatEntryAttachment[] = []
|
|
481
|
+
const hostAttachments: HostUserContentAttachment[] = []
|
|
425
482
|
const raw: ChatEntryRawBlock[] = []
|
|
426
483
|
content.forEach((block, index) => {
|
|
427
484
|
const record = asRecord(block)
|
|
@@ -435,6 +492,8 @@ export function buildChatEntryBubble(content: unknown): ChatEntryBubbleView {
|
|
|
435
492
|
}
|
|
436
493
|
if (type === 'image' && record?.['attachment'] !== undefined) {
|
|
437
494
|
attachments.push({ kind: 'image', image: { attachment: record['attachment'] } })
|
|
495
|
+
// 同一趟走查里并出宿主形状的那一份(同一顺序、原始载荷)—— 见 HostUserContentAttachment
|
|
496
|
+
hostAttachments.push({ type: 'image', image: { attachment: record['attachment'] } })
|
|
438
497
|
return
|
|
439
498
|
}
|
|
440
499
|
if (type === 'file' && record?.['attachment'] !== undefined) {
|
|
@@ -449,6 +508,7 @@ export function buildChatEntryBubble(content: unknown): ChatEntryBubbleView {
|
|
|
449
508
|
bytes: typeof bytes === 'number' && Number.isFinite(bytes) ? bytes : null,
|
|
450
509
|
},
|
|
451
510
|
})
|
|
511
|
+
hostAttachments.push({ type: 'file', file: record['attachment'] })
|
|
452
512
|
return
|
|
453
513
|
}
|
|
454
514
|
raw.push({ id: String(index), payload: block })
|
|
@@ -458,7 +518,34 @@ export function buildChatEntryBubble(content: unknown): ChatEntryBubbleView {
|
|
|
458
518
|
// images / files 是 attachments 的**派生视图**(不独立积累,避免两处真相与顺序漂移)
|
|
459
519
|
const images = attachments.flatMap((item) => (item.kind === 'image' ? [item.image] : []))
|
|
460
520
|
const files = attachments.flatMap((item) => (item.kind === 'file' ? [item.file] : []))
|
|
461
|
-
return { text, rawText, attachments, images, files, raw, showBubble: text !== '' || raw.length > 0 }
|
|
521
|
+
return { text, rawText, attachments, hostAttachments, images, files, raw, showBubble: text !== '' || raw.length > 0 }
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* 节点 data + 气泡视图 → 宿主 content factory 的入参(0.2.1+ 委托路径的**唯一**组装点)。
|
|
526
|
+
*
|
|
527
|
+
* 逐字对齐宿主 `UserStyleBubble` 的 `prepared` / `facts` 两段 useMemo:
|
|
528
|
+
* · `content.text` = 未投影的文本拼接(宿主 factory 自己把它交给 `conversation.user.text` 投影成 chip);
|
|
529
|
+
* · `content.attachments` = 宿主形状的有序附件(同一趟走查产出);
|
|
530
|
+
* · `content.extraBlocks` = rest 块原样;
|
|
531
|
+
* · `referenceLabels` / `sessionLabels` / `skillNames` **只在节点真的带了该字段时出现** ——
|
|
532
|
+
* 宿主用「字段是否存在」区分「没带」与「空数组」,我们传 `[]` 会改变它的分支。
|
|
533
|
+
*
|
|
534
|
+
* 本函数不抛(读不到就按「没带」处理),与 buildChatEntryBubble 同一条纪律。
|
|
535
|
+
*/
|
|
536
|
+
export function buildHostUserContentInput(data: ChatEntryNodeDataView, bubble: ChatEntryBubbleView): HostUserContentInput {
|
|
537
|
+
const content: HostUserContentData = {
|
|
538
|
+
text: bubble.rawText,
|
|
539
|
+
attachments: bubble.hostAttachments,
|
|
540
|
+
extraBlocks: bubble.raw.map((block) => block.payload),
|
|
541
|
+
}
|
|
542
|
+
const facts: HostUserContentFacts = {}
|
|
543
|
+
if (data.referenceLabels !== null) {
|
|
544
|
+
content.referenceLabels = data.referenceLabels
|
|
545
|
+
facts.sessionLabels = data.referenceLabels
|
|
546
|
+
}
|
|
547
|
+
if (data.skillNames !== null) facts.skillNames = data.skillNames
|
|
548
|
+
return { content, facts }
|
|
462
549
|
}
|
|
463
550
|
|
|
464
551
|
/** 渲染计划的一项:图片带 owner 载荷,文件带名称/扩展名/体积文本。 */
|
|
@@ -631,19 +718,26 @@ export interface ChatEntryView {
|
|
|
631
718
|
*/
|
|
632
719
|
referenceLabels: string[]
|
|
633
720
|
skillNames: string[]
|
|
721
|
+
/**
|
|
722
|
+
* 宿主 content factory 的入参(0.2.1+ 委托路径)。**始终**算好:新宿主用它委托,老宿主把它
|
|
723
|
+
* 闲置(那条 factory 不存在 ⇒ 渲染层走自绘气泡 fallback),两条路径都不需要再读一次节点。
|
|
724
|
+
*/
|
|
725
|
+
hostContent: HostUserContentInput
|
|
634
726
|
}
|
|
635
727
|
|
|
636
728
|
/** 节点 → 整视图模型(读不到一律回安全值;本函数不抛)。 */
|
|
637
729
|
export function buildChatEntryView(node: unknown): ChatEntryView {
|
|
638
730
|
const data = readChatEntryNodeData(node)
|
|
639
731
|
const turn = chatNodeTurn(node)
|
|
732
|
+
const bubble = buildChatEntryBubble(data.content)
|
|
640
733
|
return {
|
|
641
734
|
turnNumber: turn?.turn ?? null,
|
|
642
735
|
turnStatus: turn?.status ?? null,
|
|
643
|
-
bubble
|
|
736
|
+
bubble,
|
|
644
737
|
clock: formatChatEntryClock(data.time),
|
|
645
738
|
referenceLabels: data.referenceLabels ?? [],
|
|
646
739
|
skillNames: data.skillNames ?? [],
|
|
740
|
+
hostContent: buildHostUserContentInput(data, bubble),
|
|
647
741
|
}
|
|
648
742
|
}
|
|
649
743
|
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
* 2026-10-12(用户要求「聊天内回退入口只有每条用户消息」):旧的「整回合」档连同它的偏好模块
|
|
9
9
|
* (`chat-entry-pref` / `chat-entry-mount`)与回合尾入口(`conversation.chat.turnTail`)一并删除 ——
|
|
10
10
|
* 入口恒为每条用户消息气泡里的那一颗,于是这里不再有偏好门,也不再需要「切档时重新评估」的订阅。
|
|
11
|
+
*
|
|
12
|
+
* 2026-10-17(用户要求):开关**关闭时不得再接管用户消息气泡**。原实现是「登记常驻、只收起按钮」——
|
|
13
|
+
* 功能关掉之后那条气泡仍由我们代画(即使正文已委托宿主 factory,外层壳与动作行仍是我们的)。
|
|
14
|
+
* 现在登记本身随开关出现 / 撤销(`registerChatNodeEntryWhenEnabled`):关掉即让出 user cell,
|
|
15
|
+
* 宿主 `UserMessageNodeView` 原样接管回来。
|
|
11
16
|
*/
|
|
12
17
|
import {
|
|
13
18
|
CHAT_NODE_SLOT,
|
|
@@ -96,3 +101,69 @@ export function registerChatNodeEntry(options: {
|
|
|
96
101
|
return () => {}
|
|
97
102
|
}
|
|
98
103
|
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 开关驱动的登记闸门:**只在功能开启时才接管 user cell**。
|
|
107
|
+
*
|
|
108
|
+
* 为什么不是「常驻登记 + 组件里隐藏按钮」:接管 = priority -1 遮蔽宿主 `UserMessageNodeView`,
|
|
109
|
+
* 只要登记还在,那条气泡(含外层壳与动作行)就由我们画 —— 用户 2026-10-17 明确要求关闭后
|
|
110
|
+
* **完全不要接管**,气泡交回宿主原生渲染。反过来,开关打开时**必须**有登记,否则入口无处可放
|
|
111
|
+
* (该 key 没有 children 表,见 `chat-slots.ts`)。
|
|
112
|
+
*
|
|
113
|
+
* 纯逻辑(开关读写与订阅都从外面注进来),便于在 node:test 下逐条断言:
|
|
114
|
+
* · 关闭 → 一次注册都不发生;
|
|
115
|
+
* · 开关转开 → 登记一次(重复通知幂等);
|
|
116
|
+
* · 开关转关 → **立刻撤销登记**(cell 回到宿主 priority 0);
|
|
117
|
+
* · 装配卸载 → 解绑订阅 + 撤销登记,不留悬挂注册。
|
|
118
|
+
*
|
|
119
|
+
* @returns 卸载函数(调用点 = `ctx.slots.inject` 的回调返回值)。
|
|
120
|
+
*/
|
|
121
|
+
export interface ChatNodeEntryGateOptions {
|
|
122
|
+
/** 槽位探测面(同 {@link registerChatNodeEntry};探测只在真正注册那一次发生)。 */
|
|
123
|
+
probe: ChatNodeSlotProbe | undefined | null
|
|
124
|
+
/** 真正的注册调用(调用点包 `ctx.slots.register`,保持编译期校验)。 */
|
|
125
|
+
register: () => () => void
|
|
126
|
+
/** 功能此刻是否开启(`rollbackEnabled` 的进程内信号)。 */
|
|
127
|
+
isEnabled: () => boolean
|
|
128
|
+
/** 订阅开关变化(返回解绑函数)。 */
|
|
129
|
+
subscribe: (listener: () => void) => () => void
|
|
130
|
+
/** 诊断回调(缺省 `console.warn`;单测注入以免污染输出)。 */
|
|
131
|
+
warn?: (message: string) => void
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function registerChatNodeEntryWhenEnabled(options: ChatNodeEntryGateOptions): () => void {
|
|
135
|
+
/** 当前登记的解绑函数;null = 现在没有登记(= 宿主渲染器在渲染那条 cell)。 */
|
|
136
|
+
let disposeEntry: (() => void) | null = null
|
|
137
|
+
|
|
138
|
+
const sync = (): void => {
|
|
139
|
+
if (options.isEnabled() === true) {
|
|
140
|
+
if (disposeEntry === null) {
|
|
141
|
+
disposeEntry = registerChatNodeEntry({
|
|
142
|
+
probe: options.probe,
|
|
143
|
+
register: options.register,
|
|
144
|
+
...(options.warn === undefined ? {} : { warn: options.warn }),
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
if (disposeEntry !== null) {
|
|
150
|
+
const dispose = disposeEntry
|
|
151
|
+
disposeEntry = null
|
|
152
|
+
dispose()
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const unsubscribe = options.subscribe(sync)
|
|
157
|
+
// 订阅**先建后读**:读到与订阅之间没有窗口,开关恰在此刻翻转也不会漏(多跑一次 sync 是幂等的)。
|
|
158
|
+
sync()
|
|
159
|
+
|
|
160
|
+
return () => {
|
|
161
|
+
unsubscribe()
|
|
162
|
+
if (disposeEntry !== null) {
|
|
163
|
+
const dispose = disposeEntry
|
|
164
|
+
disposeEntry = null
|
|
165
|
+
dispose()
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
@@ -9,19 +9,25 @@
|
|
|
9
9
|
*
|
|
10
10
|
* ## 镜像来源(**逐条核过真机 app.asar,不是猜的**)
|
|
11
11
|
*
|
|
12
|
-
* 槽位声明 = `packages/client/ui-chat/src/client/contract/slots.ts
|
|
13
|
-
* conversation.chat.node / conversation.message.images / conversation.chat.commandview /
|
|
14
|
-
* conversation.chat.turnTail / conversation.chat.assistant-actions / conversation.view /
|
|
15
|
-
* conversation.composer.dock / conversation.approval.detail)。
|
|
12
|
+
* 槽位声明 = `packages/client/ui-chat/src/client/contract/slots.ts`。
|
|
16
13
|
*
|
|
17
14
|
* ### `conversation.chat.node`(本插件**接管**其 `user` key)
|
|
18
15
|
*
|
|
19
16
|
* kind: 'keyed' · scope: 'session'
|
|
20
|
-
* ownerProps: { groupPart?, cwd, openFile, openSkill, inspectCall, forkAt, loadImage,
|
|
21
|
-
* renderMessageImages, fileMentions, turnProcess, node }
|
|
22
17
|
* registerOptions: key(必填) / priority(可选,= cell 遮蔽秩,升序、最低者渲染)
|
|
23
18
|
* replaceRisk: **有**(这是 keyed 遮蔽位:priority -1 会顶掉 shipped 的 UserMessageNodeView)
|
|
24
19
|
*
|
|
20
|
+
* ownerProps **在 0.2.1-alpha.3 换过一代**(本机解包 `@deepseek-ai/dsh-client-ui-chat` 的
|
|
21
|
+
* `lib/client.js` + `lib/types/.../slots.d.ts` 实测,2026-10-17,issue #92):
|
|
22
|
+
*
|
|
23
|
+
* ≤0.2.0 :{ groupPart?, cwd, openFile, openSkill, inspectCall, forkAt, loadImage,
|
|
24
|
+
* renderMessageImages, fileMentions, turnProcess, node }
|
|
25
|
+
* ≥0.2.1 :{ groupPart?, cwd, openFile, inspectCall, forkAt, loadImage, fileMentions, node }
|
|
26
|
+
* —— **删掉 `renderMessageImages` 与 `openSkill`**;图片位不再由 ui-chat 直接声明,
|
|
27
|
+
* 而是 `conversation.chat.user.content` factory 的 children
|
|
28
|
+
* (`conversation.chat.user.images`, single/session)⇒ 想拿到图片**必须**委托那条 factory
|
|
29
|
+
* (实现与判据见 `ChatEntryView.tsx` / `CHAT_USER_CONTENT_FACTORY`)。
|
|
30
|
+
*
|
|
25
31
|
* 关键事实(决定了本功能只能走这条路,见 T1 output 的证据段):
|
|
26
32
|
* · 宿主对 `{ name:'conversation.chat.node', key:'user' }` **没有声明 children 表**
|
|
27
33
|
* (对比 turn-tail 节点声明了 turnTail + assistant-actions 两个 list 子位)
|
|
@@ -84,14 +90,26 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
84
90
|
*/
|
|
85
91
|
node: unknown
|
|
86
92
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
93
|
+
* 图片渲染位(**≤0.2.0** 宿主:`conversation.message.images` 的 renderSlot 绑定)——
|
|
94
|
+
* 必须用它渲染图片(ui-chat 的私有实现不可导入)。
|
|
95
|
+
*
|
|
96
|
+
* **0.2.1-alpha.3 起宿主整行删掉了它**(issue #92:新契约改由
|
|
97
|
+
* `conversation.chat.user.content` factory 的 `conversation.chat.user.images` 子位承载)
|
|
98
|
+
* ⇒ 镜像里跟着改成**可选**:渲染层据「它在不在」决定走旧的自绘图片位,还是委托 content factory。
|
|
99
|
+
*/
|
|
100
|
+
renderMessageImages?: (owner: unknown) => unknown
|
|
101
|
+
/**
|
|
102
|
+
* 宿主授权过的图片加载器(0.2.1 起节点渲染器 props 上的稳定席位;转发给 content factory)。
|
|
103
|
+
* 老宿主上拿不到它(那时 loadImage 被封在 renderMessageImages 的闭包里)。
|
|
89
104
|
*/
|
|
90
|
-
|
|
105
|
+
loadImage?: unknown
|
|
91
106
|
/** 宿主提供的文件打开器(本插件不消费,保持契约完整)。 */
|
|
92
|
-
openFile
|
|
93
|
-
/**
|
|
94
|
-
|
|
107
|
+
openFile?: (path: string) => void
|
|
108
|
+
/**
|
|
109
|
+
* 宿主提供的 skill 打开器(同上)。0.2.1 起该席位从 ChatNodeOwnerProps 消失
|
|
110
|
+
* (与 renderMessageImages 同一批)⇒ 同样改成可选。
|
|
111
|
+
*/
|
|
112
|
+
openSkill?: (name: string) => void
|
|
95
113
|
}
|
|
96
114
|
}
|
|
97
115
|
owner: {
|