@bendyline/squisq-editor-react 2.5.0 → 2.7.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.
@@ -15,7 +15,7 @@ import {
15
15
  import {
16
16
  RecorderPanel,
17
17
  useModalDialog
18
- } from "./chunk-ZSQN6IO7.js";
18
+ } from "./chunk-JUAQQTJE.js";
19
19
  import {
20
20
  Icon
21
21
  } from "./chunk-GS7QWYFT.js";
@@ -378,6 +378,7 @@ function EditorProvider({
378
378
  imageDisplayMode = "inline",
379
379
  mentionProvider = null,
380
380
  documentLinkProvider = null,
381
+ fenceRenderers = null,
381
382
  linkSchemes,
382
383
  allowRecording = true,
383
384
  allowNarrate = true,
@@ -865,6 +866,7 @@ function EditorProvider({
865
866
  imageDisplayMode,
866
867
  mentionProvider,
867
868
  documentLinkProvider,
869
+ fenceRenderers,
868
870
  linkSchemes,
869
871
  fileName,
870
872
  setMarkdownSource,
@@ -926,6 +928,7 @@ function EditorProvider({
926
928
  imageDisplayMode,
927
929
  mentionProvider,
928
930
  documentLinkProvider,
931
+ fenceRenderers,
929
932
  linkSchemes,
930
933
  fileName,
931
934
  setMarkdownSource,
@@ -10108,7 +10111,15 @@ function MermaidDiagramTypeThumbnail({ preview }) {
10108
10111
  }
10109
10112
 
10110
10113
  // src/Toolbar.tsx
10111
- import { useCallback as useCallback29, useEffect as useEffect24, useMemo as useMemo24, useReducer, useRef as useRef28, useState as useState31 } from "react";
10114
+ import {
10115
+ useCallback as useCallback29,
10116
+ useEffect as useEffect24,
10117
+ useLayoutEffect as useLayoutEffect4,
10118
+ useMemo as useMemo24,
10119
+ useReducer,
10120
+ useRef as useRef28,
10121
+ useState as useState31
10122
+ } from "react";
10112
10123
  import { VIEWPORT_PRESETS as VIEWPORT_PRESETS2 } from "@bendyline/squisq/schemas";
10113
10124
  import { DEFAULT_THEME, flattenBlocks } from "@bendyline/squisq/doc";
10114
10125
  import {
@@ -10286,8 +10297,8 @@ function RecorderEntry({ open, onOpenChange, showTrigger = true } = {}) {
10286
10297
  const handleSave = useCallback16(
10287
10298
  (result) => {
10288
10299
  bumpMediaRevision();
10289
- if (result.source === "mic") {
10290
- if (activeView === "raw" && monacoEditor) {
10300
+ if (result.mediaKind === "audio") {
10301
+ if (result.source === "mic" && activeView === "raw" && monacoEditor) {
10291
10302
  annotateMonacoHeading(monacoEditor, result.filename);
10292
10303
  }
10293
10304
  const audioTag = `<audio src="${result.relativePath}" controls></audio>`;
@@ -16824,6 +16835,8 @@ function Toolbar({
16824
16835
  slotAfterActions,
16825
16836
  slotRight,
16826
16837
  showPlayTab = true,
16838
+ showFormattingControls = true,
16839
+ showInsertControls = true,
16827
16840
  hostMode = "document"
16828
16841
  }) {
16829
16842
  const {
@@ -16915,9 +16928,8 @@ function Toolbar({
16915
16928
  const codeSnippetMenuRef = useRef28(null);
16916
16929
  const mermaidTypeMenuRef = useRef28(null);
16917
16930
  const chartTypeMenuRef = useRef28(null);
16918
- const [insertMenuAnchor, setInsertMenuAnchor] = useState31(
16919
- null
16920
- );
16931
+ const insertMenuTriggerRectRef = useRef28(null);
16932
+ const [insertMenuAnchor, setInsertMenuAnchor] = useState31(null);
16921
16933
  const [codeSnippetMenuAnchor, setCodeSnippetMenuAnchor] = useState31(null);
16922
16934
  const [mermaidTypeMenuAnchor, setMermaidTypeMenuAnchor] = useState31(null);
16923
16935
  const [chartTypeMenuAnchor, setChartTypeMenuAnchor] = useState31(null);
@@ -16933,7 +16945,8 @@ function Toolbar({
16933
16945
  if (left + INSERT_MENU_WIDTH + margin > vw) {
16934
16946
  left = Math.max(margin, rect.right - INSERT_MENU_WIDTH);
16935
16947
  }
16936
- setInsertMenuAnchor({ top: rect.bottom + gap, left });
16948
+ insertMenuTriggerRectRef.current = rect;
16949
+ setInsertMenuAnchor({ top: rect.bottom + gap, left, placement: "down" });
16937
16950
  setCodeSnippetMenuAnchor(null);
16938
16951
  setMermaidTypeMenuAnchor(null);
16939
16952
  setChartTypeMenuAnchor(null);
@@ -16987,6 +17000,7 @@ function Toolbar({
16987
17000
  setMermaidTypeMenuAnchor(null);
16988
17001
  }, []);
16989
17002
  const closeInsertMenu = useCallback29(() => {
17003
+ insertMenuTriggerRectRef.current = null;
16990
17004
  setInsertMenuAnchor(null);
16991
17005
  setCodeSnippetMenuAnchor(null);
16992
17006
  setMermaidTypeMenuAnchor(null);
@@ -17080,6 +17094,28 @@ function Toolbar({
17080
17094
  document.addEventListener("mousedown", handleClick);
17081
17095
  return () => document.removeEventListener("mousedown", handleClick);
17082
17096
  }, [insertMenuAnchor, closeInsertMenu]);
17097
+ const insertMenuOpen = insertMenuAnchor !== null;
17098
+ useLayoutEffect4(() => {
17099
+ if (!insertMenuOpen) return;
17100
+ const menu = insertMenuRef.current;
17101
+ const triggerRect = insertMenuTriggerRectRef.current;
17102
+ if (!menu || !triggerRect) return;
17103
+ const gap = 4;
17104
+ const margin = 8;
17105
+ const spaceBelow = Math.max(0, window.innerHeight - triggerRect.bottom - gap - margin);
17106
+ const spaceAbove = Math.max(0, triggerRect.top - gap - margin);
17107
+ const naturalHeight = Math.max(menu.scrollHeight, menu.getBoundingClientRect().height);
17108
+ const placement = naturalHeight > spaceBelow && spaceAbove > spaceBelow ? "up" : "down";
17109
+ const maxHeight = placement === "up" ? spaceAbove : spaceBelow;
17110
+ const visibleHeight = Math.min(naturalHeight, maxHeight);
17111
+ const top = placement === "up" ? Math.max(margin, triggerRect.top - gap - visibleHeight) : triggerRect.bottom + gap;
17112
+ setInsertMenuAnchor((current) => {
17113
+ if (!current || current.top === top && current.maxHeight === maxHeight && current.placement === placement) {
17114
+ return current;
17115
+ }
17116
+ return { ...current, top, maxHeight, placement };
17117
+ });
17118
+ }, [insertMenuOpen]);
17083
17119
  const [overflowPlacement, setOverflowPlacement] = useState31("down");
17084
17120
  useEffect24(() => {
17085
17121
  if (!showOverflow || !overflowRef.current) return;
@@ -17806,7 +17842,7 @@ ${TASK_LIST_MARKDOWN}
17806
17842
  return Number(m[1]) <= visibleHeadingMax;
17807
17843
  };
17808
17844
  const hasVisibleMediaButtons = MEDIA_BUTTONS.some((b) => isButtonVisible(b.id));
17809
- const showInsertInOverflow = overflowIndex !== null && overflowIndex <= FIRST_MEDIA_INDEX && hasVisibleMediaButtons;
17845
+ const showInsertInOverflow = showInsertControls && overflowIndex !== null && overflowIndex <= FIRST_MEDIA_INDEX && hasVisibleMediaButtons;
17810
17846
  const isInTable = isWysiwyg ? tiptapEditor.isActive("table") : false;
17811
17847
  const wysiwygTemplate = isWysiwyg ? tiptapEditor.isActive("heading") ? tiptapEditor.getAttributes("heading")?.dataTemplate ?? "" : null : null;
17812
17848
  const isRawView = activeView === "raw";
@@ -18026,17 +18062,17 @@ ${TASK_LIST_MARKDOWN}
18026
18062
  dataTemplateParams: updated.templateParams
18027
18063
  }).run();
18028
18064
  };
18029
- const showTemplateInOverflow = currentTemplate !== null && clippedContextual.has("template");
18030
- const showTransitionInOverflow = currentTransition !== null && clippedContextual.has("transition");
18065
+ const showTemplateInOverflow = showFormattingControls && currentTemplate !== null && clippedContextual.has("template");
18066
+ const showTransitionInOverflow = showFormattingControls && currentTransition !== null && clippedContextual.has("transition");
18031
18067
  const showBlockSectionInOverflow = showTemplateInOverflow || showTransitionInOverflow;
18032
18068
  const overflowBlockLabel = currentTemplate ? templateLabel(currentTemplate) : "Heading";
18033
- const showToolbarOverflow = !findMode && !isPreview && !isCodeMode && (overflowIndex !== null || clippedContextual.size > 0);
18069
+ const showToolbarOverflow = !findMode && !isPreview && !isCodeMode && (showFormattingControls && (overflowIndex !== null || clippedContextual.size > 0) || showInsertInOverflow);
18034
18070
  return /* @__PURE__ */ jsxs31(
18035
18071
  "div",
18036
18072
  {
18037
18073
  className: `squisq-toolbar${findMode ? " squisq-toolbar--find" : ""} ${className || ""}`,
18038
18074
  role: "toolbar",
18039
- "aria-label": findMode ? "Find toolbar" : "Formatting toolbar",
18075
+ "aria-label": findMode ? "Find toolbar" : showFormattingControls ? "Formatting toolbar" : "Editor toolbar",
18040
18076
  children: [
18041
18077
  /* @__PURE__ */ jsx41(
18042
18078
  "input",
@@ -18107,8 +18143,8 @@ ${TASK_LIST_MARKDOWN}
18107
18143
  );
18108
18144
  }) }),
18109
18145
  findMode ? /* @__PURE__ */ jsx41(FindToolbar, { onClose: () => setFindMode(false) }) : slotAfterTabs,
18110
- !findMode && !isPreview && !isCodeMode && /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-actions", ref: actionsRef, children: [
18111
- groups.map((group, gi) => /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-group", children: [
18146
+ !findMode && !isPreview && !isCodeMode && (showFormattingControls || showInsertControls) && /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-actions", ref: actionsRef, children: [
18147
+ showFormattingControls && groups.map((group, gi) => /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-group", children: [
18112
18148
  gi > 0 && /* @__PURE__ */ jsx41("div", { className: "squisq-toolbar-separator" }),
18113
18149
  BUTTONS.filter((b) => b.group === group && isButtonVisible(b.id)).map((btn) => {
18114
18150
  const active = btn.id === "emoji" ? emojiPickerAnchor !== null : formatActive && formattingEditor ? isTiptapActive(formattingEditor, btn.id) : false;
@@ -18130,8 +18166,8 @@ ${TASK_LIST_MARKDOWN}
18130
18166
  );
18131
18167
  })
18132
18168
  ] }, group)),
18133
- /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-group", children: [
18134
- /* @__PURE__ */ jsx41("div", { className: "squisq-toolbar-separator" }),
18169
+ showInsertControls && /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-group", children: [
18170
+ showFormattingControls && /* @__PURE__ */ jsx41("div", { className: "squisq-toolbar-separator" }),
18135
18171
  /* @__PURE__ */ jsx41(
18136
18172
  "button",
18137
18173
  {
@@ -18147,7 +18183,7 @@ ${TASK_LIST_MARKDOWN}
18147
18183
  }
18148
18184
  )
18149
18185
  ] }),
18150
- currentTemplate !== null && /* @__PURE__ */ jsxs31(
18186
+ showFormattingControls && currentTemplate !== null && /* @__PURE__ */ jsxs31(
18151
18187
  "div",
18152
18188
  {
18153
18189
  className: `squisq-toolbar-group squisq-toolbar-contextual squisq-template-picker${clippedContextual.has("template") ? " squisq-toolbar-contextual--clipped" : ""}`,
@@ -18167,7 +18203,7 @@ ${TASK_LIST_MARKDOWN}
18167
18203
  ]
18168
18204
  }
18169
18205
  ),
18170
- currentTransition !== null && /* @__PURE__ */ jsxs31(
18206
+ showFormattingControls && currentTransition !== null && /* @__PURE__ */ jsxs31(
18171
18207
  "div",
18172
18208
  {
18173
18209
  className: `squisq-toolbar-group squisq-toolbar-contextual squisq-transition-picker-group${clippedContextual.has("transition") ? " squisq-toolbar-contextual--clipped" : ""}`,
@@ -18186,7 +18222,7 @@ ${TASK_LIST_MARKDOWN}
18186
18222
  ]
18187
18223
  }
18188
18224
  ),
18189
- isInTable && /* @__PURE__ */ jsxs31(
18225
+ showFormattingControls && isInTable && /* @__PURE__ */ jsxs31(
18190
18226
  "div",
18191
18227
  {
18192
18228
  className: `squisq-toolbar-group squisq-toolbar-contextual squisq-table-controls${clippedContextual.has("table") ? " squisq-toolbar-contextual--clipped" : ""}`,
@@ -18386,6 +18422,7 @@ ${TASK_LIST_MARKDOWN}
18386
18422
  }
18387
18423
  )
18388
18424
  ] }),
18425
+ !findMode && !isPreview && !isCodeMode && !showFormattingControls && !showInsertControls && /* @__PURE__ */ jsx41("div", { className: "squisq-toolbar-actions" }),
18389
18426
  showToolbarOverflow && /* @__PURE__ */ jsxs31("div", { className: "squisq-toolbar-overflow", ref: overflowRef, children: [
18390
18427
  /* @__PURE__ */ jsx41(
18391
18428
  "button",
@@ -18404,7 +18441,7 @@ ${TASK_LIST_MARKDOWN}
18404
18441
  {
18405
18442
  className: `squisq-toolbar-overflow-menu squisq-toolbar-overflow-menu--${overflowPlacement}`,
18406
18443
  children: [
18407
- BUTTONS.slice(overflowIndex ?? BUTTONS.length).filter((b) => isButtonVisible(b.id)).filter((b) => b.group !== "media").map((btn) => {
18444
+ showFormattingControls && BUTTONS.slice(overflowIndex ?? BUTTONS.length).filter((b) => isButtonVisible(b.id)).filter((b) => b.group !== "media").map((btn) => {
18408
18445
  const active = btn.id === "emoji" ? emojiPickerAnchor !== null : formatActive && formattingEditor ? isTiptapActive(formattingEditor, btn.id) : false;
18409
18446
  const disabled = btn.id === "image" && !mediaProvider || !buttonAllowed(btn.id);
18410
18447
  return /* @__PURE__ */ jsxs31(
@@ -18480,7 +18517,7 @@ ${TASK_LIST_MARKDOWN}
18480
18517
  accentColor: previewSettings?.activeTheme.colors.primary
18481
18518
  }
18482
18519
  ) }),
18483
- isInTable && clippedContextual.has("table") && /* @__PURE__ */ jsxs31(Fragment13, { children: [
18520
+ showFormattingControls && isInTable && clippedContextual.has("table") && /* @__PURE__ */ jsxs31(Fragment13, { children: [
18484
18521
  /* @__PURE__ */ jsx41(
18485
18522
  "div",
18486
18523
  {
@@ -18608,7 +18645,13 @@ ${TASK_LIST_MARKDOWN}
18608
18645
  ref: insertMenuRef,
18609
18646
  className: "squisq-insert-menu",
18610
18647
  "data-theme": colorScheme,
18611
- style: { position: "fixed", top: insertMenuAnchor.top, left: insertMenuAnchor.left },
18648
+ "data-placement": insertMenuAnchor.placement,
18649
+ style: {
18650
+ position: "fixed",
18651
+ top: insertMenuAnchor.top,
18652
+ left: insertMenuAnchor.left,
18653
+ maxHeight: insertMenuAnchor.maxHeight
18654
+ },
18612
18655
  role: "menu",
18613
18656
  children: [
18614
18657
  showConvertActions && /* @__PURE__ */ jsxs31(Fragment13, { children: [
@@ -22574,7 +22617,7 @@ function mermaidErrorMessage(error) {
22574
22617
  }
22575
22618
 
22576
22619
  // src/mermaid/MermaidDiagramCanvas.tsx
22577
- import { useCallback as useCallback33, useEffect as useEffect27, useId as useId12, useLayoutEffect as useLayoutEffect4, useRef as useRef32, useState as useState36 } from "react";
22620
+ import { useCallback as useCallback33, useEffect as useEffect27, useId as useId12, useLayoutEffect as useLayoutEffect5, useRef as useRef32, useState as useState36 } from "react";
22578
22621
  import { jsx as jsx46, jsxs as jsxs35 } from "react/jsx-runtime";
22579
22622
  var renderSequence = 0;
22580
22623
  function excludesCanvasPan(target, viewport) {
@@ -22704,7 +22747,7 @@ function MermaidDiagramCanvas({
22704
22747
  current = false;
22705
22748
  };
22706
22749
  }, [source, onModelChange, theme]);
22707
- useLayoutEffect4(() => {
22750
+ useLayoutEffect5(() => {
22708
22751
  const scroll = scrollRef.current;
22709
22752
  if (!scroll) return;
22710
22753
  const measure = () => {
@@ -22725,7 +22768,7 @@ function MermaidDiagramCanvas({
22725
22768
  observer.observe(scroll);
22726
22769
  return () => observer.disconnect();
22727
22770
  }, []);
22728
- useLayoutEffect4(() => {
22771
+ useLayoutEffect5(() => {
22729
22772
  setContentSize(readSvgViewBox(svgRootRef.current));
22730
22773
  }, [svg]);
22731
22774
  const fitZoom = contentSize && canvasSize ? calculateFitScale(contentSize, canvasSize) : 1;
@@ -22734,7 +22777,7 @@ function MermaidDiagramCanvas({
22734
22777
  setZoom(fitZoom);
22735
22778
  setPan({ x: 0, y: 0 });
22736
22779
  }, [fitZoom]);
22737
- useLayoutEffect4(() => {
22780
+ useLayoutEffect5(() => {
22738
22781
  if (viewMode !== "fit") return;
22739
22782
  setZoom(fitZoom);
22740
22783
  setPan({ x: 0, y: 0 });
@@ -22801,7 +22844,7 @@ function MermaidDiagramCanvas({
22801
22844
  selectedNodeId,
22802
22845
  selectedTextId
22803
22846
  ]);
22804
- useLayoutEffect4(() => {
22847
+ useLayoutEffect5(() => {
22805
22848
  const node2 = renamingNodeId ? model2?.nodes.find((candidate) => candidate.id === renamingNodeId) : null;
22806
22849
  const edge2 = renamingEdgeId ? model2?.edges.find((candidate) => candidate.id === renamingEdgeId) : null;
22807
22850
  const text = renamingTextId ? model2 && mermaidEditableTexts(model2).find((candidate) => candidate.id === renamingTextId) : null;
@@ -22814,7 +22857,7 @@ function MermaidDiagramCanvas({
22814
22857
  });
22815
22858
  return () => window.cancelAnimationFrame(frame);
22816
22859
  }, [model2, renamingEdgeId, renamingNodeId, renamingTextId]);
22817
- useLayoutEffect4(() => {
22860
+ useLayoutEffect5(() => {
22818
22861
  const root = svgRootRef.current;
22819
22862
  if (!root || !model2) {
22820
22863
  setSelectionAnchor(null);
@@ -23354,7 +23397,7 @@ function CanvasAction({
23354
23397
  }
23355
23398
 
23356
23399
  // src/mermaid/MermaidShapePalette.tsx
23357
- import { useEffect as useEffect28, useLayoutEffect as useLayoutEffect5, useMemo as useMemo29, useRef as useRef33, useState as useState37 } from "react";
23400
+ import { useEffect as useEffect28, useLayoutEffect as useLayoutEffect6, useMemo as useMemo29, useRef as useRef33, useState as useState37 } from "react";
23358
23401
  import { jsx as jsx47, jsxs as jsxs36 } from "react/jsx-runtime";
23359
23402
  var CATEGORIES = ["Basic", "Process", "Data", "Documents", "Symbols"];
23360
23403
  var PALETTE_WIDTH = 360;
@@ -23390,7 +23433,7 @@ function MermaidShapePalette({ selected, onPick, onClose }) {
23390
23433
  )
23391
23434
  })).filter((section) => section.shapes.length > 0);
23392
23435
  }, [query]);
23393
- useLayoutEffect5(() => {
23436
+ useLayoutEffect6(() => {
23394
23437
  const palette = ref.current;
23395
23438
  const anchor = palette?.parentElement;
23396
23439
  if (!palette || !anchor) return;
@@ -23549,7 +23592,7 @@ import { createRoot as createRoot3 } from "react-dom/client";
23549
23592
  import {
23550
23593
  useCallback as useCallback34,
23551
23594
  useEffect as useEffect29,
23552
- useLayoutEffect as useLayoutEffect6,
23595
+ useLayoutEffect as useLayoutEffect7,
23553
23596
  useRef as useRef34,
23554
23597
  useState as useState38,
23555
23598
  useSyncExternalStore
@@ -24443,7 +24486,7 @@ function useDismissibleMermaidPopover(ref, onClose) {
24443
24486
  }
24444
24487
  function useClampedMermaidPopoverPosition(ref, preferredWidth, preferredMaxHeight) {
24445
24488
  const [position, setPosition] = useState38(null);
24446
- useLayoutEffect6(() => {
24489
+ useLayoutEffect7(() => {
24447
24490
  const picker = ref.current;
24448
24491
  const anchor = picker?.parentElement;
24449
24492
  if (!picker || !anchor) return;
@@ -24975,19 +25018,21 @@ function CodeSnippetWidget({
24975
25018
 
24976
25019
  // src/codeSnippet/CodeSnippetExtension.ts
24977
25020
  var CODE_SNIPPET_KEY = new PluginKey6("squisq-code-snippet");
24978
- function isCodeSnippetNode(node2) {
25021
+ function isCodeSnippetNode(node2, reserved) {
24979
25022
  const language = node2.attrs.language;
24980
- return node2.type.name === "codeBlock" && isCodeSnippetFenceLanguage(typeof language === "string" ? language : null);
25023
+ const lang = typeof language === "string" ? language : null;
25024
+ if (node2.type.name !== "codeBlock" || !isCodeSnippetFenceLanguage(lang)) return false;
25025
+ return !reserved || !reserved.has(codeSnippetFenceLanguageToken(lang));
24981
25026
  }
24982
25027
  function findCodeSnippetBlockPos(editor, blockId) {
24983
25028
  const state = CODE_SNIPPET_KEY.getState(editor.state);
24984
25029
  return state?.entries.find((entry) => entry.id === blockId)?.pos ?? null;
24985
25030
  }
24986
- function buildDecorations5(doc, entries, editor, focusBlockId = null) {
25031
+ function buildDecorations5(doc, entries, editor, focusBlockId = null, reserved) {
24987
25032
  const decorations = [];
24988
25033
  for (const entry of entries) {
24989
25034
  const node2 = doc.nodeAt(entry.pos);
24990
- if (!node2 || !isCodeSnippetNode(node2)) continue;
25035
+ if (!node2 || !isCodeSnippetNode(node2, reserved)) continue;
24991
25036
  decorations.push(
24992
25037
  Decoration6.node(entry.pos, entry.pos + node2.nodeSize, {
24993
25038
  class: "squisq-code-snippet-fence-hidden"
@@ -25029,7 +25074,7 @@ function buildDecorations5(doc, entries, editor, focusBlockId = null) {
25029
25074
  }
25030
25075
  return DecorationSet6.create(doc, decorations);
25031
25076
  }
25032
- function remapEntries2(tr, previous, doc) {
25077
+ function remapEntries2(tr, previous, doc, reserved) {
25033
25078
  const mapped = /* @__PURE__ */ new Map();
25034
25079
  const claimed = /* @__PURE__ */ new Set();
25035
25080
  for (const entry of previous.entries) {
@@ -25052,22 +25097,22 @@ function remapEntries2(tr, previous, doc) {
25052
25097
  const entries = [];
25053
25098
  doc.descendants((node2, pos) => {
25054
25099
  if (node2.type.name !== "codeBlock") return;
25055
- if (!isCodeSnippetNode(node2)) return false;
25100
+ if (!isCodeSnippetNode(node2, reserved)) return false;
25056
25101
  entries.push({ id: mapped.get(pos) ?? `code-snippet-${++seq}`, pos });
25057
25102
  return false;
25058
25103
  });
25059
25104
  return { entries, seq };
25060
25105
  }
25061
- function applyState4(tr, previous, editor, doc) {
25106
+ function applyState4(tr, previous, editor, doc, reserved) {
25062
25107
  if (!tr.docChanged) return previous;
25063
- const { entries, seq } = remapEntries2(tr, previous, doc);
25108
+ const { entries, seq } = remapEntries2(tr, previous, doc, reserved);
25064
25109
  const requestedFocusPos = tr.getMeta(CODE_SNIPPET_FOCUS_INSERTED_META);
25065
25110
  const focusBlockId = typeof requestedFocusPos === "number" ? entries.find((entry) => entry.pos === requestedFocusPos)?.id ?? null : null;
25066
25111
  return {
25067
25112
  entries,
25068
25113
  seq,
25069
25114
  focusBlockId,
25070
- decorations: buildDecorations5(doc, entries, editor, focusBlockId)
25115
+ decorations: buildDecorations5(doc, entries, editor, focusBlockId, reserved)
25071
25116
  };
25072
25117
  }
25073
25118
  var CodeSnippetExtension = Extension6.create({
@@ -25078,6 +25123,7 @@ var CodeSnippetExtension = Extension6.create({
25078
25123
  addProseMirrorPlugins() {
25079
25124
  if (this.options.enabled === false) return [];
25080
25125
  const editor = this.editor;
25126
+ const reserved = this.options.reservedLanguages && this.options.reservedLanguages.length > 0 ? new Set(this.options.reservedLanguages.map((lang) => codeSnippetFenceLanguageToken(lang))) : void 0;
25081
25127
  return [
25082
25128
  new Plugin6({
25083
25129
  key: CODE_SNIPPET_KEY,
@@ -25087,7 +25133,7 @@ var CodeSnippetExtension = Extension6.create({
25087
25133
  const entries = [];
25088
25134
  state.doc.descendants((node2, pos) => {
25089
25135
  if (node2.type.name !== "codeBlock") return;
25090
- if (!isCodeSnippetNode(node2)) return false;
25136
+ if (!isCodeSnippetNode(node2, reserved)) return false;
25091
25137
  entries.push({ id: `code-snippet-${++seq}`, pos });
25092
25138
  return false;
25093
25139
  });
@@ -25095,10 +25141,10 @@ var CodeSnippetExtension = Extension6.create({
25095
25141
  entries,
25096
25142
  seq,
25097
25143
  focusBlockId: null,
25098
- decorations: buildDecorations5(state.doc, entries, editor)
25144
+ decorations: buildDecorations5(state.doc, entries, editor, null, reserved)
25099
25145
  };
25100
25146
  },
25101
- apply: (tr, previous, _oldState, newState) => applyState4(tr, previous, editor, newState.doc)
25147
+ apply: (tr, previous, _oldState, newState) => applyState4(tr, previous, editor, newState.doc, reserved)
25102
25148
  },
25103
25149
  props: {
25104
25150
  decorations(state) {
@@ -25124,15 +25170,188 @@ function replaceCodeSnippetText(editor, blockId, nextText) {
25124
25170
  return true;
25125
25171
  }
25126
25172
 
25127
- // src/treeview/treeViewData.ts
25128
- import { useEffect as useEffect33, useMemo as useMemo32, useState as useState43 } from "react";
25129
-
25130
- // src/treeview/TreeViewExtension.ts
25173
+ // src/fenceWidgets/HostFenceExtension.tsx
25131
25174
  import { Extension as Extension7 } from "@tiptap/core";
25132
25175
  import { Plugin as Plugin7, PluginKey as PluginKey7 } from "@tiptap/pm/state";
25133
25176
  import { Decoration as Decoration7, DecorationSet as DecorationSet7 } from "@tiptap/pm/view";
25134
25177
  import { createRoot as createRoot5 } from "react-dom/client";
25135
- import { createElement as createElement11 } from "react";
25178
+ import { Component, createElement as createElement11, useEffect as useEffect33, useMemo as useMemo32, useState as useState42 } from "react";
25179
+ import { jsx as jsx50 } from "react/jsx-runtime";
25180
+ var HOST_FENCE_KEY = new PluginKey7("squisq-host-fence");
25181
+ function fenceLangToken(node2) {
25182
+ const lang = node2.attrs.language;
25183
+ if (typeof lang !== "string") return "";
25184
+ return lang.trim().split(/\s+/, 1)[0]?.toLowerCase() ?? "";
25185
+ }
25186
+ function findHostFenceBlockPos(editor, blockId) {
25187
+ return HOST_FENCE_KEY.getState(editor.state)?.entries.find((e2) => e2.id === blockId)?.pos ?? null;
25188
+ }
25189
+ function replaceHostFenceText(editor, blockId, next) {
25190
+ const pos = findHostFenceBlockPos(editor, blockId);
25191
+ if (pos === null) return false;
25192
+ return replaceAsciiFenceText(editor, pos, next);
25193
+ }
25194
+ var HostFenceErrorBoundary = class extends Component {
25195
+ constructor() {
25196
+ super(...arguments);
25197
+ this.state = { failed: false };
25198
+ }
25199
+ static getDerivedStateFromError() {
25200
+ return { failed: true };
25201
+ }
25202
+ render() {
25203
+ return this.state.failed ? this.props.fallback : this.props.children;
25204
+ }
25205
+ };
25206
+ function HostFenceWidget({
25207
+ editor,
25208
+ blockId,
25209
+ getRenderers,
25210
+ getTheme
25211
+ }) {
25212
+ const [version, setVersion] = useState42(0);
25213
+ useEffect33(() => {
25214
+ const onUpdate = () => setVersion((v2) => v2 + 1);
25215
+ editor.on("transaction", onUpdate);
25216
+ return () => {
25217
+ editor.off("transaction", onUpdate);
25218
+ };
25219
+ }, [editor]);
25220
+ const current = useMemo32(() => {
25221
+ const pos = findHostFenceBlockPos(editor, blockId);
25222
+ if (pos === null) return null;
25223
+ const node2 = editor.state.doc.nodeAt(pos);
25224
+ if (!node2 || node2.type.name !== "codeBlock") return null;
25225
+ return { lang: fenceLangToken(node2), value: node2.textContent };
25226
+ }, [editor, blockId, version]);
25227
+ if (!current) return null;
25228
+ const renderer = getRenderers()?.[current.lang];
25229
+ const rawFallback = /* @__PURE__ */ jsx50("pre", { className: "squisq-md-code-block", children: /* @__PURE__ */ jsx50("code", { children: current.value }) });
25230
+ if (!renderer) return rawFallback;
25231
+ const theme = getTheme();
25232
+ return /* @__PURE__ */ jsx50(HostFenceErrorBoundary, { fallback: rawFallback, children: renderer({
25233
+ lang: current.lang,
25234
+ value: current.value,
25235
+ ...theme ? { theme } : {},
25236
+ mode: "edit",
25237
+ replaceValue: (next) => {
25238
+ replaceHostFenceText(editor, blockId, next);
25239
+ }
25240
+ }) });
25241
+ }
25242
+ function buildDecorations6(doc, entries, editor, options) {
25243
+ const decos = [];
25244
+ for (const entry of entries) {
25245
+ const node2 = doc.nodeAt(entry.pos);
25246
+ if (!node2 || node2.type.name !== "codeBlock") continue;
25247
+ decos.push(
25248
+ Decoration7.node(entry.pos, entry.pos + node2.nodeSize, {
25249
+ class: "squisq-host-fence-hidden"
25250
+ })
25251
+ );
25252
+ const blockId = entry.id;
25253
+ decos.push(
25254
+ Decoration7.widget(
25255
+ entry.pos + node2.nodeSize,
25256
+ () => {
25257
+ const container = document.createElement("div");
25258
+ container.className = "squisq-host-fence-widget-host";
25259
+ container.contentEditable = "false";
25260
+ containFenceWidgetEvents(container);
25261
+ const root = createRoot5(container);
25262
+ root.render(
25263
+ createElement11(HostFenceWidget, {
25264
+ editor,
25265
+ blockId,
25266
+ getRenderers: () => options.renderers?.(),
25267
+ getTheme: () => options.theme?.()
25268
+ })
25269
+ );
25270
+ container.__squisqHostFenceRoot = { root };
25271
+ return container;
25272
+ },
25273
+ {
25274
+ destroy: (dom) => {
25275
+ const ref = dom.__squisqHostFenceRoot;
25276
+ if (ref) setTimeout(() => ref.root.unmount(), 0);
25277
+ },
25278
+ side: 1,
25279
+ ignoreSelection: true,
25280
+ key: `squisq-host-fence-${entry.id}`
25281
+ }
25282
+ )
25283
+ );
25284
+ }
25285
+ return DecorationSet7.create(doc, decos);
25286
+ }
25287
+ function claims(node2, options) {
25288
+ if (node2.type.name !== "codeBlock") return false;
25289
+ const lang = fenceLangToken(node2);
25290
+ if (!lang) return false;
25291
+ return Boolean(options.renderers?.()?.[lang]);
25292
+ }
25293
+ function applyState5(tr, prev, editor, doc, options) {
25294
+ if (!tr.docChanged) return prev;
25295
+ const mapped = mapFenceEntries(tr, prev.entries, doc);
25296
+ let seq = prev.seq;
25297
+ const entries = [];
25298
+ doc.descendants((node2, pos) => {
25299
+ if (node2.type.name !== "codeBlock") return;
25300
+ if (!claims(node2, options)) return false;
25301
+ entries.push({ id: mapped.get(pos) ?? `host-fence-${++seq}`, pos });
25302
+ return false;
25303
+ });
25304
+ return { entries, seq, decorations: buildDecorations6(doc, entries, editor, options) };
25305
+ }
25306
+ var HostFenceExtension = Extension7.create({
25307
+ name: "squisqHostFence",
25308
+ addOptions() {
25309
+ return { enabled: true };
25310
+ },
25311
+ addProseMirrorPlugins() {
25312
+ const editor = this.editor;
25313
+ const options = this.options;
25314
+ if (options.enabled === false || !options.renderers) return [];
25315
+ return [
25316
+ new Plugin7({
25317
+ key: HOST_FENCE_KEY,
25318
+ state: {
25319
+ init: (_config, state) => {
25320
+ let seq = 0;
25321
+ const entries = [];
25322
+ state.doc.descendants((node2, pos) => {
25323
+ if (node2.type.name !== "codeBlock") return;
25324
+ if (!claims(node2, options)) return false;
25325
+ entries.push({ id: `host-fence-${++seq}`, pos });
25326
+ return false;
25327
+ });
25328
+ return {
25329
+ entries,
25330
+ seq,
25331
+ decorations: buildDecorations6(state.doc, entries, editor, options)
25332
+ };
25333
+ },
25334
+ apply: (tr, prev, _oldState, newState) => applyState5(tr, prev, editor, newState.doc, options)
25335
+ },
25336
+ props: {
25337
+ decorations(state) {
25338
+ return this.getState(state)?.decorations ?? DecorationSet7.empty;
25339
+ }
25340
+ }
25341
+ })
25342
+ ];
25343
+ }
25344
+ });
25345
+
25346
+ // src/treeview/treeViewData.ts
25347
+ import { useEffect as useEffect34, useMemo as useMemo33, useState as useState44 } from "react";
25348
+
25349
+ // src/treeview/TreeViewExtension.ts
25350
+ import { Extension as Extension8 } from "@tiptap/core";
25351
+ import { Plugin as Plugin8, PluginKey as PluginKey8 } from "@tiptap/pm/state";
25352
+ import { Decoration as Decoration8, DecorationSet as DecorationSet8 } from "@tiptap/pm/view";
25353
+ import { createRoot as createRoot6 } from "react-dom/client";
25354
+ import { createElement as createElement12 } from "react";
25136
25355
  import {
25137
25356
  detectTree,
25138
25357
  isEligibleTreeFenceLang,
@@ -25142,7 +25361,7 @@ import {
25142
25361
  } from "@bendyline/squisq/doc";
25143
25362
 
25144
25363
  // src/treeview/TreeOutlineWidget.tsx
25145
- import { useCallback as useCallback36, useRef as useRef35, useState as useState42 } from "react";
25364
+ import { useCallback as useCallback36, useRef as useRef35, useState as useState43 } from "react";
25146
25365
 
25147
25366
  // src/treeview/treeViewCommands.ts
25148
25367
  import { parseTree, renderTree } from "@bendyline/squisq/doc";
@@ -25352,7 +25571,7 @@ function applyTreeCommand(editor, blockId, cmd) {
25352
25571
  }
25353
25572
 
25354
25573
  // src/treeview/TreeOutlineWidget.tsx
25355
- import { jsx as jsx50, jsxs as jsxs39 } from "react/jsx-runtime";
25574
+ import { jsx as jsx51, jsxs as jsxs39 } from "react/jsx-runtime";
25356
25575
  var TREE_DRAG_MIME = "application/x-squisq-tree-node";
25357
25576
  function findNode(nodes, id) {
25358
25577
  for (const node2 of nodes) {
@@ -25379,10 +25598,10 @@ function dropPositionForPointer(event) {
25379
25598
  }
25380
25599
  function TreeOutlineWidget({ editor, blockId }) {
25381
25600
  const view = useTreeViewData(editor, blockId);
25382
- const [collapsed, setCollapsed] = useState42(() => /* @__PURE__ */ new Set());
25601
+ const [collapsed, setCollapsed] = useState43(() => /* @__PURE__ */ new Set());
25383
25602
  const activeDragRef = useRef35(null);
25384
- const [draggedId, setDraggedId] = useState42(null);
25385
- const [dropTarget, setDropTarget] = useState42(null);
25603
+ const [draggedId, setDraggedId] = useState43(null);
25604
+ const [dropTarget, setDropTarget] = useState43(null);
25386
25605
  const dispatch = useCallback36(
25387
25606
  (cmd) => applyTreeCommand(editor, blockId, cmd),
25388
25607
  [editor, blockId]
@@ -25464,7 +25683,7 @@ function TreeOutlineWidget({ editor, blockId }) {
25464
25683
  disabled: !firstRootId,
25465
25684
  onClick: () => firstRootId ? dispatch({ kind: "addItem", targetId: firstRootId, position: "siblingAfter" }) : void 0,
25466
25685
  children: [
25467
- /* @__PURE__ */ jsx50(Icon, { icon: "fa-solid fa-plus" }),
25686
+ /* @__PURE__ */ jsx51(Icon, { icon: "fa-solid fa-plus" }),
25468
25687
  " Item"
25469
25688
  ]
25470
25689
  }
@@ -25483,13 +25702,13 @@ function TreeOutlineWidget({ editor, blockId }) {
25483
25702
  isDir: true
25484
25703
  }) : void 0,
25485
25704
  children: [
25486
- /* @__PURE__ */ jsx50(Icon, { icon: "fa-solid fa-folder-plus" }),
25705
+ /* @__PURE__ */ jsx51(Icon, { icon: "fa-solid fa-folder-plus" }),
25487
25706
  " Folder"
25488
25707
  ]
25489
25708
  }
25490
25709
  )
25491
25710
  ] }),
25492
- /* @__PURE__ */ jsx50("ul", { className: "squisq-tree-rows", role: "tree", children: roots.map((node2) => /* @__PURE__ */ jsx50(
25711
+ /* @__PURE__ */ jsx51("ul", { className: "squisq-tree-rows", role: "tree", children: roots.map((node2) => /* @__PURE__ */ jsx51(
25493
25712
  TreeRowView,
25494
25713
  {
25495
25714
  node: node2,
@@ -25529,7 +25748,7 @@ function TreeRowView({
25529
25748
  const hasChildren = node2.children.length > 0;
25530
25749
  const isCollapsed = collapsed.has(node2.id);
25531
25750
  const isDir = node2.isDir || hasChildren;
25532
- const [draft, setDraft] = useState42(node2.label);
25751
+ const [draft, setDraft] = useState43(node2.label);
25533
25752
  const dropPosition = dropTarget?.id === node2.id ? dropTarget.position : null;
25534
25753
  const itemClassName = [
25535
25754
  "squisq-tree-item",
@@ -25549,27 +25768,27 @@ function TreeRowView({
25549
25768
  onDragOver: (event) => onDragOver(event, node2.id),
25550
25769
  onDrop: (event) => onDrop(event, node2.id),
25551
25770
  children: [
25552
- hasChildren ? /* @__PURE__ */ jsx50(
25771
+ hasChildren ? /* @__PURE__ */ jsx51(
25553
25772
  "button",
25554
25773
  {
25555
25774
  type: "button",
25556
25775
  className: "squisq-tree-chevron",
25557
25776
  "aria-label": isCollapsed ? "Expand" : "Collapse",
25558
25777
  onClick: () => toggleCollapse(node2.id),
25559
- children: /* @__PURE__ */ jsx50(Icon, { icon: `fa-solid ${isCollapsed ? "fa-chevron-right" : "fa-chevron-down"}` })
25778
+ children: /* @__PURE__ */ jsx51(Icon, { icon: `fa-solid ${isCollapsed ? "fa-chevron-right" : "fa-chevron-down"}` })
25560
25779
  }
25561
- ) : /* @__PURE__ */ jsx50("span", { className: "squisq-tree-chevron squisq-tree-chevron--empty" }),
25562
- /* @__PURE__ */ jsx50(
25780
+ ) : /* @__PURE__ */ jsx51("span", { className: "squisq-tree-chevron squisq-tree-chevron--empty" }),
25781
+ /* @__PURE__ */ jsx51(
25563
25782
  "button",
25564
25783
  {
25565
25784
  type: "button",
25566
25785
  className: "squisq-tree-icon",
25567
25786
  title: isDir ? "Make a file" : "Make a folder",
25568
25787
  onClick: () => dispatch({ kind: "toggleDir", id: node2.id }),
25569
- children: /* @__PURE__ */ jsx50(Icon, { icon: `fa-solid ${isDir ? "fa-folder" : "fa-file"}` })
25788
+ children: /* @__PURE__ */ jsx51(Icon, { icon: `fa-solid ${isDir ? "fa-folder" : "fa-file"}` })
25570
25789
  }
25571
25790
  ),
25572
- /* @__PURE__ */ jsx50(
25791
+ /* @__PURE__ */ jsx51(
25573
25792
  "input",
25574
25793
  {
25575
25794
  className: "squisq-tree-label",
@@ -25593,7 +25812,7 @@ function TreeRowView({
25593
25812
  }
25594
25813
  ),
25595
25814
  /* @__PURE__ */ jsxs39("span", { className: "squisq-tree-controls", children: [
25596
- /* @__PURE__ */ jsx50(
25815
+ /* @__PURE__ */ jsx51(
25597
25816
  "span",
25598
25817
  {
25599
25818
  className: "squisq-tree-drag-handle",
@@ -25602,69 +25821,69 @@ function TreeRowView({
25602
25821
  title: `Drag ${node2.label} to move`,
25603
25822
  onDragStart: (event) => onDragStart(event, node2.id),
25604
25823
  onDragEnd,
25605
- children: /* @__PURE__ */ jsx50(Icon, { icon: "fa-solid fa-grip-vertical" })
25824
+ children: /* @__PURE__ */ jsx51(Icon, { icon: "fa-solid fa-grip-vertical" })
25606
25825
  }
25607
25826
  ),
25608
- /* @__PURE__ */ jsx50(
25827
+ /* @__PURE__ */ jsx51(
25609
25828
  "button",
25610
25829
  {
25611
25830
  type: "button",
25612
25831
  title: "Add child",
25613
25832
  onClick: () => dispatch({ kind: "addItem", targetId: node2.id, position: "child" }),
25614
- children: /* @__PURE__ */ jsx50(Icon, { icon: "fa-solid fa-plus" })
25833
+ children: /* @__PURE__ */ jsx51(Icon, { icon: "fa-solid fa-plus" })
25615
25834
  }
25616
25835
  ),
25617
- /* @__PURE__ */ jsx50(
25836
+ /* @__PURE__ */ jsx51(
25618
25837
  "button",
25619
25838
  {
25620
25839
  type: "button",
25621
25840
  title: "Outdent",
25622
25841
  onClick: () => dispatch({ kind: "outdentItem", id: node2.id }),
25623
- children: /* @__PURE__ */ jsx50(Icon, { icon: "fa-solid fa-outdent" })
25842
+ children: /* @__PURE__ */ jsx51(Icon, { icon: "fa-solid fa-outdent" })
25624
25843
  }
25625
25844
  ),
25626
- /* @__PURE__ */ jsx50(
25845
+ /* @__PURE__ */ jsx51(
25627
25846
  "button",
25628
25847
  {
25629
25848
  type: "button",
25630
25849
  title: "Indent",
25631
25850
  onClick: () => dispatch({ kind: "indentItem", id: node2.id }),
25632
- children: /* @__PURE__ */ jsx50(Icon, { icon: "fa-solid fa-indent" })
25851
+ children: /* @__PURE__ */ jsx51(Icon, { icon: "fa-solid fa-indent" })
25633
25852
  }
25634
25853
  ),
25635
- /* @__PURE__ */ jsx50(
25854
+ /* @__PURE__ */ jsx51(
25636
25855
  "button",
25637
25856
  {
25638
25857
  type: "button",
25639
25858
  title: "Move up",
25640
25859
  onClick: () => dispatch({ kind: "moveItemUp", id: node2.id }),
25641
- children: /* @__PURE__ */ jsx50(Icon, { icon: "fa-solid fa-arrow-up" })
25860
+ children: /* @__PURE__ */ jsx51(Icon, { icon: "fa-solid fa-arrow-up" })
25642
25861
  }
25643
25862
  ),
25644
- /* @__PURE__ */ jsx50(
25863
+ /* @__PURE__ */ jsx51(
25645
25864
  "button",
25646
25865
  {
25647
25866
  type: "button",
25648
25867
  title: "Move down",
25649
25868
  onClick: () => dispatch({ kind: "moveItemDown", id: node2.id }),
25650
- children: /* @__PURE__ */ jsx50(Icon, { icon: "fa-solid fa-arrow-down" })
25869
+ children: /* @__PURE__ */ jsx51(Icon, { icon: "fa-solid fa-arrow-down" })
25651
25870
  }
25652
25871
  ),
25653
- /* @__PURE__ */ jsx50(
25872
+ /* @__PURE__ */ jsx51(
25654
25873
  "button",
25655
25874
  {
25656
25875
  type: "button",
25657
25876
  title: "Delete",
25658
25877
  className: "squisq-tree-delete",
25659
25878
  onClick: () => dispatch({ kind: "removeItem", id: node2.id }),
25660
- children: /* @__PURE__ */ jsx50(Icon, { icon: "fa-solid fa-trash" })
25879
+ children: /* @__PURE__ */ jsx51(Icon, { icon: "fa-solid fa-trash" })
25661
25880
  }
25662
25881
  )
25663
25882
  ] })
25664
25883
  ]
25665
25884
  }
25666
25885
  ),
25667
- hasChildren && !isCollapsed ? /* @__PURE__ */ jsx50("ul", { className: "squisq-tree-rows", role: "group", children: node2.children.map((child) => /* @__PURE__ */ jsx50(
25886
+ hasChildren && !isCollapsed ? /* @__PURE__ */ jsx51("ul", { className: "squisq-tree-rows", role: "group", children: node2.children.map((child) => /* @__PURE__ */ jsx51(
25668
25887
  TreeRowView,
25669
25888
  {
25670
25889
  node: child,
@@ -25685,7 +25904,7 @@ function TreeRowView({
25685
25904
  }
25686
25905
 
25687
25906
  // src/treeview/TreeViewExtension.ts
25688
- var TREEVIEW_KEY = new PluginKey7("squisq-treeview");
25907
+ var TREEVIEW_KEY = new PluginKey8("squisq-treeview");
25689
25908
  var detectCache2 = /* @__PURE__ */ new WeakMap();
25690
25909
  var parseCache2 = /* @__PURE__ */ new WeakMap();
25691
25910
  function fenceLangOf3(node2) {
@@ -25731,27 +25950,27 @@ function parseTreeForNode(node2) {
25731
25950
  function findTreeBlockPos(editor, blockId) {
25732
25951
  return TREEVIEW_KEY.getState(editor.state)?.entries.find((e2) => e2.id === blockId)?.pos ?? null;
25733
25952
  }
25734
- function buildDecorations6(doc, entries, editor) {
25953
+ function buildDecorations7(doc, entries, editor) {
25735
25954
  const decos = [];
25736
25955
  for (const entry of entries) {
25737
25956
  const node2 = doc.nodeAt(entry.pos);
25738
25957
  if (!node2 || node2.type.name !== "codeBlock") continue;
25739
25958
  decos.push(
25740
- Decoration7.node(entry.pos, entry.pos + node2.nodeSize, { class: "squisq-tree-fence-hidden" })
25959
+ Decoration8.node(entry.pos, entry.pos + node2.nodeSize, { class: "squisq-tree-fence-hidden" })
25741
25960
  );
25742
25961
  const blockId = entry.id;
25743
25962
  const fallbackPos = entry.pos;
25744
25963
  decos.push(
25745
- Decoration7.widget(
25964
+ Decoration8.widget(
25746
25965
  entry.pos + node2.nodeSize,
25747
25966
  (view) => {
25748
25967
  const container = document.createElement("div");
25749
25968
  container.className = "squisq-tree-widget-host";
25750
25969
  container.contentEditable = "false";
25751
25970
  containFenceWidgetEvents(container);
25752
- const root = createRoot5(container);
25971
+ const root = createRoot6(container);
25753
25972
  root.render(
25754
- createElement11(TreeOutlineWidget, {
25973
+ createElement12(TreeOutlineWidget, {
25755
25974
  editor,
25756
25975
  blockId,
25757
25976
  fallbackPos,
@@ -25775,9 +25994,9 @@ function buildDecorations6(doc, entries, editor) {
25775
25994
  )
25776
25995
  );
25777
25996
  }
25778
- return DecorationSet7.create(doc, decos);
25997
+ return DecorationSet8.create(doc, decos);
25779
25998
  }
25780
- function applyState5(tr, prev, editor, doc) {
25999
+ function applyState6(tr, prev, editor, doc) {
25781
26000
  if (!tr.docChanged) return prev;
25782
26001
  const mapped = mapFenceEntries(tr, prev.entries, doc);
25783
26002
  let seq = prev.seq;
@@ -25790,9 +26009,9 @@ function applyState5(tr, prev, editor, doc) {
25790
26009
  entries.push({ id: knownId ?? `tree-${++seq}`, pos });
25791
26010
  return false;
25792
26011
  });
25793
- return { entries, seq, decorations: buildDecorations6(doc, entries, editor) };
26012
+ return { entries, seq, decorations: buildDecorations7(doc, entries, editor) };
25794
26013
  }
25795
- var TreeViewExtension = Extension7.create({
26014
+ var TreeViewExtension = Extension8.create({
25796
26015
  name: "squisqTreeView",
25797
26016
  addOptions() {
25798
26017
  return { enabled: true };
@@ -25801,7 +26020,7 @@ var TreeViewExtension = Extension7.create({
25801
26020
  const editor = this.editor;
25802
26021
  if (this.options.enabled === false) return [];
25803
26022
  return [
25804
- new Plugin7({
26023
+ new Plugin8({
25805
26024
  key: TREEVIEW_KEY,
25806
26025
  state: {
25807
26026
  init: (_config, state) => {
@@ -25816,14 +26035,14 @@ var TreeViewExtension = Extension7.create({
25816
26035
  return {
25817
26036
  entries,
25818
26037
  seq,
25819
- decorations: buildDecorations6(state.doc, entries, editor)
26038
+ decorations: buildDecorations7(state.doc, entries, editor)
25820
26039
  };
25821
26040
  },
25822
- apply: (tr, prev, _oldState, newState) => applyState5(tr, prev, editor, newState.doc)
26041
+ apply: (tr, prev, _oldState, newState) => applyState6(tr, prev, editor, newState.doc)
25823
26042
  },
25824
26043
  props: {
25825
26044
  decorations(state) {
25826
- return this.getState(state)?.decorations ?? DecorationSet7.empty;
26045
+ return this.getState(state)?.decorations ?? DecorationSet8.empty;
25827
26046
  }
25828
26047
  }
25829
26048
  })
@@ -25833,15 +26052,15 @@ var TreeViewExtension = Extension7.create({
25833
26052
 
25834
26053
  // src/treeview/treeViewData.ts
25835
26054
  function useTreeViewData(editor, blockId) {
25836
- const [version, setVersion] = useState43(0);
25837
- useEffect33(() => {
26055
+ const [version, setVersion] = useState44(0);
26056
+ useEffect34(() => {
25838
26057
  const onUpdate = () => setVersion((v2) => v2 + 1);
25839
26058
  editor.on("transaction", onUpdate);
25840
26059
  return () => {
25841
26060
  editor.off("transaction", onUpdate);
25842
26061
  };
25843
26062
  }, [editor]);
25844
- return useMemo32(() => {
26063
+ return useMemo33(() => {
25845
26064
  const pos = findTreeBlockPos(editor, blockId);
25846
26065
  if (pos === null) return null;
25847
26066
  const node2 = editor.state.doc.nodeAt(pos);
@@ -25860,14 +26079,14 @@ function shouldPasteAsTreeFence(text) {
25860
26079
  }
25861
26080
 
25862
26081
  // src/timeline/timelineData.ts
25863
- import { useEffect as useEffect35, useMemo as useMemo34, useRef as useRef37, useState as useState45 } from "react";
26082
+ import { useEffect as useEffect36, useMemo as useMemo35, useRef as useRef37, useState as useState46 } from "react";
25864
26083
 
25865
26084
  // src/timeline/TimelineViewExtension.ts
25866
- import { Extension as Extension8 } from "@tiptap/core";
25867
- import { Plugin as Plugin8, PluginKey as PluginKey8 } from "@tiptap/pm/state";
25868
- import { Decoration as Decoration8, DecorationSet as DecorationSet8 } from "@tiptap/pm/view";
25869
- import { createElement as createElement12 } from "react";
25870
- import { createRoot as createRoot6 } from "react-dom/client";
26085
+ import { Extension as Extension9 } from "@tiptap/core";
26086
+ import { Plugin as Plugin9, PluginKey as PluginKey9 } from "@tiptap/pm/state";
26087
+ import { Decoration as Decoration9, DecorationSet as DecorationSet9 } from "@tiptap/pm/view";
26088
+ import { createElement as createElement13 } from "react";
26089
+ import { createRoot as createRoot7 } from "react-dom/client";
25871
26090
  import {
25872
26091
  detectAsciiTimeline,
25873
26092
  isEligibleAsciiTimelineFenceLang,
@@ -25875,7 +26094,7 @@ import {
25875
26094
  } from "@bendyline/squisq/doc";
25876
26095
 
25877
26096
  // src/timeline/TimelineEditorWidget.tsx
25878
- import { useCallback as useCallback37, useEffect as useEffect34, useMemo as useMemo33, useRef as useRef36, useState as useState44 } from "react";
26097
+ import { useCallback as useCallback37, useEffect as useEffect35, useMemo as useMemo34, useRef as useRef36, useState as useState45 } from "react";
25879
26098
  import {
25880
26099
  asciiTimelineToTemplateData
25881
26100
  } from "@bendyline/squisq/doc";
@@ -26355,17 +26574,17 @@ function applyTimelineCommand(editor, blockId, command) {
26355
26574
  }
26356
26575
 
26357
26576
  // src/timeline/TimelineEditorWidget.tsx
26358
- import { Fragment as Fragment15, jsx as jsx51, jsxs as jsxs40 } from "react/jsx-runtime";
26577
+ import { Fragment as Fragment15, jsx as jsx52, jsxs as jsxs40 } from "react/jsx-runtime";
26359
26578
  var clamp01 = (value) => Math.max(0, Math.min(1, value));
26360
26579
  function TimelineEditorWidget({ editor, blockId }) {
26361
26580
  const view = useTimelineData(editor, blockId);
26362
- const [selectedEventId, setSelectedEventId] = useState44(null);
26363
- const [addingTrackId, setAddingTrackId] = useState44(null);
26364
- const [editingTrackId, setEditingTrackId] = useState44(null);
26365
- const [hover, setHover] = useState44(null);
26366
- const [dragged, setDragged] = useState44(null);
26581
+ const [selectedEventId, setSelectedEventId] = useState45(null);
26582
+ const [addingTrackId, setAddingTrackId] = useState45(null);
26583
+ const [editingTrackId, setEditingTrackId] = useState45(null);
26584
+ const [hover, setHover] = useState45(null);
26585
+ const [dragged, setDragged] = useState45(null);
26367
26586
  const suppressClickEventId = useRef36(null);
26368
- const [announcement, setAnnouncement] = useState44("");
26587
+ const [announcement, setAnnouncement] = useState45("");
26369
26588
  const dispatch = useCallback37(
26370
26589
  (command) => {
26371
26590
  const result = applyTimelineCommand(editor, blockId, command);
@@ -26379,7 +26598,7 @@ function TimelineEditorWidget({ editor, blockId }) {
26379
26598
  );
26380
26599
  const sourceText2 = view?.text;
26381
26600
  const sourceTimeline = view?.timeline;
26382
- const sourceSafe = useMemo33(
26601
+ const sourceSafe = useMemo34(
26383
26602
  () => sourceText2 && sourceTimeline ? isTimelineSourceSafeForSemanticEdit(sourceText2, sourceTimeline) : false,
26384
26603
  // `useTimelineData` publishes on every editor transaction so read-only
26385
26604
  // changes are observed. Unrelated transactions retain both primitive/model
@@ -26389,7 +26608,7 @@ function TimelineEditorWidget({ editor, blockId }) {
26389
26608
  );
26390
26609
  const editorEditable = editor.isEditable;
26391
26610
  const editable = editorEditable && sourceSafe;
26392
- const positioned = useMemo33(() => {
26611
+ const positioned = useMemo34(() => {
26393
26612
  if (!view) return [];
26394
26613
  const normalized = asciiTimelineToTemplateData(view.timeline).tracks;
26395
26614
  return view.timeline.tracks.flatMap((track, trackIndex) => {
@@ -26404,17 +26623,17 @@ function TimelineEditorWidget({ editor, blockId }) {
26404
26623
  );
26405
26624
  });
26406
26625
  }, [view]);
26407
- const displayedPositioned = useMemo33(
26626
+ const displayedPositioned = useMemo34(
26408
26627
  () => dragged ? positioned.map(
26409
26628
  (point) => point.event.id === dragged.eventId ? { ...point, position: dragged.position } : point
26410
26629
  ) : positioned,
26411
26630
  [dragged, positioned]
26412
26631
  );
26413
- const positionById = useMemo33(
26632
+ const positionById = useMemo34(
26414
26633
  () => new Map(displayedPositioned.map((point) => [point.event.id, point.position])),
26415
26634
  [displayedPositioned]
26416
26635
  );
26417
- const selected = useMemo33(() => {
26636
+ const selected = useMemo34(() => {
26418
26637
  if (!view || !selectedEventId) return null;
26419
26638
  for (const track of view.timeline.tracks) {
26420
26639
  const event = track.events.find((candidate) => candidate.id === selectedEventId);
@@ -26428,7 +26647,7 @@ function TimelineEditorWidget({ editor, blockId }) {
26428
26647
  }
26429
26648
  return null;
26430
26649
  }, [selectedEventId, view]);
26431
- useEffect34(() => {
26650
+ useEffect35(() => {
26432
26651
  if (!view) return;
26433
26652
  const ids = new Set(
26434
26653
  view.timeline.tracks.flatMap((track) => track.events.map((event) => event.id))
@@ -26436,13 +26655,13 @@ function TimelineEditorWidget({ editor, blockId }) {
26436
26655
  if (selectedEventId && ids.has(selectedEventId)) return;
26437
26656
  setSelectedEventId(view.timeline.tracks[0]?.events[0]?.id ?? null);
26438
26657
  }, [selectedEventId, view]);
26439
- useEffect34(() => {
26658
+ useEffect35(() => {
26440
26659
  if (editable) return;
26441
26660
  setAddingTrackId(null);
26442
26661
  setEditingTrackId(null);
26443
26662
  setHover(null);
26444
26663
  }, [editable]);
26445
- useEffect34(() => {
26664
+ useEffect35(() => {
26446
26665
  if (!editingTrackId || view?.timeline.tracks.some((track) => track.id === editingTrackId)) {
26447
26666
  return;
26448
26667
  }
@@ -26509,13 +26728,13 @@ function TimelineEditorWidget({ editor, blockId }) {
26509
26728
  children: [
26510
26729
  /* @__PURE__ */ jsxs40("header", { className: "squisq-ascii-timeline-header", children: [
26511
26730
  /* @__PURE__ */ jsxs40("span", { children: [
26512
- /* @__PURE__ */ jsx51(Icon, { icon: "fa-solid fa-timeline" }),
26731
+ /* @__PURE__ */ jsx52(Icon, { icon: "fa-solid fa-timeline" }),
26513
26732
  " Timeline"
26514
26733
  ] }),
26515
26734
  /* @__PURE__ */ jsxs40("div", { className: "squisq-ascii-timeline-header-actions", children: [
26516
- editable && addingTrackId ? /* @__PURE__ */ jsx51("small", { children: "Click the line to add a point \xB7 Esc to cancel" }) : editable ? /* @__PURE__ */ jsx51("small", { children: "Use + to add a point \xB7 Drag dots to move" }) : editorEditable ? /* @__PURE__ */ jsx51("small", { children: "Source repair needed" }) : /* @__PURE__ */ jsx51("small", { children: "Read only" }),
26735
+ editable && addingTrackId ? /* @__PURE__ */ jsx52("small", { children: "Click the line to add a point \xB7 Esc to cancel" }) : editable ? /* @__PURE__ */ jsx52("small", { children: "Use + to add a point \xB7 Drag dots to move" }) : editorEditable ? /* @__PURE__ */ jsx52("small", { children: "Source repair needed" }) : /* @__PURE__ */ jsx52("small", { children: "Read only" }),
26517
26736
  editable ? /* @__PURE__ */ jsxs40("button", { type: "button", onClick: addTrack, children: [
26518
- /* @__PURE__ */ jsx51(Icon, { icon: "fa-solid fa-plus" }),
26737
+ /* @__PURE__ */ jsx52(Icon, { icon: "fa-solid fa-plus" }),
26519
26738
  " Add line"
26520
26739
  ] }) : null
26521
26740
  ] })
@@ -26526,7 +26745,7 @@ function TimelineEditorWidget({ editor, blockId }) {
26526
26745
  className: "squisq-ascii-timeline-canvas",
26527
26746
  style: { "--squisq-ascii-timeline-track-count": view.timeline.tracks.length },
26528
26747
  children: [
26529
- view.timeline.links.length > 0 ? /* @__PURE__ */ jsx51(
26748
+ view.timeline.links.length > 0 ? /* @__PURE__ */ jsx52(
26530
26749
  "svg",
26531
26750
  {
26532
26751
  className: "squisq-ascii-timeline-branches",
@@ -26543,7 +26762,7 @@ function TimelineEditorWidget({ editor, blockId }) {
26543
26762
  const ty = target.trackIndex * 96 + 48;
26544
26763
  const sameTrack = source.trackIndex === target.trackIndex;
26545
26764
  const controlY = sameTrack ? sy + (source.event.side === "below" ? -30 : 30) : (sy + ty) / 2;
26546
- return /* @__PURE__ */ jsx51(
26765
+ return /* @__PURE__ */ jsx52(
26547
26766
  "path",
26548
26767
  {
26549
26768
  d: `M ${sx} ${sy} C ${sx} ${controlY}, ${tx} ${controlY}, ${tx} ${ty}`,
@@ -26562,7 +26781,7 @@ function TimelineEditorWidget({ editor, blockId }) {
26562
26781
  const addingPoint = editable && addingTrackId === track.id;
26563
26782
  return /* @__PURE__ */ jsxs40("div", { className: "squisq-ascii-timeline-track", children: [
26564
26783
  /* @__PURE__ */ jsxs40("div", { className: "squisq-ascii-timeline-track-label", children: [
26565
- /* @__PURE__ */ jsx51(
26784
+ /* @__PURE__ */ jsx52(
26566
26785
  InlineTrackLabel,
26567
26786
  {
26568
26787
  trackId: track.id,
@@ -26579,7 +26798,7 @@ function TimelineEditorWidget({ editor, blockId }) {
26579
26798
  }
26580
26799
  ),
26581
26800
  editable ? /* @__PURE__ */ jsxs40(Fragment15, { children: [
26582
- /* @__PURE__ */ jsx51(
26801
+ /* @__PURE__ */ jsx52(
26583
26802
  "button",
26584
26803
  {
26585
26804
  type: "button",
@@ -26588,10 +26807,10 @@ function TimelineEditorWidget({ editor, blockId }) {
26588
26807
  disabled: view.timeline.tracks.length <= 1,
26589
26808
  title: view.timeline.tracks.length > 1 ? `Delete ${trackLabel} line and all of its points` : "A timeline needs one line",
26590
26809
  onClick: () => deleteTrack(track.id, trackLabel),
26591
- children: /* @__PURE__ */ jsx51(Icon, { icon: "fa-solid fa-trash" })
26810
+ children: /* @__PURE__ */ jsx52(Icon, { icon: "fa-solid fa-trash" })
26592
26811
  }
26593
26812
  ),
26594
- /* @__PURE__ */ jsx51(
26813
+ /* @__PURE__ */ jsx52(
26595
26814
  "button",
26596
26815
  {
26597
26816
  type: "button",
@@ -26636,8 +26855,8 @@ function TimelineEditorWidget({ editor, blockId }) {
26636
26855
  addEvent(track.id, (event.clientX - rect.left) / rect.width);
26637
26856
  },
26638
26857
  children: [
26639
- /* @__PURE__ */ jsx51("span", { className: "squisq-ascii-timeline-rail-line", "aria-hidden": "true" }),
26640
- addingPoint && hover?.trackId === track.id && !dragged ? /* @__PURE__ */ jsx51(
26858
+ /* @__PURE__ */ jsx52("span", { className: "squisq-ascii-timeline-rail-line", "aria-hidden": "true" }),
26859
+ addingPoint && hover?.trackId === track.id && !dragged ? /* @__PURE__ */ jsx52(
26641
26860
  "span",
26642
26861
  {
26643
26862
  className: "squisq-ascii-timeline-add-ghost",
@@ -26646,7 +26865,7 @@ function TimelineEditorWidget({ editor, blockId }) {
26646
26865
  children: "+"
26647
26866
  }
26648
26867
  ) : null,
26649
- addingPoint && !dragged ? gaps.map((position) => /* @__PURE__ */ jsx51(
26868
+ addingPoint && !dragged ? gaps.map((position) => /* @__PURE__ */ jsx52(
26650
26869
  "button",
26651
26870
  {
26652
26871
  type: "button",
@@ -26673,21 +26892,21 @@ function TimelineEditorWidget({ editor, blockId }) {
26673
26892
  className: `squisq-ascii-timeline-point squisq-ascii-timeline-point--${side}${draggedPoint ? " squisq-ascii-timeline-point--dragging" : ""}`,
26674
26893
  style: { left: `${position * 100}%` },
26675
26894
  children: [
26676
- event.callout !== false ? /* @__PURE__ */ jsx51(
26895
+ event.callout !== false ? /* @__PURE__ */ jsx52(
26677
26896
  "span",
26678
26897
  {
26679
26898
  className: `squisq-ascii-timeline-callout squisq-ascii-timeline-callout--${side}`,
26680
26899
  children: event.label
26681
26900
  }
26682
26901
  ) : null,
26683
- event.callout !== false && event.description ? /* @__PURE__ */ jsx51(
26902
+ event.callout !== false && event.description ? /* @__PURE__ */ jsx52(
26684
26903
  "span",
26685
26904
  {
26686
26905
  className: `squisq-ascii-timeline-description squisq-ascii-timeline-description--${descriptionSide}`,
26687
26906
  children: event.description
26688
26907
  }
26689
26908
  ) : null,
26690
- /* @__PURE__ */ jsx51(
26909
+ /* @__PURE__ */ jsx52(
26691
26910
  "button",
26692
26911
  {
26693
26912
  type: "button",
@@ -26797,7 +27016,7 @@ function TimelineEditorWidget({ editor, blockId }) {
26797
27016
  event.id
26798
27017
  );
26799
27018
  }),
26800
- track.endLabel ? /* @__PURE__ */ jsx51("span", { className: "squisq-ascii-timeline-end-label", children: track.endLabel }) : null
27019
+ track.endLabel ? /* @__PURE__ */ jsx52("span", { className: "squisq-ascii-timeline-end-label", children: track.endLabel }) : null
26801
27020
  ]
26802
27021
  }
26803
27022
  )
@@ -26806,13 +27025,13 @@ function TimelineEditorWidget({ editor, blockId }) {
26806
27025
  ]
26807
27026
  }
26808
27027
  ),
26809
- view.timeline.links.length > 0 ? /* @__PURE__ */ jsx51("ul", { className: "squisq-ascii-timeline-link-list", "aria-label": "Timeline branches", children: view.timeline.links.map((link, index2) => /* @__PURE__ */ jsxs40("li", { children: [
27028
+ view.timeline.links.length > 0 ? /* @__PURE__ */ jsx52("ul", { className: "squisq-ascii-timeline-link-list", "aria-label": "Timeline branches", children: view.timeline.links.map((link, index2) => /* @__PURE__ */ jsxs40("li", { children: [
26810
27029
  link.source,
26811
27030
  " \u2192 ",
26812
27031
  link.target,
26813
27032
  link.label ? `: ${link.label}` : ""
26814
27033
  ] }, `${link.source}-${link.target}-${index2}`)) }) : null,
26815
- selected ? /* @__PURE__ */ jsx51(
27034
+ selected ? /* @__PURE__ */ jsx52(
26816
27035
  EventInspector,
26817
27036
  {
26818
27037
  selected,
@@ -26834,7 +27053,7 @@ function TimelineEditorWidget({ editor, blockId }) {
26834
27053
  "Visual editing paused: this timeline contains source the canvas cannot safely rewrite. Repair it in Source view first.",
26835
27054
  view.warnings.length > 0 ? ` (${view.warnings.length} parser note${view.warnings.length === 1 ? "" : "s"})` : ""
26836
27055
  ] }) : null,
26837
- /* @__PURE__ */ jsx51("div", { className: "squisq-sr-only", "aria-live": "polite", children: announcement })
27056
+ /* @__PURE__ */ jsx52("div", { className: "squisq-sr-only", "aria-live": "polite", children: announcement })
26838
27057
  ]
26839
27058
  }
26840
27059
  );
@@ -26848,8 +27067,8 @@ function InlineTrackLabel({
26848
27067
  onStart,
26849
27068
  onFinish
26850
27069
  }) {
26851
- const [draft, setDraft] = useState44(label);
26852
- useEffect34(() => {
27070
+ const [draft, setDraft] = useState45(label);
27071
+ useEffect35(() => {
26853
27072
  if (editing) setDraft(label);
26854
27073
  }, [editing, label]);
26855
27074
  const commitLabel = () => {
@@ -26862,8 +27081,8 @@ function InlineTrackLabel({
26862
27081
  }
26863
27082
  onFinish();
26864
27083
  };
26865
- if (!editable) return /* @__PURE__ */ jsx51("span", { className: "squisq-ascii-timeline-track-name", children: label });
26866
- return editing ? /* @__PURE__ */ jsx51(
27084
+ if (!editable) return /* @__PURE__ */ jsx52("span", { className: "squisq-ascii-timeline-track-name", children: label });
27085
+ return editing ? /* @__PURE__ */ jsx52(
26867
27086
  "input",
26868
27087
  {
26869
27088
  className: "squisq-ascii-timeline-track-name-input",
@@ -26886,7 +27105,7 @@ function InlineTrackLabel({
26886
27105
  }
26887
27106
  }
26888
27107
  }
26889
- ) : /* @__PURE__ */ jsx51(
27108
+ ) : /* @__PURE__ */ jsx52(
26890
27109
  "button",
26891
27110
  {
26892
27111
  type: "button",
@@ -26906,10 +27125,10 @@ function EventInspector({
26906
27125
  onDelete
26907
27126
  }) {
26908
27127
  const { event } = selected;
26909
- const [label, setLabel] = useState44(event.label);
26910
- const [description, setDescription] = useState44(event.description ?? "");
26911
- useEffect34(() => setLabel(event.label), [event.label]);
26912
- useEffect34(() => setDescription(event.description ?? ""), [event.description]);
27128
+ const [label, setLabel] = useState45(event.label);
27129
+ const [description, setDescription] = useState45(event.description ?? "");
27130
+ useEffect35(() => setLabel(event.label), [event.label]);
27131
+ useEffect35(() => setDescription(event.description ?? ""), [event.description]);
26913
27132
  const update = (patch) => dispatch(patch);
26914
27133
  const commitLabel = () => {
26915
27134
  const next = label.trim();
@@ -26936,11 +27155,11 @@ function EventInspector({
26936
27155
  return /* @__PURE__ */ jsxs40("fieldset", { className: "squisq-ascii-timeline-inspector", disabled: !editable, children: [
26937
27156
  /* @__PURE__ */ jsxs40("legend", { children: [
26938
27157
  "Edit point ",
26939
- /* @__PURE__ */ jsx51("span", { children: selected.trackLabel })
27158
+ /* @__PURE__ */ jsx52("span", { children: selected.trackLabel })
26940
27159
  ] }),
26941
27160
  /* @__PURE__ */ jsxs40("label", { children: [
26942
- /* @__PURE__ */ jsx51("span", { children: "Label" }),
26943
- /* @__PURE__ */ jsx51(
27161
+ /* @__PURE__ */ jsx52("span", { children: "Label" }),
27162
+ /* @__PURE__ */ jsx52(
26944
27163
  "input",
26945
27164
  {
26946
27165
  value: label,
@@ -26960,8 +27179,8 @@ function EventInspector({
26960
27179
  )
26961
27180
  ] }),
26962
27181
  /* @__PURE__ */ jsxs40("label", { className: "squisq-ascii-timeline-inspector-description", children: [
26963
- /* @__PURE__ */ jsx51("span", { children: "Callout text" }),
26964
- /* @__PURE__ */ jsx51(
27182
+ /* @__PURE__ */ jsx52("span", { children: "Callout text" }),
27183
+ /* @__PURE__ */ jsx52(
26965
27184
  "textarea",
26966
27185
  {
26967
27186
  rows: 2,
@@ -26982,7 +27201,7 @@ function EventInspector({
26982
27201
  )
26983
27202
  ] }),
26984
27203
  /* @__PURE__ */ jsxs40("label", { children: [
26985
- /* @__PURE__ */ jsx51("span", { children: "Label side" }),
27204
+ /* @__PURE__ */ jsx52("span", { children: "Label side" }),
26986
27205
  /* @__PURE__ */ jsxs40(
26987
27206
  "select",
26988
27207
  {
@@ -26993,14 +27212,14 @@ function EventInspector({
26993
27212
  patch: { side: event_.target.value }
26994
27213
  }),
26995
27214
  children: [
26996
- /* @__PURE__ */ jsx51("option", { value: "above", children: "Above" }),
26997
- /* @__PURE__ */ jsx51("option", { value: "below", children: "Below" })
27215
+ /* @__PURE__ */ jsx52("option", { value: "above", children: "Above" }),
27216
+ /* @__PURE__ */ jsx52("option", { value: "below", children: "Below" })
26998
27217
  ]
26999
27218
  }
27000
27219
  )
27001
27220
  ] }),
27002
27221
  /* @__PURE__ */ jsxs40("label", { children: [
27003
- /* @__PURE__ */ jsx51("span", { children: "Marker" }),
27222
+ /* @__PURE__ */ jsx52("span", { children: "Marker" }),
27004
27223
  /* @__PURE__ */ jsxs40(
27005
27224
  "select",
27006
27225
  {
@@ -27011,15 +27230,15 @@ function EventInspector({
27011
27230
  patch: { marker: event_.target.value }
27012
27231
  }),
27013
27232
  children: [
27014
- /* @__PURE__ */ jsx51("option", { value: "filled", children: "Filled dot" }),
27015
- /* @__PURE__ */ jsx51("option", { value: "hollow", children: "Hollow dot" }),
27016
- /* @__PURE__ */ jsx51("option", { value: "diamond", children: "Diamond" })
27233
+ /* @__PURE__ */ jsx52("option", { value: "filled", children: "Filled dot" }),
27234
+ /* @__PURE__ */ jsx52("option", { value: "hollow", children: "Hollow dot" }),
27235
+ /* @__PURE__ */ jsx52("option", { value: "diamond", children: "Diamond" })
27017
27236
  ]
27018
27237
  }
27019
27238
  )
27020
27239
  ] }),
27021
27240
  /* @__PURE__ */ jsxs40("label", { className: "squisq-ascii-timeline-checkbox", children: [
27022
- /* @__PURE__ */ jsx51(
27241
+ /* @__PURE__ */ jsx52(
27023
27242
  "input",
27024
27243
  {
27025
27244
  type: "checkbox",
@@ -27031,7 +27250,7 @@ function EventInspector({
27031
27250
  })
27032
27251
  }
27033
27252
  ),
27034
- /* @__PURE__ */ jsx51("span", { children: "Show callout" })
27253
+ /* @__PURE__ */ jsx52("span", { children: "Show callout" })
27035
27254
  ] }),
27036
27255
  /* @__PURE__ */ jsxs40(
27037
27256
  "button",
@@ -27042,7 +27261,7 @@ function EventInspector({
27042
27261
  title: canDelete ? "Delete point" : "A timeline needs at least one point",
27043
27262
  onClick: onDelete,
27044
27263
  children: [
27045
- /* @__PURE__ */ jsx51(Icon, { icon: "fa-solid fa-trash" }),
27264
+ /* @__PURE__ */ jsx52(Icon, { icon: "fa-solid fa-trash" }),
27046
27265
  " Delete point"
27047
27266
  ]
27048
27267
  }
@@ -27076,7 +27295,7 @@ function navigateTrackPoints(event) {
27076
27295
  }
27077
27296
 
27078
27297
  // src/timeline/TimelineViewExtension.ts
27079
- var TIMELINE_VIEW_KEY = new PluginKey8("squisq-timeline-view");
27298
+ var TIMELINE_VIEW_KEY = new PluginKey9("squisq-timeline-view");
27080
27299
  var detectCache3 = /* @__PURE__ */ new WeakMap();
27081
27300
  var parseCache3 = /* @__PURE__ */ new WeakMap();
27082
27301
  function fenceLangOf4(node2) {
@@ -27121,26 +27340,26 @@ function parseTimelineForNode(node2) {
27121
27340
  function findTimelineBlockPos(editor, blockId) {
27122
27341
  return TIMELINE_VIEW_KEY.getState(editor.state)?.entries.find((entry) => entry.id === blockId)?.pos ?? null;
27123
27342
  }
27124
- function buildDecorations7(doc, entries, editor) {
27343
+ function buildDecorations8(doc, entries, editor) {
27125
27344
  const decorations = [];
27126
27345
  for (const entry of entries) {
27127
27346
  const node2 = doc.nodeAt(entry.pos);
27128
27347
  if (!node2 || node2.type.name !== "codeBlock") continue;
27129
27348
  decorations.push(
27130
- Decoration8.node(entry.pos, entry.pos + node2.nodeSize, {
27349
+ Decoration9.node(entry.pos, entry.pos + node2.nodeSize, {
27131
27350
  class: "squisq-ascii-timeline-fence-hidden"
27132
27351
  })
27133
27352
  );
27134
27353
  decorations.push(
27135
- Decoration8.widget(
27354
+ Decoration9.widget(
27136
27355
  entry.pos + node2.nodeSize,
27137
27356
  () => {
27138
27357
  const container = document.createElement("div");
27139
27358
  container.className = "squisq-ascii-timeline-widget-host";
27140
27359
  container.contentEditable = "false";
27141
27360
  containFenceWidgetEvents(container);
27142
- const root = createRoot6(container);
27143
- root.render(createElement12(TimelineEditorWidget, { editor, blockId: entry.id }));
27361
+ const root = createRoot7(container);
27362
+ root.render(createElement13(TimelineEditorWidget, { editor, blockId: entry.id }));
27144
27363
  container.__squisqTimelineRoot = root;
27145
27364
  return container;
27146
27365
  },
@@ -27156,9 +27375,9 @@ function buildDecorations7(doc, entries, editor) {
27156
27375
  )
27157
27376
  );
27158
27377
  }
27159
- return DecorationSet8.create(doc, decorations);
27378
+ return DecorationSet9.create(doc, decorations);
27160
27379
  }
27161
- function applyState6(tr, previous, editor, doc) {
27380
+ function applyState7(tr, previous, editor, doc) {
27162
27381
  if (!tr.docChanged) return previous;
27163
27382
  const mapped = mapFenceEntries(tr, previous.entries, doc);
27164
27383
  let seq = previous.seq;
@@ -27171,9 +27390,9 @@ function applyState6(tr, previous, editor, doc) {
27171
27390
  entries.push({ id: knownId ?? `timeline-${++seq}`, pos });
27172
27391
  return false;
27173
27392
  });
27174
- return { entries, seq, decorations: buildDecorations7(doc, entries, editor) };
27393
+ return { entries, seq, decorations: buildDecorations8(doc, entries, editor) };
27175
27394
  }
27176
- var TimelineViewExtension = Extension8.create({
27395
+ var TimelineViewExtension = Extension9.create({
27177
27396
  name: "squisqTimelineView",
27178
27397
  addOptions() {
27179
27398
  return { enabled: true };
@@ -27182,7 +27401,7 @@ var TimelineViewExtension = Extension8.create({
27182
27401
  const editor = this.editor;
27183
27402
  if (this.options.enabled === false) return [];
27184
27403
  return [
27185
- new Plugin8({
27404
+ new Plugin9({
27186
27405
  key: TIMELINE_VIEW_KEY,
27187
27406
  state: {
27188
27407
  init: (_config, state) => {
@@ -27197,14 +27416,14 @@ var TimelineViewExtension = Extension8.create({
27197
27416
  return {
27198
27417
  entries,
27199
27418
  seq,
27200
- decorations: buildDecorations7(state.doc, entries, editor)
27419
+ decorations: buildDecorations8(state.doc, entries, editor)
27201
27420
  };
27202
27421
  },
27203
- apply: (tr, previous, _oldState, newState) => applyState6(tr, previous, editor, newState.doc)
27422
+ apply: (tr, previous, _oldState, newState) => applyState7(tr, previous, editor, newState.doc)
27204
27423
  },
27205
27424
  props: {
27206
27425
  decorations(state) {
27207
- return this.getState(state)?.decorations ?? DecorationSet8.empty;
27426
+ return this.getState(state)?.decorations ?? DecorationSet9.empty;
27208
27427
  }
27209
27428
  }
27210
27429
  })
@@ -27214,9 +27433,9 @@ var TimelineViewExtension = Extension8.create({
27214
27433
 
27215
27434
  // src/timeline/timelineData.ts
27216
27435
  function useTimelineData(editor, blockId) {
27217
- const [version, setVersion] = useState45(0);
27436
+ const [version, setVersion] = useState46(0);
27218
27437
  const dataCache = useRef37(null);
27219
- useEffect35(() => {
27438
+ useEffect36(() => {
27220
27439
  const onEditorChange = () => setVersion((value) => value + 1);
27221
27440
  editor.on("transaction", onEditorChange);
27222
27441
  editor.on("update", onEditorChange);
@@ -27225,7 +27444,7 @@ function useTimelineData(editor, blockId) {
27225
27444
  editor.off("update", onEditorChange);
27226
27445
  };
27227
27446
  }, [editor]);
27228
- return useMemo34(() => {
27447
+ return useMemo35(() => {
27229
27448
  const pos = findTimelineBlockPos(editor, blockId);
27230
27449
  if (pos === null) return null;
27231
27450
  const node2 = editor.state.doc.nodeAt(pos);
@@ -27251,9 +27470,9 @@ function shouldPasteAsTimelineFence(text) {
27251
27470
  }
27252
27471
 
27253
27472
  // src/BlockPropertiesPopover.tsx
27254
- import { useEffect as useEffect36, useId as useId13, useRef as useRef38, useState as useState46 } from "react";
27473
+ import { useEffect as useEffect37, useId as useId13, useRef as useRef38, useState as useState47 } from "react";
27255
27474
  import { createPortal as createPortal9 } from "react-dom";
27256
- import { jsx as jsx52, jsxs as jsxs41 } from "react/jsx-runtime";
27475
+ import { jsx as jsx53, jsxs as jsxs41 } from "react/jsx-runtime";
27257
27476
  var TRANSITION_FLYOUT = ".squisq-transition-flyout";
27258
27477
  function BlockPropertiesPopover({
27259
27478
  anchorRect,
@@ -27267,13 +27486,13 @@ function BlockPropertiesPopover({
27267
27486
  }) {
27268
27487
  const panelRef = useRef38(null);
27269
27488
  const panelId = `squisq-block-props-portal-${useId13().replace(/:/g, "")}`;
27270
- const [inner, setInner] = useState46(blockAttrs);
27271
- const [templateInner, setTemplateInner] = useState46(templateParams);
27272
- const [style, setStyle] = useState46(() => computeStyle(anchorRect));
27273
- useEffect36(() => {
27489
+ const [inner, setInner] = useState47(blockAttrs);
27490
+ const [templateInner, setTemplateInner] = useState47(templateParams);
27491
+ const [style, setStyle] = useState47(() => computeStyle(anchorRect));
27492
+ useEffect37(() => {
27274
27493
  requestAnimationFrame(() => setStyle(computeStyle(anchorRect, panelRef.current)));
27275
27494
  }, [anchorRect]);
27276
- useEffect36(() => {
27495
+ useEffect37(() => {
27277
27496
  const onKey = (e2) => {
27278
27497
  if (e2.key === "Escape") onClose();
27279
27498
  };
@@ -27319,8 +27538,8 @@ function BlockPropertiesPopover({
27319
27538
  style: { ...style, ...blockAccentStyle2(accentColor) },
27320
27539
  children: [
27321
27540
  /* @__PURE__ */ jsxs41("div", { className: "squisq-block-props-row", children: [
27322
- /* @__PURE__ */ jsx52("span", { className: "squisq-block-props-label", children: "Transition" }),
27323
- /* @__PURE__ */ jsx52(
27541
+ /* @__PURE__ */ jsx53("span", { className: "squisq-block-props-label", children: "Transition" }),
27542
+ /* @__PURE__ */ jsx53(
27324
27543
  TransitionPicker,
27325
27544
  {
27326
27545
  value: transition,
@@ -27331,8 +27550,8 @@ function BlockPropertiesPopover({
27331
27550
  )
27332
27551
  ] }),
27333
27552
  /* @__PURE__ */ jsxs41("div", { className: "squisq-block-props-row", children: [
27334
- /* @__PURE__ */ jsx52("span", { className: "squisq-block-props-label", children: "Duration" }),
27335
- /* @__PURE__ */ jsx52(
27553
+ /* @__PURE__ */ jsx53("span", { className: "squisq-block-props-label", children: "Duration" }),
27554
+ /* @__PURE__ */ jsx53(
27336
27555
  NumberField,
27337
27556
  {
27338
27557
  value: duration,
@@ -27344,8 +27563,8 @@ function BlockPropertiesPopover({
27344
27563
  )
27345
27564
  ] }),
27346
27565
  /* @__PURE__ */ jsxs41("div", { className: "squisq-block-props-row", children: [
27347
- /* @__PURE__ */ jsx52("span", { className: "squisq-block-props-label", children: "Start time" }),
27348
- /* @__PURE__ */ jsx52(
27566
+ /* @__PURE__ */ jsx53("span", { className: "squisq-block-props-label", children: "Start time" }),
27567
+ /* @__PURE__ */ jsx53(
27349
27568
  NumberField,
27350
27569
  {
27351
27570
  value: startTime,
@@ -27373,7 +27592,7 @@ function NumberField({
27373
27592
  onChange
27374
27593
  }) {
27375
27594
  return /* @__PURE__ */ jsxs41("div", { className: "squisq-block-props-numfield", children: [
27376
- /* @__PURE__ */ jsx52(
27595
+ /* @__PURE__ */ jsx53(
27377
27596
  "input",
27378
27597
  {
27379
27598
  type: "number",
@@ -27386,7 +27605,7 @@ function NumberField({
27386
27605
  "aria-label": ariaLabel
27387
27606
  }
27388
27607
  ),
27389
- /* @__PURE__ */ jsx52("span", { className: "squisq-block-props-unit", children: unit })
27608
+ /* @__PURE__ */ jsx53("span", { className: "squisq-block-props-unit", children: unit })
27390
27609
  ] });
27391
27610
  }
27392
27611
  function computeStyle(rect, element) {
@@ -27435,7 +27654,7 @@ function persistFromWrite(bodyMd, state) {
27435
27654
  }
27436
27655
 
27437
27656
  // src/WysiwygEditor.tsx
27438
- import { useCallback as useCallback38, useEffect as useEffect39, useMemo as useMemo35, useRef as useRef40, useState as useState49 } from "react";
27657
+ import { useCallback as useCallback38, useEffect as useEffect41, useMemo as useMemo36, useRef as useRef40, useState as useState51 } from "react";
27439
27658
  import { useEditor as useEditor2, EditorContent as EditorContent2 } from "@tiptap/react";
27440
27659
  import { Selection } from "@tiptap/pm/state";
27441
27660
  import StarterKit2 from "@tiptap/starter-kit";
@@ -27469,13 +27688,16 @@ function createMermaidThemeStore(initialTheme) {
27469
27688
  };
27470
27689
  }
27471
27690
 
27691
+ // src/WysiwygEditor.tsx
27692
+ import { fenceRendererLangs } from "@bendyline/squisq/fence";
27693
+
27472
27694
  // src/blockTagActivity.ts
27473
- import { Extension as Extension9 } from "@tiptap/core";
27474
- import { Plugin as Plugin9, PluginKey as PluginKey9 } from "@tiptap/pm/state";
27475
- import { Decoration as Decoration9, DecorationSet as DecorationSet9 } from "@tiptap/pm/view";
27695
+ import { Extension as Extension10 } from "@tiptap/core";
27696
+ import { Plugin as Plugin10, PluginKey as PluginKey10 } from "@tiptap/pm/state";
27697
+ import { Decoration as Decoration10, DecorationSet as DecorationSet10 } from "@tiptap/pm/view";
27476
27698
  var BLOCK_TAG_SELECTED_CLASS = "squisq-block-tags--selected";
27477
27699
  var BLOCK_TAG_HOVERED_CLASS = "squisq-block-tags--hovered";
27478
- var BLOCK_TAG_ACTIVITY_KEY = new PluginKey9(
27700
+ var BLOCK_TAG_ACTIVITY_KEY = new PluginKey10(
27479
27701
  "squisq-block-tag-activity"
27480
27702
  );
27481
27703
  function isHeadingElement(element) {
@@ -27525,7 +27747,7 @@ function validHeadingPosition(doc, position) {
27525
27747
  if (position === null) return null;
27526
27748
  return doc.nodeAt(position)?.type.name === "heading" ? position : null;
27527
27749
  }
27528
- function buildDecorations8(doc, selectedPosition, hoveredPosition) {
27750
+ function buildDecorations9(doc, selectedPosition, hoveredPosition) {
27529
27751
  const classesByPosition = /* @__PURE__ */ new Map();
27530
27752
  if (selectedPosition !== null) {
27531
27753
  classesByPosition.set(selectedPosition, [BLOCK_TAG_SELECTED_CLASS]);
@@ -27540,10 +27762,10 @@ function buildDecorations8(doc, selectedPosition, hoveredPosition) {
27540
27762
  const node2 = doc.nodeAt(position);
27541
27763
  if (!node2 || node2.type.name !== "heading") continue;
27542
27764
  decorations.push(
27543
- Decoration9.node(position, position + node2.nodeSize, { class: classes.join(" ") })
27765
+ Decoration10.node(position, position + node2.nodeSize, { class: classes.join(" ") })
27544
27766
  );
27545
27767
  }
27546
- return decorations.length > 0 ? DecorationSet9.create(doc, decorations) : DecorationSet9.empty;
27768
+ return decorations.length > 0 ? DecorationSet10.create(doc, decorations) : DecorationSet10.empty;
27547
27769
  }
27548
27770
  function createPluginState(doc, selectionFrom, hoveredPosition) {
27549
27771
  const selectedPosition = findOwningHeadingPosition(doc, selectionFrom);
@@ -27551,7 +27773,7 @@ function createPluginState(doc, selectionFrom, hoveredPosition) {
27551
27773
  return {
27552
27774
  selectedPosition,
27553
27775
  hoveredPosition: validHoveredPosition,
27554
- decorations: buildDecorations8(doc, selectedPosition, validHoveredPosition)
27776
+ decorations: buildDecorations9(doc, selectedPosition, validHoveredPosition)
27555
27777
  };
27556
27778
  }
27557
27779
  function nextHoveredPosition(transaction, previousPosition, doc) {
@@ -27578,11 +27800,11 @@ function setHoveredPosition(view, hoveredPosition) {
27578
27800
  view.state.tr.setMeta(BLOCK_TAG_ACTIVITY_KEY, { hoveredPosition }).setMeta("addToHistory", false)
27579
27801
  );
27580
27802
  }
27581
- var BlockTagActivityExtension = Extension9.create({
27803
+ var BlockTagActivityExtension = Extension10.create({
27582
27804
  name: "squisqBlockTagActivity",
27583
27805
  addProseMirrorPlugins() {
27584
27806
  return [
27585
- new Plugin9({
27807
+ new Plugin10({
27586
27808
  key: BLOCK_TAG_ACTIVITY_KEY,
27587
27809
  state: {
27588
27810
  init: (_config, state) => createPluginState(state.doc, state.selection.from, null),
@@ -27594,7 +27816,7 @@ var BlockTagActivityExtension = Extension9.create({
27594
27816
  },
27595
27817
  props: {
27596
27818
  decorations(state) {
27597
- return this.getState(state)?.decorations ?? DecorationSet9.empty;
27819
+ return this.getState(state)?.decorations ?? DecorationSet10.empty;
27598
27820
  },
27599
27821
  handleDOMEvents: {
27600
27822
  mouseover(view, event) {
@@ -27668,7 +27890,7 @@ var InlineIcon = Node2.create({
27668
27890
  });
27669
27891
 
27670
27892
  // src/ImageNodeView.tsx
27671
- import { useEffect as useEffect37, useRef as useRef39, useState as useState47 } from "react";
27893
+ import { useEffect as useEffect38, useRef as useRef39, useState as useState48 } from "react";
27672
27894
  import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react";
27673
27895
  import Image from "@tiptap/extension-image";
27674
27896
 
@@ -27684,20 +27906,20 @@ function normalizeMalformedAssetUrl(src) {
27684
27906
  }
27685
27907
 
27686
27908
  // src/ImageNodeView.tsx
27687
- import { Fragment as Fragment16, jsx as jsx53, jsxs as jsxs42 } from "react/jsx-runtime";
27909
+ import { Fragment as Fragment16, jsx as jsx54, jsxs as jsxs42 } from "react/jsx-runtime";
27688
27910
  function ImageComponent({ node: node2, selected, editor, updateAttributes }) {
27689
27911
  const { src, alt, title, width } = node2.attrs;
27690
27912
  const { mediaProvider, imageDisplayMode, openImageEdit, mediaRevision } = useEditorContext();
27691
- const [resolvedSrc, setResolvedSrc] = useState47(src);
27692
- const [hovered, setHovered] = useState47(false);
27913
+ const [resolvedSrc, setResolvedSrc] = useState48(src);
27914
+ const [hovered, setHovered] = useState48(false);
27693
27915
  const imgRef = useRef39(null);
27694
- const [previewWidth, setPreviewWidth] = useState47(null);
27916
+ const [previewWidth, setPreviewWidth] = useState48(null);
27695
27917
  const isThumbnail = imageDisplayMode === "thumbnail";
27696
27918
  const isEditable = editor?.isEditable ?? true;
27697
27919
  const normalizedRelativePath = normalizeMalformedAssetUrl(src);
27698
27920
  const isRelative = src && !src.startsWith("blob:") && !src.startsWith("http") && !src.startsWith("data:") && !src.startsWith("/");
27699
27921
  const resolveAs = normalizedRelativePath ?? (isRelative ? src : null);
27700
- useEffect37(() => {
27922
+ useEffect38(() => {
27701
27923
  if (!mediaProvider || !resolveAs) {
27702
27924
  setResolvedSrc(src);
27703
27925
  return;
@@ -27778,7 +28000,7 @@ function ImageComponent({ node: node2, selected, editor, updateAttributes }) {
27778
28000
  onMouseEnter: () => setHovered(true),
27779
28001
  onMouseLeave: () => setHovered(false),
27780
28002
  children: [
27781
- /* @__PURE__ */ jsx53(
28003
+ /* @__PURE__ */ jsx54(
27782
28004
  "img",
27783
28005
  {
27784
28006
  ref: imgRef,
@@ -27807,13 +28029,13 @@ function ImageComponent({ node: node2, selected, editor, updateAttributes }) {
27807
28029
  title: "Edit image",
27808
28030
  "aria-label": `Edit image ${alt || src}`,
27809
28031
  children: [
27810
- /* @__PURE__ */ jsx53("span", { "aria-hidden": "true", style: { fontSize: "0.95em", lineHeight: 1 }, children: "\u270E" }),
27811
- /* @__PURE__ */ jsx53("span", { children: "Edit" })
28032
+ /* @__PURE__ */ jsx54("span", { "aria-hidden": "true", style: { fontSize: "0.95em", lineHeight: 1 }, children: "\u270E" }),
28033
+ /* @__PURE__ */ jsx54("span", { children: "Edit" })
27812
28034
  ]
27813
28035
  }
27814
28036
  ),
27815
28037
  showResize && /* @__PURE__ */ jsxs42(Fragment16, { children: [
27816
- /* @__PURE__ */ jsx53(
28038
+ /* @__PURE__ */ jsx54(
27817
28039
  "span",
27818
28040
  {
27819
28041
  className: "squisq-image-resize-handle",
@@ -27876,14 +28098,15 @@ var ImageWithMediaProvider = Image.extend({
27876
28098
  // src/tiptap/TiptapVideo.tsx
27877
28099
  import { Node as Node3, mergeAttributes as mergeAttributes2 } from "@tiptap/core";
27878
28100
  import { NodeViewWrapper as NodeViewWrapper2, ReactNodeViewRenderer as ReactNodeViewRenderer2 } from "@tiptap/react";
28101
+ import { useEffect as useEffect40, useState as useState50 } from "react";
27879
28102
 
27880
28103
  // src/tiptap/useResolvedMediaSrc.ts
27881
- import { useEffect as useEffect38, useState as useState48 } from "react";
28104
+ import { useEffect as useEffect39, useState as useState49 } from "react";
27882
28105
  function useResolvedMediaSrc(src) {
27883
28106
  const { mediaProvider, mediaRevision } = useEditorContext();
27884
- const [resolved, setResolved] = useState48(src);
28107
+ const [resolved, setResolved] = useState49(src);
27885
28108
  const isRelative = !!src && !src.startsWith("blob:") && !src.startsWith("http:") && !src.startsWith("https:") && !src.startsWith("data:") && !src.startsWith("/");
27886
- useEffect38(() => {
28109
+ useEffect39(() => {
27887
28110
  if (!mediaProvider || !isRelative) {
27888
28111
  setResolved(src);
27889
28112
  return;
@@ -27905,7 +28128,7 @@ function useResolvedMediaSrc(src) {
27905
28128
  }
27906
28129
 
27907
28130
  // src/tiptap/TiptapVideo.tsx
27908
- import { jsx as jsx54, jsxs as jsxs43 } from "react/jsx-runtime";
28131
+ import { jsx as jsx55, jsxs as jsxs43 } from "react/jsx-runtime";
27909
28132
  var PLACEMENT_OPTIONS = [
27910
28133
  { value: "content", label: "In layout", title: "Participate in the block content layout" },
27911
28134
  {
@@ -27934,15 +28157,23 @@ function VideoNodeView({ node: node2, updateAttributes, selected }) {
27934
28157
  const placement = normalizeVideoPlacement(rawPlacement);
27935
28158
  const lockToBlock = normalizeLockToBlock(rawLockToBlock);
27936
28159
  const resolvedSrc = useResolvedMediaSrc(src ?? "");
28160
+ const [audioOnly, setAudioOnly] = useState50(false);
27937
28161
  const resolvedPoster = useResolvedMediaSrc(poster ?? "");
28162
+ useEffect40(() => {
28163
+ setAudioOnly(false);
28164
+ }, [resolvedSrc]);
28165
+ const handleLoadedMetadata = (event) => {
28166
+ const video = event.currentTarget;
28167
+ setAudioOnly(video.videoWidth <= 0 && video.videoHeight <= 0);
28168
+ };
27938
28169
  return /* @__PURE__ */ jsxs43(
27939
28170
  NodeViewWrapper2,
27940
28171
  {
27941
28172
  as: "div",
27942
- className: `squisq-inline-video-player squisq-video-node${selected ? " squisq-video-node--selected" : ""}`,
28173
+ className: `${audioOnly ? "squisq-inline-audio-player squisq-video-node--audio-only" : "squisq-inline-video-player"} squisq-video-node${selected ? " squisq-video-node--selected" : ""}`,
27943
28174
  "data-video-placement": placement,
27944
28175
  children: [
27945
- /* @__PURE__ */ jsxs43(
28176
+ !audioOnly && /* @__PURE__ */ jsxs43(
27946
28177
  "div",
27947
28178
  {
27948
28179
  className: "squisq-video-placement-toolbar",
@@ -27950,8 +28181,8 @@ function VideoNodeView({ node: node2, updateAttributes, selected }) {
27950
28181
  "aria-label": "Video placement",
27951
28182
  contentEditable: false,
27952
28183
  children: [
27953
- /* @__PURE__ */ jsx54("span", { className: "squisq-video-placement-label", children: "Video" }),
27954
- PLACEMENT_OPTIONS.map((option) => /* @__PURE__ */ jsx54(
28184
+ /* @__PURE__ */ jsx55("span", { className: "squisq-video-placement-label", children: "Video" }),
28185
+ PLACEMENT_OPTIONS.map((option) => /* @__PURE__ */ jsx55(
27955
28186
  "button",
27956
28187
  {
27957
28188
  type: "button",
@@ -27964,7 +28195,7 @@ function VideoNodeView({ node: node2, updateAttributes, selected }) {
27964
28195
  },
27965
28196
  option.value
27966
28197
  )),
27967
- placement !== "content" && /* @__PURE__ */ jsx54(
28198
+ placement !== "content" && /* @__PURE__ */ jsx55(
27968
28199
  "button",
27969
28200
  {
27970
28201
  type: "button",
@@ -27980,7 +28211,16 @@ function VideoNodeView({ node: node2, updateAttributes, selected }) {
27980
28211
  ]
27981
28212
  }
27982
28213
  ),
27983
- /* @__PURE__ */ jsx54(
28214
+ audioOnly ? /* @__PURE__ */ jsx55(
28215
+ "audio",
28216
+ {
28217
+ "data-drag-handle": true,
28218
+ draggable: true,
28219
+ src: resolvedSrc || void 0,
28220
+ controls,
28221
+ preload: "metadata"
28222
+ }
28223
+ ) : /* @__PURE__ */ jsx55(
27984
28224
  "video",
27985
28225
  {
27986
28226
  "data-drag-handle": true,
@@ -27991,7 +28231,8 @@ function VideoNodeView({ node: node2, updateAttributes, selected }) {
27991
28231
  playsInline: true,
27992
28232
  preload: "metadata",
27993
28233
  width: width ?? void 0,
27994
- height: height ?? void 0
28234
+ height: height ?? void 0,
28235
+ onLoadedMetadata: handleLoadedMetadata
27995
28236
  }
27996
28237
  )
27997
28238
  ]
@@ -28098,11 +28339,11 @@ var TiptapVideo = Node3.create({
28098
28339
  // src/tiptap/TiptapAudio.tsx
28099
28340
  import { Node as Node4, mergeAttributes as mergeAttributes3 } from "@tiptap/core";
28100
28341
  import { NodeViewWrapper as NodeViewWrapper3, ReactNodeViewRenderer as ReactNodeViewRenderer3 } from "@tiptap/react";
28101
- import { jsx as jsx55 } from "react/jsx-runtime";
28342
+ import { jsx as jsx56 } from "react/jsx-runtime";
28102
28343
  function AudioNodeView({ node: node2 }) {
28103
28344
  const { src, controls } = node2.attrs;
28104
28345
  const resolvedSrc = useResolvedMediaSrc(src ?? "");
28105
- return /* @__PURE__ */ jsx55(NodeViewWrapper3, { as: "span", className: "squisq-inline-audio-player", "data-drag-handle": true, draggable: true, children: /* @__PURE__ */ jsx55("audio", { src: resolvedSrc || void 0, controls, preload: "metadata" }) });
28346
+ return /* @__PURE__ */ jsx56(NodeViewWrapper3, { as: "span", className: "squisq-inline-audio-player", "data-drag-handle": true, draggable: true, children: /* @__PURE__ */ jsx56("audio", { src: resolvedSrc || void 0, controls, preload: "metadata" }) });
28106
28347
  }
28107
28348
  var TiptapAudio = Node4.create({
28108
28349
  name: "audio",
@@ -28163,7 +28404,7 @@ import { profileBlockContents as profileBlockContents2, recommendTemplatesForBlo
28163
28404
 
28164
28405
  // src/MentionExtension.tsx
28165
28406
  import Mention from "@tiptap/extension-mention";
28166
- import { PluginKey as PluginKey10 } from "@tiptap/pm/state";
28407
+ import { PluginKey as PluginKey11 } from "@tiptap/pm/state";
28167
28408
  var FALLBACK_KIND = "mention";
28168
28409
  function buildMentionExtension(getProvider) {
28169
28410
  return Mention.configure({
@@ -28219,7 +28460,7 @@ function buildMentionExtension(getProvider) {
28219
28460
  char: "@",
28220
28461
  // Custom plugin key so the mention suggestion doesn't collide
28221
28462
  // with any future `:` or `/` popovers.
28222
- pluginKey: new PluginKey10("mentionSuggestion"),
28463
+ pluginKey: new PluginKey11("mentionSuggestion"),
28223
28464
  command: ({
28224
28465
  editor,
28225
28466
  range,
@@ -28526,7 +28767,7 @@ function safeFontFamily(value) {
28526
28767
 
28527
28768
  // src/WysiwygEditor.tsx
28528
28769
  import { useEditor as useEditor3 } from "@tiptap/react";
28529
- import { jsx as jsx56, jsxs as jsxs44 } from "react/jsx-runtime";
28770
+ import { jsx as jsx57, jsxs as jsxs44 } from "react/jsx-runtime";
28530
28771
  var LinkWithTitle = Link2.extend({
28531
28772
  addAttributes() {
28532
28773
  return {
@@ -28566,6 +28807,7 @@ function WysiwygEditor({
28566
28807
  setTiptapEditor,
28567
28808
  mediaProvider,
28568
28809
  mentionProvider,
28810
+ fenceRenderers,
28569
28811
  blockTagVisibility,
28570
28812
  themeInheritance,
28571
28813
  colorScheme,
@@ -28584,11 +28826,11 @@ function WysiwygEditor({
28584
28826
  mermaidThemeStoreRef.current = createMermaidThemeStore(activeTheme ?? DEFAULT_THEME4);
28585
28827
  }
28586
28828
  const mermaidThemeStore = mermaidThemeStoreRef.current;
28587
- useEffect39(() => {
28829
+ useEffect41(() => {
28588
28830
  mermaidThemeStore.setTheme(activeTheme ?? DEFAULT_THEME4);
28589
28831
  }, [activeTheme, mermaidThemeStore]);
28590
28832
  const { docTemplates, onDocTemplatesChange } = useDocCustomTemplates();
28591
- const [designerState, setDesignerState] = useState49(
28833
+ const [designerState, setDesignerState] = useState51(
28592
28834
  null
28593
28835
  );
28594
28836
  const handleDesignerSave = useCallback38(
@@ -28604,15 +28846,23 @@ function WysiwygEditor({
28604
28846
  [docTemplates, onDocTemplatesChange]
28605
28847
  );
28606
28848
  const mentionProviderRef = useRef40(mentionProvider);
28607
- useEffect39(() => {
28849
+ useEffect41(() => {
28608
28850
  mentionProviderRef.current = mentionProvider;
28609
28851
  }, [mentionProvider]);
28610
- const resolvedPlaceholder = useMemo35(() => placeholder ?? pickEmptyPrompt(), [placeholder]);
28852
+ const fenceRenderersRef = useRef40(fenceRenderers);
28853
+ useEffect41(() => {
28854
+ fenceRenderersRef.current = fenceRenderers;
28855
+ }, [fenceRenderers]);
28856
+ const activeThemeRef = useRef40(activeTheme);
28857
+ useEffect41(() => {
28858
+ activeThemeRef.current = activeTheme;
28859
+ }, [activeTheme]);
28860
+ const resolvedPlaceholder = useMemo36(() => placeholder ?? pickEmptyPrompt(), [placeholder]);
28611
28861
  const isExternalUpdate = useRef40(false);
28612
28862
  const lastSourceRef = useRef40(editorSource);
28613
28863
  const pendingLocalSourcesRef = useRef40([]);
28614
28864
  const mediaProviderRef = useRef40(mediaProvider);
28615
- useEffect39(() => {
28865
+ useEffect41(() => {
28616
28866
  mediaProviderRef.current = mediaProvider;
28617
28867
  }, [mediaProvider]);
28618
28868
  const frontmatterRef = useRef40(stripFrontmatter(editorSource).frontmatter);
@@ -28629,7 +28879,7 @@ function WysiwygEditor({
28629
28879
  }
28630
28880
  }
28631
28881
  const submitOnEnterRef = useRef40(submitOnEnter);
28632
- useEffect39(() => {
28882
+ useEffect41(() => {
28633
28883
  submitOnEnterRef.current = submitOnEnter;
28634
28884
  }, [submitOnEnter]);
28635
28885
  const editor = useEditor2({
@@ -28646,7 +28896,13 @@ function WysiwygEditor({
28646
28896
  BlockTagActivityExtension,
28647
28897
  AsciiDiagramExtension.configure({ textChannel: sceneTextChannel }),
28648
28898
  MermaidDiagramExtension.configure({ themeStore: mermaidThemeStore }),
28649
- CodeSnippetExtension,
28899
+ CodeSnippetExtension.configure({
28900
+ reservedLanguages: fenceRendererLangs(fenceRenderers ?? void 0)
28901
+ }),
28902
+ HostFenceExtension.configure({
28903
+ renderers: () => fenceRenderersRef.current ?? void 0,
28904
+ theme: () => activeThemeRef.current ?? void 0
28905
+ }),
28650
28906
  RepairableDiagramExtension.configure({ onRepair: applyRepairCommand }),
28651
28907
  TimelineViewExtension,
28652
28908
  TreeViewExtension,
@@ -28811,25 +29067,25 @@ function WysiwygEditor({
28811
29067
  }
28812
29068
  }
28813
29069
  });
28814
- useEffect39(() => {
29070
+ useEffect41(() => {
28815
29071
  if (editor) {
28816
29072
  setTiptapEditor(editor);
28817
29073
  }
28818
29074
  return () => setTiptapEditor(null);
28819
29075
  }, [editor, setTiptapEditor]);
28820
- useEffect39(() => {
29076
+ useEffect41(() => {
28821
29077
  if (editor) editor.setEditable(!readOnly);
28822
29078
  }, [editor, readOnly]);
28823
29079
  const containerRef = useRef40(null);
28824
- const [badgeMenu, setBadgeMenu] = useState49(null);
28825
- const [propsMenu, setPropsMenu] = useState49(null);
29080
+ const [badgeMenu, setBadgeMenu] = useState51(null);
29081
+ const [propsMenu, setPropsMenu] = useState51(null);
28826
29082
  const closeBadgeMenu = useCallback38(() => {
28827
29083
  setBadgeMenu(null);
28828
29084
  requestAnimationFrame(() => {
28829
29085
  if (editor && !editor.isDestroyed) editor.commands.focus();
28830
29086
  });
28831
29087
  }, [editor]);
28832
- useEffect39(() => {
29088
+ useEffect41(() => {
28833
29089
  if (!editor) return;
28834
29090
  const root = containerRef.current;
28835
29091
  if (!root) return;
@@ -28885,7 +29141,7 @@ function WysiwygEditor({
28885
29141
  root.addEventListener("mousedown", onClick);
28886
29142
  return () => root.removeEventListener("mousedown", onClick);
28887
29143
  }, [editor]);
28888
- useEffect39(() => {
29144
+ useEffect41(() => {
28889
29145
  if (!editor) return;
28890
29146
  const pendingIndex = pendingLocalSourcesRef.current.lastIndexOf(editorSource);
28891
29147
  if (pendingIndex >= 0) {
@@ -28917,7 +29173,7 @@ function WysiwygEditor({
28917
29173
  lastSourceRef.current = editorSource;
28918
29174
  isExternalUpdate.current = false;
28919
29175
  }, [editorSource, editor, wrapPolicyEnabled]);
28920
- const badgePreviewSource = useMemo35(() => {
29176
+ const badgePreviewSource = useMemo36(() => {
28921
29177
  if (!badgeMenu) return void 0;
28922
29178
  try {
28923
29179
  const previewDoc = markdownToDoc2(parseMarkdown6(editorSource), { autoTemplates: false });
@@ -28942,7 +29198,7 @@ function WysiwygEditor({
28942
29198
  previewSettings?.activeTheme,
28943
29199
  previewSettings?.activeViewport
28944
29200
  ]);
28945
- const themeStyle = useMemo35(() => {
29201
+ const themeStyle = useMemo36(() => {
28946
29202
  if (!activeTheme) return {};
28947
29203
  const out = {
28948
29204
  "--squisq-block-props-accent": activeTheme.colors.primary
@@ -28968,7 +29224,7 @@ function WysiwygEditor({
28968
29224
  }
28969
29225
  return out;
28970
29226
  }, [activeTheme, themeInheritance]);
28971
- return /* @__PURE__ */ jsx56(CustomTemplateProvider, { docTemplates, onDocTemplatesChange, children: /* @__PURE__ */ jsxs44(
29227
+ return /* @__PURE__ */ jsx57(CustomTemplateProvider, { docTemplates, onDocTemplatesChange, children: /* @__PURE__ */ jsxs44(
28972
29228
  "div",
28973
29229
  {
28974
29230
  className: `squisq-wysiwyg-container${className ? ` ${className}` : ""}`,
@@ -28984,8 +29240,8 @@ function WysiwygEditor({
28984
29240
  "data-theme-inheritance": themeInheritance,
28985
29241
  ref: containerRef,
28986
29242
  children: [
28987
- /* @__PURE__ */ jsx56(EditorContent2, { editor, style: { height: "100%" } }),
28988
- badgeMenu && /* @__PURE__ */ jsx56(
29243
+ /* @__PURE__ */ jsx57(EditorContent2, { editor, style: { height: "100%" } }),
29244
+ badgeMenu && /* @__PURE__ */ jsx57(
28989
29245
  TemplateBadgePopover,
28990
29246
  {
28991
29247
  anchorRect: badgeMenu.rect,
@@ -29013,7 +29269,7 @@ function WysiwygEditor({
29013
29269
  onClose: closeBadgeMenu
29014
29270
  }
29015
29271
  ),
29016
- propsMenu && /* @__PURE__ */ jsx56(
29272
+ propsMenu && /* @__PURE__ */ jsx57(
29017
29273
  BlockPropertiesPopover,
29018
29274
  {
29019
29275
  anchorRect: propsMenu.rect,
@@ -29045,7 +29301,7 @@ function WysiwygEditor({
29045
29301
  onClose: () => setPropsMenu(null)
29046
29302
  }
29047
29303
  ),
29048
- designerState && /* @__PURE__ */ jsx56(
29304
+ designerState && /* @__PURE__ */ jsx57(
29049
29305
  TemplateDesigner,
29050
29306
  {
29051
29307
  initial: designerState.initial,
@@ -29142,7 +29398,7 @@ function moveSelectionToDropPoint(view, event) {
29142
29398
  }
29143
29399
 
29144
29400
  // src/InlinePreviewGutter.tsx
29145
- import { useLayoutEffect as useLayoutEffect7, useMemo as useMemo37, useRef as useRef41, useState as useState51 } from "react";
29401
+ import { useLayoutEffect as useLayoutEffect8, useMemo as useMemo38, useRef as useRef41, useState as useState53 } from "react";
29146
29402
  import { VIEWPORT_PRESETS as VIEWPORT_PRESETS4 } from "@bendyline/squisq/schemas";
29147
29403
  import {
29148
29404
  flattenBlocks as flattenBlocks4,
@@ -29154,14 +29410,14 @@ import { extractPlainText as extractPlainText3, getChildren } from "@bendyline/s
29154
29410
  import { BlockRenderer as BlockRenderer3, MediaContext as MediaContext4 } from "@bendyline/squisq-react";
29155
29411
 
29156
29412
  // src/useHeadingLayout.ts
29157
- import { useCallback as useCallback39, useEffect as useEffect40, useMemo as useMemo36, useState as useState50 } from "react";
29413
+ import { useCallback as useCallback39, useEffect as useEffect42, useMemo as useMemo37, useState as useState52 } from "react";
29158
29414
  import { flattenBlocks as flattenBlocks3, hasTemplate } from "@bendyline/squisq/doc";
29159
29415
  function useHeadingLayout(refInsideWrapper) {
29160
29416
  const { doc, activeView, monacoEditor, tiptapEditor } = useEditorContext();
29161
- const flatBlocks = useMemo36(() => doc ? flattenBlocks3(doc.blocks) : [], [doc]);
29162
- const [entries, setEntries] = useState50([]);
29163
- const [pageEdges, setPageEdges] = useState50(null);
29164
- useEffect40(() => {
29417
+ const flatBlocks = useMemo37(() => doc ? flattenBlocks3(doc.blocks) : [], [doc]);
29418
+ const [entries, setEntries] = useState52([]);
29419
+ const [pageEdges, setPageEdges] = useState52(null);
29420
+ useEffect42(() => {
29165
29421
  if (activeView !== "wysiwyg") return;
29166
29422
  const node2 = refInsideWrapper.current;
29167
29423
  if (!node2) return;
@@ -29229,7 +29485,7 @@ function useHeadingLayout(refInsideWrapper) {
29229
29485
  window.removeEventListener("resize", recompute);
29230
29486
  };
29231
29487
  }, [activeView, flatBlocks, refInsideWrapper]);
29232
- useEffect40(() => {
29488
+ useEffect42(() => {
29233
29489
  if (activeView !== "raw") return;
29234
29490
  if (!monacoEditor) return;
29235
29491
  const node2 = refInsideWrapper.current;
@@ -29290,7 +29546,7 @@ function useHeadingLayout(refInsideWrapper) {
29290
29546
  window.removeEventListener("resize", recompute);
29291
29547
  };
29292
29548
  }, [activeView, monacoEditor, flatBlocks, refInsideWrapper]);
29293
- useEffect40(() => {
29549
+ useEffect42(() => {
29294
29550
  setEntries([]);
29295
29551
  setPageEdges(null);
29296
29552
  }, [activeView]);
@@ -29352,7 +29608,7 @@ function sameEntries(a, b) {
29352
29608
  }
29353
29609
 
29354
29610
  // src/InlinePreviewGutter.tsx
29355
- import { Fragment as Fragment17, jsx as jsx57, jsxs as jsxs45 } from "react/jsx-runtime";
29611
+ import { Fragment as Fragment17, jsx as jsx58, jsxs as jsxs45 } from "react/jsx-runtime";
29356
29612
  function isAnnotated(block) {
29357
29613
  const annotation = block.sourceHeading?.templateAnnotation;
29358
29614
  if (!annotation) return false;
@@ -29522,7 +29778,7 @@ function InlinePreviewGutter({
29522
29778
  const { entries: headingEntries, scrollToBlock } = useHeadingLayout(gutterRef);
29523
29779
  const previewSettings = usePreviewSettingsOptional();
29524
29780
  const activeTheme = previewSettings?.activeTheme ?? DEFAULT_THEME5;
29525
- const items = useMemo37(() => {
29781
+ const items = useMemo38(() => {
29526
29782
  if (!doc || !doc.blocks.length) return [];
29527
29783
  const flat = flattenBlocks4(doc.blocks);
29528
29784
  const totalBlocks = flat.length;
@@ -29579,13 +29835,13 @@ function InlinePreviewGutter({
29579
29835
  });
29580
29836
  return result;
29581
29837
  }, [doc, viewport, activeTheme]);
29582
- const connectorTops = useMemo37(() => {
29838
+ const connectorTops = useMemo38(() => {
29583
29839
  const m = /* @__PURE__ */ new Map();
29584
29840
  headingEntries.forEach((e2) => m.set(e2.block.id, e2.top));
29585
29841
  return m;
29586
29842
  }, [headingEntries]);
29587
- const [positions, setPositions] = useState51(/* @__PURE__ */ new Map());
29588
- useLayoutEffect7(() => {
29843
+ const [positions, setPositions] = useState53(/* @__PURE__ */ new Map());
29844
+ useLayoutEffect8(() => {
29589
29845
  if (items.length === 0) {
29590
29846
  setPositions((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
29591
29847
  return;
@@ -29639,7 +29895,7 @@ function InlinePreviewGutter({
29639
29895
  "data-testid": "inline-preview-gutter",
29640
29896
  "aria-label": "Block previews",
29641
29897
  children: [
29642
- headingEntries.length > 0 && /* @__PURE__ */ jsx57(
29898
+ headingEntries.length > 0 && /* @__PURE__ */ jsx58(
29643
29899
  "div",
29644
29900
  {
29645
29901
  className: "squisq-inline-preview-connectors",
@@ -29656,7 +29912,7 @@ function InlinePreviewGutter({
29656
29912
  children: headingEntries.map((ex, i) => {
29657
29913
  const EXTENT_GAP = 6;
29658
29914
  const height = Math.max(2, ex.bottom - ex.top - EXTENT_GAP);
29659
- return /* @__PURE__ */ jsx57(
29915
+ return /* @__PURE__ */ jsx58(
29660
29916
  "div",
29661
29917
  {
29662
29918
  className: `squisq-inline-preview-extent${ex.annotated ? "" : " squisq-inline-preview-extent--untagged"}`,
@@ -29672,7 +29928,7 @@ function InlinePreviewGutter({
29672
29928
  })
29673
29929
  }
29674
29930
  ),
29675
- items.length > 0 && /* @__PURE__ */ jsx57(
29931
+ items.length > 0 && /* @__PURE__ */ jsx58(
29676
29932
  "svg",
29677
29933
  {
29678
29934
  className: "squisq-inline-preview-connector-svg",
@@ -29695,7 +29951,7 @@ function InlinePreviewGutter({
29695
29951
  const x2 = connectorWidth + CARD_LEFT_INSET;
29696
29952
  const y2 = cardTop + CARD_LABEL_OFFSET;
29697
29953
  return /* @__PURE__ */ jsxs45("g", { children: [
29698
- /* @__PURE__ */ jsx57(
29954
+ /* @__PURE__ */ jsx58(
29699
29955
  "line",
29700
29956
  {
29701
29957
  x1,
@@ -29707,13 +29963,13 @@ function InlinePreviewGutter({
29707
29963
  strokeLinecap: "round"
29708
29964
  }
29709
29965
  ),
29710
- /* @__PURE__ */ jsx57("circle", { cx: x1, cy: y1, r: "4", fill: "#6366f1", stroke: "#ffffff", strokeWidth: "2" }),
29711
- /* @__PURE__ */ jsx57("circle", { cx: x2, cy: y2, r: "4", fill: "#6366f1", stroke: "#ffffff", strokeWidth: "2" })
29966
+ /* @__PURE__ */ jsx58("circle", { cx: x1, cy: y1, r: "4", fill: "#6366f1", stroke: "#ffffff", strokeWidth: "2" }),
29967
+ /* @__PURE__ */ jsx58("circle", { cx: x2, cy: y2, r: "4", fill: "#6366f1", stroke: "#ffffff", strokeWidth: "2" })
29712
29968
  ] }, item.id);
29713
29969
  })
29714
29970
  }
29715
29971
  ),
29716
- /* @__PURE__ */ jsx57(MediaContext4.Provider, { value: mediaProvider ?? null, children: items.map((item) => {
29972
+ /* @__PURE__ */ jsx58(MediaContext4.Provider, { value: mediaProvider ?? null, children: items.map((item) => {
29717
29973
  const top = positions.get(item.id);
29718
29974
  const hidden = top == null;
29719
29975
  return /* @__PURE__ */ jsxs45(
@@ -29731,20 +29987,20 @@ function InlinePreviewGutter({
29731
29987
  onClick: () => scrollToBlock(item.block),
29732
29988
  children: [
29733
29989
  /* @__PURE__ */ jsxs45("div", { className: "squisq-inline-preview-card-label", children: [
29734
- /* @__PURE__ */ jsx57("span", { className: "squisq-inline-preview-card-template", children: templateLabel(item.template) }),
29990
+ /* @__PURE__ */ jsx58("span", { className: "squisq-inline-preview-card-template", children: templateLabel(item.template) }),
29735
29991
  item.headingText && /* @__PURE__ */ jsxs45(Fragment17, { children: [
29736
- /* @__PURE__ */ jsx57("span", { className: "squisq-inline-preview-card-sep", children: "\u2014" }),
29737
- /* @__PURE__ */ jsx57("span", { className: "squisq-inline-preview-card-title", children: item.headingText })
29992
+ /* @__PURE__ */ jsx58("span", { className: "squisq-inline-preview-card-sep", children: "\u2014" }),
29993
+ /* @__PURE__ */ jsx58("span", { className: "squisq-inline-preview-card-title", children: item.headingText })
29738
29994
  ] })
29739
29995
  ] }),
29740
- /* @__PURE__ */ jsx57(
29996
+ /* @__PURE__ */ jsx58(
29741
29997
  "div",
29742
29998
  {
29743
29999
  className: "squisq-inline-preview-card-svg",
29744
30000
  style: {
29745
30001
  aspectRatio: `${viewport.width} / ${viewport.height}`
29746
30002
  },
29747
- children: /* @__PURE__ */ jsx57(
30003
+ children: /* @__PURE__ */ jsx58(
29748
30004
  BlockRenderer3,
29749
30005
  {
29750
30006
  block: item.block,
@@ -30100,10 +30356,10 @@ function buildPreviewDoc(doc, options) {
30100
30356
  // src/OutlinePanel.tsx
30101
30357
  import {
30102
30358
  useCallback as useCallback40,
30103
- useEffect as useEffect41,
30104
- useMemo as useMemo38,
30359
+ useEffect as useEffect43,
30360
+ useMemo as useMemo39,
30105
30361
  useRef as useRef42,
30106
- useState as useState52
30362
+ useState as useState54
30107
30363
  } from "react";
30108
30364
  import { flattenBlocks as flattenBlocks5, hasTemplate as hasTemplate3 } from "@bendyline/squisq/doc";
30109
30365
  import { extractPlainText as extractPlainText5 } from "@bendyline/squisq/markdown";
@@ -30205,7 +30461,7 @@ function splitTrailingLineGap(value) {
30205
30461
  }
30206
30462
 
30207
30463
  // src/OutlinePanel.tsx
30208
- import { jsx as jsx58, jsxs as jsxs46 } from "react/jsx-runtime";
30464
+ import { jsx as jsx59, jsxs as jsxs46 } from "react/jsx-runtime";
30209
30465
  var OUTLINE_RESPONSIVE_WIDTH = "clamp(260px, 30vw, 460px)";
30210
30466
  var OUTLINE_DRAG_MIME = "application/x-squisq-outline-section";
30211
30467
  function OutlinePanel({ width, className, readOnly = false }) {
@@ -30222,9 +30478,9 @@ function OutlinePanel({ width, className, readOnly = false }) {
30222
30478
  const { scrollToBlock } = useHeadingLayout(paneRef);
30223
30479
  const cursorActiveId = useActiveOutlineBlockId();
30224
30480
  const activeDragRef = useRef42(null);
30225
- const [draggedBlockId, setDraggedBlockId] = useState52(null);
30226
- const [dropTarget, setDropTarget] = useState52(null);
30227
- const blockModeActiveId = useMemo38(() => {
30481
+ const [draggedBlockId, setDraggedBlockId] = useState54(null);
30482
+ const [dropTarget, setDropTarget] = useState54(null);
30483
+ const blockModeActiveId = useMemo39(() => {
30228
30484
  if (layoutMode !== "block" || activeBlockStartLine == null || !doc) return null;
30229
30485
  const match = flattenBlocks5(doc.blocks).find(
30230
30486
  (b) => b.sourceHeading?.position?.start.line === activeBlockStartLine
@@ -30323,7 +30579,7 @@ function OutlinePanel({ width, className, readOnly = false }) {
30323
30579
  overflow: "auto",
30324
30580
  ...accentColor ? { ["--squisq-outline-accent"]: accentColor } : {}
30325
30581
  };
30326
- return /* @__PURE__ */ jsx58(
30582
+ return /* @__PURE__ */ jsx59(
30327
30583
  "aside",
30328
30584
  {
30329
30585
  ref: paneRef,
@@ -30331,7 +30587,7 @@ function OutlinePanel({ width, className, readOnly = false }) {
30331
30587
  style: paneStyle,
30332
30588
  "data-testid": "outline-panel",
30333
30589
  "aria-label": "Document outline",
30334
- children: isEmpty2 ? /* @__PURE__ */ jsx58("div", { className: "squisq-outline-empty", children: /* @__PURE__ */ jsx58("p", { children: "Add a heading to populate the outline." }) }) : /* @__PURE__ */ jsx58("ul", { className: "squisq-outline-tree", role: "tree", children: doc.blocks.map((b) => /* @__PURE__ */ jsx58(
30590
+ 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(
30335
30591
  OutlineNode,
30336
30592
  {
30337
30593
  block: b,
@@ -30405,21 +30661,21 @@ function OutlineNode({
30405
30661
  onDragEnd,
30406
30662
  title: text || "(empty heading)",
30407
30663
  children: [
30408
- canDrag && /* @__PURE__ */ jsx58("span", { className: "squisq-outline-drag-handle", "aria-hidden": "true", children: /* @__PURE__ */ jsxs46("svg", { width: "8", height: "12", viewBox: "0 0 8 12", children: [
30409
- /* @__PURE__ */ jsx58("circle", { cx: "2", cy: "2", r: "1", fill: "currentColor" }),
30410
- /* @__PURE__ */ jsx58("circle", { cx: "6", cy: "2", r: "1", fill: "currentColor" }),
30411
- /* @__PURE__ */ jsx58("circle", { cx: "2", cy: "6", r: "1", fill: "currentColor" }),
30412
- /* @__PURE__ */ jsx58("circle", { cx: "6", cy: "6", r: "1", fill: "currentColor" }),
30413
- /* @__PURE__ */ jsx58("circle", { cx: "2", cy: "10", r: "1", fill: "currentColor" }),
30414
- /* @__PURE__ */ jsx58("circle", { cx: "6", cy: "10", r: "1", fill: "currentColor" })
30664
+ canDrag && /* @__PURE__ */ jsx59("span", { className: "squisq-outline-drag-handle", "aria-hidden": "true", children: /* @__PURE__ */ jsxs46("svg", { width: "8", height: "12", viewBox: "0 0 8 12", children: [
30665
+ /* @__PURE__ */ jsx59("circle", { cx: "2", cy: "2", r: "1", fill: "currentColor" }),
30666
+ /* @__PURE__ */ jsx59("circle", { cx: "6", cy: "2", r: "1", fill: "currentColor" }),
30667
+ /* @__PURE__ */ jsx59("circle", { cx: "2", cy: "6", r: "1", fill: "currentColor" }),
30668
+ /* @__PURE__ */ jsx59("circle", { cx: "6", cy: "6", r: "1", fill: "currentColor" }),
30669
+ /* @__PURE__ */ jsx59("circle", { cx: "2", cy: "10", r: "1", fill: "currentColor" }),
30670
+ /* @__PURE__ */ jsx59("circle", { cx: "6", cy: "10", r: "1", fill: "currentColor" })
30415
30671
  ] }) }),
30416
- /* @__PURE__ */ jsx58("span", { className: "squisq-outline-row-text", children: text || "(untitled)" }),
30417
- showChip && /* @__PURE__ */ jsx58("span", { className: "squisq-outline-template-chip", children: templateLabel(tplName) })
30672
+ /* @__PURE__ */ jsx59("span", { className: "squisq-outline-row-text", children: text || "(untitled)" }),
30673
+ showChip && /* @__PURE__ */ jsx59("span", { className: "squisq-outline-template-chip", children: templateLabel(tplName) })
30418
30674
  ]
30419
30675
  }
30420
30676
  ),
30421
30677
  heading && /* @__PURE__ */ jsxs46("span", { className: "squisq-outline-row-actions", children: [
30422
- /* @__PURE__ */ jsx58(
30678
+ /* @__PURE__ */ jsx59(
30423
30679
  "button",
30424
30680
  {
30425
30681
  type: "button",
@@ -30428,7 +30684,7 @@ function OutlineNode({
30428
30684
  title: "Promote heading",
30429
30685
  disabled: mutationsDisabled || !canPromote,
30430
30686
  onClick: () => onChangeLevel(block, -1),
30431
- children: /* @__PURE__ */ jsx58("svg", { width: "10", height: "10", viewBox: "0 0 10 10", "aria-hidden": "true", children: /* @__PURE__ */ jsx58(
30687
+ children: /* @__PURE__ */ jsx59("svg", { width: "10", height: "10", viewBox: "0 0 10 10", "aria-hidden": "true", children: /* @__PURE__ */ jsx59(
30432
30688
  "path",
30433
30689
  {
30434
30690
  d: "M6.5 2.5 L3 5 L6.5 7.5",
@@ -30441,7 +30697,7 @@ function OutlineNode({
30441
30697
  ) })
30442
30698
  }
30443
30699
  ),
30444
- /* @__PURE__ */ jsx58(
30700
+ /* @__PURE__ */ jsx59(
30445
30701
  "button",
30446
30702
  {
30447
30703
  type: "button",
@@ -30450,7 +30706,7 @@ function OutlineNode({
30450
30706
  title: "Demote heading",
30451
30707
  disabled: mutationsDisabled || !canDemote,
30452
30708
  onClick: () => onChangeLevel(block, 1),
30453
- children: /* @__PURE__ */ jsx58("svg", { width: "10", height: "10", viewBox: "0 0 10 10", "aria-hidden": "true", children: /* @__PURE__ */ jsx58(
30709
+ children: /* @__PURE__ */ jsx59("svg", { width: "10", height: "10", viewBox: "0 0 10 10", "aria-hidden": "true", children: /* @__PURE__ */ jsx59(
30454
30710
  "path",
30455
30711
  {
30456
30712
  d: "M3.5 2.5 L7 5 L3.5 7.5",
@@ -30467,7 +30723,7 @@ function OutlineNode({
30467
30723
  ]
30468
30724
  }
30469
30725
  ),
30470
- block.children && block.children.length > 0 && /* @__PURE__ */ jsx58("ul", { className: "squisq-outline-tree", children: block.children.map((child) => /* @__PURE__ */ jsx58(
30726
+ block.children && block.children.length > 0 && /* @__PURE__ */ jsx59("ul", { className: "squisq-outline-tree", children: block.children.map((child) => /* @__PURE__ */ jsx59(
30471
30727
  OutlineNode,
30472
30728
  {
30473
30729
  block: child,
@@ -30489,12 +30745,12 @@ function OutlineNode({
30489
30745
  }
30490
30746
  function useActiveOutlineBlockId() {
30491
30747
  const { doc, activeView, tiptapEditor, monacoEditor } = useEditorContext();
30492
- const flatBlocks = useMemo38(() => doc ? flattenBlocks5(doc.blocks) : [], [doc]);
30493
- const [activeId, setActiveId] = useState52(null);
30494
- useEffect41(() => {
30748
+ const flatBlocks = useMemo39(() => doc ? flattenBlocks5(doc.blocks) : [], [doc]);
30749
+ const [activeId, setActiveId] = useState54(null);
30750
+ useEffect43(() => {
30495
30751
  setActiveId(null);
30496
30752
  }, [activeView]);
30497
- useEffect41(() => {
30753
+ useEffect43(() => {
30498
30754
  if (activeView !== "wysiwyg" || !tiptapEditor) return;
30499
30755
  const update = () => {
30500
30756
  const { from } = tiptapEditor.state.selection;
@@ -30516,7 +30772,7 @@ function useActiveOutlineBlockId() {
30516
30772
  tiptapEditor.off("update", update);
30517
30773
  };
30518
30774
  }, [activeView, tiptapEditor, flatBlocks]);
30519
- useEffect41(() => {
30775
+ useEffect43(() => {
30520
30776
  if (activeView !== "raw" || !monacoEditor) return;
30521
30777
  const update = () => {
30522
30778
  const line = monacoEditor.getPosition()?.lineNumber;
@@ -30567,7 +30823,7 @@ function bumpHeadingLevelInSource(source, line, delta) {
30567
30823
  }
30568
30824
 
30569
30825
  // src/codeContext/CodeContextZones.tsx
30570
- import { useCallback as useCallback42, useEffect as useEffect43, useMemo as useMemo40, useRef as useRef44, useState as useState53 } from "react";
30826
+ import { useCallback as useCallback42, useEffect as useEffect45, useMemo as useMemo41, useRef as useRef44, useState as useState55 } from "react";
30571
30827
  import { createPortal as createPortal10 } from "react-dom";
30572
30828
 
30573
30829
  // src/codeContext/diffContextSections.ts
@@ -30691,8 +30947,8 @@ function setOrdinal(zone, ordinal) {
30691
30947
  // src/codeContext/CodeContextSectionView.tsx
30692
30948
  import { parseMarkdown as parseMarkdown8 } from "@bendyline/squisq/markdown";
30693
30949
  import { MarkdownRenderer } from "@bendyline/squisq-react";
30694
- import { useCallback as useCallback41, useEffect as useEffect42, useMemo as useMemo39, useRef as useRef43 } from "react";
30695
- import { jsx as jsx59, jsxs as jsxs47 } from "react/jsx-runtime";
30950
+ import { useCallback as useCallback41, useEffect as useEffect44, useMemo as useMemo40, useRef as useRef43 } from "react";
30951
+ import { jsx as jsx60, jsxs as jsxs47 } from "react/jsx-runtime";
30696
30952
  function CodeContextSectionView({
30697
30953
  section,
30698
30954
  expanded,
@@ -30703,15 +30959,15 @@ function CodeContextSectionView({
30703
30959
  onMeasure
30704
30960
  }) {
30705
30961
  const rootRef = useRef43(null);
30706
- const stripNodes = useMemo39(
30962
+ const stripNodes = useMemo40(
30707
30963
  () => parseMarkdown8(section.summaryMarkdown).children,
30708
30964
  [section.summaryMarkdown]
30709
30965
  );
30710
- const bodyNodes = useMemo39(
30966
+ const bodyNodes = useMemo40(
30711
30967
  () => expanded && section.markdown ? parseMarkdown8(section.markdown).children : null,
30712
30968
  [expanded, section.markdown]
30713
30969
  );
30714
- useEffect42(() => {
30970
+ useEffect44(() => {
30715
30971
  const el = rootRef.current;
30716
30972
  if (!el || typeof ResizeObserver === "undefined") return;
30717
30973
  const report = () => {
@@ -30767,12 +31023,12 @@ function CodeContextSectionView({
30767
31023
  "aria-expanded": expanded,
30768
31024
  onClick: () => onToggle(section.id),
30769
31025
  children: [
30770
- /* @__PURE__ */ jsx59("span", { className: "squisq-ccx-chevron", "aria-hidden": "true", children: expanded ? "\u25BE" : "\u25B8" }),
30771
- /* @__PURE__ */ jsx59("span", { className: "squisq-ccx-strip-text", children: /* @__PURE__ */ jsx59(MarkdownRenderer, { nodes: stripNodes, ...linkSchemes ? { linkSchemes } : {} }) })
31026
+ /* @__PURE__ */ jsx60("span", { className: "squisq-ccx-chevron", "aria-hidden": "true", children: expanded ? "\u25BE" : "\u25B8" }),
31027
+ /* @__PURE__ */ jsx60("span", { className: "squisq-ccx-strip-text", children: /* @__PURE__ */ jsx60(MarkdownRenderer, { nodes: stripNodes, ...linkSchemes ? { linkSchemes } : {} }) })
30772
31028
  ]
30773
31029
  }
30774
31030
  ),
30775
- expanded && (bodyNodes ? /* @__PURE__ */ jsx59("div", { className: "squisq-ccx-body", children: /* @__PURE__ */ jsx59(MarkdownRenderer, { nodes: bodyNodes, ...linkSchemes ? { linkSchemes } : {} }) }) : /* @__PURE__ */ jsx59("div", { className: "squisq-ccx-body squisq-ccx-body--loading", children: "Loading\u2026" }))
31031
+ expanded && (bodyNodes ? /* @__PURE__ */ jsx60("div", { className: "squisq-ccx-body", children: /* @__PURE__ */ jsx60(MarkdownRenderer, { nodes: bodyNodes, ...linkSchemes ? { linkSchemes } : {} }) }) : /* @__PURE__ */ jsx60("div", { className: "squisq-ccx-body squisq-ccx-body--loading", children: "Loading\u2026" }))
30776
31032
  ]
30777
31033
  }
30778
31034
  )
@@ -30780,14 +31036,14 @@ function CodeContextSectionView({
30780
31036
  }
30781
31037
 
30782
31038
  // src/codeContext/CodeContextZones.tsx
30783
- import { Fragment as Fragment18, jsx as jsx60 } from "react/jsx-runtime";
31039
+ import { Fragment as Fragment18, jsx as jsx61 } from "react/jsx-runtime";
30784
31040
  function CodeContextZones({ options }) {
30785
31041
  const { monacoEditor } = useEditorContext();
30786
- const [manager, setManager] = useState53(null);
30787
- const [, setZonesVersion] = useState53(0);
30788
- const [expandedById, setExpandedById] = useState53({});
31042
+ const [manager, setManager] = useState55(null);
31043
+ const [, setZonesVersion] = useState55(0);
31044
+ const [expandedById, setExpandedById] = useState55({});
30789
31045
  const seenIds = useRef44(/* @__PURE__ */ new Set());
30790
- useEffect43(() => {
31046
+ useEffect45(() => {
30791
31047
  if (!monacoEditor) return;
30792
31048
  const mgr = new CodeContextZoneManager(monacoEditor);
30793
31049
  const off = mgr.onDidChangeZones(() => setZonesVersion((v2) => v2 + 1));
@@ -30800,7 +31056,7 @@ function CodeContextZones({ options }) {
30800
31056
  }, [monacoEditor]);
30801
31057
  const fileTop = options.fileTop;
30802
31058
  const sections = options.sections;
30803
- const resolved = useMemo40(() => {
31059
+ const resolved = useMemo41(() => {
30804
31060
  const out = [];
30805
31061
  if (fileTop) {
30806
31062
  out.push({ spec: { id: fileTop.id, line: 0, ordinal: 0 }, section: fileTop });
@@ -30810,7 +31066,7 @@ function CodeContextZones({ options }) {
30810
31066
  });
30811
31067
  return out;
30812
31068
  }, [fileTop, sections]);
30813
- useEffect43(() => {
31069
+ useEffect45(() => {
30814
31070
  if (!manager) return;
30815
31071
  manager.sync(resolved.map((r) => r.spec));
30816
31072
  setExpandedById((prev) => {
@@ -30849,11 +31105,11 @@ function CodeContextZones({ options }) {
30849
31105
  [monacoEditor]
30850
31106
  );
30851
31107
  if (!manager) return null;
30852
- return /* @__PURE__ */ jsx60(Fragment18, { children: resolved.map(({ section }) => {
31108
+ return /* @__PURE__ */ jsx61(Fragment18, { children: resolved.map(({ section }) => {
30853
31109
  const domNode = manager.getDomNode(section.id);
30854
31110
  if (!domNode) return null;
30855
31111
  return createPortal10(
30856
- /* @__PURE__ */ jsx60(
31112
+ /* @__PURE__ */ jsx61(
30857
31113
  CodeContextSectionView,
30858
31114
  {
30859
31115
  section,
@@ -30873,7 +31129,7 @@ function CodeContextZones({ options }) {
30873
31129
  }
30874
31130
 
30875
31131
  // src/BlockCardView.tsx
30876
- import { jsx as jsx61, jsxs as jsxs48 } from "react/jsx-runtime";
31132
+ import { jsx as jsx62, jsxs as jsxs48 } from "react/jsx-runtime";
30877
31133
  function BlockCardView({
30878
31134
  active = true,
30879
31135
  blockCount,
@@ -30894,7 +31150,7 @@ function BlockCardView({
30894
31150
  className: `squisq-block-card-frame${active ? " squisq-block-card" : ""}${className ? ` ${className}` : ""}`,
30895
31151
  "data-testid": active ? "block-card-view" : void 0,
30896
31152
  children: [
30897
- /* @__PURE__ */ jsx61("div", { className: `squisq-block-card-frame-body${active ? " squisq-block-card-body" : ""}`, children }),
31153
+ /* @__PURE__ */ jsx62("div", { className: `squisq-block-card-frame-body${active ? " squisq-block-card-body" : ""}`, children }),
30898
31154
  active && /* @__PURE__ */ jsxs48("div", { className: "squisq-block-card-nav", role: "toolbar", "aria-label": "Block navigation", children: [
30899
31155
  /* @__PURE__ */ jsxs48(
30900
31156
  "button",
@@ -30906,7 +31162,7 @@ function BlockCardView({
30906
31162
  "aria-label": "Previous block",
30907
31163
  "data-tooltip": "Previous block",
30908
31164
  children: [
30909
- /* @__PURE__ */ jsx61("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: /* @__PURE__ */ jsx61(
31165
+ /* @__PURE__ */ jsx62("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: /* @__PURE__ */ jsx62(
30910
31166
  "path",
30911
31167
  {
30912
31168
  d: "M9 2.5 L4.5 7 L9 11.5",
@@ -30917,7 +31173,7 @@ function BlockCardView({
30917
31173
  fill: "none"
30918
31174
  }
30919
31175
  ) }),
30920
- /* @__PURE__ */ jsx61("span", { children: "Previous" })
31176
+ /* @__PURE__ */ jsx62("span", { children: "Previous" })
30921
31177
  ]
30922
31178
  }
30923
31179
  ),
@@ -30937,8 +31193,8 @@ function BlockCardView({
30937
31193
  "aria-label": "Next block",
30938
31194
  "data-tooltip": "Next block",
30939
31195
  children: [
30940
- /* @__PURE__ */ jsx61("span", { children: "Next" }),
30941
- /* @__PURE__ */ jsx61("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: /* @__PURE__ */ jsx61(
31196
+ /* @__PURE__ */ jsx62("span", { children: "Next" }),
31197
+ /* @__PURE__ */ jsx62("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: /* @__PURE__ */ jsx62(
30942
31198
  "path",
30943
31199
  {
30944
31200
  d: "M5 2.5 L9.5 7 L5 11.5",
@@ -30961,7 +31217,7 @@ function BlockCardView({
30961
31217
  "aria-label": "Add block",
30962
31218
  "data-tooltip": "Add block after this one",
30963
31219
  children: [
30964
- /* @__PURE__ */ jsx61("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: /* @__PURE__ */ jsx61(
31220
+ /* @__PURE__ */ jsx62("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: /* @__PURE__ */ jsx62(
30965
31221
  "path",
30966
31222
  {
30967
31223
  d: "M7 3 L7 11 M3 7 L11 7",
@@ -30971,7 +31227,7 @@ function BlockCardView({
30971
31227
  fill: "none"
30972
31228
  }
30973
31229
  ) }),
30974
- /* @__PURE__ */ jsx61("span", { children: "Add block" })
31230
+ /* @__PURE__ */ jsx62("span", { children: "Add block" })
30975
31231
  ]
30976
31232
  }
30977
31233
  )
@@ -31246,7 +31502,7 @@ function placeClipInBlock(source, fromLine, targetHeadingLine, spec, startAt) {
31246
31502
  }
31247
31503
 
31248
31504
  // src/TimelineTrack.tsx
31249
- import { useCallback as useCallback44, useEffect as useEffect46, useMemo as useMemo41, useRef as useRef47, useState as useState56 } from "react";
31505
+ import { useCallback as useCallback44, useEffect as useEffect48, useMemo as useMemo42, useRef as useRef47, useState as useState58 } from "react";
31250
31506
  import {
31251
31507
  resolveMediaSchedule as resolveMediaSchedule2,
31252
31508
  getDocPlaybackDuration,
@@ -31374,14 +31630,14 @@ function collectTimelinePlaybackSchedule(doc, scheduled) {
31374
31630
  import { memo } from "react";
31375
31631
  import { VIEWPORT_PRESETS as VIEWPORT_PRESETS5 } from "@bendyline/squisq/schemas";
31376
31632
  import { BlockRenderer as BlockRenderer4, MediaContext as MediaContext5 } from "@bendyline/squisq-react";
31377
- import { jsx as jsx62 } from "react/jsx-runtime";
31633
+ import { jsx as jsx63 } from "react/jsx-runtime";
31378
31634
  var BlockThumbnail = memo(function BlockThumbnail2({
31379
31635
  visual,
31380
31636
  viewport = VIEWPORT_PRESETS5.landscape,
31381
31637
  basePath = "/",
31382
31638
  mediaProvider = null
31383
31639
  }) {
31384
- return /* @__PURE__ */ jsx62(MediaContext5.Provider, { value: mediaProvider, children: /* @__PURE__ */ jsx62(BlockRenderer4, { block: visual, blockTime: 0, basePath, viewport }) });
31640
+ return /* @__PURE__ */ jsx63(MediaContext5.Provider, { value: mediaProvider, children: /* @__PURE__ */ jsx63(BlockRenderer4, { block: visual, blockTime: 0, basePath, viewport }) });
31385
31641
  });
31386
31642
 
31387
31643
  // src/resolveBlockVisual.ts
@@ -31405,7 +31661,7 @@ function resolveBlockVisual(doc, block, theme, viewport) {
31405
31661
  }
31406
31662
 
31407
31663
  // src/useTimelineClock.ts
31408
- import { useCallback as useCallback43, useEffect as useEffect44, useRef as useRef45, useState as useState54 } from "react";
31664
+ import { useCallback as useCallback43, useEffect as useEffect46, useRef as useRef45, useState as useState56 } from "react";
31409
31665
  function advanceTime(prev, dt, total) {
31410
31666
  if (total <= 0) return 0;
31411
31667
  return Math.min(total, Math.max(0, prev + dt));
@@ -31425,8 +31681,8 @@ function playTimelineMediaAt(root, time) {
31425
31681
  });
31426
31682
  }
31427
31683
  function useTimelineClock(total) {
31428
- const [currentTime, setCurrentTime] = useState54(0);
31429
- const [isPlaying, setIsPlaying] = useState54(false);
31684
+ const [currentTime, setCurrentTime] = useState56(0);
31685
+ const [isPlaying, setIsPlaying] = useState56(false);
31430
31686
  const rafRef = useRef45(null);
31431
31687
  const lastRef = useRef45(0);
31432
31688
  const currentTimeRef = useRef45(0);
@@ -31434,10 +31690,10 @@ function useTimelineClock(total) {
31434
31690
  const mediaHostRef = useRef45(null);
31435
31691
  currentTimeRef.current = currentTime;
31436
31692
  isPlayingRef.current = isPlaying;
31437
- useEffect44(() => {
31693
+ useEffect46(() => {
31438
31694
  setCurrentTime((t) => Math.min(t, Math.max(0, total)));
31439
31695
  }, [total]);
31440
- useEffect44(() => {
31696
+ useEffect46(() => {
31441
31697
  if (!isPlaying) return;
31442
31698
  lastRef.current = performance.now();
31443
31699
  const tick = (now) => {
@@ -31499,9 +31755,9 @@ function timelineMediaLabel(src, kind) {
31499
31755
  }
31500
31756
 
31501
31757
  // src/TimelineItemMenu.tsx
31502
- import { useEffect as useEffect45, useLayoutEffect as useLayoutEffect8, useRef as useRef46, useState as useState55 } from "react";
31758
+ import { useEffect as useEffect47, useLayoutEffect as useLayoutEffect9, useRef as useRef46, useState as useState57 } from "react";
31503
31759
  import { createPortal as createPortal11 } from "react-dom";
31504
- import { Fragment as Fragment19, jsx as jsx63, jsxs as jsxs49 } from "react/jsx-runtime";
31760
+ import { Fragment as Fragment19, jsx as jsx64, jsxs as jsxs49 } from "react/jsx-runtime";
31505
31761
  function TimelineItemMenu({
31506
31762
  anchor,
31507
31763
  target,
@@ -31514,8 +31770,8 @@ function TimelineItemMenu({
31514
31770
  onClose
31515
31771
  }) {
31516
31772
  const panelRef = useRef46(null);
31517
- const [style, setStyle] = useState55(() => menuStyle(anchor));
31518
- useLayoutEffect8(() => {
31773
+ const [style, setStyle] = useState57(() => menuStyle(anchor));
31774
+ useLayoutEffect9(() => {
31519
31775
  const panel = panelRef.current;
31520
31776
  if (!panel) return;
31521
31777
  const updatePosition = () => setStyle(menuStyle(anchor, panel));
@@ -31528,7 +31784,7 @@ function TimelineItemMenu({
31528
31784
  resizeObserver?.disconnect();
31529
31785
  };
31530
31786
  }, [anchor]);
31531
- useEffect45(() => {
31787
+ useEffect47(() => {
31532
31788
  const onKeyDown = (event) => {
31533
31789
  if (event.key !== "Escape") return;
31534
31790
  event.preventDefault();
@@ -31560,10 +31816,10 @@ function TimelineItemMenu({
31560
31816
  style: { ...style, ...accentStyle(accentColor) },
31561
31817
  children: [
31562
31818
  /* @__PURE__ */ jsxs49("div", { className: "squisq-timeline-item-menu-header", children: [
31563
- /* @__PURE__ */ jsx63("span", { className: "squisq-timeline-item-menu-kind", children: target.kind === "block" ? "Block" : "Video" }),
31564
- /* @__PURE__ */ jsx63("span", { className: "squisq-timeline-item-menu-title", title: target.title, children: target.title })
31819
+ /* @__PURE__ */ jsx64("span", { className: "squisq-timeline-item-menu-kind", children: target.kind === "block" ? "Block" : "Video" }),
31820
+ /* @__PURE__ */ jsx64("span", { className: "squisq-timeline-item-menu-title", title: target.title, children: target.title })
31565
31821
  ] }),
31566
- target.kind === "block" ? /* @__PURE__ */ jsx63(
31822
+ target.kind === "block" ? /* @__PURE__ */ jsx64(
31567
31823
  BlockMenu,
31568
31824
  {
31569
31825
  target,
@@ -31573,7 +31829,7 @@ function TimelineItemMenu({
31573
31829
  onStartTime: onBlockStartTime,
31574
31830
  onTransition: onBlockTransition
31575
31831
  }
31576
- ) : /* @__PURE__ */ jsx63(VideoMenu, { target, onPatch: onVideoPatch })
31832
+ ) : /* @__PURE__ */ jsx64(VideoMenu, { target, onPatch: onVideoPatch })
31577
31833
  ]
31578
31834
  }
31579
31835
  ),
@@ -31588,12 +31844,12 @@ function BlockMenu({
31588
31844
  onStartTime,
31589
31845
  onTransition
31590
31846
  }) {
31591
- const [explicitDuration, setExplicitDuration] = useState55(target.explicitDuration);
31592
- const [duration, setDuration] = useState55(formatNumber(target.duration));
31593
- const [startTime, setStartTime] = useState55(
31847
+ const [explicitDuration, setExplicitDuration] = useState57(target.explicitDuration);
31848
+ const [duration, setDuration] = useState57(formatNumber(target.duration));
31849
+ const [startTime, setStartTime] = useState57(
31594
31850
  target.startTime == null ? "" : formatNumber(target.startTime)
31595
31851
  );
31596
- const [transition, setTransition] = useState55(target.transition);
31852
+ const [transition, setTransition] = useState57(target.transition);
31597
31853
  const toggleAutotime = (autotimed) => {
31598
31854
  setExplicitDuration(!autotimed);
31599
31855
  if (autotimed) {
@@ -31606,7 +31862,7 @@ function BlockMenu({
31606
31862
  };
31607
31863
  return /* @__PURE__ */ jsxs49("div", { className: "squisq-timeline-item-menu-body", children: [
31608
31864
  /* @__PURE__ */ jsxs49("label", { className: "squisq-timeline-item-menu-check", children: [
31609
- /* @__PURE__ */ jsx63(
31865
+ /* @__PURE__ */ jsx64(
31610
31866
  "input",
31611
31867
  {
31612
31868
  type: "checkbox",
@@ -31614,10 +31870,10 @@ function BlockMenu({
31614
31870
  onChange: (event) => toggleAutotime(event.target.checked)
31615
31871
  }
31616
31872
  ),
31617
- /* @__PURE__ */ jsx63("span", { children: "Autotime block" })
31873
+ /* @__PURE__ */ jsx64("span", { children: "Autotime block" })
31618
31874
  ] }),
31619
- /* @__PURE__ */ jsx63("p", { className: "squisq-timeline-item-menu-hint", children: "Uses narration, content, and interior media when no explicit duration is set." }),
31620
- /* @__PURE__ */ jsx63(
31875
+ /* @__PURE__ */ jsx64("p", { className: "squisq-timeline-item-menu-hint", children: "Uses narration, content, and interior media when no explicit duration is set." }),
31876
+ /* @__PURE__ */ jsx64(
31621
31877
  MenuNumberField,
31622
31878
  {
31623
31879
  label: "Duration",
@@ -31631,7 +31887,7 @@ function BlockMenu({
31631
31887
  }
31632
31888
  }
31633
31889
  ),
31634
- /* @__PURE__ */ jsx63(
31890
+ /* @__PURE__ */ jsx64(
31635
31891
  MenuNumberField,
31636
31892
  {
31637
31893
  label: "Start time",
@@ -31645,8 +31901,8 @@ function BlockMenu({
31645
31901
  }
31646
31902
  ),
31647
31903
  /* @__PURE__ */ jsxs49("div", { className: "squisq-timeline-item-menu-row", children: [
31648
- /* @__PURE__ */ jsx63("span", { className: "squisq-timeline-item-menu-label", children: "Transition" }),
31649
- /* @__PURE__ */ jsx63(
31904
+ /* @__PURE__ */ jsx64("span", { className: "squisq-timeline-item-menu-label", children: "Transition" }),
31905
+ /* @__PURE__ */ jsx64(
31650
31906
  TransitionPicker,
31651
31907
  {
31652
31908
  value: transition,
@@ -31665,11 +31921,11 @@ function VideoMenu({
31665
31921
  target,
31666
31922
  onPatch
31667
31923
  }) {
31668
- const [placement, setPlacement] = useState55(target.placement);
31669
- const [locked, setLocked] = useState55(target.lockToBlock);
31670
- const [pipSize, setPipSize] = useState55(target.pipSize ?? "");
31671
- const [pipShape, setPipShape] = useState55(target.pipShape ?? "");
31672
- const [pipPosition, setPipPosition] = useState55(target.pipPosition ?? "");
31924
+ const [placement, setPlacement] = useState57(target.placement);
31925
+ const [locked, setLocked] = useState57(target.lockToBlock);
31926
+ const [pipSize, setPipSize] = useState57(target.pipSize ?? "");
31927
+ const [pipShape, setPipShape] = useState57(target.pipShape ?? "");
31928
+ const [pipPosition, setPipPosition] = useState57(target.pipPosition ?? "");
31673
31929
  const placed = placement === "picture-in-picture" || placement === "overlay";
31674
31930
  const placementOptions = [
31675
31931
  target.canUseContentPlacement ? { value: "content", label: "In layout" } : { value: "default", label: "Default" },
@@ -31678,14 +31934,14 @@ function VideoMenu({
31678
31934
  ];
31679
31935
  return /* @__PURE__ */ jsxs49("div", { className: "squisq-timeline-item-menu-body", children: [
31680
31936
  /* @__PURE__ */ jsxs49("div", { className: "squisq-timeline-item-menu-field", children: [
31681
- /* @__PURE__ */ jsx63("span", { className: "squisq-timeline-item-menu-label", children: "Placement" }),
31682
- /* @__PURE__ */ jsx63(
31937
+ /* @__PURE__ */ jsx64("span", { className: "squisq-timeline-item-menu-label", children: "Placement" }),
31938
+ /* @__PURE__ */ jsx64(
31683
31939
  "div",
31684
31940
  {
31685
31941
  className: "squisq-timeline-item-menu-segments",
31686
31942
  role: "group",
31687
31943
  "aria-label": "Video placement",
31688
- children: placementOptions.map((option) => /* @__PURE__ */ jsx63(
31944
+ children: placementOptions.map((option) => /* @__PURE__ */ jsx64(
31689
31945
  "button",
31690
31946
  {
31691
31947
  type: "button",
@@ -31707,7 +31963,7 @@ function VideoMenu({
31707
31963
  ] }),
31708
31964
  placed && /* @__PURE__ */ jsxs49(Fragment19, { children: [
31709
31965
  /* @__PURE__ */ jsxs49("label", { className: "squisq-timeline-item-menu-check", children: [
31710
- /* @__PURE__ */ jsx63(
31966
+ /* @__PURE__ */ jsx64(
31711
31967
  "input",
31712
31968
  {
31713
31969
  type: "checkbox",
@@ -31721,11 +31977,11 @@ function VideoMenu({
31721
31977
  }
31722
31978
  }
31723
31979
  ),
31724
- /* @__PURE__ */ jsx63("span", { children: "Lock to block" })
31980
+ /* @__PURE__ */ jsx64("span", { children: "Lock to block" })
31725
31981
  ] }),
31726
31982
  placement === "picture-in-picture" && /* @__PURE__ */ jsxs49(Fragment19, { children: [
31727
31983
  /* @__PURE__ */ jsxs49("label", { className: "squisq-timeline-item-menu-field", children: [
31728
- /* @__PURE__ */ jsx63("span", { className: "squisq-timeline-item-menu-label", children: "PIP size" }),
31984
+ /* @__PURE__ */ jsx64("span", { className: "squisq-timeline-item-menu-label", children: "PIP size" }),
31729
31985
  /* @__PURE__ */ jsxs49(
31730
31986
  "select",
31731
31987
  {
@@ -31742,14 +31998,14 @@ function VideoMenu({
31742
31998
  titleCase(target.defaultPipSize),
31743
31999
  ")"
31744
32000
  ] }),
31745
- /* @__PURE__ */ jsx63("option", { value: "small", children: "Small" }),
31746
- /* @__PURE__ */ jsx63("option", { value: "large", children: "Large" })
32001
+ /* @__PURE__ */ jsx64("option", { value: "small", children: "Small" }),
32002
+ /* @__PURE__ */ jsx64("option", { value: "large", children: "Large" })
31747
32003
  ]
31748
32004
  }
31749
32005
  )
31750
32006
  ] }),
31751
32007
  /* @__PURE__ */ jsxs49("label", { className: "squisq-timeline-item-menu-field", children: [
31752
- /* @__PURE__ */ jsx63("span", { className: "squisq-timeline-item-menu-label", children: "PIP shape" }),
32008
+ /* @__PURE__ */ jsx64("span", { className: "squisq-timeline-item-menu-label", children: "PIP shape" }),
31753
32009
  /* @__PURE__ */ jsxs49(
31754
32010
  "select",
31755
32011
  {
@@ -31766,14 +32022,14 @@ function VideoMenu({
31766
32022
  titleCase(target.defaultPipShape),
31767
32023
  ")"
31768
32024
  ] }),
31769
- /* @__PURE__ */ jsx63("option", { value: "square", children: "Square" }),
31770
- /* @__PURE__ */ jsx63("option", { value: "wide", children: "Wide (16:9)" })
32025
+ /* @__PURE__ */ jsx64("option", { value: "square", children: "Square" }),
32026
+ /* @__PURE__ */ jsx64("option", { value: "wide", children: "Wide (16:9)" })
31771
32027
  ]
31772
32028
  }
31773
32029
  )
31774
32030
  ] }),
31775
32031
  /* @__PURE__ */ jsxs49("label", { className: "squisq-timeline-item-menu-field", children: [
31776
- /* @__PURE__ */ jsx63("span", { className: "squisq-timeline-item-menu-label", children: "PIP position" }),
32032
+ /* @__PURE__ */ jsx64("span", { className: "squisq-timeline-item-menu-label", children: "PIP position" }),
31777
32033
  /* @__PURE__ */ jsxs49(
31778
32034
  "select",
31779
32035
  {
@@ -31790,10 +32046,10 @@ function VideoMenu({
31790
32046
  positionLabel(target.defaultPipPosition),
31791
32047
  ")"
31792
32048
  ] }),
31793
- /* @__PURE__ */ jsx63("option", { value: "top-left", children: "Top left" }),
31794
- /* @__PURE__ */ jsx63("option", { value: "top-right", children: "Top right" }),
31795
- /* @__PURE__ */ jsx63("option", { value: "bottom-left", children: "Bottom left" }),
31796
- /* @__PURE__ */ jsx63("option", { value: "bottom-right", children: "Bottom right" })
32049
+ /* @__PURE__ */ jsx64("option", { value: "top-left", children: "Top left" }),
32050
+ /* @__PURE__ */ jsx64("option", { value: "top-right", children: "Top right" }),
32051
+ /* @__PURE__ */ jsx64("option", { value: "bottom-left", children: "Bottom left" }),
32052
+ /* @__PURE__ */ jsx64("option", { value: "bottom-right", children: "Bottom right" })
31797
32053
  ]
31798
32054
  }
31799
32055
  )
@@ -31811,9 +32067,9 @@ function MenuNumberField({
31811
32067
  onChange
31812
32068
  }) {
31813
32069
  return /* @__PURE__ */ jsxs49("label", { className: "squisq-timeline-item-menu-field", children: [
31814
- /* @__PURE__ */ jsx63("span", { className: "squisq-timeline-item-menu-label", children: label }),
32070
+ /* @__PURE__ */ jsx64("span", { className: "squisq-timeline-item-menu-label", children: label }),
31815
32071
  /* @__PURE__ */ jsxs49("span", { className: "squisq-timeline-item-menu-number", children: [
31816
- /* @__PURE__ */ jsx63(
32072
+ /* @__PURE__ */ jsx64(
31817
32073
  "input",
31818
32074
  {
31819
32075
  type: "number",
@@ -31826,7 +32082,7 @@ function MenuNumberField({
31826
32082
  onChange: (event) => onChange(event.target.value)
31827
32083
  }
31828
32084
  ),
31829
- /* @__PURE__ */ jsx63("span", { children: "sec" })
32085
+ /* @__PURE__ */ jsx64("span", { children: "sec" })
31830
32086
  ] })
31831
32087
  ] });
31832
32088
  }
@@ -31871,7 +32127,7 @@ function menuStyle(anchor, element) {
31871
32127
  }
31872
32128
 
31873
32129
  // src/TimelineTrack.tsx
31874
- import { jsx as jsx64, jsxs as jsxs50 } from "react/jsx-runtime";
32130
+ import { jsx as jsx65, jsxs as jsxs50 } from "react/jsx-runtime";
31875
32131
  var PREVIEW_VIEWPORT = VIEWPORT_PRESETS6.landscape;
31876
32132
  var DEFAULT_PX_PER_SECOND = 18;
31877
32133
  var ZOOM_MIN = 4;
@@ -31894,9 +32150,9 @@ function TimelineVideoFilmstrip({
31894
32150
  FILMSTRIP_MAX_FRAMES,
31895
32151
  Math.max(1, Math.ceil(width / FILMSTRIP_FRAME_WIDTH))
31896
32152
  );
31897
- return /* @__PURE__ */ jsx64("div", { className: "squisq-timeline-video-filmstrip", "aria-hidden": "true", children: Array.from({ length: frameCount }, (_2, index2) => {
32153
+ return /* @__PURE__ */ jsx65("div", { className: "squisq-timeline-video-filmstrip", "aria-hidden": "true", children: Array.from({ length: frameCount }, (_2, index2) => {
31898
32154
  const sampleTime = Math.max(0, sourceStart) + Math.max(0, sourceLength) * ((index2 + 0.5) / frameCount);
31899
- return /* @__PURE__ */ jsx64(
32155
+ return /* @__PURE__ */ jsx65(
31900
32156
  "video",
31901
32157
  {
31902
32158
  src: resolvedSrc,
@@ -31939,14 +32195,14 @@ function TimelineTrack({
31939
32195
  colorScheme
31940
32196
  } = useEditorContext();
31941
32197
  const doc = docProp ?? contextDoc;
31942
- const [drag, setDrag] = useState56(null);
31943
- const [itemMenu, setItemMenu] = useState56(null);
31944
- const [pxPerSecond, setPxPerSecond] = useState56(DEFAULT_PX_PER_SECOND);
32198
+ const [drag, setDrag] = useState58(null);
32199
+ const [itemMenu, setItemMenu] = useState58(null);
32200
+ const [pxPerSecond, setPxPerSecond] = useState58(DEFAULT_PX_PER_SECOND);
31945
32201
  const scrollRef = useRef47(null);
31946
- const blocks = useMemo41(() => doc ? flattenBlocks6(doc.blocks) : [], [doc]);
32202
+ const blocks = useMemo42(() => doc ? flattenBlocks6(doc.blocks) : [], [doc]);
31947
32203
  const previewSettings = usePreviewSettingsOptional();
31948
32204
  const previewTheme = previewSettings?.activeTheme ?? DEFAULT_THEME6;
31949
- const visualByBlock = useMemo41(() => {
32205
+ const visualByBlock = useMemo42(() => {
31950
32206
  const map = /* @__PURE__ */ new Map();
31951
32207
  if (doc) {
31952
32208
  for (const b of blocks) {
@@ -31956,16 +32212,16 @@ function TimelineTrack({
31956
32212
  }
31957
32213
  return map;
31958
32214
  }, [doc, blocks, previewTheme]);
31959
- const derivedClips = useMemo41(
32215
+ const derivedClips = useMemo42(
31960
32216
  () => doc ? resolveMediaSchedule2(doc) : [],
31961
32217
  [doc]
31962
32218
  );
31963
32219
  const clips = schedule ?? derivedClips;
31964
- const playbackClips = useMemo41(
32220
+ const playbackClips = useMemo42(
31965
32221
  () => doc ? collectTimelinePlaybackSchedule(doc, clips) : clips,
31966
32222
  [clips, doc]
31967
32223
  );
31968
- const total = useMemo41(() => doc ? getDocPlaybackDuration(doc) : 0, [doc]);
32224
+ const total = useMemo42(() => doc ? getDocPlaybackDuration(doc) : 0, [doc]);
31969
32225
  const width = Math.max(total * pxPerSecond, 200);
31970
32226
  const internalClock = useTimelineClock(total);
31971
32227
  const activeClock = clock ?? internalClock;
@@ -31974,8 +32230,8 @@ function TimelineTrack({
31974
32230
  setDrag(null);
31975
32231
  play();
31976
32232
  }, [play]);
31977
- const [scrubbing, setScrubbing] = useState56(false);
31978
- useEffect46(() => {
32233
+ const [scrubbing, setScrubbing] = useState58(false);
32234
+ useEffect48(() => {
31979
32235
  if (!scrubbing) return;
31980
32236
  const onMove = (e2) => {
31981
32237
  const scroll = scrollRef.current;
@@ -31992,7 +32248,7 @@ function TimelineTrack({
31992
32248
  window.removeEventListener("pointerup", onUp);
31993
32249
  };
31994
32250
  }, [scrubbing, pxPerSecond, seek]);
31995
- const rawClipById = useMemo41(() => {
32251
+ const rawClipById = useMemo42(() => {
31996
32252
  const map = /* @__PURE__ */ new Map();
31997
32253
  if (doc) {
31998
32254
  for (const b of flattenBlocks6(doc.blocks)) for (const m of b.media ?? []) map.set(m.id, m);
@@ -32013,7 +32269,7 @@ function TimelineTrack({
32013
32269
  [blocks]
32014
32270
  );
32015
32271
  const followedBlockRef = useRef47(null);
32016
- useEffect46(() => {
32272
+ useEffect48(() => {
32017
32273
  if (!isPlaying) {
32018
32274
  followedBlockRef.current = null;
32019
32275
  return;
@@ -32024,7 +32280,7 @@ function TimelineTrack({
32024
32280
  const line = headingLine(block);
32025
32281
  if (line != null) goToBlockByLine(line);
32026
32282
  }, [isPlaying, currentTime, blockAtTime, goToBlockByLine]);
32027
- useEffect46(() => {
32283
+ useEffect48(() => {
32028
32284
  if (!isPlaying) return;
32029
32285
  const scroll = scrollRef.current;
32030
32286
  if (!scroll) return;
@@ -32072,8 +32328,8 @@ function TimelineTrack({
32072
32328
  );
32073
32329
  const zoomIn = useCallback44(() => setPxPerSecond((s) => Math.min(ZOOM_MAX, s * ZOOM_FACTOR)), []);
32074
32330
  const zoomOut = useCallback44(() => setPxPerSecond((s) => Math.max(ZOOM_MIN, s / ZOOM_FACTOR)), []);
32075
- const [viewportWidth, setViewportWidth] = useState56(0);
32076
- useEffect46(() => {
32331
+ const [viewportWidth, setViewportWidth] = useState58(0);
32332
+ useEffect48(() => {
32077
32333
  const el = scrollRef.current;
32078
32334
  if (!el || typeof ResizeObserver === "undefined") return;
32079
32335
  const update = () => setViewportWidth(el.clientWidth);
@@ -32087,7 +32343,7 @@ function TimelineTrack({
32087
32343
  const dragRef = useRef47(null);
32088
32344
  dragRef.current = drag;
32089
32345
  const isDragging = drag != null && !drag.committed;
32090
- useEffect46(() => {
32346
+ useEffect48(() => {
32091
32347
  if (!isDragging) return;
32092
32348
  const onMove = (e2) => {
32093
32349
  const d = dragRef.current;
@@ -32118,7 +32374,7 @@ function TimelineTrack({
32118
32374
  window.removeEventListener("pointerup", onUp);
32119
32375
  };
32120
32376
  }, [isDragging]);
32121
- useEffect46(() => {
32377
+ useEffect48(() => {
32122
32378
  if (dragRef.current?.committed) setDrag(null);
32123
32379
  }, [doc]);
32124
32380
  const beginDrag = useCallback44(
@@ -32201,7 +32457,7 @@ function TimelineTrack({
32201
32457
  for (let t = 0; t <= rulerEnd + 1e-3 && ticks.length < 1e3; t += tickSeconds) ticks.push(t);
32202
32458
  return /* @__PURE__ */ jsxs50("div", { className: "squisq-timeline", style: { height }, "data-testid": "timeline-track", children: [
32203
32459
  /* @__PURE__ */ jsxs50("div", { className: "squisq-timeline-controls", children: [
32204
- /* @__PURE__ */ jsx64(
32460
+ /* @__PURE__ */ jsx65(
32205
32461
  "button",
32206
32462
  {
32207
32463
  type: "button",
@@ -32218,7 +32474,7 @@ function TimelineTrack({
32218
32474
  " / ",
32219
32475
  formatClock2(total)
32220
32476
  ] }),
32221
- /* @__PURE__ */ jsx64(
32477
+ /* @__PURE__ */ jsx65(
32222
32478
  "button",
32223
32479
  {
32224
32480
  type: "button",
@@ -32230,7 +32486,7 @@ function TimelineTrack({
32230
32486
  children: "\u2212"
32231
32487
  }
32232
32488
  ),
32233
- /* @__PURE__ */ jsx64(
32489
+ /* @__PURE__ */ jsx65(
32234
32490
  "button",
32235
32491
  {
32236
32492
  type: "button",
@@ -32243,8 +32499,8 @@ function TimelineTrack({
32243
32499
  }
32244
32500
  )
32245
32501
  ] }),
32246
- /* @__PURE__ */ jsx64("div", { className: "squisq-timeline-scroll", ref: scrollRef, children: /* @__PURE__ */ jsxs50("div", { className: "squisq-timeline-inner", style: { width }, children: [
32247
- /* @__PURE__ */ jsx64("div", { className: "squisq-timeline-row squisq-timeline-row--blocks", children: blocks.map((b, i) => {
32502
+ /* @__PURE__ */ jsx65("div", { className: "squisq-timeline-scroll", ref: scrollRef, children: /* @__PURE__ */ jsxs50("div", { className: "squisq-timeline-inner", style: { width }, children: [
32503
+ /* @__PURE__ */ jsx65("div", { className: "squisq-timeline-row squisq-timeline-row--blocks", children: blocks.map((b, i) => {
32248
32504
  const line = headingLine(b);
32249
32505
  const isActive = line != null && line === activeBlockStartLine;
32250
32506
  const left = previewLeft(i);
@@ -32261,7 +32517,7 @@ function TimelineTrack({
32261
32517
  if (line != null) goToBlockByLine(line);
32262
32518
  },
32263
32519
  children: [
32264
- visualByBlock.has(b.id) && /* @__PURE__ */ jsx64("div", { className: "squisq-timeline-block-thumb", "aria-hidden": true, children: /* @__PURE__ */ jsx64(
32520
+ visualByBlock.has(b.id) && /* @__PURE__ */ jsx65("div", { className: "squisq-timeline-block-thumb", "aria-hidden": true, children: /* @__PURE__ */ jsx65(
32265
32521
  BlockThumbnail,
32266
32522
  {
32267
32523
  visual: visualByBlock.get(b.id),
@@ -32269,7 +32525,7 @@ function TimelineTrack({
32269
32525
  mediaProvider
32270
32526
  }
32271
32527
  ) }),
32272
- prev && headingLine(prev) != null && /* @__PURE__ */ jsx64(
32528
+ prev && headingLine(prev) != null && /* @__PURE__ */ jsx65(
32273
32529
  "span",
32274
32530
  {
32275
32531
  className: "squisq-timeline-edge squisq-timeline-edge--left",
@@ -32290,8 +32546,8 @@ function TimelineTrack({
32290
32546
  )
32291
32547
  }
32292
32548
  ),
32293
- /* @__PURE__ */ jsx64("span", { className: "squisq-timeline-block-label", children: b.title ?? b.id }),
32294
- line != null && /* @__PURE__ */ jsx64(
32549
+ /* @__PURE__ */ jsx65("span", { className: "squisq-timeline-block-label", children: b.title ?? b.id }),
32550
+ line != null && /* @__PURE__ */ jsx65(
32295
32551
  "button",
32296
32552
  {
32297
32553
  type: "button",
@@ -32313,7 +32569,7 @@ function TimelineTrack({
32313
32569
  children: "\u2026"
32314
32570
  }
32315
32571
  ),
32316
- line != null && /* @__PURE__ */ jsx64(
32572
+ line != null && /* @__PURE__ */ jsx65(
32317
32573
  "span",
32318
32574
  {
32319
32575
  className: "squisq-timeline-edge squisq-timeline-edge--right",
@@ -32361,7 +32617,7 @@ function TimelineTrack({
32361
32617
  spillover: raw.spillover
32362
32618
  } : { kind: c.kind, src: c.src, clipStart: c.sourceIn };
32363
32619
  };
32364
- return /* @__PURE__ */ jsx64(
32620
+ return /* @__PURE__ */ jsx65(
32365
32621
  "div",
32366
32622
  {
32367
32623
  className: "squisq-timeline-row squisq-timeline-row--media",
@@ -32398,7 +32654,7 @@ function TimelineTrack({
32398
32654
  if (next) setMarkdownSource(next);
32399
32655
  },
32400
32656
  children: [
32401
- c.kind === "video" && /* @__PURE__ */ jsx64(
32657
+ c.kind === "video" && /* @__PURE__ */ jsx65(
32402
32658
  TimelineVideoFilmstrip,
32403
32659
  {
32404
32660
  src: c.src,
@@ -32407,8 +32663,8 @@ function TimelineTrack({
32407
32663
  width: clipWidth
32408
32664
  }
32409
32665
  ),
32410
- /* @__PURE__ */ jsx64("span", { className: "squisq-timeline-clip-label", children: timelineMediaLabel(c.src, c.kind) }),
32411
- c.kind === "video" && c.sourceLine != null && /* @__PURE__ */ jsx64(
32666
+ /* @__PURE__ */ jsx65("span", { className: "squisq-timeline-clip-label", children: timelineMediaLabel(c.src, c.kind) }),
32667
+ c.kind === "video" && c.sourceLine != null && /* @__PURE__ */ jsx65(
32412
32668
  "button",
32413
32669
  {
32414
32670
  type: "button",
@@ -32434,7 +32690,7 @@ function TimelineTrack({
32434
32690
  children: "\u2026"
32435
32691
  }
32436
32692
  ),
32437
- editable && /* @__PURE__ */ jsx64(
32693
+ editable && /* @__PURE__ */ jsx65(
32438
32694
  "span",
32439
32695
  {
32440
32696
  className: "squisq-timeline-edge squisq-timeline-edge--right",
@@ -32479,7 +32735,7 @@ function TimelineTrack({
32479
32735
  ...m.clipStart != null ? { clipStart: m.clipStart } : {},
32480
32736
  ...m.clipEnd != null ? { clipEnd: m.clipEnd } : {}
32481
32737
  };
32482
- return /* @__PURE__ */ jsx64(
32738
+ return /* @__PURE__ */ jsx65(
32483
32739
  "div",
32484
32740
  {
32485
32741
  className: "squisq-timeline-row squisq-timeline-row--media",
@@ -32506,7 +32762,7 @@ function TimelineTrack({
32506
32762
  { onClick: () => selectClipAt(b.startTime, b.id) }
32507
32763
  ),
32508
32764
  children: [
32509
- m.kind === "video" && /* @__PURE__ */ jsx64(
32765
+ m.kind === "video" && /* @__PURE__ */ jsx65(
32510
32766
  TimelineVideoFilmstrip,
32511
32767
  {
32512
32768
  src: m.src,
@@ -32515,8 +32771,8 @@ function TimelineTrack({
32515
32771
  width: clipWidth
32516
32772
  }
32517
32773
  ),
32518
- /* @__PURE__ */ jsx64("span", { className: "squisq-timeline-clip-label", children: timelineMediaLabel(m.src, m.kind) }),
32519
- m.kind === "video" && m.sourceLine != null && /* @__PURE__ */ jsx64(
32774
+ /* @__PURE__ */ jsx65("span", { className: "squisq-timeline-clip-label", children: timelineMediaLabel(m.src, m.kind) }),
32775
+ m.kind === "video" && m.sourceLine != null && /* @__PURE__ */ jsx65(
32520
32776
  "button",
32521
32777
  {
32522
32778
  type: "button",
@@ -32541,7 +32797,7 @@ function TimelineTrack({
32541
32797
  children: "\u2026"
32542
32798
  }
32543
32799
  ),
32544
- m.sourceLine != null && /* @__PURE__ */ jsx64(
32800
+ m.sourceLine != null && /* @__PURE__ */ jsx65(
32545
32801
  "span",
32546
32802
  {
32547
32803
  className: "squisq-timeline-edge squisq-timeline-edge--right",
@@ -32563,7 +32819,7 @@ function TimelineTrack({
32563
32819
  })
32564
32820
  )
32565
32821
  ] }),
32566
- /* @__PURE__ */ jsx64(
32822
+ /* @__PURE__ */ jsx65(
32567
32823
  "div",
32568
32824
  {
32569
32825
  className: "squisq-timeline-row squisq-timeline-row--ruler",
@@ -32571,10 +32827,10 @@ function TimelineTrack({
32571
32827
  const rect = e2.currentTarget.getBoundingClientRect();
32572
32828
  seek((e2.clientX - rect.left) / pxPerSecond);
32573
32829
  },
32574
- children: ticks.map((t) => /* @__PURE__ */ jsx64("div", { className: "squisq-timeline-tick", style: { left: t * pxPerSecond }, children: /* @__PURE__ */ jsx64("span", { className: "squisq-timeline-tick-label", children: formatDur(t) }) }, t))
32830
+ children: ticks.map((t) => /* @__PURE__ */ jsx65("div", { className: "squisq-timeline-tick", style: { left: t * pxPerSecond }, children: /* @__PURE__ */ jsx65("span", { className: "squisq-timeline-tick-label", children: formatDur(t) }) }, t))
32575
32831
  }
32576
32832
  ),
32577
- /* @__PURE__ */ jsx64(
32833
+ /* @__PURE__ */ jsx65(
32578
32834
  "div",
32579
32835
  {
32580
32836
  className: "squisq-timeline-playhead",
@@ -32586,11 +32842,11 @@ function TimelineTrack({
32586
32842
  pause();
32587
32843
  setScrubbing(true);
32588
32844
  },
32589
- children: /* @__PURE__ */ jsx64("div", { className: "squisq-timeline-playhead-knob" })
32845
+ children: /* @__PURE__ */ jsx65("div", { className: "squisq-timeline-playhead-knob" })
32590
32846
  }
32591
32847
  )
32592
32848
  ] }) }),
32593
- /* @__PURE__ */ jsx64("div", { ref: activeClock.registerMediaHost, className: "squisq-timeline-media-host", "aria-hidden": true, children: /* @__PURE__ */ jsx64(MediaContext6.Provider, { value: mediaProvider ?? null, children: /* @__PURE__ */ jsx64(
32849
+ /* @__PURE__ */ jsx65("div", { ref: activeClock.registerMediaHost, className: "squisq-timeline-media-host", "aria-hidden": true, children: /* @__PURE__ */ jsx65(MediaContext6.Provider, { value: mediaProvider ?? null, children: /* @__PURE__ */ jsx65(
32594
32850
  MediaClipLayer,
32595
32851
  {
32596
32852
  schedule: playbackClips,
@@ -32599,7 +32855,7 @@ function TimelineTrack({
32599
32855
  basePath
32600
32856
  }
32601
32857
  ) }) }),
32602
- itemMenu && itemMenuTarget && /* @__PURE__ */ jsx64(
32858
+ itemMenu && itemMenuTarget && /* @__PURE__ */ jsx65(
32603
32859
  TimelineItemMenu,
32604
32860
  {
32605
32861
  anchor: itemMenu.anchor,
@@ -32654,9 +32910,9 @@ function formatDur(seconds) {
32654
32910
  }
32655
32911
 
32656
32912
  // src/TimelineVideoPanel.tsx
32657
- import { useMemo as useMemo42 } from "react";
32913
+ import { useMemo as useMemo43 } from "react";
32658
32914
  import { MediaClipLayer as MediaClipLayer2 } from "@bendyline/squisq-react";
32659
- import { jsx as jsx65, jsxs as jsxs51 } from "react/jsx-runtime";
32915
+ import { jsx as jsx66, jsxs as jsxs51 } from "react/jsx-runtime";
32660
32916
  function TimelineVideoPanel({
32661
32917
  schedule,
32662
32918
  currentTime,
@@ -32664,7 +32920,7 @@ function TimelineVideoPanel({
32664
32920
  basePath = "/",
32665
32921
  onClose
32666
32922
  }) {
32667
- const videos = useMemo42(() => schedule.filter((clip) => clip.kind === "video"), [schedule]);
32923
+ const videos = useMemo43(() => schedule.filter((clip) => clip.kind === "video"), [schedule]);
32668
32924
  const activeVideos = videos.filter(
32669
32925
  (clip) => currentTime >= clip.absoluteStart && currentTime < clip.absoluteEnd
32670
32926
  );
@@ -32677,10 +32933,10 @@ function TimelineVideoPanel({
32677
32933
  "aria-label": "Timeline video preview",
32678
32934
  children: [
32679
32935
  /* @__PURE__ */ jsxs51("header", { className: "squisq-timeline-video-header", children: [
32680
- /* @__PURE__ */ jsx65("span", { className: "squisq-timeline-video-title", children: "Video monitor" }),
32681
- /* @__PURE__ */ jsx65("span", { className: "squisq-timeline-video-label", title: activeLabel, children: activeLabel }),
32682
- /* @__PURE__ */ jsx65("span", { className: "squisq-timeline-video-time", children: formatClock3(currentTime) }),
32683
- onClose && /* @__PURE__ */ jsx65(
32936
+ /* @__PURE__ */ jsx66("span", { className: "squisq-timeline-video-title", children: "Video monitor" }),
32937
+ /* @__PURE__ */ jsx66("span", { className: "squisq-timeline-video-label", title: activeLabel, children: activeLabel }),
32938
+ /* @__PURE__ */ jsx66("span", { className: "squisq-timeline-video-time", children: formatClock3(currentTime) }),
32939
+ onClose && /* @__PURE__ */ jsx66(
32684
32940
  "button",
32685
32941
  {
32686
32942
  type: "button",
@@ -32692,8 +32948,8 @@ function TimelineVideoPanel({
32692
32948
  }
32693
32949
  )
32694
32950
  ] }),
32695
- /* @__PURE__ */ jsx65("div", { className: "squisq-timeline-video-body", children: /* @__PURE__ */ jsxs51("div", { className: "squisq-timeline-video-stage", children: [
32696
- /* @__PURE__ */ jsx65(
32951
+ /* @__PURE__ */ jsx66("div", { className: "squisq-timeline-video-body", children: /* @__PURE__ */ jsxs51("div", { className: "squisq-timeline-video-stage", children: [
32952
+ /* @__PURE__ */ jsx66(
32697
32953
  MediaClipLayer2,
32698
32954
  {
32699
32955
  schedule: videos,
@@ -32705,7 +32961,7 @@ function TimelineVideoPanel({
32705
32961
  muted: true
32706
32962
  }
32707
32963
  ),
32708
- activeVideos.length === 0 && /* @__PURE__ */ jsx65("div", { className: "squisq-timeline-video-empty", children: "Move the playhead onto a video clip" })
32964
+ activeVideos.length === 0 && /* @__PURE__ */ jsx66("div", { className: "squisq-timeline-video-empty", children: "Move the playhead onto a video clip" })
32709
32965
  ] }) })
32710
32966
  ]
32711
32967
  }
@@ -32718,12 +32974,12 @@ function formatClock3(seconds) {
32718
32974
  }
32719
32975
 
32720
32976
  // src/TimelineCompositionPanel.tsx
32721
- import { useMemo as useMemo43 } from "react";
32977
+ import { useMemo as useMemo44 } from "react";
32722
32978
  import { getDocPlaybackDuration as getDocPlaybackDuration2, resolveMediaSchedule as resolveMediaSchedule3 } from "@bendyline/squisq/schemas";
32723
32979
  import { DocPlayer } from "@bendyline/squisq-react";
32724
32980
 
32725
32981
  // src/usePreviewProjection.ts
32726
- import { useEffect as useEffect47, useState as useState57 } from "react";
32982
+ import { useEffect as useEffect49, useState as useState59 } from "react";
32727
32983
  import { resolveAudioMapping } from "@bendyline/squisq/doc";
32728
32984
  import { applyTransform } from "@bendyline/squisq/transform";
32729
32985
  function hasEquivalentAudio(left, right) {
@@ -32743,8 +32999,8 @@ function preserveEquivalentAudio(previous, next) {
32743
32999
  };
32744
33000
  }
32745
33001
  function usePreviewProjection(doc, transformStyle, workspaceContainer, documentTitle2) {
32746
- const [projection, setProjection] = useState57(null);
32747
- useEffect47(() => {
33002
+ const [projection, setProjection] = useState59(null);
33003
+ useEffect49(() => {
32748
33004
  if (!doc || !doc.blocks.length) {
32749
33005
  setProjection(null);
32750
33006
  return;
@@ -32778,7 +33034,7 @@ function usePreviewProjection(doc, transformStyle, workspaceContainer, documentT
32778
33034
  }
32779
33035
 
32780
33036
  // src/TimelineCompositionPanel.tsx
32781
- import { jsx as jsx66, jsxs as jsxs52 } from "react/jsx-runtime";
33037
+ import { jsx as jsx67, jsxs as jsxs52 } from "react/jsx-runtime";
32782
33038
  function TimelineCompositionPanel({
32783
33039
  doc,
32784
33040
  clock,
@@ -32810,11 +33066,11 @@ function TimelineCompositionPanel({
32810
33066
  );
32811
33067
  const contentDoc = projection?.contentDoc ?? null;
32812
33068
  const playerDoc = projection?.playerDoc ?? null;
32813
- const totalDuration = useMemo43(
33069
+ const totalDuration = useMemo44(
32814
33070
  () => playerDoc ? getDocPlaybackDuration2(playerDoc) : 0,
32815
33071
  [playerDoc]
32816
33072
  );
32817
- const effectiveVideoPresentation = useMemo43(() => {
33073
+ const effectiveVideoPresentation = useMemo44(() => {
32818
33074
  if (!playerDoc) return activeVideoPresentation;
32819
33075
  const activeVideo = resolveMediaSchedule3(playerDoc).find(
32820
33076
  (clip) => clip.kind === "video" && clock.currentTime >= clip.absoluteStart && clock.currentTime < clip.absoluteEnd
@@ -32823,7 +33079,7 @@ function TimelineCompositionPanel({
32823
33079
  if (activeVideo?.placement === "overlay") return "full-frame";
32824
33080
  return activeVideoPresentation;
32825
33081
  }, [activeVideoPresentation, clock.currentTime, playerDoc]);
32826
- const audioController = useMemo43(
33082
+ const audioController = useMemo44(
32827
33083
  () => ({
32828
33084
  currentTime: clock.currentTime,
32829
33085
  isPlaying: clock.isPlaying,
@@ -32849,11 +33105,11 @@ function TimelineCompositionPanel({
32849
33105
  "aria-label": "Timeline video composition preview",
32850
33106
  children: [
32851
33107
  /* @__PURE__ */ jsxs52("header", { className: "squisq-timeline-video-header", children: [
32852
- /* @__PURE__ */ jsx66("span", { className: "squisq-timeline-video-title", children: "Video composition" }),
32853
- /* @__PURE__ */ jsx66("span", { className: "squisq-timeline-video-label", children: formatPresentation(effectiveVideoPresentation) }),
32854
- /* @__PURE__ */ jsx66(PreviewToolbarControls, { displayMode: "video" }),
32855
- /* @__PURE__ */ jsx66("span", { className: "squisq-timeline-video-time", children: formatClock4(clock.currentTime) }),
32856
- onClose && /* @__PURE__ */ jsx66(
33108
+ /* @__PURE__ */ jsx67("span", { className: "squisq-timeline-video-title", children: "Video composition" }),
33109
+ /* @__PURE__ */ jsx67("span", { className: "squisq-timeline-video-label", children: formatPresentation(effectiveVideoPresentation) }),
33110
+ /* @__PURE__ */ jsx67(PreviewToolbarControls, { displayMode: "video" }),
33111
+ /* @__PURE__ */ jsx67("span", { className: "squisq-timeline-video-time", children: formatClock4(clock.currentTime) }),
33112
+ onClose && /* @__PURE__ */ jsx67(
32857
33113
  "button",
32858
33114
  {
32859
33115
  type: "button",
@@ -32865,7 +33121,7 @@ function TimelineCompositionPanel({
32865
33121
  }
32866
33122
  )
32867
33123
  ] }),
32868
- /* @__PURE__ */ jsx66("div", { className: "squisq-timeline-video-body", children: /* @__PURE__ */ jsx66("div", { className: "squisq-timeline-composition-stage", children: playerDoc ? /* @__PURE__ */ jsx66(
33124
+ /* @__PURE__ */ jsx67("div", { className: "squisq-timeline-video-body", children: /* @__PURE__ */ jsx67("div", { className: "squisq-timeline-composition-stage", children: playerDoc ? /* @__PURE__ */ jsx67(
32869
33125
  DocPlayer,
32870
33126
  {
32871
33127
  doc: playerDoc,
@@ -32891,7 +33147,7 @@ function TimelineCompositionPanel({
32891
33147
  enableSwipe: false,
32892
33148
  globalKeyboardShortcuts: false
32893
33149
  }
32894
- ) : /* @__PURE__ */ jsx66("div", { className: "squisq-timeline-video-empty", children: "Preparing video composition\u2026" }) }) })
33150
+ ) : /* @__PURE__ */ jsx67("div", { className: "squisq-timeline-video-empty", children: "Preparing video composition\u2026" }) }) })
32895
33151
  ]
32896
33152
  }
32897
33153
  );
@@ -32908,7 +33164,7 @@ function formatPresentation(presentation) {
32908
33164
  }
32909
33165
 
32910
33166
  // src/TimelineToolbar.tsx
32911
- import { jsx as jsx67, jsxs as jsxs53 } from "react/jsx-runtime";
33167
+ import { jsx as jsx68, jsxs as jsxs53 } from "react/jsx-runtime";
32912
33168
  function TimelineToolbar({
32913
33169
  literalVideoVisible,
32914
33170
  compositionVisible,
@@ -32925,7 +33181,7 @@ function TimelineToolbar({
32925
33181
  "aria-label": "Timeline view controls",
32926
33182
  "data-testid": "timeline-toolbar",
32927
33183
  children: [
32928
- /* @__PURE__ */ jsx67("span", { className: "squisq-timeline-toolbar-title", children: "Preview panes" }),
33184
+ /* @__PURE__ */ jsx68("span", { className: "squisq-timeline-toolbar-title", children: "Preview panes" }),
32929
33185
  /* @__PURE__ */ jsxs53(
32930
33186
  "button",
32931
33187
  {
@@ -32938,7 +33194,7 @@ function TimelineToolbar({
32938
33194
  "data-tooltip": literalVideoVisible ? "Hide literal video" : "Show literal video",
32939
33195
  children: [
32940
33196
  /* @__PURE__ */ jsxs53("svg", { width: "15", height: "15", viewBox: "0 0 15 15", "aria-hidden": "true", children: [
32941
- /* @__PURE__ */ jsx67(
33197
+ /* @__PURE__ */ jsx68(
32942
33198
  "rect",
32943
33199
  {
32944
33200
  x: "1.5",
@@ -32951,7 +33207,7 @@ function TimelineToolbar({
32951
33207
  strokeWidth: "1.4"
32952
33208
  }
32953
33209
  ),
32954
- /* @__PURE__ */ jsx67(
33210
+ /* @__PURE__ */ jsx68(
32955
33211
  "path",
32956
33212
  {
32957
33213
  d: "M10 5.5 13.5 4v7L10 9.5z",
@@ -32962,7 +33218,7 @@ function TimelineToolbar({
32962
33218
  }
32963
33219
  )
32964
33220
  ] }),
32965
- /* @__PURE__ */ jsx67("span", { children: "Video monitor" })
33221
+ /* @__PURE__ */ jsx68("span", { children: "Video monitor" })
32966
33222
  ]
32967
33223
  }
32968
33224
  ),
@@ -32978,7 +33234,7 @@ function TimelineToolbar({
32978
33234
  "data-tooltip": compositionVisible ? "Hide video composition" : "Show video composition",
32979
33235
  children: [
32980
33236
  /* @__PURE__ */ jsxs53("svg", { width: "15", height: "15", viewBox: "0 0 15 15", "aria-hidden": "true", children: [
32981
- /* @__PURE__ */ jsx67(
33237
+ /* @__PURE__ */ jsx68(
32982
33238
  "rect",
32983
33239
  {
32984
33240
  x: "1.5",
@@ -32991,10 +33247,10 @@ function TimelineToolbar({
32991
33247
  strokeWidth: "1.4"
32992
33248
  }
32993
33249
  ),
32994
- /* @__PURE__ */ jsx67("path", { d: "M5 13h5M7.5 11v2", stroke: "currentColor", strokeWidth: "1.4" }),
32995
- /* @__PURE__ */ jsx67("path", { d: "m6.3 5 3 1.5-3 1.5z", fill: "currentColor" })
33250
+ /* @__PURE__ */ jsx68("path", { d: "M5 13h5M7.5 11v2", stroke: "currentColor", strokeWidth: "1.4" }),
33251
+ /* @__PURE__ */ jsx68("path", { d: "m6.3 5 3 1.5-3 1.5z", fill: "currentColor" })
32996
33252
  ] }),
32997
- /* @__PURE__ */ jsx67("span", { children: "Video composition" })
33253
+ /* @__PURE__ */ jsx68("span", { children: "Video composition" })
32998
33254
  ]
32999
33255
  }
33000
33256
  )
@@ -33004,7 +33260,7 @@ function TimelineToolbar({
33004
33260
  }
33005
33261
 
33006
33262
  // src/PlainHtmlPreview.tsx
33007
- import { useCallback as useCallback45, useEffect as useEffect48, useMemo as useMemo44, useRef as useRef48, useState as useState58 } from "react";
33263
+ import { useCallback as useCallback45, useEffect as useEffect50, useMemo as useMemo45, useRef as useRef48, useState as useState60 } from "react";
33008
33264
  import { parseMarkdown as parseMarkdown9 } from "@bendyline/squisq/markdown";
33009
33265
 
33010
33266
  // src/utils/collectInlineFontAwesomeCss.ts
@@ -33039,7 +33295,7 @@ function isFontAwesomeRule(rule) {
33039
33295
  }
33040
33296
 
33041
33297
  // src/PlainHtmlPreview.tsx
33042
- import { jsx as jsx68 } from "react/jsx-runtime";
33298
+ import { jsx as jsx69 } from "react/jsx-runtime";
33043
33299
  var cachedRender = null;
33044
33300
  var cachedRenderPromise = null;
33045
33301
  function loadRenderFn() {
@@ -33070,10 +33326,13 @@ function PlainHtmlPreview({
33070
33326
  style,
33071
33327
  globalKeyboardShortcuts = false,
33072
33328
  onFrameChange,
33073
- onLinkClick
33329
+ onLinkClick,
33330
+ showCodeCopyButton = false,
33331
+ onCopyCode
33074
33332
  }) {
33075
33333
  const iframeRef = useRef48(null);
33076
33334
  const removeFrameLinkHandlerRef = useRef48(() => void 0);
33335
+ const removeFrameCodeCopyControlsRef = useRef48(() => void 0);
33077
33336
  const setIframeRef = useCallback45(
33078
33337
  (frame) => {
33079
33338
  iframeRef.current = frame;
@@ -33081,9 +33340,9 @@ function PlainHtmlPreview({
33081
33340
  },
33082
33341
  [onFrameChange]
33083
33342
  );
33084
- const mdDoc = useMemo44(() => parseMarkdown9(markdown), [markdown]);
33085
- const [resolvedImages, setResolvedImages] = useState58(null);
33086
- useEffect48(() => {
33343
+ const mdDoc = useMemo45(() => parseMarkdown9(markdown), [markdown]);
33344
+ const [resolvedImages, setResolvedImages] = useState60(null);
33345
+ useEffect50(() => {
33087
33346
  if (!mediaProvider) {
33088
33347
  setResolvedImages(null);
33089
33348
  return;
@@ -33111,16 +33370,16 @@ function PlainHtmlPreview({
33111
33370
  cancelled = true;
33112
33371
  };
33113
33372
  }, [mdDoc, mediaProvider, mediaRevision]);
33114
- const mergedImages = useMemo44(() => {
33373
+ const mergedImages = useMemo45(() => {
33115
33374
  if (!resolvedImages && !images) return void 0;
33116
33375
  const merged = /* @__PURE__ */ new Map();
33117
33376
  if (resolvedImages) for (const [k2, v2] of resolvedImages) merged.set(k2, v2);
33118
33377
  if (images) for (const [k2, v2] of images) merged.set(k2, v2);
33119
33378
  return merged;
33120
33379
  }, [resolvedImages, images]);
33121
- const iconsCss = useMemo44(() => collectInlineFontAwesomeCss(), []);
33122
- const [renderFn, setRenderFn] = useState58(() => cachedRender);
33123
- useEffect48(() => {
33380
+ const iconsCss = useMemo45(() => collectInlineFontAwesomeCss(), []);
33381
+ const [renderFn, setRenderFn] = useState60(() => cachedRender);
33382
+ useEffect50(() => {
33124
33383
  if (renderFn) return;
33125
33384
  let cancelled = false;
33126
33385
  loadRenderFn().then((fn) => {
@@ -33130,7 +33389,7 @@ function PlainHtmlPreview({
33130
33389
  cancelled = true;
33131
33390
  };
33132
33391
  }, [renderFn]);
33133
- const html = useMemo44(
33392
+ const html = useMemo45(
33134
33393
  () => renderFn ? renderFn(mdDoc, { title, images: mergedImages, theme, iconsCss }) : "",
33135
33394
  [renderFn, mdDoc, title, mergedImages, theme, iconsCss]
33136
33395
  );
@@ -33153,11 +33412,107 @@ function PlainHtmlPreview({
33153
33412
  frameDocument.addEventListener("click", handleClick, true);
33154
33413
  removeFrameLinkHandlerRef.current = () => frameDocument.removeEventListener("click", handleClick, true);
33155
33414
  }, [onLinkClick]);
33156
- useEffect48(() => {
33415
+ useEffect50(() => {
33157
33416
  installFrameLinkHandler();
33158
33417
  return () => removeFrameLinkHandlerRef.current();
33159
33418
  }, [html, installFrameLinkHandler]);
33160
- useEffect48(() => {
33419
+ const installFrameCodeCopyControls = useCallback45(() => {
33420
+ removeFrameCodeCopyControlsRef.current();
33421
+ removeFrameCodeCopyControlsRef.current = () => void 0;
33422
+ const frameDocument = iframeRef.current?.contentDocument;
33423
+ if (!frameDocument || !showCodeCopyButton) return;
33424
+ const style2 = frameDocument.createElement("style");
33425
+ style2.dataset.squisqCodeCopy = "";
33426
+ style2.textContent = `
33427
+ .squisq-preview-code-copy-frame { position: relative; margin: 1em 0; }
33428
+ .squisq-preview-code-copy-frame > pre { margin: 0; padding-right: 5rem; }
33429
+ .squisq-preview-code-copy {
33430
+ position: absolute; z-index: 1; top: .55rem; right: .55rem;
33431
+ min-width: 3.7rem; padding: .28rem .5rem;
33432
+ border: 1px solid color-mix(in srgb, var(--plain-text) 20%, transparent);
33433
+ border-radius: 5px;
33434
+ background: color-mix(in srgb, var(--plain-bg) 90%, transparent);
33435
+ color: inherit; font: 500 .72rem/1.2 system-ui, sans-serif;
33436
+ cursor: pointer; opacity: .58;
33437
+ transition: opacity 120ms ease, background-color 120ms ease;
33438
+ }
33439
+ .squisq-preview-code-copy-frame:hover > .squisq-preview-code-copy,
33440
+ .squisq-preview-code-copy:focus-visible,
33441
+ .squisq-preview-code-copy[data-copy-state="copied"],
33442
+ .squisq-preview-code-copy[data-copy-state="failed"] { opacity: 1; }
33443
+ .squisq-preview-code-copy:hover { background: var(--plain-bg); }
33444
+ .squisq-preview-code-copy:disabled { cursor: wait; }
33445
+ `;
33446
+ frameDocument.head.append(style2);
33447
+ const cleanups = [];
33448
+ const wrappers = [];
33449
+ const timers = /* @__PURE__ */ new Set();
33450
+ for (const code of frameDocument.querySelectorAll(
33451
+ "pre.squisq-code-block > code"
33452
+ )) {
33453
+ const pre = code.parentElement;
33454
+ if (!pre?.parentElement) continue;
33455
+ const wrapper = frameDocument.createElement("div");
33456
+ wrapper.className = "squisq-preview-code-copy-frame";
33457
+ pre.replaceWith(wrapper);
33458
+ wrapper.append(pre);
33459
+ wrappers.push(wrapper);
33460
+ const button = frameDocument.createElement("button");
33461
+ button.type = "button";
33462
+ button.className = "squisq-preview-code-copy";
33463
+ button.dataset.copyState = "idle";
33464
+ button.textContent = "Copy";
33465
+ button.setAttribute("aria-label", "Copy code to clipboard");
33466
+ wrapper.append(button);
33467
+ const languageClass = Array.from(code.classList).find((name) => name.startsWith("language-"));
33468
+ const language = languageClass?.slice("language-".length) || void 0;
33469
+ const handleCopy = async () => {
33470
+ if (button.dataset.copyState === "copying") return;
33471
+ button.dataset.copyState = "copying";
33472
+ button.textContent = "Copying\u2026";
33473
+ button.disabled = true;
33474
+ try {
33475
+ if (onCopyCode) {
33476
+ await onCopyCode(code.textContent ?? "", language === void 0 ? {} : { language });
33477
+ } else {
33478
+ const clipboard = frameDocument.defaultView?.navigator.clipboard ?? (typeof navigator === "undefined" ? void 0 : navigator.clipboard);
33479
+ if (!clipboard?.writeText) throw new Error("Clipboard access is unavailable");
33480
+ await clipboard.writeText(code.textContent ?? "");
33481
+ }
33482
+ button.dataset.copyState = "copied";
33483
+ button.textContent = "Copied";
33484
+ } catch {
33485
+ button.dataset.copyState = "failed";
33486
+ button.textContent = "Copy failed";
33487
+ } finally {
33488
+ button.disabled = false;
33489
+ const timer = setTimeout(() => {
33490
+ timers.delete(timer);
33491
+ button.dataset.copyState = "idle";
33492
+ button.textContent = "Copy";
33493
+ }, 1600);
33494
+ timers.add(timer);
33495
+ }
33496
+ };
33497
+ button.addEventListener("click", handleCopy);
33498
+ cleanups.push(() => button.removeEventListener("click", handleCopy));
33499
+ }
33500
+ removeFrameCodeCopyControlsRef.current = () => {
33501
+ for (const cleanup of cleanups) cleanup();
33502
+ for (const timer of timers) clearTimeout(timer);
33503
+ timers.clear();
33504
+ for (const wrapper of wrappers) {
33505
+ const pre = wrapper.firstElementChild;
33506
+ if (pre && wrapper.parentElement) wrapper.replaceWith(pre);
33507
+ }
33508
+ style2.remove();
33509
+ };
33510
+ }, [onCopyCode, showCodeCopyButton]);
33511
+ useEffect50(() => {
33512
+ installFrameCodeCopyControls();
33513
+ return () => removeFrameCodeCopyControlsRef.current();
33514
+ }, [html, installFrameCodeCopyControls]);
33515
+ useEffect50(() => {
33161
33516
  if (!globalKeyboardShortcuts) return;
33162
33517
  const handleKeyDown = (event) => {
33163
33518
  if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.key !== "ArrowDown" && event.key !== "ArrowUp") {
@@ -33181,7 +33536,7 @@ function PlainHtmlPreview({
33181
33536
  document.addEventListener("keydown", handleKeyDown);
33182
33537
  return () => document.removeEventListener("keydown", handleKeyDown);
33183
33538
  }, [globalKeyboardShortcuts]);
33184
- return /* @__PURE__ */ jsx68(
33539
+ return /* @__PURE__ */ jsx69(
33185
33540
  "iframe",
33186
33541
  {
33187
33542
  ref: setIframeRef,
@@ -33189,7 +33544,10 @@ function PlainHtmlPreview({
33189
33544
  "data-testid": "plain-html-preview",
33190
33545
  title: title ?? "HTML preview",
33191
33546
  srcDoc: html,
33192
- onLoad: installFrameLinkHandler,
33547
+ onLoad: () => {
33548
+ installFrameLinkHandler();
33549
+ installFrameCodeCopyControls();
33550
+ },
33193
33551
  sandbox: "allow-same-origin",
33194
33552
  style: { ...IFRAME_STYLE, ...style }
33195
33553
  }
@@ -33233,7 +33591,7 @@ function collectImageRefs(doc) {
33233
33591
  }
33234
33592
 
33235
33593
  // src/PreviewPanel.tsx
33236
- import { useState as useState61, useEffect as useEffect52, useMemo as useMemo48, useCallback as useCallback49, useRef as useRef52 } from "react";
33594
+ import { useState as useState63, useEffect as useEffect54, useMemo as useMemo49, useCallback as useCallback49, useRef as useRef52 } from "react";
33237
33595
  import { createPortal as createPortal13 } from "react-dom";
33238
33596
  import { DocPlayer as DocPlayer2, LinearDocView as LinearDocView2, useMediaProvider as useMediaProvider2 } from "@bendyline/squisq-react";
33239
33597
  import { resolveTransformStyle as resolveTransformStyle2 } from "@bendyline/squisq/transform";
@@ -33372,15 +33730,15 @@ import {
33372
33730
  createContext as createContext5,
33373
33731
  useCallback as useCallback46,
33374
33732
  useContext as useContext5,
33375
- useEffect as useEffect49,
33733
+ useEffect as useEffect51,
33376
33734
  useId as useId14,
33377
- useLayoutEffect as useLayoutEffect9,
33378
- useMemo as useMemo45,
33735
+ useLayoutEffect as useLayoutEffect10,
33736
+ useMemo as useMemo46,
33379
33737
  useRef as useRef49,
33380
- useState as useState59
33738
+ useState as useState61
33381
33739
  } from "react";
33382
33740
  import { createPortal as createPortal12 } from "react-dom";
33383
- import { jsx as jsx69, jsxs as jsxs54 } from "react/jsx-runtime";
33741
+ import { jsx as jsx70, jsxs as jsxs54 } from "react/jsx-runtime";
33384
33742
  var PresentationModeContext = createContext5(null);
33385
33743
  function usePresentationMode() {
33386
33744
  const value = useContext5(PresentationModeContext);
@@ -33460,10 +33818,10 @@ function PresentationModeProvider({
33460
33818
  const { activeView, colorScheme, doc } = useEditorContext();
33461
33819
  const { activeTheme } = usePreviewSettings();
33462
33820
  const popupNameId = useId14().replace(/[^a-zA-Z0-9_-]/g, "");
33463
- const [selectedTarget, setSelectedTarget] = useState59("control");
33464
- const [activeTarget, setActiveTarget] = useState59(null);
33465
- const [popupRoot, setPopupRoot] = useState59(null);
33466
- const [error, setError] = useState59(null);
33821
+ const [selectedTarget, setSelectedTarget] = useState61("control");
33822
+ const [activeTarget, setActiveTarget] = useState61(null);
33823
+ const [popupRoot, setPopupRoot] = useState61(null);
33824
+ const [error, setError] = useState61(null);
33467
33825
  const activeTargetRef = useRef49(activeTarget);
33468
33826
  activeTargetRef.current = activeTarget;
33469
33827
  const previousActiveTargetRef = useRef49(null);
@@ -33471,7 +33829,7 @@ function PresentationModeProvider({
33471
33829
  const popupRef = useRef49(null);
33472
33830
  const popupCleanupRef = useRef49(null);
33473
33831
  const fullscreenSupported = typeof document !== "undefined" && typeof document.documentElement.requestFullscreen === "function";
33474
- const availableTargets = useMemo45(
33832
+ const availableTargets = useMemo46(
33475
33833
  () => [
33476
33834
  "control",
33477
33835
  ...allowWindow ? ["window"] : [],
@@ -33606,12 +33964,12 @@ function PresentationModeProvider({
33606
33964
  },
33607
33965
  [availableTargets, selectedTarget, stop]
33608
33966
  );
33609
- useEffect49(() => {
33967
+ useEffect51(() => {
33610
33968
  if (availableTargets.includes(selectedTarget)) return;
33611
33969
  setSelectedTarget("control");
33612
33970
  if (activeTargetRef.current !== null) void stop();
33613
33971
  }, [availableTargets, selectedTarget, stop]);
33614
- useEffect49(() => {
33972
+ useEffect51(() => {
33615
33973
  const root = rootRef.current;
33616
33974
  const ownerDocument = root?.ownerDocument;
33617
33975
  if (!root || !ownerDocument) return;
@@ -33623,7 +33981,7 @@ function PresentationModeProvider({
33623
33981
  ownerDocument.addEventListener("fullscreenchange", handleFullscreenChange);
33624
33982
  return () => ownerDocument.removeEventListener("fullscreenchange", handleFullscreenChange);
33625
33983
  }, [rootRef]);
33626
- useEffect49(() => {
33984
+ useEffect51(() => {
33627
33985
  const root = rootRef.current;
33628
33986
  if (!root) return;
33629
33987
  if (activeTarget) root.dataset.presentationMode = activeTarget;
@@ -33632,10 +33990,10 @@ function PresentationModeProvider({
33632
33990
  delete root.dataset.presentationMode;
33633
33991
  };
33634
33992
  }, [activeTarget, rootRef]);
33635
- useEffect49(() => {
33993
+ useEffect51(() => {
33636
33994
  if (activeView !== "preview" && activeTargetRef.current !== null) void stop();
33637
33995
  }, [activeView, stop]);
33638
- useEffect49(() => {
33996
+ useEffect51(() => {
33639
33997
  const previous = previousActiveTargetRef.current;
33640
33998
  previousActiveTargetRef.current = activeTarget;
33641
33999
  if (previous === null || activeTarget !== null) return;
@@ -33643,7 +34001,7 @@ function PresentationModeProvider({
33643
34001
  returnFocusRef.current = null;
33644
34002
  if (target?.isConnected) target.focus();
33645
34003
  }, [activeTarget]);
33646
- useEffect49(() => {
34004
+ useEffect51(() => {
33647
34005
  if (activeTarget !== "control") return;
33648
34006
  const ownerDocument = rootRef.current?.ownerDocument;
33649
34007
  if (!ownerDocument) return;
@@ -33655,12 +34013,12 @@ function PresentationModeProvider({
33655
34013
  ownerDocument.addEventListener("keydown", handleKeyDown);
33656
34014
  return () => ownerDocument.removeEventListener("keydown", handleKeyDown);
33657
34015
  }, [activeTarget, rootRef, stop]);
33658
- useEffect49(() => {
34016
+ useEffect51(() => {
33659
34017
  if (!error) return;
33660
34018
  const timer = window.setTimeout(() => setError(null), 6e3);
33661
34019
  return () => window.clearTimeout(timer);
33662
34020
  }, [error]);
33663
- useEffect49(
34021
+ useEffect51(
33664
34022
  () => () => {
33665
34023
  popupCleanupRef.current?.();
33666
34024
  const popup = popupRef.current;
@@ -33673,7 +34031,7 @@ function PresentationModeProvider({
33673
34031
  },
33674
34032
  [rootRef]
33675
34033
  );
33676
- const value = useMemo45(
34034
+ const value = useMemo46(
33677
34035
  () => ({
33678
34036
  selectedTarget,
33679
34037
  activeTarget,
@@ -33712,8 +34070,8 @@ function PresentationModeProvider({
33712
34070
  "aria-label": "Exit presentation mode",
33713
34071
  title: "Exit presentation mode (Esc)",
33714
34072
  children: [
33715
- /* @__PURE__ */ jsx69(Icon, { icon: "fa-solid fa-compress" }),
33716
- /* @__PURE__ */ jsx69("span", { children: "Exit presentation" })
34073
+ /* @__PURE__ */ jsx70(Icon, { icon: "fa-solid fa-compress" }),
34074
+ /* @__PURE__ */ jsx70("span", { children: "Exit presentation" })
33717
34075
  ]
33718
34076
  }
33719
34077
  ) : null;
@@ -33722,7 +34080,7 @@ function PresentationModeProvider({
33722
34080
  (activeTarget === "control" || activeTarget === "fullscreen") && exitButton,
33723
34081
  activeTarget === "window" && popupRoot && exitButton ? createPortal12(exitButton, popupRoot) : null,
33724
34082
  error && typeof document !== "undefined" ? createPortal12(
33725
- /* @__PURE__ */ jsx69("div", { className: "squisq-presentation-error", "data-theme": colorScheme, role: "alert", children: error }),
34083
+ /* @__PURE__ */ jsx70("div", { className: "squisq-presentation-error", "data-theme": colorScheme, role: "alert", children: error }),
33726
34084
  document.body
33727
34085
  ) : null
33728
34086
  ] });
@@ -33763,8 +34121,8 @@ function PresentationModeControl() {
33763
34121
  const { colorScheme } = useEditorContext();
33764
34122
  const triggerRef = useRef49(null);
33765
34123
  const menuRef = useRef49(null);
33766
- const [open, setOpen] = useState59(false);
33767
- const [anchor, setAnchor] = useState59(null);
34124
+ const [open, setOpen] = useState61(false);
34125
+ const [anchor, setAnchor] = useState61(null);
33768
34126
  const options = PRESENTATION_OPTIONS.filter((option) => availableTargets.includes(option.target));
33769
34127
  const selected = options.find((option) => option.target === selectedTarget) ?? PRESENTATION_OPTIONS[0];
33770
34128
  const updatePosition = useCallback46(() => {
@@ -33789,7 +34147,7 @@ function PresentationModeControl() {
33789
34147
  updatePosition();
33790
34148
  setOpen(true);
33791
34149
  }, [updatePosition]);
33792
- useEffect49(() => {
34150
+ useEffect51(() => {
33793
34151
  if (!open) return;
33794
34152
  const handlePointerDown = (event) => {
33795
34153
  const target = event.target;
@@ -33812,14 +34170,14 @@ function PresentationModeControl() {
33812
34170
  window.removeEventListener("scroll", updatePosition, true);
33813
34171
  };
33814
34172
  }, [closeMenu, open, updatePosition]);
33815
- useLayoutEffect9(() => {
34173
+ useLayoutEffect10(() => {
33816
34174
  if (!open || !anchor) return;
33817
34175
  const checked = menuRef.current?.querySelector('[aria-checked="true"]');
33818
34176
  const first = menuRef.current?.querySelector('[role="menuitemradio"]');
33819
34177
  (checked ?? first)?.focus();
33820
34178
  }, [anchor, open]);
33821
34179
  return /* @__PURE__ */ jsxs54("div", { className: "squisq-presentation-control", children: [
33822
- /* @__PURE__ */ jsx69(
34180
+ /* @__PURE__ */ jsx70(
33823
34181
  "button",
33824
34182
  {
33825
34183
  type: "button",
@@ -33828,10 +34186,10 @@ function PresentationModeControl() {
33828
34186
  "aria-pressed": activeTarget !== null,
33829
34187
  "data-tooltip": activeTarget ? "Exit presentation" : `Present: ${selected.label}`,
33830
34188
  onClick: () => void (activeTarget ? stop() : start()),
33831
- children: /* @__PURE__ */ jsx69(Icon, { icon: "fa-solid fa-display" })
34189
+ children: /* @__PURE__ */ jsx70(Icon, { icon: "fa-solid fa-display" })
33832
34190
  }
33833
34191
  ),
33834
- options.length > 1 && /* @__PURE__ */ jsx69(
34192
+ options.length > 1 && /* @__PURE__ */ jsx70(
33835
34193
  "button",
33836
34194
  {
33837
34195
  ref: triggerRef,
@@ -33847,11 +34205,11 @@ function PresentationModeControl() {
33847
34205
  event.preventDefault();
33848
34206
  openMenu();
33849
34207
  },
33850
- children: /* @__PURE__ */ jsx69("svg", { width: "10", height: "10", viewBox: "0 0 10 10", "aria-hidden": "true", children: /* @__PURE__ */ jsx69("path", { d: "M2 3.5 5 6.5 8 3.5", fill: "none", stroke: "currentColor", strokeWidth: "1.4" }) })
34208
+ children: /* @__PURE__ */ jsx70("svg", { width: "10", height: "10", viewBox: "0 0 10 10", "aria-hidden": "true", children: /* @__PURE__ */ jsx70("path", { d: "M2 3.5 5 6.5 8 3.5", fill: "none", stroke: "currentColor", strokeWidth: "1.4" }) })
33851
34209
  }
33852
34210
  ),
33853
34211
  open && anchor ? createPortal12(
33854
- /* @__PURE__ */ jsx69(
34212
+ /* @__PURE__ */ jsx70(
33855
34213
  "div",
33856
34214
  {
33857
34215
  ref: menuRef,
@@ -33895,24 +34253,24 @@ function PresentationModeControl() {
33895
34253
  closeMenu(true);
33896
34254
  },
33897
34255
  children: [
33898
- /* @__PURE__ */ jsx69(
34256
+ /* @__PURE__ */ jsx70(
33899
34257
  "span",
33900
34258
  {
33901
34259
  className: "squisq-use-mode-menu-icon squisq-presentation-menu-icon",
33902
34260
  "aria-hidden": "true",
33903
- children: /* @__PURE__ */ jsx69(Icon, { icon: option.icon })
34261
+ children: /* @__PURE__ */ jsx70(Icon, { icon: option.icon })
33904
34262
  }
33905
34263
  ),
33906
34264
  /* @__PURE__ */ jsxs54("span", { className: "squisq-use-mode-menu-copy squisq-presentation-menu-copy", children: [
33907
- /* @__PURE__ */ jsx69("span", { className: "squisq-use-mode-menu-label squisq-presentation-menu-label", children: option.label }),
33908
- /* @__PURE__ */ jsx69("span", { className: "squisq-use-mode-menu-summary squisq-presentation-menu-summary", children: disabled ? "Full screen is unavailable." : option.summary })
34265
+ /* @__PURE__ */ jsx70("span", { className: "squisq-use-mode-menu-label squisq-presentation-menu-label", children: option.label }),
34266
+ /* @__PURE__ */ jsx70("span", { className: "squisq-use-mode-menu-summary squisq-presentation-menu-summary", children: disabled ? "Full screen is unavailable." : option.summary })
33909
34267
  ] }),
33910
- /* @__PURE__ */ jsx69(
34268
+ /* @__PURE__ */ jsx70(
33911
34269
  "span",
33912
34270
  {
33913
34271
  className: "squisq-use-mode-menu-check squisq-presentation-menu-check",
33914
34272
  "aria-hidden": "true",
33915
- children: isSelected && /* @__PURE__ */ jsx69(Icon, { icon: "fa-solid fa-check" })
34273
+ children: isSelected && /* @__PURE__ */ jsx70(Icon, { icon: "fa-solid fa-check" })
33916
34274
  }
33917
34275
  )
33918
34276
  ]
@@ -33932,12 +34290,12 @@ import {
33932
34290
  createContext as createContext6,
33933
34291
  useCallback as useCallback47,
33934
34292
  useContext as useContext6,
33935
- useEffect as useEffect50,
33936
- useMemo as useMemo46,
34293
+ useEffect as useEffect52,
34294
+ useMemo as useMemo47,
33937
34295
  useRef as useRef50,
33938
- useState as useState60
34296
+ useState as useState62
33939
34297
  } from "react";
33940
- import { jsx as jsx70, jsxs as jsxs55 } from "react/jsx-runtime";
34298
+ import { jsx as jsx71, jsxs as jsxs55 } from "react/jsx-runtime";
33941
34299
  var PrintModeContext = createContext6(null);
33942
34300
  function usePrintMode() {
33943
34301
  const value = useContext6(PrintModeContext);
@@ -33950,8 +34308,8 @@ function usePrintModeOptional() {
33950
34308
  function PrintModeProvider({ rootRef, children }) {
33951
34309
  const { activeView } = useEditorContext();
33952
34310
  const { activeTarget: presentationTarget, stop: stopPresentation } = usePresentationMode();
33953
- const [active, setActive] = useState60(false);
33954
- const [slidesPerPage, setSlidesPerPage] = useState60(1);
34311
+ const [active, setActive] = useState62(false);
34312
+ const [slidesPerPage, setSlidesPerPage] = useState62(1);
33955
34313
  const customPrintHandlerRef = useRef50(null);
33956
34314
  const printAncestorsRef = useRef50([]);
33957
34315
  const unmarkPrinting = useCallback47(() => {
@@ -34002,7 +34360,7 @@ function PrintModeProvider({ rootRef, children }) {
34002
34360
  markPrinting();
34003
34361
  ownerWindow.print();
34004
34362
  }, [markPrinting, rootRef]);
34005
- useEffect50(() => {
34363
+ useEffect52(() => {
34006
34364
  const root = rootRef.current;
34007
34365
  if (!root) return;
34008
34366
  if (active) root.dataset.printPreview = "true";
@@ -34011,10 +34369,10 @@ function PrintModeProvider({ rootRef, children }) {
34011
34369
  delete root.dataset.printPreview;
34012
34370
  };
34013
34371
  }, [active, rootRef]);
34014
- useEffect50(() => {
34372
+ useEffect52(() => {
34015
34373
  if (activeView !== "preview" && active) close();
34016
34374
  }, [active, activeView, close]);
34017
- useEffect50(() => {
34375
+ useEffect52(() => {
34018
34376
  if (!active) return;
34019
34377
  const ownerWindow = rootRef.current?.ownerDocument.defaultView;
34020
34378
  const ownerDocument = rootRef.current?.ownerDocument;
@@ -34036,7 +34394,7 @@ function PrintModeProvider({ rootRef, children }) {
34036
34394
  handleAfterPrint();
34037
34395
  };
34038
34396
  }, [active, close, markPrinting, rootRef, unmarkPrinting]);
34039
- const value = useMemo46(
34397
+ const value = useMemo47(
34040
34398
  () => ({
34041
34399
  active,
34042
34400
  slidesPerPage,
@@ -34048,7 +34406,7 @@ function PrintModeProvider({ rootRef, children }) {
34048
34406
  }),
34049
34407
  [active, close, open, print, registerPrintHandler, slidesPerPage]
34050
34408
  );
34051
- return /* @__PURE__ */ jsx70(PrintModeContext.Provider, { value, children });
34409
+ return /* @__PURE__ */ jsx71(PrintModeContext.Provider, { value, children });
34052
34410
  }
34053
34411
  function PrintModeControl() {
34054
34412
  const { open } = usePrintMode();
@@ -34061,8 +34419,8 @@ function PrintModeControl() {
34061
34419
  "aria-label": "Print",
34062
34420
  "data-tooltip": "Print",
34063
34421
  children: [
34064
- /* @__PURE__ */ jsx70(Icon, { icon: "fa-solid fa-print" }),
34065
- /* @__PURE__ */ jsx70("span", { children: "Print" })
34422
+ /* @__PURE__ */ jsx71(Icon, { icon: "fa-solid fa-print" }),
34423
+ /* @__PURE__ */ jsx71("span", { children: "Print" })
34066
34424
  ]
34067
34425
  }
34068
34426
  );
@@ -34072,8 +34430,8 @@ function PrintPreviewToolbar() {
34072
34430
  const { activeDisplayMode } = usePreviewSettings();
34073
34431
  const { slidesPerPage, setSlidesPerPage, print, close } = usePrintMode();
34074
34432
  return /* @__PURE__ */ jsxs55("div", { className: "squisq-print-toolbar", "aria-label": "Print preview controls", children: [
34075
- /* @__PURE__ */ jsx70("span", { className: "squisq-print-toolbar-title", children: "Print preview" }),
34076
- activeDisplayMode === "slideshow" && /* @__PURE__ */ jsx70("div", { className: "squisq-print-density", role: "group", "aria-label": "Slides per page", children: SLIDES_PER_PAGE_OPTIONS.map((value) => /* @__PURE__ */ jsxs55(
34433
+ /* @__PURE__ */ jsx71("span", { className: "squisq-print-toolbar-title", children: "Print preview" }),
34434
+ activeDisplayMode === "slideshow" && /* @__PURE__ */ jsx71("div", { className: "squisq-print-density", role: "group", "aria-label": "Slides per page", children: SLIDES_PER_PAGE_OPTIONS.map((value) => /* @__PURE__ */ jsxs55(
34077
34435
  "button",
34078
34436
  {
34079
34437
  type: "button",
@@ -34082,7 +34440,7 @@ function PrintPreviewToolbar() {
34082
34440
  "aria-label": `${value} ${value === 1 ? "slide" : "slides"} per page`,
34083
34441
  onClick: () => setSlidesPerPage(value),
34084
34442
  children: [
34085
- /* @__PURE__ */ jsx70("span", { children: value }),
34443
+ /* @__PURE__ */ jsx71("span", { children: value }),
34086
34444
  /* @__PURE__ */ jsxs55("span", { className: "squisq-print-density-label", children: [
34087
34445
  " ",
34088
34446
  value === 1 ? "slide" : "slides",
@@ -34092,20 +34450,20 @@ function PrintPreviewToolbar() {
34092
34450
  },
34093
34451
  value
34094
34452
  )) }),
34095
- /* @__PURE__ */ jsx70("div", { className: "squisq-print-toolbar-spacer" }),
34453
+ /* @__PURE__ */ jsx71("div", { className: "squisq-print-toolbar-spacer" }),
34096
34454
  /* @__PURE__ */ jsxs55("button", { type: "button", className: "squisq-print-action", onClick: print, children: [
34097
- /* @__PURE__ */ jsx70(Icon, { icon: "fa-solid fa-print" }),
34098
- /* @__PURE__ */ jsx70("span", { children: "Print" })
34455
+ /* @__PURE__ */ jsx71(Icon, { icon: "fa-solid fa-print" }),
34456
+ /* @__PURE__ */ jsx71("span", { children: "Print" })
34099
34457
  ] }),
34100
34458
  /* @__PURE__ */ jsxs55("button", { type: "button", className: "squisq-print-close", onClick: close, children: [
34101
- /* @__PURE__ */ jsx70(Icon, { icon: "fa-solid fa-xmark" }),
34102
- /* @__PURE__ */ jsx70("span", { children: "Close" })
34459
+ /* @__PURE__ */ jsx71(Icon, { icon: "fa-solid fa-xmark" }),
34460
+ /* @__PURE__ */ jsx71("span", { children: "Close" })
34103
34461
  ] })
34104
34462
  ] });
34105
34463
  }
34106
34464
 
34107
34465
  // src/print/PrintPreview.tsx
34108
- import { useCallback as useCallback48, useEffect as useEffect51, useMemo as useMemo47, useRef as useRef51 } from "react";
34466
+ import { useCallback as useCallback48, useEffect as useEffect53, useMemo as useMemo48, useRef as useRef51 } from "react";
34109
34467
  import {
34110
34468
  BlockRenderer as BlockRenderer5,
34111
34469
  LinearDocView,
@@ -34117,7 +34475,7 @@ import {
34117
34475
  } from "@bendyline/squisq/schemas";
34118
34476
  import { expandCoverBlock as expandCoverBlock2 } from "@bendyline/squisq/doc";
34119
34477
  import { resolveTransformStyle } from "@bendyline/squisq/transform";
34120
- import { jsx as jsx71, jsxs as jsxs56 } from "react/jsx-runtime";
34478
+ import { jsx as jsx72, jsxs as jsxs56 } from "react/jsx-runtime";
34121
34479
  function chunk(values, size) {
34122
34480
  const groups = [];
34123
34481
  for (let index2 = 0; index2 < values.length; index2 += size) {
@@ -34144,7 +34502,7 @@ function PrintPreview({
34144
34502
  documentFrameRef.current = frame;
34145
34503
  }, []);
34146
34504
  const { blocks } = useDocPlayback(previewDoc, 0, { viewport, theme });
34147
- const coverBlock = useMemo47(() => {
34505
+ const coverBlock = useMemo48(() => {
34148
34506
  if (!showCover || !previewDoc?.startBlock) return null;
34149
34507
  const context = createTemplateContext2(theme, 0, 1, viewport);
34150
34508
  return {
@@ -34156,7 +34514,7 @@ function PrintPreview({
34156
34514
  };
34157
34515
  }, [previewDoc?.startBlock, showCover, theme, viewport]);
34158
34516
  const isTextDocument = displayMode === "page" || displayMode === "narrate";
34159
- useEffect51(() => {
34517
+ useEffect53(() => {
34160
34518
  if (!isTextDocument) return registerPrintHandler(null);
34161
34519
  return registerPrintHandler(() => {
34162
34520
  const frameWindow = documentFrameRef.current?.contentWindow;
@@ -34166,7 +34524,7 @@ function PrintPreview({
34166
34524
  }, [isTextDocument, registerPrintHandler]);
34167
34525
  const title = contentDoc?.frontmatter?.title ?? contentDoc?.startBlock?.title ?? void 0;
34168
34526
  if (isTextDocument) {
34169
- return /* @__PURE__ */ jsx71("div", { className: "squisq-print-preview squisq-print-preview--document", children: /* @__PURE__ */ jsx71("div", { className: "squisq-print-document-paper", children: /* @__PURE__ */ jsx71(
34527
+ return /* @__PURE__ */ jsx72("div", { className: "squisq-print-preview squisq-print-preview--document", children: /* @__PURE__ */ jsx72("div", { className: "squisq-print-document-paper", children: /* @__PURE__ */ jsx72(
34170
34528
  PlainHtmlPreview,
34171
34529
  {
34172
34530
  markdown: documentMarkdown,
@@ -34180,8 +34538,8 @@ function PrintPreview({
34180
34538
  }
34181
34539
  if (displayMode === "linear") {
34182
34540
  return /* @__PURE__ */ jsxs56("div", { className: "squisq-print-preview squisq-print-preview--page", children: [
34183
- /* @__PURE__ */ jsx71("style", { children: "@page { margin: 0; }" }),
34184
- /* @__PURE__ */ jsx71("div", { className: "squisq-print-page-paper", children: /* @__PURE__ */ jsx71(
34541
+ /* @__PURE__ */ jsx72("style", { children: "@page { margin: 0; }" }),
34542
+ /* @__PURE__ */ jsx72("div", { className: "squisq-print-page-paper", children: /* @__PURE__ */ jsx72(
34185
34543
  LinearDocView,
34186
34544
  {
34187
34545
  className: "squisq-print-linear",
@@ -34211,14 +34569,14 @@ function PrintPreview({
34211
34569
  "data-slides-per-page": density,
34212
34570
  style: previewStyle,
34213
34571
  children: [
34214
- /* @__PURE__ */ jsx71("style", { children: `@page { size: ${orientation}; margin: 0; }` }),
34215
- sheets.map((sheet, pageIndex) => /* @__PURE__ */ jsx71(
34572
+ /* @__PURE__ */ jsx72("style", { children: `@page { size: ${orientation}; margin: 0; }` }),
34573
+ sheets.map((sheet, pageIndex) => /* @__PURE__ */ jsx72(
34216
34574
  "div",
34217
34575
  {
34218
34576
  className: "squisq-print-sheet",
34219
34577
  "data-slides-per-page": density,
34220
34578
  "aria-label": `Print page ${pageIndex + 1}`,
34221
- children: sheet.map((block) => /* @__PURE__ */ jsx71("div", { className: "squisq-print-slide-cell", children: /* @__PURE__ */ jsx71("div", { className: "squisq-print-slide-frame", children: /* @__PURE__ */ jsx71(
34579
+ children: sheet.map((block) => /* @__PURE__ */ jsx72("div", { className: "squisq-print-slide-cell", children: /* @__PURE__ */ jsx72("div", { className: "squisq-print-slide-frame", children: /* @__PURE__ */ jsx72(
34222
34580
  BlockRenderer5,
34223
34581
  {
34224
34582
  block,
@@ -34232,19 +34590,21 @@ function PrintPreview({
34232
34590
  },
34233
34591
  `${density}-${pageIndex}`
34234
34592
  )),
34235
- sheets.length === 0 && /* @__PURE__ */ jsx71("p", { className: "squisq-print-empty", children: "Nothing to print." })
34593
+ sheets.length === 0 && /* @__PURE__ */ jsx72("p", { className: "squisq-print-empty", children: "Nothing to print." })
34236
34594
  ]
34237
34595
  }
34238
34596
  );
34239
34597
  }
34240
34598
 
34241
34599
  // src/PreviewPanel.tsx
34242
- import { Fragment as Fragment20, jsx as jsx72, jsxs as jsxs57 } from "react/jsx-runtime";
34600
+ import { Fragment as Fragment20, jsx as jsx73, jsxs as jsxs57 } from "react/jsx-runtime";
34243
34601
  function PreviewPanel({
34244
34602
  basePath = "/",
34245
34603
  className,
34246
34604
  workspaceContainer,
34247
- onLinkClick
34605
+ onLinkClick,
34606
+ showCodeCopyButton = false,
34607
+ onCopyCode
34248
34608
  }) {
34249
34609
  const {
34250
34610
  doc,
@@ -34256,7 +34616,8 @@ function PreviewPanel({
34256
34616
  bumpMediaRevision,
34257
34617
  allowRecording,
34258
34618
  colorScheme,
34259
- fileName
34619
+ fileName,
34620
+ fenceRenderers
34260
34621
  } = useEditorContext();
34261
34622
  const mediaProvider = useMediaProvider2();
34262
34623
  const presentation = usePresentationModeOptional();
@@ -34280,7 +34641,7 @@ function PreviewPanel({
34280
34641
  } = usePreviewSettings();
34281
34642
  const mainSurfaceRef = useRef52(null);
34282
34643
  const popupSurfaceRef = useRef52(null);
34283
- const [playbackState, setPlaybackState] = useState61(null);
34644
+ const [playbackState, setPlaybackState] = useState63(null);
34284
34645
  const handlePlaybackStateChange = useCallback49((next) => {
34285
34646
  setPlaybackState(next);
34286
34647
  }, []);
@@ -34292,7 +34653,7 @@ function PreviewPanel({
34292
34653
  );
34293
34654
  const previewDoc = previewProjection?.playerDoc ?? null;
34294
34655
  const contentDoc = previewProjection?.contentDoc ?? null;
34295
- const followerAudioController = useMemo48(() => {
34656
+ const followerAudioController = useMemo49(() => {
34296
34657
  if (!playbackState || !previewDoc) return null;
34297
34658
  const noOp = () => Promise.resolve();
34298
34659
  return {
@@ -34311,18 +34672,18 @@ function PreviewPanel({
34311
34672
  restart: noOp
34312
34673
  };
34313
34674
  }, [playbackState, previewDoc]);
34314
- const documentMarkdown = useMemo48(
34675
+ const documentMarkdown = useMemo49(
34315
34676
  () => activeTransformStyle && contentDoc ? buildDocumentPreviewMarkdown(contentDoc) : markdownSource,
34316
34677
  [activeTransformStyle, contentDoc, markdownSource]
34317
34678
  );
34318
34679
  const isDocumentMode = activeDisplayMode === "page";
34319
34680
  const isPageMode = activeDisplayMode === "linear";
34320
34681
  const isNarrateMode = activeDisplayMode === "narrate";
34321
- useEffect52(() => {
34682
+ useEffect54(() => {
34322
34683
  if (presentation?.activeTarget === "window") return;
34323
34684
  setPlaybackState(null);
34324
34685
  }, [presentation?.activeTarget]);
34325
- useEffect52(() => {
34686
+ useEffect54(() => {
34326
34687
  if (presentation?.activeTarget !== "window" || !presentation.popupRoot) return;
34327
34688
  const mainRoot = mainSurfaceRef.current;
34328
34689
  const followerRoot = popupSurfaceRef.current;
@@ -34385,22 +34746,22 @@ function PreviewPanel({
34385
34746
  presentation?.popupRoot
34386
34747
  ]);
34387
34748
  if (isParsing && !isNarrateMode && !previewDoc) {
34388
- return /* @__PURE__ */ jsx72("div", { className: `squisq-preview-status ${className || ""}`, "data-testid": "preview-panel", children: /* @__PURE__ */ jsx72("p", { children: "Parsing\u2026" }) });
34749
+ return /* @__PURE__ */ jsx73("div", { className: `squisq-preview-status ${className || ""}`, "data-testid": "preview-panel", children: /* @__PURE__ */ jsx73("p", { children: "Parsing\u2026" }) });
34389
34750
  }
34390
34751
  if (parseError && !isNarrateMode) {
34391
34752
  return /* @__PURE__ */ jsxs57("div", { className: `squisq-preview-status ${className || ""}`, "data-testid": "preview-panel", children: [
34392
- /* @__PURE__ */ jsx72("h3", { children: "Parse Error" }),
34393
- /* @__PURE__ */ jsx72("pre", { children: parseError })
34753
+ /* @__PURE__ */ jsx73("h3", { children: "Parse Error" }),
34754
+ /* @__PURE__ */ jsx73("pre", { children: parseError })
34394
34755
  ] });
34395
34756
  }
34396
34757
  if (!previewDoc && !isDocumentMode && !isNarrateMode) {
34397
- return /* @__PURE__ */ jsx72("div", { className: `squisq-preview-status ${className || ""}`, "data-testid": "preview-panel", children: /* @__PURE__ */ jsx72("p", { children: "No content to preview. Start typing in the editor." }) });
34758
+ 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." }) });
34398
34759
  }
34399
34760
  const fillsContainer = printMode?.active || isDocumentMode || isPageMode || isNarrateMode ? "stretch" : "center";
34400
34761
  const audienceWindowOpen = presentation?.activeTarget === "window";
34401
34762
  const renderSurface = (audience) => {
34402
34763
  if (!audience && printMode?.active) {
34403
- return /* @__PURE__ */ jsx72(
34764
+ return /* @__PURE__ */ jsx73(
34404
34765
  PrintPreview,
34405
34766
  {
34406
34767
  displayMode: activeDisplayMode,
@@ -34417,7 +34778,7 @@ function PreviewPanel({
34417
34778
  );
34418
34779
  }
34419
34780
  if (isDocumentMode) {
34420
- return /* @__PURE__ */ jsx72(
34781
+ return /* @__PURE__ */ jsx73(
34421
34782
  PlainHtmlPreview,
34422
34783
  {
34423
34784
  markdown: documentMarkdown,
@@ -34426,13 +34787,15 @@ function PreviewPanel({
34426
34787
  mediaRevision,
34427
34788
  theme: activeTheme,
34428
34789
  globalKeyboardShortcuts: !audience,
34429
- onLinkClick
34790
+ onLinkClick,
34791
+ showCodeCopyButton,
34792
+ onCopyCode
34430
34793
  }
34431
34794
  );
34432
34795
  }
34433
34796
  if (isNarrateMode) {
34434
34797
  if (audience) return null;
34435
- return /* @__PURE__ */ jsx72(
34798
+ return /* @__PURE__ */ jsx73(
34436
34799
  TeleprompterView,
34437
34800
  {
34438
34801
  doc,
@@ -34451,7 +34814,7 @@ function PreviewPanel({
34451
34814
  );
34452
34815
  }
34453
34816
  if (isPageMode) {
34454
- return /* @__PURE__ */ jsx72(
34817
+ return /* @__PURE__ */ jsx73(
34455
34818
  LinearDocView2,
34456
34819
  {
34457
34820
  doc: contentDoc ?? doc,
@@ -34460,15 +34823,18 @@ function PreviewPanel({
34460
34823
  theme: activeTheme,
34461
34824
  globalKeyboardShortcuts: !audience,
34462
34825
  showCover: activeCoverSlide,
34463
- transformPage: activeTransformStyle ? resolveTransformStyle2(activeTransformStyle).page : void 0
34826
+ transformPage: activeTransformStyle ? resolveTransformStyle2(activeTransformStyle).page : void 0,
34827
+ showCodeCopyButton,
34828
+ onCopyCode,
34829
+ fenceRenderers: fenceRenderers ?? void 0
34464
34830
  }
34465
34831
  );
34466
34832
  }
34467
34833
  if (audience && !followerAudioController) {
34468
- return /* @__PURE__ */ jsx72("div", { className: "squisq-presentation-connecting", children: "Connecting presentation..." });
34834
+ return /* @__PURE__ */ jsx73("div", { className: "squisq-presentation-connecting", children: "Connecting presentation..." });
34469
34835
  }
34470
34836
  const audienceCaptionMode = playbackState?.captionMode;
34471
- return /* @__PURE__ */ jsx72(
34837
+ return /* @__PURE__ */ jsx73(
34472
34838
  DocPlayer2,
34473
34839
  {
34474
34840
  doc: previewDoc,
@@ -34494,6 +34860,8 @@ function PreviewPanel({
34494
34860
  audioController: audience ? followerAudioController : void 0,
34495
34861
  enableSwipe: !audience,
34496
34862
  globalKeyboardShortcuts: !audience,
34863
+ showCodeCopyButton,
34864
+ onCopyCode,
34497
34865
  onPlaybackStateChange: !audience && audienceWindowOpen ? handlePlaybackStateChange : void 0
34498
34866
  },
34499
34867
  `${audience ? "audience" : "primary"}-${activeTransformStyle || "none"}`
@@ -34509,7 +34877,7 @@ function PreviewPanel({
34509
34877
  background: presentation?.activeTarget ? activeTheme.colors.background : void 0
34510
34878
  };
34511
34879
  return /* @__PURE__ */ jsxs57(Fragment20, { children: [
34512
- /* @__PURE__ */ jsx72(
34880
+ /* @__PURE__ */ jsx73(
34513
34881
  "div",
34514
34882
  {
34515
34883
  className: `squisq-preview-container ${className || ""}`,
@@ -34522,16 +34890,16 @@ function PreviewPanel({
34522
34890
  overflow: "hidden",
34523
34891
  background: "var(--squisq-bg, #f5f5f5)"
34524
34892
  },
34525
- children: /* @__PURE__ */ jsx72("div", { ref: mainSurfaceRef, className: "squisq-preview-player", style: surfaceStyle, children: renderSurface(false) })
34893
+ children: /* @__PURE__ */ jsx73("div", { ref: mainSurfaceRef, className: "squisq-preview-player", style: surfaceStyle, children: renderSurface(false) })
34526
34894
  }
34527
34895
  ),
34528
34896
  audienceWindowOpen && presentation?.popupRoot && !isNarrateMode ? createPortal13(
34529
- /* @__PURE__ */ jsx72(
34897
+ /* @__PURE__ */ jsx73(
34530
34898
  "div",
34531
34899
  {
34532
34900
  className: "squisq-editor-shell squisq-presentation-window",
34533
34901
  "data-theme": colorScheme,
34534
- children: /* @__PURE__ */ jsx72("div", { className: "squisq-preview-container squisq-presentation-window-preview", children: /* @__PURE__ */ jsx72(
34902
+ children: /* @__PURE__ */ jsx73("div", { className: "squisq-preview-container squisq-presentation-window-preview", children: /* @__PURE__ */ jsx73(
34535
34903
  "div",
34536
34904
  {
34537
34905
  ref: popupSurfaceRef,
@@ -34549,8 +34917,8 @@ function PreviewPanel({
34549
34917
  }
34550
34918
 
34551
34919
  // src/MediaBin.tsx
34552
- import { useState as useState62, useEffect as useEffect53, useRef as useRef53, useCallback as useCallback50 } from "react";
34553
- import { jsx as jsx73, jsxs as jsxs58 } from "react/jsx-runtime";
34920
+ import { useState as useState64, useEffect as useEffect55, useRef as useRef53, useCallback as useCallback50 } from "react";
34921
+ import { jsx as jsx74, jsxs as jsxs58 } from "react/jsx-runtime";
34554
34922
  function formatSize(bytes) {
34555
34923
  if (bytes < 1024) return `${bytes} B`;
34556
34924
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -34614,12 +34982,12 @@ function MediaBin({
34614
34982
  isRecorderOpen = false,
34615
34983
  allowBinaryDownloads = true
34616
34984
  }) {
34617
- const [entries, setEntries] = useState62([]);
34618
- const [thumbUrls, setThumbUrls] = useState62({});
34619
- const [loading, setLoading] = useState62(false);
34620
- const [isDropActive, setIsDropActive] = useState62(false);
34621
- const [downloadingPath, setDownloadingPath] = useState62(null);
34622
- const [contextMenu, setContextMenu] = useState62(null);
34985
+ const [entries, setEntries] = useState64([]);
34986
+ const [thumbUrls, setThumbUrls] = useState64({});
34987
+ const [loading, setLoading] = useState64(false);
34988
+ const [isDropActive, setIsDropActive] = useState64(false);
34989
+ const [downloadingPath, setDownloadingPath] = useState64(null);
34990
+ const [contextMenu, setContextMenu] = useState64(null);
34623
34991
  const fileInputRef = useRef53(null);
34624
34992
  const contextMenuRef = useRef53(null);
34625
34993
  const dropDepthRef = useRef53(0);
@@ -34641,7 +35009,7 @@ function MediaBin({
34641
35009
  },
34642
35010
  [onCountChange]
34643
35011
  );
34644
- useEffect53(() => {
35012
+ useEffect55(() => {
34645
35013
  if (!contextMenu) return;
34646
35014
  const handlePointerDown = (event) => {
34647
35015
  if (contextMenuRef.current?.contains(event.target)) return;
@@ -34662,7 +35030,7 @@ function MediaBin({
34662
35030
  window.removeEventListener("resize", close);
34663
35031
  };
34664
35032
  }, [contextMenu]);
34665
- useEffect53(() => {
35033
+ useEffect55(() => {
34666
35034
  if (!mediaProvider) {
34667
35035
  setEntries([]);
34668
35036
  setThumbUrls({});
@@ -34867,12 +35235,12 @@ function MediaBin({
34867
35235
  "aria-haspopup": "dialog",
34868
35236
  "aria-expanded": isRecorderOpen,
34869
35237
  children: [
34870
- /* @__PURE__ */ jsx73("span", { className: "squisq-media-bin-record-dot", "aria-hidden": "true" }),
35238
+ /* @__PURE__ */ jsx74("span", { className: "squisq-media-bin-record-dot", "aria-hidden": "true" }),
34871
35239
  "Record"
34872
35240
  ]
34873
35241
  }
34874
35242
  ),
34875
- /* @__PURE__ */ jsx73(
35243
+ /* @__PURE__ */ jsx74(
34876
35244
  "button",
34877
35245
  {
34878
35246
  type: "button",
@@ -34888,10 +35256,10 @@ function MediaBin({
34888
35256
  /* @__PURE__ */ jsxs58("div", { className: "squisq-media-bin-list", children: [
34889
35257
  !mediaProvider && /* @__PURE__ */ jsxs58("div", { className: "squisq-media-bin-empty", children: [
34890
35258
  "No media context.",
34891
- /* @__PURE__ */ jsx73("br", {}),
35259
+ /* @__PURE__ */ jsx74("br", {}),
34892
35260
  "Load a content zip or select a storage slot."
34893
35261
  ] }),
34894
- mediaProvider && entries.length === 0 && !loading && /* @__PURE__ */ jsx73("div", { className: "squisq-media-bin-empty", children: "No files yet." }),
35262
+ mediaProvider && entries.length === 0 && !loading && /* @__PURE__ */ jsx74("div", { className: "squisq-media-bin-empty", children: "No files yet." }),
34895
35263
  entries.map((entry) => {
34896
35264
  const thumb = thumbUrls[entry.name];
34897
35265
  const basename = basenameForPath(entry.name);
@@ -34923,7 +35291,7 @@ ${formatSize(entry.size)}`,
34923
35291
  onDragStart: handleDragStart,
34924
35292
  onKeyDown: (e2) => handleItemKeyDown(entry, e2),
34925
35293
  children: [
34926
- thumb ? /* @__PURE__ */ jsx73(
35294
+ thumb ? /* @__PURE__ */ jsx74(
34927
35295
  "img",
34928
35296
  {
34929
35297
  src: thumb,
@@ -34931,15 +35299,15 @@ ${formatSize(entry.size)}`,
34931
35299
  className: "squisq-media-bin-thumb",
34932
35300
  draggable: false
34933
35301
  }
34934
- ) : /* @__PURE__ */ jsx73("span", { className: "squisq-media-bin-icon", children: iconForMime(entry.mimeType) }),
35302
+ ) : /* @__PURE__ */ jsx74("span", { className: "squisq-media-bin-icon", children: iconForMime(entry.mimeType) }),
34935
35303
  /* @__PURE__ */ jsxs58("div", { className: "squisq-media-bin-meta", children: [
34936
- /* @__PURE__ */ jsx73("div", { className: "squisq-media-bin-name", children: basename }),
35304
+ /* @__PURE__ */ jsx74("div", { className: "squisq-media-bin-name", children: basename }),
34937
35305
  /* @__PURE__ */ jsxs58("div", { className: "squisq-media-bin-detail-row", children: [
34938
- /* @__PURE__ */ jsx73("span", { className: "squisq-media-bin-size", children: formatSize(entry.size) }),
34939
- isUnused && /* @__PURE__ */ jsx73("span", { className: "squisq-media-bin-unused-badge", title: "Not used in document", children: "Unused" })
35306
+ /* @__PURE__ */ jsx74("span", { className: "squisq-media-bin-size", children: formatSize(entry.size) }),
35307
+ isUnused && /* @__PURE__ */ jsx74("span", { className: "squisq-media-bin-unused-badge", title: "Not used in document", children: "Unused" })
34940
35308
  ] })
34941
35309
  ] }),
34942
- allowBinaryDownloads && /* @__PURE__ */ jsx73(
35310
+ allowBinaryDownloads && /* @__PURE__ */ jsx74(
34943
35311
  "button",
34944
35312
  {
34945
35313
  type: "button",
@@ -34953,7 +35321,7 @@ ${formatSize(entry.size)}`,
34953
35321
  void handleDownloadEntry(entry);
34954
35322
  },
34955
35323
  onDragStart: (event) => event.preventDefault(),
34956
- children: /* @__PURE__ */ jsx73("span", { "aria-hidden": "true", children: downloadingPath === entry.name ? "\u2026" : "\u2193" })
35324
+ children: /* @__PURE__ */ jsx74("span", { "aria-hidden": "true", children: downloadingPath === entry.name ? "\u2026" : "\u2193" })
34957
35325
  }
34958
35326
  )
34959
35327
  ]
@@ -34963,11 +35331,11 @@ ${formatSize(entry.size)}`,
34963
35331
  })
34964
35332
  ] }),
34965
35333
  isDropActive && /* @__PURE__ */ jsxs58("div", { className: "squisq-media-bin-drop-target", "aria-hidden": "true", children: [
34966
- /* @__PURE__ */ jsx73("span", { className: "squisq-media-bin-drop-target-icon", children: "+" }),
34967
- /* @__PURE__ */ jsx73("strong", { children: "Drop images here" }),
34968
- /* @__PURE__ */ jsx73("span", { children: "Add to Files" })
35334
+ /* @__PURE__ */ jsx74("span", { className: "squisq-media-bin-drop-target-icon", children: "+" }),
35335
+ /* @__PURE__ */ jsx74("strong", { children: "Drop images here" }),
35336
+ /* @__PURE__ */ jsx74("span", { children: "Add to Files" })
34969
35337
  ] }),
34970
- contextMenu && /* @__PURE__ */ jsx73(
35338
+ contextMenu && /* @__PURE__ */ jsx74(
34971
35339
  "div",
34972
35340
  {
34973
35341
  ref: contextMenuRef,
@@ -34975,7 +35343,7 @@ ${formatSize(entry.size)}`,
34975
35343
  role: "menu",
34976
35344
  "aria-label": `${contextMenu.entry.name} actions`,
34977
35345
  style: contextMenuStyle,
34978
- children: /* @__PURE__ */ jsx73(
35346
+ children: /* @__PURE__ */ jsx74(
34979
35347
  "button",
34980
35348
  {
34981
35349
  type: "button",
@@ -34988,7 +35356,7 @@ ${formatSize(entry.size)}`,
34988
35356
  )
34989
35357
  }
34990
35358
  ),
34991
- /* @__PURE__ */ jsx73(
35359
+ /* @__PURE__ */ jsx74(
34992
35360
  "input",
34993
35361
  {
34994
35362
  ref: fileInputRef,
@@ -35004,8 +35372,8 @@ ${formatSize(entry.size)}`,
35004
35372
  }
35005
35373
 
35006
35374
  // src/DropZoneOverlay.tsx
35007
- import { useState as useState63 } from "react";
35008
- import { Fragment as Fragment21, jsx as jsx74, jsxs as jsxs59 } from "react/jsx-runtime";
35375
+ import { useState as useState65 } from "react";
35376
+ import { Fragment as Fragment21, jsx as jsx75, jsxs as jsxs59 } from "react/jsx-runtime";
35009
35377
  function DropZoneOverlay({
35010
35378
  dragContentType,
35011
35379
  zoneProps,
@@ -35013,8 +35381,8 @@ function DropZoneOverlay({
35013
35381
  }) {
35014
35382
  const showMedia = dragContentType === "media" || dragContentType === "mixed";
35015
35383
  const showText = dragContentType === "text" || dragContentType === "mixed";
35016
- return /* @__PURE__ */ jsx74("div", { className: "squisq-drop-overlay", children: /* @__PURE__ */ jsxs59("div", { className: "squisq-drop-overlay-inner", children: [
35017
- showMedia && /* @__PURE__ */ jsx74(
35384
+ return /* @__PURE__ */ jsx75("div", { className: "squisq-drop-overlay", children: /* @__PURE__ */ jsxs59("div", { className: "squisq-drop-overlay-inner", children: [
35385
+ showMedia && /* @__PURE__ */ jsx75(
35018
35386
  DropZone,
35019
35387
  {
35020
35388
  target: "media",
@@ -35027,7 +35395,7 @@ function DropZoneOverlay({
35027
35395
  }
35028
35396
  ),
35029
35397
  showText && /* @__PURE__ */ jsxs59(Fragment21, { children: [
35030
- /* @__PURE__ */ jsx74(
35398
+ /* @__PURE__ */ jsx75(
35031
35399
  DropZone,
35032
35400
  {
35033
35401
  target: "insert",
@@ -35038,7 +35406,7 @@ function DropZoneOverlay({
35038
35406
  variant: "insert"
35039
35407
  }
35040
35408
  ),
35041
- /* @__PURE__ */ jsx74(
35409
+ /* @__PURE__ */ jsx75(
35042
35410
  DropZone,
35043
35411
  {
35044
35412
  target: "replace",
@@ -35061,7 +35429,7 @@ function DropZone({
35061
35429
  disabled,
35062
35430
  variant
35063
35431
  }) {
35064
- const [isHovering, setIsHovering] = useState63(false);
35432
+ const [isHovering, setIsHovering] = useState65(false);
35065
35433
  const props = zoneProps(target);
35066
35434
  return /* @__PURE__ */ jsxs59(
35067
35435
  "div",
@@ -35096,16 +35464,16 @@ function DropZone({
35096
35464
  props.onDrop(e2);
35097
35465
  },
35098
35466
  children: [
35099
- /* @__PURE__ */ jsx74("span", { className: "squisq-drop-zone-icon", children: icon }),
35100
- /* @__PURE__ */ jsx74("span", { className: "squisq-drop-zone-label", children: label }),
35101
- /* @__PURE__ */ jsx74("span", { className: "squisq-drop-zone-desc", children: description })
35467
+ /* @__PURE__ */ jsx75("span", { className: "squisq-drop-zone-icon", children: icon }),
35468
+ /* @__PURE__ */ jsx75("span", { className: "squisq-drop-zone-label", children: label }),
35469
+ /* @__PURE__ */ jsx75("span", { className: "squisq-drop-zone-desc", children: description })
35102
35470
  ]
35103
35471
  }
35104
35472
  );
35105
35473
  }
35106
35474
 
35107
35475
  // src/Tooltip.tsx
35108
- import { useEffect as useEffect54, useLayoutEffect as useLayoutEffect10, useRef as useRef54, useState as useState64 } from "react";
35476
+ import { useEffect as useEffect56, useLayoutEffect as useLayoutEffect11, useRef as useRef54, useState as useState66 } from "react";
35109
35477
  import { createPortal as createPortal14 } from "react-dom";
35110
35478
 
35111
35479
  // src/tooltipPlacement.ts
@@ -35118,15 +35486,15 @@ function clampTooltipLeft(anchorX, tooltipWidth, viewportWidth, edgePadding = ED
35118
35486
  }
35119
35487
 
35120
35488
  // src/Tooltip.tsx
35121
- import { jsx as jsx75 } from "react/jsx-runtime";
35489
+ import { jsx as jsx76 } from "react/jsx-runtime";
35122
35490
  var SHOW_DELAY_MS = 180;
35123
35491
  function TooltipLayer() {
35124
- const [state, setState3] = useState64(null);
35492
+ const [state, setState3] = useState66(null);
35125
35493
  const tooltipRef = useRef54(null);
35126
35494
  const timerRef = useRef54(null);
35127
35495
  const currentTargetRef = useRef54(null);
35128
35496
  const visibleRef = useRef54(false);
35129
- useLayoutEffect10(() => {
35497
+ useLayoutEffect11(() => {
35130
35498
  if (!state) return;
35131
35499
  const node2 = tooltipRef.current;
35132
35500
  if (!node2) return;
@@ -35137,7 +35505,7 @@ function TooltipLayer() {
35137
35505
  node2.style.left = `${left}px`;
35138
35506
  node2.style.visibility = "visible";
35139
35507
  }, [state]);
35140
- useEffect54(() => {
35508
+ useEffect56(() => {
35141
35509
  const clearTimer = () => {
35142
35510
  if (timerRef.current) {
35143
35511
  clearTimeout(timerRef.current);
@@ -35203,7 +35571,7 @@ function TooltipLayer() {
35203
35571
  }, []);
35204
35572
  if (!state) return null;
35205
35573
  return createPortal14(
35206
- /* @__PURE__ */ jsx75(
35574
+ /* @__PURE__ */ jsx76(
35207
35575
  "div",
35208
35576
  {
35209
35577
  role: "tooltip",
@@ -35223,35 +35591,35 @@ function TooltipLayer() {
35223
35591
  }
35224
35592
 
35225
35593
  // src/EditorShell.tsx
35226
- import { useEffect as useEffect55, useRef as useRef55, useState as useState65, useCallback as useCallback51, useMemo as useMemo50 } from "react";
35594
+ import { useEffect as useEffect57, useRef as useRef55, useState as useState67, useCallback as useCallback51, useMemo as useMemo51 } from "react";
35227
35595
 
35228
35596
  // src/BlockPreviewPanel.tsx
35229
- import { useMemo as useMemo49 } from "react";
35597
+ import { useMemo as useMemo50 } from "react";
35230
35598
  import { VIEWPORT_PRESETS as VIEWPORT_PRESETS7 } from "@bendyline/squisq/schemas";
35231
35599
  import { flattenBlocks as flattenBlocks7, DEFAULT_THEME as DEFAULT_THEME7 } from "@bendyline/squisq/doc";
35232
- import { jsx as jsx76 } from "react/jsx-runtime";
35600
+ import { jsx as jsx77 } from "react/jsx-runtime";
35233
35601
  function BlockPreviewPanel({ basePath = "/" }) {
35234
35602
  const { doc, activeBlockStartLine, mediaProvider } = useEditorContext();
35235
35603
  const previewSettings = usePreviewSettingsOptional();
35236
35604
  const theme = previewSettings?.activeTheme ?? DEFAULT_THEME7;
35237
35605
  const viewport = previewSettings?.activeViewport ?? VIEWPORT_PRESETS7.landscape;
35238
- const block = useMemo49(() => {
35606
+ const block = useMemo50(() => {
35239
35607
  if (!doc) return null;
35240
35608
  const blocks = flattenBlocks7(doc.blocks);
35241
35609
  if (blocks.length === 0) return null;
35242
35610
  return blocks.find((b) => b.sourceHeading?.position?.start.line === activeBlockStartLine) ?? blocks[0];
35243
35611
  }, [doc, activeBlockStartLine]);
35244
- const visual = useMemo49(
35612
+ const visual = useMemo50(
35245
35613
  () => doc && block ? resolveBlockVisual(doc, block, theme, viewport) : null,
35246
35614
  [doc, block, theme, viewport]
35247
35615
  );
35248
35616
  if (!visual) return null;
35249
- return /* @__PURE__ */ jsx76("div", { className: "squisq-block-preview-panel", "data-testid": "block-preview-panel", "aria-hidden": true, children: /* @__PURE__ */ jsx76(
35617
+ return /* @__PURE__ */ jsx77("div", { className: "squisq-block-preview-panel", "data-testid": "block-preview-panel", "aria-hidden": true, children: /* @__PURE__ */ jsx77(
35250
35618
  "div",
35251
35619
  {
35252
35620
  className: "squisq-block-preview-frame",
35253
35621
  style: { aspectRatio: `${viewport.width} / ${viewport.height}` },
35254
- children: /* @__PURE__ */ jsx76(
35622
+ children: /* @__PURE__ */ jsx77(
35255
35623
  BlockThumbnail,
35256
35624
  {
35257
35625
  visual,
@@ -35505,7 +35873,7 @@ import {
35505
35873
  createMediaProviderFromContainer
35506
35874
  } from "@bendyline/squisq/storage";
35507
35875
  import { MediaContext as MediaContext7, useMediaClipDurations } from "@bendyline/squisq-react";
35508
- import { Fragment as Fragment22, jsx as jsx77, jsxs as jsxs60 } from "react/jsx-runtime";
35876
+ import { Fragment as Fragment22, jsx as jsx78, jsxs as jsxs60 } from "react/jsx-runtime";
35509
35877
  function EditorShell({
35510
35878
  initialMarkdown = "",
35511
35879
  initialView = "wysiwyg",
@@ -35515,6 +35883,8 @@ function EditorShell({
35515
35883
  basePath = "/",
35516
35884
  onChange,
35517
35885
  onLinkClick,
35886
+ showCodeCopyButton = false,
35887
+ onCopyCode,
35518
35888
  colorScheme = "light",
35519
35889
  className,
35520
35890
  height = "100vh",
@@ -35528,6 +35898,8 @@ function EditorShell({
35528
35898
  versioningAutoSaveIdleMs,
35529
35899
  onSaveVersion,
35530
35900
  showFilesToggle,
35901
+ showFormattingControls = hostMode !== "chat",
35902
+ showInsertControls = true,
35531
35903
  allowBinaryDownloads = true,
35532
35904
  toolbarSlotLeft,
35533
35905
  toolbarSlotAfterActions,
@@ -35551,6 +35923,7 @@ function EditorShell({
35551
35923
  onFindModeChange,
35552
35924
  mentionProvider,
35553
35925
  documentLinkProvider,
35926
+ fenceRenderers,
35554
35927
  linkSchemes,
35555
35928
  allowRecording = true,
35556
35929
  allowNarrate = true,
@@ -35574,14 +35947,14 @@ function EditorShell({
35574
35947
  themeOverride = null
35575
35948
  }) {
35576
35949
  const effectiveContainer = workspaceContainer ?? null;
35577
- const effectiveMediaProvider = useMemo50(() => {
35950
+ const effectiveMediaProvider = useMemo51(() => {
35578
35951
  if (mediaProvider !== void 0) return mediaProvider;
35579
35952
  if (effectiveContainer) return createMediaProviderFromContainer(effectiveContainer);
35580
35953
  return void 0;
35581
35954
  }, [mediaProvider, effectiveContainer]);
35582
35955
  const filesToggleEnabled = showFilesToggle ?? effectiveMediaProvider !== void 0;
35583
35956
  const effectiveInitialView = hostMode === "chat" || !showPlayTab && initialView === "preview" ? "wysiwyg" : initialView;
35584
- return /* @__PURE__ */ jsx77(MediaContext7.Provider, { value: effectiveMediaProvider ?? null, children: /* @__PURE__ */ jsx77(
35957
+ return /* @__PURE__ */ jsx78(MediaContext7.Provider, { value: effectiveMediaProvider ?? null, children: /* @__PURE__ */ jsx78(
35585
35958
  EditorProvider,
35586
35959
  {
35587
35960
  initialMarkdown,
@@ -35598,6 +35971,7 @@ function EditorShell({
35598
35971
  imageDisplayMode,
35599
35972
  mentionProvider,
35600
35973
  documentLinkProvider,
35974
+ fenceRenderers,
35601
35975
  linkSchemes,
35602
35976
  allowRecording,
35603
35977
  allowNarrate,
@@ -35614,13 +35988,15 @@ function EditorShell({
35614
35988
  themeInheritance,
35615
35989
  viewPreferences,
35616
35990
  onViewPreferencesChange,
35617
- children: /* @__PURE__ */ jsx77(
35991
+ children: /* @__PURE__ */ jsx78(
35618
35992
  EditorShellInner,
35619
35993
  {
35620
35994
  basePath,
35621
35995
  defaultViewportPreset,
35622
35996
  onChange,
35623
35997
  onLinkClick,
35998
+ showCodeCopyButton,
35999
+ onCopyCode,
35624
36000
  className,
35625
36001
  height,
35626
36002
  minHeight,
@@ -35629,6 +36005,8 @@ function EditorShell({
35629
36005
  mediaProvider: effectiveMediaProvider ?? null,
35630
36006
  workspaceContainer: effectiveContainer,
35631
36007
  filesToggleEnabled,
36008
+ showFormattingControls,
36009
+ showInsertControls,
35632
36010
  allowBinaryDownloads,
35633
36011
  toolbarSlotLeft,
35634
36012
  toolbarSlotAfterActions,
@@ -35663,11 +36041,11 @@ function EditorShell({
35663
36041
  }
35664
36042
  function UseModeToolbarControls({ allowPrint }) {
35665
36043
  const printMode = usePrintMode();
35666
- if (printMode.active) return /* @__PURE__ */ jsx77(PrintPreviewToolbar, {});
36044
+ if (printMode.active) return /* @__PURE__ */ jsx78(PrintPreviewToolbar, {});
35667
36045
  return /* @__PURE__ */ jsxs60(Fragment22, { children: [
35668
- /* @__PURE__ */ jsx77(PreviewToolbarControls, {}),
35669
- /* @__PURE__ */ jsx77(PresentationModeControl, {}),
35670
- allowPrint && /* @__PURE__ */ jsx77(PrintModeControl, {})
36046
+ /* @__PURE__ */ jsx78(PreviewToolbarControls, {}),
36047
+ /* @__PURE__ */ jsx78(PresentationModeControl, {}),
36048
+ allowPrint && /* @__PURE__ */ jsx78(PrintModeControl, {})
35671
36049
  ] });
35672
36050
  }
35673
36051
  function UseModeProviders({
@@ -35676,13 +36054,13 @@ function UseModeProviders({
35676
36054
  allowPresentationWindow,
35677
36055
  allowPresentationFullscreen
35678
36056
  }) {
35679
- return /* @__PURE__ */ jsx77(
36057
+ return /* @__PURE__ */ jsx78(
35680
36058
  PresentationModeProvider,
35681
36059
  {
35682
36060
  rootRef,
35683
36061
  allowWindow: allowPresentationWindow,
35684
36062
  allowFullscreen: allowPresentationFullscreen,
35685
- children: /* @__PURE__ */ jsx77(PrintModeProvider, { rootRef, children })
36063
+ children: /* @__PURE__ */ jsx78(PrintModeProvider, { rootRef, children })
35686
36064
  }
35687
36065
  );
35688
36066
  }
@@ -35691,6 +36069,8 @@ function EditorShellInner({
35691
36069
  defaultViewportPreset,
35692
36070
  onChange,
35693
36071
  onLinkClick,
36072
+ showCodeCopyButton,
36073
+ onCopyCode,
35694
36074
  className,
35695
36075
  height,
35696
36076
  minHeight,
@@ -35699,6 +36079,8 @@ function EditorShellInner({
35699
36079
  mediaProvider,
35700
36080
  workspaceContainer,
35701
36081
  filesToggleEnabled,
36082
+ showFormattingControls,
36083
+ showInsertControls,
35702
36084
  allowBinaryDownloads,
35703
36085
  toolbarSlotLeft,
35704
36086
  toolbarSlotAfterActions,
@@ -35767,7 +36149,7 @@ function EditorShellInner({
35767
36149
  workspaceContainer
35768
36150
  );
35769
36151
  const timelineDoc = timelineProjection?.contentDoc ?? doc;
35770
- const timelineRawSchedule = useMemo50(
36152
+ const timelineRawSchedule = useMemo51(
35771
36153
  () => timelineDoc ? resolveMediaSchedule4(timelineDoc) : [],
35772
36154
  [timelineDoc]
35773
36155
  );
@@ -35776,19 +36158,19 @@ function EditorShellInner({
35776
36158
  basePath,
35777
36159
  mediaProvider ?? null
35778
36160
  );
35779
- const timelineDuration = useMemo50(
36161
+ const timelineDuration = useMemo51(
35780
36162
  () => timelineDoc ? getDocPlaybackDuration3(timelineDoc, {
35781
36163
  intrinsicDuration: (clip) => timelineClipDurations.get(clip.src)
35782
36164
  }) : 0,
35783
36165
  [timelineDoc, timelineClipDurations]
35784
36166
  );
35785
- const timelineSchedule = useMemo50(
36167
+ const timelineSchedule = useMemo51(
35786
36168
  () => timelineDoc ? resolveMediaSchedule4(timelineDoc, {
35787
36169
  intrinsicDuration: (clip) => timelineClipDurations.get(clip.src)
35788
36170
  }) : [],
35789
36171
  [timelineDoc, timelineClipDurations]
35790
36172
  );
35791
- const timelineVideoSchedule = useMemo50(
36173
+ const timelineVideoSchedule = useMemo51(
35792
36174
  () => timelineDoc ? [
35793
36175
  ...timelineSchedule.filter((clip) => clip.kind === "video"),
35794
36176
  ...collectEmbeddedVideoSchedule(timelineDoc)
@@ -35797,15 +36179,15 @@ function EditorShellInner({
35797
36179
  );
35798
36180
  const timelineClock = useTimelineClock(isTimelineMode ? timelineDuration : 0);
35799
36181
  const hasTimelineVideo = timelineVideoSchedule.length > 0;
35800
- const [timelineVideoVisible, setTimelineVideoVisible] = useState65(false);
35801
- const [timelineCompositionVisible, setTimelineCompositionVisible] = useState65(false);
36182
+ const [timelineVideoVisible, setTimelineVideoVisible] = useState67(false);
36183
+ const [timelineCompositionVisible, setTimelineCompositionVisible] = useState67(false);
35802
36184
  const timelinePreviewCount = Number(timelineVideoVisible) + Number(timelineCompositionVisible);
35803
- const [showFiles, setShowFiles] = useState65(false);
35804
- const [mediaRefreshKey, setMediaRefreshKey] = useState65(0);
35805
- const [mediaCount, setMediaCount] = useState65(0);
35806
- const [mediaBinRecorderOpen, setMediaBinRecorderOpen] = useState65(false);
36185
+ const [showFiles, setShowFiles] = useState67(false);
36186
+ const [mediaRefreshKey, setMediaRefreshKey] = useState67(0);
36187
+ const [mediaCount, setMediaCount] = useState67(0);
36188
+ const [mediaBinRecorderOpen, setMediaBinRecorderOpen] = useState67(false);
35807
36189
  const mediaListRefreshKey = mediaRefreshKey + mediaRevision;
35808
- const usedMediaPaths = useMemo50(
36190
+ const usedMediaPaths = useMemo51(
35809
36191
  () => collectMediaReferencesFromMarkdown(markdownSource),
35810
36192
  [markdownSource]
35811
36193
  );
@@ -35815,13 +36197,13 @@ function EditorShellInner({
35815
36197
  }
35816
36198
  const imageEditFallbackContainer = imageEditFallbackContainerRef.current;
35817
36199
  const isDark = colorScheme === "dark";
35818
- useEffect55(() => {
36200
+ useEffect57(() => {
35819
36201
  if (!isTimelineMode || !hasTimelineVideo) setTimelineVideoVisible(false);
35820
36202
  }, [isTimelineMode, hasTimelineVideo]);
35821
- useEffect55(() => {
36203
+ useEffect57(() => {
35822
36204
  if (!isTimelineMode || !doc?.blocks.length) setTimelineCompositionVisible(false);
35823
36205
  }, [isTimelineMode, doc]);
35824
- useEffect55(() => {
36206
+ useEffect57(() => {
35825
36207
  if (!mediaProvider) {
35826
36208
  setMediaCount(0);
35827
36209
  return;
@@ -35930,7 +36312,7 @@ ${snippet}` : snippet);
35930
36312
  onDrop: handleFileDrop,
35931
36313
  enabled: !readOnly
35932
36314
  });
35933
- useEffect55(() => {
36315
+ useEffect57(() => {
35934
36316
  onChange?.(markdownSource);
35935
36317
  }, [markdownSource, onChange]);
35936
36318
  const handleShellKeyDown = useCallback51(
@@ -36018,7 +36400,7 @@ ${snippet}` : snippet);
36018
36400
  },
36019
36401
  ...containerProps,
36020
36402
  children: [
36021
- /* @__PURE__ */ jsx77(CustomThemeProvider, { docThemes, onDocThemesChange, children: /* @__PURE__ */ jsx77(
36403
+ /* @__PURE__ */ jsx78(CustomThemeProvider, { docThemes, onDocThemesChange, children: /* @__PURE__ */ jsx78(
36022
36404
  PreviewSettingsProvider,
36023
36405
  {
36024
36406
  doc,
@@ -36033,19 +36415,21 @@ ${snippet}` : snippet);
36033
36415
  children: [
36034
36416
  isImageMode ? (toolbarSlotLeft || toolbarSlotRight) && /* @__PURE__ */ jsxs60("div", { className: "squisq-editor-header squisq-editor-header--image", children: [
36035
36417
  toolbarSlotLeft,
36036
- /* @__PURE__ */ jsx77("div", { style: { flex: 1 } }),
36418
+ /* @__PURE__ */ jsx78("div", { style: { flex: 1 } }),
36037
36419
  toolbarSlotRight
36038
- ] }) : /* @__PURE__ */ jsx77("div", { className: "squisq-editor-header", children: /* @__PURE__ */ jsx77(
36420
+ ] }) : /* @__PURE__ */ jsx78("div", { className: "squisq-editor-header", children: /* @__PURE__ */ jsx78(
36039
36421
  Toolbar,
36040
36422
  {
36041
36423
  showFiles,
36042
36424
  fileCount: mediaCount,
36043
36425
  onToggleFiles: !isCodeMode && filesToggleEnabled ? handleToggleFiles : void 0,
36044
36426
  slotLeft: toolbarSlotLeft,
36045
- slotAfterTabs: !isCodeMode && isPreview && /* @__PURE__ */ jsx77(UseModeToolbarControls, { allowPrint }),
36427
+ slotAfterTabs: !isCodeMode && isPreview && /* @__PURE__ */ jsx78(UseModeToolbarControls, { allowPrint }),
36046
36428
  slotAfterActions: toolbarSlotAfterActions,
36047
36429
  slotRight: toolbarSlotRight,
36048
36430
  showPlayTab,
36431
+ showFormattingControls,
36432
+ showInsertControls,
36049
36433
  hostMode
36050
36434
  }
36051
36435
  ) }),
@@ -36074,7 +36458,7 @@ ${snippet}` : snippet);
36074
36458
  position: "relative"
36075
36459
  },
36076
36460
  children: [
36077
- isImageMode && imageSrc && (imageMode === "edit" && imageEditorContainer ? /* @__PURE__ */ jsx77(
36461
+ isImageMode && imageSrc && (imageMode === "edit" && imageEditorContainer ? /* @__PURE__ */ jsx78(
36078
36462
  ImageEditor,
36079
36463
  {
36080
36464
  filesContainer: imageEditorContainer,
@@ -36084,10 +36468,10 @@ ${snippet}` : snippet);
36084
36468
  onExport: onImageExport,
36085
36469
  surface: colorScheme === "dark" ? DARK_SURFACE : LIGHT_SURFACE
36086
36470
  }
36087
- ) : /* @__PURE__ */ jsx77(ImageViewer, { src: imageSrc, alt: imageAlt, theme: colorScheme })),
36471
+ ) : /* @__PURE__ */ jsx78(ImageViewer, { src: imageSrc, alt: imageAlt, theme: colorScheme })),
36088
36472
  !isImageMode && activeView === "raw" && /* @__PURE__ */ jsxs60("div", { className: "squisq-editor-with-gutter", children: [
36089
- isMarkdownMode && outlineVisible && /* @__PURE__ */ jsx77(OutlinePanel, { width: outlineWidth, readOnly }, "outline"),
36090
- /* @__PURE__ */ jsx77(
36473
+ isMarkdownMode && outlineVisible && /* @__PURE__ */ jsx78(OutlinePanel, { width: outlineWidth, readOnly }, "outline"),
36474
+ /* @__PURE__ */ jsx78(
36091
36475
  BlockCardView,
36092
36476
  {
36093
36477
  active: isCardMode,
@@ -36096,7 +36480,7 @@ ${snippet}` : snippet);
36096
36480
  onPrev: prevBlock,
36097
36481
  onNext: nextBlock,
36098
36482
  onAdd: addBlock,
36099
- children: /* @__PURE__ */ jsx77("div", { className: "squisq-raw-editor-container", children: /* @__PURE__ */ jsx77(
36483
+ children: /* @__PURE__ */ jsx78("div", { className: "squisq-raw-editor-container", children: /* @__PURE__ */ jsx78(
36100
36484
  RawEditor,
36101
36485
  {
36102
36486
  monacoTheme: colorScheme === "dark" ? "vs-dark" : "vs",
@@ -36107,9 +36491,9 @@ ${snippet}` : snippet);
36107
36491
  },
36108
36492
  "raw-frame"
36109
36493
  ),
36110
- isCodeMode && codeContext && /* @__PURE__ */ jsx77(CodeContextZones, { options: codeContext }, "code-context"),
36111
- isMarkdownMode && isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx77(BlockPreviewPanel, { basePath }, "block-preview"),
36112
- isMarkdownMode && !isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx77(
36494
+ isCodeMode && codeContext && /* @__PURE__ */ jsx78(CodeContextZones, { options: codeContext }, "code-context"),
36495
+ isMarkdownMode && isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx78(BlockPreviewPanel, { basePath }, "block-preview"),
36496
+ isMarkdownMode && !isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx78(
36113
36497
  InlinePreviewGutter,
36114
36498
  {
36115
36499
  width: inlinePreviewWidth,
@@ -36120,8 +36504,8 @@ ${snippet}` : snippet);
36120
36504
  )
36121
36505
  ] }, "raw-shell"),
36122
36506
  isMarkdownMode && activeView === "wysiwyg" && /* @__PURE__ */ jsxs60("div", { className: "squisq-editor-with-gutter", children: [
36123
- outlineVisible && /* @__PURE__ */ jsx77(OutlinePanel, { width: outlineWidth, readOnly }, "outline"),
36124
- /* @__PURE__ */ jsx77(
36507
+ outlineVisible && /* @__PURE__ */ jsx78(OutlinePanel, { width: outlineWidth, readOnly }, "outline"),
36508
+ /* @__PURE__ */ jsx78(
36125
36509
  BlockCardView,
36126
36510
  {
36127
36511
  active: isCardMode,
@@ -36130,7 +36514,7 @@ ${snippet}` : snippet);
36130
36514
  onPrev: prevBlock,
36131
36515
  onNext: nextBlock,
36132
36516
  onAdd: addBlock,
36133
- children: /* @__PURE__ */ jsx77(
36517
+ children: /* @__PURE__ */ jsx78(
36134
36518
  WysiwygEditor,
36135
36519
  {
36136
36520
  submitOnEnter,
@@ -36142,8 +36526,8 @@ ${snippet}` : snippet);
36142
36526
  },
36143
36527
  "wysiwyg-frame"
36144
36528
  ),
36145
- isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx77(BlockPreviewPanel, { basePath }, "block-preview"),
36146
- !isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx77(
36529
+ isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx78(BlockPreviewPanel, { basePath }, "block-preview"),
36530
+ !isCardMode && inlinePreviewVisible && /* @__PURE__ */ jsx78(
36147
36531
  InlinePreviewGutter,
36148
36532
  {
36149
36533
  width: inlinePreviewWidth,
@@ -36153,18 +36537,20 @@ ${snippet}` : snippet);
36153
36537
  "inline"
36154
36538
  )
36155
36539
  ] }, "wysiwyg-shell"),
36156
- isMarkdownMode && isPreview && /* @__PURE__ */ jsx77(
36540
+ isMarkdownMode && isPreview && /* @__PURE__ */ jsx78(
36157
36541
  PreviewPanel,
36158
36542
  {
36159
36543
  basePath,
36160
36544
  workspaceContainer,
36161
- onLinkClick
36545
+ onLinkClick,
36546
+ showCodeCopyButton,
36547
+ onCopyCode
36162
36548
  }
36163
36549
  )
36164
36550
  ]
36165
36551
  }
36166
36552
  ),
36167
- isTimelineMode && timelineVideoVisible && /* @__PURE__ */ jsx77(
36553
+ isTimelineMode && timelineVideoVisible && /* @__PURE__ */ jsx78(
36168
36554
  TimelineVideoPanel,
36169
36555
  {
36170
36556
  schedule: timelineVideoSchedule,
@@ -36174,7 +36560,7 @@ ${snippet}` : snippet);
36174
36560
  onClose: () => setTimelineVideoVisible(false)
36175
36561
  }
36176
36562
  ),
36177
- isTimelineMode && timelineCompositionVisible && /* @__PURE__ */ jsx77(
36563
+ isTimelineMode && timelineCompositionVisible && /* @__PURE__ */ jsx78(
36178
36564
  TimelineCompositionPanel,
36179
36565
  {
36180
36566
  doc,
@@ -36184,7 +36570,7 @@ ${snippet}` : snippet);
36184
36570
  onClose: () => setTimelineCompositionVisible(false)
36185
36571
  }
36186
36572
  ),
36187
- isMarkdownMode && showFiles && /* @__PURE__ */ jsx77(
36573
+ isMarkdownMode && showFiles && /* @__PURE__ */ jsx78(
36188
36574
  MediaBin,
36189
36575
  {
36190
36576
  mediaProvider,
@@ -36199,8 +36585,8 @@ ${snippet}` : snippet);
36199
36585
  isRecorderOpen: mediaBinRecorderOpen
36200
36586
  }
36201
36587
  ),
36202
- isMarkdownMode && /* @__PURE__ */ jsx77(ThemeDesignerDock, {}),
36203
- isMarkdownMode && isDragging && !(activeView === "wysiwyg" && dragContentType === "media") && /* @__PURE__ */ jsx77(
36588
+ isMarkdownMode && /* @__PURE__ */ jsx78(ThemeDesignerDock, {}),
36589
+ isMarkdownMode && isDragging && !(activeView === "wysiwyg" && dragContentType === "media") && /* @__PURE__ */ jsx78(
36204
36590
  DropZoneOverlay,
36205
36591
  {
36206
36592
  dragContentType,
@@ -36211,7 +36597,7 @@ ${snippet}` : snippet);
36211
36597
  ]
36212
36598
  }
36213
36599
  ),
36214
- isTimelineMode && /* @__PURE__ */ jsx77(
36600
+ isTimelineMode && /* @__PURE__ */ jsx78(
36215
36601
  TimelineToolbar,
36216
36602
  {
36217
36603
  literalVideoVisible: timelineVideoVisible,
@@ -36222,7 +36608,7 @@ ${snippet}` : snippet);
36222
36608
  onToggleComposition: () => setTimelineCompositionVisible((visible) => !visible)
36223
36609
  }
36224
36610
  ),
36225
- isTimelineMode && /* @__PURE__ */ jsx77(
36611
+ isTimelineMode && /* @__PURE__ */ jsx78(
36226
36612
  TimelineTrack,
36227
36613
  {
36228
36614
  doc: timelineDoc,
@@ -36231,8 +36617,8 @@ ${snippet}` : snippet);
36231
36617
  basePath
36232
36618
  }
36233
36619
  ),
36234
- statusBarVisible && !isImageMode && /* @__PURE__ */ jsx77(StatusBar, { slotRight: statusBarSlotRight }),
36235
- isMarkdownMode && allowRecording && mediaProvider && /* @__PURE__ */ jsx77(
36620
+ statusBarVisible && !isImageMode && /* @__PURE__ */ jsx78(StatusBar, { slotRight: statusBarSlotRight }),
36621
+ isMarkdownMode && allowRecording && mediaProvider && /* @__PURE__ */ jsx78(
36236
36622
  RecorderEntry,
36237
36623
  {
36238
36624
  open: mediaBinRecorderOpen,
@@ -36245,8 +36631,8 @@ ${snippet}` : snippet);
36245
36631
  )
36246
36632
  }
36247
36633
  ) }),
36248
- /* @__PURE__ */ jsx77(TooltipLayer, {}),
36249
- imageEditTarget !== null && mediaProvider && /* @__PURE__ */ jsx77(
36634
+ /* @__PURE__ */ jsx78(TooltipLayer, {}),
36635
+ imageEditTarget !== null && mediaProvider && /* @__PURE__ */ jsx78(
36250
36636
  ImageEditModal,
36251
36637
  {
36252
36638
  relativePath: imageEditTarget,
@@ -36281,7 +36667,7 @@ function ImageEditModal({
36281
36667
  useModalDialog({ rootRef: modalRef, dialogRef: surfaceRef, onClose });
36282
36668
  const extension = relativePath.split(/[?#]/, 1)[0]?.split(".").pop()?.toLowerCase();
36283
36669
  const saveFormat = extension === "png" ? "png" : extension === "jpg" || extension === "jpeg" ? "jpeg" : extension === "webp" ? "webp" : null;
36284
- const sidecar = useMemo50(() => {
36670
+ const sidecar = useMemo51(() => {
36285
36671
  const sanitized = relativePath.replace(/[^a-zA-Z0-9._-]+/g, "_");
36286
36672
  let hash = 2166136261;
36287
36673
  for (let i = 0; i < relativePath.length; i++) {
@@ -36292,9 +36678,9 @@ function ImageEditModal({
36292
36678
  const parent = container ?? new MemoryContentContainer();
36293
36679
  return scopeContainer(parent, `.imageEdits/${scopedName}`);
36294
36680
  }, [container, relativePath]);
36295
- const [initialSrc, setInitialSrc] = useState65(null);
36296
- const [resolveError, setResolveError] = useState65(null);
36297
- useEffect55(() => {
36681
+ const [initialSrc, setInitialSrc] = useState67(null);
36682
+ const [resolveError, setResolveError] = useState67(null);
36683
+ useEffect57(() => {
36298
36684
  let cancelled = false;
36299
36685
  setInitialSrc(null);
36300
36686
  setResolveError(null);
@@ -36324,7 +36710,7 @@ function ImageEditModal({
36324
36710
  },
36325
36711
  [mediaProvider, relativePath, onSaved]
36326
36712
  );
36327
- return /* @__PURE__ */ jsx77(
36713
+ return /* @__PURE__ */ jsx78(
36328
36714
  "div",
36329
36715
  {
36330
36716
  ref: modalRef,
@@ -36338,9 +36724,9 @@ function ImageEditModal({
36338
36724
  },
36339
36725
  children: /* @__PURE__ */ jsxs60("div", { ref: surfaceRef, className: "squisq-image-edit-modal__surface", children: [
36340
36726
  /* @__PURE__ */ jsxs60("header", { className: "squisq-image-edit-modal__header", children: [
36341
- /* @__PURE__ */ jsx77("span", { className: "squisq-image-edit-modal__title", children: "Edit image" }),
36342
- /* @__PURE__ */ jsx77("span", { className: "squisq-image-edit-modal__path", children: relativePath }),
36343
- /* @__PURE__ */ jsx77(
36727
+ /* @__PURE__ */ jsx78("span", { className: "squisq-image-edit-modal__title", children: "Edit image" }),
36728
+ /* @__PURE__ */ jsx78("span", { className: "squisq-image-edit-modal__path", children: relativePath }),
36729
+ /* @__PURE__ */ jsx78(
36344
36730
  "button",
36345
36731
  {
36346
36732
  type: "button",
@@ -36352,10 +36738,10 @@ function ImageEditModal({
36352
36738
  }
36353
36739
  )
36354
36740
  ] }),
36355
- /* @__PURE__ */ jsx77("div", { className: "squisq-image-edit-modal__body", children: resolveError ? /* @__PURE__ */ jsxs60("div", { className: "squisq-image-edit-modal__error", children: [
36741
+ /* @__PURE__ */ jsx78("div", { className: "squisq-image-edit-modal__body", children: resolveError ? /* @__PURE__ */ jsxs60("div", { className: "squisq-image-edit-modal__error", children: [
36356
36742
  "Failed to load image: ",
36357
36743
  resolveError
36358
- ] }) : !saveFormat ? /* @__PURE__ */ jsx77("div", { className: "squisq-image-edit-modal__error", children: "Editing supports PNG, JPEG, and WebP images without changing the asset type." }) : !initialSrc ? /* @__PURE__ */ jsx77("div", { className: "squisq-image-edit-modal__loading", children: "Loading image\u2026" }) : /* @__PURE__ */ jsx77(
36744
+ ] }) : !saveFormat ? /* @__PURE__ */ jsx78("div", { className: "squisq-image-edit-modal__error", children: "Editing supports PNG, JPEG, and WebP images without changing the asset type." }) : !initialSrc ? /* @__PURE__ */ jsx78("div", { className: "squisq-image-edit-modal__loading", children: "Loading image\u2026" }) : /* @__PURE__ */ jsx78(
36359
36745
  ImageEditor,
36360
36746
  {
36361
36747
  filesContainer: sidecar,
@@ -36474,6 +36860,7 @@ export {
36474
36860
  removeNodeOp,
36475
36861
  asciiDiagramToCanvas,
36476
36862
  useAsciiDiagramData,
36863
+ mapFenceEntries,
36477
36864
  REPAIRABLE_KEY,
36478
36865
  isRepairableFence,
36479
36866
  findRepairableBlockPos,
@@ -36482,6 +36869,8 @@ export {
36482
36869
  applyRepairCommand,
36483
36870
  applyAsciiDiagramCommand,
36484
36871
  AsciiDiagramWidget,
36872
+ FENCE_WIDGET_CONTAINED_EVENTS,
36873
+ containFenceWidgetEvents,
36485
36874
  findAsciiDiagramBlockPos,
36486
36875
  isAsciiSourceVisible,
36487
36876
  toggleAsciiSource,
@@ -36513,6 +36902,10 @@ export {
36513
36902
  isCodeSnippetNode,
36514
36903
  findCodeSnippetBlockPos,
36515
36904
  CodeSnippetExtension,
36905
+ HOST_FENCE_KEY,
36906
+ findHostFenceBlockPos,
36907
+ replaceHostFenceText,
36908
+ HostFenceExtension,
36516
36909
  useTreeViewData,
36517
36910
  sanitizeTreeLabel,
36518
36911
  renameItemOp,