@bendyline/squisq-editor-react 2.11.18 → 2.11.19

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.
@@ -17,7 +17,7 @@ import {
17
17
  useDictationState,
18
18
  useEditorContext,
19
19
  useEditorContextOptional
20
- } from "./chunk-45NQUR62.js";
20
+ } from "./chunk-TZX7RKZV.js";
21
21
  import {
22
22
  monacoLanguageRequestKey,
23
23
  monacoLanguagesForDocument,
@@ -25,14 +25,14 @@ import {
25
25
  } from "./chunk-V4NBQF5C.js";
26
26
  import {
27
27
  MarkdownEscape
28
- } from "./chunk-2XXBDECJ.js";
28
+ } from "./chunk-OCQEWWHI.js";
29
29
  import {
30
30
  escapeLinkLabel,
31
31
  formatLinkDestination,
32
32
  markdownToTiptap,
33
33
  mediaEditNodeAttributes,
34
34
  tiptapToMarkdown
35
- } from "./chunk-DPAMJV6A.js";
35
+ } from "./chunk-HSWWEWT4.js";
36
36
  import {
37
37
  ImageEditor,
38
38
  ImageViewer,
@@ -45,13 +45,13 @@ import {
45
45
  documentTitleFromFileName,
46
46
  resolveBlockVisual,
47
47
  useModalDialog
48
- } from "./chunk-U6NVT3BY.js";
48
+ } from "./chunk-UFK47ZAI.js";
49
49
  import {
50
50
  Icon
51
51
  } from "./chunk-GS7QWYFT.js";
52
52
  import {
53
53
  TeleprompterView
54
- } from "./chunk-H7YTL6SL.js";
54
+ } from "./chunk-DJS5I5NO.js";
55
55
 
56
56
  // src/useMonacoLoader.ts
57
57
  import { useEffect, useState } from "react";
@@ -863,7 +863,7 @@ function formatBytes(n) {
863
863
  return `${(n / (1024 * 1024)).toFixed(1)} MB`;
864
864
  }
865
865
  function VersionHistoryPanel() {
866
- const { versioning, saveVersion, replaceAll, markdownSource, colorScheme } = useEditorContext();
866
+ const { versioning, versioningAutoSaves, saveVersion, replaceAll, markdownSource, colorScheme } = useEditorContext();
867
867
  const [open, setOpen] = useState2(false);
868
868
  const [state, setState3] = useState2(initialState);
869
869
  const containerRef = useRef2(null);
@@ -1006,7 +1006,7 @@ function VersionHistoryPanel() {
1006
1006
  /* @__PURE__ */ jsxs("div", { className: "squisq-version-history-list-pane", children: [
1007
1007
  /* @__PURE__ */ jsx("div", { className: "squisq-version-history-header", children: /* @__PURE__ */ jsx("span", { className: "squisq-version-history-title", children: "Version history" }) }),
1008
1008
  state.error && /* @__PURE__ */ jsx("div", { className: "squisq-version-history-error", children: state.error }),
1009
- state.loading ? /* @__PURE__ */ jsx("div", { className: "squisq-version-history-empty", children: "Loading\u2026" }) : visibleVersions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "squisq-version-history-empty", children: "No versions yet. Versions are saved automatically as you edit." }) : /* @__PURE__ */ jsxs("ul", { className: "squisq-version-history-list", children: [
1009
+ state.loading ? /* @__PURE__ */ jsx("div", { className: "squisq-version-history-empty", children: "Loading\u2026" }) : state.versions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "squisq-version-history-empty", children: versioningAutoSaves ? "No versions yet. One is saved automatically a few seconds after you stop typing." : "No versions yet." }) : /* @__PURE__ */ jsxs("ul", { className: "squisq-version-history-list", children: [
1010
1010
  /* @__PURE__ */ jsx(
1011
1011
  "li",
1012
1012
  {
@@ -1085,7 +1085,8 @@ function VersionHistoryPanel() {
1085
1085
  },
1086
1086
  v2.path
1087
1087
  );
1088
- })
1088
+ }),
1089
+ visibleVersions.length === 0 && /* @__PURE__ */ jsx("li", { className: "squisq-version-history-note", children: "Your current draft is saved as a version. Earlier versions will appear here as you keep editing." })
1089
1090
  ] })
1090
1091
  ] })
1091
1092
  ]
@@ -4431,6 +4432,9 @@ function proofSuggestionLabel(suggestion) {
4431
4432
  if (suggestion.kind === "insertAfter") return `Insert \u201C${suggestion.text}\u201D after`;
4432
4433
  return suggestion.text;
4433
4434
  }
4435
+ function proofMessageText(finding) {
4436
+ return finding.message.replace(/`([^`]+)`/g, "\u201C$1\u201D");
4437
+ }
4434
4438
  function proofSuggestionLabels(finding, limit) {
4435
4439
  return finding.suggestions.slice(0, limit).map(proofSuggestionLabel);
4436
4440
  }
@@ -4438,7 +4442,7 @@ function proofHoverMarkdown(finding, suggestionLimit = 3) {
4438
4442
  const lines2 = [
4439
4443
  `**${PROOF_CATEGORY_LABELS[finding.category]}**`,
4440
4444
  "",
4441
- escapeMarkdown(finding.message)
4445
+ escapeMarkdown(proofMessageText(finding))
4442
4446
  ];
4443
4447
  const labels = proofSuggestionLabels(finding, suggestionLimit);
4444
4448
  if (labels.length > 0) {
@@ -4775,6 +4779,12 @@ function useProofing() {
4775
4779
  schedule("immediate");
4776
4780
  }
4777
4781
  }, [monacoEditor]);
4782
+ useEffect8(() => {
4783
+ lastWriteDecorationsRef.current = [];
4784
+ if (status === "ready" && enabled && activeView === "wysiwyg" && tiptapEditor) {
4785
+ schedule("immediate");
4786
+ }
4787
+ }, [tiptapEditor]);
4778
4788
  const dictionaryKey = settings.dictionary.join("\n");
4779
4789
  const syncedDictionaryRef = useRef7(dictionaryKey);
4780
4790
  useEffect8(() => {
@@ -5374,7 +5384,7 @@ function ProofingMenu({
5374
5384
  children: [
5375
5385
  /* @__PURE__ */ jsxs7("div", { className: "squisq-proof-menu-header", children: [
5376
5386
  /* @__PURE__ */ jsx8("span", { className: `squisq-proof-dot squisq-proof-dot--${finding.category}`, "aria-hidden": true }),
5377
- /* @__PURE__ */ jsx8("span", { className: "squisq-proof-menu-message", children: finding.message })
5387
+ /* @__PURE__ */ jsx8("span", { className: "squisq-proof-menu-message", children: proofMessageText(finding) })
5378
5388
  ] }),
5379
5389
  actions.map((action, index2) => /* @__PURE__ */ jsx8(
5380
5390
  "button",
@@ -5458,7 +5468,7 @@ function ProofingTooltip({
5458
5468
  /* @__PURE__ */ jsx9("span", { className: `squisq-proof-dot squisq-proof-dot--${finding.category}`, "aria-hidden": true }),
5459
5469
  /* @__PURE__ */ jsx9("span", { className: "squisq-proof-tooltip-category", children: PROOF_CATEGORY_LABELS[finding.category] })
5460
5470
  ] }),
5461
- /* @__PURE__ */ jsx9("div", { className: "squisq-proof-tooltip-message", children: finding.message }),
5471
+ /* @__PURE__ */ jsx9("div", { className: "squisq-proof-tooltip-message", children: proofMessageText(finding) }),
5462
5472
  suggestions.length > 0 && /* @__PURE__ */ jsxs8("div", { className: "squisq-proof-tooltip-suggestions", children: [
5463
5473
  suggestions.map((suggestion, index2) => /* @__PURE__ */ jsx9(
5464
5474
  "button",
@@ -17942,7 +17952,6 @@ var BUTTONS = [
17942
17952
  faIcon: "fa-solid fa-face-smile"
17943
17953
  }
17944
17954
  ];
17945
- var FIRST_MEDIA_INDEX = BUTTONS.findIndex((b) => b.group === "media");
17946
17955
  var MEDIA_BUTTONS = BUTTONS.filter((b) => b.group === "media");
17947
17956
  var FILE_STORING_BUTTONS = /* @__PURE__ */ new Set(["image", "file"]);
17948
17957
  var CONVERT_BUTTONS = MEDIA_BUTTONS.filter((b) => b.id === "table" || b.id === "tasklist");
@@ -17952,7 +17961,6 @@ var MERMAID_TYPE_MENU_WIDTH = 520;
17952
17961
  var CHART_TYPE_MENU_WIDTH = 360;
17953
17962
  var TASK_LIST_ITEMS = ["Task 1", "Task 2", "Task 3"];
17954
17963
  var TASK_LIST_MARKDOWN = TASK_LIST_ITEMS.map((item) => `- [ ] ${item}`).join("\n");
17955
- var BUTTON_INDEX_BY_ID = new Map(BUTTONS.map((b, i) => [b.id, i]));
17956
17964
  function buttonIcon(btn) {
17957
17965
  return btn.faIcon ? /* @__PURE__ */ jsx42(Icon, { icon: btn.faIcon }) : /* @__PURE__ */ jsx42("span", { className: "squisq-toolbar-text-glyph", "aria-hidden": "true", children: btn.icon });
17958
17966
  }
@@ -17963,6 +17971,66 @@ function fileCountBadge(count) {
17963
17971
  return count > 99 ? "99+" : String(count);
17964
17972
  }
17965
17973
 
17974
+ // src/toolbar/toolbarCollapse.ts
17975
+ var TOOLBAR_COLLAPSE_ORDER = [
17976
+ "contextual:table",
17977
+ "contextual:transition",
17978
+ "contextual:template",
17979
+ "subscript",
17980
+ "superscript",
17981
+ "layouts",
17982
+ "h6",
17983
+ "h5",
17984
+ "h4",
17985
+ "code",
17986
+ "hr",
17987
+ "docSettings",
17988
+ "files",
17989
+ "strikethrough",
17990
+ "quote",
17991
+ "h3",
17992
+ "ol",
17993
+ "h2",
17994
+ "h1",
17995
+ "ul",
17996
+ "italic",
17997
+ "bold",
17998
+ "insert"
17999
+ ];
18000
+ var COLLAPSE_RANK = new Map(TOOLBAR_COLLAPSE_ORDER.map((id, rank) => [id, rank]));
18001
+ function collapseRank(id) {
18002
+ return COLLAPSE_RANK.get(id) ?? -1;
18003
+ }
18004
+ var FIT_TOLERANCE = 0.5;
18005
+ function requiredRowWidth(layout, collapsed) {
18006
+ let width = 0;
18007
+ let shownGroups = 0;
18008
+ for (const group of layout.groups) {
18009
+ const shown = group.items.filter((id) => !collapsed.has(id));
18010
+ if (shown.length === 0) continue;
18011
+ if (group.separated && shownGroups > 0) width += layout.separatorWidth + layout.groupGap;
18012
+ for (const id of shown) width += layout.widthOf(id);
18013
+ width += (shown.length - 1) * layout.groupGap;
18014
+ shownGroups++;
18015
+ }
18016
+ width += Math.max(0, shownGroups - 1) * layout.laneGap;
18017
+ for (const id of layout.chrome) {
18018
+ if (!collapsed.has(id)) width += layout.widthOf(id) + layout.rowGap;
18019
+ }
18020
+ if (collapsed.size > 0) width += layout.triggerWidth + layout.rowGap;
18021
+ return width;
18022
+ }
18023
+ function chooseCollapsedItems(layout) {
18024
+ const present = [...layout.groups.flatMap((group) => group.items), ...layout.chrome];
18025
+ const order = [...present].sort((a, b) => collapseRank(a) - collapseRank(b));
18026
+ const collapsed = /* @__PURE__ */ new Set();
18027
+ for (const id of order) {
18028
+ if (requiredRowWidth(layout, collapsed) <= layout.budget + FIT_TOLERANCE) break;
18029
+ collapsed.add(id);
18030
+ }
18031
+ return collapsed;
18032
+ }
18033
+
17966
18034
  // src/toolbar/tiptapToolbar.ts
17967
18035
  function paragraphContent(text) {
17968
18036
  return text ? { type: "paragraph", content: [{ type: "text", text }] } : { type: "paragraph" };
@@ -18610,8 +18678,11 @@ function Toolbar({
18610
18678
  setChartTypeMenuAnchor(null);
18611
18679
  }, []);
18612
18680
  const actionsRef = useRef31(null);
18613
- const [measuredOverflowIndex, setMeasuredOverflowIndex] = useState33(null);
18681
+ const [collapsedKey, setCollapsedKey] = useState33("");
18614
18682
  const [clippedContextualKey, setClippedContextualKey] = useState33("");
18683
+ const itemWidthsRef = useRef31(/* @__PURE__ */ new Map());
18684
+ const chromeOnLaneRowRef = useRef31(/* @__PURE__ */ new Map());
18685
+ const collapsedCountRef = useRef31(0);
18615
18686
  const [showOverflow, setShowOverflow] = useState33(false);
18616
18687
  const overflowRef = useRef31(null);
18617
18688
  useEscapeDismissal(showOverflow, () => setShowOverflow(false), overflowTriggerRef);
@@ -18624,32 +18695,95 @@ function Toolbar({
18624
18695
  closeInsertMenu();
18625
18696
  closeEmojiPicker();
18626
18697
  }, [closeEmojiPicker, closeInsertMenu, findMode]);
18627
- const overflowIndex = measuredOverflowIndex;
18628
- const clippedContextual = new Set(clippedContextualKey ? clippedContextualKey.split("|") : []);
18698
+ const actionsLaneShown = !findMode && activeView !== "preview" && !isCodeMode && (showFormattingControls || showInsertControls);
18699
+ const collapsed = new Set(actionsLaneShown && collapsedKey ? collapsedKey.split("|") : []);
18700
+ const clippedContextual = new Set(
18701
+ actionsLaneShown && clippedContextualKey ? clippedContextualKey.split("|") : []
18702
+ );
18629
18703
  useEffect26(() => {
18630
18704
  const container = actionsRef.current;
18631
18705
  if (!container) return;
18706
+ const toolbar = container.parentElement;
18707
+ const widths = itemWidthsRef.current;
18708
+ const onLaneRow = chromeOnLaneRowRef.current;
18709
+ const gapOf = (element) => element ? Number.parseFloat(getComputedStyle(element).columnGap) || 0 : 0;
18710
+ const isDisplayed = (element) => !element.classList.contains("squisq-toolbar-button--overflowed") && !element.closest(".squisq-toolbar-group--collapsed");
18711
+ const remember = (id, element) => {
18712
+ const rect = element.getBoundingClientRect();
18713
+ if (rect.width > 0) widths.set(id, rect.width);
18714
+ return rect;
18715
+ };
18632
18716
  const measure = () => {
18633
- const containerRight = container.getBoundingClientRect().right;
18634
- const children = container.querySelectorAll(
18635
- ":scope > .squisq-toolbar-group:not(.squisq-toolbar-contextual) > .squisq-toolbar-button"
18636
- );
18637
- let firstHidden = null;
18638
- children.forEach((child) => {
18639
- if (firstHidden !== null) return;
18640
- if (child.getBoundingClientRect().right > containerRight + 2) {
18641
- const index2 = Number(child.dataset.btnIndex);
18642
- firstHidden = Number.isNaN(index2) ? null : index2;
18717
+ const laneRect = container.getBoundingClientRect();
18718
+ const groups2 = [];
18719
+ container.querySelectorAll(":scope > .squisq-toolbar-group").forEach((group) => {
18720
+ const contextual = group.dataset.contextual;
18721
+ if (contextual) {
18722
+ remember(`contextual:${contextual}`, group);
18723
+ groups2.push({ items: [`contextual:${contextual}`], separated: false });
18724
+ return;
18643
18725
  }
18726
+ const items = [];
18727
+ group.querySelectorAll(":scope > [data-toolbar-item]").forEach((item) => {
18728
+ const id = item.dataset.toolbarItem;
18729
+ items.push(id);
18730
+ if (isDisplayed(item)) remember(id, item);
18731
+ });
18732
+ if (items.length > 0) groups2.push({ items, separated: true });
18644
18733
  });
18645
- setMeasuredOverflowIndex(firstHidden);
18646
- const clipped = [];
18647
- container.querySelectorAll(":scope > .squisq-toolbar-contextual").forEach((group) => {
18648
- if (group.getBoundingClientRect().right > containerRight + 2) {
18649
- clipped.push(group.dataset.contextual ?? "");
18734
+ const separator = container.querySelector(
18735
+ ":scope > .squisq-toolbar-group:not(.squisq-toolbar-contextual):not(.squisq-toolbar-group--collapsed) > .squisq-toolbar-separator"
18736
+ );
18737
+ if (separator) {
18738
+ const style = getComputedStyle(separator);
18739
+ const width = separator.getBoundingClientRect().width + (Number.parseFloat(style.marginLeft) || 0) + (Number.parseFloat(style.marginRight) || 0);
18740
+ if (width > 0) widths.set("separator", width);
18741
+ }
18742
+ const rowGap = gapOf(toolbar);
18743
+ let budget = laneRect.width;
18744
+ const chrome = [];
18745
+ toolbar?.querySelectorAll(":scope > [data-toolbar-item]").forEach((item) => {
18746
+ const id = item.dataset.toolbarItem;
18747
+ if (isDisplayed(item)) {
18748
+ const rect = remember(id, item);
18749
+ if (rect.width > 0 && rect.height > 0) {
18750
+ onLaneRow.set(id, rect.top < laneRect.bottom && rect.bottom > laneRect.top);
18751
+ }
18752
+ if (onLaneRow.get(id)) budget += (widths.get(id) ?? 0) + rowGap;
18650
18753
  }
18754
+ if (onLaneRow.get(id)) chrome.push(id);
18651
18755
  });
18652
- setClippedContextualKey(clipped.join("|"));
18756
+ const trigger = toolbar?.querySelector(":scope > .squisq-toolbar-overflow");
18757
+ const triggerWidth = trigger?.getBoundingClientRect().width ?? 0;
18758
+ if (triggerWidth > 0) {
18759
+ widths.set("trigger", triggerWidth);
18760
+ budget += triggerWidth + rowGap;
18761
+ }
18762
+ const known = [...widths.values()];
18763
+ const fallbackWidth = known.length > 0 ? Math.min(...known) : 32;
18764
+ const layout = {
18765
+ groups: groups2,
18766
+ chrome,
18767
+ widthOf: (id) => widths.get(id) ?? fallbackWidth,
18768
+ separatorWidth: widths.get("separator") ?? 0,
18769
+ groupGap: gapOf(container.querySelector(":scope > .squisq-toolbar-group")),
18770
+ laneGap: gapOf(container),
18771
+ rowGap,
18772
+ triggerWidth: widths.get("trigger") ?? fallbackWidth,
18773
+ budget
18774
+ };
18775
+ let next = chooseCollapsedItems(layout);
18776
+ if (next.size < collapsedCountRef.current) {
18777
+ next = chooseCollapsedItems({ ...layout, budget: budget - 4 });
18778
+ }
18779
+ collapsedCountRef.current = next.size;
18780
+ const ordered = [...groups2.flatMap((group) => group.items), ...chrome].filter(
18781
+ (id) => next.has(id)
18782
+ );
18783
+ setCollapsedKey(ordered.filter((id) => !id.startsWith("contextual:")).join("|"));
18784
+ setClippedContextualKey(
18785
+ ordered.filter((id) => id.startsWith("contextual:")).map((id) => id.slice("contextual:".length)).join("|")
18786
+ );
18653
18787
  };
18654
18788
  const ro = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
18655
18789
  const observeAll = () => {
@@ -18670,7 +18804,7 @@ function Toolbar({
18670
18804
  if (!ro) window.removeEventListener("resize", measure);
18671
18805
  mo.disconnect();
18672
18806
  };
18673
- }, [activeView]);
18807
+ }, [activeView, actionsLaneShown]);
18674
18808
  useEffect26(() => {
18675
18809
  if (!showOverflow) return;
18676
18810
  const handleClick = (e2) => {
@@ -19479,7 +19613,14 @@ ${TASK_LIST_MARKDOWN}
19479
19613
  return Number(m[1]) <= visibleHeadingMax;
19480
19614
  };
19481
19615
  const hasVisibleMediaButtons = MEDIA_BUTTONS.some((b) => isButtonVisible(b.id));
19482
- const showInsertInOverflow = showInsertControls && overflowIndex !== null && overflowIndex <= FIRST_MEDIA_INDEX && hasVisibleMediaButtons;
19616
+ const showInsertInOverflow = showInsertControls && collapsed.has("insert") && hasVisibleMediaButtons;
19617
+ const collapsedFormatButtons = showFormattingControls ? BUTTONS.filter((b) => b.group !== "media" && isButtonVisible(b.id) && collapsed.has(b.id)) : [];
19618
+ const collapsedChrome = ["layouts", "docSettings", "files"].filter(
19619
+ (id) => collapsed.has(id)
19620
+ );
19621
+ const shownFormatGroups = showFormattingControls ? groups.filter(
19622
+ (group) => BUTTONS.some((b) => b.group === group && isButtonVisible(b.id) && !collapsed.has(b.id))
19623
+ ) : [];
19483
19624
  const isInTable = isWysiwyg ? tiptapEditor.isActive("table") : false;
19484
19625
  const wysiwygTemplate = isWysiwyg ? tiptapEditor.isActive("heading") ? tiptapEditor.getAttributes("heading")?.dataTemplate ?? "" : null : null;
19485
19626
  const isRawView = activeView === "raw";
@@ -19703,7 +19844,7 @@ ${TASK_LIST_MARKDOWN}
19703
19844
  const showTransitionInOverflow = showFormattingControls && currentTransition !== null && clippedContextual.has("transition");
19704
19845
  const showBlockSectionInOverflow = showTemplateInOverflow || showTransitionInOverflow;
19705
19846
  const overflowBlockLabel = currentTemplate ? templateLabel(currentTemplate) : "Heading";
19706
- const showToolbarOverflow = !findMode && !isPreview && !isCodeMode && (showFormattingControls && (overflowIndex !== null || clippedContextual.size > 0) || showInsertInOverflow);
19847
+ const showToolbarOverflow = actionsLaneShown && (collapsedFormatButtons.length > 0 || showFormattingControls && clippedContextual.size > 0 || showInsertInOverflow || collapsedChrome.length > 0);
19707
19848
  return /* @__PURE__ */ jsxs34(
19708
19849
  "div",
19709
19850
  {
@@ -19793,49 +19934,61 @@ ${TASK_LIST_MARKDOWN}
19793
19934
  );
19794
19935
  }) }),
19795
19936
  findMode ? /* @__PURE__ */ jsx44(FindToolbar, { onClose: () => setFindMode(false) }) : slotAfterTabs,
19796
- !findMode && !isPreview && !isCodeMode && (showFormattingControls || showInsertControls) && /* @__PURE__ */ jsxs34("div", { className: "squisq-toolbar-actions", ref: actionsRef, children: [
19797
- showFormattingControls && groups.map((group, gi) => /* @__PURE__ */ jsxs34("div", { className: "squisq-toolbar-group", children: [
19798
- gi > 0 && /* @__PURE__ */ jsx44("div", { className: "squisq-toolbar-separator" }),
19799
- BUTTONS.filter((b) => b.group === group && isButtonVisible(b.id)).map((btn) => {
19800
- const active = btn.id === "emoji" ? emojiPickerAnchor !== null : formatActive && formattingEditor ? isTiptapActive(formattingEditor, btn.id) : false;
19801
- const disabled = FILE_STORING_BUTTONS.has(btn.id) && !mediaProvider || !buttonAllowed(btn.id);
19802
- const btnIndex = BUTTON_INDEX_BY_ID.get(btn.id);
19803
- const overflowed = overflowIndex !== null && btnIndex !== void 0 && btnIndex >= overflowIndex;
19804
- return /* @__PURE__ */ jsx44(
19805
- "button",
19806
- {
19807
- ref: btn.id === "emoji" ? emojiButtonRef : void 0,
19808
- className: `squisq-toolbar-button${active ? " squisq-toolbar-button--active" : ""}${overflowed ? " squisq-toolbar-button--overflowed" : ""}`,
19809
- "data-btn-index": btnIndex,
19810
- "data-tooltip": dataCardFocused ? "Formatting applies to document text \u2014 click into the prose first" : FILE_STORING_BUTTONS.has(btn.id) && !mediaProvider ? `${btn.title} (requires media provider)` : btn.title,
19811
- onClick: () => handleAction(btn.id),
19812
- "aria-label": btn.title,
19813
- "aria-pressed": active,
19814
- disabled,
19815
- children: buttonIcon(btn)
19816
- },
19817
- btn.id
19818
- );
19819
- })
19820
- ] }, group)),
19821
- showInsertControls && /* @__PURE__ */ jsxs34("div", { className: "squisq-toolbar-group", children: [
19822
- showFormattingControls && /* @__PURE__ */ jsx44("div", { className: "squisq-toolbar-separator" }),
19823
- /* @__PURE__ */ jsx44(
19824
- "button",
19825
- {
19826
- ref: insertMenuButtonRef,
19827
- className: `squisq-toolbar-button${insertMenuAnchor ? " squisq-toolbar-button--active" : ""}${showInsertInOverflow ? " squisq-toolbar-button--overflowed" : ""}`,
19828
- "data-btn-index": FIRST_MEDIA_INDEX,
19829
- "data-tooltip": dataCardFocused ? "Inserts land in document text \u2014 click into the prose first" : "Insert...",
19830
- onClick: () => insertMenuAnchor ? closeInsertMenu() : openInsertMenu(),
19831
- "aria-label": "Insert",
19832
- "aria-expanded": insertMenuAnchor !== null,
19833
- "aria-haspopup": "menu",
19834
- disabled: dataCardFocused,
19835
- children: /* @__PURE__ */ jsx44(Icon, { icon: "fa-solid fa-plus" })
19836
- }
19837
- )
19838
- ] }),
19937
+ actionsLaneShown && /* @__PURE__ */ jsxs34("div", { className: "squisq-toolbar-actions", ref: actionsRef, children: [
19938
+ showFormattingControls && groups.map((group) => /* @__PURE__ */ jsxs34(
19939
+ "div",
19940
+ {
19941
+ className: `squisq-toolbar-group${shownFormatGroups.includes(group) ? "" : " squisq-toolbar-group--collapsed"}`,
19942
+ children: [
19943
+ shownFormatGroups.indexOf(group) > 0 && /* @__PURE__ */ jsx44("div", { className: "squisq-toolbar-separator" }),
19944
+ BUTTONS.filter((b) => b.group === group && isButtonVisible(b.id)).map((btn) => {
19945
+ const active = btn.id === "emoji" ? emojiPickerAnchor !== null : formatActive && formattingEditor ? isTiptapActive(formattingEditor, btn.id) : false;
19946
+ const disabled = FILE_STORING_BUTTONS.has(btn.id) && !mediaProvider || !buttonAllowed(btn.id);
19947
+ const overflowed = collapsed.has(btn.id);
19948
+ return /* @__PURE__ */ jsx44(
19949
+ "button",
19950
+ {
19951
+ ref: btn.id === "emoji" ? emojiButtonRef : void 0,
19952
+ className: `squisq-toolbar-button${active ? " squisq-toolbar-button--active" : ""}${overflowed ? " squisq-toolbar-button--overflowed" : ""}`,
19953
+ "data-toolbar-item": btn.id,
19954
+ "data-tooltip": dataCardFocused ? "Formatting applies to document text \u2014 click into the prose first" : FILE_STORING_BUTTONS.has(btn.id) && !mediaProvider ? `${btn.title} (requires media provider)` : btn.title,
19955
+ onClick: () => handleAction(btn.id),
19956
+ "aria-label": btn.title,
19957
+ "aria-pressed": active,
19958
+ disabled,
19959
+ children: buttonIcon(btn)
19960
+ },
19961
+ btn.id
19962
+ );
19963
+ })
19964
+ ]
19965
+ },
19966
+ group
19967
+ )),
19968
+ showInsertControls && /* @__PURE__ */ jsxs34(
19969
+ "div",
19970
+ {
19971
+ className: `squisq-toolbar-group${collapsed.has("insert") ? " squisq-toolbar-group--collapsed" : ""}`,
19972
+ children: [
19973
+ shownFormatGroups.length > 0 && /* @__PURE__ */ jsx44("div", { className: "squisq-toolbar-separator" }),
19974
+ /* @__PURE__ */ jsx44(
19975
+ "button",
19976
+ {
19977
+ ref: insertMenuButtonRef,
19978
+ className: `squisq-toolbar-button${insertMenuAnchor ? " squisq-toolbar-button--active" : ""}${collapsed.has("insert") ? " squisq-toolbar-button--overflowed" : ""}`,
19979
+ "data-toolbar-item": "insert",
19980
+ "data-tooltip": dataCardFocused ? "Inserts land in document text \u2014 click into the prose first" : "Insert...",
19981
+ onClick: () => insertMenuAnchor ? closeInsertMenu() : openInsertMenu(),
19982
+ "aria-label": "Insert",
19983
+ "aria-expanded": insertMenuAnchor !== null,
19984
+ "aria-haspopup": "menu",
19985
+ disabled: dataCardFocused,
19986
+ children: /* @__PURE__ */ jsx44(Icon, { icon: "fa-solid fa-plus" })
19987
+ }
19988
+ )
19989
+ ]
19990
+ }
19991
+ ),
19839
19992
  showFormattingControls && currentTemplate !== null && /* @__PURE__ */ jsxs34(
19840
19993
  "div",
19841
19994
  {
@@ -20094,7 +20247,7 @@ ${TASK_LIST_MARKDOWN}
20094
20247
  {
20095
20248
  className: `squisq-toolbar-overflow-menu squisq-toolbar-overflow-menu--${overflowPlacement}`,
20096
20249
  children: [
20097
- showFormattingControls && BUTTONS.slice(overflowIndex ?? BUTTONS.length).filter((b) => isButtonVisible(b.id)).filter((b) => b.group !== "media").map((btn) => {
20250
+ collapsedFormatButtons.map((btn) => {
20098
20251
  const active = btn.id === "emoji" ? emojiPickerAnchor !== null : formatActive && formattingEditor ? isTiptapActive(formattingEditor, btn.id) : false;
20099
20252
  const disabled = FILE_STORING_BUTTONS.has(btn.id) && !mediaProvider || !buttonAllowed(btn.id);
20100
20253
  return /* @__PURE__ */ jsxs34(
@@ -20135,6 +20288,60 @@ ${TASK_LIST_MARKDOWN}
20135
20288
  ]
20136
20289
  }
20137
20290
  ),
20291
+ collapsedChrome.length > 0 && (collapsedFormatButtons.length > 0 || showInsertInOverflow) && /* @__PURE__ */ jsx44(
20292
+ "div",
20293
+ {
20294
+ className: "squisq-toolbar-separator",
20295
+ style: { margin: "4px 0", width: "100%", height: 1 }
20296
+ }
20297
+ ),
20298
+ collapsedChrome.includes("layouts") && /* @__PURE__ */ jsxs34(
20299
+ "button",
20300
+ {
20301
+ className: "squisq-toolbar-overflow-item",
20302
+ onClick: () => {
20303
+ setShowLayoutManager(true);
20304
+ setShowOverflow(false);
20305
+ },
20306
+ children: [
20307
+ /* @__PURE__ */ jsx44("span", { className: "squisq-toolbar-overflow-icon", children: /* @__PURE__ */ jsx44(Icon, { icon: "fa-solid fa-shapes" }) }),
20308
+ /* @__PURE__ */ jsx44("span", { children: "Custom layouts" })
20309
+ ]
20310
+ }
20311
+ ),
20312
+ collapsedChrome.includes("docSettings") && /* @__PURE__ */ jsxs34(
20313
+ "button",
20314
+ {
20315
+ className: "squisq-toolbar-overflow-item",
20316
+ onClick: () => {
20317
+ setShowDocSettings(true);
20318
+ setShowOverflow(false);
20319
+ },
20320
+ children: [
20321
+ /* @__PURE__ */ jsx44("span", { className: "squisq-toolbar-overflow-icon", children: /* @__PURE__ */ jsx44(Icon, { icon: "fa-solid fa-file-lines" }) }),
20322
+ /* @__PURE__ */ jsx44("span", { children: "Document settings" })
20323
+ ]
20324
+ }
20325
+ ),
20326
+ collapsedChrome.includes("files") && onToggleFiles && /* @__PURE__ */ jsxs34(
20327
+ "button",
20328
+ {
20329
+ className: `squisq-toolbar-overflow-item${showFiles ? " squisq-toolbar-overflow-item--active" : ""}`,
20330
+ "aria-pressed": showFiles,
20331
+ onClick: () => {
20332
+ onToggleFiles();
20333
+ setShowOverflow(false);
20334
+ },
20335
+ children: [
20336
+ /* @__PURE__ */ jsx44("span", { className: "squisq-toolbar-overflow-icon", children: /* @__PURE__ */ jsx44(Icon, { icon: "fa-solid fa-paperclip" }) }),
20337
+ /* @__PURE__ */ jsxs34("span", { children: [
20338
+ showFiles ? "Hide" : "Show",
20339
+ " Files panel",
20340
+ resolvedFileCount > 0 ? ` (${fileCountLabel(resolvedFileCount)})` : ""
20341
+ ] })
20342
+ ]
20343
+ }
20344
+ ),
20138
20345
  showBlockSectionInOverflow && /* @__PURE__ */ jsxs34(Fragment14, { children: [
20139
20346
  /* @__PURE__ */ jsx44(
20140
20347
  "div",
@@ -20244,7 +20451,8 @@ ${TASK_LIST_MARKDOWN}
20244
20451
  "button",
20245
20452
  {
20246
20453
  type: "button",
20247
- className: `squisq-toolbar-button${showLayoutManager ? " squisq-toolbar-button--active" : ""}`,
20454
+ className: `squisq-toolbar-button${showLayoutManager ? " squisq-toolbar-button--active" : ""}${collapsed.has("layouts") ? " squisq-toolbar-button--overflowed" : ""}`,
20455
+ "data-toolbar-item": "layouts",
20248
20456
  onClick: () => setShowLayoutManager(true),
20249
20457
  "data-tooltip": "Custom layouts",
20250
20458
  "aria-label": "Custom layouts",
@@ -20257,7 +20465,8 @@ ${TASK_LIST_MARKDOWN}
20257
20465
  "button",
20258
20466
  {
20259
20467
  type: "button",
20260
- className: "squisq-toolbar-button",
20468
+ className: `squisq-toolbar-button${collapsed.has("docSettings") ? " squisq-toolbar-button--overflowed" : ""}`,
20469
+ "data-toolbar-item": "docSettings",
20261
20470
  onClick: () => setShowDocSettings(true),
20262
20471
  "data-tooltip": "Document settings",
20263
20472
  "aria-label": "Document settings",
@@ -20267,7 +20476,8 @@ ${TASK_LIST_MARKDOWN}
20267
20476
  onToggleFiles && /* @__PURE__ */ jsxs34(
20268
20477
  "button",
20269
20478
  {
20270
- className: `squisq-toolbar-button squisq-toolbar-files-toggle${showFiles ? " squisq-toolbar-button--active" : ""}`,
20479
+ className: `squisq-toolbar-button squisq-toolbar-files-toggle${showFiles ? " squisq-toolbar-button--active" : ""}${collapsed.has("files") ? " squisq-toolbar-button--overflowed" : ""}`,
20480
+ "data-toolbar-item": "files",
20271
20481
  onClick: onToggleFiles,
20272
20482
  "data-tooltip": `${showFiles ? "Hide" : "Show"} Files panel${resolvedFileCount > 0 ? ` (${fileCountLabel(resolvedFileCount)})` : ""}`,
20273
20483
  "aria-pressed": showFiles,
@@ -20621,6 +20831,9 @@ import { useEffect as useEffect27, useMemo as useMemo24, useState as useState34
20621
20831
  import { countBlocks } from "@bendyline/squisq/doc";
20622
20832
  import { jsx as jsx45, jsxs as jsxs35 } from "react/jsx-runtime";
20623
20833
  var STATS_DEBOUNCE_MS = 150;
20834
+ function countLabel(count, noun) {
20835
+ return `${count} ${count === 1 ? noun : `${noun}s`}`;
20836
+ }
20624
20837
  function StatusBar({ className, slotRight }) {
20625
20838
  const { markdownSource, doc, parseError } = useEditorContext();
20626
20839
  const proofingState = useProofingState();
@@ -20638,23 +20851,10 @@ function StatusBar({ className, slotRight }) {
20638
20851
  }, [statsSource]);
20639
20852
  const blocks = useMemo24(() => doc ? countBlocks(doc.blocks) : 0, [doc]);
20640
20853
  return /* @__PURE__ */ jsxs35("div", { className: `squisq-status-bar ${className || ""}`, children: [
20641
- /* @__PURE__ */ jsxs35("span", { className: "squisq-status-item", children: [
20642
- stats.words,
20643
- " words"
20644
- ] }),
20645
- /* @__PURE__ */ jsxs35("span", { className: "squisq-status-item", children: [
20646
- stats.chars,
20647
- " chars"
20648
- ] }),
20649
- /* @__PURE__ */ jsxs35("span", { className: "squisq-status-item", children: [
20650
- stats.lines,
20651
- " lines"
20652
- ] }),
20653
- /* @__PURE__ */ jsxs35("span", { className: "squisq-status-item", children: [
20654
- blocks,
20655
- " ",
20656
- blocks === 1 ? "block" : "blocks"
20657
- ] }),
20854
+ /* @__PURE__ */ jsx45("span", { className: "squisq-status-item squisq-status-words", children: countLabel(stats.words, "word") }),
20855
+ /* @__PURE__ */ jsx45("span", { className: "squisq-status-item squisq-status-chars", children: countLabel(stats.chars, "char") }),
20856
+ /* @__PURE__ */ jsx45("span", { className: "squisq-status-item squisq-status-lines", children: countLabel(stats.lines, "line") }),
20857
+ /* @__PURE__ */ jsx45("span", { className: "squisq-status-item squisq-status-blocks", children: countLabel(blocks, "block") }),
20658
20858
  proofingState && proofingState.enabled && /* @__PURE__ */ jsxs35(
20659
20859
  "button",
20660
20860
  {
@@ -39536,6 +39736,8 @@ function TooltipLayer() {
39536
39736
  const timerRef = useRef60(null);
39537
39737
  const currentTargetRef = useRef60(null);
39538
39738
  const visibleRef = useRef60(false);
39739
+ const pressedTargetRef = useRef60(null);
39740
+ const lastPointerWasTouchRef = useRef60(false);
39539
39741
  useLayoutEffect12(() => {
39540
39742
  if (!state) return;
39541
39743
  const node2 = tooltipRef.current;
@@ -39569,10 +39771,20 @@ function TooltipLayer() {
39569
39771
  anchorX: rect.left + rect.width / 2
39570
39772
  });
39571
39773
  };
39774
+ const handlePointer = (e2) => {
39775
+ lastPointerWasTouchRef.current = e2.pointerType === "touch";
39776
+ };
39777
+ const handlePress = (e2) => {
39778
+ handlePointer(e2);
39779
+ pressedTargetRef.current = e2.target?.closest?.("[data-tooltip]") ?? null;
39780
+ hide();
39781
+ };
39572
39782
  const handleOver = (e2) => {
39783
+ if (lastPointerWasTouchRef.current) return;
39573
39784
  const el = e2.target?.closest?.("[data-tooltip]");
39574
39785
  if (!el) return;
39575
39786
  if (el === currentTargetRef.current) return;
39787
+ if (el === pressedTargetRef.current) return;
39576
39788
  const label = el.getAttribute("data-tooltip");
39577
39789
  if (!label) return;
39578
39790
  const wasVisible = visibleRef.current;
@@ -39589,9 +39801,11 @@ function TooltipLayer() {
39589
39801
  }
39590
39802
  };
39591
39803
  const handleOut = (e2) => {
39804
+ const related = e2.relatedTarget;
39805
+ const pressed = pressedTargetRef.current;
39806
+ if (pressed && !(related && pressed.contains(related))) pressedTargetRef.current = null;
39592
39807
  const target = currentTargetRef.current;
39593
39808
  if (!target) return;
39594
- const related = e2.relatedTarget;
39595
39809
  if (related && target.contains(related)) return;
39596
39810
  const relatedTooltip = related?.closest?.("[data-tooltip]");
39597
39811
  if (relatedTooltip) return;
@@ -39599,12 +39813,16 @@ function TooltipLayer() {
39599
39813
  };
39600
39814
  const handleScroll = () => hide();
39601
39815
  const handleBlur = () => hide();
39816
+ document.addEventListener("pointerover", handlePointer, true);
39817
+ document.addEventListener("pointerdown", handlePress, true);
39602
39818
  document.addEventListener("mouseover", handleOver);
39603
39819
  document.addEventListener("mouseout", handleOut);
39604
39820
  document.addEventListener("scroll", handleScroll, true);
39605
39821
  window.addEventListener("blur", handleBlur);
39606
39822
  return () => {
39607
39823
  clearTimer();
39824
+ document.removeEventListener("pointerover", handlePointer, true);
39825
+ document.removeEventListener("pointerdown", handlePress, true);
39608
39826
  document.removeEventListener("mouseover", handleOver);
39609
39827
  document.removeEventListener("mouseout", handleOut);
39610
39828
  document.removeEventListener("scroll", handleScroll, true);
@@ -39754,7 +39972,7 @@ function ProofingPanel() {
39754
39972
  ),
39755
39973
  /* @__PURE__ */ jsxs67("span", { className: "squisq-proof-panel-row-body", children: [
39756
39974
  /* @__PURE__ */ jsx84("span", { className: "squisq-proof-panel-excerpt", children: finding.originalText }),
39757
- /* @__PURE__ */ jsx84("span", { className: "squisq-proof-panel-message", children: finding.message })
39975
+ /* @__PURE__ */ jsx84("span", { className: "squisq-proof-panel-message", children: proofMessageText(finding) })
39758
39976
  ] })
39759
39977
  ]
39760
39978
  }