@bendyline/squisq-editor-react 2.13.7 → 2.13.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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  NarrationStage,
3
3
  useNarrationStage
4
- } from "./chunk-ZS6G2P57.js";
4
+ } from "./chunk-CWSV5YGZ.js";
5
5
 
6
6
  // src/teleprompter/TeleprompterView.tsx
7
7
  import { jsx } from "react/jsx-runtime";
@@ -4,6 +4,10 @@ import {
4
4
 
5
5
  // src/speech/speechAudioWav.ts
6
6
  async function microphoneTakeAsWav(blob) {
7
+ const samples = await decodeSpeechPcm(blob);
8
+ return encodeMonoPcm16Wav([samples], SPEECH_INPUT_SAMPLE_RATE).buffer;
9
+ }
10
+ async function decodeSpeechPcm(blob) {
7
11
  const encoded = await blobBytes(blob);
8
12
  let audio;
9
13
  try {
@@ -17,8 +21,7 @@ async function microphoneTakeAsWav(blob) {
17
21
  (_, index) => audio.getChannelData(index)
18
22
  );
19
23
  const mono = downmixToMono(channels);
20
- const samples = audio.sampleRate === SPEECH_INPUT_SAMPLE_RATE ? mono : resampleLinear(mono, audio.sampleRate, SPEECH_INPUT_SAMPLE_RATE);
21
- return encodeMonoPcm16Wav([samples], SPEECH_INPUT_SAMPLE_RATE).buffer;
24
+ return audio.sampleRate === SPEECH_INPUT_SAMPLE_RATE ? mono : resampleLinear(mono, audio.sampleRate, SPEECH_INPUT_SAMPLE_RATE);
22
25
  }
23
26
  function blobBytes(blob) {
24
27
  if (typeof blob.arrayBuffer === "function") return blob.arrayBuffer();
@@ -135,6 +138,7 @@ function writeAscii(view, offset, value) {
135
138
 
136
139
  export {
137
140
  microphoneTakeAsWav,
141
+ decodeSpeechPcm,
138
142
  downmixToMono,
139
143
  resampleLinear,
140
144
  encodeMonoPcm16Wav
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  microphoneTakeAsWav
3
- } from "./chunk-K56KR27T.js";
3
+ } from "./chunk-ZEWQ7DTP.js";
4
4
  import "./chunk-DNEVUOBZ.js";
5
5
  import {
6
6
  DEFAULT_RECORDER_DEVICE_SETTINGS,
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as CodeContext, E as EditorHostMode, S as SceneTextChannel, a as CalcEngineFactory, b as EditorView$1, c as CsvSourceMeta, X as XlsxSourceMeta, F as FormulaEditRecord } from './shell-D0XgDAZj.js';
2
- export { A as ApplySourceEditsOptions, B as BlockInsertionBuilder, d as BlockInsertionContext, e as BlockPlacement, f as BlockRange, g as BlockSlice, h as BlockTagVisibility, i as CodeContextSection, j as CoverImageSaveOutput, D as DashboardImageSaveOutput, k as DocumentLinkCandidate, l as DocumentLinkProvider, m as EditorActions, n as EditorColorScheme, o as EditorContextMenuContext, p as EditorContextMenuItem, q as EditorContextMenuItemVisibility, r as EditorContextMenuProvider, s as EditorContextMenuProviderProps, t as EditorContextValue, u as EditorInsertMenuItem, v as EditorMode, w as EditorProvider, x as EditorProviderProps, y as EditorSelectionInfo, z as EditorShell, G as EditorShellProps, H as EditorState, I as FindOptions, J as FindRequest, K as FormulaSessionOptions, L as ImageDisplayMode, M as IngestedSidecar, N as InsertBlockOptions, O as LayoutMode, P as MentionCandidate, Q as MentionProvider, R as OutlineBlock, T as OutlineInsertion, U as PreviewPanel, V as PreviewPanelProps, W as RawEditor, Y as RawEditorProps, Z as ThemeInheritance, _ as ViewPreferences, $ as WriteCanvasSettings, a0 as WysiwygEditor, a1 as WysiwygEditorProps, a2 as XLSX_LOCKED_REASON, a3 as XlsxFormulaSession, a4 as applySourceEditsToText, a5 as blockInsertionPoint, a6 as createXlsxFormulaSession, a7 as getBlockSlices, a8 as headingDepthForInsertion, a9 as ingestSidecarBytes, aa as joinBlockAt, ab as lineToOffset, ac as minimalReplaceEdit, ad as normalizeSourceEdits, ae as offsetToLine, af as resolveOutlineInsertion, ag as sliceIndexAtOffset, ah as sourceOutline, ai as spliceBlock, aj as useEditorContext, ak as useEditorContextMenuItems, al as useEditorInsertMenuItems } from './shell-D0XgDAZj.js';
1
+ import { C as CodeContext, E as EditorHostMode, S as SceneTextChannel, a as CalcEngineFactory, b as EditorView$1, c as CsvSourceMeta, X as XlsxSourceMeta, F as FormulaEditRecord } from './shell-dM2AHXc3.js';
2
+ export { A as ApplySourceEditsOptions, B as BlockInsertionBuilder, d as BlockInsertionContext, e as BlockPlacement, f as BlockRange, g as BlockSlice, h as BlockTagVisibility, i as CodeContextSection, j as CoverImageSaveOutput, D as DashboardImageSaveOutput, k as DocumentLinkCandidate, l as DocumentLinkProvider, m as EditorActions, n as EditorColorScheme, o as EditorContextMenuContext, p as EditorContextMenuItem, q as EditorContextMenuItemVisibility, r as EditorContextMenuProvider, s as EditorContextMenuProviderProps, t as EditorContextValue, u as EditorInsertMenuItem, v as EditorMode, w as EditorProvider, x as EditorProviderProps, y as EditorSelectionInfo, z as EditorShell, G as EditorShellProps, H as EditorState, I as FindOptions, J as FindRequest, K as FormulaSessionOptions, L as ImageDisplayMode, M as IngestedSidecar, N as InsertBlockOptions, O as LayoutMode, P as MentionCandidate, Q as MentionProvider, R as OutlineBlock, T as OutlineInsertion, U as PreviewPanel, V as PreviewPanelProps, W as RawEditor, Y as RawEditorProps, Z as ThemeInheritance, _ as ViewPreferences, $ as WriteCanvasSettings, a0 as WysiwygEditor, a1 as WysiwygEditorProps, a2 as XLSX_LOCKED_REASON, a3 as XlsxFormulaSession, a4 as applySourceEditsToText, a5 as blockInsertionPoint, a6 as createXlsxFormulaSession, a7 as getBlockSlices, a8 as headingDepthForInsertion, a9 as ingestSidecarBytes, aa as joinBlockAt, ab as lineToOffset, ac as minimalReplaceEdit, ad as normalizeSourceEdits, ae as offsetToLine, af as resolveOutlineInsertion, ag as sliceIndexAtOffset, ah as sourceOutline, ai as spliceBlock, aj as useEditorContext, ak as useEditorContextMenuItems, al as useEditorInsertMenuItems } from './shell-dM2AHXc3.js';
3
3
  import * as react from 'react';
4
4
  import { ReactNode, CSSProperties, ReactElement } from 'react';
5
5
  import { Doc, ScheduledClip, VideoPlacement, VideoPipSize, VideoPipShape, VideoPipPosition, MediaEdits, MediaProvider, Theme, CustomTemplateDefinition, ThemeSeedColors, ViewportPreset, ViewportConfig, Block, MediaEntry, DiagramEdgeAnchor } from '@bendyline/squisq/schemas';
@@ -35,7 +35,7 @@ export { EYE_LINE_FRACTION, ExecuteNarrationSaveDeps, NarrationSavePlan, Narrati
35
35
  export { H as HarperProofingConfig, a as ProofingCapability, b as ProofingDocumentRef, c as ProofingIgnoreStore, d as ProofingLanguage, e as ProofingLintOptions, P as ProofingProvider, f as ProofingProviderFactory } from './types-qxwhho3e.js';
36
36
  export { createHarperProofingProvider } from './proofing/index.js';
37
37
  export { PROOF_DIALECTS, PROOF_FRONTMATTER_KEYS, ProofCategory, ProofDialect, ProofFinding, ProofRange, ProofSuggestion, ProofSuggestionKind } from '@bendyline/squisq/proof';
38
- export { D as DictationControl, a as SPEECH_INPUT_SAMPLE_RATE, b as SpeechInputCapability, S as SpeechInputProvider, c as SpeechInputProviderFactory, d as SpeechInputReadiness, e as SpeechInputReadinessState, f as SpeechInputTranscribeOptions, g as SpeechInputTranscript, i as isSpeechInputProviderFactory, r as resolveSpeechInputProvider } from './types-Bi4ZVKvN.js';
38
+ export { D as DictationControl, a as SPEECH_INPUT_SAMPLE_RATE, b as SpeechInputCapability, S as SpeechInputProvider, c as SpeechInputProviderFactory, d as SpeechInputReadiness, e as SpeechInputReadinessState, g as SpeechInputTranscribeOptions, h as SpeechInputTranscript, k as isSpeechInputProviderFactory, r as resolveSpeechInputProvider } from './types-BiKUg5nY.js';
39
39
  import '@bendyline/squisq-calc';
40
40
  import '@bendyline/squisq-formats/xlsx';
41
41
  import '@bendyline/squisq/versions';
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  PROOF_FRONTMATTER_KEYS,
4
4
  createHarperProofingProvider
5
5
  } from "./chunk-VRRT4WP4.js";
6
- import "./chunk-2MP7QE7O.js";
6
+ import "./chunk-7MAL27C2.js";
7
7
  import {
8
8
  configureMonacoWorkers
9
9
  } from "./chunk-BWPFPHWY.js";
@@ -243,7 +243,7 @@ import {
243
243
  videoEmbedParagraphJson,
244
244
  viewStateFromBinding,
245
245
  writeHeadingViewState
246
- } from "./chunk-WZONLKTC.js";
246
+ } from "./chunk-JD7IATM6.js";
247
247
  import {
248
248
  EditorProvider,
249
249
  applySourceEditsToText,
@@ -264,7 +264,7 @@ import {
264
264
  useBlockNavigator,
265
265
  useEditorContext,
266
266
  useEditorInsertMenuItems
267
- } from "./chunk-PCIVTLQG.js";
267
+ } from "./chunk-D2QFSFG2.js";
268
268
  import {
269
269
  SPEECH_INPUT_SAMPLE_RATE,
270
270
  isSpeechInputProviderFactory,
@@ -290,7 +290,7 @@ import {
290
290
  } from "./chunk-HLU6ZBS7.js";
291
291
  import {
292
292
  RecorderButton
293
- } from "./chunk-QZHUSMWA.js";
293
+ } from "./chunk-F4D3VBAS.js";
294
294
  import {
295
295
  RecorderDeviceQuickPicks,
296
296
  RecorderDeviceSettingsPanel,
@@ -315,7 +315,7 @@ import {
315
315
  unshownSlidesWarning,
316
316
  useMediaDevices,
317
317
  useMediaRecorder
318
- } from "./chunk-LXGQA2SY.js";
318
+ } from "./chunk-FOKUOC7E.js";
319
319
  import {
320
320
  DEFAULT_RECORDER_DEVICE_SETTINGS,
321
321
  buildRecorderAudioConstraints,
@@ -329,7 +329,7 @@ import {
329
329
  } from "./chunk-UYB5CPRE.js";
330
330
  import {
331
331
  TeleprompterView
332
- } from "./chunk-DJS5I5NO.js";
332
+ } from "./chunk-PQSFLC2R.js";
333
333
  import {
334
334
  DEFAULT_TELEPROMPTER_PREFS,
335
335
  EYE_LINE_FRACTION,
@@ -340,11 +340,9 @@ import {
340
340
  TeleprompterControls,
341
341
  TeleprompterSurface,
342
342
  buildNarrationSavePlan,
343
- buildTimingJson,
344
343
  cameraVideoLine,
345
344
  createFloatingWindowManager,
346
345
  detectFloatTiers,
347
- encodeTimingJson,
348
346
  ensureTeleprompterStyles,
349
347
  executeNarrationSave,
350
348
  insertNarrationPreamble,
@@ -355,7 +353,6 @@ import {
355
353
  requestCameraStream,
356
354
  stepScroll,
357
355
  targetOffsetFor,
358
- timingPathFor,
359
356
  useFloatingWindow,
360
357
  useMicAnalysis,
361
358
  useNarrationRecorder,
@@ -363,7 +360,7 @@ import {
363
360
  useStreamPreview,
364
361
  useTeleprompter,
365
362
  vadConfigForSensitivity
366
- } from "./chunk-ZS6G2P57.js";
363
+ } from "./chunk-CWSV5YGZ.js";
367
364
  import {
368
365
  buildFilename,
369
366
  requestMicStream,
@@ -372,6 +369,11 @@ import {
372
369
  supportsMediaRecorder,
373
370
  supportsUserMedia
374
371
  } from "./chunk-DNJ6HOES.js";
372
+ import {
373
+ buildTimingJson,
374
+ encodeTimingJson,
375
+ timingPathFor
376
+ } from "./chunk-GG35EBRB.js";
375
377
 
376
378
  // src/FolderView.tsx
377
379
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -0,0 +1,158 @@
1
+ import {
2
+ SPEECH_INPUT_SAMPLE_RATE
3
+ } from "./chunk-DNEVUOBZ.js";
4
+ import {
5
+ encodeNarrationTimingJson,
6
+ timingPathFor
7
+ } from "./chunk-GG35EBRB.js";
8
+
9
+ // src/documentTray/narrationTimingsFromAudio.ts
10
+ import { markdownToDoc } from "@bendyline/squisq/doc";
11
+ import { parseMarkdown } from "@bendyline/squisq/markdown";
12
+ import {
13
+ alignTranscript,
14
+ buildNarrationScript,
15
+ buildNarrationTimingJson,
16
+ parseNarrationTimingJson
17
+ } from "@bendyline/squisq/narration";
18
+ var MAX_PIECE_SEC = 29;
19
+ var CUT_SEARCH_SEC = 6;
20
+ var CUT_FRAME_SEC = 0.05;
21
+ var MIN_PIECE_SEC = 1;
22
+ var MIN_MATCHED_SHARE = 0.2;
23
+ var PROMPT_TAIL_CHARS = 1e3;
24
+ function quietestSample(pcm, lo, hi, frame) {
25
+ let best = hi;
26
+ let bestEnergy = Number.POSITIVE_INFINITY;
27
+ for (let at = lo; at + frame <= hi; at += frame >> 1) {
28
+ let energy = 0;
29
+ for (let i = at; i < at + frame; i++) energy += pcm[i] * pcm[i];
30
+ if (energy < bestEnergy) {
31
+ bestEnergy = energy;
32
+ best = at + (frame >> 1);
33
+ }
34
+ }
35
+ return best;
36
+ }
37
+ function cutSpeechPieces(pcm, sampleRate, startSec, endSec) {
38
+ const end = Math.min(pcm.length, Math.max(0, Math.round(endSec * sampleRate)));
39
+ const max = Math.round(MAX_PIECE_SEC * sampleRate);
40
+ const frame = Math.max(2, Math.round(CUT_FRAME_SEC * sampleRate));
41
+ const pieces = [];
42
+ let from = Math.min(end, Math.max(0, Math.round(startSec * sampleRate)));
43
+ while (from < end) {
44
+ if (end - from <= max) {
45
+ pieces.push({ from, to: end });
46
+ break;
47
+ }
48
+ const to = quietestSample(
49
+ pcm,
50
+ from + max - Math.round(CUT_SEARCH_SEC * sampleRate),
51
+ from + max,
52
+ frame
53
+ );
54
+ pieces.push({ from, to });
55
+ from = to;
56
+ }
57
+ const last = pieces[pieces.length - 1];
58
+ if (pieces.length > 1 && last && last.to - last.from < MIN_PIECE_SEC * sampleRate) {
59
+ pieces.pop();
60
+ pieces[pieces.length - 1].to = last.to;
61
+ }
62
+ return pieces;
63
+ }
64
+ function timedSegments(transcript, offsetSec, endSec) {
65
+ if (transcript.segments?.length) {
66
+ return transcript.segments.map((segment) => ({
67
+ text: segment.text,
68
+ startSec: offsetSec + segment.start,
69
+ endSec: offsetSec + segment.end,
70
+ ...segment.words?.length ? {
71
+ words: segment.words.map((word) => ({
72
+ text: word.text,
73
+ startSec: offsetSec + word.start,
74
+ endSec: offsetSec + word.end
75
+ }))
76
+ } : {}
77
+ }));
78
+ }
79
+ const text = transcript.text.trim();
80
+ return text ? [{ text, startSec: offsetSec, endSec }] : [];
81
+ }
82
+ async function readTake(mediaProvider, container, src) {
83
+ const url = await mediaProvider.resolveUrl(src);
84
+ if (url && url !== src) {
85
+ const response = await fetch(url);
86
+ if (response.ok) return response.blob();
87
+ }
88
+ const bytes = await container.readFile(src);
89
+ if (!bytes) throw new Error("The recording could not be read.");
90
+ return new Blob([bytes]);
91
+ }
92
+ async function updateNarrationTimingsFromAudio(input) {
93
+ const { src, provider, container, signal } = input;
94
+ const report = (phase, fraction) => input.onProgress?.({ phase, fraction });
95
+ const doc = markdownToDoc(parseMarkdown(input.markdownSource));
96
+ const clip = (doc.documentMedia ?? []).find(
97
+ (entry) => entry.anchor === "document" && entry.src === src
98
+ );
99
+ if (!clip) throw new Error("This recording is not the document narration.");
100
+ const script = buildNarrationScript(doc);
101
+ if (!script.tokens.some((token) => token.spoken)) {
102
+ throw new Error("The document has no text to match the recording against.");
103
+ }
104
+ report("reading", 0);
105
+ await provider.prepare?.(signal);
106
+ const { decodeSpeechPcm, encodeMonoPcm16Wav } = await import("./speechAudioWav-TBAG7W3V.js");
107
+ const pcm = await decodeSpeechPcm(await readTake(input.mediaProvider, container, src));
108
+ signal.throwIfAborted();
109
+ const rate = SPEECH_INPUT_SAMPLE_RATE;
110
+ const durationSec = pcm.length / rate;
111
+ const pieces = cutSpeechPieces(pcm, rate, clip.clipStart ?? 0, clip.clipEnd ?? durationSec);
112
+ if (!pieces.length) throw new Error("The recording has no audio to transcribe.");
113
+ const segments = [];
114
+ let tail = "";
115
+ for (const [index, piece] of pieces.entries()) {
116
+ report("transcribing", 0.05 + 0.9 * index / pieces.length);
117
+ const wav = encodeMonoPcm16Wav([pcm.subarray(piece.from, piece.to)], rate).buffer;
118
+ const transcript = await provider.transcribe(wav, {
119
+ signal,
120
+ timestamps: true,
121
+ ...tail ? { prompt: tail } : {}
122
+ });
123
+ signal.throwIfAborted();
124
+ segments.push(...timedSegments(transcript, piece.from / rate, piece.to / rate));
125
+ const text = transcript.text.trim();
126
+ if (text) tail = `${tail} ${text}`.slice(-PROMPT_TAIL_CHARS).replace(/^\S*\s/u, "");
127
+ }
128
+ report("matching", 0.95);
129
+ const alignment = alignTranscript({ script, segments, durationSec });
130
+ if (alignment.matchedWords < Math.max(3, alignment.spokenWords * MIN_MATCHED_SHARE)) {
131
+ throw new Error(
132
+ `Only ${alignment.matchedWords} of ${alignment.spokenWords} words in the document were heard in the recording, so its timings were left unchanged.`
133
+ );
134
+ }
135
+ report("saving", 0.98);
136
+ const sidecarPath = timingPathFor(src);
137
+ const existing = await container.readFile(sidecarPath);
138
+ const previous = existing ? parseNarrationTimingJson(existing) : null;
139
+ const payload = buildNarrationTimingJson(script, alignment, durationSec, {
140
+ generator: { name: provider.id, method: "transcript-align" },
141
+ ...previous?.cameraOffsetSec !== void 0 ? { cameraOffsetSec: previous.cameraOffsetSec } : {}
142
+ });
143
+ signal.throwIfAborted();
144
+ await container.writeFile(
145
+ sidecarPath,
146
+ encodeNarrationTimingJson(previous?.builds ? { ...payload, builds: previous.builds } : payload),
147
+ "application/json"
148
+ );
149
+ return {
150
+ matchedWords: alignment.matchedWords,
151
+ spokenWords: alignment.spokenWords,
152
+ sidecarPath
153
+ };
154
+ }
155
+ export {
156
+ cutSpeechPieces,
157
+ updateNarrationTimingsFromAudio
158
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  RecorderButton
3
- } from "../chunk-QZHUSMWA.js";
3
+ } from "../chunk-F4D3VBAS.js";
4
4
  import {
5
5
  RecorderDeviceQuickPicks,
6
6
  RecorderDeviceSettingsPanel,
@@ -14,7 +14,7 @@ import {
14
14
  requestScreenStream,
15
15
  useMediaDevices,
16
16
  useMediaRecorder
17
- } from "../chunk-LXGQA2SY.js";
17
+ } from "../chunk-FOKUOC7E.js";
18
18
  import {
19
19
  DEFAULT_RECORDER_DEVICE_SETTINGS,
20
20
  buildRecorderAudioConstraints,
@@ -25,12 +25,9 @@ import {
25
25
  } from "../chunk-LRZJD5YD.js";
26
26
  import "../chunk-UYB5CPRE.js";
27
27
  import {
28
- buildTimingJson,
29
- encodeTimingJson,
30
28
  requestCameraStream,
31
- timingPathFor,
32
29
  useStreamPreview
33
- } from "../chunk-ZS6G2P57.js";
30
+ } from "../chunk-CWSV5YGZ.js";
34
31
  import {
35
32
  buildFilename,
36
33
  requestMicStream,
@@ -39,6 +36,11 @@ import {
39
36
  supportsMediaRecorder,
40
37
  supportsUserMedia
41
38
  } from "../chunk-DNJ6HOES.js";
39
+ import {
40
+ buildTimingJson,
41
+ encodeTimingJson,
42
+ timingPathFor
43
+ } from "../chunk-GG35EBRB.js";
42
44
  export {
43
45
  DEFAULT_RECORDER_DEVICE_SETTINGS,
44
46
  RecorderButton,
@@ -19,7 +19,7 @@ import {
19
19
  updateReviewDecorations,
20
20
  useReview,
21
21
  useReviewState
22
- } from "../chunk-PCIVTLQG.js";
22
+ } from "../chunk-D2QFSFG2.js";
23
23
  import "../chunk-DNEVUOBZ.js";
24
24
  import "../chunk-ZLKINE4Y.js";
25
25
 
@@ -1,4 +1,4 @@
1
- export { h as BlockTagVisibility, k as DocumentLinkCandidate, l as DocumentLinkProvider, m as EditorActions, n as EditorColorScheme, o as EditorContextMenuContext, p as EditorContextMenuItem, q as EditorContextMenuItemVisibility, r as EditorContextMenuProvider, s as EditorContextMenuProviderProps, t as EditorContextValue, E as EditorHostMode, u as EditorInsertMenuItem, v as EditorMode, w as EditorProvider, x as EditorProviderProps, z as EditorShell, G as EditorShellProps, H as EditorState, b as EditorView, L as ImageDisplayMode, O as LayoutMode, P as MentionCandidate, Q as MentionProvider, U as PreviewPanel, V as PreviewPanelProps, W as RawEditor, Y as RawEditorProps, Z as ThemeInheritance, _ as ViewPreferences, a0 as WysiwygEditor, a1 as WysiwygEditorProps, aj as useEditorContext, ak as useEditorContextMenuItems, al as useEditorInsertMenuItems } from '../shell-D0XgDAZj.js';
1
+ export { h as BlockTagVisibility, k as DocumentLinkCandidate, l as DocumentLinkProvider, m as EditorActions, n as EditorColorScheme, o as EditorContextMenuContext, p as EditorContextMenuItem, q as EditorContextMenuItemVisibility, r as EditorContextMenuProvider, s as EditorContextMenuProviderProps, t as EditorContextValue, E as EditorHostMode, u as EditorInsertMenuItem, v as EditorMode, w as EditorProvider, x as EditorProviderProps, z as EditorShell, G as EditorShellProps, H as EditorState, b as EditorView, L as ImageDisplayMode, O as LayoutMode, P as MentionCandidate, Q as MentionProvider, U as PreviewPanel, V as PreviewPanelProps, W as RawEditor, Y as RawEditorProps, Z as ThemeInheritance, _ as ViewPreferences, a0 as WysiwygEditor, a1 as WysiwygEditorProps, aj as useEditorContext, ak as useEditorContextMenuItems, al as useEditorInsertMenuItems } from '../shell-dM2AHXc3.js';
2
2
  import 'react';
3
3
  import '@bendyline/squisq/table';
4
4
  import '@bendyline/squisq-calc';
@@ -11,7 +11,7 @@ import '@bendyline/squisq-react';
11
11
  import '@bendyline/squisq/storage';
12
12
  import '../types-qxwhho3e.js';
13
13
  import '@bendyline/squisq/proof';
14
- import '../types-Bi4ZVKvN.js';
14
+ import '../types-BiKUg5nY.js';
15
15
  import '@bendyline/squisq/versions';
16
16
  import '@tiptap/core';
17
17
  import 'monaco-editor';
@@ -5,23 +5,24 @@ import {
5
5
  RawEditor,
6
6
  WysiwygEditor,
7
7
  useEditorContextMenuItems
8
- } from "../chunk-WZONLKTC.js";
8
+ } from "../chunk-JD7IATM6.js";
9
9
  import {
10
10
  EditorProvider,
11
11
  useEditorContext,
12
12
  useEditorInsertMenuItems
13
- } from "../chunk-PCIVTLQG.js";
13
+ } from "../chunk-D2QFSFG2.js";
14
14
  import "../chunk-DNEVUOBZ.js";
15
15
  import "../chunk-A2H3JOA5.js";
16
16
  import "../chunk-4MBH7MMU.js";
17
17
  import "../chunk-ZLKINE4Y.js";
18
18
  import "../chunk-HLU6ZBS7.js";
19
- import "../chunk-LXGQA2SY.js";
19
+ import "../chunk-FOKUOC7E.js";
20
20
  import "../chunk-LRZJD5YD.js";
21
21
  import "../chunk-UYB5CPRE.js";
22
- import "../chunk-DJS5I5NO.js";
23
- import "../chunk-ZS6G2P57.js";
22
+ import "../chunk-PQSFLC2R.js";
23
+ import "../chunk-CWSV5YGZ.js";
24
24
  import "../chunk-DNJ6HOES.js";
25
+ import "../chunk-GG35EBRB.js";
25
26
  export {
26
27
  EditorContextMenuProvider,
27
28
  EditorProvider,
@@ -10,7 +10,7 @@ import { FenceRendererMap } from '@bendyline/squisq/fence';
10
10
  import { CodeBlockCopyHandler } from '@bendyline/squisq-react';
11
11
  import { ContentContainer } from '@bendyline/squisq/storage';
12
12
  import { a as ProofingCapability, c as ProofingIgnoreStore } from './types-qxwhho3e.js';
13
- import { D as DictationControl, b as SpeechInputCapability } from './types-Bi4ZVKvN.js';
13
+ import { D as DictationControl, j as SpeechRecognizerAccess, b as SpeechInputCapability } from './types-BiKUg5nY.js';
14
14
  import { DocumentVersionManager, SaveVersionOptions, SaveVersionResult, PrunePolicy } from '@bendyline/squisq/versions';
15
15
  import { Editor } from '@tiptap/core';
16
16
  import { editor } from 'monaco-editor';
@@ -823,6 +823,12 @@ interface EditorContextValue extends EditorState, EditorActions {
823
823
  * was injected. See docs/speech-input.md.
824
824
  */
825
825
  dictation: DictationControl | null;
826
+ /**
827
+ * The same capability's resolved recognizer, for transcribing recorded
828
+ * audio (e.g. updating a document narration's timings). `null` when no
829
+ * capability was injected.
830
+ */
831
+ speechRecognizer: SpeechRecognizerAccess | null;
826
832
  /** The document's article id — part of the proofing document ref. */
827
833
  articleId: string;
828
834
  /**
@@ -1,6 +1,6 @@
1
1
  import { R as RecorderDeviceSettings } from '../deviceSettings-BSkIQncC.js';
2
- import { S as SpeechInputProvider } from '../types-Bi4ZVKvN.js';
3
- export { D as DictationControl, a as SPEECH_INPUT_SAMPLE_RATE, b as SpeechInputCapability, c as SpeechInputProviderFactory, d as SpeechInputReadiness, e as SpeechInputReadinessState, f as SpeechInputTranscribeOptions, g as SpeechInputTranscript, i as isSpeechInputProviderFactory, r as resolveSpeechInputProvider } from '../types-Bi4ZVKvN.js';
2
+ import { S as SpeechInputProvider } from '../types-BiKUg5nY.js';
3
+ export { D as DictationControl, a as SPEECH_INPUT_SAMPLE_RATE, b as SpeechInputCapability, c as SpeechInputProviderFactory, d as SpeechInputReadiness, e as SpeechInputReadinessState, f as SpeechInputSegment, g as SpeechInputTranscribeOptions, h as SpeechInputTranscript, i as SpeechInputWord, j as SpeechRecognizerAccess, k as isSpeechInputProviderFactory, r as resolveSpeechInputProvider } from '../types-BiKUg5nY.js';
4
4
  import '../screenRegion-Ci_RsCzG.js';
5
5
 
6
6
  /**
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  encodeMonoPcm16Wav,
3
3
  startDictationSession
4
- } from "../chunk-2MP7QE7O.js";
4
+ } from "../chunk-7MAL27C2.js";
5
5
  import {
6
6
  SPEECH_INPUT_SAMPLE_RATE,
7
7
  isSpeechInputProviderFactory,
@@ -1,11 +1,13 @@
1
1
  import {
2
+ decodeSpeechPcm,
2
3
  downmixToMono,
3
4
  encodeMonoPcm16Wav,
4
5
  microphoneTakeAsWav,
5
6
  resampleLinear
6
- } from "./chunk-K56KR27T.js";
7
+ } from "./chunk-ZEWQ7DTP.js";
7
8
  import "./chunk-DNEVUOBZ.js";
8
9
  export {
10
+ decodeSpeechPcm,
9
11
  downmixToMono,
10
12
  encodeMonoPcm16Wav,
11
13
  microphoneTakeAsWav,
@@ -15143,6 +15143,40 @@ button.squisq-view-menu-radio-row:focus-visible {
15143
15143
  .squisq-document-tray-detail button:hover {
15144
15144
  background: var(--squisq-danger-soft);
15145
15145
  }
15146
+ .squisq-document-tray-timing {
15147
+ display: flex;
15148
+ flex-wrap: wrap;
15149
+ align-items: center;
15150
+ gap: 6px 10px;
15151
+ margin-top: 8px;
15152
+ color: var(--squisq-text-muted);
15153
+ font-size: 12px;
15154
+ }
15155
+ .squisq-document-tray-timing button {
15156
+ flex-shrink: 0;
15157
+ border: 1px solid var(--squisq-border);
15158
+ border-radius: 4px;
15159
+ padding: 4px 10px;
15160
+ background: var(--squisq-surface);
15161
+ color: var(--squisq-text-soft);
15162
+ font: inherit;
15163
+ cursor: pointer;
15164
+ }
15165
+ .squisq-document-tray-timing button:hover {
15166
+ background: var(--squisq-surface-hover);
15167
+ }
15168
+ .squisq-document-tray-timing button:focus-visible {
15169
+ outline: 2px solid var(--squisq-accent);
15170
+ outline-offset: 2px;
15171
+ }
15172
+ .squisq-document-tray-timing progress {
15173
+ flex: 1 1 120px;
15174
+ max-width: 240px;
15175
+ accent-color: var(--squisq-accent);
15176
+ }
15177
+ .squisq-document-tray-timing-error {
15178
+ color: var(--squisq-danger-strong);
15179
+ }
15146
15180
 
15147
15181
  /* src/styles/code-context.css */
15148
15182
  .squisq-ccx-zone {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TeleprompterView
3
- } from "../chunk-DJS5I5NO.js";
3
+ } from "../chunk-PQSFLC2R.js";
4
4
  import {
5
5
  DEFAULT_TELEPROMPTER_PREFS,
6
6
  EYE_LINE_FRACTION,
@@ -31,8 +31,9 @@ import {
31
31
  useNarrationStage,
32
32
  useTeleprompter,
33
33
  vadConfigForSensitivity
34
- } from "../chunk-ZS6G2P57.js";
34
+ } from "../chunk-CWSV5YGZ.js";
35
35
  import "../chunk-DNJ6HOES.js";
36
+ import "../chunk-GG35EBRB.js";
36
37
  export {
37
38
  DEFAULT_TELEPROMPTER_PREFS,
38
39
  EYE_LINE_FRACTION,
@@ -48,15 +48,36 @@ interface SpeechInputTranscribeOptions {
48
48
  readonly prompt?: string;
49
49
  /** BCP-47-ish language hint. The editor does not currently set it. */
50
50
  readonly language?: string;
51
+ /**
52
+ * Ask for {@link SpeechInputTranscript.segments} with times (and word times
53
+ * where the engine has them). Set when timing narration from its audio;
54
+ * dictation leaves it off. A provider that cannot time its output ignores it.
55
+ */
56
+ readonly timestamps?: boolean;
51
57
  /**
52
58
  * Aborted when the user cancels dictation or the editor unmounts. A
53
59
  * provider should stop promptly; a rejection after abort is ignored.
54
60
  */
55
61
  readonly signal: AbortSignal;
56
62
  }
63
+ /** A recognized word, in seconds from the start of the take. */
64
+ interface SpeechInputWord {
65
+ readonly start: number;
66
+ readonly end: number;
67
+ readonly text: string;
68
+ }
69
+ /** A recognized phrase, in seconds from the start of the take. */
70
+ interface SpeechInputSegment {
71
+ readonly start: number;
72
+ readonly end: number;
73
+ readonly text: string;
74
+ readonly words?: readonly SpeechInputWord[];
75
+ }
57
76
  interface SpeechInputTranscript {
58
77
  /** Recognized text. Blank (or Whisper's `[BLANK_AUDIO]`) means silence. */
59
78
  readonly text: string;
79
+ /** Timed phrases, when `timestamps` was requested and the engine supports it. */
80
+ readonly segments?: readonly SpeechInputSegment[];
60
81
  }
61
82
  interface SpeechInputProvider {
62
83
  /** Stable identifier, e.g. `whisper-local`. */
@@ -80,6 +101,17 @@ interface SpeechInputProvider {
80
101
  /** Release resources. Called by the editor only for a provider it created from a factory. */
81
102
  dispose?(): void;
82
103
  }
104
+ /**
105
+ * The host's resolved recognizer on the editor context
106
+ * (`useEditorContext().speechRecognizer`), for features beyond dictation that
107
+ * transcribe recorded audio — e.g. timing a document narration from its take.
108
+ * `null` when the host injected no `speechInput` capability.
109
+ */
110
+ interface SpeechRecognizerAccess {
111
+ readonly provider: SpeechInputProvider;
112
+ /** Last known readiness; `null` until the provider first answers. */
113
+ readonly readiness: SpeechInputReadiness | null;
114
+ }
83
115
  /**
84
116
  * Deferred construction. The shell creates the provider when it mounts with
85
117
  * the capability (it needs `status()` to decide whether to render the
@@ -123,4 +155,4 @@ interface DictationControl {
123
155
  toggle(): void;
124
156
  }
125
157
 
126
- export { type DictationControl as D, type SpeechInputProvider as S, SPEECH_INPUT_SAMPLE_RATE as a, type SpeechInputCapability as b, type SpeechInputProviderFactory as c, type SpeechInputReadiness as d, type SpeechInputReadinessState as e, type SpeechInputTranscribeOptions as f, type SpeechInputTranscript as g, isSpeechInputProviderFactory as i, resolveSpeechInputProvider as r };
158
+ export { type DictationControl as D, type SpeechInputProvider as S, SPEECH_INPUT_SAMPLE_RATE as a, type SpeechInputCapability as b, type SpeechInputProviderFactory as c, type SpeechInputReadiness as d, type SpeechInputReadinessState as e, type SpeechInputSegment as f, type SpeechInputTranscribeOptions as g, type SpeechInputTranscript as h, type SpeechInputWord as i, type SpeechRecognizerAccess as j, isSpeechInputProviderFactory as k, resolveSpeechInputProvider as r };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq-editor-react",
3
- "version": "2.13.7",
3
+ "version": "2.13.9",
4
4
  "description": "React editor shell with raw/WYSIWYG/preview modes for Squisq documents",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -102,12 +102,12 @@
102
102
  }
103
103
  },
104
104
  "dependencies": {
105
- "@bendyline/squisq": "2.13.5",
105
+ "@bendyline/squisq": "2.13.6",
106
106
  "@bendyline/squisq-calc": "2.11.2",
107
- "@bendyline/squisq-formats": "2.8.6",
108
- "@bendyline/squisq-grid-react": "2.11.34",
109
- "@bendyline/squisq-react": "2.12.14",
110
- "@bendyline/squisq-video-react": "2.5.15",
107
+ "@bendyline/squisq-formats": "2.8.7",
108
+ "@bendyline/squisq-grid-react": "2.11.35",
109
+ "@bendyline/squisq-react": "2.12.16",
110
+ "@bendyline/squisq-video-react": "2.5.17",
111
111
  "@fortawesome/fontawesome-free": "7.3.1",
112
112
  "@tiptap/core": "2.27.3",
113
113
  "@tiptap/extension-heading": "2.27.3",