@stll/folio-react 0.8.1 → 0.10.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.
@@ -47,9 +47,11 @@ import { schema as schema$1 } from "@stll/folio-core/prosemirror/schema";
47
47
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
48
48
  import { createFolioEditor } from "@stll/folio-core/controller/folioEditor";
49
49
  import { createFolioEditorEmitter } from "@stll/folio-core/controller/folioEditorEvents";
50
+ import { createLatestRequestGate } from "@stll/folio-core/controller/latestRequestGate";
50
51
  import { runLayoutPipeline } from "@stll/folio-core/controller/layoutPipeline";
51
52
  import { browserClock, createLayoutScheduler } from "@stll/folio-core/controller/layoutScheduler";
52
53
  import { createLayoutSession } from "@stll/folio-core/controller/layoutSession";
54
+ import { documentFontsAreLoaded, getDocumentFontSet, waitForInitialLayoutFonts } from "@stll/folio-core/controller/fontReadiness";
53
55
  import { getFootnoteText } from "@stll/folio-core/docx/footnoteParser";
54
56
  import { convertHeaderFooterPmDocToContent, convertHeaderFooterToContent } from "@stll/folio-core/layout-bridge/convert/headerFooterLayout";
55
57
  import { templatePreviewDirtyRange } from "@stll/folio-core/layout-bridge/convert/templatePreviewFlow";
@@ -59,6 +61,7 @@ import { clickToPositionInHfSlot, findHfCaretSpan, findHfPmSpans, findHfSlotForT
59
61
  import { clickToPosition } from "@stll/folio-core/layout-bridge/engine/clickToPosition";
60
62
  import { getPageTop, hitTestFragment, hitTestTableCell } from "@stll/folio-core/layout-bridge/engine/hitTest";
61
63
  import { clearAllCaches, resetCanvasContext } from "@stll/folio-core/layout-bridge/engine/measuring";
64
+ import { resolveSectionHeaderFooterRefs } from "@stll/folio-core/layout-engine";
62
65
  import { recordLayoutPhase } from "@stll/folio-core/layout-engine/layoutInstrumentation";
63
66
  import { LayoutPainter } from "@stll/folio-core/layout-painter";
64
67
  import { PAINTER_PAINTED_EVENT, findPageShellForPmPos } from "@stll/folio-core/layout-painter/renderPage";
@@ -73,11 +76,11 @@ import { resizeColumnPair } from "@stll/folio-core/paged-layout/tableColumnResiz
73
76
  import { tableInsertButtonOffset } from "@stll/folio-core/paged-layout/tableInsertButtonGeometry";
74
77
  import { getTransactionDirtyRange } from "@stll/folio-core/paged-layout/transactionDirtyRange";
75
78
  import { findStartPosForParaId } from "@stll/folio-core/prosemirror/utils/findParagraphByParaId";
76
- import { expectFontFamilyMarkAttrs, expectImageAttrs, expectTableAttrs, expectTableCellAttrs, mergeImageAttrs, mergeTableAttrs, mergeTableCellAttrs, mergeTableRowAttrs } from "@stll/folio-core/prosemirror/attrs";
79
+ import { expectImageAttrs, expectTableAttrs, expectTableCellAttrs, mergeImageAttrs, mergeTableAttrs, mergeTableCellAttrs, mergeTableRowAttrs } from "@stll/folio-core/prosemirror/attrs";
77
80
  import { extractEmbeddedFonts } from "@stll/folio-core/fonts/embeddedFonts";
78
81
  import { panic } from "better-result";
79
82
  import { collectRemoteSelections, createHiddenEditorManager, createHiddenEditorState } from "@stll/folio-core/controller/hiddenEditorManager";
80
- import { clampRangeToDocSize, resolveFolioAIBlockRange } from "@stll/folio-core/ai-edits/blockRange";
83
+ import { clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange } from "@stll/folio-core/ai-edits/blockRange";
81
84
  import { getPageTextFromLayout } from "@stll/folio-core/paged-layout/pageText";
82
85
  import { CONTENT_CONTROL_WIDGET_EVENT_NAME, dispatchDatePick, dispatchDropdownPick } from "@stll/folio-core/prosemirror/plugins/contentControlWidgets";
83
86
  import { ErrorManager } from "@stll/folio-core/managers/ErrorManager";
@@ -2540,28 +2543,6 @@ function renderHeaderFooterContentByRId(source, hfPMs, contentWidth, metrics, op
2540
2543
  }
2541
2544
  return rendered.size > 0 ? rendered : void 0;
2542
2545
  }
2543
- function getSectionHeaderFooterRefs(documentModel) {
2544
- const body = documentModel?.package.document;
2545
- if (!body) return;
2546
- const documentEvenAndOddHeaders = documentModel.package.settings?.evenAndOddHeaders === true;
2547
- const sections = body.sections;
2548
- if (sections && sections.length > 0) return sections.map((section) => getHeaderFooterRefsFromSectionProperties(section.properties, documentEvenAndOddHeaders));
2549
- const finalProps = body.finalSectionProperties;
2550
- if (!finalProps) return;
2551
- return [getHeaderFooterRefsFromSectionProperties(finalProps, documentEvenAndOddHeaders)];
2552
- }
2553
- function getHeaderFooterRefsFromSectionProperties(props, documentEvenAndOddHeaders) {
2554
- const refs = {};
2555
- if (props.titlePg !== void 0) refs.titlePg = props.titlePg;
2556
- refs.evenAndOddHeaders = props.evenAndOddHeaders ?? documentEvenAndOddHeaders;
2557
- for (const ref of props.headerReferences ?? []) if (ref.type === "default") refs.headerDefault = ref.rId;
2558
- else if (ref.type === "first") refs.headerFirst = ref.rId;
2559
- else refs.headerEven = ref.rId;
2560
- for (const ref of props.footerReferences ?? []) if (ref.type === "default") refs.footerDefault = ref.rId;
2561
- else if (ref.type === "first") refs.footerFirst = ref.rId;
2562
- else refs.footerEven = ref.rId;
2563
- return refs;
2564
- }
2565
2546
  const getPageOverlayOffset = (pagesContainer, zoom) => {
2566
2547
  const overlay = pagesContainer.parentElement?.querySelector("[data-testid=\"selection-overlay\"]");
2567
2548
  const firstPage = pagesContainer.querySelector(".layout-page");
@@ -2582,139 +2563,7 @@ function stableJsonStringify(value) {
2582
2563
  return nestedValue;
2583
2564
  });
2584
2565
  }
2585
- function getDocumentFontSet() {
2586
- if (typeof document === "undefined" || !("fonts" in document)) return null;
2587
- return document.fonts;
2588
- }
2589
- function documentFontsAreLoaded() {
2590
- const fontSet = getDocumentFontSet();
2591
- return !fontSet || fontSet.status === "loaded";
2592
- }
2593
- const INITIAL_LAYOUT_FONT_TIMEOUT_MS = 2e3;
2594
2566
  const INITIAL_FONT_READY_SUPPRESSION_MS = 250;
2595
- const DEFAULT_LAYOUT_FONT_FAMILY = "Calibri";
2596
- const OFFICE_FONT_FAMILY_MAP = {
2597
- Arial: "Arimo",
2598
- Calibri: "Carlito",
2599
- Cambria: "Caladea",
2600
- "Times New Roman": "Tinos",
2601
- "Courier New": "Cousine"
2602
- };
2603
- const CSS_GENERIC_FONT_FAMILIES = /* @__PURE__ */ new Set([
2604
- "serif",
2605
- "sans-serif",
2606
- "monospace",
2607
- "cursive",
2608
- "fantasy",
2609
- "system-ui"
2610
- ]);
2611
- const REGULAR_LAYOUT_FONT_DESCRIPTOR = [
2612
- {
2613
- style: "normal",
2614
- weight: 400
2615
- },
2616
- {
2617
- style: "italic",
2618
- weight: 400
2619
- },
2620
- {
2621
- style: "normal",
2622
- weight: 700
2623
- },
2624
- {
2625
- style: "italic",
2626
- weight: 700
2627
- }
2628
- ][0];
2629
- function waitForInitialLayoutFonts(documentModel, pmDoc) {
2630
- const fontSet = getDocumentFontSet();
2631
- if (!fontSet) return Promise.resolve(true);
2632
- const loadChecks = [];
2633
- for (const face of collectInitialLayoutFontFaces(documentModel, pmDoc)) loadChecks.push(`${face.style} ${face.weight} 16px "${escapeCssFontFamily(face.family)}"`);
2634
- const loadFonts = Promise.allSettled(loadChecks.map((check) => fontSet.load(check))).then(() => fontSet.ready).then(() => true);
2635
- return Promise.race([loadFonts, new Promise((resolve) => {
2636
- window.setTimeout(() => resolve(false), INITIAL_LAYOUT_FONT_TIMEOUT_MS);
2637
- })]);
2638
- }
2639
- function collectInitialLayoutFontFaces(documentModel, pmDoc) {
2640
- const faces = /* @__PURE__ */ new Map();
2641
- addLayoutFontFamilyFace(faces, DEFAULT_LAYOUT_FONT_FAMILY, REGULAR_LAYOUT_FONT_DESCRIPTOR);
2642
- for (const family of documentModel?.requiredFonts ?? []) addLayoutFontFamilyFace(faces, family, REGULAR_LAYOUT_FONT_DESCRIPTOR);
2643
- addLayoutFontFamilyFace(faces, documentModel?.package.theme?.fontScheme?.majorFont?.latin, REGULAR_LAYOUT_FONT_DESCRIPTOR);
2644
- addLayoutFontFamilyFace(faces, documentModel?.package.theme?.fontScheme?.minorFont?.latin, REGULAR_LAYOUT_FONT_DESCRIPTOR);
2645
- addTextFormattingFontFaces(faces, documentModel?.package.styles?.docDefaults?.rPr);
2646
- for (const style of documentModel?.package.styles?.styles ?? []) addTextFormattingFontFaces(faces, style.rPr);
2647
- collectProseMirrorFontFaces(faces, pmDoc, void 0);
2648
- return Array.from(faces.values());
2649
- }
2650
- function addTextFormattingFontFaces(faces, formatting) {
2651
- addLayoutFontFamilyFace(faces, formatting?.fontFamily, layoutDescriptorFromFormatting(formatting));
2652
- }
2653
- function collectProseMirrorFontFaces(faces, node, inheritedTextFormatting) {
2654
- const paragraphDefaults = readParagraphDefaultTextFormatting(node);
2655
- const textFormatting = paragraphDefaults ?? inheritedTextFormatting;
2656
- if (paragraphDefaults) addTextFormattingFontFaces(faces, paragraphDefaults);
2657
- if (node.attrs["listMarkerFontFamily"]) addLayoutFontFamilyFace(faces, node.attrs["listMarkerFontFamily"], REGULAR_LAYOUT_FONT_DESCRIPTOR);
2658
- if (node.isText) {
2659
- const descriptor = layoutDescriptorFromFormattingAndMarks(textFormatting, node.marks);
2660
- addLayoutFontFamilyFace(faces, readFontFamilyMarkAttrs(node.marks) ?? textFormatting?.fontFamily ?? DEFAULT_LAYOUT_FONT_FAMILY, descriptor);
2661
- }
2662
- node.forEach((child) => {
2663
- collectProseMirrorFontFaces(faces, child, textFormatting);
2664
- });
2665
- }
2666
- function readParagraphDefaultTextFormatting(node) {
2667
- const value = node.attrs["defaultTextFormatting"];
2668
- if (!value || typeof value !== "object") return;
2669
- return value;
2670
- }
2671
- function readFontFamilyMarkAttrs(marks) {
2672
- for (const mark of marks) if (mark.type.name === "fontFamily") return expectFontFamilyMarkAttrs(mark);
2673
- }
2674
- function layoutDescriptorFromFormatting(formatting) {
2675
- return {
2676
- style: formatting?.italic ? "italic" : "normal",
2677
- weight: formatting?.bold ? 700 : 400
2678
- };
2679
- }
2680
- function layoutDescriptorFromFormattingAndMarks(formatting, marks) {
2681
- let bold = formatting?.bold === true;
2682
- let italic = formatting?.italic === true;
2683
- for (const mark of marks) {
2684
- if (mark.type.name === "bold") bold = true;
2685
- if (mark.type.name === "italic") italic = true;
2686
- }
2687
- return {
2688
- style: italic ? "italic" : "normal",
2689
- weight: bold ? 700 : 400
2690
- };
2691
- }
2692
- function addLayoutFontFamilyFace(faces, value, descriptor) {
2693
- if (typeof value === "string") {
2694
- addLayoutFontFamilyNameFace(faces, value, descriptor);
2695
- return;
2696
- }
2697
- if (!value || typeof value !== "object") return;
2698
- const fontFamily = value;
2699
- addLayoutFontFamilyFace(faces, fontFamily.ascii, descriptor);
2700
- addLayoutFontFamilyFace(faces, fontFamily.hAnsi, descriptor);
2701
- }
2702
- function addLayoutFontFamilyNameFace(faces, family, descriptor) {
2703
- const normalized = family.trim();
2704
- if (!normalized || CSS_GENERIC_FONT_FAMILIES.has(normalized)) return;
2705
- addLayoutFontFace(faces, normalized, descriptor);
2706
- const mappedFamily = OFFICE_FONT_FAMILY_MAP[normalized];
2707
- if (mappedFamily) addLayoutFontFace(faces, mappedFamily, descriptor);
2708
- }
2709
- function addLayoutFontFace(faces, family, descriptor) {
2710
- faces.set(`${family}|${descriptor.style}|${descriptor.weight}`, {
2711
- family,
2712
- ...descriptor
2713
- });
2714
- }
2715
- function escapeCssFontFamily(family) {
2716
- return family.replace(/\\/gu, "\\\\").replace(/"/gu, "\\\"");
2717
- }
2718
2567
  function describeInvalidHighlightMarks(doc) {
2719
2568
  const invalidHighlights = [];
2720
2569
  const validHighlightColors = /* @__PURE__ */ new Set([
@@ -3038,14 +2887,14 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3038
2887
  const [caretPosition, setCaretPosition] = useState(null);
3039
2888
  const [anonymizationRectGroups, setAnonymizationRectGroups] = useState([]);
3040
2889
  const anonymizationMatchesRef = useRef([]);
3041
- const anonymizationOverlayRequestSeqRef = useRef(0);
2890
+ const [anonymizationOverlayRequestGate] = useState(createLatestRequestGate);
3042
2891
  const autocompleteSuggestionRef = useRef({
3043
2892
  status: "idle",
3044
2893
  anchor: null,
3045
2894
  text: "",
3046
2895
  requestId: null
3047
2896
  });
3048
- const autocompleteOverlayRequestSeqRef = useRef(0);
2897
+ const [autocompleteOverlayRequestGate] = useState(createLatestRequestGate);
3049
2898
  const [autocompleteCaret, setAutocompleteCaret] = useState(null);
3050
2899
  const [autocompleteText, setAutocompleteText] = useState("");
3051
2900
  const [autocompleteIsStreaming, setAutocompleteIsStreaming] = useState(false);
@@ -3053,7 +2902,7 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3053
2902
  const [directiveGutter, setDirectiveGutter] = useState(null);
3054
2903
  const [directiveCaretPos, setDirectiveCaretPos] = useState(null);
3055
2904
  const directivesRef = useRef([]);
3056
- const directivesOverlayRequestSeqRef = useRef(0);
2905
+ const [directivesOverlayRequestGate] = useState(createLatestRequestGate);
3057
2906
  const templatePreviewRef = useRef({
3058
2907
  entries: EMPTY_TEMPLATE_PREVIEW_ENTRIES,
3059
2908
  mode: "plain"
@@ -3063,11 +2912,11 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3063
2912
  suggestions: EMPTY_AI_SUGGESTIONS,
3064
2913
  focusedId: null
3065
2914
  });
3066
- const aiSuggestionsOverlayRequestSeqRef = useRef(0);
2915
+ const [aiSuggestionsOverlayRequestGate] = useState(createLatestRequestGate);
3067
2916
  const [remoteSelections, setRemoteSelections] = useState([]);
3068
2917
  const suppressSelectionOverlayRef = useRef(false);
3069
2918
  const revealSelectionOverlayTimerRef = useRef(null);
3070
- const selectionOverlayRequestSeqRef = useRef(0);
2919
+ const [selectionOverlayRequestGate] = useState(createLatestRequestGate);
3071
2920
  const validPrecomputedInitialState = precomputedInitialDocumentRef.current === document ? precomputedInitialState : null;
3072
2921
  precomputedInitialStateRef.current = validPrecomputedInitialState;
3073
2922
  const [selectedImageInfo, setSelectedImageInfo] = useState(null);
@@ -3190,7 +3039,7 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3190
3039
  const defaultTabStop = document?.package.settings?.defaultTabStop;
3191
3040
  const documentSettings = document?.package.settings;
3192
3041
  const mirrorMargins = documentSettings !== void 0 && "mirrorMargins" in documentSettings && documentSettings.mirrorMargins === true;
3193
- const sectionHeaderFooterRefs = useMemo(() => getSectionHeaderFooterRefs(document), [document]);
3042
+ const sectionHeaderFooterRefs = useMemo(() => resolveSectionHeaderFooterRefs(document), [document]);
3194
3043
  const layoutInputSignature = useMemo(() => buildLayoutInputSignature({
3195
3044
  columns,
3196
3045
  contentWidth,
@@ -3442,9 +3291,7 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3442
3291
  */
3443
3292
  const updateSelectionOverlay = useCallback((state_3) => {
3444
3293
  const { from, to } = state_3.selection;
3445
- const requestSeq = selectionOverlayRequestSeqRef.current + 1;
3446
- selectionOverlayRequestSeqRef.current = requestSeq;
3447
- const isCurrentRequest = () => selectionOverlayRequestSeqRef.current === requestSeq;
3294
+ const isCurrentRequest = selectionOverlayRequestGate.begin();
3448
3295
  if (directivesRef.current.length > 0) setDirectiveCaretPos(state_3.selection.head);
3449
3296
  onSelectionChangeRef.current?.(from, to);
3450
3297
  folioEmitterRef.current.emit("selectionChange", {
@@ -3586,14 +3433,13 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3586
3433
  blocks,
3587
3434
  measures,
3588
3435
  getCaretFromDom,
3436
+ selectionOverlayRequestGate,
3589
3437
  zoom
3590
3438
  ]);
3591
3439
  const updateSelectionOverlayRef = useRef(updateSelectionOverlay);
3592
3440
  updateSelectionOverlayRef.current = updateSelectionOverlay;
3593
3441
  const updateAnonymizationOverlay = useCallback(() => {
3594
- const requestSeq_0 = anonymizationOverlayRequestSeqRef.current + 1;
3595
- anonymizationOverlayRequestSeqRef.current = requestSeq_0;
3596
- const isCurrentRequest_0 = () => anonymizationOverlayRequestSeqRef.current === requestSeq_0;
3442
+ const isCurrentRequest_0 = anonymizationOverlayRequestGate.begin();
3597
3443
  const matches = anonymizationMatchesRef.current;
3598
3444
  if (matches.length === 0) {
3599
3445
  setAnonymizationRectGroups([]);
@@ -3688,16 +3534,16 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3688
3534
  if (fallbackGroups.length > 0 && isCurrentRequest_0()) setAnonymizationRectGroups([...groups, ...fallbackGroups]);
3689
3535
  }, () => void 0);
3690
3536
  }, [
3537
+ anonymizationOverlayRequestGate,
3691
3538
  layout,
3692
3539
  blocks,
3693
3540
  measures,
3694
3541
  zoom
3695
3542
  ]);
3696
3543
  const updateAutocompleteOverlay = useCallback(() => {
3697
- const requestSeq_1 = autocompleteOverlayRequestSeqRef.current + 1;
3698
- autocompleteOverlayRequestSeqRef.current = requestSeq_1;
3544
+ const requestIsLatest = autocompleteOverlayRequestGate.begin();
3699
3545
  const current = autocompleteSuggestionRef.current;
3700
- const isCurrentRequest_1 = () => autocompleteOverlayRequestSeqRef.current === requestSeq_1 && autocompleteSuggestionRef.current === current && current.status !== "idle" && current.text.length > 0;
3546
+ const isCurrentRequest_1 = () => requestIsLatest() && autocompleteSuggestionRef.current === current && current.status !== "idle" && current.text.length > 0;
3701
3547
  if (current.status === "idle" || current.text.length === 0) {
3702
3548
  setAutocompleteCaret(null);
3703
3549
  setAutocompleteText("");
@@ -3738,14 +3584,14 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3738
3584
  setAutocompleteIsStreaming(current.status === "streaming");
3739
3585
  }, () => void 0);
3740
3586
  }, [
3587
+ autocompleteOverlayRequestGate,
3741
3588
  layout,
3742
3589
  blocks,
3743
3590
  measures,
3744
3591
  zoom
3745
3592
  ]);
3746
3593
  const updateDirectivesOverlay = useCallback(() => {
3747
- const requestSeq_2 = directivesOverlayRequestSeqRef.current + 1;
3748
- directivesOverlayRequestSeqRef.current = requestSeq_2;
3594
+ const isCurrentRequest_2 = directivesOverlayRequestGate.begin();
3749
3595
  const previewedStarts = new Set(templatePreviewRef.current.entries.map((entry) => entry.from));
3750
3596
  const ranges = directivesRef.current.filter((range_2) => !(range_2.kind === "placeholder" && previewedStarts.has(range_2.from)));
3751
3597
  const pagesContainer_1 = pagesContainerRef.current;
@@ -3763,17 +3609,17 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3763
3609
  blocks,
3764
3610
  measures
3765
3611
  });
3766
- if (directivesOverlayRequestSeqRef.current === requestSeq_2) setDirectiveRectGroups(projected);
3612
+ if (isCurrentRequest_2()) setDirectiveRectGroups(projected);
3767
3613
  })();
3768
3614
  }, [
3615
+ directivesOverlayRequestGate,
3769
3616
  layout,
3770
3617
  blocks,
3771
3618
  measures,
3772
3619
  zoom
3773
3620
  ]);
3774
3621
  const updateAISuggestionsOverlay = useCallback(() => {
3775
- const requestSeq_3 = aiSuggestionsOverlayRequestSeqRef.current + 1;
3776
- aiSuggestionsOverlayRequestSeqRef.current = requestSeq_3;
3622
+ const isCurrentRequest_3 = aiSuggestionsOverlayRequestGate.begin();
3777
3623
  const { suggestions, focusedId } = aiSuggestionsRef.current;
3778
3624
  const pending = suggestions.filter((suggestion) => suggestion.status === "pending" && suggestion.range.from >= 0 && suggestion.range.to > suggestion.range.from);
3779
3625
  const pagesContainer_2 = pagesContainerRef.current;
@@ -3793,7 +3639,7 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3793
3639
  blocks,
3794
3640
  measures
3795
3641
  });
3796
- if (aiSuggestionsOverlayRequestSeqRef.current !== requestSeq_3) return;
3642
+ if (!isCurrentRequest_3()) return;
3797
3643
  const focused = projected_0.find(({ range: range_3 }) => range_3.suggestion.id === focusedId);
3798
3644
  const fonts = collectRunFontsAtPmPositions(pagesContainer_2, focused ? [focused.range.from] : []);
3799
3645
  setAiSuggestionRectGroups(projected_0.map(({ range: range_4, rects: rects_2 }) => ({
@@ -3804,13 +3650,14 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3804
3650
  })));
3805
3651
  })();
3806
3652
  }, [
3653
+ aiSuggestionsOverlayRequestGate,
3807
3654
  layout,
3808
3655
  blocks,
3809
3656
  measures,
3810
3657
  zoom
3811
3658
  ]);
3812
3659
  const hideSelectionOverlayDuringInput = useCallback((state_4) => {
3813
- selectionOverlayRequestSeqRef.current += 1;
3660
+ selectionOverlayRequestGate.invalidate();
3814
3661
  suppressSelectionOverlayRef.current = true;
3815
3662
  setCaretPosition(null);
3816
3663
  setSelectionRects([]);
@@ -3820,13 +3667,24 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3820
3667
  suppressSelectionOverlayRef.current = false;
3821
3668
  updateSelectionOverlay(hiddenPMRef.current?.getState() ?? state_4);
3822
3669
  }, SELECTION_REVEAL_AFTER_INPUT_DELAY);
3823
- }, [updateSelectionOverlay]);
3670
+ }, [selectionOverlayRequestGate, updateSelectionOverlay]);
3824
3671
  useEffect(() => () => {
3672
+ selectionOverlayRequestGate.invalidate();
3673
+ anonymizationOverlayRequestGate.invalidate();
3674
+ autocompleteOverlayRequestGate.invalidate();
3675
+ directivesOverlayRequestGate.invalidate();
3676
+ aiSuggestionsOverlayRequestGate.invalidate();
3825
3677
  if (revealSelectionOverlayTimerRef.current !== null) {
3826
3678
  window.clearTimeout(revealSelectionOverlayTimerRef.current);
3827
3679
  revealSelectionOverlayTimerRef.current = null;
3828
3680
  }
3829
- }, []);
3681
+ }, [
3682
+ aiSuggestionsOverlayRequestGate,
3683
+ anonymizationOverlayRequestGate,
3684
+ autocompleteOverlayRequestGate,
3685
+ directivesOverlayRequestGate,
3686
+ selectionOverlayRequestGate
3687
+ ]);
3830
3688
  /**
3831
3689
  * Handle PM transaction - re-layout on content/selection change.
3832
3690
  */
@@ -12306,7 +12164,7 @@ function useFolioComments({ doc, autoOpenReviewSidebar, anchorPositions, editorC
12306
12164
  * - Error boundary
12307
12165
  * - Loading states
12308
12166
  */
12309
- const CommentsSidebar = lazy(() => import("./CommentsSidebar-Byn5lyrt.js").then((m) => ({ default: m.CommentsSidebar })));
12167
+ const CommentsSidebar = lazy(() => import("./CommentsSidebar-Bopn32H9.js").then((m) => ({ default: m.CommentsSidebar })));
12310
12168
  const TextContextMenu = lazy(() => import("./TextContextMenu-Ci7-M4oU.js").then((m) => ({ default: m.TextContextMenu })));
12311
12169
  const loadAttemptSelectiveSave = async () => {
12312
12170
  const { attemptSelectiveSave } = await import("@stll/folio-core/docx/selectiveSave");
@@ -14042,67 +13900,104 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
14042
13900
  });
14043
13901
  return true;
14044
13902
  },
14045
- getTrackedChanges: () => {
13903
+ showInDocument: (target_1, snapshot_2) => {
14046
13904
  const view_27 = pagedEditorRef.current?.getView();
14047
- return view_27 ? getTrackedChangesFromDoc(view_27.state.doc) : [];
13905
+ if (!view_27) return false;
13906
+ const range_4 = target_1.type === "textRange" ? resolveFolioAITextRange({
13907
+ range: target_1,
13908
+ doc: view_27.state.doc,
13909
+ snapshot: snapshot_2
13910
+ }) : resolveFolioAIBlockRange({
13911
+ blockId: target_1.blockId,
13912
+ doc: view_27.state.doc,
13913
+ snapshot: snapshot_2
13914
+ });
13915
+ if (range_4 === null) return false;
13916
+ const { from: from_10, to: to_10 } = range_4;
13917
+ const $from_1 = view_27.state.doc.resolve(from_10);
13918
+ const $to_1 = view_27.state.doc.resolve(to_10);
13919
+ view_27.dispatch(view_27.state.tr.setSelection(TextSelection.between($from_1, $to_1)));
13920
+ requestAnimationFrame(() => {
13921
+ pagedEditorRef.current?.scrollToPosition(from_10);
13922
+ });
13923
+ return true;
14048
13924
  },
14049
- getCommentAnchors: () => {
13925
+ getTrackedChanges: () => {
14050
13926
  const view_28 = pagedEditorRef.current?.getView();
14051
- return view_28 ? getCommentAnchorsFromDoc(view_28.state.doc) : [];
13927
+ return view_28 ? getTrackedChangesFromDoc(view_28.state.doc) : [];
14052
13928
  },
14053
- getSelectionText: () => {
13929
+ getCommentAnchors: () => {
14054
13930
  const view_29 = pagedEditorRef.current?.getView();
14055
- return view_29 ? getSelectedText(view_29.state) : "";
13931
+ return view_29 ? getCommentAnchorsFromDoc(view_29.state.doc) : [];
14056
13932
  },
14057
- getPageText: (page_0) => {
13933
+ getSelectionText: () => {
14058
13934
  const view_30 = pagedEditorRef.current?.getView();
14059
- if (!view_30) return null;
14060
- return getPageTextFromLayout(pagedEditorRef.current?.getEditor()?.getLayout() ?? null, view_30.state.doc, page_0);
13935
+ return view_30 ? getSelectedText(view_30.state) : "";
14061
13936
  },
14062
- getContentControls: (filter = {}) => {
13937
+ getPageText: (page_0) => {
14063
13938
  const view_31 = pagedEditorRef.current?.getView();
14064
- if (!view_31) return [];
14065
- return findBlockSdtMatches(view_31.state.doc, filter).map((match_0) => ({
13939
+ if (!view_31) return null;
13940
+ return getPageTextFromLayout(pagedEditorRef.current?.getEditor()?.getLayout() ?? null, view_31.state.doc, page_0);
13941
+ },
13942
+ getTargetPage: (target_2, snapshot_3) => {
13943
+ const view_32 = pagedEditorRef.current?.getView();
13944
+ if (!view_32) return null;
13945
+ const range_5 = target_2.type === "textRange" ? resolveFolioAITextRange({
13946
+ range: target_2,
13947
+ doc: view_32.state.doc,
13948
+ snapshot: snapshot_3
13949
+ }) : resolveFolioAIBlockRange({
13950
+ blockId: target_2.blockId,
13951
+ doc: view_32.state.doc,
13952
+ snapshot: snapshot_3
13953
+ });
13954
+ if (range_5 === null) return null;
13955
+ return pagedEditorRef.current?.getPageNumberForPmPos(range_5.from) ?? null;
13956
+ },
13957
+ getContentControls: (filter = {}) => {
13958
+ const view_33 = pagedEditorRef.current?.getView();
13959
+ if (!view_33) return [];
13960
+ return findBlockSdtMatches(view_33.state.doc, filter).map((match_0) => ({
14066
13961
  properties: blockSdtAttrsToSdtProperties(match_0.node),
14067
13962
  path: match_0.path,
14068
13963
  pmPos: match_0.pos
14069
13964
  }));
14070
13965
  },
14071
13966
  scrollToContentControl: (filter_0) => {
14072
- const view_32 = pagedEditorRef.current?.getView();
14073
- if (!view_32) return false;
14074
- const match_1 = findBlockSdtMatch(view_32.state.doc, filter_0);
13967
+ const view_34 = pagedEditorRef.current?.getView();
13968
+ if (!view_34) return false;
13969
+ const match_1 = findBlockSdtMatch(view_34.state.doc, filter_0);
14075
13970
  if (!match_1) return false;
14076
13971
  const inside = match_1.pos + 1;
14077
- const $pos = view_32.state.doc.resolve(inside);
14078
- view_32.dispatch(view_32.state.tr.setSelection(TextSelection.near($pos)));
13972
+ const $pos = view_34.state.doc.resolve(inside);
13973
+ view_34.dispatch(view_34.state.tr.setSelection(TextSelection.near($pos)));
14079
13974
  requestAnimationFrame(() => {
14080
13975
  pagedEditorRef.current?.scrollToPosition(inside);
14081
13976
  });
14082
13977
  return true;
14083
13978
  },
14084
13979
  setContentControlContent: (filter_1, input, options_2 = {}) => {
14085
- const view_33 = pagedEditorRef.current?.getView();
14086
- if (!view_33) return false;
14087
- const tr_0 = typeof input === "string" ? setContentControlContentTr(view_33.state, filter_1, input, options_2) : setContentControlContentBlocksTr(view_33.state, filter_1, input, options_2);
13980
+ const view_35 = pagedEditorRef.current?.getView();
13981
+ if (!view_35) return false;
13982
+ const tr_0 = typeof input === "string" ? setContentControlContentTr(view_35.state, filter_1, input, options_2) : setContentControlContentBlocksTr(view_35.state, filter_1, input, options_2);
14088
13983
  if (!tr_0) return false;
14089
- view_33.dispatch(tr_0);
13984
+ view_35.dispatch(tr_0);
14090
13985
  return true;
14091
13986
  },
14092
13987
  setContentControlValue: (filter_2, input_0, options_3 = {}) => {
14093
- const view_34 = pagedEditorRef.current?.getView();
14094
- if (!view_34) return false;
14095
- const tr_1 = setContentControlValueTr(view_34.state, filter_2, input_0, options_3);
13988
+ const view_36 = pagedEditorRef.current?.getView();
13989
+ if (!view_36) return false;
13990
+ const tr_1 = setContentControlValueTr(view_36.state, filter_2, input_0, options_3);
14096
13991
  if (!tr_1) return false;
14097
- view_34.dispatch(tr_1);
13992
+ view_36.dispatch(tr_1);
14098
13993
  return true;
14099
13994
  },
14100
13995
  removeContentControl: (filter_3, options_4 = {}) => {
14101
- const view_35 = pagedEditorRef.current?.getView();
14102
- if (!view_35) return false;
14103
- const tr_2 = removeContentControlTr(view_35.state, filter_3, options_4);
13996
+ const view_37 = pagedEditorRef.current?.getView();
13997
+ if (!view_37) return false;
13998
+ const tr_2 = removeContentControlTr(view_37.state, filter_3, options_4);
14104
13999
  if (!tr_2) return false;
14105
- view_35.dispatch(tr_2);
14000
+ view_37.dispatch(tr_2);
14106
14001
  return true;
14107
14002
  }
14108
14003
  }), [
@@ -14173,39 +14068,39 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
14173
14068
  if (isDisplayMode(value)) setDisplayMode(value);
14174
14069
  }, [setDisplayMode]);
14175
14070
  const handleAcceptActiveChange = useCallback(() => {
14176
- const view_36 = pagedEditorRef.current?.getView();
14177
- if (!view_36) return;
14178
- const { from: from_10, to: to_10 } = view_36.state.selection;
14179
- const range_4 = findChangeAtPosition(view_36.state, from_10, to_10);
14180
- acceptChange(range_4.from, range_4.to)(view_36.state, view_36.dispatch);
14071
+ const view_38 = pagedEditorRef.current?.getView();
14072
+ if (!view_38) return;
14073
+ const { from: from_11, to: to_11 } = view_38.state.selection;
14074
+ const range_6 = findChangeAtPosition(view_38.state, from_11, to_11);
14075
+ acceptChange(range_6.from, range_6.to)(view_38.state, view_38.dispatch);
14181
14076
  }, []);
14182
14077
  const handleRejectActiveChange = useCallback(() => {
14183
- const view_37 = pagedEditorRef.current?.getView();
14184
- if (!view_37) return;
14185
- const { from: from_11, to: to_11 } = view_37.state.selection;
14186
- const range_5 = findChangeAtPosition(view_37.state, from_11, to_11);
14187
- rejectChange(range_5.from, range_5.to)(view_37.state, view_37.dispatch);
14078
+ const view_39 = pagedEditorRef.current?.getView();
14079
+ if (!view_39) return;
14080
+ const { from: from_12, to: to_12 } = view_39.state.selection;
14081
+ const range_7 = findChangeAtPosition(view_39.state, from_12, to_12);
14082
+ rejectChange(range_7.from, range_7.to)(view_39.state, view_39.dispatch);
14188
14083
  }, []);
14189
14084
  const handlePreviousChange = useCallback(() => {
14190
- const view_38 = pagedEditorRef.current?.getView();
14191
- if (!view_38) return;
14192
- const { from: from_12 } = view_38.state.selection;
14193
- const change = findPreviousChange(view_38.state, from_12);
14085
+ const view_40 = pagedEditorRef.current?.getView();
14086
+ if (!view_40) return;
14087
+ const { from: from_13 } = view_40.state.selection;
14088
+ const change = findPreviousChange(view_40.state, from_13);
14194
14089
  if (!change) return;
14195
- view_38.dispatch(view_38.state.tr.setSelection(TextSelection.create(view_38.state.doc, change.from, change.to)));
14196
- view_38.focus();
14090
+ view_40.dispatch(view_40.state.tr.setSelection(TextSelection.create(view_40.state.doc, change.from, change.to)));
14091
+ view_40.focus();
14197
14092
  requestAnimationFrame(() => {
14198
14093
  pagedEditorRef.current?.scrollToPosition(change.from);
14199
14094
  });
14200
14095
  }, []);
14201
14096
  const handleNextChange = useCallback(() => {
14202
- const view_39 = pagedEditorRef.current?.getView();
14203
- if (!view_39) return;
14204
- const { to: to_12 } = view_39.state.selection;
14205
- const change_0 = findNextChange(view_39.state, to_12);
14097
+ const view_41 = pagedEditorRef.current?.getView();
14098
+ if (!view_41) return;
14099
+ const { to: to_13 } = view_41.state.selection;
14100
+ const change_0 = findNextChange(view_41.state, to_13);
14206
14101
  if (!change_0) return;
14207
- view_39.dispatch(view_39.state.tr.setSelection(TextSelection.create(view_39.state.doc, change_0.from, change_0.to)));
14208
- view_39.focus();
14102
+ view_41.dispatch(view_41.state.tr.setSelection(TextSelection.create(view_41.state.doc, change_0.from, change_0.to)));
14103
+ view_41.focus();
14209
14104
  requestAnimationFrame(() => {
14210
14105
  pagedEditorRef.current?.scrollToPosition(change_0.from);
14211
14106
  });
@@ -14375,8 +14270,8 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
14375
14270
  requestAnimationFrame(syncCommentHighlightStyles);
14376
14271
  }, [onScrollTopChange, syncCommentHighlightStyles]);
14377
14272
  const handlePagedSelectionChange = useCallback((_from, _to) => {
14378
- const view_40 = getActiveEditorView() ?? pagedEditorRef.current?.getView() ?? null;
14379
- handleSelectionChange(view_40 ? extractSelectionState(view_40.state) : null);
14273
+ const view_42 = getActiveEditorView() ?? pagedEditorRef.current?.getView() ?? null;
14274
+ handleSelectionChange(view_42 ? extractSelectionState(view_42.state) : null);
14380
14275
  }, [getActiveEditorView, handleSelectionChange]);
14381
14276
  const handleTotalPagesChange = useCallback((totalPages) => {
14382
14277
  setScrollPageInfo((previous) => updateScrollPageTotal(previous, totalPages));
@@ -14401,9 +14296,9 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
14401
14296
  }, [author, updateComments]);
14402
14297
  const handleAddComment = useCallback((addText) => {
14403
14298
  const comment_3 = createComment(addText, author);
14404
- const view_41 = pagedEditorRef.current?.getView();
14405
- if (!view_41 || !commentSelectionRange) return false;
14406
- if (!applyCommentMarkRange(view_41, commentSelectionRange, comment_3.id, { replacePending: true })) return false;
14299
+ const view_43 = pagedEditorRef.current?.getView();
14300
+ if (!view_43 || !commentSelectionRange) return false;
14301
+ if (!applyCommentMarkRange(view_43, commentSelectionRange, comment_3.id, { replacePending: true })) return false;
14407
14302
  const commentAuthor_2 = getCommentAuthorKey(comment_3.author);
14408
14303
  setVisibleCommentAuthors((current) => {
14409
14304
  if (current === null) return null;
@@ -14437,8 +14332,8 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
14437
14332
  updateComments((previous_4) => [...previous_4, createComment(text_6, author, revisionId_2)]);
14438
14333
  }, [author, updateComments]);
14439
14334
  const handleCancelAddComment = useCallback(() => {
14440
- const view_42 = pagedEditorRef.current?.getView();
14441
- if (view_42 && commentSelectionRange) removePendingCommentMarkRange(view_42, commentSelectionRange);
14335
+ const view_44 = pagedEditorRef.current?.getView();
14336
+ if (view_44 && commentSelectionRange) removePendingCommentMarkRange(view_44, commentSelectionRange);
14442
14337
  setIsAddingComment(false);
14443
14338
  setCommentSelectionRange(null);
14444
14339
  setAddCommentYPosition(null);
@@ -14448,16 +14343,16 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
14448
14343
  setCommentSelectionRange,
14449
14344
  setIsAddingComment
14450
14345
  ]);
14451
- const handleSidebarAcceptChange = useCallback((from_13, to_13) => {
14452
- const view_43 = pagedEditorRef.current?.getView();
14453
- if (!view_43) return;
14454
- acceptChange(from_13, to_13)(view_43.state, view_43.dispatch);
14346
+ const handleSidebarAcceptChange = useCallback((from_14, to_14) => {
14347
+ const view_45 = pagedEditorRef.current?.getView();
14348
+ if (!view_45) return;
14349
+ acceptChange(from_14, to_14)(view_45.state, view_45.dispatch);
14455
14350
  extractTrackedChanges();
14456
14351
  }, [extractTrackedChanges]);
14457
- const handleSidebarRejectChange = useCallback((from_14, to_14) => {
14458
- const view_44 = pagedEditorRef.current?.getView();
14459
- if (!view_44) return;
14460
- rejectChange(from_14, to_14)(view_44.state, view_44.dispatch);
14352
+ const handleSidebarRejectChange = useCallback((from_15, to_15) => {
14353
+ const view_46 = pagedEditorRef.current?.getView();
14354
+ if (!view_46) return;
14355
+ rejectChange(from_15, to_15)(view_46.state, view_46.dispatch);
14461
14356
  extractTrackedChanges();
14462
14357
  }, [extractTrackedChanges]);
14463
14358
  const commentsPageWidth = history.state?.package.document.finalSectionProperties?.pageWidth;
@@ -14530,8 +14425,8 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
14530
14425
  const getBodyEditorView = useCallback(() => pagedEditorRef.current?.getView() ?? null, []);
14531
14426
  const closeTableProperties = useCallback(() => setTablePropsOpen(false), []);
14532
14427
  const applyTableProperties = useCallback((props_0) => {
14533
- const view_45 = getActiveEditorView();
14534
- if (view_45) setTableProperties(props_0)(view_45.state, view_45.dispatch);
14428
+ const view_47 = getActiveEditorView();
14429
+ if (view_47) setTableProperties(props_0)(view_47.state, view_47.dispatch);
14535
14430
  }, [getActiveEditorView]);
14536
14431
  const closeImagePosition = useCallback(() => setImagePositionOpen(false), [setImagePositionOpen]);
14537
14432
  const closeImageProperties = useCallback(() => setImagePropsOpen(false), [setImagePropsOpen]);
@@ -14539,8 +14434,8 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
14539
14434
  const closeFootnoteProperties = useCallback(() => setFootnotePropsOpen(false), []);
14540
14435
  const closeInsertSymbol = useCallback(() => setShowInsertSymbol(false), []);
14541
14436
  const insertSymbol = useCallback((symbol) => {
14542
- const view_46 = getActiveEditorView();
14543
- if (view_46) view_46.dispatch(view_46.state.tr.insertText(symbol).scrollIntoView());
14437
+ const view_48 = getActiveEditorView();
14438
+ if (view_48) view_48.dispatch(view_48.state.tr.insertText(symbol).scrollIntoView());
14544
14439
  }, [getActiveEditorView]);
14545
14440
  const imagePropertiesCurrentData = useMemo(() => buildImagePropertiesData(state.pmImageContext), [state.pmImageContext]);
14546
14441
  const currentFindResult = findResultRef.current;
@@ -14866,8 +14761,8 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
14866
14761
  onMouseDown: (e_1) => {
14867
14762
  e_1.preventDefault();
14868
14763
  e_1.stopPropagation();
14869
- const view_47 = pagedEditorRef.current?.getView();
14870
- if (!view_47) {
14764
+ const view_49 = pagedEditorRef.current?.getView();
14765
+ if (!view_49) {
14871
14766
  setFloatingCommentBtn(null);
14872
14767
  return;
14873
14768
  }
@@ -14875,9 +14770,9 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
14875
14770
  from: floatingCommentBtn.from,
14876
14771
  to: floatingCommentBtn.to
14877
14772
  };
14878
- const currentSelection = view_47.state.selection;
14773
+ const currentSelection = view_49.state.selection;
14879
14774
  const safeRange = resolveCommentCreationRange({
14880
- docSize: view_47.state.doc.content.size,
14775
+ docSize: view_49.state.doc.content.size,
14881
14776
  capturedRange,
14882
14777
  currentRange: {
14883
14778
  from: currentSelection.from,
@@ -14891,7 +14786,7 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
14891
14786
  return;
14892
14787
  }
14893
14788
  setCommentSelectionRange(safeRange);
14894
- if (!applyCommentMarkRange(view_47, safeRange, -1, { selectEnd: true })) {
14789
+ if (!applyCommentMarkRange(view_49, safeRange, -1, { selectEnd: true })) {
14895
14790
  setCommentSelectionRange(null);
14896
14791
  setFloatingCommentBtn(null);
14897
14792
  return;