@bendyline/squisq-editor-react 2.11.16 → 2.11.17

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.
@@ -178,7 +178,9 @@ async function registerPcmWorklet(ctx) {
178
178
 
179
179
  // src/teleprompter/useMicAnalysis.ts
180
180
  import { useCallback, useEffect as useEffect2, useRef, useState } from "react";
181
- function useMicAnalysis(constraints) {
181
+ function useMicAnalysis(constraints, externalStream) {
182
+ const externalStreamRef = useRef(externalStream);
183
+ externalStreamRef.current = externalStream;
182
184
  const [status, setStatus] = useState("idle");
183
185
  const [error, setError] = useState(null);
184
186
  const [devices, setDevices] = useState([]);
@@ -220,7 +222,7 @@ function useMicAnalysis(constraints) {
220
222
  graph.sink?.disconnect();
221
223
  } catch {
222
224
  }
223
- for (const track of graph.stream.getTracks()) track.stop();
225
+ if (graph.ownsStream) for (const track of graph.stream.getTracks()) track.stop();
224
226
  void graph.context.close().catch(() => void 0);
225
227
  }, []);
226
228
  const stop = useCallback(() => {
@@ -238,17 +240,26 @@ function useMicAnalysis(constraints) {
238
240
  teardown();
239
241
  setStatus("starting");
240
242
  setError(null);
243
+ const ownsStream = externalStreamRef.current === void 0;
244
+ let acquiredStream = null;
245
+ let acquiredContext = null;
241
246
  try {
247
+ if (!ownsStream && !externalStreamRef.current?.getAudioTracks().length) {
248
+ setStatus("idle");
249
+ return null;
250
+ }
242
251
  const baseConstraints = constraintsRef.current;
243
- const micStream = await requestMicStream({
252
+ const micStream = externalStreamRef.current ?? await requestMicStream({
244
253
  ...baseConstraints ?? {},
245
254
  ...deviceId ? { deviceId: { exact: deviceId } } : {}
246
255
  });
256
+ acquiredStream = micStream;
247
257
  if (generation !== generationRef.current) {
248
- for (const track of micStream.getTracks()) track.stop();
258
+ if (ownsStream) for (const track of micStream.getTracks()) track.stop();
249
259
  return null;
250
260
  }
251
261
  const context = new AudioContext();
262
+ acquiredContext = context;
252
263
  if (context.state === "suspended") {
253
264
  await context.resume().catch(() => void 0);
254
265
  }
@@ -297,17 +308,19 @@ function useMicAnalysis(constraints) {
297
308
  node = processor;
298
309
  }
299
310
  if (generation !== generationRef.current) {
300
- for (const track of micStream.getTracks()) track.stop();
311
+ if (ownsStream) for (const track of micStream.getTracks()) track.stop();
301
312
  void context.close().catch(() => void 0);
302
313
  return null;
303
314
  }
304
- graphRef.current = { stream: micStream, context, source, node, sink };
315
+ graphRef.current = { stream: micStream, ownsStream, context, source, node, sink };
305
316
  setStream(micStream);
306
317
  setSampleRate(context.sampleRate);
307
318
  setStatus("live");
308
319
  void refreshDevices();
309
320
  return micStream;
310
321
  } catch (err) {
322
+ if (ownsStream) for (const track of acquiredStream?.getTracks() ?? []) track.stop();
323
+ if (acquiredContext) void acquiredContext.close().catch(() => void 0);
311
324
  if (generation !== generationRef.current) return null;
312
325
  setError(err instanceof Error ? err : new Error(String(err)));
313
326
  setStatus("error");
@@ -316,6 +329,12 @@ function useMicAnalysis(constraints) {
316
329
  },
317
330
  [refreshDevices, teardown]
318
331
  );
332
+ useEffect2(() => {
333
+ if (externalStream === void 0) return;
334
+ if (externalStream?.getAudioTracks().length) void start(null);
335
+ else stop();
336
+ return stop;
337
+ }, [externalStream, start, stop]);
319
338
  const subscribeHop = useCallback((listener) => {
320
339
  listenersRef.current.add(listener);
321
340
  return () => {
@@ -333,7 +352,17 @@ function useMicAnalysis(constraints) {
333
352
  teardown();
334
353
  };
335
354
  }, [teardown]);
336
- return { status, error, stream, sampleRate, devices, start, stop, subscribeHop };
355
+ return {
356
+ status,
357
+ error,
358
+ stream,
359
+ sampleRate,
360
+ devices,
361
+ sourceManaged: externalStream !== void 0,
362
+ start,
363
+ stop,
364
+ subscribeHop
365
+ };
337
366
  }
338
367
 
339
368
  // src/teleprompter/types.ts
@@ -465,7 +494,7 @@ function useTeleprompter(opts) {
465
494
  const [transport, setTransport] = useState2("stopped");
466
495
  const [countdownRemaining, setCountdownRemaining] = useState2(null);
467
496
  const [view, setView] = useState2({ wordPos: 0, micLevel: 0, voiceActive: false });
468
- const mic = useMicAnalysis(opts.micConstraints);
497
+ const mic = useMicAnalysis(opts.micConstraints, opts.analysisStream);
469
498
  const scriptRef = useRef2(script);
470
499
  const prefsRef = useRef2(prefs);
471
500
  const transportRef = useRef2(transport);
@@ -1851,7 +1880,11 @@ import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMem
1851
1880
  import { wordIndexAtTime } from "@bendyline/squisq/narration";
1852
1881
  function useNarrationStage(opts) {
1853
1882
  const { doc, recording = null, getAudioBasename } = opts;
1854
- const controller = useTeleprompter({ doc, micConstraints: opts.micConstraints });
1883
+ const controller = useTeleprompter({
1884
+ doc,
1885
+ micConstraints: opts.micConstraints,
1886
+ analysisStream: opts.analysisStream
1887
+ });
1855
1888
  const float = useFloatingWindow(TELEPROMPTER_CSS);
1856
1889
  const controllerRef = useRef5(controller);
1857
1890
  controllerRef.current = controller;
@@ -2325,6 +2358,8 @@ function TeleprompterControls({
2325
2358
  {
2326
2359
  "aria-label": "Microphone",
2327
2360
  value: prefs.micDeviceId ?? "",
2361
+ disabled: mic.sourceManaged,
2362
+ title: mic.sourceManaged ? "Microphone selected in recording settings" : void 0,
2328
2363
  onChange: (e) => setPrefs({ micDeviceId: e.target.value || null }),
2329
2364
  children: [
2330
2365
  /* @__PURE__ */ jsx2("option", { value: "", children: "Default mic" }),
package/dist/index.d.ts CHANGED
@@ -26,9 +26,9 @@ import { FenceRendererMap } from '@bendyline/squisq/fence';
26
26
  import { TableViewState } from '@bendyline/squisq/table';
27
27
  import { EditJournal } from '@bendyline/squisq-grid-react';
28
28
  export { JsonEditor, JsonEditorProps } from './json-editor/index.js';
29
- export { C as CameraStreamOptions, D as DEFAULT_RECORDER_DEVICE_SETTINGS, M as MediaDeviceInventory, R as RecordedBookmark, a as RecordedMediaKind, b as RecorderAudioDeviceSettings, c as RecorderButton, d as RecorderButtonProps, e as RecorderCameraDeviceSettings, f as RecorderCameraSaveResult, g as RecorderColorScheme, h as RecorderConstraintMode, i as RecorderDeviceOption, j as RecorderDeviceQuickPicks, k as RecorderDeviceQuickPicksProps, l as RecorderDeviceSettings, m as RecorderDeviceSettingsPanel, n as RecorderDeviceSettingsPanelProps, o as RecorderEncodingSettings, p as RecorderModal, q as RecorderModalProps, r as RecorderNarrationOptions, s as RecorderPanel, t as RecorderPanelProps, u as RecorderSaveResult, v as RecorderScreenAudioSettings, w as RecorderScreenDeviceSettings, x as RecorderSlidesOptions, S as ScreenStreamHandle, y as ScreenStreamOptions, z as SlideTimingSaveResult, T as TimingJson, A as buildRecorderAudioConstraints, B as buildRecorderCameraConstraints, E as buildRecorderScreenAudioConstraints, F as buildRecorderScreenConstraints, G as buildTimingJson, H as encodeTimingJson, I as hasRecorderDeviceChoice, J as recordedMediaKind, K as recorderBitsPerSecond, L as recorderDeviceGroups, N as recorderDeviceOptions, O as requestCameraStream, P as requestMicStream, Q as requestScreenStream, U as timingPathFor, V as useMediaDevices, W as useStreamPreview } from './recorder-C6ArYQBa.js';
30
- import { R as RecorderState } from './useNarrationStage-DopDr_P5.js';
31
- export { C as CanvasSink, a as CaptureKind, D as DEFAULT_TELEPROMPTER_PREFS, F as FloatOpenOptions, b as FloatTier, c as FloatingWindowHandle, d as FloatingWindowManager, M as MicAnalysisHandle, e as MicAnalysisStatus, N as NarrationRecorderController, f as NarrationRecorderState, g as NarrationStageHandle, h as NarrationTake, P as PrompterTransport, i as RecorderAudioBitrateMode, j as RecorderCameraLane, k as RecorderExtendedMediaOptions, l as RecorderSource, m as ResolvedFormat, T as TeleprompterController, n as TeleprompterPrefs, o as TeleprompterRecordingDeps, U as UseMediaRecorderOptions, p as UseMediaRecorderResult, q as UseNarrationStageOptions, r as buildFilename, s as createFloatingWindowManager, t as detectFloatTiers, u as getCaptureKind, v as resolveFormat, w as supportsDisplayMedia, x as supportsMediaRecorder, y as supportsUserMedia, z as useFloatingWindow, A as useMediaRecorder, B as useMicAnalysis, E as useNarrationRecorder, G as useNarrationStage, H as useTeleprompter, I as vadConfigForSensitivity } from './useNarrationStage-DopDr_P5.js';
29
+ export { C as CameraStreamOptions, D as DEFAULT_RECORDER_DEVICE_SETTINGS, M as MediaDeviceInventory, R as RecordedBookmark, a as RecordedMediaKind, b as RecorderAudioDeviceSettings, c as RecorderButton, d as RecorderButtonProps, e as RecorderCameraDeviceSettings, f as RecorderCameraSaveResult, g as RecorderColorScheme, h as RecorderConstraintMode, i as RecorderDeviceOption, j as RecorderDeviceQuickPicks, k as RecorderDeviceQuickPicksProps, l as RecorderDeviceSettings, m as RecorderDeviceSettingsPanel, n as RecorderDeviceSettingsPanelProps, o as RecorderEncodingSettings, p as RecorderModal, q as RecorderModalProps, r as RecorderNarrationOptions, s as RecorderPanel, t as RecorderPanelProps, u as RecorderSaveResult, v as RecorderScreenAudioSettings, w as RecorderScreenDeviceSettings, x as RecorderSlidesOptions, S as ScreenStreamHandle, y as ScreenStreamOptions, z as SlideTimingSaveResult, T as TimingJson, A as buildRecorderAudioConstraints, B as buildRecorderCameraConstraints, E as buildRecorderScreenAudioConstraints, F as buildRecorderScreenConstraints, G as buildTimingJson, H as encodeTimingJson, I as hasRecorderDeviceChoice, J as recordedMediaKind, K as recorderBitsPerSecond, L as recorderDeviceGroups, N as recorderDeviceOptions, O as requestCameraStream, P as requestMicStream, Q as requestScreenStream, U as timingPathFor, V as useMediaDevices, W as useStreamPreview } from './recorder-CRsw0eC7.js';
30
+ import { R as RecorderState } from './useNarrationStage-vKtk6H-V.js';
31
+ export { C as CanvasSink, a as CaptureKind, D as DEFAULT_TELEPROMPTER_PREFS, F as FloatOpenOptions, b as FloatTier, c as FloatingWindowHandle, d as FloatingWindowManager, M as MicAnalysisHandle, e as MicAnalysisStatus, N as NarrationRecorderController, f as NarrationRecorderState, g as NarrationStageHandle, h as NarrationTake, P as PrompterTransport, i as RecorderAudioBitrateMode, j as RecorderCameraLane, k as RecorderExtendedMediaOptions, l as RecorderSource, m as ResolvedFormat, T as TeleprompterController, n as TeleprompterPrefs, o as TeleprompterRecordingDeps, U as UseMediaRecorderOptions, p as UseMediaRecorderResult, q as UseNarrationStageOptions, r as buildFilename, s as createFloatingWindowManager, t as detectFloatTiers, u as getCaptureKind, v as resolveFormat, w as supportsDisplayMedia, x as supportsMediaRecorder, y as supportsUserMedia, z as useFloatingWindow, A as useMediaRecorder, B as useMicAnalysis, E as useNarrationRecorder, G as useNarrationStage, H as useTeleprompter, I as vadConfigForSensitivity } from './useNarrationStage-vKtk6H-V.js';
32
32
  export { EYE_LINE_FRACTION, NarrationSavePlan, NarrationSaveResult, NarrationStage, NarrationStageProps, PCM_WORKLET_NAME, PCM_WORKLET_SOURCE, TELEPROMPTER_CSS, TeleprompterControls, TeleprompterControlsProps, TeleprompterSurface, TeleprompterSurfaceProps, TeleprompterView, TeleprompterViewProps, TokenLineMap, buildNarrationSavePlan, cameraVideoLine, ensureTeleprompterStyles, executeNarrationSave, insertNarrationPreamble, measureTokenLines, narrationAnnotationLine, prompterVarsFromTheme, registerPcmWorklet, stepScroll, targetOffsetFor } from './teleprompter/index.js';
33
33
  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-De4z-gGb.js';
34
34
  export { createHarperProofingProvider } from './proofing/index.js';
@@ -3303,24 +3303,16 @@ declare function RecorderSlidesPanel({ slides, index, onIndexChange, viewport, b
3303
3303
  * of `narrationModePolicy.ts`, kept DOM/MediaRecorder-free so every branch is
3304
3304
  * unit-testable.
3305
3305
  *
3306
- * Slides mode and narration mode both expand the dialog and both claim its
3307
- * right column, so they are mutually exclusive. That exclusion is STRUCTURAL:
3308
- * the dialog holds one {@link RecorderPanelMode} rather than two booleans, so
3309
- * checking one box cannot leave the other checked. There is deliberately no
3310
- * "are these compatible?" predicate to keep in sync.
3306
+ * Narration and slides are independent optional panels. The combined state
3307
+ * keeps both visible without changing which pipeline owns a take.
3311
3308
  */
3312
3309
 
3313
- /** Which optional panel occupies the dialog's right column. */
3314
- type RecorderPanelMode = 'none' | 'narration' | 'slides';
3315
- /** Whether the dialog should render in its full-viewport two-column form. */
3310
+ /** Which optional panels appear beside the capture controls. */
3311
+ type RecorderPanelMode = 'none' | 'narration' | 'slides' | 'both';
3312
+ /** Whether the dialog should render in its full-viewport form. */
3316
3313
  declare function isExpandedPanel(mode: RecorderPanelMode): boolean;
3317
3314
  /**
3318
- * The mode after toggling one of the two checkboxes. Checking a box selects
3319
- * it (deselecting the other by construction); unchecking returns to 'none'.
3320
- *
3321
- * Unchecking a box that is not the active mode is a no-op rather than a
3322
- * collapse — a stale change event from the box that was just superseded must
3323
- * not close the panel the user actually opened.
3315
+ * Toggle only the requested panel, preserving the other checkbox.
3324
3316
  */
3325
3317
  declare function panelModeAfterToggle(current: RecorderPanelMode, target: 'narration' | 'slides', checked: boolean): RecorderPanelMode;
3326
3318
  /** Clamp a slide index into `[0, count - 1]`, or 0 for an empty deck. */
package/dist/index.js CHANGED
@@ -219,7 +219,7 @@ import {
219
219
  useTreeViewData,
220
220
  viewStateFromBinding,
221
221
  writeHeadingViewState
222
- } from "./chunk-5TYCTE33.js";
222
+ } from "./chunk-US5DALTZ.js";
223
223
  import {
224
224
  EditorProvider,
225
225
  detectLanguageFromFileName,
@@ -231,18 +231,18 @@ import {
231
231
  spliceBlock,
232
232
  useBlockNavigator,
233
233
  useEditorContext
234
- } from "./chunk-DNB32SU2.js";
234
+ } from "./chunk-HTO5MTTJ.js";
235
235
  import "./chunk-V4NBQF5C.js";
236
236
  import {
237
237
  JsonEditor
238
- } from "./chunk-WDQFWD7Z.js";
238
+ } from "./chunk-UBFNLWCN.js";
239
239
  import {
240
240
  MarkdownEscape
241
- } from "./chunk-ZDWZRVAY.js";
241
+ } from "./chunk-2XXBDECJ.js";
242
242
  import {
243
243
  markdownToTiptap,
244
244
  tiptapToMarkdown
245
- } from "./chunk-EN5BYZOE.js";
245
+ } from "./chunk-DPAMJV6A.js";
246
246
  import {
247
247
  ImageEditor,
248
248
  ImageViewer,
@@ -252,7 +252,7 @@ import {
252
252
  } from "./chunk-K4Z2GIX4.js";
253
253
  import {
254
254
  RecorderButton
255
- } from "./chunk-27WOOJXR.js";
255
+ } from "./chunk-EJKGXWNK.js";
256
256
  import {
257
257
  DEFAULT_RECORDER_DEVICE_SETTINGS,
258
258
  RecorderDeviceQuickPicks,
@@ -283,11 +283,11 @@ import {
283
283
  unshownSlidesWarning,
284
284
  useMediaDevices,
285
285
  useMediaRecorder
286
- } from "./chunk-4D7QMRTZ.js";
286
+ } from "./chunk-C6QVA6TO.js";
287
287
  import "./chunk-GS7QWYFT.js";
288
288
  import {
289
289
  TeleprompterView
290
- } from "./chunk-ENDRPV6T.js";
290
+ } from "./chunk-QZWFSSDE.js";
291
291
  import {
292
292
  DEFAULT_TELEPROMPTER_PREFS,
293
293
  EYE_LINE_FRACTION,
@@ -327,7 +327,7 @@ import {
327
327
  useStreamPreview,
328
328
  useTeleprompter,
329
329
  vadConfigForSensitivity
330
- } from "./chunk-34FRFDQ6.js";
330
+ } from "./chunk-XZVMKR5Q.js";
331
331
 
332
332
  // src/FolderView.tsx
333
333
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -428,7 +428,7 @@ function ViewSwitcher({ className }) {
428
428
  "aria-selected": activeView === view.id,
429
429
  className: `squisq-view-tab ${activeView === view.id ? "squisq-view-tab--active" : ""}`,
430
430
  onClick: () => setActiveView(view.id),
431
- title: `${view.label} (${platformShortcut(view.shortcutKey)})`,
431
+ title: `${view.label} (${platformShortcut(`Shift+${view.shortcutKey}`)})`,
432
432
  children: [
433
433
  /* @__PURE__ */ jsx2("span", { className: "squisq-view-tab-label squisq-view-tab-label--long", children: view.label }),
434
434
  view.shortLabel && view.shortLabel !== view.label && /* @__PURE__ */ jsx2("span", { className: "squisq-view-tab-label squisq-view-tab-label--short", children: view.shortLabel })
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  JsonEditor
3
- } from "../chunk-WDQFWD7Z.js";
4
- import "../chunk-ZDWZRVAY.js";
5
- import "../chunk-EN5BYZOE.js";
3
+ } from "../chunk-UBFNLWCN.js";
4
+ import "../chunk-2XXBDECJ.js";
5
+ import "../chunk-DPAMJV6A.js";
6
6
  export {
7
7
  JsonEditor
8
8
  };
@@ -1,5 +1,5 @@
1
- export { C as CameraStreamOptions, D as DEFAULT_RECORDER_DEVICE_SETTINGS, M as MediaDeviceInventory, R as RecordedBookmark, a as RecordedMediaKind, b as RecorderAudioDeviceSettings, c as RecorderButton, d as RecorderButtonProps, e as RecorderCameraDeviceSettings, f as RecorderCameraSaveResult, g as RecorderColorScheme, h as RecorderConstraintMode, i as RecorderDeviceOption, j as RecorderDeviceQuickPicks, k as RecorderDeviceQuickPicksProps, l as RecorderDeviceSettings, m as RecorderDeviceSettingsPanel, n as RecorderDeviceSettingsPanelProps, o as RecorderEncodingSettings, p as RecorderModal, q as RecorderModalProps, s as RecorderPanel, t as RecorderPanelProps, u as RecorderSaveResult, v as RecorderScreenAudioSettings, w as RecorderScreenDeviceSettings, S as ScreenStreamHandle, y as ScreenStreamOptions, T as TimingJson, A as buildRecorderAudioConstraints, B as buildRecorderCameraConstraints, E as buildRecorderScreenAudioConstraints, F as buildRecorderScreenConstraints, G as buildTimingJson, H as encodeTimingJson, I as hasRecorderDeviceChoice, J as recordedMediaKind, K as recorderBitsPerSecond, L as recorderDeviceGroups, N as recorderDeviceOptions, O as requestCameraStream, P as requestMicStream, Q as requestScreenStream, U as timingPathFor, V as useMediaDevices, W as useStreamPreview } from '../recorder-C6ArYQBa.js';
2
- export { a as CaptureKind, i as RecorderAudioBitrateMode, j as RecorderCameraLane, k as RecorderExtendedMediaOptions, l as RecorderSource, R as RecorderState, m as ResolvedFormat, U as UseMediaRecorderOptions, p as UseMediaRecorderResult, r as buildFilename, u as getCaptureKind, v as resolveFormat, w as supportsDisplayMedia, x as supportsMediaRecorder, y as supportsUserMedia, A as useMediaRecorder } from '../useNarrationStage-DopDr_P5.js';
1
+ export { C as CameraStreamOptions, D as DEFAULT_RECORDER_DEVICE_SETTINGS, M as MediaDeviceInventory, R as RecordedBookmark, a as RecordedMediaKind, b as RecorderAudioDeviceSettings, c as RecorderButton, d as RecorderButtonProps, e as RecorderCameraDeviceSettings, f as RecorderCameraSaveResult, g as RecorderColorScheme, h as RecorderConstraintMode, i as RecorderDeviceOption, j as RecorderDeviceQuickPicks, k as RecorderDeviceQuickPicksProps, l as RecorderDeviceSettings, m as RecorderDeviceSettingsPanel, n as RecorderDeviceSettingsPanelProps, o as RecorderEncodingSettings, p as RecorderModal, q as RecorderModalProps, s as RecorderPanel, t as RecorderPanelProps, u as RecorderSaveResult, v as RecorderScreenAudioSettings, w as RecorderScreenDeviceSettings, S as ScreenStreamHandle, y as ScreenStreamOptions, T as TimingJson, A as buildRecorderAudioConstraints, B as buildRecorderCameraConstraints, E as buildRecorderScreenAudioConstraints, F as buildRecorderScreenConstraints, G as buildTimingJson, H as encodeTimingJson, I as hasRecorderDeviceChoice, J as recordedMediaKind, K as recorderBitsPerSecond, L as recorderDeviceGroups, N as recorderDeviceOptions, O as requestCameraStream, P as requestMicStream, Q as requestScreenStream, U as timingPathFor, V as useMediaDevices, W as useStreamPreview } from '../recorder-CRsw0eC7.js';
2
+ export { a as CaptureKind, i as RecorderAudioBitrateMode, j as RecorderCameraLane, k as RecorderExtendedMediaOptions, l as RecorderSource, R as RecorderState, m as ResolvedFormat, U as UseMediaRecorderOptions, p as UseMediaRecorderResult, r as buildFilename, u as getCaptureKind, v as resolveFormat, w as supportsDisplayMedia, x as supportsMediaRecorder, y as supportsUserMedia, A as useMediaRecorder } from '../useNarrationStage-vKtk6H-V.js';
3
3
  import 'react/jsx-runtime';
4
4
  import '@bendyline/squisq/schemas';
5
5
  import '@bendyline/squisq/storage';
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  RecorderButton
3
- } from "../chunk-27WOOJXR.js";
3
+ } from "../chunk-EJKGXWNK.js";
4
4
  import {
5
5
  DEFAULT_RECORDER_DEVICE_SETTINGS,
6
6
  RecorderDeviceQuickPicks,
@@ -20,7 +20,7 @@ import {
20
20
  requestScreenStream,
21
21
  useMediaDevices,
22
22
  useMediaRecorder
23
- } from "../chunk-4D7QMRTZ.js";
23
+ } from "../chunk-C6QVA6TO.js";
24
24
  import "../chunk-GS7QWYFT.js";
25
25
  import {
26
26
  buildFilename,
@@ -34,7 +34,7 @@ import {
34
34
  supportsUserMedia,
35
35
  timingPathFor,
36
36
  useStreamPreview
37
- } from "../chunk-34FRFDQ6.js";
37
+ } from "../chunk-XZVMKR5Q.js";
38
38
  export {
39
39
  DEFAULT_RECORDER_DEVICE_SETTINGS,
40
40
  RecorderButton,
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { Doc, Theme, ViewportConfig, MediaProvider } from '@bendyline/squisq/schemas';
3
3
  import { ContentContainer } from '@bendyline/squisq/storage';
4
- import { l as RecorderSource, o as TeleprompterRecordingDeps, S as ScreenCaptureRegion } from './useNarrationStage-DopDr_P5.js';
4
+ import { l as RecorderSource, o as TeleprompterRecordingDeps, S as ScreenCaptureRegion } from './useNarrationStage-vKtk6H-V.js';
5
5
  import { CSSProperties, RefObject } from 'react';
6
6
 
7
7
  type RecordedMediaKind = 'audio' | 'video';
@@ -24,17 +24,18 @@ declare function recordedMediaKind(source: RecorderSource, stream: {
24
24
  type RecorderColorScheme = 'light' | 'dark';
25
25
  /**
26
26
  * Everything narration mode needs beyond the base recorder props. Supplying
27
- * this (with non-null `recording`) surfaces the "Show narration mode"
27
+ * this (with non-null `doc`) surfaces the "Show narration mode"
28
28
  * checkbox; when checked, the dialog expands and mounts the teleprompter
29
29
  * beside the capture preview, and mic recording switches to the narration
30
- * pipeline (voice-aligned v3 timing sidecar + document preamble insertion).
30
+ * pipeline when `recording` is provided. With null `recording`, the stage
31
+ * tracks the existing capture audio and the host keeps its save pipeline.
31
32
  */
32
33
  interface RecorderNarrationOptions {
33
34
  /** Parsed document the prompter script is built from. */
34
35
  doc: Doc | null;
35
36
  /** Theme for the prompter surface (colors/fonts). */
36
37
  theme: Theme;
37
- /** Editor plumbing for the narration save pipeline; null hides the checkbox. */
38
+ /** Editor plumbing for aligned narration saves; null keeps the capture recorder and host onSave. */
38
39
  recording: TeleprompterRecordingDeps | null;
39
40
  }
40
41
  /**
@@ -89,7 +90,7 @@ interface RecorderModalProps {
89
90
  * markdown reference at the cursor — see {@link RecorderSaveResult}
90
91
  * for the fields a host needs to build that reference.
91
92
  *
92
- * NOT fired for narration-mode saves: the narration pipeline writes its
93
+ * NOT fired for aligned narration saves (non-null narration.recording): that pipeline writes its
93
94
  * own `{[audio …]}` document preamble (via `executeNarrationSave`), so a
94
95
  * host insertion here would double up.
95
96
  */
@@ -19,8 +19,8 @@ import {
19
19
  updateReviewDecorations,
20
20
  useReview,
21
21
  useReviewState
22
- } from "../chunk-DNB32SU2.js";
23
- import "../chunk-EN5BYZOE.js";
22
+ } from "../chunk-HTO5MTTJ.js";
23
+ import "../chunk-DPAMJV6A.js";
24
24
 
25
25
  // src/review/proofingAdapter.ts
26
26
  import {
@@ -5,19 +5,19 @@ import {
5
5
  RawEditor,
6
6
  WysiwygEditor,
7
7
  useEditorContextMenuItems
8
- } from "../chunk-5TYCTE33.js";
8
+ } from "../chunk-US5DALTZ.js";
9
9
  import {
10
10
  EditorProvider,
11
11
  useEditorContext
12
- } from "../chunk-DNB32SU2.js";
12
+ } from "../chunk-HTO5MTTJ.js";
13
13
  import "../chunk-V4NBQF5C.js";
14
- import "../chunk-ZDWZRVAY.js";
15
- import "../chunk-EN5BYZOE.js";
14
+ import "../chunk-2XXBDECJ.js";
15
+ import "../chunk-DPAMJV6A.js";
16
16
  import "../chunk-K4Z2GIX4.js";
17
- import "../chunk-4D7QMRTZ.js";
17
+ import "../chunk-C6QVA6TO.js";
18
18
  import "../chunk-GS7QWYFT.js";
19
- import "../chunk-ENDRPV6T.js";
20
- import "../chunk-34FRFDQ6.js";
19
+ import "../chunk-QZWFSSDE.js";
20
+ import "../chunk-XZVMKR5Q.js";
21
21
  export {
22
22
  EditorContextMenuProvider,
23
23
  EditorProvider,
@@ -8710,35 +8710,6 @@
8710
8710
  height: 100%;
8711
8711
  display: block;
8712
8712
  }
8713
- .squisq-template-gallery-content-preview-fallback {
8714
- display: flex;
8715
- align-items: center;
8716
- justify-content: center;
8717
- width: 100%;
8718
- height: 100%;
8719
- }
8720
- .squisq-template-gallery-content-preview-fallback > svg {
8721
- width: 100%;
8722
- height: 100%;
8723
- display: block;
8724
- }
8725
- .squisq-template-gallery-content-preview-warning {
8726
- position: absolute;
8727
- left: 50%;
8728
- top: 50%;
8729
- max-width: calc(100% - 16px);
8730
- padding: 3px 6px;
8731
- transform: translate(-50%, -50%);
8732
- border: 1px solid var(--squisq-template-gallery-preview-warning-border);
8733
- border-radius: 4px;
8734
- background: var(--squisq-template-gallery-preview-warning-bg);
8735
- color: var(--squisq-template-gallery-preview-warning-text);
8736
- font-size: 10px;
8737
- font-weight: 600;
8738
- line-height: 1.2;
8739
- text-align: center;
8740
- pointer-events: none;
8741
- }
8742
8713
  .squisq-template-gallery-card-body {
8743
8714
  display: flex;
8744
8715
  flex-direction: column;
@@ -15543,6 +15514,43 @@
15543
15514
  .squisq-image-edit-modal__error {
15544
15515
  color: var(--squisq-danger);
15545
15516
  }
15517
+ .squisq-image-placeholder {
15518
+ display: flex;
15519
+ align-items: center;
15520
+ gap: 12px;
15521
+ box-sizing: border-box;
15522
+ width: 260px;
15523
+ max-width: 100%;
15524
+ min-height: 80px;
15525
+ padding: 16px;
15526
+ border: 1px dashed var(--squisq-editor-control-border);
15527
+ border-radius: 8px;
15528
+ background: var(--squisq-editor-control-bg);
15529
+ color: var(--squisq-text-muted);
15530
+ font: 500 13px/1.4 var(--squisq-ux-font, system-ui, sans-serif);
15531
+ user-select: none;
15532
+ }
15533
+ .squisq-image-placeholder svg {
15534
+ flex-shrink: 0;
15535
+ }
15536
+ .squisq-image-placeholder[data-error=true] {
15537
+ border-style: solid;
15538
+ }
15539
+ .squisq-image-placeholder__text {
15540
+ display: flex;
15541
+ flex-direction: column;
15542
+ min-width: 0;
15543
+ }
15544
+ .squisq-image-placeholder__label {
15545
+ overflow: hidden;
15546
+ text-overflow: ellipsis;
15547
+ white-space: nowrap;
15548
+ font-size: 12px;
15549
+ font-weight: 400;
15550
+ }
15551
+ .squisq-image-placeholder--thumbnail {
15552
+ width: 100px;
15553
+ }
15546
15554
 
15547
15555
  /* src/styles/media-edit.css */
15548
15556
  .squisq-media-edit-modal {
@@ -1,8 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { Doc, Theme, MediaProvider } from '@bendyline/squisq/schemas';
3
3
  import { ContentContainer } from '@bendyline/squisq/storage';
4
- import { o as TeleprompterRecordingDeps, g as NarrationStageHandle, T as TeleprompterController, c as FloatingWindowHandle } from '../useNarrationStage-DopDr_P5.js';
5
- export { C as CanvasSink, D as DEFAULT_TELEPROMPTER_PREFS, F as FloatOpenOptions, b as FloatTier, d as FloatingWindowManager, M as MicAnalysisHandle, e as MicAnalysisStatus, N as NarrationRecorderController, f as NarrationRecorderState, h as NarrationTake, P as PrompterTransport, J as TELEPROMPTER_PREF_LIMITS, n as TeleprompterPrefs, q as UseNarrationStageOptions, s as createFloatingWindowManager, t as detectFloatTiers, K as normalizeTeleprompterPrefs, z as useFloatingWindow, B as useMicAnalysis, E as useNarrationRecorder, G as useNarrationStage, H as useTeleprompter, I as vadConfigForSensitivity } from '../useNarrationStage-DopDr_P5.js';
4
+ import { o as TeleprompterRecordingDeps, g as NarrationStageHandle, T as TeleprompterController, c as FloatingWindowHandle } from '../useNarrationStage-vKtk6H-V.js';
5
+ export { C as CanvasSink, D as DEFAULT_TELEPROMPTER_PREFS, F as FloatOpenOptions, b as FloatTier, d as FloatingWindowManager, M as MicAnalysisHandle, e as MicAnalysisStatus, N as NarrationRecorderController, f as NarrationRecorderState, h as NarrationTake, P as PrompterTransport, J as TELEPROMPTER_PREF_LIMITS, n as TeleprompterPrefs, q as UseNarrationStageOptions, s as createFloatingWindowManager, t as detectFloatTiers, K as normalizeTeleprompterPrefs, z as useFloatingWindow, B as useMicAnalysis, E as useNarrationRecorder, G as useNarrationStage, H as useTeleprompter, I as vadConfigForSensitivity } from '../useNarrationStage-vKtk6H-V.js';
6
6
  import { NarrationTimingJsonV3, NarrationScript, NarrationAlignment } from '@bendyline/squisq/narration';
7
7
  import { ReactNode } from 'react';
8
8
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TeleprompterView
3
- } from "../chunk-ENDRPV6T.js";
3
+ } from "../chunk-QZWFSSDE.js";
4
4
  import {
5
5
  DEFAULT_TELEPROMPTER_PREFS,
6
6
  EYE_LINE_FRACTION,
@@ -31,7 +31,7 @@ import {
31
31
  useNarrationStage,
32
32
  useTeleprompter,
33
33
  vadConfigForSensitivity
34
- } from "../chunk-34FRFDQ6.js";
34
+ } from "../chunk-XZVMKR5Q.js";
35
35
  export {
36
36
  DEFAULT_TELEPROMPTER_PREFS,
37
37
  EYE_LINE_FRACTION,
@@ -285,6 +285,8 @@ interface MicAnalysisHandle {
285
285
  sampleRate: number | null;
286
286
  /** Known audio inputs (labels appear after the first grant). */
287
287
  devices: MediaDeviceInfo[];
288
+ /** The host owns stream/device selection; analysis must never stop its tracks. */
289
+ sourceManaged?: boolean;
288
290
  /**
289
291
  * Start (or restart) capture. Resolves with the live stream — callers
290
292
  * that need it immediately must use the return value, not the `stream`
@@ -296,7 +298,7 @@ interface MicAnalysisHandle {
296
298
  /** Subscribe to PCM hops; returns an unsubscribe. */
297
299
  subscribeHop: (listener: PcmHopListener) => () => void;
298
300
  }
299
- declare function useMicAnalysis(constraints?: MediaTrackConstraints): MicAnalysisHandle;
301
+ declare function useMicAnalysis(constraints?: MediaTrackConstraints, externalStream?: MediaStream | null): MicAnalysisHandle;
300
302
 
301
303
  /**
302
304
  * Shared types for the Narrate (teleprompter) display mode.
@@ -393,6 +395,8 @@ declare function vadConfigForSensitivity(sensitivity: number): Partial<VadConfig
393
395
  declare function useTeleprompter(opts: {
394
396
  doc: Doc | null;
395
397
  micConstraints?: MediaTrackConstraints;
398
+ /** Borrow capture audio instead of opening another microphone; null waits for capture. */
399
+ analysisStream?: MediaStream | null;
396
400
  }): TeleprompterController;
397
401
 
398
402
  /**
@@ -570,6 +574,8 @@ interface UseNarrationStageOptions {
570
574
  getAudioBasename?: () => string | undefined;
571
575
  /** Microphone constraints shared by voice analysis and audio recording. */
572
576
  micConstraints?: MediaTrackConstraints;
577
+ /** Optional borrowed capture stream for a prompter-only stage; null means no stream yet. */
578
+ analysisStream?: MediaStream | null;
573
579
  /** Constraints for the optional narration camera lane. */
574
580
  cameraConstraints?: MediaTrackConstraints;
575
581
  /** Combined narration audio/camera soft stop threshold, in bytes. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq-editor-react",
3
- "version": "2.11.16",
3
+ "version": "2.11.17",
4
4
  "description": "React editor shell with raw/WYSIWYG/preview modes for Squisq documents",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -98,12 +98,12 @@
98
98
  }
99
99
  },
100
100
  "dependencies": {
101
- "@bendyline/squisq": "2.11.13",
101
+ "@bendyline/squisq": "2.11.14",
102
102
  "@bendyline/squisq-calc": "2.11.1",
103
- "@bendyline/squisq-formats": "2.6.13",
104
- "@bendyline/squisq-grid-react": "2.11.14",
105
- "@bendyline/squisq-react": "2.11.15",
106
- "@bendyline/squisq-video-react": "2.4.19",
103
+ "@bendyline/squisq-formats": "2.6.14",
104
+ "@bendyline/squisq-grid-react": "2.11.15",
105
+ "@bendyline/squisq-react": "2.11.16",
106
+ "@bendyline/squisq-video-react": "2.4.20",
107
107
  "@fortawesome/fontawesome-free": "7.2.0",
108
108
  "@tiptap/core": "2.27.3",
109
109
  "@tiptap/extension-heading": "2.27.3",