@bendyline/squisq-editor-react 2.13.16 → 2.13.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.
@@ -19,7 +19,7 @@ import {
19
19
  useDictationState,
20
20
  useEditorContext,
21
21
  useEditorContextOptional
22
- } from "./chunk-UQUYPT5P.js";
22
+ } from "./chunk-EJNCUVPU.js";
23
23
  import {
24
24
  monacoLanguageRequestKey,
25
25
  monacoLanguagesForDocument,
@@ -30619,7 +30619,7 @@ async function ingestSidecarBytes(bytes, ext, params2) {
30619
30619
 
30620
30620
  // src/blockTagActivity.ts
30621
30621
  import { Extension as Extension11 } from "@tiptap/core";
30622
- import { Plugin as Plugin11, PluginKey as PluginKey11 } from "@tiptap/pm/state";
30622
+ import { Plugin as Plugin11, PluginKey as PluginKey11, TextSelection as TextSelection3 } from "@tiptap/pm/state";
30623
30623
  import { Decoration as Decoration10, DecorationSet as DecorationSet10 } from "@tiptap/pm/view";
30624
30624
  var BLOCK_TAG_SELECTED_CLASS = "squisq-block-tags--selected";
30625
30625
  var BLOCK_TAG_HOVERED_CLASS = "squisq-block-tags--hovered";
@@ -30719,11 +30719,44 @@ function headingPositionForTarget(view, target, clientY) {
30719
30719
  return null;
30720
30720
  }
30721
30721
  }
30722
+ function isEditableDomPosition(view, node2) {
30723
+ const element = node2.nodeType === Node.ELEMENT_NODE ? node2 : node2.parentElement;
30724
+ if (!element || !view.dom.contains(element)) return false;
30725
+ const island = element.closest('[contenteditable="false"]');
30726
+ return !island || !view.dom.contains(island);
30727
+ }
30728
+ function withPendingDomSelection(view, transaction) {
30729
+ const { selection } = view.state;
30730
+ if (view.composing || !view.hasFocus() || !(selection instanceof TextSelection3)) {
30731
+ return transaction;
30732
+ }
30733
+ const domSelection = view.dom.ownerDocument.getSelection();
30734
+ const anchorNode = domSelection?.anchorNode;
30735
+ const focusNode = domSelection?.focusNode;
30736
+ if (!domSelection || !anchorNode || !focusNode) return transaction;
30737
+ if (!isEditableDomPosition(view, anchorNode) || !isEditableDomPosition(view, focusNode)) {
30738
+ return transaction;
30739
+ }
30740
+ let anchor;
30741
+ let head;
30742
+ try {
30743
+ anchor = view.posAtDOM(anchorNode, domSelection.anchorOffset);
30744
+ head = view.posAtDOM(focusNode, domSelection.focusOffset);
30745
+ } catch {
30746
+ return transaction;
30747
+ }
30748
+ if (anchor === selection.anchor && head === selection.head) return transaction;
30749
+ const { doc } = transaction;
30750
+ return transaction.setSelection(TextSelection3.between(doc.resolve(anchor), doc.resolve(head)));
30751
+ }
30722
30752
  function setHoveredPosition(view, hoveredPosition) {
30723
30753
  const current = BLOCK_TAG_ACTIVITY_KEY.getState(view.state)?.hoveredPosition ?? null;
30724
30754
  if (current === hoveredPosition) return;
30725
30755
  view.dispatch(
30726
- view.state.tr.setMeta(BLOCK_TAG_ACTIVITY_KEY, { hoveredPosition }).setMeta("addToHistory", false)
30756
+ withPendingDomSelection(
30757
+ view,
30758
+ view.state.tr.setMeta(BLOCK_TAG_ACTIVITY_KEY, { hoveredPosition }).setMeta("addToHistory", false)
30759
+ )
30727
30760
  );
30728
30761
  }
30729
30762
  var BlockTagActivityExtension = Extension11.create({
@@ -31771,7 +31804,7 @@ import { videoEmbedIframeAttributes } from "@bendyline/squisq/markdown";
31771
31804
 
31772
31805
  // src/documentTray/DocumentTrayExtension.ts
31773
31806
  import { Extension as Extension13 } from "@tiptap/core";
31774
- import { NodeSelection as NodeSelection2, Plugin as Plugin13, PluginKey as PluginKey13, Selection, TextSelection as TextSelection3 } from "@tiptap/pm/state";
31807
+ import { NodeSelection as NodeSelection2, Plugin as Plugin13, PluginKey as PluginKey13, Selection, TextSelection as TextSelection4 } from "@tiptap/pm/state";
31775
31808
  import { Decoration as Decoration12, DecorationSet as DecorationSet12 } from "@tiptap/pm/view";
31776
31809
 
31777
31810
  // src/documentTray/documentTrayMedia.ts
@@ -31870,7 +31903,7 @@ function rangeInTray(state, from, to) {
31870
31903
  function startBody(state) {
31871
31904
  const pos = state.doc.content.size;
31872
31905
  const tr = state.tr.insert(pos, state.schema.nodes.paragraph.create());
31873
- return tr.setSelection(TextSelection3.create(tr.doc, pos + 1));
31906
+ return tr.setSelection(TextSelection4.create(tr.doc, pos + 1));
31874
31907
  }
31875
31908
  function buildState2(doc, enabled, prev, tr) {
31876
31909
  let seq = prev?.seq ?? 0;
@@ -32115,7 +32148,7 @@ var VideoEmbedExtension = Extension14.create({
32115
32148
  });
32116
32149
 
32117
32150
  // src/videoEmbed/videoEmbedPaste.ts
32118
- import { TextSelection as TextSelection4 } from "@tiptap/pm/state";
32151
+ import { TextSelection as TextSelection5 } from "@tiptap/pm/state";
32119
32152
  import { parseVideoEmbedInput as parseVideoEmbedInput3 } from "@bendyline/squisq/markdown";
32120
32153
  function looksLikeEmbedCode(text) {
32121
32154
  return /^<(?!https?:)/i.test(text) && /<iframe\b/i.test(text);
@@ -32137,7 +32170,7 @@ function insertTopLevelBlock(state, node2) {
32137
32170
  tr.insert(start, node2);
32138
32171
  }
32139
32172
  }
32140
- tr.setSelection(TextSelection4.near(tr.doc.resolve(start + node2.nodeSize - 1), -1));
32173
+ tr.setSelection(TextSelection5.near(tr.doc.resolve(start + node2.nodeSize - 1), -1));
32141
32174
  return tr.scrollIntoView();
32142
32175
  }
32143
32176
  function pasteVideoEmbedCode(view, text) {
@@ -34553,7 +34586,7 @@ function persistFromWrite(bodyMd, state) {
34553
34586
  import { useCallback as useCallback43, useEffect as useEffect56, useMemo as useMemo42, useRef as useRef51, useState as useState67 } from "react";
34554
34587
  import { useEditor as useEditor2, EditorContent as EditorContent2 } from "@tiptap/react";
34555
34588
  import { Selection as Selection2 } from "@tiptap/pm/state";
34556
- import { Extension as Extension18, getHTMLFromFragment } from "@tiptap/core";
34589
+ import { Extension as Extension18 } from "@tiptap/core";
34557
34590
  import StarterKit2 from "@tiptap/starter-kit";
34558
34591
  import Table from "@tiptap/extension-table";
34559
34592
  import TableRow from "@tiptap/extension-table-row";
@@ -36269,6 +36302,30 @@ function preserveSource(baseline, edited) {
36269
36302
  return out.join("\n");
36270
36303
  }
36271
36304
 
36305
+ // src/documentHtml.ts
36306
+ import { getHTMLFromFragment } from "@tiptap/core";
36307
+ import { Fragment as Fragment25 } from "@tiptap/pm/model";
36308
+ function createDocumentHtmlSerializer() {
36309
+ const cache2 = /* @__PURE__ */ new WeakMap();
36310
+ return (doc) => {
36311
+ let hasBlockMarks = false;
36312
+ doc.forEach((node2) => {
36313
+ hasBlockMarks || (hasBlockMarks = node2.marks.length > 0);
36314
+ });
36315
+ if (hasBlockMarks) return getHTMLFromFragment(doc.content, doc.type.schema);
36316
+ const parts = [];
36317
+ doc.forEach((node2) => {
36318
+ let html = cache2.get(node2);
36319
+ if (html === void 0) {
36320
+ html = getHTMLFromFragment(Fragment25.from(node2), doc.type.schema);
36321
+ cache2.set(node2, html);
36322
+ }
36323
+ parts.push(html);
36324
+ });
36325
+ return parts.join("");
36326
+ };
36327
+ }
36328
+
36272
36329
  // src/MentionExtension.tsx
36273
36330
  import Mention from "@tiptap/extension-mention";
36274
36331
  import { PluginKey as PluginKey18 } from "@tiptap/pm/state";
@@ -36749,6 +36806,7 @@ function WysiwygEditor({
36749
36806
  const isExternalUpdate = useRef51(false);
36750
36807
  const externalApplyPendingRef = useRef51(false);
36751
36808
  const lastSourceRef = useRef51(editorSource);
36809
+ const serializeHtml = useMemo42(createDocumentHtmlSerializer, []);
36752
36810
  const sourceLayoutRef = useRef51(layoutMode);
36753
36811
  const pendingLocalSourcesRef = useRef51([]);
36754
36812
  const mediaProviderRef = useRef51(mediaProvider);
@@ -36898,12 +36956,12 @@ function WysiwygEditor({
36898
36956
  if (!transaction.docChanged) return;
36899
36957
  if (isExternalUpdate.current || externalApplyPendingRef.current) return;
36900
36958
  const serialize = (html) => persistFromWrite(tiptapToMarkdown(html), wrapStateRef.current);
36901
- let bodyMd = serialize(ed.getHTML());
36959
+ let bodyMd = serialize(serializeHtml(ed.state.doc));
36902
36960
  const loaded = loadedSourceRef.current;
36903
36961
  if (loaded) {
36904
36962
  loaded.baseline ?? (loaded.baseline = createSourceBaseline(
36905
36963
  loaded.body,
36906
- serialize(getHTMLFromFragment(transaction.before.content, ed.schema))
36964
+ serialize(serializeHtml(transaction.before))
36907
36965
  ));
36908
36966
  bodyMd = preserveSource(loaded.baseline, bodyMd);
36909
36967
  }
@@ -37488,7 +37546,7 @@ function insertDroppedMedia(view, event, point, mediaProvider, onMediaUploaded)
37488
37546
  }
37489
37547
 
37490
37548
  // src/InlinePreviewGutter.tsx
37491
- import { useLayoutEffect as useLayoutEffect13, useMemo as useMemo45, useRef as useRef52, useState as useState70 } from "react";
37549
+ import { useEffect as useEffect59, useLayoutEffect as useLayoutEffect13, useMemo as useMemo45, useRef as useRef52, useState as useState70 } from "react";
37492
37550
  import { VIEWPORT_PRESETS as VIEWPORT_PRESETS5 } from "@bendyline/squisq/schemas";
37493
37551
  import { BlockRenderer as BlockRenderer3, MediaContext as MediaContext4 } from "@bendyline/squisq-react";
37494
37552
 
@@ -37803,7 +37861,7 @@ function usePreviewProjection(doc, transformStyle, workspaceContainer, documentT
37803
37861
  }
37804
37862
 
37805
37863
  // src/useBlockPreviewItems.ts
37806
- function useBlockPreviewItems(viewport) {
37864
+ function useBlockPreviewItems(viewport, visibleIds) {
37807
37865
  const { doc, workspaceContainer, fileName, mediaRevision } = useEditorContext();
37808
37866
  const projection = usePreviewProjection(doc, "", workspaceContainer, fileName, mediaRevision);
37809
37867
  const previewDoc = projection?.contentDoc ?? doc;
@@ -37814,25 +37872,26 @@ function useBlockPreviewItems(viewport) {
37814
37872
  const flat = flattenRenderableBlocks(previewDoc.blocks);
37815
37873
  const result = [];
37816
37874
  flat.forEach((block, index2) => {
37817
- const visual = resolveBlockVisual(previewDoc, block, activeTheme, viewport, {
37875
+ const shouldResolve = visibleIds == null || visibleIds.has(block.id);
37876
+ const visual = shouldResolve ? resolveBlockVisual(previewDoc, block, activeTheme, viewport, {
37818
37877
  blockIndex: index2,
37819
37878
  totalBlocks: flat.length
37820
- });
37821
- if (!visual) return;
37879
+ }) : null;
37880
+ if (visibleIds == null && !visual) return;
37822
37881
  result.push({
37823
37882
  id: block.id,
37824
- template: visual.template ?? "content",
37883
+ template: visual?.template ?? block.template ?? "content",
37825
37884
  headingText: block.sourceHeading ? extractPlainText3(block.sourceHeading) : "",
37826
37885
  block: visual,
37827
37886
  sourceBlock: block
37828
37887
  });
37829
37888
  });
37830
37889
  return result;
37831
- }, [previewDoc, viewport, activeTheme]);
37890
+ }, [previewDoc, viewport, activeTheme, visibleIds]);
37832
37891
  }
37833
37892
 
37834
37893
  // src/InlinePreviewGutter.tsx
37835
- import { Fragment as Fragment25, jsx as jsx73, jsxs as jsxs59 } from "react/jsx-runtime";
37894
+ import { Fragment as Fragment26, jsx as jsx73, jsxs as jsxs59 } from "react/jsx-runtime";
37836
37895
  var BAR_RIGHT_OFFSET = 14;
37837
37896
  var CARD_LABEL_OFFSET = 16;
37838
37897
  var EXTENT_TOP_PAD = CARD_LABEL_OFFSET;
@@ -37847,13 +37906,56 @@ function InlinePreviewGutter({
37847
37906
  }) {
37848
37907
  const gutterRef = useRef52(null);
37849
37908
  const { entries: headingEntries, scrollToBlock } = useHeadingLayout(gutterRef);
37850
- const items = useBlockPreviewItems(viewport);
37909
+ const [visibleCards, setVisibleCards] = useState70(
37910
+ () => typeof IntersectionObserver === "undefined" ? null : /* @__PURE__ */ new Set()
37911
+ );
37912
+ const items = useBlockPreviewItems(viewport, visibleCards);
37851
37913
  const connectorTops = useMemo45(() => {
37852
37914
  const m = /* @__PURE__ */ new Map();
37853
37915
  headingEntries.forEach((e2) => m.set(e2.block.id, e2.top));
37854
37916
  return m;
37855
37917
  }, [headingEntries]);
37856
37918
  const [positions, setPositions] = useState70(/* @__PURE__ */ new Map());
37919
+ const itemIds = items.map((item) => item.id).join("\0");
37920
+ useEffect59(() => {
37921
+ const gutter = gutterRef.current;
37922
+ if (!gutter || typeof IntersectionObserver === "undefined") return;
37923
+ let active = true;
37924
+ const cards = [...gutter.querySelectorAll(".squisq-inline-preview-card")].filter(
37925
+ (card) => positions.has(card.dataset.previewId ?? "")
37926
+ );
37927
+ const ids = new Set(cards.map((card) => card.dataset.previewId));
37928
+ setVisibleCards((previous) => {
37929
+ if (previous === null || [...previous].every((id) => ids.has(id))) return previous;
37930
+ return new Set([...previous].filter((id) => ids.has(id)));
37931
+ });
37932
+ const observer = new IntersectionObserver(
37933
+ (entries) => {
37934
+ if (!active) return;
37935
+ setVisibleCards((previous) => {
37936
+ const next = new Set(previous);
37937
+ for (const entry of entries) {
37938
+ const id = entry.target.dataset.previewId;
37939
+ if (!id) continue;
37940
+ if (entry.isIntersecting) next.add(id);
37941
+ else next.delete(id);
37942
+ }
37943
+ if (previous?.size === next.size && [...next].every((id) => previous.has(id))) {
37944
+ return previous;
37945
+ }
37946
+ return next;
37947
+ });
37948
+ },
37949
+ { root: gutter, rootMargin: "320px 0px" }
37950
+ );
37951
+ for (const card of cards) {
37952
+ observer.observe(card);
37953
+ }
37954
+ return () => {
37955
+ active = false;
37956
+ observer.disconnect();
37957
+ };
37958
+ }, [itemIds, positions]);
37857
37959
  useLayoutEffect13(() => {
37858
37960
  if (items.length === 0) {
37859
37961
  setPositions((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
@@ -37890,156 +37992,170 @@ function InlinePreviewGutter({
37890
37992
  return next;
37891
37993
  });
37892
37994
  }, [items, connectorTops]);
37893
- const isEmpty2 = items.length === 0;
37894
- if (isEmpty2) return null;
37895
- const totalWidth = width + connectorWidth;
37896
- const gutterStyle = {
37897
- position: "relative",
37898
- width: `${totalWidth}px`,
37899
- flex: `0 0 ${totalWidth}px`,
37900
- overflow: "hidden"
37901
- };
37902
- return /* @__PURE__ */ jsxs59(
37903
- "aside",
37904
- {
37905
- ref: gutterRef,
37906
- className: `squisq-inline-preview-gutter ${className ?? ""}`,
37907
- style: gutterStyle,
37908
- "data-testid": "inline-preview-gutter",
37909
- "aria-label": "Block previews",
37910
- children: [
37911
- headingEntries.length > 0 && /* @__PURE__ */ jsx73(
37912
- "div",
37913
- {
37914
- className: "squisq-inline-preview-connectors",
37915
- "aria-hidden": "true",
37916
- style: {
37917
- position: "absolute",
37918
- top: 0,
37919
- left: 0,
37920
- width: connectorWidth,
37921
- height: "100%",
37922
- overflow: "hidden",
37923
- pointerEvents: "none"
37924
- },
37925
- children: headingEntries.map((ex, i) => {
37926
- const EXTENT_GAP = 6;
37927
- const height = Math.max(2, ex.bottom - ex.top - EXTENT_GAP);
37928
- return /* @__PURE__ */ jsx73(
37929
- "div",
37930
- {
37931
- className: `squisq-inline-preview-extent${ex.annotated ? "" : " squisq-inline-preview-extent--untagged"}`,
37932
- style: {
37933
- position: "absolute",
37934
- top: `${ex.top}px`,
37935
- height: `${height}px`,
37936
- right: `${BAR_RIGHT_OFFSET}px`
37937
- }
37938
- },
37939
- `h-${i}-${ex.block.id}`
37940
- );
37941
- })
37942
- }
37943
- ),
37944
- items.length > 0 && /* @__PURE__ */ jsx73(
37945
- "svg",
37946
- {
37947
- className: "squisq-inline-preview-connector-svg",
37948
- "aria-hidden": "true",
37949
- style: {
37950
- position: "absolute",
37951
- top: 0,
37952
- left: 0,
37953
- width: connectorWidth + CARD_LEFT_INSET,
37954
- height: "100%",
37955
- pointerEvents: "none",
37956
- overflow: "visible"
37957
- },
37958
- children: items.map((item) => {
37959
- const headingTop = connectorTops.get(item.id);
37960
- const cardTop = positions.get(item.id);
37961
- if (headingTop == null || cardTop == null) return null;
37962
- const x1 = connectorWidth - BAR_RIGHT_OFFSET - 2;
37963
- const y1 = headingTop + EXTENT_TOP_PAD;
37964
- const x2 = connectorWidth + CARD_LEFT_INSET;
37965
- const y2 = cardTop + CARD_LABEL_OFFSET;
37966
- return /* @__PURE__ */ jsxs59("g", { children: [
37967
- /* @__PURE__ */ jsx73(
37968
- "line",
37969
- {
37970
- x1,
37971
- y1,
37972
- x2,
37973
- y2,
37974
- stroke: "#6366f1",
37975
- strokeWidth: "2",
37976
- strokeLinecap: "round"
37977
- }
37978
- ),
37979
- /* @__PURE__ */ jsx73("circle", { cx: x1, cy: y1, r: "4", fill: "#6366f1", stroke: "#ffffff", strokeWidth: "2" }),
37980
- /* @__PURE__ */ jsx73("circle", { cx: x2, cy: y2, r: "4", fill: "#6366f1", stroke: "#ffffff", strokeWidth: "2" })
37981
- ] }, item.id);
37982
- })
37983
- }
37984
- ),
37985
- /* @__PURE__ */ jsx73(MediaContext4.Provider, { value: mediaProvider ?? null, children: items.map((item) => {
37986
- const top = positions.get(item.id);
37987
- const hidden = top == null;
37988
- return /* @__PURE__ */ jsxs59(
37995
+ return useMemo45(() => {
37996
+ if (items.length === 0) return null;
37997
+ const totalWidth = width + connectorWidth;
37998
+ const gutterStyle = {
37999
+ position: "relative",
38000
+ width: `${totalWidth}px`,
38001
+ flex: `0 0 ${totalWidth}px`,
38002
+ overflow: "hidden"
38003
+ };
38004
+ return /* @__PURE__ */ jsxs59(
38005
+ "aside",
38006
+ {
38007
+ ref: gutterRef,
38008
+ className: `squisq-inline-preview-gutter ${className ?? ""}`,
38009
+ style: gutterStyle,
38010
+ "data-testid": "inline-preview-gutter",
38011
+ "aria-label": "Block previews",
38012
+ children: [
38013
+ headingEntries.length > 0 && /* @__PURE__ */ jsx73(
37989
38014
  "div",
37990
38015
  {
37991
- className: "squisq-inline-preview-card",
37992
- "data-template": item.template,
38016
+ className: "squisq-inline-preview-connectors",
38017
+ "aria-hidden": "true",
37993
38018
  style: {
37994
38019
  position: "absolute",
37995
- top: `${top ?? 0}px`,
37996
- left: connectorWidth + CARD_LEFT_INSET,
37997
- right: 12,
37998
- visibility: hidden ? "hidden" : "visible"
38020
+ top: 0,
38021
+ left: 0,
38022
+ width: connectorWidth,
38023
+ height: "100%",
38024
+ overflow: "hidden",
38025
+ pointerEvents: "none"
37999
38026
  },
38000
- onClick: () => scrollToBlock(item.sourceBlock),
38001
- children: [
38002
- /* @__PURE__ */ jsxs59("div", { className: "squisq-inline-preview-card-label", children: [
38003
- /* @__PURE__ */ jsx73("span", { className: "squisq-inline-preview-card-template", children: templateLabel(item.template) }),
38004
- item.headingText && /* @__PURE__ */ jsxs59(Fragment25, { children: [
38005
- /* @__PURE__ */ jsx73("span", { className: "squisq-inline-preview-card-sep", children: "\u2014" }),
38006
- /* @__PURE__ */ jsx73("span", { className: "squisq-inline-preview-card-title", children: item.headingText })
38007
- ] })
38008
- ] }),
38009
- /* @__PURE__ */ jsx73(
38027
+ children: headingEntries.map((ex, i) => {
38028
+ const EXTENT_GAP = 6;
38029
+ const height = Math.max(2, ex.bottom - ex.top - EXTENT_GAP);
38030
+ return /* @__PURE__ */ jsx73(
38010
38031
  "div",
38011
38032
  {
38012
- className: "squisq-inline-preview-card-svg",
38033
+ className: `squisq-inline-preview-extent${ex.annotated ? "" : " squisq-inline-preview-extent--untagged"}`,
38013
38034
  style: {
38014
- aspectRatio: `${viewport.width} / ${viewport.height}`
38015
- },
38016
- children: /* @__PURE__ */ jsx73(
38017
- BlockRenderer3,
38018
- {
38019
- block: item.block,
38020
- buildPlayback: "all",
38021
- blockTime: 0,
38022
- basePath,
38023
- viewport
38024
- }
38025
- )
38026
- }
38027
- )
38028
- ]
38029
- },
38030
- item.id
38031
- );
38032
- }) })
38033
- ]
38034
- }
38035
- );
38035
+ position: "absolute",
38036
+ top: `${ex.top}px`,
38037
+ height: `${height}px`,
38038
+ right: `${BAR_RIGHT_OFFSET}px`
38039
+ }
38040
+ },
38041
+ `h-${i}-${ex.block.id}`
38042
+ );
38043
+ })
38044
+ }
38045
+ ),
38046
+ items.length > 0 && /* @__PURE__ */ jsx73(
38047
+ "svg",
38048
+ {
38049
+ className: "squisq-inline-preview-connector-svg",
38050
+ "aria-hidden": "true",
38051
+ style: {
38052
+ position: "absolute",
38053
+ top: 0,
38054
+ left: 0,
38055
+ width: connectorWidth + CARD_LEFT_INSET,
38056
+ height: "100%",
38057
+ pointerEvents: "none",
38058
+ overflow: "visible"
38059
+ },
38060
+ children: items.map((item) => {
38061
+ const headingTop = connectorTops.get(item.id);
38062
+ const cardTop = positions.get(item.id);
38063
+ if (headingTop == null || cardTop == null) return null;
38064
+ const x1 = connectorWidth - BAR_RIGHT_OFFSET - 2;
38065
+ const y1 = headingTop + EXTENT_TOP_PAD;
38066
+ const x2 = connectorWidth + CARD_LEFT_INSET;
38067
+ const y2 = cardTop + CARD_LABEL_OFFSET;
38068
+ return /* @__PURE__ */ jsxs59("g", { children: [
38069
+ /* @__PURE__ */ jsx73(
38070
+ "line",
38071
+ {
38072
+ x1,
38073
+ y1,
38074
+ x2,
38075
+ y2,
38076
+ stroke: "#6366f1",
38077
+ strokeWidth: "2",
38078
+ strokeLinecap: "round"
38079
+ }
38080
+ ),
38081
+ /* @__PURE__ */ jsx73("circle", { cx: x1, cy: y1, r: "4", fill: "#6366f1", stroke: "#ffffff", strokeWidth: "2" }),
38082
+ /* @__PURE__ */ jsx73("circle", { cx: x2, cy: y2, r: "4", fill: "#6366f1", stroke: "#ffffff", strokeWidth: "2" })
38083
+ ] }, item.id);
38084
+ })
38085
+ }
38086
+ ),
38087
+ /* @__PURE__ */ jsx73(MediaContext4.Provider, { value: mediaProvider ?? null, children: items.map((item) => {
38088
+ const top = positions.get(item.id);
38089
+ const hidden = top == null;
38090
+ return /* @__PURE__ */ jsxs59(
38091
+ "div",
38092
+ {
38093
+ className: "squisq-inline-preview-card",
38094
+ "data-template": item.template,
38095
+ "data-preview-id": item.id,
38096
+ style: {
38097
+ position: "absolute",
38098
+ top: `${top ?? 0}px`,
38099
+ left: connectorWidth + CARD_LEFT_INSET,
38100
+ right: 12,
38101
+ visibility: hidden ? "hidden" : "visible"
38102
+ },
38103
+ onClick: () => scrollToBlock(item.sourceBlock),
38104
+ children: [
38105
+ /* @__PURE__ */ jsxs59("div", { className: "squisq-inline-preview-card-label", children: [
38106
+ /* @__PURE__ */ jsx73("span", { className: "squisq-inline-preview-card-template", children: templateLabel(item.template) }),
38107
+ item.headingText && /* @__PURE__ */ jsxs59(Fragment26, { children: [
38108
+ /* @__PURE__ */ jsx73("span", { className: "squisq-inline-preview-card-sep", children: "\u2014" }),
38109
+ /* @__PURE__ */ jsx73("span", { className: "squisq-inline-preview-card-title", children: item.headingText })
38110
+ ] })
38111
+ ] }),
38112
+ /* @__PURE__ */ jsx73(
38113
+ "div",
38114
+ {
38115
+ className: "squisq-inline-preview-card-svg",
38116
+ style: {
38117
+ aspectRatio: `${viewport.width} / ${viewport.height}`
38118
+ },
38119
+ children: item.block && /* @__PURE__ */ jsx73(
38120
+ BlockRenderer3,
38121
+ {
38122
+ block: item.block,
38123
+ buildPlayback: "all",
38124
+ blockTime: 0,
38125
+ basePath,
38126
+ viewport
38127
+ }
38128
+ )
38129
+ }
38130
+ )
38131
+ ]
38132
+ },
38133
+ item.id
38134
+ );
38135
+ }) })
38136
+ ]
38137
+ }
38138
+ );
38139
+ }, [
38140
+ items,
38141
+ width,
38142
+ connectorWidth,
38143
+ className,
38144
+ headingEntries,
38145
+ connectorTops,
38146
+ positions,
38147
+ mediaProvider,
38148
+ scrollToBlock,
38149
+ viewport,
38150
+ basePath
38151
+ ]);
38036
38152
  }
38037
38153
 
38038
38154
  // src/OutlinePanel.tsx
38039
38155
  import {
38040
38156
  memo,
38041
38157
  useCallback as useCallback45,
38042
- useEffect as useEffect59,
38158
+ useEffect as useEffect60,
38043
38159
  useMemo as useMemo46,
38044
38160
  useRef as useRef53,
38045
38161
  useState as useState71
@@ -38512,10 +38628,10 @@ function useActiveOutlineBlockId() {
38512
38628
  flatBlocksRef.current = flatBlocks;
38513
38629
  const headingLocations = flatBlocks.map((block) => `${block.id}:${block.sourceHeading?.position?.start.line ?? ""}`).join("\0");
38514
38630
  const [activeId, setActiveId] = useState71(null);
38515
- useEffect59(() => {
38631
+ useEffect60(() => {
38516
38632
  setActiveId(null);
38517
38633
  }, [activeView]);
38518
- useEffect59(() => {
38634
+ useEffect60(() => {
38519
38635
  if (activeView !== "wysiwyg" || !tiptapEditor) return;
38520
38636
  const update = () => {
38521
38637
  const { from } = tiptapEditor.state.selection;
@@ -38537,7 +38653,7 @@ function useActiveOutlineBlockId() {
38537
38653
  tiptapEditor.off("update", update);
38538
38654
  };
38539
38655
  }, [activeView, tiptapEditor, headingLocations]);
38540
- useEffect59(() => {
38656
+ useEffect60(() => {
38541
38657
  if (activeView !== "raw" || !monacoEditor) return;
38542
38658
  const update = () => {
38543
38659
  const line = monacoEditor.getPosition()?.lineNumber;
@@ -38624,7 +38740,7 @@ function bumpHeadingLevelInSource(source, line, delta) {
38624
38740
  }
38625
38741
 
38626
38742
  // src/codeContext/CodeContextZones.tsx
38627
- import { useCallback as useCallback47, useEffect as useEffect61, useMemo as useMemo48, useRef as useRef55, useState as useState72 } from "react";
38743
+ import { useCallback as useCallback47, useEffect as useEffect62, useMemo as useMemo48, useRef as useRef55, useState as useState72 } from "react";
38628
38744
  import { createPortal as createPortal15 } from "react-dom";
38629
38745
 
38630
38746
  // src/codeContext/diffContextSections.ts
@@ -38748,7 +38864,7 @@ function setOrdinal(zone, ordinal) {
38748
38864
  // src/codeContext/CodeContextSectionView.tsx
38749
38865
  import { parseMarkdown as parseMarkdown8 } from "@bendyline/squisq/markdown";
38750
38866
  import { MarkdownRenderer } from "@bendyline/squisq-react";
38751
- import { useCallback as useCallback46, useEffect as useEffect60, useMemo as useMemo47, useRef as useRef54 } from "react";
38867
+ import { useCallback as useCallback46, useEffect as useEffect61, useMemo as useMemo47, useRef as useRef54 } from "react";
38752
38868
  import { jsx as jsx75, jsxs as jsxs61 } from "react/jsx-runtime";
38753
38869
  function CodeContextSectionView({
38754
38870
  section,
@@ -38768,7 +38884,7 @@ function CodeContextSectionView({
38768
38884
  () => expanded && section.markdown ? parseMarkdown8(section.markdown).children : null,
38769
38885
  [expanded, section.markdown]
38770
38886
  );
38771
- useEffect60(() => {
38887
+ useEffect61(() => {
38772
38888
  const el = rootRef.current;
38773
38889
  if (!el || typeof ResizeObserver === "undefined") return;
38774
38890
  const report = () => {
@@ -38837,14 +38953,14 @@ function CodeContextSectionView({
38837
38953
  }
38838
38954
 
38839
38955
  // src/codeContext/CodeContextZones.tsx
38840
- import { Fragment as Fragment26, jsx as jsx76 } from "react/jsx-runtime";
38956
+ import { Fragment as Fragment27, jsx as jsx76 } from "react/jsx-runtime";
38841
38957
  function CodeContextZones({ options }) {
38842
38958
  const { monacoEditor } = useEditorContext();
38843
38959
  const [manager, setManager] = useState72(null);
38844
38960
  const [, setZonesVersion] = useState72(0);
38845
38961
  const [expandedById, setExpandedById] = useState72({});
38846
38962
  const seenIds = useRef55(/* @__PURE__ */ new Set());
38847
- useEffect61(() => {
38963
+ useEffect62(() => {
38848
38964
  if (!monacoEditor) return;
38849
38965
  const mgr = new CodeContextZoneManager(monacoEditor);
38850
38966
  const off = mgr.onDidChangeZones(() => setZonesVersion((v2) => v2 + 1));
@@ -38867,7 +38983,7 @@ function CodeContextZones({ options }) {
38867
38983
  });
38868
38984
  return out;
38869
38985
  }, [fileTop, sections]);
38870
- useEffect61(() => {
38986
+ useEffect62(() => {
38871
38987
  if (!manager) return;
38872
38988
  manager.sync(resolved.map((r) => r.spec));
38873
38989
  setExpandedById((prev) => {
@@ -38906,7 +39022,7 @@ function CodeContextZones({ options }) {
38906
39022
  [monacoEditor]
38907
39023
  );
38908
39024
  if (!manager) return null;
38909
- return /* @__PURE__ */ jsx76(Fragment26, { children: resolved.map(({ section }) => {
39025
+ return /* @__PURE__ */ jsx76(Fragment27, { children: resolved.map(({ section }) => {
38910
39026
  const domNode = manager.getDomNode(section.id);
38911
39027
  if (!domNode) return null;
38912
39028
  return createPortal15(
@@ -39333,7 +39449,7 @@ function placeClipInBlock(source, fromLine, targetHeadingLine, spec, startAt) {
39333
39449
  }
39334
39450
 
39335
39451
  // src/TimelineTrack.tsx
39336
- import { useCallback as useCallback49, useEffect as useEffect64, useMemo as useMemo49, useRef as useRef58, useState as useState75 } from "react";
39452
+ import { useCallback as useCallback49, useEffect as useEffect65, useMemo as useMemo49, useRef as useRef58, useState as useState75 } from "react";
39337
39453
  import {
39338
39454
  resolveMediaSchedule as resolveMediaSchedule2,
39339
39455
  getDocPlaybackDuration,
@@ -39370,7 +39486,7 @@ var BlockThumbnail = memo2(function BlockThumbnail2({
39370
39486
  });
39371
39487
 
39372
39488
  // src/useTimelineClock.ts
39373
- import { useCallback as useCallback48, useEffect as useEffect62, useRef as useRef56, useState as useState73 } from "react";
39489
+ import { useCallback as useCallback48, useEffect as useEffect63, useRef as useRef56, useState as useState73 } from "react";
39374
39490
  function advanceTime(prev, dt, total) {
39375
39491
  if (total <= 0) return 0;
39376
39492
  return Math.min(total, Math.max(0, prev + dt));
@@ -39399,10 +39515,10 @@ function useTimelineClock(total) {
39399
39515
  const mediaHostRef = useRef56(null);
39400
39516
  currentTimeRef.current = currentTime;
39401
39517
  isPlayingRef.current = isPlaying;
39402
- useEffect62(() => {
39518
+ useEffect63(() => {
39403
39519
  setCurrentTime((t) => Math.min(t, Math.max(0, total)));
39404
39520
  }, [total]);
39405
- useEffect62(() => {
39521
+ useEffect63(() => {
39406
39522
  if (!isPlaying) return;
39407
39523
  lastRef.current = performance.now();
39408
39524
  const tick = (now) => {
@@ -39500,9 +39616,9 @@ function timelineBars(clips) {
39500
39616
  }
39501
39617
 
39502
39618
  // src/TimelineItemMenu.tsx
39503
- import { useEffect as useEffect63, useLayoutEffect as useLayoutEffect14, useRef as useRef57, useState as useState74 } from "react";
39619
+ import { useEffect as useEffect64, useLayoutEffect as useLayoutEffect14, useRef as useRef57, useState as useState74 } from "react";
39504
39620
  import { createPortal as createPortal16 } from "react-dom";
39505
- import { Fragment as Fragment27, jsx as jsx79, jsxs as jsxs63 } from "react/jsx-runtime";
39621
+ import { Fragment as Fragment28, jsx as jsx79, jsxs as jsxs63 } from "react/jsx-runtime";
39506
39622
  function TimelineItemMenu({
39507
39623
  anchor,
39508
39624
  target,
@@ -39531,7 +39647,7 @@ function TimelineItemMenu({
39531
39647
  resizeObserver?.disconnect();
39532
39648
  };
39533
39649
  }, [anchor]);
39534
- useEffect63(() => {
39650
+ useEffect64(() => {
39535
39651
  const onKeyDown = (event) => {
39536
39652
  if (event.key !== "Escape") return;
39537
39653
  event.preventDefault();
@@ -39577,7 +39693,7 @@ function TimelineItemMenu({
39577
39693
  onClearTiming: onBlockClearTiming,
39578
39694
  onTransition: onBlockTransition
39579
39695
  }
39580
- ) : /* @__PURE__ */ jsxs63(Fragment27, { children: [
39696
+ ) : /* @__PURE__ */ jsxs63(Fragment28, { children: [
39581
39697
  /* @__PURE__ */ jsx79(VideoMenu, { target, onPatch: onVideoPatch }),
39582
39698
  onEditMedia && /* @__PURE__ */ jsx79(
39583
39699
  "button",
@@ -39625,7 +39741,7 @@ function BlockMenu({
39625
39741
  };
39626
39742
  const pinned = explicitDuration || startTime !== "";
39627
39743
  return /* @__PURE__ */ jsxs63("div", { className: "squisq-timeline-item-menu-body", children: [
39628
- target.timingLocked ? /* @__PURE__ */ jsxs63(Fragment27, { children: [
39744
+ target.timingLocked ? /* @__PURE__ */ jsxs63(Fragment28, { children: [
39629
39745
  /* @__PURE__ */ jsxs63("p", { className: "squisq-timeline-item-menu-hint", children: [
39630
39746
  "Dynamic slides time each slide from the narration or reading pace, so this section's length and start can't be pinned.",
39631
39747
  pinned && " A pinned time here stops a narrated deck from playing."
@@ -39643,7 +39759,7 @@ function BlockMenu({
39643
39759
  children: "Clear pinned timing"
39644
39760
  }
39645
39761
  )
39646
- ] }) : /* @__PURE__ */ jsxs63(Fragment27, { children: [
39762
+ ] }) : /* @__PURE__ */ jsxs63(Fragment28, { children: [
39647
39763
  /* @__PURE__ */ jsxs63("label", { className: "squisq-timeline-item-menu-check", children: [
39648
39764
  /* @__PURE__ */ jsx79(
39649
39765
  "input",
@@ -39745,7 +39861,7 @@ function VideoMenu({
39745
39861
  }
39746
39862
  )
39747
39863
  ] }),
39748
- placed && /* @__PURE__ */ jsxs63(Fragment27, { children: [
39864
+ placed && /* @__PURE__ */ jsxs63(Fragment28, { children: [
39749
39865
  /* @__PURE__ */ jsxs63("label", { className: "squisq-timeline-item-menu-check", children: [
39750
39866
  /* @__PURE__ */ jsx79(
39751
39867
  "input",
@@ -39763,7 +39879,7 @@ function VideoMenu({
39763
39879
  ),
39764
39880
  /* @__PURE__ */ jsx79("span", { children: "Lock to block" })
39765
39881
  ] }),
39766
- placement === "picture-in-picture" && /* @__PURE__ */ jsxs63(Fragment27, { children: [
39882
+ placement === "picture-in-picture" && /* @__PURE__ */ jsxs63(Fragment28, { children: [
39767
39883
  /* @__PURE__ */ jsxs63("label", { className: "squisq-timeline-item-menu-field", children: [
39768
39884
  /* @__PURE__ */ jsx79("span", { className: "squisq-timeline-item-menu-label", children: "PIP size" }),
39769
39885
  /* @__PURE__ */ jsxs63(
@@ -40019,7 +40135,7 @@ function TimelineTrack({
40019
40135
  play();
40020
40136
  }, [play]);
40021
40137
  const [scrubbing, setScrubbing] = useState75(false);
40022
- useEffect64(() => {
40138
+ useEffect65(() => {
40023
40139
  if (!scrubbing) return;
40024
40140
  const onMove = (e2) => {
40025
40141
  const scroll = scrollRef.current;
@@ -40057,7 +40173,7 @@ function TimelineTrack({
40057
40173
  [blocks]
40058
40174
  );
40059
40175
  const followedBlockRef = useRef58(null);
40060
- useEffect64(() => {
40176
+ useEffect65(() => {
40061
40177
  if (!isPlaying) {
40062
40178
  followedBlockRef.current = null;
40063
40179
  return;
@@ -40068,7 +40184,7 @@ function TimelineTrack({
40068
40184
  const line = headingLine(block);
40069
40185
  if (line != null) goToBlockByLine(line);
40070
40186
  }, [isPlaying, currentTime, blockAtTime, goToBlockByLine]);
40071
- useEffect64(() => {
40187
+ useEffect65(() => {
40072
40188
  if (!isPlaying) return;
40073
40189
  const scroll = scrollRef.current;
40074
40190
  if (!scroll) return;
@@ -40117,7 +40233,7 @@ function TimelineTrack({
40117
40233
  const zoomIn = useCallback49(() => setPxPerSecond((s) => Math.min(ZOOM_MAX, s * ZOOM_FACTOR)), []);
40118
40234
  const zoomOut = useCallback49(() => setPxPerSecond((s) => Math.max(ZOOM_MIN, s / ZOOM_FACTOR)), []);
40119
40235
  const [viewportWidth, setViewportWidth] = useState75(0);
40120
- useEffect64(() => {
40236
+ useEffect65(() => {
40121
40237
  const el = scrollRef.current;
40122
40238
  if (!el || typeof ResizeObserver === "undefined") return;
40123
40239
  const update = () => setViewportWidth(el.clientWidth);
@@ -40131,7 +40247,7 @@ function TimelineTrack({
40131
40247
  const dragRef = useRef58(null);
40132
40248
  dragRef.current = drag;
40133
40249
  const isDragging = drag != null && !drag.committed;
40134
- useEffect64(() => {
40250
+ useEffect65(() => {
40135
40251
  if (!isDragging) return;
40136
40252
  const onMove = (e2) => {
40137
40253
  const d = dragRef.current;
@@ -40162,7 +40278,7 @@ function TimelineTrack({
40162
40278
  window.removeEventListener("pointerup", onUp);
40163
40279
  };
40164
40280
  }, [isDragging]);
40165
- useEffect64(() => {
40281
+ useEffect65(() => {
40166
40282
  if (dragRef.current?.committed) setDrag(null);
40167
40283
  }, [doc]);
40168
40284
  const beginDrag = useCallback49(
@@ -41024,7 +41140,7 @@ function TimelineToolbar({
41024
41140
  }
41025
41141
 
41026
41142
  // src/PlainHtmlPreview.tsx
41027
- import { useCallback as useCallback50, useEffect as useEffect65, useMemo as useMemo52, useRef as useRef59, useState as useState76 } from "react";
41143
+ import { useCallback as useCallback50, useEffect as useEffect66, useMemo as useMemo52, useRef as useRef59, useState as useState76 } from "react";
41028
41144
  import {
41029
41145
  isSvgImageSource as isSvgImageSource2,
41030
41146
  parseMarkdown as parseMarkdown9,
@@ -41134,7 +41250,7 @@ function PlainHtmlPreview({
41134
41250
  const parsedDoc = useMemo52(() => parseMarkdown9(markdown), [markdown]);
41135
41251
  const [wikiProjection, setWikiProjection] = useState76(null);
41136
41252
  const mdDoc = wikiProjection?.source === parsedDoc && wikiProjection.support === wikiLinks ? wikiProjection.doc : parsedDoc;
41137
- useEffect65(() => {
41253
+ useEffect66(() => {
41138
41254
  if (!wikiLinks) return;
41139
41255
  let cancelled = false;
41140
41256
  void resolveWikiLinks(
@@ -41153,7 +41269,7 @@ function PlainHtmlPreview({
41153
41269
  };
41154
41270
  }, [parsedDoc, wikiLinks]);
41155
41271
  const [resolvedImages, setResolvedImages] = useState76(null);
41156
- useEffect65(() => {
41272
+ useEffect66(() => {
41157
41273
  if (!mediaProvider) {
41158
41274
  setResolvedImages(null);
41159
41275
  return;
@@ -41190,7 +41306,7 @@ function PlainHtmlPreview({
41190
41306
  }, [resolvedImages, images]);
41191
41307
  const iconsCss = useMemo52(() => collectInlineFontAwesomeCss(), []);
41192
41308
  const [preparation, setPreparation] = useState76(null);
41193
- useEffect65(() => {
41309
+ useEffect66(() => {
41194
41310
  let cancelled = false;
41195
41311
  loadPreparation().then(
41196
41312
  (loaded) => {
@@ -41217,7 +41333,7 @@ function PlainHtmlPreview({
41217
41333
  );
41218
41334
  const [prepared, setPrepared] = useState76(null);
41219
41335
  const preparedOnceRef = useRef59(false);
41220
- useEffect65(() => {
41336
+ useEffect66(() => {
41221
41337
  if (!preparation || !needsPreparation) return;
41222
41338
  let cancelled = false;
41223
41339
  const prepare = () => {
@@ -41245,7 +41361,7 @@ function PlainHtmlPreview({
41245
41361
  }, [preparation, needsPreparation, mdDoc, container, dataTableCache]);
41246
41362
  const view = needsPreparation ? prepared : null;
41247
41363
  const [renderFn, setRenderFn] = useState76(() => cachedRender);
41248
- useEffect65(() => {
41364
+ useEffect66(() => {
41249
41365
  if (renderFn) return;
41250
41366
  let cancelled = false;
41251
41367
  loadRenderFn().then((fn) => {
@@ -41286,7 +41402,7 @@ function PlainHtmlPreview({
41286
41402
  frameDocument.addEventListener("click", handleClick, true);
41287
41403
  removeFrameLinkHandlerRef.current = () => frameDocument.removeEventListener("click", handleClick, true);
41288
41404
  }, [onLinkClick]);
41289
- useEffect65(() => {
41405
+ useEffect66(() => {
41290
41406
  installFrameLinkHandler();
41291
41407
  return () => removeFrameLinkHandlerRef.current();
41292
41408
  }, [html, installFrameLinkHandler]);
@@ -41382,11 +41498,11 @@ function PlainHtmlPreview({
41382
41498
  style2.remove();
41383
41499
  };
41384
41500
  }, [onCopyCode, showCodeCopyButton]);
41385
- useEffect65(() => {
41501
+ useEffect66(() => {
41386
41502
  installFrameCodeCopyControls();
41387
41503
  return () => removeFrameCodeCopyControlsRef.current();
41388
41504
  }, [html, installFrameCodeCopyControls]);
41389
- useEffect65(() => {
41505
+ useEffect66(() => {
41390
41506
  if (!globalKeyboardShortcuts) return;
41391
41507
  const handleKeyDown = (event) => {
41392
41508
  if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.key !== "ArrowDown" && event.key !== "ArrowUp") {
@@ -41471,7 +41587,7 @@ function collectImageRefs(doc) {
41471
41587
  }
41472
41588
 
41473
41589
  // src/PreviewPanel.tsx
41474
- import { useState as useState80, useEffect as useEffect70, useMemo as useMemo57, useCallback as useCallback54, useRef as useRef63 } from "react";
41590
+ import { useState as useState80, useEffect as useEffect71, useMemo as useMemo57, useCallback as useCallback54, useRef as useRef63 } from "react";
41475
41591
  import { createPortal as createPortal18 } from "react-dom";
41476
41592
  import {
41477
41593
  DashboardView,
@@ -41616,7 +41732,7 @@ import {
41616
41732
  createContext as createContext7,
41617
41733
  useCallback as useCallback51,
41618
41734
  useContext as useContext7,
41619
- useEffect as useEffect66,
41735
+ useEffect as useEffect67,
41620
41736
  useId as useId15,
41621
41737
  useLayoutEffect as useLayoutEffect15,
41622
41738
  useMemo as useMemo53,
@@ -41883,12 +41999,12 @@ function PresentationModeProvider({
41883
41999
  },
41884
42000
  [availableTargets, selectedTarget, stop]
41885
42001
  );
41886
- useEffect66(() => {
42002
+ useEffect67(() => {
41887
42003
  if (availableTargets.includes(selectedTarget)) return;
41888
42004
  setSelectedTarget("control");
41889
42005
  if (activeTargetRef.current !== null) void stop();
41890
42006
  }, [availableTargets, selectedTarget, stop]);
41891
- useEffect66(() => {
42007
+ useEffect67(() => {
41892
42008
  const root = rootRef.current;
41893
42009
  const ownerDocument = root?.ownerDocument;
41894
42010
  if (!root || !ownerDocument) return;
@@ -41900,7 +42016,7 @@ function PresentationModeProvider({
41900
42016
  ownerDocument.addEventListener("fullscreenchange", handleFullscreenChange);
41901
42017
  return () => ownerDocument.removeEventListener("fullscreenchange", handleFullscreenChange);
41902
42018
  }, [rootRef]);
41903
- useEffect66(() => {
42019
+ useEffect67(() => {
41904
42020
  const root = rootRef.current;
41905
42021
  if (!root) return;
41906
42022
  if (activeTarget) root.dataset.presentationMode = activeTarget;
@@ -41909,10 +42025,10 @@ function PresentationModeProvider({
41909
42025
  delete root.dataset.presentationMode;
41910
42026
  };
41911
42027
  }, [activeTarget, rootRef]);
41912
- useEffect66(() => {
42028
+ useEffect67(() => {
41913
42029
  if (activeView !== "preview" && activeTargetRef.current !== null) void stop();
41914
42030
  }, [activeView, stop]);
41915
- useEffect66(() => {
42031
+ useEffect67(() => {
41916
42032
  const previous = previousActiveTargetRef.current;
41917
42033
  previousActiveTargetRef.current = activeTarget;
41918
42034
  if (previous === null || activeTarget !== null) return;
@@ -41920,7 +42036,7 @@ function PresentationModeProvider({
41920
42036
  returnFocusRef.current = null;
41921
42037
  if (target?.isConnected) target.focus();
41922
42038
  }, [activeTarget]);
41923
- useEffect66(() => {
42039
+ useEffect67(() => {
41924
42040
  if (activeTarget !== "control") return;
41925
42041
  const ownerDocument = rootRef.current?.ownerDocument;
41926
42042
  if (!ownerDocument) return;
@@ -41932,7 +42048,7 @@ function PresentationModeProvider({
41932
42048
  ownerDocument.addEventListener("keydown", handleKeyDown);
41933
42049
  return () => ownerDocument.removeEventListener("keydown", handleKeyDown);
41934
42050
  }, [activeTarget, rootRef, stop]);
41935
- useEffect66(() => {
42051
+ useEffect67(() => {
41936
42052
  if (!error) return;
41937
42053
  const timer = window.setTimeout(() => setError(null), 6e3);
41938
42054
  return () => window.clearTimeout(timer);
@@ -41967,7 +42083,7 @@ function PresentationModeProvider({
41967
42083
  window.removeEventListener("resize", updatePosition);
41968
42084
  };
41969
42085
  }, [activeTarget, rootRef]);
41970
- useEffect66(
42086
+ useEffect67(
41971
42087
  () => () => {
41972
42088
  popupCleanupRef.current?.();
41973
42089
  const popup = popupRef.current;
@@ -42096,7 +42212,7 @@ function PresentationModeControl() {
42096
42212
  updatePosition();
42097
42213
  setOpen(true);
42098
42214
  }, [updatePosition]);
42099
- useEffect66(() => {
42215
+ useEffect67(() => {
42100
42216
  if (!open) return;
42101
42217
  const handlePointerDown = (event) => {
42102
42218
  const target = event.target;
@@ -42241,7 +42357,7 @@ import {
42241
42357
  createContext as createContext8,
42242
42358
  useCallback as useCallback52,
42243
42359
  useContext as useContext8,
42244
- useEffect as useEffect67,
42360
+ useEffect as useEffect68,
42245
42361
  useMemo as useMemo54,
42246
42362
  useRef as useRef61,
42247
42363
  useState as useState78
@@ -42311,7 +42427,7 @@ function PrintModeProvider({ rootRef, children }) {
42311
42427
  markPrinting();
42312
42428
  ownerWindow.print();
42313
42429
  }, [markPrinting, rootRef]);
42314
- useEffect67(() => {
42430
+ useEffect68(() => {
42315
42431
  const root = rootRef.current;
42316
42432
  if (!root) return;
42317
42433
  if (active) root.dataset.printPreview = "true";
@@ -42320,10 +42436,10 @@ function PrintModeProvider({ rootRef, children }) {
42320
42436
  delete root.dataset.printPreview;
42321
42437
  };
42322
42438
  }, [active, rootRef]);
42323
- useEffect67(() => {
42439
+ useEffect68(() => {
42324
42440
  if (activeView !== "preview" && active) close();
42325
42441
  }, [active, activeView, close]);
42326
- useEffect67(() => {
42442
+ useEffect68(() => {
42327
42443
  if (!active) return;
42328
42444
  const ownerWindow = rootRef.current?.ownerDocument.defaultView;
42329
42445
  const ownerDocument = rootRef.current?.ownerDocument;
@@ -42414,7 +42530,7 @@ function PrintPreviewToolbar() {
42414
42530
  }
42415
42531
 
42416
42532
  // src/print/PrintPreview.tsx
42417
- import { useCallback as useCallback53, useEffect as useEffect68, useMemo as useMemo55, useRef as useRef62 } from "react";
42533
+ import { useCallback as useCallback53, useEffect as useEffect69, useMemo as useMemo55, useRef as useRef62 } from "react";
42418
42534
  import {
42419
42535
  BlockRenderer as BlockRenderer5,
42420
42536
  FlashcardFaceView,
@@ -42471,7 +42587,7 @@ function PrintPreview({
42471
42587
  [contentDoc]
42472
42588
  );
42473
42589
  const isTextDocument = displayMode === "page" || displayMode === "narrate";
42474
- useEffect68(() => {
42590
+ useEffect69(() => {
42475
42591
  if (!isTextDocument) return registerPrintHandler(null);
42476
42592
  return registerPrintHandler(() => {
42477
42593
  const frameWindow = documentFrameRef.current?.contentWindow;
@@ -42610,7 +42726,7 @@ function PrintPreview({
42610
42726
  import {
42611
42727
  createContext as createContext9,
42612
42728
  useContext as useContext9,
42613
- useEffect as useEffect69,
42729
+ useEffect as useEffect70,
42614
42730
  useMemo as useMemo56,
42615
42731
  useState as useState79
42616
42732
  } from "react";
@@ -42654,7 +42770,7 @@ function collectSlideshowDataSources(doc) {
42654
42770
  }
42655
42771
 
42656
42772
  // src/slideshow/SlideshowDataGrid.tsx
42657
- import { Fragment as Fragment28, jsx as jsx88 } from "react/jsx-runtime";
42773
+ import { Fragment as Fragment29, jsx as jsx88 } from "react/jsx-runtime";
42658
42774
  var EMPTY_CONTEXT = {
42659
42775
  container: null,
42660
42776
  mediaRevision: 0,
@@ -42717,7 +42833,7 @@ function SlideshowDataGridRenderer({
42717
42833
  const activeSourceKey = sourceKey(source);
42718
42834
  const [remote, setRemote] = useState79({ key: "", status: "idle" });
42719
42835
  const [gridModule, setGridModule] = useState79(null);
42720
- useEffect69(() => {
42836
+ useEffect70(() => {
42721
42837
  let active = true;
42722
42838
  void loadGridModule().then((module) => {
42723
42839
  if (active) setGridModule(module);
@@ -42726,7 +42842,7 @@ function SlideshowDataGridRenderer({
42726
42842
  active = false;
42727
42843
  };
42728
42844
  }, []);
42729
- useEffect69(() => {
42845
+ useEffect70(() => {
42730
42846
  if (!container || !source || !gridModule) {
42731
42847
  setRemote({ key: activeSourceKey, status: "idle" });
42732
42848
  return;
@@ -42777,14 +42893,14 @@ function SlideshowDataGridRenderer({
42777
42893
  () => useLocalGrid && gridModule ? new gridModule.TableStoreClient({ headers: previewHeaders, cells: previewRows }) : null,
42778
42894
  [previewHeaders, previewRows, useLocalGrid, gridModule]
42779
42895
  );
42780
- useEffect69(() => () => localProvider?.dispose(), [localProvider]);
42896
+ useEffect70(() => () => localProvider?.dispose(), [localProvider]);
42781
42897
  const ready = currentRemote?.status === "ready" ? currentRemote : void 0;
42782
42898
  const provider = ready?.provider ?? localProvider;
42783
42899
  const ingest = ready?.ingest ?? (localProvider ? { headers: previewHeaders, cells: previewRows } : void 0);
42784
42900
  const initialView = ready?.initialView ?? EMPTY_VIEW;
42785
42901
  const [view, setView] = useState79(initialView);
42786
- useEffect69(() => setView(initialView), [initialView, provider]);
42787
- if (!provider || !ingest || !gridModule) return /* @__PURE__ */ jsx88(Fragment28, { children: fallback });
42902
+ useEffect70(() => setView(initialView), [initialView, provider]);
42903
+ if (!provider || !ingest || !gridModule) return /* @__PURE__ */ jsx88(Fragment29, { children: fallback });
42788
42904
  const { style } = layer.content;
42789
42905
  const rowHeight = Math.max(32, style.fontSize * 2.1);
42790
42906
  const footerHeight = Math.max(28, style.fontSize * 1.5);
@@ -42920,11 +43036,11 @@ function PreviewPanel({
42920
43036
  const isNarrateMode = activeDisplayMode === "narrate";
42921
43037
  const isDashboardMode = activeDisplayMode === "dashboard";
42922
43038
  const isFlashcardsMode = activeDisplayMode === "flashcards";
42923
- useEffect70(() => {
43039
+ useEffect71(() => {
42924
43040
  if (presentation?.activeTarget === "window") return;
42925
43041
  setPlaybackState(null);
42926
43042
  }, [presentation?.activeTarget]);
42927
- useEffect70(() => {
43043
+ useEffect71(() => {
42928
43044
  if (presentation?.activeTarget !== "window" || !presentation.popupRoot) return;
42929
43045
  const mainRoot = mainSurfaceRef.current;
42930
43046
  const followerRoot = popupSurfaceRef.current;
@@ -43201,12 +43317,12 @@ function PreviewPanel({
43201
43317
  }
43202
43318
 
43203
43319
  // src/MediaBin.tsx
43204
- import { useState as useState82, useEffect as useEffect72, useRef as useRef65, useCallback as useCallback55 } from "react";
43320
+ import { useState as useState82, useEffect as useEffect73, useRef as useRef65, useCallback as useCallback55 } from "react";
43205
43321
  import { isSvgImageSource as isSvgImageSource3 } from "@bendyline/squisq/markdown";
43206
43322
  import { sanitizedSvgImageUrl as sanitizedSvgImageUrl2 } from "@bendyline/squisq-react";
43207
43323
 
43208
43324
  // src/MediaPreviewDialog.tsx
43209
- import { useEffect as useEffect71, useId as useId16, useRef as useRef64, useState as useState81 } from "react";
43325
+ import { useEffect as useEffect72, useId as useId16, useRef as useRef64, useState as useState81 } from "react";
43210
43326
  import { jsx as jsx90, jsxs as jsxs72 } from "react/jsx-runtime";
43211
43327
  function MediaPreviewDialog({
43212
43328
  kind,
@@ -43222,7 +43338,7 @@ function MediaPreviewDialog({
43222
43338
  const titleId = useId16();
43223
43339
  const [playbackFailed, setPlaybackFailed] = useState81(false);
43224
43340
  useModalDialog({ rootRef: overlayRef, dialogRef, onClose });
43225
- useEffect71(() => setPlaybackFailed(false), [src]);
43341
+ useEffect72(() => setPlaybackFailed(false), [src]);
43226
43342
  let body;
43227
43343
  if (failed || playbackFailed || src === "") {
43228
43344
  body = /* @__PURE__ */ jsx90("div", { className: "squisq-media-preview-message", children: "This file can\u2019t be previewed." });
@@ -43292,7 +43408,7 @@ function MediaPreviewDialog({
43292
43408
  }
43293
43409
 
43294
43410
  // src/MediaBin.tsx
43295
- import { Fragment as Fragment29, jsx as jsx91, jsxs as jsxs73 } from "react/jsx-runtime";
43411
+ import { Fragment as Fragment30, jsx as jsx91, jsxs as jsxs73 } from "react/jsx-runtime";
43296
43412
  function formatSize(bytes) {
43297
43413
  if (bytes < 1024) return `${bytes} B`;
43298
43414
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -43366,7 +43482,7 @@ function MediaBin({
43366
43482
  }) {
43367
43483
  const [entries, setEntries] = useState82([]);
43368
43484
  const listRef = useRef65(null);
43369
- useEffect72(() => {
43485
+ useEffect73(() => {
43370
43486
  if (!highlightPath || !listRef.current) return;
43371
43487
  const item = listRef.current.querySelector(`[data-media-path="${CSS.escape(highlightPath)}"]`);
43372
43488
  if (item instanceof HTMLElement) {
@@ -43400,7 +43516,7 @@ function MediaBin({
43400
43516
  },
43401
43517
  [onCountChange]
43402
43518
  );
43403
- useEffect72(() => {
43519
+ useEffect73(() => {
43404
43520
  if (!contextMenu) return;
43405
43521
  const handlePointerDown = (event) => {
43406
43522
  if (contextMenuRef.current?.contains(event.target)) return;
@@ -43421,7 +43537,7 @@ function MediaBin({
43421
43537
  window.removeEventListener("resize", close);
43422
43538
  };
43423
43539
  }, [contextMenu]);
43424
- useEffect72(() => {
43540
+ useEffect73(() => {
43425
43541
  if (!mediaProvider) {
43426
43542
  setEntries([]);
43427
43543
  setThumbUrls({});
@@ -43578,7 +43694,7 @@ function MediaBin({
43578
43694
  const openContextMenu = useCallback55((entry, x, y) => {
43579
43695
  setContextMenu({ entry, x, y });
43580
43696
  }, []);
43581
- useEffect72(() => {
43697
+ useEffect73(() => {
43582
43698
  setPreview(null);
43583
43699
  }, [mediaProvider]);
43584
43700
  const openPreview = useCallback55(
@@ -43623,7 +43739,7 @@ function MediaBin({
43623
43739
  [openContextMenu, openPreview]
43624
43740
  );
43625
43741
  const previewKind = preview ? squisqMediaKind(preview.entry.mimeType) : "file";
43626
- return /* @__PURE__ */ jsxs73(Fragment29, { children: [
43742
+ return /* @__PURE__ */ jsxs73(Fragment30, { children: [
43627
43743
  /* @__PURE__ */ jsxs73(
43628
43744
  "div",
43629
43745
  {
@@ -43818,7 +43934,7 @@ ${formatSize(entry.size)}`,
43818
43934
 
43819
43935
  // src/DropZoneOverlay.tsx
43820
43936
  import { useState as useState83 } from "react";
43821
- import { Fragment as Fragment30, jsx as jsx92, jsxs as jsxs74 } from "react/jsx-runtime";
43937
+ import { Fragment as Fragment31, jsx as jsx92, jsxs as jsxs74 } from "react/jsx-runtime";
43822
43938
  function DropZoneOverlay({
43823
43939
  dragContentType,
43824
43940
  zoneProps,
@@ -43839,7 +43955,7 @@ function DropZoneOverlay({
43839
43955
  variant: "media"
43840
43956
  }
43841
43957
  ),
43842
- showText && /* @__PURE__ */ jsxs74(Fragment30, { children: [
43958
+ showText && /* @__PURE__ */ jsxs74(Fragment31, { children: [
43843
43959
  /* @__PURE__ */ jsx92(
43844
43960
  DropZone,
43845
43961
  {
@@ -43918,7 +44034,7 @@ function DropZone({
43918
44034
  }
43919
44035
 
43920
44036
  // src/Tooltip.tsx
43921
- import { useEffect as useEffect73, useLayoutEffect as useLayoutEffect16, useRef as useRef66, useState as useState84 } from "react";
44037
+ import { useEffect as useEffect74, useLayoutEffect as useLayoutEffect16, useRef as useRef66, useState as useState84 } from "react";
43922
44038
  import { createPortal as createPortal19 } from "react-dom";
43923
44039
 
43924
44040
  // src/tooltipPlacement.ts
@@ -43952,7 +44068,7 @@ function TooltipLayer() {
43952
44068
  node2.style.left = `${left}px`;
43953
44069
  node2.style.visibility = "visible";
43954
44070
  }, [state]);
43955
- useEffect73(() => {
44071
+ useEffect74(() => {
43956
44072
  const clearTimer = () => {
43957
44073
  if (timerRef.current) {
43958
44074
  clearTimeout(timerRef.current);
@@ -44054,7 +44170,7 @@ function TooltipLayer() {
44054
44170
  }
44055
44171
 
44056
44172
  // src/EditorShell.tsx
44057
- import { useEffect as useEffect80, useRef as useRef75, useState as useState91, useCallback as useCallback63, useMemo as useMemo62 } from "react";
44173
+ import { useEffect as useEffect81, useRef as useRef75, useState as useState91, useCallback as useCallback63, useMemo as useMemo62 } from "react";
44058
44174
 
44059
44175
  // src/BlockPreviewPanel.tsx
44060
44176
  import { useMemo as useMemo58 } from "react";
@@ -44098,14 +44214,14 @@ function BlockPreviewPanel({ basePath = "/" }) {
44098
44214
  }
44099
44215
 
44100
44216
  // src/NavigationSheet.tsx
44101
- import { useCallback as useCallback57, useEffect as useEffect75, useId as useId17, useRef as useRef69, useState as useState85 } from "react";
44217
+ import { useCallback as useCallback57, useEffect as useEffect76, useId as useId17, useRef as useRef69, useState as useState85 } from "react";
44102
44218
 
44103
44219
  // src/BlockPreviewList.tsx
44104
44220
  import { useCallback as useCallback56, useRef as useRef67 } from "react";
44105
44221
  import { VIEWPORT_PRESETS as VIEWPORT_PRESETS9 } from "@bendyline/squisq/schemas";
44106
44222
  import { flattenBlocks as flattenBlocks7 } from "@bendyline/squisq/doc";
44107
44223
  import { BlockRenderer as BlockRenderer6, MediaContext as MediaContext7 } from "@bendyline/squisq-react";
44108
- import { Fragment as Fragment31, jsx as jsx95, jsxs as jsxs75 } from "react/jsx-runtime";
44224
+ import { Fragment as Fragment32, jsx as jsx95, jsxs as jsxs75 } from "react/jsx-runtime";
44109
44225
  function BlockPreviewList({
44110
44226
  basePath = "/",
44111
44227
  viewport = VIEWPORT_PRESETS9.landscape,
@@ -44153,7 +44269,7 @@ function BlockPreviewList({
44153
44269
  children: [
44154
44270
  /* @__PURE__ */ jsxs75("span", { className: "squisq-inline-preview-card-label", children: [
44155
44271
  /* @__PURE__ */ jsx95("span", { className: "squisq-inline-preview-card-template", children: templateLabel(item.template) }),
44156
- item.headingText && /* @__PURE__ */ jsxs75(Fragment31, { children: [
44272
+ item.headingText && /* @__PURE__ */ jsxs75(Fragment32, { children: [
44157
44273
  /* @__PURE__ */ jsx95("span", { className: "squisq-inline-preview-card-sep", "aria-hidden": "true", children: "\u2014" }),
44158
44274
  /* @__PURE__ */ jsx95("span", { className: "squisq-inline-preview-card-title", children: item.headingText })
44159
44275
  ] })
@@ -44164,7 +44280,7 @@ function BlockPreviewList({
44164
44280
  className: "squisq-inline-preview-card-svg",
44165
44281
  "aria-hidden": "true",
44166
44282
  style: { aspectRatio: `${viewport.width} / ${viewport.height}` },
44167
- children: /* @__PURE__ */ jsx95(
44283
+ children: item.block && /* @__PURE__ */ jsx95(
44168
44284
  BlockRenderer6,
44169
44285
  {
44170
44286
  block: item.block,
@@ -44182,7 +44298,7 @@ function BlockPreviewList({
44182
44298
  }
44183
44299
 
44184
44300
  // src/useNavigationSheetSwipe.ts
44185
- import { useEffect as useEffect74, useRef as useRef68 } from "react";
44301
+ import { useEffect as useEffect75, useRef as useRef68 } from "react";
44186
44302
  var NAVIGATION_SHEET_MAX_BODY_WIDTH = 720;
44187
44303
  var EDGE_ZONE_PX = 24;
44188
44304
  var AXIS_LOCK_PX = 10;
@@ -44199,7 +44315,7 @@ function useNavigationSheetSwipe({
44199
44315
  }) {
44200
44316
  const callbacksRef = useRef68({ onDragStart, onOpen, onClose });
44201
44317
  callbacksRef.current = { onDragStart, onOpen, onClose };
44202
- useEffect74(() => {
44318
+ useEffect75(() => {
44203
44319
  const sheet = sheetRef.current;
44204
44320
  const surface = sheet?.parentElement;
44205
44321
  if (!sheet || !surface) return;
@@ -44305,7 +44421,7 @@ function useNavigationSheetSwipe({
44305
44421
  }
44306
44422
 
44307
44423
  // src/NavigationSheet.tsx
44308
- import { Fragment as Fragment32, jsx as jsx96, jsxs as jsxs76 } from "react/jsx-runtime";
44424
+ import { Fragment as Fragment33, jsx as jsx96, jsxs as jsxs76 } from "react/jsx-runtime";
44309
44425
  var PANEL_LABELS = {
44310
44426
  outline: "Outline",
44311
44427
  previews: "Block previews"
@@ -44333,7 +44449,7 @@ function NavigationSheet({
44333
44449
  const [lastShown, setLastShown] = useState85(shown);
44334
44450
  if (shown && shown !== lastShown) setLastShown(shown);
44335
44451
  const content = shown ?? lastShown;
44336
- useEffect75(() => {
44452
+ useEffect76(() => {
44337
44453
  if (shown || !lastShown) return;
44338
44454
  const timer = setTimeout(() => setLastShown(null), CLOSE_LINGER_MS);
44339
44455
  return () => clearTimeout(timer);
@@ -44358,14 +44474,14 @@ function NavigationSheet({
44358
44474
  onClose: close
44359
44475
  });
44360
44476
  useEscapeDismissal(open, close);
44361
- useEffect75(() => {
44477
+ useEffect76(() => {
44362
44478
  const scrim = scrimRef.current;
44363
44479
  if (!scrim) return;
44364
44480
  const stop = (event) => event.stopPropagation();
44365
44481
  scrim.addEventListener("pointerdown", stop);
44366
44482
  return () => scrim.removeEventListener("pointerdown", stop);
44367
44483
  }, []);
44368
- useEffect75(() => {
44484
+ useEffect76(() => {
44369
44485
  const sheet = sheetRef.current;
44370
44486
  const surface = sheet?.parentElement;
44371
44487
  if (!open || !sheet || !surface) return;
@@ -44395,7 +44511,7 @@ function NavigationSheet({
44395
44511
  role: "dialog",
44396
44512
  "aria-label": content ? PANEL_LABELS[content] : "Document navigation",
44397
44513
  tabIndex: -1,
44398
- children: content && /* @__PURE__ */ jsxs76(Fragment32, { children: [
44514
+ children: content && /* @__PURE__ */ jsxs76(Fragment33, { children: [
44399
44515
  /* @__PURE__ */ jsxs76("div", { className: "squisq-navigation-sheet-header", children: [
44400
44516
  /* @__PURE__ */ jsx96("div", { className: "squisq-navigation-sheet-tabs", role: "tablist", children: PANELS.map((option) => /* @__PURE__ */ jsx96(
44401
44517
  "button",
@@ -44534,7 +44650,7 @@ function ProofingPanel() {
44534
44650
  }
44535
44651
 
44536
44652
  // src/imageEditor/ImageEditModal.tsx
44537
- import { useCallback as useCallback58, useEffect as useEffect76, useMemo as useMemo59, useRef as useRef70, useState as useState86 } from "react";
44653
+ import { useCallback as useCallback58, useEffect as useEffect77, useMemo as useMemo59, useRef as useRef70, useState as useState86 } from "react";
44538
44654
  import { DARK_SURFACE, LIGHT_SURFACE } from "@bendyline/squisq/schemas";
44539
44655
  import {
44540
44656
  MemoryContentContainer,
@@ -44569,7 +44685,7 @@ function ImageEditModal({
44569
44685
  }, [container, relativePath]);
44570
44686
  const [initialSrc, setInitialSrc] = useState86(null);
44571
44687
  const [resolveError, setResolveError] = useState86(null);
44572
- useEffect76(() => {
44688
+ useEffect77(() => {
44573
44689
  let cancelled = false;
44574
44690
  setInitialSrc(null);
44575
44691
  setResolveError(null);
@@ -44691,7 +44807,7 @@ function MediaEditContextMenuItems() {
44691
44807
  // src/mediaEdit/MediaEditModal.tsx
44692
44808
  import {
44693
44809
  useCallback as useCallback62,
44694
- useEffect as useEffect79,
44810
+ useEffect as useEffect80,
44695
44811
  useId as useId18,
44696
44812
  useMemo as useMemo61,
44697
44813
  useRef as useRef74,
@@ -44702,7 +44818,7 @@ import {
44702
44818
  } from "@bendyline/squisq-video-react/media-edit";
44703
44819
 
44704
44820
  // src/mediaEdit/MediaEditAudioSection.tsx
44705
- import { useCallback as useCallback59, useEffect as useEffect77, useRef as useRef71, useState as useState87 } from "react";
44821
+ import { useCallback as useCallback59, useEffect as useEffect78, useRef as useRef71, useState as useState87 } from "react";
44706
44822
  import { MEDIA_FX_SPECS as MEDIA_FX_SPECS2, serializeMediaFx } from "@bendyline/squisq/mediaEdit";
44707
44823
 
44708
44824
  // src/mediaEdit/mediaEditDraft.ts
@@ -44801,7 +44917,7 @@ function centeredCrop(ratio, frameWidth, frameHeight) {
44801
44917
  }
44802
44918
 
44803
44919
  // src/mediaEdit/MediaEditAudioSection.tsx
44804
- import { Fragment as Fragment33, jsx as jsx99, jsxs as jsxs79 } from "react/jsx-runtime";
44920
+ import { Fragment as Fragment34, jsx as jsx99, jsxs as jsxs79 } from "react/jsx-runtime";
44805
44921
  var STAGES = [
44806
44922
  {
44807
44923
  id: "highpass",
@@ -44856,7 +44972,7 @@ var LOUDNESS_PRESETS = [
44856
44972
  var PREVIEW_SECONDS = 10;
44857
44973
  function useMediaDuration(url) {
44858
44974
  const [duration, setDuration] = useState87(null);
44859
- useEffect77(() => {
44975
+ useEffect78(() => {
44860
44976
  setDuration(null);
44861
44977
  if (!url || typeof Audio === "undefined") return;
44862
44978
  const probe = new Audio();
@@ -44896,8 +45012,8 @@ function usePreviewPlayer(preview) {
44896
45012
  setPosition(0);
44897
45013
  setPlaying(null);
44898
45014
  }, []);
44899
- useEffect77(() => stop, [preview, stop]);
44900
- useEffect77(
45015
+ useEffect78(() => stop, [preview, stop]);
45016
+ useEffect78(
44901
45017
  () => () => {
44902
45018
  void ctxRef.current?.close();
44903
45019
  ctxRef.current = null;
@@ -44936,7 +45052,7 @@ function usePreviewPlayer(preview) {
44936
45052
  },
44937
45053
  [duration, playing, play]
44938
45054
  );
44939
- useEffect77(() => {
45055
+ useEffect78(() => {
44940
45056
  if (!playing) return;
44941
45057
  let frame = requestAnimationFrame(function tick() {
44942
45058
  setPosition(currentOffset());
@@ -44991,7 +45107,7 @@ function MediaEditAudioSection({
44991
45107
  const [previewError, setPreviewError] = useState87(null);
44992
45108
  const previewAbort = useRef71(null);
44993
45109
  const player = usePreviewPlayer(preview);
44994
- useEffect77(() => () => previewAbort.current?.abort(), []);
45110
+ useEffect78(() => () => previewAbort.current?.abort(), []);
44995
45111
  const runPreview = useCallback59(async () => {
44996
45112
  if (draftFx.ops.length === 0) return;
44997
45113
  previewAbort.current?.abort();
@@ -45108,7 +45224,7 @@ function MediaEditAudioSection({
45108
45224
  children: previewing ? "Rendering preview\u2026" : preview ? "Update preview" : "Create preview"
45109
45225
  }
45110
45226
  ) }),
45111
- preview != null && player.duration > 0 && /* @__PURE__ */ jsxs79(Fragment33, { children: [
45227
+ preview != null && player.duration > 0 && /* @__PURE__ */ jsxs79(Fragment34, { children: [
45112
45228
  /* @__PURE__ */ jsxs79("div", { className: "squisq-media-edit-playback", "data-playing": player.playing ?? void 0, children: [
45113
45229
  /* @__PURE__ */ jsx99("span", { className: "squisq-media-edit-playback__label", children: player.playing ? `${player.playing === "original" ? "Original" : "Processed"} from ${Math.round(preview.startSec)} s` : "Stopped" }),
45114
45230
  /* @__PURE__ */ jsx99(
@@ -45171,7 +45287,7 @@ function MediaEditAudioSection({
45171
45287
  }
45172
45288
 
45173
45289
  // src/mediaEdit/MediaEditTimingSection.tsx
45174
- import { useCallback as useCallback60, useEffect as useEffect78, useRef as useRef72, useState as useState88 } from "react";
45290
+ import { useCallback as useCallback60, useEffect as useEffect79, useRef as useRef72, useState as useState88 } from "react";
45175
45291
  import { jsx as jsx100, jsxs as jsxs80 } from "react/jsx-runtime";
45176
45292
  var DEFAULT_MAX_PAUSE = 0.8;
45177
45293
  function formatDb(value) {
@@ -45189,7 +45305,7 @@ function MediaEditTimingSection({
45189
45305
  const [finding, setFinding] = useState88(false);
45190
45306
  const [error, setError] = useState88(null);
45191
45307
  const abortRef = useRef72(null);
45192
- useEffect78(() => () => abortRef.current?.abort(), []);
45308
+ useEffect79(() => () => abortRef.current?.abort(), []);
45193
45309
  const findPauses = useCallback60(async () => {
45194
45310
  abortRef.current?.abort();
45195
45311
  const controller = new AbortController();
@@ -45454,7 +45570,7 @@ function MediaEditFrameSection({ item, draft, onChange }) {
45454
45570
  }
45455
45571
 
45456
45572
  // src/mediaEdit/MediaEditModal.tsx
45457
- import { Fragment as Fragment34, jsx as jsx102, jsxs as jsxs82 } from "react/jsx-runtime";
45573
+ import { Fragment as Fragment35, jsx as jsx102, jsxs as jsxs82 } from "react/jsx-runtime";
45458
45574
  var TABS = [
45459
45575
  { id: "audio", label: "Audio cleanup" },
45460
45576
  { id: "timing", label: "Timing" },
@@ -45489,16 +45605,16 @@ function RenderStatusText({
45489
45605
  }) {
45490
45606
  switch (status.state) {
45491
45607
  case "ready":
45492
- return /* @__PURE__ */ jsx102(Fragment34, { children: "Processed audio is in use." });
45608
+ return /* @__PURE__ */ jsx102(Fragment35, { children: "Processed audio is in use." });
45493
45609
  case "queued":
45494
- return /* @__PURE__ */ jsx102(Fragment34, { children: "Waiting to process\u2026" });
45610
+ return /* @__PURE__ */ jsx102(Fragment35, { children: "Waiting to process\u2026" });
45495
45611
  case "rendering":
45496
- return /* @__PURE__ */ jsxs82(Fragment34, { children: [
45612
+ return /* @__PURE__ */ jsxs82(Fragment35, { children: [
45497
45613
  "Processing\u2026 ",
45498
45614
  percent(status)
45499
45615
  ] });
45500
45616
  case "failed":
45501
- return /* @__PURE__ */ jsxs82(Fragment34, { children: [
45617
+ return /* @__PURE__ */ jsxs82(Fragment35, { children: [
45502
45618
  "Processing failed: ",
45503
45619
  status.error,
45504
45620
  " ",
@@ -45518,7 +45634,7 @@ function MediaEditModal({ target, onClose }) {
45518
45634
  const [draft, setDraft] = useState90(() => draftFromEdits(item?.edits));
45519
45635
  const seedKey = item ? `${item.sourceLine}|${editsSignature(item.edits)}` : null;
45520
45636
  const seededRef = useRef74(null);
45521
- useEffect79(() => {
45637
+ useEffect80(() => {
45522
45638
  if (seedKey === null || seededRef.current === seedKey) return;
45523
45639
  seededRef.current = seedKey;
45524
45640
  setDraft(draftFromEdits(item?.edits));
@@ -45586,7 +45702,7 @@ function MediaEditModal({ target, onClose }) {
45586
45702
  }
45587
45703
  )
45588
45704
  ] }),
45589
- !item ? /* @__PURE__ */ jsx102("p", { className: "squisq-media-edit-modal__empty", children: "This clip is no longer in the document." }) : !mediaEditRenders ? /* @__PURE__ */ jsx102("p", { className: "squisq-media-edit-modal__empty", children: "Media editing needs a media folder for this document." }) : /* @__PURE__ */ jsxs82(Fragment34, { children: [
45705
+ !item ? /* @__PURE__ */ jsx102("p", { className: "squisq-media-edit-modal__empty", children: "This clip is no longer in the document." }) : !mediaEditRenders ? /* @__PURE__ */ jsx102("p", { className: "squisq-media-edit-modal__empty", children: "Media editing needs a media folder for this document." }) : /* @__PURE__ */ jsxs82(Fragment35, { children: [
45590
45706
  /* @__PURE__ */ jsx102("div", { className: "squisq-media-edit-tabs", role: "tablist", "aria-label": "Edit sections", children: tabs.map((tab, index2) => {
45591
45707
  const selected = tab.id === activeTab;
45592
45708
  const edited = tabHasEdits(tab.id, draft);
@@ -45948,7 +46064,7 @@ import {
45948
46064
  createMediaEditRenderManager,
45949
46065
  useProcessedAudio as useProcessedAudio4
45950
46066
  } from "@bendyline/squisq-video-react/media-edit";
45951
- import { Fragment as Fragment35, jsx as jsx103, jsxs as jsxs83 } from "react/jsx-runtime";
46067
+ import { Fragment as Fragment36, jsx as jsx103, jsxs as jsxs83 } from "react/jsx-runtime";
45952
46068
  function EditorShell({
45953
46069
  ariaLabel,
45954
46070
  initialMarkdown = "",
@@ -46054,7 +46170,7 @@ function EditorShell({
46054
46170
  () => mediaEditRenders === void 0 && effectiveMediaProvider ? createMediaEditRenderManager({ mediaProvider: effectiveMediaProvider }) : null,
46055
46171
  [mediaEditRenders, effectiveMediaProvider]
46056
46172
  );
46057
- useEffect80(() => () => ownedMediaEditRenders?.dispose(), [ownedMediaEditRenders]);
46173
+ useEffect81(() => () => ownedMediaEditRenders?.dispose(), [ownedMediaEditRenders]);
46058
46174
  const effectiveMediaEditRenders = mediaEditRenders === void 0 ? ownedMediaEditRenders : mediaEditRenders;
46059
46175
  const filesToggleEnabled = showFilesToggle ?? effectiveMediaProvider !== void 0;
46060
46176
  const effectiveInitialView = hostMode === "chat" || !showPlayTab && initialView === "preview" ? "wysiwyg" : initialView;
@@ -46171,7 +46287,7 @@ function UseModeToolbarControls({
46171
46287
  }) {
46172
46288
  const printMode = usePrintMode();
46173
46289
  if (printMode.active) return /* @__PURE__ */ jsx103(PrintPreviewToolbar, {});
46174
- return /* @__PURE__ */ jsxs83(Fragment35, { children: [
46290
+ return /* @__PURE__ */ jsxs83(Fragment36, { children: [
46175
46291
  /* @__PURE__ */ jsx103(PreviewToolbarControls, { summarizationDesigner }),
46176
46292
  /* @__PURE__ */ jsx103(PresentationModeControl, {}),
46177
46293
  allowPrint && /* @__PURE__ */ jsx103(PrintModeControl, {})
@@ -46289,7 +46405,7 @@ function EditorShellInner({
46289
46405
  const processedAudio = useProcessedAudio4(mediaEditRenders);
46290
46406
  const { docThemes, onDocThemesChange } = useDocCustomThemes();
46291
46407
  const lastEditingView = useRef75(activeView === "raw" ? "raw" : "wysiwyg");
46292
- useEffect80(() => {
46408
+ useEffect81(() => {
46293
46409
  if (activeView !== "preview") lastEditingView.current = activeView;
46294
46410
  }, [activeView]);
46295
46411
  const editingView = activeView === "preview" ? lastEditingView.current : activeView;
@@ -46355,13 +46471,13 @@ function EditorShellInner({
46355
46471
  }
46356
46472
  const imageEditFallbackContainer = imageEditFallbackContainerRef.current;
46357
46473
  const isDark = colorScheme === "dark";
46358
- useEffect80(() => {
46474
+ useEffect81(() => {
46359
46475
  if (!isTimelineMode || !hasTimelineVideo) setTimelineVideoVisible(false);
46360
46476
  }, [isTimelineMode, hasTimelineVideo]);
46361
- useEffect80(() => {
46477
+ useEffect81(() => {
46362
46478
  if (!isTimelineMode || !doc?.blocks.length) setTimelineCompositionVisible(false);
46363
46479
  }, [isTimelineMode, doc]);
46364
- useEffect80(() => {
46480
+ useEffect81(() => {
46365
46481
  if (!mediaProvider) {
46366
46482
  setMediaCount(0);
46367
46483
  return;
@@ -46385,7 +46501,7 @@ function EditorShellInner({
46385
46501
  cancelled = true;
46386
46502
  };
46387
46503
  }, [mediaProvider, mediaListRefreshKey]);
46388
- useEffect80(() => {
46504
+ useEffect81(() => {
46389
46505
  if (showFilesWhenNotEmpty && mediaCount > 0) setShowFiles(true);
46390
46506
  }, [mediaCount, showFilesWhenNotEmpty]);
46391
46507
  const handleToggleFiles = useCallback63(() => {
@@ -46405,7 +46521,7 @@ function EditorShellInner({
46405
46521
  [insertionTarget]
46406
46522
  );
46407
46523
  const docBasenameRef = useRef75("document");
46408
- useEffect80(() => {
46524
+ useEffect81(() => {
46409
46525
  let cancelled = false;
46410
46526
  docBasenameRef.current = "document";
46411
46527
  void resolveDocBasename(workspaceContainer).then((base) => {
@@ -46488,7 +46604,7 @@ function EditorShellInner({
46488
46604
  onDrop: handleFileDrop,
46489
46605
  enabled: !readOnly
46490
46606
  });
46491
- useEffect80(() => {
46607
+ useEffect81(() => {
46492
46608
  onChange?.(markdownSource);
46493
46609
  }, [markdownSource, onChange]);
46494
46610
  const handleShellKeyDown = useCallback63((e2) => {