dsh-better-display 0.3.8 → 0.3.10

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 (190) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/DESIGN.md +43 -3
  3. package/README.en.md +10 -2
  4. package/README.md +10 -2
  5. package/compat/harness-rc2.json +17 -13
  6. package/docs/dependency-update-policy.md +9 -0
  7. package/docs/extension-points.md +172 -0
  8. package/docs/official-rendering-bridge.md +49 -20
  9. package/docs/openapi.yaml +147 -0
  10. package/docs/runbooks/blank-reader.md +39 -0
  11. package/lib/client.js +1802 -872
  12. package/lib/client.js.map +1 -1
  13. package/lib/dsh-better-display.js +376 -151
  14. package/lib/types/client/AssistantNode.d.ts +20 -0
  15. package/lib/types/client/AssistantNode.d.ts.map +1 -0
  16. package/lib/types/client/AssistantNode.js +46 -0
  17. package/lib/types/client/AssistantNode.js.map +1 -0
  18. package/lib/types/client/Blocks.d.ts.map +1 -1
  19. package/lib/types/client/Blocks.js +33 -10
  20. package/lib/types/client/Blocks.js.map +1 -1
  21. package/lib/types/client/ChoreographedFlow.d.ts.map +1 -1
  22. package/lib/types/client/ChoreographedFlow.js +2 -0
  23. package/lib/types/client/ChoreographedFlow.js.map +1 -1
  24. package/lib/types/client/ClosedProcessSummary.d.ts.map +1 -1
  25. package/lib/types/client/ClosedProcessSummary.js +3 -1
  26. package/lib/types/client/ClosedProcessSummary.js.map +1 -1
  27. package/lib/types/client/McpAppFrame.d.ts.map +1 -1
  28. package/lib/types/client/McpAppFrame.js +5 -11
  29. package/lib/types/client/McpAppFrame.js.map +1 -1
  30. package/lib/types/client/OfficialContent.d.ts +17 -3
  31. package/lib/types/client/OfficialContent.d.ts.map +1 -1
  32. package/lib/types/client/OfficialContent.js +37 -12
  33. package/lib/types/client/OfficialContent.js.map +1 -1
  34. package/lib/types/client/Reader.d.ts.map +1 -1
  35. package/lib/types/client/Reader.js +45 -77
  36. package/lib/types/client/Reader.js.map +1 -1
  37. package/lib/types/client/SettingsSection.d.ts.map +1 -1
  38. package/lib/types/client/SettingsSection.js +0 -5
  39. package/lib/types/client/SettingsSection.js.map +1 -1
  40. package/lib/types/client/StickyLane.d.ts +12 -0
  41. package/lib/types/client/StickyLane.d.ts.map +1 -1
  42. package/lib/types/client/StickyLane.js +78 -2
  43. package/lib/types/client/StickyLane.js.map +1 -1
  44. package/lib/types/client/ToolActivity.js.map +1 -1
  45. package/lib/types/client/index.d.ts +3 -1
  46. package/lib/types/client/index.d.ts.map +1 -1
  47. package/lib/types/client/index.js +6 -0
  48. package/lib/types/client/index.js.map +1 -1
  49. package/lib/types/client/live-turn.d.ts +17 -5
  50. package/lib/types/client/live-turn.d.ts.map +1 -1
  51. package/lib/types/client/live-turn.js +106 -76
  52. package/lib/types/client/live-turn.js.map +1 -1
  53. package/lib/types/client/motion.d.ts +26 -2
  54. package/lib/types/client/motion.d.ts.map +1 -1
  55. package/lib/types/client/motion.js +136 -10
  56. package/lib/types/client/motion.js.map +1 -1
  57. package/lib/types/client/official-slots.d.ts +24 -3
  58. package/lib/types/client/official-slots.d.ts.map +1 -1
  59. package/lib/types/client/official-slots.js +59 -11
  60. package/lib/types/client/official-slots.js.map +1 -1
  61. package/lib/types/client/primitive-labels.d.ts.map +1 -1
  62. package/lib/types/client/primitive-labels.js +1 -0
  63. package/lib/types/client/primitive-labels.js.map +1 -1
  64. package/lib/types/client/projection.d.ts +15 -2
  65. package/lib/types/client/projection.d.ts.map +1 -1
  66. package/lib/types/client/projection.js +16 -4
  67. package/lib/types/client/projection.js.map +1 -1
  68. package/lib/types/client/rewind-cut.d.ts +131 -0
  69. package/lib/types/client/rewind-cut.d.ts.map +1 -0
  70. package/lib/types/client/rewind-cut.js +321 -0
  71. package/lib/types/client/rewind-cut.js.map +1 -0
  72. package/lib/types/client/text-transform.d.ts +93 -0
  73. package/lib/types/client/text-transform.d.ts.map +1 -0
  74. package/lib/types/client/text-transform.js +183 -0
  75. package/lib/types/client/text-transform.js.map +1 -0
  76. package/lib/types/client/timeline.d.ts.map +1 -1
  77. package/lib/types/client/timeline.js.map +1 -1
  78. package/lib/types/client/tool-activity.d.ts +9 -2
  79. package/lib/types/client/tool-activity.d.ts.map +1 -1
  80. package/lib/types/client/tool-activity.js +42 -2
  81. package/lib/types/client/tool-activity.js.map +1 -1
  82. package/lib/types/client/turn-slots.d.ts +29 -0
  83. package/lib/types/client/turn-slots.d.ts.map +1 -0
  84. package/lib/types/client/turn-slots.js +62 -0
  85. package/lib/types/client/turn-slots.js.map +1 -0
  86. package/lib/types/client/types.d.ts +18 -0
  87. package/lib/types/client/types.d.ts.map +1 -1
  88. package/lib/types/dsh-better-display.d.ts +4 -1
  89. package/lib/types/dsh-better-display.d.ts.map +1 -1
  90. package/lib/types/dsh-better-display.js +56 -7
  91. package/lib/types/dsh-better-display.js.map +1 -1
  92. package/lib/types/flags.d.ts +7 -0
  93. package/lib/types/flags.d.ts.map +1 -0
  94. package/lib/types/flags.js +37 -0
  95. package/lib/types/flags.js.map +1 -0
  96. package/lib/types/log.d.ts +17 -0
  97. package/lib/types/log.d.ts.map +1 -0
  98. package/lib/types/log.js +122 -0
  99. package/lib/types/log.js.map +1 -0
  100. package/lib/types/metrics.d.ts +18 -0
  101. package/lib/types/metrics.d.ts.map +1 -0
  102. package/lib/types/metrics.js +35 -0
  103. package/lib/types/metrics.js.map +1 -0
  104. package/package.json +55 -23
  105. package/scripts/changelog.mjs +20 -0
  106. package/scripts/check-agents-md.mjs +67 -0
  107. package/scripts/check-bundle-size.mjs +38 -0
  108. package/scripts/check-dead-flags.mjs +55 -0
  109. package/scripts/check-harness-compat.mjs +4 -2
  110. package/scripts/check-todo-debt.mjs +69 -0
  111. package/scripts/link-harness-dependencies.mjs +48 -11
  112. package/scripts/security-audit-report.mjs +92 -0
  113. package/scripts/verify-commentary-visible.mjs +627 -0
  114. package/src/client/AssistantNode.tsx +72 -0
  115. package/src/client/Blocks.tsx +49 -10
  116. package/src/client/ChoreographedFlow.tsx +3 -1
  117. package/src/client/ClosedProcessSummary.tsx +3 -1
  118. package/src/client/McpAppFrame.tsx +5 -13
  119. package/src/client/OfficialContent.tsx +52 -14
  120. package/src/client/Reader.module.css +68 -18
  121. package/src/client/Reader.tsx +38 -82
  122. package/src/client/SettingsSection.tsx +1 -7
  123. package/src/client/StickyLane.tsx +81 -2
  124. package/src/client/ToolActivity.tsx +1 -1
  125. package/src/client/index.tsx +8 -1
  126. package/src/client/live-turn.ts +100 -69
  127. package/src/client/motion.tsx +135 -7
  128. package/src/client/official-slots.tsx +61 -12
  129. package/src/client/primitive-labels.ts +1 -0
  130. package/src/client/projection.ts +16 -4
  131. package/src/client/rewind-cut.ts +340 -0
  132. package/src/client/text-transform.ts +265 -0
  133. package/src/client/timeline.ts +0 -2
  134. package/src/client/tool-activity.ts +49 -2
  135. package/src/client/turn-slots.ts +80 -0
  136. package/src/client/types.ts +19 -0
  137. package/src/dsh-better-display.ts +58 -7
  138. package/src/flags.ts +40 -0
  139. package/src/log.ts +137 -0
  140. package/src/metrics.ts +50 -0
  141. package/tests/browser-commentary.tsx +191 -0
  142. package/tests/browser-official-bridge.tsx +2 -1
  143. package/tests/chat-flow-hook.test.ts +1 -1
  144. package/tests/cloud-gate.test.ts +1 -1
  145. package/tests/context-tool-update.test.ts +1 -1
  146. package/tests/conversation-scroll.test.ts +1 -1
  147. package/tests/deliverables.test.ts +1 -1
  148. package/tests/entry.test.ts +1 -1
  149. package/tests/fixtures/reader-lane-owners.tsx +46 -0
  150. package/tests/fixtures/reader-official-users.tsx +64 -0
  151. package/tests/fixtures/reader-theme-overlap.html +80 -0
  152. package/tests/fixtures/reader-user-bubble.tsx +244 -0
  153. package/tests/flags.test.ts +66 -0
  154. package/tests/fold-choreography.test.ts +32 -8
  155. package/tests/fold-intensity.test.ts +11 -6
  156. package/tests/footer-precedence.test.ts +1 -1
  157. package/tests/history-word-regression.test.ts +1 -1
  158. package/tests/live-turn.test.ts +125 -54
  159. package/tests/log.test.ts +99 -0
  160. package/tests/mcp-apps.test.ts +1 -1
  161. package/tests/message-chrome.test.ts +1 -1
  162. package/tests/metrics.test.ts +52 -0
  163. package/tests/official-slots.test.ts +41 -2
  164. package/tests/open-file.test.ts +1 -1
  165. package/tests/pending-submission.test.ts +12 -1
  166. package/tests/pinned-selection.test.ts +16 -0
  167. package/tests/platform-media.test.ts +1 -1
  168. package/tests/projection.test.ts +66 -3
  169. package/tests/reader-glass-css.test.ts +1 -1
  170. package/tests/reader-official-users.test.ts +11 -1
  171. package/tests/reader-sticky-surface.test.ts +377 -0
  172. package/tests/reader-theme-lanes.test.ts +54 -0
  173. package/tests/reader-theme-overlap.test.ts +151 -0
  174. package/tests/reader-user-bubble.test.ts +138 -0
  175. package/tests/reasoning-follow.test.ts +1 -1
  176. package/tests/retiring-content.test.ts +98 -6
  177. package/tests/reveal.test.ts +3 -3
  178. package/tests/rewind-cut.test.ts +1090 -0
  179. package/tests/settings-copy.test.ts +1 -1
  180. package/tests/skill-status.test.ts +1 -1
  181. package/tests/stock-install.test.ts +11 -7
  182. package/tests/stream-buffer.test.ts +1 -1
  183. package/tests/styles.test.ts +30 -3
  184. package/tests/terminal-input-preview.test.ts +1 -1
  185. package/tests/text-transform.test.ts +272 -0
  186. package/tests/timeline.test.ts +1 -1
  187. package/tests/tool-clock.test.ts +1 -1
  188. package/tests/tool-diff.test.ts +1 -1
  189. package/tests/waiting-clock.test.ts +1 -1
  190. package/tests/word-timeline.test.ts +1 -1
package/lib/client.js CHANGED
@@ -4,7 +4,7 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
- //#region \0rolldown/runtime.js
7
+ // \0rolldown/runtime.js
8
8
  var __defProp = Object.defineProperty;
9
9
  var __exportAll = (all, no_symbols) => {
10
10
  let target = {};
@@ -15,12 +15,11 @@ window.__ModuleLoader__.load({
15
15
  if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
16
16
  return target;
17
17
  };
18
- //#endregion
19
18
  let react = require("react");
20
19
  let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
21
20
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
22
21
  let react_jsx_runtime = require("react/jsx-runtime");
23
- //#region node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
22
+ // node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
24
23
  /**
25
24
  * The `dsh-resource://file/…` address grammar: how a file is named across the
26
25
  * Sidebar and the resource model, built and parsed without touching a
@@ -108,13 +107,285 @@ window.__ModuleLoader__.load({
108
107
  if (root !== "" && normalized.startsWith(`${root}/`)) return sessionFileAddress(sessionId, normalized.slice(root.length + 1));
109
108
  return sessionFileAddress(sessionId, normalized);
110
109
  }
111
- //#endregion
112
- //#region src/client/tool-activity.ts
113
- /** Public Step data includes tool-only model output before a chat node exists. */
114
- function readerFlow(group, turn, get) {
110
+ // src/client/rewind-cut.ts
111
+ function asRecord$1(value) {
112
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
113
+ }
114
+ function safeSeq(value) {
115
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
116
+ }
117
+ /** The command payload of a `command` chat node, when it is structurally readable. */
118
+ function commandOf(node) {
119
+ if (node.kind !== "command") return void 0;
120
+ return asRecord$1(node.data);
121
+ }
122
+ /**
123
+ * The engine names an executed rewind command node can carry. The plugin
124
+ * registers its command as `rewind-plugin` (`ENGINE_COMMANDS = new
125
+ * Set(['rewind-plugin', 'rewind'])` in dsh-rewind-plugin's
126
+ * `src/client/hidden.ts`), and that is the name the client projection actually
127
+ * publishes — the session log's `command/run.name` is not what the Reader sees.
128
+ * `rewind` stays accepted because older logs and the bili-side proxy record the
129
+ * shorter spelling.
130
+ */
131
+ const REWIND_COMMANDS = /* @__PURE__ */ new Set(["rewind-plugin", "rewind"]);
132
+ /** True when a command payload is the rewind engine command, in either spelling. */
133
+ function isRewindCommand(command) {
134
+ return typeof command.name === "string" && REWIND_COMMANDS.has(command.name);
135
+ }
136
+ /**
137
+ * The render POSITION a row is anchored at: the view node's `anchorSeq`, else
138
+ * its payload seq.
139
+ *
140
+ * This is deliberately NOT integer-only. The host anchors synthetic rows at
141
+ * fractional offsets off a real seq (`CHAT_SYNTHETIC_SEQ_OFFSETS` in the
142
+ * official ChatView: `turn-process` at `controlAnchorSeq - 0.1`, `turn-tail` at
143
+ * `seq + 0.1`, an interrupted assistant step at `seq - 0.9`). An integer-only
144
+ * read made those rows invisible to the cut, so the last `turn-process` of a
145
+ * withdrawn turn survived on its own. Log seqs (marker, target) stay integer:
146
+ * see `safeSeq`.
147
+ */
148
+ function anchorSeqOf(node) {
149
+ const raw = node.anchorSeq ?? asRecord$1(node.data)?.seq;
150
+ return typeof raw === "number" && Number.isFinite(raw) && raw >= 0 ? raw : void 0;
151
+ }
152
+ /**
153
+ * Extract the rewind target seq from a `/rewind` command's structured `args`
154
+ * (e.g. `@5 chat`, `preview @5 both`). Locale-independent: it never parses the
155
+ * host's human outcome copy.
156
+ */
157
+ function rewindTargetOfArgs(args) {
158
+ if (typeof args !== "string") return void 0;
159
+ const match = args.match(/@(\d+)/);
160
+ return match === null ? void 0 : Number(match[1]);
161
+ }
162
+ /** True for the internal candidate-list probe (`/rewind __candidates`). */
163
+ function isCandidateCommand(command) {
164
+ return typeof command.args === "string" && command.args.includes("__candidates");
165
+ }
166
+ /** True for an impact preview (`/rewind preview @5 both`). */
167
+ function isPreviewCommand(command) {
168
+ return typeof command.args === "string" && command.args.includes("preview");
169
+ }
170
+ /**
171
+ * Merge overlapping or touching shadow ranges.
172
+ *
173
+ * Spans are kept SEPARATE unless they genuinely overlap: collapsing every cut
174
+ * to `[min target, max marker]` would hide a still-on-surface gap of new traffic
175
+ * between an earlier marker and a later target.
176
+ */
177
+ function coalesceCutSpans(spans) {
178
+ if (spans.length < 2) return [...spans];
179
+ const sorted = [...spans].sort((left, right) => left.start - right.start || left.end - right.end);
180
+ const merged = [];
181
+ for (const span of sorted) {
182
+ const last = merged.at(-1);
183
+ if (last !== void 0 && span.start <= last.end) {
184
+ if (span.end > last.end) last.end = span.end;
185
+ continue;
186
+ }
187
+ merged.push({
188
+ start: span.start,
189
+ end: span.end
190
+ });
191
+ }
192
+ return merged;
193
+ }
194
+ /**
195
+ * True when a row anchored at `seq` falls inside one of the cut spans.
196
+ *
197
+ * A cut runs from the target seq through the marker's own log seq, inclusive
198
+ * (`target <= anchorSeq && anchorSeq <= marker`), the same comparison
199
+ * `dsh-rewind-plugin`'s `RewindVisibilityBuilder` uses. The upper bound stops
200
+ * at the marker. Extending it to the next integer hides the following turn's
201
+ * process row, which the host anchors at `startSeq - 0.1` and therefore in
202
+ * `[marker, marker + 1)`. Fractional anchors inside the closed range still
203
+ * match: `anchorSeqOf` keeps them. A process or tail row parked outside the
204
+ * range is handled by `summaryTurns`, not by widening the cut. Spans stay
205
+ * disjoint, so new traffic between two rewinds survives.
206
+ */
207
+ function cutContains(cuts, seq) {
208
+ let low = 0;
209
+ let high = cuts.length - 1;
210
+ let found = -1;
211
+ while (low <= high) {
212
+ const middle = low + high >> 1;
213
+ const cut = cuts[middle];
214
+ if (cut === void 0) return false;
215
+ if (seq < cut.start) high = middle - 1;
216
+ else {
217
+ found = middle;
218
+ low = middle + 1;
219
+ }
220
+ }
221
+ const span = found < 0 ? void 0 : cuts[found];
222
+ return span !== void 0 && seq <= span.end;
223
+ }
224
+ /** The turn id a node belongs to (absent outside a turn). */
225
+ function turnIdOf(node) {
226
+ const turn = asRecord$1(asRecord$1(node.location)?.turn);
227
+ if (turn === void 0 || !Object.hasOwn(turn, "turn")) return void 0;
228
+ return turn.turn;
229
+ }
230
+ /**
231
+ * Node kinds that SUMMARIZE a turn instead of carrying its content, so the host
232
+ * anchors them at the turn's edges rather than at an event inside it.
233
+ */
234
+ const TURN_SUMMARY_KINDS = /* @__PURE__ */ new Set(["turn-process", "turn-tail"]);
235
+ /**
236
+ * True for a rewind command row the transcript should not keep.
237
+ *
238
+ * Probes (`preview`, `__candidates`) exist only to feed the popover. An
239
+ * executed rewind — success plus a marker seq — is the command that performed
240
+ * the cut, and the plugin hides that row even when its anchor sits outside
241
+ * the numeric range. A failed or still-running command stays visible.
242
+ */
243
+ function commandHidden(node) {
244
+ const command = commandOf(node);
245
+ if (command === void 0 || !isRewindCommand(command)) return false;
246
+ if (isPreviewCommand(command) || isCandidateCommand(command)) return true;
247
+ const outcome = asRecord$1(command.outcome);
248
+ return outcome?.kind === "success" && safeSeq(outcome.sourceEventSeq) !== void 0;
249
+ }
250
+ /** True when the row's own anchor falls in a cut, or the row is a rewind command to hide. */
251
+ function anchorWithdrawn(node, cuts) {
252
+ if (commandHidden(node)) return true;
253
+ const seq = anchorSeqOf(node);
254
+ return seq !== void 0 && cutContains(cuts, seq);
255
+ }
256
+ /**
257
+ * Turns whose process and tail rows must leave.
258
+ *
259
+ * The host opens a turn before its user message is logged and anchors
260
+ * `turn-process` at `controlAnchorSeq - 0.1`, below the first withdrawn
261
+ * event, and `turn-tail` at `turn.end.seq + 0.1`, which can sit past the
262
+ * marker. Containment therefore misses those two rows, and the transcript
263
+ * kept announcing `执行过程 / 本轮已停止,已生成的内容仍保留。`
264
+ *
265
+ * A summary leaves only when every content row of its turn is already
266
+ * withdrawn — the same rule as the plugin's `deriveTurnProcess`. One content
267
+ * row before the target (a mid-turn steering rewind) keeps the summary and
268
+ * that earlier message. A turn with no content rows is left alone.
269
+ */
270
+ function summaryTurns(chat, cuts) {
271
+ const stays = /* @__PURE__ */ new Map();
272
+ for (const key of chat.order) {
273
+ const node = chat.nodes.get(key);
274
+ if (node === void 0 || TURN_SUMMARY_KINDS.has(node.kind ?? "")) continue;
275
+ const id = turnIdOf(node);
276
+ if (id === void 0) continue;
277
+ const visible = node.visibility !== "hidden" && !anchorWithdrawn(node, cuts);
278
+ stays.set(id, (stays.get(id) ?? false) || visible);
279
+ }
280
+ const withdrawn = /* @__PURE__ */ new Set();
281
+ for (const [id, visible] of stays) if (!visible) withdrawn.add(id);
282
+ return withdrawn;
283
+ }
284
+ /** True when this row is a summary of a turn whose content is fully withdrawn. */
285
+ function summaryWithdrawn(node, summaries) {
286
+ if (!TURN_SUMMARY_KINDS.has(node.kind ?? "")) return false;
287
+ const id = turnIdOf(node);
288
+ return id !== void 0 && summaries.has(id);
289
+ }
290
+ /**
291
+ * The shadow ranges a session's executed rewinds produced, newest state last.
292
+ * An executed rewind cuts `[target, marker]` — the target itself is included,
293
+ * because a rewind restores the state *before* that message was sent (its text
294
+ * goes back to the composer).
295
+ */
296
+ function rewindCutSpans(chat) {
297
+ if (chat === void 0) return [];
298
+ const spans = [];
299
+ for (const key of chat.order) {
300
+ const node = chat.nodes.get(key);
301
+ if (node === void 0) continue;
302
+ const command = commandOf(node);
303
+ if (command === void 0 || !isRewindCommand(command)) continue;
304
+ if (isPreviewCommand(command) || isCandidateCommand(command)) continue;
305
+ const outcome = asRecord$1(command.outcome);
306
+ if (outcome?.kind !== "success") continue;
307
+ const marker = safeSeq(outcome.sourceEventSeq);
308
+ if (marker === void 0) continue;
309
+ const target = rewindTargetOfArgs(command.args);
310
+ if (target === void 0 || target > marker) continue;
311
+ spans.push({
312
+ start: target,
313
+ end: marker
314
+ });
315
+ }
316
+ return coalesceCutSpans(spans);
317
+ }
318
+ /**
319
+ * The chat keys the Reader should render, in snapshot order: withdrawn rows and
320
+ * rows the host itself marked hidden are dropped, everything else keeps its
321
+ * position. Returns the snapshot's own `order` array when nothing is filtered,
322
+ * so React selectors see a stable reference.
323
+ */
324
+ function visibleChatKeys(chat) {
325
+ const cuts = rewindCutSpans(chat);
326
+ const summaries = summaryTurns(chat, cuts);
327
+ const visible = [];
328
+ for (const key of chat.order) {
329
+ const node = chat.nodes.get(key);
330
+ if (node === void 0) continue;
331
+ if (node.visibility === "hidden") continue;
332
+ if (anchorWithdrawn(node, cuts) || summaryWithdrawn(node, summaries)) continue;
333
+ visible.push(key);
334
+ }
335
+ return visible.length === chat.order.length ? chat.order : visible;
336
+ }
337
+ /**
338
+ * A comparable signal for the visible row set, built for
339
+ * `useSyncExternalStore`-style subscription: the host re-runs a chat selector
340
+ * on every publish and compares the RESULT by `Object.is`, and a rewind
341
+ * publishes an in-place upsert of an existing command row. The snapshot's
342
+ * `order` array and node store are therefore both reference-stable across a
343
+ * rewind, so a selector on them never re-renders; a string does change exactly
344
+ * when the visible set does. Never return the array itself from a selector —
345
+ * a fresh array fails the `Object.is` check on every publish.
346
+ */
347
+ function visibleChatSignature(chat) {
348
+ return visibleChatKeys(chat).join("\0");
349
+ }
350
+ // src/client/tool-activity.ts
351
+ /**
352
+ * callId of a tool-call chat node. Drafts for a call whose node was cut must
353
+ * not be reintroduced after `group.keys` has already dropped that node.
354
+ */
355
+ function toolCallId(node) {
356
+ const callId = node.data?.root?.callId;
357
+ return typeof callId === "string" && callId.length > 0 ? callId : void 0;
358
+ }
359
+ /** callIds whose tool-call node sits inside a rewind cut. */
360
+ function cutToolCallIds(order, get, spans) {
361
+ const ids = /* @__PURE__ */ new Set();
362
+ for (const key of order) {
363
+ const node = get(key);
364
+ if (node?.kind !== "tool-call") continue;
365
+ const seq = anchorSeqOf(node);
366
+ if (seq === void 0 || !cutContains(spans, seq)) continue;
367
+ const callId = toolCallId(node);
368
+ if (callId !== void 0) ids.add(callId);
369
+ }
370
+ return ids;
371
+ }
372
+ /**
373
+ * Public Step data includes tool-only model output before a chat node exists.
374
+ *
375
+ * `sourceOrder` is the unfiltered chat order. Drafts that belong to a cut step,
376
+ * or to a call whose tool-call node was cut, are not put back. A draft that
377
+ * still has no tool-call node, on a step outside the cut, stays — that is the
378
+ * in-flight call the host has not published a node for yet.
379
+ */
380
+ function readerFlow(group, turn, get, sourceOrder) {
115
381
  const flow = [];
116
382
  const calls = /* @__PURE__ */ new Map();
117
383
  const assistantOrder = /* @__PURE__ */ new Map();
384
+ const spans = sourceOrder === void 0 ? [] : rewindCutSpans({
385
+ order: sourceOrder,
386
+ nodes: { get }
387
+ });
388
+ const cutCalls = spans.length === 0 || sourceOrder === void 0 ? void 0 : cutToolCallIds(sourceOrder, get, spans);
118
389
  for (const key of group.keys) {
119
390
  const node = get(key);
120
391
  if (!node || node.visibility === "hidden") continue;
@@ -141,10 +412,13 @@ window.__ModuleLoader__.load({
141
412
  }
142
413
  for (const step of turn?.steps ?? []) {
143
414
  const data = step.data.get("assistant-step");
415
+ const stepSeq = step.start?.seq;
416
+ const stepCut = typeof stepSeq === "number" && spans.length > 0 && cutContains(spans, stepSeq);
144
417
  let index = 0;
145
418
  for (const draft of data?.blocks ?? []) {
146
419
  if (draft.kind !== "tool-call" || !draft.callId) continue;
147
420
  const previous = calls.get(draft.callId);
421
+ if (!previous && (stepCut || cutCalls?.has(draft.callId) === true)) continue;
148
422
  calls.set(draft.callId, previous ? {
149
423
  ...previous,
150
424
  draft
@@ -445,8 +719,7 @@ window.__ModuleLoader__.load({
445
719
  function preparingLabel(name) {
446
720
  return /^(write|edit|apply_patch)$/.test(name) ? "正在生成文件内容" : /^(bash|shell|exec_command|pwsh)$/.test(name) ? "正在准备命令" : "正在准备工具输入";
447
721
  }
448
- //#endregion
449
- //#region src/client/deliverables.ts
722
+ // src/client/deliverables.ts
450
723
  /** Extract standard filename without directory components. */
451
724
  function basename(path) {
452
725
  const normalized = path.replace(/[/\\]+$/, "");
@@ -536,14 +809,12 @@ window.__ModuleLoader__.load({
536
809
  };
537
810
  } };
538
811
  }
539
- //#endregion
540
- //#region src/workspace-path.ts
812
+ // src/workspace-path.ts
541
813
  /** Absolute paths accepted by both POSIX and Windows hosts. */
542
814
  function isAbsoluteWorkspacePath(path) {
543
815
  return /^(?:\/|[A-Za-z]:[/\\]|\\\\)/.test(path);
544
816
  }
545
- //#endregion
546
- //#region src/client/styles.ts
817
+ // src/client/styles.ts
547
818
  const plugin = "dsh-better-display";
548
819
  const styles$1 = /* @__PURE__ */ new Map();
549
820
  /** Retain injected CSS across host cleanup and restore it before Reader paints. */
@@ -565,8 +836,7 @@ window.__ModuleLoader__.load({
565
836
  document.head.appendChild(tag);
566
837
  }
567
838
  }
568
- //#endregion
569
- //#region src/client/mcp-app.ts
839
+ // src/client/mcp-app.ts
570
840
  /**
571
841
  * Core pure functions for SEP-1865 / MCP Apps integration.
572
842
  * Free of CSS module imports so it can be cleanly tested in Node test environments.
@@ -806,8 +1076,7 @@ ${trimmed}
806
1076
  } catch {}
807
1077
  return false;
808
1078
  }
809
- //#endregion
810
- //#region \0dshx-css-module:McpAppFrame.module.css.mjs
1079
+ // \0dshx-css-module:/home/runner/work/dsh-better-display/dsh-better-display/src/client/McpAppFrame.module.css.mjs
811
1080
  const css$7 = ".tyd90a_card{border:1px solid var(--dsw-alias-border-l2,#00000014);background:var(--dsw-alias-bg-module-platform,transparent);border-radius:14px;margin:16px 0;transition:border-color .2s cubic-bezier(.22,1,.36,1),box-shadow .2s cubic-bezier(.22,1,.36,1);position:relative;overflow:hidden}.tyd90a_card:hover{border-color:var(--dsw-alias-border-l1,#00000029);box-shadow:0 2px 10px -2px #0000000a}.tyd90a_header{border-bottom:1px solid var(--dsw-alias-border-l2,#0000000f);background:var(--dsw-alias-interactive-bg-hover,#00000004);-webkit-user-select:none;user-select:none;justify-content:space-between;align-items:center;height:36px;padding:0 14px;display:flex}.tyd90a_titleArea{align-items:center;gap:7px;min-width:0;display:flex}.tyd90a_icon{color:var(--dsw-alias-label-tertiary,#94a3b8);flex-shrink:0;justify-content:center;align-items:center;display:flex}.tyd90a_title{color:var(--dsw-alias-label-secondary,#64748b);letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:500;overflow:hidden}.tyd90a_actions{align-items:center;gap:4px;display:flex}.tyd90a_iconBtn{width:24px;height:24px;color:var(--dsw-alias-label-tertiary,#94a3b8);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:0;transition:all .15s;display:inline-flex}.tyd90a_iconBtn:hover{background:var(--dsw-alias-interactive-bg-hover,#0000000f);color:var(--dsw-alias-label-primary,#0f172a)}.tyd90a_iconBtn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#2563eb);outline-offset:1px}.tyd90a_iframeWrapper{background:0 0;width:100%;transition:height .25s cubic-bezier(.22,1,.36,1);position:relative}.tyd90a_iframe{background:0 0;border:none;width:100%;height:100%;display:block}.tyd90a_receipt{background:var(--dsw-alias-interactive-bg-hover,#22c55e0d);border-top:1px solid var(--dsw-alias-border-l2,#0000000f);color:var(--dsw-alias-label-primary,#0f172a);flex-wrap:wrap;justify-content:space-between;align-items:center;gap:6px 12px;padding:8px 14px;font-size:12px;line-height:18px;animation:.2s cubic-bezier(.22,1,.36,1) tyd90a_slideUp;display:flex}.tyd90a_receiptPrompt{color:var(--dsw-alias-label-secondary,#64748b);background:var(--dsw-alias-bg-base,#00000008);border:1px solid var(--dsw-alias-border-l2,#0000000f);-webkit-user-select:text;user-select:text;cursor:text;overflow-wrap:anywhere;border-radius:6px;flex:100%;padding:6px 10px;font-size:12px;line-height:18px}@keyframes tyd90a_slideUp{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.tyd90a_receiptSummary{color:var(--dsw-alias-state-business-primary,#16a34a);text-overflow:ellipsis;white-space:nowrap;align-items:center;gap:6px;font-weight:500;display:flex;overflow:hidden}.tyd90a_receiptHint{align-items:center;gap:6px;display:flex}.tyd90a_sendKbd{color:var(--dsw-alias-label-tertiary,#94a3b8);background:var(--dsw-alias-bg-base,#0000000a);border:1px solid var(--dsw-alias-border-l2,#00000014);cursor:pointer;border-radius:5px;align-items:center;gap:4px;padding:2px 8px;font-family:inherit;font-size:11px;transition:all .15s;display:inline-flex}.tyd90a_sendKbd:hover{background:var(--dsw-alias-interactive-bg-hover,#00000014);color:var(--dsw-alias-label-primary,#0f172a)}.tyd90a_sendKbd:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#2563eb);outline-offset:1px}.tyd90a_streamingPlaceholder{border:1px dashed var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#00000004);color:var(--dsw-alias-label-secondary,#64748b);border-radius:14px;align-items:center;gap:10px;margin:16px 0;padding:16px 18px;font-size:13px;display:flex}.tyd90a_pulseDot{background:var(--dsw-alias-state-business-primary,#2563eb);border-radius:50%;width:7px;height:7px;animation:1.5s ease-in-out infinite tyd90a_pulse}@keyframes tyd90a_pulse{0%,to{opacity:.35;transform:scale(.9)}50%{opacity:1;transform:scale(1.15)}}";
812
1081
  const tagId$7 = "dsh-better-display/McpAppFrame.module.css";
813
1082
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$7) + "]") === null) {
@@ -837,8 +1106,7 @@ ${trimmed}
837
1106
  "title": "tyd90a_title",
838
1107
  "titleArea": "tyd90a_titleArea"
839
1108
  };
840
- //#endregion
841
- //#region src/client/McpAppFrame.tsx
1109
+ // src/client/McpAppFrame.tsx
842
1110
  const ComposerFillContext = (0, react.createContext)(void 0);
843
1111
  function useComposerFill() {
844
1112
  return (0, react.useContext)(ComposerFillContext);
@@ -849,7 +1117,6 @@ ${trimmed}
849
1117
  const iframeRef = (0, react.useRef)(null);
850
1118
  const lastParamsRef = (0, react.useRef)({});
851
1119
  const [height, setHeight] = (0, react.useState)(() => Math.max(60, Math.min(2400, initialHeight)));
852
- const [ready, setReady] = (0, react.useState)(false);
853
1120
  const [receipt, setReceipt] = (0, react.useState)(null);
854
1121
  const [lastPrompt, setLastPrompt] = (0, react.useState)(null);
855
1122
  const [reloadNonce, setReloadNonce] = (0, react.useState)(0);
@@ -872,7 +1139,7 @@ ${trimmed}
872
1139
  }, [title, writeComposer]);
873
1140
  const handleUserSubmit = (0, react.useCallback)((params) => {
874
1141
  lastParamsRef.current = params;
875
- let summary = "";
1142
+ let summary;
876
1143
  if (typeof params.choice === "string") {
877
1144
  const desc = typeof params.desc === "string" ? ` (${params.desc})` : "";
878
1145
  summary = `选择: ${params.choice}${desc}`;
@@ -904,13 +1171,9 @@ ${trimmed}
904
1171
  }
905
1172
  };
906
1173
  iframe.contentWindow?.postMessage(response, "*");
907
- setReady(true);
908
- return;
909
- }
910
- if (data.method === "ui/ready" || data.method === "ui/notifications/initialized") {
911
- setReady(true);
912
1174
  return;
913
1175
  }
1176
+ if (data.method === "ui/ready" || data.method === "ui/notifications/initialized") return;
914
1177
  if (data.method === "ui/resize" && data.params?.height) {
915
1178
  const h = Number(data.params.height);
916
1179
  if (Number.isFinite(h) && h > 0) setHeight(Math.max(60, Math.min(2400, Math.round(h))));
@@ -978,11 +1241,9 @@ ${trimmed}
978
1241
  styles: { variables: cssTokens }
979
1242
  }
980
1243
  }, "*");
981
- setReady(true);
982
1244
  }, []);
983
1245
  const handleReload = (0, react.useCallback)(() => {
984
1246
  setReloadNonce((n) => n + 1);
985
- setReady(false);
986
1247
  setReceipt(null);
987
1248
  }, []);
988
1249
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -1132,8 +1393,7 @@ ${trimmed}
1132
1393
  ] })]
1133
1394
  });
1134
1395
  }
1135
- //#endregion
1136
- //#region src/client/message-chrome.ts
1396
+ // src/client/message-chrome.ts
1137
1397
  /** Local clock and run-duration labels for reader message chrome. */
1138
1398
  function pad2(n) {
1139
1399
  return String(n).padStart(2, "0");
@@ -1169,8 +1429,7 @@ ${trimmed}
1169
1429
  if (d.getFullYear() === n.getFullYear()) return `${d.getMonth() + 1}月${d.getDate()}日 ${clock}`;
1170
1430
  return `${d.getFullYear()}年${d.getMonth() + 1}月${d.getDate()}日 ${clock}`;
1171
1431
  }
1172
- //#endregion
1173
- //#region src/client/primitive-labels.ts
1432
+ // src/client/primitive-labels.ts
1174
1433
  const markdownLabels = {
1175
1434
  code: {
1176
1435
  copyLabel: "复制",
@@ -1191,6 +1450,7 @@ ${trimmed}
1191
1450
  expand: (hidden) => `展开其余 ${hidden} 行`
1192
1451
  };
1193
1452
  const terminalBlockLabels = {
1453
+ commandLine: (line) => `命令第 ${line} 行`,
1194
1454
  signal: (signal) => `信号 ${signal}`,
1195
1455
  exitCode: (code) => `退出码 ${code}`,
1196
1456
  noExitCode: "无退出码",
@@ -1247,8 +1507,7 @@ ${trimmed}
1247
1507
  copyButtonTitle: (action) => action
1248
1508
  };
1249
1509
  const truncatedJsonLabel = (total) => `内容过长,已截断(共 ${total} 个字符)`;
1250
- //#endregion
1251
- //#region src/client/stream-buffer.ts
1510
+ // src/client/stream-buffer.ts
1252
1511
  /** Presentation only. The original session string remains the source of truth. */
1253
1512
  const STREAM_TIMING = {
1254
1513
  catchUpMs: 520,
@@ -1377,8 +1636,7 @@ ${trimmed}
1377
1636
  return end !== before;
1378
1637
  }
1379
1638
  };
1380
- //#endregion
1381
- //#region src/client/word-timeline.ts
1639
+ // src/client/word-timeline.ts
1382
1640
  /** Motion parameters from the public transitions.dev streaming-text recipe. */
1383
1641
  const WORD_MOTION = {
1384
1642
  duration: 350,
@@ -1479,8 +1737,7 @@ ${trimmed}
1479
1737
  }));
1480
1738
  }
1481
1739
  };
1482
- //#endregion
1483
- //#region src/client/streaming.tsx
1740
+ // src/client/streaming.tsx
1484
1741
  const StreamMotionContext = (0, react.createContext)({
1485
1742
  enabled: false,
1486
1743
  activatedAt: 0
@@ -1577,8 +1834,7 @@ ${trimmed}
1577
1834
  reveal: enabled && !options.interrupted && !options.selected
1578
1835
  };
1579
1836
  }
1580
- //#endregion
1581
- //#region src/client/markdown/incremental.ts
1837
+ // src/client/markdown/incremental.ts
1582
1838
  /**
1583
1839
  * Trailing blocks kept unstable. Appended text reshapes at most the last
1584
1840
  * block; the second-to-last is retained as safety margin so a freeze decision
@@ -1654,8 +1910,7 @@ ${trimmed}
1654
1910
  return this.cached;
1655
1911
  }
1656
1912
  };
1657
- //#endregion
1658
- //#region node_modules/mdast-util-to-string/lib/index.js
1913
+ // node_modules/mdast-util-to-string/lib/index.js
1659
1914
  /**
1660
1915
  * @typedef {import('mdast').Nodes} Nodes
1661
1916
  *
@@ -1736,8 +1991,7 @@ ${trimmed}
1736
1991
  function node(value) {
1737
1992
  return Boolean(value && typeof value === "object");
1738
1993
  }
1739
- //#endregion
1740
- //#region node_modules/decode-named-character-reference/index.dom.js
1994
+ // node_modules/decode-named-character-reference/index.dom.js
1741
1995
  const element = document.createElement("i");
1742
1996
  /**
1743
1997
  * @param {string} value
@@ -1750,8 +2004,7 @@ ${trimmed}
1750
2004
  if (character.charCodeAt(character.length - 1) === 59 && value !== "semi") return false;
1751
2005
  return character === characterReference ? false : character;
1752
2006
  }
1753
- //#endregion
1754
- //#region node_modules/micromark-util-chunked/index.js
2007
+ // node_modules/micromark-util-chunked/index.js
1755
2008
  /**
1756
2009
  * Like `Array#splice`, but smarter for giant arrays.
1757
2010
  *
@@ -1820,8 +2073,7 @@ ${trimmed}
1820
2073
  }
1821
2074
  return items;
1822
2075
  }
1823
- //#endregion
1824
- //#region node_modules/micromark-util-combine-extensions/index.js
2076
+ // node_modules/micromark-util-combine-extensions/index.js
1825
2077
  /**
1826
2078
  * @import {
1827
2079
  * Extension,
@@ -1891,8 +2143,7 @@ ${trimmed}
1891
2143
  while (++index < list.length) (list[index].add === "after" ? existing : before).push(list[index]);
1892
2144
  splice(existing, 0, 0, before);
1893
2145
  }
1894
- //#endregion
1895
- //#region node_modules/micromark-util-decode-numeric-character-reference/index.js
2146
+ // node_modules/micromark-util-decode-numeric-character-reference/index.js
1896
2147
  /**
1897
2148
  * Turn the number (in string form as either hexa- or plain decimal) coming from
1898
2149
  * a numeric character reference into a character.
@@ -1912,8 +2163,7 @@ ${trimmed}
1912
2163
  if (code < 9 || code === 11 || code > 13 && code < 32 || code > 126 && code < 160 || code > 55295 && code < 57344 || code > 64975 && code < 65008 || (code & 65535) === 65535 || (code & 65535) === 65534 || code > 1114111) return "�";
1913
2164
  return String.fromCodePoint(code);
1914
2165
  }
1915
- //#endregion
1916
- //#region node_modules/micromark-util-normalize-identifier/index.js
2166
+ // node_modules/micromark-util-normalize-identifier/index.js
1917
2167
  /**
1918
2168
  * Normalize an identifier (as found in references, definitions).
1919
2169
  *
@@ -1936,8 +2186,7 @@ ${trimmed}
1936
2186
  function normalizeIdentifier(value) {
1937
2187
  return value.replace(/[\t\n\r ]+/g, " ").replace(/^ | $/g, "").toLowerCase().toUpperCase();
1938
2188
  }
1939
- //#endregion
1940
- //#region node_modules/micromark-util-character/index.js
2189
+ // node_modules/micromark-util-character/index.js
1941
2190
  /**
1942
2191
  * @import {Code} from 'micromark-util-types'
1943
2192
  */
@@ -2164,8 +2413,7 @@ ${trimmed}
2164
2413
  return code !== null && code > -1 && regex.test(String.fromCharCode(code));
2165
2414
  }
2166
2415
  }
2167
- //#endregion
2168
- //#region node_modules/micromark-util-sanitize-uri/index.js
2416
+ // node_modules/micromark-util-sanitize-uri/index.js
2169
2417
  /**
2170
2418
  * Normalize a URL.
2171
2419
  *
@@ -2209,8 +2457,7 @@ ${trimmed}
2209
2457
  }
2210
2458
  return result.join("") + value.slice(start);
2211
2459
  }
2212
- //#endregion
2213
- //#region node_modules/micromark-factory-space/index.js
2460
+ // node_modules/micromark-factory-space/index.js
2214
2461
  /**
2215
2462
  * @import {Effects, State, TokenType} from 'micromark-util-types'
2216
2463
  */
@@ -2316,8 +2563,7 @@ ${trimmed}
2316
2563
  return size >= min ? ok(code) : nok(code);
2317
2564
  }
2318
2565
  }
2319
- //#endregion
2320
- //#region node_modules/micromark/lib/initialize/content.js
2566
+ // node_modules/micromark/lib/initialize/content.js
2321
2567
  /**
2322
2568
  * @import {
2323
2569
  * InitialConstruct,
@@ -2383,8 +2629,7 @@ ${trimmed}
2383
2629
  return data;
2384
2630
  }
2385
2631
  }
2386
- //#endregion
2387
- //#region node_modules/micromark-util-edit-map/index.js
2632
+ // node_modules/micromark-util-edit-map/index.js
2388
2633
  /**
2389
2634
  * @import {Event} from 'micromark-util-types'
2390
2635
  */
@@ -2529,8 +2774,7 @@ ${trimmed}
2529
2774
  editMap.map.push(change);
2530
2775
  editMap.index.set(at, change);
2531
2776
  }
2532
- //#endregion
2533
- //#region node_modules/micromark/lib/initialize/document.js
2777
+ // node_modules/micromark/lib/initialize/document.js
2534
2778
  /**
2535
2779
  * @import {
2536
2780
  * Construct,
@@ -2749,8 +2993,7 @@ ${trimmed}
2749
2993
  function tokenizeContainer(effects, ok, nok) {
2750
2994
  return factorySpace(effects, effects.attempt(this.parser.constructs.document, ok, nok), "linePrefix", this.parser.constructs.disable.null.includes("codeIndented") ? void 0 : 4);
2751
2995
  }
2752
- //#endregion
2753
- //#region node_modules/micromark-util-classify-character/index.js
2996
+ // node_modules/micromark-util-classify-character/index.js
2754
2997
  /**
2755
2998
  * @import {Code} from 'micromark-util-types'
2756
2999
  */
@@ -2772,8 +3015,7 @@ ${trimmed}
2772
3015
  if (code === null || markdownLineEndingOrSpace(code) || unicodeWhitespace(code)) return 1;
2773
3016
  if (unicodePunctuation(code)) return 2;
2774
3017
  }
2775
- //#endregion
2776
- //#region node_modules/micromark-util-resolve-all/index.js
3018
+ // node_modules/micromark-util-resolve-all/index.js
2777
3019
  /**
2778
3020
  * @import {Event, Resolver, TokenizeContext} from 'micromark-util-types'
2779
3021
  */
@@ -2802,8 +3044,7 @@ ${trimmed}
2802
3044
  }
2803
3045
  return events;
2804
3046
  }
2805
- //#endregion
2806
- //#region node_modules/micromark-core-commonmark/lib/attention.js
3047
+ // node_modules/micromark-core-commonmark/lib/attention.js
2807
3048
  /**
2808
3049
  * @import {
2809
3050
  * Code,
@@ -3010,8 +3251,7 @@ ${trimmed}
3010
3251
  point.offset += offset;
3011
3252
  point._bufferIndex += offset;
3012
3253
  }
3013
- //#endregion
3014
- //#region node_modules/micromark-core-commonmark/lib/autolink.js
3254
+ // node_modules/micromark-core-commonmark/lib/autolink.js
3015
3255
  /**
3016
3256
  * @import {
3017
3257
  * Construct,
@@ -3221,8 +3461,7 @@ ${trimmed}
3221
3461
  return nok(code);
3222
3462
  }
3223
3463
  }
3224
- //#endregion
3225
- //#region node_modules/micromark-core-commonmark/lib/blank-line.js
3464
+ // node_modules/micromark-core-commonmark/lib/blank-line.js
3226
3465
  /**
3227
3466
  * @import {
3228
3467
  * Construct,
@@ -3278,8 +3517,7 @@ ${trimmed}
3278
3517
  return code === null || markdownLineEnding(code) ? ok(code) : nok(code);
3279
3518
  }
3280
3519
  }
3281
- //#endregion
3282
- //#region node_modules/micromark-core-commonmark/lib/block-quote.js
3520
+ // node_modules/micromark-core-commonmark/lib/block-quote.js
3283
3521
  /**
3284
3522
  * @import {
3285
3523
  * Construct,
@@ -3405,8 +3643,7 @@ ${trimmed}
3405
3643
  function exit$1(effects) {
3406
3644
  effects.exit("blockQuote");
3407
3645
  }
3408
- //#endregion
3409
- //#region node_modules/micromark-core-commonmark/lib/character-escape.js
3646
+ // node_modules/micromark-core-commonmark/lib/character-escape.js
3410
3647
  /**
3411
3648
  * @import {
3412
3649
  * Construct,
@@ -3465,8 +3702,7 @@ ${trimmed}
3465
3702
  return nok(code);
3466
3703
  }
3467
3704
  }
3468
- //#endregion
3469
- //#region node_modules/micromark-core-commonmark/lib/character-reference.js
3705
+ // node_modules/micromark-core-commonmark/lib/character-reference.js
3470
3706
  /**
3471
3707
  * @import {
3472
3708
  * Code,
@@ -3603,8 +3839,7 @@ ${trimmed}
3603
3839
  return nok(code);
3604
3840
  }
3605
3841
  }
3606
- //#endregion
3607
- //#region node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.js
3842
+ // node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.js
3608
3843
  /** @type {Construct} */
3609
3844
  const nonLazyContinuation$2 = {
3610
3845
  partial: true,
@@ -3651,8 +3886,7 @@ ${trimmed}
3651
3886
  return self.parser.lazy[self.now().line] ? nok(code) : ok(code);
3652
3887
  }
3653
3888
  }
3654
- //#endregion
3655
- //#region node_modules/micromark-core-commonmark/lib/code-fenced.js
3889
+ // node_modules/micromark-core-commonmark/lib/code-fenced.js
3656
3890
  /**
3657
3891
  * @import {
3658
3892
  * Code,
@@ -4032,8 +4266,7 @@ ${trimmed}
4032
4266
  }
4033
4267
  }
4034
4268
  }
4035
- //#endregion
4036
- //#region node_modules/micromark-core-commonmark/lib/code-indented.js
4269
+ // node_modules/micromark-core-commonmark/lib/code-indented.js
4037
4270
  /**
4038
4271
  * @import {
4039
4272
  * Construct,
@@ -4164,8 +4397,7 @@ ${trimmed}
4164
4397
  return markdownLineEnding(code) ? furtherStart(code) : nok(code);
4165
4398
  }
4166
4399
  }
4167
- //#endregion
4168
- //#region node_modules/micromark-core-commonmark/lib/code-text.js
4400
+ // node_modules/micromark-core-commonmark/lib/code-text.js
4169
4401
  /**
4170
4402
  * @import {
4171
4403
  * Construct,
@@ -4349,8 +4581,7 @@ ${trimmed}
4349
4581
  return data(code);
4350
4582
  }
4351
4583
  }
4352
- //#endregion
4353
- //#region node_modules/micromark-util-subtokenize/lib/splice-buffer.js
4584
+ // node_modules/micromark-util-subtokenize/lib/splice-buffer.js
4354
4585
  /**
4355
4586
  * Some of the internal operations of micromark do lots of editing
4356
4587
  * operations on very large arrays. This runs into problems with two
@@ -4579,8 +4810,7 @@ ${trimmed}
4579
4810
  chunkStart += 1e4;
4580
4811
  }
4581
4812
  }
4582
- //#endregion
4583
- //#region node_modules/micromark-util-subtokenize/index.js
4813
+ // node_modules/micromark-util-subtokenize/index.js
4584
4814
  /**
4585
4815
  * @import {Chunk, Event, Token} from 'micromark-util-types'
4586
4816
  */
@@ -4734,8 +4964,7 @@ ${trimmed}
4734
4964
  }
4735
4965
  return gaps;
4736
4966
  }
4737
- //#endregion
4738
- //#region node_modules/micromark-core-commonmark/lib/content.js
4967
+ // node_modules/micromark-core-commonmark/lib/content.js
4739
4968
  /**
4740
4969
  * @import {
4741
4970
  * Construct,
@@ -4867,8 +5096,7 @@ ${trimmed}
4867
5096
  return effects.interrupt(self.parser.constructs.flow, nok, ok)(code);
4868
5097
  }
4869
5098
  }
4870
- //#endregion
4871
- //#region node_modules/micromark-factory-destination/index.js
5099
+ // node_modules/micromark-factory-destination/index.js
4872
5100
  /**
4873
5101
  * @import {Effects, State, TokenType} from 'micromark-util-types'
4874
5102
  */
@@ -5051,8 +5279,7 @@ ${trimmed}
5051
5279
  return raw(code);
5052
5280
  }
5053
5281
  }
5054
- //#endregion
5055
- //#region node_modules/micromark-factory-label/index.js
5282
+ // node_modules/micromark-factory-label/index.js
5056
5283
  /**
5057
5284
  * @import {
5058
5285
  * Effects,
@@ -5185,8 +5412,7 @@ ${trimmed}
5185
5412
  return labelInside(code);
5186
5413
  }
5187
5414
  }
5188
- //#endregion
5189
- //#region node_modules/micromark-factory-title/index.js
5415
+ // node_modules/micromark-factory-title/index.js
5190
5416
  /**
5191
5417
  * @import {
5192
5418
  * Code,
@@ -5330,8 +5556,7 @@ ${trimmed}
5330
5556
  return inside(code);
5331
5557
  }
5332
5558
  }
5333
- //#endregion
5334
- //#region node_modules/micromark-factory-whitespace/index.js
5559
+ // node_modules/micromark-factory-whitespace/index.js
5335
5560
  /**
5336
5561
  * @import {Effects, State} from 'micromark-util-types'
5337
5562
  */
@@ -5370,8 +5595,7 @@ ${trimmed}
5370
5595
  return ok(code);
5371
5596
  }
5372
5597
  }
5373
- //#endregion
5374
- //#region node_modules/micromark-core-commonmark/lib/definition.js
5598
+ // node_modules/micromark-core-commonmark/lib/definition.js
5375
5599
  /**
5376
5600
  * @import {
5377
5601
  * Construct,
@@ -5585,8 +5809,7 @@ ${trimmed}
5585
5809
  return code === null || markdownLineEnding(code) ? ok(code) : nok(code);
5586
5810
  }
5587
5811
  }
5588
- //#endregion
5589
- //#region node_modules/micromark-core-commonmark/lib/hard-break-escape.js
5812
+ // node_modules/micromark-core-commonmark/lib/hard-break-escape.js
5590
5813
  /**
5591
5814
  * @import {
5592
5815
  * Construct,
@@ -5642,8 +5865,7 @@ ${trimmed}
5642
5865
  return nok(code);
5643
5866
  }
5644
5867
  }
5645
- //#endregion
5646
- //#region node_modules/micromark-core-commonmark/lib/heading-atx.js
5868
+ // node_modules/micromark-core-commonmark/lib/heading-atx.js
5647
5869
  /**
5648
5870
  * @import {
5649
5871
  * Construct,
@@ -5823,8 +6045,7 @@ ${trimmed}
5823
6045
  return data;
5824
6046
  }
5825
6047
  }
5826
- //#endregion
5827
- //#region node_modules/micromark-util-html-tag-name/index.js
6048
+ // node_modules/micromark-util-html-tag-name/index.js
5828
6049
  /**
5829
6050
  * List of lowercase HTML “block” tag names.
5830
6051
  *
@@ -5922,8 +6143,7 @@ ${trimmed}
5922
6143
  "style",
5923
6144
  "textarea"
5924
6145
  ];
5925
- //#endregion
5926
- //#region node_modules/micromark-core-commonmark/lib/html-flow.js
6146
+ // node_modules/micromark-core-commonmark/lib/html-flow.js
5927
6147
  /**
5928
6148
  * @import {
5929
6149
  * Code,
@@ -6648,8 +6868,7 @@ ${trimmed}
6648
6868
  return effects.attempt(blankLine, ok, nok);
6649
6869
  }
6650
6870
  }
6651
- //#endregion
6652
- //#region node_modules/micromark-core-commonmark/lib/html-text.js
6871
+ // node_modules/micromark-core-commonmark/lib/html-text.js
6653
6872
  /**
6654
6873
  * @import {
6655
6874
  * Code,
@@ -7262,8 +7481,7 @@ ${trimmed}
7262
7481
  return returnState(code);
7263
7482
  }
7264
7483
  }
7265
- //#endregion
7266
- //#region node_modules/micromark-core-commonmark/lib/label-end.js
7484
+ // node_modules/micromark-core-commonmark/lib/label-end.js
7267
7485
  /**
7268
7486
  * @import {
7269
7487
  * Construct,
@@ -7731,8 +7949,7 @@ ${trimmed}
7731
7949
  return nok(code);
7732
7950
  }
7733
7951
  }
7734
- //#endregion
7735
- //#region node_modules/micromark-core-commonmark/lib/label-start-image.js
7952
+ // node_modules/micromark-core-commonmark/lib/label-start-image.js
7736
7953
  /**
7737
7954
  * @import {
7738
7955
  * Construct,
@@ -7830,8 +8047,7 @@ ${trimmed}
7830
8047
  return ok(code);
7831
8048
  }
7832
8049
  }
7833
- //#endregion
7834
- //#region node_modules/micromark-core-commonmark/lib/label-start-link.js
8050
+ // node_modules/micromark-core-commonmark/lib/label-start-link.js
7835
8051
  /**
7836
8052
  * @import {
7837
8053
  * Construct,
@@ -7884,8 +8100,7 @@ ${trimmed}
7884
8100
  return ok(code);
7885
8101
  }
7886
8102
  }
7887
- //#endregion
7888
- //#region node_modules/micromark-core-commonmark/lib/line-ending.js
8103
+ // node_modules/micromark-core-commonmark/lib/line-ending.js
7889
8104
  /**
7890
8105
  * @import {
7891
8106
  * Construct,
@@ -7914,8 +8129,7 @@ ${trimmed}
7914
8129
  return factorySpace(effects, ok, "linePrefix");
7915
8130
  }
7916
8131
  }
7917
- //#endregion
7918
- //#region node_modules/micromark-core-commonmark/lib/thematic-break.js
8132
+ // node_modules/micromark-core-commonmark/lib/thematic-break.js
7919
8133
  /**
7920
8134
  * @import {
7921
8135
  * Code,
@@ -8009,8 +8223,7 @@ ${trimmed}
8009
8223
  return markdownSpace(code) ? factorySpace(effects, atBreak, "whitespace")(code) : atBreak(code);
8010
8224
  }
8011
8225
  }
8012
- //#endregion
8013
- //#region node_modules/micromark-core-commonmark/lib/list.js
8226
+ // node_modules/micromark-core-commonmark/lib/list.js
8014
8227
  /**
8015
8228
  * @import {
8016
8229
  * Code,
@@ -8181,8 +8394,7 @@ ${trimmed}
8181
8394
  return !markdownSpace(code) && tail && tail[1].type === "listItemPrefixWhitespace" ? ok(code) : nok(code);
8182
8395
  }
8183
8396
  }
8184
- //#endregion
8185
- //#region node_modules/micromark-core-commonmark/lib/setext-underline.js
8397
+ // node_modules/micromark-core-commonmark/lib/setext-underline.js
8186
8398
  /**
8187
8399
  * @import {
8188
8400
  * Code,
@@ -8335,8 +8547,7 @@ ${trimmed}
8335
8547
  return nok(code);
8336
8548
  }
8337
8549
  }
8338
- //#endregion
8339
- //#region node_modules/micromark/lib/initialize/flow.js
8550
+ // node_modules/micromark/lib/initialize/flow.js
8340
8551
  /**
8341
8552
  * @import {
8342
8553
  * InitialConstruct,
@@ -8382,8 +8593,7 @@ ${trimmed}
8382
8593
  return initial;
8383
8594
  }
8384
8595
  }
8385
- //#endregion
8386
- //#region node_modules/micromark/lib/initialize/text.js
8596
+ // node_modules/micromark/lib/initialize/text.js
8387
8597
  /**
8388
8598
  * @import {
8389
8599
  * Code,
@@ -8558,8 +8768,7 @@ ${trimmed}
8558
8768
  editMap.consume(events);
8559
8769
  return events;
8560
8770
  }
8561
- //#endregion
8562
- //#region node_modules/micromark/lib/constructs.js
8771
+ // node_modules/micromark/lib/constructs.js
8563
8772
  /**
8564
8773
  * @import {Extension} from 'micromark-util-types'
8565
8774
  */
@@ -8636,8 +8845,7 @@ ${trimmed}
8636
8845
  const attentionMarkers = { null: [42, 95] };
8637
8846
  /** @satisfies {Extension['disable']} */
8638
8847
  const disable = { null: [] };
8639
- //#endregion
8640
- //#region node_modules/micromark/lib/create-tokenizer.js
8848
+ // node_modules/micromark/lib/create-tokenizer.js
8641
8849
  /**
8642
8850
  * @import {
8643
8851
  * Chunk,
@@ -9103,8 +9311,7 @@ ${trimmed}
9103
9311
  }
9104
9312
  return result.join("");
9105
9313
  }
9106
- //#endregion
9107
- //#region node_modules/micromark/lib/parse.js
9314
+ // node_modules/micromark/lib/parse.js
9108
9315
  /**
9109
9316
  * @import {
9110
9317
  * Create,
@@ -9147,8 +9354,7 @@ ${trimmed}
9147
9354
  }
9148
9355
  }
9149
9356
  }
9150
- //#endregion
9151
- //#region node_modules/micromark/lib/postprocess.js
9357
+ // node_modules/micromark/lib/postprocess.js
9152
9358
  /**
9153
9359
  * @import {Event} from 'micromark-util-types'
9154
9360
  */
@@ -9162,8 +9368,7 @@ ${trimmed}
9162
9368
  while (!subtokenize(events));
9163
9369
  return events;
9164
9370
  }
9165
- //#endregion
9166
- //#region node_modules/micromark/lib/preprocess.js
9371
+ // node_modules/micromark/lib/preprocess.js
9167
9372
  /**
9168
9373
  * @import {Chunk, Code, Encoding, Value} from 'micromark-util-types'
9169
9374
  */
@@ -9254,8 +9459,7 @@ ${trimmed}
9254
9459
  return chunks;
9255
9460
  }
9256
9461
  }
9257
- //#endregion
9258
- //#region node_modules/micromark-util-decode-string/index.js
9462
+ // node_modules/micromark-util-decode-string/index.js
9259
9463
  const characterEscapeOrReference = /\\([!-/:-@[-`{-~])|&(#(?:\d{1,7}|x[\da-f]{1,6})|[\da-z]{1,31});/gi;
9260
9464
  /**
9261
9465
  * Decode markdown strings (which occur in places such as fenced code info
@@ -9291,8 +9495,7 @@ ${trimmed}
9291
9495
  }
9292
9496
  return decodeNamedCharacterReference($2) || $0;
9293
9497
  }
9294
- //#endregion
9295
- //#region node_modules/unist-util-stringify-position/lib/index.js
9498
+ // node_modules/unist-util-stringify-position/lib/index.js
9296
9499
  /**
9297
9500
  * @typedef {import('unist').Node} Node
9298
9501
  * @typedef {import('unist').Point} Point
@@ -9355,8 +9558,7 @@ ${trimmed}
9355
9558
  function index(value) {
9356
9559
  return value && typeof value === "number" ? value : 1;
9357
9560
  }
9358
- //#endregion
9359
- //#region node_modules/mdast-util-from-markdown/lib/index.js
9561
+ // node_modules/mdast-util-from-markdown/lib/index.js
9360
9562
  /**
9361
9563
  * @import {
9362
9564
  * Break,
@@ -10402,8 +10604,7 @@ ${trimmed}
10402
10604
  end: right.end
10403
10605
  }) + ") is still open");
10404
10606
  }
10405
- //#endregion
10406
- //#region node_modules/ccount/index.js
10607
+ // node_modules/ccount/index.js
10407
10608
  /**
10408
10609
  * Count how often a character (or substring) is used in a string.
10409
10610
  *
@@ -10425,14 +10626,12 @@ ${trimmed}
10425
10626
  }
10426
10627
  return count;
10427
10628
  }
10428
- //#endregion
10429
- //#region node_modules/escape-string-regexp/index.js
10629
+ // node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp/index.js
10430
10630
  function escapeStringRegexp(string) {
10431
10631
  if (typeof string !== "string") throw new TypeError("Expected a string");
10432
10632
  return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
10433
10633
  }
10434
- //#endregion
10435
- //#region node_modules/unist-util-is/lib/index.js
10634
+ // node_modules/unist-util-is/lib/index.js
10436
10635
  /**
10437
10636
  * Generate an assertion from a test.
10438
10637
  *
@@ -10545,17 +10744,19 @@ ${trimmed}
10545
10744
  function looksLikeANode(value) {
10546
10745
  return value !== null && typeof value === "object" && "type" in value;
10547
10746
  }
10548
- //#endregion
10549
- //#region node_modules/unist-util-visit-parents/lib/color.node.js
10747
+ // node_modules/unist-util-visit-parents/lib/color.node.js
10550
10748
  /**
10749
+ * Color a string.
10750
+ *
10551
10751
  * @param {string} d
10752
+ * Value.
10552
10753
  * @returns {string}
10754
+ * Colored value.
10553
10755
  */
10554
10756
  function color(d) {
10555
10757
  return "\x1B[33m" + d + "\x1B[39m";
10556
10758
  }
10557
- //#endregion
10558
- //#region node_modules/unist-util-visit-parents/lib/index.js
10759
+ // node_modules/unist-util-visit-parents/lib/index.js
10559
10760
  /**
10560
10761
  * @import {Node as UnistNode, Parent as UnistParent} from 'unist'
10561
10762
  */
@@ -10713,43 +10914,93 @@ ${trimmed}
10713
10914
  * List with one or two values, the first an action, the second an index.
10714
10915
  *
10715
10916
  * @typedef {Action | ActionTuple | Index | null | undefined | void} VisitorResult
10716
- * Any value that can be returned from a visitor.
10917
+ * Any value that can be returned from an enter visitor.
10918
+ *
10919
+ * Like `VisitorResultExit`,
10920
+ * but `SKIP` can also be returned,
10921
+ * to not walk the children of the node,
10922
+ * and `ActionTuple` can be used to combine an action with an index.
10923
+ *
10924
+ * @typedef {Index | boolean | null | undefined | void} VisitorResultExit
10925
+ * Any value that can be returned from an exit visitor.
10717
10926
  */
10718
10927
  /**
10719
10928
  * @callback Visitor
10720
- * Handle a node (matching `test`, if given).
10721
- *
10722
- * Visitors are free to transform `node`.
10723
- * They can also transform the parent of node (the last of `ancestors`).
10724
- *
10725
- * Replacing `node` itself, if `SKIP` is not returned, still causes its
10726
- * descendants to be walked (which is a bug).
10727
- *
10728
- * When adding or removing previous siblings of `node` (or next siblings, in
10729
- * case of reverse), the `Visitor` should return a new `Index` to specify the
10730
- * sibling to traverse after `node` is traversed.
10731
- * Adding or removing next siblings of `node` (or previous siblings, in case
10732
- * of reverse) is handled as expected without needing to return a new `Index`.
10733
- *
10734
- * Removing the children property of an ancestor still results in them being
10735
- * traversed.
10929
+ * Handle a node (matching `test`, if given) before its children.
10930
+ *
10931
+ * Changes inside `node` are walked next.
10932
+ * Replacing or removing it is easier in `exit`.
10933
+ * When replacing or removing it here,
10934
+ * return `SKIP` (as otherwise its old children are walked),
10935
+ * or `[SKIP, index]` to continue with the node now at `index`.
10936
+ * Changing next siblings (in walk order) is fine;
10937
+ * changing previous siblings needs a new `Index`.
10938
+ * Removing an ancestor or changing its previous siblings is not supported.
10736
10939
  * @param {Visited} node
10737
10940
  * Found node.
10738
10941
  * @param {Array<VisitedParents>} ancestors
10739
10942
  * Ancestors of `node`.
10943
+ * @param {number | undefined} index
10944
+ * Index of `node` in its parent (the last of `ancestors`),
10945
+ * or `undefined` for the tree itself.
10740
10946
  * @returns {VisitorResult}
10741
10947
  * What to do next.
10742
10948
  *
10743
- * An `Index` is treated as a tuple of `[CONTINUE, Index]`.
10744
- * An `Action` is treated as a tuple of `[Action]`.
10949
+ * Like `VisitorExit`,
10950
+ * but `SKIP` can also be returned,
10951
+ * to not walk the children of `node`.
10952
+ * Passing a tuple back (`ActionTuple`) only makes sense as `[SKIP, Index]`.
10953
+ * @template {UnistNode} [Visited=UnistNode]
10954
+ * Visited node type.
10955
+ * @template {UnistParent} [VisitedParents=UnistParent]
10956
+ * Ancestor type.
10957
+ */
10958
+ /**
10959
+ * @callback VisitorExit
10960
+ * Handle a node (matching `test`, if given) after its children.
10745
10961
  *
10746
- * Passing a tuple back only makes sense if the `Action` is `SKIP`.
10747
- * When the `Action` is `EXIT`, that action can be returned.
10748
- * When the `Action` is `CONTINUE`, `Index` can be returned.
10962
+ * Changes inside `node` are not walked,
10963
+ * which makes this a good place to change `node` based on its children,
10964
+ * or to replace or remove it.
10965
+ * When removing `node` or changing previous siblings (in walk order),
10966
+ * return a new `Index`.
10967
+ * Removing an ancestor or changing its previous siblings is not supported.
10968
+ * @param {Visited} node
10969
+ * Found node.
10970
+ * @param {Array<VisitedParents>} ancestors
10971
+ * Ancestors of `node`.
10972
+ * @param {number | undefined} index
10973
+ * Index of `node` in its parent (the last of `ancestors`),
10974
+ * or `undefined` for the tree itself.
10975
+ * @returns {VisitorResultExit}
10976
+ * What to do next.
10977
+ *
10978
+ * `CONTINUE` (`true`, nothing) continues as normal,
10979
+ * `EXIT` (`false`) stops traversing,
10980
+ * and `Index` continues at that sibling.
10981
+ * @template {UnistNode} [Visited=UnistNode]
10982
+ * Visited node type.
10983
+ * @template {UnistParent} [VisitedParents=UnistParent]
10984
+ * Ancestor type.
10985
+ */
10986
+ /**
10749
10987
  * @template {UnistNode} [Visited=UnistNode]
10750
10988
  * Visited node type.
10751
10989
  * @template {UnistParent} [VisitedParents=UnistParent]
10752
10990
  * Ancestor type.
10991
+ * @typedef Visitors
10992
+ * Handle nodes when entering and exiting them.
10993
+ * The `enter` visitor is optional while `exit` is required.
10994
+ *
10995
+ * Pass an `enter` visitor directly to only handle nodes when entering them.
10996
+ *
10997
+ * `enter` is called before children (if any) are walked and `exit` after that.
10998
+ * `exit` is still called when `enter` returns `SKIP`.
10999
+ * Both `enter` and `exit` are only called for nodes that match an optional `test`.
11000
+ * @property {Visitor<Visited, VisitedParents> | null | undefined} [enter]
11001
+ * Handle a node before its children (optional).
11002
+ * @property {VisitorExit<Visited, VisitedParents>} exit
11003
+ * Handle a node after its children.
10753
11004
  */
10754
11005
  /**
10755
11006
  * @typedef {Visitor<Matches<InclusiveDescendant<Tree>, Check>, Ancestor<Tree, Matches<InclusiveDescendant<Tree>, Check>>>} BuildVisitor
@@ -10761,16 +11012,34 @@ ${trimmed}
10761
11012
  * @template {Test} [Check=Test]
10762
11013
  * Test type.
10763
11014
  */
11015
+ /**
11016
+ * @typedef {Visitors<Matches<InclusiveDescendant<Tree>, Check>, Ancestor<Tree, Matches<InclusiveDescendant<Tree>, Check>>>} BuildVisitors
11017
+ * Build typed `Visitors` from a tree and a test.
11018
+ *
11019
+ * It will infer which values are passed as `node` and which as `parents`.
11020
+ * @template {UnistNode} [Tree=UnistNode]
11021
+ * Tree type.
11022
+ * @template {Test} [Check=Test]
11023
+ * Test type.
11024
+ */
10764
11025
  /** @type {Readonly<ActionTuple>} */
10765
11026
  const empty = [];
10766
11027
  /**
11028
+ * Do not traverse this node’s children.
11029
+ * Only works when entering a node.
11030
+ */
11031
+ const SKIP = "skip";
11032
+ /**
10767
11033
  * Visit nodes, with ancestral information.
10768
11034
  *
10769
11035
  * This algorithm performs *depth-first* *tree traversal* in *preorder*
10770
- * (**NLR**) or if `reverse` is given, in *reverse preorder* (**NRL**).
11036
+ * (**NLR**) or if `reverse` is given,
11037
+ * in *reverse preorder* (**NRL**).
11038
+ * An `exit` visitor is called in *postorder* (**LRN**)
11039
+ * or if `reverse` is given, in *reverse postorder* (**RLN**).
10771
11040
  *
10772
- * You can choose for which nodes `visitor` is called by passing a `test`.
10773
- * For complex tests, you should test yourself in `visitor`, as it will be
11041
+ * You can choose which nodes are visited by passing a `test`.
11042
+ * For complex tests, you should test yourself in a visitor, as it will be
10774
11043
  * faster and will have improved type information.
10775
11044
  *
10776
11045
  * Walking the tree is an intensive task.
@@ -10779,29 +11048,72 @@ ${trimmed}
10779
11048
  * to check if a node matches, and then perform different operations.
10780
11049
  *
10781
11050
  * You can change the tree.
10782
- * See `Visitor` for more info.
11051
+ * Changes inside a node made when entering it are walked,
11052
+ * changes made when exiting it are not.
11053
+ * See `Visitor` and `VisitorExit` for more info.
10783
11054
  *
10784
11055
  * @overload
10785
11056
  * @param {Tree} tree
11057
+ * Tree to traverse.
10786
11058
  * @param {Check} check
10787
- * @param {BuildVisitor<Tree, Check>} visitor
11059
+ * `unist-util-is`-compatible test.
11060
+ * @param {BuildVisitor<Tree, Check>} enter
11061
+ * Handle each node when entering it.
10788
11062
  * @param {boolean | null | undefined} [reverse]
11063
+ * Walk children from last to first;
11064
+ * for `enter`, reverse preorder (NRL) instead of preorder (NLR);
11065
+ * for `exit`, reverse postorder (RLN) instead of postorder (LRN).
10789
11066
  * @returns {undefined}
11067
+ * Nothing.
10790
11068
  *
10791
11069
  * @overload
10792
11070
  * @param {Tree} tree
10793
- * @param {BuildVisitor<Tree>} visitor
11071
+ * Tree to traverse.
11072
+ * @param {Check} check
11073
+ * `unist-util-is`-compatible test.
11074
+ * @param {BuildVisitors<Tree, Check>} visitors
11075
+ * Handle each node when exiting it, and optionally when entering it.
10794
11076
  * @param {boolean | null | undefined} [reverse]
11077
+ * Walk children from last to first;
11078
+ * for `enter`, reverse preorder (NRL) instead of preorder (NLR);
11079
+ * for `exit`, reverse postorder (RLN) instead of postorder (LRN).
10795
11080
  * @returns {undefined}
11081
+ * Nothing.
11082
+ *
11083
+ * @overload
11084
+ * @param {Tree} tree
11085
+ * Tree to traverse.
11086
+ * @param {BuildVisitor<Tree>} enter
11087
+ * Handle each node when entering it.
11088
+ * @param {boolean | null | undefined} [reverse]
11089
+ * Walk children from last to first;
11090
+ * for `enter`, reverse preorder (NRL) instead of preorder (NLR);
11091
+ * for `exit`, reverse postorder (RLN) instead of postorder (LRN).
11092
+ * @returns {undefined}
11093
+ * Nothing.
11094
+ *
11095
+ * @overload
11096
+ * @param {Tree} tree
11097
+ * Tree to traverse.
11098
+ * @param {BuildVisitors<Tree>} visitors
11099
+ * Handle each node when exiting it, and optionally when entering it.
11100
+ * @param {boolean | null | undefined} [reverse]
11101
+ * Walk children from last to first;
11102
+ * for `enter`, reverse preorder (NRL) instead of preorder (NLR);
11103
+ * for `exit`, reverse postorder (RLN) instead of postorder (LRN).
11104
+ * @returns {undefined}
11105
+ * Nothing.
10796
11106
  *
10797
11107
  * @param {UnistNode} tree
10798
11108
  * Tree to traverse.
10799
- * @param {Visitor | Test} test
10800
- * `unist-util-is`-compatible test
10801
- * @param {Visitor | boolean | null | undefined} [visitor]
10802
- * Handle each node.
11109
+ * @param {Visitor | Visitors | Test} testOrEnterOrVisitors
11110
+ * `unist-util-is`-compatible test, `enter`, or `visitors`.
11111
+ * @param {Visitor | Visitors | boolean | null | undefined} [enterOrVisitorsOrReverse]
11112
+ * `enter`, `visitors`, or `reverse` after a test.
10803
11113
  * @param {boolean | null | undefined} [reverse]
10804
- * Traverse in reverse preorder (NRL) instead of the default preorder (NLR).
11114
+ * Walk children from last to first;
11115
+ * for `enter`, reverse preorder (NRL) instead of preorder (NLR);
11116
+ * for `exit`, reverse postorder (RLN) instead of postorder (LRN).
10805
11117
  * @returns {undefined}
10806
11118
  * Nothing.
10807
11119
  *
@@ -10810,20 +11122,46 @@ ${trimmed}
10810
11122
  * @template {Test} Check
10811
11123
  * `unist-util-is`-compatible test.
10812
11124
  */
10813
- function visitParents(tree, test, visitor, reverse) {
11125
+ function visitParents(tree, testOrEnterOrVisitors, enterOrVisitorsOrReverse, reverse) {
11126
+ if (!tree || typeof tree !== "object") throw new Error("Unexpected value `" + tree + "` for `tree`, expected node");
11127
+ let preorder = true;
10814
11128
  /** @type {Test} */
10815
11129
  let check;
10816
- if (typeof test === "function" && typeof visitor !== "function") {
10817
- reverse = visitor;
10818
- visitor = test;
10819
- } else check = test;
11130
+ /** @type {Visitors | Visitor} */
11131
+ let visitors;
11132
+ /** @type {Visitor | null | undefined} */
11133
+ let enter;
11134
+ /** @type {VisitorExit | null | undefined} */
11135
+ let exit;
11136
+ if (typeof enterOrVisitorsOrReverse === "function" || enterOrVisitorsOrReverse && typeof enterOrVisitorsOrReverse === "object") {
11137
+ check = testOrEnterOrVisitors;
11138
+ visitors = enterOrVisitorsOrReverse;
11139
+ if (reverse) preorder = false;
11140
+ } else {
11141
+ visitors = testOrEnterOrVisitors;
11142
+ if (enterOrVisitorsOrReverse) preorder = false;
11143
+ }
11144
+ if (typeof visitors === "function") enter = visitors;
11145
+ else if (visitors && typeof visitors === "object") {
11146
+ if (visitors.enter === null || visitors.enter === void 0) {} else if (typeof visitors.enter === "function") enter = visitors.enter;
11147
+ else throw new Error("Unexpected value `" + visitors.enter + "` for `visitors.enter`, expected optional visitor");
11148
+ if (typeof visitors.exit === "function") exit = visitors.exit;
11149
+ else throw new Error("Unexpected value `" + visitors.exit + "` for `visitors.exit`, expected visitor (pass a visitor directly if you only want an enter visitor)");
11150
+ } else throw new Error("Unexpected value `" + visitors + "` for `enter` or `visitors`, expected visitor or object with visitors");
10820
11151
  const is = convert(check);
10821
- const step = reverse ? -1 : 1;
11152
+ const step = preorder ? 1 : -1;
10822
11153
  factory(tree, void 0, [])();
10823
11154
  /**
11155
+ * Create a function to visit a node.
11156
+ *
10824
11157
  * @param {UnistNode} node
11158
+ * Node to visit.
10825
11159
  * @param {number | undefined} index
11160
+ * Index of `node` in its parent.
10826
11161
  * @param {Array<UnistParent>} parents
11162
+ * Ancestors of `node`.
11163
+ * @returns {() => Readonly<ActionTuple>}
11164
+ * Visit `node`.
10827
11165
  */
10828
11166
  function factory(node, index, parents) {
10829
11167
  const value = node && typeof node === "object" ? node : {};
@@ -10833,31 +11171,31 @@ ${trimmed}
10833
11171
  }
10834
11172
  return visit;
10835
11173
  function visit() {
11174
+ const matches = !check || is(node, index, parents[parents.length - 1] || void 0);
10836
11175
  /** @type {Readonly<ActionTuple>} */
10837
11176
  let result = empty;
10838
- /** @type {Readonly<ActionTuple>} */
10839
- let subresult;
10840
- /** @type {number} */
10841
- let offset;
10842
- /** @type {Array<UnistParent>} */
10843
- let grandparents;
10844
- if (!test || is(node, index, parents[parents.length - 1] || void 0)) {
10845
- result = toResult(visitor(node, parents));
11177
+ if (enter && matches) {
11178
+ result = toResult(enter(node, parents, index));
10846
11179
  if (result[0] === false) return result;
10847
11180
  }
10848
11181
  if ("children" in node && node.children) {
10849
11182
  const nodeAsParent = node;
10850
11183
  if (nodeAsParent.children && result[0] !== "skip") {
10851
- offset = (reverse ? nodeAsParent.children.length : -1) + step;
10852
- grandparents = parents.concat(nodeAsParent);
11184
+ let offset = (preorder ? -1 : nodeAsParent.children.length) + step;
11185
+ const grandparents = [...parents, nodeAsParent];
10853
11186
  while (offset > -1 && offset < nodeAsParent.children.length) {
10854
11187
  const child = nodeAsParent.children[offset];
10855
- subresult = factory(child, offset, grandparents)();
11188
+ const subresult = factory(child, offset, grandparents)();
10856
11189
  if (subresult[0] === false) return subresult;
10857
11190
  offset = typeof subresult[1] === "number" ? subresult[1] : offset + step;
10858
11191
  }
10859
11192
  }
10860
11193
  }
11194
+ if (exit && matches) {
11195
+ const exitResult = toResult(exit(node, parents, index));
11196
+ if (exitResult[0] === "skip") throw new Error("Unexpected value `" + exitResult[0] + "` from `exit`, expected `CONTINUE`, `EXIT`, index, or nothing");
11197
+ if (exitResult[0] === false || typeof exitResult[1] === "number") return exitResult;
11198
+ }
10861
11199
  return result;
10862
11200
  }
10863
11201
  }
@@ -10875,8 +11213,7 @@ ${trimmed}
10875
11213
  if (typeof value === "number") return [true, value];
10876
11214
  return value === null || value === void 0 ? empty : [value];
10877
11215
  }
10878
- //#endregion
10879
- //#region node_modules/mdast-util-find-and-replace/lib/index.js
11216
+ // node_modules/mdast-util-find-and-replace/lib/index.js
10880
11217
  /**
10881
11218
  * @import {Nodes, Parents, PhrasingContent, Root, Text} from 'mdast'
10882
11219
  * @import {BuildVisitor, Test, VisitorResult} from 'unist-util-visit-parents'
@@ -10944,7 +11281,7 @@ ${trimmed}
10944
11281
  * @param {FindAndReplaceList | FindAndReplaceTuple} list
10945
11282
  * Patterns to find.
10946
11283
  * @param {Options | null | undefined} [options]
10947
- * Configuration (when `find` is not `Find`).
11284
+ * Configuration (optional).
10948
11285
  * @returns {undefined}
10949
11286
  * Nothing.
10950
11287
  */
@@ -10952,24 +11289,15 @@ ${trimmed}
10952
11289
  const ignored = convert((options || {}).ignore || []);
10953
11290
  const pairs = toPairs(list);
10954
11291
  let pairIndex = -1;
10955
- while (++pairIndex < pairs.length) visitParents(tree, "text", visitor);
11292
+ while (++pairIndex < pairs.length) visitParents(tree, visitor);
10956
11293
  /**
10957
- * Visit a text node, and handle it if it is not in an ignored parent.
11294
+ * Handle text nodes and skip ignored nodes.
10958
11295
  *
10959
- * @type {BuildVisitor<Root, 'text'>}
11296
+ * @type {BuildVisitor<Root>}
10960
11297
  */
10961
- function visitor(node, parents) {
10962
- let index = -1;
10963
- /** @type {Parents | undefined} */
10964
- let grandparent;
10965
- while (++index < parents.length) {
10966
- const parent = parents[index];
10967
- /** @type {Array<Nodes> | undefined} */
10968
- const siblings = grandparent ? grandparent.children : void 0;
10969
- if (ignored(parent, siblings ? siblings.indexOf(parent) : void 0, grandparent)) return;
10970
- grandparent = parent;
10971
- }
10972
- if (grandparent) return handler(node, parents);
11298
+ function visitor(node, parents, index) {
11299
+ if (node.type === "text" && typeof index === "number") return handler(node, parents, index);
11300
+ if (ignored(node, index, parents[parents.length - 1])) return SKIP;
10973
11301
  }
10974
11302
  /**
10975
11303
  * Handle a text node which is not in an ignored parent.
@@ -10978,15 +11306,16 @@ ${trimmed}
10978
11306
  * Text node to search in.
10979
11307
  * @param {Array<Parents>} parents
10980
11308
  * Ancestors of `node`.
11309
+ * @param {number} index
11310
+ * Index of `node` in its parent.
10981
11311
  * @returns {VisitorResult}
10982
11312
  * Where to continue visiting.
10983
11313
  */
10984
- function handler(node, parents) {
11314
+ function handler(node, parents, index) {
10985
11315
  const parent = parents[parents.length - 1];
10986
11316
  const find = pairs[pairIndex][0];
10987
11317
  const replace = pairs[pairIndex][1];
10988
11318
  let start = 0;
10989
- const index = parent.children.indexOf(node);
10990
11319
  let change = false;
10991
11320
  /** @type {Array<PhrasingContent>} */
10992
11321
  let nodes = [];
@@ -11054,7 +11383,6 @@ ${trimmed}
11054
11383
  if (!Array.isArray(tupleOrList)) throw new TypeError("Expected find and replace tuple or list of tuples");
11055
11384
  /** @type {Pairs} */
11056
11385
  const result = [];
11057
- /** @type {FindAndReplaceList} */
11058
11386
  const list = !tupleOrList[0] || Array.isArray(tupleOrList[0]) ? tupleOrList : [tupleOrList];
11059
11387
  let index = -1;
11060
11388
  while (++index < list.length) {
@@ -11087,8 +11415,7 @@ ${trimmed}
11087
11415
  return replace;
11088
11416
  };
11089
11417
  }
11090
- //#endregion
11091
- //#region node_modules/mdast-util-gfm-autolink-literal/lib/index.js
11418
+ // node_modules/mdast-util-gfm-autolink-literal/lib/index.js
11092
11419
  /**
11093
11420
  * @import {RegExpMatchObject, ReplaceFunction} from 'mdast-util-find-and-replace'
11094
11421
  * @import {CompileContext, Extension as FromMarkdownExtension, Handle as FromMarkdownHandle, Transform as FromMarkdownTransform} from 'mdast-util-from-markdown'
@@ -11267,8 +11594,7 @@ ${trimmed}
11267
11594
  const code = match.input.charCodeAt(match.index - 1);
11268
11595
  return (match.index === 0 || unicodeWhitespace(code) || unicodePunctuation(code)) && (!email || code !== 47);
11269
11596
  }
11270
- //#endregion
11271
- //#region node_modules/mdast-util-gfm-footnote/lib/index.js
11597
+ // node_modules/mdast-util-gfm-footnote/lib/index.js
11272
11598
  /**
11273
11599
  * @import {
11274
11600
  * CompileContext,
@@ -11402,8 +11728,7 @@ ${trimmed}
11402
11728
  }
11403
11729
  };
11404
11730
  }
11405
- //#endregion
11406
- //#region node_modules/mdast-util-gfm-strikethrough/lib/index.js
11731
+ // node_modules/mdast-util-gfm-strikethrough/lib/index.js
11407
11732
  handleDelete.attention = attentionDelete;
11408
11733
  handleDelete.peek = peekDelete;
11409
11734
  /**
@@ -11483,8 +11808,7 @@ ${trimmed}
11483
11808
  function peekDelete() {
11484
11809
  return "~";
11485
11810
  }
11486
- //#endregion
11487
- //#region node_modules/mdast-util-gfm-table/lib/index.js
11811
+ // node_modules/mdast-util-gfm-table/lib/index.js
11488
11812
  /**
11489
11813
  * @typedef {import('mdast').InlineCode} InlineCode
11490
11814
  * @typedef {import('mdast').Table} Table
@@ -11608,8 +11932,7 @@ ${trimmed}
11608
11932
  function replace($0, $1) {
11609
11933
  return $1 === "|" ? $1 : $0;
11610
11934
  }
11611
- //#endregion
11612
- //#region node_modules/mdast-util-gfm-task-list-item/lib/index.js
11935
+ // node_modules/mdast-util-gfm-task-list-item/lib/index.js
11613
11936
  /**
11614
11937
  * @typedef {import('mdast').ListItem} ListItem
11615
11938
  * @typedef {import('mdast').Paragraph} Paragraph
@@ -11677,8 +12000,7 @@ ${trimmed}
11677
12000
  }
11678
12001
  this.exit(token);
11679
12002
  }
11680
- //#endregion
11681
- //#region node_modules/mdast-util-gfm/lib/index.js
12003
+ // node_modules/mdast-util-gfm/lib/index.js
11682
12004
  /**
11683
12005
  * @import {Extension as FromMarkdownExtension} from 'mdast-util-from-markdown'
11684
12006
  * @import {Options} from 'mdast-util-gfm'
@@ -11701,8 +12023,7 @@ ${trimmed}
11701
12023
  gfmTaskListItemFromMarkdown()
11702
12024
  ];
11703
12025
  }
11704
- //#endregion
11705
- //#region node_modules/mdast-util-math/lib/index.js
12026
+ // node_modules/mdast-util-math/lib/index.js
11706
12027
  /**
11707
12028
  * @typedef {import('hast').Element} HastElement
11708
12029
  * @typedef {import('hast').ElementContent} HastElementContent
@@ -11850,8 +12171,7 @@ ${trimmed}
11850
12171
  this.config.exit.data.call(this, token);
11851
12172
  }
11852
12173
  }
11853
- //#endregion
11854
- //#region node_modules/micromark-extension-gfm-autolink-literal/lib/syntax.js
12174
+ // node_modules/micromark-extension-gfm-autolink-literal/lib/syntax.js
11855
12175
  /**
11856
12176
  * @import {Code, ConstructRecord, Event, Extension, Previous, State, TokenizeContext, Tokenizer} from 'micromark-util-types'
11857
12177
  */
@@ -12551,8 +12871,7 @@ ${trimmed}
12551
12871
  if (events.length > 0 && !result) events[events.length - 1][1]._gfmAutolinkLiteralWalkedInto = true;
12552
12872
  return result;
12553
12873
  }
12554
- //#endregion
12555
- //#region node_modules/micromark-extension-gfm-footnote/lib/syntax.js
12874
+ // node_modules/micromark-extension-gfm-footnote/lib/syntax.js
12556
12875
  /**
12557
12876
  * @import {Event, Exiter, Extension, Resolver, State, Token, TokenizeContext, Tokenizer} from 'micromark-util-types'
12558
12877
  */
@@ -12970,8 +13289,7 @@ ${trimmed}
12970
13289
  return tail && tail[1].type === "gfmFootnoteDefinitionIndent" && tail[2].sliceSerialize(tail[1], true).length === 4 ? ok(code) : nok(code);
12971
13290
  }
12972
13291
  }
12973
- //#endregion
12974
- //#region node_modules/micromark-extension-gfm-strikethrough/lib/syntax.js
13292
+ // node_modules/micromark-extension-gfm-strikethrough/lib/syntax.js
12975
13293
  /**
12976
13294
  * @import {Options} from 'micromark-extension-gfm-strikethrough'
12977
13295
  * @import {Event, Extension, Resolver, State, Token, TokenizeContext, Tokenizer} from 'micromark-util-types'
@@ -13111,8 +13429,7 @@ ${trimmed}
13111
13429
  }
13112
13430
  }
13113
13431
  }
13114
- //#endregion
13115
- //#region node_modules/micromark-extension-gfm-table/lib/edit-map.js
13432
+ // node_modules/micromark-extension-gfm-table/lib/edit-map.js
13116
13433
  /**
13117
13434
  * @import {Event} from 'micromark-util-types'
13118
13435
  */
@@ -13222,8 +13539,7 @@ ${trimmed}
13222
13539
  editMap.map.push(change);
13223
13540
  editMap.index.set(at, change);
13224
13541
  }
13225
- //#endregion
13226
- //#region node_modules/micromark-extension-gfm-table/lib/infer.js
13542
+ // node_modules/micromark-extension-gfm-table/lib/infer.js
13227
13543
  /**
13228
13544
  * @import {Event} from 'micromark-util-types'
13229
13545
  */
@@ -13260,8 +13576,7 @@ ${trimmed}
13260
13576
  }
13261
13577
  return align;
13262
13578
  }
13263
- //#endregion
13264
- //#region node_modules/micromark-extension-gfm-table/lib/syntax.js
13579
+ // node_modules/micromark-extension-gfm-table/lib/syntax.js
13265
13580
  /**
13266
13581
  * @import {Event, Extension, Point, Resolver, State, Token, TokenizeContext, Tokenizer} from 'micromark-util-types'
13267
13582
  */
@@ -13977,8 +14292,7 @@ ${trimmed}
13977
14292
  const side = event[0] === "enter" ? "start" : "end";
13978
14293
  return event[1][side];
13979
14294
  }
13980
- //#endregion
13981
- //#region node_modules/micromark-extension-gfm-task-list-item/lib/syntax.js
14295
+ // node_modules/micromark-extension-gfm-task-list-item/lib/syntax.js
13982
14296
  /**
13983
14297
  * @import {Extension, State, TokenizeContext, Tokenizer} from 'micromark-util-types'
13984
14298
  */
@@ -14096,8 +14410,7 @@ ${trimmed}
14096
14410
  return code === null ? nok(code) : ok(code);
14097
14411
  }
14098
14412
  }
14099
- //#endregion
14100
- //#region node_modules/micromark-extension-gfm/index.js
14413
+ // node_modules/micromark-extension-gfm/index.js
14101
14414
  /**
14102
14415
  * @typedef {import('micromark-extension-gfm-footnote').HtmlOptions} HtmlOptions
14103
14416
  * @typedef {import('micromark-extension-gfm-strikethrough').Options} Options
@@ -14124,8 +14437,7 @@ ${trimmed}
14124
14437
  gfmTaskListItem()
14125
14438
  ]);
14126
14439
  }
14127
- //#endregion
14128
- //#region node_modules/micromark-extension-math/lib/math-flow.js
14440
+ // node_modules/micromark-extension-math/lib/math-flow.js
14129
14441
  /**
14130
14442
  * @import {Construct, State, TokenizeContext, Tokenizer} from 'micromark-util-types'
14131
14443
  */
@@ -14429,8 +14741,7 @@ ${trimmed}
14429
14741
  return self.parser.lazy[self.now().line] ? nok(code) : ok(code);
14430
14742
  }
14431
14743
  }
14432
- //#endregion
14433
- //#region node_modules/micromark-extension-math/lib/math-text.js
14744
+ // node_modules/micromark-extension-math/lib/math-text.js
14434
14745
  /**
14435
14746
  * @import {Options} from 'micromark-extension-math'
14436
14747
  * @import {Construct, Previous, Resolver, State, Token, TokenizeContext, Tokenizer} from 'micromark-util-types'
@@ -14615,8 +14926,7 @@ ${trimmed}
14615
14926
  function previous(code) {
14616
14927
  return code !== 36 || this.events[this.events.length - 1][1].type === "characterEscape";
14617
14928
  }
14618
- //#endregion
14619
- //#region node_modules/micromark-extension-math/lib/syntax.js
14929
+ // node_modules/micromark-extension-math/lib/syntax.js
14620
14930
  /**
14621
14931
  * @import {Options} from 'micromark-extension-math'
14622
14932
  * @import {Extension} from 'micromark-util-types'
@@ -14636,8 +14946,7 @@ ${trimmed}
14636
14946
  text: { [36]: mathText(options) }
14637
14947
  };
14638
14948
  }
14639
- //#endregion
14640
- //#region node_modules/katex/dist/katex.mjs
14949
+ // node_modules/katex/dist/katex.mjs
14641
14950
  /**
14642
14951
  * This is the ParseError class, which is the main error thrown by KaTeX
14643
14952
  * functions when something has gone wrong. This is used to distinguish internal
@@ -41079,8 +41388,7 @@ ${trimmed}
41079
41388
  LineNode
41080
41389
  }
41081
41390
  };
41082
- //#endregion
41083
- //#region node_modules/micromark-util-symbol/lib/codes.js
41391
+ // node_modules/micromark-util-symbol/lib/codes.js
41084
41392
  /**
41085
41393
  * Character codes.
41086
41394
  *
@@ -41237,8 +41545,7 @@ ${trimmed}
41237
41545
  byteOrderMarker: 65279,
41238
41546
  replacementCharacter: 65533
41239
41547
  };
41240
- //#endregion
41241
- //#region node_modules/micromark-util-symbol/lib/constants.js
41548
+ // node_modules/micromark-util-symbol/lib/constants.js
41242
41549
  /**
41243
41550
  * This module is compiled away!
41244
41551
  *
@@ -41283,8 +41590,7 @@ ${trimmed}
41283
41590
  thematicBreakMarkerCountMin: 3,
41284
41591
  v8MaxSafeChunkSize: 1e4
41285
41592
  };
41286
- //#endregion
41287
- //#region node_modules/micromark-util-symbol/lib/types.js
41593
+ // node_modules/micromark-util-symbol/lib/types.js
41288
41594
  /**
41289
41595
  * This module is compiled away!
41290
41596
  *
@@ -41400,8 +41706,7 @@ ${trimmed}
41400
41706
  chunkText: "chunkText",
41401
41707
  chunkString: "chunkString"
41402
41708
  };
41403
- //#endregion
41404
- //#region src/client/markdown/cjkFriendlyStrong.ts
41709
+ // src/client/markdown/cjkFriendlyStrong.ts
41405
41710
  /** Let asterisk strong emphasis close after punctuation when CJK prose continues without whitespace. */
41406
41711
  const cjkCharacter = new RegExp([
41407
41712
  "\\p{Script_Extensions=Han}",
@@ -41458,8 +41763,7 @@ ${trimmed}
41458
41763
  function cjkFriendlyStrong() {
41459
41764
  return cjkFriendlyStrongExtension;
41460
41765
  }
41461
- //#endregion
41462
- //#region src/client/markdown/mathCompatibility.ts
41766
+ // src/client/markdown/mathCompatibility.ts
41463
41767
  /** Extend upstream dollar-only math syntax with TeX delimiters while reusing its token vocabulary. */
41464
41768
  const previousBackslash = function(code) {
41465
41769
  if (code !== codes.backslash) return true;
@@ -41729,8 +42033,7 @@ ${trimmed}
41729
42033
  function mathCompatibility() {
41730
42034
  return backslashMath;
41731
42035
  }
41732
- //#endregion
41733
- //#region src/client/markdown/parse.ts
42036
+ // src/client/markdown/parse.ts
41734
42037
  /**
41735
42038
  * Parse GFM markdown (the streaming arm's grammar: no math, so incomplete
41736
42039
  * TeX never flashes KaTeX errors mid-stream).
@@ -41760,8 +42063,7 @@ ${trimmed}
41760
42063
  mdastExtensions: [gfmFromMarkdown(), mathFromMarkdown()]
41761
42064
  });
41762
42065
  }
41763
- //#endregion
41764
- //#region node_modules/clsx/dist/clsx.mjs
42066
+ // node_modules/clsx/dist/clsx.mjs
41765
42067
  function r(e) {
41766
42068
  var t, f, n = "";
41767
42069
  if ("string" == typeof e || "number" == typeof e) n += e;
@@ -41775,8 +42077,7 @@ ${trimmed}
41775
42077
  for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
41776
42078
  return n;
41777
42079
  }
41778
- //#endregion
41779
- //#region src/client/markdown/katex.tsx
42080
+ // src/client/markdown/katex.tsx
41780
42081
  /**
41781
42082
  * TeX-to-React via KaTeX, replicating the rehype-katex pipeline this renderer
41782
42083
  * replaced: the same three-arm error chain (strict render, `strict: 'ignore'`
@@ -41856,8 +42157,7 @@ ${trimmed}
41856
42157
  }
41857
42158
  return [...new DOMParser().parseFromString(html, "text/html").body.childNodes].map(domToReact);
41858
42159
  }
41859
- //#endregion
41860
- //#region \0dshx-css-module:MarkdownText.module.css.mjs
42160
+ // \0dshx-css-module:/home/runner/work/dsh-better-display/dsh-better-display/src/client/markdown/MarkdownText.module.css.mjs
41861
42161
  const css$6 = ".zfLqdW_markdown{overflow-wrap:anywhere;min-width:0;font:var(--dsw-font-markdown-base);color:var(--dsw-alias-label-primary)}.zfLqdW_markdown strong{font-weight:600}.zfLqdW_markdown h1{font:var(--dsw-font-markdown-h1);margin:32px 0 16px}.zfLqdW_markdown h2{font:var(--dsw-font-markdown-h2);margin:32px 0 16px}.zfLqdW_markdown h3{font:var(--dsw-font-markdown-h3);margin:32px 0 16px}.zfLqdW_markdown h4{font:var(--dsw-font-markdown-h4);margin:16px 0}.zfLqdW_markdown :where(h5,h6){font:var(--dsw-font-markdown-base-strong);margin:16px 0}.zfLqdW_markdown :where(h1,h2,h3,h4,h5,h6) strong{font-weight:inherit}.zfLqdW_markdown p{margin:16px 0}.zfLqdW_markdown :where(h4,h5,h6)+:where(ul,ol){margin-top:8px}.zfLqdW_markdown :where(h4,h5,h6):has(+:where(ul,ol)){margin-bottom:8px}.zfLqdW_markdown a{color:var(--dsw-alias-state-business-primary);transition:box-shadow var(--ds-transition-duration) var(--ds-ease-in-out);border:2px solid #fff0;border-width:2px 3px;margin-left:-3px;margin-right:-3px;text-decoration:none;position:relative}.zfLqdW_markdown a:hover,.zfLqdW_markdown a:focus{-webkit-text-decoration:underline var(--dsw-alias-state-business-primary);text-decoration:underline var(--dsw-alias-state-business-primary);outline:none}.zfLqdW_markdown a:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary)}.zfLqdW_markdown :where(ul,ol){margin:16px 0;padding-left:18px}.zfLqdW_markdown li:not(:first-child){margin-top:6px}.zfLqdW_markdown li>:where(ul,ol){margin-top:4px}.zfLqdW_markdown li::marker{color:var(--dsw-alias-label-secondary);line-height:28px}.zfLqdW_markdown :where(ul,ol) ol{padding-left:0;list-style-position:inside}.zfLqdW_markdown :where(ul,ol) ol li p{display:inline}.zfLqdW_markdown li>p{margin:8px 0}.zfLqdW_markdown li>:first-child{margin-top:0}.zfLqdW_markdown li>:last-child:not(.md-code-block){margin-bottom:0}.zfLqdW_markdown hr{background:var(--dsw-alias-border-l2);border:none;height:1px;margin:32px 0;display:block}.zfLqdW_markdown blockquote{border-left:2px solid var(--dsw-alias-label-caption);margin:16px 0 0;padding-left:14px}.zfLqdW_markdown pre{font-family:var(--ds-font-family-code);margin:16px 0;overflow:auto}.zfLqdW_markdown :not(pre)>code{box-sizing:border-box;font:var(--dsw-font-markdown-code);font-family:var(--ds-font-family-code);background-color:var(--dsw-alias-markdown-inline-code);border-radius:6px;align-items:center;padding:0 5px;display:inline-flex;font-size:.875em!important}.zfLqdW_markdown :where(h1,h2,h3,h4,h5,h6) code{font:inherit;font-family:var(--ds-font-family-code)}.zfLqdW_markdown .katex-display{max-width:100%;overflow:auto hidden}.zfLqdW_markdown input[type=checkbox]{accent-color:var(--dsw-alias-label-secondary);margin:0 8px 0 0}.zfLqdW_tableScroll{overscroll-behavior-x:contain;max-width:100%;overflow-x:auto}.zfLqdW_tableScroll.md-table-wide{padding-bottom:var(--dsh-scrollbar-width,8px);overflow-x:hidden}:is(.zfLqdW_tableScroll.md-table-wide:hover,.zfLqdW_tableScroll.md-table-wide:focus-visible){padding-bottom:0;overflow-x:auto}.zfLqdW_tableScroll:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}.zfLqdW_tableScroll table{border-collapse:collapse;width:max-content;max-width:max-content}.zfLqdW_tableFill table{width:100%;max-width:none}.zfLqdW_tableScroll th{text-align:start;border-bottom:1px solid var(--dsw-alias-border-l3);font:var(--dsw-font-markdown-table-head);border-top:none;min-width:100px;max-width:min(30vw,320px);padding:10px 16px}.zfLqdW_tableScroll td{border-bottom:1px solid var(--dsw-alias-border-l2);font:var(--dsw-font-markdown-table);min-width:100px;max-width:min(30vw,320px);padding:10px 16px}.zfLqdW_tableScroll th:first-child,.zfLqdW_tableScroll td:first-child{padding-left:0}.zfLqdW_tableScroll td:last-child{padding-right:0}.zfLqdW_tableScroll table code{font-size:13px}.zfLqdW_markdown>:first-child,.zfLqdW_markdown p:first-child{margin-top:0!important}.zfLqdW_markdown>:last-child,.zfLqdW_markdown p:last-child{margin-bottom:0!important}.zfLqdW_imageAlt{color:var(--dsw-alias-label-tertiary);font-style:italic}.zfLqdW_image{background:var(--dsw-alias-bg-base);object-fit:contain;border-radius:8px;width:auto;max-width:100%;height:auto;margin:0;display:block}.zfLqdW_fileMention{font:inherit;color:var(--dsw-alias-state-business-primary);cursor:pointer;background:0 0;border:none;margin:0;padding:0;text-decoration:none}.zfLqdW_fileMention:hover,.zfLqdW_fileMention:focus{-webkit-text-decoration:underline var(--dsw-alias-state-business-primary);text-decoration:underline var(--dsw-alias-state-business-primary);text-underline-offset:3px;outline:none}";
41862
42162
  const tagId$6 = "dsh-better-display/MarkdownText.module.css";
41863
42163
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$6) + "]") === null) {
@@ -41875,8 +42175,7 @@ ${trimmed}
41875
42175
  "tableFill": "zfLqdW_tableFill",
41876
42176
  "tableScroll": "zfLqdW_tableScroll"
41877
42177
  };
41878
- //#endregion
41879
- //#region src/client/markdown/render.tsx
42178
+ // src/client/markdown/render.tsx
41880
42179
  /**
41881
42180
  * Direct mdast→React markdown renderer. Replaces the react-markdown /
41882
42181
  * remark-rehype pipeline with one switch over parsed nodes so streaming can
@@ -42275,8 +42574,7 @@ ${trimmed}
42275
42574
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", { children: items })]
42276
42575
  }, "footnotes");
42277
42576
  }
42278
- //#endregion
42279
- //#region src/client/markdown/MarkdownText.tsx
42577
+ // src/client/markdown/MarkdownText.tsx
42280
42578
  /**
42281
42579
  * Untrusted assistant-Markdown renderer over the direct mdast pipeline:
42282
42580
  * `parse.ts` grammars, the incremental streaming parser, and `render.tsx`.
@@ -42455,8 +42753,7 @@ ${trimmed}
42455
42753
  children
42456
42754
  });
42457
42755
  });
42458
- //#endregion
42459
- //#region src/client/platform-media.ts
42756
+ // src/client/platform-media.ts
42460
42757
  /** Web or Desktop file route. Authorization stays Host-side. */
42461
42758
  function localPathMediaUrl(protocol, origin, value) {
42462
42759
  if (!value.startsWith("/") || value.startsWith("//")) return void 0;
@@ -42464,9 +42761,8 @@ ${trimmed}
42464
42761
  if (protocol === "dsh-app:" && origin === "dsh-app://app") return `dsh-app://app/api/file?path=${encodeURIComponent(value)}`;
42465
42762
  }
42466
42763
  const readerPathImages = { resolve: (value) => typeof window === "undefined" ? void 0 : localPathMediaUrl(window.location.protocol, window.location.origin, value) };
42467
- //#endregion
42468
- //#region \0dshx-css-module:Reader.module.css.mjs
42469
- const css$5 = ".gNoOdq_root{color:var(--dsw-alias-label-primary);padding:16px calc(var(--dsh-composer-side-clearance,16px) + 16px) 24px;overflow-anchor:auto;flex:none;min-width:0;font-family:inherit;font-size:16px;line-height:28px;container-type:inline-size}.gNoOdq_column{max-width:var(--dsh-chat-content-width,748px);flex-direction:column;gap:22px;width:100%;min-width:0;margin:0 auto;display:flex}.gNoOdq_toolbar{min-height:32px;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-base);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);justify-content:flex-end;align-items:center;gap:6px;padding:0;font-size:12px;display:flex;position:sticky;top:0}.gNoOdq_turnProcessSticky{background:var(--dsw-alias-bg-base);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);align-items:center;min-height:32px;display:flex;position:sticky}.gNoOdq_turnProcessSticky .gNoOdq_disclosureButton{min-height:32px}.gNoOdq_textButton,.gNoOdq_historyButton,.gNoOdq_iconButton,.gNoOdq_jump,.gNoOdq_disclosureButton,.gNoOdq_imageOpen,.gNoOdq_dialogClose{font:inherit;cursor:pointer}.gNoOdq_textButton{color:var(--dsw-alias-label-secondary);white-space:nowrap;background:0 0;border:0;border-radius:6px;padding:2px 6px}.gNoOdq_textButton:hover,.gNoOdq_iconButton:hover,.gNoOdq_disclosureButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.gNoOdq_root button:focus-visible,.gNoOdq_root summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:3px}.gNoOdq_turn,.gNoOdq_turnStickyRegion{flex-direction:column;gap:14px;min-width:0;display:flex}.gNoOdq_turnStickyLanes{top:max(var(--reader-toolbar-height,40px), var(--reader-status-height,40px));z-index:6;flex-direction:column;min-width:0;display:flex;position:sticky}.gNoOdq_turn+.gNoOdq_turn{margin-top:12px}.gNoOdq_userCluster{flex-direction:column;align-self:flex-end;align-items:flex-end;gap:8px;min-width:0;max-width:min(525px,82%);margin-bottom:4px;display:flex}.gNoOdq_userImages{flex-direction:column;align-items:flex-end;min-width:0;max-width:100%;display:flex}.gNoOdq_userImages .gNoOdq_imageFigure{align-self:flex-end}.gNoOdq_user{background:var(--dsw-specific-bubble);max-width:100%;color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;box-sizing:border-box;border-radius:22px;align-self:flex-end;padding:10px 16px;line-height:24px}.gNoOdq_user .gNoOdq_blocks{gap:10px}.gNoOdq_answer{overflow-wrap:anywhere;flex-direction:column;min-width:0;display:flex}.gNoOdq_readingText{min-width:0}.gNoOdq_reasonPlain{white-space:pre-wrap;overflow-wrap:anywhere;font:inherit;color:inherit}.gNoOdq_readingText button{min-width:24px;min-height:24px}.gNoOdq_streamWord{display:inline-block}.gNoOdq_streamInlineWord{display:inline}.gNoOdq_streamWord,.gNoOdq_streamInlineWord,.gNoOdq_streamAtom{opacity:1;filter:none}.gNoOdq_streamAtom{min-width:0}@media (prefers-reduced-motion:reduce){.gNoOdq_streamWord,.gNoOdq_streamInlineWord,.gNoOdq_streamAtom{opacity:1!important;filter:none!important}}.gNoOdq_retiringContent{min-width:0}.gNoOdq_retiringContent[data-reader-retiring=retiring]{overflow:hidden}.gNoOdq_mainFlow,.gNoOdq_blocks{flex-direction:column;gap:16px;min-width:0;display:flex}.gNoOdq_mainFlow:empty,.gNoOdq_blocks:empty{display:none}.gNoOdq_blocks>:first-child{margin-top:0}.gNoOdq_blocks>:last-child{margin-bottom:0}.gNoOdq_blocks .md-table-wide{max-width:100%}.gNoOdq_disclosure{min-width:0}.gNoOdq_disclosureButton{max-width:100%;min-height:32px;color:var(--dsw-alias-label-tertiary);text-align:left;background:0 0;border:0;border-radius:7px;align-items:center;gap:6px;margin-left:-6px;padding:4px 6px;font-size:14px;font-weight:400;line-height:24px;display:inline-flex}.gNoOdq_disclosure[data-reader-status-only]{align-items:center;min-height:32px;display:flex}.gNoOdq_statusText{min-width:0;max-width:100%;color:var(--dsw-alias-label-secondary);font-size:14px;font-weight:400;line-height:24px;display:inline-flex}.gNoOdq_srOnly{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.gNoOdq_think{--think-swap:.15s;--think-gap:50ms;--think-distance:8px;--think-blur:2px;--think-shimmer:2s;--think-base:var(--dsw-alias-label-secondary);--think-highlight:var(--dsw-alias-label-primary);--think-ease:ease-in-out;text-align:left;max-width:100%;display:inline-block;position:relative}.gNoOdq_thinkSizer{visibility:hidden;white-space:nowrap;display:block}.gNoOdq_thinkText{color:var(--think-base);white-space:nowrap;text-overflow:ellipsis;filter:blur();opacity:1;transition:transform var(--think-swap) var(--think-ease), filter var(--think-swap) var(--think-ease), opacity var(--think-swap) var(--think-ease);display:block;position:absolute;top:0;left:0;right:0;overflow:hidden;transform:translateY(0)}.gNoOdq_think[data-reader-status-phase=running] .gNoOdq_thinkText[data-reader-status-copy=current]{transition-delay:var(--think-gap)}.gNoOdq_think:not([data-reader-status-phase=idle]) .gNoOdq_thinkText{will-change:transform, filter, opacity}@supports (background-clip:text){.gNoOdq_think[data-active=true] .gNoOdq_thinkText:before{content:attr(data-text);pointer-events:none;background-image:linear-gradient(90deg, transparent 0%, transparent 40%, var(--think-highlight) 50%, transparent 60%, transparent 100%);color:#0000;-webkit-text-fill-color:transparent;animation:gNoOdq_thinkShimmer var(--think-shimmer) linear infinite;background-repeat:no-repeat;background-size:400% 100%;background-clip:text;position:absolute;inset:0}}@keyframes gNoOdq_thinkShimmer{0%{background-position:100% 0}to{background-position:0 0}}.gNoOdq_thinkText.gNoOdq_isExit{transform:translateY(calc(var(--think-distance) * -1));filter:blur(var(--think-blur));opacity:0}.gNoOdq_thinkText.gNoOdq_isEnterStart{transform:translateY(var(--think-distance));filter:blur(var(--think-blur));opacity:0;transition:none}[data-motion=off] .gNoOdq_thinkText{filter:none!important;opacity:1!important;transition:none!important;transform:none!important}[data-motion=off] .gNoOdq_thinkText[data-reader-status-copy=outgoing],[data-motion=off] .gNoOdq_thinkText:before{display:none!important}@media (prefers-reduced-motion:reduce),(forced-colors:active){.gNoOdq_thinkText{filter:none!important;opacity:1!important;transition:none!important;transform:none!important}.gNoOdq_thinkText[data-reader-status-copy=outgoing],.gNoOdq_thinkText:before{display:none!important}}.gNoOdq_processMeta{opacity:0;grid-template-rows:0fr;transition:grid-template-rows .26s cubic-bezier(.22,1,.36,1),opacity .16s ease-out;display:grid}.gNoOdq_processMeta[data-open=true]{opacity:1;grid-template-rows:1fr}.gNoOdq_processMetaInner{min-height:0;overflow:hidden}.gNoOdq_processMetaLine{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex-wrap:wrap;align-items:baseline;gap:8px;padding:8px 0 0 16px;font-size:12px;line-height:20px;display:flex}.gNoOdq_processMetaLine .gNoOdq_meta{color:inherit}.gNoOdq_chevron{stroke:currentColor;fill:none;stroke-width:1.4px;stroke-linecap:round;stroke-linejoin:round;flex:none;transition:transform .26s cubic-bezier(.22,1,.36,1)}.gNoOdq_chevron[data-open=true]{transform:rotate(90deg)}.gNoOdq_disclosureBody{transform-origin:top;will-change:height, opacity, transform;height:0;overflow:hidden}.gNoOdq_processContents{border-left:1px solid var(--dsw-alias-border-l2);padding:3px 0 2px var(--reader-gutter,16px);margin:0 0 0 var(--reader-rail,6px);color:var(--dsw-alias-label-secondary);font-size:14px;line-height:24px;display:flow-root}.gNoOdq_processContents>:last-child{margin-bottom:0}.gNoOdq_processCommentary{color:var(--dsw-alias-label-secondary);margin:4px 0 8px var(--reader-rail,6px);padding-left:var(--reader-gutter,16px);border-left:1px solid var(--dsw-alias-border-l2);font-size:14px;line-height:24px}.gNoOdq_processFrame{min-width:0}.gNoOdq_toolActivity{min-width:0;color:var(--dsw-alias-label-secondary)}.gNoOdq_nativeToolRow{min-width:0}.gNoOdq_rowSeparator{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.gNoOdq_nativeToolSummary{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-tertiary);flex:1;font-size:14px;line-height:24px;overflow:hidden}.gNoOdq_nativeToolRow:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:3px;border-radius:4px}.gNoOdq_toolHeading{width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:11px;grid-template-columns:20px minmax(0,1fr) auto;align-items:start;gap:10px;padding:10px 14px 4px;display:grid}.gNoOdq_toolHeading:hover{background:var(--dsw-alias-interactive-bg-hover)}.gNoOdq_toolHeading:focus-visible{outline-offset:-3px!important}.gNoOdq_toolGlyph{height:22px;color:var(--dsw-alias-label-secondary);justify-content:center;align-items:center;display:flex}.gNoOdq_toolHeadingCopy{flex-direction:column;gap:3px;min-width:0;display:flex}.gNoOdq_toolTitle{overflow-wrap:anywhere;font-size:13px;font-weight:500;line-height:22px}.gNoOdq_toolTarget{-webkit-line-clamp:2;overflow-wrap:anywhere;color:var(--dsw-alias-label-secondary);-webkit-box-orient:vertical;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:19px;display:-webkit-box;overflow:hidden}.gNoOdq_toolHeadingState{min-height:22px;color:var(--dsw-alias-label-secondary);align-items:center;gap:7px;display:flex}.gNoOdq_diffStat{font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";white-space:nowrap;flex:none;gap:6px;margin-left:8px;font-size:11px;line-height:18px;display:inline-flex}.gNoOdq_diffAdded{color:var(--dsw-alias-state-success-primary,#2f9e63)}.gNoOdq_diffRemoved{color:var(--dsw-alias-state-error-primary,#d1483c)}.gNoOdq_summaryRow{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;max-width:100%;display:flex}.gNoOdq_summaryRow>.gNoOdq_diffStatRoot{flex:none;margin-left:auto}.gNoOdq_summaryRow>:first-child{min-width:0}.gNoOdq_diffStatRoot{flex:none;position:relative}.gNoOdq_diffStatButton{cursor:pointer;font:inherit;font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";color:var(--dsw-alias-label-secondary);background:0 0;border:1px solid #0000;border-radius:6px;align-items:center;gap:6px;padding:2px 8px;font-size:11px;line-height:16px;transition:background .12s,border-color .12s;display:inline-flex}.gNoOdq_diffStatButton:hover,.gNoOdq_diffStatButton:focus-visible,.gNoOdq_diffStatButton[aria-expanded=true]{background:var(--dsw-alias-bg-module-platform,#7f7f7f1a);border-color:var(--dsw-alias-border-l2,#7f7f7f47)}.gNoOdq_diffStatButton:focus-visible{outline:none}.gNoOdq_diffStatButton[aria-expanded=true] .gNoOdq_diffCaret{opacity:1}.gNoOdq_diffOverlayRow{box-sizing:border-box;flex:100%;width:100%;min-width:0;max-width:100%;margin-top:6px}.gNoOdq_diffOverlay{box-sizing:border-box;background:var(--dsw-alias-bg-module-platform,#7f7f7f14);border:1px solid var(--dsw-alias-border-l2,#80808038);border-radius:10px;flex-direction:column;width:100%;min-width:0;max-width:100%;padding:8px;display:flex;overflow:hidden}.gNoOdq_diffTabs{scrollbar-width:thin;flex-wrap:nowrap;gap:6px;max-width:100%;margin-bottom:4px;padding-bottom:6px;display:flex;overflow-x:auto}.gNoOdq_diffTab{text-overflow:ellipsis;white-space:nowrap;cursor:pointer;max-width:220px;font:inherit;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-base,#fffc);border:1px solid var(--dsw-alias-border-l2,#80808033);border-radius:6px;flex:none;padding:3px 10px;font-size:11px;line-height:16px;transition:all .12s;overflow:hidden}.gNoOdq_diffTab:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.gNoOdq_diffTab[data-active]{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-state-business-primary,#1e90ff);background:var(--dsw-alias-bg-base);font-weight:500}.gNoOdq_diffScrollArea{overscroll-behavior:contain;-webkit-overflow-scrolling:touch;border:1px solid var(--dsw-alias-border-l2,#80808026);background:var(--dsw-alias-markdown-code-block,#0000000a);border-radius:8px;max-height:420px;overflow:auto}.gNoOdq_diffScrollArea [data-diff]{background:0 0!important;border-radius:0!important;margin:0!important}.gNoOdq_diffCaret{opacity:.6;flex:none;width:10px;font-size:10px}.gNoOdq_diffPath{text-overflow:ellipsis;white-space:nowrap;text-align:left;direction:rtl;flex:auto;min-width:0;overflow:hidden}.gNoOdq_diffStat{font-variant-numeric:tabular-nums;flex:none;gap:6px;font-size:11px;display:inline-flex}.gNoOdq_diffHunkBody{flex-direction:column;gap:4px;padding:2px 7px 7px 25px;display:flex}.gNoOdq_diffLines{font-size:11px;line-height:1.55;font-family:var(--dsw-font-mono,ui-monospace, SFMono-Regular, Menlo, monospace);white-space:pre;border-radius:6px;margin:0;padding:6px 8px;overflow-x:auto}.gNoOdq_diffLines[data-side=removed]{background:#d1483c1f}.gNoOdq_diffLines[data-side=added]{background:#2f9e631f}.gNoOdq_diffLineRemoved,.gNoOdq_diffLineAdded{display:block}.gNoOdq_diffLineRemoved{color:var(--dsw-alias-state-error-primary,#d1483c)}.gNoOdq_diffLineAdded{color:var(--dsw-alias-state-success-primary,#2f9e63)}.gNoOdq_diffEmpty{color:var(--dsw-alias-label-secondary);margin:0;padding:2px 0;font-size:11px}.gNoOdq_nativeToolSummary[data-running]{background-image:linear-gradient(90deg, currentColor 0%, currentColor 35%, color-mix(in srgb, currentColor 35%, transparent) 50%, currentColor 65%, currentColor 100%);-webkit-text-fill-color:transparent;background-position:100% 0;background-repeat:no-repeat;background-size:300% 100%;background-clip:text;animation:1.6s linear infinite gNoOdq_readerStepShimmer}@keyframes gNoOdq_readerStepShimmer{to{background-position:0 0}}.gNoOdq_toolElapsed[data-slow]{opacity:1;color:var(--dsw-alias-state-warning-primary,#d8862c);font-weight:500}.gNoOdq_toolElapsed{opacity:.72;font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";white-space:nowrap;flex:none;margin-left:8px;font-size:11px;line-height:18px}[data-motion=off] .gNoOdq_nativeToolSummary[data-running]{-webkit-text-fill-color:currentColor;background-image:none;animation:none!important}@media (prefers-reduced-motion:reduce){.gNoOdq_nativeToolSummary[data-running]{-webkit-text-fill-color:currentColor;background-image:none;animation:none!important}}.gNoOdq_toolState{white-space:nowrap;flex:none;margin-left:8px;font-size:11px;line-height:18px}.gNoOdq_toolState[data-phase=preparing],.gNoOdq_toolState[data-phase=running]{color:var(--dsw-alias-state-business-primary)}.gNoOdq_toolState[data-phase=preparing]:before,.gNoOdq_toolState[data-phase=running]:before{content:\"\";vertical-align:middle;background:currentColor;border-radius:50%;width:6px;height:6px;margin-right:6px;animation:1.4s ease-in-out infinite gNoOdq_readerRunningGlow;display:inline-block}@keyframes gNoOdq_readerRunningGlow{0%{box-shadow:0 0 0 0 color-mix(in srgb, currentColor 60%, transparent);opacity:1}70%{opacity:.7;box-shadow:0 0 0 7px #0000}to{opacity:1;box-shadow:0 0 #0000}}[data-motion=off] .gNoOdq_toolState[data-phase=preparing]:before,[data-motion=off] .gNoOdq_toolState[data-phase=running]:before{animation:none!important}@media (prefers-reduced-motion:reduce){.gNoOdq_toolState[data-phase=preparing]:before,.gNoOdq_toolState[data-phase=running]:before{animation:none!important}}.gNoOdq_toolState[data-phase=failed]{color:var(--dsw-alias-state-error-primary)}.gNoOdq_toolState[data-phase=interrupted]{color:inherit;opacity:.7}.gNoOdq_toolLedger{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex-wrap:wrap;align-items:baseline;gap:4px 12px;padding:10px 14px 0;font-size:11px;line-height:18px;display:flex}.gNoOdq_toolEngine{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.gNoOdq_toolDetails{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;min-width:0;margin:6px 0 0 22px;overflow:hidden}.gNoOdq_toolTabs{align-items:center;gap:15px;padding:0 14px;display:flex}.gNoOdq_toolTabs>button{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-bottom:2px solid #0000;padding:10px 1px 8px;font-size:12px;line-height:20px}.gNoOdq_toolTabs>button[aria-selected=true]{color:var(--dsw-alias-label-primary);border-bottom-color:var(--dsw-alias-state-business-primary)}.gNoOdq_toolPanel{flex-direction:column;gap:12px;min-width:0;padding:12px 14px 14px;font-size:13px;line-height:22px;display:flex}.gNoOdq_toolPanel:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-3px}.gNoOdq_toolDetailNote{color:var(--dsw-alias-label-secondary);margin:0 0 9px;font-size:12px;line-height:20px}.gNoOdq_toolRawLabel{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;font-weight:500}.gNoOdq_toolRaw{background:var(--dsw-alias-bg-module-platform);max-height:400px;color:var(--dsw-alias-label-primary);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:8px;margin:0;padding:12px;font:12px/20px ui-monospace,SFMono-Regular,Menlo,monospace;overflow:auto}.gNoOdq_systemPrompt{background:var(--dsw-alias-bg-module-platform);max-height:320px;color:var(--dsw-alias-label-primary);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:8px;margin:0;padding:12px;font:12px/20px ui-monospace,SFMono-Regular,Menlo,monospace;overflow:auto}.gNoOdq_commandInput{white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.gNoOdq_toolReceipt .gNoOdq_blocks,.gNoOdq_toolDocument .gNoOdq_blocks{font-size:13px;line-height:22px}.gNoOdq_toolChildren{margin:0 14px 12px var(--reader-rail,6px);padding-left:var(--reader-gutter,16px);border-left:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:9px;display:flex}@container (width<=420px){.gNoOdq_toolHeading{grid-template-columns:16px minmax(0,1fr) auto;gap:7px;padding:11px 10px 6px}.gNoOdq_toolHeadingState{gap:4px}.gNoOdq_toolLedger{padding-left:33px;padding-right:10px}.gNoOdq_toolPanel{padding:10px}}@media (pointer:coarse){.gNoOdq_toolTabs>button{min-height:44px}}.gNoOdq_reasonCard{--reason-preview-height:224px;--reason-hold:.84s;--reason-step:.5s;--reason-lines:2;--reason-fade:28px;--reason-ease:cubic-bezier(.22, 1, .36, 1);border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);min-width:0;scroll-margin-block:16px calc(var(--dsh-composer-height,152px) + 16px);border-radius:12px;position:relative;overflow:hidden}.gNoOdq_reasonHeading{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;justify-content:space-between;align-items:baseline;gap:12px;padding:10px 16px 0;font-size:12px;line-height:20px;display:flex}.gNoOdq_reasonCard[data-overflow=false][data-expanded=false]{background:0 0;border-color:#0000;border-radius:0}.gNoOdq_reasonCard[data-overflow=false][data-expanded=false] .gNoOdq_reasonHeading{padding:0}.gNoOdq_reasonCard[data-overflow=false][data-expanded=false] .gNoOdq_reasonText{padding:4px 0 0 22px}.gNoOdq_reasonLabel{font-weight:500}.gNoOdq_reasonViewport{max-height:var(--reason-preview-height);scroll-behavior:auto;overflow-anchor:none;overscroll-behavior-y:contain;scrollbar-width:thin;scrollbar-color:var(--dsw-alias-border-l1) transparent;position:relative;overflow:auto}.gNoOdq_reasonCard[data-expanded=true] .gNoOdq_reasonViewport{max-height:min(60vh, 560px, var(--reason-reading-height,560px))}.gNoOdq_reasonViewport[data-edges=both],.gNoOdq_reasonCard[data-following=true][data-overflow=true] .gNoOdq_reasonViewport{-webkit-mask-image:linear-gradient(transparent 0, black var(--reason-fade), black calc(100% - var(--reason-fade)), transparent 100%);mask-image:linear-gradient(transparent 0, black var(--reason-fade), black calc(100% - var(--reason-fade)), transparent 100%)}.gNoOdq_reasonViewport[data-edges=top]{-webkit-mask-image:linear-gradient(transparent 0, black var(--reason-fade), black 100%);mask-image:linear-gradient(transparent 0, black var(--reason-fade), black 100%)}.gNoOdq_reasonViewport[data-edges=bottom]{-webkit-mask-image:linear-gradient(black 0, black calc(100% - var(--reason-fade)), transparent 100%);mask-image:linear-gradient(black 0, black calc(100% - var(--reason-fade)), transparent 100%)}.gNoOdq_reasonCard[data-expanded=true] .gNoOdq_reasonViewport{mask-image:none}.gNoOdq_reasonViewport:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-3px}.gNoOdq_reasonTrack{position:relative;transform:translateY(0)}.gNoOdq_reasonCard[data-following=true] .gNoOdq_reasonTrack{will-change:transform}.gNoOdq_reasonText{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;padding:8px 16px 16px;font-size:14px;line-height:24px}.gNoOdq_reasonText .gNoOdq_readingText>div{font-size:inherit;line-height:inherit;color:inherit}.gNoOdq_reasonFooter{color:var(--dsw-alias-label-secondary);justify-content:space-between;align-items:center;gap:8px;padding:2px 10px 8px;font-size:12px;line-height:20px;display:flex}.gNoOdq_reasonAction{min-height:28px;color:inherit;font:inherit;cursor:pointer;white-space:nowrap;background:0 0;border:0;border-radius:6px;justify-content:center;align-items:center;gap:5px;padding:4px 6px;display:inline-flex}.gNoOdq_reasonAction:hover{background:var(--dsw-alias-interactive-bg-hover)}.gNoOdq_reasonAction:disabled{cursor:default;opacity:.65}.gNoOdq_reasonAction svg{fill:none;stroke:currentColor;stroke-width:1.3px;stroke-linecap:round;stroke-linejoin:round;flex:none}.gNoOdq_reasonCaption{padding-left:6px}@container (width<=480px){.gNoOdq_reasonCard{--reason-preview-height:192px}}@media (pointer:coarse){.gNoOdq_reasonAction{min-height:44px}}@media (prefers-reduced-motion:reduce){.gNoOdq_reasonTrack{will-change:auto!important;transition:none!important;transform:none!important}}@media (forced-colors:active){.gNoOdq_reasonViewport{mask-image:none!important}}.gNoOdq_processItem,.gNoOdq_tool{min-width:0;margin:0 0 18px}.gNoOdq_processLabel{color:var(--dsw-alias-label-tertiary);align-items:baseline;gap:12px;margin:0 0 6px;font-size:12px;line-height:20px;display:flex}.gNoOdq_processFootnote{color:var(--dsw-alias-label-tertiary);margin:14px 0 0;font-size:12px;line-height:20px}.gNoOdq_detail{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:22px}.gNoOdq_detail>summary{cursor:pointer;padding:3px 0}.gNoOdq_detail[open]>summary{margin-bottom:8px}.gNoOdq_tool>.gNoOdq_tool{margin:10px 0 0 12px}.gNoOdq_meta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px}.gNoOdq_stopped{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-tertiary);border-radius:6px;align-self:flex-start;margin-top:12px;padding:0 6px;font-size:11px;line-height:18px}.gNoOdq_compactionRow{box-sizing:border-box;flex-direction:column;align-items:center;width:100%;margin:16px 0;display:flex}.gNoOdq_compactionLine{width:100%;color:var(--dsw-alias-label-tertiary);-webkit-user-select:none;user-select:none;justify-content:center;align-items:center;gap:12px;font-size:11px;line-height:18px;display:flex}.gNoOdq_compactionLine:before,.gNoOdq_compactionLine:after{content:\"\";background:var(--dsw-alias-border-l1);opacity:.7;flex:1;height:1px}.gNoOdq_compactionRow>.gNoOdq_compactionLine{animation:.52s cubic-bezier(.4,0,.2,1) both gNoOdq_compactionLineIn}.gNoOdq_compactionRow>.gNoOdq_compactionLine>.gNoOdq_compactionPill{animation:.42s cubic-bezier(.34,1.4,.64,1) .12s both gNoOdq_compactionPillIn}.gNoOdq_compactionRow>.gNoOdq_compactionLine .gNoOdq_compactionIcon{animation:.9s cubic-bezier(.4,0,.2,1) .2s both gNoOdq_compactionDial}@keyframes gNoOdq_compactionLineIn{0%{opacity:0;transform:scaleX(.6)}to{opacity:1;transform:scaleX(1)}}@keyframes gNoOdq_compactionPillIn{0%{opacity:0;transform:translateY(-6px)scale(.94)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes gNoOdq_compactionDial{0%{opacity:0;transform:rotate(-140deg)}to{opacity:.7;transform:rotate(0)}}[data-motion=off] .gNoOdq_compactionRow>.gNoOdq_compactionLine,[data-motion=off] .gNoOdq_compactionRow>.gNoOdq_compactionLine>.gNoOdq_compactionPill,[data-motion=off] .gNoOdq_compactionRow>.gNoOdq_compactionLine .gNoOdq_compactionIcon{animation:none!important}@media (prefers-reduced-motion:reduce){.gNoOdq_compactionRow>.gNoOdq_compactionLine,.gNoOdq_compactionRow>.gNoOdq_compactionLine>.gNoOdq_compactionPill,.gNoOdq_compactionRow>.gNoOdq_compactionLine .gNoOdq_compactionIcon{animation:none!important}}.gNoOdq_compactionPill{background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-tertiary);border-radius:12px;align-items:center;gap:6px;padding:2px 10px;font-family:inherit;font-size:11px;line-height:18px;display:inline-flex}.gNoOdq_compactionButton{cursor:pointer;transition:color .14s,border-color .14s,background-color .14s}.gNoOdq_compactionButton:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-interactive-bg-hover)}.gNoOdq_compactionIcon{opacity:.7;flex:none;width:12px;height:12px}.gNoOdq_compactionToggle{opacity:.8;margin-left:2px;font-size:10px}.gNoOdq_compactionSummaryBox{background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l2);width:100%;max-width:680px;color:var(--dsw-alias-label-secondary);box-sizing:border-box;border-radius:8px;margin-top:10px;padding:12px 16px;font-size:12px;line-height:20px}.gNoOdq_compactionSummaryHeader{color:var(--dsw-alias-label-tertiary);border-bottom:1px dashed var(--dsw-alias-border-l1);margin-bottom:8px;padding-bottom:4px;font-size:11px;font-weight:500}.gNoOdq_notice,.gNoOdq_error,.gNoOdq_attention{overflow-wrap:anywhere;border-radius:10px;padding:12px 14px;font-size:13px;line-height:20px}.gNoOdq_notice{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border:1px solid var(--dsw-alias-border-l2)}.gNoOdq_error{color:var(--dsw-alias-label-primary);background:#ef44440f;border:1px solid #ef444438;align-items:flex-start;gap:10px;display:flex}.gNoOdq_errorIcon{color:#ef4444;flex:none;width:16px;height:16px;margin-top:2px}.gNoOdq_errorCopy{flex:1;min-width:0}.gNoOdq_errorTitle{color:#dc2626;align-items:center;gap:8px;margin-bottom:2px;font-weight:500;display:flex}.gNoOdq_errorMessage{color:var(--dsw-alias-label-secondary);word-break:break-word;margin:0;font-size:12px;line-height:18px}.gNoOdq_errorCode{color:#dc2626;background:#ef44441f;border-radius:4px;padding:1px 5px;font-family:inherit;font-size:11px}.gNoOdq_attention{border:1px solid var(--dsw-alias-state-business-primary);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-base);flex-direction:column;gap:3px;display:flex}.gNoOdq_attention span{color:var(--dsw-alias-label-secondary)}.gNoOdq_unknown{background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:12px 14px;font-size:13px;line-height:22px}.gNoOdq_unknown p{margin:0 0 6px}.gNoOdq_historyButton{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0;border-radius:8px;align-self:center;padding:4px 12px;font-size:12px}.gNoOdq_historyButton:disabled{opacity:.55;cursor:default}.gNoOdq_empty{color:var(--dsw-alias-label-tertiary);text-align:center;padding:40px 0}.gNoOdq_answerActions{align-items:center;gap:8px;min-height:28px;margin:13px 0 0 -6px;display:flex}.gNoOdq_userActions{align-items:center;gap:8px;min-height:28px;display:flex}.gNoOdq_messageClock{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:13px;line-height:24px}.gNoOdq_iconButton{color:var(--dsw-alias-label-tertiary);background:0 0;border:0;border-radius:6px;justify-content:center;align-items:center;padding:6px;display:inline-flex}.gNoOdq_iconButton svg{fill:none;stroke:currentColor;stroke-width:1.2px;stroke-linecap:round;stroke-linejoin:round}.gNoOdq_imageFigure{box-sizing:border-box;align-self:flex-start;width:min(100%,360px);max-width:100%;margin:0}.gNoOdq_imageFrame{background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l2);box-sizing:border-box;border-radius:12px;max-width:100%;min-height:96px;max-height:440px;position:relative;overflow:hidden}.gNoOdq_imageOpen{background:0 0;border:0;width:100%;height:100%;padding:0;display:flex;position:absolute;inset:0}.gNoOdq_imageOpen img{object-fit:contain;opacity:0;width:100%;height:100%;transition:opacity .2s cubic-bezier(.4,0,.2,1);display:block}.gNoOdq_imageOpen img[data-ready=true]{opacity:1}.gNoOdq_pendingImage{object-fit:contain;width:100%;max-width:100%;height:100%;display:block}.gNoOdq_imagePlaceholder{color:var(--dsw-alias-label-tertiary);pointer-events:none;flex-direction:column;justify-content:center;align-items:center;gap:8px;font-size:13px;display:flex;position:absolute;inset:0}.gNoOdq_imagePlaceholder button{pointer-events:auto}.gNoOdq_imageFigure figcaption{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;margin-top:6px;font-size:12px;line-height:20px}.gNoOdq_imageDialog{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);border-radius:16px;max-width:90vw;max-height:90vh;padding:38px 20px 20px}.gNoOdq_imageDialog::backdrop{background:var(--dsw-alias-bg-mask-photo)}.gNoOdq_imageDialog img{object-fit:contain;max-width:82vw;max-height:75vh;display:block}.gNoOdq_dialogClose{color:inherit;background:0 0;border:0;border-radius:8px;width:32px;height:32px;font-size:24px;position:absolute;top:4px;right:8px}.gNoOdq_jumpDock{bottom:calc(var(--dsh-composer-height,152px) + 16px);z-index:8;pointer-events:none;justify-content:flex-end;height:0;display:flex;position:sticky}.gNoOdq_deliverablesRoot{grid-template-columns:max-content minmax(0,1fr);align-items:start;column-gap:10px;margin-top:14px;font-size:13px;line-height:22px;display:grid}.gNoOdq_deliverablesLabel{color:var(--dsw-alias-label-tertiary);grid-area:1/1;padding-top:2px;font-size:12px;font-weight:500}.gNoOdq_deliverablesLane{grid-area:1/2;min-width:0}.gNoOdq_deliverablesRow{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;display:flex}.gNoOdq_deliverableChip{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:6px;align-items:center;font-size:12px;line-height:20px;transition:border-color .16s,background-color .16s,color .16s;display:inline-flex;overflow:hidden}.gNoOdq_deliverableChip:hover{border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-bg-module-platform);box-shadow:0 1px 4px var(--dsw-alias-border-l1)}.gNoOdq_deliverableChip[data-status=opened]{color:#059669;background:#10b9811f;border-color:#10b981}.gNoOdq_chipMain{max-width:220px;color:inherit;font:inherit;cursor:pointer;text-align:left;background:0 0;border:none;align-items:center;gap:5px;padding:3px 6px 3px 8px;display:inline-flex}.gNoOdq_chipMain:hover{color:var(--dsw-alias-label-primary)}.gNoOdq_deliverableIcon,.gNoOdq_statusIcon{opacity:.8;flex:none;width:13px;height:13px}.gNoOdq_statusIcon{color:#10b981}.gNoOdq_deliverableName{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.gNoOdq_chipActions{opacity:.65;border-left:1px solid #0000;align-items:center;gap:2px;padding-right:4px;transition:opacity .15s,border-left-color .15s;display:inline-flex}.gNoOdq_deliverableChip:hover .gNoOdq_chipActions{opacity:1;border-left-color:var(--dsw-alias-border-l2)}.gNoOdq_chipActionBtn{width:20px;height:20px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;padding:0;transition:color .12s,background-color .12s;display:inline-flex}.gNoOdq_chipActionBtn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.gNoOdq_actionIcon{width:12px;height:12px}.gNoOdq_deliverablesMore{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:12px}.gNoOdq_deliverablesShowFolder{color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:5px;align-items:center;gap:4px;padding:2px 7px;font-size:12px;transition:all .15s;display:inline-flex}.gNoOdq_deliverablesShowFolder:hover{color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l2)}.gNoOdq_deliverablesShowFolder[data-status=opened]{color:#10b981;background:#10b98114;border-color:#10b9814d}.gNoOdq_morphCluster{flex-direction:column;width:100%;min-width:0;display:flex}.gNoOdq_morphDrawer{transform-origin:top;will-change:height, opacity, transform;height:0;overflow:hidden}.gNoOdq_liveFoldImpact{animation:.18s cubic-bezier(.2,0,0,1) both gNoOdq_liveFoldElastic}@keyframes gNoOdq_liveFoldElastic{0%{transform:scale(1)}50%{transform:scale(1.015)}to{transform:scale(1)}}.gNoOdq_liveFoldContainer{top:calc(max(var(--reader-toolbar-height,40px), var(--reader-status-height,40px)) + var(--reader-sticky-lane,40px) * 2);z-index:4;background:var(--dsw-alias-bg-base);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);transform-origin:0;will-change:transform, opacity;margin-bottom:6px;padding:4px 0;animation:.32s cubic-bezier(.16,1,.3,1) both gNoOdq_liveFoldPulse;position:sticky}@keyframes gNoOdq_liveFoldPulse{0%{opacity:.8;transform:translateY(-4px)scale(.98)}50%{opacity:1;transform:translateY(0)scale(1.02)}to{opacity:1;transform:translateY(0)scale(1)}}[data-motion=off] .gNoOdq_liveFoldContainer{animation:none!important}@media (prefers-reduced-motion:reduce){.gNoOdq_liveFoldContainer{animation:none!important}}.gNoOdq_priorFoldDrawer{flex-direction:column;gap:14px;width:100%;min-width:0;display:flex}.gNoOdq_jump{width:34px;height:34px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-button-floating-fill,var(--dsw-alias-bg-base));box-shadow:var(--dsw-elevation-panel,0 2px 8px var(--dsw-alias-border-l1));pointer-events:auto;border:0;border-radius:100px;flex:none;justify-content:center;align-items:center;margin-top:-34px;padding:0;display:flex}.gNoOdq_jump:hover{background:var(--dsw-alias-button-floating-hover,var(--dsw-alias-interactive-bg-hover))}.gNoOdq_foldSummary{text-overflow:ellipsis;align-items:baseline;max-width:100%;display:inline-flex;overflow:hidden;white-space:nowrap!important}.gNoOdq_foldSummaryPart{flex:none;align-items:baseline;display:inline-flex;white-space:nowrap!important}.gNoOdq_numberRollRoot{vertical-align:baseline;line-height:inherit;font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";text-align:center;flex-shrink:0;justify-content:center;min-width:1.45em;display:inline-flex;position:relative;overflow:hidden;white-space:nowrap!important}.gNoOdq_numberRollSizer{visibility:hidden;-webkit-user-select:none;user-select:none;pointer-events:none;line-height:inherit;white-space:nowrap}.gNoOdq_numberRollDigit{line-height:inherit;white-space:nowrap;justify-content:center;align-items:center;display:flex;position:absolute;inset:0}.gNoOdq_numberRollExit{animation:.24s cubic-bezier(.16,1,.3,1) forwards gNoOdq_numberRollOut}.gNoOdq_numberRollEnter{animation:.24s cubic-bezier(.16,1,.3,1) forwards gNoOdq_numberRollIn}@keyframes gNoOdq_numberRollOut{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-100%)}}@keyframes gNoOdq_numberRollIn{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}[data-motion=off] .gNoOdq_chevron,[data-motion=off] .gNoOdq_imageOpen img,[data-motion=off] .gNoOdq_processMeta,[data-motion=off] .gNoOdq_numberRollDigit{transition:none!important;animation:none!important}@media (prefers-reduced-motion:reduce){.gNoOdq_chevron,.gNoOdq_imageOpen img,.gNoOdq_processMeta,.gNoOdq_numberRollDigit{transition:none!important;animation:none!important}}.gNoOdq_turnStatus{white-space:nowrap;flex:none;align-self:flex-start;align-items:center;height:26px;margin:4px 0 12px;padding:0 4px;font-size:14px;line-height:22px;display:inline-flex}.gNoOdq_turnStatusLabel{background:linear-gradient(90deg, var(--dsw-static-deepseek-500,#4d6bfe) 0%, var(--dsw-static-deepseek-500,#4d6bfe) 40%, var(--dsw-static-deepseek-200,#a8c7fa) 50%, var(--dsw-static-deepseek-500,#4d6bfe) 60%, var(--dsw-static-deepseek-500,#4d6bfe) 100%);color:#0000;-webkit-text-fill-color:transparent;background-position:100% 0;background-size:250% 100%;background-clip:text;font-weight:500;animation:1.8s linear infinite gNoOdq_dsh-turn-status-shimmer}.gNoOdq_waitClock{flex:none;align-items:center;margin-left:8px;display:inline-flex}.gNoOdq_waitSeconds{font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";color:var(--dsw-alias-label-caption,#86909c);font-size:13px;font-weight:400}.gNoOdq_waitOvertime{color:var(--dsw-alias-state-warning-primary,#d8862c);background:color-mix(in srgb, var(--dsw-alias-state-warning-primary,#d8862c) 16%, transparent);border-radius:5px;margin-left:8px;padding:0 6px;font-size:11px;line-height:16px}.gNoOdq_turnStatusClock{font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";color:var(--dsw-alias-label-caption,#86868b);-webkit-text-fill-color:var(--dsw-alias-label-caption,#86868b);margin-left:8px;font-size:13px;font-weight:400}@keyframes gNoOdq_dsh-turn-status-shimmer{to{background-position:0 0}}[data-motion=off] .gNoOdq_turnStatusLabel{color:var(--dsw-static-deepseek-500,#4d6bfe)!important;-webkit-text-fill-color:var(--dsw-static-deepseek-500,#4d6bfe)!important;animation:none!important}@media (prefers-reduced-motion:reduce){.gNoOdq_turnStatusLabel{color:var(--dsw-static-deepseek-500,#4d6bfe)!important;-webkit-text-fill-color:var(--dsw-static-deepseek-500,#4d6bfe)!important;animation:none!important}}@media (pointer:coarse){.gNoOdq_disclosureButton{min-height:44px}}@media (width<=600px){.gNoOdq_root{padding-inline:20px}.gNoOdq_column{gap:18px}.gNoOdq_toolbar{font-size:11px}.gNoOdq_user{max-width:90%}}.gNoOdq_choreographedFlow{overflow-anchor:none;flex-direction:column;min-width:0;display:flex}.gNoOdq_flowCell{min-width:0;display:flow-root;overflow:clip}.gNoOdq_flowCell[hidden]{display:none}.gNoOdq_flowCellInner{flex-direction:column;min-width:0;padding-bottom:16px;display:flex}.gNoOdq_flowCellInner:empty{padding-bottom:0}.gNoOdq_choreographedFlow[data-reader-transition=collapse] .gNoOdq_flowCell *{animation-play-state:paused!important}.gNoOdq_numberRollExit,.gNoOdq_numberRollEnter{animation-duration:.16s}.gNoOdq_foldSummary{align-items:baseline;display:inline-flex;white-space:nowrap!important;flex-wrap:nowrap!important}.gNoOdq_toolbar{box-sizing:border-box;z-index:7;-webkit-backdrop-filter:none;backdrop-filter:none;border-bottom:0;align-self:stretch;width:100%;max-width:100%;min-height:40px}.gNoOdq_turnProcessSticky{box-sizing:border-box;width:calc(100% - var(--reader-control-width,148px));top:max(var(--reader-toolbar-height,40px), var(--reader-status-height,40px));z-index:6;-webkit-backdrop-filter:none;backdrop-filter:none;min-height:40px;box-shadow:0 -14px 0 var(--dsw-alias-bg-base), var(--reader-control-width,148px) 0 0 var(--dsw-alias-bg-base);background-clip:padding-box;border-bottom:1px solid #0000;padding-right:0}.gNoOdq_turnStickyLanes .gNoOdq_turnProcessSticky,.gNoOdq_turnStickyLanes .gNoOdq_closedProcessSummary{z-index:auto;position:static;top:auto}.gNoOdq_flowCell[data-flow-summary],.gNoOdq_closedProcessSummary{top:calc(max(var(--reader-toolbar-height,40px), var(--reader-status-height,40px)) + var(--reader-sticky-lane,40px));z-index:5;box-sizing:border-box;background:var(--dsw-alias-bg-base);border-bottom:0;min-height:40px;padding-block:4px;position:sticky;overflow:visible}.gNoOdq_root .md-code-block{z-index:0}.gNoOdq_turnStickyRegion:has(>.gNoOdq_turnStickyLanes>.gNoOdq_closedProcessSummary),.gNoOdq_turnStickyRegion:has(.gNoOdq_flowCell[data-flow-summary]){--reader-code-summary-height:var(--reader-sticky-lane,40px)}.gNoOdq_root .md-code-block>:has(+[data-code-block-content]){top:calc(max(var(--reader-toolbar-height,40px), var(--reader-status-height,0px)) + var(--reader-status-height,0px) + var(--reader-code-summary-height,0px))}.gNoOdq_flowCell[data-flow-summary]{box-shadow:0 -16px 0 var(--dsw-alias-bg-base)}.gNoOdq_closedProcessSummary{box-shadow:0 -14px 0 var(--dsw-alias-bg-base)}.gNoOdq_foldControl{background:var(--dsw-alias-bg-module-platform);border-radius:8px;align-items:center;gap:0;padding:2px;display:inline-flex}.gNoOdq_foldStop{color:var(--dsw-alias-label-secondary);white-space:nowrap;font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;padding:2px 8px;font-size:12px;line-height:18px}.gNoOdq_foldStop[data-on]{background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary)}.gNoOdq_flowCell[data-flow-summary]>.gNoOdq_flowCellInner{padding-bottom:0}.gNoOdq_flowCell[data-flow-summary] .gNoOdq_disclosureButton,.gNoOdq_closedProcessSummary .gNoOdq_disclosureButton{width:auto;max-width:100%}.gNoOdq_flowCell[data-flow-summary] .gNoOdq_foldSummary,.gNoOdq_closedProcessSummary .gNoOdq_foldSummary{min-width:0}.gNoOdq_turnProcessSticky>.gNoOdq_disclosure{flex:auto;min-width:0}.gNoOdq_disclosureButton{width:max-content;max-width:calc(100% + 6px)}.gNoOdq_statusText{flex:0 auto}.gNoOdq_think{width:max-content;min-width:0}.gNoOdq_thinkSizer{white-space:normal;overflow-wrap:anywhere}.gNoOdq_thinkText{white-space:normal;overflow-wrap:anywhere;text-overflow:clip;overflow:visible}.gNoOdq_root[data-reader-glass] .gNoOdq_toolbar,.gNoOdq_root[data-reader-glass] .gNoOdq_turnProcessSticky,.gNoOdq_root[data-reader-glass] .gNoOdq_liveFoldContainer{-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);box-shadow:none;background:0 0;border:0;border-radius:0}.gNoOdq_root[data-reader-glass] .gNoOdq_toolbar,.gNoOdq_root[data-reader-glass] .gNoOdq_turnProcessSticky{-webkit-backdrop-filter:none;backdrop-filter:none}.gNoOdq_root[data-reader-glass] .gNoOdq_toolbar{pointer-events:none}.gNoOdq_root[data-reader-glass] .gNoOdq_toolbar>.gNoOdq_textButton,.gNoOdq_root[data-reader-glass] .gNoOdq_turnProcessSticky .gNoOdq_disclosureButton{pointer-events:auto;background:color-mix(in srgb, var(--dsw-alias-bg-layer-1,#ffffffb3) 80%, transparent);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-radius:999px;padding:2px 10px}.gNoOdq_root[data-reader-glass] .gNoOdq_turnProcessSticky .gNoOdq_disclosureButton{max-width:100%;margin-left:0}.gNoOdq_root[data-reader-glass] .gNoOdq_toolbar>.gNoOdq_textButton:hover,.gNoOdq_root[data-reader-glass] .gNoOdq_turnProcessSticky .gNoOdq_disclosureButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.gNoOdq_root[data-reader-glass] .gNoOdq_flowCell[data-flow-summary],.gNoOdq_root[data-reader-glass] .gNoOdq_closedProcessSummary{box-shadow:none;z-index:auto;border-left:2px solid color-mix(in srgb, var(--dsw-alias-border-l2,#80808059) 80%, transparent);padding-block:2px;background:0 0;border-radius:0;min-height:32px;padding-left:10px;position:static}.gNoOdq_root[data-reader-glass] .gNoOdq_turnStickyLanes{z-index:auto;display:contents;position:static;top:auto}.gNoOdq_root[data-reader-glass] .gNoOdq_turnStickyRegion{--reader-code-summary-height:0px}.gNoOdq_root[data-reader-glass] .gNoOdq_turnStickyLanes .gNoOdq_turnProcessSticky{top:max(var(--reader-toolbar-height,40px), var(--reader-status-height,40px));z-index:6;position:sticky}.gNoOdq_root[data-reader-glass] .gNoOdq_user{background:color-mix(in srgb, var(--dsw-specific-bubble,#00000014) 25%, transparent);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid color-mix(in srgb, var(--dsw-alias-border-l1,#8080804d) 30%, transparent)}.gNoOdq_root[data-reader-glass] .gNoOdq_reasonCard{border-color:color-mix(in srgb, var(--dsw-alias-border-l1,#80808059) 65%, transparent);background:0 0}.gNoOdq_root[data-reader-glass] .gNoOdq_toolDetails,.gNoOdq_root[data-reader-glass] .gNoOdq_notice,.gNoOdq_root[data-reader-glass] .gNoOdq_unknown,.gNoOdq_root[data-reader-glass] .gNoOdq_toolRaw,.gNoOdq_root[data-reader-glass] .gNoOdq_systemPrompt,.gNoOdq_root[data-reader-glass] .gNoOdq_compactionSummaryBox,.gNoOdq_root[data-reader-glass] .gNoOdq_attention,.gNoOdq_root[data-reader-glass] .gNoOdq_imageFrame,.gNoOdq_root[data-reader-glass] .gNoOdq_imageDialog{background:0 0}.gNoOdq_root[data-reader-glass] .gNoOdq_diffOverlay{background:color-mix(in srgb, var(--dsw-alias-bg-module-platform,#7f7f7f1a) 20%, transparent);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.gNoOdq_root[data-reader-glass] .gNoOdq_diffScrollArea{background:0 0}.gNoOdq_root[data-reader-glass] .gNoOdq_diffTab{background:color-mix(in srgb, var(--dsw-alias-bg-base,#fffc) 25%, transparent)}.gNoOdq_root[data-reader-glass] .gNoOdq_diffTab[data-active]{background:color-mix(in srgb, var(--dsw-alias-bg-base,#fff) 45%, transparent)}:is(.gNoOdq_root[data-reader-glass] .md-code-block,.gNoOdq_root[data-reader-glass] [data-diff]){border:1px solid color-mix(in srgb, var(--dsw-alias-border-l1,#80808033) 30%, transparent);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);background:color-mix(in srgb, var(--dsw-alias-markdown-code-block,#0000000f) 25%, transparent)!important}.gNoOdq_root[data-reader-glass] .gNoOdq_foldControl{background:color-mix(in srgb, var(--dsw-alias-bg-module-platform,#7f7f7f1a) 25%, transparent);border:1px solid color-mix(in srgb, var(--dsw-alias-border-l2,#80808033) 30%, transparent)}.gNoOdq_root[data-reader-glass] .gNoOdq_foldStop[data-on]{background:color-mix(in srgb, var(--dsw-alias-bg-base,#fff) 35%, transparent)}.gNoOdq_root[data-reader-glass] .gNoOdq_compactionPill,.gNoOdq_root[data-reader-glass] .gNoOdq_deliverableChip,.gNoOdq_root[data-reader-glass] .gNoOdq_toolState,.gNoOdq_root[data-reader-glass] .gNoOdq_diffStatButton{background:0 0;border-color:#0000}.gNoOdq_root[data-reader-glass] .gNoOdq_compactionPill:hover,.gNoOdq_root[data-reader-glass] .gNoOdq_compactionPill:focus-visible,.gNoOdq_root[data-reader-glass] .gNoOdq_deliverableChip:hover,.gNoOdq_root[data-reader-glass] .gNoOdq_deliverableChip:focus-within,.gNoOdq_root[data-reader-glass] .gNoOdq_toolState:hover,.gNoOdq_root[data-reader-glass] .gNoOdq_diffStatButton:hover,.gNoOdq_root[data-reader-glass] .gNoOdq_diffStatButton:focus-visible,.gNoOdq_root[data-reader-glass] .gNoOdq_diffStatButton[aria-expanded=true]{background:var(--dsw-alias-bg-module-platform,#7f7f7f1a);border-color:var(--dsw-alias-border-l2,#7f7f7f47)}";
42764
+ // \0dshx-css-module:/home/runner/work/dsh-better-display/dsh-better-display/src/client/Reader.module.css.mjs
42765
+ const css$5 = ".gNoOdq_root{color:var(--dsw-alias-label-primary);padding:16px calc(var(--dsh-composer-side-clearance,16px) + 16px) 24px;overflow-anchor:auto;--reader-lane-block:max(40px, calc(40px + var(--dsh-content-font-delta,0px)));--reader-lane-plate:linear-gradient(var(--dsw-alias-bg-base), var(--dsw-alias-bg-base)), linear-gradient(var(--dsw-alias-bg-layer-1,transparent), var(--dsw-alias-bg-layer-1,transparent));flex:none;min-width:0;font-family:inherit;font-size:16px;line-height:28px;container-type:inline-size}.gNoOdq_column{max-width:var(--dsh-chat-content-width,748px);flex-direction:column;gap:22px;width:100%;min-width:0;margin:0 auto;display:flex}.gNoOdq_column:focus,.gNoOdq_column:focus-visible{outline:none}.gNoOdq_toolbar{min-height:32px;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-base);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);justify-content:flex-end;align-items:center;gap:6px;padding:0;font-size:12px;display:flex;position:sticky;top:0}.gNoOdq_turnProcessSticky{background:var(--dsw-alias-bg-base);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);align-items:center;min-height:32px;display:flex;position:sticky}.gNoOdq_turnProcessSticky .gNoOdq_disclosureButton{min-height:32px}.gNoOdq_textButton,.gNoOdq_historyButton,.gNoOdq_iconButton,.gNoOdq_jump,.gNoOdq_disclosureButton,.gNoOdq_imageOpen,.gNoOdq_dialogClose{font:inherit;cursor:pointer}.gNoOdq_textButton{color:var(--dsw-alias-label-secondary);white-space:nowrap;background:0 0;border:0;border-radius:6px;padding:2px 6px}.gNoOdq_textButton:hover,.gNoOdq_iconButton:hover,.gNoOdq_disclosureButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.gNoOdq_root button:focus-visible,.gNoOdq_root summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:3px}.gNoOdq_turn,.gNoOdq_turnStickyRegion{flex-direction:column;gap:14px;min-width:0;display:flex}.gNoOdq_turnStickyLanes{top:var(--reader-toolbar-height,var(--reader-lane-block,40px));z-index:6;flex-direction:column;min-width:0;display:flex;position:sticky}.gNoOdq_turn+.gNoOdq_turn,.gNoOdq_retiringContent+.gNoOdq_retiringContent>.gNoOdq_turn{margin-top:12px}.gNoOdq_userCluster{flex-direction:column;align-self:stretch;align-items:flex-end;gap:8px;width:100%;min-width:0;max-width:100%;margin-bottom:4px;display:flex}.gNoOdq_userCluster>[data-slot]>*{box-sizing:border-box;align-self:stretch;width:100%;min-width:0;max-width:100%}.gNoOdq_userImages{flex-direction:column;align-self:flex-end;align-items:flex-end;width:fit-content;min-width:0;max-width:100%;display:flex}.gNoOdq_userImages .gNoOdq_imageFigure{align-self:flex-end}.gNoOdq_user{width:fit-content;max-width:min(calc(var(--dsh-chat-content-width,748px) * .702), 82%);border-radius:var(--dsw-radius-xl,22px);background:var(--dsw-specific-bubble);color:var(--dsw-alias-label-primary);font-size:var(--dsh-content-font-size,14px);line-height:calc(22px + var(--dsh-content-font-delta,0px));white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;box-sizing:border-box;align-self:flex-end;margin-left:auto;padding:10px 16px;font-weight:400}.gNoOdq_user .gNoOdq_blocks{gap:10px}.gNoOdq_answer{overflow-wrap:anywhere;flex-direction:column;min-width:0;display:flex}.gNoOdq_readingText{min-width:0}.gNoOdq_reasonPlain{white-space:pre-wrap;overflow-wrap:anywhere;font:inherit;color:inherit}.gNoOdq_readingText button{min-width:24px;min-height:24px}.gNoOdq_streamWord{display:inline-block}.gNoOdq_streamInlineWord{display:inline}.gNoOdq_streamWord,.gNoOdq_streamInlineWord,.gNoOdq_streamAtom{opacity:1;filter:none}.gNoOdq_streamAtom{min-width:0}@media (prefers-reduced-motion:reduce){.gNoOdq_streamWord,.gNoOdq_streamInlineWord,.gNoOdq_streamAtom{opacity:1!important;filter:none!important}}.gNoOdq_retiringContent{min-width:0}.gNoOdq_retiringContent[data-reader-retiring=retiring]{overflow:hidden}.gNoOdq_mainFlow,.gNoOdq_blocks{flex-direction:column;gap:16px;min-width:0;display:flex}.gNoOdq_mainFlow:empty,.gNoOdq_blocks:empty{display:none}.gNoOdq_blocks>:first-child{margin-top:0}.gNoOdq_blocks>:last-child{margin-bottom:0}.gNoOdq_blocks .md-table-wide{max-width:100%}.gNoOdq_disclosure{min-width:0}.gNoOdq_disclosureButton{max-width:100%;min-height:32px;color:var(--dsw-alias-label-tertiary);font-size:calc(14px + var(--dsh-content-font-delta,0px));line-height:calc(24px + var(--dsh-content-font-delta,0px));text-align:left;background:0 0;border:0;border-radius:7px;align-items:center;gap:6px;margin-left:-6px;padding:4px 6px;font-weight:400;display:inline-flex}.gNoOdq_disclosure[data-reader-status-only]{align-items:center;min-height:32px;display:flex}.gNoOdq_statusText{min-width:0;max-width:100%;font-size:calc(14px + var(--dsh-content-font-delta,0px));line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-secondary);font-weight:400;display:inline-flex}.gNoOdq_srOnly{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.gNoOdq_think{--think-swap:.15s;--think-gap:50ms;--think-distance:8px;--think-blur:2px;--think-shimmer:2s;--think-base:var(--dsw-alias-label-secondary);--think-highlight:var(--dsw-alias-label-primary);--think-ease:ease-in-out;text-align:left;max-width:100%;display:inline-block;position:relative}.gNoOdq_thinkSizer{visibility:hidden;white-space:nowrap;display:block}.gNoOdq_thinkText{color:var(--think-base);white-space:nowrap;text-overflow:ellipsis;filter:blur();opacity:1;transition:transform var(--think-swap) var(--think-ease), filter var(--think-swap) var(--think-ease), opacity var(--think-swap) var(--think-ease);display:block;position:absolute;top:0;left:0;right:0;overflow:hidden;transform:translateY(0)}.gNoOdq_think[data-reader-status-phase=running] .gNoOdq_thinkText[data-reader-status-copy=current]{transition-delay:var(--think-gap)}.gNoOdq_think:not([data-reader-status-phase=idle]) .gNoOdq_thinkText{will-change:transform, filter, opacity}@supports (background-clip:text){.gNoOdq_think[data-active=true] .gNoOdq_thinkText:before{content:attr(data-text);pointer-events:none;background-image:linear-gradient(90deg, transparent 0%, transparent 40%, var(--think-highlight) 50%, transparent 60%, transparent 100%);color:#0000;-webkit-text-fill-color:transparent;animation:gNoOdq_thinkShimmer var(--think-shimmer) linear infinite;background-repeat:no-repeat;background-size:400% 100%;background-clip:text;position:absolute;inset:0}}@keyframes gNoOdq_thinkShimmer{0%{background-position:100% 0}to{background-position:0 0}}.gNoOdq_thinkText.gNoOdq_isExit{transform:translateY(calc(var(--think-distance) * -1));filter:blur(var(--think-blur));opacity:0}.gNoOdq_thinkText.gNoOdq_isEnterStart{transform:translateY(var(--think-distance));filter:blur(var(--think-blur));opacity:0;transition:none}[data-motion=off] .gNoOdq_thinkText{filter:none!important;opacity:1!important;transition:none!important;transform:none!important}[data-motion=off] .gNoOdq_thinkText[data-reader-status-copy=outgoing],[data-motion=off] .gNoOdq_thinkText:before{display:none!important}@media (prefers-reduced-motion:reduce),(forced-colors:active){.gNoOdq_thinkText{filter:none!important;opacity:1!important;transition:none!important;transform:none!important}.gNoOdq_thinkText[data-reader-status-copy=outgoing],.gNoOdq_thinkText:before{display:none!important}}.gNoOdq_processMeta{opacity:0;grid-template-rows:0fr;transition:grid-template-rows .26s cubic-bezier(.22,1,.36,1),opacity .16s ease-out;display:grid}.gNoOdq_processMeta[data-open=true]{opacity:1;grid-template-rows:1fr}.gNoOdq_processMetaInner{min-height:0;overflow:hidden}.gNoOdq_processMetaLine{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex-wrap:wrap;align-items:baseline;gap:8px;padding:8px 0 0 16px;font-size:12px;line-height:20px;display:flex}.gNoOdq_processMetaLine .gNoOdq_meta{color:inherit}.gNoOdq_chevron{stroke:currentColor;fill:none;stroke-width:1.4px;stroke-linecap:round;stroke-linejoin:round;flex:none;transition:transform .26s cubic-bezier(.22,1,.36,1)}.gNoOdq_chevron[data-open=true]{transform:rotate(90deg)}.gNoOdq_disclosureBody{transform-origin:top;will-change:height, opacity, transform;height:0;overflow:hidden}.gNoOdq_processContents{border-left:1px solid var(--dsw-alias-border-l2);padding:3px 0 2px var(--reader-gutter,16px);margin:0 0 0 var(--reader-rail,6px);color:var(--dsw-alias-label-secondary);font-size:14px;line-height:24px;display:flow-root}.gNoOdq_processContents>:last-child{margin-bottom:0}.gNoOdq_processSeamRow{min-height:40px;margin-left:var(--reader-rail,6px);padding-left:var(--reader-gutter,16px);border-left:1px solid var(--dsw-alias-border-l2);box-sizing:border-box;align-items:center;display:flex}.gNoOdq_processSeamRow>.gNoOdq_disclosure{align-items:center;min-height:24px;display:flex}.gNoOdq_processSeamRow .gNoOdq_disclosureButton{min-height:24px;color:var(--dsw-alias-label-tertiary);padding-block:2px;font-size:12px;line-height:20px}.gNoOdq_flowCell:has(>.gNoOdq_flowCellInner>[data-reader-seam-after])>.gNoOdq_flowCellInner{padding-bottom:0}.gNoOdq_processFrame{min-width:0}.gNoOdq_toolActivity{min-width:0;color:var(--dsw-alias-label-secondary)}.gNoOdq_nativeToolRow{min-width:0}.gNoOdq_rowSeparator{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.gNoOdq_nativeToolSummary{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-tertiary);flex:1;font-size:14px;line-height:24px;overflow:hidden}.gNoOdq_nativeToolRow:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:3px;border-radius:4px}.gNoOdq_toolHeading{width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:11px;grid-template-columns:20px minmax(0,1fr) auto;align-items:start;gap:10px;padding:10px 14px 4px;display:grid}.gNoOdq_toolHeading:hover{background:var(--dsw-alias-interactive-bg-hover)}.gNoOdq_toolHeading:focus-visible{outline-offset:-3px!important}.gNoOdq_toolGlyph{height:22px;color:var(--dsw-alias-label-secondary);justify-content:center;align-items:center;display:flex}.gNoOdq_toolHeadingCopy{flex-direction:column;gap:3px;min-width:0;display:flex}.gNoOdq_toolTitle{overflow-wrap:anywhere;font-size:13px;font-weight:500;line-height:22px}.gNoOdq_toolTarget{-webkit-line-clamp:2;overflow-wrap:anywhere;color:var(--dsw-alias-label-secondary);-webkit-box-orient:vertical;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:19px;display:-webkit-box;overflow:hidden}.gNoOdq_toolHeadingState{min-height:22px;color:var(--dsw-alias-label-secondary);align-items:center;gap:7px;display:flex}.gNoOdq_diffStat{font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";white-space:nowrap;flex:none;gap:6px;margin-left:8px;font-size:11px;line-height:18px;display:inline-flex}.gNoOdq_diffAdded{color:var(--dsw-alias-state-success-primary,#2f9e63)}.gNoOdq_diffRemoved{color:var(--dsw-alias-state-error-primary,#d1483c)}.gNoOdq_summaryRow{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;max-width:100%;display:flex}.gNoOdq_summaryRow>.gNoOdq_diffStatRoot{flex:none;margin-left:auto}.gNoOdq_summaryRow>:first-child{min-width:0}.gNoOdq_diffStatRoot{flex:none;position:relative}.gNoOdq_diffStatButton{cursor:pointer;font:inherit;font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";color:var(--dsw-alias-label-secondary);background:0 0;border:1px solid #0000;border-radius:6px;align-items:center;gap:6px;padding:2px 8px;font-size:11px;line-height:16px;transition:background .12s,border-color .12s;display:inline-flex}.gNoOdq_diffStatButton:hover,.gNoOdq_diffStatButton:focus-visible,.gNoOdq_diffStatButton[aria-expanded=true]{background:var(--dsw-alias-bg-module-platform,#7f7f7f1a);border-color:var(--dsw-alias-border-l2,#7f7f7f47)}.gNoOdq_diffStatButton:focus-visible{outline:none}.gNoOdq_diffStatButton[aria-expanded=true] .gNoOdq_diffCaret{opacity:1}.gNoOdq_diffOverlayRow{box-sizing:border-box;flex:100%;width:100%;min-width:0;max-width:100%;margin-top:6px}.gNoOdq_diffOverlay{box-sizing:border-box;background:var(--dsw-alias-bg-module-platform,#7f7f7f14);border:1px solid var(--dsw-alias-border-l2,#80808038);border-radius:10px;flex-direction:column;width:100%;min-width:0;max-width:100%;padding:8px;display:flex;overflow:hidden}.gNoOdq_diffTabs{scrollbar-width:thin;flex-wrap:nowrap;gap:6px;max-width:100%;margin-bottom:4px;padding-bottom:6px;display:flex;overflow-x:auto}.gNoOdq_diffTab{text-overflow:ellipsis;white-space:nowrap;cursor:pointer;max-width:220px;font:inherit;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-base,#fffc);border:1px solid var(--dsw-alias-border-l2,#80808033);border-radius:6px;flex:none;padding:3px 10px;font-size:11px;line-height:16px;transition:all .12s;overflow:hidden}.gNoOdq_diffTab:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.gNoOdq_diffTab[data-active]{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-state-business-primary,#1e90ff);background:var(--dsw-alias-bg-base);font-weight:500}.gNoOdq_diffScrollArea{overscroll-behavior:contain;-webkit-overflow-scrolling:touch;border:1px solid var(--dsw-alias-border-l2,#80808026);background:var(--dsw-alias-markdown-code-block,#0000000a);border-radius:8px;max-height:420px;overflow:auto}.gNoOdq_diffScrollArea [data-diff]{background:0 0!important;border-radius:0!important;margin:0!important}.gNoOdq_diffCaret{opacity:.6;flex:none;width:10px;font-size:10px}.gNoOdq_diffPath{text-overflow:ellipsis;white-space:nowrap;text-align:left;direction:rtl;flex:auto;min-width:0;overflow:hidden}.gNoOdq_diffStat{font-variant-numeric:tabular-nums;flex:none;gap:6px;font-size:11px;display:inline-flex}.gNoOdq_diffHunkBody{flex-direction:column;gap:4px;padding:2px 7px 7px 25px;display:flex}.gNoOdq_diffLines{font-size:11px;line-height:1.55;font-family:var(--dsw-font-mono,ui-monospace, SFMono-Regular, Menlo, monospace);white-space:pre;border-radius:6px;margin:0;padding:6px 8px;overflow-x:auto}.gNoOdq_diffLines[data-side=removed]{background:#d1483c1f}.gNoOdq_diffLines[data-side=added]{background:#2f9e631f}.gNoOdq_diffLineRemoved,.gNoOdq_diffLineAdded{display:block}.gNoOdq_diffLineRemoved{color:var(--dsw-alias-state-error-primary,#d1483c)}.gNoOdq_diffLineAdded{color:var(--dsw-alias-state-success-primary,#2f9e63)}.gNoOdq_diffEmpty{color:var(--dsw-alias-label-secondary);margin:0;padding:2px 0;font-size:11px}.gNoOdq_nativeToolSummary[data-running]{background-image:linear-gradient(90deg, currentColor 0%, currentColor 35%, color-mix(in srgb, currentColor 35%, transparent) 50%, currentColor 65%, currentColor 100%);-webkit-text-fill-color:transparent;background-position:100% 0;background-repeat:no-repeat;background-size:300% 100%;background-clip:text;animation:1.6s linear infinite gNoOdq_readerStepShimmer}@keyframes gNoOdq_readerStepShimmer{to{background-position:0 0}}.gNoOdq_toolElapsed[data-slow]{opacity:1;color:var(--dsw-alias-state-warning-primary,#d8862c);font-weight:500}.gNoOdq_toolElapsed{opacity:.72;font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";white-space:nowrap;flex:none;margin-left:8px;font-size:11px;line-height:18px}[data-motion=off] .gNoOdq_nativeToolSummary[data-running]{-webkit-text-fill-color:currentColor;background-image:none;animation:none!important}@media (prefers-reduced-motion:reduce){.gNoOdq_nativeToolSummary[data-running]{-webkit-text-fill-color:currentColor;background-image:none;animation:none!important}}.gNoOdq_toolState{white-space:nowrap;flex:none;margin-left:8px;font-size:11px;line-height:18px}.gNoOdq_toolState[data-phase=preparing],.gNoOdq_toolState[data-phase=running]{color:var(--dsw-alias-state-business-primary)}.gNoOdq_toolState[data-phase=preparing]:before,.gNoOdq_toolState[data-phase=running]:before{content:\"\";vertical-align:middle;background:currentColor;border-radius:50%;width:6px;height:6px;margin-right:6px;animation:1.4s ease-in-out infinite gNoOdq_readerRunningGlow;display:inline-block}@keyframes gNoOdq_readerRunningGlow{0%{box-shadow:0 0 0 0 color-mix(in srgb, currentColor 60%, transparent);opacity:1}70%{opacity:.7;box-shadow:0 0 0 7px #0000}to{opacity:1;box-shadow:0 0 #0000}}[data-motion=off] .gNoOdq_toolState[data-phase=preparing]:before,[data-motion=off] .gNoOdq_toolState[data-phase=running]:before{animation:none!important}@media (prefers-reduced-motion:reduce){.gNoOdq_toolState[data-phase=preparing]:before,.gNoOdq_toolState[data-phase=running]:before{animation:none!important}}.gNoOdq_toolState[data-phase=failed]{color:var(--dsw-alias-state-error-primary)}.gNoOdq_toolState[data-phase=interrupted]{color:inherit;opacity:.7}.gNoOdq_toolLedger{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex-wrap:wrap;align-items:baseline;gap:4px 12px;padding:10px 14px 0;font-size:11px;line-height:18px;display:flex}.gNoOdq_toolEngine{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.gNoOdq_toolDetails{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;min-width:0;margin:6px 0 0 22px;overflow:hidden}.gNoOdq_toolTabs{align-items:center;gap:15px;padding:0 14px;display:flex}.gNoOdq_toolTabs>button{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-bottom:2px solid #0000;padding:10px 1px 8px;font-size:12px;line-height:20px}.gNoOdq_toolTabs>button[aria-selected=true]{color:var(--dsw-alias-label-primary);border-bottom-color:var(--dsw-alias-state-business-primary)}.gNoOdq_toolPanel{flex-direction:column;gap:12px;min-width:0;padding:12px 14px 14px;font-size:13px;line-height:22px;display:flex}.gNoOdq_toolPanel:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-3px}.gNoOdq_toolDetailNote{color:var(--dsw-alias-label-secondary);margin:0 0 9px;font-size:12px;line-height:20px}.gNoOdq_toolRawLabel{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;font-weight:500}.gNoOdq_toolRaw{background:var(--dsw-alias-bg-module-platform);max-height:400px;color:var(--dsw-alias-label-primary);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:8px;margin:0;padding:12px;font:12px/20px ui-monospace,SFMono-Regular,Menlo,monospace;overflow:auto}.gNoOdq_systemPrompt{background:var(--dsw-alias-bg-module-platform);max-height:320px;color:var(--dsw-alias-label-primary);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:8px;margin:0;padding:12px;font:12px/20px ui-monospace,SFMono-Regular,Menlo,monospace;overflow:auto}.gNoOdq_commandInput{white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.gNoOdq_toolReceipt .gNoOdq_blocks,.gNoOdq_toolDocument .gNoOdq_blocks{font-size:13px;line-height:22px}.gNoOdq_toolChildren{margin:0 14px 12px var(--reader-rail,6px);padding-left:var(--reader-gutter,16px);border-left:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:9px;display:flex}@container (width<=420px){.gNoOdq_toolHeading{grid-template-columns:16px minmax(0,1fr) auto;gap:7px;padding:11px 10px 6px}.gNoOdq_toolHeadingState{gap:4px}.gNoOdq_toolLedger{padding-left:33px;padding-right:10px}.gNoOdq_toolPanel{padding:10px}}@media (pointer:coarse){.gNoOdq_toolTabs>button{min-height:44px}}.gNoOdq_reasonCard{--reason-preview-height:224px;--reason-hold:.84s;--reason-step:.5s;--reason-lines:2;--reason-fade:28px;--reason-ease:cubic-bezier(.22, 1, .36, 1);border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);min-width:0;scroll-margin-block:16px calc(var(--dsh-composer-height,152px) + 16px);border-radius:12px;position:relative;overflow:hidden}.gNoOdq_reasonHeading{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;justify-content:space-between;align-items:baseline;gap:12px;padding:10px 16px 0;font-size:12px;line-height:20px;display:flex}.gNoOdq_reasonCard[data-overflow=false][data-expanded=false]{background:0 0;border-color:#0000;border-radius:0}.gNoOdq_reasonCard[data-overflow=false][data-expanded=false] .gNoOdq_reasonHeading{padding:0}.gNoOdq_reasonCard[data-overflow=false][data-expanded=false] .gNoOdq_reasonText{padding:4px 0 0 22px}.gNoOdq_reasonLabel{font-weight:500}.gNoOdq_reasonViewport{max-height:var(--reason-preview-height);scroll-behavior:auto;overflow-anchor:none;overscroll-behavior-y:contain;scrollbar-width:thin;scrollbar-color:var(--dsw-alias-border-l1) transparent;position:relative;overflow:auto}.gNoOdq_reasonCard[data-expanded=true] .gNoOdq_reasonViewport{max-height:min(60vh, 560px, var(--reason-reading-height,560px))}.gNoOdq_reasonViewport[data-edges=both],.gNoOdq_reasonCard[data-following=true][data-overflow=true] .gNoOdq_reasonViewport{-webkit-mask-image:linear-gradient(transparent 0, black var(--reason-fade), black calc(100% - var(--reason-fade)), transparent 100%);mask-image:linear-gradient(transparent 0, black var(--reason-fade), black calc(100% - var(--reason-fade)), transparent 100%)}.gNoOdq_reasonViewport[data-edges=top]{-webkit-mask-image:linear-gradient(transparent 0, black var(--reason-fade), black 100%);mask-image:linear-gradient(transparent 0, black var(--reason-fade), black 100%)}.gNoOdq_reasonViewport[data-edges=bottom]{-webkit-mask-image:linear-gradient(black 0, black calc(100% - var(--reason-fade)), transparent 100%);mask-image:linear-gradient(black 0, black calc(100% - var(--reason-fade)), transparent 100%)}.gNoOdq_reasonCard[data-expanded=true] .gNoOdq_reasonViewport{mask-image:none}.gNoOdq_reasonViewport:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-3px}.gNoOdq_reasonTrack{position:relative;transform:translateY(0)}.gNoOdq_reasonCard[data-following=true] .gNoOdq_reasonTrack{will-change:transform}.gNoOdq_reasonText{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;padding:8px 16px 16px;font-size:14px;line-height:24px}.gNoOdq_reasonText .gNoOdq_readingText>div{font-size:inherit;line-height:inherit;color:inherit}.gNoOdq_reasonFooter{color:var(--dsw-alias-label-secondary);justify-content:space-between;align-items:center;gap:8px;padding:2px 10px 8px;font-size:12px;line-height:20px;display:flex}.gNoOdq_reasonAction{min-height:28px;color:inherit;font:inherit;cursor:pointer;white-space:nowrap;background:0 0;border:0;border-radius:6px;justify-content:center;align-items:center;gap:5px;padding:4px 6px;display:inline-flex}.gNoOdq_reasonAction:hover{background:var(--dsw-alias-interactive-bg-hover)}.gNoOdq_reasonAction:disabled{cursor:default;opacity:.65}.gNoOdq_reasonAction svg{fill:none;stroke:currentColor;stroke-width:1.3px;stroke-linecap:round;stroke-linejoin:round;flex:none}.gNoOdq_reasonCaption{padding-left:6px}@container (width<=480px){.gNoOdq_reasonCard{--reason-preview-height:192px}}@media (pointer:coarse){.gNoOdq_reasonAction{min-height:44px}}@media (prefers-reduced-motion:reduce){.gNoOdq_reasonTrack{will-change:auto!important;transition:none!important;transform:none!important}}@media (forced-colors:active){.gNoOdq_reasonViewport{mask-image:none!important}}.gNoOdq_processItem,.gNoOdq_tool{min-width:0;margin:0 0 18px}.gNoOdq_processLabel{color:var(--dsw-alias-label-tertiary);align-items:baseline;gap:12px;margin:0 0 6px;font-size:12px;line-height:20px;display:flex}.gNoOdq_processFootnote{color:var(--dsw-alias-label-tertiary);margin:14px 0 0;font-size:12px;line-height:20px}.gNoOdq_detail{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:22px}.gNoOdq_detail>summary{cursor:pointer;padding:3px 0}.gNoOdq_detail[open]>summary{margin-bottom:8px}.gNoOdq_tool>.gNoOdq_tool{margin:10px 0 0 12px}.gNoOdq_meta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px}.gNoOdq_stopped{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-tertiary);border-radius:6px;align-self:flex-start;margin-top:12px;padding:0 6px;font-size:11px;line-height:18px}.gNoOdq_compactionRow{box-sizing:border-box;flex-direction:column;align-items:center;width:100%;margin:16px 0;display:flex}.gNoOdq_compactionLine{width:100%;color:var(--dsw-alias-label-tertiary);-webkit-user-select:none;user-select:none;justify-content:center;align-items:center;gap:12px;font-size:11px;line-height:18px;display:flex}.gNoOdq_compactionLine:before,.gNoOdq_compactionLine:after{content:\"\";background:var(--dsw-alias-border-l1);opacity:.7;flex:1;height:1px}.gNoOdq_compactionRow>.gNoOdq_compactionLine{animation:.52s cubic-bezier(.4,0,.2,1) both gNoOdq_compactionLineIn}.gNoOdq_compactionRow>.gNoOdq_compactionLine>.gNoOdq_compactionPill{animation:.42s cubic-bezier(.34,1.4,.64,1) .12s both gNoOdq_compactionPillIn}.gNoOdq_compactionRow>.gNoOdq_compactionLine .gNoOdq_compactionIcon{animation:.9s cubic-bezier(.4,0,.2,1) .2s both gNoOdq_compactionDial}@keyframes gNoOdq_compactionLineIn{0%{opacity:0;transform:scaleX(.6)}to{opacity:1;transform:scaleX(1)}}@keyframes gNoOdq_compactionPillIn{0%{opacity:0;transform:translateY(-6px)scale(.94)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes gNoOdq_compactionDial{0%{opacity:0;transform:rotate(-140deg)}to{opacity:.7;transform:rotate(0)}}[data-motion=off] .gNoOdq_compactionRow>.gNoOdq_compactionLine,[data-motion=off] .gNoOdq_compactionRow>.gNoOdq_compactionLine>.gNoOdq_compactionPill,[data-motion=off] .gNoOdq_compactionRow>.gNoOdq_compactionLine .gNoOdq_compactionIcon{animation:none!important}@media (prefers-reduced-motion:reduce){.gNoOdq_compactionRow>.gNoOdq_compactionLine,.gNoOdq_compactionRow>.gNoOdq_compactionLine>.gNoOdq_compactionPill,.gNoOdq_compactionRow>.gNoOdq_compactionLine .gNoOdq_compactionIcon{animation:none!important}}.gNoOdq_compactionPill{background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-tertiary);border-radius:12px;align-items:center;gap:6px;padding:2px 10px;font-family:inherit;font-size:11px;line-height:18px;display:inline-flex}.gNoOdq_compactionButton{cursor:pointer;transition:color .14s,border-color .14s,background-color .14s}.gNoOdq_compactionButton:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-interactive-bg-hover)}.gNoOdq_compactionIcon{opacity:.7;flex:none;width:12px;height:12px}.gNoOdq_compactionToggle{opacity:.8;margin-left:2px;font-size:10px}.gNoOdq_compactionSummaryBox{background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l2);width:100%;max-width:680px;color:var(--dsw-alias-label-secondary);box-sizing:border-box;border-radius:8px;margin-top:10px;padding:12px 16px;font-size:12px;line-height:20px}.gNoOdq_compactionSummaryHeader{color:var(--dsw-alias-label-tertiary);border-bottom:1px dashed var(--dsw-alias-border-l1);margin-bottom:8px;padding-bottom:4px;font-size:11px;font-weight:500}.gNoOdq_notice,.gNoOdq_error,.gNoOdq_attention{overflow-wrap:anywhere;border-radius:10px;padding:12px 14px;font-size:13px;line-height:20px}.gNoOdq_notice{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border:1px solid var(--dsw-alias-border-l2)}.gNoOdq_error{color:var(--dsw-alias-label-primary);background:#ef44440f;border:1px solid #ef444438;align-items:flex-start;gap:10px;display:flex}.gNoOdq_errorIcon{color:#ef4444;flex:none;width:16px;height:16px;margin-top:2px}.gNoOdq_errorCopy{flex:1;min-width:0}.gNoOdq_errorTitle{color:#dc2626;align-items:center;gap:8px;margin-bottom:2px;font-weight:500;display:flex}.gNoOdq_errorMessage{color:var(--dsw-alias-label-secondary);word-break:break-word;margin:0;font-size:12px;line-height:18px}.gNoOdq_errorCode{color:#dc2626;background:#ef44441f;border-radius:4px;padding:1px 5px;font-family:inherit;font-size:11px}.gNoOdq_attention{border:1px solid var(--dsw-alias-state-business-primary);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-base);flex-direction:column;gap:3px;display:flex}.gNoOdq_attention span{color:var(--dsw-alias-label-secondary)}.gNoOdq_unknown{background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:12px 14px;font-size:13px;line-height:22px}.gNoOdq_unknown p{margin:0 0 6px}.gNoOdq_historyButton{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0;border-radius:8px;align-self:center;padding:4px 12px;font-size:12px}.gNoOdq_historyButton:disabled{opacity:.55;cursor:default}.gNoOdq_empty{color:var(--dsw-alias-label-tertiary);text-align:center;padding:40px 0}.gNoOdq_answerActions{align-items:center;gap:8px;min-height:28px;margin:13px 0 0 -6px;display:flex}.gNoOdq_userActions{align-items:center;gap:8px;min-height:28px;display:flex}.gNoOdq_messageClock{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:13px;line-height:24px}.gNoOdq_iconButton{color:var(--dsw-alias-label-tertiary);background:0 0;border:0;border-radius:6px;justify-content:center;align-items:center;padding:6px;display:inline-flex}.gNoOdq_iconButton svg{fill:none;stroke:currentColor;stroke-width:1.2px;stroke-linecap:round;stroke-linejoin:round}.gNoOdq_imageFigure{box-sizing:border-box;align-self:flex-start;width:min(100%,360px);max-width:100%;margin:0}.gNoOdq_imageFrame{background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l2);box-sizing:border-box;border-radius:12px;max-width:100%;min-height:96px;max-height:440px;position:relative;overflow:hidden}.gNoOdq_imageOpen{background:0 0;border:0;width:100%;height:100%;padding:0;display:flex;position:absolute;inset:0}.gNoOdq_imageOpen img{object-fit:contain;opacity:0;width:100%;height:100%;transition:opacity .2s cubic-bezier(.4,0,.2,1);display:block}.gNoOdq_imageOpen img[data-ready=true]{opacity:1}.gNoOdq_pendingImage{object-fit:contain;width:100%;max-width:100%;height:100%;display:block}.gNoOdq_imagePlaceholder{color:var(--dsw-alias-label-tertiary);pointer-events:none;flex-direction:column;justify-content:center;align-items:center;gap:8px;font-size:13px;display:flex;position:absolute;inset:0}.gNoOdq_imagePlaceholder button{pointer-events:auto}.gNoOdq_imageFigure figcaption{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;margin-top:6px;font-size:12px;line-height:20px}.gNoOdq_imageDialog{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);border-radius:16px;max-width:90vw;max-height:90vh;padding:38px 20px 20px}.gNoOdq_imageDialog::backdrop{background:var(--dsw-alias-bg-mask-photo)}.gNoOdq_imageDialog img{object-fit:contain;max-width:82vw;max-height:75vh;display:block}.gNoOdq_dialogClose{color:inherit;background:0 0;border:0;border-radius:8px;width:32px;height:32px;font-size:24px;position:absolute;top:4px;right:8px}.gNoOdq_jumpDock{bottom:calc(var(--dsh-composer-height,152px) + 16px);z-index:8;pointer-events:none;justify-content:flex-end;height:0;display:flex;position:sticky}.gNoOdq_deliverablesRoot{grid-template-columns:max-content minmax(0,1fr);align-items:start;column-gap:10px;margin-top:14px;font-size:13px;line-height:22px;display:grid}.gNoOdq_deliverablesLabel{color:var(--dsw-alias-label-tertiary);grid-area:1/1;padding-top:2px;font-size:12px;font-weight:500}.gNoOdq_deliverablesLane{grid-area:1/2;min-width:0}.gNoOdq_deliverablesRow{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;display:flex}.gNoOdq_deliverableChip{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:6px;align-items:center;font-size:12px;line-height:20px;transition:border-color .16s,background-color .16s,color .16s;display:inline-flex;overflow:hidden}.gNoOdq_deliverableChip:hover{border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-bg-module-platform);box-shadow:0 1px 4px var(--dsw-alias-border-l1)}.gNoOdq_deliverableChip[data-status=opened]{color:#059669;background:#10b9811f;border-color:#10b981}.gNoOdq_chipMain{max-width:220px;color:inherit;font:inherit;cursor:pointer;text-align:left;background:0 0;border:none;align-items:center;gap:5px;padding:3px 6px 3px 8px;display:inline-flex}.gNoOdq_chipMain:hover{color:var(--dsw-alias-label-primary)}.gNoOdq_deliverableIcon,.gNoOdq_statusIcon{opacity:.8;flex:none;width:13px;height:13px}.gNoOdq_statusIcon{color:#10b981}.gNoOdq_deliverableName{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.gNoOdq_chipActions{opacity:.65;border-left:1px solid #0000;align-items:center;gap:2px;padding-right:4px;transition:opacity .15s,border-left-color .15s;display:inline-flex}.gNoOdq_deliverableChip:hover .gNoOdq_chipActions{opacity:1;border-left-color:var(--dsw-alias-border-l2)}.gNoOdq_chipActionBtn{width:20px;height:20px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;padding:0;transition:color .12s,background-color .12s;display:inline-flex}.gNoOdq_chipActionBtn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.gNoOdq_actionIcon{width:12px;height:12px}.gNoOdq_deliverablesMore{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:12px}.gNoOdq_deliverablesShowFolder{color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:5px;align-items:center;gap:4px;padding:2px 7px;font-size:12px;transition:all .15s;display:inline-flex}.gNoOdq_deliverablesShowFolder:hover{color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l2)}.gNoOdq_deliverablesShowFolder[data-status=opened]{color:#10b981;background:#10b98114;border-color:#10b9814d}.gNoOdq_morphCluster{flex-direction:column;width:100%;min-width:0;display:flex}.gNoOdq_morphDrawer{transform-origin:top;will-change:height, opacity, transform;height:0;overflow:hidden}.gNoOdq_liveFoldImpact{animation:.18s cubic-bezier(.2,0,0,1) both gNoOdq_liveFoldElastic}@keyframes gNoOdq_liveFoldElastic{0%{transform:scale(1)}50%{transform:scale(1.015)}to{transform:scale(1)}}.gNoOdq_liveFoldContainer{top:calc(var(--reader-toolbar-height,var(--reader-lane-block,40px)) + var(--reader-status-height,var(--reader-lane-block,40px)) + var(--reader-summary-height,var(--reader-lane-block,40px)));z-index:4;background:var(--dsw-alias-bg-base);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);transform-origin:0;will-change:transform, opacity;margin-bottom:6px;padding:4px 0;animation:.32s cubic-bezier(.16,1,.3,1) both gNoOdq_liveFoldPulse;position:sticky}@keyframes gNoOdq_liveFoldPulse{0%{opacity:.8;transform:translateY(-4px)scale(.98)}50%{opacity:1;transform:translateY(0)scale(1.02)}to{opacity:1;transform:translateY(0)scale(1)}}[data-motion=off] .gNoOdq_liveFoldContainer{animation:none!important}@media (prefers-reduced-motion:reduce){.gNoOdq_liveFoldContainer{animation:none!important}}.gNoOdq_priorFoldDrawer{flex-direction:column;gap:14px;width:100%;min-width:0;display:flex}.gNoOdq_jump{width:34px;height:34px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-button-floating-fill,var(--dsw-alias-bg-base));box-shadow:var(--dsw-elevation-panel,0 2px 8px var(--dsw-alias-border-l1));pointer-events:auto;border:0;border-radius:100px;flex:none;justify-content:center;align-items:center;margin-top:-34px;padding:0;display:flex}.gNoOdq_jump:hover{background:var(--dsw-alias-button-floating-hover,var(--dsw-alias-interactive-bg-hover))}.gNoOdq_foldSummary{text-overflow:ellipsis;align-items:baseline;max-width:100%;display:inline-flex;overflow:hidden;white-space:nowrap!important}.gNoOdq_foldSummaryPart{flex:none;align-items:baseline;display:inline-flex;white-space:nowrap!important}.gNoOdq_numberRollRoot{vertical-align:baseline;line-height:inherit;font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";text-align:center;flex-shrink:0;justify-content:center;min-width:1.45em;display:inline-flex;position:relative;overflow:hidden;white-space:nowrap!important}.gNoOdq_numberRollSizer{visibility:hidden;-webkit-user-select:none;user-select:none;pointer-events:none;line-height:inherit;white-space:nowrap}.gNoOdq_numberRollDigit{line-height:inherit;white-space:nowrap;justify-content:center;align-items:center;display:flex;position:absolute;inset:0}.gNoOdq_numberRollExit{animation:.24s cubic-bezier(.16,1,.3,1) forwards gNoOdq_numberRollOut}.gNoOdq_numberRollEnter{animation:.24s cubic-bezier(.16,1,.3,1) forwards gNoOdq_numberRollIn}@keyframes gNoOdq_numberRollOut{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-100%)}}@keyframes gNoOdq_numberRollIn{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}[data-motion=off] .gNoOdq_chevron,[data-motion=off] .gNoOdq_imageOpen img,[data-motion=off] .gNoOdq_processMeta,[data-motion=off] .gNoOdq_numberRollDigit{transition:none!important;animation:none!important}@media (prefers-reduced-motion:reduce){.gNoOdq_chevron,.gNoOdq_imageOpen img,.gNoOdq_processMeta,.gNoOdq_numberRollDigit{transition:none!important;animation:none!important}}.gNoOdq_turnStatus{white-space:nowrap;flex:none;align-self:flex-start;align-items:center;height:26px;margin:4px 0 12px;padding:0 4px;font-size:14px;line-height:22px;display:inline-flex}.gNoOdq_turnStatusLabel{background:linear-gradient(90deg, var(--dsw-static-deepseek-500,#4d6bfe) 0%, var(--dsw-static-deepseek-500,#4d6bfe) 40%, var(--dsw-static-deepseek-200,#a8c7fa) 50%, var(--dsw-static-deepseek-500,#4d6bfe) 60%, var(--dsw-static-deepseek-500,#4d6bfe) 100%);color:#0000;-webkit-text-fill-color:transparent;background-position:100% 0;background-size:250% 100%;background-clip:text;font-weight:500;animation:1.8s linear infinite gNoOdq_dsh-turn-status-shimmer}.gNoOdq_waitClock{flex:none;align-items:center;margin-left:8px;display:inline-flex}.gNoOdq_waitSeconds{font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";color:var(--dsw-alias-label-caption,#86909c);font-size:13px;font-weight:400}.gNoOdq_waitOvertime{color:var(--dsw-alias-state-warning-primary,#d8862c);background:color-mix(in srgb, var(--dsw-alias-state-warning-primary,#d8862c) 16%, transparent);border-radius:5px;margin-left:8px;padding:0 6px;font-size:11px;line-height:16px}.gNoOdq_turnStatusClock{font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";color:var(--dsw-alias-label-caption,#86868b);-webkit-text-fill-color:var(--dsw-alias-label-caption,#86868b);margin-left:8px;font-size:13px;font-weight:400}@keyframes gNoOdq_dsh-turn-status-shimmer{to{background-position:0 0}}[data-motion=off] .gNoOdq_turnStatusLabel{color:var(--dsw-static-deepseek-500,#4d6bfe)!important;-webkit-text-fill-color:var(--dsw-static-deepseek-500,#4d6bfe)!important;animation:none!important}@media (prefers-reduced-motion:reduce){.gNoOdq_turnStatusLabel{color:var(--dsw-static-deepseek-500,#4d6bfe)!important;-webkit-text-fill-color:var(--dsw-static-deepseek-500,#4d6bfe)!important;animation:none!important}}@media (pointer:coarse){.gNoOdq_disclosureButton{min-height:44px}}@media (width<=600px){.gNoOdq_root{padding-inline:20px}.gNoOdq_column{gap:18px}.gNoOdq_toolbar{font-size:11px}}.gNoOdq_choreographedFlow{overflow-anchor:none;flex-direction:column;min-width:0;display:flex}.gNoOdq_flowCell{min-width:0;display:flow-root;overflow:clip}.gNoOdq_flowCell[hidden]{display:none}.gNoOdq_flowCellInner{flex-direction:column;min-width:0;padding-bottom:16px;display:flex}.gNoOdq_flowCellInner:empty{padding-bottom:0}.gNoOdq_choreographedFlow[data-reader-transition=collapse] .gNoOdq_flowCell *{animation-play-state:paused!important}.gNoOdq_numberRollExit,.gNoOdq_numberRollEnter{animation-duration:.16s}.gNoOdq_foldSummary{align-items:baseline;display:inline-flex;white-space:nowrap!important;flex-wrap:nowrap!important}.gNoOdq_toolbar{box-sizing:border-box;width:100%;max-width:100%;min-height:var(--reader-lane-block,40px);z-index:7;-webkit-backdrop-filter:none;backdrop-filter:none;border-bottom:0;align-self:stretch}.gNoOdq_turnProcessSticky{box-sizing:border-box;width:calc(100% - var(--reader-control-width,148px));top:var(--reader-toolbar-height,var(--reader-lane-block,40px));min-height:var(--reader-lane-block,40px);z-index:6;-webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:0 -14px 0 var(--dsw-alias-bg-base), var(--reader-control-width,148px) 0 0 var(--dsw-alias-bg-base), 0 -14px 0 var(--dsw-alias-bg-layer-1,transparent), var(--reader-control-width,148px) 0 0 var(--dsw-alias-bg-layer-1,transparent), 0 -14px 0 Canvas, var(--reader-control-width,148px) 0 0 Canvas;background-clip:padding-box;border-bottom:1px solid #0000;padding-right:0}.gNoOdq_turnStickyLanes .gNoOdq_turnProcessSticky,.gNoOdq_turnStickyLanes .gNoOdq_closedProcessSummary{z-index:auto;position:static;top:auto}.gNoOdq_flowCell[data-flow-summary],.gNoOdq_closedProcessSummary{top:calc(var(--reader-toolbar-height,var(--reader-lane-block,40px)) + var(--reader-status-height,var(--reader-lane-block,40px)));z-index:5;box-sizing:border-box;min-height:var(--reader-lane-block,40px);background:var(--dsw-alias-bg-base);border-bottom:0;padding-block:4px;position:sticky;overflow:visible}.gNoOdq_root .md-code-block{z-index:0}.gNoOdq_turnStickyRegion:has(>.gNoOdq_turnStickyLanes>.gNoOdq_closedProcessSummary),.gNoOdq_turnStickyRegion:has(.gNoOdq_flowCell[data-flow-summary]){--reader-code-summary-height:var(--reader-summary-height,var(--reader-lane-block,40px))}.gNoOdq_root .md-code-block>:has(+[data-code-block-content]){top:calc(max(var(--reader-toolbar-height,40px), var(--reader-status-height,0px)) + var(--reader-status-height,0px) + var(--reader-code-summary-height,0px))}.gNoOdq_flowCell[data-flow-summary]{box-shadow:0 -16px 0 var(--dsw-alias-bg-base), 0 -16px 0 var(--dsw-alias-bg-layer-1,transparent), 0 -16px 0 Canvas}.gNoOdq_closedProcessSummary{box-shadow:0 -14px 0 var(--dsw-alias-bg-base), 0 -14px 0 var(--dsw-alias-bg-layer-1,transparent), 0 -14px 0 Canvas}.gNoOdq_foldControl{background:var(--dsw-alias-bg-module-platform);border-radius:8px;align-items:center;gap:0;padding:2px;display:inline-flex}.gNoOdq_foldStop{color:var(--dsw-alias-label-secondary);white-space:nowrap;font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;padding:2px 8px;font-size:12px;line-height:18px}.gNoOdq_foldStop[data-on]{background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary)}.gNoOdq_flowCell[data-flow-summary]>.gNoOdq_flowCellInner{padding-bottom:0}.gNoOdq_flowCell[data-flow-summary] .gNoOdq_disclosureButton,.gNoOdq_closedProcessSummary .gNoOdq_disclosureButton{width:auto;max-width:100%}.gNoOdq_flowCell[data-flow-summary] .gNoOdq_foldSummary,.gNoOdq_closedProcessSummary .gNoOdq_foldSummary{min-width:0}.gNoOdq_turnProcessSticky>.gNoOdq_disclosure{flex:auto;min-width:0}.gNoOdq_disclosureButton{width:max-content;max-width:calc(100% + 6px)}.gNoOdq_statusText{flex:0 auto}.gNoOdq_think{width:max-content;min-width:0}.gNoOdq_thinkSizer{white-space:normal;overflow-wrap:anywhere}.gNoOdq_thinkText{white-space:normal;overflow-wrap:anywhere;text-overflow:clip;overflow:visible}.gNoOdq_toolbar,.gNoOdq_turnStickyLanes,.gNoOdq_turnProcessSticky,.gNoOdq_liveFoldContainer,.gNoOdq_flowCell[data-flow-summary],.gNoOdq_closedProcessSummary{background-color:canvas;background-image:var(--reader-lane-plate);background-repeat:no-repeat}.gNoOdq_root[data-reader-glass] .gNoOdq_toolbar,.gNoOdq_root[data-reader-glass] .gNoOdq_turnProcessSticky,.gNoOdq_root[data-reader-glass] .gNoOdq_liveFoldContainer{-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);box-shadow:none;background:0 0;border:0;border-radius:0}.gNoOdq_root[data-reader-glass] .gNoOdq_toolbar,.gNoOdq_root[data-reader-glass] .gNoOdq_turnProcessSticky{-webkit-backdrop-filter:none;backdrop-filter:none}.gNoOdq_root[data-reader-glass] .gNoOdq_toolbar{pointer-events:none}.gNoOdq_root[data-reader-glass] .gNoOdq_toolbar>.gNoOdq_textButton,.gNoOdq_root[data-reader-glass] .gNoOdq_turnProcessSticky .gNoOdq_disclosureButton{pointer-events:auto;background:color-mix(in srgb, var(--dsw-alias-bg-layer-1,#ffffffb3) 80%, transparent);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-radius:999px;padding:2px 10px}.gNoOdq_root[data-reader-glass] .gNoOdq_turnProcessSticky .gNoOdq_disclosureButton{max-width:100%;margin-left:0}.gNoOdq_root[data-reader-glass] .gNoOdq_toolbar>.gNoOdq_textButton:hover,.gNoOdq_root[data-reader-glass] .gNoOdq_turnProcessSticky .gNoOdq_disclosureButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.gNoOdq_root[data-reader-glass] .gNoOdq_flowCell[data-flow-summary],.gNoOdq_root[data-reader-glass] .gNoOdq_closedProcessSummary{box-shadow:none;z-index:auto;border-left:2px solid color-mix(in srgb, var(--dsw-alias-border-l2,#80808059) 80%, transparent);padding-block:2px;background:0 0;border-radius:0;min-height:32px;padding-left:10px;position:static}.gNoOdq_root[data-reader-glass] .gNoOdq_turnStickyLanes{z-index:auto;display:contents;position:static;top:auto}.gNoOdq_root[data-reader-glass] .gNoOdq_turnStickyRegion{--reader-code-summary-height:0px}.gNoOdq_root[data-reader-glass] .gNoOdq_turnStickyLanes .gNoOdq_turnProcessSticky{top:var(--reader-toolbar-height,var(--reader-lane-block,40px));z-index:6;position:sticky}.gNoOdq_root[data-reader-glass] .gNoOdq_user{background:color-mix(in srgb, var(--dsw-specific-bubble,#00000014) 25%, transparent);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid color-mix(in srgb, var(--dsw-alias-border-l1,#8080804d) 30%, transparent)}.gNoOdq_root[data-reader-glass] .gNoOdq_reasonCard{border-color:color-mix(in srgb, var(--dsw-alias-border-l1,#80808059) 65%, transparent);background:0 0}.gNoOdq_root[data-reader-glass] .gNoOdq_toolDetails,.gNoOdq_root[data-reader-glass] .gNoOdq_notice,.gNoOdq_root[data-reader-glass] .gNoOdq_unknown,.gNoOdq_root[data-reader-glass] .gNoOdq_toolRaw,.gNoOdq_root[data-reader-glass] .gNoOdq_systemPrompt,.gNoOdq_root[data-reader-glass] .gNoOdq_compactionSummaryBox,.gNoOdq_root[data-reader-glass] .gNoOdq_attention,.gNoOdq_root[data-reader-glass] .gNoOdq_imageFrame,.gNoOdq_root[data-reader-glass] .gNoOdq_imageDialog{background:0 0}.gNoOdq_root[data-reader-glass] .gNoOdq_diffOverlay{background:color-mix(in srgb, var(--dsw-alias-bg-module-platform,#7f7f7f1a) 20%, transparent);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.gNoOdq_root[data-reader-glass] .gNoOdq_diffScrollArea{background:0 0}.gNoOdq_root[data-reader-glass] .gNoOdq_diffTab{background:color-mix(in srgb, var(--dsw-alias-bg-base,#fffc) 25%, transparent)}.gNoOdq_root[data-reader-glass] .gNoOdq_diffTab[data-active]{background:color-mix(in srgb, var(--dsw-alias-bg-base,#fff) 45%, transparent)}:is(.gNoOdq_root[data-reader-glass] .md-code-block,.gNoOdq_root[data-reader-glass] [data-diff]){border:1px solid color-mix(in srgb, var(--dsw-alias-border-l1,#80808033) 30%, transparent);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);background:color-mix(in srgb, var(--dsw-alias-markdown-code-block,#0000000f) 25%, transparent)!important}.gNoOdq_root[data-reader-glass] .gNoOdq_foldControl{background:color-mix(in srgb, var(--dsw-alias-bg-module-platform,#7f7f7f1a) 25%, transparent);border:1px solid color-mix(in srgb, var(--dsw-alias-border-l2,#80808033) 30%, transparent)}.gNoOdq_root[data-reader-glass] .gNoOdq_foldStop[data-on]{background:color-mix(in srgb, var(--dsw-alias-bg-base,#fff) 35%, transparent)}.gNoOdq_root[data-reader-glass] .gNoOdq_compactionPill,.gNoOdq_root[data-reader-glass] .gNoOdq_deliverableChip,.gNoOdq_root[data-reader-glass] .gNoOdq_toolState,.gNoOdq_root[data-reader-glass] .gNoOdq_diffStatButton{background:0 0;border-color:#0000}.gNoOdq_root[data-reader-glass] .gNoOdq_compactionPill:hover,.gNoOdq_root[data-reader-glass] .gNoOdq_compactionPill:focus-visible,.gNoOdq_root[data-reader-glass] .gNoOdq_deliverableChip:hover,.gNoOdq_root[data-reader-glass] .gNoOdq_deliverableChip:focus-within,.gNoOdq_root[data-reader-glass] .gNoOdq_toolState:hover,.gNoOdq_root[data-reader-glass] .gNoOdq_diffStatButton:hover,.gNoOdq_root[data-reader-glass] .gNoOdq_diffStatButton:focus-visible,.gNoOdq_root[data-reader-glass] .gNoOdq_diffStatButton[aria-expanded=true]{background:var(--dsw-alias-bg-module-platform,#7f7f7f1a);border-color:var(--dsw-alias-border-l2,#7f7f7f47)}";
42470
42766
  const tagId$5 = "dsh-better-display/Reader.module.css";
42471
42767
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$5) + "]") === null) {
42472
42768
  const tag = document.createElement("style");
@@ -42577,7 +42873,6 @@ ${trimmed}
42577
42873
  "numberRollSizer": "gNoOdq_numberRollSizer",
42578
42874
  "pendingImage": "gNoOdq_pendingImage",
42579
42875
  "priorFoldDrawer": "gNoOdq_priorFoldDrawer",
42580
- "processCommentary": "gNoOdq_processCommentary",
42581
42876
  "processContents": "gNoOdq_processContents",
42582
42877
  "processFootnote": "gNoOdq_processFootnote",
42583
42878
  "processFrame": "gNoOdq_processFrame",
@@ -42586,6 +42881,7 @@ ${trimmed}
42586
42881
  "processMeta": "gNoOdq_processMeta",
42587
42882
  "processMetaInner": "gNoOdq_processMetaInner",
42588
42883
  "processMetaLine": "gNoOdq_processMetaLine",
42884
+ "processSeamRow": "gNoOdq_processSeamRow",
42589
42885
  "readerRunningGlow": "gNoOdq_readerRunningGlow",
42590
42886
  "readerStepShimmer": "gNoOdq_readerStepShimmer",
42591
42887
  "readingText": "gNoOdq_readingText",
@@ -42654,8 +42950,7 @@ ${trimmed}
42654
42950
  "waitOvertime": "gNoOdq_waitOvertime",
42655
42951
  "waitSeconds": "gNoOdq_waitSeconds"
42656
42952
  };
42657
- //#endregion
42658
- //#region src/client/word-motion.tsx
42953
+ // src/client/word-motion.tsx
42659
42954
  const WordScope = (0, react.createContext)({
42660
42955
  enabled: false,
42661
42956
  generation: 0
@@ -42809,8 +43104,216 @@ ${trimmed}
42809
43104
  })
42810
43105
  });
42811
43106
  }
42812
- //#endregion
42813
- //#region \0dshx-css-module:TurnMetrics.module.css.mjs
43107
+ // src/client/text-transform.ts
43108
+ /**
43109
+ * Cordis service name. Another client plugin lists this in `inject` and
43110
+ * registers a transformer. The reading view paints the result. The session
43111
+ * block, transcript, and fold inputs stay on the original text.
43112
+ */
43113
+ const TEXT_TRANSFORM_SERVICE = "dsh-better-display.text-transform";
43114
+ const KINDS = /* @__PURE__ */ new Set(["reasoning", "body"]);
43115
+ function isKind(value) {
43116
+ return KINDS.has(value);
43117
+ }
43118
+ function reportTransformError(error) {
43119
+ console.warn("[dsh-better-display] text transform failed; showing original text", error);
43120
+ }
43121
+ function passthrough(request) {
43122
+ return {
43123
+ text: request.text,
43124
+ final: !request.streaming,
43125
+ transformRevision: 0,
43126
+ motionRevision: 0
43127
+ };
43128
+ }
43129
+ function normalize(value, request) {
43130
+ if (typeof value === "string") return {
43131
+ text: value,
43132
+ final: !request.streaming
43133
+ };
43134
+ if (!value || typeof value.text !== "string") return {
43135
+ text: request.text,
43136
+ final: !request.streaming
43137
+ };
43138
+ return {
43139
+ text: value.text,
43140
+ final: typeof value.final === "boolean" ? value.final : !request.streaming
43141
+ };
43142
+ }
43143
+ /**
43144
+ * Appends keep the motion revision. A replacement gets a new one so the
43145
+ * timeline treats the swapped string as already settled.
43146
+ */
43147
+ function nextDisplayCursor(previous, display) {
43148
+ if (!previous) return {
43149
+ ...display,
43150
+ transformRevision: 0,
43151
+ motionRevision: 0
43152
+ };
43153
+ if (previous.text === display.text && previous.final === display.final) return previous;
43154
+ const appended = display.text.startsWith(previous.text);
43155
+ return {
43156
+ text: display.text,
43157
+ final: display.final,
43158
+ transformRevision: previous.transformRevision + 1,
43159
+ motionRevision: appended ? previous.motionRevision : previous.motionRevision + 1
43160
+ };
43161
+ }
43162
+ /**
43163
+ * Streaming flag fed to the reveal. With no transformer this is the model's
43164
+ * flag. A transformer that reports `final: !streaming` produces the same flag.
43165
+ */
43166
+ function displayStreaming(sourceStreaming, shown, active) {
43167
+ return active ? !shown.final : sourceStreaming;
43168
+ }
43169
+ var TextTransformRegistryImpl = class {
43170
+ stacks = /* @__PURE__ */ new Map();
43171
+ listeners = /* @__PURE__ */ new Map();
43172
+ cursors = /* @__PURE__ */ new Map();
43173
+ register(kind, transformer) {
43174
+ const requested = [...new Set(Array.isArray(kind) ? kind : [kind])];
43175
+ for (const item of requested) if (!isKind(item)) throw new Error(`Unknown display text kind: ${String(item)}`);
43176
+ const bound = [];
43177
+ for (const item of requested) {
43178
+ const entry = { transformer };
43179
+ try {
43180
+ entry.unsubscribe = transformer.subscribe?.(() => {
43181
+ this.emit(item);
43182
+ });
43183
+ } catch (error) {
43184
+ reportTransformError(error);
43185
+ }
43186
+ const stack = this.stacks.get(item) ?? [];
43187
+ stack.push(entry);
43188
+ this.stacks.set(item, stack);
43189
+ bound.push({
43190
+ kind: item,
43191
+ entry
43192
+ });
43193
+ this.emit(item);
43194
+ }
43195
+ return () => {
43196
+ for (const { kind: item, entry } of bound) {
43197
+ try {
43198
+ entry.unsubscribe?.();
43199
+ } catch (error) {
43200
+ reportTransformError(error);
43201
+ }
43202
+ const stack = this.stacks.get(item);
43203
+ if (!stack) continue;
43204
+ const next = stack.filter((candidate) => candidate !== entry);
43205
+ if (next.length > 0) this.stacks.set(item, next);
43206
+ else this.stacks.delete(item);
43207
+ this.emit(item);
43208
+ }
43209
+ };
43210
+ }
43211
+ has(kind) {
43212
+ return (this.stacks.get(kind)?.length ?? 0) > 0;
43213
+ }
43214
+ read(request) {
43215
+ const transformer = this.stacks.get(request.kind)?.at(-1)?.transformer;
43216
+ if (!transformer) return passthrough(request);
43217
+ let display;
43218
+ try {
43219
+ display = normalize(transformer.read(request), request);
43220
+ } catch (error) {
43221
+ reportTransformError(error);
43222
+ display = {
43223
+ text: request.text,
43224
+ final: !request.streaming
43225
+ };
43226
+ }
43227
+ const key = `${request.kind}\u0000${request.blockKey}`;
43228
+ const next = nextDisplayCursor(this.cursors.get(key), display);
43229
+ this.cursors.set(key, next);
43230
+ return next;
43231
+ }
43232
+ subscribe(kind, listener) {
43233
+ const set = this.listeners.get(kind) ?? /* @__PURE__ */ new Set();
43234
+ set.add(listener);
43235
+ this.listeners.set(kind, set);
43236
+ return () => {
43237
+ set.delete(listener);
43238
+ };
43239
+ }
43240
+ release(kind, blockKey) {
43241
+ this.cursors.delete(`${kind}\u0000${blockKey}`);
43242
+ }
43243
+ emit(kind) {
43244
+ const set = this.listeners.get(kind);
43245
+ if (!set) return;
43246
+ for (const listener of [...set]) listener();
43247
+ }
43248
+ };
43249
+ function createTextTransformRegistry() {
43250
+ return new TextTransformRegistryImpl();
43251
+ }
43252
+ let active;
43253
+ /** Points the reading view at the registry this client plugin provides. */
43254
+ function installTextTransformRegistry(registry) {
43255
+ const previous = active;
43256
+ active = registry;
43257
+ return () => {
43258
+ active = previous;
43259
+ };
43260
+ }
43261
+ function activeTextTransform() {
43262
+ return active;
43263
+ }
43264
+ /**
43265
+ * Subscribe while mounted. The reveal components call this only after
43266
+ * `has(kind)`, so an empty registry adds no hook and no listener.
43267
+ */
43268
+ function useDisplayText(registry, request) {
43269
+ const [epoch, setEpoch] = (0, react.useState)(0);
43270
+ const { text, streaming, blockKey, kind, source } = request;
43271
+ (0, react.useEffect)(() => registry.subscribe(kind, () => {
43272
+ setEpoch((value) => value + 1);
43273
+ }), [registry, kind]);
43274
+ (0, react.useEffect)(() => () => {
43275
+ registry.release(kind, blockKey);
43276
+ }, [
43277
+ registry,
43278
+ kind,
43279
+ blockKey
43280
+ ]);
43281
+ return (0, react.useMemo)(() => registry.read({
43282
+ text,
43283
+ streaming,
43284
+ blockKey,
43285
+ kind,
43286
+ source
43287
+ }), [
43288
+ registry,
43289
+ text,
43290
+ streaming,
43291
+ blockKey,
43292
+ kind,
43293
+ source,
43294
+ epoch
43295
+ ]);
43296
+ }
43297
+ /** Chain-slot owner. Extra fields are optional so older callers still type-check. */
43298
+ function readerBlockOwner(block, streaming, source, presentation, holdFormatting) {
43299
+ return {
43300
+ block,
43301
+ streaming,
43302
+ source,
43303
+ presentation: {
43304
+ startedAt: presentation.startedAt,
43305
+ interrupted: presentation.interrupted,
43306
+ liveText: presentation.liveText
43307
+ },
43308
+ holdFormatting,
43309
+ liveText: presentation.liveText
43310
+ };
43311
+ }
43312
+ /** Copy actions read this. Display text never replaces it. */
43313
+ function originalAnswerText(blocks) {
43314
+ return blocks.flatMap((block) => block.kind === "text" && typeof block.text === "string" ? [block.text] : []).join("\n\n");
43315
+ }
43316
+ // \0dshx-css-module:/home/runner/work/dsh-better-display/dsh-better-display/src/client/TurnMetrics.module.css.mjs
42814
43317
  const css$4 = "._QZbGG_container{color:var(--dsw-alias-label-tertiary);align-items:center;gap:2px;font-size:13px;line-height:24px;display:inline-flex;position:relative}._QZbGG_pillButton{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-tertiary);cursor:pointer;border-radius:12px;outline:none;align-items:center;gap:5px;padding:2px 8px;font-family:inherit;font-size:11px;transition:all .14s;display:inline-flex}._QZbGG_pillButton:hover,._QZbGG_pillButton[data-active=true]{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-interactive-bg-hover)}._QZbGG_timeButton{height:28px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;cursor:pointer;background:0 0;border:none;border-radius:28px;align-items:center;gap:4px;padding:6px 8px;font-family:inherit;font-size:13px;line-height:24px;display:inline-flex}._QZbGG_timeButton:hover,._QZbGG_timeButton[data-active=true]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}._QZbGG_pillIcon{opacity:.8;flex:none;width:12px;height:12px}._QZbGG_metricsPop{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);z-index:50;text-align:left;border-radius:10px;flex-direction:column;gap:8px;width:260px;padding:12px 14px;animation:.14s ease-out _QZbGG_popIn;display:flex;position:absolute;bottom:calc(100% + 8px);left:0;box-shadow:0 6px 20px #00000024}@keyframes _QZbGG_popIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}._QZbGG_popHeader{color:var(--dsw-alias-label-primary);border-bottom:1px solid var(--dsw-alias-border-l1);justify-content:space-between;align-items:center;padding-bottom:6px;font-size:12px;font-weight:600;display:flex}._QZbGG_popSection{flex-direction:column;gap:4px;display:flex}._QZbGG_popSectionTitle{text-transform:uppercase;letter-spacing:.5px;color:var(--dsw-alias-label-tertiary);margin-top:2px;font-size:10px;font-weight:600}._QZbGG_popGrid{grid-template-columns:1fr auto;gap:3px 12px;font-size:11px;line-height:18px;display:grid}._QZbGG_popLabel{color:var(--dsw-alias-label-secondary)}._QZbGG_popValue{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;text-align:right;font-weight:500}._QZbGG_popBadge{background:var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:3px;margin-left:4px;padding:0 4px;font-size:10px}";
42815
43318
  const tagId$4 = "dsh-better-display/TurnMetrics.module.css";
42816
43319
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
@@ -42835,8 +43338,7 @@ ${trimmed}
42835
43338
  "popValue": "_QZbGG_popValue",
42836
43339
  "timeButton": "_QZbGG_timeButton"
42837
43340
  };
42838
- //#endregion
42839
- //#region src/client/TurnMetrics.tsx
43341
+ // src/client/TurnMetrics.tsx
42840
43342
  function formatTokens(count) {
42841
43343
  if (count >= 1e6) return `${(count / 1e6).toFixed(2)}M tok`;
42842
43344
  if (count >= 1e3) return `${(count / 1e3).toFixed(1)}k tok`;
@@ -43025,8 +43527,7 @@ ${trimmed}
43025
43527
  ]
43026
43528
  });
43027
43529
  });
43028
- //#endregion
43029
- //#region src/client/Blocks.tsx
43530
+ // src/client/Blocks.tsx
43030
43531
  const IMAGE_TYPES = /* @__PURE__ */ new Set([
43031
43532
  "image/png",
43032
43533
  "image/jpeg",
@@ -43174,7 +43675,7 @@ ${trimmed}
43174
43675
  };
43175
43676
  });
43176
43677
  }
43177
- function ReadingMarkdown({ text, streaming, holdFormatting, startedAt, interrupted = false, liveText = false, kind = "body", fileMentions }) {
43678
+ function MarkdownStream({ text, streaming, holdFormatting, startedAt, interrupted = false, liveText = false, kind = "body", fileMentions, transformed = false }) {
43178
43679
  const root = (0, react.useRef)(null);
43179
43680
  const presentation = useStreamingText(text, streaming, {
43180
43681
  startedAt,
@@ -43198,6 +43699,7 @@ ${trimmed}
43198
43699
  "data-ud-motion": "reader-text-arrival",
43199
43700
  "data-ud-motion-type": "reveal",
43200
43701
  "data-ud-motion-no-flash": "true",
43702
+ "data-text-transform": transformed ? streaming ? "live" : "final" : void 0,
43201
43703
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MotionMarkdown, {
43202
43704
  text: presentation.text,
43203
43705
  streaming: effectiveMode,
@@ -43207,7 +43709,32 @@ ${trimmed}
43207
43709
  })
43208
43710
  });
43209
43711
  }
43210
- function ReadingReasoning({ text, streaming, holdFormatting, startedAt, interrupted = false, liveText = false }) {
43712
+ function TransformedMarkdown({ registry, blockKey, source, text, streaming, ...props }) {
43713
+ const shown = useDisplayText(registry, {
43714
+ text,
43715
+ streaming,
43716
+ blockKey,
43717
+ kind: "body",
43718
+ source
43719
+ });
43720
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownStream, {
43721
+ ...props,
43722
+ text: shown.text,
43723
+ streaming: displayStreaming(streaming, shown, true),
43724
+ transformed: true
43725
+ });
43726
+ }
43727
+ function ReadingMarkdown({ blockKey, source, ...props }) {
43728
+ const registry = activeTextTransform();
43729
+ if (registry?.has("body")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TransformedMarkdown, {
43730
+ registry,
43731
+ blockKey,
43732
+ source,
43733
+ ...props
43734
+ });
43735
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownStream, { ...props });
43736
+ }
43737
+ function ReasoningStream({ text, streaming, holdFormatting, startedAt, interrupted = false, liveText = false, transformed = false }) {
43211
43738
  const presentation = useStreamingText(text, streaming, {
43212
43739
  startedAt,
43213
43740
  interrupted: interrupted || !liveText,
@@ -43224,6 +43751,7 @@ ${trimmed}
43224
43751
  "data-ud-motion": "reader-text-arrival",
43225
43752
  "data-ud-motion-type": "reveal",
43226
43753
  "data-ud-motion-no-flash": "true",
43754
+ "data-text-transform": transformed ? streaming ? "live" : "final" : void 0,
43227
43755
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MotionPlainText, {
43228
43756
  text: presentation.text,
43229
43757
  enabled: liveText && presentation.reveal,
@@ -43231,7 +43759,32 @@ ${trimmed}
43231
43759
  })
43232
43760
  });
43233
43761
  }
43234
- function fallback(block, streaming, source, loadImage, fillComposer, holdFormatting, presentation, fileMentions) {
43762
+ function TransformedReasoning({ registry, blockKey, source, text, streaming, ...props }) {
43763
+ const shown = useDisplayText(registry, {
43764
+ text,
43765
+ streaming,
43766
+ blockKey,
43767
+ kind: "reasoning",
43768
+ source
43769
+ });
43770
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningStream, {
43771
+ ...props,
43772
+ text: shown.text,
43773
+ streaming: displayStreaming(streaming, shown, true),
43774
+ transformed: true
43775
+ });
43776
+ }
43777
+ function ReadingReasoning({ blockKey, source, ...props }) {
43778
+ const registry = activeTextTransform();
43779
+ if (registry?.has("reasoning")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TransformedReasoning, {
43780
+ registry,
43781
+ blockKey,
43782
+ source,
43783
+ ...props
43784
+ });
43785
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningStream, { ...props });
43786
+ }
43787
+ function fallback(block, streaming, source, loadImage, fillComposer, holdFormatting, presentation, blockKey, fileMentions) {
43235
43788
  switch (block.kind) {
43236
43789
  case "text": return source === "user" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
43237
43790
  text: block.text,
@@ -43241,7 +43794,9 @@ ${trimmed}
43241
43794
  streaming,
43242
43795
  holdFormatting,
43243
43796
  ...presentation,
43244
- fileMentions
43797
+ fileMentions,
43798
+ blockKey,
43799
+ source
43245
43800
  });
43246
43801
  case "image": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageBlock, {
43247
43802
  attachment: block.attachment,
@@ -43251,7 +43806,9 @@ ${trimmed}
43251
43806
  text: block.text,
43252
43807
  streaming,
43253
43808
  holdFormatting,
43254
- ...presentation
43809
+ ...presentation,
43810
+ blockKey,
43811
+ source
43255
43812
  });
43256
43813
  case "tool-call": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.JsonBlock, {
43257
43814
  label: `工具参数 · ${block.name}`,
@@ -43280,20 +43837,21 @@ ${trimmed}
43280
43837
  }
43281
43838
  }
43282
43839
  const Blocks = (0, react.memo)(function Blocks({ blocks, streaming = false, source = "assistant", holdFormatting = false, startedAt, interrupted, liveText, renderSlotChain, loadImage, fillComposer, fileMentions }) {
43840
+ const reactId = (0, react.useId)();
43283
43841
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComposerFillContext.Provider, {
43284
43842
  value: fillComposer,
43285
43843
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
43286
43844
  className: Reader_module_css_default.blocks,
43287
43845
  "data-streaming": streaming || void 0,
43288
- children: blocks.map((block, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockBoundary, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderSlotChain("dsh-better-display.block", {
43289
- block,
43290
- streaming,
43291
- source
43292
- }, { fallback: fallback(block, streaming, source, loadImage, fillComposer, holdFormatting, {
43846
+ children: blocks.map((block, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockBoundary, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderSlotChain("dsh-better-display.block", readerBlockOwner(block, streaming, source, {
43293
43847
  startedAt,
43294
43848
  interrupted,
43295
43849
  liveText
43296
- }, fileMentions) }) }) }, block.kind === "image" ? `image:${block.attachment.attachmentId}:${index}` : `${index}:${block.kind}`))
43850
+ }, holdFormatting), { fallback: fallback(block, streaming, source, loadImage, fillComposer, holdFormatting, {
43851
+ startedAt,
43852
+ interrupted,
43853
+ liveText
43854
+ }, `${reactId}:${index}:${block.kind}`, fileMentions) }) }) }, block.kind === "image" ? `image:${block.attachment.attachmentId}:${index}` : `${index}:${block.kind}`))
43297
43855
  })
43298
43856
  });
43299
43857
  });
@@ -43338,7 +43896,7 @@ ${trimmed}
43338
43896
  }
43339
43897
  function CopyAnswer({ blocks, onFork, metrics, extraActions }) {
43340
43898
  const { receipt, copy } = useCopyReceipt();
43341
- const text = blocks.filter((block) => block.kind === "text").map((block) => block.text).join("\n\n");
43899
+ const text = originalAnswerText(blocks);
43342
43900
  const endedAt = metrics?.endedAt;
43343
43901
  const hasMetrics = metrics !== void 0 && (metrics.usage !== void 0 || metrics.runMs !== void 0 || endedAt !== void 0);
43344
43902
  if (!text.trim() && !onFork && !hasMetrics) return null;
@@ -43437,8 +43995,7 @@ ${trimmed}
43437
43995
  const end = Math.max(0, contentHeight - viewportHeight);
43438
43996
  return Math.min(end, Math.min(end, Math.max(0, top)) + Math.max(1, lineHeight) * Math.max(2, Math.min(40, Math.ceil(backlogLines))));
43439
43997
  }
43440
- //#endregion
43441
- //#region src/client/ReasoningCard.tsx
43998
+ // src/client/ReasoningCard.tsx
43442
43999
  const EASING$1 = "cubic-bezier(.22,1,.36,1)";
43443
44000
  /** One real transcript: reference transform while following, native scroll while reading. */
43444
44001
  function ReasoningCard({ children, step, active, motion, selected, onRead }) {
@@ -43802,8 +44359,7 @@ ${trimmed}
43802
44359
  ]
43803
44360
  });
43804
44361
  }
43805
- //#endregion
43806
- //#region src/client/official-slots.tsx
44362
+ // src/client/official-slots.tsx
43807
44363
  /**
43808
44364
  * RC2 composition adapter. Only public registry operations are used. We lend
43809
44365
  * registrations a Reader-owned seat, not a second implementation of their UI.
@@ -43819,22 +44375,62 @@ ${trimmed}
43819
44375
  tools: "tool.call.toolview",
43820
44376
  tail: "conversation.chat.turnTail",
43821
44377
  nodes: "conversation.chat.node",
43822
- images: "conversation.message.images"
44378
+ messageImages: "conversation.chat.message.images",
44379
+ userImages: "conversation.chat.user.images"
43823
44380
  };
43824
44381
  const OFFICIAL_SEATS = {
43825
44382
  actions: "dsh-better-display.official.actions/conversation.chat.assistant-actions",
43826
44383
  tools: "dsh-better-display.official.tools/tool.call.toolview",
43827
44384
  tail: "dsh-better-display.official.tail/conversation.chat.turnTail",
43828
44385
  nodes: "dsh-better-display.official.nodes/conversation.chat.node",
43829
- images: "dsh-better-display.official.images/conversation.message.images"
44386
+ messageImages: "dsh-better-display.official.messageImages/conversation.chat.message.images",
44387
+ userImages: "dsh-better-display.official.userImages/conversation.chat.user.images"
43830
44388
  };
43831
44389
  const officialSeat = (family, source = OFFICIAL_SLOTS[family]) => `dsh-better-display.official.${family}/${source}`;
44390
+ /**
44391
+ * Searchable hide for official nodes drawn outside the Chat flow viewport.
44392
+ * The flow's own `useGroupAction` is not in scope here; this keeps the same
44393
+ * `hidden="until-found"` contract so browser find can still reveal the row.
44394
+ */
44395
+ function useReaderGroupAction(hidden, reveal) {
44396
+ const ref = (0, react.useRef)(null);
44397
+ (0, react.useLayoutEffect)(() => {
44398
+ const element = ref.current;
44399
+ if (element === null) return;
44400
+ if (hidden && element.contains(element.ownerDocument.activeElement)) {
44401
+ reveal();
44402
+ return;
44403
+ }
44404
+ if (hidden) element.setAttribute("hidden", "until-found");
44405
+ else element.removeAttribute("hidden");
44406
+ }, [hidden, reveal]);
44407
+ (0, react.useEffect)(() => {
44408
+ const element = ref.current;
44409
+ if (element === null) return;
44410
+ element.addEventListener("beforematch", reveal);
44411
+ return () => {
44412
+ element.removeEventListener("beforematch", reveal);
44413
+ };
44414
+ }, [reveal]);
44415
+ return ref;
44416
+ }
44417
+ const readerGroupAction = useReaderGroupAction;
43832
44418
  const READER_OWNED_NODES = /* @__PURE__ */ new Set([
43833
- "assistant-step",
43834
44419
  "tool-call",
43835
44420
  "turn-tail",
43836
44421
  "turn-process"
43837
44422
  ]);
44423
+ /**
44424
+ * Mirror a chat-node registration into the Reader seat.
44425
+ * Assistant replacements are the entries that would actually replace the
44426
+ * official renderer on the host. The official priority itself stays out, so
44427
+ * Reader keeps its own answer markdown until a plugin opts in.
44428
+ */
44429
+ function acceptMirroredChatNode(entry) {
44430
+ const key = entry.options.key ?? "";
44431
+ if (key === "assistant-step") return (entry.options.priority ?? 0) < 0;
44432
+ return !READER_OWNED_NODES.has(key);
44433
+ }
43838
44434
  const EXPECTED = {
43839
44435
  actions: {
43840
44436
  kind: "list",
@@ -43852,7 +44448,11 @@ ${trimmed}
43852
44448
  kind: "keyed",
43853
44449
  scope: "session"
43854
44450
  },
43855
- images: {
44451
+ messageImages: {
44452
+ kind: "single",
44453
+ scope: "session"
44454
+ },
44455
+ userImages: {
43856
44456
  kind: "single",
43857
44457
  scope: "session"
43858
44458
  }
@@ -43984,7 +44584,7 @@ ${trimmed}
43984
44584
  const target = officialSeat(family);
43985
44585
  const declared = slots.spec(target);
43986
44586
  if (spec.kind !== declared?.kind || spec.scope !== declared.scope) throw new Error(`Official slot contract changed: ${source}`);
43987
- return mirrorOfficialSlot(slots, source, target, `dsh-better-display.official.${family}`, family === "nodes" ? (entry) => !READER_OWNED_NODES.has(entry.options.key ?? "") : void 0);
44587
+ return mirrorOfficialSlot(slots, source, target, `dsh-better-display.official.${family}`, family === "nodes" ? acceptMirroredChatNode : void 0);
43988
44588
  }));
43989
44589
  }
43990
44590
  } catch (error) {
@@ -43995,8 +44595,7 @@ ${trimmed}
43995
44595
  for (const stop of disposers.splice(0).reverse()) stop();
43996
44596
  };
43997
44597
  }
43998
- //#endregion
43999
- //#region src/client/chat-seat.ts
44598
+ // src/client/chat-seat.ts
44000
44599
  function chatSeatProps(kind, key) {
44001
44600
  return {
44002
44601
  "data-chat-anchor-key": key,
@@ -44005,8 +44604,7 @@ ${trimmed}
44005
44604
  "data-chat-flow-kind": kind
44006
44605
  };
44007
44606
  }
44008
- //#endregion
44009
- //#region src/client/OfficialContent.tsx
44607
+ // src/client/OfficialContent.tsx
44010
44608
  function OfficialActions({ official, messageId }) {
44011
44609
  if (!official || !messageId) return null;
44012
44610
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -44049,41 +44647,79 @@ ${trimmed}
44049
44647
  })
44050
44648
  });
44051
44649
  }
44052
- function OfficialNode({ node, fallback, ...render }) {
44053
- const official = render.official;
44054
- const [disclosureReset] = (0, react.useState)(() => (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(0));
44055
- if (!official) return fallback;
44056
- const owner = {
44650
+ function chatNodeOwner(render, official) {
44651
+ return {
44057
44652
  cwd: render.cwd,
44058
44653
  openFile: render.openFile ?? (() => {}),
44059
- openSkill: () => {},
44060
44654
  forkAt: render.forkAt ?? (() => {}),
44061
44655
  inspectCall: (callId) => official.openView("trajectory", callId),
44062
44656
  loadImage: official.officialImageLoader,
44063
- renderMessageImages: (images) => official.renderSlot(OFFICIAL_SEATS.images, {
44064
- ...images,
44065
- loadImage: official.officialImageLoader
44066
- }),
44067
44657
  fileMentions: official.officialFileMentions
44068
44658
  };
44659
+ }
44660
+ function nodeHookContext(turnData, disclosureReset) {
44661
+ return {
44662
+ turnData,
44663
+ disclosureReset,
44664
+ useGroupAction: readerGroupAction
44665
+ };
44666
+ }
44667
+ function OfficialNode({ node, fallback, ...render }) {
44668
+ const official = render.official;
44669
+ const [disclosureReset] = (0, react.useState)(() => (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(0));
44670
+ if (!official) return fallback;
44069
44671
  const turn = node.location.kind === "turn" || node.location.kind === "step" ? node.location.turn : void 0;
44070
44672
  const renderNode = official.renderSlot;
44071
44673
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
44072
44674
  "data-reader-official-node": node.kind,
44073
44675
  ...node.kind === "user" || node.kind === "steering" ? chatSeatProps(node.kind, node.key) : {},
44074
44676
  children: renderNode(OFFICIAL_SEATS.nodes, {
44075
- ...owner,
44677
+ ...chatNodeOwner(render, official),
44076
44678
  node
44077
44679
  }, {
44078
44680
  entryKey: node.kind,
44079
- hookContext: {
44080
- turnData: turn?.data,
44081
- disclosureReset
44082
- },
44681
+ hookContext: nodeHookContext(turn?.data, disclosureReset),
44083
44682
  fallback
44084
44683
  })
44085
44684
  });
44086
44685
  }
44686
+ /**
44687
+ * Body of one assistant segment. A mirrored assistant-step replacement draws
44688
+ * it; Reader markdown is the fallback when no replacement is registered.
44689
+ * The caller keeps reasoning blocks on the reading card.
44690
+ */
44691
+ function OfficialAssistantBody({ node, blocks, streaming = false, holdFormatting = false, startedAt, interrupted = false, liveText = false, ...render }) {
44692
+ const official = render.official;
44693
+ const [disclosureReset] = (0, react.useState)(() => (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(0));
44694
+ const fallback = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Blocks, {
44695
+ ...render,
44696
+ blocks,
44697
+ streaming,
44698
+ holdFormatting,
44699
+ startedAt,
44700
+ interrupted,
44701
+ liveText
44702
+ });
44703
+ if (!official) return fallback;
44704
+ const turn = node.location.kind === "turn" || node.location.kind === "step" ? node.location.turn : void 0;
44705
+ const view = blocks.length === node.data.blocks.length && blocks.every((block, index) => block === node.data.blocks[index]) ? node : {
44706
+ ...node,
44707
+ data: {
44708
+ ...node.data,
44709
+ blocks
44710
+ }
44711
+ };
44712
+ const renderNode = official.renderSlot;
44713
+ return renderNode(OFFICIAL_SEATS.nodes, {
44714
+ ...chatNodeOwner(render, official),
44715
+ node: view,
44716
+ groupPart: "response"
44717
+ }, {
44718
+ entryKey: "assistant-step",
44719
+ hookContext: nodeHookContext(turn?.data, disclosureReset),
44720
+ fallback
44721
+ });
44722
+ }
44087
44723
  function OfficialTail({ official, owner, produced }) {
44088
44724
  const tailOwner = owner && official ? {
44089
44725
  ...owner,
@@ -44096,8 +44732,69 @@ ${trimmed}
44096
44732
  children: official.renderSlot(OFFICIAL_SEATS.tail, tailOwner)
44097
44733
  }) : null;
44098
44734
  }
44099
- //#endregion
44100
- //#region src/client/motion.tsx
44735
+ // src/client/turn-slots.ts
44736
+ /**
44737
+ * Keep live turns in the incoming `groups` order, and keep a departing turn
44738
+ * next to the live neighbour that preceded it.
44739
+ *
44740
+ * A rewind removes turn keys from the visible order. Unmounting them in that
44741
+ * same commit snaps the transcript, so the lane retains the last group object
44742
+ * until the collapse finishes. A re-render, a stream tick, or a reconnect that
44743
+ * still contains the same keys only replaces the group object and leaves
44744
+ * `live` true — those updates must not start a collapse. A key that returns
44745
+ * is live again where `groups` places it. Loading older history therefore
44746
+ * stays in transcript order instead of appending the older turns after the
44747
+ * ones already on screen. A departing slot is inserted immediately after the
44748
+ * nearest preceding slot that is still live, or at the front when it had none,
44749
+ * so several departing neighbours keep their relative order.
44750
+ *
44751
+ * Returns `current` itself when nothing changed, so a React state update can
44752
+ * bail out on `Object.is`.
44753
+ */
44754
+ function reconcileTurnSlots(current, groups) {
44755
+ const incoming = new Map(groups.map((group) => [group.key, group]));
44756
+ const next = groups.map((group) => ({
44757
+ group,
44758
+ live: true
44759
+ }));
44760
+ for (const slot of current) {
44761
+ if (incoming.has(slot.group.key)) continue;
44762
+ insertDeparting(next, {
44763
+ group: slot.group,
44764
+ live: false
44765
+ }, precedingLiveKey(current, incoming, slot.group.key));
44766
+ }
44767
+ return next.length === current.length && next.every((slot, index) => slot.live === current[index].live && slot.group === current[index].group) ? current : next;
44768
+ }
44769
+ /** Nearest preceding slot that is still in the incoming order, if any. */
44770
+ function precedingLiveKey(current, incoming, key) {
44771
+ const index = current.findIndex((slot) => slot.group.key === key);
44772
+ for (let cursor = index - 1; cursor >= 0; cursor--) {
44773
+ const candidate = current[cursor].group.key;
44774
+ if (incoming.has(candidate)) return candidate;
44775
+ }
44776
+ }
44777
+ /**
44778
+ * Place a departing slot after `anchorKey`, or at the front when the slot had
44779
+ * no preceding live neighbour. Slots that share an anchor keep `current` order
44780
+ * because each one is inserted at the end of the departing run after that anchor.
44781
+ */
44782
+ function insertDeparting(next, slot, anchorKey) {
44783
+ if (anchorKey === void 0) {
44784
+ const firstLive = next.findIndex((item) => item.live);
44785
+ next.splice(firstLive < 0 ? next.length : firstLive, 0, slot);
44786
+ return;
44787
+ }
44788
+ const anchorIndex = next.findIndex((item) => item.group.key === anchorKey);
44789
+ if (anchorIndex < 0) {
44790
+ next.unshift(slot);
44791
+ return;
44792
+ }
44793
+ let insertAt = anchorIndex + 1;
44794
+ while (insertAt < next.length && next[insertAt]?.live === false) insertAt++;
44795
+ next.splice(insertAt, 0, slot);
44796
+ }
44797
+ // src/client/motion.tsx
44101
44798
  const EASING = "cubic-bezier(.22,1,.36,1)";
44102
44799
  function useMotionAllowed(enabled) {
44103
44800
  const [reduced, setReduced] = (0, react.useState)(() => window.matchMedia("(prefers-reduced-motion: reduce)").matches);
@@ -44109,7 +44806,7 @@ ${trimmed}
44109
44806
  }, []);
44110
44807
  return enabled && !reduced;
44111
44808
  }
44112
- function usePinnedSelection(root, selector = "[data-reader-answer], [data-reader-process]") {
44809
+ function usePinnedSelection(root, selector = "[data-reader-answer], [data-reader-body], [data-reader-process]") {
44113
44810
  const [keys, setKeys] = (0, react.useState)([]);
44114
44811
  (0, react.useEffect)(() => {
44115
44812
  const update = () => {
@@ -44355,12 +45052,14 @@ ${trimmed}
44355
45052
  });
44356
45053
  }
44357
45054
  /** Retire only narration that was actually visible; historical rows stay folded. */
44358
- function RetiringContent({ visible, children }) {
45055
+ function RetiringContent({ visible, children, onRetired }) {
44359
45056
  const { enabled } = (0, react.useContext)(StreamMotionContext);
44360
45057
  const root = (0, react.useRef)(null);
44361
45058
  const animation = (0, react.useRef)(null);
44362
45059
  const [present, setPresent] = (0, react.useState)(visible);
44363
45060
  const [focusHeld, setFocusHeld] = (0, react.useState)(false);
45061
+ const retired = (0, react.useRef)(onRetired);
45062
+ retired.current = onRetired;
44364
45063
  (0, react.useLayoutEffect)(() => {
44365
45064
  const element = root.current;
44366
45065
  if (visible) {
@@ -44370,16 +45069,38 @@ ${trimmed}
44370
45069
  return;
44371
45070
  }
44372
45071
  if (!element) return;
44373
- if (element.contains(document.activeElement)) {
44374
- setFocusHeld(true);
45072
+ let retiredNow = false;
45073
+ const retireNow = () => {
45074
+ if (retiredNow) return;
45075
+ retiredNow = true;
45076
+ animation.current?.cancel();
45077
+ animation.current = null;
45078
+ focusSurvivesRetire(element);
45079
+ setPresent(false);
45080
+ retired.current?.();
45081
+ };
45082
+ if (!enabled || document.hidden) {
45083
+ retireNow();
44375
45084
  return;
44376
45085
  }
44377
- if (focusHeld) return;
45086
+ const canListen = typeof document.addEventListener === "function";
45087
+ const holdsFocus = element.contains(document.activeElement);
45088
+ if (holdsFocus || focusHeld) {
45089
+ if (holdsFocus) setFocusHeld(true);
45090
+ const onHiddenWhileHeld = () => {
45091
+ if (document.hidden) retireNow();
45092
+ };
45093
+ if (canListen) document.addEventListener("visibilitychange", onHiddenWhileHeld);
45094
+ return () => {
45095
+ if (canListen) document.removeEventListener("visibilitychange", onHiddenWhileHeld);
45096
+ };
45097
+ }
44378
45098
  const from = element.getBoundingClientRect().height;
44379
45099
  animation.current?.cancel();
44380
45100
  animation.current = null;
44381
- if (!enabled || from < 1) {
45101
+ if (from < 1) {
44382
45102
  setPresent(false);
45103
+ retired.current?.();
44383
45104
  return;
44384
45105
  }
44385
45106
  const next = element.animate([{
@@ -44394,17 +45115,24 @@ ${trimmed}
44394
45115
  fill: "both"
44395
45116
  });
44396
45117
  animation.current = next;
45118
+ let settled = false;
44397
45119
  const settle = () => {
44398
- if (animation.current === next) {
44399
- animation.current = null;
44400
- next.cancel();
44401
- setPresent(false);
44402
- }
45120
+ if (settled || animation.current !== next) return;
45121
+ settled = true;
45122
+ animation.current = null;
45123
+ next.cancel();
45124
+ setPresent(false);
45125
+ retired.current?.();
44403
45126
  };
44404
45127
  next.onfinish = settle;
44405
45128
  const deadline = window.setTimeout(settle, 460);
45129
+ const onHide = () => {
45130
+ if (document.hidden) settle();
45131
+ };
45132
+ if (canListen) document.addEventListener("visibilitychange", onHide);
44406
45133
  return () => {
44407
45134
  window.clearTimeout(deadline);
45135
+ if (canListen) document.removeEventListener("visibilitychange", onHide);
44408
45136
  if (animation.current === next) animation.current = null;
44409
45137
  next.cancel();
44410
45138
  };
@@ -44426,6 +45154,80 @@ ${trimmed}
44426
45154
  children
44427
45155
  });
44428
45156
  }
45157
+ /**
45158
+ * Keeps a departing turn mounted for the length of its own collapse.
45159
+ *
45160
+ * A rewind removes turns from the visible order, and unmounting them outright
45161
+ * snapped the transcript: every withdrawn row disappeared in one frame with
45162
+ * nothing to scroll. Instead each departing turn is retained, rendered with
45163
+ * `visible={false}` so it animates to zero height, and released only once the
45164
+ * collapse has settled — so the reader watches the rows fold away and the rows
45165
+ * below glide up into their place.
45166
+ *
45167
+ * Live slots follow the incoming group order, so older history stays above the
45168
+ * tail. A collapsing turn stays next to the live neighbour that preceded it;
45169
+ * appending it would jump it to the bottom for the length of the animation.
45170
+ * Retention is bounded by the animation itself: `onRetired`
45171
+ * drops the slot, so a departing turn lives for one collapse and no longer —
45172
+ * and a key that comes back later is re-added as a live slot.
45173
+ */
45174
+ function RetiringTurns({ groups, render }) {
45175
+ const [slots, setSlots] = (0, react.useState)(() => groups.map((group) => ({
45176
+ group,
45177
+ live: true
45178
+ })));
45179
+ (0, react.useLayoutEffect)(() => {
45180
+ setSlots((current) => reconcileTurnSlots(current, groups));
45181
+ }, [groups]);
45182
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: slots.map((slot) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RetiringContent, {
45183
+ visible: slot.live,
45184
+ onRetired: () => setSlots((current) => current.filter((candidate) => candidate.live || candidate.group.key !== slot.group.key)),
45185
+ children: render(slot.group)
45186
+ }, slot.group.key)) });
45187
+ }
45188
+ /** A turn that is still on screen, not one this commit is about to unmount. */
45189
+ function survivingTurn(node) {
45190
+ if (!(node instanceof HTMLElement) || node.dataset.readerRetiring === "retiring") return null;
45191
+ const turn = node.hasAttribute("data-reader-turn") ? node : node.querySelector("[data-reader-turn]");
45192
+ if (turn === null) return null;
45193
+ const retiring = turn.closest("[data-reader-retiring]");
45194
+ if (retiring instanceof HTMLElement && retiring.dataset.readerRetiring === "retiring") return null;
45195
+ return turn;
45196
+ }
45197
+ /**
45198
+ * Where focus goes when cut content is removed without an animation.
45199
+ * Prefer the nearest turn that is still live. Otherwise the reading column
45200
+ * (or the host scrollport) so focus does not fall through to the document.
45201
+ */
45202
+ function retireFocusTarget(element) {
45203
+ const parent = element.parentElement;
45204
+ if (parent) {
45205
+ const siblings = [...parent.children];
45206
+ const index = siblings.indexOf(element);
45207
+ for (let cursor = index - 1; cursor >= 0; cursor--) {
45208
+ const turn = survivingTurn(siblings[cursor]);
45209
+ if (turn) return turn;
45210
+ }
45211
+ for (let cursor = index + 1; cursor < siblings.length; cursor++) {
45212
+ const turn = survivingTurn(siblings[cursor]);
45213
+ if (turn) return turn;
45214
+ }
45215
+ }
45216
+ const column = element.closest("[data-chat-flow]");
45217
+ if (column && !element.contains(column)) return column;
45218
+ const scroll = element.closest("[data-conversation-scroll]");
45219
+ if (scroll && !element.contains(scroll)) return scroll;
45220
+ return null;
45221
+ }
45222
+ /** Move focus out of `element` before it unmounts. A no-op when focus is elsewhere. */
45223
+ function focusSurvivesRetire(element) {
45224
+ const active = document.activeElement;
45225
+ if (active === null || !element.contains(active)) return;
45226
+ const target = retireFocusTarget(element);
45227
+ if (target === null || element.contains(target)) return;
45228
+ if (target.tabIndex < 0) target.tabIndex = -1;
45229
+ target.focus({ preventScroll: true });
45230
+ }
44429
45231
  function useReadingScroll(root, motion) {
44430
45232
  const port = (0, react.useRef)(null);
44431
45233
  const following = (0, react.useRef)(true);
@@ -44640,8 +45442,342 @@ ${trimmed}
44640
45442
  }, [])
44641
45443
  };
44642
45444
  }
44643
- //#endregion
44644
- //#region src/client/native/tool-call-model.ts
45445
+ // src/client/fold-choreography.ts
45446
+ /** Presentation clock, not model/network time. */
45447
+ const FOLD_TIMING = {
45448
+ collapse: 320,
45449
+ count: 160,
45450
+ settle: 80,
45451
+ reveal: 180,
45452
+ /**
45453
+ * Slack added to every phase deadline before the watchdog forces the next phase.
45454
+ *
45455
+ * The choreography advances on animation promises and rendered frames. Both are
45456
+ * best-effort: a cancelled animation *rejects* and a compositor that never
45457
+ * finishes one leaves its promise pending, while a backgrounded tab stops
45458
+ * delivering frames. Without a deadline the machine would sit in a non-idle
45459
+ * phase forever, and a non-idle phase freezes both the frame source and the text
45460
+ * reveal, so the turn would look stuck until a remount. This is the liveness
45461
+ * guarantee that does not depend on any of them.
45462
+ */
45463
+ watchdogSlack: 240
45464
+ };
45465
+ /** A step always has the same parent and React key, even when its membership changes. */
45466
+ function flowRows(items) {
45467
+ return items.flatMap((item) => item.kind === "fold" ? [{
45468
+ kind: "summary",
45469
+ key: item.key,
45470
+ item
45471
+ }, ...item.steps.map((step) => ({
45472
+ kind: "step",
45473
+ key: step.key,
45474
+ step,
45475
+ foldKey: item.key
45476
+ }))] : [{
45477
+ kind: "step",
45478
+ key: item.key,
45479
+ step: item.step
45480
+ }]);
45481
+ }
45482
+ function retiringKeys(before, after, open) {
45483
+ const folded = new Set(flowRows(after).filter((row) => row.kind === "step" && row.foldKey && !open[row.foldKey]).map((row) => row.key));
45484
+ return flowRows(before).filter((row) => row.kind === "step" && !row.foldKey && folded.has(row.key)).map((row) => row.key);
45485
+ }
45486
+ /** Insert only new summary slots. Never admit incoming content during shrink. */
45487
+ function collapseRows(before, target) {
45488
+ const rows = flowRows(before);
45489
+ const headers = new Set(rows.filter((row) => row.kind === "summary").map((row) => row.key));
45490
+ for (const item of target) {
45491
+ if (item.kind !== "fold" || headers.has(item.key)) continue;
45492
+ const members = new Set(item.steps.map((step) => step.key));
45493
+ const at = rows.findIndex((row) => members.has(row.key));
45494
+ if (at >= 0) rows.splice(at, 0, {
45495
+ kind: "summary",
45496
+ key: item.key,
45497
+ item: {
45498
+ ...item,
45499
+ summary: item.summary.replace(/\d+/g, "0")
45500
+ }
45501
+ });
45502
+ }
45503
+ return rows;
45504
+ }
45505
+ function containsNewUser(before, after) {
45506
+ const users = new Set(before.filter((item) => item.kind === "user").map((item) => item.key));
45507
+ return after.some((item) => item.kind === "user" && !users.has(item.key));
45508
+ }
45509
+ // src/client/LiveFold.tsx
45510
+ /** Values are presentation commits, never live network counters. */
45511
+ function SubtleNumberRoll({ value, motion }) {
45512
+ const [outgoing, setOutgoing] = (0, react.useState)(null);
45513
+ const previous = (0, react.useRef)(value);
45514
+ (0, react.useLayoutEffect)(() => {
45515
+ if (value === previous.current) return;
45516
+ const old = previous.current;
45517
+ previous.current = value;
45518
+ if (!motion) {
45519
+ setOutgoing(null);
45520
+ return;
45521
+ }
45522
+ setOutgoing(old);
45523
+ const timer = window.setTimeout(() => setOutgoing(null), FOLD_TIMING.count);
45524
+ return () => window.clearTimeout(timer);
45525
+ }, [value, motion]);
45526
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
45527
+ className: Reader_module_css_default.numberRollRoot,
45528
+ "aria-hidden": "true",
45529
+ children: [
45530
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
45531
+ className: Reader_module_css_default.numberRollSizer,
45532
+ children: value
45533
+ }),
45534
+ outgoing !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
45535
+ className: `${Reader_module_css_default.numberRollDigit} ${Reader_module_css_default.numberRollExit}`,
45536
+ children: outgoing
45537
+ }, `out-${outgoing}`),
45538
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
45539
+ className: `${Reader_module_css_default.numberRollDigit} ${outgoing !== null ? Reader_module_css_default.numberRollEnter : ""}`,
45540
+ children: value
45541
+ }, `cur-${value}`)
45542
+ ]
45543
+ });
45544
+ }
45545
+ function FoldSummaryText({ summary, motion }) {
45546
+ const parts = (0, react.useMemo)(() => {
45547
+ const regex = /([^\d]+)(\d+)/g;
45548
+ const result = [];
45549
+ let end = 0;
45550
+ for (const match of summary.matchAll(regex)) {
45551
+ result.push({
45552
+ text: match[1],
45553
+ number: Number(match[2])
45554
+ });
45555
+ end = match.index + match[0].length;
45556
+ }
45557
+ if (end < summary.length) result.push({ text: summary.slice(end) });
45558
+ return result;
45559
+ }, [summary]);
45560
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
45561
+ className: Reader_module_css_default.foldSummary,
45562
+ "data-reader-fold-summary": summary,
45563
+ "aria-label": summary,
45564
+ children: parts.map((part) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
45565
+ className: Reader_module_css_default.foldSummaryPart,
45566
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: part.text }), part.number !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubtleNumberRoll, {
45567
+ value: part.number,
45568
+ motion
45569
+ })]
45570
+ }, part.text))
45571
+ });
45572
+ }
45573
+ // src/client/projection.ts
45574
+ function groupNodes(order, get) {
45575
+ const groups = [];
45576
+ const seenTurns = /* @__PURE__ */ new Set();
45577
+ for (const key of order) {
45578
+ const node = get(key);
45579
+ if (!node || node.visibility === "hidden") continue;
45580
+ const turn = node.location.kind === "step" || node.location.kind === "turn" ? node.location.turn.turn : null;
45581
+ const previous = groups.at(-1);
45582
+ if (turn !== null && previous?.turn === turn) previous.keys.push(key);
45583
+ else {
45584
+ groups.push({
45585
+ key: turn === null ? `node:${key}` : seenTurns.has(turn) ? `turn:${turn}:${key}` : `turn:${turn}`,
45586
+ turn,
45587
+ keys: [key]
45588
+ });
45589
+ if (turn !== null) seenTurns.add(turn);
45590
+ }
45591
+ }
45592
+ return groups;
45593
+ }
45594
+ function boundaryOf(turn) {
45595
+ return {
45596
+ status: turn?.status ?? "unknown",
45597
+ reason: turn?.end?.data.reason.kind ?? null,
45598
+ latestStep: turn?.steps.at(-1)?.step ?? -1,
45599
+ closingStep: turn?.data.get("turn-tail")?.closing?.step ?? null
45600
+ };
45601
+ }
45602
+ /**
45603
+ * A settled body step before the closing step of a successful turn.
45604
+ *
45605
+ * This is presentation classification only. Narration is never process content
45606
+ * and never folds. The flag only marks body text that is no longer the turn's
45607
+ * final answer, so copy and fork stay on that final answer.
45608
+ */
45609
+ function isEarlierNarration(data, boundary) {
45610
+ if (data.status !== "settled" || boundary.status !== "closed" || boundary.reason !== "completed") return false;
45611
+ if (data.blocks.some((block) => block.kind === "image" || block.kind === "other")) return false;
45612
+ return boundary.closingStep !== null && data.step < boundary.closingStep;
45613
+ }
45614
+ function processExpanded(choice, boundary) {
45615
+ return choice ?? !(boundary.status === "closed" && boundary.reason === "completed");
45616
+ }
45617
+ /** Reading while running must not pin the process open after completion. */
45618
+ function processChoiceKey(groupKey, boundary) {
45619
+ return `${groupKey}:${boundary.status}:${boundary.reason ?? "pending"}`;
45620
+ }
45621
+ /**
45622
+ * A body-only assistant step is not a thinking/process disclosure.
45623
+ *
45624
+ * Narration (text/image/other blocks) is always visible, so it never counts as
45625
+ * foldable process content; only literal reasoning does. Tool calls arrive as
45626
+ * their own flow entries and are checked separately by the reader.
45627
+ */
45628
+ function hasProcessContent(node, _boundary) {
45629
+ if (!node || node.visibility === "hidden") return false;
45630
+ if (node.kind === "assistant-step") return node.data.blocks.some((block) => block.kind === "reasoning" && block.text.trim() !== "");
45631
+ return node.kind === "context" || node.kind === "model-retry" || node.kind === "system-prompt" || node.kind === "turn-process" || node.kind === "command" || node.kind === "manual-compaction" || node.kind === "compaction";
45632
+ }
45633
+ function hasVisibleBody(blocks) {
45634
+ return blocks.some((block) => block.kind === "image" || block.kind === "other" || block.kind === "text" && block.text.trim() !== "");
45635
+ }
45636
+ /** Keep native block order. In particular, never lift a later Think above text. */
45637
+ function assistantSegments(blocks) {
45638
+ const segments = [];
45639
+ let previous;
45640
+ blocks.forEach((block, index) => {
45641
+ if (block.kind === "tool-call") {
45642
+ previous = void 0;
45643
+ return;
45644
+ }
45645
+ const kind = block.kind === "reasoning" ? "reasoning" : "body";
45646
+ if (previous?.kind === kind) previous.blocks.push(block);
45647
+ else {
45648
+ previous = {
45649
+ kind,
45650
+ start: index,
45651
+ blocks: [block]
45652
+ };
45653
+ segments.push(previous);
45654
+ }
45655
+ });
45656
+ return segments;
45657
+ }
45658
+ function terminalLabel(reason) {
45659
+ switch (reason) {
45660
+ case "completed": return null;
45661
+ case "aborted":
45662
+ case "interrupted": return "本轮已停止,已生成的内容仍保留。";
45663
+ case "blocked": return "本轮需要处理阻塞事项;请查看原对话与下方操作区。";
45664
+ case "max-tokens": return "输出达到本轮长度限制,内容可能尚未完整。";
45665
+ case "error": return "本轮未完成;错误信息保留在下方。";
45666
+ case null: return null;
45667
+ default: return `本轮结束状态:${reason}。请在原对话中核对完整记录。`;
45668
+ }
45669
+ }
45670
+ /**
45671
+ * Extract the first usable fork anchor seq from candidate message nodes.
45672
+ * Only the durable closing message seq cuts the intended turn prefix: an
45673
+ * absent anchor must surface as undefined (never as 0 or NaN), because the
45674
+ * host treats a missing atSeq as a whole-session fork.
45675
+ */
45676
+ function forkAnchorSeq(candidates) {
45677
+ for (const candidate of candidates) {
45678
+ const seq = candidate?.seq;
45679
+ if (typeof seq === "number" && Number.isFinite(seq)) return seq;
45680
+ }
45681
+ }
45682
+ // src/client/AssistantNode.tsx
45683
+ function isNode$1(node, kind) {
45684
+ return node.kind === kind;
45685
+ }
45686
+ const AssistantNode = (0, react.memo)(function AssistantNode({ useChat, nodeKey, boundary, processOpen = false, pinned = false, folded = false, partStart, seamSummary, seamAfter = false, motion, onRead, returnFocusTo, ...render }) {
45687
+ const node = useChat((snapshot) => snapshot.nodes.get(nodeKey));
45688
+ if (!node || node.visibility === "hidden" || !isNode$1(node, "assistant-step")) return null;
45689
+ const data = node.data;
45690
+ const parts = assistantSegments(data.blocks);
45691
+ const earlier = isEarlierNarration(data, boundary);
45692
+ const hasToolCalls = data.blocks.some((block) => block.kind === "tool-call");
45693
+ const isProcessStep = earlier || folded || hasToolCalls || boundary.latestStep > 0 && data.step < boundary.latestStep;
45694
+ const body = data.blocks.filter((block) => block.kind !== "reasoning" && block.kind !== "tool-call");
45695
+ const visible = partStart === void 0 ? parts : parts.filter((part) => part.start === partStart);
45696
+ const bodyProps = {
45697
+ streaming: data.status === "running",
45698
+ holdFormatting: pinned,
45699
+ startedAt: data.time,
45700
+ interrupted: data.status === "interrupted",
45701
+ liveText: true
45702
+ };
45703
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: visible.map((part) => {
45704
+ const last = parts.findIndex((item) => item.start === part.start) === parts.length - 1;
45705
+ if (part.kind === "reasoning") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProcessFragment, {
45706
+ open: processOpen,
45707
+ motion,
45708
+ onRead,
45709
+ returnFocusTo,
45710
+ nodeKey,
45711
+ framed: true,
45712
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningCard, {
45713
+ step: data.step,
45714
+ active: processOpen && boundary.status === "open" && data.step === boundary.latestStep,
45715
+ motion,
45716
+ selected: pinned,
45717
+ onRead,
45718
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Blocks, {
45719
+ ...render,
45720
+ blocks: part.blocks,
45721
+ streaming: data.status === "running" && last && data.blocks.at(-1)?.kind === "reasoning",
45722
+ holdFormatting: pinned,
45723
+ startedAt: data.time,
45724
+ interrupted: data.status === "interrupted",
45725
+ liveText: true
45726
+ })
45727
+ })
45728
+ }, part.start);
45729
+ if (!hasVisibleBody(part.blocks)) return null;
45730
+ const isFinalAnswer = !isProcessStep;
45731
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [seamSummary && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
45732
+ className: Reader_module_css_default.processSeamRow,
45733
+ "data-reader-process-seam": true,
45734
+ "data-reader-process-seam-summary": seamSummary,
45735
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Disclosure, {
45736
+ open: false,
45737
+ onChange: () => onRead(),
45738
+ ariaLabel: "被折叠的过程",
45739
+ label: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FoldSummaryText, {
45740
+ summary: seamSummary,
45741
+ motion: false
45742
+ })
45743
+ })
45744
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
45745
+ className: Reader_module_css_default.answer,
45746
+ "data-reader-body": true,
45747
+ "data-reader-anchor": true,
45748
+ "data-reader-key": nodeKey,
45749
+ "data-reader-source-start": part.start,
45750
+ "data-reader-seam-after": seamAfter || void 0,
45751
+ "data-answer-status": data.status,
45752
+ "data-answer-phase": isFinalAnswer ? "body" : "process",
45753
+ children: [
45754
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OfficialAssistantBody, {
45755
+ ...render,
45756
+ node,
45757
+ blocks: part.blocks,
45758
+ ...bodyProps
45759
+ }),
45760
+ isFinalAnswer && last && data.status === "interrupted" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
45761
+ className: Reader_module_css_default.stopped,
45762
+ children: "已停止"
45763
+ }),
45764
+ isFinalAnswer && last && data.status !== "running" && boundary.status === "closed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyAnswer, {
45765
+ blocks: body,
45766
+ onFork: (() => {
45767
+ const anchor = forkAnchorSeq([data.finalNode, { seq: render.forkSeq }]);
45768
+ return render.forkAt && anchor !== void 0 ? () => render.forkAt(anchor) : void 0;
45769
+ })(),
45770
+ metrics: render.metrics,
45771
+ extraActions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OfficialActions, {
45772
+ official: render.official,
45773
+ messageId: data.finalNode?.messageId
45774
+ })
45775
+ })
45776
+ ]
45777
+ })] }, part.start);
45778
+ }) });
45779
+ });
45780
+ // src/client/native/tool-call-model.ts
44645
45781
  /**
44646
45782
  * Pure row-model derivation for tool summary rows: variant classification,
44647
45783
  * one-line summary, expanded-body text, and flattened result output from the
@@ -44831,8 +45967,7 @@ ${trimmed}
44831
45967
  state
44832
45968
  };
44833
45969
  }
44834
- //#endregion
44835
- //#region src/client/ToolActivity.tsx
45970
+ // src/client/ToolActivity.tsx
44836
45971
  const LABEL = {
44837
45972
  preparing: "输入生成中",
44838
45973
  running: "执行中",
@@ -45433,108 +46568,7 @@ ${trimmed}
45433
46568
  depth: depth + 1
45434
46569
  }, child.callId))] });
45435
46570
  }
45436
- //#endregion
45437
- //#region src/client/projection.ts
45438
- function groupNodes(order, get) {
45439
- const groups = [];
45440
- const seenTurns = /* @__PURE__ */ new Set();
45441
- for (const key of order) {
45442
- const node = get(key);
45443
- if (!node || node.visibility === "hidden") continue;
45444
- const turn = node.location.kind === "step" || node.location.kind === "turn" ? node.location.turn.turn : null;
45445
- const previous = groups.at(-1);
45446
- if (turn !== null && previous?.turn === turn) previous.keys.push(key);
45447
- else {
45448
- groups.push({
45449
- key: turn === null ? `node:${key}` : seenTurns.has(turn) ? `turn:${turn}:${key}` : `turn:${turn}`,
45450
- turn,
45451
- keys: [key]
45452
- });
45453
- if (turn !== null) seenTurns.add(turn);
45454
- }
45455
- }
45456
- return groups;
45457
- }
45458
- function boundaryOf(turn) {
45459
- return {
45460
- status: turn?.status ?? "unknown",
45461
- reason: turn?.end?.data.reason.kind ?? null,
45462
- latestStep: turn?.steps.at(-1)?.step ?? -1,
45463
- closingStep: turn?.data.get("turn-tail")?.closing?.step ?? null
45464
- };
45465
- }
45466
- function isEarlierNarration(data, boundary) {
45467
- if (data.status !== "settled" || boundary.status !== "closed" || boundary.reason !== "completed") return false;
45468
- if (data.blocks.some((block) => block.kind === "image" || block.kind === "other")) return false;
45469
- return boundary.closingStep !== null && data.step < boundary.closingStep;
45470
- }
45471
- function processExpanded(choice, boundary) {
45472
- return choice ?? !(boundary.status === "closed" && boundary.reason === "completed");
45473
- }
45474
- /** Reading while running must not pin the process open after completion. */
45475
- function processChoiceKey(groupKey, boundary) {
45476
- return `${groupKey}:${boundary.status}:${boundary.reason ?? "pending"}`;
45477
- }
45478
- /** A body-only assistant step is not a thinking/process disclosure. */
45479
- function hasProcessContent(node, boundary) {
45480
- if (!node || node.visibility === "hidden") return false;
45481
- if (node.kind === "assistant-step") {
45482
- const data = node.data;
45483
- return data.blocks.some((block) => block.kind === "reasoning" && block.text.trim() !== "") || isEarlierNarration(data, boundary) && hasVisibleBody(data.blocks);
45484
- }
45485
- return node.kind === "context" || node.kind === "model-retry" || node.kind === "system-prompt" || node.kind === "turn-process" || node.kind === "command" || node.kind === "manual-compaction" || node.kind === "compaction";
45486
- }
45487
- function hasVisibleBody(blocks) {
45488
- return blocks.some((block) => block.kind === "image" || block.kind === "other" || block.kind === "text" && block.text.trim() !== "");
45489
- }
45490
- /** Keep native block order. In particular, never lift a later Think above text. */
45491
- function assistantSegments(blocks) {
45492
- const segments = [];
45493
- let previous;
45494
- blocks.forEach((block, index) => {
45495
- if (block.kind === "tool-call") {
45496
- previous = void 0;
45497
- return;
45498
- }
45499
- const kind = block.kind === "reasoning" ? "reasoning" : "body";
45500
- if (previous?.kind === kind) previous.blocks.push(block);
45501
- else {
45502
- previous = {
45503
- kind,
45504
- start: index,
45505
- blocks: [block]
45506
- };
45507
- segments.push(previous);
45508
- }
45509
- });
45510
- return segments;
45511
- }
45512
- function terminalLabel(reason) {
45513
- switch (reason) {
45514
- case "completed": return null;
45515
- case "aborted":
45516
- case "interrupted": return "本轮已停止,已生成的内容仍保留。";
45517
- case "blocked": return "本轮需要处理阻塞事项;请查看原对话与下方操作区。";
45518
- case "max-tokens": return "输出达到本轮长度限制,内容可能尚未完整。";
45519
- case "error": return "本轮未完成;错误信息保留在下方。";
45520
- case null: return null;
45521
- default: return `本轮结束状态:${reason}。请在原对话中核对完整记录。`;
45522
- }
45523
- }
45524
- /**
45525
- * Extract the first usable fork anchor seq from candidate message nodes.
45526
- * Only the durable closing message seq cuts the intended turn prefix: an
45527
- * absent anchor must surface as undefined (never as 0 or NaN), because the
45528
- * host treats a missing atSeq as a whole-session fork.
45529
- */
45530
- function forkAnchorSeq(candidates) {
45531
- for (const candidate of candidates) {
45532
- const seq = candidate?.seq;
45533
- if (typeof seq === "number" && Number.isFinite(seq)) return seq;
45534
- }
45535
- }
45536
- //#endregion
45537
- //#region src/client/open-file.ts
46571
+ // src/client/open-file.ts
45538
46572
  function deliverableOpenModeOf(value) {
45539
46573
  return value === "sidebar" ? "sidebar" : "external";
45540
46574
  }
@@ -45589,8 +46623,7 @@ ${trimmed}
45589
46623
  await args.openExternal(targetPath);
45590
46624
  return "external";
45591
46625
  }
45592
- //#endregion
45593
- //#region src/client/pending-submission.ts
46626
+ // src/client/pending-submission.ts
45594
46627
  function asReadonlyArray(value) {
45595
46628
  return Array.isArray(value) ? value : [];
45596
46629
  }
@@ -45612,8 +46645,7 @@ ${trimmed}
45612
46645
  }
45613
46646
  return [];
45614
46647
  }
45615
- //#endregion
45616
- //#region src/client/WaitClock.tsx
46648
+ // src/client/WaitClock.tsx
45617
46649
  /** Past this, a wait stops being a pause and reads as the model not answering. */
45618
46650
  const WAIT_OVERTIME_MS = 1e4;
45619
46651
  /**
@@ -45650,8 +46682,7 @@ ${trimmed}
45650
46682
  })]
45651
46683
  });
45652
46684
  }
45653
- //#endregion
45654
- //#region src/client/WaitingStatus.tsx
46685
+ // src/client/WaitingStatus.tsx
45655
46686
  function WaitingStatus({ anchor, label }) {
45656
46687
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
45657
46688
  className: Reader_module_css_default.turnStatus,
@@ -45665,8 +46696,7 @@ ${trimmed}
45665
46696
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WaitClock, { startTime: anchor.time }, anchor.key)]
45666
46697
  });
45667
46698
  }
45668
- //#endregion
45669
- //#region src/client/waiting-clock.ts
46699
+ // src/client/waiting-clock.ts
45670
46700
  const timestamp = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
45671
46701
  /**
45672
46702
  * Chat-layer kinds that hand the turn back to the model unconditionally.
@@ -45740,8 +46770,7 @@ ${trimmed}
45740
46770
  }
45741
46771
  return anchor;
45742
46772
  }
45743
- //#endregion
45744
- //#region src/client/native/ReferenceIcon.tsx
46773
+ // src/client/native/ReferenceIcon.tsx
45745
46774
  /**
45746
46775
  * Render the icon that identifies one inline reference domain.
45747
46776
  * @param props - Reference kind, optional size, and optional CSS class.
@@ -45771,8 +46800,7 @@ ${trimmed}
45771
46800
  });
45772
46801
  }
45773
46802
  }
45774
- //#endregion
45775
- //#region \0dshx-css-module:ContextBody.module.css.mjs
46803
+ // \0dshx-css-module:/home/runner/work/dsh-better-display/dsh-better-display/src/client/native/ContextBody.module.css.mjs
45776
46804
  const css$3 = ".O-IYlG_text{color:var(--dsw-alias-label-secondary);font:inherit;white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.O-IYlG_fields{border-top:1px solid var(--dsw-alias-line-secondary);flex-direction:column;gap:2px;margin:8px 0 0;padding-top:8px;display:flex}.O-IYlG_field{gap:8px;min-width:0;display:flex}.O-IYlG_fieldKey{min-width:96px;color:var(--dsw-alias-label-caption);flex:none}.O-IYlG_fieldValue{min-width:0;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;flex:auto;margin:0}.O-IYlG_files{flex-wrap:wrap;gap:4px 12px;margin:0 0 8px;padding:0;list-style:none;display:flex}.O-IYlG_file{align-items:baseline;gap:6px;min-width:0;display:flex}.O-IYlG_filePath{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}.O-IYlG_fileAction{color:var(--dsw-alias-label-caption)}.O-IYlG_catalogNotice{color:var(--dsw-alias-label-caption);margin:0 0 6px}.O-IYlG_entries{flex-direction:column;gap:4px;margin:0;padding:0;list-style:none;display:flex}.O-IYlG_entry{gap:8px;min-width:0;display:flex}.O-IYlG_entryName{color:var(--dsw-alias-label-secondary);flex:none}.O-IYlG_entryDescription{min-width:0;color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;flex:auto;overflow:hidden}.O-IYlG_sections{flex-direction:column;gap:8px;margin:0;display:flex}.O-IYlG_section{flex-direction:column;gap:2px;min-width:0;display:flex}.O-IYlG_sectionName{color:var(--dsw-alias-label-caption)}.O-IYlG_sectionText{color:var(--dsw-alias-label-secondary);white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.O-IYlG_relaySender{color:var(--dsw-alias-label-caption);overflow-wrap:anywhere;margin:0 0 6px}.O-IYlG_recalls{flex-direction:column;gap:2px;margin:0 0 8px;padding:0;list-style:none;display:flex}.O-IYlG_recall{gap:8px;min-width:0;display:flex}.O-IYlG_recallLabel{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}.O-IYlG_recallCounts{color:var(--dsw-alias-label-caption);flex:none}";
45777
46805
  const tagId$3 = "dsh-better-display/ContextBody.module.css";
45778
46806
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
@@ -45807,8 +46835,7 @@ ${trimmed}
45807
46835
  "sectionText": "O-IYlG_sectionText",
45808
46836
  "text": "O-IYlG_text"
45809
46837
  };
45810
- //#endregion
45811
- //#region src/client/native/ContextBody.tsx
46838
+ // src/client/native/ContextBody.tsx
45812
46839
  /** Model-facing text stays bounded at the disclosure, not at the producer. */
45813
46840
  const MAX_CHARS = 2e4;
45814
46841
  /** Rows a list body materializes before summarizing the remainder. */
@@ -46328,8 +47355,7 @@ ${trimmed}
46328
47355
  default: throw new Error(`unreachable context form: ${String(form)}`);
46329
47356
  }
46330
47357
  }
46331
- //#endregion
46332
- //#region src/client/native/tool-update.ts
47358
+ // src/client/native/tool-update.ts
46333
47359
  /**
46334
47360
  * Classify a context as the rc.2 tool-update row.
46335
47361
  * Mixed or empty content stays on the generic injection presentation.
@@ -46353,8 +47379,7 @@ ${trimmed}
46353
47379
  single: rows.length === 1 ? rows[0] : null
46354
47380
  };
46355
47381
  }
46356
- //#endregion
46357
- //#region \0dshx-css-module:ContextInjectionRow.module.css.mjs
47382
+ // \0dshx-css-module:/home/runner/work/dsh-better-display/dsh-better-display/src/client/native/ContextInjectionRow.module.css.mjs
46358
47383
  const css$2 = ".QsaPka_root{min-width:0}.QsaPka_root[data-open]{padding-bottom:4px}.QsaPka_chevron{color:var(--dsw-alias-label-secondary)}.QsaPka_sep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.QsaPka_source{min-width:0;color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;flex:none;font-size:14px;line-height:24px;overflow:hidden}.QsaPka_summary{min-width:0;color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;flex:auto;font-size:14px;line-height:24px;overflow:hidden}.QsaPka_body{box-sizing:border-box;background:var(--dsw-alias-markdown-code-block);width:calc(100% - 22px);max-height:141px;color:var(--dsw-alias-label-tertiary);font:400 11px/16px var(--ds-font-family-code);border:none;border-radius:8px;margin:4px 0 0 22px;padding:10px 16px 12px 12px;overflow:auto}.QsaPka_toolChanges{white-space:nowrap}";
46359
47384
  const tagId$2 = "dsh-better-display/ContextInjectionRow.module.css";
46360
47385
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
@@ -46373,8 +47398,7 @@ ${trimmed}
46373
47398
  "summary": "QsaPka_summary",
46374
47399
  "toolChanges": "QsaPka_toolChanges"
46375
47400
  };
46376
- //#endregion
46377
- //#region src/client/native/ContextInjectionRow.tsx
47401
+ // src/client/native/ContextInjectionRow.tsx
46378
47402
  /**
46379
47403
  * Render logged context with the Tool calls disclosure chrome from Figma.
46380
47404
  *
@@ -46453,8 +47477,7 @@ ${trimmed}
46453
47477
  })
46454
47478
  });
46455
47479
  }
46456
- //#endregion
46457
- //#region \0dshx-css-module:TimelineRail.module.css.mjs
47480
+ // \0dshx-css-module:/home/runner/work/dsh-better-display/dsh-better-display/src/client/TimelineRail.module.css.mjs
46458
47481
  const css$1 = ".oG8zJG_slot{z-index:10;pointer-events:none;height:0;position:sticky;top:0}.oG8zJG_frame{--turn-rail-band:calc(var(--dsh-conversation-viewport-height,100dvh) - var(--dsh-composer-height,152px));--turn-preview-height:100px;top:calc(var(--turn-rail-band) / 2);width:26px;height:min(var(--turn-natural-height), max(0px, calc(var(--turn-rail-band) - 64px)), 420px);cursor:pointer;pointer-events:auto;transition:height .22s cubic-bezier(.2,.8,.2,1);position:absolute;right:6px;transform:translateY(-50%)}.oG8zJG_scroller{overscroll-behavior:contain;scrollbar-width:none;position:absolute;inset:0;overflow-y:auto}.oG8zJG_scroller::-webkit-scrollbar{display:none}.oG8zJG_fadeTop{mask-image:linear-gradient(#0000 0,#000 24px 100%)}.oG8zJG_fadeBottom{mask-image:linear-gradient(#000 0 calc(100% - 24px),#0000 100%)}.oG8zJG_fadeTop.oG8zJG_fadeBottom{mask-image:linear-gradient(#0000 0,#000 24px calc(100% - 24px),#0000 100%)}.oG8zJG_marks{height:var(--turn-natural-height);position:relative}.oG8zJG_markPosition{top:calc(var(--turn-natural-position) + var(--turn-rail-inset));height:12px;transition:top .22s cubic-bezier(.2,.8,.2,1);position:absolute;left:0;right:0;transform:translateY(-50%)}.oG8zJG_mark{cursor:pointer;pointer-events:none;background:0 0;border:0;justify-content:flex-end;align-items:center;width:24px;padding:0;display:flex;position:absolute;inset:0 0 0 auto}.oG8zJG_tick{border-radius:2px;flex:none;transition:width .1s cubic-bezier(.16,1,.3,1),height .1s cubic-bezier(.16,1,.3,1),opacity .1s,background-color .14s}.oG8zJG_markBusy .oG8zJG_tick{animation:1s ease-in-out infinite alternate oG8zJG_markBusyPulse}@keyframes oG8zJG_markBusyPulse{0%{opacity:.4;width:8px}to{opacity:1;width:18px}}.oG8zJG_preview{top:clamp(0px, calc(var(--turn-natural-position) + var(--turn-rail-inset) - var(--turn-scroll-top,0px) - var(--turn-preview-height) / 2), calc(100% - var(--turn-preview-height)));box-sizing:border-box;width:min(280px,100cqw - 120px);max-height:var(--turn-preview-height);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-base);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);pointer-events:none;z-index:40;border-radius:10px;padding:10px 12px;transition:top .14s cubic-bezier(.2,.8,.2,1);animation:.12s ease-out oG8zJG_previewFadeIn;position:absolute;right:calc(100% + 10px);overflow:hidden;box-shadow:0 4px 18px #0000001f}@keyframes oG8zJG_previewFadeIn{0%{opacity:0;transform:translate(4px)}to{opacity:1;transform:translate(0)}}.oG8zJG_previewPrompt{-webkit-line-clamp:2;text-overflow:ellipsis;word-break:break-word;-webkit-box-orient:vertical;font-size:12px;font-weight:500;line-height:18px;display:-webkit-box;overflow:hidden}.oG8zJG_previewResponse{color:var(--dsw-alias-label-tertiary);-webkit-line-clamp:2;text-overflow:ellipsis;word-break:break-word;-webkit-box-orient:vertical;margin-top:4px;font-size:11px;line-height:16px;display:-webkit-box;overflow:hidden}@media (width<=960px){.oG8zJG_slot{display:none!important}}";
46459
47482
  const tagId$1 = "dsh-better-display/TimelineRail.module.css";
46460
47483
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
@@ -46481,8 +47504,7 @@ ${trimmed}
46481
47504
  "slot": "oG8zJG_slot",
46482
47505
  "tick": "oG8zJG_tick"
46483
47506
  };
46484
- //#endregion
46485
- //#region src/client/TimelineRail.tsx
47507
+ // src/client/TimelineRail.tsx
46486
47508
  const TURN_SPACING_PX = 12;
46487
47509
  const RAIL_INSET_PX = 6;
46488
47510
  const FADE_PX = 24;
@@ -46699,8 +47721,7 @@ ${trimmed}
46699
47721
  function landTurn(row, scrollport, offsetPx = 24) {
46700
47722
  scrollport.scrollTop += flowTop(row, scrollport) - offsetPx;
46701
47723
  }
46702
- //#endregion
46703
- //#region src/client/timeline.ts
47724
+ // src/client/timeline.ts
46704
47725
  /**
46705
47726
  * Merge host turn outline with loaded navigation items and turn deliverables.
46706
47727
  * Guarantees strictly ascending turn order and dedupes entries.
@@ -46743,8 +47764,7 @@ ${trimmed}
46743
47764
  if (byTurn.size === 0) return [];
46744
47765
  return [...byTurn.values()].sort((a, b) => a.turn - b.turn);
46745
47766
  }
46746
- //#endregion
46747
- //#region src/client/live-turn.ts
47767
+ // src/client/live-turn.ts
46748
47768
  function liveFoldEnabled(boundary) {
46749
47769
  return boundary.status === "open";
46750
47770
  }
@@ -46764,27 +47784,6 @@ ${trimmed}
46764
47784
  if (extra) parts.push(`记录×${extra}`);
46765
47785
  return parts.join(" · ") || "此前步骤";
46766
47786
  }
46767
- /** One chain: fold only when a new reasoning step has prior body/tool/reasoning. */
46768
- function splitChain(chain) {
46769
- const lastReasoning = chain.findLastIndex((step) => step.kind === "reasoning");
46770
- if (lastReasoning < 0) return {
46771
- fold: null,
46772
- open: chain
46773
- };
46774
- const prior = chain.slice(0, lastReasoning);
46775
- if (!prior.length) return {
46776
- fold: null,
46777
- open: chain
46778
- };
46779
- if (!prior.some((step) => step.kind === "reasoning" || step.kind === "body" || step.kind === "tool")) return {
46780
- fold: null,
46781
- open: chain
46782
- };
46783
- return {
46784
- fold: prior,
46785
- open: chain.slice(lastReasoning)
46786
- };
46787
- }
46788
47787
  function skipReasoning(part) {
46789
47788
  return part.kind === "reasoning" && !part.blocks.some((block) => block.kind === "reasoning" && block.text.trim() !== "");
46790
47789
  }
@@ -46864,48 +47863,118 @@ ${trimmed}
46864
47863
  }
46865
47864
  return steps;
46866
47865
  }
47866
+ /** Only a run that contains real thinking/tool work is worth folding. */
47867
+ function foldableRun(run) {
47868
+ return run.some((step) => step.kind === "reasoning" || step.kind === "tool");
47869
+ }
47870
+ /**
47871
+ * One live chain: narration stays visible, while every earlier reasoning/tool
47872
+ * run collapses into its own box at its original position.
47873
+ */
46867
47874
  function presentLiveTurn(steps, boundary, autoFold = true) {
46868
47875
  const live = autoFold && liveFoldEnabled(boundary);
46869
47876
  const items = [];
46870
- let chain = [];
46871
- const flush = () => {
46872
- if (!chain.length) return;
46873
- if (!live) {
46874
- for (const step of chain) items.push({
46875
- kind: "open",
46876
- key: step.key,
46877
- step
46878
- });
46879
- chain = [];
46880
- return;
46881
- }
46882
- const { fold, open } = splitChain(chain);
46883
- if (fold?.length) items.push({
47877
+ const open = (step) => items.push({
47878
+ kind: "open",
47879
+ key: step.key,
47880
+ step
47881
+ });
47882
+ const fold = (run) => {
47883
+ if (!run.length) return;
47884
+ if (live && foldableRun(run)) items.push({
46884
47885
  kind: "fold",
46885
- key: `live-fold:${chain[0].key}`,
46886
- steps: fold,
46887
- summary: foldSummary(fold)
46888
- });
46889
- for (const step of open) items.push({
46890
- kind: "open",
46891
- key: step.key,
46892
- step
47886
+ key: `live-fold:${run[0].key}`,
47887
+ steps: run,
47888
+ summary: foldSummary(run)
46893
47889
  });
46894
- chain = [];
47890
+ else for (const step of run) open(step);
47891
+ };
47892
+ let run = [];
47893
+ const flush = () => {
47894
+ fold(run);
47895
+ run = [];
46895
47896
  };
47897
+ const segments = [];
47898
+ let segment = [];
46896
47899
  for (const step of steps) if (step.kind === "user") {
47900
+ segments.push(segment);
47901
+ segments.push([step]);
47902
+ segment = [];
47903
+ } else segment.push(step);
47904
+ segments.push(segment);
47905
+ for (const part of segments) {
47906
+ if (!part.length) continue;
47907
+ const first = part[0];
47908
+ if (part.length === 1 && first.kind === "user") {
47909
+ items.push({
47910
+ kind: "user",
47911
+ key: first.key,
47912
+ step: first
47913
+ });
47914
+ continue;
47915
+ }
47916
+ if (!live) {
47917
+ for (const step of part) open(step);
47918
+ continue;
47919
+ }
47920
+ const lastReasoning = part.findLastIndex((step) => step.kind === "reasoning");
47921
+ if (lastReasoning <= 0) {
47922
+ for (const step of part) open(step);
47923
+ continue;
47924
+ }
47925
+ for (let index = 0; index < part.length; index++) {
47926
+ const step = part[index];
47927
+ if (index < lastReasoning) {
47928
+ if (step.kind === "body") {
47929
+ flush();
47930
+ open(step);
47931
+ } else run.push(step);
47932
+ } else {
47933
+ if (index === lastReasoning) flush();
47934
+ open(step);
47935
+ }
47936
+ }
46897
47937
  flush();
46898
- items.push({
46899
- kind: "user",
46900
- key: step.key,
46901
- step
46902
- });
46903
- } else chain.push(step);
46904
- flush();
47938
+ }
46905
47939
  return items;
46906
47940
  }
46907
- //#endregion
46908
- //#region src/client/fold-intensity.ts
47941
+ /**
47942
+ * Summaries for collapsed process that sits between two narration blocks.
47943
+ *
47944
+ * A live-fold cell already shows that summary while the turn is open. Drawing
47945
+ * the seam as well repeats the same counts, so a run whose every step is
47946
+ * already inside a fold cell contributes neither a seam nor `after`. `after`
47947
+ * is what marks the previous body with `data-reader-seam-after` and drops its
47948
+ * bottom padding; skipping the seam must leave that spacing alone.
47949
+ */
47950
+ function planProcessSeams(steps, liveItems, expanded) {
47951
+ const before = /* @__PURE__ */ new Map();
47952
+ const after = /* @__PURE__ */ new Set();
47953
+ if (expanded) return {
47954
+ before,
47955
+ after
47956
+ };
47957
+ const liveFolded = new Set(liveItems.flatMap((item) => item.kind === "fold" ? item.steps.map((step) => step.key) : []));
47958
+ let previousBody;
47959
+ let process = [];
47960
+ for (const step of steps) if (step.kind === "body") {
47961
+ const folded = process.filter((item) => item.kind !== "user");
47962
+ if (!(folded.length > 0 && folded.every((item) => liveFolded.has(item.key))) && previousBody && folded.some((item) => item.kind === "reasoning" || item.kind === "tool" || item.kind === "other")) {
47963
+ before.set(step.key, foldSummary(folded));
47964
+ after.add(previousBody.key);
47965
+ }
47966
+ previousBody = step;
47967
+ process = [];
47968
+ } else if (step.kind === "user") {
47969
+ previousBody = void 0;
47970
+ process = [];
47971
+ } else process.push(step);
47972
+ return {
47973
+ before,
47974
+ after
47975
+ };
47976
+ }
47977
+ // src/client/fold-intensity.ts
46909
47978
  function frostedGlassOf(state) {
46910
47979
  if (state && typeof state === "object" && "frostedGlass" in state) return state.frostedGlass === true;
46911
47980
  return state === true;
@@ -46916,138 +47985,7 @@ ${trimmed}
46916
47985
  function processOnlyFromIntensity(intensity) {
46917
47986
  return intensity === 2;
46918
47987
  }
46919
- //#endregion
46920
- //#region src/client/fold-choreography.ts
46921
- /** Presentation clock, not model/network time. */
46922
- const FOLD_TIMING = {
46923
- collapse: 320,
46924
- count: 160,
46925
- settle: 80,
46926
- reveal: 180,
46927
- /**
46928
- * Slack added to every phase deadline before the watchdog forces the next phase.
46929
- *
46930
- * The choreography advances on animation promises and rendered frames. Both are
46931
- * best-effort: a cancelled animation *rejects* and a compositor that never
46932
- * finishes one leaves its promise pending, while a backgrounded tab stops
46933
- * delivering frames. Without a deadline the machine would sit in a non-idle
46934
- * phase forever, and a non-idle phase freezes both the frame source and the text
46935
- * reveal, so the turn would look stuck until a remount. This is the liveness
46936
- * guarantee that does not depend on any of them.
46937
- */
46938
- watchdogSlack: 240
46939
- };
46940
- /** A step always has the same parent and React key, even when its membership changes. */
46941
- function flowRows(items) {
46942
- return items.flatMap((item) => item.kind === "fold" ? [{
46943
- kind: "summary",
46944
- key: item.key,
46945
- item
46946
- }, ...item.steps.map((step) => ({
46947
- kind: "step",
46948
- key: step.key,
46949
- step,
46950
- foldKey: item.key
46951
- }))] : [{
46952
- kind: "step",
46953
- key: item.key,
46954
- step: item.step
46955
- }]);
46956
- }
46957
- function retiringKeys(before, after, open) {
46958
- const folded = new Set(flowRows(after).filter((row) => row.kind === "step" && row.foldKey && !open[row.foldKey]).map((row) => row.key));
46959
- return flowRows(before).filter((row) => row.kind === "step" && !row.foldKey && folded.has(row.key)).map((row) => row.key);
46960
- }
46961
- /** Insert only new summary slots. Never admit incoming content during shrink. */
46962
- function collapseRows(before, target) {
46963
- const rows = flowRows(before);
46964
- const headers = new Set(rows.filter((row) => row.kind === "summary").map((row) => row.key));
46965
- for (const item of target) {
46966
- if (item.kind !== "fold" || headers.has(item.key)) continue;
46967
- const members = new Set(item.steps.map((step) => step.key));
46968
- const at = rows.findIndex((row) => members.has(row.key));
46969
- if (at >= 0) rows.splice(at, 0, {
46970
- kind: "summary",
46971
- key: item.key,
46972
- item: {
46973
- ...item,
46974
- summary: item.summary.replace(/\d+/g, "0")
46975
- }
46976
- });
46977
- }
46978
- return rows;
46979
- }
46980
- function containsNewUser(before, after) {
46981
- const users = new Set(before.filter((item) => item.kind === "user").map((item) => item.key));
46982
- return after.some((item) => item.kind === "user" && !users.has(item.key));
46983
- }
46984
- //#endregion
46985
- //#region src/client/LiveFold.tsx
46986
- /** Values are presentation commits, never live network counters. */
46987
- function SubtleNumberRoll({ value, motion }) {
46988
- const [outgoing, setOutgoing] = (0, react.useState)(null);
46989
- const previous = (0, react.useRef)(value);
46990
- (0, react.useLayoutEffect)(() => {
46991
- if (value === previous.current) return;
46992
- const old = previous.current;
46993
- previous.current = value;
46994
- if (!motion) {
46995
- setOutgoing(null);
46996
- return;
46997
- }
46998
- setOutgoing(old);
46999
- const timer = window.setTimeout(() => setOutgoing(null), FOLD_TIMING.count);
47000
- return () => window.clearTimeout(timer);
47001
- }, [value, motion]);
47002
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
47003
- className: Reader_module_css_default.numberRollRoot,
47004
- "aria-hidden": "true",
47005
- children: [
47006
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47007
- className: Reader_module_css_default.numberRollSizer,
47008
- children: value
47009
- }),
47010
- outgoing !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47011
- className: `${Reader_module_css_default.numberRollDigit} ${Reader_module_css_default.numberRollExit}`,
47012
- children: outgoing
47013
- }, `out-${outgoing}`),
47014
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47015
- className: `${Reader_module_css_default.numberRollDigit} ${outgoing !== null ? Reader_module_css_default.numberRollEnter : ""}`,
47016
- children: value
47017
- }, `cur-${value}`)
47018
- ]
47019
- });
47020
- }
47021
- function FoldSummaryText({ summary, motion }) {
47022
- const parts = (0, react.useMemo)(() => {
47023
- const regex = /([^\d]+)(\d+)/g;
47024
- const result = [];
47025
- let end = 0;
47026
- for (const match of summary.matchAll(regex)) {
47027
- result.push({
47028
- text: match[1],
47029
- number: Number(match[2])
47030
- });
47031
- end = match.index + match[0].length;
47032
- }
47033
- if (end < summary.length) result.push({ text: summary.slice(end) });
47034
- return result;
47035
- }, [summary]);
47036
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47037
- className: Reader_module_css_default.foldSummary,
47038
- "data-reader-fold-summary": summary,
47039
- "aria-label": summary,
47040
- children: parts.map((part) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
47041
- className: Reader_module_css_default.foldSummaryPart,
47042
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: part.text }), part.number !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubtleNumberRoll, {
47043
- value: part.number,
47044
- motion
47045
- })]
47046
- }, part.text))
47047
- });
47048
- }
47049
- //#endregion
47050
- //#region src/client/DiffPanel.tsx
47988
+ // src/client/DiffPanel.tsx
47051
47989
  /**
47052
47990
  * Reveals and withdraws its child by animating real height, in the plugin's own
47053
47991
  * motion language. Height is the animated property rather than transform because
@@ -47214,8 +48152,123 @@ ${trimmed}
47214
48152
  })
47215
48153
  })] });
47216
48154
  }
47217
- //#endregion
47218
- //#region src/client/ChoreographedFlow.tsx
48155
+ // src/client/StickyLane.tsx
48156
+ const MIN_LANE_PX = 40;
48157
+ const LaneOwnerContext = (0, react.createContext)(null);
48158
+ const ownersByRoot = /* @__PURE__ */ new WeakMap();
48159
+ /** One map per Reader, so two roots that both contain turn 1 do not share a writer. */
48160
+ function ReaderLaneScope({ children }) {
48161
+ const owners = (0, react.useRef)(/* @__PURE__ */ new Map());
48162
+ (0, react.useLayoutEffect)(() => {
48163
+ const map = owners.current;
48164
+ return () => {
48165
+ map.clear();
48166
+ };
48167
+ }, []);
48168
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LaneOwnerContext.Provider, {
48169
+ value: owners.current,
48170
+ children
48171
+ });
48172
+ }
48173
+ function laneScope(el) {
48174
+ return el.closest("[data-reader-turn]") ?? el.closest("[data-dsh-better-display]");
48175
+ }
48176
+ function ownersFor(el, scoped) {
48177
+ if (scoped) return scoped;
48178
+ const root = el.closest("[data-dsh-better-display]") ?? el;
48179
+ let owners = ownersByRoot.get(root);
48180
+ if (!owners) {
48181
+ owners = /* @__PURE__ */ new Map();
48182
+ ownersByRoot.set(root, owners);
48183
+ }
48184
+ return owners;
48185
+ }
48186
+ function publishedLanePx(el) {
48187
+ return Math.max(MIN_LANE_PX, Math.ceil(el.getBoundingClientRect().height));
48188
+ }
48189
+ /** One writer per turn inside a single Reader. A retiring summary cannot clear the lane that replaced it. */
48190
+ function publishLaneHeight(el, property, scoped = null) {
48191
+ const scope = laneScope(el);
48192
+ if (!scope) return () => {};
48193
+ const owners = ownersFor(el, scoped);
48194
+ const root = el.closest("[data-dsh-better-display]");
48195
+ const key = `${property}\0${scope.getAttribute("data-reader-turn") ?? "root"}`;
48196
+ const update = () => {
48197
+ if (owners.get(key) !== el) return;
48198
+ scope.style.setProperty(property, `${publishedLanePx(el)}px`);
48199
+ };
48200
+ owners.set(key, el);
48201
+ update();
48202
+ const observer = new ResizeObserver(update);
48203
+ observer.observe(el);
48204
+ return () => {
48205
+ observer.disconnect();
48206
+ if (owners.get(key) !== el) return;
48207
+ owners.delete(key);
48208
+ scope.style.removeProperty(property);
48209
+ if (!scoped && root && owners.size === 0) ownersByRoot.delete(root);
48210
+ };
48211
+ }
48212
+ function usePublishedHeight(property) {
48213
+ const owners = (0, react.useContext)(LaneOwnerContext);
48214
+ const ref = (0, react.useRef)(null);
48215
+ (0, react.useLayoutEffect)(() => {
48216
+ const el = ref.current;
48217
+ if (!el) return;
48218
+ return publishLaneHeight(el, property, owners);
48219
+ }, [owners, property]);
48220
+ return ref;
48221
+ }
48222
+ function useSummaryLane(ref, active) {
48223
+ const owners = (0, react.useContext)(LaneOwnerContext);
48224
+ (0, react.useLayoutEffect)(() => {
48225
+ const el = ref.current;
48226
+ if (!active || !el) return;
48227
+ return publishLaneHeight(el, "--reader-summary-height", owners);
48228
+ }, [
48229
+ active,
48230
+ owners,
48231
+ ref
48232
+ ]);
48233
+ }
48234
+ /** Publish real lane geometry; wrapped labels must not collide with statistics. */
48235
+ function StickyLane({ kind, className, children }) {
48236
+ const ref = (0, react.useRef)(null);
48237
+ (0, react.useLayoutEffect)(() => {
48238
+ const el = ref.current;
48239
+ const scope = el?.closest(kind === "toolbar" ? "[data-dsh-better-display]" : "[data-reader-turn]");
48240
+ if (!el || !scope) return;
48241
+ const property = kind === "toolbar" ? "--reader-toolbar-height" : "--reader-status-height";
48242
+ const update = () => {
48243
+ scope.style.setProperty(property, `${Math.max(MIN_LANE_PX, el.getBoundingClientRect().height)}px`);
48244
+ if (kind !== "toolbar") return;
48245
+ const controls = el.querySelectorAll("button");
48246
+ let width = 80;
48247
+ if (controls.length > 0) {
48248
+ const first = controls[0].getBoundingClientRect();
48249
+ width = controls[controls.length - 1].getBoundingClientRect().right - first.left;
48250
+ }
48251
+ scope.style.setProperty("--reader-control-width", `${width + 28}px`);
48252
+ };
48253
+ update();
48254
+ const observer = new ResizeObserver(update);
48255
+ observer.observe(el);
48256
+ if (kind === "toolbar") for (const control of el.querySelectorAll("button")) observer.observe(control);
48257
+ return () => {
48258
+ observer.disconnect();
48259
+ scope.style.removeProperty(property);
48260
+ if (kind === "toolbar") scope.style.removeProperty("--reader-control-width");
48261
+ };
48262
+ }, [kind]);
48263
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
48264
+ ref,
48265
+ className,
48266
+ "data-reader-lane": kind,
48267
+ "data-ud-check": kind === "toolbar" ? "reader-toolbar" : void 0,
48268
+ children
48269
+ });
48270
+ }
48271
+ // src/client/ChoreographedFlow.tsx
47219
48272
  const FlowSnapshot = (0, react.createContext)(null);
47220
48273
  /** Public projection only. Descendants cannot leak live data through a frozen frame. */
47221
48274
  const useFlowChat = (selector) => {
@@ -47257,6 +48310,7 @@ ${trimmed}
47257
48310
  /** Manual disclosure shares the measured-size path; children survive until finish. */
47258
48311
  function FlowCell({ hidden, instant, motion, rowKey, children, summary = false }) {
47259
48312
  const ref = (0, react.useRef)(null);
48313
+ useSummaryLane(ref, summary);
47260
48314
  const [present, setPresent] = (0, react.useState)(!hidden);
47261
48315
  const previous = (0, react.useRef)(hidden);
47262
48316
  if (!hidden && !present) setPresent(true);
@@ -47509,14 +48563,15 @@ ${trimmed}
47509
48563
  })
47510
48564
  });
47511
48565
  }
47512
- //#endregion
47513
- //#region src/client/ClosedProcessSummary.tsx
48566
+ // src/client/ClosedProcessSummary.tsx
47514
48567
  /** Keep process counts discoverable after the live-fold presentation retires. */
47515
48568
  function ClosedProcessSummary({ steps, open, onChange, controls }) {
47516
48569
  const button = (0, react.useRef)(null);
48570
+ const lane = usePublishedHeight("--reader-summary-height");
47517
48571
  if (!steps.length) return null;
47518
48572
  const summary = foldSummary(steps);
47519
48573
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
48574
+ ref: lane,
47520
48575
  className: Reader_module_css_default.closedProcessSummary,
47521
48576
  "data-reader-closed-summary": summary,
47522
48577
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -47538,47 +48593,7 @@ ${trimmed}
47538
48593
  })
47539
48594
  });
47540
48595
  }
47541
- //#endregion
47542
- //#region src/client/StickyLane.tsx
47543
- /** Publish real lane geometry; wrapped labels must not collide with statistics. */
47544
- function StickyLane({ kind, className, children }) {
47545
- const ref = (0, react.useRef)(null);
47546
- (0, react.useLayoutEffect)(() => {
47547
- const el = ref.current;
47548
- const scope = el?.closest(kind === "toolbar" ? "[data-dsh-better-display]" : "[data-reader-turn]");
47549
- if (!el || !scope) return;
47550
- const property = kind === "toolbar" ? "--reader-toolbar-height" : "--reader-status-height";
47551
- const update = () => {
47552
- scope.style.setProperty(property, `${el.getBoundingClientRect().height}px`);
47553
- if (kind !== "toolbar") return;
47554
- const controls = el.querySelectorAll("button");
47555
- let width = 80;
47556
- if (controls.length > 0) {
47557
- const first = controls[0].getBoundingClientRect();
47558
- width = controls[controls.length - 1].getBoundingClientRect().right - first.left;
47559
- }
47560
- scope.style.setProperty("--reader-control-width", `${width + 28}px`);
47561
- };
47562
- update();
47563
- const observer = new ResizeObserver(update);
47564
- observer.observe(el);
47565
- if (kind === "toolbar") for (const control of el.querySelectorAll("button")) observer.observe(control);
47566
- return () => {
47567
- observer.disconnect();
47568
- scope.style.removeProperty(property);
47569
- if (kind === "toolbar") scope.style.removeProperty("--reader-control-width");
47570
- };
47571
- }, [kind]);
47572
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47573
- ref,
47574
- className,
47575
- "data-reader-lane": kind,
47576
- "data-ud-check": kind === "toolbar" ? "reader-toolbar" : void 0,
47577
- children
47578
- });
47579
- }
47580
- //#endregion
47581
- //#region src/client/Reader.tsx
48596
+ // src/client/Reader.tsx
47582
48597
  function isNode(node, kind) {
47583
48598
  return node.kind === kind;
47584
48599
  }
@@ -47597,6 +48612,9 @@ ${trimmed}
47597
48612
  return typeof block === "object" && block !== null && block.type === "text" && typeof block.text === "string";
47598
48613
  }).map((block) => block.text).join("");
47599
48614
  }
48615
+ function inboxAttachmentCount(item) {
48616
+ return (item.content ?? []).filter((block) => typeof block === "object" && block !== null && block.type !== "text").length;
48617
+ }
47600
48618
  function cleanErrorMessage(raw) {
47601
48619
  if (!raw) return "模型服务暂时无响应或连接中断,请稍后重试。";
47602
48620
  let str = raw.trim();
@@ -47749,101 +48767,7 @@ ${trimmed}
47749
48767
  children: content
47750
48768
  });
47751
48769
  });
47752
- const AssistantNode = (0, react.memo)(function AssistantNode({ useChat, nodeKey, boundary, processOpen = false, pinned = false, folded = false, partStart, motion, onRead, returnFocusTo, ...render }) {
47753
- const node = useChat((snapshot) => snapshot.nodes.get(nodeKey));
47754
- if (!node || node.visibility === "hidden" || !isNode(node, "assistant-step")) return null;
47755
- const data = node.data;
47756
- const parts = assistantSegments(data.blocks);
47757
- const earlier = isEarlierNarration(data, boundary);
47758
- const hasToolCalls = data.blocks.some((block) => block.kind === "tool-call");
47759
- const isProcessStep = earlier || folded || hasToolCalls || boundary.latestStep > 0 && data.step < boundary.latestStep;
47760
- const body = data.blocks.filter((block) => block.kind !== "reasoning" && block.kind !== "tool-call");
47761
- const visible = partStart === void 0 ? parts : parts.filter((part) => part.start === partStart);
47762
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: visible.map((part) => {
47763
- const last = parts.findIndex((item) => item.start === part.start) === parts.length - 1;
47764
- return part.kind === "reasoning" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProcessFragment, {
47765
- open: processOpen,
47766
- motion,
47767
- onRead,
47768
- returnFocusTo,
47769
- nodeKey,
47770
- framed: true,
47771
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningCard, {
47772
- step: data.step,
47773
- active: processOpen && boundary.status === "open" && data.step === boundary.latestStep,
47774
- motion,
47775
- selected: pinned,
47776
- onRead,
47777
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Blocks, {
47778
- ...render,
47779
- blocks: part.blocks,
47780
- streaming: data.status === "running" && last && data.blocks.at(-1)?.kind === "reasoning",
47781
- holdFormatting: pinned,
47782
- startedAt: data.time,
47783
- interrupted: data.status === "interrupted",
47784
- liveText: true
47785
- })
47786
- })
47787
- }, part.start) : isProcessStep ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProcessFragment, {
47788
- open: processOpen,
47789
- motion,
47790
- onRead,
47791
- returnFocusTo,
47792
- nodeKey,
47793
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("article", {
47794
- className: Reader_module_css_default.processCommentary,
47795
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Blocks, {
47796
- ...render,
47797
- blocks: part.blocks,
47798
- streaming: data.status === "running",
47799
- holdFormatting: pinned,
47800
- startedAt: data.time,
47801
- interrupted: data.status === "interrupted",
47802
- liveText: true
47803
- })
47804
- })
47805
- }, part.start) : hasVisibleBody(part.blocks) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RetiringContent, {
47806
- visible: pinned || processOpen || !earlier && !folded,
47807
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
47808
- className: Reader_module_css_default.answer,
47809
- "data-reader-answer": true,
47810
- "data-reader-anchor": true,
47811
- "data-reader-key": nodeKey,
47812
- "data-reader-source-start": part.start,
47813
- "data-answer-status": data.status,
47814
- "data-answer-phase": earlier || folded ? "process" : "body",
47815
- children: [
47816
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Blocks, {
47817
- ...render,
47818
- blocks: part.blocks,
47819
- streaming: data.status === "running",
47820
- holdFormatting: pinned,
47821
- startedAt: data.time,
47822
- interrupted: data.status === "interrupted",
47823
- liveText: true
47824
- }),
47825
- last && data.status === "interrupted" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47826
- className: Reader_module_css_default.stopped,
47827
- children: "已停止"
47828
- }),
47829
- last && !earlier && !folded && data.status !== "running" && boundary.status === "closed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyAnswer, {
47830
- blocks: body,
47831
- onFork: (() => {
47832
- const anchor = forkAnchorSeq([data.finalNode, { seq: render.forkSeq }]);
47833
- return render.forkAt && anchor !== void 0 ? () => render.forkAt(anchor) : void 0;
47834
- })(),
47835
- metrics: render.metrics,
47836
- extraActions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OfficialActions, {
47837
- official: render.official,
47838
- messageId: data.finalNode?.messageId
47839
- })
47840
- })
47841
- ]
47842
- })
47843
- }, part.start);
47844
- }) });
47845
- });
47846
- const MainNode = (0, react.memo)(function MainNode({ useChat, nodeKey, boundary, pinned, processOpen = false, ...render }) {
48770
+ const MainNode = (0, react.memo)(function MainNode({ useChat, nodeKey, boundary, pinned, processOpen: _processOpen = false, ...render }) {
47847
48771
  const node = useChat((snapshot) => snapshot.nodes.get(nodeKey));
47848
48772
  const [disclosureReset] = (0, react.useState)(() => (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(0));
47849
48773
  if (!node || node.visibility === "hidden") return null;
@@ -47886,21 +48810,17 @@ ${trimmed}
47886
48810
  const owner = {
47887
48811
  cwd: render.cwd,
47888
48812
  openFile: render.openFile ?? (() => {}),
47889
- openSkill: () => {},
47890
48813
  forkAt: render.forkAt ?? (() => {}),
47891
48814
  inspectCall: (callId) => official.openView("trajectory", callId),
47892
48815
  loadImage: official.officialImageLoader,
47893
- renderMessageImages: (images) => official.renderSlot(OFFICIAL_SEATS.images, {
47894
- ...images,
47895
- loadImage: official.officialImageLoader
47896
- }),
47897
48816
  fileMentions: official.officialFileMentions,
47898
48817
  sessionId: render.sessionId
47899
48818
  };
47900
48819
  const options = {
47901
48820
  hookContext: {
47902
48821
  turnData: turn?.data,
47903
- disclosureReset
48822
+ disclosureReset,
48823
+ useGroupAction: readerGroupAction
47904
48824
  },
47905
48825
  fallback
47906
48826
  };
@@ -48059,7 +48979,7 @@ ${trimmed}
48059
48979
  }
48060
48980
  return 0;
48061
48981
  }
48062
- function GroupStatus({ group, sessionId, useChat, pending, motion }) {
48982
+ function GroupStatus({ group, useChat, pending, motion }) {
48063
48983
  const text = useChat((snapshot) => {
48064
48984
  const turn = group.turn === null ? void 0 : snapshot.timeline.turns.get(group.turn);
48065
48985
  if (turn?.status === "closed") {
@@ -48310,11 +49230,12 @@ ${trimmed}
48310
49230
  const flow = (0, react.useMemo)(() => readerFlow({
48311
49231
  ...group,
48312
49232
  keys: mainKeys
48313
- }, turn, (key) => nodes.get(key)), [
49233
+ }, turn, (key) => nodes.get(key), snapshot.order), [
48314
49234
  nodes,
48315
49235
  group,
48316
49236
  mainKeys,
48317
- turn
49237
+ turn,
49238
+ snapshot.order
48318
49239
  ]);
48319
49240
  const steps = (0, react.useMemo)(() => segmentLiveTurn(flow, (key) => nodes.get(key)), [flow, nodes]);
48320
49241
  const liveItems = (0, react.useMemo)(() => presentLiveTurn(steps, boundary, autoFold), [
@@ -48330,6 +49251,11 @@ ${trimmed}
48330
49251
  const showClosedSummary = boundary.status === "closed" && hasProcess && autoFold;
48331
49252
  const holdingSelection = selectedProcessKeys.some((key) => flow.some((item) => item.key === key) || liveItems.some((item) => item.kind === "fold" ? item.key === key || item.steps.some((step) => step.key === key || "nodeKey" in step && step.nodeKey === key) : item.key === key || "nodeKey" in item.step && item.step.nodeKey === key));
48332
49253
  const expanded = !autoFold || holdingSelection || processExpanded(expansionChoice, boundary);
49254
+ const seamPlan = (0, react.useMemo)(() => planProcessSeams(steps, liveItems, expanded), [
49255
+ steps,
49256
+ liveItems,
49257
+ expanded
49258
+ ]);
48333
49259
  const [foldOpenByKey, setFoldOpenByKey] = (0, react.useState)({});
48334
49260
  (0, react.useLayoutEffect)(() => {
48335
49261
  if (autoFold) setFoldOpenByKey((current) => Object.keys(current).length ? {} : current);
@@ -48448,7 +49374,9 @@ ${trimmed}
48448
49374
  folded,
48449
49375
  motion,
48450
49376
  onRead: pinProcess,
48451
- returnFocusTo: processButton
49377
+ returnFocusTo: processButton,
49378
+ seamSummary: step.kind === "body" ? seamPlan.before.get(step.key) : void 0,
49379
+ seamAfter: step.kind === "body" && seamPlan.after.has(step.key)
48452
49380
  }) });
48453
49381
  if (step.kind === "tool") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockBoundary, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ProcessFragment, {
48454
49382
  open: processOpen,
@@ -48516,7 +49444,6 @@ ${trimmed}
48516
49444
  buttonRef: processButton,
48517
49445
  label: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupStatus, {
48518
49446
  group,
48519
- sessionId: props.sessionId,
48520
49447
  useChat: props.useChat,
48521
49448
  pending: interaction,
48522
49449
  motion
@@ -48526,12 +49453,7 @@ ${trimmed}
48526
49453
  open: expanded,
48527
49454
  onChange: setExpanded,
48528
49455
  controls: flowId,
48529
- steps: steps.filter((step) => {
48530
- if (step.kind === "user") return false;
48531
- if (step.kind !== "body") return true;
48532
- const node = nodes.get(step.nodeKey);
48533
- return !!node && isNode(node, "assistant-step") && (isEarlierNarration(node.data, boundary) || node.data.blocks.some((block) => block.kind === "tool-call") || boundary.latestStep > 0 && node.data.step < boundary.latestStep);
48534
- })
49456
+ steps: steps.filter((step) => step.kind !== "user" && step.kind !== "body")
48535
49457
  })]
48536
49458
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChoreographedFlow, {
48537
49459
  id: flowId,
@@ -48556,7 +49478,6 @@ ${trimmed}
48556
49478
  "data-reader-status-only": true,
48557
49479
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupStatus, {
48558
49480
  group,
48559
- sessionId: props.sessionId,
48560
49481
  useChat: props.useChat,
48561
49482
  pending: interaction,
48562
49483
  motion
@@ -48589,6 +49510,15 @@ ${trimmed}
48589
49510
  const activatedAt = (0, react.useRef)(Date.now());
48590
49511
  const order = props.useChat((snapshot) => snapshot.order);
48591
49512
  const nodes = props.useChat((snapshot) => snapshot.nodes);
49513
+ const rewindSignature = props.useChat((snapshot) => visibleChatSignature(snapshot));
49514
+ const renderKeys = (0, react.useMemo)(() => visibleChatKeys({
49515
+ order,
49516
+ nodes
49517
+ }), [
49518
+ order,
49519
+ nodes,
49520
+ rewindSignature
49521
+ ]);
48592
49522
  const timeline = props.useChat((snapshot) => snapshot.timeline);
48593
49523
  const running = props.useSession((snapshot) => snapshot.running);
48594
49524
  const pending = props.useSessionStatus((snapshot) => snapshot.get(props.sessionId)?.pendingInteraction);
@@ -48624,7 +49554,7 @@ ${trimmed}
48624
49554
  pendingList,
48625
49555
  visibleSubmissions
48626
49556
  ]);
48627
- const waitAnchor = waitingAnchor(order, (key) => nodes.get(key), pendingList);
49557
+ const waitAnchor = waitingAnchor(renderKeys, (key) => nodes.get(key), pendingList);
48628
49558
  const motion = useMotionAllowed(props.useStore((state) => state.motion));
48629
49559
  const prefsSnap = (0, react.useSyncExternalStore)(props.openPrefs?.subscribe ?? ((fn) => {
48630
49560
  return () => {};
@@ -48642,14 +49572,14 @@ ${trimmed}
48642
49572
  enabled: motion,
48643
49573
  activatedAt: activatedAt.current
48644
49574
  }), [motion]);
48645
- const groups = (0, react.useMemo)(() => groupNodes(order, (key) => nodes.get(key)), [
48646
- order,
49575
+ const groups = (0, react.useMemo)(() => groupNodes(renderKeys, (key) => nodes.get(key)), [
49576
+ renderKeys,
48647
49577
  nodes,
48648
49578
  timeline
48649
49579
  ]);
48650
49580
  const isAwaitingModel = (0, react.useMemo)(() => {
48651
49581
  if (!running && pendingList.length > 0) return true;
48652
- const lastKey = order.at(-1);
49582
+ const lastKey = renderKeys.at(-1);
48653
49583
  const lastNode = lastKey ? nodes.get(lastKey) : void 0;
48654
49584
  if (!lastKey || !lastNode) return false;
48655
49585
  if (lastNode.kind === "assistant-step") {
@@ -48662,7 +49592,7 @@ ${trimmed}
48662
49592
  }, [
48663
49593
  running,
48664
49594
  pendingList,
48665
- order,
49595
+ renderKeys,
48666
49596
  nodes,
48667
49597
  groups,
48668
49598
  timeline
@@ -48752,7 +49682,7 @@ ${trimmed}
48752
49682
  scroll.release,
48753
49683
  timelineItems
48754
49684
  ]);
48755
- const lastKey = order.at(-1);
49685
+ const lastKey = renderKeys.at(-1);
48756
49686
  const lastNode = lastKey ? nodes.get(lastKey) : void 0;
48757
49687
  const lastSubmissionId = pendingList.length ? pendingList[pendingList.length - 1].requestId : null;
48758
49688
  const lastOrderKeyRef = (0, react.useRef)(lastKey);
@@ -48771,7 +49701,7 @@ ${trimmed}
48771
49701
  ]);
48772
49702
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StreamMotionContext.Provider, {
48773
49703
  value: streamMotion,
48774
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
49704
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReaderLaneScope, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48775
49705
  ref: root,
48776
49706
  className: Reader_module_css_default.root,
48777
49707
  "data-reader-build": "0.3.4",
@@ -48789,6 +49719,7 @@ ${trimmed}
48789
49719
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48790
49720
  className: Reader_module_css_default.column,
48791
49721
  "data-chat-flow": "",
49722
+ tabIndex: -1,
48792
49723
  children: [
48793
49724
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StickyLane, {
48794
49725
  kind: "toolbar",
@@ -48833,20 +49764,23 @@ ${trimmed}
48833
49764
  role: "status",
48834
49765
  children: "正在读取会话…"
48835
49766
  }),
48836
- groups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TurnGroup, {
48837
- ...props,
48838
- group,
48839
- motion,
48840
- autoFold,
48841
- pinnedKeys,
48842
- selectedProcessKeys,
48843
- isAwaitingModel: isAwaitingModel && group.key === groups.at(-1)?.key
48844
- }, group.key)),
49767
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RetiringTurns, {
49768
+ groups,
49769
+ render: (group) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TurnGroup, {
49770
+ ...props,
49771
+ group,
49772
+ motion,
49773
+ autoFold,
49774
+ pinnedKeys,
49775
+ selectedProcessKeys,
49776
+ isAwaitingModel: isAwaitingModel && group.key === groups.at(-1)?.key
49777
+ })
49778
+ }),
48845
49779
  pendingInputs.map((item, index) => {
48846
49780
  const submission = "requestId" in item ? item : void 0;
48847
49781
  const text = submission?.text ?? inboxText(item);
48848
49782
  const images = submission ? pendingSubmissionImages(submission) : [];
48849
- const attachmentCount = images.length;
49783
+ const attachmentCount = submission ? images.length : inboxAttachmentCount(item);
48850
49784
  const requestId = submission?.requestId ?? `inbox-${index}`;
48851
49785
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48852
49786
  className: Reader_module_css_default.userCluster,
@@ -48919,11 +49853,10 @@ ${trimmed}
48919
49853
  })
48920
49854
  ]
48921
49855
  })]
48922
- })
49856
+ }) })
48923
49857
  });
48924
49858
  }
48925
- //#endregion
48926
- //#region src/client/store.ts
49859
+ // src/client/store.ts
48927
49860
  function applyFoldIntensity(draft, value) {
48928
49861
  draft.foldIntensity = value;
48929
49862
  draft.autoFold = autoFoldFromIntensity(value);
@@ -48966,8 +49899,7 @@ ${trimmed}
48966
49899
  }
48967
49900
  });
48968
49901
  }
48969
- //#endregion
48970
- //#region src/client/entry-policy.ts
49902
+ // src/client/entry-policy.ts
48971
49903
  /** Select the reading view on entry, without fighting a later explicit tab choice. */
48972
49904
  var ReaderEntryPolicy = class {
48973
49905
  requested;
@@ -48990,8 +49922,7 @@ ${trimmed}
48990
49922
  const value = new URLSearchParams(search).get("reader");
48991
49923
  return value === "1" || /^0\.1\.0-trial\.\d+$/.test(value ?? "");
48992
49924
  }
48993
- //#endregion
48994
- //#region src/client/entry.tsx
49925
+ // src/client/entry.tsx
48995
49926
  function isConversationStore(store) {
48996
49927
  return typeof store === "object" && store !== null && store.spec.persist === "dsh.conversation" && typeof store.spec.actions.setView === "function";
48997
49928
  }
@@ -49023,8 +49954,7 @@ ${trimmed}
49023
49954
  inject: () => ({ policy })
49024
49955
  }, ReaderEntry);
49025
49956
  }
49026
- //#endregion
49027
- //#region src/client/settings-copy.ts
49957
+ // src/client/settings-copy.ts
49028
49958
  const en = {
49029
49959
  nav: "Better Display",
49030
49960
  openTitle: "Open deliverables in built-in panel",
@@ -49073,8 +50003,7 @@ ${trimmed}
49073
50003
  function settingsCopyFor(tag) {
49074
50004
  return settingsLanguage(tag) === "zh" ? zh : en;
49075
50005
  }
49076
- //#endregion
49077
- //#region src/skill-status.ts
50006
+ // src/skill-status.ts
49078
50007
  /** Shared generative-mcpapps identity. Plugin-tree presence is not installation. */
49079
50008
  const GENERATIVE_MCPAPPS_SKILL = "generative-mcpapps";
49080
50009
  /** Conventional relative roots only. Never expand $HOME or dump host `root.path`. */
@@ -49118,8 +50047,7 @@ ${trimmed}
49118
50047
  roots: publicSkillRoots()
49119
50048
  };
49120
50049
  }
49121
- //#endregion
49122
- //#region src/client/skill-status.ts
50050
+ // src/client/skill-status.ts
49123
50051
  async function detectGenerativeMcpappsSkill(probe) {
49124
50052
  let host;
49125
50053
  if (probe.fetchHostStatus) try {
@@ -49153,8 +50081,7 @@ ${trimmed}
49153
50081
  if (list?.ids && list.ids.length > 0) return list.ids[0];
49154
50082
  return (list?.byId ? Object.keys(list.byId) : [])[0];
49155
50083
  }
49156
- //#endregion
49157
- //#region \0dshx-css-module:SettingsSection.module.css.mjs
50084
+ // \0dshx-css-module:/home/runner/work/dsh-better-display/dsh-better-display/src/client/SettingsSection.module.css.mjs
49158
50085
  const css = ".fiqtMa_section{flex-direction:column;gap:28px;width:100%;max-width:720px;display:flex}.fiqtMa_row{border-bottom:.5px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}.fiqtMa_rowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:24px;display:flex}.fiqtMa_title{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.fiqtMa_desc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.fiqtMa_switch{background:var(--dsw-alias-bg-module-platform);cursor:pointer;border:none;border-radius:12px;flex:none;width:40px;height:24px;padding:0;position:relative}.fiqtMa_switch:after{content:\"\";background:var(--dsw-alias-label-primary);border-radius:50%;width:18px;height:18px;transition:transform .16s;position:absolute;top:3px;left:3px}.fiqtMa_switch[data-on]:after{transform:translate(16px)}.fiqtMa_switch[data-on]{background:var(--dsw-alias-state-business-primary)}.fiqtMa_block{flex-direction:column;gap:10px;display:flex}.fiqtMa_status{color:var(--dsw-alias-label-secondary);align-items:center;gap:8px;font-size:13px;line-height:20px;display:inline-flex}.fiqtMa_badge{background:var(--dsw-alias-bg-module-platform);height:22px;color:var(--dsw-alias-label-secondary);border-radius:11px;align-items:center;padding:0 8px;font-size:12px;display:inline-flex}.fiqtMa_badge[data-ok]{color:#059669;background:#10b98124}.fiqtMa_pre{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;white-space:pre-wrap;border-radius:8px;margin:0;padding:10px 12px;font-size:12px;line-height:18px}.fiqtMa_roots{color:var(--dsw-alias-label-tertiary);margin:0;padding-left:18px;font-size:12px;line-height:18px}.fiqtMa_actions{gap:8px;display:flex}.fiqtMa_button{border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);height:32px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:8px;padding:0 12px;font-size:13px}.fiqtMa_button:hover{background:var(--dsw-alias-interactive-bg-hover)}.fiqtMa_button:disabled{opacity:.6;cursor:default}.fiqtMa_segment{background:var(--dsw-alias-bg-module-platform);border-radius:10px;gap:2px;width:100%;margin-top:8px;padding:2px;display:flex}.fiqtMa_segmentStop{min-width:0;height:32px;color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:8px;flex:1 1 0;padding:0 8px;font-size:12px;line-height:18px}.fiqtMa_segmentStop[data-on]{background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);box-shadow:0 0 0 .5px var(--dsw-alias-border-l2)}.fiqtMa_slider{width:100%;accent-color:var(--dsw-alias-state-business-primary);margin:10px 0 0}";
49159
50086
  const tagId = "dsh-better-display/SettingsSection.module.css";
49160
50087
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
@@ -49182,8 +50109,7 @@ ${trimmed}
49182
50109
  "switch": "fiqtMa_switch",
49183
50110
  "title": "fiqtMa_title"
49184
50111
  };
49185
- //#endregion
49186
- //#region src/client/SettingsSection.tsx
50112
+ // src/client/SettingsSection.tsx
49187
50113
  function text(props, copy, key) {
49188
50114
  if (typeof props.t === "function") try {
49189
50115
  const value = props.t(key);
@@ -49352,8 +50278,7 @@ ${trimmed}
49352
50278
  ]
49353
50279
  });
49354
50280
  }
49355
- //#endregion
49356
- //#region src/client/settings.tsx
50281
+ // src/client/settings.tsx
49357
50282
  function languageTag(ctx) {
49358
50283
  const snap = (ctx.get?.("locale") ?? ctx.locale)?.getSnapshot?.();
49359
50284
  return snap?.locale ?? snap?.language ?? (typeof document !== "undefined" ? document.documentElement.lang : void 0) ?? (typeof navigator !== "undefined" ? navigator.language : void 0);
@@ -49400,8 +50325,7 @@ ${trimmed}
49400
50325
  inject: injected
49401
50326
  }, SettingsSection));
49402
50327
  }
49403
- //#endregion
49404
- //#region src/client/index.tsx
50328
+ // src/client/index.tsx
49405
50329
  const officialFileAddressFor = fileAddressFor$1;
49406
50330
  async function openWorkspacePath(ctx, path) {
49407
50331
  const remoteSession = ctx.remote?.session ?? ctx.get?.("remote.session") ?? (ctx.get?.("remote"))?.session;
@@ -49421,6 +50345,10 @@ ${trimmed}
49421
50345
  ];
49422
50346
  ensureStyles();
49423
50347
  function apply(ctx) {
50348
+ const textTransform = createTextTransformRegistry();
50349
+ const restoreTextTransform = installTextTransformRegistry(textTransform);
50350
+ ctx.provide(TEXT_TRANSFORM_SERVICE, textTransform);
50351
+ ctx.effect(() => restoreTextTransform);
49424
50352
  const store = createReaderStore();
49425
50353
  const prefs = store.create();
49426
50354
  installBetterDisplaySettings(ctx, prefs);
@@ -49573,11 +50501,13 @@ ${trimmed}
49573
50501
  yield installReaderEntry(ctx);
49574
50502
  });
49575
50503
  }
49576
- //#endregion
49577
50504
  exports.McpAppFrame = McpAppFrame;
50505
+ exports.TEXT_TRANSFORM_SERVICE = TEXT_TRANSFORM_SERVICE;
49578
50506
  exports.apply = apply;
50507
+ exports.createTextTransformRegistry = createTextTransformRegistry;
49579
50508
  exports.inject = inject;
49580
50509
  exports.name = name;
50510
+ exports.originalAnswerText = originalAnswerText;
49581
50511
  return module.exports;
49582
50512
  }
49583
50513
  });