@bendyline/docblocks-react 2.7.5 → 2.7.7

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 (29) hide show
  1. package/THIRD_PARTY_NOTICES.txt +26 -26
  2. package/dist/{AiDiagrams-5P657GFC.js → AiDiagrams-7KBAOXPB.js} +4 -2
  3. package/dist/{AiEditorAssistant-V4MZRC6Q.js → AiEditorAssistant-4B2PFFOT.js} +128 -12
  4. package/dist/{AiStudyDialog-GSCFTG3G.js → AiStudyDialog-JPVFFZBB.js} +14 -4
  5. package/dist/{AudioExportDialog-IOYIEAER.js → AudioExportDialog-EICCBSSE.js} +1 -1
  6. package/dist/{ExportDialog-LGWBXIWN.js → ExportDialog-EYDGTJ3S.js} +1 -1
  7. package/dist/{ExportToolbarControls-HOV424TO.js → ExportToolbarControls-2XY2CKGH.js} +45 -4
  8. package/dist/{PresentationMaker-H5B44Q7P.js → PresentationMaker-VYQKNL3L.js} +2 -2
  9. package/dist/{SpeechToolbarControl-KNI2C4JJ.js → SpeechToolbarControl-E6OSJGHL.js} +217 -71
  10. package/dist/{TextFromNarration-EYOMW2IW.js → TextFromNarration-ME6FAYWO.js} +1 -1
  11. package/dist/{WorkspaceSearchPanel-J6BKEZQJ.js → WorkspaceSearchPanel-MEBBTK7F.js} +3 -3
  12. package/dist/{chunk-I44HKDQX.js → chunk-3TSJQESW.js} +16 -3
  13. package/dist/chunk-5OIHB3SR.js +1162 -0
  14. package/dist/chunk-6QHZNL6C.js +208 -0
  15. package/dist/{chunk-FBPCURCS.js → chunk-7HBKKXRV.js} +291 -82
  16. package/dist/{chunk-KBDZDFPU.js → chunk-BNKIPCXO.js} +4 -1
  17. package/dist/{chunk-2IYBBFQX.js → chunk-BZRYQENB.js} +59 -16
  18. package/dist/{chunk-GFK4TCKN.js → chunk-G63YXKBC.js} +8 -5
  19. package/dist/{chunk-DICE7VBJ.js → chunk-KHQ44ZFA.js} +16 -1
  20. package/dist/{chunk-CUCWCZRO.js → chunk-N7AKLJPY.js} +143 -98
  21. package/dist/{chunk-USKX66SA.js → chunk-T2PA7QEF.js} +1 -1
  22. package/dist/editor.js +2 -2
  23. package/dist/export/index.js +1 -1
  24. package/dist/index.js +232 -134
  25. package/dist/settings/index.js +2 -1
  26. package/package.json +8 -8
  27. package/src/styles/docblocks.css +547 -11
  28. package/dist/chunk-I4JURPYS.js +0 -812
  29. package/dist/{QuickOpenDialog-ENVFGIHS.js → QuickOpenDialog-3QEX7RZG.js} +3 -3
@@ -39,6 +39,7 @@ import {
39
39
  useRef,
40
40
  useMemo
41
41
  } from "react";
42
+ import { createPortal } from "react-dom";
42
43
  import {
43
44
  useEditorContext,
44
45
  usePreviewSettings,
@@ -66,7 +67,7 @@ function saveOpenAfterExport(value) {
66
67
  // src/Export/ExportToolbarControls.tsx
67
68
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
68
69
  var ExportDialog = lazy(
69
- () => import("./ExportDialog-LGWBXIWN.js").then((module) => ({ default: module.ExportDialog }))
70
+ () => import("./ExportDialog-EYDGTJ3S.js").then((module) => ({ default: module.ExportDialog }))
70
71
  );
71
72
  var ShareDialog = lazy(
72
73
  () => import("./ShareDialog-PF7D7ORP.js").then((module) => ({ default: module.ShareDialog }))
@@ -79,7 +80,7 @@ function loadRunExportModule() {
79
80
  var ExportCancelledError = class extends Error {
80
81
  };
81
82
  var AudioExportDialog = lazy(
82
- () => import("./AudioExportDialog-IOYIEAER.js").then((module) => ({
83
+ () => import("./AudioExportDialog-EICCBSSE.js").then((module) => ({
83
84
  default: module.AudioExportDialog
84
85
  }))
85
86
  );
@@ -180,6 +181,8 @@ function ExportToolbarControls({
180
181
  const [exportError, setExportError] = useState(null);
181
182
  const [openAfterSave, setOpenAfterSave] = useState(loadOpenAfterExport);
182
183
  const [openError, setOpenError] = useState(null);
184
+ const [exportCompletion, setExportCompletion] = useState(null);
185
+ useEffect(() => setExportCompletion(null), [selectedFile]);
183
186
  const handleOpenAfterSaveChange = useCallback((checked) => {
184
187
  setOpenAfterSave(checked);
185
188
  saveOpenAfterExport(checked);
@@ -359,8 +362,8 @@ function ExportToolbarControls({
359
362
  [destinationAdapter]
360
363
  );
361
364
  const openSaved = useCallback(
362
- async (target) => {
363
- if (!openAfterSave || !destinationAdapter?.openSaved) return;
365
+ async (target, requested = false) => {
366
+ if (!requested && !openAfterSave || !destinationAdapter?.openSaved) return;
364
367
  try {
365
368
  await destinationAdapter.openSaved(target);
366
369
  } catch (caught) {
@@ -376,6 +379,7 @@ function ExportToolbarControls({
376
379
  setExportError(null);
377
380
  setOpenError(null);
378
381
  setExportNotice(null);
382
+ setExportCompletion(null);
379
383
  try {
380
384
  let exportTarget = destinationTarget;
381
385
  if (!fileShare && destinationAdapter?.pickBeforeSave) {
@@ -399,6 +403,12 @@ function ExportToolbarControls({
399
403
  } : saveBlob
400
404
  );
401
405
  setDialogOpen(false);
406
+ if (!fileShare) {
407
+ setExportCompletion({
408
+ filename: buildExportFilename(selectedFile, opts),
409
+ target: savedTarget
410
+ });
411
+ }
402
412
  if (result.warnings.length > 0) setExportNotice(result.warnings);
403
413
  if (savedTarget) await openSaved(savedTarget);
404
414
  } catch (caught) {
@@ -426,6 +436,7 @@ function ExportToolbarControls({
426
436
  setExportError(null);
427
437
  setOpenError(null);
428
438
  setExportNotice(null);
439
+ setExportCompletion(null);
429
440
  try {
430
441
  saveExportOptions(lastOptions);
431
442
  let quickTarget = null;
@@ -450,6 +461,10 @@ function ExportToolbarControls({
450
461
  savedTarget = await saveToDestination(blob, filename, quickTarget);
451
462
  } : saveBlob
452
463
  );
464
+ setExportCompletion({
465
+ filename: buildExportFilename(selectedFile, lastOptions),
466
+ target: savedTarget
467
+ });
453
468
  if (result.warnings.length > 0) setExportNotice(result.warnings);
454
469
  if (savedTarget) await openSaved(savedTarget);
455
470
  } catch (caught) {
@@ -705,6 +720,32 @@ function ExportToolbarControls({
705
720
  }
706
721
  ),
707
722
  openError && !videoModalOpen && /* @__PURE__ */ jsx(Dialog, { title: "Open saved export", onClose: () => setOpenError(null), children: /* @__PURE__ */ jsx("p", { role: "alert", children: openError }) }),
723
+ exportCompletion && !dialogOpen && !exportError && !openError && !exportNotice && createPortal(
724
+ /* @__PURE__ */ jsxs("aside", { className: "db-export-completion", "aria-label": "Export result", children: [
725
+ /* @__PURE__ */ jsx("p", { role: "status", children: exportCompletion.target ? `Saved ${exportCompletion.target.displayPath}` : !destinationAdapter && !saveBlob ? `Sent ${exportCompletion.filename} to your browser\u2019s downloads.` : `Exported ${exportCompletion.filename}` }),
726
+ /* @__PURE__ */ jsxs("div", { className: "db-export-completion-actions", children: [
727
+ exportCompletion.target && destinationAdapter?.openSaved && /* @__PURE__ */ jsx(
728
+ "button",
729
+ {
730
+ type: "button",
731
+ className: "db-export-btn db-export-btn--primary",
732
+ onClick: () => void openSaved(exportCompletion.target, true),
733
+ children: "Open exported file"
734
+ }
735
+ ),
736
+ /* @__PURE__ */ jsx(
737
+ "button",
738
+ {
739
+ type: "button",
740
+ className: "db-export-btn db-export-btn--secondary",
741
+ onClick: () => setExportCompletion(null),
742
+ children: "Dismiss"
743
+ }
744
+ )
745
+ ] })
746
+ ] }),
747
+ document.body
748
+ ),
708
749
  exportNotice && !dialogOpen && !exportError && !openError && /* @__PURE__ */ jsx(
709
750
  Dialog,
710
751
  {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PresentationMaker
3
- } from "./chunk-USKX66SA.js";
4
- import "./chunk-CUCWCZRO.js";
3
+ } from "./chunk-T2PA7QEF.js";
4
+ import "./chunk-N7AKLJPY.js";
5
5
  import "./chunk-K4I3NJFL.js";
6
6
  import "./chunk-VJPGZCFS.js";
7
7
  export {
@@ -5,11 +5,11 @@ import {
5
5
  hasDocumentNarration,
6
6
  planSpeech,
7
7
  useGenerateNarration
8
- } from "./chunk-I44HKDQX.js";
8
+ } from "./chunk-3TSJQESW.js";
9
9
  import {
10
10
  AutoPlayGate,
11
11
  ProgressivePlayer
12
- } from "./chunk-2IYBBFQX.js";
12
+ } from "./chunk-BZRYQENB.js";
13
13
  import {
14
14
  useMenuKeyboard
15
15
  } from "./chunk-GMA4KGGO.js";
@@ -89,44 +89,144 @@ function GenerateNarrationDialog({ speech, onClose }) {
89
89
 
90
90
  // src/Speech/useReadAloud.ts
91
91
  import { useCallback, useEffect, useRef, useState } from "react";
92
+ var RESTART_SEGMENT_SECONDS = 2;
93
+ var POLL_MS = 250;
94
+ var SENTENCE_BREAK = /(?<=[.!?…])\s+/u;
95
+ function* upcomingSentences(segments, segment, offset) {
96
+ for (let index = segment; index < segments.length; index += 1) {
97
+ const text = segments[index]?.text ?? "";
98
+ for (const sentence of (index === segment ? text.slice(offset) : text).split(SENTENCE_BREAK)) {
99
+ const length = sentence.trim().length;
100
+ if (length > 0) yield length;
101
+ }
102
+ }
103
+ }
104
+ function playIfReady(current) {
105
+ if (current.cancelled || current.userPaused || current.synthesizing === null) return;
106
+ const snapshot = current.player.snapshot();
107
+ if (snapshot.isPlaying) return;
108
+ const ready = current.gate.ready(
109
+ snapshot.bufferedDuration - snapshot.currentTime,
110
+ upcomingSentences(current.segments, current.synthesizing, current.synthesizedThrough),
111
+ performance.now()
112
+ );
113
+ if (ready) void current.player.play();
114
+ }
115
+ function endSession(current) {
116
+ current.cancelled = true;
117
+ current.handle?.cancel();
118
+ if (current.poll) clearInterval(current.poll);
119
+ current.player.dispose();
120
+ }
121
+ function sameState(a, b) {
122
+ if (a.phase === "reading" && b.phase === "reading") {
123
+ return a.playing === b.playing && a.waiting === b.waiting && a.segment === b.segment && a.segments === b.segments;
124
+ }
125
+ return a.phase === b.phase && a.phase !== "error";
126
+ }
127
+ function segmentAt(current, snapshot) {
128
+ const caughtUp = !snapshot.isPlaying && snapshot.currentTime >= snapshot.bufferedDuration - 0.05;
129
+ if (caughtUp && current.synthesizing !== null) return current.synthesizing;
130
+ if (snapshot.currentChunk === null) return current.firstSegment;
131
+ return current.chunkSegments[snapshot.currentChunk] ?? current.firstSegment;
132
+ }
92
133
  function useReadAloud(speech) {
93
134
  const [state, setState] = useState({ phase: "idle" });
94
135
  const session = useRef(null);
95
136
  const stop = useCallback(() => {
96
137
  const current = session.current;
97
138
  session.current = null;
98
- if (current) {
99
- current.cancelled = true;
100
- current.handle?.cancel();
101
- current.player.dispose();
102
- }
139
+ if (current) endSession(current);
103
140
  setState({ phase: "idle" });
104
141
  }, []);
105
142
  useEffect(() => stop, [stop]);
143
+ const fail = useCallback(
144
+ (message) => {
145
+ stop();
146
+ setState({ phase: "error", message });
147
+ },
148
+ [stop]
149
+ );
150
+ const refresh = useCallback(() => {
151
+ const current = session.current;
152
+ if (!current) return;
153
+ const snapshot = current.player.snapshot();
154
+ if (snapshot.complete && !snapshot.isPlaying && snapshot.currentTime >= snapshot.bufferedDuration - 0.05) {
155
+ session.current = null;
156
+ endSession(current);
157
+ setState({ phase: "idle" });
158
+ return;
159
+ }
160
+ if (snapshot.isPlaying) current.started = true;
161
+ const next2 = current.started ? {
162
+ phase: "reading",
163
+ playing: !current.userPaused,
164
+ waiting: !current.userPaused && !snapshot.isPlaying,
165
+ segment: segmentAt(current, snapshot),
166
+ segments: current.segments.length
167
+ } : { phase: "loading" };
168
+ setState((previous2) => sameState(previous2, next2) ? previous2 : next2);
169
+ }, []);
170
+ const synthesizeFrom = useCallback(
171
+ async (current, first, synthesize) => {
172
+ if (!synthesize) return;
173
+ current.run += 1;
174
+ const run = current.run;
175
+ const stale = () => current.cancelled || current.run !== run;
176
+ current.handle?.cancel();
177
+ current.handle = null;
178
+ current.firstSegment = first;
179
+ current.synthesizing = first;
180
+ current.segmentStarts.clear();
181
+ current.chunkSegments.length = 0;
182
+ current.gate.restart();
183
+ const { player, gate, segments } = current;
184
+ player.reset();
185
+ let chunkIndex = 0;
186
+ for (let index = first; index < segments.length; index += 1) {
187
+ if (stale()) return;
188
+ current.synthesizing = index;
189
+ current.synthesizedThrough = 0;
190
+ const handle = synthesize({ text: segments[index]?.text ?? "" }, (event) => {
191
+ if (stale() || event.kind !== "chunk") return;
192
+ const { chunk } = event;
193
+ if (!current.segmentStarts.has(index)) {
194
+ current.segmentStarts.set(index, player.snapshot().bufferedDuration);
195
+ }
196
+ current.chunkSegments[chunkIndex] = index;
197
+ player.append({ ...chunk, index: chunkIndex });
198
+ chunkIndex += 1;
199
+ gate.record(
200
+ chunk.textEnd - current.synthesizedThrough,
201
+ chunk.durationSec,
202
+ performance.now()
203
+ );
204
+ current.synthesizedThrough = chunk.textEnd;
205
+ playIfReady(current);
206
+ });
207
+ current.handle = handle;
208
+ const result = await handle.done;
209
+ if (stale()) return;
210
+ if (!result.ok) {
211
+ fail(result.error.message);
212
+ return;
213
+ }
214
+ }
215
+ current.handle = null;
216
+ current.synthesizing = null;
217
+ player.finish();
218
+ if (!player.snapshot().isPlaying && !current.userPaused) void player.play();
219
+ },
220
+ [fail]
221
+ );
106
222
  const start = useCallback(
107
223
  (markdown) => {
108
224
  const synthesize = speech?.synthesize;
109
225
  if (!synthesize) return;
110
226
  stop();
111
- const onChange = (snapshot) => {
112
- const current2 = session.current;
113
- if (!current2 || current2.player !== player) return;
114
- if (snapshot.complete && !snapshot.isPlaying && snapshot.currentTime >= snapshot.bufferedDuration - 0.05) {
115
- session.current = null;
116
- player.dispose();
117
- setState({ phase: "idle" });
118
- return;
119
- }
120
- const segment = snapshot.currentChunk === null ? 0 : current2.chunkSegments[snapshot.currentChunk] ?? 0;
121
- setState({
122
- phase: "reading",
123
- playing: snapshot.isPlaying,
124
- waiting: snapshot.waitingForAudio,
125
- segment,
126
- segments: current2.segments.length
127
- });
128
- };
129
- const player = ProgressivePlayer.create(onChange);
227
+ const player = ProgressivePlayer.create(() => {
228
+ if (session.current?.player === player) refresh();
229
+ });
130
230
  if (!player) {
131
231
  setState({ phase: "error", message: "Audio playback is not available." });
132
232
  return;
@@ -134,12 +234,24 @@ function useReadAloud(speech) {
134
234
  player.prime();
135
235
  const current = {
136
236
  player,
137
- chunkSegments: [],
237
+ gate: new AutoPlayGate(),
138
238
  segments: [],
239
+ run: 0,
240
+ firstSegment: 0,
241
+ synthesizing: null,
242
+ synthesizedThrough: 0,
243
+ poll: null,
244
+ segmentStarts: /* @__PURE__ */ new Map(),
245
+ chunkSegments: [],
139
246
  handle: null,
247
+ started: false,
140
248
  cancelled: false,
141
249
  userPaused: false
142
250
  };
251
+ current.poll = setInterval(() => {
252
+ playIfReady(current);
253
+ if (session.current === current) refresh();
254
+ }, POLL_MS);
143
255
  session.current = current;
144
256
  setState({ phase: "loading" });
145
257
  void (async () => {
@@ -147,67 +259,65 @@ function useReadAloud(speech) {
147
259
  if (current.cancelled) return;
148
260
  current.segments = plan.segments;
149
261
  if (plan.segments.length === 0) {
150
- stop();
151
- setState({ phase: "error", message: "There is nothing to read aloud." });
262
+ fail("There is nothing to read aloud.");
152
263
  return;
153
264
  }
154
- const totalCharacters = plan.segments.reduce((sum, s) => sum + s.text.length, 0);
155
- const gate = new AutoPlayGate();
156
- let spokenCharacters = 0;
157
- let chunkIndex = 0;
158
- for (const [segmentIndex, segment] of plan.segments.entries()) {
159
- if (current.cancelled) return;
160
- const handle = synthesize({ text: segment.text }, (event) => {
161
- if (current.cancelled || event.kind !== "chunk") return;
162
- current.chunkSegments[chunkIndex] = segmentIndex;
163
- player.append({ ...event.chunk, index: chunkIndex });
164
- chunkIndex += 1;
165
- const snapshot = player.snapshot();
166
- const progress = {
167
- completedCharacters: spokenCharacters + event.chunk.textEnd,
168
- totalCharacters
169
- };
170
- if (!snapshot.isPlaying && !current.userPaused && gate.ready(snapshot, progress, performance.now())) {
171
- void player.play();
172
- }
173
- });
174
- current.handle = handle;
175
- const result = await handle.done;
176
- if (current.cancelled) return;
177
- if (!result.ok) {
178
- stop();
179
- setState({ phase: "error", message: result.error.message });
180
- return;
181
- }
182
- spokenCharacters += segment.text.length;
183
- }
184
- current.handle = null;
185
- player.finish();
186
- if (!player.snapshot().isPlaying && !current.userPaused) void player.play();
265
+ await synthesizeFrom(current, 0, synthesize);
187
266
  })().catch((error) => {
188
267
  if (current.cancelled) return;
189
- stop();
190
- setState({
191
- phase: "error",
192
- message: error instanceof Error ? error.message : "Reading aloud failed."
193
- });
268
+ fail(error instanceof Error ? error.message : "Reading aloud failed.");
194
269
  });
195
270
  },
196
- [speech, stop]
271
+ [speech, stop, fail, refresh, synthesizeFrom]
197
272
  );
198
273
  const pause = useCallback(() => {
199
274
  const current = session.current;
200
275
  if (!current) return;
201
276
  current.userPaused = true;
202
277
  current.player.pause();
203
- }, []);
278
+ refresh();
279
+ }, [refresh]);
204
280
  const resume = useCallback(() => {
205
281
  const current = session.current;
206
282
  if (!current) return;
207
283
  current.userPaused = false;
284
+ current.player.prime();
208
285
  void current.player.play();
209
- }, []);
210
- return { state, start, pause, resume, stop };
286
+ refresh();
287
+ }, [refresh]);
288
+ const skip = useCallback(
289
+ (direction) => {
290
+ const current = session.current;
291
+ if (!current || current.segments.length === 0) return;
292
+ current.player.prime();
293
+ const snapshot = current.player.snapshot();
294
+ const segment = segmentAt(current, snapshot);
295
+ const segmentStart = current.segmentStarts.get(segment);
296
+ const restart = direction < 0 && segmentStart !== void 0 && snapshot.currentTime - segmentStart > RESTART_SEGMENT_SECONDS;
297
+ const target = restart ? segment : segment + direction;
298
+ if (target < 0 || target >= current.segments.length) {
299
+ if (target < 0 && segmentStart !== void 0) current.player.seek(segmentStart);
300
+ refresh();
301
+ return;
302
+ }
303
+ const start2 = current.segmentStarts.get(target);
304
+ if (start2 !== void 0) {
305
+ current.player.seek(start2);
306
+ } else if (target === current.synthesizing) {
307
+ current.player.seek(snapshot.bufferedDuration);
308
+ } else {
309
+ void synthesizeFrom(current, target, speech?.synthesize).catch((error) => {
310
+ if (current.cancelled) return;
311
+ fail(error instanceof Error ? error.message : "Reading aloud failed.");
312
+ });
313
+ }
314
+ refresh();
315
+ },
316
+ [speech, fail, refresh, synthesizeFrom]
317
+ );
318
+ const previous = useCallback(() => skip(-1), [skip]);
319
+ const next = useCallback(() => skip(1), [skip]);
320
+ return { state, start, pause, resume, previous, next, stop };
211
321
  }
212
322
 
213
323
  // src/Speech/SpeechToolbarControl.tsx
@@ -286,6 +396,18 @@ function SpeechToolbarControl({
286
396
  }
287
397
  ),
288
398
  active && /* @__PURE__ */ jsxs2("div", { className: "db-speech-transport", role: "group", "aria-label": "Reading aloud", children: [
399
+ /* @__PURE__ */ jsx2(
400
+ "button",
401
+ {
402
+ type: "button",
403
+ className: "squisq-toolbar-button",
404
+ "aria-label": "Previous segment",
405
+ "data-tooltip": "Previous segment",
406
+ disabled: state.phase !== "reading",
407
+ onClick: () => readAloud.previous(),
408
+ children: /* @__PURE__ */ jsx2(PreviousGlyph, {})
409
+ }
410
+ ),
289
411
  state.phase === "reading" && state.playing ? /* @__PURE__ */ jsx2(
290
412
  "button",
291
413
  {
@@ -308,6 +430,18 @@ function SpeechToolbarControl({
308
430
  children: /* @__PURE__ */ jsx2(PlayGlyph, {})
309
431
  }
310
432
  ),
433
+ /* @__PURE__ */ jsx2(
434
+ "button",
435
+ {
436
+ type: "button",
437
+ className: "squisq-toolbar-button",
438
+ "aria-label": "Next segment",
439
+ "data-tooltip": "Next segment",
440
+ disabled: state.phase !== "reading" || state.segment >= state.segments - 1,
441
+ onClick: () => readAloud.next(),
442
+ children: /* @__PURE__ */ jsx2(NextGlyph, {})
443
+ }
444
+ ),
311
445
  /* @__PURE__ */ jsx2(
312
446
  "button",
313
447
  {
@@ -425,6 +559,18 @@ function PlayGlyph() {
425
559
  function PauseGlyph() {
426
560
  return /* @__PURE__ */ jsx2(Glyph, { children: /* @__PURE__ */ jsx2("path", { d: "M6.5 4.5v9M11.5 4.5v9" }) });
427
561
  }
562
+ function PreviousGlyph() {
563
+ return /* @__PURE__ */ jsxs2(Glyph, { children: [
564
+ /* @__PURE__ */ jsx2("path", { d: "M5 4.5v9" }),
565
+ /* @__PURE__ */ jsx2("path", { d: "M13 4.5v9L6.5 9z" })
566
+ ] });
567
+ }
568
+ function NextGlyph() {
569
+ return /* @__PURE__ */ jsxs2(Glyph, { children: [
570
+ /* @__PURE__ */ jsx2("path", { d: "M13 4.5v9" }),
571
+ /* @__PURE__ */ jsx2("path", { d: "M5 4.5v9L11.5 9z" })
572
+ ] });
573
+ }
428
574
  function StopGlyph() {
429
575
  return /* @__PURE__ */ jsx2(Glyph, { children: /* @__PURE__ */ jsx2("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1" }) });
430
576
  }
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-6JJOGV6M.js";
4
4
  import {
5
5
  createSpeechInputProvider
6
- } from "./chunk-KBDZDFPU.js";
6
+ } from "./chunk-BNKIPCXO.js";
7
7
  import {
8
8
  unavailableSentence,
9
9
  useAiStatus
@@ -1,9 +1,9 @@
1
+ import {
2
+ FilesIcon
3
+ } from "./chunk-KHQ44ZFA.js";
1
4
  import {
2
5
  HighlightedText
3
6
  } from "./chunk-TBLZBNQF.js";
4
- import {
5
- FilesIcon
6
- } from "./chunk-DICE7VBJ.js";
7
7
 
8
8
  // src/WorkspaceSearch/WorkspaceSearchPanel.tsx
9
9
  import { useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
@@ -12,6 +12,14 @@ var FENCE = /^(?<indent> {0,3})(?<marker>`{3,}|~{3,})[^\n]*\n[\s\S]*?^\k<indent>
12
12
  function stripFencedCode(markdown) {
13
13
  return markdown.replace(FENCE, "");
14
14
  }
15
+ function joinSoftBreaks(node) {
16
+ if (typeof node !== "object" || node === null) return;
17
+ const { type, value, children } = node;
18
+ if (type === "text" && typeof value === "string") {
19
+ node.value = value.replace(/\n/gu, " ");
20
+ }
21
+ if (Array.isArray(children)) for (const child of children) joinSoftBreaks(child);
22
+ }
15
23
  function ensureSentenceEnd(text) {
16
24
  const trimmed = text.trimEnd();
17
25
  if (!trimmed) return trimmed;
@@ -54,11 +62,16 @@ async function planSpeech(markdown) {
54
62
  import("@bendyline/squisq/doc"),
55
63
  import("@bendyline/squisq/narration")
56
64
  ]);
57
- const doc = markdownToDoc(parseMarkdown(stripFencedCode(markdown)));
65
+ const source = stripFencedCode(markdown);
66
+ const doc = markdownToDoc(parseMarkdown(source));
58
67
  const script = buildNarrationScript(doc);
68
+ const joined = parseMarkdown(source);
69
+ joinSoftBreaks(joined);
70
+ const joinedText = buildNarrationScript(markdownToDoc(joined)).sourceText;
71
+ const spoken = joinedText.length === script.sourceText.length ? joinedText : script.sourceText;
59
72
  const segments = [];
60
73
  for (const block of script.blocks) {
61
- const text = script.sourceText.slice(block.charStart, block.charEnd);
74
+ const text = spoken.slice(block.charStart, block.charEnd);
62
75
  let bodyStart = block.charStart;
63
76
  const heading = block.heading?.trim();
64
77
  if (heading && text.startsWith(heading)) {
@@ -72,7 +85,7 @@ async function planSpeech(markdown) {
72
85
  bodyStart = block.charStart + heading.length;
73
86
  }
74
87
  const lines = /[^\n]+/gu;
75
- const body = script.sourceText.slice(bodyStart, block.charEnd);
88
+ const body = spoken.slice(bodyStart, block.charEnd);
76
89
  for (const line of body.matchAll(lines)) {
77
90
  for (const piece of splitLongText(line[0], bodyStart + line.index)) {
78
91
  segments.push({