@oadank/dsh-input-tools 0.3.11 → 0.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +44 -7
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -32,6 +32,10 @@ window.__ModuleLoader__.load({
|
|
|
32
32
|
|
|
33
33
|
// ── 附件槽桥:官方 onAddImages 由 attachments 槽组件挂载时存入,left 按钮调用 ──
|
|
34
34
|
let sharedOnAddImages = null;
|
|
35
|
+
// [2026-08-21] draft 图片共享:attachments 槽挂载时把当前 draft 图(ComposerAttachment[])
|
|
36
|
+
// 与移除回调存入模块级,语音发送时可一起带上、发完清掉(解决"选了图发语音图被留下")。
|
|
37
|
+
let sharedDraftImages = [];
|
|
38
|
+
let sharedRemoveImage = null;
|
|
35
39
|
|
|
36
40
|
// ── [2026-08-21] 语音气泡(聊天界面 DOM 注入,安装即用,不依赖 dsh 源码支持)────
|
|
37
41
|
// 录音 → 存服务器(/voice/outbox/save)→ ASR 转文本 → 发【用户语音】标记文本;
|
|
@@ -158,13 +162,38 @@ window.__ModuleLoader__.load({
|
|
|
158
162
|
if (voiceErrorTimerRef.current !== null) window.clearTimeout(voiceErrorTimerRef.current);
|
|
159
163
|
voiceErrorTimerRef.current = window.setTimeout(() => setVoiceError(null), 6000);
|
|
160
164
|
};
|
|
161
|
-
|
|
165
|
+
// [2026-08-21] draft 图片转 image content(File→base64),语音可与图片一起发送
|
|
166
|
+
const draftImageContents = async () => {
|
|
167
|
+
const imgs = Array.isArray(sharedDraftImages) ? sharedDraftImages : [];
|
|
168
|
+
const out = [];
|
|
169
|
+
for (const a of imgs) {
|
|
170
|
+
const file = a?.file;
|
|
171
|
+
if (!file) continue;
|
|
172
|
+
const b64 = await new Promise((resolve) => {
|
|
173
|
+
const r = new FileReader();
|
|
174
|
+
r.onload = () => resolve(String(r.result).split(",")[1] ?? "");
|
|
175
|
+
r.onerror = () => resolve("");
|
|
176
|
+
r.readAsDataURL(file);
|
|
177
|
+
});
|
|
178
|
+
if (b64 !== "") out.push({ type: "image", mediaType: file.type || "image/jpeg", data: b64, name: file.name });
|
|
179
|
+
}
|
|
180
|
+
return out;
|
|
181
|
+
};
|
|
182
|
+
// [2026-08-21] 语音发送成功后清掉 draft 图片(否则图还留在输入框上)
|
|
183
|
+
const clearDraftImages = () => {
|
|
184
|
+
const imgs = Array.isArray(sharedDraftImages) ? sharedDraftImages : [];
|
|
185
|
+
if (typeof sharedRemoveImage === "function") {
|
|
186
|
+
for (const a of imgs) { try { sharedRemoveImage(a.id); } catch { /* ignore */ } }
|
|
187
|
+
}
|
|
188
|
+
sharedDraftImages = [];
|
|
189
|
+
};
|
|
190
|
+
const sendAsText = async (text, images) => {
|
|
162
191
|
// [2026-08-21] 降级路径:XDN(npm rc.7) 不支持 voice content。带【用户语音】标记让 AI
|
|
163
192
|
// 知道这是语音转的文本,可以按规则(自动 TTS)回复。
|
|
164
193
|
const marked = "【用户语音】" + text;
|
|
165
194
|
const response = await connection.api.sessions.prompt({
|
|
166
195
|
sessionId, mode: "queue",
|
|
167
|
-
content: [{ type: "text", text: marked }],
|
|
196
|
+
content: [{ type: "text", text: marked }, ...images],
|
|
168
197
|
});
|
|
169
198
|
const result = response?.result;
|
|
170
199
|
if (!result || !result.ok) {
|
|
@@ -172,25 +201,26 @@ window.__ModuleLoader__.load({
|
|
|
172
201
|
? result.error.message : "语音发送失败,请重试");
|
|
173
202
|
}
|
|
174
203
|
};
|
|
175
|
-
const sendAsVoice = async () => {
|
|
204
|
+
const sendAsVoice = async (images) => {
|
|
176
205
|
// 首选:多模态直发(AI 能听原音,消息渲染为语音气泡)——本机 lecoo / dev rc.8 支持
|
|
177
206
|
const response = await connection.api.sessions.prompt({
|
|
178
207
|
sessionId, mode: "queue",
|
|
179
|
-
content: [{ type: "voice", mediaType, data }],
|
|
208
|
+
content: [{ type: "voice", mediaType, data }, ...images],
|
|
180
209
|
});
|
|
181
210
|
return response?.result;
|
|
182
211
|
};
|
|
183
212
|
try {
|
|
213
|
+
const images = await draftImageContents();
|
|
184
214
|
// [2026-08-21 修] 先直发 voice,失败时降级 ASR 转文本(rc.7 兼容)。
|
|
185
215
|
// 这样本机/rc.8 享受多模态(AI 听到原音 + 语音消息气泡),XDN/rc.7 自动降级不报错。
|
|
186
216
|
let result;
|
|
187
217
|
try {
|
|
188
|
-
result = await sendAsVoice();
|
|
218
|
+
result = await sendAsVoice(images);
|
|
189
219
|
} catch (voiceErr) {
|
|
190
220
|
result = null;
|
|
191
221
|
}
|
|
192
222
|
// 直发成功(rc.8/dev):result.ok true
|
|
193
|
-
if (result && result.ok) return;
|
|
223
|
+
if (result && result.ok) { clearDraftImages(); return; }
|
|
194
224
|
// 失败或不支持:尝试降级
|
|
195
225
|
const errMsg = result?.error?.message ?? "";
|
|
196
226
|
// 只有"contract/payload"类错误才降级;其他业务错误直接提示
|
|
@@ -216,7 +246,8 @@ window.__ModuleLoader__.load({
|
|
|
216
246
|
if (!td?.ok) { fail(td?.error ?? "语音识别失败,请检查 ASR 配置"); return; }
|
|
217
247
|
const text = typeof td?.text === "string" ? td.text.trim() : "";
|
|
218
248
|
if (text === "") { fail("没听清,请再说一次"); return; }
|
|
219
|
-
await sendAsText(text);
|
|
249
|
+
await sendAsText(text, images);
|
|
250
|
+
clearDraftImages();
|
|
220
251
|
} catch (e) {
|
|
221
252
|
fail(String(e && typeof e.message === "string" && e.message !== "" ? e.message : e));
|
|
222
253
|
}
|
|
@@ -318,6 +349,12 @@ window.__ModuleLoader__.load({
|
|
|
318
349
|
if (typeof onAddImages === "function") sharedOnAddImages = onAddImages;
|
|
319
350
|
}, [onAddImages]);
|
|
320
351
|
|
|
352
|
+
// [2026-08-21] draft 图同步到模块级(语音发送一起带 + 发完清掉)
|
|
353
|
+
useEffect(() => {
|
|
354
|
+
sharedDraftImages = Array.isArray(attachments) ? attachments : [];
|
|
355
|
+
if (typeof onRemoveImage === "function") sharedRemoveImage = onRemoveImage;
|
|
356
|
+
}, [attachments, onRemoveImage]);
|
|
357
|
+
|
|
321
358
|
useEffect(() => {
|
|
322
359
|
if (zoom !== null && !items.some((a) => a.id === zoom.id)) setZoom(null);
|
|
323
360
|
}, [items, zoom]);
|