dsh-quote-annotate-pro 0.2.2 → 0.2.3
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/README.en.md +1 -0
- package/README.md +1 -0
- package/lib/client.js +106 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
本项目的所有重要变更都记在这里。版本号遵循 [SemVer](https://semver.org/lang/zh-CN/)。
|
|
4
4
|
|
|
5
|
+
## 0.2.3 — 对话页里的引用也变紧凑(隐藏位置标签)
|
|
6
|
+
|
|
7
|
+
- **已发送消息里的引用**:原来显示 `【引用 · 第N轮 · 角色】` + `> 原文` 两行,现在只显示一个**紧凑的原文块**(最多 3 行、超出省略),**位置标签不再显示**
|
|
8
|
+
- **点击该引用块即跳回原文并高亮**,没有任何可见的"跳转"按钮
|
|
9
|
+
- **消息本体不变**:发送给模型的内容仍以 `【引用 · 第N轮 · 角色】` 开头 → "让模型知道你说的是哪一处"这一核心能力完整保留
|
|
10
|
+
- 输入框上方的引用块(0.2.2 引入)维持紧凑样式
|
|
11
|
+
|
|
5
12
|
## 0.2.2 — 引用界面改为紧凑式
|
|
6
13
|
|
|
7
14
|
- **输入框上方的引用块**:不再显示 `引用#N · 第N轮 · 角色` 这类标签,改为**只显示原文片段**(单行、超出省略);点一下即跳回原文并高亮,且**不再弹出"已跳转"提示**、取消常驻导航条 —— 输入区更干净
|
package/README.en.md
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
| Select & annotate | Select text in the transcript → a bubble appears above the selection → write a comment (optional) → `Enter` |
|
|
19
19
|
| Quote chip | An atomic `引用1` chip (short label) lands in the composer; it expands to the full quote block on send |
|
|
20
20
|
| **Quote block** | While a quote is in the composer, a compact block above it shows the **quoted text snippet** (single line, ellipsised). Click it to jump back to the source and highlight it — no visible "jump" affordance, so the composer stays clean |
|
|
21
|
+
| **Compact quotes in the transcript** | A sent message shows only a **compact quote block** (up to 3 lines) — the `【引用 · 第N轮 · 角色】` label is hidden. Clicking the block jumps back to the source and highlights it, while the message payload still carries the turn/role, so the model keeps the position context |
|
|
21
22
|
| **Turn-accurate context** | The quote block starts with `【引用 · 第3轮 · 你的消息】` followed by `> quoted text`, so the model knows exactly which turn and whose message |
|
|
22
23
|
| Turn-level quoting | Every finished turn gets `引用提问` / `引用回答` buttons that quote the whole question or answer |
|
|
23
24
|
| Anchors survive reload | The `ref → anchor` map is persisted in `localStorage` (per session, 60 entries max), so quotes still jump after a page refresh |
|
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
| 划词批注 | 会话里选中任意文字 → 选区上方浮出气泡(预览 + 「批注」)→ 编辑框里写评语(可留空)→ `Enter` 插入 |
|
|
19
19
|
| 引用 chip | 输入框里出现 `引用1` 的原子 chip(短标签);发送时展开为完整引用块 |
|
|
20
20
|
| **引用块(输入框上方)** | 有引用时在输入框上方显示**原文片段**(单行、超出省略);**点一下即跳回原文并高亮** —— 不做任何可见的"跳转"提示,保持输入区干净 |
|
|
21
|
+
| **对话页里的紧凑引用** | 已发送消息里的引用也只显示**紧凑原文块**(最多 3 行),**不显示"第N轮/谁说的"标签**;点该块可跳回原文。而**消息本体仍带位置信息**,模型照旧知道出处 |
|
|
21
22
|
| **模型侧精确定位** | 引用块首行是 `【引用 · 第5轮 · 你的消息】`,后接 `> 原文`,让模型明确知道**哪一轮、谁说的** |
|
|
22
23
|
| 回合级引用 | 每条已完成回合尾部有「引用提问 / 引用回答」,整段引用该轮 |
|
|
23
24
|
| 锚点持久化 | `ref → 锚点` 映射写入 `localStorage`(按会话,最多 60 条),**刷新页面后引用块仍能跳回原文** |
|
package/lib/client.js
CHANGED
|
@@ -991,6 +991,11 @@ window.__ModuleLoader__.load({
|
|
|
991
991
|
.dshq-quote-block{display:flex;align-items:center;min-width:0;max-width:100%;box-sizing:border-box;padding:5px 10px;border-radius:8px;background:var(--dsw-alias-bg-layer-2);background:color-mix(in oklab,var(--dsw-alias-brand-primary) 8%,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;cursor:pointer;transition:background .12s ease,color .12s ease}
|
|
992
992
|
.dshq-quote-block:hover{color:var(--dsw-alias-label-primary)}
|
|
993
993
|
.dshq-quote-block-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
994
|
+
/* 已发送消息里的引用:隐藏位置标签,只显示紧凑的原文块(点击可跳回原文,无可见引导) */
|
|
995
|
+
.dshq-sent-header{display:none}
|
|
996
|
+
.dshq-sent-quote{display:block;margin:3px 0;padding:6px 10px;border-radius:8px;background:rgba(127,127,127,.10);color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.5;cursor:pointer;overflow:hidden;transition:background .12s ease,color .12s ease}
|
|
997
|
+
.dshq-sent-quote:hover{color:var(--dsw-alias-label-primary)}
|
|
998
|
+
.dshq-sent-quote-text{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;white-space:pre-wrap;word-break:break-word}
|
|
994
999
|
`
|
|
995
1000
|
|
|
996
1001
|
// ============ 插件主体 ============
|
|
@@ -1224,6 +1229,107 @@ window.__ModuleLoader__.load({
|
|
|
1224
1229
|
} catch (e) {
|
|
1225
1230
|
report('apply:listeners-throw', String((e && e.stack) || (e && e.message) || e))
|
|
1226
1231
|
}
|
|
1232
|
+
|
|
1233
|
+
// ============ 已发送消息里的引用:把「【引用 · 第N轮 · 角色】+ > 原文」变成紧凑块 ============
|
|
1234
|
+
// 目标(用户要求):对话页面里不再显示"第几轮/谁说的"这类位置标签,只留一个紧凑的原文块;
|
|
1235
|
+
// 点击该块可跳回原文并高亮;**消息本体文本不变**,模型仍然拿得到完整的位置信息。
|
|
1236
|
+
try {
|
|
1237
|
+
ctx.effect(() => {
|
|
1238
|
+
let timer = null
|
|
1239
|
+
let observer = null
|
|
1240
|
+
const HEADER_RE = /【引用\s*·[^\n】]*】/
|
|
1241
|
+
const jumpByText = (text, pos) => {
|
|
1242
|
+
const probe = String(text || '').replace(/\s+/g, ' ').trim().slice(0, 24)
|
|
1243
|
+
if (probe === '') return
|
|
1244
|
+
for (const r of document.querySelectorAll('[data-chat-anchor-key]')) {
|
|
1245
|
+
const t = String(r.textContent || '').replace(/\s+/g, ' ')
|
|
1246
|
+
if (t.indexOf(probe) !== -1) {
|
|
1247
|
+
r.scrollIntoView({ block: 'center', behavior: 'smooth' })
|
|
1248
|
+
r.classList.add('dshq-flash')
|
|
1249
|
+
setTimeout(() => r.classList.remove('dshq-flash'), 1600)
|
|
1250
|
+
report('sent:jump', { anchorKey: r.getAttribute('data-chat-anchor-key') })
|
|
1251
|
+
return
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
notify('跳转失败:原文不在当前页面', pos, false)
|
|
1255
|
+
}
|
|
1256
|
+
const buildBlock = (quoteText) => {
|
|
1257
|
+
const wrap = document.createElement('div')
|
|
1258
|
+
wrap.className = 'dshq-sent-quote'
|
|
1259
|
+
wrap.setAttribute('role', 'button')
|
|
1260
|
+
wrap.setAttribute('title', '点击跳回原文')
|
|
1261
|
+
const span = document.createElement('span')
|
|
1262
|
+
span.className = 'dshq-sent-quote-text'
|
|
1263
|
+
span.textContent = quoteText
|
|
1264
|
+
wrap.appendChild(span)
|
|
1265
|
+
wrap.addEventListener('click', (ev) => {
|
|
1266
|
+
const r = wrap.getBoundingClientRect()
|
|
1267
|
+
jumpByText(quoteText, { left: r.left + 16, top: r.top })
|
|
1268
|
+
if (ev && ev.stopPropagation) ev.stopPropagation()
|
|
1269
|
+
})
|
|
1270
|
+
return wrap
|
|
1271
|
+
}
|
|
1272
|
+
/** 把承载「【引用…】\n> 原文…」的文本节点拆成:隐藏头部 + 紧凑引用块 + 其余文本 */
|
|
1273
|
+
const transformTextNode = (node) => {
|
|
1274
|
+
const raw = node.nodeValue || ''
|
|
1275
|
+
const m = HEADER_RE.exec(raw)
|
|
1276
|
+
if (!m) return false
|
|
1277
|
+
const quoteStart = m.index + m[0].length
|
|
1278
|
+
const nl = raw.indexOf('\n', quoteStart)
|
|
1279
|
+
if (nl < 0) return false
|
|
1280
|
+
const lines = raw.slice(nl + 1).split('\n')
|
|
1281
|
+
const quote = []
|
|
1282
|
+
let i = 0
|
|
1283
|
+
while (i < lines.length && /^>\s?/.test(lines[i])) { quote.push(lines[i].replace(/^>\s?/, '')); i++ }
|
|
1284
|
+
if (quote.length === 0) return false
|
|
1285
|
+
const rest = lines.slice(i).join('\n')
|
|
1286
|
+
const frag = document.createDocumentFragment()
|
|
1287
|
+
const before = raw.slice(0, m.index)
|
|
1288
|
+
if (before !== '') frag.appendChild(document.createTextNode(before))
|
|
1289
|
+
const hidden = document.createElement('span')
|
|
1290
|
+
hidden.className = 'dshq-sent-header'
|
|
1291
|
+
hidden.textContent = m[0] // 位置信息仍留在 DOM(只是不显示)
|
|
1292
|
+
frag.appendChild(hidden)
|
|
1293
|
+
frag.appendChild(buildBlock(quote.join(' ')))
|
|
1294
|
+
if (rest.trim() !== '') frag.appendChild(document.createTextNode(rest))
|
|
1295
|
+
node.parentNode.replaceChild(frag, node)
|
|
1296
|
+
report('sent:decorated', { chars: quote.join(' ').length })
|
|
1297
|
+
return true
|
|
1298
|
+
}
|
|
1299
|
+
const scan = () => {
|
|
1300
|
+
timer = null
|
|
1301
|
+
try {
|
|
1302
|
+
for (const row of document.querySelectorAll('[data-chat-anchor-key]')) {
|
|
1303
|
+
if (row.getAttribute('data-chat-flow-kind') !== 'user') continue
|
|
1304
|
+
if (row.dataset && row.dataset.dshqSent === '1') continue
|
|
1305
|
+
let walker
|
|
1306
|
+
try { walker = document.createTreeWalker(row, NodeFilter.SHOW_TEXT, null) } catch (e) { continue }
|
|
1307
|
+
const targets = []
|
|
1308
|
+
let n = walker.nextNode()
|
|
1309
|
+
while (n) {
|
|
1310
|
+
if (HEADER_RE.test(n.nodeValue || '')) targets.push(n)
|
|
1311
|
+
n = walker.nextNode()
|
|
1312
|
+
}
|
|
1313
|
+
let done = false
|
|
1314
|
+
for (const t of targets) { try { if (transformTextNode(t)) done = true } catch (e) { /* ignore */ } }
|
|
1315
|
+
if (done && row.dataset) row.dataset.dshqSent = '1'
|
|
1316
|
+
}
|
|
1317
|
+
} catch (e) { report('sent:scan-throw', String((e && e.message) || e)) }
|
|
1318
|
+
}
|
|
1319
|
+
const schedule = () => { if (timer === null) timer = setTimeout(scan, 300) }
|
|
1320
|
+
scan()
|
|
1321
|
+
try {
|
|
1322
|
+
observer = new MutationObserver(schedule)
|
|
1323
|
+
observer.observe(document.body, { childList: true, subtree: true })
|
|
1324
|
+
} catch (e) { report('sent:observer-throw', String((e && e.message) || e)) }
|
|
1325
|
+
return () => {
|
|
1326
|
+
if (timer !== null) clearTimeout(timer)
|
|
1327
|
+
if (observer) { try { observer.disconnect() } catch (e) { /* ignore */ } }
|
|
1328
|
+
}
|
|
1329
|
+
}, 'dsh-quote-annotate-pro: sent quote decoration')
|
|
1330
|
+
} catch (e) {
|
|
1331
|
+
report('apply:sent-decor-throw', String((e && e.message) || e))
|
|
1332
|
+
}
|
|
1227
1333
|
const tryRegister = (tag, fn) => {
|
|
1228
1334
|
try { fn(); report('register:ok', tag); return true } catch (e) {
|
|
1229
1335
|
report('register:throw', tag + ' :: ' + String((e && e.stack) || (e && e.message) || e))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-quote-annotate-pro",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "选区引用与锚点批注(增强 fork):划词 → 批注 → 带精确锚点的引用;输入框上方只显示原文片段,点一下跳回原文;发送时把轮次/角色写入引用块,让模型精确知道你指的是哪一处。",
|
|
5
5
|
"keywords": ["dsh", "dsh-plugin", "deepseek-harness", "quote", "annotate", "selection", "reference", "anchor"],
|
|
6
6
|
"license": "MIT",
|