@bendyline/squisq-editor-react 2.8.0 → 2.9.0

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.
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  markdownToTiptap,
8
8
  tiptapToMarkdown
9
- } from "./chunk-K2WJYVS4.js";
9
+ } from "./chunk-TLRB3IQC.js";
10
10
  import {
11
11
  ImageEditor,
12
12
  ImageViewer,
@@ -3400,6 +3400,9 @@ function resolveDisplayMode(value) {
3400
3400
  if (v2 === "slides" || v2 === "presentation" || v2 === "deck") return "slideshow";
3401
3401
  if (v2 === "teleprompter" || v2 === "prompter") return "narrate";
3402
3402
  if (v2 === "dashboard" || v2 === "dash") return "dashboard";
3403
+ if (v2 === "flashcards" || v2 === "flashcard" || v2 === "study" || v2 === "quiz") {
3404
+ return "flashcards";
3405
+ }
3403
3406
  if (v2 === "page" || v2 === "paged") return "linear";
3404
3407
  if (v2 === "document" || v2 === "scroll" || v2 === "html" || v2 === "plain" || v2 === "reader") {
3405
3408
  return "page";
@@ -4139,6 +4142,12 @@ var DISPLAY_MODE_OPTIONS = [
4139
4142
  icon: "fa-solid fa-images",
4140
4143
  summary: "Present designed slides one at a time."
4141
4144
  },
4145
+ {
4146
+ key: "flashcards",
4147
+ label: "Flashcards",
4148
+ icon: "fa-solid fa-clone",
4149
+ summary: "Study with progressive reveals and quizzes."
4150
+ },
4142
4151
  {
4143
4152
  key: "video",
4144
4153
  label: "Video",
@@ -4190,6 +4199,9 @@ var CONTROL_KEYS = [
4190
4199
  "dashboardImage"
4191
4200
  ];
4192
4201
  function controlKeysForMode(displayMode, hasVideoMedia) {
4202
+ if (displayMode === "flashcards") {
4203
+ return ["theme", "transform"];
4204
+ }
4193
4205
  if (displayMode === "dashboard") {
4194
4206
  return ["format", "layout", "theme", "transform", "dashboardImage"];
4195
4207
  }
@@ -7768,6 +7780,7 @@ import {
7768
7780
  readFrontmatterThemeId as readFrontmatterThemeId2,
7769
7781
  setFrontmatterValues as setFrontmatterValues3
7770
7782
  } from "@bendyline/squisq/markdown";
7783
+ import { markdownToDoc as markdownToDoc2 } from "@bendyline/squisq/doc";
7771
7784
  import { getTransformStyleSummaries as getTransformStyleSummaries3 } from "@bendyline/squisq/transform";
7772
7785
  import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
7773
7786
  function readFm(fm, canonical, legacy) {
@@ -7788,7 +7801,17 @@ function DocumentSettingsDialog({
7788
7801
  const parsed = useMemo14(() => parseMarkdown4(markdownSource), [markdownSource]);
7789
7802
  const frontmatter = parsed.frontmatter;
7790
7803
  const inferredTitle = useMemo14(() => inferDocumentTitle(parsed), [parsed]);
7804
+ const inferredSubtitle = useMemo14(
7805
+ () => markdownToDoc2({
7806
+ ...parsed,
7807
+ // Ignore an explicit subtitle while calculating the value the cover
7808
+ // would otherwise inherit from document content.
7809
+ frontmatter: { ...parsed.frontmatter, subtitle: null }
7810
+ }).startBlock?.subtitle,
7811
+ [parsed]
7812
+ );
7791
7813
  const currentTitle = typeof frontmatter?.title === "string" ? frontmatter.title : "";
7814
+ const currentSubtitle = typeof frontmatter?.subtitle === "string" ? frontmatter.subtitle : "";
7792
7815
  const currentTheme = readFrontmatterThemeId2(frontmatter) ?? "";
7793
7816
  const currentTransform = resolvePersistedTransformStyleId(
7794
7817
  readFm(
@@ -7803,6 +7826,7 @@ function DocumentSettingsDialog({
7803
7826
  FRONTMATTER_SETTING_KEYS.captions.legacy
7804
7827
  );
7805
7828
  const [title, setTitle] = useState15(currentTitle);
7829
+ const [subtitle, setSubtitle] = useState15(currentSubtitle);
7806
7830
  const [theme, setTheme] = useState15(currentTheme);
7807
7831
  const [transform, setTransform] = useState15(currentTransform);
7808
7832
  const [captions, setCaptions] = useState15(currentCaptions);
@@ -7830,8 +7854,12 @@ function DocumentSettingsDialog({
7830
7854
  const trimmedTitle = title.trim();
7831
7855
  const titleMatchesInferred = !!inferredTitle && trimmedTitle === inferredTitle;
7832
7856
  const nextTitle = !trimmedTitle || titleMatchesInferred ? null : trimmedTitle;
7857
+ const trimmedSubtitle = subtitle.trim();
7858
+ const subtitleMatchesInferred = !!inferredSubtitle && trimmedSubtitle === inferredSubtitle.trim();
7859
+ const nextSubtitle = !trimmedSubtitle || subtitleMatchesInferred ? null : trimmedSubtitle;
7833
7860
  const updates = {
7834
7861
  title: nextTitle,
7862
+ subtitle: nextSubtitle,
7835
7863
  [FRONTMATTER_SETTING_KEYS.theme.canonical]: omitFrontmatterDefault(
7836
7864
  theme || FRONTMATTER_SETTING_DEFAULTS.theme,
7837
7865
  FRONTMATTER_SETTING_DEFAULTS.theme
@@ -7853,7 +7881,17 @@ function DocumentSettingsDialog({
7853
7881
  const nextSource = setFrontmatterValues3(markdownSource, updates);
7854
7882
  onSave(nextSource);
7855
7883
  },
7856
- [title, theme, transform, captions, inferredTitle, markdownSource, onSave]
7884
+ [
7885
+ title,
7886
+ subtitle,
7887
+ theme,
7888
+ transform,
7889
+ captions,
7890
+ inferredTitle,
7891
+ inferredSubtitle,
7892
+ markdownSource,
7893
+ onSave
7894
+ ]
7857
7895
  );
7858
7896
  return /* @__PURE__ */ jsx17("div", { ref: overlayRef, className: "squisq-doc-settings-overlay", onMouseDown: handleBackdrop, children: /* @__PURE__ */ jsxs13(
7859
7897
  "form",
@@ -7901,6 +7939,26 @@ function DocumentSettingsDialog({
7901
7939
  " \u2014 leave blank to use it."
7902
7940
  ] }) : /* @__PURE__ */ jsx17("span", { className: "squisq-doc-settings-hint", children: "No title heading found \u2014 set one here or add an H1 to the document." })
7903
7941
  ] }),
7942
+ /* @__PURE__ */ jsxs13("div", { className: "squisq-doc-settings-field", children: [
7943
+ /* @__PURE__ */ jsx17("label", { className: "squisq-doc-settings-label", htmlFor: "squisq-doc-settings-subtitle", children: "Subtitle" }),
7944
+ /* @__PURE__ */ jsx17(
7945
+ "input",
7946
+ {
7947
+ id: "squisq-doc-settings-subtitle",
7948
+ type: "text",
7949
+ className: "squisq-doc-settings-input",
7950
+ value: subtitle,
7951
+ onChange: (e2) => setSubtitle(e2.target.value),
7952
+ placeholder: inferredSubtitle ?? "Cover subtitle",
7953
+ spellCheck: true
7954
+ }
7955
+ ),
7956
+ inferredSubtitle ? /* @__PURE__ */ jsxs13("span", { className: "squisq-doc-settings-hint", children: [
7957
+ "Defaults to ",
7958
+ /* @__PURE__ */ jsx17("strong", { children: inferredSubtitle }),
7959
+ " \u2014 leave blank to use it."
7960
+ ] }) : /* @__PURE__ */ jsx17("span", { className: "squisq-doc-settings-hint", children: "Optional supporting text shown on the document cover." })
7961
+ ] }),
7904
7962
  /* @__PURE__ */ jsxs13("div", { className: "squisq-doc-settings-field", children: [
7905
7963
  /* @__PURE__ */ jsx17("span", { className: "squisq-doc-settings-label", children: "Theme" }),
7906
7964
  /* @__PURE__ */ jsx17(
@@ -19176,18 +19234,25 @@ ${TASK_LIST_MARKDOWN}
19176
19234
  }
19177
19235
 
19178
19236
  // src/StatusBar.tsx
19179
- import { useMemo as useMemo25 } from "react";
19237
+ import { useEffect as useEffect25, useMemo as useMemo25, useState as useState32 } from "react";
19180
19238
  import { countBlocks } from "@bendyline/squisq/doc";
19181
19239
  import { jsx as jsx42, jsxs as jsxs32 } from "react/jsx-runtime";
19240
+ var STATS_DEBOUNCE_MS = 150;
19182
19241
  function StatusBar({ className, slotRight }) {
19183
- const { markdownSource, doc, parseError, isParsing } = useEditorContext();
19242
+ const { markdownSource, doc, parseError } = useEditorContext();
19243
+ const [statsSource, setStatsSource] = useState32(markdownSource);
19244
+ useEffect25(() => {
19245
+ if (statsSource === markdownSource) return;
19246
+ const timeout = setTimeout(() => setStatsSource(markdownSource), STATS_DEBOUNCE_MS);
19247
+ return () => clearTimeout(timeout);
19248
+ }, [markdownSource, statsSource]);
19184
19249
  const stats = useMemo25(() => {
19185
- const chars = markdownSource.length;
19186
- const words = markdownSource.trim() ? markdownSource.trim().split(/\s+/).length : 0;
19187
- const lines2 = markdownSource.split("\n").length;
19188
- const blocks = doc ? countBlocks(doc.blocks) : 0;
19189
- return { chars, words, lines: lines2, blocks };
19190
- }, [markdownSource, doc]);
19250
+ const chars = statsSource.length;
19251
+ const words = statsSource.trim() ? statsSource.trim().split(/\s+/).length : 0;
19252
+ const lines2 = statsSource.split("\n").length;
19253
+ return { chars, words, lines: lines2 };
19254
+ }, [statsSource]);
19255
+ const blocks = useMemo25(() => doc ? countBlocks(doc.blocks) : 0, [doc]);
19191
19256
  return /* @__PURE__ */ jsxs32("div", { className: `squisq-status-bar ${className || ""}`, children: [
19192
19257
  /* @__PURE__ */ jsxs32("span", { className: "squisq-status-item", children: [
19193
19258
  stats.words,
@@ -19202,19 +19267,19 @@ function StatusBar({ className, slotRight }) {
19202
19267
  " lines"
19203
19268
  ] }),
19204
19269
  /* @__PURE__ */ jsxs32("span", { className: "squisq-status-item", children: [
19205
- stats.blocks,
19270
+ blocks,
19206
19271
  " ",
19207
- stats.blocks === 1 ? "block" : "blocks"
19272
+ blocks === 1 ? "block" : "blocks"
19208
19273
  ] }),
19209
19274
  /* @__PURE__ */ jsx42("span", { className: "squisq-status-spacer" }),
19210
19275
  parseError && /* @__PURE__ */ jsx42("span", { className: "squisq-status-item squisq-status-error", title: parseError, children: "\u26A0 Error" }),
19211
- !isParsing && !parseError && /* @__PURE__ */ jsx42("span", { className: "squisq-status-item squisq-status-ok", children: "\u2713 OK" }),
19276
+ !parseError && /* @__PURE__ */ jsx42("span", { className: "squisq-status-item squisq-status-ok", children: "\u2713 OK" }),
19212
19277
  slotRight
19213
19278
  ] });
19214
19279
  }
19215
19280
 
19216
19281
  // src/RawEditor.tsx
19217
- import { useRef as useRef29, useCallback as useCallback30, useEffect as useEffect25, useState as useState32 } from "react";
19282
+ import { useRef as useRef29, useCallback as useCallback30, useEffect as useEffect26, useState as useState33 } from "react";
19218
19283
  import { getAvailableTemplates } from "@bendyline/squisq/doc";
19219
19284
  import { suggestIcons, resolveIcon, iconGlyph } from "@bendyline/squisq/icons";
19220
19285
  import { BLOCK_META_KEY_DESCRIPTORS, tokenizeAttrTokens as tokenizeAttrTokens3 } from "@bendyline/squisq/markdown";
@@ -19286,10 +19351,10 @@ var SQUISQ_THEMES = {
19286
19351
  "vs-dark": "squisq-dark"
19287
19352
  };
19288
19353
  function useDocumentMonacoLanguages(language, source) {
19289
- const [requested, setRequested] = useState32(
19354
+ const [requested, setRequested] = useState33(
19290
19355
  () => monacoLanguagesForDocument(language, source)
19291
19356
  );
19292
- useEffect25(() => {
19357
+ useEffect26(() => {
19293
19358
  const delay = normalizeMonacoLanguage(language) === "markdown" ? 250 : 0;
19294
19359
  const timer = window.setTimeout(() => {
19295
19360
  setRequested(monacoLanguagesForDocument(language, source));
@@ -19325,14 +19390,14 @@ function RawEditor({
19325
19390
  const keyDisposable = useRef29(null);
19326
19391
  const submitOnEnterRef = useRef29(submitOnEnter);
19327
19392
  const readOnlyRef = useRef29(readOnly);
19328
- useEffect25(() => {
19393
+ useEffect26(() => {
19329
19394
  submitOnEnterRef.current = submitOnEnter;
19330
19395
  }, [submitOnEnter]);
19331
- useEffect25(() => {
19396
+ useEffect26(() => {
19332
19397
  readOnlyRef.current = readOnly;
19333
19398
  }, [readOnly]);
19334
19399
  const mentionProviderRef = useRef29(mentionProvider);
19335
- useEffect25(() => {
19400
+ useEffect26(() => {
19336
19401
  mentionProviderRef.current = mentionProvider;
19337
19402
  }, [mentionProvider]);
19338
19403
  const docCustomTemplates = doc?.customTemplates;
@@ -19343,7 +19408,7 @@ function RawEditor({
19343
19408
  detail: "Block template"
19344
19409
  }));
19345
19410
  }
19346
- useEffect25(() => {
19411
+ useEffect26(() => {
19347
19412
  const builtIns = getAvailableTemplates().map((name) => ({
19348
19413
  name,
19349
19414
  detail: "Block template"
@@ -19685,7 +19750,7 @@ function RawEditor({
19685
19750
  },
19686
19751
  [setMonacoEditor, language]
19687
19752
  );
19688
- useEffect25(() => {
19753
+ useEffect26(() => {
19689
19754
  return () => {
19690
19755
  setMonacoEditor(null);
19691
19756
  completionDisposable.current?.dispose();
@@ -19713,7 +19778,7 @@ function RawEditor({
19713
19778
  },
19714
19779
  [setEditorSource]
19715
19780
  );
19716
- useEffect25(() => {
19781
+ useEffect26(() => {
19717
19782
  const editor = editorRef.current;
19718
19783
  if (editor) {
19719
19784
  const currentValue = editor.getValue();
@@ -19724,7 +19789,7 @@ function RawEditor({
19724
19789
  }
19725
19790
  }
19726
19791
  }, [editorSource]);
19727
- useEffect25(() => {
19792
+ useEffect26(() => {
19728
19793
  const editor = editorRef.current;
19729
19794
  if (!editor || !monacoNs) return;
19730
19795
  if (language !== "markdown") return;
@@ -20015,7 +20080,7 @@ function propsBadgeSpec(summary) {
20015
20080
  }
20016
20081
 
20017
20082
  // src/diagram/DiagramCanvas.tsx
20018
- import { useCallback as useCallback31, useMemo as useMemo26, useRef as useRef30, useState as useState33 } from "react";
20083
+ import { useCallback as useCallback31, useMemo as useMemo26, useRef as useRef30, useState as useState34 } from "react";
20019
20084
 
20020
20085
  // src/diagram/diagramConstants.ts
20021
20086
  var DIAGRAM_VIEWPORT = { width: 1600, height: 900 };
@@ -20040,7 +20105,7 @@ function DiagramCanvas({
20040
20105
  () => buildDiagramScene(incomingNodes, incomingEdges),
20041
20106
  [incomingNodes, incomingEdges]
20042
20107
  );
20043
- const [internalToolId, setInternalToolId] = useState33("select");
20108
+ const [internalToolId, setInternalToolId] = useState34("select");
20044
20109
  const activeToolId = controlledToolId ?? internalToolId;
20045
20110
  const setActiveToolId = onActiveToolIdChange ?? setInternalToolId;
20046
20111
  const handleSceneCommand = useCallback31(
@@ -20328,7 +20393,7 @@ function removeNodeOp(diagram, nodeId) {
20328
20393
  }
20329
20394
 
20330
20395
  // src/asciiDiagram/asciiDiagramData.ts
20331
- import { useEffect as useEffect26, useMemo as useMemo28, useState as useState35 } from "react";
20396
+ import { useEffect as useEffect27, useMemo as useMemo28, useState as useState36 } from "react";
20332
20397
  import {
20333
20398
  asciiCellToCanvas,
20334
20399
  asciiDiagramToTemplateData,
@@ -20350,7 +20415,7 @@ import {
20350
20415
  } from "@bendyline/squisq/doc";
20351
20416
 
20352
20417
  // src/asciiDiagram/AsciiDiagramWidget.tsx
20353
- import { useCallback as useCallback32, useMemo as useMemo27, useRef as useRef31, useState as useState34 } from "react";
20418
+ import { useCallback as useCallback32, useMemo as useMemo27, useRef as useRef31, useState as useState35 } from "react";
20354
20419
 
20355
20420
  // src/asciiDiagram/asciiDiagramCommands.ts
20356
20421
  import {
@@ -20598,11 +20663,11 @@ function AsciiDiagramWidget({
20598
20663
  textChannel
20599
20664
  }) {
20600
20665
  const view = useAsciiDiagramData(editor, blockId);
20601
- const [maximized, setMaximized] = useState34(false);
20602
- const [height, setHeight] = useState34(null);
20603
- const [dragHeight, setDragHeight] = useState34(null);
20666
+ const [maximized, setMaximized] = useState35(false);
20667
+ const [height, setHeight] = useState35(null);
20668
+ const [dragHeight, setDragHeight] = useState35(null);
20604
20669
  const canvasOffsetRef = useRef31(null);
20605
- const [canvasOffsetVersion, setCanvasOffsetVersion] = useState34(0);
20670
+ const [canvasOffsetVersion, setCanvasOffsetVersion] = useState35(0);
20606
20671
  const inlineRef = useRef31(null);
20607
20672
  const effectiveHeight = dragHeight ?? height;
20608
20673
  if (view && canvasOffsetRef.current === null) {
@@ -20655,8 +20720,8 @@ function AsciiDiagramWidget({
20655
20720
  setDragHeight(null);
20656
20721
  setHeight(null);
20657
20722
  }, []);
20658
- const [activeToolId, setActiveToolId] = useState34("select");
20659
- const [selectedIds, setSelectedIds] = useState34(() => /* @__PURE__ */ new Set());
20723
+ const [activeToolId, setActiveToolId] = useState35("select");
20724
+ const [selectedIds, setSelectedIds] = useState35(() => /* @__PURE__ */ new Set());
20660
20725
  const selectedNodeIds = useMemo27(
20661
20726
  () => [...selectedIds].map((id) => nodeIdFromCardLayerId(id)).filter((id) => id != null),
20662
20727
  [selectedIds]
@@ -21027,8 +21092,8 @@ function asciiDiagramToCanvas(diagram) {
21027
21092
  return { nodes, edges };
21028
21093
  }
21029
21094
  function useAsciiDiagramData(editor, blockId) {
21030
- const [version, setVersion] = useState35(0);
21031
- useEffect26(() => {
21095
+ const [version, setVersion] = useState36(0);
21096
+ useEffect27(() => {
21032
21097
  const onUpdate = () => setVersion((v2) => v2 + 1);
21033
21098
  editor.on("transaction", onUpdate);
21034
21099
  return () => {
@@ -22858,7 +22923,7 @@ function mermaidErrorMessage(error) {
22858
22923
  }
22859
22924
 
22860
22925
  // src/mermaid/mermaidSurfaceTheme.ts
22861
- import { useEffect as useEffect27, useMemo as useMemo29, useState as useState36 } from "react";
22926
+ import { useEffect as useEffect28, useMemo as useMemo29, useState as useState37 } from "react";
22862
22927
  import { relativeLuminance } from "@bendyline/squisq/schemas";
22863
22928
  var EDITOR_SURFACE = {
22864
22929
  light: {
@@ -22894,8 +22959,8 @@ function resolveMermaidSurfaceTheme(host, theme) {
22894
22959
  return applyHostSurface(readHostSurface(host), theme);
22895
22960
  }
22896
22961
  function useMermaidSurfaceTheme(host, theme) {
22897
- const [surface, setSurface] = useState36(() => readHostSurface(host));
22898
- useEffect27(() => {
22962
+ const [surface, setSurface] = useState37(() => readHostSurface(host));
22963
+ useEffect28(() => {
22899
22964
  const sync = () => setSurface((current) => {
22900
22965
  const next = readHostSurface(host);
22901
22966
  return current.scheme === next.scheme && current.inheritsThemeColors === next.inheritsThemeColors ? current : next;
@@ -22921,7 +22986,7 @@ function useMermaidSurfaceTheme(host, theme) {
22921
22986
  }
22922
22987
 
22923
22988
  // src/mermaid/MermaidDiagramCanvas.tsx
22924
- import { useCallback as useCallback33, useEffect as useEffect28, useId as useId12, useLayoutEffect as useLayoutEffect5, useRef as useRef32, useState as useState37 } from "react";
22989
+ import { useCallback as useCallback33, useEffect as useEffect29, useId as useId12, useLayoutEffect as useLayoutEffect5, useRef as useRef32, useState as useState38 } from "react";
22925
22990
  import { jsx as jsx46, jsxs as jsxs35 } from "react/jsx-runtime";
22926
22991
  var renderSequence = 0;
22927
22992
  function excludesCanvasPan(target, viewport) {
@@ -22985,20 +23050,20 @@ function MermaidDiagramCanvas({
22985
23050
  const renameInputRef = useRef32(null);
22986
23051
  const panDragRef = useRef32(null);
22987
23052
  const renameHintId = useId12();
22988
- const [svg, setSvg] = useState37("");
22989
- const [model2, setModel] = useState37(null);
22990
- const [diagramType, setDiagramType] = useState37("mermaid");
22991
- const [error, setError] = useState37("");
22992
- const [rendering, setRendering] = useState37(true);
22993
- const [zoom, setZoom] = useState37(1);
22994
- const [viewMode, setViewMode] = useState37("fit");
22995
- const [contentSize, setContentSize] = useState37(null);
22996
- const [canvasSize, setCanvasSize] = useState37(null);
22997
- const [pan, setPan] = useState37({ x: 0, y: 0 });
22998
- const [panning, setPanning] = useState37(false);
22999
- const [selectionAnchor, setSelectionAnchor] = useState37(null);
23000
- const [renameValue, setRenameValue] = useState37("");
23001
- useEffect28(() => {
23053
+ const [svg, setSvg] = useState38("");
23054
+ const [model2, setModel] = useState38(null);
23055
+ const [diagramType, setDiagramType] = useState38("mermaid");
23056
+ const [error, setError] = useState38("");
23057
+ const [rendering, setRendering] = useState38(true);
23058
+ const [zoom, setZoom] = useState38(1);
23059
+ const [viewMode, setViewMode] = useState38("fit");
23060
+ const [contentSize, setContentSize] = useState38(null);
23061
+ const [canvasSize, setCanvasSize] = useState38(null);
23062
+ const [pan, setPan] = useState38({ x: 0, y: 0 });
23063
+ const [panning, setPanning] = useState38(false);
23064
+ const [selectionAnchor, setSelectionAnchor] = useState38(null);
23065
+ const [renameValue, setRenameValue] = useState38("");
23066
+ useEffect29(() => {
23002
23067
  if (!panning) return;
23003
23068
  const finish = () => {
23004
23069
  panDragRef.current = null;
@@ -23026,7 +23091,7 @@ function MermaidDiagramCanvas({
23026
23091
  window.removeEventListener("blur", finish);
23027
23092
  };
23028
23093
  }, [panning]);
23029
- useEffect28(() => {
23094
+ useEffect29(() => {
23030
23095
  let current = true;
23031
23096
  const id = `squisq-mermaid-svg-${++renderSequence}`;
23032
23097
  setRendering(true);
@@ -23297,7 +23362,7 @@ function MermaidDiagramCanvas({
23297
23362
  updateSelectionAnchor,
23298
23363
  zoom
23299
23364
  ]);
23300
- useEffect28(() => {
23365
+ useEffect29(() => {
23301
23366
  const scroll = scrollRef.current;
23302
23367
  if (!scroll) return;
23303
23368
  const update = () => updateSelectionAnchor();
@@ -23702,7 +23767,7 @@ function CanvasAction({
23702
23767
  }
23703
23768
 
23704
23769
  // src/mermaid/MermaidShapePalette.tsx
23705
- import { useEffect as useEffect29, useLayoutEffect as useLayoutEffect6, useMemo as useMemo30, useRef as useRef33, useState as useState38 } from "react";
23770
+ import { useEffect as useEffect30, useLayoutEffect as useLayoutEffect6, useMemo as useMemo30, useRef as useRef33, useState as useState39 } from "react";
23706
23771
  import { jsx as jsx47, jsxs as jsxs36 } from "react/jsx-runtime";
23707
23772
  var CATEGORIES = ["Basic", "Process", "Data", "Documents", "Symbols"];
23708
23773
  var PALETTE_WIDTH = 360;
@@ -23710,10 +23775,10 @@ var PALETTE_MAX_HEIGHT = 520;
23710
23775
  var PALETTE_GAP = 6;
23711
23776
  var VIEWPORT_GUTTER = 8;
23712
23777
  function MermaidShapePalette({ selected, onPick, onClose }) {
23713
- const [query, setQuery] = useState38("");
23714
- const [position, setPosition] = useState38(null);
23778
+ const [query, setQuery] = useState39("");
23779
+ const [position, setPosition] = useState39(null);
23715
23780
  const ref = useRef33(null);
23716
- useEffect29(() => {
23781
+ useEffect30(() => {
23717
23782
  const onPointerDown = (event) => {
23718
23783
  const target = event.target;
23719
23784
  if (target?.closest(".squisq-scene-block-toolbar")) return;
@@ -23884,7 +23949,7 @@ function MermaidShapeThumb({ shape }) {
23884
23949
  }
23885
23950
 
23886
23951
  // src/mermaid/mermaidData.ts
23887
- import { useEffect as useEffect31, useMemo as useMemo31, useState as useState40 } from "react";
23952
+ import { useEffect as useEffect32, useMemo as useMemo31, useState as useState41 } from "react";
23888
23953
 
23889
23954
  // src/mermaid/MermaidDiagramExtension.ts
23890
23955
  import { Extension as Extension5 } from "@tiptap/core";
@@ -23896,10 +23961,10 @@ import { createRoot as createRoot3 } from "react-dom/client";
23896
23961
  // src/mermaid/MermaidDiagramWidget.tsx
23897
23962
  import {
23898
23963
  useCallback as useCallback34,
23899
- useEffect as useEffect30,
23964
+ useEffect as useEffect31,
23900
23965
  useLayoutEffect as useLayoutEffect7,
23901
23966
  useRef as useRef34,
23902
- useState as useState39,
23967
+ useState as useState40,
23903
23968
  useSyncExternalStore
23904
23969
  } from "react";
23905
23970
 
@@ -24268,16 +24333,16 @@ function MermaidDiagramWidget({
24268
24333
  themeStore?.getSnapshot ?? getDefaultTheme
24269
24334
  );
24270
24335
  const theme = useMermaidSurfaceTheme(host, docTheme);
24271
- const [model2, setModel] = useState39(null);
24272
- const [selection, setSelection] = useState39(null);
24273
- const [renaming, setRenaming] = useState39(null);
24274
- const [connecting, setConnecting] = useState39(false);
24275
- const [connectSourceId, setConnectSourceId] = useState39(null);
24276
- const [openPopover, setOpenPopover] = useState39(null);
24277
- const [editNotice, setEditNotice] = useState39("");
24278
- const [maximized, setMaximized] = useState39(false);
24279
- const [height, setHeight] = useState39(null);
24280
- const [dragHeight, setDragHeight] = useState39(null);
24336
+ const [model2, setModel] = useState40(null);
24337
+ const [selection, setSelection] = useState40(null);
24338
+ const [renaming, setRenaming] = useState40(null);
24339
+ const [connecting, setConnecting] = useState40(false);
24340
+ const [connectSourceId, setConnectSourceId] = useState40(null);
24341
+ const [openPopover, setOpenPopover] = useState40(null);
24342
+ const [editNotice, setEditNotice] = useState40("");
24343
+ const [maximized, setMaximized] = useState40(false);
24344
+ const [height, setHeight] = useState40(null);
24345
+ const [dragHeight, setDragHeight] = useState40(null);
24281
24346
  const inlineRef = useRef34(null);
24282
24347
  const effectiveHeight = dragHeight ?? height;
24283
24348
  const onModelChange = useCallback34((next) => {
@@ -24536,7 +24601,7 @@ function MermaidDiagramWidget({
24536
24601
  },
24537
24602
  [effectiveHeight]
24538
24603
  );
24539
- useEffect30(() => {
24604
+ useEffect31(() => {
24540
24605
  if (!editNotice) return;
24541
24606
  const timeout = window.setTimeout(() => setEditNotice(""), 7e3);
24542
24607
  return () => window.clearTimeout(timeout);
@@ -24773,7 +24838,7 @@ function MermaidDiagramWidget({
24773
24838
  ] });
24774
24839
  }
24775
24840
  function useDismissibleMermaidPopover(ref, onClose) {
24776
- useEffect30(() => {
24841
+ useEffect31(() => {
24777
24842
  const onPointerDown = (event) => {
24778
24843
  const target = event.target;
24779
24844
  if (target?.closest(".squisq-scene-block-toolbar")) return;
@@ -24791,7 +24856,7 @@ function useDismissibleMermaidPopover(ref, onClose) {
24791
24856
  }, [onClose, ref]);
24792
24857
  }
24793
24858
  function useClampedMermaidPopoverPosition(ref, preferredWidth, preferredMaxHeight) {
24794
- const [position, setPosition] = useState39(null);
24859
+ const [position, setPosition] = useState40(null);
24795
24860
  useLayoutEffect7(() => {
24796
24861
  const picker = ref.current;
24797
24862
  const anchor = picker?.parentElement;
@@ -24857,7 +24922,7 @@ function MermaidPropertiesPalette({
24857
24922
  onApply,
24858
24923
  onClose
24859
24924
  }) {
24860
- const [values, setValues] = useState39(
24925
+ const [values, setValues] = useState40(
24861
24926
  () => Object.fromEntries(properties.map((property) => [property.id, property.value]))
24862
24927
  );
24863
24928
  const ref = useRef34(null);
@@ -25151,8 +25216,8 @@ var MermaidDiagramExtension = Extension5.create({
25151
25216
 
25152
25217
  // src/mermaid/mermaidData.ts
25153
25218
  function useMermaidDiagramData(editor, blockId) {
25154
- const [version, setVersion] = useState40(0);
25155
- useEffect31(() => {
25219
+ const [version, setVersion] = useState41(0);
25220
+ useEffect32(() => {
25156
25221
  const onTransaction = () => setVersion((value) => value + 1);
25157
25222
  editor.on("transaction", onTransaction);
25158
25223
  return () => {
@@ -25176,13 +25241,13 @@ import { createElement as createElement10 } from "react";
25176
25241
  import { createRoot as createRoot4 } from "react-dom/client";
25177
25242
 
25178
25243
  // src/codeSnippet/CodeSnippetWidget.tsx
25179
- import { useCallback as useCallback35, useEffect as useEffect33, useState as useState42 } from "react";
25244
+ import { useCallback as useCallback35, useEffect as useEffect34, useState as useState43 } from "react";
25180
25245
 
25181
25246
  // src/codeSnippet/codeSnippetData.ts
25182
- import { useEffect as useEffect32, useMemo as useMemo32, useState as useState41 } from "react";
25247
+ import { useEffect as useEffect33, useMemo as useMemo32, useState as useState42 } from "react";
25183
25248
  function useCodeSnippetData(editor, blockId) {
25184
- const [version, setVersion] = useState41(0);
25185
- useEffect32(() => {
25249
+ const [version, setVersion] = useState42(0);
25250
+ useEffect33(() => {
25186
25251
  const onTransaction = () => setVersion((value) => value + 1);
25187
25252
  editor.on("transaction", onTransaction);
25188
25253
  return () => {
@@ -25227,10 +25292,10 @@ function schemeFromHost(host) {
25227
25292
  return host?.closest("[data-theme]")?.dataset.theme === "dark" ? "dark" : "light";
25228
25293
  }
25229
25294
  function useHostColorScheme(host) {
25230
- const [scheme, setScheme] = useState42(
25295
+ const [scheme, setScheme] = useState43(
25231
25296
  () => host != null && !host.isConnected ? null : schemeFromHost(host)
25232
25297
  );
25233
- useEffect33(() => {
25298
+ useEffect34(() => {
25234
25299
  if (host != null && !host.isConnected) {
25235
25300
  setScheme(null);
25236
25301
  const raf = requestAnimationFrame(() => {
@@ -25248,8 +25313,8 @@ function useHostColorScheme(host) {
25248
25313
  return scheme;
25249
25314
  }
25250
25315
  function useEditorEditable(editor) {
25251
- const [editable, setEditable] = useState42(editor.isEditable);
25252
- useEffect33(() => {
25316
+ const [editable, setEditable] = useState43(editor.isEditable);
25317
+ useEffect34(() => {
25253
25318
  const sync = () => setEditable(editor.isEditable);
25254
25319
  editor.on("update", sync);
25255
25320
  editor.on("transaction", sync);
@@ -25270,7 +25335,7 @@ function CodeSnippetWidget({
25270
25335
  const { ready } = useMonacoLoader(data?.monacoLanguage);
25271
25336
  const colorScheme = useHostColorScheme(host);
25272
25337
  const editable = useEditorEditable(editor);
25273
- const [modelInstanceId] = useState42(allocateCodeSnippetModelInstanceId);
25338
+ const [modelInstanceId] = useState43(allocateCodeSnippetModelInstanceId);
25274
25339
  const handleChange = useCallback35(
25275
25340
  (value) => {
25276
25341
  if (!editable) return;
@@ -25493,7 +25558,7 @@ import { createRoot as createRoot5 } from "react-dom/client";
25493
25558
  import { createElement as createElement11 } from "react";
25494
25559
 
25495
25560
  // src/fenceWidgets/HostFenceWidget.tsx
25496
- import { Component, useEffect as useEffect34, useMemo as useMemo33, useState as useState43 } from "react";
25561
+ import { Component, useEffect as useEffect35, useMemo as useMemo33, useState as useState44 } from "react";
25497
25562
  import { jsx as jsx50 } from "react/jsx-runtime";
25498
25563
  var HostFenceErrorBoundary = class extends Component {
25499
25564
  constructor() {
@@ -25508,8 +25573,8 @@ var HostFenceErrorBoundary = class extends Component {
25508
25573
  }
25509
25574
  };
25510
25575
  function HostFenceWidget({ editor, blockId, getRenderers, getTheme }) {
25511
- const [version, setVersion] = useState43(0);
25512
- useEffect34(() => {
25576
+ const [version, setVersion] = useState44(0);
25577
+ useEffect35(() => {
25513
25578
  const onUpdate = () => setVersion((v2) => v2 + 1);
25514
25579
  editor.on("transaction", onUpdate);
25515
25580
  return () => {
@@ -25659,7 +25724,7 @@ var HostFenceExtension = Extension7.create({
25659
25724
  });
25660
25725
 
25661
25726
  // src/treeview/treeViewData.ts
25662
- import { useEffect as useEffect35, useMemo as useMemo34, useState as useState45 } from "react";
25727
+ import { useEffect as useEffect36, useMemo as useMemo34, useState as useState46 } from "react";
25663
25728
 
25664
25729
  // src/treeview/TreeViewExtension.ts
25665
25730
  import { Extension as Extension8 } from "@tiptap/core";
@@ -25676,7 +25741,7 @@ import {
25676
25741
  } from "@bendyline/squisq/doc";
25677
25742
 
25678
25743
  // src/treeview/TreeOutlineWidget.tsx
25679
- import { useCallback as useCallback36, useRef as useRef35, useState as useState44 } from "react";
25744
+ import { useCallback as useCallback36, useRef as useRef35, useState as useState45 } from "react";
25680
25745
 
25681
25746
  // src/treeview/treeViewCommands.ts
25682
25747
  import { parseTree, renderTree } from "@bendyline/squisq/doc";
@@ -25913,10 +25978,10 @@ function dropPositionForPointer(event) {
25913
25978
  }
25914
25979
  function TreeOutlineWidget({ editor, blockId }) {
25915
25980
  const view = useTreeViewData(editor, blockId);
25916
- const [collapsed, setCollapsed] = useState44(() => /* @__PURE__ */ new Set());
25981
+ const [collapsed, setCollapsed] = useState45(() => /* @__PURE__ */ new Set());
25917
25982
  const activeDragRef = useRef35(null);
25918
- const [draggedId, setDraggedId] = useState44(null);
25919
- const [dropTarget, setDropTarget] = useState44(null);
25983
+ const [draggedId, setDraggedId] = useState45(null);
25984
+ const [dropTarget, setDropTarget] = useState45(null);
25920
25985
  const dispatch = useCallback36(
25921
25986
  (cmd) => applyTreeCommand(editor, blockId, cmd),
25922
25987
  [editor, blockId]
@@ -26063,7 +26128,7 @@ function TreeRowView({
26063
26128
  const hasChildren = node2.children.length > 0;
26064
26129
  const isCollapsed = collapsed.has(node2.id);
26065
26130
  const isDir = node2.isDir || hasChildren;
26066
- const [draft, setDraft] = useState44(node2.label);
26131
+ const [draft, setDraft] = useState45(node2.label);
26067
26132
  const dropPosition = dropTarget?.id === node2.id ? dropTarget.position : null;
26068
26133
  const itemClassName = [
26069
26134
  "squisq-tree-item",
@@ -26367,8 +26432,8 @@ var TreeViewExtension = Extension8.create({
26367
26432
 
26368
26433
  // src/treeview/treeViewData.ts
26369
26434
  function useTreeViewData(editor, blockId) {
26370
- const [version, setVersion] = useState45(0);
26371
- useEffect35(() => {
26435
+ const [version, setVersion] = useState46(0);
26436
+ useEffect36(() => {
26372
26437
  const onUpdate = () => setVersion((v2) => v2 + 1);
26373
26438
  editor.on("transaction", onUpdate);
26374
26439
  return () => {
@@ -26394,7 +26459,7 @@ function shouldPasteAsTreeFence(text) {
26394
26459
  }
26395
26460
 
26396
26461
  // src/timeline/timelineData.ts
26397
- import { useEffect as useEffect37, useMemo as useMemo36, useRef as useRef37, useState as useState47 } from "react";
26462
+ import { useEffect as useEffect38, useMemo as useMemo36, useRef as useRef37, useState as useState48 } from "react";
26398
26463
 
26399
26464
  // src/timeline/TimelineViewExtension.ts
26400
26465
  import { Extension as Extension9 } from "@tiptap/core";
@@ -26409,7 +26474,7 @@ import {
26409
26474
  } from "@bendyline/squisq/doc";
26410
26475
 
26411
26476
  // src/timeline/TimelineEditorWidget.tsx
26412
- import { useCallback as useCallback37, useEffect as useEffect36, useMemo as useMemo35, useRef as useRef36, useState as useState46 } from "react";
26477
+ import { useCallback as useCallback37, useEffect as useEffect37, useMemo as useMemo35, useRef as useRef36, useState as useState47 } from "react";
26413
26478
  import {
26414
26479
  asciiTimelineToTemplateData
26415
26480
  } from "@bendyline/squisq/doc";
@@ -26893,13 +26958,13 @@ import { Fragment as Fragment15, jsx as jsx52, jsxs as jsxs40 } from "react/jsx-
26893
26958
  var clamp01 = (value) => Math.max(0, Math.min(1, value));
26894
26959
  function TimelineEditorWidget({ editor, blockId }) {
26895
26960
  const view = useTimelineData(editor, blockId);
26896
- const [selectedEventId, setSelectedEventId] = useState46(null);
26897
- const [addingTrackId, setAddingTrackId] = useState46(null);
26898
- const [editingTrackId, setEditingTrackId] = useState46(null);
26899
- const [hover, setHover] = useState46(null);
26900
- const [dragged, setDragged] = useState46(null);
26961
+ const [selectedEventId, setSelectedEventId] = useState47(null);
26962
+ const [addingTrackId, setAddingTrackId] = useState47(null);
26963
+ const [editingTrackId, setEditingTrackId] = useState47(null);
26964
+ const [hover, setHover] = useState47(null);
26965
+ const [dragged, setDragged] = useState47(null);
26901
26966
  const suppressClickEventId = useRef36(null);
26902
- const [announcement, setAnnouncement] = useState46("");
26967
+ const [announcement, setAnnouncement] = useState47("");
26903
26968
  const dispatch = useCallback37(
26904
26969
  (command) => {
26905
26970
  const result = applyTimelineCommand(editor, blockId, command);
@@ -26962,7 +27027,7 @@ function TimelineEditorWidget({ editor, blockId }) {
26962
27027
  }
26963
27028
  return null;
26964
27029
  }, [selectedEventId, view]);
26965
- useEffect36(() => {
27030
+ useEffect37(() => {
26966
27031
  if (!view) return;
26967
27032
  const ids = new Set(
26968
27033
  view.timeline.tracks.flatMap((track) => track.events.map((event) => event.id))
@@ -26970,13 +27035,13 @@ function TimelineEditorWidget({ editor, blockId }) {
26970
27035
  if (selectedEventId && ids.has(selectedEventId)) return;
26971
27036
  setSelectedEventId(view.timeline.tracks[0]?.events[0]?.id ?? null);
26972
27037
  }, [selectedEventId, view]);
26973
- useEffect36(() => {
27038
+ useEffect37(() => {
26974
27039
  if (editable) return;
26975
27040
  setAddingTrackId(null);
26976
27041
  setEditingTrackId(null);
26977
27042
  setHover(null);
26978
27043
  }, [editable]);
26979
- useEffect36(() => {
27044
+ useEffect37(() => {
26980
27045
  if (!editingTrackId || view?.timeline.tracks.some((track) => track.id === editingTrackId)) {
26981
27046
  return;
26982
27047
  }
@@ -27382,8 +27447,8 @@ function InlineTrackLabel({
27382
27447
  onStart,
27383
27448
  onFinish
27384
27449
  }) {
27385
- const [draft, setDraft] = useState46(label);
27386
- useEffect36(() => {
27450
+ const [draft, setDraft] = useState47(label);
27451
+ useEffect37(() => {
27387
27452
  if (editing) setDraft(label);
27388
27453
  }, [editing, label]);
27389
27454
  const commitLabel = () => {
@@ -27440,10 +27505,10 @@ function EventInspector({
27440
27505
  onDelete
27441
27506
  }) {
27442
27507
  const { event } = selected;
27443
- const [label, setLabel] = useState46(event.label);
27444
- const [description, setDescription] = useState46(event.description ?? "");
27445
- useEffect36(() => setLabel(event.label), [event.label]);
27446
- useEffect36(() => setDescription(event.description ?? ""), [event.description]);
27508
+ const [label, setLabel] = useState47(event.label);
27509
+ const [description, setDescription] = useState47(event.description ?? "");
27510
+ useEffect37(() => setLabel(event.label), [event.label]);
27511
+ useEffect37(() => setDescription(event.description ?? ""), [event.description]);
27447
27512
  const update = (patch) => dispatch(patch);
27448
27513
  const commitLabel = () => {
27449
27514
  const next = label.trim();
@@ -27748,9 +27813,9 @@ var TimelineViewExtension = Extension9.create({
27748
27813
 
27749
27814
  // src/timeline/timelineData.ts
27750
27815
  function useTimelineData(editor, blockId) {
27751
- const [version, setVersion] = useState47(0);
27816
+ const [version, setVersion] = useState48(0);
27752
27817
  const dataCache = useRef37(null);
27753
- useEffect37(() => {
27818
+ useEffect38(() => {
27754
27819
  const onEditorChange = () => setVersion((value) => value + 1);
27755
27820
  editor.on("transaction", onEditorChange);
27756
27821
  editor.on("update", onEditorChange);
@@ -27785,7 +27850,7 @@ function shouldPasteAsTimelineFence(text) {
27785
27850
  }
27786
27851
 
27787
27852
  // src/BlockPropertiesPopover.tsx
27788
- import { useEffect as useEffect38, useId as useId13, useRef as useRef38, useState as useState48 } from "react";
27853
+ import { useEffect as useEffect39, useId as useId13, useRef as useRef38, useState as useState49 } from "react";
27789
27854
  import { createPortal as createPortal9 } from "react-dom";
27790
27855
  import { jsx as jsx53, jsxs as jsxs41 } from "react/jsx-runtime";
27791
27856
  var TRANSITION_FLYOUT = ".squisq-transition-flyout";
@@ -27801,13 +27866,13 @@ function BlockPropertiesPopover({
27801
27866
  }) {
27802
27867
  const panelRef = useRef38(null);
27803
27868
  const panelId = `squisq-block-props-portal-${useId13().replace(/:/g, "")}`;
27804
- const [inner, setInner] = useState48(blockAttrs);
27805
- const [templateInner, setTemplateInner] = useState48(templateParams);
27806
- const [style, setStyle] = useState48(() => computeStyle(anchorRect));
27807
- useEffect38(() => {
27869
+ const [inner, setInner] = useState49(blockAttrs);
27870
+ const [templateInner, setTemplateInner] = useState49(templateParams);
27871
+ const [style, setStyle] = useState49(() => computeStyle(anchorRect));
27872
+ useEffect39(() => {
27808
27873
  requestAnimationFrame(() => setStyle(computeStyle(anchorRect, panelRef.current)));
27809
27874
  }, [anchorRect]);
27810
- useEffect38(() => {
27875
+ useEffect39(() => {
27811
27876
  const onKey = (e2) => {
27812
27877
  if (e2.key === "Escape") onClose();
27813
27878
  };
@@ -27969,7 +28034,7 @@ function persistFromWrite(bodyMd, state) {
27969
28034
  }
27970
28035
 
27971
28036
  // src/WysiwygEditor.tsx
27972
- import { useCallback as useCallback38, useEffect as useEffect42, useMemo as useMemo37, useRef as useRef40, useState as useState52 } from "react";
28037
+ import { useCallback as useCallback38, useEffect as useEffect43, useMemo as useMemo37, useRef as useRef40, useState as useState53 } from "react";
27973
28038
  import { useEditor as useEditor2, EditorContent as EditorContent2 } from "@tiptap/react";
27974
28039
  import { Selection } from "@tiptap/pm/state";
27975
28040
  import StarterKit2 from "@tiptap/starter-kit";
@@ -27982,7 +28047,7 @@ import TaskItem from "@tiptap/extension-task-item";
27982
28047
  import Link2 from "@tiptap/extension-link";
27983
28048
  import Placeholder from "@tiptap/extension-placeholder";
27984
28049
  import { resolveFontFamily as resolveFontFamily2, FONT_FALLBACKS as FONT_FALLBACKS2, VIEWPORT_PRESETS as VIEWPORT_PRESETS3 } from "@bendyline/squisq/schemas";
27985
- import { DEFAULT_THEME as DEFAULT_THEME4, flattenBlocks as flattenBlocks2, markdownToDoc as markdownToDoc2 } from "@bendyline/squisq/doc";
28050
+ import { DEFAULT_THEME as DEFAULT_THEME4, flattenBlocks as flattenBlocks2, markdownToDoc as markdownToDoc3 } from "@bendyline/squisq/doc";
27986
28051
  import { parseMarkdown as parseMarkdown6 } from "@bendyline/squisq/markdown";
27987
28052
 
27988
28053
  // src/mermaid/mermaidThemeStore.ts
@@ -28205,7 +28270,7 @@ var InlineIcon = Node2.create({
28205
28270
  });
28206
28271
 
28207
28272
  // src/ImageNodeView.tsx
28208
- import { useEffect as useEffect39, useRef as useRef39, useState as useState49 } from "react";
28273
+ import { useEffect as useEffect40, useRef as useRef39, useState as useState50 } from "react";
28209
28274
  import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react";
28210
28275
  import Image from "@tiptap/extension-image";
28211
28276
 
@@ -28225,16 +28290,16 @@ import { Fragment as Fragment16, jsx as jsx54, jsxs as jsxs42 } from "react/jsx-
28225
28290
  function ImageComponent({ node: node2, selected, editor, updateAttributes }) {
28226
28291
  const { src, alt, title, width } = node2.attrs;
28227
28292
  const { mediaProvider, imageDisplayMode, openImageEdit, mediaRevision } = useEditorContext();
28228
- const [resolvedSrc, setResolvedSrc] = useState49(src);
28229
- const [hovered, setHovered] = useState49(false);
28293
+ const [resolvedSrc, setResolvedSrc] = useState50(src);
28294
+ const [hovered, setHovered] = useState50(false);
28230
28295
  const imgRef = useRef39(null);
28231
- const [previewWidth, setPreviewWidth] = useState49(null);
28296
+ const [previewWidth, setPreviewWidth] = useState50(null);
28232
28297
  const isThumbnail = imageDisplayMode === "thumbnail";
28233
28298
  const isEditable = editor?.isEditable ?? true;
28234
28299
  const normalizedRelativePath = normalizeMalformedAssetUrl(src);
28235
28300
  const isRelative = src && !src.startsWith("blob:") && !src.startsWith("http") && !src.startsWith("data:") && !src.startsWith("/");
28236
28301
  const resolveAs = normalizedRelativePath ?? (isRelative ? src : null);
28237
- useEffect39(() => {
28302
+ useEffect40(() => {
28238
28303
  if (!mediaProvider || !resolveAs) {
28239
28304
  setResolvedSrc(src);
28240
28305
  return;
@@ -28413,15 +28478,15 @@ var ImageWithMediaProvider = Image.extend({
28413
28478
  // src/tiptap/TiptapVideo.tsx
28414
28479
  import { Node as Node3, mergeAttributes as mergeAttributes2 } from "@tiptap/core";
28415
28480
  import { NodeViewWrapper as NodeViewWrapper2, ReactNodeViewRenderer as ReactNodeViewRenderer2 } from "@tiptap/react";
28416
- import { useEffect as useEffect41, useState as useState51 } from "react";
28481
+ import { useEffect as useEffect42, useState as useState52 } from "react";
28417
28482
 
28418
28483
  // src/tiptap/useResolvedMediaSrc.ts
28419
- import { useEffect as useEffect40, useState as useState50 } from "react";
28484
+ import { useEffect as useEffect41, useState as useState51 } from "react";
28420
28485
  function useResolvedMediaSrc(src) {
28421
28486
  const { mediaProvider, mediaRevision } = useEditorContext();
28422
- const [resolved, setResolved] = useState50(src);
28487
+ const [resolved, setResolved] = useState51(src);
28423
28488
  const isRelative = !!src && !src.startsWith("blob:") && !src.startsWith("http:") && !src.startsWith("https:") && !src.startsWith("data:") && !src.startsWith("/");
28424
- useEffect40(() => {
28489
+ useEffect41(() => {
28425
28490
  if (!mediaProvider || !isRelative) {
28426
28491
  setResolved(src);
28427
28492
  return;
@@ -28472,9 +28537,9 @@ function VideoNodeView({ node: node2, updateAttributes, selected }) {
28472
28537
  const placement = normalizeVideoPlacement(rawPlacement);
28473
28538
  const lockToBlock = normalizeLockToBlock(rawLockToBlock);
28474
28539
  const resolvedSrc = useResolvedMediaSrc(src ?? "");
28475
- const [audioOnly, setAudioOnly] = useState51(false);
28540
+ const [audioOnly, setAudioOnly] = useState52(false);
28476
28541
  const resolvedPoster = useResolvedMediaSrc(poster ?? "");
28477
- useEffect41(() => {
28542
+ useEffect42(() => {
28478
28543
  setAudioOnly(false);
28479
28544
  }, [resolvedSrc]);
28480
28545
  const handleLoadedMetadata = (event) => {
@@ -29141,11 +29206,11 @@ function WysiwygEditor({
29141
29206
  mermaidThemeStoreRef.current = createMermaidThemeStore(activeTheme ?? DEFAULT_THEME4);
29142
29207
  }
29143
29208
  const mermaidThemeStore = mermaidThemeStoreRef.current;
29144
- useEffect42(() => {
29209
+ useEffect43(() => {
29145
29210
  mermaidThemeStore.setTheme(activeTheme ?? DEFAULT_THEME4);
29146
29211
  }, [activeTheme, mermaidThemeStore]);
29147
29212
  const { docTemplates, onDocTemplatesChange } = useDocCustomTemplates();
29148
- const [designerState, setDesignerState] = useState52(
29213
+ const [designerState, setDesignerState] = useState53(
29149
29214
  null
29150
29215
  );
29151
29216
  const handleDesignerSave = useCallback38(
@@ -29161,15 +29226,15 @@ function WysiwygEditor({
29161
29226
  [docTemplates, onDocTemplatesChange]
29162
29227
  );
29163
29228
  const mentionProviderRef = useRef40(mentionProvider);
29164
- useEffect42(() => {
29229
+ useEffect43(() => {
29165
29230
  mentionProviderRef.current = mentionProvider;
29166
29231
  }, [mentionProvider]);
29167
29232
  const fenceRenderersRef = useRef40(fenceRenderers);
29168
- useEffect42(() => {
29233
+ useEffect43(() => {
29169
29234
  fenceRenderersRef.current = fenceRenderers;
29170
29235
  }, [fenceRenderers]);
29171
29236
  const activeThemeRef = useRef40(activeTheme);
29172
- useEffect42(() => {
29237
+ useEffect43(() => {
29173
29238
  activeThemeRef.current = activeTheme;
29174
29239
  }, [activeTheme]);
29175
29240
  const resolvedPlaceholder = useMemo37(() => placeholder ?? pickEmptyPrompt(), [placeholder]);
@@ -29177,7 +29242,7 @@ function WysiwygEditor({
29177
29242
  const lastSourceRef = useRef40(editorSource);
29178
29243
  const pendingLocalSourcesRef = useRef40([]);
29179
29244
  const mediaProviderRef = useRef40(mediaProvider);
29180
- useEffect42(() => {
29245
+ useEffect43(() => {
29181
29246
  mediaProviderRef.current = mediaProvider;
29182
29247
  }, [mediaProvider]);
29183
29248
  const frontmatterRef = useRef40(stripFrontmatter(editorSource).frontmatter);
@@ -29194,7 +29259,7 @@ function WysiwygEditor({
29194
29259
  }
29195
29260
  }
29196
29261
  const submitOnEnterRef = useRef40(submitOnEnter);
29197
- useEffect42(() => {
29262
+ useEffect43(() => {
29198
29263
  submitOnEnterRef.current = submitOnEnter;
29199
29264
  }, [submitOnEnter]);
29200
29265
  const editor = useEditor2({
@@ -29382,25 +29447,25 @@ function WysiwygEditor({
29382
29447
  }
29383
29448
  }
29384
29449
  });
29385
- useEffect42(() => {
29450
+ useEffect43(() => {
29386
29451
  if (editor) {
29387
29452
  setTiptapEditor(editor);
29388
29453
  }
29389
29454
  return () => setTiptapEditor(null);
29390
29455
  }, [editor, setTiptapEditor]);
29391
- useEffect42(() => {
29456
+ useEffect43(() => {
29392
29457
  if (editor) editor.setEditable(!readOnly);
29393
29458
  }, [editor, readOnly]);
29394
29459
  const containerRef = useRef40(null);
29395
- const [badgeMenu, setBadgeMenu] = useState52(null);
29396
- const [propsMenu, setPropsMenu] = useState52(null);
29460
+ const [badgeMenu, setBadgeMenu] = useState53(null);
29461
+ const [propsMenu, setPropsMenu] = useState53(null);
29397
29462
  const closeBadgeMenu = useCallback38(() => {
29398
29463
  setBadgeMenu(null);
29399
29464
  requestAnimationFrame(() => {
29400
29465
  if (editor && !editor.isDestroyed) editor.commands.focus();
29401
29466
  });
29402
29467
  }, [editor]);
29403
- useEffect42(() => {
29468
+ useEffect43(() => {
29404
29469
  if (!editor) return;
29405
29470
  const root = containerRef.current;
29406
29471
  if (!root) return;
@@ -29456,7 +29521,7 @@ function WysiwygEditor({
29456
29521
  root.addEventListener("mousedown", onClick);
29457
29522
  return () => root.removeEventListener("mousedown", onClick);
29458
29523
  }, [editor]);
29459
- useEffect42(() => {
29524
+ useEffect43(() => {
29460
29525
  if (!editor) return;
29461
29526
  const pendingIndex = pendingLocalSourcesRef.current.lastIndexOf(editorSource);
29462
29527
  if (pendingIndex >= 0) {
@@ -29491,7 +29556,7 @@ function WysiwygEditor({
29491
29556
  const badgePreviewSource = useMemo37(() => {
29492
29557
  if (!badgeMenu) return void 0;
29493
29558
  try {
29494
- const previewDoc = markdownToDoc2(parseMarkdown6(editorSource), { autoTemplates: false });
29559
+ const previewDoc = markdownToDoc3(parseMarkdown6(editorSource), { autoTemplates: false });
29495
29560
  const block = flattenBlocks2(previewDoc.blocks)[badgeMenu.headingIndex];
29496
29561
  if (!block) return void 0;
29497
29562
  return {
@@ -29713,7 +29778,7 @@ function moveSelectionToDropPoint(view, event) {
29713
29778
  }
29714
29779
 
29715
29780
  // src/InlinePreviewGutter.tsx
29716
- import { useLayoutEffect as useLayoutEffect8, useMemo as useMemo39, useRef as useRef41, useState as useState54 } from "react";
29781
+ import { useLayoutEffect as useLayoutEffect8, useMemo as useMemo39, useRef as useRef41, useState as useState55 } from "react";
29717
29782
  import { VIEWPORT_PRESETS as VIEWPORT_PRESETS4 } from "@bendyline/squisq/schemas";
29718
29783
  import {
29719
29784
  flattenBlocks as flattenBlocks4,
@@ -29725,14 +29790,14 @@ import { extractPlainText as extractPlainText3, getChildren } from "@bendyline/s
29725
29790
  import { BlockRenderer as BlockRenderer3, MediaContext as MediaContext4 } from "@bendyline/squisq-react";
29726
29791
 
29727
29792
  // src/useHeadingLayout.ts
29728
- import { useCallback as useCallback39, useEffect as useEffect43, useMemo as useMemo38, useState as useState53 } from "react";
29793
+ import { useCallback as useCallback39, useEffect as useEffect44, useMemo as useMemo38, useState as useState54 } from "react";
29729
29794
  import { flattenBlocks as flattenBlocks3, hasTemplate } from "@bendyline/squisq/doc";
29730
29795
  function useHeadingLayout(refInsideWrapper) {
29731
29796
  const { doc, activeView, monacoEditor, tiptapEditor } = useEditorContext();
29732
29797
  const flatBlocks = useMemo38(() => doc ? flattenBlocks3(doc.blocks) : [], [doc]);
29733
- const [entries, setEntries] = useState53([]);
29734
- const [pageEdges, setPageEdges] = useState53(null);
29735
- useEffect43(() => {
29798
+ const [entries, setEntries] = useState54([]);
29799
+ const [pageEdges, setPageEdges] = useState54(null);
29800
+ useEffect44(() => {
29736
29801
  if (activeView !== "wysiwyg") return;
29737
29802
  const node2 = refInsideWrapper.current;
29738
29803
  if (!node2) return;
@@ -29800,7 +29865,7 @@ function useHeadingLayout(refInsideWrapper) {
29800
29865
  window.removeEventListener("resize", recompute);
29801
29866
  };
29802
29867
  }, [activeView, flatBlocks, refInsideWrapper]);
29803
- useEffect43(() => {
29868
+ useEffect44(() => {
29804
29869
  if (activeView !== "raw") return;
29805
29870
  if (!monacoEditor) return;
29806
29871
  const node2 = refInsideWrapper.current;
@@ -29861,7 +29926,7 @@ function useHeadingLayout(refInsideWrapper) {
29861
29926
  window.removeEventListener("resize", recompute);
29862
29927
  };
29863
29928
  }, [activeView, monacoEditor, flatBlocks, refInsideWrapper]);
29864
- useEffect43(() => {
29929
+ useEffect44(() => {
29865
29930
  setEntries([]);
29866
29931
  setPageEdges(null);
29867
29932
  }, [activeView]);
@@ -30155,7 +30220,7 @@ function InlinePreviewGutter({
30155
30220
  headingEntries.forEach((e2) => m.set(e2.block.id, e2.top));
30156
30221
  return m;
30157
30222
  }, [headingEntries]);
30158
- const [positions, setPositions] = useState54(/* @__PURE__ */ new Map());
30223
+ const [positions, setPositions] = useState55(/* @__PURE__ */ new Map());
30159
30224
  useLayoutEffect8(() => {
30160
30225
  if (items.length === 0) {
30161
30226
  setPositions((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
@@ -30344,11 +30409,12 @@ import {
30344
30409
 
30345
30410
  // src/OutlinePanel.tsx
30346
30411
  import {
30412
+ memo,
30347
30413
  useCallback as useCallback40,
30348
- useEffect as useEffect44,
30414
+ useEffect as useEffect45,
30349
30415
  useMemo as useMemo40,
30350
30416
  useRef as useRef42,
30351
- useState as useState55
30417
+ useState as useState56
30352
30418
  } from "react";
30353
30419
  import { flattenBlocks as flattenBlocks5, hasTemplate as hasTemplate2 } from "@bendyline/squisq/doc";
30354
30420
  import { extractPlainText as extractPlainText4 } from "@bendyline/squisq/markdown";
@@ -30451,8 +30517,9 @@ function splitTrailingLineGap(value) {
30451
30517
 
30452
30518
  // src/OutlinePanel.tsx
30453
30519
  import { jsx as jsx59, jsxs as jsxs46 } from "react/jsx-runtime";
30454
- var OUTLINE_RESPONSIVE_WIDTH = "clamp(180px, 15vw, 260px)";
30520
+ var OUTLINE_RESPONSIVE_WIDTH = "clamp(260px, 30vw, 460px)";
30455
30521
  var OUTLINE_DRAG_MIME = "application/x-squisq-outline-section";
30522
+ var EMPTY_BLOCKS = [];
30456
30523
  function OutlinePanel({ width, className, readOnly = false }) {
30457
30524
  const {
30458
30525
  doc,
@@ -30461,14 +30528,44 @@ function OutlinePanel({ width, className, readOnly = false }) {
30461
30528
  isParsing,
30462
30529
  layoutMode,
30463
30530
  goToBlockByLine,
30464
- activeBlockStartLine
30531
+ activeBlockStartLine,
30532
+ activeView,
30533
+ monacoEditor,
30534
+ tiptapEditor
30465
30535
  } = useEditorContext();
30466
30536
  const paneRef = useRef42(null);
30467
- const { scrollToBlock } = useHeadingLayout(paneRef);
30468
30537
  const cursorActiveId = useActiveOutlineBlockId();
30469
30538
  const activeDragRef = useRef42(null);
30470
- const [draggedBlockId, setDraggedBlockId] = useState55(null);
30471
- const [dropTarget, setDropTarget] = useState55(null);
30539
+ const [draggedBlockId, setDraggedBlockId] = useState56(null);
30540
+ const [dropTarget, setDropTarget] = useState56(null);
30541
+ const nextOutlineBlocks = doc?.blocks ?? EMPTY_BLOCKS;
30542
+ const outlineBlocksRef = useRef42(nextOutlineBlocks);
30543
+ if (!sameOutlineStructure(outlineBlocksRef.current, nextOutlineBlocks)) {
30544
+ outlineBlocksRef.current = nextOutlineBlocks;
30545
+ }
30546
+ const outlineBlocks = outlineBlocksRef.current;
30547
+ const interactionRef = useRef42({
30548
+ doc,
30549
+ markdownSource,
30550
+ setMarkdownSource,
30551
+ isParsing,
30552
+ layoutMode,
30553
+ goToBlockByLine,
30554
+ activeView,
30555
+ monacoEditor,
30556
+ tiptapEditor
30557
+ });
30558
+ interactionRef.current = {
30559
+ doc,
30560
+ markdownSource,
30561
+ setMarkdownSource,
30562
+ isParsing,
30563
+ layoutMode,
30564
+ goToBlockByLine,
30565
+ activeView,
30566
+ monacoEditor,
30567
+ tiptapEditor
30568
+ };
30472
30569
  const blockModeActiveId = useMemo40(() => {
30473
30570
  if (layoutMode !== "block" || activeBlockStartLine == null || !doc) return null;
30474
30571
  const match = flattenBlocks5(doc.blocks).find(
@@ -30477,28 +30574,53 @@ function OutlinePanel({ width, className, readOnly = false }) {
30477
30574
  return match?.id ?? null;
30478
30575
  }, [layoutMode, activeBlockStartLine, doc]);
30479
30576
  const activeBlockId = layoutMode === "block" ? blockModeActiveId : cursorActiveId;
30480
- const handleSelect = useCallback40(
30481
- (block) => {
30482
- if (layoutMode === "block") {
30483
- const line = block.sourceHeading?.position?.start.line;
30484
- if (typeof line === "number") {
30485
- goToBlockByLine(line);
30486
- return;
30577
+ const handleSelect = useCallback40((outlineBlock) => {
30578
+ const current = interactionRef.current;
30579
+ const block = findLiveBlock(current.doc, outlineBlock.id) ?? outlineBlock;
30580
+ if (current.layoutMode === "block") {
30581
+ const line = block.sourceHeading?.position?.start.line;
30582
+ if (typeof line === "number") {
30583
+ current.goToBlockByLine(line);
30584
+ return;
30585
+ }
30586
+ }
30587
+ if (current.activeView === "wysiwyg") {
30588
+ const wrapper = paneRef.current ? findEditorWrapper(paneRef.current) : null;
30589
+ const wysiwygContainer = wrapper?.querySelector(".squisq-wysiwyg-container");
30590
+ if (!wysiwygContainer) return;
30591
+ const headings = wysiwygContainer.querySelectorAll("h1, h2, h3, h4, h5, h6");
30592
+ const blocks = current.doc ? flattenBlocks5(current.doc.blocks) : [];
30593
+ const index2 = blocks.findIndex((candidate) => candidate.id === block.id);
30594
+ if (index2 < 0 || index2 >= headings.length) return;
30595
+ headings[index2].scrollIntoView({ behavior: "smooth", block: "start" });
30596
+ if (current.tiptapEditor) {
30597
+ try {
30598
+ current.tiptapEditor.chain().focus().run();
30599
+ } catch {
30487
30600
  }
30488
30601
  }
30489
- scrollToBlock(block);
30490
- },
30491
- [layoutMode, goToBlockByLine, scrollToBlock]
30492
- );
30602
+ return;
30603
+ }
30604
+ if (current.activeView === "raw" && current.monacoEditor) {
30605
+ const line = block.sourceHeading?.position?.start.line;
30606
+ if (typeof line === "number") {
30607
+ current.monacoEditor.revealLineInCenter(line);
30608
+ current.monacoEditor.setPosition({ lineNumber: line, column: 1 });
30609
+ current.monacoEditor.focus();
30610
+ }
30611
+ }
30612
+ }, []);
30493
30613
  const changeHeadingLevel = useCallback40(
30494
- (block, delta) => {
30495
- if (readOnly || isParsing) return;
30614
+ (outlineBlock, delta) => {
30615
+ const current = interactionRef.current;
30616
+ if (readOnly || current.isParsing) return;
30617
+ const block = findLiveBlock(current.doc, outlineBlock.id) ?? outlineBlock;
30496
30618
  const line = block.sourceHeading?.position?.start.line;
30497
30619
  if (typeof line !== "number") return;
30498
- const next = bumpHeadingLevelInSource(markdownSource, line, delta);
30499
- if (next != null) setMarkdownSource(next);
30620
+ const next = bumpHeadingLevelInSource(current.markdownSource, line, delta);
30621
+ if (next != null) current.setMarkdownSource(next);
30500
30622
  },
30501
- [isParsing, markdownSource, readOnly, setMarkdownSource]
30623
+ [readOnly]
30502
30624
  );
30503
30625
  const clearDragState = useCallback40(() => {
30504
30626
  activeDragRef.current = null;
@@ -30507,26 +30629,31 @@ function OutlinePanel({ width, className, readOnly = false }) {
30507
30629
  }, []);
30508
30630
  const handleDragStart = useCallback40(
30509
30631
  (event, section) => {
30510
- if (readOnly || isParsing) {
30632
+ const current = interactionRef.current;
30633
+ if (readOnly || current.isParsing) {
30511
30634
  event.preventDefault();
30512
30635
  return;
30513
30636
  }
30514
- activeDragRef.current = { ...section, sourceAtStart: markdownSource };
30637
+ const liveBlock = findLiveBlock(current.doc, section.blockId);
30638
+ const liveHeadingLine = liveBlock?.sourceHeading?.position?.start.line;
30639
+ const liveSection = typeof liveHeadingLine === "number" ? { ...section, headingLine: liveHeadingLine } : section;
30640
+ activeDragRef.current = { ...liveSection, sourceAtStart: current.markdownSource };
30515
30641
  setDraggedBlockId(section.blockId);
30516
30642
  setDropTarget(null);
30517
30643
  event.dataTransfer.effectAllowed = "move";
30518
- event.dataTransfer.setData(OUTLINE_DRAG_MIME, String(section.headingLine));
30519
- event.dataTransfer.setData("text/plain", String(section.headingLine));
30644
+ event.dataTransfer.setData(OUTLINE_DRAG_MIME, String(liveSection.headingLine));
30645
+ event.dataTransfer.setData("text/plain", String(liveSection.headingLine));
30520
30646
  },
30521
- [isParsing, markdownSource, readOnly]
30647
+ [readOnly]
30522
30648
  );
30523
30649
  const handleDragOver = useCallback40(
30524
30650
  (event, section) => {
30525
30651
  const dragged = activeDragRef.current;
30526
30652
  if (!dragged) return;
30653
+ const current = interactionRef.current;
30527
30654
  event.preventDefault();
30528
30655
  event.stopPropagation();
30529
- if (!canDropOnSection(dragged, section) || readOnly || isParsing) {
30656
+ if (!canDropOnSection(dragged, section) || readOnly || current.isParsing) {
30530
30657
  event.dataTransfer.dropEffect = "none";
30531
30658
  setDropTarget(null);
30532
30659
  return;
@@ -30534,33 +30661,37 @@ function OutlinePanel({ width, className, readOnly = false }) {
30534
30661
  const placement = placementForPointer(event);
30535
30662
  event.dataTransfer.dropEffect = "move";
30536
30663
  setDropTarget(
30537
- (current) => current?.blockId === section.blockId && current.placement === placement ? current : { blockId: section.blockId, placement }
30664
+ (current2) => current2?.blockId === section.blockId && current2.placement === placement ? current2 : { blockId: section.blockId, placement }
30538
30665
  );
30539
30666
  },
30540
- [isParsing, readOnly]
30667
+ [readOnly]
30541
30668
  );
30542
30669
  const handleDrop = useCallback40(
30543
30670
  (event, section) => {
30544
30671
  const dragged = activeDragRef.current;
30545
30672
  if (!dragged) return;
30673
+ const current = interactionRef.current;
30546
30674
  event.preventDefault();
30547
30675
  event.stopPropagation();
30548
30676
  const placement = placementForPointer(event);
30549
- const canMove = !readOnly && !isParsing && dragged.sourceAtStart === markdownSource && canDropOnSection(dragged, section);
30677
+ const liveTarget = findLiveBlock(current.doc, section.blockId);
30678
+ const liveTargetLine = liveTarget?.sourceHeading?.position?.start.line;
30679
+ const targetSection = typeof liveTargetLine === "number" ? { ...section, headingLine: liveTargetLine } : section;
30680
+ const canMove = !readOnly && !current.isParsing && dragged.sourceAtStart === current.markdownSource && canDropOnSection(dragged, targetSection);
30550
30681
  const next = canMove ? moveHeadingSectionInSource(
30551
- markdownSource,
30682
+ current.markdownSource,
30552
30683
  dragged.headingLine,
30553
- section.headingLine,
30684
+ targetSection.headingLine,
30554
30685
  placement
30555
30686
  ) : null;
30556
30687
  clearDragState();
30557
- if (next != null) setMarkdownSource(next);
30688
+ if (next != null) current.setMarkdownSource(next);
30558
30689
  },
30559
- [clearDragState, isParsing, markdownSource, readOnly, setMarkdownSource]
30690
+ [clearDragState, readOnly]
30560
30691
  );
30561
30692
  const previewSettings = usePreviewSettingsOptional();
30562
30693
  const accentColor = previewSettings?.activeTheme?.colors?.primary;
30563
- const isEmpty2 = !doc || doc.blocks.length === 0 || !hasAnyHeading(doc.blocks);
30694
+ const isEmpty2 = outlineBlocks.length === 0 || !hasAnyHeading(outlineBlocks);
30564
30695
  const basis = width != null ? `${width}px` : `var(--squisq-outline-width, ${OUTLINE_RESPONSIVE_WIDTH})`;
30565
30696
  const paneStyle = {
30566
30697
  width: basis,
@@ -30576,27 +30707,28 @@ function OutlinePanel({ width, className, readOnly = false }) {
30576
30707
  style: paneStyle,
30577
30708
  "data-testid": "outline-panel",
30578
30709
  "aria-label": "Document outline",
30579
- children: isEmpty2 ? /* @__PURE__ */ jsx59("div", { className: "squisq-outline-empty", children: /* @__PURE__ */ jsx59("p", { children: "Add a heading to populate the outline." }) }) : /* @__PURE__ */ jsx59("ul", { className: "squisq-outline-tree", role: "tree", children: doc.blocks.map((b) => /* @__PURE__ */ jsx59(
30580
- OutlineNode,
30710
+ children: isEmpty2 ? /* @__PURE__ */ jsx59("div", { className: "squisq-outline-empty", children: /* @__PURE__ */ jsx59("p", { children: "Add a heading to populate the outline." }) }) : /* @__PURE__ */ jsx59(
30711
+ OutlineTree,
30581
30712
  {
30582
- block: b,
30583
- parentId: null,
30713
+ blocks: outlineBlocks,
30584
30714
  activeBlockId,
30585
30715
  draggedBlockId,
30586
30716
  dropTarget,
30587
- mutationsDisabled: readOnly || isParsing,
30717
+ mutationsDisabled: readOnly,
30588
30718
  onSelect: handleSelect,
30589
30719
  onChangeLevel: changeHeadingLevel,
30590
30720
  onDragStart: handleDragStart,
30591
30721
  onDragOver: handleDragOver,
30592
30722
  onDrop: handleDrop,
30593
30723
  onDragEnd: clearDragState
30594
- },
30595
- b.id
30596
- )) })
30724
+ }
30725
+ )
30597
30726
  }
30598
30727
  );
30599
30728
  }
30729
+ var OutlineTree = memo(function OutlineTree2({ blocks, ...nodeProps }) {
30730
+ return /* @__PURE__ */ jsx59("ul", { className: "squisq-outline-tree", role: "tree", children: blocks.map((block) => /* @__PURE__ */ jsx59(OutlineNode, { block, parentId: null, ...nodeProps }, block.id)) });
30731
+ });
30600
30732
  function OutlineNode({
30601
30733
  block,
30602
30734
  parentId,
@@ -30735,11 +30867,14 @@ function OutlineNode({
30735
30867
  function useActiveOutlineBlockId() {
30736
30868
  const { doc, activeView, tiptapEditor, monacoEditor } = useEditorContext();
30737
30869
  const flatBlocks = useMemo40(() => doc ? flattenBlocks5(doc.blocks) : [], [doc]);
30738
- const [activeId, setActiveId] = useState55(null);
30739
- useEffect44(() => {
30870
+ const flatBlocksRef = useRef42(flatBlocks);
30871
+ flatBlocksRef.current = flatBlocks;
30872
+ const headingLocations = flatBlocks.map((block) => `${block.id}:${block.sourceHeading?.position?.start.line ?? ""}`).join("\0");
30873
+ const [activeId, setActiveId] = useState56(null);
30874
+ useEffect45(() => {
30740
30875
  setActiveId(null);
30741
30876
  }, [activeView]);
30742
- useEffect44(() => {
30877
+ useEffect45(() => {
30743
30878
  if (activeView !== "wysiwyg" || !tiptapEditor) return;
30744
30879
  const update = () => {
30745
30880
  const { from } = tiptapEditor.state.selection;
@@ -30750,7 +30885,7 @@ function useActiveOutlineBlockId() {
30750
30885
  seen += 1;
30751
30886
  if (offset <= from) lastIndex = seen;
30752
30887
  });
30753
- const block = lastIndex >= 0 ? flatBlocks[lastIndex] : null;
30888
+ const block = lastIndex >= 0 ? flatBlocksRef.current[lastIndex] : null;
30754
30889
  setActiveId(block?.id ?? null);
30755
30890
  };
30756
30891
  update();
@@ -30760,8 +30895,8 @@ function useActiveOutlineBlockId() {
30760
30895
  tiptapEditor.off("selectionUpdate", update);
30761
30896
  tiptapEditor.off("update", update);
30762
30897
  };
30763
- }, [activeView, tiptapEditor, flatBlocks]);
30764
- useEffect44(() => {
30898
+ }, [activeView, tiptapEditor, headingLocations]);
30899
+ useEffect45(() => {
30765
30900
  if (activeView !== "raw" || !monacoEditor) return;
30766
30901
  const update = () => {
30767
30902
  const line = monacoEditor.getPosition()?.lineNumber;
@@ -30770,19 +30905,55 @@ function useActiveOutlineBlockId() {
30770
30905
  return;
30771
30906
  }
30772
30907
  let lastIndex = -1;
30773
- flatBlocks.forEach((b, i) => {
30908
+ flatBlocksRef.current.forEach((b, i) => {
30774
30909
  const headingLine2 = b.sourceHeading?.position?.start.line;
30775
30910
  if (typeof headingLine2 === "number" && headingLine2 <= line) lastIndex = i;
30776
30911
  });
30777
- const block = lastIndex >= 0 ? flatBlocks[lastIndex] : null;
30912
+ const block = lastIndex >= 0 ? flatBlocksRef.current[lastIndex] : null;
30778
30913
  setActiveId(block?.id ?? null);
30779
30914
  };
30780
30915
  update();
30781
30916
  const sub = monacoEditor.onDidChangeCursorPosition(update);
30782
30917
  return () => sub.dispose();
30783
- }, [activeView, monacoEditor, flatBlocks]);
30918
+ }, [activeView, monacoEditor, headingLocations]);
30784
30919
  return activeId;
30785
30920
  }
30921
+ function sameOutlineStructure(previous, next) {
30922
+ if (previous === next) return true;
30923
+ if (previous.length !== next.length) return false;
30924
+ for (let index2 = 0; index2 < previous.length; index2 += 1) {
30925
+ const before = previous[index2];
30926
+ const after = next[index2];
30927
+ if (before.id !== after.id) return false;
30928
+ const beforeHeading = before.sourceHeading;
30929
+ const afterHeading = after.sourceHeading;
30930
+ if (!!beforeHeading !== !!afterHeading) return false;
30931
+ if (beforeHeading && afterHeading) {
30932
+ if (beforeHeading.depth !== afterHeading.depth) return false;
30933
+ if (extractPlainText4(beforeHeading).trim() !== extractPlainText4(afterHeading).trim()) {
30934
+ return false;
30935
+ }
30936
+ if (beforeHeading.templateAnnotation?.template !== afterHeading.templateAnnotation?.template) {
30937
+ return false;
30938
+ }
30939
+ }
30940
+ if (!sameOutlineStructure(before.children ?? EMPTY_BLOCKS, after.children ?? EMPTY_BLOCKS)) {
30941
+ return false;
30942
+ }
30943
+ }
30944
+ return true;
30945
+ }
30946
+ function findLiveBlock(doc, blockId) {
30947
+ return doc ? flattenBlocks5(doc.blocks).find((block) => block.id === blockId) ?? null : null;
30948
+ }
30949
+ function findEditorWrapper(node2) {
30950
+ let current = node2.parentElement;
30951
+ while (current) {
30952
+ if (current.classList.contains("squisq-editor-with-gutter")) return current;
30953
+ current = current.parentElement;
30954
+ }
30955
+ return node2.parentElement;
30956
+ }
30786
30957
  function canDropOnSection(dragged, target) {
30787
30958
  return dragged.blockId !== target.blockId && dragged.depth === target.depth && dragged.parentId === target.parentId;
30788
30959
  }
@@ -30812,7 +30983,7 @@ function bumpHeadingLevelInSource(source, line, delta) {
30812
30983
  }
30813
30984
 
30814
30985
  // src/codeContext/CodeContextZones.tsx
30815
- import { useCallback as useCallback42, useEffect as useEffect46, useMemo as useMemo42, useRef as useRef44, useState as useState56 } from "react";
30986
+ import { useCallback as useCallback42, useEffect as useEffect47, useMemo as useMemo42, useRef as useRef44, useState as useState57 } from "react";
30816
30987
  import { createPortal as createPortal10 } from "react-dom";
30817
30988
 
30818
30989
  // src/codeContext/diffContextSections.ts
@@ -30936,7 +31107,7 @@ function setOrdinal(zone, ordinal) {
30936
31107
  // src/codeContext/CodeContextSectionView.tsx
30937
31108
  import { parseMarkdown as parseMarkdown8 } from "@bendyline/squisq/markdown";
30938
31109
  import { MarkdownRenderer } from "@bendyline/squisq-react";
30939
- import { useCallback as useCallback41, useEffect as useEffect45, useMemo as useMemo41, useRef as useRef43 } from "react";
31110
+ import { useCallback as useCallback41, useEffect as useEffect46, useMemo as useMemo41, useRef as useRef43 } from "react";
30940
31111
  import { jsx as jsx60, jsxs as jsxs47 } from "react/jsx-runtime";
30941
31112
  function CodeContextSectionView({
30942
31113
  section,
@@ -30956,7 +31127,7 @@ function CodeContextSectionView({
30956
31127
  () => expanded && section.markdown ? parseMarkdown8(section.markdown).children : null,
30957
31128
  [expanded, section.markdown]
30958
31129
  );
30959
- useEffect45(() => {
31130
+ useEffect46(() => {
30960
31131
  const el = rootRef.current;
30961
31132
  if (!el || typeof ResizeObserver === "undefined") return;
30962
31133
  const report = () => {
@@ -31028,11 +31199,11 @@ function CodeContextSectionView({
31028
31199
  import { Fragment as Fragment18, jsx as jsx61 } from "react/jsx-runtime";
31029
31200
  function CodeContextZones({ options }) {
31030
31201
  const { monacoEditor } = useEditorContext();
31031
- const [manager, setManager] = useState56(null);
31032
- const [, setZonesVersion] = useState56(0);
31033
- const [expandedById, setExpandedById] = useState56({});
31202
+ const [manager, setManager] = useState57(null);
31203
+ const [, setZonesVersion] = useState57(0);
31204
+ const [expandedById, setExpandedById] = useState57({});
31034
31205
  const seenIds = useRef44(/* @__PURE__ */ new Set());
31035
- useEffect46(() => {
31206
+ useEffect47(() => {
31036
31207
  if (!monacoEditor) return;
31037
31208
  const mgr = new CodeContextZoneManager(monacoEditor);
31038
31209
  const off = mgr.onDidChangeZones(() => setZonesVersion((v2) => v2 + 1));
@@ -31055,7 +31226,7 @@ function CodeContextZones({ options }) {
31055
31226
  });
31056
31227
  return out;
31057
31228
  }, [fileTop, sections]);
31058
- useEffect46(() => {
31229
+ useEffect47(() => {
31059
31230
  if (!manager) return;
31060
31231
  manager.sync(resolved.map((r) => r.spec));
31061
31232
  setExpandedById((prev) => {
@@ -31491,7 +31662,7 @@ function placeClipInBlock(source, fromLine, targetHeadingLine, spec, startAt) {
31491
31662
  }
31492
31663
 
31493
31664
  // src/TimelineTrack.tsx
31494
- import { useCallback as useCallback44, useEffect as useEffect49, useMemo as useMemo43, useRef as useRef47, useState as useState59 } from "react";
31665
+ import { useCallback as useCallback44, useEffect as useEffect50, useMemo as useMemo43, useRef as useRef47, useState as useState60 } from "react";
31495
31666
  import {
31496
31667
  resolveMediaSchedule as resolveMediaSchedule2,
31497
31668
  getDocPlaybackDuration,
@@ -31616,11 +31787,11 @@ function collectTimelinePlaybackSchedule(doc, scheduled) {
31616
31787
  }
31617
31788
 
31618
31789
  // src/TimelineBlockPreview.tsx
31619
- import { memo } from "react";
31790
+ import { memo as memo2 } from "react";
31620
31791
  import { VIEWPORT_PRESETS as VIEWPORT_PRESETS5 } from "@bendyline/squisq/schemas";
31621
31792
  import { BlockRenderer as BlockRenderer4, MediaContext as MediaContext5 } from "@bendyline/squisq-react";
31622
31793
  import { jsx as jsx63 } from "react/jsx-runtime";
31623
- var BlockThumbnail = memo(function BlockThumbnail2({
31794
+ var BlockThumbnail = memo2(function BlockThumbnail2({
31624
31795
  visual,
31625
31796
  viewport = VIEWPORT_PRESETS5.landscape,
31626
31797
  basePath = "/",
@@ -31650,7 +31821,7 @@ function resolveBlockVisual(doc, block, theme, viewport) {
31650
31821
  }
31651
31822
 
31652
31823
  // src/useTimelineClock.ts
31653
- import { useCallback as useCallback43, useEffect as useEffect47, useRef as useRef45, useState as useState57 } from "react";
31824
+ import { useCallback as useCallback43, useEffect as useEffect48, useRef as useRef45, useState as useState58 } from "react";
31654
31825
  function advanceTime(prev, dt, total) {
31655
31826
  if (total <= 0) return 0;
31656
31827
  return Math.min(total, Math.max(0, prev + dt));
@@ -31670,8 +31841,8 @@ function playTimelineMediaAt(root, time) {
31670
31841
  });
31671
31842
  }
31672
31843
  function useTimelineClock(total) {
31673
- const [currentTime, setCurrentTime] = useState57(0);
31674
- const [isPlaying, setIsPlaying] = useState57(false);
31844
+ const [currentTime, setCurrentTime] = useState58(0);
31845
+ const [isPlaying, setIsPlaying] = useState58(false);
31675
31846
  const rafRef = useRef45(null);
31676
31847
  const lastRef = useRef45(0);
31677
31848
  const currentTimeRef = useRef45(0);
@@ -31679,10 +31850,10 @@ function useTimelineClock(total) {
31679
31850
  const mediaHostRef = useRef45(null);
31680
31851
  currentTimeRef.current = currentTime;
31681
31852
  isPlayingRef.current = isPlaying;
31682
- useEffect47(() => {
31853
+ useEffect48(() => {
31683
31854
  setCurrentTime((t) => Math.min(t, Math.max(0, total)));
31684
31855
  }, [total]);
31685
- useEffect47(() => {
31856
+ useEffect48(() => {
31686
31857
  if (!isPlaying) return;
31687
31858
  lastRef.current = performance.now();
31688
31859
  const tick = (now) => {
@@ -31744,7 +31915,7 @@ function timelineMediaLabel(src, kind) {
31744
31915
  }
31745
31916
 
31746
31917
  // src/TimelineItemMenu.tsx
31747
- import { useEffect as useEffect48, useLayoutEffect as useLayoutEffect9, useRef as useRef46, useState as useState58 } from "react";
31918
+ import { useEffect as useEffect49, useLayoutEffect as useLayoutEffect9, useRef as useRef46, useState as useState59 } from "react";
31748
31919
  import { createPortal as createPortal11 } from "react-dom";
31749
31920
  import { Fragment as Fragment19, jsx as jsx64, jsxs as jsxs49 } from "react/jsx-runtime";
31750
31921
  function TimelineItemMenu({
@@ -31759,7 +31930,7 @@ function TimelineItemMenu({
31759
31930
  onClose
31760
31931
  }) {
31761
31932
  const panelRef = useRef46(null);
31762
- const [style, setStyle] = useState58(() => menuStyle(anchor));
31933
+ const [style, setStyle] = useState59(() => menuStyle(anchor));
31763
31934
  useLayoutEffect9(() => {
31764
31935
  const panel = panelRef.current;
31765
31936
  if (!panel) return;
@@ -31773,7 +31944,7 @@ function TimelineItemMenu({
31773
31944
  resizeObserver?.disconnect();
31774
31945
  };
31775
31946
  }, [anchor]);
31776
- useEffect48(() => {
31947
+ useEffect49(() => {
31777
31948
  const onKeyDown = (event) => {
31778
31949
  if (event.key !== "Escape") return;
31779
31950
  event.preventDefault();
@@ -31833,12 +32004,12 @@ function BlockMenu({
31833
32004
  onStartTime,
31834
32005
  onTransition
31835
32006
  }) {
31836
- const [explicitDuration, setExplicitDuration] = useState58(target.explicitDuration);
31837
- const [duration, setDuration] = useState58(formatNumber(target.duration));
31838
- const [startTime, setStartTime] = useState58(
32007
+ const [explicitDuration, setExplicitDuration] = useState59(target.explicitDuration);
32008
+ const [duration, setDuration] = useState59(formatNumber(target.duration));
32009
+ const [startTime, setStartTime] = useState59(
31839
32010
  target.startTime == null ? "" : formatNumber(target.startTime)
31840
32011
  );
31841
- const [transition, setTransition] = useState58(target.transition);
32012
+ const [transition, setTransition] = useState59(target.transition);
31842
32013
  const toggleAutotime = (autotimed) => {
31843
32014
  setExplicitDuration(!autotimed);
31844
32015
  if (autotimed) {
@@ -31910,11 +32081,11 @@ function VideoMenu({
31910
32081
  target,
31911
32082
  onPatch
31912
32083
  }) {
31913
- const [placement, setPlacement] = useState58(target.placement);
31914
- const [locked, setLocked] = useState58(target.lockToBlock);
31915
- const [pipSize, setPipSize] = useState58(target.pipSize ?? "");
31916
- const [pipShape, setPipShape] = useState58(target.pipShape ?? "");
31917
- const [pipPosition, setPipPosition] = useState58(target.pipPosition ?? "");
32084
+ const [placement, setPlacement] = useState59(target.placement);
32085
+ const [locked, setLocked] = useState59(target.lockToBlock);
32086
+ const [pipSize, setPipSize] = useState59(target.pipSize ?? "");
32087
+ const [pipShape, setPipShape] = useState59(target.pipShape ?? "");
32088
+ const [pipPosition, setPipPosition] = useState59(target.pipPosition ?? "");
31918
32089
  const placed = placement === "picture-in-picture" || placement === "overlay";
31919
32090
  const placementOptions = [
31920
32091
  target.canUseContentPlacement ? { value: "content", label: "In layout" } : { value: "default", label: "Default" },
@@ -32184,9 +32355,9 @@ function TimelineTrack({
32184
32355
  colorScheme
32185
32356
  } = useEditorContext();
32186
32357
  const doc = docProp ?? contextDoc;
32187
- const [drag, setDrag] = useState59(null);
32188
- const [itemMenu, setItemMenu] = useState59(null);
32189
- const [pxPerSecond, setPxPerSecond] = useState59(DEFAULT_PX_PER_SECOND);
32358
+ const [drag, setDrag] = useState60(null);
32359
+ const [itemMenu, setItemMenu] = useState60(null);
32360
+ const [pxPerSecond, setPxPerSecond] = useState60(DEFAULT_PX_PER_SECOND);
32190
32361
  const scrollRef = useRef47(null);
32191
32362
  const blocks = useMemo43(() => doc ? flattenBlocks6(doc.blocks) : [], [doc]);
32192
32363
  const previewSettings = usePreviewSettingsOptional();
@@ -32219,8 +32390,8 @@ function TimelineTrack({
32219
32390
  setDrag(null);
32220
32391
  play();
32221
32392
  }, [play]);
32222
- const [scrubbing, setScrubbing] = useState59(false);
32223
- useEffect49(() => {
32393
+ const [scrubbing, setScrubbing] = useState60(false);
32394
+ useEffect50(() => {
32224
32395
  if (!scrubbing) return;
32225
32396
  const onMove = (e2) => {
32226
32397
  const scroll = scrollRef.current;
@@ -32258,7 +32429,7 @@ function TimelineTrack({
32258
32429
  [blocks]
32259
32430
  );
32260
32431
  const followedBlockRef = useRef47(null);
32261
- useEffect49(() => {
32432
+ useEffect50(() => {
32262
32433
  if (!isPlaying) {
32263
32434
  followedBlockRef.current = null;
32264
32435
  return;
@@ -32269,7 +32440,7 @@ function TimelineTrack({
32269
32440
  const line = headingLine(block);
32270
32441
  if (line != null) goToBlockByLine(line);
32271
32442
  }, [isPlaying, currentTime, blockAtTime, goToBlockByLine]);
32272
- useEffect49(() => {
32443
+ useEffect50(() => {
32273
32444
  if (!isPlaying) return;
32274
32445
  const scroll = scrollRef.current;
32275
32446
  if (!scroll) return;
@@ -32317,8 +32488,8 @@ function TimelineTrack({
32317
32488
  );
32318
32489
  const zoomIn = useCallback44(() => setPxPerSecond((s) => Math.min(ZOOM_MAX, s * ZOOM_FACTOR)), []);
32319
32490
  const zoomOut = useCallback44(() => setPxPerSecond((s) => Math.max(ZOOM_MIN, s / ZOOM_FACTOR)), []);
32320
- const [viewportWidth, setViewportWidth] = useState59(0);
32321
- useEffect49(() => {
32491
+ const [viewportWidth, setViewportWidth] = useState60(0);
32492
+ useEffect50(() => {
32322
32493
  const el = scrollRef.current;
32323
32494
  if (!el || typeof ResizeObserver === "undefined") return;
32324
32495
  const update = () => setViewportWidth(el.clientWidth);
@@ -32332,7 +32503,7 @@ function TimelineTrack({
32332
32503
  const dragRef = useRef47(null);
32333
32504
  dragRef.current = drag;
32334
32505
  const isDragging = drag != null && !drag.committed;
32335
- useEffect49(() => {
32506
+ useEffect50(() => {
32336
32507
  if (!isDragging) return;
32337
32508
  const onMove = (e2) => {
32338
32509
  const d = dragRef.current;
@@ -32363,7 +32534,7 @@ function TimelineTrack({
32363
32534
  window.removeEventListener("pointerup", onUp);
32364
32535
  };
32365
32536
  }, [isDragging]);
32366
- useEffect49(() => {
32537
+ useEffect50(() => {
32367
32538
  if (dragRef.current?.committed) setDrag(null);
32368
32539
  }, [doc]);
32369
32540
  const beginDrag = useCallback44(
@@ -32968,7 +33139,7 @@ import { getDocPlaybackDuration as getDocPlaybackDuration2, resolveMediaSchedule
32968
33139
  import { DocPlayer } from "@bendyline/squisq-react";
32969
33140
 
32970
33141
  // src/usePreviewProjection.ts
32971
- import { useEffect as useEffect50, useState as useState60 } from "react";
33142
+ import { useEffect as useEffect51, useState as useState61 } from "react";
32972
33143
  import { resolveAudioMapping } from "@bendyline/squisq/doc";
32973
33144
  import { applyTransform } from "@bendyline/squisq/transform";
32974
33145
  function hasEquivalentAudio(left, right) {
@@ -32988,8 +33159,8 @@ function preserveEquivalentAudio(previous, next) {
32988
33159
  };
32989
33160
  }
32990
33161
  function usePreviewProjection(doc, transformStyle, workspaceContainer, documentTitle2) {
32991
- const [projection, setProjection] = useState60(null);
32992
- useEffect50(() => {
33162
+ const [projection, setProjection] = useState61(null);
33163
+ useEffect51(() => {
32993
33164
  if (!doc || !doc.blocks.length) {
32994
33165
  setProjection(null);
32995
33166
  return;
@@ -33249,7 +33420,7 @@ function TimelineToolbar({
33249
33420
  }
33250
33421
 
33251
33422
  // src/PlainHtmlPreview.tsx
33252
- import { useCallback as useCallback45, useEffect as useEffect51, useMemo as useMemo46, useRef as useRef48, useState as useState61 } from "react";
33423
+ import { useCallback as useCallback45, useEffect as useEffect52, useMemo as useMemo46, useRef as useRef48, useState as useState62 } from "react";
33253
33424
  import { parseMarkdown as parseMarkdown9 } from "@bendyline/squisq/markdown";
33254
33425
 
33255
33426
  // src/utils/collectInlineFontAwesomeCss.ts
@@ -33330,8 +33501,8 @@ function PlainHtmlPreview({
33330
33501
  [onFrameChange]
33331
33502
  );
33332
33503
  const mdDoc = useMemo46(() => parseMarkdown9(markdown), [markdown]);
33333
- const [resolvedImages, setResolvedImages] = useState61(null);
33334
- useEffect51(() => {
33504
+ const [resolvedImages, setResolvedImages] = useState62(null);
33505
+ useEffect52(() => {
33335
33506
  if (!mediaProvider) {
33336
33507
  setResolvedImages(null);
33337
33508
  return;
@@ -33367,8 +33538,8 @@ function PlainHtmlPreview({
33367
33538
  return merged;
33368
33539
  }, [resolvedImages, images]);
33369
33540
  const iconsCss = useMemo46(() => collectInlineFontAwesomeCss(), []);
33370
- const [renderFn, setRenderFn] = useState61(() => cachedRender);
33371
- useEffect51(() => {
33541
+ const [renderFn, setRenderFn] = useState62(() => cachedRender);
33542
+ useEffect52(() => {
33372
33543
  if (renderFn) return;
33373
33544
  let cancelled = false;
33374
33545
  loadRenderFn().then((fn) => {
@@ -33401,7 +33572,7 @@ function PlainHtmlPreview({
33401
33572
  frameDocument.addEventListener("click", handleClick, true);
33402
33573
  removeFrameLinkHandlerRef.current = () => frameDocument.removeEventListener("click", handleClick, true);
33403
33574
  }, [onLinkClick]);
33404
- useEffect51(() => {
33575
+ useEffect52(() => {
33405
33576
  installFrameLinkHandler();
33406
33577
  return () => removeFrameLinkHandlerRef.current();
33407
33578
  }, [html, installFrameLinkHandler]);
@@ -33497,11 +33668,11 @@ function PlainHtmlPreview({
33497
33668
  style2.remove();
33498
33669
  };
33499
33670
  }, [onCopyCode, showCodeCopyButton]);
33500
- useEffect51(() => {
33671
+ useEffect52(() => {
33501
33672
  installFrameCodeCopyControls();
33502
33673
  return () => removeFrameCodeCopyControlsRef.current();
33503
33674
  }, [html, installFrameCodeCopyControls]);
33504
- useEffect51(() => {
33675
+ useEffect52(() => {
33505
33676
  if (!globalKeyboardShortcuts) return;
33506
33677
  const handleKeyDown = (event) => {
33507
33678
  if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.key !== "ArrowDown" && event.key !== "ArrowUp") {
@@ -33580,9 +33751,15 @@ function collectImageRefs(doc) {
33580
33751
  }
33581
33752
 
33582
33753
  // src/PreviewPanel.tsx
33583
- import { useState as useState64, useEffect as useEffect55, useMemo as useMemo50, useCallback as useCallback49, useRef as useRef52 } from "react";
33754
+ import { useState as useState65, useEffect as useEffect56, useMemo as useMemo50, useCallback as useCallback49, useRef as useRef52 } from "react";
33584
33755
  import { createPortal as createPortal13 } from "react-dom";
33585
- import { DashboardView, DocPlayer as DocPlayer2, LinearDocView as LinearDocView2, useMediaProvider as useMediaProvider2 } from "@bendyline/squisq-react";
33756
+ import {
33757
+ DashboardView,
33758
+ DocPlayer as DocPlayer2,
33759
+ FlashcardView,
33760
+ LinearDocView as LinearDocView2,
33761
+ useMediaProvider as useMediaProvider2
33762
+ } from "@bendyline/squisq-react";
33586
33763
  import { resolveTransformStyle as resolveTransformStyle2 } from "@bendyline/squisq/transform";
33587
33764
 
33588
33765
  // src/buildDocumentPreviewMarkdown.ts
@@ -33719,12 +33896,12 @@ import {
33719
33896
  createContext as createContext5,
33720
33897
  useCallback as useCallback46,
33721
33898
  useContext as useContext5,
33722
- useEffect as useEffect52,
33899
+ useEffect as useEffect53,
33723
33900
  useId as useId14,
33724
33901
  useLayoutEffect as useLayoutEffect10,
33725
33902
  useMemo as useMemo47,
33726
33903
  useRef as useRef49,
33727
- useState as useState62
33904
+ useState as useState63
33728
33905
  } from "react";
33729
33906
  import { createPortal as createPortal12 } from "react-dom";
33730
33907
  import { jsx as jsx70, jsxs as jsxs54 } from "react/jsx-runtime";
@@ -33826,10 +34003,11 @@ function PresentationModeProvider({
33826
34003
  const { activeView, colorScheme, doc } = useEditorContext();
33827
34004
  const { activeTheme } = usePreviewSettings();
33828
34005
  const popupNameId = useId14().replace(/[^a-zA-Z0-9_-]/g, "");
33829
- const [selectedTarget, setSelectedTarget] = useState62("control");
33830
- const [activeTarget, setActiveTarget] = useState62(null);
33831
- const [popupRoot, setPopupRoot] = useState62(null);
33832
- const [error, setError] = useState62(null);
34006
+ const [selectedTarget, setSelectedTarget] = useState63("control");
34007
+ const [activeTarget, setActiveTarget] = useState63(null);
34008
+ const [popupRoot, setPopupRoot] = useState63(null);
34009
+ const [error, setError] = useState63(null);
34010
+ const [fullscreenExitStyle, setFullscreenExitStyle] = useState63(null);
33833
34011
  const activeTargetRef = useRef49(activeTarget);
33834
34012
  activeTargetRef.current = activeTarget;
33835
34013
  const previousActiveTargetRef = useRef49(null);
@@ -33977,12 +34155,12 @@ function PresentationModeProvider({
33977
34155
  },
33978
34156
  [availableTargets, selectedTarget, stop]
33979
34157
  );
33980
- useEffect52(() => {
34158
+ useEffect53(() => {
33981
34159
  if (availableTargets.includes(selectedTarget)) return;
33982
34160
  setSelectedTarget("control");
33983
34161
  if (activeTargetRef.current !== null) void stop();
33984
34162
  }, [availableTargets, selectedTarget, stop]);
33985
- useEffect52(() => {
34163
+ useEffect53(() => {
33986
34164
  const root = rootRef.current;
33987
34165
  const ownerDocument = root?.ownerDocument;
33988
34166
  if (!root || !ownerDocument) return;
@@ -33994,7 +34172,7 @@ function PresentationModeProvider({
33994
34172
  ownerDocument.addEventListener("fullscreenchange", handleFullscreenChange);
33995
34173
  return () => ownerDocument.removeEventListener("fullscreenchange", handleFullscreenChange);
33996
34174
  }, [rootRef]);
33997
- useEffect52(() => {
34175
+ useEffect53(() => {
33998
34176
  const root = rootRef.current;
33999
34177
  if (!root) return;
34000
34178
  if (activeTarget) root.dataset.presentationMode = activeTarget;
@@ -34003,10 +34181,10 @@ function PresentationModeProvider({
34003
34181
  delete root.dataset.presentationMode;
34004
34182
  };
34005
34183
  }, [activeTarget, rootRef]);
34006
- useEffect52(() => {
34184
+ useEffect53(() => {
34007
34185
  if (activeView !== "preview" && activeTargetRef.current !== null) void stop();
34008
34186
  }, [activeView, stop]);
34009
- useEffect52(() => {
34187
+ useEffect53(() => {
34010
34188
  const previous = previousActiveTargetRef.current;
34011
34189
  previousActiveTargetRef.current = activeTarget;
34012
34190
  if (previous === null || activeTarget !== null) return;
@@ -34014,7 +34192,7 @@ function PresentationModeProvider({
34014
34192
  returnFocusRef.current = null;
34015
34193
  if (target?.isConnected) target.focus();
34016
34194
  }, [activeTarget]);
34017
- useEffect52(() => {
34195
+ useEffect53(() => {
34018
34196
  if (activeTarget !== "control") return;
34019
34197
  const ownerDocument = rootRef.current?.ownerDocument;
34020
34198
  if (!ownerDocument) return;
@@ -34026,12 +34204,42 @@ function PresentationModeProvider({
34026
34204
  ownerDocument.addEventListener("keydown", handleKeyDown);
34027
34205
  return () => ownerDocument.removeEventListener("keydown", handleKeyDown);
34028
34206
  }, [activeTarget, rootRef, stop]);
34029
- useEffect52(() => {
34207
+ useEffect53(() => {
34030
34208
  if (!error) return;
34031
34209
  const timer = window.setTimeout(() => setError(null), 6e3);
34032
34210
  return () => window.clearTimeout(timer);
34033
34211
  }, [error]);
34034
- useEffect52(
34212
+ useLayoutEffect10(() => {
34213
+ if (activeTarget !== "fullscreen") {
34214
+ setFullscreenExitStyle(null);
34215
+ return;
34216
+ }
34217
+ const root = rootRef.current;
34218
+ const controls = root?.querySelector(".doc-controls-slideshow");
34219
+ if (!root || !controls) {
34220
+ setFullscreenExitStyle(null);
34221
+ return;
34222
+ }
34223
+ const updatePosition = () => {
34224
+ const rootBounds = root.getBoundingClientRect();
34225
+ const controlsBounds = controls.getBoundingClientRect();
34226
+ setFullscreenExitStyle({
34227
+ left: "auto",
34228
+ right: `${Math.max(0, rootBounds.right - controlsBounds.left + 8)}px`,
34229
+ transform: "none"
34230
+ });
34231
+ };
34232
+ updatePosition();
34233
+ const resizeObserver = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(updatePosition);
34234
+ resizeObserver?.observe(root);
34235
+ resizeObserver?.observe(controls);
34236
+ window.addEventListener("resize", updatePosition);
34237
+ return () => {
34238
+ resizeObserver?.disconnect();
34239
+ window.removeEventListener("resize", updatePosition);
34240
+ };
34241
+ }, [activeTarget, rootRef]);
34242
+ useEffect53(
34035
34243
  () => () => {
34036
34244
  popupCleanupRef.current?.();
34037
34245
  const popup = popupRef.current;
@@ -34077,7 +34285,7 @@ function PresentationModeProvider({
34077
34285
  type: "button",
34078
34286
  className: "squisq-presentation-exit",
34079
34287
  "data-theme": colorScheme,
34080
- style: presentationThemeStyle,
34288
+ style: { ...presentationThemeStyle, ...fullscreenExitStyle },
34081
34289
  onClick: () => void stop(),
34082
34290
  autoFocus: activeTarget === "control" || activeTarget === "fullscreen",
34083
34291
  "aria-label": "Exit presentation mode",
@@ -34134,8 +34342,8 @@ function PresentationModeControl() {
34134
34342
  const { colorScheme } = useEditorContext();
34135
34343
  const triggerRef = useRef49(null);
34136
34344
  const menuRef = useRef49(null);
34137
- const [open, setOpen] = useState62(false);
34138
- const [anchor, setAnchor] = useState62(null);
34345
+ const [open, setOpen] = useState63(false);
34346
+ const [anchor, setAnchor] = useState63(null);
34139
34347
  const options = PRESENTATION_OPTIONS.filter((option) => availableTargets.includes(option.target));
34140
34348
  const selected = options.find((option) => option.target === selectedTarget) ?? PRESENTATION_OPTIONS[0];
34141
34349
  const updatePosition = useCallback46(() => {
@@ -34160,7 +34368,7 @@ function PresentationModeControl() {
34160
34368
  updatePosition();
34161
34369
  setOpen(true);
34162
34370
  }, [updatePosition]);
34163
- useEffect52(() => {
34371
+ useEffect53(() => {
34164
34372
  if (!open) return;
34165
34373
  const handlePointerDown = (event) => {
34166
34374
  const target = event.target;
@@ -34303,10 +34511,10 @@ import {
34303
34511
  createContext as createContext6,
34304
34512
  useCallback as useCallback47,
34305
34513
  useContext as useContext6,
34306
- useEffect as useEffect53,
34514
+ useEffect as useEffect54,
34307
34515
  useMemo as useMemo48,
34308
34516
  useRef as useRef50,
34309
- useState as useState63
34517
+ useState as useState64
34310
34518
  } from "react";
34311
34519
  import { jsx as jsx71, jsxs as jsxs55 } from "react/jsx-runtime";
34312
34520
  var PrintModeContext = createContext6(null);
@@ -34321,8 +34529,8 @@ function usePrintModeOptional() {
34321
34529
  function PrintModeProvider({ rootRef, children }) {
34322
34530
  const { activeView } = useEditorContext();
34323
34531
  const { activeTarget: presentationTarget, stop: stopPresentation } = usePresentationMode();
34324
- const [active, setActive] = useState63(false);
34325
- const [slidesPerPage, setSlidesPerPage] = useState63(1);
34532
+ const [active, setActive] = useState64(false);
34533
+ const [slidesPerPage, setSlidesPerPage] = useState64(1);
34326
34534
  const customPrintHandlerRef = useRef50(null);
34327
34535
  const printAncestorsRef = useRef50([]);
34328
34536
  const unmarkPrinting = useCallback47(() => {
@@ -34373,7 +34581,7 @@ function PrintModeProvider({ rootRef, children }) {
34373
34581
  markPrinting();
34374
34582
  ownerWindow.print();
34375
34583
  }, [markPrinting, rootRef]);
34376
- useEffect53(() => {
34584
+ useEffect54(() => {
34377
34585
  const root = rootRef.current;
34378
34586
  if (!root) return;
34379
34587
  if (active) root.dataset.printPreview = "true";
@@ -34382,10 +34590,10 @@ function PrintModeProvider({ rootRef, children }) {
34382
34590
  delete root.dataset.printPreview;
34383
34591
  };
34384
34592
  }, [active, rootRef]);
34385
- useEffect53(() => {
34593
+ useEffect54(() => {
34386
34594
  if (activeView !== "preview" && active) close();
34387
34595
  }, [active, activeView, close]);
34388
- useEffect53(() => {
34596
+ useEffect54(() => {
34389
34597
  if (!active) return;
34390
34598
  const ownerWindow = rootRef.current?.ownerDocument.defaultView;
34391
34599
  const ownerDocument = rootRef.current?.ownerDocument;
@@ -34476,9 +34684,10 @@ function PrintPreviewToolbar() {
34476
34684
  }
34477
34685
 
34478
34686
  // src/print/PrintPreview.tsx
34479
- import { useCallback as useCallback48, useEffect as useEffect54, useMemo as useMemo49, useRef as useRef51 } from "react";
34687
+ import { useCallback as useCallback48, useEffect as useEffect55, useMemo as useMemo49, useRef as useRef51 } from "react";
34480
34688
  import {
34481
34689
  BlockRenderer as BlockRenderer5,
34690
+ FlashcardFaceView,
34482
34691
  LinearDocView,
34483
34692
  useDocPlayback,
34484
34693
  useMediaProvider
@@ -34486,7 +34695,7 @@ import {
34486
34695
  import {
34487
34696
  createTemplateContext as createTemplateContext2
34488
34697
  } from "@bendyline/squisq/schemas";
34489
- import { expandCoverBlock as expandCoverBlock2 } from "@bendyline/squisq/doc";
34698
+ import { expandCoverBlock as expandCoverBlock2, materializeFlashcards } from "@bendyline/squisq/doc";
34490
34699
  import { resolveTransformStyle } from "@bendyline/squisq/transform";
34491
34700
  import { jsx as jsx72, jsxs as jsxs56 } from "react/jsx-runtime";
34492
34701
  function chunk(values, size) {
@@ -34526,8 +34735,12 @@ function PrintPreview({
34526
34735
  layers: expandCoverBlock2(previewDoc.startBlock, context)
34527
34736
  };
34528
34737
  }, [previewDoc?.startBlock, showCover, theme, viewport]);
34738
+ const flashcardDeck = useMemo49(
34739
+ () => contentDoc ? materializeFlashcards(contentDoc) : null,
34740
+ [contentDoc]
34741
+ );
34529
34742
  const isTextDocument = displayMode === "page" || displayMode === "narrate";
34530
- useEffect54(() => {
34743
+ useEffect55(() => {
34531
34744
  if (!isTextDocument) return registerPrintHandler(null);
34532
34745
  return registerPrintHandler(() => {
34533
34746
  const frameWindow = documentFrameRef.current?.contentWindow;
@@ -34567,6 +34780,58 @@ function PrintPreview({
34567
34780
  ) })
34568
34781
  ] });
34569
34782
  }
34783
+ if (displayMode === "flashcards") {
34784
+ const faceProps = {
34785
+ theme,
34786
+ basePath,
34787
+ showCodeCopyButton: false,
34788
+ viewport,
34789
+ customTemplates: contentDoc?.customTemplates
34790
+ };
34791
+ return /* @__PURE__ */ jsxs56(
34792
+ "div",
34793
+ {
34794
+ className: "squisq-print-preview squisq-print-preview--flashcards",
34795
+ style: {
34796
+ "--squisq-print-flashcard-text": theme.colors.text,
34797
+ "--squisq-print-flashcard-muted": theme.colors.textMuted,
34798
+ "--squisq-print-flashcard-accent": theme.colors.primary,
34799
+ "--squisq-print-flashcard-bg": theme.colors.background,
34800
+ "--squisq-print-flashcard-surface": theme.colors.backgroundLight
34801
+ },
34802
+ children: [
34803
+ /* @__PURE__ */ jsx72("style", { children: "@page { size: landscape; margin: 0; }" }),
34804
+ flashcardDeck?.cards.map((card, index2) => /* @__PURE__ */ jsxs56("article", { className: "squisq-print-flashcard-sheet", children: [
34805
+ /* @__PURE__ */ jsxs56("header", { children: [
34806
+ /* @__PURE__ */ jsxs56("span", { children: [
34807
+ "Card ",
34808
+ index2 + 1,
34809
+ " of ",
34810
+ flashcardDeck.cards.length
34811
+ ] }),
34812
+ card.label && /* @__PURE__ */ jsx72("strong", { children: card.label })
34813
+ ] }),
34814
+ /* @__PURE__ */ jsxs56("div", { className: "squisq-print-flashcard-pair", children: [
34815
+ /* @__PURE__ */ jsxs56("section", { className: "squisq-print-flashcard-face", "aria-label": "Question side", children: [
34816
+ /* @__PURE__ */ jsx72("small", { children: "Front" }),
34817
+ /* @__PURE__ */ jsx72(FlashcardFaceView, { ...faceProps, face: card.front }),
34818
+ card.kind === "multiple-choice" && /* @__PURE__ */ jsx72("ol", { className: "squisq-print-flashcard-choices", children: card.choices?.map((choice) => /* @__PURE__ */ jsx72("li", { children: /* @__PURE__ */ jsx72(FlashcardFaceView, { ...faceProps, face: choice.content }) }, choice.id)) })
34819
+ ] }),
34820
+ /* @__PURE__ */ jsxs56("section", { className: "squisq-print-flashcard-face", "aria-label": "Answer side", children: [
34821
+ /* @__PURE__ */ jsx72("small", { children: "Back" }),
34822
+ /* @__PURE__ */ jsx72(FlashcardFaceView, { ...faceProps, face: card.back }),
34823
+ card.explanation && /* @__PURE__ */ jsxs56("div", { className: "squisq-print-flashcard-explanation", children: [
34824
+ /* @__PURE__ */ jsx72("small", { children: "Explanation" }),
34825
+ /* @__PURE__ */ jsx72(FlashcardFaceView, { ...faceProps, face: card.explanation })
34826
+ ] })
34827
+ ] })
34828
+ ] })
34829
+ ] }, card.id)),
34830
+ (!flashcardDeck || flashcardDeck.cards.length === 0) && /* @__PURE__ */ jsx72("p", { className: "squisq-print-empty", children: "Nothing to print." })
34831
+ ]
34832
+ }
34833
+ );
34834
+ }
34570
34835
  const slides = coverBlock ? [coverBlock, ...blocks] : blocks;
34571
34836
  const density = displayMode === "slideshow" ? slidesPerPage : 1;
34572
34837
  const sheets = chunk(slides, density);
@@ -34657,7 +34922,7 @@ function PreviewPanel({
34657
34922
  } = usePreviewSettings();
34658
34923
  const mainSurfaceRef = useRef52(null);
34659
34924
  const popupSurfaceRef = useRef52(null);
34660
- const [playbackState, setPlaybackState] = useState64(null);
34925
+ const [playbackState, setPlaybackState] = useState65(null);
34661
34926
  const handlePlaybackStateChange = useCallback49((next) => {
34662
34927
  setPlaybackState(next);
34663
34928
  }, []);
@@ -34696,11 +34961,12 @@ function PreviewPanel({
34696
34961
  const isPageMode = activeDisplayMode === "linear";
34697
34962
  const isNarrateMode = activeDisplayMode === "narrate";
34698
34963
  const isDashboardMode = activeDisplayMode === "dashboard";
34699
- useEffect55(() => {
34964
+ const isFlashcardsMode = activeDisplayMode === "flashcards";
34965
+ useEffect56(() => {
34700
34966
  if (presentation?.activeTarget === "window") return;
34701
34967
  setPlaybackState(null);
34702
34968
  }, [presentation?.activeTarget]);
34703
- useEffect55(() => {
34969
+ useEffect56(() => {
34704
34970
  if (presentation?.activeTarget !== "window" || !presentation.popupRoot) return;
34705
34971
  const mainRoot = mainSurfaceRef.current;
34706
34972
  const followerRoot = popupSurfaceRef.current;
@@ -34774,7 +35040,7 @@ function PreviewPanel({
34774
35040
  if (!previewDoc && !isDocumentMode && !isNarrateMode) {
34775
35041
  return /* @__PURE__ */ jsx73("div", { className: `squisq-preview-status ${className || ""}`, "data-testid": "preview-panel", children: /* @__PURE__ */ jsx73("p", { children: "No content to preview. Start typing in the editor." }) });
34776
35042
  }
34777
- const fillsContainer = printMode?.active || isDocumentMode || isPageMode || isNarrateMode ? "stretch" : "center";
35043
+ const fillsContainer = printMode?.active || isDocumentMode || isPageMode || isNarrateMode || isFlashcardsMode ? "stretch" : "center";
34778
35044
  const audienceWindowOpen = presentation?.activeTarget === "window";
34779
35045
  const renderSurface = (audience) => {
34780
35046
  if (!audience && printMode?.active) {
@@ -34847,6 +35113,21 @@ function PreviewPanel({
34847
35113
  }
34848
35114
  );
34849
35115
  }
35116
+ if (isFlashcardsMode) {
35117
+ return /* @__PURE__ */ jsx73(
35118
+ FlashcardView,
35119
+ {
35120
+ doc: contentDoc ?? doc,
35121
+ basePath,
35122
+ theme: activeTheme,
35123
+ globalKeyboardShortcuts: !audience,
35124
+ showCodeCopyButton,
35125
+ onCopyCode,
35126
+ fenceRenderers: fenceRenderers ?? void 0,
35127
+ viewport: activeViewport
35128
+ }
35129
+ );
35130
+ }
34850
35131
  if (isDashboardMode) {
34851
35132
  return /* @__PURE__ */ jsx73("div", { className: "squisq-dashboard-fit", children: /* @__PURE__ */ jsx73(
34852
35133
  DashboardView,
@@ -34949,7 +35230,7 @@ function PreviewPanel({
34949
35230
  }
34950
35231
 
34951
35232
  // src/MediaBin.tsx
34952
- import { useState as useState65, useEffect as useEffect56, useRef as useRef53, useCallback as useCallback50 } from "react";
35233
+ import { useState as useState66, useEffect as useEffect57, useRef as useRef53, useCallback as useCallback50 } from "react";
34953
35234
  import { jsx as jsx74, jsxs as jsxs58 } from "react/jsx-runtime";
34954
35235
  function formatSize(bytes) {
34955
35236
  if (bytes < 1024) return `${bytes} B`;
@@ -35014,12 +35295,12 @@ function MediaBin({
35014
35295
  isRecorderOpen = false,
35015
35296
  allowBinaryDownloads = true
35016
35297
  }) {
35017
- const [entries, setEntries] = useState65([]);
35018
- const [thumbUrls, setThumbUrls] = useState65({});
35019
- const [loading, setLoading] = useState65(false);
35020
- const [isDropActive, setIsDropActive] = useState65(false);
35021
- const [downloadingPath, setDownloadingPath] = useState65(null);
35022
- const [contextMenu, setContextMenu] = useState65(null);
35298
+ const [entries, setEntries] = useState66([]);
35299
+ const [thumbUrls, setThumbUrls] = useState66({});
35300
+ const [loading, setLoading] = useState66(false);
35301
+ const [isDropActive, setIsDropActive] = useState66(false);
35302
+ const [downloadingPath, setDownloadingPath] = useState66(null);
35303
+ const [contextMenu, setContextMenu] = useState66(null);
35023
35304
  const fileInputRef = useRef53(null);
35024
35305
  const contextMenuRef = useRef53(null);
35025
35306
  const dropDepthRef = useRef53(0);
@@ -35041,7 +35322,7 @@ function MediaBin({
35041
35322
  },
35042
35323
  [onCountChange]
35043
35324
  );
35044
- useEffect56(() => {
35325
+ useEffect57(() => {
35045
35326
  if (!contextMenu) return;
35046
35327
  const handlePointerDown = (event) => {
35047
35328
  if (contextMenuRef.current?.contains(event.target)) return;
@@ -35062,7 +35343,7 @@ function MediaBin({
35062
35343
  window.removeEventListener("resize", close);
35063
35344
  };
35064
35345
  }, [contextMenu]);
35065
- useEffect56(() => {
35346
+ useEffect57(() => {
35066
35347
  if (!mediaProvider) {
35067
35348
  setEntries([]);
35068
35349
  setThumbUrls({});
@@ -35404,7 +35685,7 @@ ${formatSize(entry.size)}`,
35404
35685
  }
35405
35686
 
35406
35687
  // src/DropZoneOverlay.tsx
35407
- import { useState as useState66 } from "react";
35688
+ import { useState as useState67 } from "react";
35408
35689
  import { Fragment as Fragment21, jsx as jsx75, jsxs as jsxs59 } from "react/jsx-runtime";
35409
35690
  function DropZoneOverlay({
35410
35691
  dragContentType,
@@ -35461,7 +35742,7 @@ function DropZone({
35461
35742
  disabled,
35462
35743
  variant
35463
35744
  }) {
35464
- const [isHovering, setIsHovering] = useState66(false);
35745
+ const [isHovering, setIsHovering] = useState67(false);
35465
35746
  const props = zoneProps(target);
35466
35747
  return /* @__PURE__ */ jsxs59(
35467
35748
  "div",
@@ -35505,7 +35786,7 @@ function DropZone({
35505
35786
  }
35506
35787
 
35507
35788
  // src/Tooltip.tsx
35508
- import { useEffect as useEffect57, useLayoutEffect as useLayoutEffect11, useRef as useRef54, useState as useState67 } from "react";
35789
+ import { useEffect as useEffect58, useLayoutEffect as useLayoutEffect11, useRef as useRef54, useState as useState68 } from "react";
35509
35790
  import { createPortal as createPortal14 } from "react-dom";
35510
35791
 
35511
35792
  // src/tooltipPlacement.ts
@@ -35521,7 +35802,7 @@ function clampTooltipLeft(anchorX, tooltipWidth, viewportWidth, edgePadding = ED
35521
35802
  import { jsx as jsx76 } from "react/jsx-runtime";
35522
35803
  var SHOW_DELAY_MS = 180;
35523
35804
  function TooltipLayer() {
35524
- const [state, setState3] = useState67(null);
35805
+ const [state, setState3] = useState68(null);
35525
35806
  const tooltipRef = useRef54(null);
35526
35807
  const timerRef = useRef54(null);
35527
35808
  const currentTargetRef = useRef54(null);
@@ -35537,7 +35818,7 @@ function TooltipLayer() {
35537
35818
  node2.style.left = `${left}px`;
35538
35819
  node2.style.visibility = "visible";
35539
35820
  }, [state]);
35540
- useEffect57(() => {
35821
+ useEffect58(() => {
35541
35822
  const clearTimer = () => {
35542
35823
  if (timerRef.current) {
35543
35824
  clearTimeout(timerRef.current);
@@ -35623,7 +35904,7 @@ function TooltipLayer() {
35623
35904
  }
35624
35905
 
35625
35906
  // src/EditorShell.tsx
35626
- import { useEffect as useEffect58, useRef as useRef55, useState as useState68, useCallback as useCallback51, useMemo as useMemo52 } from "react";
35907
+ import { useEffect as useEffect59, useRef as useRef55, useState as useState69, useCallback as useCallback51, useMemo as useMemo52 } from "react";
35627
35908
 
35628
35909
  // src/BlockPreviewPanel.tsx
35629
35910
  import { useMemo as useMemo51 } from "react";
@@ -36215,13 +36496,13 @@ function EditorShellInner({
36215
36496
  );
36216
36497
  const timelineClock = useTimelineClock(isTimelineMode ? timelineDuration : 0);
36217
36498
  const hasTimelineVideo = timelineVideoSchedule.length > 0;
36218
- const [timelineVideoVisible, setTimelineVideoVisible] = useState68(false);
36219
- const [timelineCompositionVisible, setTimelineCompositionVisible] = useState68(false);
36499
+ const [timelineVideoVisible, setTimelineVideoVisible] = useState69(false);
36500
+ const [timelineCompositionVisible, setTimelineCompositionVisible] = useState69(false);
36220
36501
  const timelinePreviewCount = Number(timelineVideoVisible) + Number(timelineCompositionVisible);
36221
- const [showFiles, setShowFiles] = useState68(false);
36222
- const [mediaRefreshKey, setMediaRefreshKey] = useState68(0);
36223
- const [mediaCount, setMediaCount] = useState68(0);
36224
- const [mediaBinRecorderOpen, setMediaBinRecorderOpen] = useState68(false);
36502
+ const [showFiles, setShowFiles] = useState69(false);
36503
+ const [mediaRefreshKey, setMediaRefreshKey] = useState69(0);
36504
+ const [mediaCount, setMediaCount] = useState69(0);
36505
+ const [mediaBinRecorderOpen, setMediaBinRecorderOpen] = useState69(false);
36225
36506
  const mediaListRefreshKey = mediaRefreshKey + mediaRevision;
36226
36507
  const usedMediaPaths = useMemo52(
36227
36508
  () => collectMediaReferencesFromMarkdown(markdownSource),
@@ -36233,13 +36514,13 @@ function EditorShellInner({
36233
36514
  }
36234
36515
  const imageEditFallbackContainer = imageEditFallbackContainerRef.current;
36235
36516
  const isDark = colorScheme === "dark";
36236
- useEffect58(() => {
36517
+ useEffect59(() => {
36237
36518
  if (!isTimelineMode || !hasTimelineVideo) setTimelineVideoVisible(false);
36238
36519
  }, [isTimelineMode, hasTimelineVideo]);
36239
- useEffect58(() => {
36520
+ useEffect59(() => {
36240
36521
  if (!isTimelineMode || !doc?.blocks.length) setTimelineCompositionVisible(false);
36241
36522
  }, [isTimelineMode, doc]);
36242
- useEffect58(() => {
36523
+ useEffect59(() => {
36243
36524
  if (!mediaProvider) {
36244
36525
  setMediaCount(0);
36245
36526
  return;
@@ -36348,7 +36629,7 @@ ${snippet}` : snippet);
36348
36629
  onDrop: handleFileDrop,
36349
36630
  enabled: !readOnly
36350
36631
  });
36351
- useEffect58(() => {
36632
+ useEffect59(() => {
36352
36633
  onChange?.(markdownSource);
36353
36634
  }, [markdownSource, onChange]);
36354
36635
  const handleShellKeyDown = useCallback51(
@@ -36714,9 +36995,9 @@ function ImageEditModal({
36714
36995
  const parent = container ?? new MemoryContentContainer();
36715
36996
  return scopeContainer(parent, `.imageEdits/${scopedName}`);
36716
36997
  }, [container, relativePath]);
36717
- const [initialSrc, setInitialSrc] = useState68(null);
36718
- const [resolveError, setResolveError] = useState68(null);
36719
- useEffect58(() => {
36998
+ const [initialSrc, setInitialSrc] = useState69(null);
36999
+ const [resolveError, setResolveError] = useState69(null);
37000
+ useEffect59(() => {
36720
37001
  let cancelled = false;
36721
37002
  setInitialSrc(null);
36722
37003
  setResolveError(null);