dsh-sidenote 0.8.3 → 0.8.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.
Files changed (33) hide show
  1. package/lib/client.js +375 -160
  2. package/lib/index.js +5 -1
  3. package/lib/types/client/annotate/anchor.d.ts +2 -2
  4. package/lib/types/client/annotate/overlay.d.ts +1 -0
  5. package/lib/types/client/annotate/selection.d.ts +6 -14
  6. package/lib/types/client/annotations/format.d.ts +1 -1
  7. package/lib/types/client/annotations/model.d.ts +10 -6
  8. package/lib/types/client/annotations/source.d.ts +5 -0
  9. package/lib/types/client/host/dom.d.ts +5 -1
  10. package/lib/types/client/host/replyDefaults.d.ts +19 -0
  11. package/lib/types/client/locales.d.ts +2 -0
  12. package/lib/types/client/preferences.d.ts +6 -3
  13. package/lib/types/client/sidechat/context.d.ts +1 -0
  14. package/lib/types/settings.d.ts +4 -0
  15. package/package.json +1 -1
  16. package/src/client/annotate/anchor.ts +32 -22
  17. package/src/client/annotate/annotate.module.css +10 -5
  18. package/src/client/annotate/overlay.tsx +89 -51
  19. package/src/client/annotate/selection.ts +26 -30
  20. package/src/client/annotations/format.ts +7 -3
  21. package/src/client/annotations/model.ts +50 -16
  22. package/src/client/annotations/send.ts +11 -2
  23. package/src/client/annotations/source.ts +56 -8
  24. package/src/client/host/dom.ts +14 -1
  25. package/src/client/host/replyDefaults.ts +38 -0
  26. package/src/client/host/submission.ts +9 -2
  27. package/src/client/index.tsx +8 -2
  28. package/src/client/locales.ts +4 -0
  29. package/src/client/preferences.ts +19 -14
  30. package/src/client/settings.tsx +9 -0
  31. package/src/client/sidechat/ContextProgress.tsx +1 -1
  32. package/src/client/sidechat/context.ts +3 -2
  33. package/src/settings.ts +2 -0
package/lib/client.js CHANGED
@@ -330,7 +330,18 @@ window.__ModuleLoader__.load({
330
330
  /** Internal conversation DOM seams, validated by the pinned host journeys.
331
331
  * Main message markers and composer seats have no public replacement API. */
332
332
  const MAIN_ASSISTANT = "[data-chat-flow-kind=\"assistant-step\"]";
333
- const NON_QUOTE = "button, [role=\"button\"], input, textarea, select, [contenteditable=\"true\"], [data-code-block-banner], [data-variant=\"think\"], [data-sidenote-no-quote]";
333
+ const NON_QUOTE = "button, [role=\"button\"], input, textarea, select, [contenteditable=\"true\"], [data-code-block-banner], [data-variant=\"think\"], [data-chat-group-part=\"reasoning\"], [data-sidenote-no-quote]";
334
+ const PROCESS_UI = "[data-variant=\"think\"], [data-chat-group-part=\"reasoning\"], [data-chat-flow-kind=\"tool-call\"], [data-chat-flow-kind=\"tool-result\"], [data-chat-flow-kind=\"turn-process\"], [data-context-kind=\"thinking\"], [data-context-kind=\"tool\"]";
335
+ /** Collapsed process rows can remain mounted, including during their animation. */
336
+ function quoteNodeVisible(node) {
337
+ for (let element = elementOf(node); element !== null; element = element.parentElement) {
338
+ if (element.matches("[hidden], [data-turn-process-hidden], [data-turn-process-inline]")) return false;
339
+ if (element instanceof HTMLDetailsElement && !element.open && !element.querySelector(":scope > summary")?.contains(node)) return false;
340
+ const style = getComputedStyle(element);
341
+ if (style.display === "none" || style.visibility === "hidden") return false;
342
+ }
343
+ return true;
344
+ }
334
345
  function nativeAssistantBodies(root = document) {
335
346
  return [...root.querySelectorAll(MAIN_ASSISTANT)];
336
347
  }
@@ -349,6 +360,7 @@ window.__ModuleLoader__.load({
349
360
  view: conversation ?? document.body,
350
361
  anchor,
351
362
  key: anchor.dataset.chatAnchorKey,
363
+ turn: body.closest("[data-chat-turn]")?.dataset.chatTurn,
352
364
  sessionId,
353
365
  surface: "main",
354
366
  streaming: body.hasAttribute("data-streaming") || body.querySelector("[data-streaming]") !== null
@@ -659,7 +671,8 @@ window.__ModuleLoader__.load({
659
671
  const inbox = inboxFaceOf(binding);
660
672
  const nodes = () => chat.getLegacy()?.nodes ?? [];
661
673
  const maxSeq = Math.max(-1, ...nodes().map((node) => node.seq ?? -1));
662
- const queueIds = new Set(queuedMessages(binding, inbox).map((message) => message.messageId ?? message.id));
674
+ const queuedBefore = queuedMessages(binding, inbox);
675
+ const queueIds = new Set(queuedBefore.map((message) => message.messageId ?? message.id));
663
676
  const previousError = binding.session.getSnapshot().promptError;
664
677
  const previousNoticeSeq = input.notices?.getSnapshot()?.seq ?? -1;
665
678
  const off = [];
@@ -695,7 +708,10 @@ window.__ModuleLoader__.load({
695
708
  };
696
709
  const check = () => {
697
710
  if (!submitted || settled) return;
698
- if (nodes().some((node) => (node.kind === "user" || node.kind === "steering") && (node.seq ?? -1) > maxSeq && matches(node)) || queuedMessages(binding, inbox).some((message) => {
711
+ if (nodes().some((node) => {
712
+ const id = node.messageId ?? node.id;
713
+ return (node.kind === "user" || node.kind === "steering") && (node.seq ?? -1) > maxSeq && matches(node) && (id === void 0 ? !queuedBefore.some(matches) : !queueIds.has(id));
714
+ }) || queuedMessages(binding, inbox).some((message) => {
699
715
  const id = message.messageId ?? message.id;
700
716
  return id !== void 0 && !queueIds.has(id) && matches(message);
701
717
  })) {
@@ -750,7 +766,7 @@ window.__ModuleLoader__.load({
750
766
  let dockRegistered = false;
751
767
  let report = {
752
768
  versions: {
753
- sidenote: "0.8.3",
769
+ sidenote: "0.8.5",
754
770
  dsh: "unknown",
755
771
  betterSidebar: null
756
772
  },
@@ -950,6 +966,8 @@ window.__ModuleLoader__.load({
950
966
  settingsDescription: "Choose the actions available when you select conversation text.",
951
967
  hideNativeQuote: "Hide native quote",
952
968
  hideNativeQuoteHint: "Hide the Quote action in main and side chats.",
969
+ showSideChatNote: "Add a note before asking in side chat",
970
+ showSideChatNoteHint: "Turn off to add selected text directly to side chat and write your question there.",
953
971
  settingsSaving: "Saving…",
954
972
  settingsLoading: "Loading preferences…",
955
973
  settingsUnavailable: "Preferences are unavailable. Reload the page to reconnect.",
@@ -1125,6 +1143,8 @@ window.__ModuleLoader__.load({
1125
1143
  settingsDescription: "调整划选对话文字时显示的操作。",
1126
1144
  hideNativeQuote: "隐藏原生引用",
1127
1145
  hideNativeQuoteHint: "在主对话和侧边聊天中隐藏“引用”按钮。",
1146
+ showSideChatNote: "侧边提问前填写注解",
1147
+ showSideChatNoteHint: "关闭后,划选内容直接添加到侧边聊天,可在侧聊输入框中继续提问。",
1128
1148
  settingsSaving: "正在保存…",
1129
1149
  settingsLoading: "正在加载偏好设置…",
1130
1150
  settingsUnavailable: "暂时无法读取设置,请刷新页面重新连接。",
@@ -1405,12 +1425,12 @@ window.__ModuleLoader__.load({
1405
1425
  }
1406
1426
  var diagnostics_module_css_default = {
1407
1427
  "trigger": "tGe0LW_trigger",
1408
- "header": "tGe0LW_header",
1409
1428
  "list": "tGe0LW_list",
1410
- "description": "tGe0LW_description",
1429
+ "header": "tGe0LW_header",
1411
1430
  "panel": "tGe0LW_panel",
1412
- "versions": "tGe0LW_versions",
1413
- "actions": "tGe0LW_actions"
1431
+ "actions": "tGe0LW_actions",
1432
+ "description": "tGe0LW_description",
1433
+ "versions": "tGe0LW_versions"
1414
1434
  };
1415
1435
  //#endregion
1416
1436
  //#region src/client/diagnostics/index.tsx
@@ -1718,9 +1738,13 @@ window.__ModuleLoader__.load({
1718
1738
  if (typeof a.text !== "string" || typeof a.anchorText !== "string") return null;
1719
1739
  if (typeof a.occurrence !== "number") return null;
1720
1740
  if (a.state !== "active" && a.state !== "sent") return null;
1741
+ if (a.anchors !== void 0) {
1742
+ if (!Array.isArray(a.anchors) || a.anchors.length < 2) return null;
1743
+ for (const anchor of a.anchors) if (!anchor || typeof anchor.anchorKey !== "string" || !anchor.anchorKey || typeof anchor.anchorText !== "string" || !anchor.anchorText.trim() || !Number.isSafeInteger(anchor.occurrence) || anchor.occurrence < 0) return null;
1744
+ }
1721
1745
  return {
1722
1746
  id: a.id,
1723
- number: a.number,
1747
+ number: a.state === "sent" && typeof a.localNumber === "number" ? a.localNumber : a.number,
1724
1748
  sessionId: a.sessionId,
1725
1749
  sourceSessionId: typeof a.sourceSessionId === "string" && a.sourceSessionId !== "" ? a.sourceSessionId : void 0,
1726
1750
  sourceSurface: a.sourceSurface === "main" || a.sourceSurface === "side" ? a.sourceSurface : void 0,
@@ -1728,6 +1752,7 @@ window.__ModuleLoader__.load({
1728
1752
  text: a.text,
1729
1753
  anchorText: a.anchorText,
1730
1754
  occurrence: a.occurrence,
1755
+ ...a.anchors ? { anchors: a.anchors } : {},
1731
1756
  note: typeof a.note === "string" ? a.note : "",
1732
1757
  state: a.state,
1733
1758
  createdAt: typeof a.createdAt === "number" ? a.createdAt : 0
@@ -1737,11 +1762,24 @@ window.__ModuleLoader__.load({
1737
1762
  let annotations = [];
1738
1763
  let nextId = 1;
1739
1764
  let version = 0;
1740
- /** Per-session next display number. */
1741
- const counters = /* @__PURE__ */ new Map();
1742
1765
  const listeners = /* @__PURE__ */ new Set();
1743
1766
  const submissions = /* @__PURE__ */ new Map();
1744
1767
  const idleSubmission = { pending: false };
1768
+ const countActiveOf = (sessionId) => annotations.reduce((n, a) => n + (a.sessionId === sessionId && a.state === "active" ? 1 : 0), 0);
1769
+ /** 只重排目标会话的待发送编号,已发送编号保持冻结。 */
1770
+ const renumberActive = (sessionId) => {
1771
+ const numbers = /* @__PURE__ */ new Map();
1772
+ annotations.filter((a) => a.sessionId === sessionId && a.state === "active").sort((left, right) => left.id - right.id).forEach((annotation, index) => {
1773
+ numbers.set(annotation.id, index + 1);
1774
+ });
1775
+ annotations = annotations.map((a) => {
1776
+ const number = numbers.get(a.id);
1777
+ return number === void 0 || a.number === number ? a : {
1778
+ ...a,
1779
+ number
1780
+ };
1781
+ });
1782
+ };
1745
1783
  let store = storage ?? null;
1746
1784
  if (storage === void 0) try {
1747
1785
  store = globalThis.localStorage ?? null;
@@ -1772,7 +1810,6 @@ window.__ModuleLoader__.load({
1772
1810
  const revived = reviveStored(item);
1773
1811
  if (revived === null) continue;
1774
1812
  annotations.push(revived);
1775
- counters.set(revived.sessionId, Math.max(counters.get(revived.sessionId) ?? 0, revived.number));
1776
1813
  nextId = Math.max(nextId, revived.id + 1);
1777
1814
  }
1778
1815
  }
@@ -1805,6 +1842,7 @@ window.__ModuleLoader__.load({
1805
1842
  version += 1;
1806
1843
  for (const fn of [...listeners]) fn();
1807
1844
  };
1845
+ for (const sessionId of new Set(annotations.map((a) => a.sessionId))) renumberActive(sessionId);
1808
1846
  const replace = (id, patch) => {
1809
1847
  const target = annotations.find((a) => a.id === id);
1810
1848
  annotations = annotations.map((a) => a.id === id ? {
@@ -1829,8 +1867,7 @@ window.__ModuleLoader__.load({
1829
1867
  };
1830
1868
  },
1831
1869
  add(draft) {
1832
- const number = (counters.get(draft.sessionId) ?? 0) + 1;
1833
- counters.set(draft.sessionId, number);
1870
+ const number = countActiveOf(draft.sessionId) + 1;
1834
1871
  const annotation = {
1835
1872
  id: nextId,
1836
1873
  number,
@@ -1841,6 +1878,7 @@ window.__ModuleLoader__.load({
1841
1878
  text: draft.text,
1842
1879
  anchorText: draft.anchorText,
1843
1880
  occurrence: draft.occurrence,
1881
+ ...draft.anchors ? { anchors: draft.anchors } : {},
1844
1882
  note: draft.note,
1845
1883
  state: "active",
1846
1884
  createdAt: now()
@@ -1859,10 +1897,11 @@ window.__ModuleLoader__.load({
1859
1897
  const target = annotations.find((a) => a.id === id);
1860
1898
  if (target === void 0) return;
1861
1899
  annotations = annotations.filter((a) => a.id !== id);
1900
+ renumberActive(target.sessionId);
1862
1901
  persistTouched([target.sessionId]);
1863
1902
  notify();
1864
1903
  },
1865
- markSent(ids) {
1904
+ markSent(ids, frozenNumbers) {
1866
1905
  if (ids.length === 0) return;
1867
1906
  const idSet = new Set(ids);
1868
1907
  if (!annotations.some((a) => idSet.has(a.id) && a.state === "active")) return;
@@ -1872,9 +1911,11 @@ window.__ModuleLoader__.load({
1872
1911
  touched.add(a.sessionId);
1873
1912
  return {
1874
1913
  ...a,
1875
- state: "sent"
1914
+ state: "sent",
1915
+ number: frozenNumbers?.get(a.id) ?? a.number
1876
1916
  };
1877
1917
  });
1918
+ for (const sessionId of touched) renumberActive(sessionId);
1878
1919
  persistTouched([...touched]);
1879
1920
  notify();
1880
1921
  },
@@ -1888,7 +1929,7 @@ window.__ModuleLoader__.load({
1888
1929
  return annotations.filter((a) => a.sessionId === sessionId && a.state === "active");
1889
1930
  },
1890
1931
  countActive(sessionId) {
1891
- return annotations.reduce((n, a) => n + (a.sessionId === sessionId && a.state === "active" ? 1 : 0), 0);
1932
+ return countActiveOf(sessionId);
1892
1933
  },
1893
1934
  sessions() {
1894
1935
  return [...new Set(annotations.map((a) => a.sessionId))];
@@ -2238,7 +2279,7 @@ window.__ModuleLoader__.load({
2238
2279
  }
2239
2280
  /** 协议头识别(zh/en 都认):整行匹配。 */
2240
2281
  const PROTOCOL_HEADER_RE = /^(?:我批注了以下 (\d+) 处内容:|I annotated (\d+) passage\(s\) of the conversation above:)$/;
2241
- const ANNOTATION_RE = /<annotation id="(\d+)">\s*<quote>([\s\S]*?)<\/quote>\s*(?:<note>([\s\S]*?)<\/note>\s*)?<\/annotation>/y;
2282
+ const ANNOTATION_RE = /<annotation id="(\d+)"(?: local="(\d+)")?(?: scope="[^"]*")?>\s*<quote>([\s\S]*?)<\/quote>\s*(?:<note>([\s\S]*?)<\/note>\s*)?<\/annotation>/y;
2242
2283
  const REFLOW_RE = /<reflow\s+source="([^"]*)"(?:\s+reason="([^"]*)")?\s*>([\s\S]*?)<\/reflow>/y;
2243
2284
  /**
2244
2285
  * 气泡 tooltip / 面板留痕 chip 的展示拍平:剥掉 <问>/<答> 协议标签只留内容行
@@ -2276,14 +2317,16 @@ window.__ModuleLoader__.load({
2276
2317
  const headerMatch = /^([^\n]*)\n/.exec(rest);
2277
2318
  if (headerMatch !== null && PROTOCOL_HEADER_RE.test(headerMatch[1] ?? "")) {
2278
2319
  let cursor = pos + headerMatch[0].length;
2320
+ const legacyGuidance = /^Annotation ids stay stable within (?:their session scope|this conversation)\.[^\n]*\n/.exec(text.slice(cursor));
2321
+ if (legacyGuidance) cursor += legacyGuidance[0].length;
2279
2322
  for (;;) {
2280
2323
  ANNOTATION_RE.lastIndex = cursor;
2281
2324
  const m = ANNOTATION_RE.exec(text);
2282
2325
  if (m === null) break;
2283
2326
  annotations.push({
2284
- id: Number(m[1]),
2285
- quote: m[2] ?? "",
2286
- note: (m[3] ?? "").trim()
2327
+ id: Number(m[2] ?? m[1]),
2328
+ quote: m[3] ?? "",
2329
+ note: (m[4] ?? "").trim()
2287
2330
  });
2288
2331
  cursor = skipBlank(text, ANNOTATION_RE.lastIndex);
2289
2332
  }
@@ -2309,33 +2352,33 @@ window.__ModuleLoader__.load({
2309
2352
  document.head.appendChild(tag);
2310
2353
  }
2311
2354
  var annotations_module_css_default = {
2312
- "chipDismiss": "SVnebq_chipDismiss",
2313
- "userContextHost": "SVnebq_userContextHost",
2314
- "contextAppear": "SVnebq_contextAppear",
2315
- "sentChips": "SVnebq_sentChips",
2316
- "contextSource": "SVnebq_contextSource",
2317
- "sentChevronOpen": "SVnebq_sentChevronOpen",
2318
- "contextPanel": "SVnebq_contextPanel",
2319
- "contextContent": "SVnebq_contextContent",
2355
+ "contextNumber": "SVnebq_contextNumber",
2356
+ "contextLabel": "SVnebq_contextLabel",
2357
+ "contextEditor": "SVnebq_contextEditor",
2320
2358
  "sentChipGroup": "SVnebq_sentChipGroup",
2359
+ "sentChip": "SVnebq_sentChip",
2321
2360
  "chipMain": "SVnebq_chipMain",
2322
- "sentChevron": "SVnebq_sentChevron",
2323
- "contextActions": "SVnebq_contextActions",
2324
- "contextAction": "SVnebq_contextAction",
2325
- "contextNumber": "SVnebq_contextNumber",
2361
+ "contextText": "SVnebq_contextText",
2362
+ "chipClearAll": "SVnebq_chipClearAll",
2326
2363
  "storageNotice": "SVnebq_storageNotice",
2327
- "sentChip": "SVnebq_sentChip",
2364
+ "contextContent": "SVnebq_contextContent",
2365
+ "sentChevron": "SVnebq_sentChevron",
2366
+ "sentChevronOpen": "SVnebq_sentChevronOpen",
2367
+ "userContextHost": "SVnebq_userContextHost",
2328
2368
  "sentPreview": "SVnebq_sentPreview",
2329
- "contextItem": "SVnebq_contextItem",
2369
+ "contextActions": "SVnebq_contextActions",
2330
2370
  "contextFooter": "SVnebq_contextFooter",
2331
- "contextLabel": "SVnebq_contextLabel",
2371
+ "contextAppear": "SVnebq_contextAppear",
2332
2372
  "contextList": "SVnebq_contextList",
2333
- "chipWrap": "SVnebq_chipWrap",
2334
- "chipDismissShade": "SVnebq_chipDismissShade",
2373
+ "contextPanel": "SVnebq_contextPanel",
2335
2374
  "chip": "SVnebq_chip",
2336
- "contextText": "SVnebq_contextText",
2337
- "chipClearAll": "SVnebq_chipClearAll",
2338
- "contextEditor": "SVnebq_contextEditor"
2375
+ "contextAction": "SVnebq_contextAction",
2376
+ "sentChips": "SVnebq_sentChips",
2377
+ "chipWrap": "SVnebq_chipWrap",
2378
+ "chipDismiss": "SVnebq_chipDismiss",
2379
+ "contextSource": "SVnebq_contextSource",
2380
+ "contextItem": "SVnebq_contextItem",
2381
+ "chipDismissShade": "SVnebq_chipDismissShade"
2339
2382
  };
2340
2383
  //#endregion
2341
2384
  //#region src/client/annotations/NativeUserContext.tsx
@@ -2809,38 +2852,38 @@ window.__ModuleLoader__.load({
2809
2852
  document.head.appendChild(tag);
2810
2853
  }
2811
2854
  var sidechat_module_css_default = {
2855
+ "root": "_5KEEsG_root",
2812
2856
  "progress": "_5KEEsG_progress",
2857
+ "progressProcess": "_5KEEsG_progressProcess",
2858
+ "nativeToolbarActions": "_5KEEsG_nativeToolbarActions",
2859
+ "nativeToolbar": "_5KEEsG_nativeToolbar",
2860
+ "stateDetail": "_5KEEsG_stateDetail",
2861
+ "headerSide": "_5KEEsG_headerSide",
2862
+ "nativeConversation": "_5KEEsG_nativeConversation",
2863
+ "emptyText": "_5KEEsG_emptyText",
2864
+ "contextChevron": "_5KEEsG_contextChevron",
2865
+ "contextHint": "_5KEEsG_contextHint",
2866
+ "progressEntry": "_5KEEsG_progressEntry",
2867
+ "progressPreview": "_5KEEsG_progressPreview",
2868
+ "nativeAction": "_5KEEsG_nativeAction",
2869
+ "reflowAction": "_5KEEsG_reflowAction",
2870
+ "chatView": "_5KEEsG_chatView",
2813
2871
  "contextColumn": "_5KEEsG_contextColumn",
2814
- "emptyTitle": "_5KEEsG_emptyTitle",
2815
- "contextUserRow": "_5KEEsG_contextUserRow",
2816
- "progressDetail": "_5KEEsG_progressDetail",
2817
2872
  "contextChevronOpen": "_5KEEsG_contextChevronOpen",
2873
+ "mainContext": "_5KEEsG_mainContext",
2874
+ "emptyTitle": "_5KEEsG_emptyTitle",
2818
2875
  "errorRow": "_5KEEsG_errorRow",
2819
- "progressContent": "_5KEEsG_progressContent",
2820
- "reflowAction": "_5KEEsG_reflowAction",
2821
- "nativeAction": "_5KEEsG_nativeAction",
2822
- "root": "_5KEEsG_root",
2876
+ "progressDetail": "_5KEEsG_progressDetail",
2877
+ "historyToggle": "_5KEEsG_historyToggle",
2878
+ "nativeHost": "_5KEEsG_nativeHost",
2879
+ "progressEntries": "_5KEEsG_progressEntries",
2823
2880
  "progressLatest": "_5KEEsG_progressLatest",
2824
- "emptyText": "_5KEEsG_emptyText",
2881
+ "progressContent": "_5KEEsG_progressContent",
2882
+ "stateScreen": "_5KEEsG_stateScreen",
2825
2883
  "contextImage": "_5KEEsG_contextImage",
2826
- "progressEntries": "_5KEEsG_progressEntries",
2827
- "nativeConversation": "_5KEEsG_nativeConversation",
2828
- "nativeHost": "_5KEEsG_nativeHost",
2829
- "chatView": "_5KEEsG_chatView",
2830
2884
  "contextBubble": "_5KEEsG_contextBubble",
2831
- "nativeToolbarActions": "_5KEEsG_nativeToolbarActions",
2832
- "historyToggle": "_5KEEsG_historyToggle",
2833
- "mainContext": "_5KEEsG_mainContext",
2834
- "stateScreen": "_5KEEsG_stateScreen",
2835
- "stateDetail": "_5KEEsG_stateDetail",
2836
- "progressPreview": "_5KEEsG_progressPreview",
2837
- "nativeToolbar": "_5KEEsG_nativeToolbar",
2838
- "contextChevron": "_5KEEsG_contextChevron",
2839
- "contextHint": "_5KEEsG_contextHint",
2840
- "progressProcess": "_5KEEsG_progressProcess",
2841
- "headerSide": "_5KEEsG_headerSide",
2842
- "progressEntry": "_5KEEsG_progressEntry",
2843
- "emptyIcon": "_5KEEsG_emptyIcon"
2885
+ "emptyIcon": "_5KEEsG_emptyIcon",
2886
+ "contextUserRow": "_5KEEsG_contextUserRow"
2844
2887
  };
2845
2888
  //#endregion
2846
2889
  //#region src/client/sidechat/ContextProgress.tsx
@@ -2914,6 +2957,7 @@ window.__ModuleLoader__.load({
2914
2957
  "data-sidenote-quote-body": entry.messageKey ? "" : void 0,
2915
2958
  "data-sidenote-message-role": entry.messageKey ? "assistant" : void 0,
2916
2959
  "data-sidenote-message-key": entry.messageKey,
2960
+ "data-sidenote-turn": entry.turn === void 0 ? void 0 : `history:${entry.turn}`,
2917
2961
  "data-sidenote-streaming": live && unfinished ? "true" : "false",
2918
2962
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
2919
2963
  text: entry.text,
@@ -3492,7 +3536,10 @@ ${body}
3492
3536
  return {
3493
3537
  ...entry,
3494
3538
  key,
3495
- ...node.kind === "assistant" && entry.kind === "reply" ? { messageKey: `a:${node.seq}` } : {},
3539
+ ...node.kind === "assistant" && entry.kind === "reply" ? {
3540
+ messageKey: `a:${node.seq}`,
3541
+ turn: node.turn
3542
+ } : {},
3496
3543
  ...node.kind !== "user" && node.kind !== "steering" && images.length && index === entries.length - 1 ? { images } : {}
3497
3544
  };
3498
3545
  });
@@ -6218,7 +6265,7 @@ ${body}
6218
6265
  /** Explicit side bodies are the sole exception to the legacy sidebar exclusion. */
6219
6266
  function quoteSourceOf(node, currentSessionId) {
6220
6267
  const element = elementOf(node);
6221
- if (element?.closest("button, [role=\"button\"], input, textarea, select, [contenteditable=\"true\"], [data-code-block-banner], [data-variant=\"think\"], [data-sidenote-no-quote]") !== null) return null;
6268
+ if (!element || element.closest(`button, [role="button"], input, textarea, select, [contenteditable="true"], [data-code-block-banner], [data-variant="think"], [data-chat-group-part="reasoning"], [data-sidenote-no-quote], [data-variant="think"], [data-chat-group-part="reasoning"], [data-chat-flow-kind="tool-call"], [data-chat-flow-kind="tool-result"], [data-chat-flow-kind="turn-process"], [data-context-kind="thinking"], [data-context-kind="tool"]`) !== null) return null;
6222
6269
  const body = element?.closest("[data-sidenote-quote-body]");
6223
6270
  if (body !== void 0 && body !== null) {
6224
6271
  const anchor = body.closest("[data-sidenote-message-key][data-sidenote-message-role=\"assistant\"]");
@@ -6232,6 +6279,7 @@ ${body}
6232
6279
  sessionId,
6233
6280
  surface: "side",
6234
6281
  key: anchor.dataset.sidenoteMessageKey,
6282
+ turn: body.dataset.sidenoteTurn ?? body.closest("[data-chat-turn]")?.dataset.chatTurn,
6235
6283
  streaming: body.dataset.sidenoteStreaming === "true"
6236
6284
  };
6237
6285
  }
@@ -6249,19 +6297,57 @@ ${body}
6249
6297
  return source.key;
6250
6298
  }
6251
6299
  function sourceBodies(sessionId, surface, anchorKey, currentSessionId) {
6252
- if (surface === "main") return mainAnchorBodies(sessionId, anchorKey, currentSessionId);
6253
- return [...document.querySelectorAll("[data-sidenote-quote-body]"), ...nativeAssistantBodies()].filter((body) => {
6300
+ return (surface === "main" ? mainAnchorBodies(sessionId, anchorKey, currentSessionId) : [...document.querySelectorAll("[data-sidenote-quote-body]"), ...nativeAssistantBodies().filter((body) => !body.querySelector("[data-sidenote-quote-body]"))]).filter((body) => {
6254
6301
  const source = quoteSourceOf(body, currentSessionId);
6255
- return source?.surface === "side" && source.sessionId === sessionId && sourceKey(source) === anchorKey;
6302
+ return source?.surface === surface && source.sessionId === sessionId && sourceKey(source) === anchorKey;
6256
6303
  });
6257
6304
  }
6305
+ /** Admit the complete visible selection, never silently drop process content. */
6306
+ function quoteSourcesOfRange(range, currentSessionId) {
6307
+ const root = range.commonAncestorContainer;
6308
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
6309
+ const sources = [];
6310
+ const seen = /* @__PURE__ */ new Set();
6311
+ for (let node = root.nodeType === Node.TEXT_NODE ? root : walker.nextNode(); node; node = walker.nextNode()) {
6312
+ if (!range.intersectsNode(node) || !quoteNodeVisible(node)) continue;
6313
+ const start = node === range.startContainer ? range.startOffset : 0;
6314
+ const end = node === range.endContainer ? range.endOffset : node.textContent.length;
6315
+ if (node.textContent.slice(start, end).trim() === "") continue;
6316
+ const element = elementOf(node);
6317
+ if (element.closest("[data-variant=\"think\"], [data-chat-group-part=\"reasoning\"], [data-chat-flow-kind=\"tool-call\"], [data-chat-flow-kind=\"tool-result\"], [data-chat-flow-kind=\"turn-process\"], [data-context-kind=\"thinking\"], [data-context-kind=\"tool\"]")) return null;
6318
+ if (element.closest("button, [role=\"button\"], input, textarea, select, [contenteditable=\"true\"], [data-code-block-banner], [data-variant=\"think\"], [data-chat-group-part=\"reasoning\"], [data-sidenote-no-quote]")) {
6319
+ const body = element.closest(`[data-sidenote-quote-body], ${MAIN_ASSISTANT}`);
6320
+ if (body && quoteSourceOf(body, currentSessionId)) continue;
6321
+ return null;
6322
+ }
6323
+ const source = quoteSourceOf(node, currentSessionId);
6324
+ if (!source || source.streaming) return null;
6325
+ const first = sources[0];
6326
+ if (first && (source.view !== first.view || source.sessionId !== first.sessionId || source.surface !== first.surface)) return null;
6327
+ if (seen.has(source.body)) continue;
6328
+ if (first && (!first.turn || source.turn !== first.turn || !first.key || !source.key)) return null;
6329
+ sources.push(source);
6330
+ seen.add(source.body);
6331
+ }
6332
+ return sources.length ? sources : null;
6333
+ }
6334
+ /** Trim each stored fragment to the actual selected text, excluding surrounding UI. */
6335
+ function quoteRangeOf(body, selection) {
6336
+ const nodes = quoteTextNodes(body).filter((node) => selection.intersectsNode(node) && (node !== selection.startContainer || selection.startOffset < node.length) && (node !== selection.endContainer || selection.endOffset > 0));
6337
+ const first = nodes[0], last = nodes.at(-1);
6338
+ if (!first || !last) return null;
6339
+ const range = document.createRange();
6340
+ range.setStart(first, first === selection.startContainer ? selection.startOffset : 0);
6341
+ range.setEnd(last, last === selection.endContainer ? selection.endOffset : last.length);
6342
+ return range;
6343
+ }
6258
6344
  /** The same text-node projection is used at capture and when rebuilding anchors.
6259
6345
  * Browser selections contain block newlines; anchor offsets deliberately do not. */
6260
6346
  function quoteTextNodes(body) {
6261
6347
  const walker = document.createTreeWalker(body, NodeFilter.SHOW_TEXT);
6262
6348
  const nodes = [];
6263
6349
  for (let node = walker.nextNode(); node; node = walker.nextNode()) {
6264
- if (node.parentElement?.closest("button, [role=\"button\"], input, textarea, select, [contenteditable=\"true\"], [data-code-block-banner], [data-variant=\"think\"], [data-sidenote-no-quote]") !== null) continue;
6350
+ if (node.parentElement?.closest(`button, [role="button"], input, textarea, select, [contenteditable="true"], [data-code-block-banner], [data-variant="think"], [data-chat-group-part="reasoning"], [data-sidenote-no-quote], [data-variant="think"], [data-chat-group-part="reasoning"], [data-chat-flow-kind="tool-call"], [data-chat-flow-kind="tool-result"], [data-chat-flow-kind="turn-process"], [data-context-kind="thinking"], [data-context-kind="tool"]`) !== null || !quoteNodeVisible(node)) continue;
6265
6351
  if (node.parentElement !== null && getComputedStyle(node.parentElement).userSelect === "none") continue;
6266
6352
  nodes.push(node);
6267
6353
  }
@@ -6278,7 +6364,7 @@ ${body}
6278
6364
  function selectedQuoteText(range, browserText, anchorText) {
6279
6365
  if (browserText.replace(/\s/g, "") === anchorText.replace(/\s/g, "")) return browserText;
6280
6366
  const fragment = range.cloneContents();
6281
- for (const element of fragment.querySelectorAll(NON_QUOTE)) element.remove();
6367
+ for (const element of fragment.querySelectorAll(`${NON_QUOTE}, ${PROCESS_UI}, [hidden], [data-turn-process-hidden], [data-turn-process-inline]`)) element.remove();
6282
6368
  const textOf = (node) => {
6283
6369
  if (node.nodeType === Node.TEXT_NODE) return node.textContent ?? "";
6284
6370
  if (node instanceof Element && node.tagName === "BR") return "\n";
@@ -6302,11 +6388,11 @@ ${body}
6302
6388
  * toolbar through a small external-store controller. Mirrors the
6303
6389
  * production-proven dsh-sidebar-qa contract, narrowed to assistant messages:
6304
6390
  *
6305
- * - both selection endpoints must sit in the SAME `[data-chat-flow-kind=
6306
- * "assistant-step"]` message node (跨消息划选不弹);
6391
+ * - completed reply bodies in one view and Turn may be selected together;
6392
+ * - selected visible process/tool UI invalidates the whole selection;
6307
6393
  * - `[data-streaming]` messages (still streaming) are excluded;
6308
6394
  * - explicit side assistant bodies are admitted; other sidebar/plugin UI is
6309
- * excluded; both endpoints belong to the same message and source view;
6395
+ * excluded; every selected reply belongs to the same source view;
6310
6396
  * - quote text is kept complete, while anchor offsets use one DOM projection.
6311
6397
  *
6312
6398
  * These are DSH-internal DOM attributes rather than a public contract — an
@@ -6338,24 +6424,38 @@ ${body}
6338
6424
  const sel = window.getSelection();
6339
6425
  if (sel === null || sel.isCollapsed || sel.rangeCount === 0) return null;
6340
6426
  const range = sel.getRangeAt(0);
6341
- const source = quoteSourceOf(range.startContainer, currentSessionId);
6342
- const end = quoteSourceOf(range.endContainer, currentSessionId);
6343
- if (source === null || end === null || source.body !== end.body || source.view !== end.view || source.streaming) return null;
6344
- const anchorText = projectedRangeText(source.body, range);
6427
+ const sources = quoteSourcesOfRange(range, currentSessionId);
6428
+ if (!sources) return null;
6429
+ const ranges = sources.map((source) => quoteRangeOf(source.body, range));
6430
+ if (ranges.some((fragment) => fragment === null)) return null;
6431
+ const fragments = ranges;
6432
+ const anchors = sources.map((source, index) => {
6433
+ const anchorText = projectedRangeText(source.body, fragments[index]);
6434
+ return {
6435
+ anchorKey: sourceKey(source),
6436
+ anchorText,
6437
+ occurrence: occurrenceOf(source.body, fragments[index], anchorText)
6438
+ };
6439
+ });
6440
+ const source = sources[0];
6441
+ const anchorText = anchors.map((anchor) => anchor.anchorText).join("");
6345
6442
  if (anchorText.trim() === "") return null;
6346
- const text = selectedQuoteText(range, sel.toString(), anchorText);
6443
+ const browserText = sel.toString();
6444
+ const text = browserText.replace(/\s/g, "") === anchorText.replace(/\s/g, "") ? browserText : fragments.map((fragment, index) => selectedQuoteText(fragment, fragment.toString(), anchors[index].anchorText)).join("\n\n");
6347
6445
  const rect = range.getBoundingClientRect();
6348
6446
  return {
6349
6447
  text,
6350
6448
  anchorText,
6351
6449
  anchorKey: sourceKey(source),
6352
- occurrence: occurrenceOf(source.body, range, anchorText),
6450
+ occurrence: anchors[0].occurrence,
6451
+ ...anchors.length > 1 ? { anchors } : {},
6353
6452
  rect: {
6354
6453
  left: rect.left,
6355
6454
  top: rect.top,
6356
6455
  width: rect.width
6357
6456
  },
6358
6457
  range: range.cloneRange(),
6458
+ ranges: fragments,
6359
6459
  sessionId: source.sessionId,
6360
6460
  source
6361
6461
  };
@@ -6379,7 +6479,7 @@ ${body}
6379
6479
  } catch {
6380
6480
  next = null;
6381
6481
  }
6382
- if (!(selection === null !== (next === null) || selection !== null && next !== null && (selection.anchorText !== next.anchorText || selection.anchorKey !== next.anchorKey || selection.sessionId !== next.sessionId || selection.source.view !== next.source.view || selection.source.surface !== next.source.surface || selection.rect.top !== next.rect.top || selection.rect.left !== next.rect.left || selection.rect.width !== next.rect.width))) return;
6482
+ if (!(selection === null !== (next === null) || selection !== null && next !== null && (selection.anchorText !== next.anchorText || selection.anchorKey !== next.anchorKey || selection.occurrence !== next.occurrence || JSON.stringify(selection.anchors) !== JSON.stringify(next.anchors) || selection.sessionId !== next.sessionId || selection.source.view !== next.source.view || selection.source.surface !== next.source.surface || selection.rect.top !== next.rect.top || selection.rect.left !== next.rect.left || selection.rect.width !== next.rect.width))) return;
6383
6483
  selection = next;
6384
6484
  notify();
6385
6485
  };
@@ -6458,31 +6558,41 @@ ${body}
6458
6558
  range.setEnd(endNode, endOffset);
6459
6559
  return range;
6460
6560
  }
6461
- function resolveRange(annotation, cache, currentSessionId = "") {
6561
+ function resolveRanges(annotation, cache, currentSessionId = "") {
6462
6562
  try {
6463
6563
  const sessionId = annotation.sourceSessionId ?? annotation.sessionId;
6464
6564
  const surface = annotation.sourceSurface ?? "main";
6465
- const valid = (range, view) => {
6565
+ const anchors = annotation.anchors ?? [annotation];
6566
+ const valid = (ranges, view) => ranges.length === anchors.length && ranges.every((range, index) => {
6466
6567
  if (!range.startContainer.isConnected || !range.endContainer.isConnected) return false;
6467
- const source = quoteSourceOf(range.startContainer, currentSessionId);
6468
- const end = quoteSourceOf(range.endContainer, currentSessionId);
6469
- return source !== null && end?.body === source.body && source.view === view && source.sessionId === sessionId && source.surface === surface && sourceKey(source) === annotation.anchorKey && !source.streaming && projectedRangeText(source.body, range) === annotation.anchorText;
6470
- };
6568
+ const sources = quoteSourcesOfRange(range, currentSessionId);
6569
+ const source = sources?.[0];
6570
+ const anchor = anchors[index];
6571
+ return sources?.length === 1 && source !== void 0 && source.view === view && source.sessionId === sessionId && source.surface === surface && sourceKey(source) === anchor.anchorKey && projectedRangeText(source.body, range) === anchor.anchorText;
6572
+ });
6471
6573
  const cached = cache.get(annotation.id);
6472
- if (cached !== void 0 && valid(cached.range, cached.view)) return cached.range;
6574
+ if (cached !== void 0 && valid(cached.ranges, cached.view)) return cached.ranges;
6473
6575
  cache.delete(annotation.id);
6474
- if (annotation.anchorKey === void 0 || annotation.anchorText === "") return null;
6475
- const bodies = sourceBodies(sessionId, surface, annotation.anchorKey, currentSessionId).filter((body) => body.getClientRects().length > 0);
6476
- if (bodies.length !== 1) return null;
6477
- const body = bodies[0];
6478
- const source = quoteSourceOf(body, currentSessionId);
6479
- if (source === null || source.streaming) return null;
6480
- const range = rangeOfOccurrence(body, annotation.anchorText, annotation.occurrence);
6481
- if (range !== null) cache.set(annotation.id, {
6482
- range,
6483
- view: source.view
6576
+ const ranges = [];
6577
+ let view;
6578
+ for (const anchor of anchors) {
6579
+ if (!anchor.anchorKey || !anchor.anchorText) return null;
6580
+ const bodies = sourceBodies(sessionId, surface, anchor.anchorKey, currentSessionId).filter((body) => body.getClientRects().length > 0);
6581
+ if (bodies.length !== 1) return null;
6582
+ const body = bodies[0];
6583
+ const source = quoteSourceOf(body, currentSessionId);
6584
+ if (!source || view && source.view !== view) return null;
6585
+ view = source.view;
6586
+ const range = rangeOfOccurrence(body, anchor.anchorText, anchor.occurrence);
6587
+ if (!range) return null;
6588
+ ranges.push(range);
6589
+ }
6590
+ if (!view || !valid(ranges, view)) return null;
6591
+ cache.set(annotation.id, {
6592
+ ranges,
6593
+ view
6484
6594
  });
6485
- return range;
6595
+ return ranges;
6486
6596
  } catch {
6487
6597
  return null;
6488
6598
  }
@@ -6568,7 +6678,7 @@ ${body}
6568
6678
  }
6569
6679
  //#endregion
6570
6680
  //#region \0dsh-css:/home/runner/work/dsh-sidenote/dsh-sidenote/src/client/annotate/annotate.module.css.mjs
6571
- const css$1 = "._SVViG_toolbar{z-index:50;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#00000014);user-select:none;border-radius:10px;flex-wrap:wrap;align-items:stretch;width:max-content;display:flex;position:fixed;overflow:hidden;transform:translate(-50%,-100%);box-shadow:0 4px 16px #0000001f}._SVViG_toolbarBelow{transform:translate(-50%)}._SVViG_toolbarButton{appearance:none;color:var(--dsw-alias-label-primary,#1f2329);cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:7px 12px;font-size:13px;line-height:20px}._SVViG_toolbarButton+._SVViG_toolbarButton{border-left:1px solid var(--dsw-alias-border-l1,#00000014)}._SVViG_toolbarButton:hover{background:var(--dsw-alias-interactive-bg-hover,#0000000d)}._SVViG_toolbarButton:focus-visible{outline-offset:-3px;outline:2px solid #2f88ff}._SVViG_toolbarStatus{color:var(--dsw-alias-label-secondary,#646a73);flex-basis:100%;padding:6px 12px;font-size:12px;line-height:18px}._SVViG_badge{z-index:50;color:#fff;text-align:center;cursor:pointer;user-select:none;background:#2f88ff;border:none;border-radius:999px;width:18px;height:18px;padding:0;font-size:11px;font-weight:600;line-height:18px;position:fixed;transform:translateY(-50%);box-shadow:0 1px 4px #2f88ff73}._SVViG_badge:hover{background:#1f78f0}._SVViG_highlight{z-index:49;pointer-events:none;background:#2f88ff38;border-radius:2px;position:fixed}._SVViG_editorNew{z-index:50;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:999px;align-items:center;gap:8px;padding:6px 8px 6px 12px;display:flex;position:fixed;box-shadow:0 6px 24px #00000024}._SVViG_editorEdit{z-index:50;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:14px;flex-direction:column;display:flex;position:fixed;overflow:hidden;box-shadow:0 8px 28px #00000029}._SVViG_editorInput{min-width:0;color:var(--dsw-alias-label-primary,#1f2329);background:0 0;border:none;outline:none;flex:auto;font-size:13px;line-height:22px}._SVViG_editorTextarea{resize:none;color:var(--dsw-alias-label-primary,#1f2329);background:0 0;border:none;outline:none;padding:10px 12px 4px;font-family:inherit;font-size:13px;line-height:20px}._SVViG_editorInput::placeholder,._SVViG_editorTextarea::placeholder{color:var(--dsw-alias-label-caption,#8f959e)}._SVViG_editorAsk{z-index:50;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:14px;flex-direction:column;gap:2px;padding:6px 8px 6px 12px;display:flex;position:fixed;box-shadow:0 6px 24px #00000024}._SVViG_editorAskRow{align-items:center;gap:8px;display:flex}._SVViG_editorTarget{color:var(--dsw-alias-label-caption,#8f959e);user-select:none;padding:2px 2px 0;font-size:11px;line-height:16px}._SVViG_confirmButton{color:#fff;cursor:pointer;background:#1f2329;border:none;border-radius:999px;flex:none;place-items:center;width:26px;height:26px;padding:0;display:grid}._SVViG_confirmButton:hover{background:#000}._SVViG_editorFooter{align-items:center;gap:8px;padding:6px 10px 8px;display:flex}._SVViG_editorSpacer{flex:auto}._SVViG_deleteButton{width:26px;height:26px;color:var(--dsw-alias-label-tertiary,#8f959e);cursor:pointer;background:0 0;border:none;border-radius:999px;place-items:center;padding:0;display:grid}._SVViG_deleteButton:hover{background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-state-error-primary,#d53f3f)}._SVViG_cancelButton,._SVViG_saveButton{cursor:pointer;border:none;border-radius:999px;padding:4px 14px;font-size:13px;line-height:20px}._SVViG_cancelButton{background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-label-primary,#1f2329)}._SVViG_saveButton{color:#fff;background:#1f2329}._SVViG_saveButton:hover{background:#000}._SVViG_editorError{color:#e5484d;white-space:nowrap;font-size:12px;position:absolute;top:calc(100% + 4px);left:12px}._SVViG_badgeSent{background:var(--dsw-alias-label-caption,#8f959e);box-shadow:none;opacity:.75}._SVViG_badgeSent:hover{background:var(--dsw-alias-label-tertiary,#6b7075)}._SVViG_sentChip{border:1px solid var(--dsw-alias-border-l1,#0000001a);background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-label-secondary,#3f454d);cursor:default;user-select:none;border-radius:999px;align-items:center;gap:4px;margin:0 0 6px;padding:1px 10px;font-size:12px;line-height:18px;display:inline-flex}._SVViG_sentCardTitle{color:var(--dsw-alias-label-tertiary,#8f959e);padding:2px 12px 0;font-size:12px;font-weight:600}._SVViG_sentCardQuote{color:var(--dsw-alias-label-secondary,#3f454d);border-left:2px solid var(--dsw-alias-border-l1,#0000001f);word-break:break-word;margin:4px 12px 0;padding:4px 12px;font-size:13px;line-height:20px}._SVViG_sentCardNote{color:var(--dsw-alias-label-primary,#1f2329);word-break:break-word;padding:4px 12px 10px;font-size:13px;line-height:20px}._SVViG_hint{z-index:50;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#00000014);max-width:320px;color:var(--dsw-alias-label-secondary,#3f454d);border-radius:12px;align-items:center;gap:10px;padding:10px 12px;font-size:12px;line-height:18px;animation:.25s ease-out _SVViG_dshSidenoteHintIn;display:flex;position:fixed;bottom:160px;right:24px;box-shadow:0 8px 28px #00000024}._SVViG_hintClose{width:20px;height:20px;color:var(--dsw-alias-label-tertiary,#8f959e);cursor:pointer;background:0 0;border:none;border-radius:999px;flex:none;place-items:center;padding:0;font-size:14px;line-height:1;display:grid}._SVViG_hintClose:hover{background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-label-primary,#1f2329)}@keyframes _SVViG_dshSidenoteHintIn{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}._SVViG_sentChipRow{align-items:center;gap:6px;margin:0 0 6px;display:inline-flex}._SVViG_badgeSent{background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-tertiary,#8f959e);border:1.5px solid var(--dsw-alias-label-caption,#b0b5bb);box-shadow:none;opacity:1;line-height:15px}._SVViG_badgeSent:hover{background:var(--dsw-alias-interactive-bg-hover,#0000000a);color:var(--dsw-alias-label-secondary,#3f454d)}._SVViG_hint{background:color-mix(in srgb, var(--dsw-alias-bg-layer-1,#fff) 88%, transparent);backdrop-filter:blur(8px);bottom:130px}";
6681
+ const css$1 = "._SVViG_toolbar{z-index:50;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#00000014);user-select:none;border-radius:10px;flex-wrap:wrap;align-items:stretch;width:max-content;display:flex;position:fixed;overflow:hidden;transform:translate(-50%,-100%);box-shadow:0 4px 16px #0000001f}._SVViG_toolbarBelow{transform:translate(-50%)}._SVViG_toolbarButton{appearance:none;color:var(--dsw-alias-label-primary,#1f2329);cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:7px 12px;font-size:13px;line-height:20px}._SVViG_toolbarButton+._SVViG_toolbarButton{border-left:1px solid var(--dsw-alias-border-l1,#00000014)}._SVViG_toolbarButton:hover{background:var(--dsw-alias-interactive-bg-hover,#0000000d)}._SVViG_toolbarButton:focus-visible{outline-offset:-3px;outline:2px solid #2f88ff}._SVViG_toolbarStatus{color:var(--dsw-alias-label-secondary,#646a73);flex-basis:100%;padding:6px 12px;font-size:12px;line-height:18px}._SVViG_badge{z-index:50;color:#fff;text-align:center;cursor:pointer;user-select:none;background:#2f88ff;border:none;border-radius:999px;width:18px;height:18px;padding:0;font-size:11px;font-weight:600;line-height:18px;position:fixed;transform:translateY(-50%);box-shadow:0 1px 4px #2f88ff73}._SVViG_badge:hover{background:#1f78f0}._SVViG_highlight{z-index:49;pointer-events:none;background:#2f88ff38;border-radius:2px;position:fixed}._SVViG_editorNew{z-index:50;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:18px;align-items:center;gap:8px;padding:6px 8px 6px 12px;display:flex;position:fixed;box-shadow:0 6px 24px #00000024}._SVViG_editorEdit{z-index:50;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:14px;flex-direction:column;display:flex;position:fixed;overflow:hidden;box-shadow:0 8px 28px #00000029}._SVViG_editorInput{resize:none;min-width:0;min-height:21px;max-height:110px;color:var(--dsw-alias-label-primary,#1f2329);background:0 0;border:none;outline:none;flex:auto;padding:0;font-family:inherit;font-size:13px;line-height:22px;overflow-y:auto}._SVViG_editorTextarea{resize:none;color:var(--dsw-alias-label-primary,#1f2329);background:0 0;border:none;outline:none;padding:10px 12px 4px;font-family:inherit;font-size:13px;line-height:20px}._SVViG_editorInput::placeholder,._SVViG_editorTextarea::placeholder{color:var(--dsw-alias-label-caption,#8f959e)}._SVViG_editorAsk{z-index:50;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:14px;flex-direction:column;gap:2px;padding:6px 8px 6px 12px;display:flex;position:fixed;box-shadow:0 6px 24px #00000024}._SVViG_editorAskRow{align-items:center;gap:8px;display:flex}._SVViG_editorTarget{color:var(--dsw-alias-label-caption,#8f959e);user-select:none;padding:2px 2px 0;font-size:11px;line-height:16px}._SVViG_confirmButton{color:#fff;cursor:pointer;background:#1f2329;border:none;border-radius:999px;flex:none;place-items:center;width:26px;height:26px;padding:0;display:grid}._SVViG_confirmButton:hover{background:#000}._SVViG_editorFooter{align-items:center;gap:8px;padding:6px 10px 8px;display:flex}._SVViG_editorSpacer{flex:auto}._SVViG_deleteButton{width:26px;height:26px;color:var(--dsw-alias-label-tertiary,#8f959e);cursor:pointer;background:0 0;border:none;border-radius:999px;place-items:center;padding:0;display:grid}._SVViG_deleteButton:hover{background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-state-error-primary,#d53f3f)}._SVViG_cancelButton,._SVViG_saveButton{cursor:pointer;border:none;border-radius:999px;padding:4px 14px;font-size:13px;line-height:20px}._SVViG_cancelButton{background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-label-primary,#1f2329)}._SVViG_saveButton{color:#fff;background:#1f2329}._SVViG_saveButton:hover{background:#000}._SVViG_editorError{color:#e5484d;white-space:normal;overflow-wrap:anywhere;padding:2px 2px 0;font-size:12px}._SVViG_badgeSent{background:var(--dsw-alias-label-caption,#8f959e);box-shadow:none;opacity:.75}._SVViG_badgeSent:hover{background:var(--dsw-alias-label-tertiary,#6b7075)}._SVViG_sentChip{border:1px solid var(--dsw-alias-border-l1,#0000001a);background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-label-secondary,#3f454d);cursor:default;user-select:none;border-radius:999px;align-items:center;gap:4px;margin:0 0 6px;padding:1px 10px;font-size:12px;line-height:18px;display:inline-flex}._SVViG_sentCardTitle{color:var(--dsw-alias-label-tertiary,#8f959e);padding:2px 12px 0;font-size:12px;font-weight:600}._SVViG_sentCardQuote{color:var(--dsw-alias-label-secondary,#3f454d);border-left:2px solid var(--dsw-alias-border-l1,#0000001f);word-break:break-word;margin:4px 12px 0;padding:4px 12px;font-size:13px;line-height:20px}._SVViG_sentCardNote{color:var(--dsw-alias-label-primary,#1f2329);word-break:break-word;padding:4px 12px 10px;font-size:13px;line-height:20px}._SVViG_hint{z-index:50;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#00000014);max-width:320px;color:var(--dsw-alias-label-secondary,#3f454d);border-radius:12px;align-items:center;gap:10px;padding:10px 12px;font-size:12px;line-height:18px;animation:.25s ease-out _SVViG_dshSidenoteHintIn;display:flex;position:fixed;bottom:160px;right:24px;box-shadow:0 8px 28px #00000024}._SVViG_hintClose{width:20px;height:20px;color:var(--dsw-alias-label-tertiary,#8f959e);cursor:pointer;background:0 0;border:none;border-radius:999px;flex:none;place-items:center;padding:0;font-size:14px;line-height:1;display:grid}._SVViG_hintClose:hover{background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-label-primary,#1f2329)}@keyframes _SVViG_dshSidenoteHintIn{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}._SVViG_sentChipRow{align-items:center;gap:6px;margin:0 0 6px;display:inline-flex}._SVViG_badgeSent{background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-tertiary,#8f959e);border:1.5px solid var(--dsw-alias-label-caption,#b0b5bb);box-shadow:none;opacity:1;line-height:15px}._SVViG_badgeSent:hover{background:var(--dsw-alias-interactive-bg-hover,#0000000a);color:var(--dsw-alias-label-secondary,#3f454d)}._SVViG_hint{background:color-mix(in srgb, var(--dsw-alias-bg-layer-1,#fff) 88%, transparent);backdrop-filter:blur(8px);bottom:130px}";
6572
6682
  const tagId$1 = "dsh-sidenote/annotate.module.css";
6573
6683
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
6574
6684
  const tag = document.createElement("style");
@@ -6578,35 +6688,35 @@ ${body}
6578
6688
  document.head.appendChild(tag);
6579
6689
  }
6580
6690
  var annotate_module_css_default = {
6581
- "confirmButton": "_SVViG_confirmButton",
6582
- "hintClose": "_SVViG_hintClose",
6583
- "dshSidenoteHintIn": "_SVViG_dshSidenoteHintIn",
6584
- "toolbarButton": "_SVViG_toolbarButton",
6585
- "deleteButton": "_SVViG_deleteButton",
6586
- "editorAsk": "_SVViG_editorAsk",
6587
6691
  "badge": "_SVViG_badge",
6588
- "badgeSent": "_SVViG_badgeSent",
6589
- "toolbarBelow": "_SVViG_toolbarBelow",
6692
+ "editorEdit": "_SVViG_editorEdit",
6693
+ "sentCardQuote": "_SVViG_sentCardQuote",
6694
+ "confirmButton": "_SVViG_confirmButton",
6590
6695
  "saveButton": "_SVViG_saveButton",
6591
- "editorTextarea": "_SVViG_editorTextarea",
6592
6696
  "cancelButton": "_SVViG_cancelButton",
6593
- "sentChipRow": "_SVViG_sentChipRow",
6697
+ "sentCardNote": "_SVViG_sentCardNote",
6698
+ "toolbarBelow": "_SVViG_toolbarBelow",
6699
+ "sentCardTitle": "_SVViG_sentCardTitle",
6700
+ "editorError": "_SVViG_editorError",
6701
+ "sentChip": "_SVViG_sentChip",
6702
+ "highlight": "_SVViG_highlight",
6594
6703
  "editorTarget": "_SVViG_editorTarget",
6704
+ "toolbarStatus": "_SVViG_toolbarStatus",
6705
+ "dshSidenoteHintIn": "_SVViG_dshSidenoteHintIn",
6706
+ "toolbarButton": "_SVViG_toolbarButton",
6595
6707
  "editorAskRow": "_SVViG_editorAskRow",
6596
- "highlight": "_SVViG_highlight",
6597
6708
  "editorInput": "_SVViG_editorInput",
6709
+ "deleteButton": "_SVViG_deleteButton",
6710
+ "hintClose": "_SVViG_hintClose",
6598
6711
  "toolbar": "_SVViG_toolbar",
6599
- "editorError": "_SVViG_editorError",
6600
- "editorNew": "_SVViG_editorNew",
6601
- "sentCardNote": "_SVViG_sentCardNote",
6602
- "toolbarStatus": "_SVViG_toolbarStatus",
6603
- "sentCardQuote": "_SVViG_sentCardQuote",
6604
- "hint": "_SVViG_hint",
6605
- "editorFooter": "_SVViG_editorFooter",
6606
- "editorEdit": "_SVViG_editorEdit",
6712
+ "editorAsk": "_SVViG_editorAsk",
6607
6713
  "editorSpacer": "_SVViG_editorSpacer",
6608
- "sentChip": "_SVViG_sentChip",
6609
- "sentCardTitle": "_SVViG_sentCardTitle"
6714
+ "badgeSent": "_SVViG_badgeSent",
6715
+ "editorFooter": "_SVViG_editorFooter",
6716
+ "hint": "_SVViG_hint",
6717
+ "editorNew": "_SVViG_editorNew",
6718
+ "sentChipRow": "_SVViG_sentChipRow",
6719
+ "editorTextarea": "_SVViG_editorTextarea"
6610
6720
  };
6611
6721
  //#endregion
6612
6722
  //#region src/client/annotate/hint.tsx
@@ -6791,6 +6901,17 @@ ${body}
6791
6901
  * so the selection survives until the click commits) and failure-safe (an
6792
6902
  * error boundary around the tree; layout math is wrapped in anchor.ts).
6793
6903
  */
6904
+ /** 两种新建注解入口共用紧凑的多行输入高度。 */
6905
+ function useAutoGrowingNote(note) {
6906
+ const input = (0, react.useRef)(null);
6907
+ (0, react.useLayoutEffect)(() => {
6908
+ const element = input.current;
6909
+ if (!element) return;
6910
+ element.style.height = "auto";
6911
+ element.style.height = `${Math.min(element.scrollHeight, 110)}px`;
6912
+ }, [note]);
6913
+ return input;
6914
+ }
6794
6915
  /** Badge anchor of a freshly captured selection (editor placement on create). */
6795
6916
  function selectionBadgePoint(snapshot) {
6796
6917
  const anchor = badgeAnchorOf(snapshot.range);
@@ -6880,11 +7001,11 @@ ${body}
6880
7001
  text: snapshot.text,
6881
7002
  anchorText: snapshot.anchorText,
6882
7003
  occurrence: snapshot.occurrence,
7004
+ anchors: snapshot.anchors,
6883
7005
  note: ""
6884
7006
  });
6885
- const cached = snapshot.range.cloneRange();
6886
7007
  rangeCache.current.set(annotation.id, {
6887
- range: cached,
7008
+ ranges: snapshot.ranges.map((range) => range.cloneRange()),
6888
7009
  view: snapshot.source.view
6889
7010
  });
6890
7011
  setEditor({
@@ -6908,6 +7029,7 @@ ${body}
6908
7029
  y: anchor.y,
6909
7030
  abort: new AbortController(),
6910
7031
  target,
7032
+ skipNote: !preference.showSideChatNote,
6911
7033
  ...targetTitle !== void 0 ? { targetTitle } : {}
6912
7034
  });
6913
7035
  controller.clear();
@@ -6928,13 +7050,18 @@ ${body}
6928
7050
  text: snapshot.text,
6929
7051
  anchorText: snapshot.anchorText,
6930
7052
  occurrence: snapshot.occurrence,
7053
+ anchors: snapshot.anchors,
6931
7054
  note
6932
7055
  });
6933
7056
  rangeCache.current.set(annotation.id, {
6934
- range: snapshot.range.cloneRange(),
7057
+ ranges: snapshot.ranges.map((range) => range.cloneRange()),
6935
7058
  view: snapshot.source.view
6936
7059
  });
6937
7060
  setSideDraft((current) => current === sideDraft ? null : current);
7061
+ if (sideDraft.skipNote) {
7062
+ const view = [...document.querySelectorAll("[data-sidenote-chat-session]")].find((element) => element.dataset.sidenoteChatSession === target.childId);
7063
+ if (view) focusSideComposer(view);
7064
+ }
6938
7065
  return true;
6939
7066
  } catch {
6940
7067
  return false;
@@ -6995,7 +7122,7 @@ ${body}
6995
7122
  onSave: (note) => {
6996
7123
  store.setNote(editingAnnotation.id, note);
6997
7124
  closeEditor();
6998
- const range = rangeCache.current.get(editingAnnotation.id)?.range;
7125
+ const range = rangeCache.current.get(editingAnnotation.id)?.ranges[0];
6999
7126
  const source = range === void 0 ? null : quoteSourceOf(range.startContainer, currentSessionId);
7000
7127
  if (source !== null) requestAnimationFrame(() => {
7001
7128
  if (document.activeElement === document.body) focusSourceComposer(source);
@@ -7018,6 +7145,7 @@ ${body}
7018
7145
  x: sideDraft.x,
7019
7146
  y: sideDraft.y,
7020
7147
  targetTitle: sideDraft.targetTitle,
7148
+ skipNote: sideDraft.skipNote,
7021
7149
  bounds: sourceClip(sideDraft.snapshot.source),
7022
7150
  onSave: commitSideDraft,
7023
7151
  onCancel: () => {
@@ -7114,6 +7242,8 @@ ${body}
7114
7242
  function SideChatNoteEditor(props) {
7115
7243
  useLocaleTick();
7116
7244
  const [note, setNote] = (0, react.useState)("");
7245
+ const [showNote, setShowNote] = (0, react.useState)(!props.skipNote);
7246
+ const noteInputRef = useAutoGrowingNote(note);
7117
7247
  const [failed, setFailed] = (0, react.useState)(false);
7118
7248
  const [pending, setPending] = (0, react.useState)(false);
7119
7249
  const rootRef = (0, react.useRef)(null);
@@ -7143,6 +7273,7 @@ ${body}
7143
7273
  if (cancelled.current) return;
7144
7274
  saved.current = ok;
7145
7275
  failedRef.current = !ok;
7276
+ if (!ok) setShowNote(true);
7146
7277
  setFailed(!ok);
7147
7278
  setPending(false);
7148
7279
  };
@@ -7161,6 +7292,9 @@ ${body}
7161
7292
  };
7162
7293
  const saveRef = (0, react.useRef)(save);
7163
7294
  saveRef.current = save;
7295
+ (0, react.useEffect)(() => {
7296
+ if (props.skipNote) saveRef.current();
7297
+ }, [props.skipNote]);
7164
7298
  (0, react.useEffect)(() => {
7165
7299
  const onKeyDown = (event) => {
7166
7300
  if (event.key === "Escape") {
@@ -7192,16 +7326,20 @@ ${body}
7192
7326
  style: {
7193
7327
  left,
7194
7328
  top,
7195
- width
7329
+ width,
7330
+ maxHeight: (props.bounds?.bottom ?? window.innerHeight) - top - 8,
7331
+ overflowY: "auto"
7196
7332
  },
7197
7333
  children: [
7198
7334
  props.targetTitle !== void 0 && props.targetTitle !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7199
7335
  className: annotate_module_css_default.editorTarget,
7200
7336
  children: t("sendToTarget", { title: props.targetTitle })
7201
7337
  }),
7202
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7338
+ showNote && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7203
7339
  className: annotate_module_css_default.editorAskRow,
7204
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
7340
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
7341
+ ref: noteInputRef,
7342
+ rows: 1,
7205
7343
  className: annotate_module_css_default.editorInput,
7206
7344
  value: note,
7207
7345
  placeholder: t("sideNotePlaceholder"),
@@ -7214,7 +7352,7 @@ ${body}
7214
7352
  setFailed(false);
7215
7353
  },
7216
7354
  onKeyDown: (event) => {
7217
- if (event.key !== "Enter") return;
7355
+ if (event.key !== "Enter" || event.shiftKey) return;
7218
7356
  if (event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) return;
7219
7357
  event.preventDefault();
7220
7358
  save();
@@ -7252,12 +7390,13 @@ ${body}
7252
7390
  const [hoveredId, setHoveredId] = (0, react.useState)(null);
7253
7391
  const highlightId = props.editingId ?? hoveredId;
7254
7392
  for (const annotation of annotations) {
7255
- const range = resolveRange(annotation, props.cache, props.sessionId);
7256
- if (range === null) continue;
7393
+ const ranges = resolveRanges(annotation, props.cache, props.sessionId);
7394
+ if (ranges === null) continue;
7395
+ const range = ranges[0];
7257
7396
  const source = quoteSourceOf(range.startContainer, props.sessionId);
7258
- if (source === null) continue;
7397
+ if (source === null || source.sessionId !== annotation.sessionId) continue;
7259
7398
  const clip = sourceClip(source);
7260
- if (annotation.id === highlightId) highlight = highlightRectsOf(range).map((rect) => {
7399
+ if (annotation.id === highlightId) highlight = ranges.flatMap(highlightRectsOf).map((rect) => {
7261
7400
  const left = Math.max(rect.left, clip.left), top = Math.max(rect.top, clip.top);
7262
7401
  return {
7263
7402
  left,
@@ -7325,6 +7464,7 @@ ${body}
7325
7464
  const rootRef = (0, react.useRef)(null);
7326
7465
  const noteRef = (0, react.useRef)(note);
7327
7466
  noteRef.current = note;
7467
+ const noteInputRef = useAutoGrowingNote(note);
7328
7468
  (0, react.useEffect)(() => {
7329
7469
  const onKeyDown = (event) => {
7330
7470
  if (event.key === "Escape") {
@@ -7359,7 +7499,9 @@ ${body}
7359
7499
  style: {
7360
7500
  left,
7361
7501
  top,
7362
- width
7502
+ width,
7503
+ maxHeight: (props.bounds?.bottom ?? window.innerHeight) - top - 8,
7504
+ overflowY: "auto"
7363
7505
  },
7364
7506
  children: [props.targetTitle !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7365
7507
  className: annotate_module_css_default.editorTarget,
@@ -7367,16 +7509,18 @@ ${body}
7367
7509
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7368
7510
  className: annotate_module_css_default.editorAskRow,
7369
7511
  style: { flex: 1 },
7370
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
7512
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
7513
+ ref: noteInputRef,
7371
7514
  className: annotate_module_css_default.editorInput,
7372
7515
  value: note,
7373
7516
  placeholder: t("notePlaceholder"),
7517
+ rows: 1,
7374
7518
  autoFocus: true,
7375
7519
  onChange: (event) => {
7376
7520
  setNote(event.target.value);
7377
7521
  },
7378
7522
  onKeyDown: (event) => {
7379
- if (event.key !== "Enter") return;
7523
+ if (event.key !== "Enter" || event.shiftKey) return;
7380
7524
  if (event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) return;
7381
7525
  event.preventDefault();
7382
7526
  save();
@@ -7810,6 +7954,7 @@ ${body}
7810
7954
  cancelInputRollback(input);
7811
7955
  const parts = [...options.leadingBlocks ?? []];
7812
7956
  if (annotations.length > 0) parts.push(buildProtocolBlock(annotations));
7957
+ const wireNumbers = new Map(annotations.map((annotation) => [annotation.id, annotation.number]));
7813
7958
  const block = parts.join("\n\n");
7814
7959
  if (block === "") return false;
7815
7960
  const draftBeforeWait = input.state.getSnapshot();
@@ -7831,7 +7976,10 @@ ${body}
7831
7976
  }
7832
7977
  receipt.submit(options.mode);
7833
7978
  await receipt.outcome;
7834
- store.markSent(annotations.filter((annotation) => store.get(annotation.id) === annotation).map((annotation) => annotation.id));
7979
+ store.markSent(annotations.filter((annotation) => {
7980
+ const current = store.get(annotation.id);
7981
+ return current !== void 0 && current.state === "active" && (current === annotation || current.note === annotation.note && current.text === annotation.text);
7982
+ }).map((annotation) => annotation.id), wireNumbers);
7835
7983
  return true;
7836
7984
  } catch (error) {
7837
7985
  rollbackInputPrefix(input, prefix, staged ? () => {
@@ -8206,7 +8354,7 @@ ${body}
8206
8354
  }
8207
8355
  //#endregion
8208
8356
  //#region src/client/preferences.ts
8209
- /** Share one host-backed value between every main and side conversation. */
8357
+ /** Share host-backed preferences between every main and side conversation. */
8210
8358
  function createSidenotePreferences(form) {
8211
8359
  const listeners = /* @__PURE__ */ new Set();
8212
8360
  let saving = false;
@@ -8215,6 +8363,7 @@ ${body}
8215
8363
  const state = form.getSnapshot();
8216
8364
  return {
8217
8365
  hidden: state.value?.hideNativeQuote ?? true,
8366
+ showSideChatNote: state.value?.showSideChatNote ?? true,
8218
8367
  status: state.status,
8219
8368
  writable: state.writable,
8220
8369
  saving,
@@ -8227,6 +8376,20 @@ ${body}
8227
8376
  for (const listener of listeners) listener();
8228
8377
  };
8229
8378
  const unsubscribe = form.subscribe(publish);
8379
+ const set = async (field, value) => {
8380
+ if (saving || snapshot.status !== "ready" || !snapshot.writable) return;
8381
+ saving = true;
8382
+ failed = false;
8383
+ publish();
8384
+ try {
8385
+ failed = !await form.set(field, value);
8386
+ } catch {
8387
+ failed = true;
8388
+ } finally {
8389
+ saving = false;
8390
+ publish();
8391
+ }
8392
+ };
8230
8393
  return {
8231
8394
  getSnapshot: () => snapshot,
8232
8395
  subscribe(listener) {
@@ -8235,20 +8398,8 @@ ${body}
8235
8398
  listeners.delete(listener);
8236
8399
  };
8237
8400
  },
8238
- async setHidden(hidden) {
8239
- if (saving || snapshot.status !== "ready" || !snapshot.writable) return;
8240
- saving = true;
8241
- failed = false;
8242
- publish();
8243
- try {
8244
- failed = !await form.set("hideNativeQuote", hidden);
8245
- } catch {
8246
- failed = true;
8247
- } finally {
8248
- saving = false;
8249
- publish();
8250
- }
8251
- },
8401
+ setHidden: (hidden) => set("hideNativeQuote", hidden),
8402
+ setShowSideChatNote: (show) => set("showSideChatNote", show),
8252
8403
  dispose() {
8253
8404
  unsubscribe();
8254
8405
  listeners.clear();
@@ -8267,15 +8418,15 @@ ${body}
8267
8418
  document.head.appendChild(tag);
8268
8419
  }
8269
8420
  var settings_module_css_default = {
8270
- "page": "_2xJwMG_page",
8421
+ "hint": "_2xJwMG_hint",
8271
8422
  "description": "_2xJwMG_description",
8272
- "error": "_2xJwMG_error",
8273
8423
  "status": "_2xJwMG_status",
8274
8424
  "row": "_2xJwMG_row",
8275
- "hint": "_2xJwMG_hint",
8276
- "label": "_2xJwMG_label",
8425
+ "page": "_2xJwMG_page",
8426
+ "copy": "_2xJwMG_copy",
8277
8427
  "title": "_2xJwMG_title",
8278
- "copy": "_2xJwMG_copy"
8428
+ "label": "_2xJwMG_label",
8429
+ "error": "_2xJwMG_error"
8279
8430
  };
8280
8431
  //#endregion
8281
8432
  //#region src/client/settings.tsx
@@ -8322,6 +8473,32 @@ ${body}
8322
8473
  }
8323
8474
  })]
8324
8475
  }),
8476
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8477
+ className: settings_module_css_default.row,
8478
+ role: "group",
8479
+ "aria-labelledby": `${id}-side-note-label`,
8480
+ "aria-describedby": `${id}-side-note-description`,
8481
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8482
+ className: settings_module_css_default.copy,
8483
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8484
+ id: `${id}-side-note-label`,
8485
+ className: settings_module_css_default.label,
8486
+ children: t("showSideChatNote")
8487
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8488
+ id: `${id}-side-note-description`,
8489
+ className: settings_module_css_default.hint,
8490
+ children: t("showSideChatNoteHint")
8491
+ })]
8492
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
8493
+ checked: state.showSideChatNote,
8494
+ label: t("showSideChatNote"),
8495
+ title: t("showSideChatNoteHint"),
8496
+ disabled: state.status !== "ready" || !state.writable || state.saving,
8497
+ onChange: (show) => {
8498
+ preferences.setShowSideChatNote(show);
8499
+ }
8500
+ })]
8501
+ }),
8325
8502
  state.saving && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8326
8503
  className: settings_module_css_default.status,
8327
8504
  role: "status",
@@ -8365,6 +8542,41 @@ ${body}
8365
8542
  }, ({ size }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconListPenOutline16, { size })));
8366
8543
  }
8367
8544
  //#endregion
8545
+ //#region src/client/host/replyDefaults.ts
8546
+ function initializeFinalReplies(sidenote, chat) {
8547
+ let stopped = false;
8548
+ let attempted = false;
8549
+ const initialize = () => {
8550
+ if (stopped || attempted) return;
8551
+ const own = sidenote.getSnapshot(), target = chat.getSnapshot();
8552
+ if (own.status !== "ready" || target.status !== "ready" || !own.writable || !target.writable || !own.value || !target.value || own.revision === void 0 || target.revision === void 0) return;
8553
+ if (own.value.finalRepliesDefaultAttempted) return;
8554
+ attempted = true;
8555
+ (async () => {
8556
+ if (!await sidenote.mutate([{
8557
+ op: "set",
8558
+ path: ["finalRepliesDefaultAttempted"],
8559
+ value: true
8560
+ }], own.revision) || stopped) return;
8561
+ if (target.value.foldedReplies !== "final") await chat.mutate([{
8562
+ op: "set",
8563
+ path: ["foldedReplies"],
8564
+ value: "final"
8565
+ }], target.revision);
8566
+ })().catch(() => {
8567
+ console.warn("[dsh-sidenote] 最终回答显示偏好初始化未完成,保留当前设置。");
8568
+ });
8569
+ };
8570
+ const unsubscribeOwn = sidenote.subscribe(initialize);
8571
+ const unsubscribeChat = chat.subscribe(initialize);
8572
+ initialize();
8573
+ return () => {
8574
+ stopped = true;
8575
+ unsubscribeOwn();
8576
+ unsubscribeChat();
8577
+ };
8578
+ }
8579
+ //#endregion
8368
8580
  //#region src/client/index.tsx
8369
8581
  const inject = [
8370
8582
  "sessions",
@@ -8387,10 +8599,13 @@ ${body}
8387
8599
  sweepOrphanedSideChatMeta();
8388
8600
  const reflow = createReflowStore();
8389
8601
  const annotations = createAnnotationStore();
8390
- const preferences = createSidenotePreferences(ctx.get("configForms").get("side-chat"));
8602
+ const forms = ctx.get("configForms");
8603
+ const form = forms.get("side-chat");
8604
+ const preferences = createSidenotePreferences(form);
8391
8605
  ctx.effect(() => () => {
8392
8606
  preferences.dispose();
8393
8607
  }, "dsh-sidenote: preferences");
8608
+ ctx.effect(() => initializeFinalReplies(form, forms.get("ui-chat")), "dsh-sidenote: final replies default");
8394
8609
  registerSidenoteSettings(ctx, preferences);
8395
8610
  registerSideChat(ctx, reflow, annotations);
8396
8611
  registerAnnotations(ctx, reflow, annotations, preferences);