dsh-recall-plugin 1.7.0 → 1.7.1
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 +6 -0
- package/lib/client.js +18 -55
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
本文件格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循语义化版本。
|
|
4
4
|
|
|
5
|
+
## [1.7.1] - 2026-08-25
|
|
6
|
+
|
|
7
|
+
### 修复
|
|
8
|
+
|
|
9
|
+
- 历史会话中用户消息图片从未渲染([#9](https://github.com/limbo947/dsh-recall-plugin/issues/9)):插件渲染器读取的 `props.loadImage` 在官方 `conversation.chat.node` slot 契约中**从不存在**(实际入口是 `props.renderMessageImages`),自研加载链在守卫处直接 return——v1.6.2 的重试链、v1.7.0 的失败按钮全部从未执行,图片永久无声空白(v1.6.2/#8 的修复因此「修了却无效」)。用户消息图片改走官方 `renderMessageImages` 管线(自带鉴权、缓存、失败重试与灯箱预览),布局对齐官方(图片在上、气泡在下);自研 `ImageBox`/`useImageSrc` 及对应 CSS 作为死代码移除。
|
|
10
|
+
|
|
5
11
|
## [1.7.0] - 2026-08-25
|
|
6
12
|
|
|
7
13
|
### 新增
|
package/lib/client.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* dsh-recall-plugin — Client 半(持久插件形态,浏览器 bundle)
|
|
3
3
|
*
|
|
4
4
|
* 职责:注册 conversation.chat.node 的 user 渲染器,重绘用户消息气泡
|
|
5
|
-
* (文本/图片/JSON
|
|
5
|
+
* (文本/图片/JSON 块;图片经官方 props.renderMessageImages 管线渲染,
|
|
6
|
+
* 见 UserRecallNode 内注释),在复制按钮旁加「撤回」按钮;二次确认面板展示
|
|
6
7
|
* 文件变更清单,确认后经 /api/recall/* 调用 Host 回退文件,并用官方
|
|
7
8
|
* sessions.fork 把对话一并回退(新会话打开、原会话归档)。
|
|
8
9
|
*
|
|
@@ -38,9 +39,6 @@ window.__ModuleLoader__.load({
|
|
|
38
39
|
'.dsh-recall-row{flex-direction:column;align-items:flex-end;gap:6px;display:flex}',
|
|
39
40
|
'.dsh-recall-stack{flex-direction:column;align-items:flex-end;gap:8px;min-width:0;max-width:min(525px,82%);display:flex}',
|
|
40
41
|
'.dsh-recall-bubble{background:var(--dsw-specific-bubble);max-width:100%;color:var(--dsw-alias-label-primary);border-radius:22px;padding:10px 16px;font-size:16px;line-height:24px;white-space:pre-wrap;word-break:break-word}',
|
|
41
|
-
'.dsh-recall-img{max-width:100%;max-height:320px;border-radius:12px;object-fit:contain}',
|
|
42
|
-
'.dsh-recall-img-failed{border:1px dashed var(--dsw-alias-border-l2);border-radius:12px;padding:8px 14px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0}',
|
|
43
|
-
'.dsh-recall-img-failed:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}',
|
|
44
42
|
'.dsh-recall-json{margin:0;max-width:100%;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);white-space:pre-wrap;word-break:break-word;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:8px 10px;background:var(--dsw-alias-markdown-code-block)}',
|
|
45
43
|
'.dsh-recall-actions{align-items:center;gap:10px;height:28px;display:flex}',
|
|
46
44
|
'.dsh-recall-time{color:var(--dsw-alias-label-tertiary);white-space:nowrap;padding-right:12px;font-size:14px;line-height:24px}',
|
|
@@ -268,52 +266,6 @@ window.__ModuleLoader__.load({
|
|
|
268
266
|
)
|
|
269
267
|
}
|
|
270
268
|
|
|
271
|
-
// 图片加载失败的暂态判定(issue #8):会话回放时最早的图片消息先于
|
|
272
|
-
// 会话 binding 就绪渲染,loadImage 以 unknown session 拒绝——这类
|
|
273
|
-
// 失败靠短退避自动重试即可自愈;此前 `.catch(() => {})` 把失败静默
|
|
274
|
-
// 吞掉且 src 恒为 null,图片永久空白且刷新无法恢复。自动重试耗尽
|
|
275
|
-
// 后置 failed,由 ImageBox 提供「点击重试」(对齐官方 MessageImage
|
|
276
|
-
// 的失败态语义),attempt 仅被手动重试递增以复用整条加载链。
|
|
277
|
-
const IMAGE_RETRY_DELAYS = [400, 1500, 4000]
|
|
278
|
-
|
|
279
|
-
function useImageSrc(attachment, loadImage) {
|
|
280
|
-
const [src, setSrc] = React.useState(null)
|
|
281
|
-
const [failed, setFailed] = React.useState(false)
|
|
282
|
-
const [attempt, setAttempt] = React.useState(0)
|
|
283
|
-
React.useEffect(() => {
|
|
284
|
-
let alive = true
|
|
285
|
-
let timer = null
|
|
286
|
-
if (!attachment || typeof loadImage !== 'function') return undefined
|
|
287
|
-
// 复位旧状态:attachment 变化或手动重试时清掉上一轮的 src/failed,
|
|
288
|
-
// 避免加载期间短暂显示上一张图或残留的失败态
|
|
289
|
-
setSrc(null)
|
|
290
|
-
setFailed(false)
|
|
291
|
-
const tryLoad = (retried) => {
|
|
292
|
-
Promise.resolve(loadImage(attachment)).then((url) => {
|
|
293
|
-
if (!alive) return
|
|
294
|
-
if (url) setSrc(String(url))
|
|
295
|
-
else if (retried < IMAGE_RETRY_DELAYS.length) timer = setTimeout(() => tryLoad(retried + 1), IMAGE_RETRY_DELAYS[retried])
|
|
296
|
-
else setFailed(true)
|
|
297
|
-
}).catch(() => {
|
|
298
|
-
if (!alive) return
|
|
299
|
-
if (retried < IMAGE_RETRY_DELAYS.length) timer = setTimeout(() => tryLoad(retried + 1), IMAGE_RETRY_DELAYS[retried])
|
|
300
|
-
else setFailed(true)
|
|
301
|
-
})
|
|
302
|
-
}
|
|
303
|
-
tryLoad(0)
|
|
304
|
-
return () => { alive = false; if (timer) clearTimeout(timer) }
|
|
305
|
-
}, [attachment, loadImage, attempt])
|
|
306
|
-
return { src, failed, retry: () => setAttempt(attempt + 1) }
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function ImageBox(props) {
|
|
310
|
-
const { src, failed, retry } = useImageSrc(props.attachment, props.loadImage)
|
|
311
|
-
if (src) return React.createElement('img', { className: 'dsh-recall-img', src, alt: '' })
|
|
312
|
-
// 加载中保持原零占位语义(不撑气泡);只在确定失败后渲染可重试入口
|
|
313
|
-
if (failed) return React.createElement('button', { type: 'button', className: 'dsh-recall-img-failed', onClick: retry }, '图片加载失败,点击重试')
|
|
314
|
-
return null
|
|
315
|
-
}
|
|
316
|
-
|
|
317
269
|
// kind 语义单表承载(文案/徽章类名/汇总顺序):新增 kind 时只改
|
|
318
270
|
// 这一处,避免出现「映射表改了、CSS 类名忘了」的半改状态
|
|
319
271
|
const KIND_INFO = {
|
|
@@ -404,14 +356,21 @@ window.__ModuleLoader__.load({
|
|
|
404
356
|
|
|
405
357
|
function UserRecallNode(props) {
|
|
406
358
|
const node = props && props.node
|
|
407
|
-
|
|
359
|
+
// 图片渲染入口:官方 ChatNodeSeat 传给本 slot 的 props 契约只有
|
|
360
|
+
// renderMessageImages(内部经 conversation.message.images slot 渲染
|
|
361
|
+
// 官方 MessageImages,自带鉴权、缓存、失败重试与灯箱预览)。
|
|
362
|
+
// 契约里从不存在 loadImage(issue #9):v1.6/v1.7 读该不存在的
|
|
363
|
+
// 字段导致自研加载链从未执行、用户消息图片永久无声空白。
|
|
364
|
+
const renderMessageImages = props && props.renderMessageImages
|
|
408
365
|
const sessionId = props && props.sessionId
|
|
409
366
|
const data = node && node.data ? node.data : {}
|
|
410
367
|
// node.id 是会话事件匹配时写入的真实消息 ID;node.key 是位置键(如 13:input),不能用于快照查询
|
|
411
368
|
const messageId = node ? String(node.id || node.key || '') : ''
|
|
412
369
|
const blocks = Array.isArray(data.content) ? data.content : []
|
|
413
370
|
const text = blocks.filter((b) => b && b.type === 'text' && typeof b.text === 'string').map((b) => b.text).join('')
|
|
414
|
-
|
|
371
|
+
// 官方契约:images 传 image 块数组({attachment}),官方内部取
|
|
372
|
+
// image.attachment.attachmentId —— 不是裸 attachment 对象
|
|
373
|
+
const imageBlocks = blocks.filter((b) => b && b.type === 'image' && b.attachment).map((b) => ({ attachment: b.attachment }))
|
|
415
374
|
const rest = blocks.filter((b) => !b || !(b.type === 'text' && typeof b.text === 'string') && !(b.type === 'image' && b.attachment))
|
|
416
375
|
|
|
417
376
|
const [copied, setCopied] = React.useState(false)
|
|
@@ -597,10 +556,14 @@ window.__ModuleLoader__.load({
|
|
|
597
556
|
}
|
|
598
557
|
|
|
599
558
|
const bubbleChildren = []
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
559
|
+
// 图片在上、气泡在下:布局顺序对齐官方 UserStyleBubble(历史会话
|
|
560
|
+
// 里用户消息的观感与官方一致)。Fragment 包一层仅为挂 key。
|
|
561
|
+
if (imageBlocks.length && typeof renderMessageImages === 'function') {
|
|
562
|
+
bubbleChildren.push(React.createElement(React.Fragment, { key: 'images' },
|
|
563
|
+
renderMessageImages({ images: imageBlocks, align: 'end' })
|
|
564
|
+
))
|
|
603
565
|
}
|
|
566
|
+
if (text !== '') bubbleChildren.push(React.createElement('div', { className: 'dsh-recall-bubble', key: 'text' }, text))
|
|
604
567
|
for (let i = 0; i < rest.length; i++) {
|
|
605
568
|
bubbleChildren.push(React.createElement('pre', { className: 'dsh-recall-json', key: 'rest-' + i }, JSON.stringify(rest[i], null, 2)))
|
|
606
569
|
}
|