@bendyline/squisq-editor-react 2.13.8 → 2.13.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,7 +19,7 @@ import {
19
19
  useDictationState,
20
20
  useEditorContext,
21
21
  useEditorContextOptional
22
- } from "./chunk-PCIVTLQG.js";
22
+ } from "./chunk-D2QFSFG2.js";
23
23
  import {
24
24
  monacoLanguageRequestKey,
25
25
  monacoLanguagesForDocument,
@@ -52,13 +52,13 @@ import {
52
52
  documentTitleFromFileName,
53
53
  resolveBlockVisual,
54
54
  useModalDialog
55
- } from "./chunk-LXGQA2SY.js";
55
+ } from "./chunk-FOKUOC7E.js";
56
56
  import {
57
57
  Icon
58
58
  } from "./chunk-UYB5CPRE.js";
59
59
  import {
60
60
  TeleprompterView
61
- } from "./chunk-DJS5I5NO.js";
61
+ } from "./chunk-PQSFLC2R.js";
62
62
 
63
63
  // src/useMonacoLoader.ts
64
64
  import { useEffect, useState } from "react";
@@ -33908,7 +33908,7 @@ function persistFromWrite(bodyMd, state) {
33908
33908
  }
33909
33909
 
33910
33910
  // src/WysiwygEditor.tsx
33911
- import { useCallback as useCallback43, useEffect as useEffect54, useMemo as useMemo41, useRef as useRef50, useState as useState67 } from "react";
33911
+ import { useCallback as useCallback43, useEffect as useEffect54, useMemo as useMemo42, useRef as useRef51, useState as useState67 } from "react";
33912
33912
  import { useEditor as useEditor2, EditorContent as EditorContent2 } from "@tiptap/react";
33913
33913
  import { Selection as Selection2 } from "@tiptap/pm/state";
33914
33914
  import { Extension as Extension17, getHTMLFromFragment } from "@tiptap/core";
@@ -34252,17 +34252,17 @@ import { parseMarkdown as parseMarkdown6 } from "@bendyline/squisq/markdown";
34252
34252
  // src/mermaid/mermaidThemeStore.ts
34253
34253
  function createMermaidThemeStore(initialTheme) {
34254
34254
  let theme = initialTheme;
34255
- const listeners = /* @__PURE__ */ new Set();
34255
+ const listeners2 = /* @__PURE__ */ new Set();
34256
34256
  return {
34257
34257
  getSnapshot: () => theme,
34258
34258
  subscribe: (listener) => {
34259
- listeners.add(listener);
34260
- return () => listeners.delete(listener);
34259
+ listeners2.add(listener);
34260
+ return () => listeners2.delete(listener);
34261
34261
  },
34262
34262
  setTheme: (nextTheme) => {
34263
34263
  if (Object.is(theme, nextTheme)) return;
34264
34264
  theme = nextTheme;
34265
- listeners.forEach((listener) => listener());
34265
+ listeners2.forEach((listener) => listener());
34266
34266
  }
34267
34267
  };
34268
34268
  }
@@ -35061,9 +35061,126 @@ var TiptapAudio = Node5.create({
35061
35061
  });
35062
35062
 
35063
35063
  // src/documentTray/DocumentTray.tsx
35064
- import { useCallback as useCallback42, useId as useId14, useState as useState66, useSyncExternalStore as useSyncExternalStore3 } from "react";
35064
+ import { useCallback as useCallback42, useId as useId14, useMemo as useMemo41, useRef as useRef50, useState as useState66, useSyncExternalStore as useSyncExternalStore3 } from "react";
35065
35065
  import { videoEmbedIframeAttributes as videoEmbedIframeAttributes2 } from "@bendyline/squisq/markdown";
35066
+
35067
+ // src/documentTray/narrationTimingJobs.ts
35068
+ var jobsByContainer = /* @__PURE__ */ new WeakMap();
35069
+ var listeners = /* @__PURE__ */ new Set();
35070
+ function jobsFor(container) {
35071
+ let jobs = jobsByContainer.get(container);
35072
+ if (!jobs) {
35073
+ jobs = /* @__PURE__ */ new Map();
35074
+ jobsByContainer.set(container, jobs);
35075
+ }
35076
+ return jobs;
35077
+ }
35078
+ function notify() {
35079
+ for (const listener of listeners) listener();
35080
+ }
35081
+ function subscribeNarrationTimingJobs(listener) {
35082
+ listeners.add(listener);
35083
+ return () => {
35084
+ listeners.delete(listener);
35085
+ };
35086
+ }
35087
+ function narrationTimingJob(container, src) {
35088
+ return jobsByContainer.get(container)?.get(src)?.state ?? null;
35089
+ }
35090
+ function startNarrationTimingJob(input) {
35091
+ const jobs = jobsFor(input.container);
35092
+ if (jobs.get(input.src)?.state.status === "running") return;
35093
+ const job = {
35094
+ state: { status: "running", progress: { phase: "reading", fraction: 0 } },
35095
+ abort: new AbortController()
35096
+ };
35097
+ jobs.set(input.src, job);
35098
+ notify();
35099
+ const settle = (state) => {
35100
+ if (jobs.get(input.src) !== job) return;
35101
+ if (state) job.state = state;
35102
+ else jobs.delete(input.src);
35103
+ notify();
35104
+ };
35105
+ import("./narrationTimingsFromAudio-ICMDSWPI.js").then(
35106
+ ({ updateNarrationTimingsFromAudio }) => updateNarrationTimingsFromAudio({
35107
+ ...input,
35108
+ signal: job.abort.signal,
35109
+ onProgress: (progress) => settle({ status: "running", progress })
35110
+ })
35111
+ ).then(
35112
+ (result) => {
35113
+ settle({ status: "done", result });
35114
+ input.onSaved?.();
35115
+ },
35116
+ (caught) => {
35117
+ if (job.abort.signal.aborted) settle(null);
35118
+ else
35119
+ settle({
35120
+ status: "error",
35121
+ message: caught instanceof Error ? caught.message : String(caught)
35122
+ });
35123
+ }
35124
+ );
35125
+ }
35126
+ function cancelNarrationTimingJob(container, src) {
35127
+ jobsByContainer.get(container)?.get(src)?.abort.abort();
35128
+ }
35129
+
35130
+ // src/documentTray/DocumentTray.tsx
35066
35131
  import { jsx as jsx71, jsxs as jsxs57 } from "react/jsx-runtime";
35132
+ function progressLabel({ phase, fraction }) {
35133
+ if (phase === "reading") return "Reading the recording\u2026";
35134
+ if (phase === "transcribing")
35135
+ return `Updating timings from audio\u2026 ${Math.round(fraction * 100)}%`;
35136
+ if (phase === "matching") return "Matching words to the document\u2026";
35137
+ return "Saving timings\u2026";
35138
+ }
35139
+ function NarrationTimingControl({ src, access }) {
35140
+ const { container, recognizer } = access;
35141
+ const getJob = useCallback42(() => narrationTimingJob(container, src), [container, src]);
35142
+ const job = useSyncExternalStore3(subscribeNarrationTimingJobs, getJob, getJob);
35143
+ const start = () => {
35144
+ if (recognizer.readiness?.state === "download-required") {
35145
+ recognizer.provider.requestSetup?.();
35146
+ return;
35147
+ }
35148
+ startNarrationTimingJob({
35149
+ src,
35150
+ markdownSource: access.getMarkdownSource(),
35151
+ provider: recognizer.provider,
35152
+ container,
35153
+ mediaProvider: access.mediaProvider,
35154
+ onSaved: access.onSaved
35155
+ });
35156
+ };
35157
+ if (job?.status === "running") {
35158
+ return /* @__PURE__ */ jsxs57("div", { className: "squisq-document-tray-timing", children: [
35159
+ /* @__PURE__ */ jsx71("span", { role: "status", children: progressLabel(job.progress) }),
35160
+ /* @__PURE__ */ jsx71("progress", { max: 1, value: job.progress.fraction, "aria-label": "Timing progress" }),
35161
+ /* @__PURE__ */ jsx71("button", { type: "button", onClick: () => cancelNarrationTimingJob(container, src), children: "Cancel" })
35162
+ ] });
35163
+ }
35164
+ return /* @__PURE__ */ jsxs57("div", { className: "squisq-document-tray-timing", children: [
35165
+ /* @__PURE__ */ jsx71(
35166
+ "button",
35167
+ {
35168
+ type: "button",
35169
+ onClick: start,
35170
+ title: "Transcribe this recording and match its words to the document, so slides and captions follow the voice.",
35171
+ children: "Update timings from audio"
35172
+ }
35173
+ ),
35174
+ job?.status === "done" && /* @__PURE__ */ jsxs57("span", { role: "status", children: [
35175
+ "Timings updated \xB7 ",
35176
+ job.result.matchedWords,
35177
+ " of ",
35178
+ job.result.spokenWords,
35179
+ " words matched"
35180
+ ] }),
35181
+ job?.status === "error" && /* @__PURE__ */ jsx71("span", { role: "alert", className: "squisq-document-tray-timing-error", children: job.message })
35182
+ ] });
35183
+ }
35067
35184
  function mediaLabel(item) {
35068
35185
  if (item.title) return item.title;
35069
35186
  const path = item.src.split(/[?#]/, 1)[0];
@@ -35074,7 +35191,11 @@ function mediaLabel(item) {
35074
35191
  return name;
35075
35192
  }
35076
35193
  }
35077
- function TrayItem({ item, editor }) {
35194
+ function TrayItem({
35195
+ item,
35196
+ editor,
35197
+ timing
35198
+ }) {
35078
35199
  const [expanded, setExpanded] = useState66(false);
35079
35200
  const id = useId14();
35080
35201
  const label = mediaLabel(item);
@@ -35141,6 +35262,7 @@ function TrayItem({ item, editor }) {
35141
35262
  updateAttributes: item.editableAttributes ? updateAttributes : void 0
35142
35263
  }
35143
35264
  ),
35265
+ timing && !readOnly && /* @__PURE__ */ jsx71(NarrationTimingControl, { src: item.src, access: timing }),
35144
35266
  /* @__PURE__ */ jsxs57("div", { className: "squisq-document-tray-detail", children: [
35145
35267
  /* @__PURE__ */ jsx71("span", { title: item.src, children: item.src }),
35146
35268
  !readOnly && /* @__PURE__ */ jsx71("button", { type: "button", onClick: remove, "aria-label": `Remove ${label} from document`, children: "Remove" })
@@ -35162,13 +35284,46 @@ function DocumentTray({ editor }) {
35162
35284
  );
35163
35285
  const getSnapshot = useCallback42(() => DOCUMENT_TRAY_KEY.getState(editor.state), [editor]);
35164
35286
  const state = useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
35287
+ const context = useEditorContextOptional();
35288
+ const markdownRef = useRef50("");
35289
+ markdownRef.current = context?.markdownSource ?? "";
35290
+ const container = context?.workspaceContainer;
35291
+ const mediaProvider = context?.mediaProvider;
35292
+ const recognizer = context?.speechRecognizer;
35293
+ const onSaved = context?.bumpMediaRevision;
35294
+ const timingAccess = useMemo41(
35295
+ () => container && mediaProvider && recognizer && onSaved ? {
35296
+ recognizer,
35297
+ container,
35298
+ mediaProvider,
35299
+ getMarkdownSource: () => markdownRef.current,
35300
+ onSaved
35301
+ } : null,
35302
+ [container, mediaProvider, recognizer, onSaved]
35303
+ );
35304
+ const documentMedia = context?.doc?.documentMedia;
35305
+ const narrationSources = useMemo41(
35306
+ () => new Set(
35307
+ (documentMedia ?? []).filter((clip) => clip.anchor === "document").map((clip) => clip.src)
35308
+ ),
35309
+ [documentMedia]
35310
+ );
35165
35311
  if (!state?.items.length) return null;
35312
+ const timing = recognizer?.readiness?.state === "unavailable" ? null : timingAccess;
35166
35313
  return /* @__PURE__ */ jsxs57("section", { className: "squisq-document-tray", "aria-label": "Document tray", children: [
35167
35314
  /* @__PURE__ */ jsxs57("div", { className: "squisq-document-tray-heading", children: [
35168
35315
  /* @__PURE__ */ jsx71("span", { children: "Document media" }),
35169
35316
  /* @__PURE__ */ jsx71("span", { className: "squisq-document-tray-hint", children: "Audio & video before the body" })
35170
35317
  ] }),
35171
- /* @__PURE__ */ jsx71("div", { className: "squisq-document-tray-items", children: state.items.map((item) => /* @__PURE__ */ jsx71(TrayItem, { item, editor }, item.id)) })
35318
+ /* @__PURE__ */ jsx71("div", { className: "squisq-document-tray-items", children: state.items.map((item) => /* @__PURE__ */ jsx71(
35319
+ TrayItem,
35320
+ {
35321
+ item,
35322
+ editor,
35323
+ timing: !item.embed && narrationSources.has(item.src) ? timing : null
35324
+ },
35325
+ item.id
35326
+ )) })
35172
35327
  ] });
35173
35328
  }
35174
35329
 
@@ -36028,16 +36183,16 @@ function WysiwygEditor({
36028
36183
  layoutMode,
36029
36184
  linkSchemes
36030
36185
  } = useEditorContext();
36031
- const wikiLinksRef = useRef50(wikiLinks);
36186
+ const wikiLinksRef = useRef51(wikiLinks);
36032
36187
  wikiLinksRef.current = wikiLinks;
36033
- const linkSchemesRef = useRef50(linkSchemes);
36188
+ const linkSchemesRef = useRef51(linkSchemes);
36034
36189
  linkSchemesRef.current = linkSchemes;
36035
36190
  const wrapPolicyEnabled = preserveSourceWrapping && layoutMode === "document";
36036
36191
  const previewSettings = usePreviewSettingsOptional();
36037
- const setEditorSourceRef = useRef50(setEditorSource);
36192
+ const setEditorSourceRef = useRef51(setEditorSource);
36038
36193
  setEditorSourceRef.current = setEditorSource;
36039
36194
  const activeTheme = previewSettings?.activeTheme;
36040
- const mermaidThemeStoreRef = useRef50(null);
36195
+ const mermaidThemeStoreRef = useRef51(null);
36041
36196
  if (mermaidThemeStoreRef.current === null) {
36042
36197
  mermaidThemeStoreRef.current = createMermaidThemeStore(activeTheme ?? DEFAULT_THEME4);
36043
36198
  }
@@ -36061,46 +36216,46 @@ function WysiwygEditor({
36061
36216
  },
36062
36217
  [docTemplates, onDocTemplatesChange]
36063
36218
  );
36064
- const mentionProviderRef = useRef50(mentionProvider);
36219
+ const mentionProviderRef = useRef51(mentionProvider);
36065
36220
  useEffect54(() => {
36066
36221
  mentionProviderRef.current = mentionProvider;
36067
36222
  }, [mentionProvider]);
36068
- const fenceRenderersRef = useRef50(fenceRenderers);
36223
+ const fenceRenderersRef = useRef51(fenceRenderers);
36069
36224
  useEffect54(() => {
36070
36225
  fenceRenderersRef.current = fenceRenderers;
36071
36226
  }, [fenceRenderers]);
36072
- const onCopyCodeRef = useRef50(onCopyCode);
36227
+ const onCopyCodeRef = useRef51(onCopyCode);
36073
36228
  useEffect54(() => {
36074
36229
  onCopyCodeRef.current = onCopyCode;
36075
36230
  }, [onCopyCode]);
36076
- const activeThemeRef = useRef50(activeTheme);
36231
+ const activeThemeRef = useRef51(activeTheme);
36077
36232
  useEffect54(() => {
36078
36233
  activeThemeRef.current = activeTheme;
36079
36234
  }, [activeTheme]);
36080
- const resolvedPlaceholder = useMemo41(() => placeholder ?? pickEmptyPrompt(), [placeholder]);
36081
- const isExternalUpdate = useRef50(false);
36082
- const externalApplyPendingRef = useRef50(false);
36083
- const lastSourceRef = useRef50(editorSource);
36084
- const sourceLayoutRef = useRef50(layoutMode);
36085
- const pendingLocalSourcesRef = useRef50([]);
36086
- const mediaProviderRef = useRef50(mediaProvider);
36087
- const mediaRevisionRef = useRef50(mediaRevision);
36235
+ const resolvedPlaceholder = useMemo42(() => placeholder ?? pickEmptyPrompt(), [placeholder]);
36236
+ const isExternalUpdate = useRef51(false);
36237
+ const externalApplyPendingRef = useRef51(false);
36238
+ const lastSourceRef = useRef51(editorSource);
36239
+ const sourceLayoutRef = useRef51(layoutMode);
36240
+ const pendingLocalSourcesRef = useRef51([]);
36241
+ const mediaProviderRef = useRef51(mediaProvider);
36242
+ const mediaRevisionRef = useRef51(mediaRevision);
36088
36243
  useEffect54(() => {
36089
36244
  mediaRevisionRef.current = mediaRevision;
36090
36245
  }, [mediaRevision]);
36091
- const onOpenFilesPanelRef = useRef50(onOpenFilesPanel);
36246
+ const onOpenFilesPanelRef = useRef51(onOpenFilesPanel);
36092
36247
  useEffect54(() => {
36093
36248
  onOpenFilesPanelRef.current = onOpenFilesPanel;
36094
36249
  }, [onOpenFilesPanel]);
36095
- const calcEngineFactoryRef = useRef50(calcEngineFactory);
36250
+ const calcEngineFactoryRef = useRef51(calcEngineFactory);
36096
36251
  useEffect54(() => {
36097
36252
  calcEngineFactoryRef.current = calcEngineFactory;
36098
36253
  }, [calcEngineFactory]);
36099
- const workspaceContainerRef = useRef50(workspaceContainer);
36254
+ const workspaceContainerRef = useRef51(workspaceContainer);
36100
36255
  useEffect54(() => {
36101
36256
  workspaceContainerRef.current = workspaceContainer;
36102
36257
  }, [workspaceContainer]);
36103
- const bumpMediaRevisionRef = useRef50(bumpMediaRevision);
36258
+ const bumpMediaRevisionRef = useRef51(bumpMediaRevision);
36104
36259
  useEffect54(() => {
36105
36260
  bumpMediaRevisionRef.current = bumpMediaRevision;
36106
36261
  }, [bumpMediaRevision]);
@@ -36126,9 +36281,9 @@ function WysiwygEditor({
36126
36281
  cancelled = true;
36127
36282
  };
36128
36283
  }, [mediaProvider, mediaRevision]);
36129
- const frontmatterRef = useRef50(stripFrontmatter(editorSource).frontmatter);
36130
- const wrapStateRef = useRef50(null);
36131
- const initialDisplayBodyRef = useRef50(void 0);
36284
+ const frontmatterRef = useRef51(stripFrontmatter(editorSource).frontmatter);
36285
+ const wrapStateRef = useRef51(null);
36286
+ const initialDisplayBodyRef = useRef51(void 0);
36132
36287
  if (initialDisplayBodyRef.current === void 0) {
36133
36288
  const body = stripFrontmatter(editorSource).body;
36134
36289
  if (wrapPolicyEnabled) {
@@ -36139,15 +36294,15 @@ function WysiwygEditor({
36139
36294
  initialDisplayBodyRef.current = body;
36140
36295
  }
36141
36296
  }
36142
- const loadedSourceRef = useRef50(null);
36297
+ const loadedSourceRef = useRef51(null);
36143
36298
  if (loadedSourceRef.current === null) {
36144
36299
  loadedSourceRef.current = { body: stripFrontmatter(editorSource).body, baseline: null };
36145
36300
  }
36146
- const initialContentRef = useRef50(void 0);
36301
+ const initialContentRef = useRef51(void 0);
36147
36302
  if (initialContentRef.current === void 0) {
36148
36303
  initialContentRef.current = markdownToTiptap(initialDisplayBodyRef.current);
36149
36304
  }
36150
- const submitOnEnterRef = useRef50(submitOnEnter);
36305
+ const submitOnEnterRef = useRef51(submitOnEnter);
36151
36306
  useEffect54(() => {
36152
36307
  submitOnEnterRef.current = submitOnEnter;
36153
36308
  }, [submitOnEnter]);
@@ -36405,7 +36560,7 @@ function WysiwygEditor({
36405
36560
  useEffect54(() => {
36406
36561
  if (editor) editor.setEditable(!readOnly);
36407
36562
  }, [editor, readOnly]);
36408
- const containerRef = useRef50(null);
36563
+ const containerRef = useRef51(null);
36409
36564
  const [badgeMenu, setBadgeMenu] = useState67(null);
36410
36565
  const [propsMenu, setPropsMenu] = useState67(null);
36411
36566
  const closeBadgeMenu = useCallback43(() => {
@@ -36530,7 +36685,7 @@ function WysiwygEditor({
36530
36685
  sourceLayoutRef.current = previousLayout;
36531
36686
  };
36532
36687
  }, [editorSource, editor, wrapPolicyEnabled, layoutMode]);
36533
- const badgePreviewSource = useMemo41(() => {
36688
+ const badgePreviewSource = useMemo42(() => {
36534
36689
  if (!badgeMenu) return void 0;
36535
36690
  try {
36536
36691
  const previewDoc = markdownToDoc2(parseMarkdown6(editorSource), { autoTemplates: false });
@@ -36555,7 +36710,7 @@ function WysiwygEditor({
36555
36710
  previewSettings?.activeTheme,
36556
36711
  previewSettings?.activeViewport
36557
36712
  ]);
36558
- const themeStyle = useMemo41(() => {
36713
+ const themeStyle = useMemo42(() => {
36559
36714
  if (!activeTheme) return {};
36560
36715
  const out = {
36561
36716
  "--squisq-block-props-accent": activeTheme.colors.primary
@@ -36815,18 +36970,18 @@ function insertDroppedMedia(view, event, point, mediaProvider, onMediaUploaded)
36815
36970
  }
36816
36971
 
36817
36972
  // src/InlinePreviewGutter.tsx
36818
- import { useLayoutEffect as useLayoutEffect13, useMemo as useMemo43, useRef as useRef51, useState as useState70 } from "react";
36973
+ import { useLayoutEffect as useLayoutEffect13, useMemo as useMemo44, useRef as useRef52, useState as useState70 } from "react";
36819
36974
  import { VIEWPORT_PRESETS as VIEWPORT_PRESETS5 } from "@bendyline/squisq/schemas";
36820
36975
  import { flattenRenderableBlocks, DEFAULT_THEME as DEFAULT_THEME5 } from "@bendyline/squisq/doc";
36821
36976
  import { extractPlainText as extractPlainText3 } from "@bendyline/squisq/markdown";
36822
36977
  import { BlockRenderer as BlockRenderer3, MediaContext as MediaContext4 } from "@bendyline/squisq-react";
36823
36978
 
36824
36979
  // src/useHeadingLayout.ts
36825
- import { useCallback as useCallback44, useEffect as useEffect55, useMemo as useMemo42, useState as useState68 } from "react";
36980
+ import { useCallback as useCallback44, useEffect as useEffect55, useMemo as useMemo43, useState as useState68 } from "react";
36826
36981
  import { flattenBlocks as flattenBlocks3, hasTemplate } from "@bendyline/squisq/doc";
36827
36982
  function useHeadingLayout(refInsideWrapper) {
36828
36983
  const { doc, activeView, monacoEditor, tiptapEditor } = useEditorContext();
36829
- const flatBlocks = useMemo42(() => doc ? flattenBlocks3(doc.blocks) : [], [doc]);
36984
+ const flatBlocks = useMemo43(() => doc ? flattenBlocks3(doc.blocks) : [], [doc]);
36830
36985
  const [entries, setEntries] = useState68([]);
36831
36986
  const [pageEdges, setPageEdges] = useState68(null);
36832
36987
  useEffect55(() => {
@@ -37143,11 +37298,11 @@ function InlinePreviewGutter({
37143
37298
  const { doc, workspaceContainer, fileName, mediaRevision } = useEditorContext();
37144
37299
  const projection = usePreviewProjection(doc, "", workspaceContainer, fileName, mediaRevision);
37145
37300
  const previewDoc = projection?.contentDoc ?? doc;
37146
- const gutterRef = useRef51(null);
37301
+ const gutterRef = useRef52(null);
37147
37302
  const { entries: headingEntries, scrollToBlock } = useHeadingLayout(gutterRef);
37148
37303
  const previewSettings = usePreviewSettingsOptional();
37149
37304
  const activeTheme = previewSettings?.activeTheme ?? DEFAULT_THEME5;
37150
- const items = useMemo43(() => {
37305
+ const items = useMemo44(() => {
37151
37306
  if (!previewDoc || !previewDoc.blocks.length) return [];
37152
37307
  const flat = flattenRenderableBlocks(previewDoc.blocks);
37153
37308
  const result = [];
@@ -37167,7 +37322,7 @@ function InlinePreviewGutter({
37167
37322
  });
37168
37323
  return result;
37169
37324
  }, [previewDoc, viewport, activeTheme]);
37170
- const connectorTops = useMemo43(() => {
37325
+ const connectorTops = useMemo44(() => {
37171
37326
  const m = /* @__PURE__ */ new Map();
37172
37327
  headingEntries.forEach((e2) => m.set(e2.block.id, e2.top));
37173
37328
  return m;
@@ -37359,8 +37514,8 @@ import {
37359
37514
  memo,
37360
37515
  useCallback as useCallback45,
37361
37516
  useEffect as useEffect57,
37362
- useMemo as useMemo44,
37363
- useRef as useRef52,
37517
+ useMemo as useMemo45,
37518
+ useRef as useRef53,
37364
37519
  useState as useState71
37365
37520
  } from "react";
37366
37521
  import { flattenBlocks as flattenBlocks4, hasTemplate as hasTemplate2 } from "@bendyline/squisq/doc";
@@ -37480,18 +37635,18 @@ function OutlinePanel({ width, className, readOnly = false }) {
37480
37635
  monacoEditor,
37481
37636
  tiptapEditor
37482
37637
  } = useEditorContext();
37483
- const paneRef = useRef52(null);
37638
+ const paneRef = useRef53(null);
37484
37639
  const cursorActiveId = useActiveOutlineBlockId();
37485
- const activeDragRef = useRef52(null);
37640
+ const activeDragRef = useRef53(null);
37486
37641
  const [draggedBlockId, setDraggedBlockId] = useState71(null);
37487
37642
  const [dropTarget, setDropTarget] = useState71(null);
37488
37643
  const nextOutlineBlocks = doc?.blocks ?? EMPTY_BLOCKS;
37489
- const outlineBlocksRef = useRef52(nextOutlineBlocks);
37644
+ const outlineBlocksRef = useRef53(nextOutlineBlocks);
37490
37645
  if (!sameOutlineStructure(outlineBlocksRef.current, nextOutlineBlocks)) {
37491
37646
  outlineBlocksRef.current = nextOutlineBlocks;
37492
37647
  }
37493
37648
  const outlineBlocks = outlineBlocksRef.current;
37494
- const interactionRef = useRef52({
37649
+ const interactionRef = useRef53({
37495
37650
  doc,
37496
37651
  markdownSource,
37497
37652
  setMarkdownSource,
@@ -37513,7 +37668,7 @@ function OutlinePanel({ width, className, readOnly = false }) {
37513
37668
  monacoEditor,
37514
37669
  tiptapEditor
37515
37670
  };
37516
- const blockModeActiveId = useMemo44(() => {
37671
+ const blockModeActiveId = useMemo45(() => {
37517
37672
  if (layoutMode !== "block" || activeBlockStartLine == null || !doc) return null;
37518
37673
  const match = flattenBlocks4(doc.blocks).find(
37519
37674
  (b) => b.sourceHeading?.position?.start.line === activeBlockStartLine
@@ -37813,8 +37968,8 @@ function OutlineNode({
37813
37968
  }
37814
37969
  function useActiveOutlineBlockId() {
37815
37970
  const { doc, activeView, tiptapEditor, monacoEditor } = useEditorContext();
37816
- const flatBlocks = useMemo44(() => doc ? flattenBlocks4(doc.blocks) : [], [doc]);
37817
- const flatBlocksRef = useRef52(flatBlocks);
37971
+ const flatBlocks = useMemo45(() => doc ? flattenBlocks4(doc.blocks) : [], [doc]);
37972
+ const flatBlocksRef = useRef53(flatBlocks);
37818
37973
  flatBlocksRef.current = flatBlocks;
37819
37974
  const headingLocations = flatBlocks.map((block) => `${block.id}:${block.sourceHeading?.position?.start.line ?? ""}`).join("\0");
37820
37975
  const [activeId, setActiveId] = useState71(null);
@@ -37930,7 +38085,7 @@ function bumpHeadingLevelInSource(source, line, delta) {
37930
38085
  }
37931
38086
 
37932
38087
  // src/codeContext/CodeContextZones.tsx
37933
- import { useCallback as useCallback47, useEffect as useEffect59, useMemo as useMemo46, useRef as useRef54, useState as useState72 } from "react";
38088
+ import { useCallback as useCallback47, useEffect as useEffect59, useMemo as useMemo47, useRef as useRef55, useState as useState72 } from "react";
37934
38089
  import { createPortal as createPortal15 } from "react-dom";
37935
38090
 
37936
38091
  // src/codeContext/diffContextSections.ts
@@ -38054,7 +38209,7 @@ function setOrdinal(zone, ordinal) {
38054
38209
  // src/codeContext/CodeContextSectionView.tsx
38055
38210
  import { parseMarkdown as parseMarkdown8 } from "@bendyline/squisq/markdown";
38056
38211
  import { MarkdownRenderer } from "@bendyline/squisq-react";
38057
- import { useCallback as useCallback46, useEffect as useEffect58, useMemo as useMemo45, useRef as useRef53 } from "react";
38212
+ import { useCallback as useCallback46, useEffect as useEffect58, useMemo as useMemo46, useRef as useRef54 } from "react";
38058
38213
  import { jsx as jsx75, jsxs as jsxs61 } from "react/jsx-runtime";
38059
38214
  function CodeContextSectionView({
38060
38215
  section,
@@ -38065,12 +38220,12 @@ function CodeContextSectionView({
38065
38220
  onRevealLine,
38066
38221
  onMeasure
38067
38222
  }) {
38068
- const rootRef = useRef53(null);
38069
- const stripNodes = useMemo45(
38223
+ const rootRef = useRef54(null);
38224
+ const stripNodes = useMemo46(
38070
38225
  () => parseMarkdown8(section.summaryMarkdown).children,
38071
38226
  [section.summaryMarkdown]
38072
38227
  );
38073
- const bodyNodes = useMemo45(
38228
+ const bodyNodes = useMemo46(
38074
38229
  () => expanded && section.markdown ? parseMarkdown8(section.markdown).children : null,
38075
38230
  [expanded, section.markdown]
38076
38231
  );
@@ -38149,7 +38304,7 @@ function CodeContextZones({ options }) {
38149
38304
  const [manager, setManager] = useState72(null);
38150
38305
  const [, setZonesVersion] = useState72(0);
38151
38306
  const [expandedById, setExpandedById] = useState72({});
38152
- const seenIds = useRef54(/* @__PURE__ */ new Set());
38307
+ const seenIds = useRef55(/* @__PURE__ */ new Set());
38153
38308
  useEffect59(() => {
38154
38309
  if (!monacoEditor) return;
38155
38310
  const mgr = new CodeContextZoneManager(monacoEditor);
@@ -38163,7 +38318,7 @@ function CodeContextZones({ options }) {
38163
38318
  }, [monacoEditor]);
38164
38319
  const fileTop = options.fileTop;
38165
38320
  const sections = options.sections;
38166
- const resolved = useMemo46(() => {
38321
+ const resolved = useMemo47(() => {
38167
38322
  const out = [];
38168
38323
  if (fileTop) {
38169
38324
  out.push({ spec: { id: fileTop.id, line: 0, ordinal: 0 }, section: fileTop });
@@ -38639,7 +38794,7 @@ function placeClipInBlock(source, fromLine, targetHeadingLine, spec, startAt) {
38639
38794
  }
38640
38795
 
38641
38796
  // src/TimelineTrack.tsx
38642
- import { useCallback as useCallback49, useEffect as useEffect62, useMemo as useMemo47, useRef as useRef57, useState as useState75 } from "react";
38797
+ import { useCallback as useCallback49, useEffect as useEffect62, useMemo as useMemo48, useRef as useRef58, useState as useState75 } from "react";
38643
38798
  import {
38644
38799
  resolveMediaSchedule as resolveMediaSchedule2,
38645
38800
  getDocPlaybackDuration,
@@ -38647,11 +38802,12 @@ import {
38647
38802
  } from "@bendyline/squisq/schemas";
38648
38803
  import { flattenBlocks as flattenBlocks5, DEFAULT_THEME as DEFAULT_THEME6, getPinnedBlockMeta } from "@bendyline/squisq/doc";
38649
38804
  import { createMediaTimeMap } from "@bendyline/squisq/mediaEdit";
38805
+ import { usesDynamicPresentation } from "@bendyline/squisq/transform";
38650
38806
  import { useProcessedAudio as useProcessedAudio2 } from "@bendyline/squisq-video-react/media-edit";
38651
38807
  import { MediaClipLayer, MediaContext as MediaContext5 } from "@bendyline/squisq-react";
38652
38808
 
38653
38809
  // src/useTimelineClock.ts
38654
- import { useCallback as useCallback48, useEffect as useEffect60, useRef as useRef55, useState as useState73 } from "react";
38810
+ import { useCallback as useCallback48, useEffect as useEffect60, useRef as useRef56, useState as useState73 } from "react";
38655
38811
  function advanceTime(prev, dt, total) {
38656
38812
  if (total <= 0) return 0;
38657
38813
  return Math.min(total, Math.max(0, prev + dt));
@@ -38673,11 +38829,11 @@ function playTimelineMediaAt(root, time) {
38673
38829
  function useTimelineClock(total) {
38674
38830
  const [currentTime, setCurrentTime] = useState73(0);
38675
38831
  const [isPlaying, setIsPlaying] = useState73(false);
38676
- const rafRef = useRef55(null);
38677
- const lastRef = useRef55(0);
38678
- const currentTimeRef = useRef55(0);
38679
- const isPlayingRef = useRef55(false);
38680
- const mediaHostRef = useRef55(null);
38832
+ const rafRef = useRef56(null);
38833
+ const lastRef = useRef56(0);
38834
+ const currentTimeRef = useRef56(0);
38835
+ const isPlayingRef = useRef56(false);
38836
+ const mediaHostRef = useRef56(null);
38681
38837
  currentTimeRef.current = currentTime;
38682
38838
  isPlayingRef.current = isPlaying;
38683
38839
  useEffect60(() => {
@@ -38781,7 +38937,7 @@ function timelineBars(clips) {
38781
38937
  }
38782
38938
 
38783
38939
  // src/TimelineItemMenu.tsx
38784
- import { useEffect as useEffect61, useLayoutEffect as useLayoutEffect14, useRef as useRef56, useState as useState74 } from "react";
38940
+ import { useEffect as useEffect61, useLayoutEffect as useLayoutEffect14, useRef as useRef57, useState as useState74 } from "react";
38785
38941
  import { createPortal as createPortal16 } from "react-dom";
38786
38942
  import { Fragment as Fragment26, jsx as jsx78, jsxs as jsxs63 } from "react/jsx-runtime";
38787
38943
  function TimelineItemMenu({
@@ -38791,12 +38947,13 @@ function TimelineItemMenu({
38791
38947
  accentColor,
38792
38948
  onBlockDuration,
38793
38949
  onBlockStartTime,
38950
+ onBlockClearTiming,
38794
38951
  onBlockTransition,
38795
38952
  onVideoPatch,
38796
38953
  onEditMedia,
38797
38954
  onClose
38798
38955
  }) {
38799
- const panelRef = useRef56(null);
38956
+ const panelRef = useRef57(null);
38800
38957
  const [style, setStyle] = useState74(() => menuStyle(anchor));
38801
38958
  useLayoutEffect14(() => {
38802
38959
  const panel = panelRef.current;
@@ -38854,6 +39011,7 @@ function TimelineItemMenu({
38854
39011
  accentColor,
38855
39012
  onDuration: onBlockDuration,
38856
39013
  onStartTime: onBlockStartTime,
39014
+ onClearTiming: onBlockClearTiming,
38857
39015
  onTransition: onBlockTransition
38858
39016
  }
38859
39017
  ) : /* @__PURE__ */ jsxs63(Fragment26, { children: [
@@ -38883,6 +39041,7 @@ function BlockMenu({
38883
39041
  accentColor,
38884
39042
  onDuration,
38885
39043
  onStartTime,
39044
+ onClearTiming,
38886
39045
  onTransition
38887
39046
  }) {
38888
39047
  const [explicitDuration, setExplicitDuration] = useState74(target.explicitDuration);
@@ -38901,46 +39060,67 @@ function BlockMenu({
38901
39060
  onDuration(seconds);
38902
39061
  }
38903
39062
  };
39063
+ const pinned = explicitDuration || startTime !== "";
38904
39064
  return /* @__PURE__ */ jsxs63("div", { className: "squisq-timeline-item-menu-body", children: [
38905
- /* @__PURE__ */ jsxs63("label", { className: "squisq-timeline-item-menu-check", children: [
39065
+ target.timingLocked ? /* @__PURE__ */ jsxs63(Fragment26, { children: [
39066
+ /* @__PURE__ */ jsxs63("p", { className: "squisq-timeline-item-menu-hint", children: [
39067
+ "Dynamic slides time each slide from the narration or reading pace, so this section's length and start can't be pinned.",
39068
+ pinned && " A pinned time here stops a narrated deck from playing."
39069
+ ] }),
39070
+ pinned && onClearTiming && /* @__PURE__ */ jsx78(
39071
+ "button",
39072
+ {
39073
+ type: "button",
39074
+ className: "squisq-timeline-item-menu-action",
39075
+ onClick: () => {
39076
+ setExplicitDuration(false);
39077
+ setStartTime("");
39078
+ onClearTiming();
39079
+ },
39080
+ children: "Clear pinned timing"
39081
+ }
39082
+ )
39083
+ ] }) : /* @__PURE__ */ jsxs63(Fragment26, { children: [
39084
+ /* @__PURE__ */ jsxs63("label", { className: "squisq-timeline-item-menu-check", children: [
39085
+ /* @__PURE__ */ jsx78(
39086
+ "input",
39087
+ {
39088
+ type: "checkbox",
39089
+ checked: !explicitDuration,
39090
+ onChange: (event) => toggleAutotime(event.target.checked)
39091
+ }
39092
+ ),
39093
+ /* @__PURE__ */ jsx78("span", { children: "Autotime block" })
39094
+ ] }),
39095
+ /* @__PURE__ */ jsx78("p", { className: "squisq-timeline-item-menu-hint", children: "Uses narration, content, and interior media when no explicit duration is set." }),
38906
39096
  /* @__PURE__ */ jsx78(
38907
- "input",
39097
+ MenuNumberField,
38908
39098
  {
38909
- type: "checkbox",
38910
- checked: !explicitDuration,
38911
- onChange: (event) => toggleAutotime(event.target.checked)
39099
+ label: "Duration",
39100
+ ariaLabel: "Block duration in seconds",
39101
+ value: duration,
39102
+ disabled: !explicitDuration,
39103
+ onChange: (value) => {
39104
+ setDuration(value);
39105
+ const seconds = numericValue(value);
39106
+ if (seconds != null) onDuration(seconds);
39107
+ }
38912
39108
  }
38913
39109
  ),
38914
- /* @__PURE__ */ jsx78("span", { children: "Autotime block" })
38915
- ] }),
38916
- /* @__PURE__ */ jsx78("p", { className: "squisq-timeline-item-menu-hint", children: "Uses narration, content, and interior media when no explicit duration is set." }),
38917
- /* @__PURE__ */ jsx78(
38918
- MenuNumberField,
38919
- {
38920
- label: "Duration",
38921
- ariaLabel: "Block duration in seconds",
38922
- value: duration,
38923
- disabled: !explicitDuration,
38924
- onChange: (value) => {
38925
- setDuration(value);
38926
- const seconds = numericValue(value);
38927
- if (seconds != null) onDuration(seconds);
38928
- }
38929
- }
38930
- ),
38931
- /* @__PURE__ */ jsx78(
38932
- MenuNumberField,
38933
- {
38934
- label: "Start time",
38935
- ariaLabel: "Block start time in seconds",
38936
- value: startTime,
38937
- placeholder: "auto",
38938
- onChange: (value) => {
38939
- setStartTime(value);
38940
- onStartTime(value.trim() === "" ? null : numericValue(value));
39110
+ /* @__PURE__ */ jsx78(
39111
+ MenuNumberField,
39112
+ {
39113
+ label: "Start time",
39114
+ ariaLabel: "Block start time in seconds",
39115
+ value: startTime,
39116
+ placeholder: "auto",
39117
+ onChange: (value) => {
39118
+ setStartTime(value);
39119
+ onStartTime(value.trim() === "" ? null : numericValue(value));
39120
+ }
38941
39121
  }
38942
- }
38943
- ),
39122
+ )
39123
+ ] }),
38944
39124
  /* @__PURE__ */ jsxs63("div", { className: "squisq-timeline-item-menu-row", children: [
38945
39125
  /* @__PURE__ */ jsx78("span", { className: "squisq-timeline-item-menu-label", children: "Transition" }),
38946
39126
  /* @__PURE__ */ jsx78(
@@ -39242,11 +39422,12 @@ function TimelineTrack({
39242
39422
  const [drag, setDrag] = useState75(null);
39243
39423
  const [itemMenu, setItemMenu] = useState75(null);
39244
39424
  const [pxPerSecond, setPxPerSecond] = useState75(DEFAULT_PX_PER_SECOND);
39245
- const scrollRef = useRef57(null);
39246
- const blocks = useMemo47(() => doc ? flattenBlocks5(doc.blocks) : [], [doc]);
39425
+ const scrollRef = useRef58(null);
39426
+ const blocks = useMemo48(() => doc ? flattenBlocks5(doc.blocks) : [], [doc]);
39427
+ const timingLocked = doc ? usesDynamicPresentation(doc) : false;
39247
39428
  const previewSettings = usePreviewSettingsOptional();
39248
39429
  const previewTheme = previewSettings?.activeTheme ?? DEFAULT_THEME6;
39249
- const visualByBlock = useMemo47(() => {
39430
+ const visualByBlock = useMemo48(() => {
39250
39431
  const map = /* @__PURE__ */ new Map();
39251
39432
  if (doc) {
39252
39433
  for (const b of blocks) {
@@ -39256,16 +39437,16 @@ function TimelineTrack({
39256
39437
  }
39257
39438
  return map;
39258
39439
  }, [doc, blocks, previewTheme]);
39259
- const derivedClips = useMemo47(
39440
+ const derivedClips = useMemo48(
39260
39441
  () => doc ? resolveMediaSchedule2(doc) : [],
39261
39442
  [doc]
39262
39443
  );
39263
39444
  const clips = schedule ?? derivedClips;
39264
- const playbackClips = useMemo47(
39445
+ const playbackClips = useMemo48(
39265
39446
  () => doc ? collectTimelinePlaybackSchedule(doc, clips, { processedAudio }) : clips,
39266
39447
  [clips, doc, processedAudio]
39267
39448
  );
39268
- const total = useMemo47(() => doc ? getDocPlaybackDuration(doc) : 0, [doc]);
39449
+ const total = useMemo48(() => doc ? getDocPlaybackDuration(doc) : 0, [doc]);
39269
39450
  const width = Math.max(total * pxPerSecond, 200);
39270
39451
  const internalClock = useTimelineClock(total);
39271
39452
  const activeClock = clock ?? internalClock;
@@ -39292,7 +39473,7 @@ function TimelineTrack({
39292
39473
  window.removeEventListener("pointerup", onUp);
39293
39474
  };
39294
39475
  }, [scrubbing, pxPerSecond, seek]);
39295
- const rawClipById = useMemo47(() => {
39476
+ const rawClipById = useMemo48(() => {
39296
39477
  const map = /* @__PURE__ */ new Map();
39297
39478
  if (doc) {
39298
39479
  for (const b of flattenBlocks5(doc.blocks)) for (const m of b.media ?? []) map.set(m.id, m);
@@ -39312,7 +39493,7 @@ function TimelineTrack({
39312
39493
  },
39313
39494
  [blocks]
39314
39495
  );
39315
- const followedBlockRef = useRef57(null);
39496
+ const followedBlockRef = useRef58(null);
39316
39497
  useEffect62(() => {
39317
39498
  if (!isPlaying) {
39318
39499
  followedBlockRef.current = null;
@@ -39382,9 +39563,9 @@ function TimelineTrack({
39382
39563
  ro.observe(el);
39383
39564
  return () => ro.disconnect();
39384
39565
  }, []);
39385
- const scaleRef = useRef57(pxPerSecond);
39566
+ const scaleRef = useRef58(pxPerSecond);
39386
39567
  scaleRef.current = pxPerSecond;
39387
- const dragRef = useRef57(null);
39568
+ const dragRef = useRef58(null);
39388
39569
  dragRef.current = drag;
39389
39570
  const isDragging = drag != null && !drag.committed;
39390
39571
  useEffect62(() => {
@@ -39463,7 +39644,8 @@ function TimelineTrack({
39463
39644
  duration: block.duration,
39464
39645
  explicitDuration: pinned.duration != null,
39465
39646
  startTime: pinned.startTime ?? null,
39466
- transition: readHeadingLineTransition(heading)
39647
+ transition: readHeadingLineTransition(heading),
39648
+ timingLocked
39467
39649
  };
39468
39650
  }
39469
39651
  } else if (itemMenu?.kind === "video") {
@@ -39571,7 +39753,7 @@ function TimelineTrack({
39571
39753
  mediaProvider
39572
39754
  }
39573
39755
  ) }),
39574
- prev && headingLine(prev) != null && /* @__PURE__ */ jsx79(
39756
+ !timingLocked && prev && headingLine(prev) != null && /* @__PURE__ */ jsx79(
39575
39757
  "span",
39576
39758
  {
39577
39759
  className: "squisq-timeline-edge squisq-timeline-edge--left",
@@ -39615,7 +39797,7 @@ function TimelineTrack({
39615
39797
  children: "\u2026"
39616
39798
  }
39617
39799
  ),
39618
- line != null && /* @__PURE__ */ jsx79(
39800
+ !timingLocked && line != null && /* @__PURE__ */ jsx79(
39619
39801
  "span",
39620
39802
  {
39621
39803
  className: "squisq-timeline-edge squisq-timeline-edge--right",
@@ -39933,6 +40115,13 @@ function TimelineTrack({
39933
40115
  const next = setBlockStartTimeInSource(markdownSource, itemMenu.sourceLine, seconds);
39934
40116
  if (next) setMarkdownSource(next);
39935
40117
  },
40118
+ onBlockClearTiming: () => {
40119
+ if (itemMenu.kind !== "block") return;
40120
+ const line = itemMenu.sourceLine;
40121
+ const cleared = setBlockDurationInSource(markdownSource, line, null) ?? markdownSource;
40122
+ const next = setBlockStartTimeInSource(cleared, line, null) ?? cleared;
40123
+ if (next !== markdownSource) setMarkdownSource(next);
40124
+ },
39936
40125
  onBlockTransition: (transition) => {
39937
40126
  if (itemMenu.kind !== "block") return;
39938
40127
  const next = setBlockTransitionInSource(
@@ -39978,7 +40167,7 @@ function formatDur(seconds) {
39978
40167
  }
39979
40168
 
39980
40169
  // src/TimelineVideoPanel.tsx
39981
- import { useMemo as useMemo48 } from "react";
40170
+ import { useMemo as useMemo49 } from "react";
39982
40171
  import { MediaClipLayer as MediaClipLayer2 } from "@bendyline/squisq-react";
39983
40172
  import { jsx as jsx80, jsxs as jsxs65 } from "react/jsx-runtime";
39984
40173
  function TimelineVideoPanel({
@@ -39988,7 +40177,7 @@ function TimelineVideoPanel({
39988
40177
  basePath = "/",
39989
40178
  onClose
39990
40179
  }) {
39991
- const videos = useMemo48(() => schedule.filter((clip) => clip.kind === "video"), [schedule]);
40180
+ const videos = useMemo49(() => schedule.filter((clip) => clip.kind === "video"), [schedule]);
39992
40181
  const activeVideos = videos.filter(
39993
40182
  (clip) => currentTime >= clip.absoluteStart && currentTime < clip.absoluteEnd
39994
40183
  );
@@ -40042,7 +40231,7 @@ function formatClock4(seconds) {
40042
40231
  }
40043
40232
 
40044
40233
  // src/TimelineCompositionPanel.tsx
40045
- import { useMemo as useMemo49 } from "react";
40234
+ import { useMemo as useMemo50 } from "react";
40046
40235
  import { getDocPlaybackDuration as getDocPlaybackDuration2, resolveMediaSchedule as resolveMediaSchedule3 } from "@bendyline/squisq/schemas";
40047
40236
  import { DocPlayer } from "@bendyline/squisq-react";
40048
40237
  import { jsx as jsx81, jsxs as jsxs66 } from "react/jsx-runtime";
@@ -40078,11 +40267,11 @@ function TimelineCompositionPanel({
40078
40267
  );
40079
40268
  const contentDoc = projection?.contentDoc ?? null;
40080
40269
  const playerDoc = projection?.playerDoc ?? null;
40081
- const totalDuration = useMemo49(
40270
+ const totalDuration = useMemo50(
40082
40271
  () => playerDoc ? getDocPlaybackDuration2(playerDoc) : 0,
40083
40272
  [playerDoc]
40084
40273
  );
40085
- const effectiveVideoPresentation = useMemo49(() => {
40274
+ const effectiveVideoPresentation = useMemo50(() => {
40086
40275
  if (!playerDoc) return activeVideoPresentation;
40087
40276
  const activeVideo = resolveMediaSchedule3(playerDoc).find(
40088
40277
  (clip) => clip.kind === "video" && clock.currentTime >= clip.absoluteStart && clock.currentTime < clip.absoluteEnd
@@ -40091,7 +40280,7 @@ function TimelineCompositionPanel({
40091
40280
  if (activeVideo?.placement === "overlay") return "full-frame";
40092
40281
  return activeVideoPresentation;
40093
40282
  }, [activeVideoPresentation, clock.currentTime, playerDoc]);
40094
- const audioController = useMemo49(
40283
+ const audioController = useMemo50(
40095
40284
  () => ({
40096
40285
  currentTime: clock.currentTime,
40097
40286
  isPlaying: clock.isPlaying,
@@ -40272,7 +40461,7 @@ function TimelineToolbar({
40272
40461
  }
40273
40462
 
40274
40463
  // src/PlainHtmlPreview.tsx
40275
- import { useCallback as useCallback50, useEffect as useEffect63, useMemo as useMemo50, useRef as useRef58, useState as useState76 } from "react";
40464
+ import { useCallback as useCallback50, useEffect as useEffect63, useMemo as useMemo51, useRef as useRef59, useState as useState76 } from "react";
40276
40465
  import {
40277
40466
  isSvgImageSource as isSvgImageSource2,
40278
40467
  parseMarkdown as parseMarkdown9,
@@ -40348,9 +40537,9 @@ function PlainHtmlPreview({
40348
40537
  showCodeCopyButton = false,
40349
40538
  onCopyCode
40350
40539
  }) {
40351
- const iframeRef = useRef58(null);
40352
- const removeFrameLinkHandlerRef = useRef58(() => void 0);
40353
- const removeFrameCodeCopyControlsRef = useRef58(() => void 0);
40540
+ const iframeRef = useRef59(null);
40541
+ const removeFrameLinkHandlerRef = useRef59(() => void 0);
40542
+ const removeFrameCodeCopyControlsRef = useRef59(() => void 0);
40354
40543
  const setIframeRef = useCallback50(
40355
40544
  (frame) => {
40356
40545
  iframeRef.current = frame;
@@ -40358,7 +40547,7 @@ function PlainHtmlPreview({
40358
40547
  },
40359
40548
  [onFrameChange]
40360
40549
  );
40361
- const parsedDoc = useMemo50(() => parseMarkdown9(markdown), [markdown]);
40550
+ const parsedDoc = useMemo51(() => parseMarkdown9(markdown), [markdown]);
40362
40551
  const [wikiProjection, setWikiProjection] = useState76(null);
40363
40552
  const mdDoc = wikiProjection?.source === parsedDoc && wikiProjection.support === wikiLinks ? wikiProjection.doc : parsedDoc;
40364
40553
  useEffect63(() => {
@@ -40408,14 +40597,14 @@ function PlainHtmlPreview({
40408
40597
  cancelled = true;
40409
40598
  };
40410
40599
  }, [mdDoc, mediaProvider, mediaRevision]);
40411
- const mergedImages = useMemo50(() => {
40600
+ const mergedImages = useMemo51(() => {
40412
40601
  if (!resolvedImages && !images) return void 0;
40413
40602
  const merged = /* @__PURE__ */ new Map();
40414
40603
  if (resolvedImages) for (const [k2, v2] of resolvedImages) merged.set(k2, v2);
40415
40604
  if (images) for (const [k2, v2] of images) merged.set(k2, v2);
40416
40605
  return merged;
40417
40606
  }, [resolvedImages, images]);
40418
- const iconsCss = useMemo50(() => collectInlineFontAwesomeCss(), []);
40607
+ const iconsCss = useMemo51(() => collectInlineFontAwesomeCss(), []);
40419
40608
  const [renderFn, setRenderFn] = useState76(() => cachedRender);
40420
40609
  useEffect63(() => {
40421
40610
  if (renderFn) return;
@@ -40427,7 +40616,7 @@ function PlainHtmlPreview({
40427
40616
  cancelled = true;
40428
40617
  };
40429
40618
  }, [renderFn]);
40430
- const html = useMemo50(
40619
+ const html = useMemo51(
40431
40620
  () => renderFn ? (
40432
40621
  // The frame is sandboxed without scripts, and a nested player
40433
40622
  // iframe inherits that sandbox — so videos show as linked posters.
@@ -40633,7 +40822,7 @@ function collectImageRefs(doc) {
40633
40822
  }
40634
40823
 
40635
40824
  // src/PreviewPanel.tsx
40636
- import { useState as useState80, useEffect as useEffect68, useMemo as useMemo55, useCallback as useCallback54, useRef as useRef62 } from "react";
40825
+ import { useState as useState80, useEffect as useEffect68, useMemo as useMemo56, useCallback as useCallback54, useRef as useRef63 } from "react";
40637
40826
  import { createPortal as createPortal18 } from "react-dom";
40638
40827
  import {
40639
40828
  DashboardView,
@@ -40781,8 +40970,8 @@ import {
40781
40970
  useEffect as useEffect64,
40782
40971
  useId as useId15,
40783
40972
  useLayoutEffect as useLayoutEffect15,
40784
- useMemo as useMemo51,
40785
- useRef as useRef59,
40973
+ useMemo as useMemo52,
40974
+ useRef as useRef60,
40786
40975
  useState as useState77
40787
40976
  } from "react";
40788
40977
  import { createPortal as createPortal17 } from "react-dom";
@@ -40892,14 +41081,14 @@ function PresentationModeProvider({
40892
41081
  const [popupRoot, setPopupRoot] = useState77(null);
40893
41082
  const [error, setError] = useState77(null);
40894
41083
  const [fullscreenExitStyle, setFullscreenExitStyle] = useState77(null);
40895
- const activeTargetRef = useRef59(activeTarget);
41084
+ const activeTargetRef = useRef60(activeTarget);
40896
41085
  activeTargetRef.current = activeTarget;
40897
- const previousActiveTargetRef = useRef59(null);
40898
- const returnFocusRef = useRef59(null);
40899
- const popupRef = useRef59(null);
40900
- const popupCleanupRef = useRef59(null);
41086
+ const previousActiveTargetRef = useRef60(null);
41087
+ const returnFocusRef = useRef60(null);
41088
+ const popupRef = useRef60(null);
41089
+ const popupCleanupRef = useRef60(null);
40901
41090
  const fullscreenSupported = typeof document !== "undefined" && typeof document.documentElement.requestFullscreen === "function";
40902
- const availableTargets = useMemo51(
41091
+ const availableTargets = useMemo52(
40903
41092
  () => [
40904
41093
  "control",
40905
41094
  ...allowWindow ? ["window"] : [],
@@ -41142,7 +41331,7 @@ function PresentationModeProvider({
41142
41331
  },
41143
41332
  [rootRef]
41144
41333
  );
41145
- const value = useMemo51(
41334
+ const value = useMemo52(
41146
41335
  () => ({
41147
41336
  selectedTarget,
41148
41337
  activeTarget,
@@ -41230,8 +41419,8 @@ function PresentationModeControl() {
41230
41419
  stop
41231
41420
  } = usePresentationMode();
41232
41421
  const { colorScheme } = useEditorContext();
41233
- const triggerRef = useRef59(null);
41234
- const menuRef = useRef59(null);
41422
+ const triggerRef = useRef60(null);
41423
+ const menuRef = useRef60(null);
41235
41424
  const [open, setOpen] = useState77(false);
41236
41425
  const [anchor, setAnchor] = useState77(null);
41237
41426
  const options = PRESENTATION_OPTIONS.filter((option) => availableTargets.includes(option.target));
@@ -41404,8 +41593,8 @@ import {
41404
41593
  useCallback as useCallback52,
41405
41594
  useContext as useContext7,
41406
41595
  useEffect as useEffect65,
41407
- useMemo as useMemo52,
41408
- useRef as useRef60,
41596
+ useMemo as useMemo53,
41597
+ useRef as useRef61,
41409
41598
  useState as useState78
41410
41599
  } from "react";
41411
41600
  import { jsx as jsx85, jsxs as jsxs69 } from "react/jsx-runtime";
@@ -41423,8 +41612,8 @@ function PrintModeProvider({ rootRef, children }) {
41423
41612
  const { activeTarget: presentationTarget, stop: stopPresentation } = usePresentationMode();
41424
41613
  const [active, setActive] = useState78(false);
41425
41614
  const [slidesPerPage, setSlidesPerPage] = useState78(1);
41426
- const customPrintHandlerRef = useRef60(null);
41427
- const printAncestorsRef = useRef60([]);
41615
+ const customPrintHandlerRef = useRef61(null);
41616
+ const printAncestorsRef = useRef61([]);
41428
41617
  const unmarkPrinting = useCallback52(() => {
41429
41618
  const ownerDocument = rootRef.current?.ownerDocument;
41430
41619
  ownerDocument?.body.classList.remove("squisq-printing");
@@ -41507,7 +41696,7 @@ function PrintModeProvider({ rootRef, children }) {
41507
41696
  handleAfterPrint();
41508
41697
  };
41509
41698
  }, [active, close, markPrinting, rootRef, unmarkPrinting]);
41510
- const value = useMemo52(
41699
+ const value = useMemo53(
41511
41700
  () => ({
41512
41701
  active,
41513
41702
  slidesPerPage,
@@ -41576,7 +41765,7 @@ function PrintPreviewToolbar() {
41576
41765
  }
41577
41766
 
41578
41767
  // src/print/PrintPreview.tsx
41579
- import { useCallback as useCallback53, useEffect as useEffect66, useMemo as useMemo53, useRef as useRef61 } from "react";
41768
+ import { useCallback as useCallback53, useEffect as useEffect66, useMemo as useMemo54, useRef as useRef62 } from "react";
41580
41769
  import {
41581
41770
  BlockRenderer as BlockRenderer4,
41582
41771
  FlashcardFaceView,
@@ -41611,12 +41800,12 @@ function PrintPreview({
41611
41800
  }) {
41612
41801
  const { slidesPerPage, registerPrintHandler } = usePrintMode();
41613
41802
  const mediaProvider = useMediaProvider();
41614
- const documentFrameRef = useRef61(null);
41803
+ const documentFrameRef = useRef62(null);
41615
41804
  const setDocumentFrame = useCallback53((frame) => {
41616
41805
  documentFrameRef.current = frame;
41617
41806
  }, []);
41618
41807
  const { blocks } = useDocPlayback(previewDoc, 0, { viewport, theme });
41619
- const coverBlock = useMemo53(() => {
41808
+ const coverBlock = useMemo54(() => {
41620
41809
  if (!showCover || !previewDoc?.startBlock) return null;
41621
41810
  const context = createTemplateContext2(theme, 0, 1, viewport);
41622
41811
  return {
@@ -41627,7 +41816,7 @@ function PrintPreview({
41627
41816
  layers: expandCoverBlock2(previewDoc.startBlock, context)
41628
41817
  };
41629
41818
  }, [previewDoc?.startBlock, showCover, theme, viewport]);
41630
- const flashcardDeck = useMemo53(
41819
+ const flashcardDeck = useMemo54(
41631
41820
  () => contentDoc ? materializeFlashcards(contentDoc) : null,
41632
41821
  [contentDoc]
41633
41822
  );
@@ -41771,7 +41960,7 @@ import {
41771
41960
  createContext as createContext8,
41772
41961
  useContext as useContext8,
41773
41962
  useEffect as useEffect67,
41774
- useMemo as useMemo54,
41963
+ useMemo as useMemo55,
41775
41964
  useState as useState79
41776
41965
  } from "react";
41777
41966
  import { parseTableViewState as parseTableViewState2 } from "@bendyline/squisq/table";
@@ -41827,8 +42016,8 @@ function SlideshowDataGridProvider({
41827
42016
  mediaRevision,
41828
42017
  children
41829
42018
  }) {
41830
- const sources = useMemo54(() => collectSlideshowDataSources(doc), [doc]);
41831
- const value = useMemo54(
42019
+ const sources = useMemo55(() => collectSlideshowDataSources(doc), [doc]);
42020
+ const value = useMemo55(
41832
42021
  () => ({ container: container ?? null, mediaRevision, sources }),
41833
42022
  [container, mediaRevision, sources]
41834
42023
  );
@@ -41924,16 +42113,16 @@ function SlideshowDataGridRenderer({
41924
42113
  };
41925
42114
  }, [activeSourceKey, container, mediaRevision, gridModule]);
41926
42115
  const currentRemote = remote.key === activeSourceKey ? remote : void 0;
41927
- const previewRows = useMemo54(
42116
+ const previewRows = useMemo55(
41928
42117
  () => Array.isArray(layer.content.rows) ? layer.content.rows : [],
41929
42118
  [layer.content.rows]
41930
42119
  );
41931
- const previewHeaders = useMemo54(
42120
+ const previewHeaders = useMemo55(
41932
42121
  () => Array.isArray(layer.content.headers) ? layer.content.headers : [],
41933
42122
  [layer.content.headers]
41934
42123
  );
41935
42124
  const useLocalGrid = (!source || currentRemote?.status === "error") && previewRows.length > (layer.content.maxVisibleRows ?? 10);
41936
- const localProvider = useMemo54(
42125
+ const localProvider = useMemo55(
41937
42126
  () => useLocalGrid && gridModule ? new gridModule.TableStoreClient({ headers: previewHeaders, cells: previewRows }) : null,
41938
42127
  [previewHeaders, previewRows, useLocalGrid, gridModule]
41939
42128
  );
@@ -42037,8 +42226,8 @@ function PreviewPanel({
42037
42226
  activeDashboardTitle,
42038
42227
  activeDashboardStyle
42039
42228
  } = usePreviewSettings();
42040
- const mainSurfaceRef = useRef62(null);
42041
- const popupSurfaceRef = useRef62(null);
42229
+ const mainSurfaceRef = useRef63(null);
42230
+ const popupSurfaceRef = useRef63(null);
42042
42231
  const [playbackState, setPlaybackState] = useState80(null);
42043
42232
  const handlePlaybackStateChange = useCallback54((next) => {
42044
42233
  setPlaybackState(next);
@@ -42052,7 +42241,7 @@ function PreviewPanel({
42052
42241
  );
42053
42242
  const previewDoc = previewProjection?.playerDoc ?? null;
42054
42243
  const contentDoc = previewProjection?.contentDoc ?? null;
42055
- const followerAudioController = useMemo55(() => {
42244
+ const followerAudioController = useMemo56(() => {
42056
42245
  if (!playbackState || !previewDoc) return null;
42057
42246
  const noOp = () => Promise.resolve();
42058
42247
  return {
@@ -42071,7 +42260,7 @@ function PreviewPanel({
42071
42260
  restart: noOp
42072
42261
  };
42073
42262
  }, [playbackState, previewDoc]);
42074
- const documentMarkdown = useMemo55(
42263
+ const documentMarkdown = useMemo56(
42075
42264
  () => activeTransformStyle && contentDoc ? buildDocumentPreviewMarkdown(contentDoc) : markdownSource,
42076
42265
  [activeTransformStyle, contentDoc, markdownSource]
42077
42266
  );
@@ -42359,7 +42548,7 @@ function PreviewPanel({
42359
42548
  }
42360
42549
 
42361
42550
  // src/MediaBin.tsx
42362
- import { useState as useState81, useEffect as useEffect69, useRef as useRef63, useCallback as useCallback55 } from "react";
42551
+ import { useState as useState81, useEffect as useEffect69, useRef as useRef64, useCallback as useCallback55 } from "react";
42363
42552
  import { isSvgImageSource as isSvgImageSource3 } from "@bendyline/squisq/markdown";
42364
42553
  import { sanitizedSvgImageUrl as sanitizedSvgImageUrl2 } from "@bendyline/squisq-react";
42365
42554
  import { jsx as jsx89, jsxs as jsxs72 } from "react/jsx-runtime";
@@ -42435,7 +42624,7 @@ function MediaBin({
42435
42624
  highlightPath = null
42436
42625
  }) {
42437
42626
  const [entries, setEntries] = useState81([]);
42438
- const listRef = useRef63(null);
42627
+ const listRef = useRef64(null);
42439
42628
  useEffect69(() => {
42440
42629
  if (!highlightPath || !listRef.current) return;
42441
42630
  const item = listRef.current.querySelector(`[data-media-path="${CSS.escape(highlightPath)}"]`);
@@ -42448,9 +42637,9 @@ function MediaBin({
42448
42637
  const [isDropActive, setIsDropActive] = useState81(false);
42449
42638
  const [downloadingPath, setDownloadingPath] = useState81(null);
42450
42639
  const [contextMenu, setContextMenu] = useState81(null);
42451
- const fileInputRef = useRef63(null);
42452
- const contextMenuRef = useRef63(null);
42453
- const dropDepthRef = useRef63(0);
42640
+ const fileInputRef = useRef64(null);
42641
+ const contextMenuRef = useRef64(null);
42642
+ const dropDepthRef = useRef64(0);
42454
42643
  const updateEntries = useCallback55(
42455
42644
  async (provider) => {
42456
42645
  const list = sortMediaEntries(filterVisibleMediaEntries(await provider.listMedia()));
@@ -42938,7 +43127,7 @@ function DropZone({
42938
43127
  }
42939
43128
 
42940
43129
  // src/Tooltip.tsx
42941
- import { useEffect as useEffect70, useLayoutEffect as useLayoutEffect16, useRef as useRef64, useState as useState83 } from "react";
43130
+ import { useEffect as useEffect70, useLayoutEffect as useLayoutEffect16, useRef as useRef65, useState as useState83 } from "react";
42942
43131
  import { createPortal as createPortal19 } from "react-dom";
42943
43132
 
42944
43133
  // src/tooltipPlacement.ts
@@ -42955,12 +43144,12 @@ import { jsx as jsx91 } from "react/jsx-runtime";
42955
43144
  var SHOW_DELAY_MS = 180;
42956
43145
  function TooltipLayer() {
42957
43146
  const [state, setState3] = useState83(null);
42958
- const tooltipRef = useRef64(null);
42959
- const timerRef = useRef64(null);
42960
- const currentTargetRef = useRef64(null);
42961
- const visibleRef = useRef64(false);
42962
- const pressedTargetRef = useRef64(null);
42963
- const lastPointerWasTouchRef = useRef64(false);
43147
+ const tooltipRef = useRef65(null);
43148
+ const timerRef = useRef65(null);
43149
+ const currentTargetRef = useRef65(null);
43150
+ const visibleRef = useRef65(false);
43151
+ const pressedTargetRef = useRef65(null);
43152
+ const lastPointerWasTouchRef = useRef65(false);
42964
43153
  useLayoutEffect16(() => {
42965
43154
  if (!state) return;
42966
43155
  const node2 = tooltipRef.current;
@@ -43074,10 +43263,10 @@ function TooltipLayer() {
43074
43263
  }
43075
43264
 
43076
43265
  // src/EditorShell.tsx
43077
- import { useEffect as useEffect75, useRef as useRef70, useState as useState89, useCallback as useCallback61, useMemo as useMemo60 } from "react";
43266
+ import { useEffect as useEffect75, useRef as useRef71, useState as useState89, useCallback as useCallback61, useMemo as useMemo61 } from "react";
43078
43267
 
43079
43268
  // src/BlockPreviewPanel.tsx
43080
- import { useMemo as useMemo56 } from "react";
43269
+ import { useMemo as useMemo57 } from "react";
43081
43270
  import { VIEWPORT_PRESETS as VIEWPORT_PRESETS7 } from "@bendyline/squisq/schemas";
43082
43271
  import { flattenBlocks as flattenBlocks6, DEFAULT_THEME as DEFAULT_THEME7 } from "@bendyline/squisq/doc";
43083
43272
  import { jsx as jsx92 } from "react/jsx-runtime";
@@ -43088,13 +43277,13 @@ function BlockPreviewPanel({ basePath = "/" }) {
43088
43277
  const viewport = previewSettings?.activeViewport ?? VIEWPORT_PRESETS7.landscape;
43089
43278
  const projection = usePreviewProjection(doc, "", workspaceContainer, fileName, mediaRevision);
43090
43279
  const previewDoc = projection?.contentDoc ?? doc;
43091
- const block = useMemo56(() => {
43280
+ const block = useMemo57(() => {
43092
43281
  if (!previewDoc) return null;
43093
43282
  const blocks = flattenBlocks6(previewDoc.blocks);
43094
43283
  if (blocks.length === 0) return null;
43095
43284
  return blocks.find((b) => b.sourceHeading?.position?.start.line === activeBlockStartLine) ?? blocks[0];
43096
43285
  }, [previewDoc, activeBlockStartLine]);
43097
- const visual = useMemo56(
43286
+ const visual = useMemo57(
43098
43287
  () => previewDoc && block ? resolveBlockVisual(previewDoc, block, theme, viewport) : null,
43099
43288
  [previewDoc, block, theme, viewport]
43100
43289
  );
@@ -43204,7 +43393,7 @@ function ProofingPanel() {
43204
43393
  }
43205
43394
 
43206
43395
  // src/imageEditor/ImageEditModal.tsx
43207
- import { useCallback as useCallback56, useEffect as useEffect71, useMemo as useMemo57, useRef as useRef65, useState as useState84 } from "react";
43396
+ import { useCallback as useCallback56, useEffect as useEffect71, useMemo as useMemo58, useRef as useRef66, useState as useState84 } from "react";
43208
43397
  import { DARK_SURFACE, LIGHT_SURFACE } from "@bendyline/squisq/schemas";
43209
43398
  import {
43210
43399
  MemoryContentContainer,
@@ -43221,12 +43410,12 @@ function ImageEditModal({
43221
43410
  versioningAutoSaveIdleMs,
43222
43411
  shellTheme
43223
43412
  }) {
43224
- const modalRef = useRef65(null);
43225
- const surfaceRef = useRef65(null);
43413
+ const modalRef = useRef66(null);
43414
+ const surfaceRef = useRef66(null);
43226
43415
  useModalDialog({ rootRef: modalRef, dialogRef: surfaceRef, onClose });
43227
43416
  const extension = relativePath.split(/[?#]/, 1)[0]?.split(".").pop()?.toLowerCase();
43228
43417
  const saveFormat = extension === "png" ? "png" : extension === "jpg" || extension === "jpeg" ? "jpeg" : extension === "webp" ? "webp" : null;
43229
- const sidecar = useMemo57(() => {
43418
+ const sidecar = useMemo58(() => {
43230
43419
  const sanitized = relativePath.replace(/[^a-zA-Z0-9._-]+/g, "_");
43231
43420
  let hash = 2166136261;
43232
43421
  for (let i = 0; i < relativePath.length; i++) {
@@ -43321,10 +43510,10 @@ function ImageEditModal({
43321
43510
  }
43322
43511
 
43323
43512
  // src/mediaEdit/MediaEditContextMenuItems.tsx
43324
- import { useMemo as useMemo58 } from "react";
43513
+ import { useMemo as useMemo59 } from "react";
43325
43514
  function MediaEditContextMenuItems() {
43326
43515
  const { doc, monacoEditor, mediaEditRenders, openMediaEdit } = useEditorContext();
43327
- const items = useMemo58(() => {
43516
+ const items = useMemo59(() => {
43328
43517
  if (!mediaEditRenders) return [];
43329
43518
  const resolve = (context) => {
43330
43519
  if (!doc) return null;
@@ -43363,8 +43552,8 @@ import {
43363
43552
  useCallback as useCallback60,
43364
43553
  useEffect as useEffect74,
43365
43554
  useId as useId16,
43366
- useMemo as useMemo59,
43367
- useRef as useRef69,
43555
+ useMemo as useMemo60,
43556
+ useRef as useRef70,
43368
43557
  useState as useState88
43369
43558
  } from "react";
43370
43559
  import {
@@ -43372,7 +43561,7 @@ import {
43372
43561
  } from "@bendyline/squisq-video-react/media-edit";
43373
43562
 
43374
43563
  // src/mediaEdit/MediaEditAudioSection.tsx
43375
- import { useCallback as useCallback57, useEffect as useEffect72, useRef as useRef66, useState as useState85 } from "react";
43564
+ import { useCallback as useCallback57, useEffect as useEffect72, useRef as useRef67, useState as useState85 } from "react";
43376
43565
  import { MEDIA_FX_SPECS as MEDIA_FX_SPECS2, serializeMediaFx } from "@bendyline/squisq/mediaEdit";
43377
43566
 
43378
43567
  // src/mediaEdit/mediaEditDraft.ts
@@ -43543,11 +43732,11 @@ function useMediaDuration(url) {
43543
43732
  return duration;
43544
43733
  }
43545
43734
  function usePreviewPlayer(preview) {
43546
- const ctxRef = useRef66(null);
43547
- const sourceRef = useRef66(null);
43548
- const startedRef = useRef66(null);
43735
+ const ctxRef = useRef67(null);
43736
+ const sourceRef = useRef67(null);
43737
+ const startedRef = useRef67(null);
43549
43738
  const [playing, setPlaying] = useState85(null);
43550
- const cueRef = useRef66(0);
43739
+ const cueRef = useRef67(0);
43551
43740
  const [position, setPosition] = useState85(0);
43552
43741
  const frames = preview?.original[0]?.length ?? 0;
43553
43742
  const duration = preview && frames > 0 ? frames / preview.sampleRate : 0;
@@ -43659,7 +43848,7 @@ function MediaEditAudioSection({
43659
43848
  const [previewedFx, setPreviewedFx] = useState85(null);
43660
43849
  const [previewing, setPreviewing] = useState85(false);
43661
43850
  const [previewError, setPreviewError] = useState85(null);
43662
- const previewAbort = useRef66(null);
43851
+ const previewAbort = useRef67(null);
43663
43852
  const player = usePreviewPlayer(preview);
43664
43853
  useEffect72(() => () => previewAbort.current?.abort(), []);
43665
43854
  const runPreview = useCallback57(async () => {
@@ -43841,7 +44030,7 @@ function MediaEditAudioSection({
43841
44030
  }
43842
44031
 
43843
44032
  // src/mediaEdit/MediaEditTimingSection.tsx
43844
- import { useCallback as useCallback58, useEffect as useEffect73, useRef as useRef67, useState as useState86 } from "react";
44033
+ import { useCallback as useCallback58, useEffect as useEffect73, useRef as useRef68, useState as useState86 } from "react";
43845
44034
  import { jsx as jsx96, jsxs as jsxs77 } from "react/jsx-runtime";
43846
44035
  var DEFAULT_MAX_PAUSE = 0.8;
43847
44036
  function formatDb(value) {
@@ -43858,7 +44047,7 @@ function MediaEditTimingSection({
43858
44047
  const [maxPause, setMaxPause] = useState86(DEFAULT_MAX_PAUSE);
43859
44048
  const [finding, setFinding] = useState86(false);
43860
44049
  const [error, setError] = useState86(null);
43861
- const abortRef = useRef67(null);
44050
+ const abortRef = useRef68(null);
43862
44051
  useEffect73(() => () => abortRef.current?.abort(), []);
43863
44052
  const findPauses = useCallback58(async () => {
43864
44053
  abortRef.current?.abort();
@@ -43958,7 +44147,7 @@ function MediaEditTimingSection({
43958
44147
  // src/mediaEdit/MediaEditFrameSection.tsx
43959
44148
  import {
43960
44149
  useCallback as useCallback59,
43961
- useRef as useRef68,
44150
+ useRef as useRef69,
43962
44151
  useState as useState87
43963
44152
  } from "react";
43964
44153
  import { normalizeMediaCrop } from "@bendyline/squisq/mediaEdit";
@@ -43987,7 +44176,7 @@ function MediaEditFrameSection({ item, draft, onChange }) {
43987
44176
  const src = useResolvedMediaSrc(item.src);
43988
44177
  const [frame, setFrame] = useState87(null);
43989
44178
  const [aspect, setAspect] = useState87(null);
43990
- const stageRef = useRef68(null);
44179
+ const stageRef = useRef69(null);
43991
44180
  const crop = draft.crop ?? FULL;
43992
44181
  const setCrop = useCallback59(
43993
44182
  (next) => onChange({ ...draft, crop: normalizeMediaCrop(clampCrop(next)) }),
@@ -44178,16 +44367,16 @@ function RenderStatusText({
44178
44367
  }
44179
44368
  function MediaEditModal({ target, onClose }) {
44180
44369
  const { doc, markdownSource, setMarkdownSource, mediaEditRenders } = useEditorContext();
44181
- const modalRef = useRef69(null);
44182
- const surfaceRef = useRef69(null);
44370
+ const modalRef = useRef70(null);
44371
+ const surfaceRef = useRef70(null);
44183
44372
  const titleId = useId16();
44184
44373
  useModalDialog({ rootRef: modalRef, dialogRef: surfaceRef, onClose });
44185
- const item = useMemo59(() => doc ? findEditableMedia(doc, target) : null, [doc, target]);
44186
- const companions = useMemo59(() => doc && item ? groupCompanions(doc, item) : [], [doc, item]);
44374
+ const item = useMemo60(() => doc ? findEditableMedia(doc, target) : null, [doc, target]);
44375
+ const companions = useMemo60(() => doc && item ? groupCompanions(doc, item) : [], [doc, item]);
44187
44376
  const renderStatus = useRenderStatus(item, mediaEditRenders);
44188
44377
  const [draft, setDraft] = useState88(() => draftFromEdits(item?.edits));
44189
44378
  const seedKey = item ? `${item.sourceLine}|${editsSignature(item.edits)}` : null;
44190
- const seededRef = useRef69(null);
44379
+ const seededRef = useRef70(null);
44191
44380
  useEffect74(() => {
44192
44381
  if (seedKey === null || seededRef.current === seedKey) return;
44193
44382
  seededRef.current = seedKey;
@@ -44195,7 +44384,7 @@ function MediaEditModal({ target, onClose }) {
44195
44384
  }, [seedKey]);
44196
44385
  const tabs = TABS.filter((t) => t.id !== "frame" || item?.kind === "video");
44197
44386
  const [activeTab, setActiveTab] = useState88("audio");
44198
- const tabRefs = useRef69(/* @__PURE__ */ new Map());
44387
+ const tabRefs = useRef70(/* @__PURE__ */ new Map());
44199
44388
  const onTabKeyDown = (event, index2) => {
44200
44389
  const next = event.key === "ArrowRight" ? (index2 + 1) % tabs.length : event.key === "ArrowLeft" ? (index2 - 1 + tabs.length) % tabs.length : event.key === "Home" ? 0 : event.key === "End" ? tabs.length - 1 : null;
44201
44390
  if (next == null) return;
@@ -44712,12 +44901,12 @@ function EditorShell({
44712
44901
  defaultThemeLabel
44713
44902
  }) {
44714
44903
  const effectiveContainer = workspaceContainer ?? null;
44715
- const effectiveMediaProvider = useMemo60(() => {
44904
+ const effectiveMediaProvider = useMemo61(() => {
44716
44905
  if (mediaProvider !== void 0) return mediaProvider;
44717
44906
  if (effectiveContainer) return createMediaProviderFromContainer(effectiveContainer);
44718
44907
  return void 0;
44719
44908
  }, [mediaProvider, effectiveContainer]);
44720
- const ownedMediaEditRenders = useMemo60(
44909
+ const ownedMediaEditRenders = useMemo61(
44721
44910
  () => mediaEditRenders === void 0 && effectiveMediaProvider ? createMediaEditRenderManager({ mediaProvider: effectiveMediaProvider }) : null,
44722
44911
  [mediaEditRenders, effectiveMediaProvider]
44723
44912
  );
@@ -44911,7 +45100,7 @@ function EditorShellInner({
44911
45100
  defaultThemeId,
44912
45101
  defaultThemeLabel
44913
45102
  }) {
44914
- const shellRef = useRef70(null);
45103
+ const shellRef = useRef71(null);
44915
45104
  const {
44916
45105
  activeView,
44917
45106
  markdownSource,
@@ -44942,7 +45131,7 @@ function EditorShellInner({
44942
45131
  } = useEditorContext();
44943
45132
  const processedAudio = useProcessedAudio4(mediaEditRenders);
44944
45133
  const { docThemes, onDocThemesChange } = useDocCustomThemes();
44945
- const lastEditingView = useRef70(activeView === "raw" ? "raw" : "wysiwyg");
45134
+ const lastEditingView = useRef71(activeView === "raw" ? "raw" : "wysiwyg");
44946
45135
  useEffect75(() => {
44947
45136
  if (activeView !== "preview") lastEditingView.current = activeView;
44948
45137
  }, [activeView]);
@@ -44959,7 +45148,7 @@ function EditorShellInner({
44959
45148
  workspaceContainer
44960
45149
  );
44961
45150
  const timelineDoc = timelineProjection?.contentDoc ?? doc;
44962
- const timelineRawSchedule = useMemo60(
45151
+ const timelineRawSchedule = useMemo61(
44963
45152
  () => timelineDoc ? resolveMediaSchedule4(timelineDoc) : [],
44964
45153
  [timelineDoc]
44965
45154
  );
@@ -44968,20 +45157,20 @@ function EditorShellInner({
44968
45157
  basePath,
44969
45158
  mediaProvider ?? null
44970
45159
  );
44971
- const timelineDuration = useMemo60(
45160
+ const timelineDuration = useMemo61(
44972
45161
  () => timelineDoc ? getDocPlaybackDuration3(timelineDoc, {
44973
45162
  intrinsicDuration: (clip) => timelineClipDurations.get(clip.src)
44974
45163
  }) : 0,
44975
45164
  [timelineDoc, timelineClipDurations]
44976
45165
  );
44977
- const timelineSchedule = useMemo60(
45166
+ const timelineSchedule = useMemo61(
44978
45167
  () => timelineDoc ? resolveMediaSchedule4(timelineDoc, {
44979
45168
  intrinsicDuration: (clip) => timelineClipDurations.get(clip.src),
44980
45169
  processedAudio
44981
45170
  }) : [],
44982
45171
  [timelineDoc, timelineClipDurations, processedAudio]
44983
45172
  );
44984
- const timelineVideoSchedule = useMemo60(
45173
+ const timelineVideoSchedule = useMemo61(
44985
45174
  () => timelineDoc ? [
44986
45175
  ...timelineSchedule.filter((clip) => clip.kind === "video"),
44987
45176
  ...collectEmbeddedVideoSchedule(timelineDoc)
@@ -44999,11 +45188,11 @@ function EditorShellInner({
44999
45188
  const [mediaCount, setMediaCount] = useState89(0);
45000
45189
  const [mediaBinRecorderOpen, setMediaBinRecorderOpen] = useState89(false);
45001
45190
  const mediaListRefreshKey = mediaRefreshKey + mediaRevision;
45002
- const usedMediaPaths = useMemo60(
45191
+ const usedMediaPaths = useMemo61(
45003
45192
  () => collectMediaReferencesFromMarkdown(markdownSource),
45004
45193
  [markdownSource]
45005
45194
  );
45006
- const imageEditFallbackContainerRef = useRef70(null);
45195
+ const imageEditFallbackContainerRef = useRef71(null);
45007
45196
  if (imageEditFallbackContainerRef.current === null) {
45008
45197
  imageEditFallbackContainerRef.current = new MemoryContentContainer2();
45009
45198
  }
@@ -45048,7 +45237,7 @@ function EditorShellInner({
45048
45237
  const handleOpenMediaBinRecorder = useCallback61(() => {
45049
45238
  setMediaBinRecorderOpen(true);
45050
45239
  }, []);
45051
- const insertionTarget = useMemo60(
45240
+ const insertionTarget = useMemo61(
45052
45241
  () => ({ activeView, tiptapEditor, monacoEditor, appendMarkdown: insertAtCursor }),
45053
45242
  [activeView, tiptapEditor, monacoEditor, insertAtCursor]
45054
45243
  );
@@ -45058,7 +45247,7 @@ function EditorShellInner({
45058
45247
  },
45059
45248
  [insertionTarget]
45060
45249
  );
45061
- const docBasenameRef = useRef70("document");
45250
+ const docBasenameRef = useRef71("document");
45062
45251
  useEffect75(() => {
45063
45252
  let cancelled = false;
45064
45253
  docBasenameRef.current = "document";