@bendyline/squisq-editor-react 2.11.19 → 2.11.21

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.
@@ -3,9 +3,57 @@ import {
3
3
  resolveSpeechInputProvider
4
4
  } from "./chunk-DNEVUOBZ.js";
5
5
  import {
6
- markdownToTiptap
6
+ markdownToTiptap,
7
+ tiptapToMarkdown
7
8
  } from "./chunk-HSWWEWT4.js";
8
9
 
10
+ // src/InsertMenuItems.tsx
11
+ import { createContext, useContext, useEffect, useMemo, useRef } from "react";
12
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
+ var Registry = createContext(null);
14
+ function EditorInsertMenuProvider({ children }) {
15
+ const registry = useMemo(() => /* @__PURE__ */ new Set(), []);
16
+ return /* @__PURE__ */ jsx(Registry.Provider, { value: registry, children });
17
+ }
18
+ function useEditorInsertMenuItems(items) {
19
+ const registry = useContext(Registry);
20
+ const latest = useRef(items);
21
+ latest.current = items;
22
+ if (!registry) throw new Error("Insert actions require an EditorProvider.");
23
+ useEffect(() => {
24
+ const get = () => latest.current;
25
+ registry.add(get);
26
+ return () => {
27
+ registry.delete(get);
28
+ };
29
+ }, [registry]);
30
+ }
31
+ function EditorInsertMenuItems({ onClose }) {
32
+ const registry = useContext(Registry);
33
+ return /* @__PURE__ */ jsx(Fragment, { children: Array.from(registry ?? []).flatMap(
34
+ (get, group) => get().map((item) => /* @__PURE__ */ jsxs(
35
+ "button",
36
+ {
37
+ type: "button",
38
+ className: "squisq-toolbar-overflow-item",
39
+ role: "menuitem",
40
+ disabled: item.disabled,
41
+ onClick: () => {
42
+ const current = get().find((entry) => entry.id === item.id);
43
+ if (!current || current.disabled) return;
44
+ onClose();
45
+ current.onSelect();
46
+ },
47
+ children: [
48
+ /* @__PURE__ */ jsx("span", { className: "squisq-toolbar-overflow-icon", "aria-hidden": "true", children: item.icon }),
49
+ /* @__PURE__ */ jsx("span", { children: item.label })
50
+ ]
51
+ },
52
+ `${group}:${item.id}`
53
+ ))
54
+ ) });
55
+ }
56
+
9
57
  // src/blockRange.ts
10
58
  import { parseMarkdown } from "@bendyline/squisq/markdown";
11
59
 
@@ -362,11 +410,11 @@ function resolveFileKind(fileName, language) {
362
410
  }
363
411
 
364
412
  // src/useBlockNavigator.ts
365
- import { useCallback, useMemo, useState } from "react";
413
+ import { useCallback, useMemo as useMemo2, useState } from "react";
366
414
  var NEW_BLOCK_MARKDOWN = "## New section\n\n";
367
415
  function useBlockNavigator(source, setSource, opts) {
368
416
  const enabled = opts?.enabled ?? false;
369
- const slices = useMemo(() => getBlockSlices(source), [source]);
417
+ const slices = useMemo2(() => getBlockSlices(source), [source]);
370
418
  const blockCount = slices.length;
371
419
  const [rawKey, setRawKey] = useState(0);
372
420
  const activeBlockKey = blockCount === 0 ? 0 : Math.min(Math.max(rawKey, 0), blockCount - 1);
@@ -432,25 +480,25 @@ function useBlockNavigator(source, setSource, opts) {
432
480
 
433
481
  // src/EditorContext.tsx
434
482
  import {
435
- createContext as createContext2,
436
- useContext as useContext2,
483
+ createContext as createContext3,
484
+ useContext as useContext3,
437
485
  useState as useState3,
438
486
  useCallback as useCallback3,
439
- useMemo as useMemo3,
440
- useRef as useRef2,
441
- useEffect as useEffect2
487
+ useMemo as useMemo4,
488
+ useRef as useRef3,
489
+ useEffect as useEffect3
442
490
  } from "react";
443
491
  import { parseMarkdown as parseMarkdown3, stringifyMarkdown } from "@bendyline/squisq/markdown";
444
492
  import { markdownToDoc } from "@bendyline/squisq/doc";
445
493
 
446
494
  // src/speech/useDictationController.ts
447
495
  import {
448
- createContext,
496
+ createContext as createContext2,
449
497
  useCallback as useCallback2,
450
- useContext,
451
- useEffect,
452
- useMemo as useMemo2,
453
- useRef,
498
+ useContext as useContext2,
499
+ useEffect as useEffect2,
500
+ useMemo as useMemo3,
501
+ useRef as useRef2,
454
502
  useState as useState2
455
503
  } from "react";
456
504
 
@@ -742,9 +790,9 @@ function createLevelStore() {
742
790
  }
743
791
  };
744
792
  }
745
- var DictationStateContext = createContext(null);
793
+ var DictationStateContext = createContext2(null);
746
794
  function useDictationState() {
747
- return useContext(DictationStateContext);
795
+ return useContext2(DictationStateContext);
748
796
  }
749
797
  function dictationInterimText(status, pendingTakes) {
750
798
  switch (status) {
@@ -773,24 +821,24 @@ function useDictationController({
773
821
  const [status, setStatus] = useState2("idle");
774
822
  const [pendingTakes, setPendingTakes] = useState2(0);
775
823
  const [error, setError] = useState2(null);
776
- const levelsRef = useRef(null);
824
+ const levelsRef = useRef2(null);
777
825
  if (!levelsRef.current) levelsRef.current = createLevelStore();
778
826
  const levels = levelsRef.current;
779
- const providerRef = useRef(provider);
827
+ const providerRef = useRef2(provider);
780
828
  providerRef.current = provider;
781
- const readinessRef = useRef(readiness);
829
+ const readinessRef = useRef2(readiness);
782
830
  readinessRef.current = readiness;
783
- const activeViewRef = useRef(activeView);
831
+ const activeViewRef = useRef2(activeView);
784
832
  activeViewRef.current = activeView;
785
- const tiptapRef = useRef(tiptapEditor);
833
+ const tiptapRef = useRef2(tiptapEditor);
786
834
  tiptapRef.current = tiptapEditor;
787
- const monacoRef = useRef(monacoEditor);
835
+ const monacoRef = useRef2(monacoEditor);
788
836
  monacoRef.current = monacoEditor;
789
- const sessionRef = useRef(null);
790
- const heldPhrasesRef = useRef([]);
837
+ const sessionRef = useRef2(null);
838
+ const heldPhrasesRef = useRef2([]);
791
839
  const textViewActive = editorMode === "markdown" && isTextView(activeView);
792
840
  const available = provider !== null && readiness !== null && readiness.state !== "unavailable" && textViewActive;
793
- const availableRef = useRef(available);
841
+ const availableRef = useRef2(available);
794
842
  availableRef.current = available;
795
843
  const restoreEditorFocus = useCallback2(() => {
796
844
  if (typeof document === "undefined") return;
@@ -876,7 +924,7 @@ function useDictationController({
876
924
  }
877
925
  return;
878
926
  }
879
- const { startDictationSession } = await import("./dictationSession-PN3OT5LJ.js");
927
+ const { startDictationSession } = await import("./dictationSession-SLDNYIZ2.js");
880
928
  if (!isCurrent()) return;
881
929
  const handle = await startDictationSession({
882
930
  provider: active,
@@ -939,7 +987,7 @@ function useDictationController({
939
987
  void start();
940
988
  }, [start, stop]);
941
989
  const dismissError = useCallback2(() => setError(null), []);
942
- useEffect(() => {
990
+ useEffect2(() => {
943
991
  if (!capability) {
944
992
  setProvider(null);
945
993
  setReadiness(null);
@@ -977,7 +1025,7 @@ function useDictationController({
977
1025
  if (owned) instance.dispose?.();
978
1026
  };
979
1027
  }, [capability]);
980
- useEffect(
1028
+ useEffect2(
981
1029
  () => () => {
982
1030
  const session = sessionRef.current;
983
1031
  if (!session) return;
@@ -987,11 +1035,11 @@ function useDictationController({
987
1035
  },
988
1036
  []
989
1037
  );
990
- useEffect(() => {
1038
+ useEffect2(() => {
991
1039
  if (!textViewActive && sessionRef.current) void stop();
992
1040
  }, [textViewActive, stop]);
993
1041
  const sessionActive = status !== "idle";
994
- useEffect(() => {
1042
+ useEffect2(() => {
995
1043
  if (!sessionActive || typeof window === "undefined") return;
996
1044
  const onKeyDown = (event) => {
997
1045
  if (event.key !== "Escape" || event.defaultPrevented) return;
@@ -1000,7 +1048,7 @@ function useDictationController({
1000
1048
  window.addEventListener("keydown", onKeyDown);
1001
1049
  return () => window.removeEventListener("keydown", onKeyDown);
1002
1050
  }, [sessionActive, stop]);
1003
- useEffect(() => {
1051
+ useEffect2(() => {
1004
1052
  if (heldPhrasesRef.current.length === 0) return;
1005
1053
  const held = heldPhrasesRef.current;
1006
1054
  heldPhrasesRef.current = [];
@@ -1013,23 +1061,23 @@ function useDictationController({
1013
1061
  }, [activeView, tiptapEditor, monacoEditor, insertPhrase]);
1014
1062
  const hasCapability = capability !== null;
1015
1063
  const interimLabel = hasCapability ? dictationInterimText(status, pendingTakes) : null;
1016
- useEffect(() => {
1064
+ useEffect2(() => {
1017
1065
  if (!tiptapEditor || !hasCapability) return;
1018
1066
  setDictationInterim(tiptapEditor, activeView === "wysiwyg" ? interimLabel : null);
1019
1067
  }, [tiptapEditor, hasCapability, activeView, interimLabel]);
1020
- useEffect(() => {
1068
+ useEffect2(() => {
1021
1069
  if (!tiptapEditor || !hasCapability) return;
1022
1070
  return () => setDictationInterim(tiptapEditor, null);
1023
1071
  }, [tiptapEditor, hasCapability]);
1024
- const monacoInterimRef = useRef(null);
1025
- useEffect(() => {
1072
+ const monacoInterimRef = useRef2(null);
1073
+ useEffect2(() => {
1026
1074
  if (!monacoEditor) return;
1027
1075
  return () => {
1028
1076
  monacoInterimRef.current?.dispose();
1029
1077
  monacoInterimRef.current = null;
1030
1078
  };
1031
1079
  }, [monacoEditor]);
1032
- useEffect(() => {
1080
+ useEffect2(() => {
1033
1081
  if (!monacoEditor) return;
1034
1082
  const label = activeView === "raw" ? interimLabel : null;
1035
1083
  if (!label && !monacoInterimRef.current) return;
@@ -1037,12 +1085,12 @@ function useDictationController({
1037
1085
  monacoInterimRef.current = createMonacoDictationInterim(monacoEditor);
1038
1086
  monacoInterimRef.current.update(label);
1039
1087
  }, [monacoEditor, activeView, interimLabel]);
1040
- const control = useMemo2(
1088
+ const control = useMemo3(
1041
1089
  () => hasCapability ? { active: sessionActive, available, toggle } : null,
1042
1090
  [hasCapability, sessionActive, available, toggle]
1043
1091
  );
1044
1092
  const providerLabel = provider?.label ?? "";
1045
- const state = useMemo2(
1093
+ const state = useMemo3(
1046
1094
  () => hasCapability ? {
1047
1095
  status,
1048
1096
  readiness,
@@ -1077,6 +1125,24 @@ import {
1077
1125
  import { createDocument } from "@tiptap/core";
1078
1126
  import { closeHistory as closeHistory2 } from "@tiptap/pm/history";
1079
1127
 
1128
+ // src/selectionMarkdown.ts
1129
+ import { DOMSerializer, Fragment as Fragment2 } from "@tiptap/pm/model";
1130
+ function selectionMarkdown(state) {
1131
+ const { selection } = state;
1132
+ if (selection.empty) return "";
1133
+ const { $from, $to, from, to } = selection;
1134
+ let fragment = selection.content().content;
1135
+ let inline = false;
1136
+ if ($from.sameParent($to) && $from.parent.isTextblock) {
1137
+ inline = $from.parentOffset !== 0 || $to.parentOffset !== $from.parent.content.size;
1138
+ fragment = !inline ? Fragment2.from($from.parent) : state.doc.slice(from, to).content;
1139
+ }
1140
+ const container = document.createElement("div");
1141
+ const target = inline ? container.appendChild(document.createElement("p")) : container;
1142
+ target.append(DOMSerializer.fromSchema(state.schema).serializeFragment(fragment));
1143
+ return tiptapToMarkdown(container.innerHTML).trimEnd();
1144
+ }
1145
+
1080
1146
  // src/blockInsertion.ts
1081
1147
  import { isContainerTemplate as isContainerTemplate2 } from "@bendyline/squisq/doc";
1082
1148
  function tiptapOutline(doc) {
@@ -1400,17 +1466,17 @@ function restoreWysiwygScrollRatio(editor, ratio) {
1400
1466
  }
1401
1467
 
1402
1468
  // src/EditorContext.tsx
1403
- import { jsx } from "react/jsx-runtime";
1404
- var EditorContext = createContext2(null);
1469
+ import { jsx as jsx2 } from "react/jsx-runtime";
1470
+ var EditorContext = createContext3(null);
1405
1471
  function useEditorContext() {
1406
- const ctx = useContext2(EditorContext);
1472
+ const ctx = useContext3(EditorContext);
1407
1473
  if (!ctx) {
1408
1474
  throw new Error("useEditorContext must be used within an <EditorProvider>");
1409
1475
  }
1410
1476
  return ctx;
1411
1477
  }
1412
1478
  function useEditorContextOptional() {
1413
- return useContext2(EditorContext);
1479
+ return useContext3(EditorContext);
1414
1480
  }
1415
1481
  var MEDIA_EDIT_GC_DELAY_MS = 5e3;
1416
1482
  var DEFAULT_PRUNE_POLICY = { type: "keep-last-n", n: 50 };
@@ -1473,13 +1539,13 @@ function EditorProvider({
1473
1539
  );
1474
1540
  const effectiveThemeInheritance = viewPreferences?.themeInheritance ?? themeInheritance;
1475
1541
  const effectiveLayoutMode = viewPreferences?.layoutMode ?? layoutMode;
1476
- const { mode: fileMode, language: resolvedLanguage } = useMemo3(
1542
+ const { mode: fileMode, language: resolvedLanguage } = useMemo4(
1477
1543
  () => resolveFileKind(fileName, language),
1478
1544
  [fileName, language]
1479
1545
  );
1480
1546
  const editorMode = fileMode === "data" ? "code" : fileMode;
1481
1547
  const [markdownSource, setMarkdownSourceRaw] = useState3(initialMarkdown);
1482
- const markdownSourceRef = useRef2(markdownSource);
1548
+ const markdownSourceRef = useRef3(markdownSource);
1483
1549
  markdownSourceRef.current = markdownSource;
1484
1550
  const [markdownDoc, setMarkdownDocState] = useState3(null);
1485
1551
  const [doc, setDoc] = useState3(null);
@@ -1496,11 +1562,11 @@ function EditorProvider({
1496
1562
  },
1497
1563
  [controlledFindMode, editorMode, onFindModeChange]
1498
1564
  );
1499
- const activeViewRef = useRef2(activeView);
1565
+ const activeViewRef = useRef3(activeView);
1500
1566
  activeViewRef.current = activeView;
1501
- const tiptapEditorRef = useRef2(null);
1502
- const monacoEditorRef = useRef2(null);
1503
- const pendingEditorScrollRef = useRef2(null);
1567
+ const tiptapEditorRef = useRef3(null);
1568
+ const monacoEditorRef = useRef3(null);
1569
+ const pendingEditorScrollRef = useRef3(null);
1504
1570
  const setActiveView = useCallback3(
1505
1571
  (view) => {
1506
1572
  if (editorMode === "code" && view !== "raw") return;
@@ -1525,34 +1591,34 @@ function EditorProvider({
1525
1591
  const [isParsing, setIsParsing] = useState3(false);
1526
1592
  const [colorScheme, setColorScheme] = useState3(initialColorScheme);
1527
1593
  const [inlinePreviewVisible, setInlinePreviewVisibleRaw] = useState3(effectiveInlinePreview);
1528
- useEffect2(() => {
1594
+ useEffect3(() => {
1529
1595
  if (viewPreferences?.inlinePreview === void 0) setInlinePreviewVisibleRaw(inlinePreview);
1530
1596
  }, [inlinePreview, viewPreferences?.inlinePreview]);
1531
1597
  const [statusBarVisible, setStatusBarVisibleRaw] = useState3(effectiveShowStatusBar);
1532
- useEffect2(() => {
1598
+ useEffect3(() => {
1533
1599
  if (viewPreferences?.showStatusBar === void 0) setStatusBarVisibleRaw(showStatusBar);
1534
1600
  }, [showStatusBar, viewPreferences?.showStatusBar]);
1535
1601
  const [outlineVisible, setOutlineVisibleRaw] = useState3(effectiveOutline);
1536
- useEffect2(() => {
1602
+ useEffect3(() => {
1537
1603
  if (viewPreferences?.outline === void 0) setOutlineVisibleRaw(outline);
1538
1604
  }, [outline, viewPreferences?.outline]);
1539
1605
  const [blockTagVisibilityState, setBlockTagVisibilityRaw] = useState3(
1540
1606
  effectiveBlockTagVisibility
1541
1607
  );
1542
1608
  const blockTagsVisible = blockTagVisibilityState !== "none";
1543
- useEffect2(() => {
1609
+ useEffect3(() => {
1544
1610
  if (viewPreferences?.blockTagVisibility === void 0 && viewPreferences?.blockTags === void 0) {
1545
1611
  setBlockTagVisibilityRaw(normalizeBlockTagVisibility(initialBlockTagVisibility ?? blockTags));
1546
1612
  }
1547
1613
  }, [blockTags, initialBlockTagVisibility, viewPreferences]);
1548
1614
  const [themeInheritanceState, setThemeInheritanceRaw] = useState3(effectiveThemeInheritance);
1549
- useEffect2(() => {
1615
+ useEffect3(() => {
1550
1616
  if (viewPreferences?.themeInheritance === void 0) {
1551
1617
  setThemeInheritanceRaw(themeInheritance);
1552
1618
  }
1553
1619
  }, [themeInheritance, viewPreferences?.themeInheritance]);
1554
1620
  const [layoutModeState, setLayoutModeRaw] = useState3(effectiveLayoutMode);
1555
- useEffect2(() => {
1621
+ useEffect3(() => {
1556
1622
  if (viewPreferences?.layoutMode === void 0) setLayoutModeRaw(layoutMode);
1557
1623
  }, [layoutMode, viewPreferences?.layoutMode]);
1558
1624
  const [imageEditTarget, setImageEditTarget] = useState3(null);
@@ -1573,7 +1639,7 @@ function EditorProvider({
1573
1639
  const bumpMediaRevision = useCallback3(() => {
1574
1640
  setMediaRevision((n) => n + 1);
1575
1641
  }, []);
1576
- useEffect2(() => {
1642
+ useEffect3(() => {
1577
1643
  if (!viewPreferences) return;
1578
1644
  if (viewPreferences.inlinePreview !== void 0) {
1579
1645
  setInlinePreviewVisibleRaw(viewPreferences.inlinePreview);
@@ -1596,19 +1662,19 @@ function EditorProvider({
1596
1662
  setLayoutModeRaw(viewPreferences.layoutMode);
1597
1663
  }
1598
1664
  }, [viewPreferences]);
1599
- const onViewPreferencesChangeRef = useRef2(onViewPreferencesChange);
1665
+ const onViewPreferencesChangeRef = useRef3(onViewPreferencesChange);
1600
1666
  onViewPreferencesChangeRef.current = onViewPreferencesChange;
1601
- const inlinePreviewRef = useRef2(inlinePreviewVisible);
1667
+ const inlinePreviewRef = useRef3(inlinePreviewVisible);
1602
1668
  inlinePreviewRef.current = inlinePreviewVisible;
1603
- const statusBarRef = useRef2(statusBarVisible);
1669
+ const statusBarRef = useRef3(statusBarVisible);
1604
1670
  statusBarRef.current = statusBarVisible;
1605
- const outlineRef = useRef2(outlineVisible);
1671
+ const outlineRef = useRef3(outlineVisible);
1606
1672
  outlineRef.current = outlineVisible;
1607
- const blockTagVisibilityRef = useRef2(blockTagVisibilityState);
1673
+ const blockTagVisibilityRef = useRef3(blockTagVisibilityState);
1608
1674
  blockTagVisibilityRef.current = blockTagVisibilityState;
1609
- const themeInheritanceRef = useRef2(themeInheritanceState);
1675
+ const themeInheritanceRef = useRef3(themeInheritanceState);
1610
1676
  themeInheritanceRef.current = themeInheritanceState;
1611
- const layoutModeRef = useRef2(layoutModeState);
1677
+ const layoutModeRef = useRef3(layoutModeState);
1612
1678
  layoutModeRef.current = layoutModeState;
1613
1679
  const setInlinePreviewVisible = useCallback3((visible) => {
1614
1680
  setInlinePreviewVisibleRaw(visible);
@@ -1696,7 +1762,7 @@ function EditorProvider({
1696
1762
  monacoEditorRef.current = editor;
1697
1763
  setMonacoEditorRaw(editor);
1698
1764
  }, []);
1699
- useEffect2(() => {
1765
+ useEffect3(() => {
1700
1766
  const pending = pendingEditorScrollRef.current;
1701
1767
  if (!pending || pending.view !== activeView) return;
1702
1768
  const restore = activeView === "raw" && monacoEditor ? () => restoreMonacoScrollRatio(monacoEditor, pending.ratio) : activeView === "wysiwyg" && tiptapEditor ? () => restoreWysiwygScrollRatio(tiptapEditor, pending.ratio) : null;
@@ -1721,17 +1787,17 @@ function EditorProvider({
1721
1787
  };
1722
1788
  }, [activeView, monacoEditor, tiptapEditor]);
1723
1789
  const [activeSceneText, setActiveSceneText] = useState3(null);
1724
- const sceneTextChannel = useMemo3(createSceneTextChannel, []);
1725
- useEffect2(() => sceneTextChannel.subscribe(setActiveSceneText), [sceneTextChannel]);
1726
- const articleIdRef = useRef2(articleId);
1790
+ const sceneTextChannel = useMemo4(createSceneTextChannel, []);
1791
+ useEffect3(() => sceneTextChannel.subscribe(setActiveSceneText), [sceneTextChannel]);
1792
+ const articleIdRef = useRef3(articleId);
1727
1793
  articleIdRef.current = articleId;
1728
- const fileNameRef = useRef2(fileName);
1794
+ const fileNameRef = useRef3(fileName);
1729
1795
  fileNameRef.current = fileName;
1730
- useEffect2(() => {
1796
+ useEffect3(() => {
1731
1797
  setColorScheme(initialColorScheme);
1732
1798
  }, [initialColorScheme]);
1733
- const parseTimeoutRef = useRef2(null);
1734
- const skipInitialDebouncedParseRef = useRef2(Boolean(initialMarkdown));
1799
+ const parseTimeoutRef = useRef3(null);
1800
+ const skipInitialDebouncedParseRef = useRef3(Boolean(initialMarkdown));
1735
1801
  const doParse = useCallback3((source) => {
1736
1802
  setIsParsing(true);
1737
1803
  try {
@@ -1756,7 +1822,7 @@ function EditorProvider({
1756
1822
  setIsParsing(false);
1757
1823
  }
1758
1824
  }, []);
1759
- useEffect2(() => {
1825
+ useEffect3(() => {
1760
1826
  if (editorMode !== "markdown") return;
1761
1827
  if (skipInitialDebouncedParseRef.current) {
1762
1828
  skipInitialDebouncedParseRef.current = false;
@@ -1775,7 +1841,7 @@ function EditorProvider({
1775
1841
  }
1776
1842
  };
1777
1843
  }, [markdownSource, doParse, editorMode]);
1778
- useEffect2(() => {
1844
+ useEffect3(() => {
1779
1845
  if (editorMode !== "markdown") return;
1780
1846
  if (initialMarkdown) {
1781
1847
  doParse(initialMarkdown);
@@ -1800,7 +1866,7 @@ function EditorProvider({
1800
1866
  addBlock
1801
1867
  } = blockNav;
1802
1868
  const [selectionVersion, setSelectionVersion] = useState3(0);
1803
- useEffect2(() => {
1869
+ useEffect3(() => {
1804
1870
  const bump = () => setSelectionVersion((version) => version + 1);
1805
1871
  const disposers = [];
1806
1872
  if (typeof tiptapEditor?.on === "function" && typeof tiptapEditor.off === "function") {
@@ -1823,6 +1889,7 @@ function EditorProvider({
1823
1889
  return {
1824
1890
  view: "wysiwyg",
1825
1891
  text: empty ? "" : tiptapEditor.state.doc.textBetween(from, to, "\n", " "),
1892
+ markdown: selectionMarkdown(tiptapEditor.state),
1826
1893
  empty
1827
1894
  };
1828
1895
  }
@@ -1893,7 +1960,7 @@ function EditorProvider({
1893
1960
  const applySourceEdits = useCallback3(
1894
1961
  (edits, options) => {
1895
1962
  if (editorMode !== "markdown" || layoutModeState !== "document") return false;
1896
- if (activeView === "raw" && monacoEditor) {
1963
+ if ((activeView === "raw" || activeView === "preview") && monacoEditor) {
1897
1964
  const model = monacoEditor.getModel();
1898
1965
  if (!model) return false;
1899
1966
  const base = model.getValue();
@@ -1919,7 +1986,7 @@ function EditorProvider({
1919
1986
  monacoEditor.pushUndoStop();
1920
1987
  return true;
1921
1988
  }
1922
- if (activeView === "wysiwyg" && tiptapEditor) {
1989
+ if ((activeView === "wysiwyg" || activeView === "preview") && tiptapEditor) {
1923
1990
  const base = markdownSourceRef.current;
1924
1991
  if (options?.baseSource !== void 0 && options.baseSource !== base) return false;
1925
1992
  const sorted = normalizeSourceEdits(base, edits);
@@ -2008,8 +2075,8 @@ function EditorProvider({
2008
2075
  },
2009
2076
  [activeView, editorMode, monacoEditor, tiptapEditor]
2010
2077
  );
2011
- const versioningWarnedRef = useRef2(false);
2012
- useEffect2(() => {
2078
+ const versioningWarnedRef = useRef3(false);
2079
+ useEffect3(() => {
2013
2080
  if (allowVersioning && !workspaceContainer && !versioningWarnedRef.current) {
2014
2081
  console.warn(
2015
2082
  "[squisq-editor] allowVersioning requires a `workspaceContainer` prop; versioning is disabled."
@@ -2017,13 +2084,13 @@ function EditorProvider({
2017
2084
  versioningWarnedRef.current = true;
2018
2085
  }
2019
2086
  }, [allowVersioning, workspaceContainer]);
2020
- const versioning = useMemo3(() => {
2087
+ const versioning = useMemo4(() => {
2021
2088
  if (!allowVersioning || !workspaceContainer) return null;
2022
2089
  return new DocumentVersionManager(workspaceContainer, { basename: versionBasename });
2023
2090
  }, [allowVersioning, workspaceContainer, versionBasename]);
2024
- const onSaveVersionRef = useRef2(onSaveVersion);
2091
+ const onSaveVersionRef = useRef3(onSaveVersion);
2025
2092
  onSaveVersionRef.current = onSaveVersion;
2026
- const prunePolicyRef = useRef2(versioningPrunePolicy);
2093
+ const prunePolicyRef = useRef3(versioningPrunePolicy);
2027
2094
  prunePolicyRef.current = versioningPrunePolicy;
2028
2095
  const saveVersion = useCallback3(
2029
2096
  async (options) => {
@@ -2046,8 +2113,8 @@ function EditorProvider({
2046
2113
  },
2047
2114
  [versioning]
2048
2115
  );
2049
- const autoSaveSourceRef = useRef2(null);
2050
- useEffect2(() => {
2116
+ const autoSaveSourceRef = useRef3(null);
2117
+ useEffect3(() => {
2051
2118
  if (!versioning) return;
2052
2119
  if (versioningAutoSaveIdleMs <= 0) return;
2053
2120
  const previous = autoSaveSourceRef.current;
@@ -2083,12 +2150,12 @@ function EditorProvider({
2083
2150
  setParseError(err instanceof Error ? err.message : "Stringify error");
2084
2151
  }
2085
2152
  }, []);
2086
- useEffect2(() => {
2153
+ useEffect3(() => {
2087
2154
  if (mediaEditRenders && doc) mediaEditRenders.sync(doc, embeddedMediaClips(doc));
2088
2155
  }, [mediaEditRenders, doc]);
2089
- const latestDocRef = useRef2(doc);
2156
+ const latestDocRef = useRef3(doc);
2090
2157
  latestDocRef.current = doc;
2091
- useEffect2(() => {
2158
+ useEffect3(() => {
2092
2159
  if (!mediaEditRenders) return;
2093
2160
  const timer = setTimeout(() => {
2094
2161
  const current = latestDocRef.current;
@@ -2105,7 +2172,7 @@ function EditorProvider({
2105
2172
  monacoEditor
2106
2173
  });
2107
2174
  const dictation = dictationController.control;
2108
- const value = useMemo3(
2175
+ const value = useMemo4(
2109
2176
  () => ({
2110
2177
  markdownSource,
2111
2178
  markdownDoc,
@@ -2283,7 +2350,7 @@ function EditorProvider({
2283
2350
  bumpMediaRevision
2284
2351
  ]
2285
2352
  );
2286
- return /* @__PURE__ */ jsx(EditorContext.Provider, { value, children: /* @__PURE__ */ jsx(DictationStateContext.Provider, { value: dictationController.state, children }) });
2353
+ return /* @__PURE__ */ jsx2(EditorContext.Provider, { value, children: /* @__PURE__ */ jsx2(DictationStateContext.Provider, { value: dictationController.state, children: /* @__PURE__ */ jsx2(EditorInsertMenuProvider, { children }) }) });
2287
2354
  }
2288
2355
 
2289
2356
  // src/review/decorations.ts
@@ -2651,7 +2718,7 @@ function messageOf(error) {
2651
2718
  }
2652
2719
 
2653
2720
  // src/review/useReview.ts
2654
- import { useCallback as useCallback4, useEffect as useEffect3, useMemo as useMemo4, useRef as useRef3, useState as useState4 } from "react";
2721
+ import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo5, useRef as useRef4, useState as useState4 } from "react";
2655
2722
  var EMPTY = [];
2656
2723
  var EMPTY_FINDINGS = [];
2657
2724
  function useReview(capabilities = EMPTY) {
@@ -2664,23 +2731,23 @@ function useReview(capabilities = EMPTY) {
2664
2731
  articleId,
2665
2732
  goToBlockByLine
2666
2733
  } = useEditorContext();
2667
- const providers = useMemo4(
2734
+ const providers = useMemo5(
2668
2735
  () => capabilities.map(resolveReviewProvider),
2669
2736
  [capabilities]
2670
2737
  );
2671
2738
  const [snapshot, setSnapshot] = useState4(null);
2672
- const sourceRef = useRef3(markdownSource);
2673
- const revisionRef = useRef3(0);
2674
- const blocksRef = useRef3([]);
2675
- const runnerRef = useRef3(null);
2739
+ const sourceRef = useRef4(markdownSource);
2740
+ const revisionRef = useRef4(0);
2741
+ const blocksRef = useRef4([]);
2742
+ const runnerRef = useRef4(null);
2676
2743
  sourceRef.current = markdownSource;
2677
- const hueFor = useMemo4(() => {
2744
+ const hueFor = useMemo5(() => {
2678
2745
  const hues = new Map(
2679
2746
  providers.map((provider) => [provider.id, provider.hue ?? DEFAULT_REVIEW_HUE])
2680
2747
  );
2681
2748
  return (source) => hues.get(source) ?? DEFAULT_REVIEW_HUE;
2682
2749
  }, [providers]);
2683
- useEffect3(() => {
2750
+ useEffect4(() => {
2684
2751
  if (providers.length === 0) {
2685
2752
  setSnapshot(null);
2686
2753
  return;
@@ -2707,12 +2774,12 @@ function useReview(capabilities = EMPTY) {
2707
2774
  runnerRef.current = null;
2708
2775
  };
2709
2776
  }, [providers, articleId, fileName]);
2710
- useEffect3(() => {
2777
+ useEffect4(() => {
2711
2778
  revisionRef.current += 1;
2712
2779
  runnerRef.current?.schedule();
2713
2780
  }, [markdownSource]);
2714
- const findings = useMemo4(() => snapshot?.findings ?? EMPTY_FINDINGS, [snapshot]);
2715
- useEffect3(() => {
2781
+ const findings = useMemo5(() => snapshot?.findings ?? EMPTY_FINDINGS, [snapshot]);
2782
+ useEffect4(() => {
2716
2783
  if (!tiptapEditor || activeView !== "wysiwyg") return;
2717
2784
  if (findings.length === 0) {
2718
2785
  clearReviewDecorations(tiptapEditor);
@@ -2723,8 +2790,8 @@ function useReview(capabilities = EMPTY) {
2723
2790
  buildWriteViewDecorations(tiptapEditor.state.doc, findings, hueFor)
2724
2791
  );
2725
2792
  }, [tiptapEditor, activeView, findings, hueFor]);
2726
- const collectionRef = useRef3(null);
2727
- useEffect3(() => {
2793
+ const collectionRef = useRef4(null);
2794
+ useEffect4(() => {
2728
2795
  if (!monacoEditor || activeView !== "raw") return;
2729
2796
  const model = monacoEditor.getModel();
2730
2797
  if (!model) return;
@@ -2749,40 +2816,40 @@ function useReview(capabilities = EMPTY) {
2749
2816
  const dismissFinding = useCallback4((findingId) => {
2750
2817
  runnerRef.current?.removeFinding(findingId);
2751
2818
  }, []);
2752
- return useMemo4(
2819
+ return useMemo5(
2753
2820
  () => snapshot ? { ...snapshot, reviewNow, dismissFinding, goToFinding } : null,
2754
2821
  [snapshot, reviewNow, dismissFinding, goToFinding]
2755
2822
  );
2756
2823
  }
2757
2824
 
2758
2825
  // src/review/ReviewContext.tsx
2759
- import { createContext as createContext3, useContext as useContext3 } from "react";
2760
- import { jsx as jsx2 } from "react/jsx-runtime";
2761
- var ReviewStateContext = createContext3(null);
2826
+ import { createContext as createContext4, useContext as useContext4 } from "react";
2827
+ import { jsx as jsx3 } from "react/jsx-runtime";
2828
+ var ReviewStateContext = createContext4(null);
2762
2829
  function useReviewState() {
2763
- return useContext3(ReviewStateContext);
2830
+ return useContext4(ReviewStateContext);
2764
2831
  }
2765
2832
  function ReviewRoot({
2766
2833
  providers,
2767
2834
  children
2768
2835
  }) {
2769
2836
  const state = useReview(providers);
2770
- return /* @__PURE__ */ jsx2(ReviewStateContext.Provider, { value: state, children });
2837
+ return /* @__PURE__ */ jsx3(ReviewStateContext.Provider, { value: state, children });
2771
2838
  }
2772
2839
 
2773
2840
  // src/review/ReviewPanel.tsx
2774
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
2841
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
2775
2842
  function ReviewPanel() {
2776
2843
  const state = useReviewState();
2777
2844
  if (!state || state.providers.length === 0) return null;
2778
2845
  const total = state.findings.length;
2779
- return /* @__PURE__ */ jsxs("aside", { className: "squisq-review-panel", "aria-label": "Review findings", children: [
2780
- /* @__PURE__ */ jsxs("div", { className: "squisq-review-panel-header", children: [
2781
- /* @__PURE__ */ jsxs("span", { className: "squisq-review-panel-title", children: [
2846
+ return /* @__PURE__ */ jsxs2("aside", { className: "squisq-review-panel", "aria-label": "Review findings", children: [
2847
+ /* @__PURE__ */ jsxs2("div", { className: "squisq-review-panel-header", children: [
2848
+ /* @__PURE__ */ jsxs2("span", { className: "squisq-review-panel-title", children: [
2782
2849
  "Review",
2783
- /* @__PURE__ */ jsx3("span", { className: "squisq-review-panel-count", children: total })
2850
+ /* @__PURE__ */ jsx4("span", { className: "squisq-review-panel-count", children: total })
2784
2851
  ] }),
2785
- /* @__PURE__ */ jsx3(
2852
+ /* @__PURE__ */ jsx4(
2786
2853
  "button",
2787
2854
  {
2788
2855
  type: "button",
@@ -2793,22 +2860,22 @@ function ReviewPanel() {
2793
2860
  }
2794
2861
  )
2795
2862
  ] }),
2796
- state.providers.map((provider) => /* @__PURE__ */ jsx3(ProviderGroup, { provider, state }, provider.id))
2863
+ state.providers.map((provider) => /* @__PURE__ */ jsx4(ProviderGroup, { provider, state }, provider.id))
2797
2864
  ] });
2798
2865
  }
2799
2866
  function ProviderGroup({
2800
2867
  provider,
2801
2868
  state
2802
2869
  }) {
2803
- return /* @__PURE__ */ jsxs("section", { className: "squisq-review-panel-group", children: [
2804
- /* @__PURE__ */ jsx3("h3", { className: "squisq-review-panel-group-title", children: provider.label }),
2805
- provider.phase === "preparing" && /* @__PURE__ */ jsx3("div", { className: "squisq-review-panel-empty", children: "Preparing\u2026" }),
2806
- provider.phase === "running" && /* @__PURE__ */ jsx3("div", { className: "squisq-review-panel-empty", children: "Reviewing\u2026" }),
2870
+ return /* @__PURE__ */ jsxs2("section", { className: "squisq-review-panel-group", children: [
2871
+ /* @__PURE__ */ jsx4("h3", { className: "squisq-review-panel-group-title", children: provider.label }),
2872
+ provider.phase === "preparing" && /* @__PURE__ */ jsx4("div", { className: "squisq-review-panel-empty", children: "Preparing\u2026" }),
2873
+ provider.phase === "running" && /* @__PURE__ */ jsx4("div", { className: "squisq-review-panel-empty", children: "Reviewing\u2026" }),
2807
2874
  provider.phase === "error" && // The provider's own message, verbatim: a reviewer that cannot reach
2808
2875
  // its model and one whose engine is missing need different fixes.
2809
- /* @__PURE__ */ jsx3("div", { className: "squisq-review-panel-empty", role: "status", children: provider.error ?? "Review failed." }),
2810
- provider.phase === "ready" && provider.findings.length === 0 && /* @__PURE__ */ jsx3("div", { className: "squisq-review-panel-empty", children: "Nothing to report." }),
2811
- /* @__PURE__ */ jsx3("ul", { className: "squisq-review-panel-list", children: provider.findings.map((finding) => /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsx3(Row, { finding, onGo: state.goToFinding, onDismiss: state.dismissFinding }) }, finding.id)) })
2876
+ /* @__PURE__ */ jsx4("div", { className: "squisq-review-panel-empty", role: "status", children: provider.error ?? "Review failed." }),
2877
+ provider.phase === "ready" && provider.findings.length === 0 && /* @__PURE__ */ jsx4("div", { className: "squisq-review-panel-empty", children: "Nothing to report." }),
2878
+ /* @__PURE__ */ jsx4("ul", { className: "squisq-review-panel-list", children: provider.findings.map((finding) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(Row, { finding, onGo: state.goToFinding, onDismiss: state.dismissFinding }) }, finding.id)) })
2812
2879
  ] });
2813
2880
  }
2814
2881
  function Row({
@@ -2816,30 +2883,30 @@ function Row({
2816
2883
  onGo,
2817
2884
  onDismiss
2818
2885
  }) {
2819
- return /* @__PURE__ */ jsxs("div", { className: "squisq-review-panel-row", children: [
2820
- /* @__PURE__ */ jsxs(
2886
+ return /* @__PURE__ */ jsxs2("div", { className: "squisq-review-panel-row", children: [
2887
+ /* @__PURE__ */ jsxs2(
2821
2888
  "button",
2822
2889
  {
2823
2890
  type: "button",
2824
2891
  className: "squisq-review-panel-row-main",
2825
2892
  onClick: () => onGo(finding.id),
2826
2893
  children: [
2827
- /* @__PURE__ */ jsx3(
2894
+ /* @__PURE__ */ jsx4(
2828
2895
  "span",
2829
2896
  {
2830
2897
  className: `squisq-review-dot squisq-review-dot--${finding.severity}`,
2831
2898
  "aria-hidden": true
2832
2899
  }
2833
2900
  ),
2834
- /* @__PURE__ */ jsxs("span", { className: "squisq-review-panel-row-body", children: [
2835
- /* @__PURE__ */ jsx3("span", { className: "squisq-review-panel-excerpt", children: finding.originalText }),
2836
- /* @__PURE__ */ jsx3("span", { className: "squisq-review-panel-message", children: finding.message }),
2837
- finding.rationale && /* @__PURE__ */ jsx3("span", { className: "squisq-review-panel-rationale", children: finding.rationale })
2901
+ /* @__PURE__ */ jsxs2("span", { className: "squisq-review-panel-row-body", children: [
2902
+ /* @__PURE__ */ jsx4("span", { className: "squisq-review-panel-excerpt", children: finding.originalText }),
2903
+ /* @__PURE__ */ jsx4("span", { className: "squisq-review-panel-message", children: finding.message }),
2904
+ finding.rationale && /* @__PURE__ */ jsx4("span", { className: "squisq-review-panel-rationale", children: finding.rationale })
2838
2905
  ] })
2839
2906
  ]
2840
2907
  }
2841
2908
  ),
2842
- /* @__PURE__ */ jsx3(
2909
+ /* @__PURE__ */ jsx4(
2843
2910
  "button",
2844
2911
  {
2845
2912
  type: "button",
@@ -2854,6 +2921,8 @@ function Row({
2854
2921
  }
2855
2922
 
2856
2923
  export {
2924
+ useEditorInsertMenuItems,
2925
+ EditorInsertMenuItems,
2857
2926
  DictationExtension,
2858
2927
  useDictationState,
2859
2928
  stripFrontmatter,