superdoc 1.0.0-beta.14 → 1.0.0-beta.16

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.
Files changed (30) hide show
  1. package/dist/chunks/{PdfViewer-Cdc_EOZ9.cjs → PdfViewer-93eWvs8z.cjs} +1 -1
  2. package/dist/chunks/{PdfViewer-DrsLL5TV.es.js → PdfViewer-ODeuH1gb.es.js} +1 -1
  3. package/dist/chunks/{index-DB1DyDmZ.es.js → index-DW5_UKLM.es.js} +3 -3
  4. package/dist/chunks/{index-I4Ew0HDV-Bht7-IGi.es.js → index-DexFffM7-Cbdy0Zy6.es.js} +1 -1
  5. package/dist/chunks/{index-I4Ew0HDV-EPhjcu7T.cjs → index-DexFffM7-XZD_g6eY.cjs} +1 -1
  6. package/dist/chunks/{index-9ZvcPlCg.cjs → index-RquHXtgI.cjs} +3 -3
  7. package/dist/chunks/{super-editor.es-DuS77THX.cjs → super-editor.es-BLKWkx5G.cjs} +3220 -569
  8. package/dist/chunks/{super-editor.es-DxAG4BVh.es.js → super-editor.es-Dg5uoFkw.es.js} +3220 -569
  9. package/dist/packages/superdoc/src/core/SuperDoc.d.ts.map +1 -1
  10. package/dist/style.css +6 -6
  11. package/dist/super-editor/ai-writer.es.js +2 -2
  12. package/dist/super-editor/chunks/{converter-CfmIU--8.js → converter-C_R_BK8X.js} +412 -252
  13. package/dist/super-editor/chunks/{docx-zipper-B-CXiNSb.js → docx-zipper-BvQAYmi1.js} +1 -1
  14. package/dist/super-editor/chunks/{editor-wC_gs8Bl.js → editor-DFFvalb1.js} +2822 -312
  15. package/dist/super-editor/chunks/{index-I4Ew0HDV.js → index-DexFffM7.js} +1 -1
  16. package/dist/super-editor/chunks/{toolbar-BR1GYvwW.js → toolbar-DLPfegtw.js} +2 -2
  17. package/dist/super-editor/converter.es.js +1 -1
  18. package/dist/super-editor/docx-zipper.es.js +2 -2
  19. package/dist/super-editor/editor.es.js +3 -3
  20. package/dist/super-editor/file-zipper.es.js +1 -1
  21. package/dist/super-editor/style.css +6 -6
  22. package/dist/super-editor/super-editor.es.js +8 -100
  23. package/dist/super-editor/toolbar.es.js +2 -2
  24. package/dist/super-editor.cjs +1 -1
  25. package/dist/super-editor.es.js +1 -1
  26. package/dist/superdoc.cjs +2 -2
  27. package/dist/superdoc.es.js +2 -2
  28. package/dist/superdoc.umd.js +3222 -571
  29. package/dist/superdoc.umd.js.map +1 -1
  30. package/package.json +1 -1
@@ -9,11 +9,11 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
9
9
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
10
10
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
11
11
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
12
- var _Attribute_static, getGlobalAttributes_fn, getNodeAndMarksAttributes_fn, _Schema_static, createNodesSchema_fn, createMarksSchema_fn, _events, _ExtensionService_instances, setupExtensions_fn, attachEditorEvents_fn, _editor, _stateValidators, _xmlValidators, _requiredNodeTypes, _requiredMarkTypes, _SuperValidator_instances, initializeValidators_fn, collectValidatorRequirements_fn, analyzeDocument_fn, dispatchWithFallback_fn, _commandService, _Editor_instances, initContainerElement_fn, init_fn, initRichText_fn, onFocus_fn, checkHeadless_fn, registerCopyHandler_fn, insertNewFileData_fn, getPluginKeyName_fn, createExtensionService_fn, createCommandService_fn, createConverter_fn, initMedia_fn, initFonts_fn, checkFonts_fn, determineUnsupportedFonts_fn, createSchema_fn, generatePmData_fn, createView_fn, onCollaborationReady_fn, initComments_fn, dispatchTransaction_fn, handleNodeSelection_fn, prepareDocumentForImport_fn, prepareDocumentForExport_fn, endCollaboration_fn, validateDocumentInit_fn, validateDocumentExport_fn, initDevTools_fn, _map, _editor2, _descriptors, _collections, _editorEntries, _maxCachedEditors, _editorAccessOrder, _pendingCreations, _cacheHits, _cacheMisses, _evictions, _HeaderFooterEditorManager_instances, hasConverter_fn, extractCollections_fn, collectDescriptors_fn, teardownMissingEditors_fn, teardownEditors_fn, createEditor_fn, createEditorContainer_fn, registerConverterEditor_fn, unregisterConverterEditor_fn, updateAccessOrder_fn, enforceCacheSizeLimit_fn, _manager, _mediaFiles, _blockCache, _HeaderFooterLayoutAdapter_instances, getBlocks_fn, getConverterContext_fn, _instances, _options, _editor3, _visibleHost, _viewportHost, _painterHost, _selectionOverlay, _hiddenHost, _layoutOptions, _layoutState, _domPainter, _layoutError, _layoutErrorState, _errorBanner, _errorBannerMessage, _telemetryEmitter, _renderScheduled, _pendingDocChange, _isRerendering, _selectionUpdateScheduled, _remoteCursorUpdateScheduled, _rafHandle, _editorListeners, _sectionMetadata, _documentMode, _inputBridge, _trackedChangesMode, _trackedChangesEnabled, _trackedChangesOverrides, _headerFooterManager, _headerFooterAdapter, _headerFooterIdentifier, _headerLayoutResults, _footerLayoutResults, _headerDecorationProvider, _footerDecorationProvider, _headerFooterManagerCleanups, _headerRegions, _footerRegions, _session, _activeHeaderFooterEditor, _hoverOverlay, _hoverTooltip, _modeBanner, _ariaLiveRegion, _hoverRegion, _clickCount, _lastClickTime, _lastClickPosition, _lastSelectedImageBlockId, _remoteCursorState, _remoteCursorDirty, _remoteCursorOverlay, _localSelectionLayer, _awarenessCleanup, _scrollCleanup, _remoteCursorRafHandle, _scrollTimeout, _PresentationEditor_instances, aggregateLayoutBounds_fn, safeCleanup_fn, setupEditorListeners_fn, setupCollaborationCursors_fn, normalizeAwarenessStates_fn, getFallbackColor_fn, getValidatedColor_fn, scheduleRemoteCursorUpdate_fn, scheduleRemoteCursorReRender_fn, updateRemoteCursors_fn, renderRemoteCursors_fn, renderRemoteCaret_fn, renderRemoteCursorLabel_fn, renderRemoteSelection_fn, setupPointerHandlers_fn, setupInputBridge_fn, initHeaderFooterRegistry_fn, _handlePointerDown, getFirstTextPosition_fn, registerPointerClick_fn, selectWordAt_fn, selectParagraphAt_fn, isWordCharacter_fn, _handlePointerMove, _handlePointerLeave, _handleDoubleClick, _handleKeyDown, focusHeaderFooterShortcut_fn, scheduleRerender_fn, flushRerenderQueue_fn, rerender_fn, ensurePainter_fn, scheduleSelectionUpdate_fn, updateSelection_fn, resolveLayoutOptions_fn, buildHeaderFooterInput_fn, computeHeaderFooterConstraints_fn, updateDecorationProviders_fn, createDecorationProvider_fn, findHeaderFooterPageForPageNumber_fn, computeDecorationBox_fn, rebuildHeaderFooterRegions_fn, hitTestHeaderFooterRegion_fn, pointInRegion_fn, activateHeaderFooterRegion_fn, enterHeaderFooterMode_fn, exitHeaderFooterMode_fn, getActiveDomTarget_fn, emitHeaderFooterModeChanged_fn, emitHeaderFooterEditingContext_fn, updateAwarenessSession_fn, updateModeBanner_fn, announce_fn, validateHeaderFooterEditPermission_fn, emitHeaderFooterEditBlocked_fn, resolveDescriptorForRegion_fn, getBodyPageHeight_fn, getHeaderFooterPageHeight_fn, renderSelectionRects_fn, renderHoverRegion_fn, clearHoverRegion_fn, renderCaretOverlay_fn, getHeaderFooterContext_fn, computeHeaderFooterSelectionRects_fn, computeHeaderFooterCaretRect_fn, syncTrackedChangesPreferences_fn, deriveTrackedChangesMode_fn, deriveTrackedChangesEnabled_fn, getTrackChangesPluginState_fn, computeDefaultLayoutDefaults_fn, parseColumns_fn, inchesToPx_fn, applyZoom_fn, createLayoutMetrics_fn, convertPageLocalToOverlayCoords_fn, normalizeClientPoint_fn, computeCaretLayoutRect_fn, findLineContainingPos_fn, lineHeightBeforeIndex_fn, getCurrentPageIndex_fn, findRegionForPage_fn, handleLayoutError_fn, decorateError_fn, showLayoutErrorBanner_fn, dismissErrorBanner_fn, createHiddenHost_fn, _windowRoot, _layoutSurfaces, _getTargetDom, _onTargetChanged, _listeners, _currentTarget, _destroyed, _useWindowFallback, _PresentationInputBridge_instances, addListener_fn, dispatchToTarget_fn, forwardKeyboardEvent_fn, forwardTextEvent_fn, forwardCompositionEvent_fn, forwardContextMenu_fn, isEventOnActiveTarget_fn, shouldSkipSurface_fn, isInLayoutSurface_fn, getListenerTargets_fn, isPlainCharacterKey_fn, _DocumentSectionView_instances, init_fn2, addToolTip_fn, _ParagraphNodeView_instances, checkShouldUpdate_fn, updateHTMLAttributes_fn, updateDOMStyles_fn, resolveNeighborParagraphProperties_fn, updateListStyles_fn, initList_fn, checkIsList_fn, createMarker_fn, createSeparator_fn, calculateTabSeparatorStyle_fn, calculateMarkerStyle_fn, removeList_fn, getParagraphContext_fn, scheduleAnimation_fn, cancelScheduledAnimation_fn, _FieldAnnotationView_instances, createAnnotation_fn, _AutoPageNumberNodeView_instances, renderDom_fn, scheduleUpdateNodeStyle_fn, _VectorShapeView_instances, ensureParentPositioned_fn, _ShapeGroupView_instances, ensureParentPositioned_fn2;
12
+ var _Attribute_static, getGlobalAttributes_fn, getNodeAndMarksAttributes_fn, _Schema_static, createNodesSchema_fn, createMarksSchema_fn, _events, _ExtensionService_instances, setupExtensions_fn, attachEditorEvents_fn, _editor, _stateValidators, _xmlValidators, _requiredNodeTypes, _requiredMarkTypes, _SuperValidator_instances, initializeValidators_fn, collectValidatorRequirements_fn, analyzeDocument_fn, dispatchWithFallback_fn, _commandService, _Editor_instances, initContainerElement_fn, init_fn, initRichText_fn, onFocus_fn, checkHeadless_fn, registerCopyHandler_fn, insertNewFileData_fn, getPluginKeyName_fn, createExtensionService_fn, createCommandService_fn, createConverter_fn, initMedia_fn, initFonts_fn, checkFonts_fn, determineUnsupportedFonts_fn, createSchema_fn, generatePmData_fn, createView_fn, onCollaborationReady_fn, initComments_fn, dispatchTransaction_fn, handleNodeSelection_fn, prepareDocumentForImport_fn, prepareDocumentForExport_fn, endCollaboration_fn, validateDocumentInit_fn, validateDocumentExport_fn, initDevTools_fn, _map, _editor2, _descriptors, _collections, _editorEntries, _maxCachedEditors, _editorAccessOrder, _pendingCreations, _cacheHits, _cacheMisses, _evictions, _HeaderFooterEditorManager_instances, hasConverter_fn, extractCollections_fn, collectDescriptors_fn, teardownMissingEditors_fn, teardownEditors_fn, createEditor_fn, createEditorContainer_fn, registerConverterEditor_fn, unregisterConverterEditor_fn, updateAccessOrder_fn, enforceCacheSizeLimit_fn, _manager, _mediaFiles, _blockCache, _HeaderFooterLayoutAdapter_instances, getBlocks_fn, getConverterContext_fn, _instances, _options, _editor3, _visibleHost, _viewportHost, _painterHost, _selectionOverlay, _hiddenHost, _layoutOptions, _layoutState, _domPainter, _layoutError, _layoutErrorState, _errorBanner, _errorBannerMessage, _telemetryEmitter, _renderScheduled, _pendingDocChange, _isRerendering, _selectionUpdateScheduled, _remoteCursorUpdateScheduled, _rafHandle, _editorListeners, _sectionMetadata, _documentMode, _inputBridge, _trackedChangesMode, _trackedChangesEnabled, _trackedChangesOverrides, _headerFooterManager, _headerFooterAdapter, _headerFooterIdentifier, _headerLayoutResults, _footerLayoutResults, _headerDecorationProvider, _footerDecorationProvider, _headerFooterManagerCleanups, _headerRegions, _footerRegions, _session, _activeHeaderFooterEditor, _hoverOverlay, _hoverTooltip, _modeBanner, _ariaLiveRegion, _hoverRegion, _clickCount, _lastClickTime, _lastClickPosition, _lastSelectedImageBlockId, _remoteCursorState, _remoteCursorDirty, _remoteCursorOverlay, _localSelectionLayer, _awarenessCleanup, _scrollCleanup, _remoteCursorRafHandle, _scrollTimeout, _PresentationEditor_instances, aggregateLayoutBounds_fn, safeCleanup_fn, setupEditorListeners_fn, setupCollaborationCursors_fn, normalizeAwarenessStates_fn, getFallbackColor_fn, getValidatedColor_fn, scheduleRemoteCursorUpdate_fn, scheduleRemoteCursorReRender_fn, updateRemoteCursors_fn, renderRemoteCursors_fn, renderRemoteCaret_fn, renderRemoteCursorLabel_fn, renderRemoteSelection_fn, setupPointerHandlers_fn, setupInputBridge_fn, initHeaderFooterRegistry_fn, _handlePointerDown, getFirstTextPosition_fn, registerPointerClick_fn, selectWordAt_fn, selectParagraphAt_fn, isWordCharacter_fn, _handlePointerMove, _handlePointerLeave, _handleDoubleClick, _handleKeyDown, focusHeaderFooterShortcut_fn, scheduleRerender_fn, flushRerenderQueue_fn, rerender_fn, ensurePainter_fn, scheduleSelectionUpdate_fn, updateSelection_fn, resolveLayoutOptions_fn, buildHeaderFooterInput_fn, computeHeaderFooterConstraints_fn, updateDecorationProviders_fn, createDecorationProvider_fn, findHeaderFooterPageForPageNumber_fn, computeDecorationBox_fn, rebuildHeaderFooterRegions_fn, hitTestHeaderFooterRegion_fn, pointInRegion_fn, activateHeaderFooterRegion_fn, enterHeaderFooterMode_fn, exitHeaderFooterMode_fn, getActiveDomTarget_fn, emitHeaderFooterModeChanged_fn, emitHeaderFooterEditingContext_fn, updateAwarenessSession_fn, updateModeBanner_fn, announce_fn, validateHeaderFooterEditPermission_fn, emitHeaderFooterEditBlocked_fn, resolveDescriptorForRegion_fn, getBodyPageHeight_fn, getHeaderFooterPageHeight_fn, renderSelectionRects_fn, renderHoverRegion_fn, clearHoverRegion_fn, renderCaretOverlay_fn, getHeaderFooterContext_fn, computeHeaderFooterSelectionRects_fn, computeHeaderFooterCaretRect_fn, syncTrackedChangesPreferences_fn, deriveTrackedChangesMode_fn, deriveTrackedChangesEnabled_fn, getTrackChangesPluginState_fn, computeDefaultLayoutDefaults_fn, parseColumns_fn, inchesToPx_fn, applyZoom_fn, createLayoutMetrics_fn, convertPageLocalToOverlayCoords_fn, normalizeClientPoint_fn, computeCaretLayoutRect_fn, computeCaretLayoutRectFromDOM_fn, computeTableCaretLayoutRect_fn, findLineContainingPos_fn, lineHeightBeforeIndex_fn, getCurrentPageIndex_fn, findRegionForPage_fn, handleLayoutError_fn, decorateError_fn, showLayoutErrorBanner_fn, dismissErrorBanner_fn, createHiddenHost_fn, _windowRoot, _layoutSurfaces, _getTargetDom, _onTargetChanged, _listeners, _currentTarget, _destroyed, _useWindowFallback, _PresentationInputBridge_instances, addListener_fn, dispatchToTarget_fn, forwardKeyboardEvent_fn, forwardTextEvent_fn, forwardCompositionEvent_fn, forwardContextMenu_fn, isEventOnActiveTarget_fn, shouldSkipSurface_fn, isInLayoutSurface_fn, getListenerTargets_fn, isPlainCharacterKey_fn, _DocumentSectionView_instances, init_fn2, addToolTip_fn, _ParagraphNodeView_instances, checkShouldUpdate_fn, updateHTMLAttributes_fn, updateDOMStyles_fn, resolveNeighborParagraphProperties_fn, updateListStyles_fn, initList_fn, checkIsList_fn, createMarker_fn, createSeparator_fn, calculateTabSeparatorStyle_fn, calculateMarkerStyle_fn, removeList_fn, getParagraphContext_fn, scheduleAnimation_fn, cancelScheduledAnimation_fn, _FieldAnnotationView_instances, createAnnotation_fn, _AutoPageNumberNodeView_instances, renderDom_fn, scheduleUpdateNodeStyle_fn, _VectorShapeView_instances, ensureParentPositioned_fn, _ShapeGroupView_instances, ensureParentPositioned_fn2;
13
13
  import * as Y from "yjs";
14
14
  import { UndoManager, Item as Item$1, ContentType, Text as Text$1, XmlElement, encodeStateAsUpdate } from "yjs";
15
- import { P as PluginKey, a as Plugin, M as Mapping, N as NodeSelection, S as Selection, T as TextSelection, b as Slice, D as DOMSerializer, F as Fragment, c as DOMParser$1, d as Mark$1, e as dropPoint, A as AllSelection, p as process$1, B as Buffer2, f as callOrGet, g as getExtensionConfigField, h as getMarkType, i as getMarksFromSelection, j as getNodeType, k as getSchemaTypeNameByName, l as Schema$1, m as cleanSchemaItem, n as canSplit, o as defaultBlockAt$1, q as liftTarget, r as canJoin, s as joinPoint, t as replaceStep$1, R as ReplaceAroundStep$1, u as isTextSelection, v as getMarkRange, w as isMarkActive, x as isNodeActive, y as deleteProps, z as processContent, C as htmlHandler, E as ReplaceStep, G as twipsToInches, H as inchesToTwips, I as ptToTwips, J as getResolvedParagraphProperties, K as linesToTwips, L as ListHelpers, O as updateNumberingProperties, Q as changeListLevel, U as findParentNode, V as isList, W as isMacOS, X as isIOS, Y as getSchemaTypeByName, Z as inputRulesPlugin, _ as TrackDeleteMarkName, $ as TrackInsertMarkName, a0 as v4, a1 as TrackFormatMarkName, a2 as comments_module_events, a3 as findMark, a4 as objectIncludes, a5 as AddMarkStep, a6 as RemoveMarkStep, a7 as twipsToLines, a8 as pixelsToTwips, a9 as helpers, aa as posToDOMRect, ab as CommandService, ac as SuperConverter, ad as createDocument, ae as createDocFromMarkdown, af as createDocFromHTML, ag as EditorState, ah as isActive, ai as unflattenListsInHtml, aj as resolveParagraphProperties, ak as _getReferencedTableStyles, al as parseSizeUnit, am as minMax, an as updateDOMAttributes, ao as findChildren$5, ap as generateRandomSigned32BitIntStrId, aq as calculateResolvedParagraphProperties, ar as encodeCSSFromPPr, as as twipsToPixels$2, at as resolveRunProperties, au as encodeCSSFromRPr, av as generateOrderedListIndex, aw as docxNumberingHelpers, ax as InputRule, ay as convertSizeToCSS, az as SelectionRange, aA as Transform, aB as findParentNodeClosestToPos, aC as isInTable$1, aD as generateDocxRandomId, aE as insertNewRelationship, aF as inchesToPixels, aG as kebabCase, aH as getUnderlineCssString } from "./converter-CfmIU--8.js";
16
- import { D as DocxZipper } from "./docx-zipper-B-CXiNSb.js";
15
+ import { P as PluginKey, a as Plugin, M as Mapping, N as NodeSelection, S as Selection, T as TextSelection, b as Slice, D as DOMSerializer, F as Fragment, c as DOMParser$1, d as Mark$1, e as dropPoint, A as AllSelection, p as process$1, B as Buffer2, f as callOrGet, g as getExtensionConfigField, h as getMarkType, i as getMarksFromSelection, j as getNodeType, k as getSchemaTypeNameByName, l as Schema$1, m as cleanSchemaItem, n as canSplit, o as defaultBlockAt$1, q as liftTarget, r as canJoin, s as joinPoint, t as replaceStep$1, R as ReplaceAroundStep$1, u as isTextSelection, v as getMarkRange, w as isMarkActive, x as isNodeActive, y as deleteProps, z as processContent, C as htmlHandler, E as ReplaceStep, G as twipsToInches, H as inchesToTwips, I as ptToTwips, J as getResolvedParagraphProperties, K as linesToTwips, L as ListHelpers, O as updateNumberingProperties, Q as changeListLevel, U as findParentNode, V as isList, W as isMacOS, X as isIOS, Y as getSchemaTypeByName, Z as inputRulesPlugin, _ as TrackDeleteMarkName, $ as TrackInsertMarkName, a0 as v4, a1 as TrackFormatMarkName, a2 as comments_module_events, a3 as findMark, a4 as objectIncludes, a5 as AddMarkStep, a6 as RemoveMarkStep, a7 as twipsToLines, a8 as pixelsToTwips, a9 as helpers, aa as posToDOMRect, ab as CommandService, ac as SuperConverter, ad as createDocument, ae as createDocFromMarkdown, af as createDocFromHTML, ag as EditorState, ah as isActive, ai as unflattenListsInHtml, aj as resolveParagraphProperties, ak as _getReferencedTableStyles, al as parseSizeUnit, am as minMax, an as updateDOMAttributes, ao as findChildren$5, ap as generateRandomSigned32BitIntStrId, aq as calculateResolvedParagraphProperties, ar as encodeCSSFromPPr, as as twipsToPixels$2, at as resolveRunProperties, au as encodeCSSFromRPr, av as generateOrderedListIndex, aw as docxNumberingHelpers, ax as InputRule, ay as convertSizeToCSS, az as SelectionRange, aA as Transform, aB as findParentNodeClosestToPos, aC as isInTable$1, aD as generateDocxRandomId, aE as insertNewRelationship, aF as inchesToPixels, aG as kebabCase, aH as getUnderlineCssString } from "./converter-C_R_BK8X.js";
16
+ import { D as DocxZipper } from "./docx-zipper-BvQAYmi1.js";
17
17
  import { ref, computed, createElementBlock, openBlock, withModifiers, Fragment as Fragment$1, renderList, normalizeClass, createCommentVNode, toDisplayString, createElementVNode, createApp } from "vue";
18
18
  var GOOD_LEAF_SIZE = 200;
19
19
  var RopeSequence = function RopeSequence2() {
@@ -887,17 +887,20 @@ function findOffsetInNode(node, coords) {
887
887
  }
888
888
  function findOffsetInText(node, coords) {
889
889
  let len = node.nodeValue.length;
890
- let range = document.createRange();
890
+ let range = document.createRange(), result;
891
891
  for (let i = 0; i < len; i++) {
892
892
  range.setEnd(node, i + 1);
893
893
  range.setStart(node, i);
894
894
  let rect = singleRect(range, 1);
895
895
  if (rect.top == rect.bottom)
896
896
  continue;
897
- if (inRect(coords, rect))
898
- return { node, offset: i + (coords.left >= (rect.left + rect.right) / 2 ? 1 : 0) };
897
+ if (inRect(coords, rect)) {
898
+ result = { node, offset: i + (coords.left >= (rect.left + rect.right) / 2 ? 1 : 0) };
899
+ break;
900
+ }
899
901
  }
900
- return { node, offset: 0 };
902
+ range.detach();
903
+ return result || { node, offset: 0 };
901
904
  }
902
905
  function inRect(coords, rect) {
903
906
  return coords.left >= rect.left - 1 && coords.left <= rect.right + 1 && coords.top >= rect.top - 1 && coords.top <= rect.bottom + 1;
@@ -3644,7 +3647,7 @@ editHandlers.compositionstart = editHandlers.compositionupdate = (view) => {
3644
3647
  if (!view.composing) {
3645
3648
  view.domObserver.flush();
3646
3649
  let { state } = view, $pos = state.selection.$to;
3647
- if (state.selection instanceof TextSelection && (state.storedMarks || !$pos.textOffset && $pos.parentOffset && $pos.nodeBefore.marks.some((m) => m.type.spec.inclusive === false))) {
3650
+ if (state.selection instanceof TextSelection && (state.storedMarks || !$pos.textOffset && $pos.parentOffset && $pos.nodeBefore.marks.some((m) => m.type.spec.inclusive === false) || chrome && windows$1 && selectionBeforeUneditable(view))) {
3648
3651
  view.markCursor = view.state.storedMarks || $pos.marks();
3649
3652
  endComposition(view, true);
3650
3653
  view.markCursor = null;
@@ -3672,6 +3675,13 @@ editHandlers.compositionstart = editHandlers.compositionupdate = (view) => {
3672
3675
  }
3673
3676
  scheduleComposeEnd(view, timeoutComposition);
3674
3677
  };
3678
+ function selectionBeforeUneditable(view) {
3679
+ let { focusNode, focusOffset } = view.domSelectionRange();
3680
+ if (!focusNode || focusNode.nodeType != 1 || focusOffset >= focusNode.childNodes.length)
3681
+ return false;
3682
+ let next = focusNode.childNodes[focusOffset];
3683
+ return next.nodeType == 1 && next.contentEditable == "false";
3684
+ }
3675
3685
  editHandlers.compositionend = (view, event) => {
3676
3686
  if (view.composing) {
3677
3687
  view.input.composing = false;
@@ -3875,10 +3885,14 @@ handlers.dragend = (view) => {
3875
3885
  }, 50);
3876
3886
  };
3877
3887
  editHandlers.dragover = editHandlers.dragenter = (_, e) => e.preventDefault();
3878
- editHandlers.drop = (view, _event) => {
3879
- let event = _event;
3880
- let dragging = view.dragging;
3881
- view.dragging = null;
3888
+ editHandlers.drop = (view, event) => {
3889
+ try {
3890
+ handleDrop(view, event, view.dragging);
3891
+ } finally {
3892
+ view.dragging = null;
3893
+ }
3894
+ };
3895
+ function handleDrop(view, event, dragging) {
3882
3896
  if (!event.dataTransfer)
3883
3897
  return;
3884
3898
  let eventPos = view.posAtCoords(eventCoords(event));
@@ -3931,7 +3945,7 @@ editHandlers.drop = (view, _event) => {
3931
3945
  }
3932
3946
  view.focus();
3933
3947
  view.dispatch(tr.setMeta("uiEvent", "drop"));
3934
- };
3948
+ }
3935
3949
  handlers.focus = (view) => {
3936
3950
  view.input.lastFocus = Date.now();
3937
3951
  if (!view.focused) {
@@ -4781,6 +4795,13 @@ class DOMObserver {
4781
4795
  br.remove();
4782
4796
  }
4783
4797
  }
4798
+ } else if ((chrome || safari) && added.some((n) => n.nodeName == "BR") && (view.input.lastKeyCode == 8 || view.input.lastKeyCode == 46)) {
4799
+ for (let node of added)
4800
+ if (node.nodeName == "BR" && node.parentNode) {
4801
+ let after = node.nextSibling;
4802
+ if (after && after.nodeType == 1 && after.contentEditable == "false")
4803
+ node.parentNode.removeChild(node);
4804
+ }
4784
4805
  }
4785
4806
  let readSel = null;
4786
4807
  if (from2 < 0 && newSel && view.input.lastFocus > Date.now() - 200 && Math.max(view.input.lastTouch, view.input.lastClick.time) < Date.now() - 300 && selectionCollapsed(sel) && (readSel = selectionFromDOM(view)) && readSel.eq(Selection.near(view.state.doc.resolve(0), 1))) {
@@ -9964,7 +9985,7 @@ const Keymap = Extension.create({
9964
9985
  const baseKeymap = {
9965
9986
  Enter: () => handleEnter(this.editor),
9966
9987
  "Shift-Enter": () => this.editor.commands.insertLineBreak(),
9967
- "Mod-Enter": () => this.editor.commands.exitCode(),
9988
+ "Mod-Enter": () => this.editor.commands.insertPageBreak(),
9968
9989
  Backspace: () => handleBackspace(this.editor),
9969
9990
  "Mod-Backspace": () => handleBackspace(this.editor),
9970
9991
  "Shift-Backspace": () => handleBackspace(this.editor),
@@ -13553,7 +13574,7 @@ const isHeadless = (editor) => {
13553
13574
  const shouldSkipNodeView = (editor) => {
13554
13575
  return isHeadless(editor);
13555
13576
  };
13556
- const summaryVersion = "1.0.0-beta.14";
13577
+ const summaryVersion = "1.0.0-beta.16";
13557
13578
  const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
13558
13579
  const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
13559
13580
  function mapAttributes(attrs) {
@@ -14335,7 +14356,7 @@ const _Editor = class _Editor extends EventEmitter {
14335
14356
  { default: remarkStringify },
14336
14357
  { default: remarkGfm }
14337
14358
  ] = await Promise.all([
14338
- import("./index-I4Ew0HDV.js"),
14359
+ import("./index-DexFffM7.js"),
14339
14360
  import("./index-DRCvimau.js"),
14340
14361
  import("./index-C_x_N6Uh.js"),
14341
14362
  import("./index-D_sWOSiG.js"),
@@ -14540,7 +14561,7 @@ const _Editor = class _Editor extends EventEmitter {
14540
14561
  * Process collaboration migrations
14541
14562
  */
14542
14563
  processCollaborationMigrations() {
14543
- console.debug("[checkVersionMigrations] Current editor version", "1.0.0-beta.14");
14564
+ console.debug("[checkVersionMigrations] Current editor version", "1.0.0-beta.16");
14544
14565
  if (!this.options.ydoc) return;
14545
14566
  const metaMap = this.options.ydoc.getMap("meta");
14546
14567
  let docVersion = metaMap.get("version");
@@ -15703,7 +15724,7 @@ const pickLang = (value) => {
15703
15724
  const normalized = value.trim().toLowerCase();
15704
15725
  return normalized || void 0;
15705
15726
  };
15706
- const normalizeColor = (value) => {
15727
+ const normalizeColor$1 = (value) => {
15707
15728
  if (typeof value !== "string") return void 0;
15708
15729
  const trimmed = value.trim();
15709
15730
  if (!trimmed || trimmed === "auto" || trimmed === "none") return void 0;
@@ -16343,20 +16364,37 @@ const resolveThemeColor = (attrs, themeColors) => {
16343
16364
  const resolveColorFromAttributes = (attrs, themeColors) => {
16344
16365
  if (!attrs) return void 0;
16345
16366
  if (typeof attrs.color === "string") {
16346
- const normalized = normalizeColor(attrs.color);
16367
+ const normalized = normalizeColor$1(attrs.color);
16347
16368
  if (normalized) {
16348
16369
  return normalized;
16349
16370
  }
16350
16371
  }
16351
16372
  const theme = resolveThemeColor(attrs, themeColors);
16352
16373
  if (theme) {
16353
- return normalizeColor(theme);
16374
+ return normalizeColor$1(theme);
16354
16375
  }
16355
16376
  return void 0;
16356
16377
  };
16357
16378
  const MAX_DATA_ATTR_COUNT = 50;
16358
16379
  const MAX_DATA_ATTR_VALUE_LENGTH = 1e3;
16359
16380
  const MAX_DATA_ATTR_NAME_LENGTH = 100;
16381
+ const pushCommentAnnotation = (run, attrs) => {
16382
+ const commentId = typeof attrs?.commentId === "string" ? attrs.commentId : void 0;
16383
+ const importedId = typeof attrs?.importedId === "string" ? attrs.importedId : void 0;
16384
+ const internal = attrs?.internal === true;
16385
+ if (!commentId && !importedId) return;
16386
+ const annotations = run.comments ? [...run.comments] : [];
16387
+ const key2 = `${commentId ?? ""}::${importedId ?? ""}`;
16388
+ const exists = annotations.some((c) => `${c.commentId ?? ""}::${c.importedId ?? ""}` === key2);
16389
+ if (!exists) {
16390
+ annotations.push({
16391
+ commentId: commentId ?? importedId,
16392
+ importedId,
16393
+ internal
16394
+ });
16395
+ }
16396
+ run.comments = annotations;
16397
+ };
16360
16398
  const extractDataAttributes = (attrs) => {
16361
16399
  if (!attrs) return void 0;
16362
16400
  const result = {};
@@ -16639,6 +16677,11 @@ const applyMarksToRun = (run, marks, hyperlinkConfig = DEFAULT_HYPERLINK_CONFIG,
16639
16677
  case "textStyle":
16640
16678
  applyTextStyleMark(run, mark.attrs ?? {}, themeColors);
16641
16679
  break;
16680
+ case "commentMark":
16681
+ case "comment": {
16682
+ pushCommentAnnotation(run, mark.attrs ?? {});
16683
+ break;
16684
+ }
16642
16685
  case "underline": {
16643
16686
  const style = normalizeUnderlineStyle(mark.attrs?.underlineType);
16644
16687
  if (style) {
@@ -16763,13 +16806,8 @@ const MAX_BORDER_SIZE_PX = 100;
16763
16806
  const borderSizeToPx = (size) => {
16764
16807
  if (!isFiniteNumber(size)) return void 0;
16765
16808
  if (size <= 0) return 0;
16766
- let pixelValue;
16767
- if (size < EIGHTHS_PER_POINT) {
16768
- pixelValue = size;
16769
- } else {
16770
- const points = size / EIGHTHS_PER_POINT;
16771
- pixelValue = points * PX_PER_PT;
16772
- }
16809
+ const points = size / EIGHTHS_PER_POINT;
16810
+ const pixelValue = points * PX_PER_PT;
16773
16811
  return Math.min(MAX_BORDER_SIZE_PX, Math.max(MIN_BORDER_SIZE_PX, pixelValue));
16774
16812
  };
16775
16813
  const normalizeColorWithDefault = (color) => {
@@ -16908,7 +16946,7 @@ const normalizeBorderSide = (value) => {
16908
16946
  if (style === "none") return void 0;
16909
16947
  const width = pickNumber(raw.size);
16910
16948
  const widthPx = borderSizeToPx(width);
16911
- const color = normalizeColor(raw.color);
16949
+ const color = normalizeColor$1(raw.color);
16912
16950
  const space = pickNumber(raw.space);
16913
16951
  if (!style && widthPx == null && space == null && !color) {
16914
16952
  return void 0;
@@ -16964,7 +17002,7 @@ const normalizeParagraphShading = (value) => {
16964
17002
  return Object.keys(shading).length > 0 ? shading : void 0;
16965
17003
  };
16966
17004
  const normalizeShadingColor = (value) => {
16967
- const normalized = normalizeColor(value);
17005
+ const normalized = normalizeColor$1(value);
16968
17006
  if (!normalized) return void 0;
16969
17007
  if (normalized.toLowerCase() === "#auto") {
16970
17008
  return void 0;
@@ -18127,11 +18165,11 @@ const buildMarkerLayout = ({
18127
18165
  baselineOffsetPx: markerRun.baselineShift ?? 0,
18128
18166
  gutterWidthPx: markerBoxWidthPx,
18129
18167
  justification: numbering.lvlJc ?? "left",
18130
- suffix: normalizeSuffix(numbering.suffix) ?? "tab",
18168
+ suffix: normalizeSuffix$1(numbering.suffix) ?? "tab",
18131
18169
  run: markerRun,
18132
18170
  path: numbering.path
18133
18171
  });
18134
- const normalizeSuffix = (suffix) => {
18172
+ const normalizeSuffix$1 = (suffix) => {
18135
18173
  if (suffix === "tab" || suffix === "space" || suffix === "nothing") {
18136
18174
  return suffix;
18137
18175
  }
@@ -18555,10 +18593,17 @@ const hydrateParagraphStyleAttrs = (para, context, preResolved) => {
18555
18593
  }
18556
18594
  const resolvedExtended = resolved;
18557
18595
  const resolvedAsRecord = resolved;
18596
+ let resolvedIndent = cloneIfObject(resolvedAsRecord.indent);
18597
+ const styleIdLower = typeof styleId === "string" ? styleId.toLowerCase() : "";
18598
+ const isHeadingStyle = typeof resolvedExtended.outlineLvl === "number" || styleIdLower.startsWith("heading ") || styleIdLower.startsWith("heading");
18599
+ const onlyFirstLineIndent = resolvedIndent && resolvedIndent.firstLine != null && resolvedIndent.hanging == null && resolvedIndent.left == null && resolvedIndent.right == null;
18600
+ if (isHeadingStyle && (!resolvedIndent || Object.keys(resolvedIndent).length === 0 || onlyFirstLineIndent)) {
18601
+ resolvedIndent = { firstLine: 0, hanging: 0, left: resolvedIndent?.left, right: resolvedIndent?.right };
18602
+ }
18558
18603
  const hydrated = {
18559
18604
  resolved,
18560
18605
  spacing: cloneIfObject(resolvedAsRecord.spacing),
18561
- indent: cloneIfObject(resolvedAsRecord.indent),
18606
+ indent: resolvedIndent,
18562
18607
  borders: cloneIfObject(resolvedExtended.borders),
18563
18608
  shading: cloneIfObject(resolvedExtended.shading),
18564
18609
  alignment: resolvedExtended.justification,
@@ -18578,6 +18623,154 @@ const cloneIfObject = (value) => {
18578
18623
  };
18579
18624
  const { resolveSpacingIndent } = Engines;
18580
18625
  const DEFAULT_DECIMAL_SEPARATOR$2 = ".";
18626
+ const asOoxmlElement = (value) => {
18627
+ if (!value || typeof value !== "object") return void 0;
18628
+ const element = value;
18629
+ if (element.name == null && element.attributes == null && element.elements == null) return void 0;
18630
+ return element;
18631
+ };
18632
+ const findChild = (parent, name) => {
18633
+ return parent?.elements?.find((child) => child?.name === name);
18634
+ };
18635
+ const getAttribute = (element, key2) => {
18636
+ if (!element?.attributes) return void 0;
18637
+ const attrs = element.attributes;
18638
+ return attrs[key2] ?? attrs[key2.startsWith("w:") ? key2.slice(2) : `w:${key2}`];
18639
+ };
18640
+ const parseNumberAttr = (value) => {
18641
+ if (value == null) return void 0;
18642
+ const num = typeof value === "number" ? value : Number.parseInt(String(value), 10);
18643
+ return Number.isFinite(num) ? num : void 0;
18644
+ };
18645
+ const normalizeNumFmt = (value) => {
18646
+ if (typeof value !== "string") return void 0;
18647
+ switch (value) {
18648
+ case "decimal":
18649
+ return "decimal";
18650
+ case "lowerLetter":
18651
+ return "lowerLetter";
18652
+ case "upperLetter":
18653
+ return "upperLetter";
18654
+ case "lowerRoman":
18655
+ return "lowerRoman";
18656
+ case "upperRoman":
18657
+ return "upperRoman";
18658
+ case "bullet":
18659
+ return "bullet";
18660
+ default:
18661
+ return void 0;
18662
+ }
18663
+ };
18664
+ const normalizeSuffix = (value) => {
18665
+ if (typeof value !== "string") return void 0;
18666
+ if (value === "tab" || value === "space" || value === "nothing") {
18667
+ return value;
18668
+ }
18669
+ return void 0;
18670
+ };
18671
+ const normalizeJustification = (value) => {
18672
+ if (typeof value !== "string") return void 0;
18673
+ if (value === "start") return "left";
18674
+ if (value === "end") return "right";
18675
+ if (value === "left" || value === "center" || value === "right") return value;
18676
+ return void 0;
18677
+ };
18678
+ const extractIndentFromLevel = (lvl) => {
18679
+ const pPr = findChild(lvl, "w:pPr");
18680
+ const ind = findChild(pPr, "w:ind");
18681
+ if (!ind) return void 0;
18682
+ const left2 = parseNumberAttr(getAttribute(ind, "w:left"));
18683
+ const right2 = parseNumberAttr(getAttribute(ind, "w:right"));
18684
+ const firstLine = parseNumberAttr(getAttribute(ind, "w:firstLine"));
18685
+ const hanging = parseNumberAttr(getAttribute(ind, "w:hanging"));
18686
+ const indent = {};
18687
+ if (left2 != null) indent.left = left2;
18688
+ if (right2 != null) indent.right = right2;
18689
+ if (firstLine != null) indent.firstLine = firstLine;
18690
+ if (hanging != null) indent.hanging = hanging;
18691
+ return Object.keys(indent).length ? indent : void 0;
18692
+ };
18693
+ const normalizeColor = (value) => {
18694
+ if (typeof value !== "string") return void 0;
18695
+ const trimmed = value.trim();
18696
+ if (!trimmed || trimmed.toLowerCase() === "auto") return void 0;
18697
+ const upper = trimmed.startsWith("#") ? trimmed.slice(1) : trimmed;
18698
+ return `#${upper.toUpperCase()}`;
18699
+ };
18700
+ const extractMarkerRun = (lvl) => {
18701
+ const rPr = findChild(lvl, "w:rPr");
18702
+ if (!rPr) return void 0;
18703
+ const run = {};
18704
+ const rFonts = findChild(rPr, "w:rFonts");
18705
+ const font = getAttribute(rFonts, "w:ascii") ?? getAttribute(rFonts, "w:hAnsi") ?? getAttribute(rFonts, "w:eastAsia");
18706
+ if (typeof font === "string" && font.trim()) {
18707
+ run.fontFamily = font;
18708
+ }
18709
+ const sz = parseNumberAttr(getAttribute(findChild(rPr, "w:sz"), "w:val")) ?? parseNumberAttr(getAttribute(findChild(rPr, "w:szCs"), "w:val"));
18710
+ if (sz != null) {
18711
+ run.fontSize = sz / 2;
18712
+ }
18713
+ const color = normalizeColor(getAttribute(findChild(rPr, "w:color"), "w:val"));
18714
+ if (color) run.color = color;
18715
+ if (findChild(rPr, "w:b")) run.bold = true;
18716
+ if (findChild(rPr, "w:i")) run.italic = true;
18717
+ const spacingTwips = parseNumberAttr(getAttribute(findChild(rPr, "w:spacing"), "w:val"));
18718
+ if (spacingTwips != null && Number.isFinite(spacingTwips)) {
18719
+ run.letterSpacing = twipsToPx$1(spacingTwips);
18720
+ }
18721
+ return Object.keys(run).length ? run : void 0;
18722
+ };
18723
+ const findNumFmtElement = (lvl) => {
18724
+ if (!lvl) return void 0;
18725
+ const direct = findChild(lvl, "w:numFmt");
18726
+ if (direct) return direct;
18727
+ const alternate = findChild(lvl, "mc:AlternateContent");
18728
+ const choice = findChild(alternate, "mc:Choice");
18729
+ if (choice) {
18730
+ return findChild(choice, "w:numFmt");
18731
+ }
18732
+ return void 0;
18733
+ };
18734
+ const resolveNumberingFromContext = (numId, ilvl, numbering) => {
18735
+ const definitions = numbering?.definitions;
18736
+ const abstracts = numbering?.abstracts;
18737
+ if (!definitions || !abstracts) return void 0;
18738
+ const numDef = asOoxmlElement(definitions[String(numId)]);
18739
+ if (!numDef) return void 0;
18740
+ const abstractId = getAttribute(findChild(numDef, "w:abstractNumId"), "w:val");
18741
+ if (abstractId == null) return void 0;
18742
+ const abstract = asOoxmlElement(abstracts[String(abstractId)]);
18743
+ if (!abstract) return void 0;
18744
+ let levelDef = abstract.elements?.find(
18745
+ (el) => el?.name === "w:lvl" && parseNumberAttr(el.attributes?.["w:ilvl"]) === ilvl
18746
+ );
18747
+ const override = numDef.elements?.find(
18748
+ (el) => el?.name === "w:lvlOverride" && parseNumberAttr(el.attributes?.["w:ilvl"]) === ilvl
18749
+ );
18750
+ const overrideLvl = findChild(override, "w:lvl");
18751
+ if (overrideLvl) {
18752
+ levelDef = overrideLvl;
18753
+ }
18754
+ const startOverride = parseNumberAttr(getAttribute(findChild(override, "w:startOverride"), "w:val"));
18755
+ if (!levelDef) return void 0;
18756
+ const numFmtEl = findNumFmtElement(levelDef);
18757
+ const lvlText = getAttribute(findChild(levelDef, "w:lvlText"), "w:val");
18758
+ const start2 = startOverride ?? parseNumberAttr(getAttribute(findChild(levelDef, "w:start"), "w:val"));
18759
+ const suffix = normalizeSuffix(getAttribute(findChild(levelDef, "w:suff"), "w:val"));
18760
+ const lvlJc = normalizeJustification(getAttribute(findChild(levelDef, "w:lvlJc"), "w:val"));
18761
+ const indent = extractIndentFromLevel(levelDef);
18762
+ const markerRun = extractMarkerRun(levelDef);
18763
+ const numFmt = normalizeNumFmt(getAttribute(numFmtEl, "w:val"));
18764
+ return {
18765
+ format: numFmt,
18766
+ lvlText,
18767
+ start: start2,
18768
+ suffix,
18769
+ lvlJc,
18770
+ resolvedLevelIndent: indent,
18771
+ resolvedMarkerRpr: markerRun
18772
+ };
18773
+ };
18581
18774
  const isTruthy = (value) => {
18582
18775
  if (value === true || value === 1) return true;
18583
18776
  if (typeof value === "string") {
@@ -18666,6 +18859,12 @@ const cloneParagraphAttrs = (attrs) => {
18666
18859
  }
18667
18860
  if (attrs.shading) clone.shading = { ...attrs.shading };
18668
18861
  if (attrs.tabs) clone.tabs = attrs.tabs.map((tab) => ({ ...tab }));
18862
+ if (attrs.dropCapDescriptor) {
18863
+ clone.dropCapDescriptor = {
18864
+ ...attrs.dropCapDescriptor,
18865
+ run: { ...attrs.dropCapDescriptor.run }
18866
+ };
18867
+ }
18669
18868
  return clone;
18670
18869
  };
18671
18870
  const buildStyleNodeFromAttrs = (attrs, spacing, indent) => {
@@ -18777,6 +18976,119 @@ const normalizeResolvedTabAlignment = (value) => {
18777
18976
  return void 0;
18778
18977
  }
18779
18978
  };
18979
+ const DEFAULT_DROP_CAP_FONT_SIZE_PX = 64;
18980
+ const DEFAULT_DROP_CAP_FONT_FAMILY = "Times New Roman";
18981
+ const extractDropCapRunFromParagraph = (para) => {
18982
+ const content = para.content;
18983
+ if (!Array.isArray(content) || content.length === 0) {
18984
+ return null;
18985
+ }
18986
+ let text = "";
18987
+ let runProperties = {};
18988
+ let textStyleMarks = {};
18989
+ const MAX_RECURSION_DEPTH = 50;
18990
+ const extractTextAndStyle = (nodes, depth = 0) => {
18991
+ if (depth > MAX_RECURSION_DEPTH) {
18992
+ console.warn(`extractTextAndStyle exceeded max recursion depth (${MAX_RECURSION_DEPTH})`);
18993
+ return false;
18994
+ }
18995
+ for (const node of nodes) {
18996
+ if (!node) continue;
18997
+ if (node.type === "text" && typeof node.text === "string" && node.text.length > 0) {
18998
+ text = node.text;
18999
+ if (Array.isArray(node.marks)) {
19000
+ for (const mark of node.marks) {
19001
+ if (mark?.type === "textStyle" && mark.attrs) {
19002
+ textStyleMarks = { ...textStyleMarks, ...mark.attrs };
19003
+ }
19004
+ }
19005
+ }
19006
+ return true;
19007
+ }
19008
+ if (node.type === "run") {
19009
+ if (node.attrs?.runProperties && typeof node.attrs.runProperties === "object") {
19010
+ runProperties = { ...runProperties, ...node.attrs.runProperties };
19011
+ }
19012
+ if (Array.isArray(node.marks)) {
19013
+ for (const mark of node.marks) {
19014
+ if (mark?.type === "textStyle" && mark.attrs) {
19015
+ textStyleMarks = { ...textStyleMarks, ...mark.attrs };
19016
+ }
19017
+ }
19018
+ }
19019
+ if (Array.isArray(node.content) && extractTextAndStyle(node.content, depth + 1)) {
19020
+ return true;
19021
+ }
19022
+ }
19023
+ if (Array.isArray(node.content) && extractTextAndStyle(node.content, depth + 1)) {
19024
+ return true;
19025
+ }
19026
+ }
19027
+ return false;
19028
+ };
19029
+ extractTextAndStyle(content);
19030
+ if (!text) {
19031
+ return null;
19032
+ }
19033
+ const mergedStyle = { ...runProperties, ...textStyleMarks };
19034
+ let fontSizePx = DEFAULT_DROP_CAP_FONT_SIZE_PX;
19035
+ const rawFontSize = mergedStyle.fontSize ?? mergedStyle["w:sz"] ?? mergedStyle.sz;
19036
+ if (rawFontSize != null) {
19037
+ if (typeof rawFontSize === "number") {
19038
+ const converted = rawFontSize > 100 ? ptToPx(rawFontSize / 2) : rawFontSize;
19039
+ fontSizePx = converted ?? DEFAULT_DROP_CAP_FONT_SIZE_PX;
19040
+ } else if (typeof rawFontSize === "string") {
19041
+ const numericPart = parseFloat(rawFontSize);
19042
+ if (Number.isFinite(numericPart)) {
19043
+ if (rawFontSize.endsWith("pt")) {
19044
+ const converted = ptToPx(numericPart);
19045
+ fontSizePx = converted ?? DEFAULT_DROP_CAP_FONT_SIZE_PX;
19046
+ } else if (rawFontSize.endsWith("px")) {
19047
+ fontSizePx = numericPart;
19048
+ } else {
19049
+ const converted = numericPart > 100 ? ptToPx(numericPart / 2) : numericPart;
19050
+ fontSizePx = converted ?? DEFAULT_DROP_CAP_FONT_SIZE_PX;
19051
+ }
19052
+ }
19053
+ }
19054
+ }
19055
+ let fontFamily = DEFAULT_DROP_CAP_FONT_FAMILY;
19056
+ const rawFontFamily = mergedStyle.fontFamily ?? mergedStyle["w:rFonts"] ?? mergedStyle.rFonts;
19057
+ if (typeof rawFontFamily === "string") {
19058
+ fontFamily = rawFontFamily;
19059
+ } else if (rawFontFamily && typeof rawFontFamily === "object") {
19060
+ const rFonts = rawFontFamily;
19061
+ const ascii = rFonts["w:ascii"] ?? rFonts.ascii;
19062
+ if (typeof ascii === "string") {
19063
+ fontFamily = ascii;
19064
+ }
19065
+ }
19066
+ const dropCapRun = {
19067
+ text,
19068
+ fontFamily,
19069
+ fontSize: fontSizePx
19070
+ };
19071
+ const bold = mergedStyle.bold ?? mergedStyle["w:b"] ?? mergedStyle.b;
19072
+ if (isTruthy(bold)) {
19073
+ dropCapRun.bold = true;
19074
+ }
19075
+ const italic = mergedStyle.italic ?? mergedStyle["w:i"] ?? mergedStyle.i;
19076
+ if (isTruthy(italic)) {
19077
+ dropCapRun.italic = true;
19078
+ }
19079
+ const color = mergedStyle.color ?? mergedStyle["w:color"] ?? mergedStyle.val;
19080
+ if (typeof color === "string" && color.length > 0 && color.toLowerCase() !== "auto") {
19081
+ dropCapRun.color = color.startsWith("#") ? color : `#${color}`;
19082
+ }
19083
+ const position = mergedStyle.position ?? mergedStyle["w:position"];
19084
+ if (position != null) {
19085
+ const posNum = pickNumber(position);
19086
+ if (posNum != null) {
19087
+ dropCapRun.position = ptToPx(posNum / 2);
19088
+ }
19089
+ }
19090
+ return dropCapRun;
19091
+ };
18780
19092
  const computeWordLayoutForParagraph = (paragraphAttrs, numberingProps, styleContext, _paragraphNode) => {
18781
19093
  if (numberingProps === null) {
18782
19094
  return null;
@@ -19042,6 +19354,24 @@ const computeParagraphAttrs = (para, styleContext, listCounterContext, converter
19042
19354
  const dropCap = framePr["w:dropCap"] ?? framePr.dropCap;
19043
19355
  if (dropCap != null && (typeof dropCap === "string" || typeof dropCap === "number" || typeof dropCap === "boolean")) {
19044
19356
  paragraphAttrs.dropCap = dropCap;
19357
+ const dropCapMode = typeof dropCap === "string" ? dropCap.toLowerCase() : "drop";
19358
+ const linesValue = pickNumber(framePr["w:lines"] ?? framePr.lines);
19359
+ const wrapValue = asString(framePr["w:wrap"] ?? framePr.wrap);
19360
+ const dropCapRunInfo = extractDropCapRunFromParagraph(para);
19361
+ if (dropCapRunInfo) {
19362
+ const descriptor = {
19363
+ mode: dropCapMode === "margin" ? "margin" : "drop",
19364
+ lines: linesValue != null && linesValue > 0 ? linesValue : 3,
19365
+ run: dropCapRunInfo
19366
+ };
19367
+ if (wrapValue) {
19368
+ const normalizedWrap = wrapValue.toLowerCase();
19369
+ if (normalizedWrap === "around" || normalizedWrap === "notbeside" || normalizedWrap === "none" || normalizedWrap === "tight") {
19370
+ descriptor.wrap = normalizedWrap === "notbeside" ? "notBeside" : normalizedWrap;
19371
+ }
19372
+ }
19373
+ paragraphAttrs.dropCapDescriptor = descriptor;
19374
+ }
19045
19375
  }
19046
19376
  const frame = {};
19047
19377
  const wrap = asString(framePr["w:wrap"] ?? framePr.wrap);
@@ -19073,6 +19403,30 @@ const computeParagraphAttrs = (para, styleContext, listCounterContext, converter
19073
19403
  const ilvl = Number.isFinite(numberingProps.ilvl) ? Math.max(0, Math.floor(Number(numberingProps.ilvl))) : 0;
19074
19404
  const listRendering = normalizeListRenderingAttrs(attrs.listRendering);
19075
19405
  const numericNumId = typeof numId === "number" ? numId : void 0;
19406
+ const resolvedLevel = resolveNumberingFromContext(numId, ilvl, converterContext?.numbering);
19407
+ if (resolvedLevel) {
19408
+ if (resolvedLevel.format && numberingProps.format == null) {
19409
+ numberingProps.format = resolvedLevel.format;
19410
+ }
19411
+ if (resolvedLevel.lvlText && numberingProps.lvlText == null) {
19412
+ numberingProps.lvlText = resolvedLevel.lvlText;
19413
+ }
19414
+ if (resolvedLevel.start != null && numberingProps.start == null) {
19415
+ numberingProps.start = resolvedLevel.start;
19416
+ }
19417
+ if (resolvedLevel.suffix && numberingProps.suffix == null) {
19418
+ numberingProps.suffix = resolvedLevel.suffix;
19419
+ }
19420
+ if (resolvedLevel.lvlJc && numberingProps.lvlJc == null) {
19421
+ numberingProps.lvlJc = resolvedLevel.lvlJc;
19422
+ }
19423
+ if (resolvedLevel.resolvedLevelIndent && !numberingProps.resolvedLevelIndent) {
19424
+ numberingProps.resolvedLevelIndent = resolvedLevel.resolvedLevelIndent;
19425
+ }
19426
+ if (resolvedLevel.resolvedMarkerRpr && !numberingProps.resolvedMarkerRpr) {
19427
+ numberingProps.resolvedMarkerRpr = resolvedLevel.resolvedMarkerRpr;
19428
+ }
19429
+ }
19076
19430
  let counterValue = 1;
19077
19431
  if (listCounterContext && typeof numericNumId === "number") {
19078
19432
  counterValue = listCounterContext.incrementListCounter(numericNumId, ilvl);
@@ -19815,13 +20169,26 @@ const dataAttrsCompatible = (a, b) => {
19815
20169
  }
19816
20170
  return true;
19817
20171
  };
20172
+ const commentsCompatible = (a, b) => {
20173
+ const aComments = a.comments ?? [];
20174
+ const bComments = b.comments ?? [];
20175
+ if (aComments.length === 0 && bComments.length === 0) return true;
20176
+ if (aComments.length !== bComments.length) return false;
20177
+ const normalize2 = (c) => `${c.commentId ?? ""}::${c.importedId ?? ""}::${c.internal ? "1" : "0"}`;
20178
+ const aKeys = aComments.map(normalize2).sort();
20179
+ const bKeys = bComments.map(normalize2).sort();
20180
+ for (let i = 0; i < aKeys.length; i++) {
20181
+ if (aKeys[i] !== bKeys[i]) return false;
20182
+ }
20183
+ return true;
20184
+ };
19818
20185
  function mergeAdjacentRuns(runs) {
19819
20186
  if (runs.length <= 1) return runs;
19820
20187
  const merged = [];
19821
20188
  let current = runs[0];
19822
20189
  for (let i = 1; i < runs.length; i++) {
19823
20190
  const next = runs[i];
19824
- const canMerge = isTextRun$1(current) && isTextRun$1(next) && !current.token && !next.token && current.pmStart != null && current.pmEnd != null && next.pmStart != null && next.pmEnd != null && current.pmEnd === next.pmStart && current.fontFamily === next.fontFamily && current.fontSize === next.fontSize && current.bold === next.bold && current.italic === next.italic && current.underline === next.underline && current.strike === next.strike && current.color === next.color && current.highlight === next.highlight && (current.letterSpacing ?? 0) === (next.letterSpacing ?? 0) && trackedChangesCompatible(current, next) && dataAttrsCompatible(current, next);
20191
+ const canMerge = isTextRun$1(current) && isTextRun$1(next) && !current.token && !next.token && current.pmStart != null && current.pmEnd != null && next.pmStart != null && next.pmEnd != null && current.pmEnd === next.pmStart && current.fontFamily === next.fontFamily && current.fontSize === next.fontSize && current.bold === next.bold && current.italic === next.italic && current.underline === next.underline && current.strike === next.strike && current.color === next.color && current.highlight === next.highlight && (current.letterSpacing ?? 0) === (next.letterSpacing ?? 0) && trackedChangesCompatible(current, next) && dataAttrsCompatible(current, next) && commentsCompatible(current, next);
19825
20192
  if (canMerge) {
19826
20193
  const currText = current.text ?? "";
19827
20194
  const nextText = next.text ?? "";
@@ -20285,25 +20652,45 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
20285
20652
  }
20286
20653
  return;
20287
20654
  }
20288
- if (node.type === "hardBreak") {
20289
- flushParagraph();
20290
- blocks.push({
20291
- kind: "pageBreak",
20292
- id: nextId(),
20293
- attrs: node.attrs || {}
20294
- });
20295
- return;
20296
- }
20297
- if (node.type === "lineBreak") {
20655
+ if (node.type === "hardBreak" || node.type === "lineBreak") {
20298
20656
  const attrs = node.attrs ?? {};
20299
- if (attrs.lineBreakType === "column") {
20657
+ const breakType = attrs.pageBreakType ?? attrs.lineBreakType ?? "line";
20658
+ if (breakType === "page") {
20659
+ flushParagraph();
20660
+ blocks.push({
20661
+ kind: "pageBreak",
20662
+ id: nextId(),
20663
+ attrs: node.attrs || {}
20664
+ });
20665
+ return;
20666
+ }
20667
+ if (breakType === "column") {
20300
20668
  flushParagraph();
20301
20669
  blocks.push({
20302
20670
  kind: "columnBreak",
20303
20671
  id: nextId(),
20304
20672
  attrs: node.attrs || {}
20305
20673
  });
20674
+ return;
20675
+ }
20676
+ const lineBreakRun = { kind: "lineBreak", attrs: {} };
20677
+ const lbAttrs = {};
20678
+ if (attrs.lineBreakType) lbAttrs.lineBreakType = String(attrs.lineBreakType);
20679
+ if (attrs.clear) lbAttrs.clear = String(attrs.clear);
20680
+ if (Object.keys(lbAttrs).length > 0) {
20681
+ lineBreakRun.attrs = lbAttrs;
20682
+ } else {
20683
+ delete lineBreakRun.attrs;
20306
20684
  }
20685
+ const pos = positions.get(node);
20686
+ if (pos) {
20687
+ lineBreakRun.pmStart = pos.start;
20688
+ lineBreakRun.pmEnd = pos.end;
20689
+ }
20690
+ if (activeSdt) {
20691
+ lineBreakRun.sdt = activeSdt;
20692
+ }
20693
+ currentRuns.push(lineBreakRun);
20307
20694
  return;
20308
20695
  }
20309
20696
  };
@@ -20896,6 +21283,22 @@ const normalizeTableWidth = (value) => {
20896
21283
  };
20897
21284
  const isTableRowNode = (node) => node.type === "tableRow" || node.type === "table_row";
20898
21285
  const isTableCellNode = (node) => node.type === "tableCell" || node.type === "table_cell" || node.type === "tableHeader" || node.type === "table_header";
21286
+ const normalizeRowHeight = (rowProps) => {
21287
+ if (!rowProps || typeof rowProps !== "object") return void 0;
21288
+ const rawRowHeight = rowProps.rowHeight;
21289
+ if (!rawRowHeight || typeof rawRowHeight !== "object") return void 0;
21290
+ const heightObj = rawRowHeight;
21291
+ const rawValue = pickNumber(heightObj.value ?? heightObj.val);
21292
+ if (rawValue == null) return void 0;
21293
+ const rawRule = heightObj.rule ?? heightObj.hRule;
21294
+ const rule = rawRule === "exact" || rawRule === "atLeast" || rawRule === "auto" ? rawRule : "atLeast";
21295
+ const isLikelyTwips = rawValue >= 300 || Math.abs(rawValue % 15) < 1e-6;
21296
+ const valuePx = isLikelyTwips ? twipsToPx$1(rawValue) : rawValue;
21297
+ return {
21298
+ value: valuePx,
21299
+ rule
21300
+ };
21301
+ };
20899
21302
  const parseTableCell = (args) => {
20900
21303
  const { cellNode, rowIndex, cellIndex, context, defaultCellPadding } = args;
20901
21304
  if (!isTableCellNode(cellNode) || !Array.isArray(cellNode.content)) {
@@ -20933,8 +21336,9 @@ const parseTableCell = (args) => {
20933
21336
  const padding = extractCellPadding(cellNode.attrs ?? {}) ?? (defaultCellPadding ? { ...defaultCellPadding } : void 0);
20934
21337
  if (padding) cellAttrs.padding = padding;
20935
21338
  const verticalAlign = cellNode.attrs?.verticalAlign;
20936
- if (verticalAlign === "top" || verticalAlign === "middle" || verticalAlign === "bottom") {
20937
- cellAttrs.verticalAlign = verticalAlign;
21339
+ const normalizedVerticalAlign = verticalAlign === "middle" ? "center" : verticalAlign === "center" ? "center" : verticalAlign;
21340
+ if (normalizedVerticalAlign === "top" || normalizedVerticalAlign === "center" || normalizedVerticalAlign === "bottom") {
21341
+ cellAttrs.verticalAlign = normalizedVerticalAlign;
20938
21342
  }
20939
21343
  const background = cellNode.attrs?.background;
20940
21344
  if (background && typeof background.color === "string") {
@@ -20977,15 +21381,89 @@ const parseTableRow = (args) => {
20977
21381
  });
20978
21382
  if (cells.length === 0) return null;
20979
21383
  const rowProps = rowNode.attrs?.tableRowProperties;
21384
+ const rowHeight = normalizeRowHeight(rowProps);
20980
21385
  const attrs = rowProps && typeof rowProps === "object" ? {
20981
- tableRowProperties: rowProps
20982
- } : void 0;
21386
+ tableRowProperties: rowProps,
21387
+ ...rowHeight ? { rowHeight } : {}
21388
+ } : rowHeight ? { rowHeight } : void 0;
20983
21389
  return {
20984
21390
  id: context.nextBlockId(`row-${rowIndex}`),
20985
21391
  cells,
20986
21392
  attrs
20987
21393
  };
20988
21394
  };
21395
+ function extractFloatingTableAnchorWrap(node) {
21396
+ const tableProperties = node.attrs?.tableProperties;
21397
+ const floatingProps = tableProperties?.floatingTableProperties;
21398
+ if (!floatingProps) {
21399
+ return {};
21400
+ }
21401
+ const hasPositioning = floatingProps.tblpX !== void 0 || floatingProps.tblpY !== void 0 || floatingProps.tblpXSpec !== void 0 || floatingProps.tblpYSpec !== void 0 || floatingProps.horzAnchor !== void 0 || floatingProps.vertAnchor !== void 0;
21402
+ if (!hasPositioning) {
21403
+ return {};
21404
+ }
21405
+ const mapHorzAnchor = (val) => {
21406
+ switch (val) {
21407
+ case "page":
21408
+ return "page";
21409
+ case "margin":
21410
+ return "margin";
21411
+ case "text":
21412
+ default:
21413
+ return "column";
21414
+ }
21415
+ };
21416
+ const mapVertAnchor = (val) => {
21417
+ switch (val) {
21418
+ case "page":
21419
+ return "page";
21420
+ case "margin":
21421
+ return "margin";
21422
+ case "text":
21423
+ default:
21424
+ return "paragraph";
21425
+ }
21426
+ };
21427
+ const anchor = {
21428
+ isAnchored: true,
21429
+ hRelativeFrom: mapHorzAnchor(floatingProps.horzAnchor),
21430
+ vRelativeFrom: mapVertAnchor(floatingProps.vertAnchor)
21431
+ };
21432
+ if (floatingProps.tblpXSpec) {
21433
+ anchor.alignH = floatingProps.tblpXSpec;
21434
+ }
21435
+ if (floatingProps.tblpYSpec) {
21436
+ anchor.alignV = floatingProps.tblpYSpec;
21437
+ }
21438
+ if (floatingProps.tblpX !== void 0) {
21439
+ anchor.offsetH = twipsToPx$1(floatingProps.tblpX);
21440
+ }
21441
+ if (floatingProps.tblpY !== void 0) {
21442
+ anchor.offsetV = twipsToPx$1(floatingProps.tblpY);
21443
+ }
21444
+ const hasDistances = floatingProps.leftFromText !== void 0 || floatingProps.rightFromText !== void 0 || floatingProps.topFromText !== void 0 || floatingProps.bottomFromText !== void 0;
21445
+ const wrap = {
21446
+ type: "Square",
21447
+ // Floating tables with text distances use square wrapping
21448
+ wrapText: "bothSides"
21449
+ // Default to text on both sides
21450
+ };
21451
+ if (hasDistances) {
21452
+ if (floatingProps.topFromText !== void 0) {
21453
+ wrap.distTop = twipsToPx$1(floatingProps.topFromText);
21454
+ }
21455
+ if (floatingProps.bottomFromText !== void 0) {
21456
+ wrap.distBottom = twipsToPx$1(floatingProps.bottomFromText);
21457
+ }
21458
+ if (floatingProps.leftFromText !== void 0) {
21459
+ wrap.distLeft = twipsToPx$1(floatingProps.leftFromText);
21460
+ }
21461
+ if (floatingProps.rightFromText !== void 0) {
21462
+ wrap.distRight = twipsToPx$1(floatingProps.rightFromText);
21463
+ }
21464
+ }
21465
+ return { anchor, wrap };
21466
+ }
20989
21467
  function tableNodeToBlock$1(node, nextBlockId, positions, defaultFont, defaultSize, _styleContext, trackedChanges, bookmarks, hyperlinkConfig, themeColors, paragraphToFlowBlocks2, converterContext) {
20990
21468
  if (!Array.isArray(node.content) || node.content.length === 0) return null;
20991
21469
  if (!paragraphToFlowBlocks2) return null;
@@ -21050,6 +21528,10 @@ function tableNodeToBlock$1(node, nextBlockId, positions, defaultFont, defaultSi
21050
21528
  if (tableLayout) {
21051
21529
  tableAttrs.tableLayout = tableLayout;
21052
21530
  }
21531
+ const tableProperties = node.attrs?.tableProperties;
21532
+ if (tableProperties && typeof tableProperties === "object") {
21533
+ tableAttrs.tableProperties = tableProperties;
21534
+ }
21053
21535
  let columnWidths = void 0;
21054
21536
  const twipsToPixels2 = (twips) => {
21055
21537
  const PIXELS_PER_INCH2 = 96;
@@ -21093,12 +21575,15 @@ function tableNodeToBlock$1(node, nextBlockId, positions, defaultFont, defaultSi
21093
21575
  columnWidths = void 0;
21094
21576
  }
21095
21577
  }
21578
+ const { anchor, wrap } = extractFloatingTableAnchorWrap(node);
21096
21579
  const tableBlock = {
21097
21580
  kind: "table",
21098
21581
  id: nextBlockId("table"),
21099
21582
  rows,
21100
21583
  attrs: Object.keys(tableAttrs).length > 0 ? tableAttrs : void 0,
21101
- columnWidths
21584
+ columnWidths,
21585
+ ...anchor ? { anchor } : {},
21586
+ ...wrap ? { wrap } : {}
21102
21587
  };
21103
21588
  return tableBlock;
21104
21589
  }
@@ -21327,7 +21812,39 @@ function toFlowBlocks(pmDoc, options) {
21327
21812
  }
21328
21813
  instrumentation?.log?.({ totalBlocks: blocks.length, blockCounts, bookmarks: bookmarks.size });
21329
21814
  const hydratedBlocks = hydrateImageBlocks(blocks, options?.mediaFiles);
21330
- return { blocks: hydratedBlocks, bookmarks };
21815
+ const mergedBlocks = mergeDropCapParagraphs(hydratedBlocks);
21816
+ return { blocks: mergedBlocks, bookmarks };
21817
+ }
21818
+ function mergeDropCapParagraphs(blocks) {
21819
+ const result = [];
21820
+ let i = 0;
21821
+ while (i < blocks.length) {
21822
+ const block = blocks[i];
21823
+ if (block.kind === "paragraph" && block.attrs?.dropCapDescriptor && i + 1 < blocks.length) {
21824
+ const dropCapBlock = block;
21825
+ const nextBlock = blocks[i + 1];
21826
+ if (nextBlock.kind === "paragraph") {
21827
+ const textBlock = nextBlock;
21828
+ const mergedBlock = {
21829
+ kind: "paragraph",
21830
+ id: textBlock.id,
21831
+ runs: textBlock.runs,
21832
+ attrs: {
21833
+ ...textBlock.attrs,
21834
+ dropCapDescriptor: dropCapBlock.attrs?.dropCapDescriptor,
21835
+ // Clear the legacy dropCap flag on the merged block
21836
+ dropCap: void 0
21837
+ }
21838
+ };
21839
+ result.push(mergedBlock);
21840
+ i += 2;
21841
+ continue;
21842
+ }
21843
+ }
21844
+ result.push(block);
21845
+ i += 1;
21846
+ }
21847
+ return result;
21331
21848
  }
21332
21849
  function paragraphToFlowBlocks(para, nextBlockId, positions, defaultFont, defaultSize, styleContext, listCounterContext, trackedChanges, bookmarks, hyperlinkConfig = DEFAULT_HYPERLINK_CONFIG$1, themeColors, converterContext) {
21333
21850
  return paragraphToFlowBlocks$1(
@@ -21402,7 +21919,7 @@ function getMeasurementContext() {
21402
21919
  return measurementCtx;
21403
21920
  }
21404
21921
  function getRunFontString(run) {
21405
- if (run.kind === "tab" || run.kind === "image") {
21922
+ if (run.kind === "tab" || run.kind === "lineBreak" || run.kind === "break" || "src" in run) {
21406
21923
  return "normal normal 16px Arial";
21407
21924
  }
21408
21925
  const style = run.italic ? "italic" : "normal";
@@ -21421,7 +21938,15 @@ function sliceRunsForLine$1(block, line) {
21421
21938
  result.push(run);
21422
21939
  continue;
21423
21940
  }
21424
- if (run.kind === "image") {
21941
+ if ("src" in run) {
21942
+ result.push(run);
21943
+ continue;
21944
+ }
21945
+ if (run.kind === "lineBreak") {
21946
+ result.push(run);
21947
+ continue;
21948
+ }
21949
+ if (run.kind === "break") {
21425
21950
  result.push(run);
21426
21951
  continue;
21427
21952
  }
@@ -21448,13 +21973,17 @@ function sliceRunsForLine$1(block, line) {
21448
21973
  }
21449
21974
  function measureCharacterX(block, line, charOffset) {
21450
21975
  const ctx2 = getMeasurementContext();
21976
+ const hasExplicitPositioning = line.segments?.some((seg) => seg.x !== void 0);
21977
+ if (hasExplicitPositioning && line.segments && ctx2) {
21978
+ return measureCharacterXSegmentBased(block, line, charOffset, ctx2);
21979
+ }
21451
21980
  if (!ctx2) {
21452
21981
  const runs2 = sliceRunsForLine$1(block, line);
21453
21982
  const charsInLine = Math.max(
21454
21983
  1,
21455
21984
  runs2.reduce((sum, run) => {
21456
21985
  if (isTabRun$1(run)) return sum + TAB_CHAR_LENGTH;
21457
- if (run.kind === "image") return sum;
21986
+ if ("src" in run || run.kind === "lineBreak" || run.kind === "break") return sum;
21458
21987
  return sum + (run.text ?? "").length;
21459
21988
  }, 0)
21460
21989
  );
@@ -21475,7 +22004,7 @@ function measureCharacterX(block, line, charOffset) {
21475
22004
  currentCharOffset += runLength2;
21476
22005
  continue;
21477
22006
  }
21478
- const text = run.kind === "image" ? "" : run.text ?? "";
22007
+ const text = "src" in run || run.kind === "lineBreak" || run.kind === "break" ? "" : run.text ?? "";
21479
22008
  const runLength = text.length;
21480
22009
  if (currentCharOffset + runLength >= charOffset) {
21481
22010
  const offsetInRun = charOffset - currentCharOffset;
@@ -21492,6 +22021,47 @@ function measureCharacterX(block, line, charOffset) {
21492
22021
  }
21493
22022
  return currentX;
21494
22023
  }
22024
+ function measureCharacterXSegmentBased(block, line, charOffset, ctx2) {
22025
+ if (block.kind !== "paragraph" || !line.segments) return 0;
22026
+ let lineCharCount = 0;
22027
+ for (const segment of line.segments) {
22028
+ const run = block.runs[segment.runIndex];
22029
+ if (!run) continue;
22030
+ const segmentChars = segment.toChar - segment.fromChar;
22031
+ if (lineCharCount + segmentChars >= charOffset) {
22032
+ const offsetInSegment = charOffset - lineCharCount;
22033
+ let segmentBaseX = segment.x;
22034
+ if (segmentBaseX === void 0) {
22035
+ segmentBaseX = 0;
22036
+ for (const prevSeg of line.segments) {
22037
+ if (prevSeg === segment) break;
22038
+ const prevRun = block.runs[prevSeg.runIndex];
22039
+ if (!prevRun) continue;
22040
+ if (prevSeg.x !== void 0) {
22041
+ segmentBaseX = prevSeg.x + (prevSeg.width ?? 0);
22042
+ } else {
22043
+ segmentBaseX += prevSeg.width ?? 0;
22044
+ }
22045
+ }
22046
+ }
22047
+ if (isTabRun$1(run)) {
22048
+ return segmentBaseX + (offsetInSegment > 0 ? segment.width ?? 0 : 0);
22049
+ }
22050
+ if ("src" in run || run.kind === "lineBreak" || run.kind === "break") {
22051
+ return segmentBaseX + (offsetInSegment >= segmentChars ? segment.width ?? 0 : 0);
22052
+ }
22053
+ const text = run.text ?? "";
22054
+ const segmentText = text.slice(segment.fromChar, segment.toChar);
22055
+ const textUpToTarget = segmentText.slice(0, offsetInSegment);
22056
+ ctx2.font = getRunFontString(run);
22057
+ const measured = ctx2.measureText(textUpToTarget);
22058
+ const spacingWidth = computeLetterSpacingWidth(run, offsetInSegment, segmentChars);
22059
+ return segmentBaseX + measured.width + spacingWidth;
22060
+ }
22061
+ lineCharCount += segmentChars;
22062
+ }
22063
+ return line.width;
22064
+ }
21495
22065
  function findCharacterAtX(block, line, x, pmStart) {
21496
22066
  const ctx2 = getMeasurementContext();
21497
22067
  if (!ctx2) {
@@ -21500,7 +22070,7 @@ function findCharacterAtX(block, line, x, pmStart) {
21500
22070
  1,
21501
22071
  runs2.reduce((sum, run) => {
21502
22072
  if (isTabRun$1(run)) return sum + TAB_CHAR_LENGTH;
21503
- if (run.kind === "image") return sum;
22073
+ if ("src" in run || run.kind === "lineBreak" || run.kind === "break") return sum;
21504
22074
  return sum + (run.text ?? "").length;
21505
22075
  }, 0)
21506
22076
  );
@@ -21535,7 +22105,7 @@ function findCharacterAtX(block, line, x, pmStart) {
21535
22105
  currentCharOffset += TAB_CHAR_LENGTH;
21536
22106
  continue;
21537
22107
  }
21538
- const text = run.kind === "image" ? "" : run.text ?? "";
22108
+ const text = "src" in run || run.kind === "lineBreak" || run.kind === "break" ? "" : run.text ?? "";
21539
22109
  const runLength = text.length;
21540
22110
  if (runLength === 0) continue;
21541
22111
  ctx2.font = getRunFontString(run);
@@ -21572,7 +22142,7 @@ function findCharacterAtX(block, line, x, pmStart) {
21572
22142
  };
21573
22143
  }
21574
22144
  const computeLetterSpacingWidth = (run, precedingChars, runLength) => {
21575
- if (isTabRun$1(run) || run.kind === "image" || !run.letterSpacing) {
22145
+ if (isTabRun$1(run) || "src" in run || !("letterSpacing" in run) || !run.letterSpacing) {
21576
22146
  return 0;
21577
22147
  }
21578
22148
  const maxGaps = Math.max(runLength - 1, 0);
@@ -21582,6 +22152,8 @@ const computeLetterSpacingWidth = (run, precedingChars, runLength) => {
21582
22152
  const clamped = Math.min(Math.max(precedingChars, 0), maxGaps);
21583
22153
  return clamped * run.letterSpacing;
21584
22154
  };
22155
+ const log = (...args) => {
22156
+ };
21585
22157
  const CLASS_NAMES$1 = {
21586
22158
  page: "superdoc-page",
21587
22159
  fragment: "superdoc-fragment",
@@ -21597,6 +22169,11 @@ function clickToPositionDom(domContainer, clientX, clientY) {
21597
22169
  const pageLocalY = clientY - pageRect.top;
21598
22170
  const viewX = pageRect.left + pageLocalX;
21599
22171
  const viewY = pageRect.top + pageLocalY;
22172
+ log("Page found:", {
22173
+ pageIndex: pageEl.dataset.pageIndex,
22174
+ pageRect: { left: pageRect.left, top: pageRect.top, width: pageRect.width, height: pageRect.height },
22175
+ viewCoords: { viewX, viewY }
22176
+ });
21600
22177
  let hitChain = [];
21601
22178
  const doc2 = document;
21602
22179
  if (typeof doc2.elementsFromPoint === "function") {
@@ -21608,15 +22185,62 @@ function clickToPositionDom(domContainer, clientX, clientY) {
21608
22185
  if (!Array.isArray(hitChain)) {
21609
22186
  return null;
21610
22187
  }
22188
+ const hitChainData = hitChain.map((el) => {
22189
+ const rect = el.getBoundingClientRect();
22190
+ return {
22191
+ tag: el.tagName,
22192
+ classes: el.className,
22193
+ blockId: el.dataset?.blockId,
22194
+ pmStart: el.dataset?.pmStart,
22195
+ pmEnd: el.dataset?.pmEnd,
22196
+ rect: {
22197
+ top: Math.round(rect.top),
22198
+ bottom: Math.round(rect.bottom),
22199
+ left: Math.round(rect.left),
22200
+ right: Math.round(rect.right),
22201
+ height: Math.round(rect.height)
22202
+ }
22203
+ };
22204
+ });
22205
+ log("Hit chain elements:", JSON.stringify(hitChainData, null, 2));
22206
+ const allFragments = Array.from(pageEl.querySelectorAll(`.${CLASS_NAMES$1.fragment}`));
22207
+ const fragmentData = allFragments.map((el) => {
22208
+ const rect = el.getBoundingClientRect();
22209
+ return {
22210
+ blockId: el.dataset.blockId,
22211
+ pmStart: el.dataset.pmStart,
22212
+ pmEnd: el.dataset.pmEnd,
22213
+ rect: {
22214
+ top: Math.round(rect.top),
22215
+ bottom: Math.round(rect.bottom),
22216
+ left: Math.round(rect.left),
22217
+ right: Math.round(rect.right),
22218
+ height: Math.round(rect.height)
22219
+ }
22220
+ };
22221
+ });
22222
+ log("All fragments on page:", JSON.stringify(fragmentData, null, 2));
21611
22223
  const fragmentEl = hitChain.find((el) => el.classList?.contains?.(CLASS_NAMES$1.fragment));
21612
22224
  if (!fragmentEl) {
21613
22225
  const fallbackFragment = pageEl.querySelector(`.${CLASS_NAMES$1.fragment}`);
21614
22226
  if (!fallbackFragment) {
21615
22227
  return null;
21616
22228
  }
21617
- return processFragment(fallbackFragment, viewX, viewY);
22229
+ log("Using fallback fragment:", {
22230
+ blockId: fallbackFragment.dataset.blockId,
22231
+ pmStart: fallbackFragment.dataset.pmStart,
22232
+ pmEnd: fallbackFragment.dataset.pmEnd
22233
+ });
22234
+ const result2 = processFragment(fallbackFragment, viewX, viewY);
22235
+ return result2;
21618
22236
  }
21619
- return processFragment(fragmentEl, viewX, viewY);
22237
+ log("Fragment found:", {
22238
+ blockId: fragmentEl.dataset.blockId,
22239
+ pmStart: fragmentEl.dataset.pmStart,
22240
+ pmEnd: fragmentEl.dataset.pmEnd
22241
+ });
22242
+ const result = processFragment(fragmentEl, viewX, viewY);
22243
+ return result;
21620
22244
  }
21621
22245
  function findPageElement(domContainer, clientX, clientY) {
21622
22246
  if (domContainer.classList?.contains?.(CLASS_NAMES$1.page)) {
@@ -21648,7 +22272,20 @@ function findPageElement(domContainer, clientX, clientY) {
21648
22272
  return null;
21649
22273
  }
21650
22274
  function processFragment(fragmentEl, viewX, viewY) {
22275
+ log("processFragment:", { viewX, viewY, blockId: fragmentEl.dataset.blockId });
21651
22276
  const lineEls = Array.from(fragmentEl.querySelectorAll(`.${CLASS_NAMES$1.line}`));
22277
+ log(
22278
+ "Lines in fragment:",
22279
+ lineEls.map((el, i) => {
22280
+ const rect = el.getBoundingClientRect();
22281
+ return {
22282
+ index: i,
22283
+ pmStart: el.dataset.pmStart,
22284
+ pmEnd: el.dataset.pmEnd,
22285
+ rect: { top: rect.top, bottom: rect.bottom, left: rect.left, right: rect.right }
22286
+ };
22287
+ })
22288
+ );
21652
22289
  if (lineEls.length === 0) {
21653
22290
  return null;
21654
22291
  }
@@ -21658,10 +22295,30 @@ function processFragment(fragmentEl, viewX, viewY) {
21658
22295
  }
21659
22296
  const lineStart = Number(lineEl.dataset.pmStart ?? "NaN");
21660
22297
  const lineEnd = Number(lineEl.dataset.pmEnd ?? "NaN");
22298
+ const lineRect = lineEl.getBoundingClientRect();
22299
+ log("Selected line:", {
22300
+ pmStart: lineStart,
22301
+ pmEnd: lineEnd,
22302
+ rect: { top: lineRect.top, bottom: lineRect.bottom, left: lineRect.left, right: lineRect.right }
22303
+ });
21661
22304
  if (!Number.isFinite(lineStart) || !Number.isFinite(lineEnd)) {
21662
22305
  return null;
21663
22306
  }
21664
22307
  const spanEls = Array.from(lineEl.querySelectorAll("span"));
22308
+ log(
22309
+ "Spans in line:",
22310
+ spanEls.map((el, i) => {
22311
+ const rect = el.getBoundingClientRect();
22312
+ return {
22313
+ index: i,
22314
+ pmStart: el.dataset.pmStart,
22315
+ pmEnd: el.dataset.pmEnd,
22316
+ text: el.textContent?.substring(0, 20) + (el.textContent && el.textContent.length > 20 ? "..." : ""),
22317
+ visibility: el.style.visibility,
22318
+ rect: { left: rect.left, right: rect.right, width: rect.width }
22319
+ };
22320
+ })
22321
+ );
21665
22322
  if (spanEls.length === 0) {
21666
22323
  return lineStart;
21667
22324
  }
@@ -21679,6 +22336,14 @@ function processFragment(fragmentEl, viewX, viewY) {
21679
22336
  }
21680
22337
  const spanStart = Number(targetSpan.dataset.pmStart ?? "NaN");
21681
22338
  const spanEnd = Number(targetSpan.dataset.pmEnd ?? "NaN");
22339
+ const targetRect = targetSpan.getBoundingClientRect();
22340
+ log("Target span:", {
22341
+ pmStart: spanStart,
22342
+ pmEnd: spanEnd,
22343
+ text: targetSpan.textContent?.substring(0, 30),
22344
+ visibility: targetSpan.style.visibility,
22345
+ rect: { left: targetRect.left, right: targetRect.right, width: targetRect.width }
22346
+ });
21682
22347
  if (!Number.isFinite(spanStart) || !Number.isFinite(spanEnd)) {
21683
22348
  return null;
21684
22349
  }
@@ -21698,28 +22363,53 @@ function findLineAtY(lineEls, viewY) {
21698
22363
  if (lineEls.length === 0) {
21699
22364
  return null;
21700
22365
  }
21701
- for (const lineEl of lineEls) {
22366
+ for (let i = 0; i < lineEls.length; i++) {
22367
+ const lineEl = lineEls[i];
21702
22368
  const rect = lineEl.getBoundingClientRect();
21703
22369
  if (viewY >= rect.top && viewY <= rect.bottom) {
22370
+ log("findLineAtY: Found line at index", i, {
22371
+ pmStart: lineEl.dataset.pmStart,
22372
+ pmEnd: lineEl.dataset.pmEnd,
22373
+ rect: { top: rect.top, bottom: rect.bottom },
22374
+ viewY
22375
+ });
21704
22376
  return lineEl;
21705
22377
  }
21706
22378
  }
21707
- return lineEls[lineEls.length - 1];
22379
+ const lastLine = lineEls[lineEls.length - 1];
22380
+ log("findLineAtY: Y beyond all lines, using last line:", {
22381
+ pmStart: lastLine.dataset.pmStart,
22382
+ pmEnd: lastLine.dataset.pmEnd,
22383
+ viewY
22384
+ });
22385
+ return lastLine;
21708
22386
  }
21709
22387
  function findSpanAtX(spanEls, viewX) {
21710
22388
  if (spanEls.length === 0) {
21711
22389
  return null;
21712
22390
  }
21713
22391
  let targetSpan = spanEls[0];
21714
- for (const span of spanEls) {
22392
+ for (let i = 0; i < spanEls.length; i++) {
22393
+ const span = spanEls[i];
21715
22394
  const rect = span.getBoundingClientRect();
21716
22395
  if (viewX >= rect.left && viewX <= rect.right) {
22396
+ log("findSpanAtX: Found containing span at index", i, {
22397
+ pmStart: span.dataset.pmStart,
22398
+ pmEnd: span.dataset.pmEnd,
22399
+ rect: { left: rect.left, right: rect.right },
22400
+ viewX
22401
+ });
21717
22402
  return span;
21718
22403
  }
21719
22404
  if (viewX > rect.right) {
21720
22405
  targetSpan = span;
21721
22406
  }
21722
22407
  }
22408
+ log("findSpanAtX: No containing span, using nearest:", {
22409
+ pmStart: targetSpan.dataset.pmStart,
22410
+ pmEnd: targetSpan.dataset.pmEnd,
22411
+ viewX
22412
+ });
21723
22413
  return targetSpan;
21724
22414
  }
21725
22415
  function findCharIndexAtX(textNode, span, targetX) {
@@ -21848,8 +22538,42 @@ function createFloatingObjectManager(columns, margins, pageWidth) {
21848
22538
  };
21849
22539
  zones.push(zone);
21850
22540
  },
22541
+ registerTable(tableBlock, measure, anchorY, columnIndex, pageNumber) {
22542
+ if (!tableBlock.anchor?.isAnchored) {
22543
+ return;
22544
+ }
22545
+ const { wrap, anchor } = tableBlock;
22546
+ const wrapType = wrap?.type ?? "None";
22547
+ if (wrapType === "None") {
22548
+ return;
22549
+ }
22550
+ const tableWidth = measure.totalWidth ?? 0;
22551
+ const tableHeight = measure.totalHeight ?? 0;
22552
+ const x = computeTableAnchorX(anchor, columnIndex, columns, tableWidth, margins, pageWidth);
22553
+ const y = anchorY + (anchor.offsetV ?? 0);
22554
+ const zone = {
22555
+ imageBlockId: tableBlock.id,
22556
+ // Reusing imageBlockId field for table id
22557
+ pageNumber,
22558
+ columnIndex,
22559
+ bounds: {
22560
+ x,
22561
+ y,
22562
+ width: tableWidth,
22563
+ height: tableHeight
22564
+ },
22565
+ distances: {
22566
+ top: wrap?.distTop ?? 0,
22567
+ bottom: wrap?.distBottom ?? 0,
22568
+ left: wrap?.distLeft ?? 0,
22569
+ right: wrap?.distRight ?? 0
22570
+ },
22571
+ wrapMode: computeTableWrapMode(wrap)
22572
+ };
22573
+ zones.push(zone);
22574
+ },
21851
22575
  getExclusionsForLine(lineY, lineHeight, columnIndex, pageNumber) {
21852
- return zones.filter((zone) => {
22576
+ const result = zones.filter((zone) => {
21853
22577
  if (zone.pageNumber !== pageNumber || zone.columnIndex !== columnIndex) {
21854
22578
  return false;
21855
22579
  }
@@ -21857,8 +22581,10 @@ function createFloatingObjectManager(columns, margins, pageWidth) {
21857
22581
  const lineBottom = lineY + lineHeight;
21858
22582
  const zoneTop = zone.bounds.y - zone.distances.top;
21859
22583
  const zoneBottom = zone.bounds.y + zone.bounds.height + zone.distances.bottom;
21860
- return lineBottom > zoneTop && lineTop < zoneBottom;
22584
+ const overlaps = lineBottom > zoneTop && lineTop < zoneBottom;
22585
+ return overlaps;
21861
22586
  });
22587
+ return result;
21862
22588
  },
21863
22589
  computeAvailableWidth(lineY, lineHeight, baseWidth, columnIndex, pageNumber) {
21864
22590
  const exclusions = this.getExclusionsForLine(lineY, lineHeight, columnIndex, pageNumber);
@@ -21871,6 +22597,8 @@ function createFloatingObjectManager(columns, margins, pageWidth) {
21871
22597
  }
21872
22598
  const leftFloats = [];
21873
22599
  const rightFloats = [];
22600
+ const columnOrigin = marginLeft + columnIndex * (columns.width + columns.gap);
22601
+ const columnCenter = columnOrigin + baseWidth / 2;
21874
22602
  for (const zone of wrappingZones) {
21875
22603
  if (zone.wrapMode === "left") {
21876
22604
  leftFloats.push(zone);
@@ -21878,7 +22606,7 @@ function createFloatingObjectManager(columns, margins, pageWidth) {
21878
22606
  rightFloats.push(zone);
21879
22607
  } else if (zone.wrapMode === "both" || zone.wrapMode === "largest") {
21880
22608
  const zoneCenter = zone.bounds.x + zone.bounds.width / 2;
21881
- if (zoneCenter < baseWidth / 2) {
22609
+ if (zoneCenter < columnCenter) {
21882
22610
  leftFloats.push(zone);
21883
22611
  } else {
21884
22612
  rightFloats.push(zone);
@@ -21890,7 +22618,6 @@ function createFloatingObjectManager(columns, margins, pageWidth) {
21890
22618
  const boundary = zone.bounds.x + zone.bounds.width + zone.distances.left + zone.distances.right;
21891
22619
  leftBoundary = Math.max(leftBoundary, boundary);
21892
22620
  }
21893
- const columnOrigin = marginLeft + columnIndex * (columns.width + columns.gap);
21894
22621
  const columnRightEdge = columnOrigin + baseWidth;
21895
22622
  let rightBoundary = columnRightEdge;
21896
22623
  for (const zone of rightFloats) {
@@ -21952,6 +22679,49 @@ function computeWrapMode(wrap, _anchor) {
21952
22679
  if (wrapText === "largest") return "largest";
21953
22680
  return "both";
21954
22681
  }
22682
+ function computeTableAnchorX(anchor, columnIndex, columns, tableWidth, margins, pageWidth) {
22683
+ const alignH = anchor.alignH ?? "left";
22684
+ const offsetH = anchor.offsetH ?? 0;
22685
+ const marginLeft = Math.max(0, margins?.left ?? 0);
22686
+ const marginRight = Math.max(0, margins?.right ?? 0);
22687
+ const contentWidth = pageWidth != null ? Math.max(1, pageWidth - (marginLeft + marginRight)) : columns.width;
22688
+ const contentLeft = marginLeft;
22689
+ const columnLeft = contentLeft + columnIndex * (columns.width + columns.gap);
22690
+ const relativeFrom = anchor.hRelativeFrom ?? "column";
22691
+ let baseX;
22692
+ let availableWidth;
22693
+ if (relativeFrom === "page") {
22694
+ if (columns.count === 1) {
22695
+ baseX = contentLeft;
22696
+ availableWidth = contentWidth;
22697
+ } else {
22698
+ baseX = 0;
22699
+ availableWidth = pageWidth != null ? pageWidth : contentWidth;
22700
+ }
22701
+ } else if (relativeFrom === "margin") {
22702
+ baseX = contentLeft;
22703
+ availableWidth = contentWidth;
22704
+ } else {
22705
+ baseX = columnLeft;
22706
+ availableWidth = columns.width;
22707
+ }
22708
+ let effectiveAlignH = alignH;
22709
+ if (alignH === "inside") effectiveAlignH = "left";
22710
+ if (alignH === "outside") effectiveAlignH = "right";
22711
+ const result = effectiveAlignH === "left" ? baseX + offsetH : effectiveAlignH === "right" ? baseX + availableWidth - tableWidth - offsetH : effectiveAlignH === "center" ? baseX + (availableWidth - tableWidth) / 2 + offsetH : baseX;
22712
+ return result;
22713
+ }
22714
+ function computeTableWrapMode(wrap) {
22715
+ if (!wrap) return "none";
22716
+ if (wrap.type === "None") {
22717
+ return "none";
22718
+ }
22719
+ const wrapText = wrap.wrapText ?? "bothSides";
22720
+ if (wrapText === "left") return "right";
22721
+ if (wrapText === "right") return "left";
22722
+ if (wrapText === "largest") return "largest";
22723
+ return "both";
22724
+ }
21955
22725
  function computeNextSectionPropsAtBreak(blocks) {
21956
22726
  const nextSectionPropsAtBreak = /* @__PURE__ */ new Map();
21957
22727
  const docxBreakIndexes = [];
@@ -22213,9 +22983,6 @@ const extractBlockPmRange = (block) => {
22213
22983
  pmEnd: end2 ?? (start2 != null ? start2 + 1 : void 0)
22214
22984
  };
22215
22985
  };
22216
- const anchorDebugLog = (...args) => {
22217
- return;
22218
- };
22219
22986
  function layoutParagraphBlock(ctx2, anchors) {
22220
22987
  const { block, measure, columnWidth, ensurePage, advanceColumn, columnX, floatManager } = ctx2;
22221
22988
  const remeasureParagraph2 = ctx2.remeasureParagraph;
@@ -22224,23 +22991,45 @@ function layoutParagraphBlock(ctx2, anchors) {
22224
22991
  for (const entry of anchors.anchoredDrawings) {
22225
22992
  if (anchors.placedAnchoredIds.has(entry.block.id)) continue;
22226
22993
  const state = ensurePage();
22227
- const baseAnchorY = state.cursorY;
22228
- const firstLineHeight = measure.lines?.[0]?.lineHeight ?? 0;
22229
22994
  const vRelativeFrom = entry.block.anchor?.vRelativeFrom;
22230
- const paragraphAdjustment = vRelativeFrom === "paragraph" ? firstLineHeight / 2 : 0;
22231
- const anchorY = baseAnchorY + paragraphAdjustment;
22232
- anchorDebugLog("Positioning anchored image:", {
22233
- blockId: entry.block.id,
22234
- baseAnchorY,
22235
- paragraphAdjustment,
22236
- anchorY,
22237
- offsetV: entry.block.anchor?.offsetV,
22238
- finalY: anchorY + (entry.block.anchor?.offsetV ?? 0),
22239
- measureHeight: entry.measure.height,
22240
- measureWidth: entry.measure.width,
22241
- pageNumber: state.page.number,
22242
- vRelativeFrom
22243
- });
22995
+ const alignV = entry.block.anchor?.alignV;
22996
+ const offsetV = entry.block.anchor?.offsetV ?? 0;
22997
+ const imageHeight = entry.measure.height;
22998
+ const contentTop = state.topMargin;
22999
+ const contentBottom = state.contentBottom;
23000
+ const contentHeight = Math.max(0, contentBottom - contentTop);
23001
+ let anchorY;
23002
+ if (vRelativeFrom === "margin") {
23003
+ if (alignV === "top") {
23004
+ anchorY = contentTop + offsetV;
23005
+ } else if (alignV === "bottom") {
23006
+ anchorY = contentBottom - imageHeight + offsetV;
23007
+ } else if (alignV === "center") {
23008
+ anchorY = contentTop + (contentHeight - imageHeight) / 2 + offsetV;
23009
+ } else {
23010
+ anchorY = contentTop + offsetV;
23011
+ }
23012
+ } else if (vRelativeFrom === "page") {
23013
+ if (alignV === "top") {
23014
+ anchorY = offsetV;
23015
+ } else if (alignV === "bottom") {
23016
+ const pageHeight = contentBottom + (anchors.pageMargins.bottom ?? 0);
23017
+ anchorY = pageHeight - imageHeight + offsetV;
23018
+ } else if (alignV === "center") {
23019
+ const pageHeight = contentBottom + (anchors.pageMargins.bottom ?? 0);
23020
+ anchorY = (pageHeight - imageHeight) / 2 + offsetV;
23021
+ } else {
23022
+ anchorY = offsetV;
23023
+ }
23024
+ } else if (vRelativeFrom === "paragraph") {
23025
+ const baseAnchorY = state.cursorY;
23026
+ const firstLineHeight = measure.lines?.[0]?.lineHeight ?? 0;
23027
+ const paragraphAdjustment = firstLineHeight / 2;
23028
+ anchorY = baseAnchorY + paragraphAdjustment + offsetV;
23029
+ } else {
23030
+ const baseAnchorY = state.cursorY;
23031
+ anchorY = baseAnchorY + offsetV;
23032
+ }
22244
23033
  floatManager.registerDrawing(entry.block, entry.measure, anchorY, state.columnIndex, state.page.number);
22245
23034
  const anchorX = entry.block.anchor ? computeAnchorX(
22246
23035
  entry.block.anchor,
@@ -22280,7 +23069,7 @@ function layoutParagraphBlock(ctx2, anchors) {
22280
23069
  kind: "image",
22281
23070
  blockId: entry.block.id,
22282
23071
  x: anchorX,
22283
- y: anchorY + (entry.block.anchor?.offsetV ?? 0),
23072
+ y: anchorY,
22284
23073
  width: entry.measure.width,
22285
23074
  height: entry.measure.height,
22286
23075
  isAnchored: true,
@@ -22296,7 +23085,7 @@ function layoutParagraphBlock(ctx2, anchors) {
22296
23085
  blockId: entry.block.id,
22297
23086
  drawingKind: entry.block.drawingKind,
22298
23087
  x: anchorX,
22299
- y: anchorY + (entry.block.anchor?.offsetV ?? 0),
23088
+ y: anchorY,
22300
23089
  width: entry.measure.width,
22301
23090
  height: entry.measure.height,
22302
23091
  geometry: entry.measure.geometry,
@@ -22357,7 +23146,39 @@ function layoutParagraphBlock(ctx2, anchors) {
22357
23146
  state.lastParagraphStyleId = styleId;
22358
23147
  return;
22359
23148
  }
23149
+ let narrowestWidth = columnWidth;
23150
+ let narrowestOffsetX = 0;
22360
23151
  let didRemeasureForFloats = false;
23152
+ if (typeof remeasureParagraph2 === "function") {
23153
+ const tempState = ensurePage();
23154
+ let tempY = tempState.cursorY;
23155
+ if (!appliedSpacingBefore && spacingBefore > 0) {
23156
+ const prevTrailing = tempState.trailingSpacing ?? 0;
23157
+ const neededSpacingBefore = Math.max(spacingBefore - prevTrailing, 0);
23158
+ tempY += neededSpacingBefore;
23159
+ }
23160
+ for (let i = 0; i < lines.length; i++) {
23161
+ const lineY = tempY;
23162
+ const lineHeight = lines[i]?.lineHeight || 0;
23163
+ const { width: availableWidth, offsetX: computedOffset } = floatManager.computeAvailableWidth(
23164
+ lineY,
23165
+ lineHeight,
23166
+ columnWidth,
23167
+ tempState.columnIndex,
23168
+ tempState.page.number
23169
+ );
23170
+ if (availableWidth < narrowestWidth) {
23171
+ narrowestWidth = availableWidth;
23172
+ narrowestOffsetX = computedOffset;
23173
+ }
23174
+ tempY += lineHeight;
23175
+ }
23176
+ if (narrowestWidth < columnWidth) {
23177
+ const newMeasure = remeasureParagraph2(block, narrowestWidth);
23178
+ lines = normalizeLines(newMeasure);
23179
+ didRemeasureForFloats = true;
23180
+ }
23181
+ }
22361
23182
  while (fromLine < lines.length) {
22362
23183
  let state = ensurePage();
22363
23184
  if (state.trailingSpacing == null) state.trailingSpacing = 0;
@@ -22402,23 +23223,9 @@ function layoutParagraphBlock(ctx2, anchors) {
22402
23223
  }
22403
23224
  let effectiveColumnWidth = columnWidth;
22404
23225
  let offsetX = 0;
22405
- if (!didRemeasureForFloats && typeof remeasureParagraph2 === "function") {
22406
- const firstLineY = state.cursorY;
22407
- const firstLineHeight = lines[fromLine]?.lineHeight || 0;
22408
- const { width: adjustedWidth, offsetX: computedOffset } = floatManager.computeAvailableWidth(
22409
- firstLineY,
22410
- firstLineHeight,
22411
- columnWidth,
22412
- state.columnIndex,
22413
- state.page.number
22414
- );
22415
- if (adjustedWidth < columnWidth) {
22416
- const newMeasure = remeasureParagraph2(block, adjustedWidth);
22417
- lines = normalizeLines(newMeasure);
22418
- didRemeasureForFloats = true;
22419
- effectiveColumnWidth = adjustedWidth;
22420
- offsetX = computedOffset;
22421
- }
23226
+ if (didRemeasureForFloats) {
23227
+ effectiveColumnWidth = narrowestWidth;
23228
+ offsetX = narrowestOffsetX;
22422
23229
  }
22423
23230
  const slice2 = sliceLines(lines, fromLine, state.contentBottom - state.cursorY);
22424
23231
  const fragmentHeight = slice2.height;
@@ -22432,15 +23239,6 @@ function layoutParagraphBlock(ctx2, anchors) {
22432
23239
  width: effectiveColumnWidth,
22433
23240
  ...computeFragmentPmRange(block, lines, fromLine, slice2.toLine)
22434
23241
  };
22435
- anchorDebugLog("Positioning paragraph fragment:", {
22436
- blockId: block.id,
22437
- fragmentY: state.cursorY,
22438
- fragmentHeight,
22439
- firstLineHeight: lines[fromLine]?.lineHeight,
22440
- firstLineAscent: lines[fromLine]?.ascent,
22441
- firstLineDescent: lines[fromLine]?.descent,
22442
- pageNumber: state.page.number
22443
- });
22444
23242
  if (measure.marker && fromLine === 0) {
22445
23243
  fragment.markerWidth = measure.marker.markerWidth;
22446
23244
  }
@@ -22620,6 +23418,206 @@ function generateColumnBoundaries(measure) {
22620
23418
  }
22621
23419
  return boundaries;
22622
23420
  }
23421
+ function countHeaderRows(block) {
23422
+ let count = 0;
23423
+ for (let i = 0; i < block.rows.length; i++) {
23424
+ const row = block.rows[i];
23425
+ const repeatHeader = row.attrs?.tableRowProperties?.repeatHeader;
23426
+ if (repeatHeader === true) {
23427
+ count++;
23428
+ } else {
23429
+ break;
23430
+ }
23431
+ }
23432
+ return count;
23433
+ }
23434
+ function sumRowHeights(rows, fromRow, toRow) {
23435
+ let total = 0;
23436
+ for (let i = fromRow; i < toRow && i < rows.length; i++) {
23437
+ total += rows[i].height;
23438
+ }
23439
+ return total;
23440
+ }
23441
+ function calculateFragmentHeight(fragment, measure, _headerCount) {
23442
+ let height = 0;
23443
+ if (fragment.repeatHeaderCount && fragment.repeatHeaderCount > 0) {
23444
+ height += sumRowHeights(measure.rows, 0, fragment.repeatHeaderCount);
23445
+ }
23446
+ height += sumRowHeights(measure.rows, fragment.fromRow, fragment.toRow);
23447
+ return height;
23448
+ }
23449
+ const MIN_PARTIAL_ROW_HEIGHT = 20;
23450
+ function getCellLines(cell) {
23451
+ if (cell.blocks && cell.blocks.length > 0) {
23452
+ const allLines = [];
23453
+ for (const block of cell.blocks) {
23454
+ if (block.kind === "paragraph") {
23455
+ if (block.kind === "paragraph" && "lines" in block) {
23456
+ const paraBlock = block;
23457
+ if (paraBlock.lines) {
23458
+ allLines.push(...paraBlock.lines);
23459
+ }
23460
+ }
23461
+ }
23462
+ }
23463
+ return allLines;
23464
+ }
23465
+ if (cell.paragraph?.lines) {
23466
+ return cell.paragraph.lines;
23467
+ }
23468
+ return [];
23469
+ }
23470
+ function getCellPadding(cellIdx, blockRow) {
23471
+ const padding = blockRow?.cells?.[cellIdx]?.attrs?.padding ?? {};
23472
+ return {
23473
+ top: padding.top ?? 2,
23474
+ bottom: padding.bottom ?? 2,
23475
+ left: padding.left ?? 4,
23476
+ right: padding.right ?? 4
23477
+ };
23478
+ }
23479
+ function getCellTotalLines(cell) {
23480
+ return getCellLines(cell).length;
23481
+ }
23482
+ function computePartialRow(rowIndex, blockRow, measure, availableHeight, fromLineByCell) {
23483
+ const row = measure.rows[rowIndex];
23484
+ if (!row) {
23485
+ throw new Error(`Invalid rowIndex ${rowIndex}: measure.rows has ${measure.rows.length} rows`);
23486
+ }
23487
+ const cellCount = row.cells.length;
23488
+ const startLines = fromLineByCell || new Array(cellCount).fill(0);
23489
+ const toLineByCell = [];
23490
+ const heightByCell = [];
23491
+ const cellPaddings = row.cells.map((_, idx) => getCellPadding(idx, blockRow));
23492
+ for (let cellIdx = 0; cellIdx < cellCount; cellIdx++) {
23493
+ const cell = row.cells[cellIdx];
23494
+ const startLine = startLines[cellIdx] || 0;
23495
+ const cellPadding = cellPaddings[cellIdx];
23496
+ const availableForLines = Math.max(0, availableHeight - (cellPadding.top + cellPadding.bottom));
23497
+ const lines = getCellLines(cell);
23498
+ let cumulativeHeight = 0;
23499
+ let cutLine = startLine;
23500
+ for (let i = startLine; i < lines.length; i++) {
23501
+ const lineHeight = lines[i].lineHeight || 0;
23502
+ if (cumulativeHeight + lineHeight > availableForLines) {
23503
+ break;
23504
+ }
23505
+ cumulativeHeight += lineHeight;
23506
+ cutLine = i + 1;
23507
+ }
23508
+ toLineByCell.push(cutLine);
23509
+ heightByCell.push(cumulativeHeight);
23510
+ }
23511
+ const positiveHeights = heightByCell.filter((h) => h > 0);
23512
+ const minHeight = positiveHeights.length > 0 ? Math.min(...positiveHeights) : 0;
23513
+ let actualPartialHeight = 0;
23514
+ let maxPaddingTotal = 0;
23515
+ for (let cellIdx = 0; cellIdx < cellCount; cellIdx++) {
23516
+ const cell = row.cells[cellIdx];
23517
+ const startLine = startLines[cellIdx] || 0;
23518
+ const lines = getCellLines(cell);
23519
+ const cellPadding = cellPaddings[cellIdx];
23520
+ const paddingTotal = cellPadding.top + cellPadding.bottom;
23521
+ maxPaddingTotal = Math.max(maxPaddingTotal, paddingTotal);
23522
+ let cumulativeHeight = 0;
23523
+ let cutLine = startLine;
23524
+ for (let i = startLine; i < lines.length; i++) {
23525
+ const lineHeight = lines[i].lineHeight || 0;
23526
+ if (cumulativeHeight + lineHeight > minHeight) {
23527
+ break;
23528
+ }
23529
+ cumulativeHeight += lineHeight;
23530
+ cutLine = i + 1;
23531
+ }
23532
+ toLineByCell[cellIdx] = cutLine;
23533
+ actualPartialHeight = Math.max(actualPartialHeight, cumulativeHeight + paddingTotal);
23534
+ }
23535
+ const madeProgress = toLineByCell.some((cutLine, idx) => cutLine > (startLines[idx] || 0));
23536
+ const isFirstPart = startLines.every((l) => l === 0);
23537
+ const allCellsExhausted = toLineByCell.every((cutLine, idx) => {
23538
+ const totalLines = getCellTotalLines(row.cells[idx]);
23539
+ return cutLine >= totalLines;
23540
+ });
23541
+ const isLastPart = allCellsExhausted || !madeProgress;
23542
+ if (actualPartialHeight === 0 && isFirstPart) {
23543
+ actualPartialHeight = maxPaddingTotal;
23544
+ }
23545
+ return {
23546
+ rowIndex,
23547
+ fromLineByCell: startLines,
23548
+ toLineByCell,
23549
+ isFirstPart,
23550
+ isLastPart,
23551
+ partialHeight: actualPartialHeight
23552
+ };
23553
+ }
23554
+ function findSplitPoint(block, measure, startRow, availableHeight, fullPageHeight, _pendingPartialRow) {
23555
+ let accumulatedHeight = 0;
23556
+ let lastFitRow = startRow;
23557
+ for (let i = startRow; i < block.rows.length; i++) {
23558
+ const row = block.rows[i];
23559
+ const rowHeight = measure.rows[i]?.height || 0;
23560
+ const cantSplit = row.attrs?.tableRowProperties?.cantSplit === true;
23561
+ if (accumulatedHeight + rowHeight <= availableHeight) {
23562
+ accumulatedHeight += rowHeight;
23563
+ lastFitRow = i + 1;
23564
+ } else {
23565
+ const remainingHeight = availableHeight - accumulatedHeight;
23566
+ if (fullPageHeight && rowHeight > fullPageHeight) {
23567
+ const partialRow = computePartialRow(i, block.rows[i], measure, remainingHeight);
23568
+ return { endRow: i + 1, partialRow };
23569
+ }
23570
+ if (cantSplit) {
23571
+ if (lastFitRow === startRow) {
23572
+ return { endRow: startRow, partialRow: null };
23573
+ }
23574
+ return { endRow: lastFitRow, partialRow: null };
23575
+ }
23576
+ if (remainingHeight >= MIN_PARTIAL_ROW_HEIGHT) {
23577
+ const partialRow = computePartialRow(i, block.rows[i], measure, remainingHeight);
23578
+ const hasContent = partialRow.toLineByCell.some(
23579
+ (cutLine, idx) => cutLine > (partialRow.fromLineByCell[idx] || 0)
23580
+ );
23581
+ if (hasContent) {
23582
+ return { endRow: i + 1, partialRow };
23583
+ }
23584
+ }
23585
+ return { endRow: lastFitRow, partialRow: null };
23586
+ }
23587
+ }
23588
+ return { endRow: block.rows.length, partialRow: null };
23589
+ }
23590
+ function generateFragmentMetadata(measure, _fromRow, _toRow, _repeatHeaderCount) {
23591
+ return {
23592
+ columnBoundaries: generateColumnBoundaries(measure),
23593
+ coordinateSystem: "fragment"
23594
+ };
23595
+ }
23596
+ function layoutMonolithicTable(context) {
23597
+ let state = context.ensurePage();
23598
+ if (state.cursorY + context.measure.totalHeight > state.contentBottom && state.page.fragments.length > 0) {
23599
+ state = context.advanceColumn(state);
23600
+ }
23601
+ state = context.ensurePage();
23602
+ const height = Math.min(context.measure.totalHeight, state.contentBottom - state.cursorY);
23603
+ const metadata = {
23604
+ columnBoundaries: generateColumnBoundaries(context.measure),
23605
+ coordinateSystem: "fragment"
23606
+ };
23607
+ const fragment = {
23608
+ kind: "table",
23609
+ blockId: context.block.id,
23610
+ fromRow: 0,
23611
+ toRow: context.block.rows.length,
23612
+ x: context.columnX(state.columnIndex),
23613
+ y: state.cursorY,
23614
+ width: Math.min(context.columnWidth, context.measure.totalWidth || context.columnWidth),
23615
+ height,
23616
+ metadata
23617
+ };
23618
+ state.page.fragments.push(fragment);
23619
+ state.cursorY += height;
23620
+ }
22623
23621
  function layoutTableBlock({
22624
23622
  block,
22625
23623
  measure,
@@ -22628,36 +23626,202 @@ function layoutTableBlock({
22628
23626
  advanceColumn,
22629
23627
  columnX
22630
23628
  }) {
23629
+ if (block.anchor?.isAnchored) {
23630
+ return;
23631
+ }
23632
+ const tableProps = block.attrs?.tableProperties;
23633
+ const floatingProps = tableProps?.floatingTableProperties;
23634
+ if (floatingProps && Object.keys(floatingProps).length > 0) {
23635
+ layoutMonolithicTable({ block, measure, columnWidth, ensurePage, advanceColumn, columnX });
23636
+ return;
23637
+ }
23638
+ const headerCount = countHeaderRows(block);
23639
+ const headerHeight = headerCount > 0 ? sumRowHeights(measure.rows, 0, headerCount) : 0;
22631
23640
  let state = ensurePage();
22632
- if (state.cursorY + measure.totalHeight > state.contentBottom && state.page.fragments.length > 0) {
22633
- state = advanceColumn(state);
23641
+ let currentRow = 0;
23642
+ let isTableContinuation = false;
23643
+ let pendingPartialRow = null;
23644
+ while (currentRow < block.rows.length || pendingPartialRow !== null) {
23645
+ state = ensurePage();
23646
+ const availableHeight = state.contentBottom - state.cursorY;
23647
+ let repeatHeaderCount = 0;
23648
+ if (currentRow === 0 && !pendingPartialRow) {
23649
+ repeatHeaderCount = 0;
23650
+ } else {
23651
+ if (headerCount > 0 && headerHeight <= availableHeight) {
23652
+ repeatHeaderCount = headerCount;
23653
+ } else if (headerCount > 0 && headerHeight > availableHeight) {
23654
+ repeatHeaderCount = 0;
23655
+ }
23656
+ }
23657
+ const availableForBody = repeatHeaderCount > 0 ? availableHeight - headerHeight : availableHeight;
23658
+ const fullPageHeight = state.contentBottom;
23659
+ if (pendingPartialRow !== null) {
23660
+ const rowIndex = pendingPartialRow.rowIndex;
23661
+ const fromLineByCell = pendingPartialRow.toLineByCell;
23662
+ const continuationPartialRow = computePartialRow(
23663
+ rowIndex,
23664
+ block.rows[rowIndex],
23665
+ measure,
23666
+ availableForBody,
23667
+ fromLineByCell
23668
+ );
23669
+ const madeProgress = continuationPartialRow.toLineByCell.some(
23670
+ (toLine, idx) => toLine > (fromLineByCell[idx] || 0)
23671
+ );
23672
+ const hasRemainingLinesAfterContinuation = continuationPartialRow.toLineByCell.some(
23673
+ (toLine, idx) => {
23674
+ const totalLines = getCellTotalLines(measure.rows[rowIndex].cells[idx]);
23675
+ return toLine < totalLines;
23676
+ }
23677
+ );
23678
+ const hadRemainingLinesBefore = fromLineByCell.some((fromLine, idx) => {
23679
+ const totalLines = getCellTotalLines(measure.rows[rowIndex].cells[idx]);
23680
+ return fromLine < totalLines;
23681
+ });
23682
+ const fragmentHeight2 = continuationPartialRow.partialHeight + (repeatHeaderCount > 0 ? headerHeight : 0);
23683
+ if (fragmentHeight2 > 0) {
23684
+ const fragment2 = {
23685
+ kind: "table",
23686
+ blockId: block.id,
23687
+ fromRow: rowIndex,
23688
+ toRow: rowIndex + 1,
23689
+ x: columnX(state.columnIndex),
23690
+ y: state.cursorY,
23691
+ width: Math.min(columnWidth, measure.totalWidth || columnWidth),
23692
+ height: fragmentHeight2,
23693
+ continuesFromPrev: true,
23694
+ continuesOnNext: hasRemainingLinesAfterContinuation || rowIndex + 1 < block.rows.length,
23695
+ repeatHeaderCount,
23696
+ partialRow: continuationPartialRow,
23697
+ metadata: generateFragmentMetadata(measure)
23698
+ };
23699
+ state.page.fragments.push(fragment2);
23700
+ state.cursorY += fragmentHeight2;
23701
+ }
23702
+ const rowComplete = !hasRemainingLinesAfterContinuation;
23703
+ if (rowComplete) {
23704
+ currentRow = rowIndex + 1;
23705
+ pendingPartialRow = null;
23706
+ } else if (!madeProgress && hadRemainingLinesBefore) {
23707
+ state = advanceColumn(state);
23708
+ } else {
23709
+ state = advanceColumn(state);
23710
+ pendingPartialRow = continuationPartialRow;
23711
+ }
23712
+ isTableContinuation = true;
23713
+ continue;
23714
+ }
23715
+ const bodyStartRow = currentRow;
23716
+ const { endRow, partialRow } = findSplitPoint(block, measure, bodyStartRow, availableForBody, fullPageHeight);
23717
+ if (endRow === bodyStartRow && partialRow === null && state.page.fragments.length > 0) {
23718
+ state = advanceColumn(state);
23719
+ continue;
23720
+ }
23721
+ if (endRow === bodyStartRow && partialRow === null) {
23722
+ const forcedPartialRow = computePartialRow(bodyStartRow, block.rows[bodyStartRow], measure, availableForBody);
23723
+ const forcedEndRow = bodyStartRow + 1;
23724
+ const fragmentHeight2 = forcedPartialRow.partialHeight + (repeatHeaderCount > 0 ? headerHeight : 0);
23725
+ const fragment2 = {
23726
+ kind: "table",
23727
+ blockId: block.id,
23728
+ fromRow: bodyStartRow,
23729
+ toRow: forcedEndRow,
23730
+ x: columnX(state.columnIndex),
23731
+ y: state.cursorY,
23732
+ width: Math.min(columnWidth, measure.totalWidth || columnWidth),
23733
+ height: fragmentHeight2,
23734
+ continuesFromPrev: isTableContinuation,
23735
+ continuesOnNext: !forcedPartialRow.isLastPart || forcedEndRow < block.rows.length,
23736
+ repeatHeaderCount,
23737
+ partialRow: forcedPartialRow,
23738
+ metadata: generateFragmentMetadata(measure)
23739
+ };
23740
+ state.page.fragments.push(fragment2);
23741
+ state.cursorY += fragmentHeight2;
23742
+ pendingPartialRow = forcedPartialRow;
23743
+ isTableContinuation = true;
23744
+ continue;
23745
+ }
23746
+ let fragmentHeight;
23747
+ if (partialRow) {
23748
+ const fullRowsHeight = sumRowHeights(measure.rows, bodyStartRow, endRow - 1);
23749
+ fragmentHeight = fullRowsHeight + partialRow.partialHeight + (repeatHeaderCount > 0 ? headerHeight : 0);
23750
+ } else {
23751
+ fragmentHeight = calculateFragmentHeight(
23752
+ { fromRow: bodyStartRow, toRow: endRow, repeatHeaderCount },
23753
+ measure
23754
+ );
23755
+ }
23756
+ const fragment = {
23757
+ kind: "table",
23758
+ blockId: block.id,
23759
+ fromRow: bodyStartRow,
23760
+ toRow: endRow,
23761
+ x: columnX(state.columnIndex),
23762
+ y: state.cursorY,
23763
+ width: Math.min(columnWidth, measure.totalWidth || columnWidth),
23764
+ height: fragmentHeight,
23765
+ continuesFromPrev: isTableContinuation,
23766
+ continuesOnNext: endRow < block.rows.length || (partialRow ? !partialRow.isLastPart : false),
23767
+ repeatHeaderCount,
23768
+ partialRow: partialRow || void 0,
23769
+ metadata: generateFragmentMetadata(measure)
23770
+ };
23771
+ state.page.fragments.push(fragment);
23772
+ state.cursorY += fragmentHeight;
23773
+ if (partialRow && !partialRow.isLastPart) {
23774
+ pendingPartialRow = partialRow;
23775
+ currentRow = partialRow.rowIndex;
23776
+ } else {
23777
+ currentRow = endRow;
23778
+ pendingPartialRow = null;
23779
+ }
23780
+ isTableContinuation = true;
22634
23781
  }
22635
- state = ensurePage();
22636
- const height = Math.min(measure.totalHeight, state.contentBottom - state.cursorY);
23782
+ }
23783
+ function createAnchoredTableFragment(block, measure, x, y) {
22637
23784
  const metadata = {
22638
23785
  columnBoundaries: generateColumnBoundaries(measure),
22639
23786
  coordinateSystem: "fragment"
22640
- // rowBoundaries omitted - not needed for column resize, reduces DOM overhead
22641
23787
  };
22642
- const fragment = {
23788
+ return {
22643
23789
  kind: "table",
22644
23790
  blockId: block.id,
22645
23791
  fromRow: 0,
22646
23792
  toRow: block.rows.length,
22647
- x: columnX(state.columnIndex),
22648
- y: state.cursorY,
22649
- width: Math.min(columnWidth, measure.totalWidth || columnWidth),
22650
- height,
23793
+ x,
23794
+ y,
23795
+ width: measure.totalWidth ?? 0,
23796
+ height: measure.totalHeight ?? 0,
22651
23797
  metadata
22652
23798
  };
22653
- state.page.fragments.push(fragment);
22654
- state.cursorY += height;
23799
+ }
23800
+ function isPageRelativeAnchor(block) {
23801
+ const vRelativeFrom = block.anchor?.vRelativeFrom;
23802
+ return vRelativeFrom === "margin" || vRelativeFrom === "page";
23803
+ }
23804
+ function collectPreRegisteredAnchors(blocks, measures) {
23805
+ const result = [];
23806
+ const len = Math.min(blocks.length, measures.length);
23807
+ for (let i = 0; i < len; i += 1) {
23808
+ const block = blocks[i];
23809
+ const measure = measures[i];
23810
+ const isImage = block.kind === "image" && measure?.kind === "image";
23811
+ const isDrawing = block.kind === "drawing" && measure?.kind === "drawing";
23812
+ if (!isImage && !isDrawing) continue;
23813
+ const drawingBlock = block;
23814
+ const drawingMeasure = measure;
23815
+ if (!drawingBlock.anchor?.isAnchored) continue;
23816
+ if (isPageRelativeAnchor(drawingBlock)) {
23817
+ result.push({ block: drawingBlock, measure: drawingMeasure });
23818
+ }
23819
+ }
23820
+ return result;
22655
23821
  }
22656
23822
  function collectAnchoredDrawings(blocks, measures) {
22657
23823
  const map2 = /* @__PURE__ */ new Map();
22658
- for (let i = 0; i < blocks.length; i += 1) {
22659
- if (blocks[i].kind === "paragraph") ;
22660
- }
23824
+ const len = Math.min(blocks.length, measures.length);
22661
23825
  const nearestPrevParagraph = (fromIndex) => {
22662
23826
  for (let i = fromIndex - 1; i >= 0; i -= 1) {
22663
23827
  if (blocks[i].kind === "paragraph") return i;
@@ -22665,12 +23829,12 @@ function collectAnchoredDrawings(blocks, measures) {
22665
23829
  return null;
22666
23830
  };
22667
23831
  const nearestNextParagraph = (fromIndex) => {
22668
- for (let i = fromIndex + 1; i < blocks.length; i += 1) {
23832
+ for (let i = fromIndex + 1; i < len; i += 1) {
22669
23833
  if (blocks[i].kind === "paragraph") return i;
22670
23834
  }
22671
23835
  return null;
22672
23836
  };
22673
- for (let i = 0; i < blocks.length; i += 1) {
23837
+ for (let i = 0; i < len; i += 1) {
22674
23838
  const block = blocks[i];
22675
23839
  const measure = measures[i];
22676
23840
  const isImage = block.kind === "image" && measure?.kind === "image";
@@ -22678,7 +23842,12 @@ function collectAnchoredDrawings(blocks, measures) {
22678
23842
  if (!isImage && !isDrawing) continue;
22679
23843
  const drawingBlock = block;
22680
23844
  const drawingMeasure = measure;
22681
- if (!drawingBlock.anchor?.isAnchored) continue;
23845
+ if (!drawingBlock.anchor?.isAnchored) {
23846
+ continue;
23847
+ }
23848
+ if (isPageRelativeAnchor(drawingBlock)) {
23849
+ continue;
23850
+ }
22682
23851
  let anchorParaIndex = nearestPrevParagraph(i);
22683
23852
  if (anchorParaIndex == null) anchorParaIndex = nearestNextParagraph(i);
22684
23853
  if (anchorParaIndex == null) continue;
@@ -22688,6 +23857,36 @@ function collectAnchoredDrawings(blocks, measures) {
22688
23857
  }
22689
23858
  return map2;
22690
23859
  }
23860
+ function collectAnchoredTables(blocks, measures) {
23861
+ const map2 = /* @__PURE__ */ new Map();
23862
+ const nearestPrevParagraph = (fromIndex) => {
23863
+ for (let i = fromIndex - 1; i >= 0; i -= 1) {
23864
+ if (blocks[i].kind === "paragraph") return i;
23865
+ }
23866
+ return null;
23867
+ };
23868
+ const nearestNextParagraph = (fromIndex) => {
23869
+ for (let i = fromIndex + 1; i < blocks.length; i += 1) {
23870
+ if (blocks[i].kind === "paragraph") return i;
23871
+ }
23872
+ return null;
23873
+ };
23874
+ for (let i = 0; i < blocks.length; i += 1) {
23875
+ const block = blocks[i];
23876
+ const measure = measures[i];
23877
+ if (block.kind !== "table" || measure?.kind !== "table") continue;
23878
+ const tableBlock = block;
23879
+ const tableMeasure = measure;
23880
+ if (!tableBlock.anchor?.isAnchored) continue;
23881
+ let anchorParaIndex = nearestPrevParagraph(i);
23882
+ if (anchorParaIndex == null) anchorParaIndex = nearestNextParagraph(i);
23883
+ if (anchorParaIndex == null) continue;
23884
+ const list = map2.get(anchorParaIndex) ?? [];
23885
+ list.push({ block: tableBlock, measure: tableMeasure });
23886
+ map2.set(anchorParaIndex, list);
23887
+ }
23888
+ return map2;
23889
+ }
22691
23890
  function createPaginator(opts) {
22692
23891
  const states = [];
22693
23892
  const pages = [];
@@ -23273,7 +24472,57 @@ function layoutDocument(blocks, measures, options = {}) {
23273
24472
  cachedColumnsState.state = null;
23274
24473
  };
23275
24474
  const anchoredByParagraph = collectAnchoredDrawings(blocks, measures);
24475
+ const anchoredTablesByParagraph = collectAnchoredTables(blocks, measures);
23276
24476
  const placedAnchoredIds = /* @__PURE__ */ new Set();
24477
+ const placedAnchoredTableIds = /* @__PURE__ */ new Set();
24478
+ const preRegisteredAnchors = collectPreRegisteredAnchors(blocks, measures);
24479
+ const preRegisteredPositions = /* @__PURE__ */ new Map();
24480
+ for (const entry of preRegisteredAnchors) {
24481
+ const state = paginator.ensurePage();
24482
+ const vRelativeFrom = entry.block.anchor?.vRelativeFrom ?? "paragraph";
24483
+ const alignV = entry.block.anchor?.alignV ?? "top";
24484
+ const offsetV = entry.block.anchor?.offsetV ?? 0;
24485
+ const imageHeight = entry.measure.height ?? 0;
24486
+ const contentTop = state.topMargin;
24487
+ const contentBottom = state.contentBottom;
24488
+ const contentHeight = Math.max(0, contentBottom - contentTop);
24489
+ let anchorY;
24490
+ if (vRelativeFrom === "margin") {
24491
+ if (alignV === "top") {
24492
+ anchorY = contentTop + offsetV;
24493
+ } else if (alignV === "bottom") {
24494
+ anchorY = contentBottom - imageHeight + offsetV;
24495
+ } else if (alignV === "center") {
24496
+ anchorY = contentTop + (contentHeight - imageHeight) / 2 + offsetV;
24497
+ } else {
24498
+ anchorY = contentTop + offsetV;
24499
+ }
24500
+ } else if (vRelativeFrom === "page") {
24501
+ if (alignV === "top") {
24502
+ anchorY = offsetV;
24503
+ } else if (alignV === "bottom") {
24504
+ const pageHeight = contentBottom + margins.bottom;
24505
+ anchorY = pageHeight - imageHeight + offsetV;
24506
+ } else if (alignV === "center") {
24507
+ const pageHeight = contentBottom + margins.bottom;
24508
+ anchorY = (pageHeight - imageHeight) / 2 + offsetV;
24509
+ } else {
24510
+ anchorY = offsetV;
24511
+ }
24512
+ } else {
24513
+ anchorY = contentTop + offsetV;
24514
+ }
24515
+ const anchorX = entry.block.anchor ? computeAnchorX(
24516
+ entry.block.anchor,
24517
+ state.columnIndex,
24518
+ normalizeColumns(activeColumns, contentWidth),
24519
+ entry.measure.width,
24520
+ { left: margins.left, right: margins.right },
24521
+ activePageSize.w
24522
+ ) : margins.left;
24523
+ floatManager.registerDrawing(entry.block, entry.measure, anchorY, state.columnIndex, state.page.number);
24524
+ preRegisteredPositions.set(entry.block.id, { anchorX, anchorY, pageNumber: state.page.number });
24525
+ }
23277
24526
  for (let index2 = 0; index2 < blocks.length; index2 += 1) {
23278
24527
  const block = blocks[index2];
23279
24528
  const measure = measures[index2];
@@ -23403,6 +24652,19 @@ function layoutDocument(blocks, measures, options = {}) {
23403
24652
  }
23404
24653
  }
23405
24654
  const anchorsForPara = anchoredByParagraph.get(index2);
24655
+ const tablesForPara = anchoredTablesByParagraph.get(index2);
24656
+ if (tablesForPara) {
24657
+ const state = paginator.ensurePage();
24658
+ for (const { block: tableBlock, measure: tableMeasure } of tablesForPara) {
24659
+ if (placedAnchoredTableIds.has(tableBlock.id)) continue;
24660
+ floatManager.registerTable(tableBlock, tableMeasure, state.cursorY, state.columnIndex, state.page.number);
24661
+ const anchorX = tableBlock.anchor?.offsetH ?? columnX(state.columnIndex);
24662
+ const anchorY = state.cursorY + (tableBlock.anchor?.offsetV ?? 0);
24663
+ const tableFragment = createAnchoredTableFragment(tableBlock, tableMeasure, anchorX, anchorY);
24664
+ state.page.fragments.push(tableFragment);
24665
+ placedAnchoredTableIds.add(tableBlock.id);
24666
+ }
24667
+ }
23406
24668
  layoutParagraphBlock(
23407
24669
  {
23408
24670
  block,
@@ -23433,6 +24695,52 @@ function layoutDocument(blocks, measures, options = {}) {
23433
24695
  if (measure.kind !== "image") {
23434
24696
  throw new Error(`layoutDocument: expected image measure for block ${block.id}`);
23435
24697
  }
24698
+ const preRegPos = preRegisteredPositions.get(block.id);
24699
+ if (preRegPos && Number.isFinite(preRegPos.anchorX) && Number.isFinite(preRegPos.anchorY) && Number.isFinite(preRegPos.pageNumber)) {
24700
+ const state = paginator.ensurePage();
24701
+ const imgBlock = block;
24702
+ const imgMeasure = measure;
24703
+ const pageContentHeight = Math.max(0, state.contentBottom - state.topMargin);
24704
+ const relativeFrom = imgBlock.anchor?.hRelativeFrom ?? "column";
24705
+ const cols = getCurrentColumns();
24706
+ let maxWidth;
24707
+ if (relativeFrom === "page") {
24708
+ maxWidth = cols.count === 1 ? activePageSize.w - margins.left - margins.right : activePageSize.w;
24709
+ } else if (relativeFrom === "margin") {
24710
+ maxWidth = activePageSize.w - margins.left - margins.right;
24711
+ } else {
24712
+ maxWidth = cols.width;
24713
+ }
24714
+ const aspectRatio = imgMeasure.width > 0 && imgMeasure.height > 0 ? imgMeasure.width / imgMeasure.height : 1;
24715
+ const minWidth = 20;
24716
+ const minHeight = minWidth / aspectRatio;
24717
+ const metadata = {
24718
+ originalWidth: imgMeasure.width,
24719
+ originalHeight: imgMeasure.height,
24720
+ maxWidth,
24721
+ maxHeight: pageContentHeight,
24722
+ aspectRatio,
24723
+ minWidth,
24724
+ minHeight
24725
+ };
24726
+ const fragment = {
24727
+ kind: "image",
24728
+ blockId: imgBlock.id,
24729
+ x: preRegPos.anchorX,
24730
+ y: preRegPos.anchorY,
24731
+ width: imgMeasure.width,
24732
+ height: imgMeasure.height,
24733
+ isAnchored: true,
24734
+ zIndex: imgBlock.anchor?.behindDoc ? 0 : 1,
24735
+ metadata
24736
+ };
24737
+ const attrs = imgBlock.attrs;
24738
+ if (attrs?.pmStart != null) fragment.pmStart = attrs.pmStart;
24739
+ if (attrs?.pmEnd != null) fragment.pmEnd = attrs.pmEnd;
24740
+ state.page.fragments.push(fragment);
24741
+ placedAnchoredIds.add(imgBlock.id);
24742
+ continue;
24743
+ }
23436
24744
  layoutImageBlock({
23437
24745
  block,
23438
24746
  measure,
@@ -23628,7 +24936,9 @@ const hashRuns = (block) => {
23628
24936
  const trackedMode = block.attrs && "trackedChangesMode" in block.attrs && block.attrs.trackedChangesMode || "review";
23629
24937
  const trackedEnabled = resolveTrackedChangesEnabled(block.attrs, true);
23630
24938
  const runsHash = block.runs.map((run) => {
23631
- const text = normalizeText(run.kind === "image" ? "" : run.text ?? "");
24939
+ const text = normalizeText(
24940
+ "src" in run || run.kind === "lineBreak" || run.kind === "break" ? "" : run.text ?? ""
24941
+ );
23632
24942
  const bold = "bold" in run ? run.bold : false;
23633
24943
  const italic = "italic" in run ? run.italic : false;
23634
24944
  const color = "color" in run ? run.color : void 0;
@@ -23897,7 +25207,13 @@ const FeatureFlags = {
23897
25207
  * Logs cache hits, misses, invalidations, and bucket selection.
23898
25208
  * Should be disabled in production (only enabled for debugging).
23899
25209
  */
23900
- DEBUG_HF_CACHE: isEnabled("SD_DEBUG_HF_CACHE", false)
25210
+ DEBUG_HF_CACHE: isEnabled("SD_DEBUG_HF_CACHE", false),
25211
+ /**
25212
+ * Enable debug logging for layout version tracking.
25213
+ * Logs stale layout reads, geometry fallbacks, PM transactions, and layout completions.
25214
+ * Should be disabled in production (only enabled for debugging).
25215
+ */
25216
+ DEBUG_LAYOUT_VERSION: isEnabled("SD_DEBUG_LAYOUT_VERSION", false)
23901
25217
  };
23902
25218
  const PageTokenLogger = {
23903
25219
  /**
@@ -24282,7 +25598,7 @@ function fontString(run) {
24282
25598
  return `${italic}${bold}${size}px ${family}`.trim();
24283
25599
  }
24284
25600
  function runText(run) {
24285
- return run.kind === "image" ? "" : run.text ?? "";
25601
+ return "src" in run || run.kind === "lineBreak" || run.kind === "break" ? "" : run.text ?? "";
24286
25602
  }
24287
25603
  function measureRunSliceWidth(run, fromChar, toChar) {
24288
25604
  const context = getCtx();
@@ -24444,7 +25760,7 @@ const paragraphBlocksEqual = (a, b) => {
24444
25760
  for (let i = 0; i < a.runs.length; i += 1) {
24445
25761
  const runA = a.runs[i];
24446
25762
  const runB = b.runs[i];
24447
- if ((runA.kind === "image" ? "" : runA.text) !== (runB.kind === "image" ? "" : runB.text) || ("bold" in runA ? runA.bold : false) !== ("bold" in runB ? runB.bold : false) || ("italic" in runA ? runA.italic : false) !== ("italic" in runB ? runB.italic : false) || ("color" in runA ? runA.color : void 0) !== ("color" in runB ? runB.color : void 0) || getTrackedChangeKey(runA) !== getTrackedChangeKey(runB)) {
25763
+ if (("src" in runA || runA.kind === "lineBreak" || runA.kind === "break" ? "" : runA.text) !== ("src" in runB || runB.kind === "lineBreak" || runB.kind === "break" ? "" : runB.text) || ("bold" in runA ? runA.bold : false) !== ("bold" in runB ? runB.bold : false) || ("italic" in runA ? runA.italic : false) !== ("italic" in runB ? runB.italic : false) || ("color" in runA ? runA.color : void 0) !== ("color" in runB ? runB.color : void 0) || getTrackedChangeKey(runA) !== getTrackedChangeKey(runB)) {
24448
25764
  return false;
24449
25765
  }
24450
25766
  }
@@ -24564,7 +25880,7 @@ function computeHeaderFooterContentHash(blocks) {
24564
25880
  parts.push(block.id);
24565
25881
  if (block.kind === "paragraph") {
24566
25882
  for (const run of block.runs) {
24567
- if (run.kind !== "image") {
25883
+ if (!("src" in run) && run.kind !== "lineBreak" && run.kind !== "break") {
24568
25884
  parts.push(run.text ?? "");
24569
25885
  }
24570
25886
  if ("bold" in run && run.bold) parts.push("b");
@@ -24993,6 +26309,23 @@ async function remeasureAffectedBlocks(blocks, measures, affectedBlockIds, const
24993
26309
  }
24994
26310
  return updatedMeasures;
24995
26311
  }
26312
+ var Priority = /* @__PURE__ */ ((Priority2) => {
26313
+ Priority2[Priority2["P0"] = 0] = "P0";
26314
+ Priority2[Priority2["P1"] = 1] = "P1";
26315
+ Priority2[Priority2["P2"] = 2] = "P2";
26316
+ Priority2[Priority2["P3"] = 3] = "P3";
26317
+ return Priority2;
26318
+ })(Priority || {});
26319
+ ({
26320
+ /** P0: No debounce for synchronous cursor positioning */
26321
+ [Priority.P0]: 0,
26322
+ /** P1: One animation frame (~60fps) for viewport layout */
26323
+ [Priority.P1]: 16,
26324
+ /** P2: Typing burst threshold for adjacent pages layout */
26325
+ [Priority.P2]: 50,
26326
+ /** P3: Heavy debounce for full document layout */
26327
+ [Priority.P3]: 150
26328
+ });
24996
26329
  const isAtomicFragment = (fragment) => {
24997
26330
  return fragment.kind === "drawing" || fragment.kind === "image";
24998
26331
  };
@@ -25080,6 +26413,111 @@ const hitTestAtomicFragment = (pageHit, blocks, measures, point) => {
25080
26413
  }
25081
26414
  return null;
25082
26415
  };
26416
+ const hitTestTableFragment = (pageHit, blocks, measures, point) => {
26417
+ for (const fragment of pageHit.page.fragments) {
26418
+ if (fragment.kind !== "table") continue;
26419
+ const tableFragment = fragment;
26420
+ const withinX = point.x >= tableFragment.x && point.x <= tableFragment.x + tableFragment.width;
26421
+ const withinY = point.y >= tableFragment.y && point.y <= tableFragment.y + tableFragment.height;
26422
+ if (!withinX || !withinY) continue;
26423
+ const blockIndex = blocks.findIndex((block2) => block2.id === tableFragment.blockId);
26424
+ if (blockIndex === -1) continue;
26425
+ const block = blocks[blockIndex];
26426
+ const measure = measures[blockIndex];
26427
+ if (!block || block.kind !== "table" || !measure || measure.kind !== "table") continue;
26428
+ const tableBlock = block;
26429
+ const tableMeasure = measure;
26430
+ const localX = point.x - tableFragment.x;
26431
+ const localY = point.y - tableFragment.y;
26432
+ let rowY = 0;
26433
+ let rowIndex = -1;
26434
+ if (tableMeasure.rows.length === 0 || tableBlock.rows.length === 0) continue;
26435
+ for (let r2 = tableFragment.fromRow; r2 < tableFragment.toRow && r2 < tableMeasure.rows.length; r2++) {
26436
+ const rowMeasure2 = tableMeasure.rows[r2];
26437
+ if (localY >= rowY && localY < rowY + rowMeasure2.height) {
26438
+ rowIndex = r2;
26439
+ break;
26440
+ }
26441
+ rowY += rowMeasure2.height;
26442
+ }
26443
+ if (rowIndex === -1) {
26444
+ rowIndex = Math.min(tableFragment.toRow - 1, tableMeasure.rows.length - 1);
26445
+ if (rowIndex < tableFragment.fromRow) continue;
26446
+ }
26447
+ const rowMeasure = tableMeasure.rows[rowIndex];
26448
+ const row = tableBlock.rows[rowIndex];
26449
+ if (!rowMeasure || !row) continue;
26450
+ let colX = 0;
26451
+ let colIndex = -1;
26452
+ if (rowMeasure.cells.length === 0 || row.cells.length === 0) continue;
26453
+ for (let c = 0; c < rowMeasure.cells.length; c++) {
26454
+ const cellMeasure2 = rowMeasure.cells[c];
26455
+ if (localX >= colX && localX < colX + cellMeasure2.width) {
26456
+ colIndex = c;
26457
+ break;
26458
+ }
26459
+ colX += cellMeasure2.width;
26460
+ }
26461
+ if (colIndex === -1) {
26462
+ colIndex = rowMeasure.cells.length - 1;
26463
+ if (colIndex < 0) continue;
26464
+ }
26465
+ const cellMeasure = rowMeasure.cells[colIndex];
26466
+ const cell = row.cells[colIndex];
26467
+ if (!cellMeasure || !cell) continue;
26468
+ const cellBlocks = cell.blocks ?? (cell.paragraph ? [cell.paragraph] : []);
26469
+ const rawMeasures = cellMeasure.blocks ?? (cellMeasure.paragraph ? [cellMeasure.paragraph] : []);
26470
+ const cellBlockMeasures = (Array.isArray(rawMeasures) ? rawMeasures : []).filter(
26471
+ (m) => m != null && typeof m === "object" && "kind" in m
26472
+ );
26473
+ let blockStartY = 0;
26474
+ const getBlockHeight = (m) => {
26475
+ if (!m) return 0;
26476
+ if ("totalHeight" in m && typeof m.totalHeight === "number") {
26477
+ return m.totalHeight;
26478
+ }
26479
+ if ("height" in m && typeof m.height === "number") {
26480
+ return m.height;
26481
+ }
26482
+ return 0;
26483
+ };
26484
+ for (let i = 0; i < cellBlocks.length && i < cellBlockMeasures.length; i++) {
26485
+ const cellBlock = cellBlocks[i];
26486
+ const cellBlockMeasure = cellBlockMeasures[i];
26487
+ if (cellBlock?.kind !== "paragraph" || cellBlockMeasure?.kind !== "paragraph") {
26488
+ blockStartY += getBlockHeight(cellBlockMeasure);
26489
+ continue;
26490
+ }
26491
+ const blockHeight = getBlockHeight(cellBlockMeasure);
26492
+ const blockEndY = blockStartY + blockHeight;
26493
+ const padding = cell.attrs?.padding ?? { top: 2, left: 4 };
26494
+ const cellLocalX = localX - colX - (padding.left ?? 4);
26495
+ const cellLocalY = localY - rowY - (padding.top ?? 2);
26496
+ const paragraphBlock = cellBlock;
26497
+ const paragraphMeasure = cellBlockMeasure;
26498
+ const isWithinBlock = cellLocalY >= blockStartY && cellLocalY < blockEndY;
26499
+ const isLastParagraph = i === Math.min(cellBlocks.length, cellBlockMeasures.length) - 1;
26500
+ if (isWithinBlock || isLastParagraph) {
26501
+ const unclampedLocalY = cellLocalY - blockStartY;
26502
+ const localYWithinBlock = Math.max(0, Math.min(unclampedLocalY, Math.max(blockHeight, 0)));
26503
+ return {
26504
+ fragment: tableFragment,
26505
+ block: tableBlock,
26506
+ measure: tableMeasure,
26507
+ pageIndex: pageHit.pageIndex,
26508
+ cellRowIndex: rowIndex,
26509
+ cellColIndex: colIndex,
26510
+ cellBlock: paragraphBlock,
26511
+ cellMeasure: paragraphMeasure,
26512
+ localX: Math.max(0, cellLocalX),
26513
+ localY: Math.max(0, localYWithinBlock)
26514
+ };
26515
+ }
26516
+ blockStartY = blockEndY;
26517
+ }
26518
+ }
26519
+ return null;
26520
+ };
25083
26521
  function clickToPosition(layout, blocks, measures, containerPoint, domContainer, clientX, clientY) {
25084
26522
  logClickStage("log", "entry", {
25085
26523
  pages: layout.pages.length
@@ -25174,6 +26612,49 @@ function clickToPosition(layout, blocks, measures, containerPoint, domContainer,
25174
26612
  // lineIndex is now already absolute (within measure.lines), no need to add fragment.fromLine
25175
26613
  };
25176
26614
  }
26615
+ const tableHit = hitTestTableFragment(pageHit, blocks, measures, pageRelativePoint);
26616
+ if (tableHit) {
26617
+ const { cellBlock, cellMeasure, localX, localY, pageIndex } = tableHit;
26618
+ const lineIndex = findLineIndexAtY(cellMeasure, localY, 0, cellMeasure.lines.length);
26619
+ if (lineIndex != null) {
26620
+ const line = cellMeasure.lines[lineIndex];
26621
+ const isRTL = isRtlBlock(cellBlock);
26622
+ const pos = mapPointToPm(cellBlock, line, localX, isRTL);
26623
+ if (pos != null) {
26624
+ logClickStage("log", "success", {
26625
+ blockId: tableHit.fragment.blockId,
26626
+ column: determineColumn(layout, tableHit.fragment.x)
26627
+ });
26628
+ return {
26629
+ pos,
26630
+ blockId: tableHit.fragment.blockId,
26631
+ pageIndex,
26632
+ column: determineColumn(layout, tableHit.fragment.x),
26633
+ lineIndex
26634
+ };
26635
+ }
26636
+ }
26637
+ const firstRun = cellBlock.runs?.[0];
26638
+ if (firstRun && firstRun.pmStart != null) {
26639
+ logClickStage("log", "success", {
26640
+ blockId: tableHit.fragment.blockId,
26641
+ pos: firstRun.pmStart,
26642
+ column: determineColumn(layout, tableHit.fragment.x)
26643
+ });
26644
+ return {
26645
+ pos: firstRun.pmStart,
26646
+ blockId: tableHit.fragment.blockId,
26647
+ pageIndex,
26648
+ column: determineColumn(layout, tableHit.fragment.x),
26649
+ lineIndex: 0
26650
+ };
26651
+ }
26652
+ logClickStage("warn", "table-cell-no-position", {
26653
+ blockId: tableHit.fragment.blockId,
26654
+ cellRow: tableHit.cellRowIndex,
26655
+ cellCol: tableHit.cellColIndex
26656
+ });
26657
+ }
25177
26658
  const atomicHit = hitTestAtomicFragment(pageHit, blocks, measures, pageRelativePoint);
25178
26659
  if (atomicHit && isAtomicFragment(atomicHit.fragment)) {
25179
26660
  const { fragment, block, pageIndex } = atomicHit;
@@ -25285,6 +26766,44 @@ function getFragmentAtPosition(layout, blocks, measures, pos) {
25285
26766
  }
25286
26767
  continue;
25287
26768
  }
26769
+ if (fragment.kind === "table") {
26770
+ if (block.kind !== "table" || measure.kind !== "table") continue;
26771
+ const tableBlock = block;
26772
+ const tableFragment = fragment;
26773
+ let tableMinPos = null;
26774
+ let tableMaxPos = null;
26775
+ for (let r2 = tableFragment.fromRow; r2 < tableFragment.toRow && r2 < tableBlock.rows.length; r2++) {
26776
+ const row = tableBlock.rows[r2];
26777
+ for (const cell of row.cells) {
26778
+ const cellBlocks = cell.blocks ?? (cell.paragraph ? [cell.paragraph] : []);
26779
+ for (const cellBlock of cellBlocks) {
26780
+ if (cellBlock?.kind === "paragraph") {
26781
+ const paraBlock = cellBlock;
26782
+ for (const run of paraBlock.runs ?? []) {
26783
+ if (run.pmStart != null) {
26784
+ if (tableMinPos === null || run.pmStart < tableMinPos) tableMinPos = run.pmStart;
26785
+ if (tableMaxPos === null || run.pmStart > tableMaxPos) tableMaxPos = run.pmStart;
26786
+ }
26787
+ if (run.pmEnd != null) {
26788
+ if (tableMinPos === null || run.pmEnd < tableMinPos) tableMinPos = run.pmEnd;
26789
+ if (tableMaxPos === null || run.pmEnd > tableMaxPos) tableMaxPos = run.pmEnd;
26790
+ }
26791
+ }
26792
+ }
26793
+ }
26794
+ }
26795
+ }
26796
+ if (tableMinPos != null && tableMaxPos != null && pos >= tableMinPos && pos <= tableMaxPos) {
26797
+ return {
26798
+ fragment,
26799
+ block,
26800
+ measure,
26801
+ pageIndex,
26802
+ pageY: 0
26803
+ };
26804
+ }
26805
+ continue;
26806
+ }
25288
26807
  if (isAtomicFragment(fragment)) {
25289
26808
  const { pmStart, pmEnd } = getAtomicPmRange(fragment, block);
25290
26809
  const start2 = pmStart ?? pmEnd;
@@ -25332,7 +26851,7 @@ function computeLinePmRange$1(block, line) {
25332
26851
  for (let runIndex = line.fromRun; runIndex <= line.toRun; runIndex += 1) {
25333
26852
  const run = block.runs[runIndex];
25334
26853
  if (!run) continue;
25335
- const text = run.kind === "image" ? "" : run.text ?? "";
26854
+ const text = "src" in run || run.kind === "lineBreak" || run.kind === "break" ? "" : run.text ?? "";
25336
26855
  const runLength = text.length;
25337
26856
  const runPmStart = run.pmStart ?? null;
25338
26857
  const runPmEnd = run.pmEnd ?? (runPmStart != null ? runPmStart + runLength : null);
@@ -27728,16 +29247,6 @@ const LINK_AND_TOC_STYLES = `
27728
29247
  opacity: 0.8;
27729
29248
  }
27730
29249
 
27731
- /* External link indicator (WCAG 2.4.4 Link Purpose) */
27732
- .superdoc-link[target="_blank"]::after {
27733
- content: "↗";
27734
- display: inline-block;
27735
- margin-left: 0.25em;
27736
- font-size: 0.85em;
27737
- text-decoration: none;
27738
- speak: literal-punctuation; /* Screen readers read the arrow */
27739
- }
27740
-
27741
29250
  /* Print mode: show URLs after links */
27742
29251
  @media print {
27743
29252
  .superdoc-link::after {
@@ -27750,11 +29259,6 @@ const LINK_AND_TOC_STYLES = `
27750
29259
  .superdoc-link[href^="#"]::after {
27751
29260
  content: "";
27752
29261
  }
27753
-
27754
- /* Don't show URL for external link indicator */
27755
- .superdoc-link[target="_blank"]::after {
27756
- content: " (" attr(href) ")";
27757
- }
27758
29262
  }
27759
29263
 
27760
29264
  /* High contrast mode support */
@@ -27772,13 +29276,6 @@ const LINK_AND_TOC_STYLES = `
27772
29276
  }
27773
29277
  }
27774
29278
 
27775
- /* RTL layout support */
27776
- .superdoc-layout[dir="rtl"] .superdoc-link[target="_blank"]::after {
27777
- margin-left: 0;
27778
- margin-right: 0.25em;
27779
- content: "↖"; /* Mirror the arrow for RTL */
27780
- }
27781
-
27782
29279
  /* Screen reader only content (WCAG SC 1.3.1) */
27783
29280
  .sr-only {
27784
29281
  position: absolute;
@@ -27962,7 +29459,20 @@ const resolveTableCellBorders = (tableBorders, rowIndex, colIndex, totalRows, to
27962
29459
  };
27963
29460
  };
27964
29461
  const renderTableCell = (deps) => {
27965
- const { doc: doc2, x, y, rowHeight, cellMeasure, cell, borders, renderLine, context, applySdtDataset } = deps;
29462
+ const {
29463
+ doc: doc2,
29464
+ x,
29465
+ y,
29466
+ rowHeight,
29467
+ cellMeasure,
29468
+ cell,
29469
+ borders,
29470
+ renderLine,
29471
+ context,
29472
+ applySdtDataset,
29473
+ fromLine,
29474
+ toLine
29475
+ } = deps;
27966
29476
  const cellEl = doc2.createElement("div");
27967
29477
  cellEl.style.position = "absolute";
27968
29478
  cellEl.style.left = `${x}px`;
@@ -27970,6 +29480,7 @@ const renderTableCell = (deps) => {
27970
29480
  cellEl.style.width = `${cellMeasure.width}px`;
27971
29481
  cellEl.style.height = `${rowHeight}px`;
27972
29482
  cellEl.style.boxSizing = "border-box";
29483
+ cellEl.style.overflow = "hidden";
27973
29484
  if (borders) {
27974
29485
  applyCellBorders(cellEl, borders);
27975
29486
  }
@@ -27983,36 +29494,87 @@ const renderTableCell = (deps) => {
27983
29494
  }
27984
29495
  let contentElement;
27985
29496
  const attrs = cell?.attrs;
27986
- const padding = attrs?.padding || { top: 2, left: 4, right: 4 };
29497
+ const padding = attrs?.padding || { top: 2, left: 4, right: 4, bottom: 2 };
27987
29498
  const paddingLeft = padding.left ?? 4;
27988
29499
  const paddingTop = padding.top ?? 2;
27989
29500
  const paddingRight = padding.right ?? 4;
29501
+ const paddingBottom = padding.bottom ?? 2;
27990
29502
  const cellBlocks = cell?.blocks ?? (cell?.paragraph ? [cell.paragraph] : []);
27991
- const blockMeasures = cellMeasure.blocks ?? (cellMeasure.paragraph ? [cellMeasure.paragraph] : []);
29503
+ const blockMeasures = cellMeasure?.blocks ?? (cellMeasure?.paragraph ? [cellMeasure.paragraph] : []);
27992
29504
  if (cellBlocks.length > 0 && blockMeasures.length > 0) {
27993
29505
  const content = doc2.createElement("div");
27994
29506
  content.style.position = "absolute";
27995
29507
  content.style.left = `${x + paddingLeft}px`;
27996
29508
  content.style.top = `${y + paddingTop}px`;
27997
- content.style.width = `${Math.max(0, cellMeasure.width - paddingLeft - paddingRight)}px`;
27998
- let blockY = 0;
29509
+ const contentWidth = Math.max(0, cellMeasure.width - paddingLeft - paddingRight);
29510
+ const contentHeight = Math.max(0, rowHeight - paddingTop - paddingBottom);
29511
+ content.style.width = `${contentWidth + 1}px`;
29512
+ content.style.height = `${contentHeight}px`;
29513
+ content.style.display = "flex";
29514
+ content.style.flexDirection = "column";
29515
+ content.style.overflowX = "visible";
29516
+ content.style.overflowY = "hidden";
29517
+ if (cell?.attrs?.verticalAlign === "center") {
29518
+ content.style.justifyContent = "center";
29519
+ } else if (cell?.attrs?.verticalAlign === "bottom") {
29520
+ content.style.justifyContent = "flex-end";
29521
+ } else {
29522
+ content.style.justifyContent = "flex-start";
29523
+ }
29524
+ const blockLineCounts = [];
29525
+ for (let i = 0; i < Math.min(blockMeasures.length, cellBlocks.length); i++) {
29526
+ const bm = blockMeasures[i];
29527
+ if (bm.kind === "paragraph") {
29528
+ blockLineCounts.push(bm.lines?.length || 0);
29529
+ } else {
29530
+ blockLineCounts.push(0);
29531
+ }
29532
+ }
29533
+ const totalLines = blockLineCounts.reduce((a, b) => a + b, 0);
29534
+ const globalFromLine = fromLine ?? 0;
29535
+ const globalToLine = toLine === -1 || toLine === void 0 ? totalLines : toLine;
29536
+ let cumulativeLineCount = 0;
27999
29537
  for (let i = 0; i < Math.min(blockMeasures.length, cellBlocks.length); i++) {
28000
29538
  const blockMeasure = blockMeasures[i];
28001
29539
  const block = cellBlocks[i];
28002
29540
  if (blockMeasure.kind === "paragraph" && block?.kind === "paragraph") {
29541
+ const lines = blockMeasure.lines;
29542
+ const blockLineCount = lines?.length || 0;
29543
+ const blockStartGlobal = cumulativeLineCount;
29544
+ const blockEndGlobal = cumulativeLineCount + blockLineCount;
29545
+ if (blockEndGlobal <= globalFromLine) {
29546
+ cumulativeLineCount += blockLineCount;
29547
+ continue;
29548
+ }
29549
+ if (blockStartGlobal >= globalToLine) {
29550
+ cumulativeLineCount += blockLineCount;
29551
+ continue;
29552
+ }
29553
+ const localStartLine = Math.max(0, globalFromLine - blockStartGlobal);
29554
+ const localEndLine = Math.min(blockLineCount, globalToLine - blockStartGlobal);
28003
29555
  const paraWrapper = doc2.createElement("div");
28004
- paraWrapper.style.position = "absolute";
28005
- paraWrapper.style.top = `${blockY}px`;
29556
+ paraWrapper.style.position = "relative";
28006
29557
  paraWrapper.style.left = "0";
28007
29558
  paraWrapper.style.width = "100%";
28008
29559
  applySdtDataset(paraWrapper, block.attrs?.sdt);
28009
- const lines = blockMeasure.lines;
28010
- lines.forEach((line) => {
29560
+ let renderedHeight = 0;
29561
+ for (let lineIdx = localStartLine; lineIdx < localEndLine && lineIdx < lines.length; lineIdx++) {
29562
+ const line = lines[lineIdx];
28011
29563
  const lineEl = renderLine(block, line, { ...context, section: "body" });
28012
29564
  paraWrapper.appendChild(lineEl);
28013
- });
29565
+ renderedHeight += line.lineHeight;
29566
+ }
29567
+ const renderedEntireBlock = localStartLine === 0 && localEndLine >= blockLineCount;
29568
+ if (renderedEntireBlock && blockMeasure.totalHeight && blockMeasure.totalHeight > renderedHeight) {
29569
+ renderedHeight = blockMeasure.totalHeight;
29570
+ }
28014
29571
  content.appendChild(paraWrapper);
28015
- blockY += blockMeasure.totalHeight;
29572
+ if (renderedHeight > 0) {
29573
+ paraWrapper.style.height = `${renderedHeight}px`;
29574
+ }
29575
+ cumulativeLineCount += blockLineCount;
29576
+ } else {
29577
+ cumulativeLineCount += 0;
28016
29578
  }
28017
29579
  }
28018
29580
  contentElement = content;
@@ -28033,7 +29595,10 @@ const renderTableRow = (deps) => {
28033
29595
  allRowHeights,
28034
29596
  context,
28035
29597
  renderLine,
28036
- applySdtDataset
29598
+ applySdtDataset,
29599
+ continuesFromPrev,
29600
+ continuesOnNext,
29601
+ partialRow
28037
29602
  } = deps;
28038
29603
  const calculateXPosition = (gridColumnStart) => {
28039
29604
  let x = 0;
@@ -28066,25 +29631,57 @@ const renderTableRow = (deps) => {
28066
29631
  const isLastRow = rowIndex === totalRows - 1;
28067
29632
  const isFirstCol = gridColIndex === 0;
28068
29633
  const isLastCol = gridColIndex === totalCols - 1;
29634
+ const treatAsFirstRow = isFirstRow || continuesFromPrev;
29635
+ const treatAsLastRow = isLastRow || continuesOnNext;
28069
29636
  resolvedBorders = {
28070
- // For top: use cell's if defined, otherwise use table's top for first row
28071
- top: cellBordersAttr.top ?? borderValueToSpec(isFirstRow ? tableBorders.top : tableBorders.insideH),
28072
- // For bottom: use cell's if defined, otherwise use table's bottom for last row only
28073
- bottom: cellBordersAttr.bottom ?? borderValueToSpec(isLastRow ? tableBorders.bottom : void 0),
29637
+ // For top: use cell's if defined, otherwise use table's top border for first row OR continuation
29638
+ top: cellBordersAttr.top ?? borderValueToSpec(treatAsFirstRow ? tableBorders.top : tableBorders.insideH),
29639
+ // For bottom: use cell's if defined, otherwise use table's bottom border for last row OR before continuation
29640
+ bottom: cellBordersAttr.bottom ?? borderValueToSpec(treatAsLastRow ? tableBorders.bottom : void 0),
28074
29641
  // For left: use cell's if defined, otherwise use table's left for first col
28075
29642
  left: cellBordersAttr.left ?? borderValueToSpec(isFirstCol ? tableBorders.left : tableBorders.insideV),
28076
29643
  // For right: use cell's if defined, otherwise use table's right for last col only
28077
29644
  right: cellBordersAttr.right ?? borderValueToSpec(isLastCol ? tableBorders.right : void 0)
28078
29645
  };
28079
29646
  } else if (hasExplicitBorders) {
28080
- resolvedBorders = cellBordersAttr;
29647
+ resolvedBorders = {
29648
+ top: cellBordersAttr.top,
29649
+ bottom: cellBordersAttr.bottom,
29650
+ left: cellBordersAttr.left,
29651
+ right: cellBordersAttr.right
29652
+ };
28081
29653
  } else if (tableBorders) {
28082
- resolvedBorders = resolveTableCellBorders(tableBorders, rowIndex, gridColIndex, totalRows, totalCols);
29654
+ const isFirstRow = rowIndex === 0;
29655
+ const isLastRow = rowIndex === totalRows - 1;
29656
+ const treatAsFirstRow = isFirstRow || continuesFromPrev;
29657
+ const treatAsLastRow = isLastRow || continuesOnNext;
29658
+ const baseBorders = resolveTableCellBorders(tableBorders, rowIndex, gridColIndex, totalRows, totalCols);
29659
+ if (baseBorders) {
29660
+ resolvedBorders = {
29661
+ // If this is a continuation (continuesFromPrev), use table's top border
29662
+ top: treatAsFirstRow ? borderValueToSpec(tableBorders.top) : baseBorders.top,
29663
+ // If this continues on next (continuesOnNext), use table's bottom border
29664
+ bottom: treatAsLastRow ? borderValueToSpec(tableBorders.bottom) : baseBorders.bottom,
29665
+ left: baseBorders.left,
29666
+ right: baseBorders.right
29667
+ };
29668
+ } else {
29669
+ resolvedBorders = void 0;
29670
+ }
28083
29671
  } else {
28084
29672
  resolvedBorders = void 0;
28085
29673
  }
28086
29674
  const rowSpan = cellMeasure.rowSpan ?? 1;
28087
- const cellHeight = rowSpan > 1 ? calculateRowspanHeight(rowIndex, rowSpan) : rowMeasure.height;
29675
+ let cellHeight;
29676
+ if (partialRow) {
29677
+ cellHeight = partialRow.partialHeight;
29678
+ } else if (rowSpan > 1) {
29679
+ cellHeight = calculateRowspanHeight(rowIndex, rowSpan);
29680
+ } else {
29681
+ cellHeight = rowMeasure.height;
29682
+ }
29683
+ const fromLine = partialRow?.fromLineByCell?.[cellIndex];
29684
+ const toLine = partialRow?.toLineByCell?.[cellIndex];
28088
29685
  const { cellElement, contentElement } = renderTableCell({
28089
29686
  doc: doc2,
28090
29687
  x,
@@ -28095,7 +29692,9 @@ const renderTableRow = (deps) => {
28095
29692
  borders: resolvedBorders,
28096
29693
  renderLine,
28097
29694
  context,
28098
- applySdtDataset
29695
+ applySdtDataset,
29696
+ fromLine,
29697
+ toLine
28099
29698
  });
28100
29699
  container.appendChild(cellElement);
28101
29700
  if (contentElement) {
@@ -28209,11 +29808,46 @@ const renderTableFragment = (deps) => {
28209
29808
  if (borderCollapse === "separate" && block.attrs?.cellSpacing) {
28210
29809
  container.style.borderSpacing = `${block.attrs.cellSpacing}px`;
28211
29810
  }
28212
- const allRowHeights = measure.rows.map((r2) => r2.height);
29811
+ const allRowHeights = measure.rows.map((r2, idx) => {
29812
+ if (fragment.partialRow && fragment.partialRow.rowIndex === idx) {
29813
+ return fragment.partialRow.partialHeight;
29814
+ }
29815
+ return r2?.height ?? 0;
29816
+ });
28213
29817
  let y = 0;
29818
+ if (fragment.repeatHeaderCount && fragment.repeatHeaderCount > 0) {
29819
+ for (let r2 = 0; r2 < fragment.repeatHeaderCount; r2 += 1) {
29820
+ const rowMeasure = measure.rows[r2];
29821
+ if (!rowMeasure) break;
29822
+ renderTableRow({
29823
+ doc: doc2,
29824
+ container,
29825
+ rowIndex: r2,
29826
+ y,
29827
+ rowMeasure,
29828
+ row: block.rows[r2],
29829
+ totalRows: block.rows.length,
29830
+ tableBorders,
29831
+ columnWidths: measure.columnWidths,
29832
+ allRowHeights,
29833
+ context,
29834
+ renderLine,
29835
+ applySdtDataset,
29836
+ // Headers are always rendered as-is (no border suppression)
29837
+ continuesFromPrev: false,
29838
+ continuesOnNext: false
29839
+ });
29840
+ y += rowMeasure.height;
29841
+ }
29842
+ }
28214
29843
  for (let r2 = fragment.fromRow; r2 < fragment.toRow; r2 += 1) {
28215
29844
  const rowMeasure = measure.rows[r2];
28216
29845
  if (!rowMeasure) break;
29846
+ const isFirstRenderedBodyRow = r2 === fragment.fromRow;
29847
+ const isLastRenderedBodyRow = r2 === fragment.toRow - 1;
29848
+ const isPartialRow = fragment.partialRow && fragment.partialRow.rowIndex === r2;
29849
+ const partialRowData = isPartialRow ? fragment.partialRow : void 0;
29850
+ const actualRowHeight = partialRowData ? partialRowData.partialHeight : rowMeasure.height;
28217
29851
  renderTableRow({
28218
29852
  doc: doc2,
28219
29853
  container,
@@ -28227,13 +29861,112 @@ const renderTableFragment = (deps) => {
28227
29861
  allRowHeights,
28228
29862
  context,
28229
29863
  renderLine,
28230
- applySdtDataset
29864
+ applySdtDataset,
29865
+ // Draw top border if table continues from previous fragment (MS Word behavior)
29866
+ continuesFromPrev: isFirstRenderedBodyRow && fragment.continuesFromPrev === true,
29867
+ // Draw bottom border if table continues on next fragment (MS Word behavior)
29868
+ continuesOnNext: isLastRenderedBodyRow && fragment.continuesOnNext === true,
29869
+ // Pass partial row data for mid-row splits
29870
+ partialRow: partialRowData
28231
29871
  });
28232
- y += rowMeasure.height;
29872
+ y += actualRowHeight;
28233
29873
  }
28234
29874
  return container;
28235
29875
  };
29876
+ const isDevelopment = () => {
29877
+ if (typeof process$1 !== "undefined" && typeof process$1.env !== "undefined") {
29878
+ return process$1.env.NODE_ENV === "development";
29879
+ }
29880
+ return false;
29881
+ };
29882
+ class ValidationStatsCollector {
29883
+ constructor() {
29884
+ this.stats = {
29885
+ totalSpans: 0,
29886
+ validSpans: 0,
29887
+ missingPmStart: 0,
29888
+ missingPmEnd: 0,
29889
+ missingBoth: 0
29890
+ };
29891
+ }
29892
+ record(hasPmStart, hasPmEnd) {
29893
+ this.stats.totalSpans++;
29894
+ if (hasPmStart && hasPmEnd) {
29895
+ this.stats.validSpans++;
29896
+ } else if (!hasPmStart && !hasPmEnd) {
29897
+ this.stats.missingBoth++;
29898
+ } else if (!hasPmStart) {
29899
+ this.stats.missingPmStart++;
29900
+ } else {
29901
+ this.stats.missingPmEnd++;
29902
+ }
29903
+ }
29904
+ getStats() {
29905
+ return { ...this.stats };
29906
+ }
29907
+ reset() {
29908
+ this.stats = {
29909
+ totalSpans: 0,
29910
+ validSpans: 0,
29911
+ missingPmStart: 0,
29912
+ missingPmEnd: 0,
29913
+ missingBoth: 0
29914
+ };
29915
+ }
29916
+ getCoveragePercent() {
29917
+ if (this.stats.totalSpans === 0) return 100;
29918
+ return this.stats.validSpans / this.stats.totalSpans * 100;
29919
+ }
29920
+ logSummary() {
29921
+ if (!isDevelopment()) return;
29922
+ const coverage = this.getCoveragePercent();
29923
+ const s2 = this.stats;
29924
+ if (coverage < 100) {
29925
+ console.warn("[PmPositionValidation] PM position coverage:", {
29926
+ coverage: `${coverage.toFixed(1)}%`,
29927
+ totalSpans: s2.totalSpans,
29928
+ validSpans: s2.validSpans,
29929
+ missingPmStart: s2.missingPmStart,
29930
+ missingPmEnd: s2.missingPmEnd,
29931
+ missingBoth: s2.missingBoth
29932
+ });
29933
+ }
29934
+ }
29935
+ }
29936
+ const globalValidationStats = new ValidationStatsCollector();
29937
+ function assertPmPositions(run, context) {
29938
+ const hasPmStart = run.pmStart != null;
29939
+ const hasPmEnd = run.pmEnd != null;
29940
+ globalValidationStats.record(hasPmStart, hasPmEnd);
29941
+ if (!isDevelopment()) return;
29942
+ if (!hasPmStart || !hasPmEnd) {
29943
+ const textPreview = run.text ? run.text.substring(0, 20) + (run.text.length > 20 ? "..." : "") : "(no text)";
29944
+ console.warn(`[PmPositionValidation] Missing PM positions in ${context}:`, {
29945
+ hasPmStart,
29946
+ hasPmEnd,
29947
+ textPreview,
29948
+ fallback: "Will use PM DOM coordinates for cursor positioning"
29949
+ });
29950
+ }
29951
+ }
29952
+ function assertFragmentPmPositions(fragment, context) {
29953
+ const hasPmStart = fragment.pmStart != null;
29954
+ const hasPmEnd = fragment.pmEnd != null;
29955
+ globalValidationStats.record(hasPmStart, hasPmEnd);
29956
+ if (!isDevelopment()) return;
29957
+ if (!hasPmStart || !hasPmEnd) {
29958
+ console.warn(`[PmPositionValidation] Missing PM positions in ${context}:`, {
29959
+ fragmentKind: fragment.kind ?? "unknown",
29960
+ hasPmStart,
29961
+ hasPmEnd,
29962
+ fallback: "Will use PM DOM coordinates for cursor positioning"
29963
+ });
29964
+ }
29965
+ }
28236
29966
  const LIST_MARKER_GAP$1 = 8;
29967
+ const COMMENT_EXTERNAL_COLOR = "#B1124B";
29968
+ const COMMENT_INTERNAL_COLOR = "#078383";
29969
+ const COMMENT_INACTIVE_ALPHA = "22";
28237
29970
  const LINK_DATASET_KEYS = {
28238
29971
  blocked: "linkBlocked",
28239
29972
  docLocation: "linkDocLocation",
@@ -28890,7 +30623,7 @@ const _DomPainter = class _DomPainter {
28890
30623
  current.element = replacement;
28891
30624
  current.signature = fragmentSignature(fragment, this.blockLookup);
28892
30625
  }
28893
- this.updateFragmentElement(current.element, fragment);
30626
+ this.updateFragmentElement(current.element, fragment, contextBase.section);
28894
30627
  current.fragment = fragment;
28895
30628
  current.key = key2;
28896
30629
  current.context = contextBase;
@@ -28958,10 +30691,10 @@ const _DomPainter = class _DomPainter {
28958
30691
  return this.renderListItemFragment(fragment, context);
28959
30692
  }
28960
30693
  if (fragment.kind === "image") {
28961
- return this.renderImageFragment(fragment);
30694
+ return this.renderImageFragment(fragment, context);
28962
30695
  }
28963
30696
  if (fragment.kind === "drawing") {
28964
- return this.renderDrawingFragment(fragment);
30697
+ return this.renderDrawingFragment(fragment, context);
28965
30698
  }
28966
30699
  if (fragment.kind === "table") {
28967
30700
  return this.renderTableFragment(fragment, context);
@@ -28994,7 +30727,7 @@ const _DomPainter = class _DomPainter {
28994
30727
  const hasMarker = !fragment.continuesFromPrev && fragment.markerWidth && wordLayout?.marker;
28995
30728
  const styles = isTocEntry ? { ...fragmentStyles, whiteSpace: "nowrap" } : hasMarker ? { ...fragmentStyles, overflow: "visible" } : fragmentStyles;
28996
30729
  applyStyles$2(fragmentEl, styles);
28997
- this.applyFragmentFrame(fragmentEl, fragment);
30730
+ this.applyFragmentFrame(fragmentEl, fragment, context.section);
28998
30731
  if (isTocEntry) {
28999
30732
  fragmentEl.classList.add("superdoc-toc-entry");
29000
30733
  }
@@ -29012,8 +30745,32 @@ const _DomPainter = class _DomPainter {
29012
30745
  }
29013
30746
  this.applySdtDataset(fragmentEl, block.attrs?.sdt);
29014
30747
  this.applyContainerSdtDataset(fragmentEl, block.attrs?.containerSdt);
30748
+ const dropCapDescriptor = block.attrs?.dropCapDescriptor;
30749
+ const dropCapMeasure = measure.dropCap;
30750
+ if (dropCapDescriptor && dropCapMeasure && !fragment.continuesFromPrev) {
30751
+ const dropCapEl = this.renderDropCap(dropCapDescriptor, dropCapMeasure);
30752
+ fragmentEl.appendChild(dropCapEl);
30753
+ }
30754
+ if (fragmentEl.style.paddingLeft) fragmentEl.style.removeProperty("padding-left");
30755
+ if (fragmentEl.style.paddingRight) fragmentEl.style.removeProperty("padding-right");
30756
+ if (fragmentEl.style.textIndent) fragmentEl.style.removeProperty("text-indent");
30757
+ const paraIndent = block.attrs?.indent;
30758
+ const paraIndentLeft = paraIndent?.left ?? 0;
30759
+ const paraIndentRight = paraIndent?.right ?? 0;
30760
+ const firstLineOffset = (paraIndent?.firstLine ?? 0) - (paraIndent?.hanging ?? 0);
29015
30761
  lines.forEach((line, index2) => {
29016
30762
  const lineEl = this.renderLine(block, line, context);
30763
+ if (paraIndentLeft) {
30764
+ lineEl.style.paddingLeft = `${paraIndentLeft}px`;
30765
+ }
30766
+ if (paraIndentRight) {
30767
+ lineEl.style.paddingRight = `${paraIndentRight}px`;
30768
+ }
30769
+ if (!fragment.continuesFromPrev && index2 === 0 && firstLineOffset) {
30770
+ lineEl.style.textIndent = `${firstLineOffset}px`;
30771
+ } else if (firstLineOffset) {
30772
+ lineEl.style.textIndent = "0px";
30773
+ }
29017
30774
  if (index2 === 0 && !fragment.continuesFromPrev && fragment.markerWidth && wordLayout?.marker) {
29018
30775
  const markerContainer = this.doc.createElement("span");
29019
30776
  markerContainer.style.display = "inline-block";
@@ -29024,6 +30781,12 @@ const _DomPainter = class _DomPainter {
29024
30781
  markerEl.style.textAlign = wordLayout.marker.justification ?? "right";
29025
30782
  markerEl.style.paddingRight = `${LIST_MARKER_GAP$1}px`;
29026
30783
  markerEl.style.pointerEvents = "none";
30784
+ const indentLeft = paraIndentLeft;
30785
+ const hanging = paraIndent?.hanging ?? 0;
30786
+ const textStartX = indentLeft - hanging;
30787
+ const markerLeftX = textStartX - fragment.markerWidth;
30788
+ markerEl.style.position = "relative";
30789
+ markerEl.style.left = `${markerLeftX}px`;
29027
30790
  markerEl.style.fontFamily = wordLayout.marker.run.fontFamily;
29028
30791
  markerEl.style.fontSize = `${wordLayout.marker.run.fontSize}px`;
29029
30792
  markerEl.style.fontWeight = wordLayout.marker.run.bold ? "bold" : "";
@@ -29082,6 +30845,53 @@ const _DomPainter = class _DomPainter {
29082
30845
  }
29083
30846
  return el;
29084
30847
  }
30848
+ /**
30849
+ * Renders a drop cap element as a floated span at the start of a paragraph.
30850
+ *
30851
+ * Drop caps are large initial letters that span multiple lines of text.
30852
+ * This method creates a floated element with the drop cap letter styled
30853
+ * according to the descriptor's run properties.
30854
+ *
30855
+ * @param descriptor - The drop cap descriptor with text and styling info
30856
+ * @param measure - The measured dimensions of the drop cap
30857
+ * @returns HTMLElement containing the rendered drop cap
30858
+ */
30859
+ renderDropCap(descriptor, measure) {
30860
+ const doc2 = this.doc;
30861
+ const { run, mode } = descriptor;
30862
+ const dropCapEl = doc2.createElement("span");
30863
+ dropCapEl.classList.add("superdoc-drop-cap");
30864
+ dropCapEl.textContent = run.text;
30865
+ dropCapEl.style.fontFamily = run.fontFamily;
30866
+ dropCapEl.style.fontSize = `${run.fontSize}px`;
30867
+ if (run.bold) {
30868
+ dropCapEl.style.fontWeight = "bold";
30869
+ }
30870
+ if (run.italic) {
30871
+ dropCapEl.style.fontStyle = "italic";
30872
+ }
30873
+ if (run.color) {
30874
+ dropCapEl.style.color = run.color;
30875
+ }
30876
+ if (mode === "drop") {
30877
+ dropCapEl.style.float = "left";
30878
+ dropCapEl.style.marginRight = "4px";
30879
+ dropCapEl.style.lineHeight = "1";
30880
+ } else if (mode === "margin") {
30881
+ dropCapEl.style.position = "absolute";
30882
+ dropCapEl.style.left = "0";
30883
+ dropCapEl.style.lineHeight = "1";
30884
+ }
30885
+ if (run.position && run.position !== 0) {
30886
+ dropCapEl.style.position = dropCapEl.style.position || "relative";
30887
+ dropCapEl.style.top = `${run.position}px`;
30888
+ }
30889
+ if (measure) {
30890
+ dropCapEl.style.width = `${measure.width}px`;
30891
+ dropCapEl.style.height = `${measure.height}px`;
30892
+ }
30893
+ return dropCapEl;
30894
+ }
29085
30895
  renderListItemFragment(fragment, context) {
29086
30896
  try {
29087
30897
  const lookup = this.blockLookup.get(fragment.blockId);
@@ -29159,7 +30969,7 @@ const _DomPainter = class _DomPainter {
29159
30969
  return this.createErrorPlaceholder(fragment.blockId, error);
29160
30970
  }
29161
30971
  }
29162
- renderImageFragment(fragment) {
30972
+ renderImageFragment(fragment, context) {
29163
30973
  try {
29164
30974
  const lookup = this.blockLookup.get(fragment.blockId);
29165
30975
  if (!lookup || lookup.block.kind !== "image" || lookup.measure.kind !== "image") {
@@ -29172,7 +30982,7 @@ const _DomPainter = class _DomPainter {
29172
30982
  const fragmentEl = this.doc.createElement("div");
29173
30983
  fragmentEl.classList.add(CLASS_NAMES.fragment, "superdoc-image-fragment");
29174
30984
  applyStyles$2(fragmentEl, fragmentStyles);
29175
- this.applyFragmentFrame(fragmentEl, fragment);
30985
+ this.applyFragmentFrame(fragmentEl, fragment, context.section);
29176
30986
  fragmentEl.style.height = `${fragment.height}px`;
29177
30987
  this.applySdtDataset(fragmentEl, block.attrs?.sdt);
29178
30988
  this.applyContainerSdtDataset(fragmentEl, block.attrs?.containerSdt);
@@ -29207,7 +31017,7 @@ const _DomPainter = class _DomPainter {
29207
31017
  return this.createErrorPlaceholder(fragment.blockId, error);
29208
31018
  }
29209
31019
  }
29210
- renderDrawingFragment(fragment) {
31020
+ renderDrawingFragment(fragment, context) {
29211
31021
  try {
29212
31022
  const lookup = this.blockLookup.get(fragment.blockId);
29213
31023
  if (!lookup || lookup.block.kind !== "drawing" || lookup.measure.kind !== "drawing") {
@@ -29221,7 +31031,7 @@ const _DomPainter = class _DomPainter {
29221
31031
  const fragmentEl = this.doc.createElement("div");
29222
31032
  fragmentEl.classList.add(CLASS_NAMES.fragment, "superdoc-drawing-fragment");
29223
31033
  applyStyles$2(fragmentEl, fragmentStyles);
29224
- this.applyFragmentFrame(fragmentEl, fragment);
31034
+ this.applyFragmentFrame(fragmentEl, fragment, context.section);
29225
31035
  fragmentEl.style.height = `${fragment.height}px`;
29226
31036
  fragmentEl.style.position = "absolute";
29227
31037
  fragmentEl.style.overflow = "hidden";
@@ -29668,13 +31478,16 @@ const _DomPainter = class _DomPainter {
29668
31478
  if (!this.doc) {
29669
31479
  throw new Error("DomPainter: document is not available");
29670
31480
  }
31481
+ const applyFragmentFrameWithSection = (el, frag) => {
31482
+ this.applyFragmentFrame(el, frag, context.section);
31483
+ };
29671
31484
  return renderTableFragment({
29672
31485
  doc: this.doc,
29673
31486
  fragment,
29674
31487
  context,
29675
31488
  blockLookup: this.blockLookup,
29676
31489
  renderLine: this.renderLine.bind(this),
29677
- applyFragmentFrame: this.applyFragmentFrame.bind(this),
31490
+ applyFragmentFrame: applyFragmentFrameWithSection,
29678
31491
  applySdtDataset: this.applySdtDataset.bind(this),
29679
31492
  applyStyles: applyStyles$2
29680
31493
  });
@@ -29684,7 +31497,7 @@ const _DomPainter = class _DomPainter {
29684
31497
  * @returns Sanitized link data or null if invalid/missing
29685
31498
  */
29686
31499
  extractLinkData(run) {
29687
- if (run.kind === "tab") {
31500
+ if (run.kind === "tab" || run.kind === "image" || run.kind === "lineBreak") {
29688
31501
  return null;
29689
31502
  }
29690
31503
  const link = run.link;
@@ -29838,11 +31651,29 @@ const _DomPainter = class _DomPainter {
29838
31651
  isImageRun(run) {
29839
31652
  return run.kind === "image";
29840
31653
  }
31654
+ /**
31655
+ * Type guard to check if a run is a line break run.
31656
+ */
31657
+ isLineBreakRun(run) {
31658
+ return run.kind === "lineBreak";
31659
+ }
31660
+ /**
31661
+ * Type guard to check if a run is a break run.
31662
+ */
31663
+ isBreakRun(run) {
31664
+ return run.kind === "break";
31665
+ }
29841
31666
  renderRun(run, context, trackedConfig) {
29842
31667
  if (this.isImageRun(run)) {
29843
31668
  return this.renderImageRun(run);
29844
31669
  }
29845
- if (!run.text || !this.doc) {
31670
+ if (this.isLineBreakRun(run)) {
31671
+ return null;
31672
+ }
31673
+ if (this.isBreakRun(run)) {
31674
+ return null;
31675
+ }
31676
+ if (!("text" in run) || !run.text || !this.doc) {
29846
31677
  return null;
29847
31678
  }
29848
31679
  const linkData = this.extractLinkData(run);
@@ -29866,8 +31697,21 @@ const _DomPainter = class _DomPainter {
29866
31697
  }
29867
31698
  }
29868
31699
  applyRunStyles(elem, run, isActiveLink);
31700
+ const commentColor = getCommentHighlight(run);
31701
+ if (commentColor && !run.highlight) {
31702
+ elem.style.backgroundColor = commentColor;
31703
+ }
31704
+ const commentAnnotations = run.comments;
31705
+ if (commentAnnotations?.length) {
31706
+ elem.dataset.commentIds = commentAnnotations.map((c) => c.commentId).join(",");
31707
+ if (commentAnnotations.some((c) => c.internal)) {
31708
+ elem.dataset.commentInternal = "true";
31709
+ }
31710
+ elem.classList.add("superdoc-comment-highlight");
31711
+ }
29869
31712
  elem.style.zIndex = "1";
29870
31713
  applyRunDataAttributes(elem, run.dataAttrs);
31714
+ assertPmPositions(run, "paragraph text run");
29871
31715
  if (run.pmStart != null) elem.dataset.pmStart = String(run.pmStart);
29872
31716
  if (run.pmEnd != null) elem.dataset.pmEnd = String(run.pmEnd);
29873
31717
  if (trackedConfig) {
@@ -29946,6 +31790,7 @@ const _DomPainter = class _DomPainter {
29946
31790
  img.style.marginRight = `${run.distRight}px`;
29947
31791
  }
29948
31792
  img.style.zIndex = "1";
31793
+ assertPmPositions(run, "inline image run");
29949
31794
  if (run.pmStart != null) {
29950
31795
  img.dataset.pmStart = String(run.pmStart);
29951
31796
  }
@@ -29969,6 +31814,12 @@ const _DomPainter = class _DomPainter {
29969
31814
  if (styleId) {
29970
31815
  el.setAttribute("styleid", styleId);
29971
31816
  }
31817
+ const alignment = block.attrs?.alignment;
31818
+ if (alignment === "center" || alignment === "right" || alignment === "justify") {
31819
+ el.style.textAlign = alignment === "justify" ? "justify" : alignment;
31820
+ } else {
31821
+ el.style.textAlign = "left";
31822
+ }
29972
31823
  const lineRange = computeLinePmRange(block, line);
29973
31824
  if (lineRange.pmStart != null) {
29974
31825
  el.dataset.pmStart = String(lineRange.pmStart);
@@ -29976,9 +31827,9 @@ const _DomPainter = class _DomPainter {
29976
31827
  if (lineRange.pmEnd != null) {
29977
31828
  el.dataset.pmEnd = String(lineRange.pmEnd);
29978
31829
  }
29979
- const runs = sliceRunsForLine(block, line);
31830
+ const runsForLine = sliceRunsForLine(block, line);
29980
31831
  const trackedConfig = this.resolveTrackedChangesConfig(block);
29981
- if (runs.length === 0) {
31832
+ if (runsForLine.length === 0) {
29982
31833
  const span = this.doc.createElement("span");
29983
31834
  span.innerHTML = "&nbsp;";
29984
31835
  el.appendChild(span);
@@ -30026,49 +31877,115 @@ const _DomPainter = class _DomPainter {
30026
31877
  const hasExplicitPositioning = line.segments?.some((seg) => seg.x !== void 0);
30027
31878
  if (hasExplicitPositioning && line.segments) {
30028
31879
  let cumulativeX = 0;
30029
- line.segments.forEach((segment, _segIdx) => {
30030
- const baseRun = runs[segment.runIndex];
30031
- if (!baseRun || baseRun.kind === "tab") return;
30032
- if (this.isImageRun(baseRun)) {
30033
- const elem2 = this.renderRun(baseRun, context, trackedConfig);
30034
- if (elem2) {
30035
- if (styleId) {
30036
- elem2.setAttribute("styleid", styleId);
30037
- }
30038
- el.appendChild(elem2);
31880
+ const segmentsByRun = /* @__PURE__ */ new Map();
31881
+ line.segments.forEach((segment) => {
31882
+ const list = segmentsByRun.get(segment.runIndex);
31883
+ if (list) {
31884
+ list.push(segment);
31885
+ } else {
31886
+ segmentsByRun.set(segment.runIndex, [segment]);
31887
+ }
31888
+ });
31889
+ const findImmediateNextSegmentX = (fromRunIndex) => {
31890
+ const nextRunIdx = fromRunIndex + 1;
31891
+ if (nextRunIdx <= line.toRun) {
31892
+ const nextSegments = segmentsByRun.get(nextRunIdx);
31893
+ if (nextSegments && nextSegments.length > 0) {
31894
+ const firstSegment = nextSegments[0];
31895
+ return firstSegment.x;
30039
31896
  }
30040
- return;
30041
31897
  }
30042
- const segmentText = baseRun.text.slice(segment.fromChar, segment.toChar);
30043
- const segmentRun = { ...baseRun, text: segmentText };
30044
- const elem = this.renderRun(segmentRun, context, trackedConfig);
30045
- if (elem) {
31898
+ return void 0;
31899
+ };
31900
+ for (let runIndex = line.fromRun; runIndex <= line.toRun; runIndex += 1) {
31901
+ const baseRun = block.runs[runIndex];
31902
+ if (!baseRun) continue;
31903
+ if (baseRun.kind === "tab") {
31904
+ const immediateNextX = findImmediateNextSegmentX(runIndex);
31905
+ const tabStartX = cumulativeX;
31906
+ const tabEndX = immediateNextX !== void 0 ? immediateNextX : tabStartX + (baseRun.width ?? 0);
31907
+ const actualTabWidth = tabEndX - tabStartX;
31908
+ const tabEl = this.doc.createElement("span");
31909
+ tabEl.style.position = "absolute";
31910
+ tabEl.style.left = `${tabStartX}px`;
31911
+ tabEl.style.top = "0px";
31912
+ tabEl.style.width = `${actualTabWidth}px`;
31913
+ tabEl.style.height = `${line.lineHeight}px`;
31914
+ tabEl.style.display = "inline-block";
31915
+ tabEl.style.visibility = "hidden";
31916
+ tabEl.style.pointerEvents = "none";
31917
+ tabEl.style.zIndex = "1";
30046
31918
  if (styleId) {
30047
- elem.setAttribute("styleid", styleId);
31919
+ tabEl.setAttribute("styleid", styleId);
30048
31920
  }
30049
- let xPos;
30050
- if (segment.x !== void 0) {
30051
- xPos = segment.x;
30052
- } else {
30053
- xPos = cumulativeX;
31921
+ if (baseRun.pmStart != null) tabEl.dataset.pmStart = String(baseRun.pmStart);
31922
+ if (baseRun.pmEnd != null) tabEl.dataset.pmEnd = String(baseRun.pmEnd);
31923
+ el.appendChild(tabEl);
31924
+ cumulativeX = tabEndX;
31925
+ continue;
31926
+ }
31927
+ if (this.isImageRun(baseRun)) {
31928
+ const elem = this.renderRun(baseRun, context, trackedConfig);
31929
+ if (elem) {
31930
+ if (styleId) {
31931
+ elem.setAttribute("styleid", styleId);
31932
+ }
31933
+ el.appendChild(elem);
30054
31934
  }
30055
- elem.style.position = "absolute";
30056
- elem.style.left = `${xPos}px`;
30057
- el.appendChild(elem);
30058
- if (this.doc) {
30059
- const measureEl = elem.cloneNode(true);
30060
- measureEl.style.position = "absolute";
30061
- measureEl.style.visibility = "hidden";
30062
- measureEl.style.left = "-9999px";
30063
- this.doc.body.appendChild(measureEl);
30064
- const width = measureEl.offsetWidth;
30065
- this.doc.body.removeChild(measureEl);
31935
+ continue;
31936
+ }
31937
+ if (this.isLineBreakRun(baseRun)) {
31938
+ continue;
31939
+ }
31940
+ if (this.isBreakRun(baseRun)) {
31941
+ continue;
31942
+ }
31943
+ const runSegments = segmentsByRun.get(runIndex);
31944
+ if (!runSegments || runSegments.length === 0) {
31945
+ continue;
31946
+ }
31947
+ if (!("text" in baseRun)) {
31948
+ continue;
31949
+ }
31950
+ const baseText = baseRun.text ?? "";
31951
+ const runPmStart = baseRun.pmStart ?? null;
31952
+ const fallbackPmEnd = runPmStart != null && baseRun.pmEnd == null ? runPmStart + baseText.length : baseRun.pmEnd ?? null;
31953
+ runSegments.forEach((segment) => {
31954
+ const segmentText = baseText.slice(segment.fromChar, segment.toChar);
31955
+ if (!segmentText) return;
31956
+ const pmSliceStart = runPmStart != null ? runPmStart + segment.fromChar : void 0;
31957
+ const pmSliceEnd = runPmStart != null ? runPmStart + segment.toChar : fallbackPmEnd ?? void 0;
31958
+ const segmentRun = {
31959
+ ...baseRun,
31960
+ text: segmentText,
31961
+ pmStart: pmSliceStart,
31962
+ pmEnd: pmSliceEnd
31963
+ };
31964
+ const elem = this.renderRun(segmentRun, context, trackedConfig);
31965
+ if (elem) {
31966
+ if (styleId) {
31967
+ elem.setAttribute("styleid", styleId);
31968
+ }
31969
+ const xPos = segment.x !== void 0 ? segment.x : cumulativeX;
31970
+ elem.style.position = "absolute";
31971
+ elem.style.left = `${xPos}px`;
31972
+ el.appendChild(elem);
31973
+ let width = segment.width ?? 0;
31974
+ if (width <= 0 && this.doc) {
31975
+ const measureEl = elem.cloneNode(true);
31976
+ measureEl.style.position = "absolute";
31977
+ measureEl.style.visibility = "hidden";
31978
+ measureEl.style.left = "-9999px";
31979
+ this.doc.body.appendChild(measureEl);
31980
+ width = measureEl.offsetWidth;
31981
+ this.doc.body.removeChild(measureEl);
31982
+ }
30066
31983
  cumulativeX = xPos + width;
30067
31984
  }
30068
- }
30069
- });
31985
+ });
31986
+ }
30070
31987
  } else {
30071
- runs.forEach((run) => {
31988
+ runsForLine.forEach((run) => {
30072
31989
  const elem = this.renderRun(run, context, trackedConfig);
30073
31990
  if (elem) {
30074
31991
  if (styleId) {
@@ -30126,8 +32043,18 @@ const _DomPainter = class _DomPainter {
30126
32043
  elem.dataset.trackChangeDate = meta.date;
30127
32044
  }
30128
32045
  }
30129
- updateFragmentElement(el, fragment) {
30130
- this.applyFragmentFrame(el, fragment);
32046
+ /**
32047
+ * Updates an existing fragment element's position and dimensions in place.
32048
+ * Used during incremental updates to efficiently reposition fragments without full re-render.
32049
+ *
32050
+ * @param el - The HTMLElement representing the fragment to update
32051
+ * @param fragment - The fragment data containing updated position and dimensions
32052
+ * @param section - The document section ('body', 'header', 'footer') containing this fragment.
32053
+ * Affects PM position validation - only body sections validate PM positions.
32054
+ * If undefined, defaults to 'body' section behavior.
32055
+ */
32056
+ updateFragmentElement(el, fragment, section) {
32057
+ this.applyFragmentFrame(el, fragment, section);
30131
32058
  if (fragment.kind === "image") {
30132
32059
  el.style.height = `${fragment.height}px`;
30133
32060
  }
@@ -30135,12 +32062,27 @@ const _DomPainter = class _DomPainter {
30135
32062
  el.style.height = `${fragment.height}px`;
30136
32063
  }
30137
32064
  }
30138
- applyFragmentFrame(el, fragment) {
32065
+ /**
32066
+ * Applies fragment positioning, dimensions, and metadata to an HTML element.
32067
+ * Sets CSS positioning, block ID, and PM position data attributes for paragraph fragments.
32068
+ *
32069
+ * @param el - The HTMLElement to apply fragment properties to
32070
+ * @param fragment - The fragment data containing position, dimensions, and PM position information
32071
+ * @param section - The document section ('body', 'header', 'footer') containing this fragment.
32072
+ * Controls PM position validation behavior:
32073
+ * - 'body' or undefined: PM positions are validated and required for paragraph fragments
32074
+ * - 'header' or 'footer': PM position validation is skipped (these sections have separate PM coordinate spaces)
32075
+ * When undefined, defaults to 'body' section behavior (validation enabled).
32076
+ */
32077
+ applyFragmentFrame(el, fragment, section) {
30139
32078
  el.style.left = `${fragment.x}px`;
30140
32079
  el.style.top = `${fragment.y}px`;
30141
32080
  el.style.width = `${fragment.width}px`;
30142
32081
  el.dataset.blockId = fragment.blockId;
30143
32082
  if (fragment.kind === "para") {
32083
+ if (section === "body" || section === void 0) {
32084
+ assertFragmentPmPositions(fragment, "paragraph fragment");
32085
+ }
30144
32086
  if (fragment.pmStart != null) {
30145
32087
  el.dataset.pmStart = String(fragment.pmStart);
30146
32088
  } else {
@@ -30323,7 +32265,12 @@ const fragmentKey = (fragment) => {
30323
32265
  if (fragment.kind === "drawing") {
30324
32266
  return `drawing:${fragment.blockId}:${fragment.x}:${fragment.y}`;
30325
32267
  }
30326
- return `${fragment.kind}:${fragment.blockId}`;
32268
+ if (fragment.kind === "table") {
32269
+ const partialKey = fragment.partialRow ? `:${fragment.partialRow.fromLineByCell.join(",")}-${fragment.partialRow.toLineByCell.join(",")}` : "";
32270
+ return `table:${fragment.blockId}:${fragment.fromRow}:${fragment.toRow}${partialKey}`;
32271
+ }
32272
+ const _exhaustiveCheck = fragment;
32273
+ return _exhaustiveCheck;
30327
32274
  };
30328
32275
  const fragmentSignature = (fragment, lookup) => {
30329
32276
  const base2 = lookup.get(fragment.blockId)?.version ?? "missing";
@@ -30365,6 +32312,20 @@ const fragmentSignature = (fragment, lookup) => {
30365
32312
  fragment.zIndex ?? ""
30366
32313
  ].join("|");
30367
32314
  }
32315
+ if (fragment.kind === "table") {
32316
+ const partialSig = fragment.partialRow ? `${fragment.partialRow.fromLineByCell.join(",")}-${fragment.partialRow.toLineByCell.join(",")}-${fragment.partialRow.partialHeight}` : "";
32317
+ return [
32318
+ base2,
32319
+ fragment.fromRow,
32320
+ fragment.toRow,
32321
+ fragment.width,
32322
+ fragment.height,
32323
+ fragment.continuesFromPrev ? 1 : 0,
32324
+ fragment.continuesOnNext ? 1 : 0,
32325
+ fragment.repeatHeaderCount ?? 0,
32326
+ partialSig
32327
+ ].join("|");
32328
+ }
30368
32329
  return base2;
30369
32330
  };
30370
32331
  const deriveBlockVersion = (block) => {
@@ -30387,22 +32348,29 @@ const deriveBlockVersion = (block) => {
30387
32348
  imgRun.pmEnd ?? ""
30388
32349
  ].join(",");
30389
32350
  }
32351
+ if (run.kind === "lineBreak") {
32352
+ return ["linebreak", run.pmStart ?? "", run.pmEnd ?? ""].join(",");
32353
+ }
32354
+ if (run.kind === "tab") {
32355
+ return [run.text ?? "", "tab", run.pmStart ?? "", run.pmEnd ?? ""].join(",");
32356
+ }
32357
+ const textRun = run;
30390
32358
  return [
30391
- run.text ?? "",
30392
- run.kind !== "tab" ? run.fontFamily : "",
30393
- run.kind !== "tab" ? run.fontSize : "",
30394
- run.kind !== "tab" && run.bold ? 1 : 0,
30395
- run.kind !== "tab" && run.italic ? 1 : 0,
30396
- run.kind !== "tab" ? run.color ?? "" : "",
32359
+ textRun.text ?? "",
32360
+ textRun.fontFamily,
32361
+ textRun.fontSize,
32362
+ textRun.bold ? 1 : 0,
32363
+ textRun.italic ? 1 : 0,
32364
+ textRun.color ?? "",
30397
32365
  // Text decorations - ensures DOM updates when decoration properties change.
30398
- run.kind !== "tab" ? run.underline?.style ?? "" : "",
30399
- run.kind !== "tab" ? run.underline?.color ?? "" : "",
30400
- run.kind !== "tab" && run.strike ? 1 : 0,
30401
- run.kind !== "tab" ? run.highlight ?? "" : "",
30402
- run.kind !== "tab" && run.letterSpacing != null ? run.letterSpacing : "",
30403
- run.pmStart ?? "",
30404
- run.pmEnd ?? "",
30405
- run.kind !== "tab" ? run.token ?? "" : ""
32366
+ textRun.underline?.style ?? "",
32367
+ textRun.underline?.color ?? "",
32368
+ textRun.strike ? 1 : 0,
32369
+ textRun.highlight ?? "",
32370
+ textRun.letterSpacing != null ? textRun.letterSpacing : "",
32371
+ textRun.pmStart ?? "",
32372
+ textRun.pmEnd ?? "",
32373
+ textRun.token ?? ""
30406
32374
  ].join(",");
30407
32375
  }).join("|");
30408
32376
  }
@@ -30453,16 +32421,57 @@ const deriveBlockVersion = (block) => {
30453
32421
  }
30454
32422
  if (block.kind === "table") {
30455
32423
  const tableBlock = block;
30456
- return [
30457
- block.id,
30458
- tableBlock.columnWidths ? JSON.stringify(tableBlock.columnWidths) : "",
30459
- tableBlock.rows.length
30460
- ].join("|");
32424
+ const hashString = (seed, value) => {
32425
+ let hash22 = seed >>> 0;
32426
+ for (let i = 0; i < value.length; i++) {
32427
+ hash22 ^= value.charCodeAt(i);
32428
+ hash22 = Math.imul(hash22, 16777619);
32429
+ }
32430
+ return hash22 >>> 0;
32431
+ };
32432
+ const hashNumber = (seed, value) => {
32433
+ const n = Number.isFinite(value) ? value : 0;
32434
+ let hash22 = seed ^ n;
32435
+ hash22 = Math.imul(hash22, 16777619);
32436
+ hash22 ^= hash22 >>> 13;
32437
+ return hash22 >>> 0;
32438
+ };
32439
+ let hash2 = 2166136261;
32440
+ hash2 = hashString(hash2, block.id);
32441
+ hash2 = hashNumber(hash2, tableBlock.rows.length);
32442
+ hash2 = (tableBlock.columnWidths ?? []).reduce((acc, width) => hashNumber(acc, Math.round(width * 1e3)), hash2);
32443
+ const rows = tableBlock.rows ?? [];
32444
+ for (const row of rows) {
32445
+ if (!row || !Array.isArray(row.cells)) continue;
32446
+ hash2 = hashNumber(hash2, row.cells.length);
32447
+ for (const cell of row.cells) {
32448
+ if (!cell) continue;
32449
+ const cellBlocks = cell.blocks ?? (cell.paragraph ? [cell.paragraph] : []);
32450
+ hash2 = hashNumber(hash2, cellBlocks.length);
32451
+ hash2 = hashNumber(hash2, cell.rowSpan ?? 1);
32452
+ hash2 = hashNumber(hash2, cell.colSpan ?? 1);
32453
+ for (const cellBlock of cellBlocks) {
32454
+ hash2 = hashString(hash2, cellBlock?.kind ?? "unknown");
32455
+ if (cellBlock?.kind === "paragraph") {
32456
+ const runs = cellBlock.runs ?? [];
32457
+ hash2 = hashNumber(hash2, runs.length);
32458
+ for (const run of runs) {
32459
+ if ("text" in run && typeof run.text === "string") {
32460
+ hash2 = hashString(hash2, run.text);
32461
+ }
32462
+ hash2 = hashNumber(hash2, run.pmStart ?? -1);
32463
+ hash2 = hashNumber(hash2, run.pmEnd ?? -1);
32464
+ }
32465
+ }
32466
+ }
32467
+ }
32468
+ }
32469
+ return [block.id, tableBlock.rows.length, hash2.toString(16)].join("|");
30461
32470
  }
30462
32471
  return block.id;
30463
32472
  };
30464
32473
  const applyRunStyles = (element, run, isLink = false) => {
30465
- if (run.kind === "tab" || run.kind === "image") {
32474
+ if (run.kind === "tab" || run.kind === "image" || run.kind === "lineBreak" || run.kind === "break") {
30466
32475
  return;
30467
32476
  }
30468
32477
  element.style.fontFamily = run.fontFamily;
@@ -30494,6 +32503,13 @@ const applyRunStyles = (element, run, isLink = false) => {
30494
32503
  element.style.textDecorationLine = decorations.join(" ");
30495
32504
  }
30496
32505
  };
32506
+ const getCommentHighlight = (run) => {
32507
+ const comments = run.comments;
32508
+ if (!comments || comments.length === 0) return void 0;
32509
+ const primary = comments[0];
32510
+ const base2 = primary.internal ? COMMENT_INTERNAL_COLOR : COMMENT_EXTERNAL_COLOR;
32511
+ return `${base2}${COMMENT_INACTIVE_ALPHA}`;
32512
+ };
30497
32513
  const applyRunDataAttributes = (element, dataAttrs) => {
30498
32514
  if (!dataAttrs) return;
30499
32515
  Object.entries(dataAttrs).forEach(([key2, value]) => {
@@ -30585,6 +32601,21 @@ const sliceRunsForLine = (block, line) => {
30585
32601
  result.push(run);
30586
32602
  continue;
30587
32603
  }
32604
+ if (run.kind === "lineBreak") {
32605
+ result.push(run);
32606
+ continue;
32607
+ }
32608
+ if (run.kind === "break") {
32609
+ result.push(run);
32610
+ continue;
32611
+ }
32612
+ if (run.kind === "tab") {
32613
+ result.push(run);
32614
+ continue;
32615
+ }
32616
+ if (!("text" in run)) {
32617
+ continue;
32618
+ }
30588
32619
  const text = run.text ?? "";
30589
32620
  const isFirstRun = runIndex === line.fromRun;
30590
32621
  const isLastRun = runIndex === line.toRun;
@@ -30598,19 +32629,14 @@ const sliceRunsForLine = (block, line) => {
30598
32629
  if (!slice2) continue;
30599
32630
  const pmSliceStart = runPmStart != null ? runPmStart + start2 : void 0;
30600
32631
  const pmSliceEnd = runPmStart != null ? runPmStart + end2 : fallbackPmEnd ?? void 0;
30601
- if (run.kind === "tab") {
30602
- if (slice2.includes(" ")) {
30603
- result.push(run);
30604
- }
30605
- } else {
30606
- const sliced = {
30607
- ...run,
30608
- text: slice2,
30609
- pmStart: pmSliceStart,
30610
- pmEnd: pmSliceEnd
30611
- };
30612
- result.push(sliced);
30613
- }
32632
+ const sliced = {
32633
+ ...run,
32634
+ text: slice2,
32635
+ pmStart: pmSliceStart,
32636
+ pmEnd: pmSliceEnd,
32637
+ comments: run.comments ? [...run.comments] : void 0
32638
+ };
32639
+ result.push(sliced);
30614
32640
  } else {
30615
32641
  result.push(run);
30616
32642
  }
@@ -30638,6 +32664,54 @@ const computeLinePmRange = (block, line) => {
30638
32664
  }
30639
32665
  continue;
30640
32666
  }
32667
+ if (run.kind === "lineBreak") {
32668
+ const runPmStart2 = run.pmStart ?? null;
32669
+ const runPmEnd = run.pmEnd ?? null;
32670
+ if (runPmStart2 == null || runPmEnd == null) {
32671
+ continue;
32672
+ }
32673
+ if (pmStart == null) {
32674
+ pmStart = runPmStart2;
32675
+ }
32676
+ pmEnd = runPmEnd;
32677
+ if (runIndex === line.toRun) {
32678
+ break;
32679
+ }
32680
+ continue;
32681
+ }
32682
+ if (run.kind === "break") {
32683
+ const runPmStart2 = run.pmStart ?? null;
32684
+ const runPmEnd = run.pmEnd ?? null;
32685
+ if (runPmStart2 == null || runPmEnd == null) {
32686
+ continue;
32687
+ }
32688
+ if (pmStart == null) {
32689
+ pmStart = runPmStart2;
32690
+ }
32691
+ pmEnd = runPmEnd;
32692
+ if (runIndex === line.toRun) {
32693
+ break;
32694
+ }
32695
+ continue;
32696
+ }
32697
+ if (run.kind === "tab") {
32698
+ const runPmStart2 = run.pmStart ?? null;
32699
+ const runPmEnd = run.pmEnd ?? null;
32700
+ if (runPmStart2 == null || runPmEnd == null) {
32701
+ continue;
32702
+ }
32703
+ if (pmStart == null) {
32704
+ pmStart = runPmStart2;
32705
+ }
32706
+ pmEnd = runPmEnd;
32707
+ if (runIndex === line.toRun) {
32708
+ break;
32709
+ }
32710
+ continue;
32711
+ }
32712
+ if (!("text" in run)) {
32713
+ continue;
32714
+ }
30641
32715
  const text = run.text ?? "";
30642
32716
  const runLength = text.length;
30643
32717
  const runPmStart = run.pmStart ?? null;
@@ -30676,6 +32750,15 @@ const resolveRunText = (run, context) => {
30676
32750
  if (run.kind === "image") {
30677
32751
  return "";
30678
32752
  }
32753
+ if (run.kind === "lineBreak") {
32754
+ return "";
32755
+ }
32756
+ if (run.kind === "break") {
32757
+ return "";
32758
+ }
32759
+ if (!("text" in run)) {
32760
+ return "";
32761
+ }
30679
32762
  if (!runToken) {
30680
32763
  return run.text ?? "";
30681
32764
  }
@@ -30842,6 +32925,9 @@ function isTabRun(run) {
30842
32925
  function isImageRun(run) {
30843
32926
  return run.kind === "image";
30844
32927
  }
32928
+ function isLineBreakRun(run) {
32929
+ return run.kind === "lineBreak";
32930
+ }
30845
32931
  async function measureBlock(block, constraints) {
30846
32932
  const normalized = normalizeConstraints(constraints);
30847
32933
  if (block.kind === "drawing") {
@@ -30870,6 +32956,7 @@ async function measureBlock(block, constraints) {
30870
32956
  async function measureParagraphBlock(block, maxWidth) {
30871
32957
  const ctx2 = getCanvasContext();
30872
32958
  const wordLayout = block.attrs?.wordLayout;
32959
+ const WIDTH_FUDGE_PX = 0.5;
30873
32960
  const lines = [];
30874
32961
  const indent = block.attrs?.indent;
30875
32962
  const spacing = block.attrs?.spacing;
@@ -30879,7 +32966,7 @@ async function measureParagraphBlock(block, maxWidth) {
30879
32966
  const hanging = indent?.hanging ?? 0;
30880
32967
  const firstLineOffset = firstLine - hanging;
30881
32968
  const contentWidth = Math.max(1, maxWidth - indentLeft - indentRight);
30882
- let availableWidth = Math.max(1, contentWidth - firstLineOffset);
32969
+ const initialAvailableWidth = Math.max(1, contentWidth - firstLineOffset);
30883
32970
  const tabStops = buildTabStopsPx(
30884
32971
  indent,
30885
32972
  block.attrs?.tabs,
@@ -30892,6 +32979,18 @@ async function measureParagraphBlock(block, maxWidth) {
30892
32979
  line.bars = barTabStops.map((stop) => ({ x: stop.pos }));
30893
32980
  }
30894
32981
  };
32982
+ const dropCapDescriptor = block.attrs?.dropCapDescriptor;
32983
+ let dropCapMeasure = null;
32984
+ if (dropCapDescriptor) {
32985
+ if (!dropCapDescriptor.run || !dropCapDescriptor.run.text || !dropCapDescriptor.lines) {
32986
+ console.warn("Invalid drop cap descriptor - missing required fields:", dropCapDescriptor);
32987
+ } else {
32988
+ const dropCapMeasured = measureDropCap(ctx2, dropCapDescriptor, spacing);
32989
+ dropCapMeasure = dropCapMeasured;
32990
+ dropCapDescriptor.measuredWidth = dropCapMeasured.width;
32991
+ dropCapDescriptor.measuredHeight = dropCapMeasured.height;
32992
+ }
32993
+ }
30895
32994
  if (block.runs.length === 0) {
30896
32995
  const metrics = calculateTypographyMetrics(12, spacing);
30897
32996
  const emptyLine = {
@@ -30911,6 +33010,13 @@ async function measureParagraphBlock(block, maxWidth) {
30911
33010
  };
30912
33011
  }
30913
33012
  let currentLine = null;
33013
+ const getEffectiveWidth = (baseWidth) => {
33014
+ if (dropCapMeasure && lines.length < dropCapMeasure.lines && dropCapMeasure.mode === "drop") {
33015
+ return Math.max(1, baseWidth - dropCapMeasure.width);
33016
+ }
33017
+ return baseWidth;
33018
+ };
33019
+ let lastFontSize = 12;
30914
33020
  let tabStopCursor = 0;
30915
33021
  let pendingTabAlignment = null;
30916
33022
  let lastAppliedTabAlign = null;
@@ -30945,8 +33051,108 @@ async function measureParagraphBlock(block, maxWidth) {
30945
33051
  lastAppliedTabAlign = { target, val };
30946
33052
  pendingTabAlignment = null;
30947
33053
  };
30948
- for (let runIndex = 0; runIndex < block.runs.length; runIndex++) {
30949
- const run = block.runs[runIndex];
33054
+ const runsToProcess = [];
33055
+ for (const run of block.runs) {
33056
+ if (run.text && typeof run.text === "string" && run.text.includes("\n")) {
33057
+ const textRun = run;
33058
+ const segments = textRun.text.split("\n");
33059
+ let cursor = textRun.pmStart ?? 0;
33060
+ segments.forEach((seg, idx) => {
33061
+ runsToProcess.push({
33062
+ ...textRun,
33063
+ text: seg,
33064
+ pmStart: cursor,
33065
+ pmEnd: cursor + seg.length
33066
+ });
33067
+ cursor += seg.length;
33068
+ if (idx !== segments.length - 1) {
33069
+ runsToProcess.push({
33070
+ kind: "break",
33071
+ breakType: "line",
33072
+ pmStart: cursor,
33073
+ pmEnd: cursor + 1,
33074
+ sdt: run.sdt
33075
+ });
33076
+ cursor += 1;
33077
+ }
33078
+ });
33079
+ } else {
33080
+ runsToProcess.push(run);
33081
+ }
33082
+ }
33083
+ for (let runIndex = 0; runIndex < runsToProcess.length; runIndex++) {
33084
+ const run = runsToProcess[runIndex];
33085
+ if (run.kind === "break") {
33086
+ if (currentLine) {
33087
+ const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing);
33088
+ const completedLine = { ...currentLine, ...metrics };
33089
+ addBarTabsToLine(completedLine);
33090
+ lines.push(completedLine);
33091
+ currentLine = null;
33092
+ } else {
33093
+ const textRunWithSize = block.runs.find(
33094
+ (r2) => r2.kind !== "tab" && r2.kind !== "lineBreak" && r2.kind !== "break" && !("src" in r2) && "fontSize" in r2
33095
+ );
33096
+ const fallbackSize = textRunWithSize?.fontSize ?? 12;
33097
+ const metrics = calculateTypographyMetrics(fallbackSize, spacing);
33098
+ const emptyLine = {
33099
+ fromRun: runIndex,
33100
+ fromChar: 0,
33101
+ toRun: runIndex,
33102
+ toChar: 0,
33103
+ width: 0,
33104
+ segments: [],
33105
+ ...metrics
33106
+ };
33107
+ addBarTabsToLine(emptyLine);
33108
+ lines.push(emptyLine);
33109
+ }
33110
+ tabStopCursor = 0;
33111
+ pendingTabAlignment = null;
33112
+ lastAppliedTabAlign = null;
33113
+ continue;
33114
+ }
33115
+ if (isLineBreakRun(run)) {
33116
+ if (currentLine) {
33117
+ const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing);
33118
+ const completedLine = {
33119
+ ...currentLine,
33120
+ ...metrics
33121
+ };
33122
+ addBarTabsToLine(completedLine);
33123
+ lines.push(completedLine);
33124
+ } else {
33125
+ const metrics = calculateTypographyMetrics(lastFontSize, spacing);
33126
+ const emptyLine = {
33127
+ fromRun: runIndex,
33128
+ fromChar: 0,
33129
+ toRun: runIndex,
33130
+ toChar: 0,
33131
+ width: 0,
33132
+ maxWidth: getEffectiveWidth(initialAvailableWidth),
33133
+ segments: [],
33134
+ ...metrics
33135
+ };
33136
+ addBarTabsToLine(emptyLine);
33137
+ lines.push(emptyLine);
33138
+ }
33139
+ const hadPreviousLine = lines.length > 0;
33140
+ const nextLineMaxWidth = hadPreviousLine ? getEffectiveWidth(contentWidth) : getEffectiveWidth(initialAvailableWidth);
33141
+ currentLine = {
33142
+ fromRun: runIndex,
33143
+ fromChar: 0,
33144
+ toRun: runIndex,
33145
+ toChar: 0,
33146
+ width: 0,
33147
+ maxFontSize: lastFontSize,
33148
+ maxWidth: nextLineMaxWidth,
33149
+ segments: []
33150
+ };
33151
+ tabStopCursor = 0;
33152
+ pendingTabAlignment = null;
33153
+ lastAppliedTabAlign = null;
33154
+ continue;
33155
+ }
30950
33156
  if (isTabRun(run)) {
30951
33157
  if (!currentLine) {
30952
33158
  currentLine = {
@@ -30957,7 +33163,7 @@ async function measureParagraphBlock(block, maxWidth) {
30957
33163
  width: 0,
30958
33164
  maxFontSize: 12,
30959
33165
  // Default font size for tabs
30960
- maxWidth: availableWidth,
33166
+ maxWidth: getEffectiveWidth(initialAvailableWidth),
30961
33167
  segments: []
30962
33168
  };
30963
33169
  }
@@ -31002,7 +33208,7 @@ async function measureParagraphBlock(block, maxWidth) {
31002
33208
  width: imageWidth,
31003
33209
  maxFontSize: imageHeight,
31004
33210
  // Use image height for line height calculation
31005
- maxWidth: availableWidth,
33211
+ maxWidth: getEffectiveWidth(initialAvailableWidth),
31006
33212
  segments: [
31007
33213
  {
31008
33214
  runIndex,
@@ -31012,7 +33218,6 @@ async function measureParagraphBlock(block, maxWidth) {
31012
33218
  }
31013
33219
  ]
31014
33220
  };
31015
- availableWidth = contentWidth;
31016
33221
  continue;
31017
33222
  }
31018
33223
  if (currentLine.width + imageWidth > currentLine.maxWidth && currentLine.width > 0) {
@@ -31032,7 +33237,7 @@ async function measureParagraphBlock(block, maxWidth) {
31032
33237
  toChar: 1,
31033
33238
  width: imageWidth,
31034
33239
  maxFontSize: imageHeight,
31035
- maxWidth: contentWidth,
33240
+ maxWidth: getEffectiveWidth(contentWidth),
31036
33241
  segments: [
31037
33242
  {
31038
33243
  runIndex,
@@ -31042,7 +33247,6 @@ async function measureParagraphBlock(block, maxWidth) {
31042
33247
  }
31043
33248
  ]
31044
33249
  };
31045
- availableWidth = contentWidth;
31046
33250
  } else {
31047
33251
  currentLine.toRun = runIndex;
31048
33252
  currentLine.toChar = 1;
@@ -31058,6 +33262,10 @@ async function measureParagraphBlock(block, maxWidth) {
31058
33262
  }
31059
33263
  continue;
31060
33264
  }
33265
+ if (!("text" in run) || !("fontSize" in run)) {
33266
+ continue;
33267
+ }
33268
+ lastFontSize = run.fontSize;
31061
33269
  const { font } = buildFontString(run);
31062
33270
  const tabSegments = run.text.split(" ");
31063
33271
  let charPosInRun = 0;
@@ -31092,23 +33300,22 @@ async function measureParagraphBlock(block, maxWidth) {
31092
33300
  toChar: wordEndNoSpace,
31093
33301
  width: wordOnlyWidth,
31094
33302
  maxFontSize: run.fontSize,
31095
- maxWidth: availableWidth,
33303
+ maxWidth: getEffectiveWidth(initialAvailableWidth),
31096
33304
  segments: [{ runIndex, fromChar: wordStartChar, toChar: wordEndNoSpace, width: wordOnlyWidth }]
31097
33305
  };
31098
33306
  const ls = run.letterSpacing ?? 0;
31099
- if (!isLastWord && currentLine.width + spaceWidth <= currentLine.maxWidth) {
33307
+ if (!isLastWord && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX) {
31100
33308
  currentLine.toChar = wordEndWithSpace;
31101
33309
  currentLine.width = roundValue(currentLine.width + spaceWidth + ls);
31102
33310
  charPosInRun = wordEndWithSpace;
31103
33311
  } else {
31104
- charPosInRun = wordEndNoSpace;
33312
+ charPosInRun = wordEndWithSpace;
31105
33313
  }
31106
- availableWidth = contentWidth;
31107
33314
  continue;
31108
33315
  }
31109
33316
  const isTocEntry = block.attrs?.isTocEntry;
31110
33317
  const boundarySpacing = currentLine.width > 0 ? run.letterSpacing ?? 0 : 0;
31111
- if (currentLine.width + boundarySpacing + wordOnlyWidth > currentLine.maxWidth && currentLine.width > 0 && !isTocEntry) {
33318
+ if (currentLine.width + boundarySpacing + wordOnlyWidth > currentLine.maxWidth - WIDTH_FUDGE_PX && currentLine.width > 0 && !isTocEntry) {
31112
33319
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing);
31113
33320
  const completedLine = {
31114
33321
  ...currentLine,
@@ -31125,19 +33332,19 @@ async function measureParagraphBlock(block, maxWidth) {
31125
33332
  toChar: wordEndNoSpace,
31126
33333
  width: wordOnlyWidth,
31127
33334
  maxFontSize: run.fontSize,
31128
- maxWidth: contentWidth,
33335
+ maxWidth: getEffectiveWidth(contentWidth),
31129
33336
  segments: [{ runIndex, fromChar: wordStartChar, toChar: wordEndNoSpace, width: wordOnlyWidth }]
31130
33337
  };
31131
- if (!isLastWord && currentLine.width + spaceWidth <= currentLine.maxWidth) {
33338
+ if (!isLastWord && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX) {
31132
33339
  currentLine.toChar = wordEndWithSpace;
31133
33340
  currentLine.width = roundValue(currentLine.width + spaceWidth + (run.letterSpacing ?? 0));
31134
33341
  charPosInRun = wordEndWithSpace;
31135
33342
  } else {
31136
- charPosInRun = wordEndNoSpace;
33343
+ charPosInRun = wordEndWithSpace;
31137
33344
  }
31138
33345
  } else {
31139
33346
  currentLine.toRun = runIndex;
31140
- if (!isLastWord && currentLine.width + boundarySpacing + wordOnlyWidth + spaceWidth > currentLine.maxWidth) {
33347
+ if (!isLastWord && currentLine.width + boundarySpacing + wordOnlyWidth + spaceWidth > currentLine.maxWidth - WIDTH_FUDGE_PX) {
31141
33348
  currentLine.toChar = wordEndNoSpace;
31142
33349
  currentLine.width = roundValue(currentLine.width + boundarySpacing + wordOnlyWidth);
31143
33350
  currentLine.maxFontSize = Math.max(currentLine.maxFontSize, run.fontSize);
@@ -31181,10 +33388,9 @@ async function measureParagraphBlock(block, maxWidth) {
31181
33388
  toChar: charPosInRun,
31182
33389
  width: 0,
31183
33390
  maxFontSize: run.fontSize,
31184
- maxWidth: availableWidth,
33391
+ maxWidth: getEffectiveWidth(initialAvailableWidth),
31185
33392
  segments: []
31186
33393
  };
31187
- availableWidth = contentWidth;
31188
33394
  }
31189
33395
  const originX = currentLine.width;
31190
33396
  const { target, nextIndex, stop } = getNextTabStopPx(currentLine.width, tabStops, tabStopCursor);
@@ -31257,12 +33463,41 @@ async function measureParagraphBlock(block, maxWidth) {
31257
33463
  kind: "paragraph",
31258
33464
  lines,
31259
33465
  totalHeight,
31260
- ...markerInfo ? { marker: markerInfo } : {}
33466
+ ...markerInfo ? { marker: markerInfo } : {},
33467
+ ...dropCapMeasure ? { dropCap: dropCapMeasure } : {}
31261
33468
  };
31262
33469
  }
31263
33470
  async function measureTableBlock(block, constraints) {
31264
33471
  const maxWidth = typeof constraints === "number" ? constraints : constraints.maxWidth;
31265
33472
  let columnWidths;
33473
+ const scaleColumnWidths = (widths, targetWidth) => {
33474
+ const totalWidth2 = widths.reduce((a, b) => a + b, 0);
33475
+ if (totalWidth2 <= targetWidth || widths.length === 0) return widths;
33476
+ const scale = targetWidth / totalWidth2;
33477
+ const scaled = widths.map((w) => Math.max(1, Math.round(w * scale)));
33478
+ const sum = scaled.reduce((a, b) => a + b, 0);
33479
+ if (sum !== targetWidth) {
33480
+ const adjust = (delta) => {
33481
+ let idx = 0;
33482
+ const direction = delta > 0 ? 1 : -1;
33483
+ delta = Math.abs(delta);
33484
+ while (delta > 0 && scaled.length > 0) {
33485
+ const i = idx % scaled.length;
33486
+ if (direction > 0) {
33487
+ scaled[i] += 1;
33488
+ delta -= 1;
33489
+ } else if (scaled[i] > 1) {
33490
+ scaled[i] -= 1;
33491
+ delta -= 1;
33492
+ }
33493
+ idx += 1;
33494
+ if (idx > scaled.length * 2 && delta > 0) break;
33495
+ }
33496
+ };
33497
+ adjust(targetWidth - sum);
33498
+ }
33499
+ return scaled;
33500
+ };
31266
33501
  const maxCellCount = Math.max(1, Math.max(...block.rows.map((r2) => r2.cells.length)));
31267
33502
  if (block.columnWidths && block.columnWidths.length > 0) {
31268
33503
  columnWidths = [...block.columnWidths];
@@ -31271,8 +33506,7 @@ async function measureTableBlock(block, constraints) {
31271
33506
  if (hasExplicitWidth || hasFixedLayout) {
31272
33507
  const totalWidth2 = columnWidths.reduce((a, b) => a + b, 0);
31273
33508
  if (totalWidth2 > maxWidth) {
31274
- const scale = maxWidth / totalWidth2;
31275
- columnWidths = columnWidths.map((w) => Math.max(1, Math.floor(w * scale)));
33509
+ columnWidths = scaleColumnWidths(columnWidths, maxWidth);
31276
33510
  }
31277
33511
  } else {
31278
33512
  if (columnWidths.length < maxCellCount) {
@@ -31286,8 +33520,7 @@ async function measureTableBlock(block, constraints) {
31286
33520
  }
31287
33521
  const totalWidth2 = columnWidths.reduce((a, b) => a + b, 0);
31288
33522
  if (totalWidth2 > maxWidth) {
31289
- const scale = maxWidth / totalWidth2;
31290
- columnWidths = columnWidths.map((w) => Math.max(1, Math.floor(w * scale)));
33523
+ columnWidths = scaleColumnWidths(columnWidths, maxWidth);
31291
33524
  }
31292
33525
  }
31293
33526
  } else {
@@ -31304,6 +33537,8 @@ async function measureTableBlock(block, constraints) {
31304
33537
  };
31305
33538
  const rowspanTracker = new Array(gridColumnCount).fill(0);
31306
33539
  const rows = [];
33540
+ const rowBaseHeights = new Array(block.rows.length).fill(0);
33541
+ const spanConstraints = [];
31307
33542
  for (let rowIndex = 0; rowIndex < block.rows.length; rowIndex++) {
31308
33543
  const row = block.rows[rowIndex];
31309
33544
  const cellMeasures = [];
@@ -31350,6 +33585,11 @@ async function measureTableBlock(block, constraints) {
31350
33585
  colSpan: colspan,
31351
33586
  rowSpan: rowspan
31352
33587
  });
33588
+ if (rowspan === 1) {
33589
+ rowBaseHeights[rowIndex] = Math.max(rowBaseHeights[rowIndex], totalCellHeight);
33590
+ } else {
33591
+ spanConstraints.push({ startRow: rowIndex, rowSpan: rowspan, requiredHeight: totalCellHeight });
33592
+ }
31353
33593
  gridColIndex += colspan;
31354
33594
  }
31355
33595
  for (let col = gridColIndex; col < gridColumnCount; col++) {
@@ -31357,10 +33597,39 @@ async function measureTableBlock(block, constraints) {
31357
33597
  rowspanTracker[col]--;
31358
33598
  }
31359
33599
  }
31360
- const rowHeight = Math.max(0, ...cellMeasures.map((c) => c.height));
31361
- rows.push({ cells: cellMeasures, height: rowHeight });
33600
+ rows.push({ cells: cellMeasures, height: 0 });
33601
+ }
33602
+ const rowHeights = [...rowBaseHeights];
33603
+ for (const constraint of spanConstraints) {
33604
+ const { startRow, rowSpan, requiredHeight } = constraint;
33605
+ if (rowSpan <= 0) continue;
33606
+ let currentHeight = 0;
33607
+ for (let i = 0; i < rowSpan && startRow + i < rowHeights.length; i++) {
33608
+ currentHeight += rowHeights[startRow + i];
33609
+ }
33610
+ if (currentHeight < requiredHeight) {
33611
+ const spanLength = Math.min(rowSpan, rowHeights.length - startRow);
33612
+ const increment = spanLength > 0 ? (requiredHeight - currentHeight) / spanLength : 0;
33613
+ for (let i = 0; i < spanLength; i++) {
33614
+ rowHeights[startRow + i] += increment;
33615
+ }
33616
+ }
31362
33617
  }
31363
- const totalHeight = rows.reduce((sum, r2) => sum + r2.height, 0);
33618
+ block.rows.forEach((row, index2) => {
33619
+ const spec = row.attrs?.rowHeight;
33620
+ if (spec?.value != null && Number.isFinite(spec.value)) {
33621
+ const rule = spec.rule ?? "atLeast";
33622
+ if (rule === "exact") {
33623
+ rowHeights[index2] = spec.value;
33624
+ } else {
33625
+ rowHeights[index2] = Math.max(rowHeights[index2], spec.value);
33626
+ }
33627
+ }
33628
+ });
33629
+ for (let i = 0; i < rows.length; i++) {
33630
+ rows[i].height = Math.max(0, rowHeights[i]);
33631
+ }
33632
+ const totalHeight = rowHeights.reduce((sum, h) => sum + h, 0);
31364
33633
  const totalWidth = columnWidths.reduce((a, b) => a + b, 0);
31365
33634
  return {
31366
33635
  kind: "table",
@@ -31537,7 +33806,7 @@ const getPrimaryRun = (paragraph) => {
31537
33806
  };
31538
33807
  };
31539
33808
  const measureRunWidth = (text, font, ctx2, run) => {
31540
- const letterSpacing = run.kind === "text" ? run.letterSpacing || 0 : 0;
33809
+ const letterSpacing = run.kind === "text" || run.kind === void 0 ? run.letterSpacing || 0 : 0;
31541
33810
  const width = getMeasuredTextWidth(text, font, letterSpacing, ctx2);
31542
33811
  return roundValue(width);
31543
33812
  };
@@ -31566,13 +33835,37 @@ const resolveLineHeight = (spacing, baseLineHeight) => {
31566
33835
  if (spacing.lineRule === "atLeast") {
31567
33836
  return Math.max(baseLineHeight, raw);
31568
33837
  }
31569
- return raw;
33838
+ return Math.max(baseLineHeight, raw);
31570
33839
  };
31571
33840
  const sanitizePositive = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
31572
33841
  const sanitizeDecimalSeparator = (value) => {
31573
33842
  if (value === ",") return ",";
31574
33843
  return DEFAULT_DECIMAL_SEPARATOR;
31575
33844
  };
33845
+ const DROP_CAP_PADDING_PX = 4;
33846
+ const measureDropCap = (ctx2, descriptor, spacing) => {
33847
+ const { run, lines, mode } = descriptor;
33848
+ const { font } = buildFontString({
33849
+ fontFamily: run.fontFamily,
33850
+ fontSize: run.fontSize,
33851
+ bold: run.bold,
33852
+ italic: run.italic
33853
+ });
33854
+ ctx2.font = font;
33855
+ const metrics = ctx2.measureText(run.text);
33856
+ const advanceWidth = metrics.width;
33857
+ const paintedWidth = (metrics.actualBoundingBoxLeft || 0) + (metrics.actualBoundingBoxRight || 0);
33858
+ const textWidth = Math.max(advanceWidth, paintedWidth);
33859
+ const width = roundValue(textWidth + DROP_CAP_PADDING_PX);
33860
+ const baseLineHeight = resolveLineHeight(spacing, run.fontSize * 1.2);
33861
+ const height = roundValue(baseLineHeight * lines);
33862
+ return {
33863
+ width,
33864
+ height,
33865
+ lines,
33866
+ mode
33867
+ };
33868
+ };
31576
33869
  const resolveIndentLeft = (item) => {
31577
33870
  const indentLeft = sanitizePositive(item.paragraph.attrs?.indent?.left);
31578
33871
  if (indentLeft > 0) {
@@ -32711,10 +35004,7 @@ const _PresentationEditor = class _PresentationEditor extends EventEmitter {
32711
35004
  const tr = __privateGet(this, _editor3).state.tr.setSelection(TextSelection.create(__privateGet(this, _editor3).state.doc, hit.pos));
32712
35005
  try {
32713
35006
  __privateGet(this, _editor3).view?.dispatch(tr);
32714
- } catch (error) {
32715
- if (process$1.env.NODE_ENV === "development") {
32716
- console.warn("[PresentationEditor] Failed to dispatch selection at position:", hit.pos, error);
32717
- }
35007
+ } catch {
32718
35008
  }
32719
35009
  }
32720
35010
  __privateMethod(this, _PresentationEditor_instances, scheduleSelectionUpdate_fn).call(this);
@@ -35247,10 +37537,10 @@ computeHeaderFooterCaretRect_fn = function(pos) {
35247
37537
  if (!lineInfo) return null;
35248
37538
  const { line, index: index2 } = lineInfo;
35249
37539
  const range = computeLinePmRange$1(block, line);
35250
- if (range.pmStart == null) return null;
35251
- const charsInLine = Math.max(0, line.toChar - line.fromChar);
35252
- const offsetChars = Math.max(0, Math.min(charsInLine, pos - range.pmStart));
35253
- const localX = hit.fragment.x + measureCharacterX(block, line, offsetChars);
37540
+ if (range.pmStart == null || range.pmEnd == null) return null;
37541
+ const pmCharsInLine = Math.max(1, range.pmEnd - range.pmStart);
37542
+ const pmOffset = Math.max(0, Math.min(pmCharsInLine, pos - range.pmStart));
37543
+ const localX = hit.fragment.x + measureCharacterX(block, line, pmOffset);
35254
37544
  const lineOffset = __privateMethod(this, _PresentationEditor_instances, lineHeightBeforeIndex_fn).call(this, measure.lines, hit.fragment.fromLine, index2);
35255
37545
  const headerPageHeight = context.layout.pageSize?.h ?? context.region.height ?? 1;
35256
37546
  const headerLocalY = hit.pageIndex * headerPageHeight + (hit.fragment.y + lineOffset);
@@ -35390,23 +37680,34 @@ normalizeClientPoint_fn = function(clientX, clientY) {
35390
37680
  computeCaretLayoutRect_fn = function(pos) {
35391
37681
  const layout = __privateGet(this, _layoutState).layout;
35392
37682
  if (!layout) return null;
37683
+ const domResult = __privateMethod(this, _PresentationEditor_instances, computeCaretLayoutRectFromDOM_fn).call(this, pos);
37684
+ if (domResult) {
37685
+ return domResult;
37686
+ }
35393
37687
  const hit = getFragmentAtPosition(layout, __privateGet(this, _layoutState).blocks, __privateGet(this, _layoutState).measures, pos);
35394
- if (!hit) return null;
37688
+ if (!hit) {
37689
+ return null;
37690
+ }
35395
37691
  const block = hit.block;
35396
37692
  const measure = hit.measure;
37693
+ if (hit.fragment.kind === "table" && block?.kind === "table" && measure?.kind === "table") {
37694
+ return __privateMethod(this, _PresentationEditor_instances, computeTableCaretLayoutRect_fn).call(this, pos, hit.fragment, block, measure, hit.pageIndex);
37695
+ }
35397
37696
  if (!block || block.kind !== "paragraph" || measure?.kind !== "paragraph") return null;
35398
37697
  if (hit.fragment.kind !== "para") {
35399
37698
  return null;
35400
37699
  }
35401
37700
  const fragment = hit.fragment;
35402
37701
  const lineInfo = __privateMethod(this, _PresentationEditor_instances, findLineContainingPos_fn).call(this, block, measure, fragment.fromLine, fragment.toLine, pos);
35403
- if (!lineInfo) return null;
37702
+ if (!lineInfo) {
37703
+ return null;
37704
+ }
35404
37705
  const { line, index: index2 } = lineInfo;
35405
37706
  const range = computeLinePmRange$1(block, line);
35406
- if (range.pmStart == null) return null;
35407
- const charsInLine = Math.max(0, line.toChar - line.fromChar);
35408
- const offsetChars = Math.max(0, Math.min(charsInLine, pos - range.pmStart));
35409
- const localX = fragment.x + measureCharacterX(block, line, offsetChars);
37707
+ if (range.pmStart == null || range.pmEnd == null) return null;
37708
+ const pmCharsInLine = Math.max(1, range.pmEnd - range.pmStart);
37709
+ const pmOffset = Math.max(0, Math.min(pmCharsInLine, pos - range.pmStart));
37710
+ const localX = fragment.x + measureCharacterX(block, line, pmOffset);
35410
37711
  const lineOffset = __privateMethod(this, _PresentationEditor_instances, lineHeightBeforeIndex_fn).call(this, measure.lines, fragment.fromLine, index2);
35411
37712
  const localY = fragment.y + lineOffset;
35412
37713
  return {
@@ -35416,17 +37717,179 @@ computeCaretLayoutRect_fn = function(pos) {
35416
37717
  height: line.lineHeight
35417
37718
  };
35418
37719
  };
37720
+ /**
37721
+ * Computes caret position using DOM-based positioning.
37722
+ * This matches how click-to-position mapping works and correctly handles
37723
+ * segment-based rendering with tab stops.
37724
+ *
37725
+ * Returns page-local coordinates (x, y relative to the page element).
37726
+ */
37727
+ computeCaretLayoutRectFromDOM_fn = function(pos) {
37728
+ const zoom = __privateGet(this, _layoutOptions).zoom ?? 1;
37729
+ let targetPageEl = null;
37730
+ if (__privateGet(this, _layoutState).layout && __privateGet(this, _layoutState).blocks && __privateGet(this, _layoutState).measures) {
37731
+ const fragmentHit = getFragmentAtPosition(
37732
+ __privateGet(this, _layoutState).layout,
37733
+ __privateGet(this, _layoutState).blocks,
37734
+ __privateGet(this, _layoutState).measures,
37735
+ pos
37736
+ );
37737
+ if (fragmentHit) {
37738
+ const pageEl = __privateGet(this, _viewportHost).querySelector(
37739
+ `.superdoc-page[data-page-index="${fragmentHit.pageIndex}"]`
37740
+ );
37741
+ if (pageEl) {
37742
+ targetPageEl = pageEl;
37743
+ }
37744
+ }
37745
+ }
37746
+ const spanEls = Array.from(
37747
+ targetPageEl ? targetPageEl.querySelectorAll("span[data-pm-start][data-pm-end]") : __privateGet(this, _viewportHost).querySelectorAll("span[data-pm-start][data-pm-end]")
37748
+ );
37749
+ for (const spanEl of spanEls) {
37750
+ const pmStart = Number(spanEl.dataset.pmStart ?? "NaN");
37751
+ const pmEnd = Number(spanEl.dataset.pmEnd ?? "NaN");
37752
+ if (!Number.isFinite(pmStart) || !Number.isFinite(pmEnd)) continue;
37753
+ if (pos < pmStart || pos > pmEnd) continue;
37754
+ const pageEl = spanEl.closest(".superdoc-page");
37755
+ if (!pageEl) continue;
37756
+ const pageIndex = Number(pageEl.dataset.pageIndex ?? "0");
37757
+ const pageRect = pageEl.getBoundingClientRect();
37758
+ const textNode = spanEl.firstChild;
37759
+ if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
37760
+ const spanRect2 = spanEl.getBoundingClientRect();
37761
+ return {
37762
+ pageIndex,
37763
+ x: (spanRect2.left - pageRect.left) / zoom,
37764
+ y: (spanRect2.top - pageRect.top) / zoom,
37765
+ height: spanRect2.height / zoom
37766
+ };
37767
+ }
37768
+ const text = textNode.textContent ?? "";
37769
+ const charOffset = Math.max(0, Math.min(text.length, pos - pmStart));
37770
+ const range = document.createRange();
37771
+ try {
37772
+ range.setStart(textNode, charOffset);
37773
+ range.setEnd(textNode, charOffset);
37774
+ } catch (error) {
37775
+ if (process$1.env.NODE_ENV === "development") {
37776
+ console.warn("[PresentationEditor] Range.setStart/setEnd failed:", {
37777
+ error: error instanceof Error ? error.message : String(error),
37778
+ charOffset,
37779
+ textLength: text.length,
37780
+ pos,
37781
+ pmStart,
37782
+ pmEnd
37783
+ });
37784
+ }
37785
+ const spanRect2 = spanEl.getBoundingClientRect();
37786
+ return {
37787
+ pageIndex,
37788
+ x: (spanRect2.left - pageRect.left) / zoom,
37789
+ y: (spanRect2.top - pageRect.top) / zoom,
37790
+ height: spanRect2.height / zoom
37791
+ };
37792
+ }
37793
+ const rangeRect = range.getBoundingClientRect();
37794
+ const spanRect = spanEl.getBoundingClientRect();
37795
+ const lineEl = spanEl.closest(".superdoc-line");
37796
+ const lineRect = lineEl ? lineEl.getBoundingClientRect() : spanRect;
37797
+ const caretHeight = spanRect.height;
37798
+ const verticalOffset = (lineRect.height - caretHeight) / 2;
37799
+ const caretY = lineRect.top + verticalOffset;
37800
+ return {
37801
+ pageIndex,
37802
+ x: (rangeRect.left - pageRect.left) / zoom,
37803
+ y: (caretY - pageRect.top) / zoom,
37804
+ height: caretHeight / zoom
37805
+ };
37806
+ }
37807
+ return null;
37808
+ };
37809
+ /**
37810
+ * Computes caret position within a table cell using DOM-based positioning.
37811
+ * This uses the actual rendered DOM elements to get accurate positions,
37812
+ * matching how click-to-position mapping works.
37813
+ */
37814
+ computeTableCaretLayoutRect_fn = function(pos, _fragment, _tableBlock, _tableMeasure, pageIndex) {
37815
+ const lineEls = Array.from(__privateGet(this, _viewportHost).querySelectorAll(".superdoc-line"));
37816
+ if (lineEls.length === 0) return null;
37817
+ for (const lineEl of lineEls) {
37818
+ const pmStart = Number(lineEl.dataset.pmStart ?? "NaN");
37819
+ const pmEnd = Number(lineEl.dataset.pmEnd ?? "NaN");
37820
+ if (!Number.isFinite(pmStart) || !Number.isFinite(pmEnd)) continue;
37821
+ if (pos < pmStart || pos > pmEnd) continue;
37822
+ const spanEls = Array.from(lineEl.querySelectorAll("span[data-pm-start]"));
37823
+ for (const spanEl of spanEls) {
37824
+ const spanStart = Number(spanEl.dataset.pmStart ?? "NaN");
37825
+ const spanEnd = Number(spanEl.dataset.pmEnd ?? "NaN");
37826
+ if (!Number.isFinite(spanStart) || !Number.isFinite(spanEnd)) continue;
37827
+ if (pos < spanStart || pos > spanEnd) continue;
37828
+ const textNode = spanEl.firstChild;
37829
+ if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
37830
+ const spanRect = spanEl.getBoundingClientRect();
37831
+ const viewportRect3 = __privateGet(this, _viewportHost).getBoundingClientRect();
37832
+ const zoom3 = __privateGet(this, _layoutOptions).zoom ?? 1;
37833
+ return {
37834
+ pageIndex,
37835
+ x: (spanRect.left - viewportRect3.left + __privateGet(this, _visibleHost).scrollLeft) / zoom3,
37836
+ y: (spanRect.top - viewportRect3.top + __privateGet(this, _visibleHost).scrollTop) / zoom3,
37837
+ height: spanRect.height / zoom3
37838
+ };
37839
+ }
37840
+ const text = textNode.textContent ?? "";
37841
+ const charOffset = Math.max(0, Math.min(text.length, pos - spanStart));
37842
+ const range = document.createRange();
37843
+ range.setStart(textNode, charOffset);
37844
+ range.setEnd(textNode, charOffset);
37845
+ const rangeRect = range.getBoundingClientRect();
37846
+ const viewportRect2 = __privateGet(this, _viewportHost).getBoundingClientRect();
37847
+ const zoom2 = __privateGet(this, _layoutOptions).zoom ?? 1;
37848
+ const lineRect2 = lineEl.getBoundingClientRect();
37849
+ return {
37850
+ pageIndex,
37851
+ x: (rangeRect.left - viewportRect2.left + __privateGet(this, _visibleHost).scrollLeft) / zoom2,
37852
+ y: (lineRect2.top - viewportRect2.top + __privateGet(this, _visibleHost).scrollTop) / zoom2,
37853
+ height: lineRect2.height / zoom2
37854
+ };
37855
+ }
37856
+ const lineRect = lineEl.getBoundingClientRect();
37857
+ const viewportRect = __privateGet(this, _viewportHost).getBoundingClientRect();
37858
+ const zoom = __privateGet(this, _layoutOptions).zoom ?? 1;
37859
+ return {
37860
+ pageIndex,
37861
+ x: (lineRect.left - viewportRect.left + __privateGet(this, _visibleHost).scrollLeft) / zoom,
37862
+ y: (lineRect.top - viewportRect.top + __privateGet(this, _visibleHost).scrollTop) / zoom,
37863
+ height: lineRect.height / zoom
37864
+ };
37865
+ }
37866
+ return null;
37867
+ };
35419
37868
  findLineContainingPos_fn = function(block, measure, fromLine, toLine, pos) {
37869
+ const log2 = (...args) => {
37870
+ console.log("[LINE-SEARCH]", ...args);
37871
+ };
37872
+ log2("Searching for pos:", pos, "in lines", fromLine, "to", toLine);
35420
37873
  if (measure.kind !== "paragraph" || block.kind !== "paragraph") return null;
35421
37874
  for (let lineIndex = fromLine; lineIndex < toLine; lineIndex += 1) {
35422
37875
  const line = measure.lines[lineIndex];
35423
37876
  if (!line) continue;
35424
37877
  const range = computeLinePmRange$1(block, line);
37878
+ log2("Line", lineIndex, ":", {
37879
+ pmStart: range.pmStart,
37880
+ pmEnd: range.pmEnd,
37881
+ fromRun: line.fromRun,
37882
+ toRun: line.toRun,
37883
+ fromChar: line.fromChar,
37884
+ toChar: line.toChar
37885
+ });
35425
37886
  if (range.pmStart == null || range.pmEnd == null) continue;
35426
37887
  if (pos >= range.pmStart && pos <= range.pmEnd) {
37888
+ log2("Found line", lineIndex, "for pos", pos);
35427
37889
  return { line, index: lineIndex };
35428
37890
  }
35429
37891
  }
37892
+ log2("No line found for pos", pos);
35430
37893
  return null;
35431
37894
  };
35432
37895
  lineHeightBeforeIndex_fn = function(lines, fromLine, targetIndex) {
@@ -48995,6 +51458,49 @@ const ShapeGroup = Node$1.create({
48995
51458
  };
48996
51459
  }
48997
51460
  });
51461
+ const sharedAttributes = () => ({
51462
+ originalName: {
51463
+ default: null
51464
+ },
51465
+ originalXml: {
51466
+ default: null
51467
+ }
51468
+ });
51469
+ const hiddenRender = (type) => ["sd-passthrough", { "data-sd-passthrough": type, style: "display: none;" }];
51470
+ const PassthroughBlock = Node$1.create({
51471
+ name: "passthroughBlock",
51472
+ group: "block",
51473
+ atom: true,
51474
+ draggable: false,
51475
+ selectable: false,
51476
+ defining: true,
51477
+ parseDOM() {
51478
+ return [{ tag: 'sd-passthrough[data-sd-passthrough="block"]' }];
51479
+ },
51480
+ renderDOM() {
51481
+ return hiddenRender("block");
51482
+ },
51483
+ addAttributes() {
51484
+ return sharedAttributes();
51485
+ }
51486
+ });
51487
+ const PassthroughInline = Node$1.create({
51488
+ name: "passthroughInline",
51489
+ group: "inline",
51490
+ inline: true,
51491
+ atom: true,
51492
+ draggable: false,
51493
+ selectable: false,
51494
+ parseDOM() {
51495
+ return [{ tag: 'sd-passthrough[data-sd-passthrough="inline"]' }];
51496
+ },
51497
+ renderDOM() {
51498
+ return hiddenRender("inline");
51499
+ },
51500
+ addAttributes() {
51501
+ return sharedAttributes();
51502
+ }
51503
+ });
48998
51504
  const TextStyle = Mark.create({
48999
51505
  name: "textStyle",
49000
51506
  addOptions() {
@@ -54905,7 +57411,9 @@ const getRichTextExtensions = () => {
54905
57411
  AiPlugin,
54906
57412
  Image,
54907
57413
  NodeResizer,
54908
- CustomSelection
57414
+ CustomSelection,
57415
+ PassthroughInline,
57416
+ PassthroughBlock
54909
57417
  ];
54910
57418
  };
54911
57419
  const getStarterExtensions = () => {
@@ -54977,7 +57485,9 @@ const getStarterExtensions = () => {
54977
57485
  CustomSelection,
54978
57486
  TextTransform,
54979
57487
  VectorShape,
54980
- ShapeGroup
57488
+ ShapeGroup,
57489
+ PassthroughInline,
57490
+ PassthroughBlock
54981
57491
  ];
54982
57492
  };
54983
57493
  export {