dsh-better-display 0.3.8 → 0.3.9

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 (182) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/DESIGN.md +43 -3
  3. package/README.en.md +9 -1
  4. package/README.md +9 -1
  5. package/docs/dependency-update-policy.md +9 -0
  6. package/docs/extension-points.md +172 -0
  7. package/docs/official-rendering-bridge.md +48 -19
  8. package/docs/openapi.yaml +147 -0
  9. package/docs/runbooks/blank-reader.md +39 -0
  10. package/lib/client.js +1417 -490
  11. package/lib/client.js.map +1 -1
  12. package/lib/dsh-better-display.js +378 -144
  13. package/lib/types/client/AssistantNode.d.ts +20 -0
  14. package/lib/types/client/AssistantNode.d.ts.map +1 -0
  15. package/lib/types/client/AssistantNode.js +46 -0
  16. package/lib/types/client/AssistantNode.js.map +1 -0
  17. package/lib/types/client/Blocks.d.ts.map +1 -1
  18. package/lib/types/client/Blocks.js +33 -10
  19. package/lib/types/client/Blocks.js.map +1 -1
  20. package/lib/types/client/ChoreographedFlow.d.ts.map +1 -1
  21. package/lib/types/client/ChoreographedFlow.js +2 -0
  22. package/lib/types/client/ChoreographedFlow.js.map +1 -1
  23. package/lib/types/client/ClosedProcessSummary.d.ts.map +1 -1
  24. package/lib/types/client/ClosedProcessSummary.js +3 -1
  25. package/lib/types/client/ClosedProcessSummary.js.map +1 -1
  26. package/lib/types/client/McpAppFrame.d.ts.map +1 -1
  27. package/lib/types/client/McpAppFrame.js +5 -11
  28. package/lib/types/client/McpAppFrame.js.map +1 -1
  29. package/lib/types/client/OfficialContent.d.ts +17 -3
  30. package/lib/types/client/OfficialContent.d.ts.map +1 -1
  31. package/lib/types/client/OfficialContent.js +33 -7
  32. package/lib/types/client/OfficialContent.js.map +1 -1
  33. package/lib/types/client/Reader.d.ts.map +1 -1
  34. package/lib/types/client/Reader.js +43 -71
  35. package/lib/types/client/Reader.js.map +1 -1
  36. package/lib/types/client/SettingsSection.d.ts.map +1 -1
  37. package/lib/types/client/SettingsSection.js +0 -5
  38. package/lib/types/client/SettingsSection.js.map +1 -1
  39. package/lib/types/client/StickyLane.d.ts +12 -0
  40. package/lib/types/client/StickyLane.d.ts.map +1 -1
  41. package/lib/types/client/StickyLane.js +78 -2
  42. package/lib/types/client/StickyLane.js.map +1 -1
  43. package/lib/types/client/ToolActivity.js.map +1 -1
  44. package/lib/types/client/index.d.ts +3 -1
  45. package/lib/types/client/index.d.ts.map +1 -1
  46. package/lib/types/client/index.js +6 -0
  47. package/lib/types/client/index.js.map +1 -1
  48. package/lib/types/client/live-turn.d.ts +17 -5
  49. package/lib/types/client/live-turn.d.ts.map +1 -1
  50. package/lib/types/client/live-turn.js +106 -76
  51. package/lib/types/client/live-turn.js.map +1 -1
  52. package/lib/types/client/motion.d.ts +26 -2
  53. package/lib/types/client/motion.d.ts.map +1 -1
  54. package/lib/types/client/motion.js +136 -10
  55. package/lib/types/client/motion.js.map +1 -1
  56. package/lib/types/client/official-slots.d.ts +9 -0
  57. package/lib/types/client/official-slots.d.ts.map +1 -1
  58. package/lib/types/client/official-slots.js +22 -7
  59. package/lib/types/client/official-slots.js.map +1 -1
  60. package/lib/types/client/projection.d.ts +15 -2
  61. package/lib/types/client/projection.d.ts.map +1 -1
  62. package/lib/types/client/projection.js +16 -4
  63. package/lib/types/client/projection.js.map +1 -1
  64. package/lib/types/client/rewind-cut.d.ts +131 -0
  65. package/lib/types/client/rewind-cut.d.ts.map +1 -0
  66. package/lib/types/client/rewind-cut.js +321 -0
  67. package/lib/types/client/rewind-cut.js.map +1 -0
  68. package/lib/types/client/text-transform.d.ts +93 -0
  69. package/lib/types/client/text-transform.d.ts.map +1 -0
  70. package/lib/types/client/text-transform.js +183 -0
  71. package/lib/types/client/text-transform.js.map +1 -0
  72. package/lib/types/client/timeline.d.ts.map +1 -1
  73. package/lib/types/client/timeline.js.map +1 -1
  74. package/lib/types/client/tool-activity.d.ts +9 -2
  75. package/lib/types/client/tool-activity.d.ts.map +1 -1
  76. package/lib/types/client/tool-activity.js +42 -2
  77. package/lib/types/client/tool-activity.js.map +1 -1
  78. package/lib/types/client/turn-slots.d.ts +29 -0
  79. package/lib/types/client/turn-slots.d.ts.map +1 -0
  80. package/lib/types/client/turn-slots.js +62 -0
  81. package/lib/types/client/turn-slots.js.map +1 -0
  82. package/lib/types/client/types.d.ts +18 -0
  83. package/lib/types/client/types.d.ts.map +1 -1
  84. package/lib/types/dsh-better-display.d.ts +4 -1
  85. package/lib/types/dsh-better-display.d.ts.map +1 -1
  86. package/lib/types/dsh-better-display.js +56 -7
  87. package/lib/types/dsh-better-display.js.map +1 -1
  88. package/lib/types/flags.d.ts +7 -0
  89. package/lib/types/flags.d.ts.map +1 -0
  90. package/lib/types/flags.js +37 -0
  91. package/lib/types/flags.js.map +1 -0
  92. package/lib/types/log.d.ts +17 -0
  93. package/lib/types/log.d.ts.map +1 -0
  94. package/lib/types/log.js +122 -0
  95. package/lib/types/log.js.map +1 -0
  96. package/lib/types/metrics.d.ts +18 -0
  97. package/lib/types/metrics.d.ts.map +1 -0
  98. package/lib/types/metrics.js +35 -0
  99. package/lib/types/metrics.js.map +1 -0
  100. package/package.json +36 -5
  101. package/scripts/changelog.mjs +20 -0
  102. package/scripts/check-agents-md.mjs +67 -0
  103. package/scripts/check-bundle-size.mjs +38 -0
  104. package/scripts/check-dead-flags.mjs +55 -0
  105. package/scripts/check-todo-debt.mjs +69 -0
  106. package/scripts/security-audit-report.mjs +92 -0
  107. package/scripts/verify-commentary-visible.mjs +627 -0
  108. package/src/client/AssistantNode.tsx +72 -0
  109. package/src/client/Blocks.tsx +49 -10
  110. package/src/client/ChoreographedFlow.tsx +3 -1
  111. package/src/client/ClosedProcessSummary.tsx +3 -1
  112. package/src/client/McpAppFrame.tsx +5 -13
  113. package/src/client/OfficialContent.tsx +47 -9
  114. package/src/client/Reader.module.css +68 -18
  115. package/src/client/Reader.tsx +36 -76
  116. package/src/client/SettingsSection.tsx +1 -7
  117. package/src/client/StickyLane.tsx +81 -2
  118. package/src/client/ToolActivity.tsx +1 -1
  119. package/src/client/index.tsx +8 -1
  120. package/src/client/live-turn.ts +100 -69
  121. package/src/client/motion.tsx +135 -7
  122. package/src/client/official-slots.tsx +23 -7
  123. package/src/client/projection.ts +16 -4
  124. package/src/client/rewind-cut.ts +340 -0
  125. package/src/client/text-transform.ts +265 -0
  126. package/src/client/timeline.ts +0 -2
  127. package/src/client/tool-activity.ts +49 -2
  128. package/src/client/turn-slots.ts +80 -0
  129. package/src/client/types.ts +19 -0
  130. package/src/dsh-better-display.ts +58 -7
  131. package/src/flags.ts +40 -0
  132. package/src/log.ts +137 -0
  133. package/src/metrics.ts +50 -0
  134. package/tests/browser-commentary.tsx +191 -0
  135. package/tests/chat-flow-hook.test.ts +1 -1
  136. package/tests/cloud-gate.test.ts +1 -1
  137. package/tests/context-tool-update.test.ts +1 -1
  138. package/tests/conversation-scroll.test.ts +1 -1
  139. package/tests/deliverables.test.ts +1 -1
  140. package/tests/entry.test.ts +1 -1
  141. package/tests/fixtures/reader-lane-owners.tsx +46 -0
  142. package/tests/fixtures/reader-official-users.tsx +64 -0
  143. package/tests/fixtures/reader-theme-overlap.html +80 -0
  144. package/tests/fixtures/reader-user-bubble.tsx +222 -0
  145. package/tests/flags.test.ts +66 -0
  146. package/tests/fold-choreography.test.ts +32 -8
  147. package/tests/fold-intensity.test.ts +11 -6
  148. package/tests/footer-precedence.test.ts +1 -1
  149. package/tests/history-word-regression.test.ts +1 -1
  150. package/tests/live-turn.test.ts +125 -54
  151. package/tests/log.test.ts +99 -0
  152. package/tests/mcp-apps.test.ts +1 -1
  153. package/tests/message-chrome.test.ts +1 -1
  154. package/tests/metrics.test.ts +52 -0
  155. package/tests/official-slots.test.ts +41 -2
  156. package/tests/open-file.test.ts +1 -1
  157. package/tests/pending-submission.test.ts +12 -1
  158. package/tests/pinned-selection.test.ts +16 -0
  159. package/tests/platform-media.test.ts +1 -1
  160. package/tests/projection.test.ts +66 -3
  161. package/tests/reader-glass-css.test.ts +1 -1
  162. package/tests/reader-official-users.test.ts +11 -1
  163. package/tests/reader-sticky-surface.test.ts +377 -0
  164. package/tests/reader-theme-lanes.test.ts +54 -0
  165. package/tests/reader-theme-overlap.test.ts +151 -0
  166. package/tests/reader-user-bubble.test.ts +137 -0
  167. package/tests/reasoning-follow.test.ts +1 -1
  168. package/tests/retiring-content.test.ts +98 -6
  169. package/tests/reveal.test.ts +3 -3
  170. package/tests/rewind-cut.test.ts +1090 -0
  171. package/tests/settings-copy.test.ts +1 -1
  172. package/tests/skill-status.test.ts +1 -1
  173. package/tests/stock-install.test.ts +1 -1
  174. package/tests/stream-buffer.test.ts +1 -1
  175. package/tests/styles.test.ts +30 -3
  176. package/tests/terminal-input-preview.test.ts +1 -1
  177. package/tests/text-transform.test.ts +272 -0
  178. package/tests/timeline.test.ts +1 -1
  179. package/tests/tool-clock.test.ts +1 -1
  180. package/tests/tool-diff.test.ts +1 -1
  181. package/tests/waiting-clock.test.ts +1 -1
  182. package/tests/word-timeline.test.ts +1 -1
package/lib/client.js CHANGED
@@ -109,12 +109,286 @@ window.__ModuleLoader__.load({
109
109
  return sessionFileAddress(sessionId, normalized);
110
110
  }
111
111
  //#endregion
112
+ //#region src/client/rewind-cut.ts
113
+ function asRecord$1(value) {
114
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
115
+ }
116
+ function safeSeq(value) {
117
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
118
+ }
119
+ /** The command payload of a `command` chat node, when it is structurally readable. */
120
+ function commandOf(node) {
121
+ if (node.kind !== "command") return void 0;
122
+ return asRecord$1(node.data);
123
+ }
124
+ /**
125
+ * The engine names an executed rewind command node can carry. The plugin
126
+ * registers its command as `rewind-plugin` (`ENGINE_COMMANDS = new
127
+ * Set(['rewind-plugin', 'rewind'])` in dsh-rewind-plugin's
128
+ * `src/client/hidden.ts`), and that is the name the client projection actually
129
+ * publishes — the session log's `command/run.name` is not what the Reader sees.
130
+ * `rewind` stays accepted because older logs and the bili-side proxy record the
131
+ * shorter spelling.
132
+ */
133
+ const REWIND_COMMANDS = /* @__PURE__ */ new Set(["rewind-plugin", "rewind"]);
134
+ /** True when a command payload is the rewind engine command, in either spelling. */
135
+ function isRewindCommand(command) {
136
+ return typeof command.name === "string" && REWIND_COMMANDS.has(command.name);
137
+ }
138
+ /**
139
+ * The render POSITION a row is anchored at: the view node's `anchorSeq`, else
140
+ * its payload seq.
141
+ *
142
+ * This is deliberately NOT integer-only. The host anchors synthetic rows at
143
+ * fractional offsets off a real seq (`CHAT_SYNTHETIC_SEQ_OFFSETS` in the
144
+ * official ChatView: `turn-process` at `controlAnchorSeq - 0.1`, `turn-tail` at
145
+ * `seq + 0.1`, an interrupted assistant step at `seq - 0.9`). An integer-only
146
+ * read made those rows invisible to the cut, so the last `turn-process` of a
147
+ * withdrawn turn survived on its own. Log seqs (marker, target) stay integer:
148
+ * see `safeSeq`.
149
+ */
150
+ function anchorSeqOf(node) {
151
+ const raw = node.anchorSeq ?? asRecord$1(node.data)?.seq;
152
+ return typeof raw === "number" && Number.isFinite(raw) && raw >= 0 ? raw : void 0;
153
+ }
154
+ /**
155
+ * Extract the rewind target seq from a `/rewind` command's structured `args`
156
+ * (e.g. `@5 chat`, `preview @5 both`). Locale-independent: it never parses the
157
+ * host's human outcome copy.
158
+ */
159
+ function rewindTargetOfArgs(args) {
160
+ if (typeof args !== "string") return void 0;
161
+ const match = args.match(/@(\d+)/);
162
+ return match === null ? void 0 : Number(match[1]);
163
+ }
164
+ /** True for the internal candidate-list probe (`/rewind __candidates`). */
165
+ function isCandidateCommand(command) {
166
+ return typeof command.args === "string" && command.args.includes("__candidates");
167
+ }
168
+ /** True for an impact preview (`/rewind preview @5 both`). */
169
+ function isPreviewCommand(command) {
170
+ return typeof command.args === "string" && command.args.includes("preview");
171
+ }
172
+ /**
173
+ * Merge overlapping or touching shadow ranges.
174
+ *
175
+ * Spans are kept SEPARATE unless they genuinely overlap: collapsing every cut
176
+ * to `[min target, max marker]` would hide a still-on-surface gap of new traffic
177
+ * between an earlier marker and a later target.
178
+ */
179
+ function coalesceCutSpans(spans) {
180
+ if (spans.length < 2) return [...spans];
181
+ const sorted = [...spans].sort((left, right) => left.start - right.start || left.end - right.end);
182
+ const merged = [];
183
+ for (const span of sorted) {
184
+ const last = merged.at(-1);
185
+ if (last !== void 0 && span.start <= last.end) {
186
+ if (span.end > last.end) last.end = span.end;
187
+ continue;
188
+ }
189
+ merged.push({
190
+ start: span.start,
191
+ end: span.end
192
+ });
193
+ }
194
+ return merged;
195
+ }
196
+ /**
197
+ * True when a row anchored at `seq` falls inside one of the cut spans.
198
+ *
199
+ * A cut runs from the target seq through the marker's own log seq, inclusive
200
+ * (`target <= anchorSeq && anchorSeq <= marker`), the same comparison
201
+ * `dsh-rewind-plugin`'s `RewindVisibilityBuilder` uses. The upper bound stops
202
+ * at the marker. Extending it to the next integer hides the following turn's
203
+ * process row, which the host anchors at `startSeq - 0.1` and therefore in
204
+ * `[marker, marker + 1)`. Fractional anchors inside the closed range still
205
+ * match: `anchorSeqOf` keeps them. A process or tail row parked outside the
206
+ * range is handled by `summaryTurns`, not by widening the cut. Spans stay
207
+ * disjoint, so new traffic between two rewinds survives.
208
+ */
209
+ function cutContains(cuts, seq) {
210
+ let low = 0;
211
+ let high = cuts.length - 1;
212
+ let found = -1;
213
+ while (low <= high) {
214
+ const middle = low + high >> 1;
215
+ const cut = cuts[middle];
216
+ if (cut === void 0) return false;
217
+ if (seq < cut.start) high = middle - 1;
218
+ else {
219
+ found = middle;
220
+ low = middle + 1;
221
+ }
222
+ }
223
+ const span = found < 0 ? void 0 : cuts[found];
224
+ return span !== void 0 && seq <= span.end;
225
+ }
226
+ /** The turn id a node belongs to (absent outside a turn). */
227
+ function turnIdOf(node) {
228
+ const turn = asRecord$1(asRecord$1(node.location)?.turn);
229
+ if (turn === void 0 || !Object.hasOwn(turn, "turn")) return void 0;
230
+ return turn.turn;
231
+ }
232
+ /**
233
+ * Node kinds that SUMMARIZE a turn instead of carrying its content, so the host
234
+ * anchors them at the turn's edges rather than at an event inside it.
235
+ */
236
+ const TURN_SUMMARY_KINDS = /* @__PURE__ */ new Set(["turn-process", "turn-tail"]);
237
+ /**
238
+ * True for a rewind command row the transcript should not keep.
239
+ *
240
+ * Probes (`preview`, `__candidates`) exist only to feed the popover. An
241
+ * executed rewind — success plus a marker seq — is the command that performed
242
+ * the cut, and the plugin hides that row even when its anchor sits outside
243
+ * the numeric range. A failed or still-running command stays visible.
244
+ */
245
+ function commandHidden(node) {
246
+ const command = commandOf(node);
247
+ if (command === void 0 || !isRewindCommand(command)) return false;
248
+ if (isPreviewCommand(command) || isCandidateCommand(command)) return true;
249
+ const outcome = asRecord$1(command.outcome);
250
+ return outcome?.kind === "success" && safeSeq(outcome.sourceEventSeq) !== void 0;
251
+ }
252
+ /** True when the row's own anchor falls in a cut, or the row is a rewind command to hide. */
253
+ function anchorWithdrawn(node, cuts) {
254
+ if (commandHidden(node)) return true;
255
+ const seq = anchorSeqOf(node);
256
+ return seq !== void 0 && cutContains(cuts, seq);
257
+ }
258
+ /**
259
+ * Turns whose process and tail rows must leave.
260
+ *
261
+ * The host opens a turn before its user message is logged and anchors
262
+ * `turn-process` at `controlAnchorSeq - 0.1`, below the first withdrawn
263
+ * event, and `turn-tail` at `turn.end.seq + 0.1`, which can sit past the
264
+ * marker. Containment therefore misses those two rows, and the transcript
265
+ * kept announcing `执行过程 / 本轮已停止,已生成的内容仍保留。`
266
+ *
267
+ * A summary leaves only when every content row of its turn is already
268
+ * withdrawn — the same rule as the plugin's `deriveTurnProcess`. One content
269
+ * row before the target (a mid-turn steering rewind) keeps the summary and
270
+ * that earlier message. A turn with no content rows is left alone.
271
+ */
272
+ function summaryTurns(chat, cuts) {
273
+ const stays = /* @__PURE__ */ new Map();
274
+ for (const key of chat.order) {
275
+ const node = chat.nodes.get(key);
276
+ if (node === void 0 || TURN_SUMMARY_KINDS.has(node.kind ?? "")) continue;
277
+ const id = turnIdOf(node);
278
+ if (id === void 0) continue;
279
+ const visible = node.visibility !== "hidden" && !anchorWithdrawn(node, cuts);
280
+ stays.set(id, (stays.get(id) ?? false) || visible);
281
+ }
282
+ const withdrawn = /* @__PURE__ */ new Set();
283
+ for (const [id, visible] of stays) if (!visible) withdrawn.add(id);
284
+ return withdrawn;
285
+ }
286
+ /** True when this row is a summary of a turn whose content is fully withdrawn. */
287
+ function summaryWithdrawn(node, summaries) {
288
+ if (!TURN_SUMMARY_KINDS.has(node.kind ?? "")) return false;
289
+ const id = turnIdOf(node);
290
+ return id !== void 0 && summaries.has(id);
291
+ }
292
+ /**
293
+ * The shadow ranges a session's executed rewinds produced, newest state last.
294
+ * An executed rewind cuts `[target, marker]` — the target itself is included,
295
+ * because a rewind restores the state *before* that message was sent (its text
296
+ * goes back to the composer).
297
+ */
298
+ function rewindCutSpans(chat) {
299
+ if (chat === void 0) return [];
300
+ const spans = [];
301
+ for (const key of chat.order) {
302
+ const node = chat.nodes.get(key);
303
+ if (node === void 0) continue;
304
+ const command = commandOf(node);
305
+ if (command === void 0 || !isRewindCommand(command)) continue;
306
+ if (isPreviewCommand(command) || isCandidateCommand(command)) continue;
307
+ const outcome = asRecord$1(command.outcome);
308
+ if (outcome?.kind !== "success") continue;
309
+ const marker = safeSeq(outcome.sourceEventSeq);
310
+ if (marker === void 0) continue;
311
+ const target = rewindTargetOfArgs(command.args);
312
+ if (target === void 0 || target > marker) continue;
313
+ spans.push({
314
+ start: target,
315
+ end: marker
316
+ });
317
+ }
318
+ return coalesceCutSpans(spans);
319
+ }
320
+ /**
321
+ * The chat keys the Reader should render, in snapshot order: withdrawn rows and
322
+ * rows the host itself marked hidden are dropped, everything else keeps its
323
+ * position. Returns the snapshot's own `order` array when nothing is filtered,
324
+ * so React selectors see a stable reference.
325
+ */
326
+ function visibleChatKeys(chat) {
327
+ const cuts = rewindCutSpans(chat);
328
+ const summaries = summaryTurns(chat, cuts);
329
+ const visible = [];
330
+ for (const key of chat.order) {
331
+ const node = chat.nodes.get(key);
332
+ if (node === void 0) continue;
333
+ if (node.visibility === "hidden") continue;
334
+ if (anchorWithdrawn(node, cuts) || summaryWithdrawn(node, summaries)) continue;
335
+ visible.push(key);
336
+ }
337
+ return visible.length === chat.order.length ? chat.order : visible;
338
+ }
339
+ /**
340
+ * A comparable signal for the visible row set, built for
341
+ * `useSyncExternalStore`-style subscription: the host re-runs a chat selector
342
+ * on every publish and compares the RESULT by `Object.is`, and a rewind
343
+ * publishes an in-place upsert of an existing command row. The snapshot's
344
+ * `order` array and node store are therefore both reference-stable across a
345
+ * rewind, so a selector on them never re-renders; a string does change exactly
346
+ * when the visible set does. Never return the array itself from a selector —
347
+ * a fresh array fails the `Object.is` check on every publish.
348
+ */
349
+ function visibleChatSignature(chat) {
350
+ return visibleChatKeys(chat).join("\0");
351
+ }
352
+ //#endregion
112
353
  //#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) {
354
+ /**
355
+ * callId of a tool-call chat node. Drafts for a call whose node was cut must
356
+ * not be reintroduced after `group.keys` has already dropped that node.
357
+ */
358
+ function toolCallId(node) {
359
+ const callId = node.data?.root?.callId;
360
+ return typeof callId === "string" && callId.length > 0 ? callId : void 0;
361
+ }
362
+ /** callIds whose tool-call node sits inside a rewind cut. */
363
+ function cutToolCallIds(order, get, spans) {
364
+ const ids = /* @__PURE__ */ new Set();
365
+ for (const key of order) {
366
+ const node = get(key);
367
+ if (node?.kind !== "tool-call") continue;
368
+ const seq = anchorSeqOf(node);
369
+ if (seq === void 0 || !cutContains(spans, seq)) continue;
370
+ const callId = toolCallId(node);
371
+ if (callId !== void 0) ids.add(callId);
372
+ }
373
+ return ids;
374
+ }
375
+ /**
376
+ * Public Step data includes tool-only model output before a chat node exists.
377
+ *
378
+ * `sourceOrder` is the unfiltered chat order. Drafts that belong to a cut step,
379
+ * or to a call whose tool-call node was cut, are not put back. A draft that
380
+ * still has no tool-call node, on a step outside the cut, stays — that is the
381
+ * in-flight call the host has not published a node for yet.
382
+ */
383
+ function readerFlow(group, turn, get, sourceOrder) {
115
384
  const flow = [];
116
385
  const calls = /* @__PURE__ */ new Map();
117
386
  const assistantOrder = /* @__PURE__ */ new Map();
387
+ const spans = sourceOrder === void 0 ? [] : rewindCutSpans({
388
+ order: sourceOrder,
389
+ nodes: { get }
390
+ });
391
+ const cutCalls = spans.length === 0 || sourceOrder === void 0 ? void 0 : cutToolCallIds(sourceOrder, get, spans);
118
392
  for (const key of group.keys) {
119
393
  const node = get(key);
120
394
  if (!node || node.visibility === "hidden") continue;
@@ -141,10 +415,13 @@ window.__ModuleLoader__.load({
141
415
  }
142
416
  for (const step of turn?.steps ?? []) {
143
417
  const data = step.data.get("assistant-step");
418
+ const stepSeq = step.start?.seq;
419
+ const stepCut = typeof stepSeq === "number" && spans.length > 0 && cutContains(spans, stepSeq);
144
420
  let index = 0;
145
421
  for (const draft of data?.blocks ?? []) {
146
422
  if (draft.kind !== "tool-call" || !draft.callId) continue;
147
423
  const previous = calls.get(draft.callId);
424
+ if (!previous && (stepCut || cutCalls?.has(draft.callId) === true)) continue;
148
425
  calls.set(draft.callId, previous ? {
149
426
  ...previous,
150
427
  draft
@@ -849,7 +1126,6 @@ ${trimmed}
849
1126
  const iframeRef = (0, react.useRef)(null);
850
1127
  const lastParamsRef = (0, react.useRef)({});
851
1128
  const [height, setHeight] = (0, react.useState)(() => Math.max(60, Math.min(2400, initialHeight)));
852
- const [ready, setReady] = (0, react.useState)(false);
853
1129
  const [receipt, setReceipt] = (0, react.useState)(null);
854
1130
  const [lastPrompt, setLastPrompt] = (0, react.useState)(null);
855
1131
  const [reloadNonce, setReloadNonce] = (0, react.useState)(0);
@@ -872,7 +1148,7 @@ ${trimmed}
872
1148
  }, [title, writeComposer]);
873
1149
  const handleUserSubmit = (0, react.useCallback)((params) => {
874
1150
  lastParamsRef.current = params;
875
- let summary = "";
1151
+ let summary;
876
1152
  if (typeof params.choice === "string") {
877
1153
  const desc = typeof params.desc === "string" ? ` (${params.desc})` : "";
878
1154
  summary = `选择: ${params.choice}${desc}`;
@@ -904,13 +1180,9 @@ ${trimmed}
904
1180
  }
905
1181
  };
906
1182
  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
1183
  return;
913
1184
  }
1185
+ if (data.method === "ui/ready" || data.method === "ui/notifications/initialized") return;
914
1186
  if (data.method === "ui/resize" && data.params?.height) {
915
1187
  const h = Number(data.params.height);
916
1188
  if (Number.isFinite(h) && h > 0) setHeight(Math.max(60, Math.min(2400, Math.round(h))));
@@ -978,11 +1250,9 @@ ${trimmed}
978
1250
  styles: { variables: cssTokens }
979
1251
  }
980
1252
  }, "*");
981
- setReady(true);
982
1253
  }, []);
983
1254
  const handleReload = (0, react.useCallback)(() => {
984
1255
  setReloadNonce((n) => n + 1);
985
- setReady(false);
986
1256
  setReceipt(null);
987
1257
  }, []);
988
1258
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -10426,7 +10696,7 @@ ${trimmed}
10426
10696
  return count;
10427
10697
  }
10428
10698
  //#endregion
10429
- //#region node_modules/escape-string-regexp/index.js
10699
+ //#region node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp/index.js
10430
10700
  function escapeStringRegexp(string) {
10431
10701
  if (typeof string !== "string") throw new TypeError("Expected a string");
10432
10702
  return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
@@ -42466,7 +42736,7 @@ ${trimmed}
42466
42736
  const readerPathImages = { resolve: (value) => typeof window === "undefined" ? void 0 : localPathMediaUrl(window.location.protocol, window.location.origin, value) };
42467
42737
  //#endregion
42468
42738
  //#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)}";
42739
+ 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
42740
  const tagId$5 = "dsh-better-display/Reader.module.css";
42471
42741
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$5) + "]") === null) {
42472
42742
  const tag = document.createElement("style");
@@ -42577,7 +42847,6 @@ ${trimmed}
42577
42847
  "numberRollSizer": "gNoOdq_numberRollSizer",
42578
42848
  "pendingImage": "gNoOdq_pendingImage",
42579
42849
  "priorFoldDrawer": "gNoOdq_priorFoldDrawer",
42580
- "processCommentary": "gNoOdq_processCommentary",
42581
42850
  "processContents": "gNoOdq_processContents",
42582
42851
  "processFootnote": "gNoOdq_processFootnote",
42583
42852
  "processFrame": "gNoOdq_processFrame",
@@ -42586,6 +42855,7 @@ ${trimmed}
42586
42855
  "processMeta": "gNoOdq_processMeta",
42587
42856
  "processMetaInner": "gNoOdq_processMetaInner",
42588
42857
  "processMetaLine": "gNoOdq_processMetaLine",
42858
+ "processSeamRow": "gNoOdq_processSeamRow",
42589
42859
  "readerRunningGlow": "gNoOdq_readerRunningGlow",
42590
42860
  "readerStepShimmer": "gNoOdq_readerStepShimmer",
42591
42861
  "readingText": "gNoOdq_readingText",
@@ -42810,6 +43080,216 @@ ${trimmed}
42810
43080
  });
42811
43081
  }
42812
43082
  //#endregion
43083
+ //#region src/client/text-transform.ts
43084
+ /**
43085
+ * Cordis service name. Another client plugin lists this in `inject` and
43086
+ * registers a transformer. The reading view paints the result. The session
43087
+ * block, transcript, and fold inputs stay on the original text.
43088
+ */
43089
+ const TEXT_TRANSFORM_SERVICE = "dsh-better-display.text-transform";
43090
+ const KINDS = /* @__PURE__ */ new Set(["reasoning", "body"]);
43091
+ function isKind(value) {
43092
+ return KINDS.has(value);
43093
+ }
43094
+ function reportTransformError(error) {
43095
+ console.warn("[dsh-better-display] text transform failed; showing original text", error);
43096
+ }
43097
+ function passthrough(request) {
43098
+ return {
43099
+ text: request.text,
43100
+ final: !request.streaming,
43101
+ transformRevision: 0,
43102
+ motionRevision: 0
43103
+ };
43104
+ }
43105
+ function normalize(value, request) {
43106
+ if (typeof value === "string") return {
43107
+ text: value,
43108
+ final: !request.streaming
43109
+ };
43110
+ if (!value || typeof value.text !== "string") return {
43111
+ text: request.text,
43112
+ final: !request.streaming
43113
+ };
43114
+ return {
43115
+ text: value.text,
43116
+ final: typeof value.final === "boolean" ? value.final : !request.streaming
43117
+ };
43118
+ }
43119
+ /**
43120
+ * Appends keep the motion revision. A replacement gets a new one so the
43121
+ * timeline treats the swapped string as already settled.
43122
+ */
43123
+ function nextDisplayCursor(previous, display) {
43124
+ if (!previous) return {
43125
+ ...display,
43126
+ transformRevision: 0,
43127
+ motionRevision: 0
43128
+ };
43129
+ if (previous.text === display.text && previous.final === display.final) return previous;
43130
+ const appended = display.text.startsWith(previous.text);
43131
+ return {
43132
+ text: display.text,
43133
+ final: display.final,
43134
+ transformRevision: previous.transformRevision + 1,
43135
+ motionRevision: appended ? previous.motionRevision : previous.motionRevision + 1
43136
+ };
43137
+ }
43138
+ /**
43139
+ * Streaming flag fed to the reveal. With no transformer this is the model's
43140
+ * flag. A transformer that reports `final: !streaming` produces the same flag.
43141
+ */
43142
+ function displayStreaming(sourceStreaming, shown, active) {
43143
+ return active ? !shown.final : sourceStreaming;
43144
+ }
43145
+ var TextTransformRegistryImpl = class {
43146
+ stacks = /* @__PURE__ */ new Map();
43147
+ listeners = /* @__PURE__ */ new Map();
43148
+ cursors = /* @__PURE__ */ new Map();
43149
+ register(kind, transformer) {
43150
+ const requested = [...new Set(Array.isArray(kind) ? kind : [kind])];
43151
+ for (const item of requested) if (!isKind(item)) throw new Error(`Unknown display text kind: ${String(item)}`);
43152
+ const bound = [];
43153
+ for (const item of requested) {
43154
+ const entry = { transformer };
43155
+ try {
43156
+ entry.unsubscribe = transformer.subscribe?.(() => {
43157
+ this.emit(item);
43158
+ });
43159
+ } catch (error) {
43160
+ reportTransformError(error);
43161
+ }
43162
+ const stack = this.stacks.get(item) ?? [];
43163
+ stack.push(entry);
43164
+ this.stacks.set(item, stack);
43165
+ bound.push({
43166
+ kind: item,
43167
+ entry
43168
+ });
43169
+ this.emit(item);
43170
+ }
43171
+ return () => {
43172
+ for (const { kind: item, entry } of bound) {
43173
+ try {
43174
+ entry.unsubscribe?.();
43175
+ } catch (error) {
43176
+ reportTransformError(error);
43177
+ }
43178
+ const stack = this.stacks.get(item);
43179
+ if (!stack) continue;
43180
+ const next = stack.filter((candidate) => candidate !== entry);
43181
+ if (next.length > 0) this.stacks.set(item, next);
43182
+ else this.stacks.delete(item);
43183
+ this.emit(item);
43184
+ }
43185
+ };
43186
+ }
43187
+ has(kind) {
43188
+ return (this.stacks.get(kind)?.length ?? 0) > 0;
43189
+ }
43190
+ read(request) {
43191
+ const transformer = this.stacks.get(request.kind)?.at(-1)?.transformer;
43192
+ if (!transformer) return passthrough(request);
43193
+ let display;
43194
+ try {
43195
+ display = normalize(transformer.read(request), request);
43196
+ } catch (error) {
43197
+ reportTransformError(error);
43198
+ display = {
43199
+ text: request.text,
43200
+ final: !request.streaming
43201
+ };
43202
+ }
43203
+ const key = `${request.kind}\u0000${request.blockKey}`;
43204
+ const next = nextDisplayCursor(this.cursors.get(key), display);
43205
+ this.cursors.set(key, next);
43206
+ return next;
43207
+ }
43208
+ subscribe(kind, listener) {
43209
+ const set = this.listeners.get(kind) ?? /* @__PURE__ */ new Set();
43210
+ set.add(listener);
43211
+ this.listeners.set(kind, set);
43212
+ return () => {
43213
+ set.delete(listener);
43214
+ };
43215
+ }
43216
+ release(kind, blockKey) {
43217
+ this.cursors.delete(`${kind}\u0000${blockKey}`);
43218
+ }
43219
+ emit(kind) {
43220
+ const set = this.listeners.get(kind);
43221
+ if (!set) return;
43222
+ for (const listener of [...set]) listener();
43223
+ }
43224
+ };
43225
+ function createTextTransformRegistry() {
43226
+ return new TextTransformRegistryImpl();
43227
+ }
43228
+ let active;
43229
+ /** Points the reading view at the registry this client plugin provides. */
43230
+ function installTextTransformRegistry(registry) {
43231
+ const previous = active;
43232
+ active = registry;
43233
+ return () => {
43234
+ active = previous;
43235
+ };
43236
+ }
43237
+ function activeTextTransform() {
43238
+ return active;
43239
+ }
43240
+ /**
43241
+ * Subscribe while mounted. The reveal components call this only after
43242
+ * `has(kind)`, so an empty registry adds no hook and no listener.
43243
+ */
43244
+ function useDisplayText(registry, request) {
43245
+ const [epoch, setEpoch] = (0, react.useState)(0);
43246
+ const { text, streaming, blockKey, kind, source } = request;
43247
+ (0, react.useEffect)(() => registry.subscribe(kind, () => {
43248
+ setEpoch((value) => value + 1);
43249
+ }), [registry, kind]);
43250
+ (0, react.useEffect)(() => () => {
43251
+ registry.release(kind, blockKey);
43252
+ }, [
43253
+ registry,
43254
+ kind,
43255
+ blockKey
43256
+ ]);
43257
+ return (0, react.useMemo)(() => registry.read({
43258
+ text,
43259
+ streaming,
43260
+ blockKey,
43261
+ kind,
43262
+ source
43263
+ }), [
43264
+ registry,
43265
+ text,
43266
+ streaming,
43267
+ blockKey,
43268
+ kind,
43269
+ source,
43270
+ epoch
43271
+ ]);
43272
+ }
43273
+ /** Chain-slot owner. Extra fields are optional so older callers still type-check. */
43274
+ function readerBlockOwner(block, streaming, source, presentation, holdFormatting) {
43275
+ return {
43276
+ block,
43277
+ streaming,
43278
+ source,
43279
+ presentation: {
43280
+ startedAt: presentation.startedAt,
43281
+ interrupted: presentation.interrupted,
43282
+ liveText: presentation.liveText
43283
+ },
43284
+ holdFormatting,
43285
+ liveText: presentation.liveText
43286
+ };
43287
+ }
43288
+ /** Copy actions read this. Display text never replaces it. */
43289
+ function originalAnswerText(blocks) {
43290
+ return blocks.flatMap((block) => block.kind === "text" && typeof block.text === "string" ? [block.text] : []).join("\n\n");
43291
+ }
43292
+ //#endregion
42813
43293
  //#region \0dshx-css-module:TurnMetrics.module.css.mjs
42814
43294
  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
43295
  const tagId$4 = "dsh-better-display/TurnMetrics.module.css";
@@ -43174,7 +43654,7 @@ ${trimmed}
43174
43654
  };
43175
43655
  });
43176
43656
  }
43177
- function ReadingMarkdown({ text, streaming, holdFormatting, startedAt, interrupted = false, liveText = false, kind = "body", fileMentions }) {
43657
+ function MarkdownStream({ text, streaming, holdFormatting, startedAt, interrupted = false, liveText = false, kind = "body", fileMentions, transformed = false }) {
43178
43658
  const root = (0, react.useRef)(null);
43179
43659
  const presentation = useStreamingText(text, streaming, {
43180
43660
  startedAt,
@@ -43198,6 +43678,7 @@ ${trimmed}
43198
43678
  "data-ud-motion": "reader-text-arrival",
43199
43679
  "data-ud-motion-type": "reveal",
43200
43680
  "data-ud-motion-no-flash": "true",
43681
+ "data-text-transform": transformed ? streaming ? "live" : "final" : void 0,
43201
43682
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MotionMarkdown, {
43202
43683
  text: presentation.text,
43203
43684
  streaming: effectiveMode,
@@ -43207,7 +43688,32 @@ ${trimmed}
43207
43688
  })
43208
43689
  });
43209
43690
  }
43210
- function ReadingReasoning({ text, streaming, holdFormatting, startedAt, interrupted = false, liveText = false }) {
43691
+ function TransformedMarkdown({ registry, blockKey, source, text, streaming, ...props }) {
43692
+ const shown = useDisplayText(registry, {
43693
+ text,
43694
+ streaming,
43695
+ blockKey,
43696
+ kind: "body",
43697
+ source
43698
+ });
43699
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownStream, {
43700
+ ...props,
43701
+ text: shown.text,
43702
+ streaming: displayStreaming(streaming, shown, true),
43703
+ transformed: true
43704
+ });
43705
+ }
43706
+ function ReadingMarkdown({ blockKey, source, ...props }) {
43707
+ const registry = activeTextTransform();
43708
+ if (registry?.has("body")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TransformedMarkdown, {
43709
+ registry,
43710
+ blockKey,
43711
+ source,
43712
+ ...props
43713
+ });
43714
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownStream, { ...props });
43715
+ }
43716
+ function ReasoningStream({ text, streaming, holdFormatting, startedAt, interrupted = false, liveText = false, transformed = false }) {
43211
43717
  const presentation = useStreamingText(text, streaming, {
43212
43718
  startedAt,
43213
43719
  interrupted: interrupted || !liveText,
@@ -43224,6 +43730,7 @@ ${trimmed}
43224
43730
  "data-ud-motion": "reader-text-arrival",
43225
43731
  "data-ud-motion-type": "reveal",
43226
43732
  "data-ud-motion-no-flash": "true",
43733
+ "data-text-transform": transformed ? streaming ? "live" : "final" : void 0,
43227
43734
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MotionPlainText, {
43228
43735
  text: presentation.text,
43229
43736
  enabled: liveText && presentation.reveal,
@@ -43231,7 +43738,32 @@ ${trimmed}
43231
43738
  })
43232
43739
  });
43233
43740
  }
43234
- function fallback(block, streaming, source, loadImage, fillComposer, holdFormatting, presentation, fileMentions) {
43741
+ function TransformedReasoning({ registry, blockKey, source, text, streaming, ...props }) {
43742
+ const shown = useDisplayText(registry, {
43743
+ text,
43744
+ streaming,
43745
+ blockKey,
43746
+ kind: "reasoning",
43747
+ source
43748
+ });
43749
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningStream, {
43750
+ ...props,
43751
+ text: shown.text,
43752
+ streaming: displayStreaming(streaming, shown, true),
43753
+ transformed: true
43754
+ });
43755
+ }
43756
+ function ReadingReasoning({ blockKey, source, ...props }) {
43757
+ const registry = activeTextTransform();
43758
+ if (registry?.has("reasoning")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TransformedReasoning, {
43759
+ registry,
43760
+ blockKey,
43761
+ source,
43762
+ ...props
43763
+ });
43764
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningStream, { ...props });
43765
+ }
43766
+ function fallback(block, streaming, source, loadImage, fillComposer, holdFormatting, presentation, blockKey, fileMentions) {
43235
43767
  switch (block.kind) {
43236
43768
  case "text": return source === "user" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
43237
43769
  text: block.text,
@@ -43241,7 +43773,9 @@ ${trimmed}
43241
43773
  streaming,
43242
43774
  holdFormatting,
43243
43775
  ...presentation,
43244
- fileMentions
43776
+ fileMentions,
43777
+ blockKey,
43778
+ source
43245
43779
  });
43246
43780
  case "image": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageBlock, {
43247
43781
  attachment: block.attachment,
@@ -43251,7 +43785,9 @@ ${trimmed}
43251
43785
  text: block.text,
43252
43786
  streaming,
43253
43787
  holdFormatting,
43254
- ...presentation
43788
+ ...presentation,
43789
+ blockKey,
43790
+ source
43255
43791
  });
43256
43792
  case "tool-call": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.JsonBlock, {
43257
43793
  label: `工具参数 · ${block.name}`,
@@ -43280,20 +43816,21 @@ ${trimmed}
43280
43816
  }
43281
43817
  }
43282
43818
  const Blocks = (0, react.memo)(function Blocks({ blocks, streaming = false, source = "assistant", holdFormatting = false, startedAt, interrupted, liveText, renderSlotChain, loadImage, fillComposer, fileMentions }) {
43819
+ const reactId = (0, react.useId)();
43283
43820
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComposerFillContext.Provider, {
43284
43821
  value: fillComposer,
43285
43822
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
43286
43823
  className: Reader_module_css_default.blocks,
43287
43824
  "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, {
43825
+ 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, {
43826
+ startedAt,
43827
+ interrupted,
43828
+ liveText
43829
+ }, holdFormatting), { fallback: fallback(block, streaming, source, loadImage, fillComposer, holdFormatting, {
43293
43830
  startedAt,
43294
43831
  interrupted,
43295
43832
  liveText
43296
- }, fileMentions) }) }) }, block.kind === "image" ? `image:${block.attachment.attachmentId}:${index}` : `${index}:${block.kind}`))
43833
+ }, `${reactId}:${index}:${block.kind}`, fileMentions) }) }) }, block.kind === "image" ? `image:${block.attachment.attachmentId}:${index}` : `${index}:${block.kind}`))
43297
43834
  })
43298
43835
  });
43299
43836
  });
@@ -43338,7 +43875,7 @@ ${trimmed}
43338
43875
  }
43339
43876
  function CopyAnswer({ blocks, onFork, metrics, extraActions }) {
43340
43877
  const { receipt, copy } = useCopyReceipt();
43341
- const text = blocks.filter((block) => block.kind === "text").map((block) => block.text).join("\n\n");
43878
+ const text = originalAnswerText(blocks);
43342
43879
  const endedAt = metrics?.endedAt;
43343
43880
  const hasMetrics = metrics !== void 0 && (metrics.usage !== void 0 || metrics.runMs !== void 0 || endedAt !== void 0);
43344
43881
  if (!text.trim() && !onFork && !hasMetrics) return null;
@@ -43830,11 +44367,21 @@ ${trimmed}
43830
44367
  };
43831
44368
  const officialSeat = (family, source = OFFICIAL_SLOTS[family]) => `dsh-better-display.official.${family}/${source}`;
43832
44369
  const READER_OWNED_NODES = /* @__PURE__ */ new Set([
43833
- "assistant-step",
43834
44370
  "tool-call",
43835
44371
  "turn-tail",
43836
44372
  "turn-process"
43837
44373
  ]);
44374
+ /**
44375
+ * Mirror a chat-node registration into the Reader seat.
44376
+ * Assistant replacements are the entries that would actually replace the
44377
+ * official renderer on the host. The official priority itself stays out, so
44378
+ * Reader keeps its own answer markdown until a plugin opts in.
44379
+ */
44380
+ function acceptMirroredChatNode(entry) {
44381
+ const key = entry.options.key ?? "";
44382
+ if (key === "assistant-step") return (entry.options.priority ?? 0) < 0;
44383
+ return !READER_OWNED_NODES.has(key);
44384
+ }
43838
44385
  const EXPECTED = {
43839
44386
  actions: {
43840
44387
  kind: "list",
@@ -43984,7 +44531,7 @@ ${trimmed}
43984
44531
  const target = officialSeat(family);
43985
44532
  const declared = slots.spec(target);
43986
44533
  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);
44534
+ return mirrorOfficialSlot(slots, source, target, `dsh-better-display.official.${family}`, family === "nodes" ? acceptMirroredChatNode : void 0);
43988
44535
  }));
43989
44536
  }
43990
44537
  } catch (error) {
@@ -44049,11 +44596,8 @@ ${trimmed}
44049
44596
  })
44050
44597
  });
44051
44598
  }
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 = {
44599
+ function chatNodeOwner(render, official) {
44600
+ return {
44057
44601
  cwd: render.cwd,
44058
44602
  openFile: render.openFile ?? (() => {}),
44059
44603
  openSkill: () => {},
@@ -44066,13 +44610,18 @@ ${trimmed}
44066
44610
  }),
44067
44611
  fileMentions: official.officialFileMentions
44068
44612
  };
44613
+ }
44614
+ function OfficialNode({ node, fallback, ...render }) {
44615
+ const official = render.official;
44616
+ const [disclosureReset] = (0, react.useState)(() => (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(0));
44617
+ if (!official) return fallback;
44069
44618
  const turn = node.location.kind === "turn" || node.location.kind === "step" ? node.location.turn : void 0;
44070
44619
  const renderNode = official.renderSlot;
44071
44620
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
44072
44621
  "data-reader-official-node": node.kind,
44073
44622
  ...node.kind === "user" || node.kind === "steering" ? chatSeatProps(node.kind, node.key) : {},
44074
44623
  children: renderNode(OFFICIAL_SEATS.nodes, {
44075
- ...owner,
44624
+ ...chatNodeOwner(render, official),
44076
44625
  node
44077
44626
  }, {
44078
44627
  entryKey: node.kind,
@@ -44084,6 +44633,46 @@ ${trimmed}
44084
44633
  })
44085
44634
  });
44086
44635
  }
44636
+ /**
44637
+ * Body of one assistant segment. A mirrored assistant-step replacement draws
44638
+ * it; Reader markdown is the fallback when no replacement is registered.
44639
+ * The caller keeps reasoning blocks on the reading card.
44640
+ */
44641
+ function OfficialAssistantBody({ node, blocks, streaming = false, holdFormatting = false, startedAt, interrupted = false, liveText = false, ...render }) {
44642
+ const official = render.official;
44643
+ const [disclosureReset] = (0, react.useState)(() => (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(0));
44644
+ const fallback = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Blocks, {
44645
+ ...render,
44646
+ blocks,
44647
+ streaming,
44648
+ holdFormatting,
44649
+ startedAt,
44650
+ interrupted,
44651
+ liveText
44652
+ });
44653
+ if (!official) return fallback;
44654
+ const turn = node.location.kind === "turn" || node.location.kind === "step" ? node.location.turn : void 0;
44655
+ const view = blocks.length === node.data.blocks.length && blocks.every((block, index) => block === node.data.blocks[index]) ? node : {
44656
+ ...node,
44657
+ data: {
44658
+ ...node.data,
44659
+ blocks
44660
+ }
44661
+ };
44662
+ const renderNode = official.renderSlot;
44663
+ return renderNode(OFFICIAL_SEATS.nodes, {
44664
+ ...chatNodeOwner(render, official),
44665
+ node: view,
44666
+ groupPart: "response"
44667
+ }, {
44668
+ entryKey: "assistant-step",
44669
+ hookContext: {
44670
+ turnData: turn?.data,
44671
+ disclosureReset
44672
+ },
44673
+ fallback
44674
+ });
44675
+ }
44087
44676
  function OfficialTail({ official, owner, produced }) {
44088
44677
  const tailOwner = owner && official ? {
44089
44678
  ...owner,
@@ -44097,6 +44686,69 @@ ${trimmed}
44097
44686
  }) : null;
44098
44687
  }
44099
44688
  //#endregion
44689
+ //#region src/client/turn-slots.ts
44690
+ /**
44691
+ * Keep live turns in the incoming `groups` order, and keep a departing turn
44692
+ * next to the live neighbour that preceded it.
44693
+ *
44694
+ * A rewind removes turn keys from the visible order. Unmounting them in that
44695
+ * same commit snaps the transcript, so the lane retains the last group object
44696
+ * until the collapse finishes. A re-render, a stream tick, or a reconnect that
44697
+ * still contains the same keys only replaces the group object and leaves
44698
+ * `live` true — those updates must not start a collapse. A key that returns
44699
+ * is live again where `groups` places it. Loading older history therefore
44700
+ * stays in transcript order instead of appending the older turns after the
44701
+ * ones already on screen. A departing slot is inserted immediately after the
44702
+ * nearest preceding slot that is still live, or at the front when it had none,
44703
+ * so several departing neighbours keep their relative order.
44704
+ *
44705
+ * Returns `current` itself when nothing changed, so a React state update can
44706
+ * bail out on `Object.is`.
44707
+ */
44708
+ function reconcileTurnSlots(current, groups) {
44709
+ const incoming = new Map(groups.map((group) => [group.key, group]));
44710
+ const next = groups.map((group) => ({
44711
+ group,
44712
+ live: true
44713
+ }));
44714
+ for (const slot of current) {
44715
+ if (incoming.has(slot.group.key)) continue;
44716
+ insertDeparting(next, {
44717
+ group: slot.group,
44718
+ live: false
44719
+ }, precedingLiveKey(current, incoming, slot.group.key));
44720
+ }
44721
+ return next.length === current.length && next.every((slot, index) => slot.live === current[index].live && slot.group === current[index].group) ? current : next;
44722
+ }
44723
+ /** Nearest preceding slot that is still in the incoming order, if any. */
44724
+ function precedingLiveKey(current, incoming, key) {
44725
+ const index = current.findIndex((slot) => slot.group.key === key);
44726
+ for (let cursor = index - 1; cursor >= 0; cursor--) {
44727
+ const candidate = current[cursor].group.key;
44728
+ if (incoming.has(candidate)) return candidate;
44729
+ }
44730
+ }
44731
+ /**
44732
+ * Place a departing slot after `anchorKey`, or at the front when the slot had
44733
+ * no preceding live neighbour. Slots that share an anchor keep `current` order
44734
+ * because each one is inserted at the end of the departing run after that anchor.
44735
+ */
44736
+ function insertDeparting(next, slot, anchorKey) {
44737
+ if (anchorKey === void 0) {
44738
+ const firstLive = next.findIndex((item) => item.live);
44739
+ next.splice(firstLive < 0 ? next.length : firstLive, 0, slot);
44740
+ return;
44741
+ }
44742
+ const anchorIndex = next.findIndex((item) => item.group.key === anchorKey);
44743
+ if (anchorIndex < 0) {
44744
+ next.unshift(slot);
44745
+ return;
44746
+ }
44747
+ let insertAt = anchorIndex + 1;
44748
+ while (insertAt < next.length && next[insertAt]?.live === false) insertAt++;
44749
+ next.splice(insertAt, 0, slot);
44750
+ }
44751
+ //#endregion
44100
44752
  //#region src/client/motion.tsx
44101
44753
  const EASING = "cubic-bezier(.22,1,.36,1)";
44102
44754
  function useMotionAllowed(enabled) {
@@ -44109,7 +44761,7 @@ ${trimmed}
44109
44761
  }, []);
44110
44762
  return enabled && !reduced;
44111
44763
  }
44112
- function usePinnedSelection(root, selector = "[data-reader-answer], [data-reader-process]") {
44764
+ function usePinnedSelection(root, selector = "[data-reader-answer], [data-reader-body], [data-reader-process]") {
44113
44765
  const [keys, setKeys] = (0, react.useState)([]);
44114
44766
  (0, react.useEffect)(() => {
44115
44767
  const update = () => {
@@ -44355,12 +45007,14 @@ ${trimmed}
44355
45007
  });
44356
45008
  }
44357
45009
  /** Retire only narration that was actually visible; historical rows stay folded. */
44358
- function RetiringContent({ visible, children }) {
45010
+ function RetiringContent({ visible, children, onRetired }) {
44359
45011
  const { enabled } = (0, react.useContext)(StreamMotionContext);
44360
45012
  const root = (0, react.useRef)(null);
44361
45013
  const animation = (0, react.useRef)(null);
44362
45014
  const [present, setPresent] = (0, react.useState)(visible);
44363
45015
  const [focusHeld, setFocusHeld] = (0, react.useState)(false);
45016
+ const retired = (0, react.useRef)(onRetired);
45017
+ retired.current = onRetired;
44364
45018
  (0, react.useLayoutEffect)(() => {
44365
45019
  const element = root.current;
44366
45020
  if (visible) {
@@ -44370,16 +45024,38 @@ ${trimmed}
44370
45024
  return;
44371
45025
  }
44372
45026
  if (!element) return;
44373
- if (element.contains(document.activeElement)) {
44374
- setFocusHeld(true);
45027
+ let retiredNow = false;
45028
+ const retireNow = () => {
45029
+ if (retiredNow) return;
45030
+ retiredNow = true;
45031
+ animation.current?.cancel();
45032
+ animation.current = null;
45033
+ focusSurvivesRetire(element);
45034
+ setPresent(false);
45035
+ retired.current?.();
45036
+ };
45037
+ if (!enabled || document.hidden) {
45038
+ retireNow();
44375
45039
  return;
44376
45040
  }
44377
- if (focusHeld) return;
45041
+ const canListen = typeof document.addEventListener === "function";
45042
+ const holdsFocus = element.contains(document.activeElement);
45043
+ if (holdsFocus || focusHeld) {
45044
+ if (holdsFocus) setFocusHeld(true);
45045
+ const onHiddenWhileHeld = () => {
45046
+ if (document.hidden) retireNow();
45047
+ };
45048
+ if (canListen) document.addEventListener("visibilitychange", onHiddenWhileHeld);
45049
+ return () => {
45050
+ if (canListen) document.removeEventListener("visibilitychange", onHiddenWhileHeld);
45051
+ };
45052
+ }
44378
45053
  const from = element.getBoundingClientRect().height;
44379
45054
  animation.current?.cancel();
44380
45055
  animation.current = null;
44381
- if (!enabled || from < 1) {
45056
+ if (from < 1) {
44382
45057
  setPresent(false);
45058
+ retired.current?.();
44383
45059
  return;
44384
45060
  }
44385
45061
  const next = element.animate([{
@@ -44394,17 +45070,24 @@ ${trimmed}
44394
45070
  fill: "both"
44395
45071
  });
44396
45072
  animation.current = next;
45073
+ let settled = false;
44397
45074
  const settle = () => {
44398
- if (animation.current === next) {
44399
- animation.current = null;
44400
- next.cancel();
44401
- setPresent(false);
44402
- }
45075
+ if (settled || animation.current !== next) return;
45076
+ settled = true;
45077
+ animation.current = null;
45078
+ next.cancel();
45079
+ setPresent(false);
45080
+ retired.current?.();
44403
45081
  };
44404
45082
  next.onfinish = settle;
44405
45083
  const deadline = window.setTimeout(settle, 460);
45084
+ const onHide = () => {
45085
+ if (document.hidden) settle();
45086
+ };
45087
+ if (canListen) document.addEventListener("visibilitychange", onHide);
44406
45088
  return () => {
44407
45089
  window.clearTimeout(deadline);
45090
+ if (canListen) document.removeEventListener("visibilitychange", onHide);
44408
45091
  if (animation.current === next) animation.current = null;
44409
45092
  next.cancel();
44410
45093
  };
@@ -44426,6 +45109,80 @@ ${trimmed}
44426
45109
  children
44427
45110
  });
44428
45111
  }
45112
+ /**
45113
+ * Keeps a departing turn mounted for the length of its own collapse.
45114
+ *
45115
+ * A rewind removes turns from the visible order, and unmounting them outright
45116
+ * snapped the transcript: every withdrawn row disappeared in one frame with
45117
+ * nothing to scroll. Instead each departing turn is retained, rendered with
45118
+ * `visible={false}` so it animates to zero height, and released only once the
45119
+ * collapse has settled — so the reader watches the rows fold away and the rows
45120
+ * below glide up into their place.
45121
+ *
45122
+ * Live slots follow the incoming group order, so older history stays above the
45123
+ * tail. A collapsing turn stays next to the live neighbour that preceded it;
45124
+ * appending it would jump it to the bottom for the length of the animation.
45125
+ * Retention is bounded by the animation itself: `onRetired`
45126
+ * drops the slot, so a departing turn lives for one collapse and no longer —
45127
+ * and a key that comes back later is re-added as a live slot.
45128
+ */
45129
+ function RetiringTurns({ groups, render }) {
45130
+ const [slots, setSlots] = (0, react.useState)(() => groups.map((group) => ({
45131
+ group,
45132
+ live: true
45133
+ })));
45134
+ (0, react.useLayoutEffect)(() => {
45135
+ setSlots((current) => reconcileTurnSlots(current, groups));
45136
+ }, [groups]);
45137
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: slots.map((slot) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RetiringContent, {
45138
+ visible: slot.live,
45139
+ onRetired: () => setSlots((current) => current.filter((candidate) => candidate.live || candidate.group.key !== slot.group.key)),
45140
+ children: render(slot.group)
45141
+ }, slot.group.key)) });
45142
+ }
45143
+ /** A turn that is still on screen, not one this commit is about to unmount. */
45144
+ function survivingTurn(node) {
45145
+ if (!(node instanceof HTMLElement) || node.dataset.readerRetiring === "retiring") return null;
45146
+ const turn = node.hasAttribute("data-reader-turn") ? node : node.querySelector("[data-reader-turn]");
45147
+ if (turn === null) return null;
45148
+ const retiring = turn.closest("[data-reader-retiring]");
45149
+ if (retiring instanceof HTMLElement && retiring.dataset.readerRetiring === "retiring") return null;
45150
+ return turn;
45151
+ }
45152
+ /**
45153
+ * Where focus goes when cut content is removed without an animation.
45154
+ * Prefer the nearest turn that is still live. Otherwise the reading column
45155
+ * (or the host scrollport) so focus does not fall through to the document.
45156
+ */
45157
+ function retireFocusTarget(element) {
45158
+ const parent = element.parentElement;
45159
+ if (parent) {
45160
+ const siblings = [...parent.children];
45161
+ const index = siblings.indexOf(element);
45162
+ for (let cursor = index - 1; cursor >= 0; cursor--) {
45163
+ const turn = survivingTurn(siblings[cursor]);
45164
+ if (turn) return turn;
45165
+ }
45166
+ for (let cursor = index + 1; cursor < siblings.length; cursor++) {
45167
+ const turn = survivingTurn(siblings[cursor]);
45168
+ if (turn) return turn;
45169
+ }
45170
+ }
45171
+ const column = element.closest("[data-chat-flow]");
45172
+ if (column && !element.contains(column)) return column;
45173
+ const scroll = element.closest("[data-conversation-scroll]");
45174
+ if (scroll && !element.contains(scroll)) return scroll;
45175
+ return null;
45176
+ }
45177
+ /** Move focus out of `element` before it unmounts. A no-op when focus is elsewhere. */
45178
+ function focusSurvivesRetire(element) {
45179
+ const active = document.activeElement;
45180
+ if (active === null || !element.contains(active)) return;
45181
+ const target = retireFocusTarget(element);
45182
+ if (target === null || element.contains(target)) return;
45183
+ if (target.tabIndex < 0) target.tabIndex = -1;
45184
+ target.focus({ preventScroll: true });
45185
+ }
44429
45186
  function useReadingScroll(root, motion) {
44430
45187
  const port = (0, react.useRef)(null);
44431
45188
  const following = (0, react.useRef)(true);
@@ -44641,6 +45398,345 @@ ${trimmed}
44641
45398
  };
44642
45399
  }
44643
45400
  //#endregion
45401
+ //#region src/client/fold-choreography.ts
45402
+ /** Presentation clock, not model/network time. */
45403
+ const FOLD_TIMING = {
45404
+ collapse: 320,
45405
+ count: 160,
45406
+ settle: 80,
45407
+ reveal: 180,
45408
+ /**
45409
+ * Slack added to every phase deadline before the watchdog forces the next phase.
45410
+ *
45411
+ * The choreography advances on animation promises and rendered frames. Both are
45412
+ * best-effort: a cancelled animation *rejects* and a compositor that never
45413
+ * finishes one leaves its promise pending, while a backgrounded tab stops
45414
+ * delivering frames. Without a deadline the machine would sit in a non-idle
45415
+ * phase forever, and a non-idle phase freezes both the frame source and the text
45416
+ * reveal, so the turn would look stuck until a remount. This is the liveness
45417
+ * guarantee that does not depend on any of them.
45418
+ */
45419
+ watchdogSlack: 240
45420
+ };
45421
+ /** A step always has the same parent and React key, even when its membership changes. */
45422
+ function flowRows(items) {
45423
+ return items.flatMap((item) => item.kind === "fold" ? [{
45424
+ kind: "summary",
45425
+ key: item.key,
45426
+ item
45427
+ }, ...item.steps.map((step) => ({
45428
+ kind: "step",
45429
+ key: step.key,
45430
+ step,
45431
+ foldKey: item.key
45432
+ }))] : [{
45433
+ kind: "step",
45434
+ key: item.key,
45435
+ step: item.step
45436
+ }]);
45437
+ }
45438
+ function retiringKeys(before, after, open) {
45439
+ const folded = new Set(flowRows(after).filter((row) => row.kind === "step" && row.foldKey && !open[row.foldKey]).map((row) => row.key));
45440
+ return flowRows(before).filter((row) => row.kind === "step" && !row.foldKey && folded.has(row.key)).map((row) => row.key);
45441
+ }
45442
+ /** Insert only new summary slots. Never admit incoming content during shrink. */
45443
+ function collapseRows(before, target) {
45444
+ const rows = flowRows(before);
45445
+ const headers = new Set(rows.filter((row) => row.kind === "summary").map((row) => row.key));
45446
+ for (const item of target) {
45447
+ if (item.kind !== "fold" || headers.has(item.key)) continue;
45448
+ const members = new Set(item.steps.map((step) => step.key));
45449
+ const at = rows.findIndex((row) => members.has(row.key));
45450
+ if (at >= 0) rows.splice(at, 0, {
45451
+ kind: "summary",
45452
+ key: item.key,
45453
+ item: {
45454
+ ...item,
45455
+ summary: item.summary.replace(/\d+/g, "0")
45456
+ }
45457
+ });
45458
+ }
45459
+ return rows;
45460
+ }
45461
+ function containsNewUser(before, after) {
45462
+ const users = new Set(before.filter((item) => item.kind === "user").map((item) => item.key));
45463
+ return after.some((item) => item.kind === "user" && !users.has(item.key));
45464
+ }
45465
+ //#endregion
45466
+ //#region src/client/LiveFold.tsx
45467
+ /** Values are presentation commits, never live network counters. */
45468
+ function SubtleNumberRoll({ value, motion }) {
45469
+ const [outgoing, setOutgoing] = (0, react.useState)(null);
45470
+ const previous = (0, react.useRef)(value);
45471
+ (0, react.useLayoutEffect)(() => {
45472
+ if (value === previous.current) return;
45473
+ const old = previous.current;
45474
+ previous.current = value;
45475
+ if (!motion) {
45476
+ setOutgoing(null);
45477
+ return;
45478
+ }
45479
+ setOutgoing(old);
45480
+ const timer = window.setTimeout(() => setOutgoing(null), FOLD_TIMING.count);
45481
+ return () => window.clearTimeout(timer);
45482
+ }, [value, motion]);
45483
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
45484
+ className: Reader_module_css_default.numberRollRoot,
45485
+ "aria-hidden": "true",
45486
+ children: [
45487
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
45488
+ className: Reader_module_css_default.numberRollSizer,
45489
+ children: value
45490
+ }),
45491
+ outgoing !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
45492
+ className: `${Reader_module_css_default.numberRollDigit} ${Reader_module_css_default.numberRollExit}`,
45493
+ children: outgoing
45494
+ }, `out-${outgoing}`),
45495
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
45496
+ className: `${Reader_module_css_default.numberRollDigit} ${outgoing !== null ? Reader_module_css_default.numberRollEnter : ""}`,
45497
+ children: value
45498
+ }, `cur-${value}`)
45499
+ ]
45500
+ });
45501
+ }
45502
+ function FoldSummaryText({ summary, motion }) {
45503
+ const parts = (0, react.useMemo)(() => {
45504
+ const regex = /([^\d]+)(\d+)/g;
45505
+ const result = [];
45506
+ let end = 0;
45507
+ for (const match of summary.matchAll(regex)) {
45508
+ result.push({
45509
+ text: match[1],
45510
+ number: Number(match[2])
45511
+ });
45512
+ end = match.index + match[0].length;
45513
+ }
45514
+ if (end < summary.length) result.push({ text: summary.slice(end) });
45515
+ return result;
45516
+ }, [summary]);
45517
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
45518
+ className: Reader_module_css_default.foldSummary,
45519
+ "data-reader-fold-summary": summary,
45520
+ "aria-label": summary,
45521
+ children: parts.map((part) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
45522
+ className: Reader_module_css_default.foldSummaryPart,
45523
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: part.text }), part.number !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubtleNumberRoll, {
45524
+ value: part.number,
45525
+ motion
45526
+ })]
45527
+ }, part.text))
45528
+ });
45529
+ }
45530
+ //#endregion
45531
+ //#region src/client/projection.ts
45532
+ function groupNodes(order, get) {
45533
+ const groups = [];
45534
+ const seenTurns = /* @__PURE__ */ new Set();
45535
+ for (const key of order) {
45536
+ const node = get(key);
45537
+ if (!node || node.visibility === "hidden") continue;
45538
+ const turn = node.location.kind === "step" || node.location.kind === "turn" ? node.location.turn.turn : null;
45539
+ const previous = groups.at(-1);
45540
+ if (turn !== null && previous?.turn === turn) previous.keys.push(key);
45541
+ else {
45542
+ groups.push({
45543
+ key: turn === null ? `node:${key}` : seenTurns.has(turn) ? `turn:${turn}:${key}` : `turn:${turn}`,
45544
+ turn,
45545
+ keys: [key]
45546
+ });
45547
+ if (turn !== null) seenTurns.add(turn);
45548
+ }
45549
+ }
45550
+ return groups;
45551
+ }
45552
+ function boundaryOf(turn) {
45553
+ return {
45554
+ status: turn?.status ?? "unknown",
45555
+ reason: turn?.end?.data.reason.kind ?? null,
45556
+ latestStep: turn?.steps.at(-1)?.step ?? -1,
45557
+ closingStep: turn?.data.get("turn-tail")?.closing?.step ?? null
45558
+ };
45559
+ }
45560
+ /**
45561
+ * A settled body step before the closing step of a successful turn.
45562
+ *
45563
+ * This is presentation classification only. Narration is never process content
45564
+ * and never folds. The flag only marks body text that is no longer the turn's
45565
+ * final answer, so copy and fork stay on that final answer.
45566
+ */
45567
+ function isEarlierNarration(data, boundary) {
45568
+ if (data.status !== "settled" || boundary.status !== "closed" || boundary.reason !== "completed") return false;
45569
+ if (data.blocks.some((block) => block.kind === "image" || block.kind === "other")) return false;
45570
+ return boundary.closingStep !== null && data.step < boundary.closingStep;
45571
+ }
45572
+ function processExpanded(choice, boundary) {
45573
+ return choice ?? !(boundary.status === "closed" && boundary.reason === "completed");
45574
+ }
45575
+ /** Reading while running must not pin the process open after completion. */
45576
+ function processChoiceKey(groupKey, boundary) {
45577
+ return `${groupKey}:${boundary.status}:${boundary.reason ?? "pending"}`;
45578
+ }
45579
+ /**
45580
+ * A body-only assistant step is not a thinking/process disclosure.
45581
+ *
45582
+ * Narration (text/image/other blocks) is always visible, so it never counts as
45583
+ * foldable process content; only literal reasoning does. Tool calls arrive as
45584
+ * their own flow entries and are checked separately by the reader.
45585
+ */
45586
+ function hasProcessContent(node, _boundary) {
45587
+ if (!node || node.visibility === "hidden") return false;
45588
+ if (node.kind === "assistant-step") return node.data.blocks.some((block) => block.kind === "reasoning" && block.text.trim() !== "");
45589
+ 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";
45590
+ }
45591
+ function hasVisibleBody(blocks) {
45592
+ return blocks.some((block) => block.kind === "image" || block.kind === "other" || block.kind === "text" && block.text.trim() !== "");
45593
+ }
45594
+ /** Keep native block order. In particular, never lift a later Think above text. */
45595
+ function assistantSegments(blocks) {
45596
+ const segments = [];
45597
+ let previous;
45598
+ blocks.forEach((block, index) => {
45599
+ if (block.kind === "tool-call") {
45600
+ previous = void 0;
45601
+ return;
45602
+ }
45603
+ const kind = block.kind === "reasoning" ? "reasoning" : "body";
45604
+ if (previous?.kind === kind) previous.blocks.push(block);
45605
+ else {
45606
+ previous = {
45607
+ kind,
45608
+ start: index,
45609
+ blocks: [block]
45610
+ };
45611
+ segments.push(previous);
45612
+ }
45613
+ });
45614
+ return segments;
45615
+ }
45616
+ function terminalLabel(reason) {
45617
+ switch (reason) {
45618
+ case "completed": return null;
45619
+ case "aborted":
45620
+ case "interrupted": return "本轮已停止,已生成的内容仍保留。";
45621
+ case "blocked": return "本轮需要处理阻塞事项;请查看原对话与下方操作区。";
45622
+ case "max-tokens": return "输出达到本轮长度限制,内容可能尚未完整。";
45623
+ case "error": return "本轮未完成;错误信息保留在下方。";
45624
+ case null: return null;
45625
+ default: return `本轮结束状态:${reason}。请在原对话中核对完整记录。`;
45626
+ }
45627
+ }
45628
+ /**
45629
+ * Extract the first usable fork anchor seq from candidate message nodes.
45630
+ * Only the durable closing message seq cuts the intended turn prefix: an
45631
+ * absent anchor must surface as undefined (never as 0 or NaN), because the
45632
+ * host treats a missing atSeq as a whole-session fork.
45633
+ */
45634
+ function forkAnchorSeq(candidates) {
45635
+ for (const candidate of candidates) {
45636
+ const seq = candidate?.seq;
45637
+ if (typeof seq === "number" && Number.isFinite(seq)) return seq;
45638
+ }
45639
+ }
45640
+ //#endregion
45641
+ //#region src/client/AssistantNode.tsx
45642
+ function isNode$1(node, kind) {
45643
+ return node.kind === kind;
45644
+ }
45645
+ const AssistantNode = (0, react.memo)(function AssistantNode({ useChat, nodeKey, boundary, processOpen = false, pinned = false, folded = false, partStart, seamSummary, seamAfter = false, motion, onRead, returnFocusTo, ...render }) {
45646
+ const node = useChat((snapshot) => snapshot.nodes.get(nodeKey));
45647
+ if (!node || node.visibility === "hidden" || !isNode$1(node, "assistant-step")) return null;
45648
+ const data = node.data;
45649
+ const parts = assistantSegments(data.blocks);
45650
+ const earlier = isEarlierNarration(data, boundary);
45651
+ const hasToolCalls = data.blocks.some((block) => block.kind === "tool-call");
45652
+ const isProcessStep = earlier || folded || hasToolCalls || boundary.latestStep > 0 && data.step < boundary.latestStep;
45653
+ const body = data.blocks.filter((block) => block.kind !== "reasoning" && block.kind !== "tool-call");
45654
+ const visible = partStart === void 0 ? parts : parts.filter((part) => part.start === partStart);
45655
+ const bodyProps = {
45656
+ streaming: data.status === "running",
45657
+ holdFormatting: pinned,
45658
+ startedAt: data.time,
45659
+ interrupted: data.status === "interrupted",
45660
+ liveText: true
45661
+ };
45662
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: visible.map((part) => {
45663
+ const last = parts.findIndex((item) => item.start === part.start) === parts.length - 1;
45664
+ if (part.kind === "reasoning") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProcessFragment, {
45665
+ open: processOpen,
45666
+ motion,
45667
+ onRead,
45668
+ returnFocusTo,
45669
+ nodeKey,
45670
+ framed: true,
45671
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningCard, {
45672
+ step: data.step,
45673
+ active: processOpen && boundary.status === "open" && data.step === boundary.latestStep,
45674
+ motion,
45675
+ selected: pinned,
45676
+ onRead,
45677
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Blocks, {
45678
+ ...render,
45679
+ blocks: part.blocks,
45680
+ streaming: data.status === "running" && last && data.blocks.at(-1)?.kind === "reasoning",
45681
+ holdFormatting: pinned,
45682
+ startedAt: data.time,
45683
+ interrupted: data.status === "interrupted",
45684
+ liveText: true
45685
+ })
45686
+ })
45687
+ }, part.start);
45688
+ if (!hasVisibleBody(part.blocks)) return null;
45689
+ const isFinalAnswer = !isProcessStep;
45690
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [seamSummary && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
45691
+ className: Reader_module_css_default.processSeamRow,
45692
+ "data-reader-process-seam": true,
45693
+ "data-reader-process-seam-summary": seamSummary,
45694
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Disclosure, {
45695
+ open: false,
45696
+ onChange: () => onRead(),
45697
+ ariaLabel: "被折叠的过程",
45698
+ label: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FoldSummaryText, {
45699
+ summary: seamSummary,
45700
+ motion: false
45701
+ })
45702
+ })
45703
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
45704
+ className: Reader_module_css_default.answer,
45705
+ "data-reader-body": true,
45706
+ "data-reader-anchor": true,
45707
+ "data-reader-key": nodeKey,
45708
+ "data-reader-source-start": part.start,
45709
+ "data-reader-seam-after": seamAfter || void 0,
45710
+ "data-answer-status": data.status,
45711
+ "data-answer-phase": isFinalAnswer ? "body" : "process",
45712
+ children: [
45713
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OfficialAssistantBody, {
45714
+ ...render,
45715
+ node,
45716
+ blocks: part.blocks,
45717
+ ...bodyProps
45718
+ }),
45719
+ isFinalAnswer && last && data.status === "interrupted" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
45720
+ className: Reader_module_css_default.stopped,
45721
+ children: "已停止"
45722
+ }),
45723
+ isFinalAnswer && last && data.status !== "running" && boundary.status === "closed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyAnswer, {
45724
+ blocks: body,
45725
+ onFork: (() => {
45726
+ const anchor = forkAnchorSeq([data.finalNode, { seq: render.forkSeq }]);
45727
+ return render.forkAt && anchor !== void 0 ? () => render.forkAt(anchor) : void 0;
45728
+ })(),
45729
+ metrics: render.metrics,
45730
+ extraActions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OfficialActions, {
45731
+ official: render.official,
45732
+ messageId: data.finalNode?.messageId
45733
+ })
45734
+ })
45735
+ ]
45736
+ })] }, part.start);
45737
+ }) });
45738
+ });
45739
+ //#endregion
44644
45740
  //#region src/client/native/tool-call-model.ts
44645
45741
  /**
44646
45742
  * Pure row-model derivation for tool summary rows: variant classification,
@@ -45434,106 +46530,6 @@ ${trimmed}
45434
46530
  }, child.callId))] });
45435
46531
  }
45436
46532
  //#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
46533
  //#region src/client/open-file.ts
45538
46534
  function deliverableOpenModeOf(value) {
45539
46535
  return value === "sidebar" ? "sidebar" : "external";
@@ -46764,27 +47760,6 @@ ${trimmed}
46764
47760
  if (extra) parts.push(`记录×${extra}`);
46765
47761
  return parts.join(" · ") || "此前步骤";
46766
47762
  }
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
47763
  function skipReasoning(part) {
46789
47764
  return part.kind === "reasoning" && !part.blocks.some((block) => block.kind === "reasoning" && block.text.trim() !== "");
46790
47765
  }
@@ -46864,46 +47839,117 @@ ${trimmed}
46864
47839
  }
46865
47840
  return steps;
46866
47841
  }
47842
+ /** Only a run that contains real thinking/tool work is worth folding. */
47843
+ function foldableRun(run) {
47844
+ return run.some((step) => step.kind === "reasoning" || step.kind === "tool");
47845
+ }
47846
+ /**
47847
+ * One live chain: narration stays visible, while every earlier reasoning/tool
47848
+ * run collapses into its own box at its original position.
47849
+ */
46867
47850
  function presentLiveTurn(steps, boundary, autoFold = true) {
46868
47851
  const live = autoFold && liveFoldEnabled(boundary);
46869
47852
  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({
47853
+ const open = (step) => items.push({
47854
+ kind: "open",
47855
+ key: step.key,
47856
+ step
47857
+ });
47858
+ const fold = (run) => {
47859
+ if (!run.length) return;
47860
+ if (live && foldableRun(run)) items.push({
46884
47861
  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
47862
+ key: `live-fold:${run[0].key}`,
47863
+ steps: run,
47864
+ summary: foldSummary(run)
46893
47865
  });
46894
- chain = [];
47866
+ else for (const step of run) open(step);
46895
47867
  };
47868
+ let run = [];
47869
+ const flush = () => {
47870
+ fold(run);
47871
+ run = [];
47872
+ };
47873
+ const segments = [];
47874
+ let segment = [];
46896
47875
  for (const step of steps) if (step.kind === "user") {
47876
+ segments.push(segment);
47877
+ segments.push([step]);
47878
+ segment = [];
47879
+ } else segment.push(step);
47880
+ segments.push(segment);
47881
+ for (const part of segments) {
47882
+ if (!part.length) continue;
47883
+ const first = part[0];
47884
+ if (part.length === 1 && first.kind === "user") {
47885
+ items.push({
47886
+ kind: "user",
47887
+ key: first.key,
47888
+ step: first
47889
+ });
47890
+ continue;
47891
+ }
47892
+ if (!live) {
47893
+ for (const step of part) open(step);
47894
+ continue;
47895
+ }
47896
+ const lastReasoning = part.findLastIndex((step) => step.kind === "reasoning");
47897
+ if (lastReasoning <= 0) {
47898
+ for (const step of part) open(step);
47899
+ continue;
47900
+ }
47901
+ for (let index = 0; index < part.length; index++) {
47902
+ const step = part[index];
47903
+ if (index < lastReasoning) {
47904
+ if (step.kind === "body") {
47905
+ flush();
47906
+ open(step);
47907
+ } else run.push(step);
47908
+ } else {
47909
+ if (index === lastReasoning) flush();
47910
+ open(step);
47911
+ }
47912
+ }
46897
47913
  flush();
46898
- items.push({
46899
- kind: "user",
46900
- key: step.key,
46901
- step
46902
- });
46903
- } else chain.push(step);
46904
- flush();
47914
+ }
46905
47915
  return items;
46906
47916
  }
47917
+ /**
47918
+ * Summaries for collapsed process that sits between two narration blocks.
47919
+ *
47920
+ * A live-fold cell already shows that summary while the turn is open. Drawing
47921
+ * the seam as well repeats the same counts, so a run whose every step is
47922
+ * already inside a fold cell contributes neither a seam nor `after`. `after`
47923
+ * is what marks the previous body with `data-reader-seam-after` and drops its
47924
+ * bottom padding; skipping the seam must leave that spacing alone.
47925
+ */
47926
+ function planProcessSeams(steps, liveItems, expanded) {
47927
+ const before = /* @__PURE__ */ new Map();
47928
+ const after = /* @__PURE__ */ new Set();
47929
+ if (expanded) return {
47930
+ before,
47931
+ after
47932
+ };
47933
+ const liveFolded = new Set(liveItems.flatMap((item) => item.kind === "fold" ? item.steps.map((step) => step.key) : []));
47934
+ let previousBody;
47935
+ let process = [];
47936
+ for (const step of steps) if (step.kind === "body") {
47937
+ const folded = process.filter((item) => item.kind !== "user");
47938
+ if (!(folded.length > 0 && folded.every((item) => liveFolded.has(item.key))) && previousBody && folded.some((item) => item.kind === "reasoning" || item.kind === "tool" || item.kind === "other")) {
47939
+ before.set(step.key, foldSummary(folded));
47940
+ after.add(previousBody.key);
47941
+ }
47942
+ previousBody = step;
47943
+ process = [];
47944
+ } else if (step.kind === "user") {
47945
+ previousBody = void 0;
47946
+ process = [];
47947
+ } else process.push(step);
47948
+ return {
47949
+ before,
47950
+ after
47951
+ };
47952
+ }
46907
47953
  //#endregion
46908
47954
  //#region src/client/fold-intensity.ts
46909
47955
  function frostedGlassOf(state) {
@@ -46917,136 +47963,6 @@ ${trimmed}
46917
47963
  return intensity === 2;
46918
47964
  }
46919
47965
  //#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
47966
  //#region src/client/DiffPanel.tsx
47051
47967
  /**
47052
47968
  * Reveals and withdraws its child by animating real height, in the plugin's own
@@ -47215,6 +48131,123 @@ ${trimmed}
47215
48131
  })] });
47216
48132
  }
47217
48133
  //#endregion
48134
+ //#region src/client/StickyLane.tsx
48135
+ const MIN_LANE_PX = 40;
48136
+ const LaneOwnerContext = (0, react.createContext)(null);
48137
+ const ownersByRoot = /* @__PURE__ */ new WeakMap();
48138
+ /** One map per Reader, so two roots that both contain turn 1 do not share a writer. */
48139
+ function ReaderLaneScope({ children }) {
48140
+ const owners = (0, react.useRef)(/* @__PURE__ */ new Map());
48141
+ (0, react.useLayoutEffect)(() => {
48142
+ const map = owners.current;
48143
+ return () => {
48144
+ map.clear();
48145
+ };
48146
+ }, []);
48147
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LaneOwnerContext.Provider, {
48148
+ value: owners.current,
48149
+ children
48150
+ });
48151
+ }
48152
+ function laneScope(el) {
48153
+ return el.closest("[data-reader-turn]") ?? el.closest("[data-dsh-better-display]");
48154
+ }
48155
+ function ownersFor(el, scoped) {
48156
+ if (scoped) return scoped;
48157
+ const root = el.closest("[data-dsh-better-display]") ?? el;
48158
+ let owners = ownersByRoot.get(root);
48159
+ if (!owners) {
48160
+ owners = /* @__PURE__ */ new Map();
48161
+ ownersByRoot.set(root, owners);
48162
+ }
48163
+ return owners;
48164
+ }
48165
+ function publishedLanePx(el) {
48166
+ return Math.max(MIN_LANE_PX, Math.ceil(el.getBoundingClientRect().height));
48167
+ }
48168
+ /** One writer per turn inside a single Reader. A retiring summary cannot clear the lane that replaced it. */
48169
+ function publishLaneHeight(el, property, scoped = null) {
48170
+ const scope = laneScope(el);
48171
+ if (!scope) return () => {};
48172
+ const owners = ownersFor(el, scoped);
48173
+ const root = el.closest("[data-dsh-better-display]");
48174
+ const key = `${property}\0${scope.getAttribute("data-reader-turn") ?? "root"}`;
48175
+ const update = () => {
48176
+ if (owners.get(key) !== el) return;
48177
+ scope.style.setProperty(property, `${publishedLanePx(el)}px`);
48178
+ };
48179
+ owners.set(key, el);
48180
+ update();
48181
+ const observer = new ResizeObserver(update);
48182
+ observer.observe(el);
48183
+ return () => {
48184
+ observer.disconnect();
48185
+ if (owners.get(key) !== el) return;
48186
+ owners.delete(key);
48187
+ scope.style.removeProperty(property);
48188
+ if (!scoped && root && owners.size === 0) ownersByRoot.delete(root);
48189
+ };
48190
+ }
48191
+ function usePublishedHeight(property) {
48192
+ const owners = (0, react.useContext)(LaneOwnerContext);
48193
+ const ref = (0, react.useRef)(null);
48194
+ (0, react.useLayoutEffect)(() => {
48195
+ const el = ref.current;
48196
+ if (!el) return;
48197
+ return publishLaneHeight(el, property, owners);
48198
+ }, [owners, property]);
48199
+ return ref;
48200
+ }
48201
+ function useSummaryLane(ref, active) {
48202
+ const owners = (0, react.useContext)(LaneOwnerContext);
48203
+ (0, react.useLayoutEffect)(() => {
48204
+ const el = ref.current;
48205
+ if (!active || !el) return;
48206
+ return publishLaneHeight(el, "--reader-summary-height", owners);
48207
+ }, [
48208
+ active,
48209
+ owners,
48210
+ ref
48211
+ ]);
48212
+ }
48213
+ /** Publish real lane geometry; wrapped labels must not collide with statistics. */
48214
+ function StickyLane({ kind, className, children }) {
48215
+ const ref = (0, react.useRef)(null);
48216
+ (0, react.useLayoutEffect)(() => {
48217
+ const el = ref.current;
48218
+ const scope = el?.closest(kind === "toolbar" ? "[data-dsh-better-display]" : "[data-reader-turn]");
48219
+ if (!el || !scope) return;
48220
+ const property = kind === "toolbar" ? "--reader-toolbar-height" : "--reader-status-height";
48221
+ const update = () => {
48222
+ scope.style.setProperty(property, `${Math.max(MIN_LANE_PX, el.getBoundingClientRect().height)}px`);
48223
+ if (kind !== "toolbar") return;
48224
+ const controls = el.querySelectorAll("button");
48225
+ let width = 80;
48226
+ if (controls.length > 0) {
48227
+ const first = controls[0].getBoundingClientRect();
48228
+ width = controls[controls.length - 1].getBoundingClientRect().right - first.left;
48229
+ }
48230
+ scope.style.setProperty("--reader-control-width", `${width + 28}px`);
48231
+ };
48232
+ update();
48233
+ const observer = new ResizeObserver(update);
48234
+ observer.observe(el);
48235
+ if (kind === "toolbar") for (const control of el.querySelectorAll("button")) observer.observe(control);
48236
+ return () => {
48237
+ observer.disconnect();
48238
+ scope.style.removeProperty(property);
48239
+ if (kind === "toolbar") scope.style.removeProperty("--reader-control-width");
48240
+ };
48241
+ }, [kind]);
48242
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
48243
+ ref,
48244
+ className,
48245
+ "data-reader-lane": kind,
48246
+ "data-ud-check": kind === "toolbar" ? "reader-toolbar" : void 0,
48247
+ children
48248
+ });
48249
+ }
48250
+ //#endregion
47218
48251
  //#region src/client/ChoreographedFlow.tsx
47219
48252
  const FlowSnapshot = (0, react.createContext)(null);
47220
48253
  /** Public projection only. Descendants cannot leak live data through a frozen frame. */
@@ -47257,6 +48290,7 @@ ${trimmed}
47257
48290
  /** Manual disclosure shares the measured-size path; children survive until finish. */
47258
48291
  function FlowCell({ hidden, instant, motion, rowKey, children, summary = false }) {
47259
48292
  const ref = (0, react.useRef)(null);
48293
+ useSummaryLane(ref, summary);
47260
48294
  const [present, setPresent] = (0, react.useState)(!hidden);
47261
48295
  const previous = (0, react.useRef)(hidden);
47262
48296
  if (!hidden && !present) setPresent(true);
@@ -47514,9 +48548,11 @@ ${trimmed}
47514
48548
  /** Keep process counts discoverable after the live-fold presentation retires. */
47515
48549
  function ClosedProcessSummary({ steps, open, onChange, controls }) {
47516
48550
  const button = (0, react.useRef)(null);
48551
+ const lane = usePublishedHeight("--reader-summary-height");
47517
48552
  if (!steps.length) return null;
47518
48553
  const summary = foldSummary(steps);
47519
48554
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
48555
+ ref: lane,
47520
48556
  className: Reader_module_css_default.closedProcessSummary,
47521
48557
  "data-reader-closed-summary": summary,
47522
48558
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -47539,45 +48575,6 @@ ${trimmed}
47539
48575
  });
47540
48576
  }
47541
48577
  //#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
48578
  //#region src/client/Reader.tsx
47582
48579
  function isNode(node, kind) {
47583
48580
  return node.kind === kind;
@@ -47597,6 +48594,9 @@ ${trimmed}
47597
48594
  return typeof block === "object" && block !== null && block.type === "text" && typeof block.text === "string";
47598
48595
  }).map((block) => block.text).join("");
47599
48596
  }
48597
+ function inboxAttachmentCount(item) {
48598
+ return (item.content ?? []).filter((block) => typeof block === "object" && block !== null && block.type !== "text").length;
48599
+ }
47600
48600
  function cleanErrorMessage(raw) {
47601
48601
  if (!raw) return "模型服务暂时无响应或连接中断,请稍后重试。";
47602
48602
  let str = raw.trim();
@@ -47749,101 +48749,7 @@ ${trimmed}
47749
48749
  children: content
47750
48750
  });
47751
48751
  });
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 }) {
48752
+ const MainNode = (0, react.memo)(function MainNode({ useChat, nodeKey, boundary, pinned, processOpen: _processOpen = false, ...render }) {
47847
48753
  const node = useChat((snapshot) => snapshot.nodes.get(nodeKey));
47848
48754
  const [disclosureReset] = (0, react.useState)(() => (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(0));
47849
48755
  if (!node || node.visibility === "hidden") return null;
@@ -48059,7 +48965,7 @@ ${trimmed}
48059
48965
  }
48060
48966
  return 0;
48061
48967
  }
48062
- function GroupStatus({ group, sessionId, useChat, pending, motion }) {
48968
+ function GroupStatus({ group, useChat, pending, motion }) {
48063
48969
  const text = useChat((snapshot) => {
48064
48970
  const turn = group.turn === null ? void 0 : snapshot.timeline.turns.get(group.turn);
48065
48971
  if (turn?.status === "closed") {
@@ -48310,11 +49216,12 @@ ${trimmed}
48310
49216
  const flow = (0, react.useMemo)(() => readerFlow({
48311
49217
  ...group,
48312
49218
  keys: mainKeys
48313
- }, turn, (key) => nodes.get(key)), [
49219
+ }, turn, (key) => nodes.get(key), snapshot.order), [
48314
49220
  nodes,
48315
49221
  group,
48316
49222
  mainKeys,
48317
- turn
49223
+ turn,
49224
+ snapshot.order
48318
49225
  ]);
48319
49226
  const steps = (0, react.useMemo)(() => segmentLiveTurn(flow, (key) => nodes.get(key)), [flow, nodes]);
48320
49227
  const liveItems = (0, react.useMemo)(() => presentLiveTurn(steps, boundary, autoFold), [
@@ -48330,6 +49237,11 @@ ${trimmed}
48330
49237
  const showClosedSummary = boundary.status === "closed" && hasProcess && autoFold;
48331
49238
  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
49239
  const expanded = !autoFold || holdingSelection || processExpanded(expansionChoice, boundary);
49240
+ const seamPlan = (0, react.useMemo)(() => planProcessSeams(steps, liveItems, expanded), [
49241
+ steps,
49242
+ liveItems,
49243
+ expanded
49244
+ ]);
48333
49245
  const [foldOpenByKey, setFoldOpenByKey] = (0, react.useState)({});
48334
49246
  (0, react.useLayoutEffect)(() => {
48335
49247
  if (autoFold) setFoldOpenByKey((current) => Object.keys(current).length ? {} : current);
@@ -48448,7 +49360,9 @@ ${trimmed}
48448
49360
  folded,
48449
49361
  motion,
48450
49362
  onRead: pinProcess,
48451
- returnFocusTo: processButton
49363
+ returnFocusTo: processButton,
49364
+ seamSummary: step.kind === "body" ? seamPlan.before.get(step.key) : void 0,
49365
+ seamAfter: step.kind === "body" && seamPlan.after.has(step.key)
48452
49366
  }) });
48453
49367
  if (step.kind === "tool") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockBoundary, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ProcessFragment, {
48454
49368
  open: processOpen,
@@ -48516,7 +49430,6 @@ ${trimmed}
48516
49430
  buttonRef: processButton,
48517
49431
  label: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupStatus, {
48518
49432
  group,
48519
- sessionId: props.sessionId,
48520
49433
  useChat: props.useChat,
48521
49434
  pending: interaction,
48522
49435
  motion
@@ -48526,12 +49439,7 @@ ${trimmed}
48526
49439
  open: expanded,
48527
49440
  onChange: setExpanded,
48528
49441
  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
- })
49442
+ steps: steps.filter((step) => step.kind !== "user" && step.kind !== "body")
48535
49443
  })]
48536
49444
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChoreographedFlow, {
48537
49445
  id: flowId,
@@ -48556,7 +49464,6 @@ ${trimmed}
48556
49464
  "data-reader-status-only": true,
48557
49465
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupStatus, {
48558
49466
  group,
48559
- sessionId: props.sessionId,
48560
49467
  useChat: props.useChat,
48561
49468
  pending: interaction,
48562
49469
  motion
@@ -48589,6 +49496,15 @@ ${trimmed}
48589
49496
  const activatedAt = (0, react.useRef)(Date.now());
48590
49497
  const order = props.useChat((snapshot) => snapshot.order);
48591
49498
  const nodes = props.useChat((snapshot) => snapshot.nodes);
49499
+ const rewindSignature = props.useChat((snapshot) => visibleChatSignature(snapshot));
49500
+ const renderKeys = (0, react.useMemo)(() => visibleChatKeys({
49501
+ order,
49502
+ nodes
49503
+ }), [
49504
+ order,
49505
+ nodes,
49506
+ rewindSignature
49507
+ ]);
48592
49508
  const timeline = props.useChat((snapshot) => snapshot.timeline);
48593
49509
  const running = props.useSession((snapshot) => snapshot.running);
48594
49510
  const pending = props.useSessionStatus((snapshot) => snapshot.get(props.sessionId)?.pendingInteraction);
@@ -48624,7 +49540,7 @@ ${trimmed}
48624
49540
  pendingList,
48625
49541
  visibleSubmissions
48626
49542
  ]);
48627
- const waitAnchor = waitingAnchor(order, (key) => nodes.get(key), pendingList);
49543
+ const waitAnchor = waitingAnchor(renderKeys, (key) => nodes.get(key), pendingList);
48628
49544
  const motion = useMotionAllowed(props.useStore((state) => state.motion));
48629
49545
  const prefsSnap = (0, react.useSyncExternalStore)(props.openPrefs?.subscribe ?? ((fn) => {
48630
49546
  return () => {};
@@ -48642,14 +49558,14 @@ ${trimmed}
48642
49558
  enabled: motion,
48643
49559
  activatedAt: activatedAt.current
48644
49560
  }), [motion]);
48645
- const groups = (0, react.useMemo)(() => groupNodes(order, (key) => nodes.get(key)), [
48646
- order,
49561
+ const groups = (0, react.useMemo)(() => groupNodes(renderKeys, (key) => nodes.get(key)), [
49562
+ renderKeys,
48647
49563
  nodes,
48648
49564
  timeline
48649
49565
  ]);
48650
49566
  const isAwaitingModel = (0, react.useMemo)(() => {
48651
49567
  if (!running && pendingList.length > 0) return true;
48652
- const lastKey = order.at(-1);
49568
+ const lastKey = renderKeys.at(-1);
48653
49569
  const lastNode = lastKey ? nodes.get(lastKey) : void 0;
48654
49570
  if (!lastKey || !lastNode) return false;
48655
49571
  if (lastNode.kind === "assistant-step") {
@@ -48662,7 +49578,7 @@ ${trimmed}
48662
49578
  }, [
48663
49579
  running,
48664
49580
  pendingList,
48665
- order,
49581
+ renderKeys,
48666
49582
  nodes,
48667
49583
  groups,
48668
49584
  timeline
@@ -48752,7 +49668,7 @@ ${trimmed}
48752
49668
  scroll.release,
48753
49669
  timelineItems
48754
49670
  ]);
48755
- const lastKey = order.at(-1);
49671
+ const lastKey = renderKeys.at(-1);
48756
49672
  const lastNode = lastKey ? nodes.get(lastKey) : void 0;
48757
49673
  const lastSubmissionId = pendingList.length ? pendingList[pendingList.length - 1].requestId : null;
48758
49674
  const lastOrderKeyRef = (0, react.useRef)(lastKey);
@@ -48771,7 +49687,7 @@ ${trimmed}
48771
49687
  ]);
48772
49688
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StreamMotionContext.Provider, {
48773
49689
  value: streamMotion,
48774
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
49690
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReaderLaneScope, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48775
49691
  ref: root,
48776
49692
  className: Reader_module_css_default.root,
48777
49693
  "data-reader-build": "0.3.4",
@@ -48789,6 +49705,7 @@ ${trimmed}
48789
49705
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48790
49706
  className: Reader_module_css_default.column,
48791
49707
  "data-chat-flow": "",
49708
+ tabIndex: -1,
48792
49709
  children: [
48793
49710
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StickyLane, {
48794
49711
  kind: "toolbar",
@@ -48833,20 +49750,23 @@ ${trimmed}
48833
49750
  role: "status",
48834
49751
  children: "正在读取会话…"
48835
49752
  }),
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)),
49753
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RetiringTurns, {
49754
+ groups,
49755
+ render: (group) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TurnGroup, {
49756
+ ...props,
49757
+ group,
49758
+ motion,
49759
+ autoFold,
49760
+ pinnedKeys,
49761
+ selectedProcessKeys,
49762
+ isAwaitingModel: isAwaitingModel && group.key === groups.at(-1)?.key
49763
+ })
49764
+ }),
48845
49765
  pendingInputs.map((item, index) => {
48846
49766
  const submission = "requestId" in item ? item : void 0;
48847
49767
  const text = submission?.text ?? inboxText(item);
48848
49768
  const images = submission ? pendingSubmissionImages(submission) : [];
48849
- const attachmentCount = images.length;
49769
+ const attachmentCount = submission ? images.length : inboxAttachmentCount(item);
48850
49770
  const requestId = submission?.requestId ?? `inbox-${index}`;
48851
49771
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48852
49772
  className: Reader_module_css_default.userCluster,
@@ -48919,7 +49839,7 @@ ${trimmed}
48919
49839
  })
48920
49840
  ]
48921
49841
  })]
48922
- })
49842
+ }) })
48923
49843
  });
48924
49844
  }
48925
49845
  //#endregion
@@ -49421,6 +50341,10 @@ ${trimmed}
49421
50341
  ];
49422
50342
  ensureStyles();
49423
50343
  function apply(ctx) {
50344
+ const textTransform = createTextTransformRegistry();
50345
+ const restoreTextTransform = installTextTransformRegistry(textTransform);
50346
+ ctx.provide(TEXT_TRANSFORM_SERVICE, textTransform);
50347
+ ctx.effect(() => restoreTextTransform);
49424
50348
  const store = createReaderStore();
49425
50349
  const prefs = store.create();
49426
50350
  installBetterDisplaySettings(ctx, prefs);
@@ -49575,9 +50499,12 @@ ${trimmed}
49575
50499
  }
49576
50500
  //#endregion
49577
50501
  exports.McpAppFrame = McpAppFrame;
50502
+ exports.TEXT_TRANSFORM_SERVICE = TEXT_TRANSFORM_SERVICE;
49578
50503
  exports.apply = apply;
50504
+ exports.createTextTransformRegistry = createTextTransformRegistry;
49579
50505
  exports.inject = inject;
49580
50506
  exports.name = name;
50507
+ exports.originalAnswerText = originalAnswerText;
49581
50508
  return module.exports;
49582
50509
  }
49583
50510
  });