dsh-quote-annotate-pro 0.3.0 → 0.3.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 +11 -0
- package/README.en.md +1 -0
- package/lib/client.js +56 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
本项目的所有重要变更都记在这里。版本号遵循 [SemVer](https://semver.org/lang/zh-CN/)。
|
|
4
4
|
|
|
5
|
+
## 0.3.1 — 引用管线懒重试:页面刷新后不再退化成纯文本
|
|
6
|
+
|
|
7
|
+
- **根因**:页面刷新时插件 `apply` 早于宿主 `inputTriggers` 服务就绪,`ctx.get('inputTriggers')` 拿到空值后
|
|
8
|
+
`pipelineAvailable` 被永久置为 `false` → 之后每次批注插入都走「纯文本回退」,输入框里出现 `【批注 1 …】` 裸文本块
|
|
9
|
+
(实测证据:localStorage 草稿记录为 `{"draft":{"text":"【批注 1 · …】\n钟)\n\n批注:1…","references":[]}}`,即没有 chip)
|
|
10
|
+
- **修复**:引用源注册改为幂等懒加载 `ensureQuoteSource()`,服务未就绪时按 500ms × 20 次(≈10s)自动重试,
|
|
11
|
+
每次插入前也会先试一次;重试成功即恢复正常 chip 插入,无需用户刷新页面或重启
|
|
12
|
+
- **可观测性**:`insert:draft` 诊断补充 `draftLen` / `pipeline`;`insert:fallback-plaintext` 补充 `draftLen` / `bodyLen` / `pipeline`;
|
|
13
|
+
编辑器提示按状态区分(`null` = 正在初始化引用管线,`false` = 回退为纯文本)
|
|
14
|
+
- 自检照旧:`npm test`(8 组纯函数断言)、`node --check`、`check-manifest` 全绿
|
|
15
|
+
|
|
5
16
|
## 0.3.0 — 编号批注 + 回复逐条对照
|
|
6
17
|
|
|
7
18
|
- **批注自动编号**:同一条消息里每次插入批注都会编号(`批注 1`、`批注 2`…,删掉中间某条不会重号,见 `nextAnnotationNo()`);
|
package/README.en.md
CHANGED
|
@@ -104,6 +104,7 @@ Official surfaces used: `slots` (`shell.overlay`, `conversation.chat.turnTail`,
|
|
|
104
104
|
|
|
105
105
|
```sh
|
|
106
106
|
npm run check # manifest self-check (package.json / patch / bundle registration id)
|
|
107
|
+
npm test # pure-function self-check for the client bundle (numbering, block parsing, snippets — no browser/network)
|
|
107
108
|
npm run diag # local diagnostics sink (http://127.0.0.1:47821)
|
|
108
109
|
```
|
|
109
110
|
|
package/lib/client.js
CHANGED
|
@@ -215,6 +215,8 @@ window.__ModuleLoader__.load({
|
|
|
215
215
|
let currentSessionId = null
|
|
216
216
|
let refSeq = 0
|
|
217
217
|
let pipelineAvailable = null // null=未知 false=不可用 true=可用
|
|
218
|
+
// 引用 source 的注册函数(懒加载 + 重试):服务晚就绪、HMR 重载后仍能恢复 chip 插入
|
|
219
|
+
let ensureQuoteSource = null
|
|
218
220
|
// ref -> { sessionId, anchorKey, body, label } 用于点击 chip 跳回原文 / 悬停显示原文
|
|
219
221
|
const refTargets = new Map()
|
|
220
222
|
// ref -> { body, clipboardText } 提交时经 codec 序列化
|
|
@@ -910,6 +912,8 @@ window.__ModuleLoader__.load({
|
|
|
910
912
|
refCodecs.delete(oldest)
|
|
911
913
|
refTargets.delete(oldest)
|
|
912
914
|
}
|
|
915
|
+
// 懒重试:若 apply 时 inputTriggers 还没就绪,插入前再试一次(常见于"刚启动就点批注")
|
|
916
|
+
try { if (typeof ensureQuoteSource === 'function') ensureQuoteSource() } catch (e) { /* ignore */ }
|
|
913
917
|
// 事件派发目标:优先用 sessions.scope()(社区插件在本版本已验证的路径),退回 binding.ctx
|
|
914
918
|
let emitCtx = binding.ctx
|
|
915
919
|
try {
|
|
@@ -929,10 +933,13 @@ window.__ModuleLoader__.load({
|
|
|
929
933
|
try {
|
|
930
934
|
const snap2 = input.state.getSnapshot()
|
|
931
935
|
const occ2 = (snap2 && snap2.occurrences) || []
|
|
936
|
+
const draft2 = String((snap2 && snap2.draft) || '')
|
|
932
937
|
report('insert:draft', {
|
|
933
|
-
draft:
|
|
938
|
+
draft: draft2.replace(/\s+/g, ' ').slice(0, 400),
|
|
939
|
+
draftLen: draft2.length,
|
|
934
940
|
occ: occ2.length,
|
|
935
941
|
occLabels: occ2.map((o) => (o && o.label) || '').join('|').slice(0, 80),
|
|
942
|
+
pipeline: pipelineAvailable,
|
|
936
943
|
})
|
|
937
944
|
} catch (e) { /* ignore */ }
|
|
938
945
|
report('insert:chip', { ref, anchorKey: item.anchorKey || null, header, chars: item.text.length })
|
|
@@ -950,7 +957,9 @@ window.__ModuleLoader__.load({
|
|
|
950
957
|
refTargets.delete(ref)
|
|
951
958
|
refCodecs.delete(ref)
|
|
952
959
|
input.setDraft(base === '' ? quoteBody : base + '\n\n' + quoteBody)
|
|
953
|
-
|
|
960
|
+
let draftLen = 0
|
|
961
|
+
try { draftLen = String((input.state.getSnapshot() || {}).draft || '').length } catch (e) { /* ignore */ }
|
|
962
|
+
report('insert:fallback-plaintext', { ref, header, draftLen, bodyLen: quoteBody.length, pipeline: pipelineAvailable })
|
|
954
963
|
setStatus({ ok: false, text: 'chip 插入未生效,已回退为纯文本引用' })
|
|
955
964
|
editorStore.close()
|
|
956
965
|
} catch (e) {
|
|
@@ -1183,9 +1192,21 @@ window.__ModuleLoader__.load({
|
|
|
1183
1192
|
available: pipelineAvailable,
|
|
1184
1193
|
hasInsertReference: !!(inputTriggers && typeof inputTriggers.insertReference === 'function'),
|
|
1185
1194
|
})
|
|
1186
|
-
|
|
1195
|
+
// 注册机:幂等、可重试。宿主服务可能晚于插件就绪(或 HMR 重载后旧注册仍在),
|
|
1196
|
+
// 一次性判定会让整页退化成"纯文本回退",因此这里允许后续再次调用。
|
|
1197
|
+
let quoteSourceRegistered = false
|
|
1198
|
+
ensureQuoteSource = () => {
|
|
1199
|
+
if (quoteSourceRegistered) return true
|
|
1200
|
+
let triggers = inputTriggers
|
|
1201
|
+
if (!triggers) {
|
|
1202
|
+
try { triggers = ctx.get('inputTriggers') } catch (e) { triggers = undefined }
|
|
1203
|
+
}
|
|
1204
|
+
if (!triggers || typeof triggers.registerSource !== 'function') {
|
|
1205
|
+
pipelineAvailable = false
|
|
1206
|
+
return false
|
|
1207
|
+
}
|
|
1187
1208
|
try {
|
|
1188
|
-
ctx.effect(() =>
|
|
1209
|
+
ctx.effect(() => triggers.registerSource({
|
|
1189
1210
|
trigger: '@',
|
|
1190
1211
|
name: 'quote-ref',
|
|
1191
1212
|
order: 100,
|
|
@@ -1196,12 +1217,42 @@ window.__ModuleLoader__.load({
|
|
|
1196
1217
|
serialize: (ref) => Promise.resolve(refCodecs.get(ref) ? refCodecs.get(ref).body : ''),
|
|
1197
1218
|
},
|
|
1198
1219
|
}), 'dsh-quote-annotate-pro: quote-ref source')
|
|
1220
|
+
quoteSourceRegistered = true
|
|
1221
|
+
pipelineAvailable = true
|
|
1199
1222
|
report('register:quote-ref-source', 'ok')
|
|
1223
|
+
return true
|
|
1200
1224
|
} catch (e) {
|
|
1225
|
+
const msg = String((e && e.stack) || (e && e.message) || e)
|
|
1226
|
+
// 已注册(HMR 重载后宿主保留旧注册)不算失败:序列化仍会路由到当前模块的 codec
|
|
1227
|
+
if (/already|duplicate|exist|注册/i.test(msg)) {
|
|
1228
|
+
quoteSourceRegistered = true
|
|
1229
|
+
pipelineAvailable = true
|
|
1230
|
+
report('register:quote-ref-source', 'already')
|
|
1231
|
+
return true
|
|
1232
|
+
}
|
|
1201
1233
|
pipelineAvailable = false
|
|
1202
|
-
report('register:quote-ref-source', 'throw ' +
|
|
1234
|
+
report('register:quote-ref-source', 'throw ' + msg)
|
|
1235
|
+
return false
|
|
1203
1236
|
}
|
|
1204
1237
|
}
|
|
1238
|
+
if (!ensureQuoteSource()) {
|
|
1239
|
+
// 退避重试:最多 20 次、间隔 500ms(约 10s),把启动时序造成的退化救回来
|
|
1240
|
+
try {
|
|
1241
|
+
ctx.effect(() => {
|
|
1242
|
+
let tries = 0
|
|
1243
|
+
let timer = null
|
|
1244
|
+
const tick = () => {
|
|
1245
|
+
timer = null
|
|
1246
|
+
if (quoteSourceRegistered || tries >= 20) return
|
|
1247
|
+
tries += 1
|
|
1248
|
+
if (!ensureQuoteSource()) timer = setTimeout(tick, 500)
|
|
1249
|
+
}
|
|
1250
|
+
timer = setTimeout(tick, 500)
|
|
1251
|
+
report('register:quote-ref-source', 'retry-scheduled')
|
|
1252
|
+
return () => { if (timer !== null) clearTimeout(timer) }
|
|
1253
|
+
}, 'dsh-quote-annotate-pro: quote-ref retry')
|
|
1254
|
+
} catch (e) { /* ignore */ }
|
|
1255
|
+
}
|
|
1205
1256
|
// 全局监听(选区气泡 / chip 点击跳转 / chip 悬停提示;ctx.effect 保证卸载时全部清理)
|
|
1206
1257
|
try {
|
|
1207
1258
|
ctx.effect(() => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-quote-annotate-pro",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "划词批注与引用锚点:划词 → 批注(自动编号)→ 引用;引用块在输入框和已发送消息里始终可见(最多 3 行、位置行隐藏),点一下跳回原文并高亮;模型按「Annotation N:」逐条回应,回复里的标签可悬浮看原文与评语。纯客户端,锚点存本地 localStorage。",
|
|
5
5
|
"keywords": ["dsh", "dsh-plugin", "deepseek-harness", "quote", "annotate", "selection", "reference", "anchor"],
|
|
6
6
|
"license": "MIT",
|