dsh-plugin-image-tools 0.6.4 → 0.6.5
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/CHANGELOG.md +7 -0
- package/lib/client.js +26 -23
- package/package.json +5 -1
- package/scripts/smoke-client.mjs +29 -22
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.5] - 2026-09-04
|
|
4
|
+
|
|
5
|
+
- 适配 dsh 0.1.2-rc.1:conversation.composer 链条目 select 入参由 `{interactions}` 改为 `{pendingInteraction}`(原生 PendingQuestion 单对象),按 questions/answer/cancel 鸭子类型认领带图片标记的问题批,plan-review 放行;
|
|
6
|
+
- 待答载体 PendingChoice 改为包装 PendingQuestion(`.answer({answers})` / `.cancel()` 直调,替代旧 `wait.respond` 协议);
|
|
7
|
+
- DOM 增强部分(data-chat-flow 容器、dshimg 占位符、lightbox)经核验在新版 dsh-client-ui-chat 中锚点全部保留,无需改动。
|
|
8
|
+
# Changelog
|
|
9
|
+
|
|
3
10
|
## [0.6.4] - 2026-08-29
|
|
4
11
|
|
|
5
12
|
- 文档:「实现要点」整节下移到「模型用法示例」之后(访客先看价值再看原理);
|
package/lib/client.js
CHANGED
|
@@ -140,22 +140,20 @@ window.__ModuleLoader__.load({
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
// ------------------------------------------------------------------
|
|
143
|
-
//
|
|
143
|
+
// 待答载体(dsh 0.1.2 起 composer 链的 matched 即原生 PendingQuestion:
|
|
144
|
+
// .answer({answers}) / .cancel() / .key / .sessionId / .questions,
|
|
145
|
+
// 这里包一层维持组件既有协议)
|
|
144
146
|
// ------------------------------------------------------------------
|
|
145
147
|
var PendingChoice = class {
|
|
146
|
-
constructor(
|
|
147
|
-
get key() { return this.
|
|
148
|
-
get
|
|
148
|
+
constructor(matched) { this.matched = matched; }
|
|
149
|
+
get key() { return this.matched.key; }
|
|
150
|
+
get sessionId() { return this.matched.sessionId; }
|
|
151
|
+
get questions() { return this.matched.questions; }
|
|
149
152
|
async answer(answer) {
|
|
150
|
-
|
|
151
|
-
if (!receipt.accepted) throw new Error('question response rejected: ' + receipt.reason);
|
|
153
|
+
await this.matched.answer(answer);
|
|
152
154
|
}
|
|
153
155
|
async cancel() {
|
|
154
|
-
|
|
155
|
-
ok: false,
|
|
156
|
-
error: { code: 'cancelled', message: 'the user closed this question request', details: {} }
|
|
157
|
-
});
|
|
158
|
-
if (!receipt.accepted) throw new Error('question cancellation rejected: ' + receipt.reason);
|
|
156
|
+
await this.matched.cancel();
|
|
159
157
|
}
|
|
160
158
|
};
|
|
161
159
|
|
|
@@ -1299,19 +1297,24 @@ window.__ModuleLoader__.load({
|
|
|
1299
1297
|
return jsx(ImageChoiceFlow, { pending: pending, t: props.t }, pending.key);
|
|
1300
1298
|
}
|
|
1301
1299
|
|
|
1302
|
-
/**
|
|
1300
|
+
/**
|
|
1301
|
+
* 认领“至少一题带图片标记”的 question 交互;其余交给原生 UI。
|
|
1302
|
+
* dsh 0.1.2 起 select 入参为 { pendingInteraction }(单对象,即原生
|
|
1303
|
+
* PendingQuestion 实例;无交互时为 null),不再是 { interactions } 数组。
|
|
1304
|
+
*/
|
|
1305
|
+
function isPendingQuestion(value) {
|
|
1306
|
+
return value !== null && typeof value === 'object'
|
|
1307
|
+
&& Array.isArray(value.questions)
|
|
1308
|
+
&& typeof value.answer === 'function'
|
|
1309
|
+
&& typeof value.cancel === 'function';
|
|
1310
|
+
}
|
|
1303
1311
|
function selectPickChoice(_ref) {
|
|
1304
|
-
var
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
var claimed = false;
|
|
1311
|
-
for (var j = 0; j < questions.length; j++) {
|
|
1312
|
-
if (parseMarker(questions[j].detail) !== null) { claimed = true; break; }
|
|
1313
|
-
}
|
|
1314
|
-
if (claimed) return item;
|
|
1312
|
+
var pendingInteraction = _ref.pendingInteraction;
|
|
1313
|
+
if (!isPendingQuestion(pendingInteraction)) return null;
|
|
1314
|
+
if (pendingInteraction.kind === 'plan-review') return null;
|
|
1315
|
+
var questions = pendingInteraction.questions;
|
|
1316
|
+
for (var j = 0; j < questions.length; j++) {
|
|
1317
|
+
if (questions[j] !== null && typeof questions[j] === 'object' && parseMarker(questions[j].detail) !== null) return pendingInteraction;
|
|
1315
1318
|
}
|
|
1316
1319
|
return null;
|
|
1317
1320
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-image-tools",
|
|
3
3
|
"description": "DSH 图片插件,三个工具覆盖三种场景:ask_user_choice 图片/图文混合选择卡(Web GUI 渲染,可放大查看)+ show_images 回复内嵌图片(图文混排)+ save_received_images 盲模型收图存为工作区文件;聊天栏所有图片点击放大,支持滚轮缩放与拖拽平移。来源支持本地路径 / http(s) URL / base64 data URI。零 token 本地渲染,纯插件实现不改核心包。",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"@deepseek-ai/cordis": "^4.0.1"
|
|
31
31
|
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"react": "^19.0.0",
|
|
34
|
+
"react-dom": "^19.0.0"
|
|
35
|
+
},
|
|
32
36
|
"engines": {
|
|
33
37
|
"node": ">=18"
|
|
34
38
|
},
|
package/scripts/smoke-client.mjs
CHANGED
|
@@ -19,12 +19,13 @@ import { buildPickMarker } from '../lib/index.js'
|
|
|
19
19
|
|
|
20
20
|
const require = createRequire(import.meta.url)
|
|
21
21
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
const
|
|
22
|
+
// 优先用插件自带的 devDependencies react(0.6.5 起 dsh-app 根部 react 可能被降级
|
|
23
|
+
// 到 16.8、无 jsx-runtime,与 react-dom 19 不同源);本地没有时退回 profile 共享树。
|
|
24
|
+
const localRequire = createRequire(join(ROOT, 'noop.cjs'))
|
|
25
|
+
const reactRoot = dirname(localRequire.resolve('react'))
|
|
25
26
|
const jsxRuntime = join(reactRoot, 'jsx-runtime.js')
|
|
26
|
-
const reactModule =
|
|
27
|
-
const reactDomServer =
|
|
27
|
+
const reactModule = localRequire('react')
|
|
28
|
+
const reactDomServer = localRequire('react-dom/server')
|
|
28
29
|
|
|
29
30
|
// 1) 模拟浏览器模块加载器
|
|
30
31
|
let spec = null
|
|
@@ -121,24 +122,28 @@ assert.deepEqual(mod.extractImageTokenIds('📷 图片 dshimg:att-aaa111bbb222
|
|
|
121
122
|
assert.deepEqual(mod.extractImageTokenIds('没有 token'), [])
|
|
122
123
|
assert.deepEqual(mod.extractImageTokenIds(undefined), [])
|
|
123
124
|
|
|
124
|
-
// 2) select
|
|
125
|
+
// 2) select:认领带标记问题,放过纯文字/plan-review(dsh 0.1.2 协议:
|
|
126
|
+
// 入参 { pendingInteraction },questions 直挂、带 answer()/cancel())
|
|
125
127
|
const markerDetail = buildPickMarker('pick-1', [0, 2]) + '\n\n请选择一张图'
|
|
126
|
-
const
|
|
128
|
+
const fakePending = (questions, extra) => ({
|
|
127
129
|
kind: 'question',
|
|
128
|
-
key: 'q:
|
|
130
|
+
key: 'q:x',
|
|
129
131
|
sessionId: 's1',
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
assert.equal(mod.selectPickChoice({
|
|
132
|
+
questions,
|
|
133
|
+
answer: async () => {},
|
|
134
|
+
cancel: async () => {},
|
|
135
|
+
...extra,
|
|
136
|
+
})
|
|
137
|
+
const withImage = fakePending([{ id: 'a', question: '选图', detail: markerDetail, options: [{ label: 'A' }, { label: 'B' }, { label: 'C' }] }], { key: 'q:1' })
|
|
138
|
+
const plain = fakePending([{ id: 'b', question: '纯文字', options: [{ label: 'X' }] }], { key: 'q:2', sessionId: 's2' })
|
|
139
|
+
const planReview = fakePending(
|
|
140
|
+
[{ id: 'c', question: '确认计划', detail: '计划', intent: { kind: 'plan-review', approve: '执行' }, options: [{ label: '执行' }] }],
|
|
141
|
+
{ key: 'q:3', kind: 'plan-review' },
|
|
142
|
+
)
|
|
143
|
+
assert.equal(mod.selectPickChoice({ pendingInteraction: plain }), null, '纯文字问题应放行')
|
|
144
|
+
assert.equal(mod.selectPickChoice({ pendingInteraction: planReview }), null, 'plan-review 应放行')
|
|
145
|
+
assert.equal(mod.selectPickChoice({ pendingInteraction: null }), null, '无 pendingInteraction 应放行')
|
|
146
|
+
assert.equal(mod.selectPickChoice({ pendingInteraction: withImage }), withImage, '带图问题应认领')
|
|
142
147
|
|
|
143
148
|
// 3) parseMarker 与服务端互通
|
|
144
149
|
const parsed = mod.parseMarker(markerDetail)
|
|
@@ -148,10 +153,12 @@ assert.deepEqual({ pickId: parsed.pickId, images: parsed.images, human: parsed.h
|
|
|
148
153
|
const { renderToString } = reactDomServer
|
|
149
154
|
const fakeT = (key) => ({ 'nav.cancel': '取消', 'action.skip': '跳过', 'action.next': '下一步', 'submit': '提交', 'option.recommended': '推荐', 'custom.placeholder': '输入答案', 'image.failed': '加载失败', 'image.zoom': '放大查看', 'image.close': '关闭' }[key] ?? key)
|
|
150
155
|
const fakeWait = {
|
|
156
|
+
kind: 'question',
|
|
151
157
|
key: 'q:1',
|
|
152
158
|
sessionId: 's1',
|
|
153
|
-
|
|
154
|
-
|
|
159
|
+
questions: [{ id: 'a', question: '选一张封面', header: '封面', detail: markerDetail, multiSelect: false, options: [{ label: 'A (Recommended)', description: '第一张' }, { label: 'B' }, { label: 'C' }] }],
|
|
160
|
+
answer: async () => {},
|
|
161
|
+
cancel: async () => {},
|
|
155
162
|
}
|
|
156
163
|
const html = renderToString(reactModule.createElement(mod.ImageChoiceComposer, { matched: fakeWait, t: fakeT }))
|
|
157
164
|
assert.ok(html.includes('dshpick-card'), '卡片未渲染')
|