@stll/folio-react 0.18.0 → 0.18.2

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.
@@ -9,6 +9,7 @@ import { IntlProvider, useTranslations } from "use-intl";
9
9
  import { FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, createFolioAIEditSnapshot, getCommentAnchorsFromDoc, getFolioDocumentOperationIssues, getTrackedChangesFromDoc } from "@stll/folio-core/ai-edits";
10
10
  import { resolveActiveEditorStory } from "@stll/folio-core/controller/activeEditorStory";
11
11
  import { FOLIO_DOCX_SERIALIZATION_MODE, createFolioEditor } from "@stll/folio-core/controller/folioEditor";
12
+ import { cloneDocumentWithParagraphPropertySources } from "@stll/folio-core/docx/document-clone";
12
13
  import { normalizeBaseDirection } from "@stll/folio-core/docx/normalizeBaseDirection";
13
14
  import { getCachedNumberingMap } from "@stll/folio-core/docx/numberingParser";
14
15
  import { computeScrollPageInfo, updateScrollPageTotal } from "@stll/folio-core/paged-layout/scrollPageInfo";
@@ -54,14 +55,16 @@ import { documentFontsAreLoaded, getDocumentFontSet, waitForInitialLayoutFonts }
54
55
  import { getFootnoteText } from "@stll/folio-core/docx/footnoteParser";
55
56
  import { convertHeaderFooterPmDocToContent, convertHeaderFooterToContent } from "@stll/folio-core/layout-bridge/convert/headerFooterLayout";
56
57
  import { templatePreviewDirtyRange } from "@stll/folio-core/layout-bridge/convert/templatePreviewFlow";
57
- import { clickToPositionDom, findCollapsedLineEdgeCaretTarget } from "@stll/folio-core/layout-bridge/dom/clickToPositionDom";
58
+ import { clickToPositionDom, getCaretPositionFromDom, getSelectionRectsFromDom } from "@stll/folio-core/layout-bridge/dom/clickToPositionDom";
59
+ import { measurePaintedSpanRange } from "@stll/folio-core/layout-bridge/dom/textStreamDom";
58
60
  import { resetImeCaretAnchor, syncImeCaretAnchor } from "@stll/folio-core/layout-bridge/dom/imeCaretAnchor";
59
- import { findBodyEmptyRuns, findBodyPmAnchor, findBodyPmAnchors, findBodyPmSpans } from "@stll/folio-core/layout-bridge/dom/findBodyPmSpans";
60
- import { clickToPositionInHfSlot, findHfCaretSpan, findHfPmSpans, findHfSlotForTarget, findHfSlotKindForTarget } from "@stll/folio-core/layout-bridge/dom/findHfPmSpans";
61
+ import { findBodyPmAnchor, findBodyPmAnchors, findBodyPmSpans } from "@stll/folio-core/layout-bridge/dom/findBodyPmSpans";
62
+ import { clickToPositionInHfSlot, findHfSlotForTarget, findHfSlotKindForTarget } from "@stll/folio-core/layout-bridge/dom/findHfPmSpans";
61
63
  import { findNoteStoryForTarget } from "@stll/folio-core/layout-bridge/dom/noteStoryDom";
62
64
  import { clickToPosition } from "@stll/folio-core/layout-bridge/engine/clickToPosition";
63
65
  import { getPageTop, hitTestFragment, hitTestTableCell } from "@stll/folio-core/layout-bridge/engine/hitTest";
64
66
  import { clearAllCaches, resetCanvasContext } from "@stll/folio-core/layout-bridge/engine/measuring";
67
+ import { resolveHeaderFooterSelectionGeometry } from "@stll/folio-core/render-dom/HeaderFooterSelectionOverlay";
65
68
  import { setEmbeddedFontFamilyMap } from "@stll/folio-core/utils/fontResolver";
66
69
  import { resolveSectionHeaderFooterRefs } from "@stll/folio-core/layout-engine";
67
70
  import { recordLayoutPhase } from "@stll/folio-core/layout-engine/layoutInstrumentation";
@@ -77,12 +80,13 @@ import { flashParagraphFragmentsByParaId } from "@stll/folio-core/paged-layout/p
77
80
  import { DEFAULT_PAGE_HEIGHT_PX, getMargins, getPageSize, twipsToPixels as twipsToPixels$1 } from "@stll/folio-core/paged-layout/sectionGeometry";
78
81
  import { resizeColumnPair } from "@stll/folio-core/paged-layout/tableColumnResize";
79
82
  import { tableInsertButtonOffset } from "@stll/folio-core/paged-layout/tableInsertButtonGeometry";
80
- import { getTransactionDirtyRange } from "@stll/folio-core/paged-layout/transactionDirtyRange";
83
+ import { getTransactionsDirtyRange } from "@stll/folio-core/paged-layout/transactionDirtyRange";
81
84
  import { findStartPosForParaId } from "@stll/folio-core/prosemirror/utils/findParagraphByParaId";
82
85
  import { buildEmbeddedFontFamilyMap, extractEmbeddedFonts } from "@stll/folio-core/fonts/embeddedFonts";
83
86
  import { panic } from "better-result";
84
87
  import { collectRemoteSelections, createHiddenEditorManager, createHiddenEditorState } from "@stll/folio-core/controller/hiddenEditorManager";
85
88
  import { loadCollaborationModules } from "@stll/folio-core/controller/collaborationModules";
89
+ import { createVisualLineState, handleVisualLineKeyDown } from "@stll/folio-core/prosemirror/utils/visualLineNavigation";
86
90
  import { clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange } from "@stll/folio-core/ai-edits/blockRange";
87
91
  import { getFolioParaIdFromBlockId } from "@stll/folio-core/types/block-id";
88
92
  import { getPageTextFromLayout } from "@stll/folio-core/paged-layout/pageText";
@@ -1264,7 +1268,7 @@ const HiddenProseMirror = forwardRef((props, ref) => {
1264
1268
  getReadOnly: () => readOnlyRef.current,
1265
1269
  getDocumentIdentity: () => documentIdentityRef.current,
1266
1270
  getDocumentContext: () => documentRef.current,
1267
- onTransaction: (transaction, newState) => onTransactionRef.current?.(transaction, newState),
1271
+ onTransaction: (update) => onTransactionRef.current?.(update),
1268
1272
  onSelectionChange: (state) => onSelectionChangeRef.current?.(state),
1269
1273
  onKeyDown: (view, event) => onKeyDownRef.current?.(view, event) ?? false,
1270
1274
  onCopy: () => onCopyRef.current?.(),
@@ -2162,7 +2166,7 @@ const MAX_SPEED = 12;
2162
2166
  /**
2163
2167
  * Find the nearest scrollable ancestor.
2164
2168
  */
2165
- function findScrollParent$1(el) {
2169
+ function findScrollParent(el) {
2166
2170
  let parent = el.parentElement;
2167
2171
  while (parent && parent !== document.documentElement) {
2168
2172
  const { overflowY } = getComputedStyle(parent);
@@ -2183,7 +2187,7 @@ function useDragAutoScroll({ pagesContainerRef, onScrollExtendSelection }) {
2183
2187
  if (scrollParentRef.current) return scrollParentRef.current;
2184
2188
  const pages = pagesContainerRef.current;
2185
2189
  if (!pages) return null;
2186
- scrollParentRef.current = findScrollParent$1(pages);
2190
+ scrollParentRef.current = findScrollParent(pages);
2187
2191
  return scrollParentRef.current;
2188
2192
  }, [pagesContainerRef]);
2189
2193
  const stopAutoScroll = useCallback(() => {
@@ -2236,246 +2240,30 @@ function useDragAutoScroll({ pagesContainerRef, onScrollExtendSelection }) {
2236
2240
  }
2237
2241
  //#endregion
2238
2242
  //#region src/paged-editor/useVisualLineNavigation.ts
2239
- /**
2240
- * Visual Line Navigation Hook
2241
- *
2242
- * Implements visual-line-aware ArrowUp/ArrowDown navigation with sticky X.
2243
- * Extracted from PagedEditor.tsx for better separation of concerns.
2244
- *
2245
- * This hook provides:
2246
- * - getCaretClientX: Get the screen X of the caret at a PM position
2247
- * - findLineElementAtPosition: Find the .layout-line element for a PM position
2248
- * - findPositionOnLineAtClientX: Find a PM position on a line at a given screen X
2249
- * - handlePMKeyDown: Key handler for ArrowUp/ArrowDown with sticky X
2250
- */
2251
- /** Only match lines inside page body content, skipping header/footer lines. */
2252
- const CONTENT_LINE_SELECTOR = ".layout-page-content .layout-line";
2253
- /**
2254
- * Find the nearest ancestor that actually scrolls (overflow auto/scroll
2255
- * and scrollHeight > clientHeight).
2256
- */
2257
- function findScrollParent(el) {
2258
- let parent = el.parentElement;
2259
- while (parent && parent !== document.documentElement) {
2260
- const { overflowY } = getComputedStyle(parent);
2261
- if ((overflowY === "auto" || overflowY === "scroll") && parent.scrollHeight > parent.clientHeight) return parent;
2262
- parent = parent.parentElement;
2263
- }
2264
- return null;
2265
- }
2266
- /**
2267
- * Scroll the nearest scrollable ancestor so that the target element is visible.
2268
- * Uses manual scroll math because `scrollIntoView` misbehaves when the
2269
- * content is inside a CSS `transform: scale()` viewport.
2270
- */
2271
- function scrollIntoViewIfNeeded(el) {
2272
- const container = findScrollParent(el);
2273
- if (!container) return;
2274
- const elRect = el.getBoundingClientRect();
2275
- const containerRect = container.getBoundingClientRect();
2276
- const margin = 40;
2277
- if (elRect.bottom > containerRect.bottom - margin) container.scrollTop += elRect.bottom - containerRect.bottom + margin;
2278
- else if (elRect.top < containerRect.top + margin) container.scrollTop -= containerRect.top - elRect.top + margin;
2279
- }
2280
- function useVisualLineNavigation({ pagesContainerRef }) {
2281
- const stickyXRef = useRef(null);
2282
- const lastVisualLineIndexRef = useRef(-1);
2283
- /**
2284
- * Get the client X coordinate of the caret at a PM position.
2285
- */
2286
- const getCaretClientX = useCallback((pmPos) => {
2287
- if (!pagesContainerRef.current) return null;
2288
- const spans = findBodyPmSpans(pagesContainerRef.current);
2289
- for (const spanEl of spans) {
2290
- const pmStart = Number(spanEl.dataset["pmStart"]);
2291
- const pmEnd = Number(spanEl.dataset["pmEnd"]);
2292
- if (spanEl.classList.contains("layout-run-tab")) {
2293
- if (pmPos >= pmStart && pmPos < pmEnd) return spanEl.getBoundingClientRect().left;
2294
- continue;
2295
- }
2296
- if (pmPos >= pmStart && pmPos <= pmEnd && spanEl.firstChild?.nodeType === Node.TEXT_NODE) {
2297
- const textNode = spanEl.firstChild;
2298
- const charIndex = Math.min(pmPos - pmStart, textNode.length);
2299
- const range = spanEl.ownerDocument.createRange();
2300
- range.setStart(textNode, charIndex);
2301
- range.setEnd(textNode, charIndex);
2302
- return range.getBoundingClientRect().left;
2303
- }
2304
- if (pmPos >= pmStart && pmPos <= pmEnd) return spanEl.getBoundingClientRect().left;
2305
- }
2306
- const emptyRuns = findBodyEmptyRuns(pagesContainerRef.current);
2307
- for (const emptyRun of emptyRuns) {
2308
- const paragraph = closestHtmlElement(emptyRun, ".layout-paragraph");
2309
- if (!paragraph) continue;
2310
- const pmStart_0 = Number(paragraph.dataset["pmStart"]);
2311
- const pmEnd_0 = Number(paragraph.dataset["pmEnd"]);
2312
- if (pmPos >= pmStart_0 && pmPos <= pmEnd_0) return emptyRun.getBoundingClientRect().left;
2313
- }
2314
- return null;
2315
- }, [pagesContainerRef]);
2316
- /**
2317
- * Find the visual line element (.layout-line) containing a PM position.
2318
- */
2319
- const findLineElementAtPosition = useCallback((pmPos_0) => {
2320
- if (!pagesContainerRef.current) return null;
2321
- const allLines = htmlQueryAll(pagesContainerRef.current, CONTENT_LINE_SELECTOR);
2322
- for (const lineEl of allLines) {
2323
- const spans_0 = htmlQueryAll(lineEl, "span[data-pm-start][data-pm-end]");
2324
- for (const span of spans_0) {
2325
- const start = Number(span.dataset["pmStart"]);
2326
- const end = Number(span.dataset["pmEnd"]);
2327
- if (pmPos_0 >= start && pmPos_0 <= end) return lineEl;
2328
- }
2329
- }
2330
- for (const lineEl_0 of allLines) {
2331
- const paragraph_0 = closestHtmlElement(lineEl_0, ".layout-paragraph");
2332
- if (!paragraph_0) continue;
2333
- const pStart = Number(paragraph_0.dataset["pmStart"]);
2334
- const pEnd = Number(paragraph_0.dataset["pmEnd"]);
2335
- if (pmPos_0 >= pStart && pmPos_0 <= pEnd) {
2336
- if (paragraph_0.querySelector(".layout-line") === lineEl_0) return lineEl_0;
2337
- }
2338
- }
2339
- return null;
2340
- }, [pagesContainerRef]);
2341
- /**
2342
- * Find the PM position on a visual line closest to a client X coordinate.
2343
- */
2344
- const findPositionOnLineAtClientX = useCallback((lineEl_1, clientX) => {
2345
- const spans_1 = htmlQueryAll(lineEl_1, "span[data-pm-start][data-pm-end]");
2346
- const emptyRun_0 = lineEl_1.querySelector(".layout-empty-run");
2347
- if (emptyRun_0) {
2348
- if (emptyRun_0 instanceof HTMLElement && emptyRun_0.dataset["pmStart"]) return Number(emptyRun_0.dataset["pmStart"]);
2349
- const paragraph_1 = closestHtmlElement(lineEl_1, ".layout-paragraph");
2350
- if (paragraph_1?.dataset["pmStart"]) return Number(paragraph_1.dataset["pmStart"]) + 1;
2351
- return null;
2352
- }
2353
- if (spans_1.length === 0) {
2354
- const paragraph_2 = closestHtmlElement(lineEl_1, ".layout-paragraph");
2355
- if (paragraph_2?.dataset["pmStart"]) return Number(paragraph_2.dataset["pmStart"]) + 1;
2356
- return null;
2357
- }
2358
- for (const spanEl_0 of spans_1) {
2359
- const rect = spanEl_0.getBoundingClientRect();
2360
- const pmStart_1 = Number(spanEl_0.dataset["pmStart"]);
2361
- const pmEnd_1 = Number(spanEl_0.dataset["pmEnd"]);
2362
- if (spanEl_0.classList.contains("layout-run-tab")) {
2363
- if (clientX >= rect.left && clientX <= rect.right) return clientX < (rect.left + rect.right) / 2 ? pmStart_1 : pmEnd_1;
2364
- continue;
2365
- }
2366
- if (clientX >= rect.left && clientX <= rect.right) {
2367
- const textNode_0 = spanEl_0.firstChild;
2368
- if (!textNode_0 || textNode_0.nodeType !== Node.TEXT_NODE) return pmStart_1;
2369
- const text = textNode_0;
2370
- const ownerDoc_0 = spanEl_0.ownerDocument;
2371
- let lo = 0;
2372
- let hi = text.length;
2373
- while (lo < hi) {
2374
- const mid_0 = Math.floor((lo + hi) / 2);
2375
- const r = ownerDoc_0.createRange();
2376
- r.setStart(text, mid_0);
2377
- r.setEnd(text, mid_0);
2378
- if (clientX < r.getBoundingClientRect().left) hi = mid_0;
2379
- else lo = mid_0 + 1;
2380
- }
2381
- if (lo > 0 && lo <= text.length) {
2382
- const r_0 = ownerDoc_0.createRange();
2383
- r_0.setStart(text, lo - 1);
2384
- r_0.setEnd(text, lo - 1);
2385
- const leftX = r_0.getBoundingClientRect().left;
2386
- r_0.setStart(text, Math.min(lo, text.length));
2387
- r_0.setEnd(text, Math.min(lo, text.length));
2388
- const rightX = r_0.getBoundingClientRect().left;
2389
- if (Math.abs(clientX - leftX) < Math.abs(clientX - rightX)) return pmStart_1 + (lo - 1);
2390
- }
2391
- return pmStart_1 + Math.min(lo, pmEnd_1 - pmStart_1);
2392
- }
2393
- }
2394
- let closestSpan = null;
2395
- let closestDist = Infinity;
2396
- for (const span_0 of spans_1) {
2397
- const rect_0 = span_0.getBoundingClientRect();
2398
- const dist = clientX < rect_0.left ? rect_0.left - clientX : clientX - rect_0.right;
2399
- if (dist < closestDist) {
2400
- closestDist = dist;
2401
- closestSpan = span_0;
2402
- }
2403
- }
2404
- if (!closestSpan) return null;
2405
- return clientX < closestSpan.getBoundingClientRect().left ? Number(closestSpan.dataset["pmStart"]) : Number(closestSpan.dataset["pmEnd"]);
2406
- }, []);
2407
- return {
2408
- stickyXRef,
2409
- lastVisualLineIndexRef,
2410
- getCaretClientX,
2411
- findLineElementAtPosition,
2412
- findPositionOnLineAtClientX,
2413
- handlePMKeyDown: useCallback((view, event) => {
2414
- if (event.key !== "ArrowUp" && event.key !== "ArrowDown") {
2415
- if ([
2416
- "ArrowLeft",
2417
- "ArrowRight",
2418
- "Home",
2419
- "End"
2420
- ].includes(event.key) || event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
2421
- stickyXRef.current = null;
2422
- lastVisualLineIndexRef.current = -1;
2423
- }
2424
- return false;
2425
- }
2426
- if (event.ctrlKey || event.metaKey) {
2427
- stickyXRef.current = null;
2428
- lastVisualLineIndexRef.current = -1;
2429
- return false;
2430
- }
2431
- if (!pagesContainerRef.current) return false;
2432
- const allLines_0 = htmlQueryAll(pagesContainerRef.current, CONTENT_LINE_SELECTOR);
2433
- if (allLines_0.length === 0) return false;
2434
- const { from, anchor } = view.state.selection;
2435
- if (stickyXRef.current === null) {
2436
- const clientX_0 = getCaretClientX(from);
2437
- if (clientX_0 === null) return false;
2438
- stickyXRef.current = clientX_0;
2439
- }
2440
- let currentIndex;
2441
- if (lastVisualLineIndexRef.current >= 0 && lastVisualLineIndexRef.current < allLines_0.length) currentIndex = lastVisualLineIndexRef.current;
2442
- else {
2443
- const currentLine = findLineElementAtPosition(from);
2444
- if (!currentLine) return false;
2445
- currentIndex = allLines_0.indexOf(currentLine);
2446
- if (currentIndex === -1) return false;
2447
- }
2448
- const targetIndex = event.key === "ArrowUp" ? currentIndex - 1 : currentIndex + 1;
2449
- if (targetIndex < 0 || targetIndex >= allLines_0.length) {
2450
- lastVisualLineIndexRef.current = -1;
2451
- return false;
2452
- }
2453
- const targetLine = allLines_0[targetIndex];
2454
- if (!targetLine) return false;
2455
- const newPos = findPositionOnLineAtClientX(targetLine, stickyXRef.current);
2456
- if (newPos === null) return false;
2457
- lastVisualLineIndexRef.current = targetIndex;
2458
- const { state, dispatch } = view;
2459
- const clampedPos = Math.max(0, Math.min(newPos, state.doc.content.size));
2460
- const clampedAnchor = Math.max(0, Math.min(anchor, state.doc.content.size));
2461
- try {
2462
- const $newPos_0 = state.doc.resolve(clampedPos);
2463
- const sel_0 = event.shiftKey ? TextSelection$1.between(state.doc.resolve(clampedAnchor), $newPos_0) : Selection.near($newPos_0);
2464
- dispatch(state.tr.setSelection(sel_0));
2465
- } catch {
2466
- const $newPos = state.doc.resolve(clampedPos);
2467
- const sel = event.shiftKey ? TextSelection$1.between(state.doc.resolve(clampedAnchor), $newPos) : Selection.near($newPos);
2468
- dispatch(state.tr.setSelection(sel));
2469
- }
2470
- scrollIntoViewIfNeeded(targetLine);
2471
- return true;
2472
- }, [
2473
- pagesContainerRef,
2474
- getCaretClientX,
2475
- findLineElementAtPosition,
2476
- findPositionOnLineAtClientX
2477
- ])
2478
- };
2243
+ /** Bind the framework-neutral visual-line navigation state to React. */
2244
+ function useVisualLineNavigation(t0) {
2245
+ const $ = c(5);
2246
+ const { pagesContainerRef } = t0;
2247
+ let t1;
2248
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
2249
+ t1 = createVisualLineState();
2250
+ $[0] = t1;
2251
+ } else t1 = $[0];
2252
+ const stateRef = useRef(t1);
2253
+ let t2;
2254
+ if ($[1] !== pagesContainerRef) {
2255
+ t2 = (view, event) => handleVisualLineKeyDown(stateRef.current, view, event, pagesContainerRef.current);
2256
+ $[1] = pagesContainerRef;
2257
+ $[2] = t2;
2258
+ } else t2 = $[2];
2259
+ const handlePMKeyDown = t2;
2260
+ let t3;
2261
+ if ($[3] !== handlePMKeyDown) {
2262
+ t3 = { handlePMKeyDown };
2263
+ $[3] = handlePMKeyDown;
2264
+ $[4] = t3;
2265
+ } else t3 = $[4];
2266
+ return t3;
2479
2267
  }
2480
2268
  /** Distance in px from a row/column boundary that triggers the insert button */
2481
2269
  /** Distance in px from the table edge where boundary detection is active */
@@ -2565,15 +2353,6 @@ const getPageIndex = (el) => {
2565
2353
  const raw = pageEl.dataset["pageNumber"];
2566
2354
  return raw ? Number(raw) - 1 : 0;
2567
2355
  };
2568
- /**
2569
- * Get the line height for a node by climbing to its `.layout-line`
2570
- * ancestor and reading `offsetHeight`. Returns `fallback` if no
2571
- * ancestor is found.
2572
- */
2573
- const getLineHeight = (el, fallback = 16) => {
2574
- const lineEl = closestHtmlElement(el, ".layout-line");
2575
- return lineEl ? lineEl.offsetHeight : fallback;
2576
- };
2577
2356
  const containerStyles = {
2578
2357
  position: "relative",
2579
2358
  width: "100%",
@@ -2599,7 +2378,7 @@ const loadSelectionGeometry = () => {
2599
2378
  return selectionGeometryPromise;
2600
2379
  };
2601
2380
  function HfCaretOverlay(t0) {
2602
- const $ = c(22);
2381
+ const $ = c(27);
2603
2382
  const { selection, pagesContainer, hiddenHost, editorView, zoom } = t0;
2604
2383
  const [caret, setCaret] = useState(null);
2605
2384
  let t1;
@@ -2609,62 +2388,24 @@ function HfCaretOverlay(t0) {
2609
2388
  } else t1 = $[0];
2610
2389
  const [rangeRects, setRangeRects] = useState(t1);
2611
2390
  let t2;
2612
- let t3;
2613
- if ($[1] !== editorView || $[2] !== hiddenHost || $[3] !== pagesContainer || $[4] !== selection.from || $[5] !== selection.kind || $[6] !== selection.pageNumber || $[7] !== selection.rId || $[8] !== selection.to || $[9] !== zoom) {
2391
+ if ($[1] !== editorView || $[2] !== hiddenHost || $[3] !== pagesContainer || $[4] !== selection || $[5] !== zoom) {
2614
2392
  t2 = () => {
2615
2393
  if (!pagesContainer) return;
2616
2394
  const recompute = () => {
2617
2395
  const cr = pagesContainer.getBoundingClientRect();
2618
2396
  const zoomDivisor = zoom !== 0 ? zoom : 1;
2619
- const pageScope = selection.pageNumber ? pagesContainer.querySelector(`.layout-page[data-page-number="${selection.pageNumber}"]`) ?? pagesContainer : pagesContainer;
2620
- if (selection.from === selection.to) {
2621
- const hit = findHfCaretSpan(pageScope, selection.kind, selection.rId, selection.from);
2622
- if (!hit) {
2623
- syncImeCaretAnchor({
2624
- hiddenHost,
2625
- editorView,
2626
- visibleCaret: null
2627
- });
2628
- setCaret(null);
2629
- setRangeRects([]);
2630
- return;
2631
- }
2632
- const ar = hit.element.getBoundingClientRect();
2633
- let absX = hit.edge === "right" ? ar.right : ar.left;
2634
- let absY = ar.top;
2635
- let absHeight = ar.height || 16;
2636
- const pmStartStr = hit.element.dataset.pmStart;
2637
- const pmEndStr = hit.element.dataset.pmEnd;
2638
- const pmStart = pmStartStr ? Number.parseInt(pmStartStr, 10) : NaN;
2639
- const pmEnd = pmEndStr ? Number.parseInt(pmEndStr, 10) : NaN;
2640
- const textNode = hit.element.firstChild;
2641
- if (textNode && textNode.nodeType === Node.TEXT_NODE && Number.isFinite(pmStart) && Number.isFinite(pmEnd)) {
2642
- const textContent = textNode.textContent ?? "";
2643
- const charOffset = Math.min(Math.max(0, selection.from - pmStart), textContent.length);
2644
- const range = hit.element.ownerDocument.createRange();
2645
- range.setStart(textNode, charOffset);
2646
- range.setEnd(textNode, charOffset);
2647
- const rRect = range.getBoundingClientRect();
2648
- if (rRect.height > 0 || rRect.width > 0 || rRect.left > 0) {
2649
- absX = rRect.left;
2650
- absY = rRect.top;
2651
- absHeight = rRect.height || absHeight;
2652
- }
2653
- }
2654
- setCaret({
2655
- x: (absX - cr.left) / zoomDivisor,
2656
- y: (absY - cr.top) / zoomDivisor,
2657
- height: absHeight / zoomDivisor
2658
- });
2397
+ const geometry = resolveHeaderFooterSelectionGeometry(pagesContainer, selection, zoomDivisor);
2398
+ setCaret(geometry.caret);
2399
+ setRangeRects(geometry.ranges);
2400
+ if (geometry.caret) {
2659
2401
  syncImeCaretAnchor({
2660
2402
  hiddenHost,
2661
2403
  editorView,
2662
2404
  visibleCaret: {
2663
- left: absX,
2664
- top: absY
2405
+ left: cr.left + geometry.caret.x * zoomDivisor,
2406
+ top: cr.top + geometry.caret.y * zoomDivisor
2665
2407
  }
2666
2408
  });
2667
- setRangeRects([]);
2668
2409
  return;
2669
2410
  }
2670
2411
  syncImeCaretAnchor({
@@ -2672,41 +2413,6 @@ function HfCaretOverlay(t0) {
2672
2413
  editorView,
2673
2414
  visibleCaret: null
2674
2415
  });
2675
- const spans = findHfPmSpans(pageScope, selection.kind, selection.rId);
2676
- const rects = [];
2677
- for (const span of spans) {
2678
- const spanStart = Number.parseInt(span.dataset.pmStart ?? "", 10);
2679
- const spanEnd = Number.parseInt(span.dataset.pmEnd ?? "", 10);
2680
- if (!Number.isFinite(spanStart) || !Number.isFinite(spanEnd)) continue;
2681
- if (spanEnd <= selection.from || spanStart >= selection.to) continue;
2682
- const textNode_0 = span.firstChild;
2683
- if (textNode_0 && textNode_0.nodeType === Node.TEXT_NODE) {
2684
- const textContent_0 = textNode_0.textContent ?? "";
2685
- const startChar = Math.max(0, selection.from - spanStart);
2686
- const endChar = Math.min(textContent_0.length, selection.to - spanStart);
2687
- if (startChar < endChar) {
2688
- const range_0 = span.ownerDocument.createRange();
2689
- range_0.setStart(textNode_0, startChar);
2690
- range_0.setEnd(textNode_0, endChar);
2691
- for (const cRect of Array.from(range_0.getClientRects())) rects.push({
2692
- x: (cRect.left - cr.left) / zoomDivisor,
2693
- y: (cRect.top - cr.top) / zoomDivisor,
2694
- width: cRect.width / zoomDivisor,
2695
- height: cRect.height / zoomDivisor
2696
- });
2697
- continue;
2698
- }
2699
- }
2700
- const r = span.getBoundingClientRect();
2701
- rects.push({
2702
- x: (r.left - cr.left) / zoomDivisor,
2703
- y: (r.top - cr.top) / zoomDivisor,
2704
- width: r.width / zoomDivisor,
2705
- height: r.height / zoomDivisor
2706
- });
2707
- }
2708
- setRangeRects(rects);
2709
- setCaret(null);
2710
2416
  };
2711
2417
  recompute();
2712
2418
  const onPainted = () => recompute();
@@ -2731,6 +2437,15 @@ function HfCaretOverlay(t0) {
2731
2437
  resetImeCaretAnchor(hiddenHost);
2732
2438
  };
2733
2439
  };
2440
+ $[1] = editorView;
2441
+ $[2] = hiddenHost;
2442
+ $[3] = pagesContainer;
2443
+ $[4] = selection;
2444
+ $[5] = zoom;
2445
+ $[6] = t2;
2446
+ } else t2 = $[6];
2447
+ let t3;
2448
+ if ($[7] !== editorView || $[8] !== hiddenHost || $[9] !== pagesContainer || $[10] !== selection.from || $[11] !== selection.kind || $[12] !== selection.pageNumber || $[13] !== selection.rId || $[14] !== selection.to || $[15] !== zoom) {
2734
2449
  t3 = [
2735
2450
  selection.rId,
2736
2451
  selection.kind,
@@ -2742,31 +2457,27 @@ function HfCaretOverlay(t0) {
2742
2457
  editorView,
2743
2458
  zoom
2744
2459
  ];
2745
- $[1] = editorView;
2746
- $[2] = hiddenHost;
2747
- $[3] = pagesContainer;
2748
- $[4] = selection.from;
2749
- $[5] = selection.kind;
2750
- $[6] = selection.pageNumber;
2751
- $[7] = selection.rId;
2752
- $[8] = selection.to;
2753
- $[9] = zoom;
2754
- $[10] = t2;
2755
- $[11] = t3;
2756
- } else {
2757
- t2 = $[10];
2758
- t3 = $[11];
2759
- }
2460
+ $[7] = editorView;
2461
+ $[8] = hiddenHost;
2462
+ $[9] = pagesContainer;
2463
+ $[10] = selection.from;
2464
+ $[11] = selection.kind;
2465
+ $[12] = selection.pageNumber;
2466
+ $[13] = selection.rId;
2467
+ $[14] = selection.to;
2468
+ $[15] = zoom;
2469
+ $[16] = t3;
2470
+ } else t3 = $[16];
2760
2471
  useEffect(t2, t3);
2761
2472
  if (!pagesContainer) return null;
2762
2473
  let t4;
2763
- if ($[12] !== rangeRects) {
2474
+ if ($[17] !== rangeRects) {
2764
2475
  t4 = rangeRects.map(_temp$8);
2765
- $[12] = rangeRects;
2766
- $[13] = t4;
2767
- } else t4 = $[13];
2476
+ $[17] = rangeRects;
2477
+ $[18] = t4;
2478
+ } else t4 = $[18];
2768
2479
  let t5;
2769
- if ($[14] !== caret) {
2480
+ if ($[19] !== caret) {
2770
2481
  t5 = caret && /* @__PURE__ */ jsx("div", {
2771
2482
  "data-testid": "hf-caret",
2772
2483
  style: {
@@ -2781,39 +2492,39 @@ function HfCaretOverlay(t0) {
2781
2492
  animation: "folio-caret-blink 1060ms steps(1, end) infinite"
2782
2493
  }
2783
2494
  });
2784
- $[14] = caret;
2785
- $[15] = t5;
2786
- } else t5 = $[15];
2495
+ $[19] = caret;
2496
+ $[20] = t5;
2497
+ } else t5 = $[20];
2787
2498
  let t6;
2788
- if ($[16] !== t4 || $[17] !== t5) {
2499
+ if ($[21] !== t4 || $[22] !== t5) {
2789
2500
  t6 = /* @__PURE__ */ jsxs(Fragment, { children: [t4, t5] });
2790
- $[16] = t4;
2791
- $[17] = t5;
2792
- $[18] = t6;
2793
- } else t6 = $[18];
2501
+ $[21] = t4;
2502
+ $[22] = t5;
2503
+ $[23] = t6;
2504
+ } else t6 = $[23];
2794
2505
  let t7;
2795
- if ($[19] !== pagesContainer || $[20] !== t6) {
2506
+ if ($[24] !== pagesContainer || $[25] !== t6) {
2796
2507
  t7 = createPortal(t6, pagesContainer);
2797
- $[19] = pagesContainer;
2798
- $[20] = t6;
2799
- $[21] = t7;
2800
- } else t7 = $[21];
2508
+ $[24] = pagesContainer;
2509
+ $[25] = t6;
2510
+ $[26] = t7;
2511
+ } else t7 = $[26];
2801
2512
  return t7;
2802
2513
  }
2803
- function _temp$8(r_0, i) {
2514
+ function _temp$8(r, i) {
2804
2515
  return /* @__PURE__ */ jsx("div", {
2805
2516
  "data-testid": "hf-selection-rect",
2806
2517
  style: {
2807
2518
  position: "absolute",
2808
- left: r_0.x,
2809
- top: r_0.y,
2810
- width: r_0.width,
2811
- height: r_0.height,
2519
+ left: r.x,
2520
+ top: r.y,
2521
+ width: r.width,
2522
+ height: r.height,
2812
2523
  backgroundColor: "var(--doc-selection, rgba(66, 133, 244, 0.3))",
2813
2524
  pointerEvents: "none",
2814
2525
  zIndex: 10
2815
2526
  }
2816
- }, `hf-sel-${i}-${r_0.x}-${r_0.y}-${r_0.width}`);
2527
+ }, `hf-sel-${i}-${r.x}-${r.y}-${r.width}`);
2817
2528
  }
2818
2529
  function renderHfFromContentOrPm(hf, rId, hfPMs, contentWidth, metrics, options) {
2819
2530
  if (!hf) return;
@@ -3547,87 +3258,17 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3547
3258
  * This uses the browser's text rendering to get precise pixel positions.
3548
3259
  */
3549
3260
  const getCaretFromDom = useCallback((pmPos_0, currentZoom = 1) => {
3550
- if (!pagesContainerRef.current) return null;
3551
- const overlay = pagesContainerRef.current.parentElement?.querySelector("[data-testid=\"selection-overlay\"]");
3261
+ const pagesContainer = pagesContainerRef.current;
3262
+ if (!pagesContainer) return null;
3263
+ const overlay = pagesContainer.parentElement?.querySelector("[data-testid=\"selection-overlay\"]");
3552
3264
  if (!overlay) return null;
3553
- const overlayRect = overlay.getBoundingClientRect();
3554
- const spans = findBodyPmSpans(pagesContainerRef.current);
3555
- const collapsedTarget = findCollapsedLineEdgeCaretTarget(spans, pmPos_0);
3556
- if (collapsedTarget) return {
3557
- x: (collapsedTarget.geometry.left - overlayRect.left) / currentZoom,
3558
- y: (collapsedTarget.geometry.top - overlayRect.top) / currentZoom,
3559
- height: getLineHeight(collapsedTarget.span),
3560
- pageIndex: getPageIndex(collapsedTarget.span)
3561
- };
3562
- for (const spanEl of spans) {
3563
- const pmStart = Number(spanEl.dataset["pmStart"]);
3564
- const pmEnd = Number(spanEl.dataset["pmEnd"]);
3565
- if (spanEl.classList.contains("layout-run-tab")) {
3566
- if (pmPos_0 >= pmStart && pmPos_0 < pmEnd) {
3567
- const spanRect = spanEl.getBoundingClientRect();
3568
- return {
3569
- x: (spanRect.left - overlayRect.left) / currentZoom,
3570
- y: (spanRect.top - overlayRect.top) / currentZoom,
3571
- height: getLineHeight(spanEl),
3572
- pageIndex: getPageIndex(spanEl)
3573
- };
3574
- }
3575
- continue;
3576
- }
3577
- if (spanEl.classList.contains("layout-empty-run") && pmPos_0 >= pmStart && pmPos_0 <= pmEnd) {
3578
- const spanRect_0 = spanEl.getBoundingClientRect();
3579
- return {
3580
- x: (spanRect_0.left - overlayRect.left) / currentZoom,
3581
- y: (spanRect_0.top - overlayRect.top) / currentZoom,
3582
- height: getLineHeight(spanEl, Math.max(16, spanRect_0.height)),
3583
- pageIndex: getPageIndex(spanEl)
3584
- };
3585
- }
3586
- if (pmPos_0 >= pmStart && pmPos_0 <= pmEnd && spanEl.firstChild?.nodeType === Node.TEXT_NODE) {
3587
- const textNode = spanEl.firstChild;
3588
- const charIndex = Math.min(pmPos_0 - pmStart, textNode.length);
3589
- const range = spanEl.ownerDocument.createRange();
3590
- range.setStart(textNode, charIndex);
3591
- range.setEnd(textNode, charIndex);
3592
- const rangeRect = range.getBoundingClientRect();
3593
- const spanRect_1 = spanEl.getBoundingClientRect();
3594
- const useSpanStart = charIndex === 0 || rangeRect.width === 0 && rangeRect.left < spanRect_1.left;
3595
- const caretLeft = useSpanStart ? spanRect_1.left : rangeRect.left;
3596
- const caretTop = rangeRect.height > 0 && !useSpanStart ? rangeRect.top : spanRect_1.top;
3597
- return {
3598
- x: (caretLeft - overlayRect.left) / currentZoom,
3599
- y: (caretTop - overlayRect.top) / currentZoom,
3600
- height: getLineHeight(spanEl),
3601
- pageIndex: getPageIndex(spanEl)
3602
- };
3603
- }
3604
- if (pmPos_0 >= pmStart && pmPos_0 <= pmEnd) {
3605
- const spanRect_2 = spanEl.getBoundingClientRect();
3606
- return {
3607
- x: (spanRect_2.left - overlayRect.left) / currentZoom,
3608
- y: (spanRect_2.top - overlayRect.top) / currentZoom,
3609
- height: getLineHeight(spanEl, Math.max(16, spanRect_2.height)),
3610
- pageIndex: getPageIndex(spanEl)
3611
- };
3612
- }
3613
- }
3614
- const emptyRuns = findBodyEmptyRuns(pagesContainerRef.current);
3615
- for (const emptyRun of emptyRuns) {
3616
- const paragraph = closestHtmlElement(emptyRun, ".layout-paragraph");
3617
- if (!paragraph) continue;
3618
- const pmStart_0 = Number(paragraph.dataset["pmStart"]);
3619
- const pmEnd_0 = Number(paragraph.dataset["pmEnd"]);
3620
- if (pmPos_0 >= pmStart_0 && pmPos_0 <= pmEnd_0) {
3621
- const runRect = emptyRun.getBoundingClientRect();
3622
- return {
3623
- x: (runRect.left - overlayRect.left) / currentZoom,
3624
- y: (runRect.top - overlayRect.top) / currentZoom,
3625
- height: getLineHeight(emptyRun),
3626
- pageIndex: getPageIndex(paragraph)
3627
- };
3628
- }
3629
- }
3630
- return null;
3265
+ const caret = getCaretPositionFromDom(pagesContainer, pmPos_0, overlay.getBoundingClientRect());
3266
+ return caret ? {
3267
+ x: caret.x / currentZoom,
3268
+ y: caret.y / currentZoom,
3269
+ height: caret.height / currentZoom,
3270
+ pageIndex: caret.pageIndex
3271
+ } : null;
3631
3272
  }, []);
3632
3273
  const refreshBodyImeCaretAnchor = useCallback(() => {
3633
3274
  const view_1 = hiddenPMRef.current?.getView();
@@ -3726,12 +3367,12 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3726
3367
  setCaretPosition(null);
3727
3368
  loadSelectionGeometry().then(({ getCaretPosition }) => {
3728
3369
  if (!isCurrentRequest()) return;
3729
- const caret = getCaretPosition(layout, blocks, measures, from);
3730
- if (caret) {
3370
+ const caret_0 = getCaretPosition(layout, blocks, measures, from);
3371
+ if (caret_0) {
3731
3372
  const fallbackCaret = {
3732
- ...caret,
3733
- x: caret.x + (pageRect.left - overlayRect_2.left) / zoom,
3734
- y: caret.y + (pageRect.top - overlayRect_2.top) / zoom
3373
+ ...caret_0,
3374
+ x: caret_0.x + (pageRect.left - overlayRect_2.left) / zoom,
3375
+ y: caret_0.y + (pageRect.top - overlayRect_2.top) / zoom
3735
3376
  };
3736
3377
  setCaretPosition(fallbackCaret);
3737
3378
  syncImeCaretAnchor({
@@ -3779,45 +3420,13 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3779
3420
  const overlay_3 = pagesContainerRef.current?.parentElement?.querySelector("[data-testid=\"selection-overlay\"]");
3780
3421
  if (overlay_3 && pagesContainerRef.current) {
3781
3422
  const overlayRect_3 = overlay_3.getBoundingClientRect();
3782
- const domRects = [];
3783
- const spans_0 = findBodyPmSpans(pagesContainerRef.current);
3784
- for (const spanEl_0 of spans_0) {
3785
- const pmStart_1 = Number(spanEl_0.dataset["pmStart"]);
3786
- if (Number(spanEl_0.dataset["pmEnd"]) > from && pmStart_1 < to) {
3787
- if (spanEl_0.classList.contains("layout-run-tab")) {
3788
- const spanRect_3 = spanEl_0.getBoundingClientRect();
3789
- domRects.push({
3790
- x: (spanRect_3.left - overlayRect_3.left) / zoom,
3791
- y: (spanRect_3.top - overlayRect_3.top) / zoom,
3792
- width: spanRect_3.width / zoom,
3793
- height: spanRect_3.height / zoom,
3794
- pageIndex: getPageIndex(spanEl_0)
3795
- });
3796
- continue;
3797
- }
3798
- let textNode_0 = null;
3799
- if (spanEl_0.firstChild?.nodeType === Node.TEXT_NODE) textNode_0 = spanEl_0.firstChild;
3800
- else if (spanEl_0.firstChild instanceof HTMLElement && spanEl_0.firstChild.tagName === "A" && spanEl_0.firstChild.firstChild?.nodeType === Node.TEXT_NODE) textNode_0 = spanEl_0.firstChild.firstChild;
3801
- if (!textNode_0) continue;
3802
- const ownerDoc_0 = spanEl_0.ownerDocument;
3803
- const startChar = Math.max(0, from - pmStart_1);
3804
- const endChar = Math.min(textNode_0.length, to - pmStart_1);
3805
- if (startChar < endChar) {
3806
- const range_0 = ownerDoc_0.createRange();
3807
- range_0.setStart(textNode_0, startChar);
3808
- range_0.setEnd(textNode_0, endChar);
3809
- const clientRects = range_0.getClientRects();
3810
- const pageIndex = getPageIndex(spanEl_0);
3811
- for (const rect_0 of Array.from(clientRects)) domRects.push({
3812
- x: (rect_0.left - overlayRect_3.left) / zoom,
3813
- y: (rect_0.top - overlayRect_3.top) / zoom,
3814
- width: rect_0.width / zoom,
3815
- height: rect_0.height / zoom,
3816
- pageIndex
3817
- });
3818
- }
3819
- }
3820
- }
3423
+ const domRects = getSelectionRectsFromDom(pagesContainerRef.current, from, to, overlayRect_3).map((rect_0) => ({
3424
+ pageIndex: rect_0.pageIndex,
3425
+ x: rect_0.x / zoom,
3426
+ y: rect_0.y / zoom,
3427
+ width: rect_0.width / zoom,
3428
+ height: rect_0.height / zoom
3429
+ }));
3821
3430
  if (domRects.length > 0) setSelectionRects(domRects);
3822
3431
  else {
3823
3432
  const firstPage_0 = pagesContainerRef.current.querySelector(".layout-page");
@@ -3854,6 +3463,24 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3854
3463
  ]);
3855
3464
  const updateSelectionOverlayRef = useRef(updateSelectionOverlay);
3856
3465
  updateSelectionOverlayRef.current = updateSelectionOverlay;
3466
+ useEffect(() => {
3467
+ const pagesContainer_0 = pagesContainerRef.current;
3468
+ if (!pagesContainer_0) return void 0;
3469
+ let animationFrame = null;
3470
+ const repaintSelection = () => {
3471
+ if (animationFrame !== null) cancelAnimationFrame(animationFrame);
3472
+ animationFrame = requestAnimationFrame(() => {
3473
+ animationFrame = null;
3474
+ const state_4 = hiddenPMRef.current?.getState();
3475
+ if (state_4) updateSelectionOverlayRef.current(state_4);
3476
+ });
3477
+ };
3478
+ pagesContainer_0.addEventListener(PAINTER_PAINTED_EVENT, repaintSelection);
3479
+ return () => {
3480
+ if (animationFrame !== null) cancelAnimationFrame(animationFrame);
3481
+ pagesContainer_0.removeEventListener(PAINTER_PAINTED_EVENT, repaintSelection);
3482
+ };
3483
+ }, []);
3857
3484
  const updateAnonymizationOverlay = useCallback(() => {
3858
3485
  const isCurrentRequest_0 = anonymizationOverlayRequestGate.begin();
3859
3486
  const matches = anonymizationMatchesRef.current;
@@ -3861,13 +3488,13 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3861
3488
  setAnonymizationRectGroups([]);
3862
3489
  return;
3863
3490
  }
3864
- const pagesContainer = pagesContainerRef.current;
3865
- if (!pagesContainer) {
3491
+ const pagesContainer_1 = pagesContainerRef.current;
3492
+ if (!pagesContainer_1) {
3866
3493
  setAnonymizationRectGroups([]);
3867
3494
  return;
3868
3495
  }
3869
- const overlay_4 = pagesContainer.parentElement?.querySelector("[data-testid=\"selection-overlay\"]");
3870
- const firstPage_1 = pagesContainer.querySelector(".layout-page");
3496
+ const overlay_4 = pagesContainer_1.parentElement?.querySelector("[data-testid=\"selection-overlay\"]");
3497
+ const firstPage_1 = pagesContainer_1.querySelector(".layout-page");
3871
3498
  if (!overlay_4 || !firstPage_1) {
3872
3499
  setAnonymizationRectGroups([]);
3873
3500
  return;
@@ -3876,42 +3503,21 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3876
3503
  const pageRect_1 = firstPage_1.getBoundingClientRect();
3877
3504
  const pageOffsetX_0 = (pageRect_1.left - overlayRect_4.left) / zoom;
3878
3505
  const pageOffsetY_0 = (pageRect_1.top - overlayRect_4.top) / zoom;
3879
- const pmSpans = findBodyPmSpans(pagesContainer);
3506
+ const pmSpans = findBodyPmSpans(pagesContainer_1);
3880
3507
  const layoutFallbackMatches = [];
3881
3508
  const rectsForMatch = (match, from_0, to_0) => {
3882
3509
  const domRects_0 = [];
3883
- for (const spanEl_1 of pmSpans) {
3884
- const pmStart_2 = Number(spanEl_1.dataset["pmStart"]);
3885
- if (!(Number(spanEl_1.dataset["pmEnd"]) > from_0 && pmStart_2 < to_0)) continue;
3886
- if (spanEl_1.classList.contains("layout-run-tab")) {
3887
- const spanRect_4 = spanEl_1.getBoundingClientRect();
3888
- domRects_0.push({
3889
- x: (spanRect_4.left - overlayRect_4.left) / zoom,
3890
- y: (spanRect_4.top - overlayRect_4.top) / zoom,
3891
- width: spanRect_4.width / zoom,
3892
- height: spanRect_4.height / zoom,
3893
- pageIndex: getPageIndex(spanEl_1)
3894
- });
3895
- continue;
3896
- }
3897
- let textNode_1 = null;
3898
- if (spanEl_1.firstChild?.nodeType === Node.TEXT_NODE) textNode_1 = spanEl_1.firstChild;
3899
- else if (spanEl_1.firstChild instanceof HTMLElement && spanEl_1.firstChild.tagName === "A" && spanEl_1.firstChild.firstChild?.nodeType === Node.TEXT_NODE) textNode_1 = spanEl_1.firstChild.firstChild;
3900
- if (!textNode_1) continue;
3901
- const ownerDoc_1 = spanEl_1.ownerDocument;
3902
- const startChar_0 = Math.max(0, from_0 - pmStart_2);
3903
- const endChar_0 = Math.min(textNode_1.length, to_0 - pmStart_2);
3904
- if (startChar_0 >= endChar_0) continue;
3905
- const range_1 = ownerDoc_1.createRange();
3906
- range_1.setStart(textNode_1, startChar_0);
3907
- range_1.setEnd(textNode_1, endChar_0);
3908
- const pageIndex_0 = getPageIndex(spanEl_1);
3909
- for (const rect_2 of Array.from(range_1.getClientRects())) domRects_0.push({
3510
+ for (const spanEl of pmSpans) {
3511
+ const measurement = measurePaintedSpanRange(spanEl, from_0, to_0);
3512
+ if (measurement.type === "outside") continue;
3513
+ const pageIndex = getPageIndex(spanEl);
3514
+ const measuredRects = measurement.type === "element" ? [measurement.rect] : measurement.rects;
3515
+ for (const rect_2 of measuredRects) domRects_0.push({
3910
3516
  x: (rect_2.left - overlayRect_4.left) / zoom,
3911
3517
  y: (rect_2.top - overlayRect_4.top) / zoom,
3912
3518
  width: rect_2.width / zoom,
3913
3519
  height: rect_2.height / zoom,
3914
- pageIndex: pageIndex_0
3520
+ pageIndex
3915
3521
  });
3916
3522
  }
3917
3523
  if (domRects_0.length > 0) return domRects_0;
@@ -3970,15 +3576,15 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3970
3576
  const anchor_0 = current.anchor;
3971
3577
  loadSelectionGeometry().then(({ getCaretPosition: getCaretPosition_0 }) => {
3972
3578
  if (!isCurrentRequest_1()) return;
3973
- const caret_0 = getCaretPosition_0(layout, blocks, measures, anchor_0);
3579
+ const caret_1 = getCaretPosition_0(layout, blocks, measures, anchor_0);
3974
3580
  if (!isCurrentRequest_1()) return;
3975
- if (caret_0 === null) {
3581
+ if (caret_1 === null) {
3976
3582
  setAutocompleteCaret(null);
3977
3583
  return;
3978
3584
  }
3979
- const pagesContainer_0 = pagesContainerRef.current;
3980
- const overlay_5 = pagesContainer_0?.parentElement?.querySelector("[data-testid=\"selection-overlay\"]");
3981
- const firstPage_2 = pagesContainer_0?.querySelector(".layout-page");
3585
+ const pagesContainer_2 = pagesContainerRef.current;
3586
+ const overlay_5 = pagesContainer_2?.parentElement?.querySelector("[data-testid=\"selection-overlay\"]");
3587
+ const firstPage_2 = pagesContainer_2?.querySelector(".layout-page");
3982
3588
  if (!overlay_5 || !firstPage_2) {
3983
3589
  setAutocompleteCaret(null);
3984
3590
  return;
@@ -3988,12 +3594,12 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
3988
3594
  const pageOffsetX_1 = (pageRect_2.left - overlayRect_5.left) / zoom;
3989
3595
  const pageOffsetY_1 = (pageRect_2.top - overlayRect_5.top) / zoom;
3990
3596
  if (!isCurrentRequest_1()) return;
3991
- const caretPage = layout.pages.at(caret_0.pageIndex);
3992
- const maxWidth = caretPage ? Math.max(AUTOCOMPLETE_GHOST_MIN_WIDTH, caretPage.size.w - caretPage.margins.right - caret_0.x) : void 0;
3597
+ const caretPage = layout.pages.at(caret_1.pageIndex);
3598
+ const maxWidth = caretPage ? Math.max(AUTOCOMPLETE_GHOST_MIN_WIDTH, caretPage.size.w - caretPage.margins.right - caret_1.x) : void 0;
3993
3599
  setAutocompleteCaret({
3994
- x: caret_0.x + pageOffsetX_1,
3995
- y: caret_0.y + pageOffsetY_1,
3996
- lineHeight: caret_0.height,
3600
+ x: caret_1.x + pageOffsetX_1,
3601
+ y: caret_1.y + pageOffsetY_1,
3602
+ lineHeight: caret_1.height,
3997
3603
  maxWidth
3998
3604
  });
3999
3605
  setAutocompleteText(current.text);
@@ -4009,17 +3615,17 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4009
3615
  const updateDirectivesOverlay = useCallback(() => {
4010
3616
  const isCurrentRequest_2 = directivesOverlayRequestGate.begin();
4011
3617
  const previewedStarts = new Set(templatePreviewRef.current.entries.map((entry) => entry.from));
4012
- const ranges = directivesRef.current.filter((range_2) => !(range_2.kind === "placeholder" && previewedStarts.has(range_2.from)));
4013
- const pagesContainer_1 = pagesContainerRef.current;
4014
- if (ranges.length === 0 || !pagesContainer_1) {
3618
+ const ranges = directivesRef.current.filter((range) => !(range.kind === "placeholder" && previewedStarts.has(range.from)));
3619
+ const pagesContainer_3 = pagesContainerRef.current;
3620
+ if (ranges.length === 0 || !pagesContainer_3) {
4015
3621
  setDirectiveRectGroups([]);
4016
3622
  setDirectiveGutter(null);
4017
3623
  return;
4018
3624
  }
4019
- setDirectiveGutter(measureDirectiveGutter(pagesContainer_1, zoom));
3625
+ setDirectiveGutter(measureDirectiveGutter(pagesContainer_3, zoom));
4020
3626
  (async () => {
4021
3627
  const projected = await projectRangesToRects(ranges, {
4022
- pagesContainer: pagesContainer_1,
3628
+ pagesContainer: pagesContainer_3,
4023
3629
  zoom,
4024
3630
  layout,
4025
3631
  blocks,
@@ -4038,8 +3644,8 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4038
3644
  const isCurrentRequest_3 = aiSuggestionsOverlayRequestGate.begin();
4039
3645
  const { suggestions, focusedId } = aiSuggestionsRef.current;
4040
3646
  const pending = suggestions.filter((suggestion) => suggestion.status === "pending" && suggestion.range.from >= 0 && suggestion.range.to > suggestion.range.from);
4041
- const pagesContainer_2 = pagesContainerRef.current;
4042
- if (pending.length === 0 || !pagesContainer_2) {
3647
+ const pagesContainer_4 = pagesContainerRef.current;
3648
+ if (pending.length === 0 || !pagesContainer_4) {
4043
3649
  setAiSuggestionRectGroups((prev) => prev.length === 0 ? prev : []);
4044
3650
  return;
4045
3651
  }
@@ -4049,20 +3655,20 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4049
3655
  to: suggestion_0.range.to,
4050
3656
  suggestion: suggestion_0
4051
3657
  })), {
4052
- pagesContainer: pagesContainer_2,
3658
+ pagesContainer: pagesContainer_4,
4053
3659
  zoom,
4054
3660
  layout,
4055
3661
  blocks,
4056
3662
  measures
4057
3663
  });
4058
3664
  if (!isCurrentRequest_3()) return;
4059
- const focused = projected_0.find(({ range: range_3 }) => range_3.suggestion.id === focusedId);
4060
- const fonts = collectRunFontsAtPmPositions(pagesContainer_2, focused ? [focused.range.from] : []);
4061
- setAiSuggestionRectGroups(projected_0.map(({ range: range_4, rects: rects_2 }) => ({
4062
- suggestion: range_4.suggestion,
3665
+ const focused = projected_0.find(({ range: range_0 }) => range_0.suggestion.id === focusedId);
3666
+ const fonts = collectRunFontsAtPmPositions(pagesContainer_4, focused ? [focused.range.from] : []);
3667
+ setAiSuggestionRectGroups(projected_0.map(({ range: range_1, rects: rects_2 }) => ({
3668
+ suggestion: range_1.suggestion,
4063
3669
  rects: rects_2,
4064
- focused: range_4.suggestion.id === focusedId,
4065
- font: range_4.suggestion.id === focusedId ? fonts.get(range_4.from) ?? null : null
3670
+ focused: range_1.suggestion.id === focusedId,
3671
+ font: range_1.suggestion.id === focusedId ? fonts.get(range_1.from) ?? null : null
4066
3672
  })));
4067
3673
  })();
4068
3674
  }, [
@@ -4074,15 +3680,15 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4074
3680
  ]);
4075
3681
  const updatePassageHighlightOverlay = useCallback(() => {
4076
3682
  const isCurrentRequest_4 = passageHighlightOverlayRequestGate.begin();
4077
- const range_5 = passageHighlightRangeRef.current;
4078
- const pagesContainer_3 = pagesContainerRef.current;
4079
- if (range_5 === null || !pagesContainer_3) {
3683
+ const range_2 = passageHighlightRangeRef.current;
3684
+ const pagesContainer_5 = pagesContainerRef.current;
3685
+ if (range_2 === null || !pagesContainer_5) {
4080
3686
  setPassageHighlightRects((prev_0) => prev_0.length === 0 ? prev_0 : []);
4081
3687
  return;
4082
3688
  }
4083
3689
  (async () => {
4084
- const projected_1 = await projectRangesToRects([range_5], {
4085
- pagesContainer: pagesContainer_3,
3690
+ const projected_1 = await projectRangesToRects([range_2], {
3691
+ pagesContainer: pagesContainer_5,
4086
3692
  zoom,
4087
3693
  layout,
4088
3694
  blocks,
@@ -4098,16 +3704,16 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4098
3704
  measures,
4099
3705
  zoom
4100
3706
  ]);
4101
- const setPassageHighlightImpl = useCallback((range_6) => {
4102
- passageHighlightRangeRef.current = range_6;
4103
- if (range_6 === null) {
3707
+ const setPassageHighlightImpl = useCallback((range_3) => {
3708
+ passageHighlightRangeRef.current = range_3;
3709
+ if (range_3 === null) {
4104
3710
  passageHighlightOverlayRequestGate.invalidate();
4105
3711
  setPassageHighlightRects((prev_1) => prev_1.length === 0 ? prev_1 : []);
4106
3712
  return;
4107
3713
  }
4108
3714
  updatePassageHighlightOverlay();
4109
3715
  }, [passageHighlightOverlayRequestGate, updatePassageHighlightOverlay]);
4110
- const hideSelectionOverlayDuringInput = useCallback((state_4) => {
3716
+ const hideSelectionOverlayDuringInput = useCallback((state_5) => {
4111
3717
  selectionOverlayRequestGate.invalidate();
4112
3718
  suppressSelectionOverlayRef.current = true;
4113
3719
  setCaretPosition(null);
@@ -4116,7 +3722,7 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4116
3722
  revealSelectionOverlayTimerRef.current = window.setTimeout(() => {
4117
3723
  revealSelectionOverlayTimerRef.current = null;
4118
3724
  suppressSelectionOverlayRef.current = false;
4119
- updateSelectionOverlay(hiddenPMRef.current?.getState() ?? state_4);
3725
+ updateSelectionOverlay(hiddenPMRef.current?.getState() ?? state_5);
4120
3726
  }, SELECTION_REVEAL_AFTER_INPUT_DELAY);
4121
3727
  }, [selectionOverlayRequestGate, updateSelectionOverlay]);
4122
3728
  useEffect(() => () => {
@@ -4141,7 +3747,7 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4141
3747
  /**
4142
3748
  * Handle PM transaction - re-layout on content/selection change.
4143
3749
  */
4144
- const handleTransaction = useCallback((transaction, newState) => {
3750
+ const handleTransaction = useCallback(({ transactions, newState, docChanged }) => {
4145
3751
  const nextMatches = anonymizationDecorationsKey.getState(newState)?.matches ?? [];
4146
3752
  const matchesChanged = nextMatches !== anonymizationMatchesRef.current;
4147
3753
  anonymizationMatchesRef.current = nextMatches;
@@ -4154,7 +3760,7 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4154
3760
  const nextDirectives = templateDirectivesKey.getState(newState)?.ranges ?? [];
4155
3761
  if (nextDirectives !== directivesRef.current) {
4156
3762
  directivesRef.current = nextDirectives;
4157
- if (!transaction.docChanged) updateDirectivesOverlay();
3763
+ if (!docChanged) updateDirectivesOverlay();
4158
3764
  }
4159
3765
  const nextPreviewState = templatePreviewValuesKey.getState(newState);
4160
3766
  const nextPreviewEntries = nextPreviewState?.entries ?? EMPTY_TEMPLATE_PREVIEW_ENTRIES;
@@ -4165,7 +3771,7 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4165
3771
  entries: nextPreviewEntries,
4166
3772
  mode: nextPreviewMode
4167
3773
  };
4168
- if (!transaction.docChanged) if (nextPreviewMode !== previousPreview.mode) scheduleLayout(newState, null);
3774
+ if (!docChanged) if (nextPreviewMode !== previousPreview.mode) scheduleLayout(newState, null);
4169
3775
  else {
4170
3776
  const previewDirty = templatePreviewDirtyRange(previousPreview.entries, nextPreviewEntries);
4171
3777
  if (previewDirty) scheduleLayout(newState, previewDirty);
@@ -4179,9 +3785,9 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4179
3785
  suggestions: nextAiSuggestions,
4180
3786
  focusedId: nextAiFocusedId
4181
3787
  };
4182
- if (!transaction.docChanged) updateAISuggestionsOverlay();
3788
+ if (!docChanged) updateAISuggestionsOverlay();
4183
3789
  }
4184
- if (transaction.docChanged) {
3790
+ if (docChanged) {
4185
3791
  syncCoordinator.incrementStateSeq();
4186
3792
  if (passageHighlightRangeRef.current !== null) {
4187
3793
  passageHighlightRangeRef.current = null;
@@ -4189,11 +3795,11 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4189
3795
  setPassageHighlightRects((prev_2) => prev_2.length === 0 ? prev_2 : []);
4190
3796
  }
4191
3797
  hideSelectionOverlayDuringInput(newState);
4192
- scheduleLayout(newState, getTransactionDirtyRange(transaction));
3798
+ scheduleLayout(newState, getTransactionsDirtyRange(transactions));
4193
3799
  scheduleDocumentChangeNotification();
4194
3800
  }
4195
3801
  syncCoordinator.requestRender();
4196
- if (!transaction.docChanged) updateSelectionOverlay(newState);
3802
+ if (!docChanged) updateSelectionOverlay(newState);
4197
3803
  }, [
4198
3804
  scheduleLayout,
4199
3805
  scheduleDocumentChangeNotification,
@@ -4223,8 +3829,8 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4223
3829
  */
4224
3830
  const [hfCaretSelection, setHfCaretSelection] = useState(null);
4225
3831
  const activeHfPageNumberRef = useRef(null);
4226
- const handleHfPmTransaction = useCallback((rId, kind, view_2, docChanged, selectionChanged) => {
4227
- if (docChanged) {
3832
+ const handleHfPmTransaction = useCallback((rId, kind, view_2, docChanged_0, selectionChanged) => {
3833
+ if (docChanged_0) {
4228
3834
  let bodyState = hiddenPMRef.current?.getState();
4229
3835
  if (!bodyState && precomputedInitialStateRef.current) bodyState = precomputedInitialStateRef.current;
4230
3836
  if (!bodyState) {
@@ -4233,7 +3839,7 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4233
3839
  }
4234
3840
  if (bodyState) scheduleLayout(bodyState, null);
4235
3841
  }
4236
- if (docChanged || selectionChanged) {
3842
+ if (docChanged_0 || selectionChanged) {
4237
3843
  const { from: from_1, to: to_1 } = view_2.state.selection;
4238
3844
  const pageNumber = activeHfPageNumberRef.current;
4239
3845
  setHfCaretSelection({
@@ -4250,12 +3856,12 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4250
3856
  });
4251
3857
  }
4252
3858
  }, [scheduleLayout, ensureHiddenEditorView]);
4253
- const handleNoteStoryTransaction = useCallback((view_3, docChanged_0, selectionChanged_0) => {
4254
- if (docChanged_0) {
3859
+ const handleNoteStoryTransaction = useCallback((view_3, docChanged_1, selectionChanged_0) => {
3860
+ if (docChanged_1) {
4255
3861
  const bodyState_0 = hiddenPMRef.current?.getState();
4256
3862
  if (bodyState_0) scheduleLayout(bodyState_0, null);
4257
3863
  }
4258
- if (docChanged_0 || selectionChanged_0) {
3864
+ if (docChanged_1 || selectionChanged_0) {
4259
3865
  const { from: from_2, to: to_2 } = view_3.state.selection;
4260
3866
  onSelectionChangeRef.current?.(from_2, to_2);
4261
3867
  folioEmitterRef.current.emit("selectionChange", {
@@ -4277,12 +3883,12 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4277
3883
  /**
4278
3884
  * Handle selection change from PM.
4279
3885
  */
4280
- const handleSelectionChange = useCallback((state_5) => {
4281
- const { selection: selection_2 } = state_5;
3886
+ const handleSelectionChange = useCallback((state_6) => {
3887
+ const { selection: selection_2 } = state_6;
4282
3888
  if (selection_2 instanceof NodeSelection && selection_2.node.type.name === "image") {
4283
3889
  setSelectionRects([]);
4284
3890
  setCaretPosition(null);
4285
- } else if (syncCoordinator.isSafeToRender()) updateSelectionOverlay(state_5);
3891
+ } else if (syncCoordinator.isSafeToRender()) updateSelectionOverlay(state_6);
4286
3892
  requestAnimationFrame(() => {
4287
3893
  const view_4 = hiddenPMRef.current?.getView();
4288
3894
  if (!view_4) {
@@ -4404,9 +4010,9 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4404
4010
  });
4405
4011
  }, [layout, scrollToPositionImpl]);
4406
4012
  const scrollToParaIdImpl = useCallback((paraId, options_2) => {
4407
- const state_6 = hiddenPMRef.current?.getState();
4408
- if (!state_6) return false;
4409
- const startPos = findStartPosForParaId(state_6.doc, paraId);
4013
+ const state_7 = hiddenPMRef.current?.getState();
4014
+ if (!state_7) return false;
4015
+ const startPos = findStartPosForParaId(state_7.doc, paraId);
4410
4016
  if (startPos == null || startPos < 0) return false;
4411
4017
  scrollToPositionImpl(startPos);
4412
4018
  requestAnimationFrame(() => {
@@ -4414,8 +4020,8 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4414
4020
  const pages = pagesContainerRef.current;
4415
4021
  if (pages) flashParagraphFragmentsByParaId(pages, paraId, options_2.highlight);
4416
4022
  }
4417
- const targetNode = state_6.doc.nodeAt(startPos);
4418
- const inner = targetNode?.isTextblock === true ? Math.min(startPos + 1 + targetNode.content.size, state_6.doc.content.size) : Math.min(startPos + 1, state_6.doc.content.size);
4023
+ const targetNode = state_7.doc.nodeAt(startPos);
4024
+ const inner = targetNode?.isTextblock === true ? Math.min(startPos + 1 + targetNode.content.size, state_7.doc.content.size) : Math.min(startPos + 1, state_7.doc.content.size);
4419
4025
  hiddenPMRef.current?.setSelection(inner);
4420
4026
  hiddenPMRef.current?.focus();
4421
4027
  });
@@ -4670,9 +4276,9 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4670
4276
  if (!readOnly && imageEl) {
4671
4277
  e.preventDefault();
4672
4278
  e.stopPropagation();
4673
- const pmStart_3 = imageEl.dataset["pmStart"];
4674
- if (pmStart_3 !== void 0) {
4675
- const pos_1 = Number.parseInt(pmStart_3, 10);
4279
+ const pmStart = imageEl.dataset["pmStart"];
4280
+ if (pmStart !== void 0) {
4281
+ const pos_1 = Number.parseInt(pmStart, 10);
4676
4282
  const hfSlot = hfEditMode ? findHfSlotForTarget(imageEl) : null;
4677
4283
  const hfView_0 = hfSlot ? hfPMsRef.current?.getView(hfSlot.rId) : null;
4678
4284
  if (hfView_0) {
@@ -4856,10 +4462,10 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4856
4462
  const view_12 = (resizingHfSurfaceRef.current ? hfPMsRef.current?.getView(resizingHfSurfaceRef.current.rId) : hiddenPMRef.current?.getView()) ?? null;
4857
4463
  resizingHfSurfaceRef.current = null;
4858
4464
  if (view_12) {
4859
- const pmStart_4 = resizeTablePmStartRef.current;
4465
+ const pmStart_0 = resizeTablePmStartRef.current;
4860
4466
  const colIdx = resizeColumnIndexRef.current;
4861
4467
  const { left: newLeft, right: newRight } = resizeOrigWidthsRef.current;
4862
- const $pos_5 = view_12.state.doc.resolve(pmStart_4 + 1);
4468
+ const $pos_5 = view_12.state.doc.resolve(pmStart_0 + 1);
4863
4469
  for (let d_3 = $pos_5.depth; d_3 >= 0; d_3--) {
4864
4470
  const node_4 = $pos_5.node(d_3);
4865
4471
  if (node_4.type.name === "table") {
@@ -4906,10 +4512,10 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4906
4512
  const view_13 = (resizingHfSurfaceRef.current ? hfPMsRef.current?.getView(resizingHfSurfaceRef.current.rId) : hiddenPMRef.current?.getView()) ?? null;
4907
4513
  resizingHfSurfaceRef.current = null;
4908
4514
  if (view_13) {
4909
- const pmStart_5 = resizeRowTablePmStartRef.current;
4515
+ const pmStart_1 = resizeRowTablePmStartRef.current;
4910
4516
  const rowIdx = resizeRowIndexRef.current;
4911
4517
  const newHeight_0 = resizeRowOrigHeightRef.current;
4912
- const $pos_6 = view_13.state.doc.resolve(pmStart_5 + 1);
4518
+ const $pos_6 = view_13.state.doc.resolve(pmStart_1 + 1);
4913
4519
  for (let d_4 = $pos_6.depth; d_4 >= 0; d_4--) {
4914
4520
  const node_5 = $pos_6.node(d_4);
4915
4521
  if (node_5.type.name === "table") {
@@ -4941,10 +4547,10 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
4941
4547
  const view_14 = (resizingHfSurfaceRef.current ? hfPMsRef.current?.getView(resizingHfSurfaceRef.current.rId) : hiddenPMRef.current?.getView()) ?? null;
4942
4548
  resizingHfSurfaceRef.current = null;
4943
4549
  if (view_14) {
4944
- const pmStart_6 = resizeRightEdgePmStartRef.current;
4550
+ const pmStart_2 = resizeRightEdgePmStartRef.current;
4945
4551
  const colIdx_0 = resizeRightEdgeColIndexRef.current;
4946
4552
  const newWidth_1 = resizeRightEdgeOrigWidthRef.current;
4947
- const $pos_7 = view_14.state.doc.resolve(pmStart_6 + 1);
4553
+ const $pos_7 = view_14.state.doc.resolve(pmStart_2 + 1);
4948
4554
  for (let d_5 = $pos_7.depth; d_5 >= 0; d_5--) {
4949
4555
  const node_6 = $pos_7.node(d_5);
4950
4556
  if (node_6.type.name === "table") {
@@ -5808,8 +5414,8 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
5808
5414
  const container = containerRef.current;
5809
5415
  if (!container) return;
5810
5416
  const observer = new ResizeObserver(() => {
5811
- const state_7 = hiddenPMRef.current?.getState();
5812
- if (state_7) updateSelectionOverlay(state_7);
5417
+ const state_8 = hiddenPMRef.current?.getState();
5418
+ if (state_8) updateSelectionOverlay(state_8);
5813
5419
  });
5814
5420
  observer.observe(container);
5815
5421
  return () => observer.disconnect();
@@ -5817,11 +5423,11 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
5817
5423
  useEffect(() => {
5818
5424
  const hiddenHost = hiddenPMRef.current?.getHostElement();
5819
5425
  if (!hiddenHost) return;
5820
- let animationFrame = null;
5426
+ let animationFrame_0 = null;
5821
5427
  const scheduleRefresh = () => {
5822
- if (animationFrame !== null) cancelAnimationFrame(animationFrame);
5823
- animationFrame = requestAnimationFrame(() => {
5824
- animationFrame = null;
5428
+ if (animationFrame_0 !== null) cancelAnimationFrame(animationFrame_0);
5429
+ animationFrame_0 = requestAnimationFrame(() => {
5430
+ animationFrame_0 = null;
5825
5431
  refreshBodyImeCaretAnchor();
5826
5432
  });
5827
5433
  };
@@ -5830,7 +5436,7 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
5830
5436
  hiddenHost.addEventListener("compositionend", scheduleRefresh);
5831
5437
  window.addEventListener("scroll", scheduleRefresh, true);
5832
5438
  return () => {
5833
- if (animationFrame !== null) cancelAnimationFrame(animationFrame);
5439
+ if (animationFrame_0 !== null) cancelAnimationFrame(animationFrame_0);
5834
5440
  hiddenHost.removeEventListener("focusin", refreshOnFocus);
5835
5441
  hiddenHost.removeEventListener("compositionend", scheduleRefresh);
5836
5442
  window.removeEventListener("scroll", scheduleRefresh, true);
@@ -5946,33 +5552,33 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
5946
5552
  return bestNumber;
5947
5553
  },
5948
5554
  getBlockRect(blockId) {
5949
- const state_8 = folioEditor.getState();
5950
- const pagesContainer_4 = pagesContainerRef.current;
5555
+ const state_9 = folioEditor.getState();
5556
+ const pagesContainer_6 = pagesContainerRef.current;
5951
5557
  const scrollRoot = getScrollContainer();
5952
- if (!state_8 || !pagesContainer_4 || !scrollRoot) return null;
5558
+ if (!state_9 || !pagesContainer_6 || !scrollRoot) return null;
5953
5559
  return readBlockRects({
5954
5560
  blockIds: [blockId],
5955
- snapshot: createFolioAIEditSnapshot$1(state_8.doc),
5956
- pagesContainer: pagesContainer_4,
5561
+ snapshot: createFolioAIEditSnapshot$1(state_9.doc),
5562
+ pagesContainer: pagesContainer_6,
5957
5563
  scrollRoot
5958
5564
  }).get(blockId) ?? null;
5959
5565
  },
5960
5566
  getBlockRects(blockIds) {
5961
- const state_9 = folioEditor.getState();
5962
- const pagesContainer_5 = pagesContainerRef.current;
5567
+ const state_10 = folioEditor.getState();
5568
+ const pagesContainer_7 = pagesContainerRef.current;
5963
5569
  const scrollRoot_0 = getScrollContainer();
5964
- if (!state_9 || !pagesContainer_5 || !scrollRoot_0) return /* @__PURE__ */ new Map();
5570
+ if (!state_10 || !pagesContainer_7 || !scrollRoot_0) return /* @__PURE__ */ new Map();
5965
5571
  return readBlockRects({
5966
5572
  blockIds,
5967
- snapshot: createFolioAIEditSnapshot$1(state_9.doc),
5968
- pagesContainer: pagesContainer_5,
5573
+ snapshot: createFolioAIEditSnapshot$1(state_10.doc),
5574
+ pagesContainer: pagesContainer_7,
5969
5575
  scrollRoot: scrollRoot_0
5970
5576
  });
5971
5577
  },
5972
5578
  getScrollRoot: getScrollContainer,
5973
5579
  onLayoutChange(listener) {
5974
- const pagesContainer_6 = pagesContainerRef.current;
5975
- return pagesContainer_6 ? onPaintedLayoutChange(pagesContainer_6, listener) : () => {};
5580
+ const pagesContainer_8 = pagesContainerRef.current;
5581
+ return pagesContainer_8 ? onPaintedLayoutChange(pagesContainer_8, listener) : () => {};
5976
5582
  }
5977
5583
  }), [
5978
5584
  ensureHiddenEditorView,
@@ -5993,8 +5599,8 @@ const PagedEditor = forwardRef(function PagedEditor(props, ref) {
5993
5599
  onTotalPagesChangeRef.current?.(totalPages);
5994
5600
  }, [layout]);
5995
5601
  useEffect(() => {
5996
- const state_10 = hiddenPMRef.current?.getState();
5997
- if (layout && state_10) updateSelectionOverlay(state_10);
5602
+ const state_11 = hiddenPMRef.current?.getState();
5603
+ if (layout && state_11) updateSelectionOverlay(state_11);
5998
5604
  }, [layout, updateSelectionOverlay]);
5999
5605
  const totalHeight = useMemo(() => {
6000
5606
  if (!layout) return DEFAULT_PAGE_HEIGHT_PX + 48;
@@ -10585,6 +10191,26 @@ function useFindReplace(t0) {
10585
10191
  * glue — the edit-position state, the memoised resolution, the host
10586
10192
  * `pushDocument` routing, and reading the live hidden HF PM doc at save time.
10587
10193
  */
10194
+ const saveAndCloseHeaderFooterEdit = ({ activeRId, document, editPosition, isFirstPage, pushDocument, setEditPosition, view }) => {
10195
+ if (!activeRId || !view) {
10196
+ setEditPosition(null);
10197
+ return;
10198
+ }
10199
+ const source = (editPosition === "header" ? document.package.headers : document.package.footers)?.get(activeRId);
10200
+ if (!source) {
10201
+ setEditPosition(null);
10202
+ return;
10203
+ }
10204
+ const newDocument = saveHeaderFooterContent({
10205
+ document,
10206
+ position: editPosition,
10207
+ isFirstPage,
10208
+ activeRId,
10209
+ blocks: proseDocToBlocks(view.state.doc, source.content)
10210
+ });
10211
+ if (newDocument) pushDocument(newDocument);
10212
+ setEditPosition(null);
10213
+ };
10588
10214
  const useHeaderFooterEditor = ({ history, pushDocument, getHfView }) => {
10589
10215
  const [hfEditPosition, setHfEditPosition] = useState(null);
10590
10216
  const [hfEditIsFirstPage, setHfEditIsFirstPage] = useState(false);
@@ -10624,17 +10250,15 @@ const useHeaderFooterEditor = ({ history, pushDocument, getHfView }) => {
10624
10250
  }
10625
10251
  const activeRId = pickActiveHeaderFooterRId(resolution, hfEditPosition, hfEditIsFirstPage);
10626
10252
  const view = activeRId ? getHfView(activeRId) : null;
10627
- if (activeRId && view) {
10628
- const newDoc_0 = saveHeaderFooterContent({
10629
- document: history.state,
10630
- position: hfEditPosition,
10631
- isFirstPage: hfEditIsFirstPage,
10632
- activeRId,
10633
- blocks: proseDocToBlocks(view.state.doc)
10634
- });
10635
- if (newDoc_0) pushDocument(newDoc_0);
10636
- }
10637
- setHfEditPosition(null);
10253
+ saveAndCloseHeaderFooterEdit({
10254
+ activeRId,
10255
+ document: history.state,
10256
+ editPosition: hfEditPosition,
10257
+ isFirstPage: hfEditIsFirstPage,
10258
+ pushDocument,
10259
+ setEditPosition: setHfEditPosition,
10260
+ view
10261
+ });
10638
10262
  }, [
10639
10263
  hfEditPosition,
10640
10264
  hfEditIsFirstPage,
@@ -12680,7 +12304,7 @@ function useFolioComments({ doc, autoOpenReviewSidebar, anchorPositions, editorC
12680
12304
  * - Error boundary
12681
12305
  * - Loading states
12682
12306
  */
12683
- const CommentsSidebar = lazy(() => import("./CommentsSidebar-TCygZ9-U.js").then((m) => ({ default: m.CommentsSidebar })));
12307
+ const CommentsSidebar = lazy(() => import("./CommentsSidebar-Bvlu7MpL.js").then((m) => ({ default: m.CommentsSidebar })));
12684
12308
  const TextContextMenu = lazy(() => import("./TextContextMenu-Ci7-M4oU.js").then((m) => ({ default: m.TextContextMenu })));
12685
12309
  const loadAttemptSelectiveSave = async () => {
12686
12310
  const { attemptSelectiveSave } = await import("@stll/folio-core/docx/selectiveSave");
@@ -12941,7 +12565,7 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
12941
12565
  const scrollContainerRef = useRef(null);
12942
12566
  const [formatPainterMode, setFormatPainterModeState] = useState("off");
12943
12567
  const formatPainterModeRef = useRef("off");
12944
- const capturedFormatMarksRef = useRef([]);
12568
+ const capturedFormatMarksRef = useRef(null);
12945
12569
  const setFormatPainterMode = useCallback((next) => {
12946
12570
  formatPainterModeRef.current = next;
12947
12571
  setFormatPainterModeState(next);
@@ -13211,7 +12835,7 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
13211
12835
  ]);
13212
12836
  const buildCurrentDocument = useCallback(() => {
13213
12837
  if (!history.state) return null;
13214
- let doc_0 = structuredClone(history.state);
12838
+ let doc_0 = cloneDocumentWithParagraphPropertySources(history.state);
13215
12839
  const pmDoc = pagedEditorRef.current?.getDocument();
13216
12840
  if (pmDoc) {
13217
12841
  doc_0.package.document.content = pmDoc.package.document.content;
@@ -13224,7 +12848,7 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
13224
12848
  if (!bag) return;
13225
12849
  for (const [rId_0, hf] of bag) {
13226
12850
  const view_5 = editor.getHfView(rId_0);
13227
- if (view_5) hf.content = proseDocToBlocks(view_5.state.doc);
12851
+ if (view_5) hf.content = proseDocToBlocks(view_5.state.doc, hf.content);
13228
12852
  }
13229
12853
  };
13230
12854
  flushBag(doc_0.package.headers);
@@ -13808,7 +13432,7 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
13808
13432
  }, [getActiveEditorView, getCachedStyleResolver]);
13809
13433
  const captureFormatPainter = useCallback(() => {
13810
13434
  const view_10 = getActiveEditorView();
13811
- capturedFormatMarksRef.current = view_10 ? captureFormatMarks(view_10.state) : [];
13435
+ capturedFormatMarksRef.current = view_10 ? captureFormatMarks(view_10.state) : null;
13812
13436
  }, [getActiveEditorView]);
13813
13437
  const paintFormatToSelection = useCallback(() => {
13814
13438
  const view_11 = getActiveEditorView();
@@ -13819,7 +13443,7 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
13819
13443
  }, [getActiveEditorView]);
13820
13444
  const handleFormatPainter = useCallback((sticky) => {
13821
13445
  if (formatPainterModeRef.current !== "off") {
13822
- capturedFormatMarksRef.current = [];
13446
+ capturedFormatMarksRef.current = null;
13823
13447
  setFormatPainterMode("off");
13824
13448
  return;
13825
13449
  }
@@ -13842,11 +13466,11 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
13842
13466
  const handleKeyDown = (event_0) => {
13843
13467
  if (event_0.key === "Escape") {
13844
13468
  const armed = formatPainterModeRef.current !== "off";
13845
- const hasCopiedFormat = capturedFormatMarksRef.current.length > 0;
13469
+ const hasCopiedFormat = capturedFormatMarksRef.current !== null;
13846
13470
  if (!armed && !hasCopiedFormat) return;
13847
13471
  event_0.preventDefault();
13848
13472
  event_0.stopPropagation();
13849
- capturedFormatMarksRef.current = [];
13473
+ capturedFormatMarksRef.current = null;
13850
13474
  if (armed) setFormatPainterMode("off");
13851
13475
  return;
13852
13476
  }