superdoc 1.0.0-beta.14 → 1.0.0-beta.15

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-DrsLL5TV.es.js → PdfViewer-CjlHzt9e.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-Cdc_EOZ9.cjs → PdfViewer-DltPlBWC.cjs} +1 -1
  3. package/dist/chunks/{index-9ZvcPlCg.cjs → index-BZnlco_f.cjs} +3 -3
  4. package/dist/chunks/{index-I4Ew0HDV-EPhjcu7T.cjs → index-Bds7gW4r-JPDW6c39.cjs} +1 -1
  5. package/dist/chunks/{index-I4Ew0HDV-Bht7-IGi.es.js → index-Bds7gW4r-Pk_xAuWe.es.js} +1 -1
  6. package/dist/chunks/{index-DB1DyDmZ.es.js → index-qg0AxQJC.es.js} +3 -3
  7. package/dist/chunks/{super-editor.es-DxAG4BVh.es.js → super-editor.es-CQTkj_nb.es.js} +2031 -472
  8. package/dist/chunks/{super-editor.es-DuS77THX.cjs → super-editor.es-CuAhqbzW.cjs} +2031 -472
  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-qMoZOGGn.js} +412 -252
  13. package/dist/super-editor/chunks/{docx-zipper-B-CXiNSb.js → docx-zipper-QKiyORxV.js} +1 -1
  14. package/dist/super-editor/chunks/{editor-wC_gs8Bl.js → editor-D8ZdjC2V.js} +1633 -215
  15. package/dist/super-editor/chunks/{index-I4Ew0HDV.js → index-Bds7gW4r.js} +1 -1
  16. package/dist/super-editor/chunks/{toolbar-BR1GYvwW.js → toolbar-Spi7vpev.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 +2033 -474
  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-qMoZOGGn.js";
16
+ import { D as DocxZipper } from "./docx-zipper-QKiyORxV.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.15";
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-Bds7gW4r.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.15");
14544
14565
  if (!this.options.ydoc) return;
14545
14566
  const metaMap = this.options.ydoc.getMap("meta");
14546
14567
  let docVersion = metaMap.get("version");
@@ -16357,6 +16378,23 @@ const resolveColorFromAttributes = (attrs, themeColors) => {
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) {
@@ -18555,10 +18598,17 @@ const hydrateParagraphStyleAttrs = (para, context, preResolved) => {
18555
18598
  }
18556
18599
  const resolvedExtended = resolved;
18557
18600
  const resolvedAsRecord = resolved;
18601
+ let resolvedIndent = cloneIfObject(resolvedAsRecord.indent);
18602
+ const styleIdLower = typeof styleId === "string" ? styleId.toLowerCase() : "";
18603
+ const isHeadingStyle = typeof resolvedExtended.outlineLvl === "number" || styleIdLower.startsWith("heading ") || styleIdLower.startsWith("heading");
18604
+ const onlyFirstLineIndent = resolvedIndent && resolvedIndent.firstLine != null && resolvedIndent.hanging == null && resolvedIndent.left == null && resolvedIndent.right == null;
18605
+ if (isHeadingStyle && (!resolvedIndent || Object.keys(resolvedIndent).length === 0 || onlyFirstLineIndent)) {
18606
+ resolvedIndent = { firstLine: 0, hanging: 0, left: resolvedIndent?.left, right: resolvedIndent?.right };
18607
+ }
18558
18608
  const hydrated = {
18559
18609
  resolved,
18560
18610
  spacing: cloneIfObject(resolvedAsRecord.spacing),
18561
- indent: cloneIfObject(resolvedAsRecord.indent),
18611
+ indent: resolvedIndent,
18562
18612
  borders: cloneIfObject(resolvedExtended.borders),
18563
18613
  shading: cloneIfObject(resolvedExtended.shading),
18564
18614
  alignment: resolvedExtended.justification,
@@ -18666,6 +18716,12 @@ const cloneParagraphAttrs = (attrs) => {
18666
18716
  }
18667
18717
  if (attrs.shading) clone.shading = { ...attrs.shading };
18668
18718
  if (attrs.tabs) clone.tabs = attrs.tabs.map((tab) => ({ ...tab }));
18719
+ if (attrs.dropCapDescriptor) {
18720
+ clone.dropCapDescriptor = {
18721
+ ...attrs.dropCapDescriptor,
18722
+ run: { ...attrs.dropCapDescriptor.run }
18723
+ };
18724
+ }
18669
18725
  return clone;
18670
18726
  };
18671
18727
  const buildStyleNodeFromAttrs = (attrs, spacing, indent) => {
@@ -18777,6 +18833,119 @@ const normalizeResolvedTabAlignment = (value) => {
18777
18833
  return void 0;
18778
18834
  }
18779
18835
  };
18836
+ const DEFAULT_DROP_CAP_FONT_SIZE_PX = 64;
18837
+ const DEFAULT_DROP_CAP_FONT_FAMILY = "Times New Roman";
18838
+ const extractDropCapRunFromParagraph = (para) => {
18839
+ const content = para.content;
18840
+ if (!Array.isArray(content) || content.length === 0) {
18841
+ return null;
18842
+ }
18843
+ let text = "";
18844
+ let runProperties = {};
18845
+ let textStyleMarks = {};
18846
+ const MAX_RECURSION_DEPTH = 50;
18847
+ const extractTextAndStyle = (nodes, depth = 0) => {
18848
+ if (depth > MAX_RECURSION_DEPTH) {
18849
+ console.warn(`extractTextAndStyle exceeded max recursion depth (${MAX_RECURSION_DEPTH})`);
18850
+ return false;
18851
+ }
18852
+ for (const node of nodes) {
18853
+ if (!node) continue;
18854
+ if (node.type === "text" && typeof node.text === "string" && node.text.length > 0) {
18855
+ text = node.text;
18856
+ if (Array.isArray(node.marks)) {
18857
+ for (const mark of node.marks) {
18858
+ if (mark?.type === "textStyle" && mark.attrs) {
18859
+ textStyleMarks = { ...textStyleMarks, ...mark.attrs };
18860
+ }
18861
+ }
18862
+ }
18863
+ return true;
18864
+ }
18865
+ if (node.type === "run") {
18866
+ if (node.attrs?.runProperties && typeof node.attrs.runProperties === "object") {
18867
+ runProperties = { ...runProperties, ...node.attrs.runProperties };
18868
+ }
18869
+ if (Array.isArray(node.marks)) {
18870
+ for (const mark of node.marks) {
18871
+ if (mark?.type === "textStyle" && mark.attrs) {
18872
+ textStyleMarks = { ...textStyleMarks, ...mark.attrs };
18873
+ }
18874
+ }
18875
+ }
18876
+ if (Array.isArray(node.content) && extractTextAndStyle(node.content, depth + 1)) {
18877
+ return true;
18878
+ }
18879
+ }
18880
+ if (Array.isArray(node.content) && extractTextAndStyle(node.content, depth + 1)) {
18881
+ return true;
18882
+ }
18883
+ }
18884
+ return false;
18885
+ };
18886
+ extractTextAndStyle(content);
18887
+ if (!text) {
18888
+ return null;
18889
+ }
18890
+ const mergedStyle = { ...runProperties, ...textStyleMarks };
18891
+ let fontSizePx = DEFAULT_DROP_CAP_FONT_SIZE_PX;
18892
+ const rawFontSize = mergedStyle.fontSize ?? mergedStyle["w:sz"] ?? mergedStyle.sz;
18893
+ if (rawFontSize != null) {
18894
+ if (typeof rawFontSize === "number") {
18895
+ const converted = rawFontSize > 100 ? ptToPx(rawFontSize / 2) : rawFontSize;
18896
+ fontSizePx = converted ?? DEFAULT_DROP_CAP_FONT_SIZE_PX;
18897
+ } else if (typeof rawFontSize === "string") {
18898
+ const numericPart = parseFloat(rawFontSize);
18899
+ if (Number.isFinite(numericPart)) {
18900
+ if (rawFontSize.endsWith("pt")) {
18901
+ const converted = ptToPx(numericPart);
18902
+ fontSizePx = converted ?? DEFAULT_DROP_CAP_FONT_SIZE_PX;
18903
+ } else if (rawFontSize.endsWith("px")) {
18904
+ fontSizePx = numericPart;
18905
+ } else {
18906
+ const converted = numericPart > 100 ? ptToPx(numericPart / 2) : numericPart;
18907
+ fontSizePx = converted ?? DEFAULT_DROP_CAP_FONT_SIZE_PX;
18908
+ }
18909
+ }
18910
+ }
18911
+ }
18912
+ let fontFamily = DEFAULT_DROP_CAP_FONT_FAMILY;
18913
+ const rawFontFamily = mergedStyle.fontFamily ?? mergedStyle["w:rFonts"] ?? mergedStyle.rFonts;
18914
+ if (typeof rawFontFamily === "string") {
18915
+ fontFamily = rawFontFamily;
18916
+ } else if (rawFontFamily && typeof rawFontFamily === "object") {
18917
+ const rFonts = rawFontFamily;
18918
+ const ascii = rFonts["w:ascii"] ?? rFonts.ascii;
18919
+ if (typeof ascii === "string") {
18920
+ fontFamily = ascii;
18921
+ }
18922
+ }
18923
+ const dropCapRun = {
18924
+ text,
18925
+ fontFamily,
18926
+ fontSize: fontSizePx
18927
+ };
18928
+ const bold = mergedStyle.bold ?? mergedStyle["w:b"] ?? mergedStyle.b;
18929
+ if (isTruthy(bold)) {
18930
+ dropCapRun.bold = true;
18931
+ }
18932
+ const italic = mergedStyle.italic ?? mergedStyle["w:i"] ?? mergedStyle.i;
18933
+ if (isTruthy(italic)) {
18934
+ dropCapRun.italic = true;
18935
+ }
18936
+ const color = mergedStyle.color ?? mergedStyle["w:color"] ?? mergedStyle.val;
18937
+ if (typeof color === "string" && color.length > 0 && color.toLowerCase() !== "auto") {
18938
+ dropCapRun.color = color.startsWith("#") ? color : `#${color}`;
18939
+ }
18940
+ const position = mergedStyle.position ?? mergedStyle["w:position"];
18941
+ if (position != null) {
18942
+ const posNum = pickNumber(position);
18943
+ if (posNum != null) {
18944
+ dropCapRun.position = ptToPx(posNum / 2);
18945
+ }
18946
+ }
18947
+ return dropCapRun;
18948
+ };
18780
18949
  const computeWordLayoutForParagraph = (paragraphAttrs, numberingProps, styleContext, _paragraphNode) => {
18781
18950
  if (numberingProps === null) {
18782
18951
  return null;
@@ -19042,6 +19211,24 @@ const computeParagraphAttrs = (para, styleContext, listCounterContext, converter
19042
19211
  const dropCap = framePr["w:dropCap"] ?? framePr.dropCap;
19043
19212
  if (dropCap != null && (typeof dropCap === "string" || typeof dropCap === "number" || typeof dropCap === "boolean")) {
19044
19213
  paragraphAttrs.dropCap = dropCap;
19214
+ const dropCapMode = typeof dropCap === "string" ? dropCap.toLowerCase() : "drop";
19215
+ const linesValue = pickNumber(framePr["w:lines"] ?? framePr.lines);
19216
+ const wrapValue = asString(framePr["w:wrap"] ?? framePr.wrap);
19217
+ const dropCapRunInfo = extractDropCapRunFromParagraph(para);
19218
+ if (dropCapRunInfo) {
19219
+ const descriptor = {
19220
+ mode: dropCapMode === "margin" ? "margin" : "drop",
19221
+ lines: linesValue != null && linesValue > 0 ? linesValue : 3,
19222
+ run: dropCapRunInfo
19223
+ };
19224
+ if (wrapValue) {
19225
+ const normalizedWrap = wrapValue.toLowerCase();
19226
+ if (normalizedWrap === "around" || normalizedWrap === "notbeside" || normalizedWrap === "none" || normalizedWrap === "tight") {
19227
+ descriptor.wrap = normalizedWrap === "notbeside" ? "notBeside" : normalizedWrap;
19228
+ }
19229
+ }
19230
+ paragraphAttrs.dropCapDescriptor = descriptor;
19231
+ }
19045
19232
  }
19046
19233
  const frame = {};
19047
19234
  const wrap = asString(framePr["w:wrap"] ?? framePr.wrap);
@@ -19815,13 +20002,26 @@ const dataAttrsCompatible = (a, b) => {
19815
20002
  }
19816
20003
  return true;
19817
20004
  };
20005
+ const commentsCompatible = (a, b) => {
20006
+ const aComments = a.comments ?? [];
20007
+ const bComments = b.comments ?? [];
20008
+ if (aComments.length === 0 && bComments.length === 0) return true;
20009
+ if (aComments.length !== bComments.length) return false;
20010
+ const normalize2 = (c) => `${c.commentId ?? ""}::${c.importedId ?? ""}::${c.internal ? "1" : "0"}`;
20011
+ const aKeys = aComments.map(normalize2).sort();
20012
+ const bKeys = bComments.map(normalize2).sort();
20013
+ for (let i = 0; i < aKeys.length; i++) {
20014
+ if (aKeys[i] !== bKeys[i]) return false;
20015
+ }
20016
+ return true;
20017
+ };
19818
20018
  function mergeAdjacentRuns(runs) {
19819
20019
  if (runs.length <= 1) return runs;
19820
20020
  const merged = [];
19821
20021
  let current = runs[0];
19822
20022
  for (let i = 1; i < runs.length; i++) {
19823
20023
  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);
20024
+ 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
20025
  if (canMerge) {
19826
20026
  const currText = current.text ?? "";
19827
20027
  const nextText = next.text ?? "";
@@ -20303,6 +20503,22 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
20303
20503
  id: nextId(),
20304
20504
  attrs: node.attrs || {}
20305
20505
  });
20506
+ } else {
20507
+ const lineBreakRun = { kind: "lineBreak", attrs: {} };
20508
+ const lbAttrs = {};
20509
+ if (attrs.lineBreakType) lbAttrs.lineBreakType = String(attrs.lineBreakType);
20510
+ if (attrs.clear) lbAttrs.clear = String(attrs.clear);
20511
+ if (Object.keys(lbAttrs).length > 0) {
20512
+ lineBreakRun.attrs = lbAttrs;
20513
+ } else {
20514
+ delete lineBreakRun.attrs;
20515
+ }
20516
+ const pos = positions.get(node);
20517
+ if (pos) {
20518
+ lineBreakRun.pmStart = pos.start;
20519
+ lineBreakRun.pmEnd = pos.end;
20520
+ }
20521
+ currentRuns.push(lineBreakRun);
20306
20522
  }
20307
20523
  return;
20308
20524
  }
@@ -21327,7 +21543,39 @@ function toFlowBlocks(pmDoc, options) {
21327
21543
  }
21328
21544
  instrumentation?.log?.({ totalBlocks: blocks.length, blockCounts, bookmarks: bookmarks.size });
21329
21545
  const hydratedBlocks = hydrateImageBlocks(blocks, options?.mediaFiles);
21330
- return { blocks: hydratedBlocks, bookmarks };
21546
+ const mergedBlocks = mergeDropCapParagraphs(hydratedBlocks);
21547
+ return { blocks: mergedBlocks, bookmarks };
21548
+ }
21549
+ function mergeDropCapParagraphs(blocks) {
21550
+ const result = [];
21551
+ let i = 0;
21552
+ while (i < blocks.length) {
21553
+ const block = blocks[i];
21554
+ if (block.kind === "paragraph" && block.attrs?.dropCapDescriptor && i + 1 < blocks.length) {
21555
+ const dropCapBlock = block;
21556
+ const nextBlock = blocks[i + 1];
21557
+ if (nextBlock.kind === "paragraph") {
21558
+ const textBlock = nextBlock;
21559
+ const mergedBlock = {
21560
+ kind: "paragraph",
21561
+ id: textBlock.id,
21562
+ runs: textBlock.runs,
21563
+ attrs: {
21564
+ ...textBlock.attrs,
21565
+ dropCapDescriptor: dropCapBlock.attrs?.dropCapDescriptor,
21566
+ // Clear the legacy dropCap flag on the merged block
21567
+ dropCap: void 0
21568
+ }
21569
+ };
21570
+ result.push(mergedBlock);
21571
+ i += 2;
21572
+ continue;
21573
+ }
21574
+ }
21575
+ result.push(block);
21576
+ i += 1;
21577
+ }
21578
+ return result;
21331
21579
  }
21332
21580
  function paragraphToFlowBlocks(para, nextBlockId, positions, defaultFont, defaultSize, styleContext, listCounterContext, trackedChanges, bookmarks, hyperlinkConfig = DEFAULT_HYPERLINK_CONFIG$1, themeColors, converterContext) {
21333
21581
  return paragraphToFlowBlocks$1(
@@ -21402,7 +21650,7 @@ function getMeasurementContext() {
21402
21650
  return measurementCtx;
21403
21651
  }
21404
21652
  function getRunFontString(run) {
21405
- if (run.kind === "tab" || run.kind === "image") {
21653
+ if (run.kind === "tab" || run.kind === "lineBreak" || "src" in run) {
21406
21654
  return "normal normal 16px Arial";
21407
21655
  }
21408
21656
  const style = run.italic ? "italic" : "normal";
@@ -21421,7 +21669,11 @@ function sliceRunsForLine$1(block, line) {
21421
21669
  result.push(run);
21422
21670
  continue;
21423
21671
  }
21424
- if (run.kind === "image") {
21672
+ if ("src" in run) {
21673
+ result.push(run);
21674
+ continue;
21675
+ }
21676
+ if (run.kind === "lineBreak") {
21425
21677
  result.push(run);
21426
21678
  continue;
21427
21679
  }
@@ -21448,13 +21700,17 @@ function sliceRunsForLine$1(block, line) {
21448
21700
  }
21449
21701
  function measureCharacterX(block, line, charOffset) {
21450
21702
  const ctx2 = getMeasurementContext();
21703
+ const hasExplicitPositioning = line.segments?.some((seg) => seg.x !== void 0);
21704
+ if (hasExplicitPositioning && line.segments && ctx2) {
21705
+ return measureCharacterXSegmentBased(block, line, charOffset, ctx2);
21706
+ }
21451
21707
  if (!ctx2) {
21452
21708
  const runs2 = sliceRunsForLine$1(block, line);
21453
21709
  const charsInLine = Math.max(
21454
21710
  1,
21455
21711
  runs2.reduce((sum, run) => {
21456
21712
  if (isTabRun$1(run)) return sum + TAB_CHAR_LENGTH;
21457
- if (run.kind === "image") return sum;
21713
+ if ("src" in run || run.kind === "lineBreak") return sum;
21458
21714
  return sum + (run.text ?? "").length;
21459
21715
  }, 0)
21460
21716
  );
@@ -21475,7 +21731,7 @@ function measureCharacterX(block, line, charOffset) {
21475
21731
  currentCharOffset += runLength2;
21476
21732
  continue;
21477
21733
  }
21478
- const text = run.kind === "image" ? "" : run.text ?? "";
21734
+ const text = "src" in run || run.kind === "lineBreak" ? "" : run.text ?? "";
21479
21735
  const runLength = text.length;
21480
21736
  if (currentCharOffset + runLength >= charOffset) {
21481
21737
  const offsetInRun = charOffset - currentCharOffset;
@@ -21492,6 +21748,47 @@ function measureCharacterX(block, line, charOffset) {
21492
21748
  }
21493
21749
  return currentX;
21494
21750
  }
21751
+ function measureCharacterXSegmentBased(block, line, charOffset, ctx2) {
21752
+ if (block.kind !== "paragraph" || !line.segments) return 0;
21753
+ let lineCharCount = 0;
21754
+ for (const segment of line.segments) {
21755
+ const run = block.runs[segment.runIndex];
21756
+ if (!run) continue;
21757
+ const segmentChars = segment.toChar - segment.fromChar;
21758
+ if (lineCharCount + segmentChars >= charOffset) {
21759
+ const offsetInSegment = charOffset - lineCharCount;
21760
+ let segmentBaseX = segment.x;
21761
+ if (segmentBaseX === void 0) {
21762
+ segmentBaseX = 0;
21763
+ for (const prevSeg of line.segments) {
21764
+ if (prevSeg === segment) break;
21765
+ const prevRun = block.runs[prevSeg.runIndex];
21766
+ if (!prevRun) continue;
21767
+ if (prevSeg.x !== void 0) {
21768
+ segmentBaseX = prevSeg.x + (prevSeg.width ?? 0);
21769
+ } else {
21770
+ segmentBaseX += prevSeg.width ?? 0;
21771
+ }
21772
+ }
21773
+ }
21774
+ if (isTabRun$1(run)) {
21775
+ return segmentBaseX + (offsetInSegment > 0 ? segment.width ?? 0 : 0);
21776
+ }
21777
+ if ("src" in run || run.kind === "lineBreak") {
21778
+ return segmentBaseX + (offsetInSegment >= segmentChars ? segment.width ?? 0 : 0);
21779
+ }
21780
+ const text = run.text ?? "";
21781
+ const segmentText = text.slice(segment.fromChar, segment.toChar);
21782
+ const textUpToTarget = segmentText.slice(0, offsetInSegment);
21783
+ ctx2.font = getRunFontString(run);
21784
+ const measured = ctx2.measureText(textUpToTarget);
21785
+ const spacingWidth = computeLetterSpacingWidth(run, offsetInSegment, segmentChars);
21786
+ return segmentBaseX + measured.width + spacingWidth;
21787
+ }
21788
+ lineCharCount += segmentChars;
21789
+ }
21790
+ return line.width;
21791
+ }
21495
21792
  function findCharacterAtX(block, line, x, pmStart) {
21496
21793
  const ctx2 = getMeasurementContext();
21497
21794
  if (!ctx2) {
@@ -21500,7 +21797,7 @@ function findCharacterAtX(block, line, x, pmStart) {
21500
21797
  1,
21501
21798
  runs2.reduce((sum, run) => {
21502
21799
  if (isTabRun$1(run)) return sum + TAB_CHAR_LENGTH;
21503
- if (run.kind === "image") return sum;
21800
+ if ("src" in run || run.kind === "lineBreak") return sum;
21504
21801
  return sum + (run.text ?? "").length;
21505
21802
  }, 0)
21506
21803
  );
@@ -21535,7 +21832,7 @@ function findCharacterAtX(block, line, x, pmStart) {
21535
21832
  currentCharOffset += TAB_CHAR_LENGTH;
21536
21833
  continue;
21537
21834
  }
21538
- const text = run.kind === "image" ? "" : run.text ?? "";
21835
+ const text = "src" in run || run.kind === "lineBreak" ? "" : run.text ?? "";
21539
21836
  const runLength = text.length;
21540
21837
  if (runLength === 0) continue;
21541
21838
  ctx2.font = getRunFontString(run);
@@ -21572,7 +21869,7 @@ function findCharacterAtX(block, line, x, pmStart) {
21572
21869
  };
21573
21870
  }
21574
21871
  const computeLetterSpacingWidth = (run, precedingChars, runLength) => {
21575
- if (isTabRun$1(run) || run.kind === "image" || !run.letterSpacing) {
21872
+ if (isTabRun$1(run) || "src" in run || !("letterSpacing" in run) || !run.letterSpacing) {
21576
21873
  return 0;
21577
21874
  }
21578
21875
  const maxGaps = Math.max(runLength - 1, 0);
@@ -21582,6 +21879,8 @@ const computeLetterSpacingWidth = (run, precedingChars, runLength) => {
21582
21879
  const clamped = Math.min(Math.max(precedingChars, 0), maxGaps);
21583
21880
  return clamped * run.letterSpacing;
21584
21881
  };
21882
+ const log = (...args) => {
21883
+ };
21585
21884
  const CLASS_NAMES$1 = {
21586
21885
  page: "superdoc-page",
21587
21886
  fragment: "superdoc-fragment",
@@ -21597,6 +21896,11 @@ function clickToPositionDom(domContainer, clientX, clientY) {
21597
21896
  const pageLocalY = clientY - pageRect.top;
21598
21897
  const viewX = pageRect.left + pageLocalX;
21599
21898
  const viewY = pageRect.top + pageLocalY;
21899
+ log("Page found:", {
21900
+ pageIndex: pageEl.dataset.pageIndex,
21901
+ pageRect: { left: pageRect.left, top: pageRect.top, width: pageRect.width, height: pageRect.height },
21902
+ viewCoords: { viewX, viewY }
21903
+ });
21600
21904
  let hitChain = [];
21601
21905
  const doc2 = document;
21602
21906
  if (typeof doc2.elementsFromPoint === "function") {
@@ -21608,15 +21912,62 @@ function clickToPositionDom(domContainer, clientX, clientY) {
21608
21912
  if (!Array.isArray(hitChain)) {
21609
21913
  return null;
21610
21914
  }
21915
+ const hitChainData = hitChain.map((el) => {
21916
+ const rect = el.getBoundingClientRect();
21917
+ return {
21918
+ tag: el.tagName,
21919
+ classes: el.className,
21920
+ blockId: el.dataset?.blockId,
21921
+ pmStart: el.dataset?.pmStart,
21922
+ pmEnd: el.dataset?.pmEnd,
21923
+ rect: {
21924
+ top: Math.round(rect.top),
21925
+ bottom: Math.round(rect.bottom),
21926
+ left: Math.round(rect.left),
21927
+ right: Math.round(rect.right),
21928
+ height: Math.round(rect.height)
21929
+ }
21930
+ };
21931
+ });
21932
+ log("Hit chain elements:", JSON.stringify(hitChainData, null, 2));
21933
+ const allFragments = Array.from(pageEl.querySelectorAll(`.${CLASS_NAMES$1.fragment}`));
21934
+ const fragmentData = allFragments.map((el) => {
21935
+ const rect = el.getBoundingClientRect();
21936
+ return {
21937
+ blockId: el.dataset.blockId,
21938
+ pmStart: el.dataset.pmStart,
21939
+ pmEnd: el.dataset.pmEnd,
21940
+ rect: {
21941
+ top: Math.round(rect.top),
21942
+ bottom: Math.round(rect.bottom),
21943
+ left: Math.round(rect.left),
21944
+ right: Math.round(rect.right),
21945
+ height: Math.round(rect.height)
21946
+ }
21947
+ };
21948
+ });
21949
+ log("All fragments on page:", JSON.stringify(fragmentData, null, 2));
21611
21950
  const fragmentEl = hitChain.find((el) => el.classList?.contains?.(CLASS_NAMES$1.fragment));
21612
21951
  if (!fragmentEl) {
21613
21952
  const fallbackFragment = pageEl.querySelector(`.${CLASS_NAMES$1.fragment}`);
21614
21953
  if (!fallbackFragment) {
21615
21954
  return null;
21616
21955
  }
21617
- return processFragment(fallbackFragment, viewX, viewY);
21956
+ log("Using fallback fragment:", {
21957
+ blockId: fallbackFragment.dataset.blockId,
21958
+ pmStart: fallbackFragment.dataset.pmStart,
21959
+ pmEnd: fallbackFragment.dataset.pmEnd
21960
+ });
21961
+ const result2 = processFragment(fallbackFragment, viewX, viewY);
21962
+ return result2;
21618
21963
  }
21619
- return processFragment(fragmentEl, viewX, viewY);
21964
+ log("Fragment found:", {
21965
+ blockId: fragmentEl.dataset.blockId,
21966
+ pmStart: fragmentEl.dataset.pmStart,
21967
+ pmEnd: fragmentEl.dataset.pmEnd
21968
+ });
21969
+ const result = processFragment(fragmentEl, viewX, viewY);
21970
+ return result;
21620
21971
  }
21621
21972
  function findPageElement(domContainer, clientX, clientY) {
21622
21973
  if (domContainer.classList?.contains?.(CLASS_NAMES$1.page)) {
@@ -21648,7 +21999,20 @@ function findPageElement(domContainer, clientX, clientY) {
21648
21999
  return null;
21649
22000
  }
21650
22001
  function processFragment(fragmentEl, viewX, viewY) {
22002
+ log("processFragment:", { viewX, viewY, blockId: fragmentEl.dataset.blockId });
21651
22003
  const lineEls = Array.from(fragmentEl.querySelectorAll(`.${CLASS_NAMES$1.line}`));
22004
+ log(
22005
+ "Lines in fragment:",
22006
+ lineEls.map((el, i) => {
22007
+ const rect = el.getBoundingClientRect();
22008
+ return {
22009
+ index: i,
22010
+ pmStart: el.dataset.pmStart,
22011
+ pmEnd: el.dataset.pmEnd,
22012
+ rect: { top: rect.top, bottom: rect.bottom, left: rect.left, right: rect.right }
22013
+ };
22014
+ })
22015
+ );
21652
22016
  if (lineEls.length === 0) {
21653
22017
  return null;
21654
22018
  }
@@ -21658,10 +22022,30 @@ function processFragment(fragmentEl, viewX, viewY) {
21658
22022
  }
21659
22023
  const lineStart = Number(lineEl.dataset.pmStart ?? "NaN");
21660
22024
  const lineEnd = Number(lineEl.dataset.pmEnd ?? "NaN");
22025
+ const lineRect = lineEl.getBoundingClientRect();
22026
+ log("Selected line:", {
22027
+ pmStart: lineStart,
22028
+ pmEnd: lineEnd,
22029
+ rect: { top: lineRect.top, bottom: lineRect.bottom, left: lineRect.left, right: lineRect.right }
22030
+ });
21661
22031
  if (!Number.isFinite(lineStart) || !Number.isFinite(lineEnd)) {
21662
22032
  return null;
21663
22033
  }
21664
22034
  const spanEls = Array.from(lineEl.querySelectorAll("span"));
22035
+ log(
22036
+ "Spans in line:",
22037
+ spanEls.map((el, i) => {
22038
+ const rect = el.getBoundingClientRect();
22039
+ return {
22040
+ index: i,
22041
+ pmStart: el.dataset.pmStart,
22042
+ pmEnd: el.dataset.pmEnd,
22043
+ text: el.textContent?.substring(0, 20) + (el.textContent && el.textContent.length > 20 ? "..." : ""),
22044
+ visibility: el.style.visibility,
22045
+ rect: { left: rect.left, right: rect.right, width: rect.width }
22046
+ };
22047
+ })
22048
+ );
21665
22049
  if (spanEls.length === 0) {
21666
22050
  return lineStart;
21667
22051
  }
@@ -21679,6 +22063,14 @@ function processFragment(fragmentEl, viewX, viewY) {
21679
22063
  }
21680
22064
  const spanStart = Number(targetSpan.dataset.pmStart ?? "NaN");
21681
22065
  const spanEnd = Number(targetSpan.dataset.pmEnd ?? "NaN");
22066
+ const targetRect = targetSpan.getBoundingClientRect();
22067
+ log("Target span:", {
22068
+ pmStart: spanStart,
22069
+ pmEnd: spanEnd,
22070
+ text: targetSpan.textContent?.substring(0, 30),
22071
+ visibility: targetSpan.style.visibility,
22072
+ rect: { left: targetRect.left, right: targetRect.right, width: targetRect.width }
22073
+ });
21682
22074
  if (!Number.isFinite(spanStart) || !Number.isFinite(spanEnd)) {
21683
22075
  return null;
21684
22076
  }
@@ -21698,28 +22090,53 @@ function findLineAtY(lineEls, viewY) {
21698
22090
  if (lineEls.length === 0) {
21699
22091
  return null;
21700
22092
  }
21701
- for (const lineEl of lineEls) {
22093
+ for (let i = 0; i < lineEls.length; i++) {
22094
+ const lineEl = lineEls[i];
21702
22095
  const rect = lineEl.getBoundingClientRect();
21703
22096
  if (viewY >= rect.top && viewY <= rect.bottom) {
22097
+ log("findLineAtY: Found line at index", i, {
22098
+ pmStart: lineEl.dataset.pmStart,
22099
+ pmEnd: lineEl.dataset.pmEnd,
22100
+ rect: { top: rect.top, bottom: rect.bottom },
22101
+ viewY
22102
+ });
21704
22103
  return lineEl;
21705
22104
  }
21706
22105
  }
21707
- return lineEls[lineEls.length - 1];
22106
+ const lastLine = lineEls[lineEls.length - 1];
22107
+ log("findLineAtY: Y beyond all lines, using last line:", {
22108
+ pmStart: lastLine.dataset.pmStart,
22109
+ pmEnd: lastLine.dataset.pmEnd,
22110
+ viewY
22111
+ });
22112
+ return lastLine;
21708
22113
  }
21709
22114
  function findSpanAtX(spanEls, viewX) {
21710
22115
  if (spanEls.length === 0) {
21711
22116
  return null;
21712
22117
  }
21713
22118
  let targetSpan = spanEls[0];
21714
- for (const span of spanEls) {
22119
+ for (let i = 0; i < spanEls.length; i++) {
22120
+ const span = spanEls[i];
21715
22121
  const rect = span.getBoundingClientRect();
21716
22122
  if (viewX >= rect.left && viewX <= rect.right) {
22123
+ log("findSpanAtX: Found containing span at index", i, {
22124
+ pmStart: span.dataset.pmStart,
22125
+ pmEnd: span.dataset.pmEnd,
22126
+ rect: { left: rect.left, right: rect.right },
22127
+ viewX
22128
+ });
21717
22129
  return span;
21718
22130
  }
21719
22131
  if (viewX > rect.right) {
21720
22132
  targetSpan = span;
21721
22133
  }
21722
22134
  }
22135
+ log("findSpanAtX: No containing span, using nearest:", {
22136
+ pmStart: targetSpan.dataset.pmStart,
22137
+ pmEnd: targetSpan.dataset.pmEnd,
22138
+ viewX
22139
+ });
21723
22140
  return targetSpan;
21724
22141
  }
21725
22142
  function findCharIndexAtX(textNode, span, targetX) {
@@ -21849,7 +22266,7 @@ function createFloatingObjectManager(columns, margins, pageWidth) {
21849
22266
  zones.push(zone);
21850
22267
  },
21851
22268
  getExclusionsForLine(lineY, lineHeight, columnIndex, pageNumber) {
21852
- return zones.filter((zone) => {
22269
+ const result = zones.filter((zone) => {
21853
22270
  if (zone.pageNumber !== pageNumber || zone.columnIndex !== columnIndex) {
21854
22271
  return false;
21855
22272
  }
@@ -21857,8 +22274,10 @@ function createFloatingObjectManager(columns, margins, pageWidth) {
21857
22274
  const lineBottom = lineY + lineHeight;
21858
22275
  const zoneTop = zone.bounds.y - zone.distances.top;
21859
22276
  const zoneBottom = zone.bounds.y + zone.bounds.height + zone.distances.bottom;
21860
- return lineBottom > zoneTop && lineTop < zoneBottom;
22277
+ const overlaps = lineBottom > zoneTop && lineTop < zoneBottom;
22278
+ return overlaps;
21861
22279
  });
22280
+ return result;
21862
22281
  },
21863
22282
  computeAvailableWidth(lineY, lineHeight, baseWidth, columnIndex, pageNumber) {
21864
22283
  const exclusions = this.getExclusionsForLine(lineY, lineHeight, columnIndex, pageNumber);
@@ -21871,6 +22290,8 @@ function createFloatingObjectManager(columns, margins, pageWidth) {
21871
22290
  }
21872
22291
  const leftFloats = [];
21873
22292
  const rightFloats = [];
22293
+ const columnOrigin = marginLeft + columnIndex * (columns.width + columns.gap);
22294
+ const columnCenter = columnOrigin + baseWidth / 2;
21874
22295
  for (const zone of wrappingZones) {
21875
22296
  if (zone.wrapMode === "left") {
21876
22297
  leftFloats.push(zone);
@@ -21878,7 +22299,7 @@ function createFloatingObjectManager(columns, margins, pageWidth) {
21878
22299
  rightFloats.push(zone);
21879
22300
  } else if (zone.wrapMode === "both" || zone.wrapMode === "largest") {
21880
22301
  const zoneCenter = zone.bounds.x + zone.bounds.width / 2;
21881
- if (zoneCenter < baseWidth / 2) {
22302
+ if (zoneCenter < columnCenter) {
21882
22303
  leftFloats.push(zone);
21883
22304
  } else {
21884
22305
  rightFloats.push(zone);
@@ -21890,7 +22311,6 @@ function createFloatingObjectManager(columns, margins, pageWidth) {
21890
22311
  const boundary = zone.bounds.x + zone.bounds.width + zone.distances.left + zone.distances.right;
21891
22312
  leftBoundary = Math.max(leftBoundary, boundary);
21892
22313
  }
21893
- const columnOrigin = marginLeft + columnIndex * (columns.width + columns.gap);
21894
22314
  const columnRightEdge = columnOrigin + baseWidth;
21895
22315
  let rightBoundary = columnRightEdge;
21896
22316
  for (const zone of rightFloats) {
@@ -22213,9 +22633,6 @@ const extractBlockPmRange = (block) => {
22213
22633
  pmEnd: end2 ?? (start2 != null ? start2 + 1 : void 0)
22214
22634
  };
22215
22635
  };
22216
- const anchorDebugLog = (...args) => {
22217
- return;
22218
- };
22219
22636
  function layoutParagraphBlock(ctx2, anchors) {
22220
22637
  const { block, measure, columnWidth, ensurePage, advanceColumn, columnX, floatManager } = ctx2;
22221
22638
  const remeasureParagraph2 = ctx2.remeasureParagraph;
@@ -22224,23 +22641,45 @@ function layoutParagraphBlock(ctx2, anchors) {
22224
22641
  for (const entry of anchors.anchoredDrawings) {
22225
22642
  if (anchors.placedAnchoredIds.has(entry.block.id)) continue;
22226
22643
  const state = ensurePage();
22227
- const baseAnchorY = state.cursorY;
22228
- const firstLineHeight = measure.lines?.[0]?.lineHeight ?? 0;
22229
22644
  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
- });
22645
+ const alignV = entry.block.anchor?.alignV;
22646
+ const offsetV = entry.block.anchor?.offsetV ?? 0;
22647
+ const imageHeight = entry.measure.height;
22648
+ const contentTop = state.topMargin;
22649
+ const contentBottom = state.contentBottom;
22650
+ const contentHeight = Math.max(0, contentBottom - contentTop);
22651
+ let anchorY;
22652
+ if (vRelativeFrom === "margin") {
22653
+ if (alignV === "top") {
22654
+ anchorY = contentTop + offsetV;
22655
+ } else if (alignV === "bottom") {
22656
+ anchorY = contentBottom - imageHeight + offsetV;
22657
+ } else if (alignV === "center") {
22658
+ anchorY = contentTop + (contentHeight - imageHeight) / 2 + offsetV;
22659
+ } else {
22660
+ anchorY = contentTop + offsetV;
22661
+ }
22662
+ } else if (vRelativeFrom === "page") {
22663
+ if (alignV === "top") {
22664
+ anchorY = offsetV;
22665
+ } else if (alignV === "bottom") {
22666
+ const pageHeight = contentBottom + (anchors.pageMargins.bottom ?? 0);
22667
+ anchorY = pageHeight - imageHeight + offsetV;
22668
+ } else if (alignV === "center") {
22669
+ const pageHeight = contentBottom + (anchors.pageMargins.bottom ?? 0);
22670
+ anchorY = (pageHeight - imageHeight) / 2 + offsetV;
22671
+ } else {
22672
+ anchorY = offsetV;
22673
+ }
22674
+ } else if (vRelativeFrom === "paragraph") {
22675
+ const baseAnchorY = state.cursorY;
22676
+ const firstLineHeight = measure.lines?.[0]?.lineHeight ?? 0;
22677
+ const paragraphAdjustment = firstLineHeight / 2;
22678
+ anchorY = baseAnchorY + paragraphAdjustment + offsetV;
22679
+ } else {
22680
+ const baseAnchorY = state.cursorY;
22681
+ anchorY = baseAnchorY + offsetV;
22682
+ }
22244
22683
  floatManager.registerDrawing(entry.block, entry.measure, anchorY, state.columnIndex, state.page.number);
22245
22684
  const anchorX = entry.block.anchor ? computeAnchorX(
22246
22685
  entry.block.anchor,
@@ -22280,7 +22719,7 @@ function layoutParagraphBlock(ctx2, anchors) {
22280
22719
  kind: "image",
22281
22720
  blockId: entry.block.id,
22282
22721
  x: anchorX,
22283
- y: anchorY + (entry.block.anchor?.offsetV ?? 0),
22722
+ y: anchorY,
22284
22723
  width: entry.measure.width,
22285
22724
  height: entry.measure.height,
22286
22725
  isAnchored: true,
@@ -22296,7 +22735,7 @@ function layoutParagraphBlock(ctx2, anchors) {
22296
22735
  blockId: entry.block.id,
22297
22736
  drawingKind: entry.block.drawingKind,
22298
22737
  x: anchorX,
22299
- y: anchorY + (entry.block.anchor?.offsetV ?? 0),
22738
+ y: anchorY,
22300
22739
  width: entry.measure.width,
22301
22740
  height: entry.measure.height,
22302
22741
  geometry: entry.measure.geometry,
@@ -22357,7 +22796,39 @@ function layoutParagraphBlock(ctx2, anchors) {
22357
22796
  state.lastParagraphStyleId = styleId;
22358
22797
  return;
22359
22798
  }
22799
+ let narrowestWidth = columnWidth;
22800
+ let narrowestOffsetX = 0;
22360
22801
  let didRemeasureForFloats = false;
22802
+ if (typeof remeasureParagraph2 === "function") {
22803
+ const tempState = ensurePage();
22804
+ let tempY = tempState.cursorY;
22805
+ if (!appliedSpacingBefore && spacingBefore > 0) {
22806
+ const prevTrailing = tempState.trailingSpacing ?? 0;
22807
+ const neededSpacingBefore = Math.max(spacingBefore - prevTrailing, 0);
22808
+ tempY += neededSpacingBefore;
22809
+ }
22810
+ for (let i = 0; i < lines.length; i++) {
22811
+ const lineY = tempY;
22812
+ const lineHeight = lines[i]?.lineHeight || 0;
22813
+ const { width: availableWidth, offsetX: computedOffset } = floatManager.computeAvailableWidth(
22814
+ lineY,
22815
+ lineHeight,
22816
+ columnWidth,
22817
+ tempState.columnIndex,
22818
+ tempState.page.number
22819
+ );
22820
+ if (availableWidth < narrowestWidth) {
22821
+ narrowestWidth = availableWidth;
22822
+ narrowestOffsetX = computedOffset;
22823
+ }
22824
+ tempY += lineHeight;
22825
+ }
22826
+ if (narrowestWidth < columnWidth) {
22827
+ const newMeasure = remeasureParagraph2(block, narrowestWidth);
22828
+ lines = normalizeLines(newMeasure);
22829
+ didRemeasureForFloats = true;
22830
+ }
22831
+ }
22361
22832
  while (fromLine < lines.length) {
22362
22833
  let state = ensurePage();
22363
22834
  if (state.trailingSpacing == null) state.trailingSpacing = 0;
@@ -22402,23 +22873,9 @@ function layoutParagraphBlock(ctx2, anchors) {
22402
22873
  }
22403
22874
  let effectiveColumnWidth = columnWidth;
22404
22875
  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
- }
22876
+ if (didRemeasureForFloats) {
22877
+ effectiveColumnWidth = narrowestWidth;
22878
+ offsetX = narrowestOffsetX;
22422
22879
  }
22423
22880
  const slice2 = sliceLines(lines, fromLine, state.contentBottom - state.cursorY);
22424
22881
  const fragmentHeight = slice2.height;
@@ -22432,15 +22889,6 @@ function layoutParagraphBlock(ctx2, anchors) {
22432
22889
  width: effectiveColumnWidth,
22433
22890
  ...computeFragmentPmRange(block, lines, fromLine, slice2.toLine)
22434
22891
  };
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
22892
  if (measure.marker && fromLine === 0) {
22445
22893
  fragment.markerWidth = measure.marker.markerWidth;
22446
22894
  }
@@ -22653,9 +23101,32 @@ function layoutTableBlock({
22653
23101
  state.page.fragments.push(fragment);
22654
23102
  state.cursorY += height;
22655
23103
  }
23104
+ function isPageRelativeAnchor(block) {
23105
+ const vRelativeFrom = block.anchor?.vRelativeFrom;
23106
+ return vRelativeFrom === "margin" || vRelativeFrom === "page";
23107
+ }
23108
+ function collectPreRegisteredAnchors(blocks, measures) {
23109
+ const result = [];
23110
+ const len = Math.min(blocks.length, measures.length);
23111
+ for (let i = 0; i < len; i += 1) {
23112
+ const block = blocks[i];
23113
+ const measure = measures[i];
23114
+ const isImage = block.kind === "image" && measure?.kind === "image";
23115
+ const isDrawing = block.kind === "drawing" && measure?.kind === "drawing";
23116
+ if (!isImage && !isDrawing) continue;
23117
+ const drawingBlock = block;
23118
+ const drawingMeasure = measure;
23119
+ if (!drawingBlock.anchor?.isAnchored) continue;
23120
+ if (isPageRelativeAnchor(drawingBlock)) {
23121
+ result.push({ block: drawingBlock, measure: drawingMeasure });
23122
+ }
23123
+ }
23124
+ return result;
23125
+ }
22656
23126
  function collectAnchoredDrawings(blocks, measures) {
22657
23127
  const map2 = /* @__PURE__ */ new Map();
22658
- for (let i = 0; i < blocks.length; i += 1) {
23128
+ const len = Math.min(blocks.length, measures.length);
23129
+ for (let i = 0; i < len; i += 1) {
22659
23130
  if (blocks[i].kind === "paragraph") ;
22660
23131
  }
22661
23132
  const nearestPrevParagraph = (fromIndex) => {
@@ -22665,12 +23136,12 @@ function collectAnchoredDrawings(blocks, measures) {
22665
23136
  return null;
22666
23137
  };
22667
23138
  const nearestNextParagraph = (fromIndex) => {
22668
- for (let i = fromIndex + 1; i < blocks.length; i += 1) {
23139
+ for (let i = fromIndex + 1; i < len; i += 1) {
22669
23140
  if (blocks[i].kind === "paragraph") return i;
22670
23141
  }
22671
23142
  return null;
22672
23143
  };
22673
- for (let i = 0; i < blocks.length; i += 1) {
23144
+ for (let i = 0; i < len; i += 1) {
22674
23145
  const block = blocks[i];
22675
23146
  const measure = measures[i];
22676
23147
  const isImage = block.kind === "image" && measure?.kind === "image";
@@ -22678,7 +23149,12 @@ function collectAnchoredDrawings(blocks, measures) {
22678
23149
  if (!isImage && !isDrawing) continue;
22679
23150
  const drawingBlock = block;
22680
23151
  const drawingMeasure = measure;
22681
- if (!drawingBlock.anchor?.isAnchored) continue;
23152
+ if (!drawingBlock.anchor?.isAnchored) {
23153
+ continue;
23154
+ }
23155
+ if (isPageRelativeAnchor(drawingBlock)) {
23156
+ continue;
23157
+ }
22682
23158
  let anchorParaIndex = nearestPrevParagraph(i);
22683
23159
  if (anchorParaIndex == null) anchorParaIndex = nearestNextParagraph(i);
22684
23160
  if (anchorParaIndex == null) continue;
@@ -23274,6 +23750,54 @@ function layoutDocument(blocks, measures, options = {}) {
23274
23750
  };
23275
23751
  const anchoredByParagraph = collectAnchoredDrawings(blocks, measures);
23276
23752
  const placedAnchoredIds = /* @__PURE__ */ new Set();
23753
+ const preRegisteredAnchors = collectPreRegisteredAnchors(blocks, measures);
23754
+ const preRegisteredPositions = /* @__PURE__ */ new Map();
23755
+ for (const entry of preRegisteredAnchors) {
23756
+ const state = paginator.ensurePage();
23757
+ const vRelativeFrom = entry.block.anchor?.vRelativeFrom ?? "paragraph";
23758
+ const alignV = entry.block.anchor?.alignV ?? "top";
23759
+ const offsetV = entry.block.anchor?.offsetV ?? 0;
23760
+ const imageHeight = entry.measure.height ?? 0;
23761
+ const contentTop = state.topMargin;
23762
+ const contentBottom = state.contentBottom;
23763
+ const contentHeight = Math.max(0, contentBottom - contentTop);
23764
+ let anchorY;
23765
+ if (vRelativeFrom === "margin") {
23766
+ if (alignV === "top") {
23767
+ anchorY = contentTop + offsetV;
23768
+ } else if (alignV === "bottom") {
23769
+ anchorY = contentBottom - imageHeight + offsetV;
23770
+ } else if (alignV === "center") {
23771
+ anchorY = contentTop + (contentHeight - imageHeight) / 2 + offsetV;
23772
+ } else {
23773
+ anchorY = contentTop + offsetV;
23774
+ }
23775
+ } else if (vRelativeFrom === "page") {
23776
+ if (alignV === "top") {
23777
+ anchorY = offsetV;
23778
+ } else if (alignV === "bottom") {
23779
+ const pageHeight = contentBottom + margins.bottom;
23780
+ anchorY = pageHeight - imageHeight + offsetV;
23781
+ } else if (alignV === "center") {
23782
+ const pageHeight = contentBottom + margins.bottom;
23783
+ anchorY = (pageHeight - imageHeight) / 2 + offsetV;
23784
+ } else {
23785
+ anchorY = offsetV;
23786
+ }
23787
+ } else {
23788
+ anchorY = contentTop + offsetV;
23789
+ }
23790
+ const anchorX = entry.block.anchor ? computeAnchorX(
23791
+ entry.block.anchor,
23792
+ state.columnIndex,
23793
+ normalizeColumns(activeColumns, contentWidth),
23794
+ entry.measure.width,
23795
+ { left: margins.left, right: margins.right },
23796
+ activePageSize.w
23797
+ ) : margins.left;
23798
+ floatManager.registerDrawing(entry.block, entry.measure, anchorY, state.columnIndex, state.page.number);
23799
+ preRegisteredPositions.set(entry.block.id, { anchorX, anchorY, pageNumber: state.page.number });
23800
+ }
23277
23801
  for (let index2 = 0; index2 < blocks.length; index2 += 1) {
23278
23802
  const block = blocks[index2];
23279
23803
  const measure = measures[index2];
@@ -23433,6 +23957,52 @@ function layoutDocument(blocks, measures, options = {}) {
23433
23957
  if (measure.kind !== "image") {
23434
23958
  throw new Error(`layoutDocument: expected image measure for block ${block.id}`);
23435
23959
  }
23960
+ const preRegPos = preRegisteredPositions.get(block.id);
23961
+ if (preRegPos && Number.isFinite(preRegPos.anchorX) && Number.isFinite(preRegPos.anchorY) && Number.isFinite(preRegPos.pageNumber)) {
23962
+ const state = paginator.ensurePage();
23963
+ const imgBlock = block;
23964
+ const imgMeasure = measure;
23965
+ const pageContentHeight = Math.max(0, state.contentBottom - state.topMargin);
23966
+ const relativeFrom = imgBlock.anchor?.hRelativeFrom ?? "column";
23967
+ const cols = getCurrentColumns();
23968
+ let maxWidth;
23969
+ if (relativeFrom === "page") {
23970
+ maxWidth = cols.count === 1 ? activePageSize.w - margins.left - margins.right : activePageSize.w;
23971
+ } else if (relativeFrom === "margin") {
23972
+ maxWidth = activePageSize.w - margins.left - margins.right;
23973
+ } else {
23974
+ maxWidth = cols.width;
23975
+ }
23976
+ const aspectRatio = imgMeasure.width > 0 && imgMeasure.height > 0 ? imgMeasure.width / imgMeasure.height : 1;
23977
+ const minWidth = 20;
23978
+ const minHeight = minWidth / aspectRatio;
23979
+ const metadata = {
23980
+ originalWidth: imgMeasure.width,
23981
+ originalHeight: imgMeasure.height,
23982
+ maxWidth,
23983
+ maxHeight: pageContentHeight,
23984
+ aspectRatio,
23985
+ minWidth,
23986
+ minHeight
23987
+ };
23988
+ const fragment = {
23989
+ kind: "image",
23990
+ blockId: imgBlock.id,
23991
+ x: preRegPos.anchorX,
23992
+ y: preRegPos.anchorY,
23993
+ width: imgMeasure.width,
23994
+ height: imgMeasure.height,
23995
+ isAnchored: true,
23996
+ zIndex: imgBlock.anchor?.behindDoc ? 0 : 1,
23997
+ metadata
23998
+ };
23999
+ const attrs = imgBlock.attrs;
24000
+ if (attrs?.pmStart != null) fragment.pmStart = attrs.pmStart;
24001
+ if (attrs?.pmEnd != null) fragment.pmEnd = attrs.pmEnd;
24002
+ state.page.fragments.push(fragment);
24003
+ placedAnchoredIds.add(imgBlock.id);
24004
+ continue;
24005
+ }
23436
24006
  layoutImageBlock({
23437
24007
  block,
23438
24008
  measure,
@@ -23628,7 +24198,7 @@ const hashRuns = (block) => {
23628
24198
  const trackedMode = block.attrs && "trackedChangesMode" in block.attrs && block.attrs.trackedChangesMode || "review";
23629
24199
  const trackedEnabled = resolveTrackedChangesEnabled(block.attrs, true);
23630
24200
  const runsHash = block.runs.map((run) => {
23631
- const text = normalizeText(run.kind === "image" ? "" : run.text ?? "");
24201
+ const text = normalizeText("src" in run || run.kind === "lineBreak" ? "" : run.text ?? "");
23632
24202
  const bold = "bold" in run ? run.bold : false;
23633
24203
  const italic = "italic" in run ? run.italic : false;
23634
24204
  const color = "color" in run ? run.color : void 0;
@@ -23897,7 +24467,13 @@ const FeatureFlags = {
23897
24467
  * Logs cache hits, misses, invalidations, and bucket selection.
23898
24468
  * Should be disabled in production (only enabled for debugging).
23899
24469
  */
23900
- DEBUG_HF_CACHE: isEnabled("SD_DEBUG_HF_CACHE", false)
24470
+ DEBUG_HF_CACHE: isEnabled("SD_DEBUG_HF_CACHE", false),
24471
+ /**
24472
+ * Enable debug logging for layout version tracking.
24473
+ * Logs stale layout reads, geometry fallbacks, PM transactions, and layout completions.
24474
+ * Should be disabled in production (only enabled for debugging).
24475
+ */
24476
+ DEBUG_LAYOUT_VERSION: isEnabled("SD_DEBUG_LAYOUT_VERSION", false)
23901
24477
  };
23902
24478
  const PageTokenLogger = {
23903
24479
  /**
@@ -24282,7 +24858,7 @@ function fontString(run) {
24282
24858
  return `${italic}${bold}${size}px ${family}`.trim();
24283
24859
  }
24284
24860
  function runText(run) {
24285
- return run.kind === "image" ? "" : run.text ?? "";
24861
+ return "src" in run || run.kind === "lineBreak" ? "" : run.text ?? "";
24286
24862
  }
24287
24863
  function measureRunSliceWidth(run, fromChar, toChar) {
24288
24864
  const context = getCtx();
@@ -24444,7 +25020,7 @@ const paragraphBlocksEqual = (a, b) => {
24444
25020
  for (let i = 0; i < a.runs.length; i += 1) {
24445
25021
  const runA = a.runs[i];
24446
25022
  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)) {
25023
+ if (("src" in runA || runA.kind === "lineBreak" ? "" : runA.text) !== ("src" in runB || runB.kind === "lineBreak" ? "" : 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
25024
  return false;
24449
25025
  }
24450
25026
  }
@@ -24564,7 +25140,7 @@ function computeHeaderFooterContentHash(blocks) {
24564
25140
  parts.push(block.id);
24565
25141
  if (block.kind === "paragraph") {
24566
25142
  for (const run of block.runs) {
24567
- if (run.kind !== "image") {
25143
+ if (!("src" in run) && run.kind !== "lineBreak") {
24568
25144
  parts.push(run.text ?? "");
24569
25145
  }
24570
25146
  if ("bold" in run && run.bold) parts.push("b");
@@ -24993,6 +25569,23 @@ async function remeasureAffectedBlocks(blocks, measures, affectedBlockIds, const
24993
25569
  }
24994
25570
  return updatedMeasures;
24995
25571
  }
25572
+ var Priority = /* @__PURE__ */ ((Priority2) => {
25573
+ Priority2[Priority2["P0"] = 0] = "P0";
25574
+ Priority2[Priority2["P1"] = 1] = "P1";
25575
+ Priority2[Priority2["P2"] = 2] = "P2";
25576
+ Priority2[Priority2["P3"] = 3] = "P3";
25577
+ return Priority2;
25578
+ })(Priority || {});
25579
+ ({
25580
+ /** P0: No debounce for synchronous cursor positioning */
25581
+ [Priority.P0]: 0,
25582
+ /** P1: One animation frame (~60fps) for viewport layout */
25583
+ [Priority.P1]: 16,
25584
+ /** P2: Typing burst threshold for adjacent pages layout */
25585
+ [Priority.P2]: 50,
25586
+ /** P3: Heavy debounce for full document layout */
25587
+ [Priority.P3]: 150
25588
+ });
24996
25589
  const isAtomicFragment = (fragment) => {
24997
25590
  return fragment.kind === "drawing" || fragment.kind === "image";
24998
25591
  };
@@ -25080,6 +25673,111 @@ const hitTestAtomicFragment = (pageHit, blocks, measures, point) => {
25080
25673
  }
25081
25674
  return null;
25082
25675
  };
25676
+ const hitTestTableFragment = (pageHit, blocks, measures, point) => {
25677
+ for (const fragment of pageHit.page.fragments) {
25678
+ if (fragment.kind !== "table") continue;
25679
+ const tableFragment = fragment;
25680
+ const withinX = point.x >= tableFragment.x && point.x <= tableFragment.x + tableFragment.width;
25681
+ const withinY = point.y >= tableFragment.y && point.y <= tableFragment.y + tableFragment.height;
25682
+ if (!withinX || !withinY) continue;
25683
+ const blockIndex = blocks.findIndex((block2) => block2.id === tableFragment.blockId);
25684
+ if (blockIndex === -1) continue;
25685
+ const block = blocks[blockIndex];
25686
+ const measure = measures[blockIndex];
25687
+ if (!block || block.kind !== "table" || !measure || measure.kind !== "table") continue;
25688
+ const tableBlock = block;
25689
+ const tableMeasure = measure;
25690
+ const localX = point.x - tableFragment.x;
25691
+ const localY = point.y - tableFragment.y;
25692
+ let rowY = 0;
25693
+ let rowIndex = -1;
25694
+ if (tableMeasure.rows.length === 0 || tableBlock.rows.length === 0) continue;
25695
+ for (let r2 = tableFragment.fromRow; r2 < tableFragment.toRow && r2 < tableMeasure.rows.length; r2++) {
25696
+ const rowMeasure2 = tableMeasure.rows[r2];
25697
+ if (localY >= rowY && localY < rowY + rowMeasure2.height) {
25698
+ rowIndex = r2;
25699
+ break;
25700
+ }
25701
+ rowY += rowMeasure2.height;
25702
+ }
25703
+ if (rowIndex === -1) {
25704
+ rowIndex = Math.min(tableFragment.toRow - 1, tableMeasure.rows.length - 1);
25705
+ if (rowIndex < tableFragment.fromRow) continue;
25706
+ }
25707
+ const rowMeasure = tableMeasure.rows[rowIndex];
25708
+ const row = tableBlock.rows[rowIndex];
25709
+ if (!rowMeasure || !row) continue;
25710
+ let colX = 0;
25711
+ let colIndex = -1;
25712
+ if (rowMeasure.cells.length === 0 || row.cells.length === 0) continue;
25713
+ for (let c = 0; c < rowMeasure.cells.length; c++) {
25714
+ const cellMeasure2 = rowMeasure.cells[c];
25715
+ if (localX >= colX && localX < colX + cellMeasure2.width) {
25716
+ colIndex = c;
25717
+ break;
25718
+ }
25719
+ colX += cellMeasure2.width;
25720
+ }
25721
+ if (colIndex === -1) {
25722
+ colIndex = rowMeasure.cells.length - 1;
25723
+ if (colIndex < 0) continue;
25724
+ }
25725
+ const cellMeasure = rowMeasure.cells[colIndex];
25726
+ const cell = row.cells[colIndex];
25727
+ if (!cellMeasure || !cell) continue;
25728
+ const cellBlocks = cell.blocks ?? (cell.paragraph ? [cell.paragraph] : []);
25729
+ const rawMeasures = cellMeasure.blocks ?? (cellMeasure.paragraph ? [cellMeasure.paragraph] : []);
25730
+ const cellBlockMeasures = (Array.isArray(rawMeasures) ? rawMeasures : []).filter(
25731
+ (m) => m != null && typeof m === "object" && "kind" in m
25732
+ );
25733
+ let blockStartY = 0;
25734
+ const getBlockHeight = (m) => {
25735
+ if (!m) return 0;
25736
+ if ("totalHeight" in m && typeof m.totalHeight === "number") {
25737
+ return m.totalHeight;
25738
+ }
25739
+ if ("height" in m && typeof m.height === "number") {
25740
+ return m.height;
25741
+ }
25742
+ return 0;
25743
+ };
25744
+ for (let i = 0; i < cellBlocks.length && i < cellBlockMeasures.length; i++) {
25745
+ const cellBlock = cellBlocks[i];
25746
+ const cellBlockMeasure = cellBlockMeasures[i];
25747
+ if (cellBlock?.kind !== "paragraph" || cellBlockMeasure?.kind !== "paragraph") {
25748
+ blockStartY += getBlockHeight(cellBlockMeasure);
25749
+ continue;
25750
+ }
25751
+ const blockHeight = getBlockHeight(cellBlockMeasure);
25752
+ const blockEndY = blockStartY + blockHeight;
25753
+ const padding = cell.attrs?.padding ?? { top: 2, left: 4 };
25754
+ const cellLocalX = localX - colX - (padding.left ?? 4);
25755
+ const cellLocalY = localY - rowY - (padding.top ?? 2);
25756
+ const paragraphBlock = cellBlock;
25757
+ const paragraphMeasure = cellBlockMeasure;
25758
+ const isWithinBlock = cellLocalY >= blockStartY && cellLocalY < blockEndY;
25759
+ const isLastParagraph = i === Math.min(cellBlocks.length, cellBlockMeasures.length) - 1;
25760
+ if (isWithinBlock || isLastParagraph) {
25761
+ const unclampedLocalY = cellLocalY - blockStartY;
25762
+ const localYWithinBlock = Math.max(0, Math.min(unclampedLocalY, Math.max(blockHeight, 0)));
25763
+ return {
25764
+ fragment: tableFragment,
25765
+ block: tableBlock,
25766
+ measure: tableMeasure,
25767
+ pageIndex: pageHit.pageIndex,
25768
+ cellRowIndex: rowIndex,
25769
+ cellColIndex: colIndex,
25770
+ cellBlock: paragraphBlock,
25771
+ cellMeasure: paragraphMeasure,
25772
+ localX: Math.max(0, cellLocalX),
25773
+ localY: Math.max(0, localYWithinBlock)
25774
+ };
25775
+ }
25776
+ blockStartY = blockEndY;
25777
+ }
25778
+ }
25779
+ return null;
25780
+ };
25083
25781
  function clickToPosition(layout, blocks, measures, containerPoint, domContainer, clientX, clientY) {
25084
25782
  logClickStage("log", "entry", {
25085
25783
  pages: layout.pages.length
@@ -25174,6 +25872,49 @@ function clickToPosition(layout, blocks, measures, containerPoint, domContainer,
25174
25872
  // lineIndex is now already absolute (within measure.lines), no need to add fragment.fromLine
25175
25873
  };
25176
25874
  }
25875
+ const tableHit = hitTestTableFragment(pageHit, blocks, measures, pageRelativePoint);
25876
+ if (tableHit) {
25877
+ const { cellBlock, cellMeasure, localX, localY, pageIndex } = tableHit;
25878
+ const lineIndex = findLineIndexAtY(cellMeasure, localY, 0, cellMeasure.lines.length);
25879
+ if (lineIndex != null) {
25880
+ const line = cellMeasure.lines[lineIndex];
25881
+ const isRTL = isRtlBlock(cellBlock);
25882
+ const pos = mapPointToPm(cellBlock, line, localX, isRTL);
25883
+ if (pos != null) {
25884
+ logClickStage("log", "success", {
25885
+ blockId: tableHit.fragment.blockId,
25886
+ column: determineColumn(layout, tableHit.fragment.x)
25887
+ });
25888
+ return {
25889
+ pos,
25890
+ blockId: tableHit.fragment.blockId,
25891
+ pageIndex,
25892
+ column: determineColumn(layout, tableHit.fragment.x),
25893
+ lineIndex
25894
+ };
25895
+ }
25896
+ }
25897
+ const firstRun = cellBlock.runs?.[0];
25898
+ if (firstRun && firstRun.pmStart != null) {
25899
+ logClickStage("log", "success", {
25900
+ blockId: tableHit.fragment.blockId,
25901
+ pos: firstRun.pmStart,
25902
+ column: determineColumn(layout, tableHit.fragment.x)
25903
+ });
25904
+ return {
25905
+ pos: firstRun.pmStart,
25906
+ blockId: tableHit.fragment.blockId,
25907
+ pageIndex,
25908
+ column: determineColumn(layout, tableHit.fragment.x),
25909
+ lineIndex: 0
25910
+ };
25911
+ }
25912
+ logClickStage("warn", "table-cell-no-position", {
25913
+ blockId: tableHit.fragment.blockId,
25914
+ cellRow: tableHit.cellRowIndex,
25915
+ cellCol: tableHit.cellColIndex
25916
+ });
25917
+ }
25177
25918
  const atomicHit = hitTestAtomicFragment(pageHit, blocks, measures, pageRelativePoint);
25178
25919
  if (atomicHit && isAtomicFragment(atomicHit.fragment)) {
25179
25920
  const { fragment, block, pageIndex } = atomicHit;
@@ -25285,6 +26026,44 @@ function getFragmentAtPosition(layout, blocks, measures, pos) {
25285
26026
  }
25286
26027
  continue;
25287
26028
  }
26029
+ if (fragment.kind === "table") {
26030
+ if (block.kind !== "table" || measure.kind !== "table") continue;
26031
+ const tableBlock = block;
26032
+ const tableFragment = fragment;
26033
+ let tableMinPos = null;
26034
+ let tableMaxPos = null;
26035
+ for (let r2 = tableFragment.fromRow; r2 < tableFragment.toRow && r2 < tableBlock.rows.length; r2++) {
26036
+ const row = tableBlock.rows[r2];
26037
+ for (const cell of row.cells) {
26038
+ const cellBlocks = cell.blocks ?? (cell.paragraph ? [cell.paragraph] : []);
26039
+ for (const cellBlock of cellBlocks) {
26040
+ if (cellBlock?.kind === "paragraph") {
26041
+ const paraBlock = cellBlock;
26042
+ for (const run of paraBlock.runs ?? []) {
26043
+ if (run.pmStart != null) {
26044
+ if (tableMinPos === null || run.pmStart < tableMinPos) tableMinPos = run.pmStart;
26045
+ if (tableMaxPos === null || run.pmStart > tableMaxPos) tableMaxPos = run.pmStart;
26046
+ }
26047
+ if (run.pmEnd != null) {
26048
+ if (tableMinPos === null || run.pmEnd < tableMinPos) tableMinPos = run.pmEnd;
26049
+ if (tableMaxPos === null || run.pmEnd > tableMaxPos) tableMaxPos = run.pmEnd;
26050
+ }
26051
+ }
26052
+ }
26053
+ }
26054
+ }
26055
+ }
26056
+ if (tableMinPos != null && tableMaxPos != null && pos >= tableMinPos && pos <= tableMaxPos) {
26057
+ return {
26058
+ fragment,
26059
+ block,
26060
+ measure,
26061
+ pageIndex,
26062
+ pageY: 0
26063
+ };
26064
+ }
26065
+ continue;
26066
+ }
25288
26067
  if (isAtomicFragment(fragment)) {
25289
26068
  const { pmStart, pmEnd } = getAtomicPmRange(fragment, block);
25290
26069
  const start2 = pmStart ?? pmEnd;
@@ -25332,7 +26111,7 @@ function computeLinePmRange$1(block, line) {
25332
26111
  for (let runIndex = line.fromRun; runIndex <= line.toRun; runIndex += 1) {
25333
26112
  const run = block.runs[runIndex];
25334
26113
  if (!run) continue;
25335
- const text = run.kind === "image" ? "" : run.text ?? "";
26114
+ const text = "src" in run || run.kind === "lineBreak" ? "" : run.text ?? "";
25336
26115
  const runLength = text.length;
25337
26116
  const runPmStart = run.pmStart ?? null;
25338
26117
  const runPmEnd = run.pmEnd ?? (runPmStart != null ? runPmStart + runLength : null);
@@ -27728,16 +28507,6 @@ const LINK_AND_TOC_STYLES = `
27728
28507
  opacity: 0.8;
27729
28508
  }
27730
28509
 
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
28510
  /* Print mode: show URLs after links */
27742
28511
  @media print {
27743
28512
  .superdoc-link::after {
@@ -27750,11 +28519,6 @@ const LINK_AND_TOC_STYLES = `
27750
28519
  .superdoc-link[href^="#"]::after {
27751
28520
  content: "";
27752
28521
  }
27753
-
27754
- /* Don't show URL for external link indicator */
27755
- .superdoc-link[target="_blank"]::after {
27756
- content: " (" attr(href) ")";
27757
- }
27758
28522
  }
27759
28523
 
27760
28524
  /* High contrast mode support */
@@ -27772,13 +28536,6 @@ const LINK_AND_TOC_STYLES = `
27772
28536
  }
27773
28537
  }
27774
28538
 
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
28539
  /* Screen reader only content (WCAG SC 1.3.1) */
27783
28540
  .sr-only {
27784
28541
  position: absolute;
@@ -28233,7 +28990,100 @@ const renderTableFragment = (deps) => {
28233
28990
  }
28234
28991
  return container;
28235
28992
  };
28993
+ const isDevelopment = () => {
28994
+ if (typeof process$1 !== "undefined" && typeof process$1.env !== "undefined") {
28995
+ return process$1.env.NODE_ENV === "development";
28996
+ }
28997
+ return false;
28998
+ };
28999
+ class ValidationStatsCollector {
29000
+ constructor() {
29001
+ this.stats = {
29002
+ totalSpans: 0,
29003
+ validSpans: 0,
29004
+ missingPmStart: 0,
29005
+ missingPmEnd: 0,
29006
+ missingBoth: 0
29007
+ };
29008
+ }
29009
+ record(hasPmStart, hasPmEnd) {
29010
+ this.stats.totalSpans++;
29011
+ if (hasPmStart && hasPmEnd) {
29012
+ this.stats.validSpans++;
29013
+ } else if (!hasPmStart && !hasPmEnd) {
29014
+ this.stats.missingBoth++;
29015
+ } else if (!hasPmStart) {
29016
+ this.stats.missingPmStart++;
29017
+ } else {
29018
+ this.stats.missingPmEnd++;
29019
+ }
29020
+ }
29021
+ getStats() {
29022
+ return { ...this.stats };
29023
+ }
29024
+ reset() {
29025
+ this.stats = {
29026
+ totalSpans: 0,
29027
+ validSpans: 0,
29028
+ missingPmStart: 0,
29029
+ missingPmEnd: 0,
29030
+ missingBoth: 0
29031
+ };
29032
+ }
29033
+ getCoveragePercent() {
29034
+ if (this.stats.totalSpans === 0) return 100;
29035
+ return this.stats.validSpans / this.stats.totalSpans * 100;
29036
+ }
29037
+ logSummary() {
29038
+ if (!isDevelopment()) return;
29039
+ const coverage = this.getCoveragePercent();
29040
+ const s2 = this.stats;
29041
+ if (coverage < 100) {
29042
+ console.warn("[PmPositionValidation] PM position coverage:", {
29043
+ coverage: `${coverage.toFixed(1)}%`,
29044
+ totalSpans: s2.totalSpans,
29045
+ validSpans: s2.validSpans,
29046
+ missingPmStart: s2.missingPmStart,
29047
+ missingPmEnd: s2.missingPmEnd,
29048
+ missingBoth: s2.missingBoth
29049
+ });
29050
+ }
29051
+ }
29052
+ }
29053
+ const globalValidationStats = new ValidationStatsCollector();
29054
+ function assertPmPositions(run, context) {
29055
+ const hasPmStart = run.pmStart != null;
29056
+ const hasPmEnd = run.pmEnd != null;
29057
+ globalValidationStats.record(hasPmStart, hasPmEnd);
29058
+ if (!isDevelopment()) return;
29059
+ if (!hasPmStart || !hasPmEnd) {
29060
+ const textPreview = run.text ? run.text.substring(0, 20) + (run.text.length > 20 ? "..." : "") : "(no text)";
29061
+ console.warn(`[PmPositionValidation] Missing PM positions in ${context}:`, {
29062
+ hasPmStart,
29063
+ hasPmEnd,
29064
+ textPreview,
29065
+ fallback: "Will use PM DOM coordinates for cursor positioning"
29066
+ });
29067
+ }
29068
+ }
29069
+ function assertFragmentPmPositions(fragment, context) {
29070
+ const hasPmStart = fragment.pmStart != null;
29071
+ const hasPmEnd = fragment.pmEnd != null;
29072
+ globalValidationStats.record(hasPmStart, hasPmEnd);
29073
+ if (!isDevelopment()) return;
29074
+ if (!hasPmStart || !hasPmEnd) {
29075
+ console.warn(`[PmPositionValidation] Missing PM positions in ${context}:`, {
29076
+ fragmentKind: fragment.kind ?? "unknown",
29077
+ hasPmStart,
29078
+ hasPmEnd,
29079
+ fallback: "Will use PM DOM coordinates for cursor positioning"
29080
+ });
29081
+ }
29082
+ }
28236
29083
  const LIST_MARKER_GAP$1 = 8;
29084
+ const COMMENT_EXTERNAL_COLOR = "#B1124B";
29085
+ const COMMENT_INTERNAL_COLOR = "#078383";
29086
+ const COMMENT_INACTIVE_ALPHA = "22";
28237
29087
  const LINK_DATASET_KEYS = {
28238
29088
  blocked: "linkBlocked",
28239
29089
  docLocation: "linkDocLocation",
@@ -28890,7 +29740,7 @@ const _DomPainter = class _DomPainter {
28890
29740
  current.element = replacement;
28891
29741
  current.signature = fragmentSignature(fragment, this.blockLookup);
28892
29742
  }
28893
- this.updateFragmentElement(current.element, fragment);
29743
+ this.updateFragmentElement(current.element, fragment, contextBase.section);
28894
29744
  current.fragment = fragment;
28895
29745
  current.key = key2;
28896
29746
  current.context = contextBase;
@@ -28958,10 +29808,10 @@ const _DomPainter = class _DomPainter {
28958
29808
  return this.renderListItemFragment(fragment, context);
28959
29809
  }
28960
29810
  if (fragment.kind === "image") {
28961
- return this.renderImageFragment(fragment);
29811
+ return this.renderImageFragment(fragment, context);
28962
29812
  }
28963
29813
  if (fragment.kind === "drawing") {
28964
- return this.renderDrawingFragment(fragment);
29814
+ return this.renderDrawingFragment(fragment, context);
28965
29815
  }
28966
29816
  if (fragment.kind === "table") {
28967
29817
  return this.renderTableFragment(fragment, context);
@@ -28994,7 +29844,7 @@ const _DomPainter = class _DomPainter {
28994
29844
  const hasMarker = !fragment.continuesFromPrev && fragment.markerWidth && wordLayout?.marker;
28995
29845
  const styles = isTocEntry ? { ...fragmentStyles, whiteSpace: "nowrap" } : hasMarker ? { ...fragmentStyles, overflow: "visible" } : fragmentStyles;
28996
29846
  applyStyles$2(fragmentEl, styles);
28997
- this.applyFragmentFrame(fragmentEl, fragment);
29847
+ this.applyFragmentFrame(fragmentEl, fragment, context.section);
28998
29848
  if (isTocEntry) {
28999
29849
  fragmentEl.classList.add("superdoc-toc-entry");
29000
29850
  }
@@ -29012,8 +29862,32 @@ const _DomPainter = class _DomPainter {
29012
29862
  }
29013
29863
  this.applySdtDataset(fragmentEl, block.attrs?.sdt);
29014
29864
  this.applyContainerSdtDataset(fragmentEl, block.attrs?.containerSdt);
29865
+ const dropCapDescriptor = block.attrs?.dropCapDescriptor;
29866
+ const dropCapMeasure = measure.dropCap;
29867
+ if (dropCapDescriptor && dropCapMeasure && !fragment.continuesFromPrev) {
29868
+ const dropCapEl = this.renderDropCap(dropCapDescriptor, dropCapMeasure);
29869
+ fragmentEl.appendChild(dropCapEl);
29870
+ }
29871
+ if (fragmentEl.style.paddingLeft) fragmentEl.style.removeProperty("padding-left");
29872
+ if (fragmentEl.style.paddingRight) fragmentEl.style.removeProperty("padding-right");
29873
+ if (fragmentEl.style.textIndent) fragmentEl.style.removeProperty("text-indent");
29874
+ const paraIndent = block.attrs?.indent;
29875
+ const paraIndentLeft = paraIndent?.left ?? 0;
29876
+ const paraIndentRight = paraIndent?.right ?? 0;
29877
+ const firstLineOffset = (paraIndent?.firstLine ?? 0) - (paraIndent?.hanging ?? 0);
29015
29878
  lines.forEach((line, index2) => {
29016
29879
  const lineEl = this.renderLine(block, line, context);
29880
+ if (paraIndentLeft) {
29881
+ lineEl.style.paddingLeft = `${paraIndentLeft}px`;
29882
+ }
29883
+ if (paraIndentRight) {
29884
+ lineEl.style.paddingRight = `${paraIndentRight}px`;
29885
+ }
29886
+ if (!fragment.continuesFromPrev && index2 === 0 && firstLineOffset) {
29887
+ lineEl.style.textIndent = `${firstLineOffset}px`;
29888
+ } else if (firstLineOffset) {
29889
+ lineEl.style.textIndent = "0px";
29890
+ }
29017
29891
  if (index2 === 0 && !fragment.continuesFromPrev && fragment.markerWidth && wordLayout?.marker) {
29018
29892
  const markerContainer = this.doc.createElement("span");
29019
29893
  markerContainer.style.display = "inline-block";
@@ -29024,6 +29898,12 @@ const _DomPainter = class _DomPainter {
29024
29898
  markerEl.style.textAlign = wordLayout.marker.justification ?? "right";
29025
29899
  markerEl.style.paddingRight = `${LIST_MARKER_GAP$1}px`;
29026
29900
  markerEl.style.pointerEvents = "none";
29901
+ const indentLeft = paraIndentLeft;
29902
+ const hanging = paraIndent?.hanging ?? 0;
29903
+ const textStartX = indentLeft - hanging;
29904
+ const markerLeftX = textStartX - fragment.markerWidth;
29905
+ markerEl.style.position = "relative";
29906
+ markerEl.style.left = `${markerLeftX}px`;
29027
29907
  markerEl.style.fontFamily = wordLayout.marker.run.fontFamily;
29028
29908
  markerEl.style.fontSize = `${wordLayout.marker.run.fontSize}px`;
29029
29909
  markerEl.style.fontWeight = wordLayout.marker.run.bold ? "bold" : "";
@@ -29082,6 +29962,53 @@ const _DomPainter = class _DomPainter {
29082
29962
  }
29083
29963
  return el;
29084
29964
  }
29965
+ /**
29966
+ * Renders a drop cap element as a floated span at the start of a paragraph.
29967
+ *
29968
+ * Drop caps are large initial letters that span multiple lines of text.
29969
+ * This method creates a floated element with the drop cap letter styled
29970
+ * according to the descriptor's run properties.
29971
+ *
29972
+ * @param descriptor - The drop cap descriptor with text and styling info
29973
+ * @param measure - The measured dimensions of the drop cap
29974
+ * @returns HTMLElement containing the rendered drop cap
29975
+ */
29976
+ renderDropCap(descriptor, measure) {
29977
+ const doc2 = this.doc;
29978
+ const { run, mode } = descriptor;
29979
+ const dropCapEl = doc2.createElement("span");
29980
+ dropCapEl.classList.add("superdoc-drop-cap");
29981
+ dropCapEl.textContent = run.text;
29982
+ dropCapEl.style.fontFamily = run.fontFamily;
29983
+ dropCapEl.style.fontSize = `${run.fontSize}px`;
29984
+ if (run.bold) {
29985
+ dropCapEl.style.fontWeight = "bold";
29986
+ }
29987
+ if (run.italic) {
29988
+ dropCapEl.style.fontStyle = "italic";
29989
+ }
29990
+ if (run.color) {
29991
+ dropCapEl.style.color = run.color;
29992
+ }
29993
+ if (mode === "drop") {
29994
+ dropCapEl.style.float = "left";
29995
+ dropCapEl.style.marginRight = "4px";
29996
+ dropCapEl.style.lineHeight = "1";
29997
+ } else if (mode === "margin") {
29998
+ dropCapEl.style.position = "absolute";
29999
+ dropCapEl.style.left = "0";
30000
+ dropCapEl.style.lineHeight = "1";
30001
+ }
30002
+ if (run.position && run.position !== 0) {
30003
+ dropCapEl.style.position = dropCapEl.style.position || "relative";
30004
+ dropCapEl.style.top = `${run.position}px`;
30005
+ }
30006
+ if (measure) {
30007
+ dropCapEl.style.width = `${measure.width}px`;
30008
+ dropCapEl.style.height = `${measure.height}px`;
30009
+ }
30010
+ return dropCapEl;
30011
+ }
29085
30012
  renderListItemFragment(fragment, context) {
29086
30013
  try {
29087
30014
  const lookup = this.blockLookup.get(fragment.blockId);
@@ -29159,7 +30086,7 @@ const _DomPainter = class _DomPainter {
29159
30086
  return this.createErrorPlaceholder(fragment.blockId, error);
29160
30087
  }
29161
30088
  }
29162
- renderImageFragment(fragment) {
30089
+ renderImageFragment(fragment, context) {
29163
30090
  try {
29164
30091
  const lookup = this.blockLookup.get(fragment.blockId);
29165
30092
  if (!lookup || lookup.block.kind !== "image" || lookup.measure.kind !== "image") {
@@ -29172,7 +30099,7 @@ const _DomPainter = class _DomPainter {
29172
30099
  const fragmentEl = this.doc.createElement("div");
29173
30100
  fragmentEl.classList.add(CLASS_NAMES.fragment, "superdoc-image-fragment");
29174
30101
  applyStyles$2(fragmentEl, fragmentStyles);
29175
- this.applyFragmentFrame(fragmentEl, fragment);
30102
+ this.applyFragmentFrame(fragmentEl, fragment, context.section);
29176
30103
  fragmentEl.style.height = `${fragment.height}px`;
29177
30104
  this.applySdtDataset(fragmentEl, block.attrs?.sdt);
29178
30105
  this.applyContainerSdtDataset(fragmentEl, block.attrs?.containerSdt);
@@ -29207,7 +30134,7 @@ const _DomPainter = class _DomPainter {
29207
30134
  return this.createErrorPlaceholder(fragment.blockId, error);
29208
30135
  }
29209
30136
  }
29210
- renderDrawingFragment(fragment) {
30137
+ renderDrawingFragment(fragment, context) {
29211
30138
  try {
29212
30139
  const lookup = this.blockLookup.get(fragment.blockId);
29213
30140
  if (!lookup || lookup.block.kind !== "drawing" || lookup.measure.kind !== "drawing") {
@@ -29221,7 +30148,7 @@ const _DomPainter = class _DomPainter {
29221
30148
  const fragmentEl = this.doc.createElement("div");
29222
30149
  fragmentEl.classList.add(CLASS_NAMES.fragment, "superdoc-drawing-fragment");
29223
30150
  applyStyles$2(fragmentEl, fragmentStyles);
29224
- this.applyFragmentFrame(fragmentEl, fragment);
30151
+ this.applyFragmentFrame(fragmentEl, fragment, context.section);
29225
30152
  fragmentEl.style.height = `${fragment.height}px`;
29226
30153
  fragmentEl.style.position = "absolute";
29227
30154
  fragmentEl.style.overflow = "hidden";
@@ -29668,13 +30595,16 @@ const _DomPainter = class _DomPainter {
29668
30595
  if (!this.doc) {
29669
30596
  throw new Error("DomPainter: document is not available");
29670
30597
  }
30598
+ const applyFragmentFrameWithSection = (el, frag) => {
30599
+ this.applyFragmentFrame(el, frag, context.section);
30600
+ };
29671
30601
  return renderTableFragment({
29672
30602
  doc: this.doc,
29673
30603
  fragment,
29674
30604
  context,
29675
30605
  blockLookup: this.blockLookup,
29676
30606
  renderLine: this.renderLine.bind(this),
29677
- applyFragmentFrame: this.applyFragmentFrame.bind(this),
30607
+ applyFragmentFrame: applyFragmentFrameWithSection,
29678
30608
  applySdtDataset: this.applySdtDataset.bind(this),
29679
30609
  applyStyles: applyStyles$2
29680
30610
  });
@@ -29684,7 +30614,7 @@ const _DomPainter = class _DomPainter {
29684
30614
  * @returns Sanitized link data or null if invalid/missing
29685
30615
  */
29686
30616
  extractLinkData(run) {
29687
- if (run.kind === "tab") {
30617
+ if (run.kind === "tab" || run.kind === "image" || run.kind === "lineBreak") {
29688
30618
  return null;
29689
30619
  }
29690
30620
  const link = run.link;
@@ -29838,10 +30768,19 @@ const _DomPainter = class _DomPainter {
29838
30768
  isImageRun(run) {
29839
30769
  return run.kind === "image";
29840
30770
  }
30771
+ /**
30772
+ * Type guard to check if a run is a line break run.
30773
+ */
30774
+ isLineBreakRun(run) {
30775
+ return run.kind === "lineBreak";
30776
+ }
29841
30777
  renderRun(run, context, trackedConfig) {
29842
30778
  if (this.isImageRun(run)) {
29843
30779
  return this.renderImageRun(run);
29844
30780
  }
30781
+ if (this.isLineBreakRun(run)) {
30782
+ return null;
30783
+ }
29845
30784
  if (!run.text || !this.doc) {
29846
30785
  return null;
29847
30786
  }
@@ -29866,8 +30805,21 @@ const _DomPainter = class _DomPainter {
29866
30805
  }
29867
30806
  }
29868
30807
  applyRunStyles(elem, run, isActiveLink);
30808
+ const commentColor = getCommentHighlight(run);
30809
+ if (commentColor && !run.highlight) {
30810
+ elem.style.backgroundColor = commentColor;
30811
+ }
30812
+ const commentAnnotations = run.comments;
30813
+ if (commentAnnotations?.length) {
30814
+ elem.dataset.commentIds = commentAnnotations.map((c) => c.commentId).join(",");
30815
+ if (commentAnnotations.some((c) => c.internal)) {
30816
+ elem.dataset.commentInternal = "true";
30817
+ }
30818
+ elem.classList.add("superdoc-comment-highlight");
30819
+ }
29869
30820
  elem.style.zIndex = "1";
29870
30821
  applyRunDataAttributes(elem, run.dataAttrs);
30822
+ assertPmPositions(run, "paragraph text run");
29871
30823
  if (run.pmStart != null) elem.dataset.pmStart = String(run.pmStart);
29872
30824
  if (run.pmEnd != null) elem.dataset.pmEnd = String(run.pmEnd);
29873
30825
  if (trackedConfig) {
@@ -29946,6 +30898,7 @@ const _DomPainter = class _DomPainter {
29946
30898
  img.style.marginRight = `${run.distRight}px`;
29947
30899
  }
29948
30900
  img.style.zIndex = "1";
30901
+ assertPmPositions(run, "inline image run");
29949
30902
  if (run.pmStart != null) {
29950
30903
  img.dataset.pmStart = String(run.pmStart);
29951
30904
  }
@@ -29976,9 +30929,9 @@ const _DomPainter = class _DomPainter {
29976
30929
  if (lineRange.pmEnd != null) {
29977
30930
  el.dataset.pmEnd = String(lineRange.pmEnd);
29978
30931
  }
29979
- const runs = sliceRunsForLine(block, line);
30932
+ const runsForLine = sliceRunsForLine(block, line);
29980
30933
  const trackedConfig = this.resolveTrackedChangesConfig(block);
29981
- if (runs.length === 0) {
30934
+ if (runsForLine.length === 0) {
29982
30935
  const span = this.doc.createElement("span");
29983
30936
  span.innerHTML = "&nbsp;";
29984
30937
  el.appendChild(span);
@@ -30026,49 +30979,109 @@ const _DomPainter = class _DomPainter {
30026
30979
  const hasExplicitPositioning = line.segments?.some((seg) => seg.x !== void 0);
30027
30980
  if (hasExplicitPositioning && line.segments) {
30028
30981
  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);
30982
+ const segmentsByRun = /* @__PURE__ */ new Map();
30983
+ line.segments.forEach((segment) => {
30984
+ const list = segmentsByRun.get(segment.runIndex);
30985
+ if (list) {
30986
+ list.push(segment);
30987
+ } else {
30988
+ segmentsByRun.set(segment.runIndex, [segment]);
30989
+ }
30990
+ });
30991
+ const findImmediateNextSegmentX = (fromRunIndex) => {
30992
+ const nextRunIdx = fromRunIndex + 1;
30993
+ if (nextRunIdx <= line.toRun) {
30994
+ const nextSegments = segmentsByRun.get(nextRunIdx);
30995
+ if (nextSegments && nextSegments.length > 0) {
30996
+ const firstSegment = nextSegments[0];
30997
+ return firstSegment.x;
30039
30998
  }
30040
- return;
30041
30999
  }
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) {
31000
+ return void 0;
31001
+ };
31002
+ for (let runIndex = line.fromRun; runIndex <= line.toRun; runIndex += 1) {
31003
+ const baseRun = block.runs[runIndex];
31004
+ if (!baseRun) continue;
31005
+ if (baseRun.kind === "tab") {
31006
+ const immediateNextX = findImmediateNextSegmentX(runIndex);
31007
+ const tabStartX = cumulativeX;
31008
+ const tabEndX = immediateNextX !== void 0 ? immediateNextX : tabStartX + (baseRun.width ?? 0);
31009
+ const actualTabWidth = tabEndX - tabStartX;
31010
+ const tabEl = this.doc.createElement("span");
31011
+ tabEl.style.position = "absolute";
31012
+ tabEl.style.left = `${tabStartX}px`;
31013
+ tabEl.style.top = "0px";
31014
+ tabEl.style.width = `${actualTabWidth}px`;
31015
+ tabEl.style.height = `${line.lineHeight}px`;
31016
+ tabEl.style.display = "inline-block";
31017
+ tabEl.style.visibility = "hidden";
31018
+ tabEl.style.pointerEvents = "none";
31019
+ tabEl.style.zIndex = "1";
30046
31020
  if (styleId) {
30047
- elem.setAttribute("styleid", styleId);
31021
+ tabEl.setAttribute("styleid", styleId);
30048
31022
  }
30049
- let xPos;
30050
- if (segment.x !== void 0) {
30051
- xPos = segment.x;
30052
- } else {
30053
- xPos = cumulativeX;
31023
+ if (baseRun.pmStart != null) tabEl.dataset.pmStart = String(baseRun.pmStart);
31024
+ if (baseRun.pmEnd != null) tabEl.dataset.pmEnd = String(baseRun.pmEnd);
31025
+ el.appendChild(tabEl);
31026
+ cumulativeX = tabEndX;
31027
+ continue;
31028
+ }
31029
+ if (this.isImageRun(baseRun)) {
31030
+ const elem = this.renderRun(baseRun, context, trackedConfig);
31031
+ if (elem) {
31032
+ if (styleId) {
31033
+ elem.setAttribute("styleid", styleId);
31034
+ }
31035
+ el.appendChild(elem);
30054
31036
  }
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);
31037
+ continue;
31038
+ }
31039
+ if (this.isLineBreakRun(baseRun)) {
31040
+ continue;
31041
+ }
31042
+ const runSegments = segmentsByRun.get(runIndex);
31043
+ if (!runSegments || runSegments.length === 0) {
31044
+ continue;
31045
+ }
31046
+ const baseText = baseRun.text ?? "";
31047
+ const runPmStart = baseRun.pmStart ?? null;
31048
+ const fallbackPmEnd = runPmStart != null && baseRun.pmEnd == null ? runPmStart + baseText.length : baseRun.pmEnd ?? null;
31049
+ runSegments.forEach((segment) => {
31050
+ const segmentText = baseText.slice(segment.fromChar, segment.toChar);
31051
+ if (!segmentText) return;
31052
+ const pmSliceStart = runPmStart != null ? runPmStart + segment.fromChar : void 0;
31053
+ const pmSliceEnd = runPmStart != null ? runPmStart + segment.toChar : fallbackPmEnd ?? void 0;
31054
+ const segmentRun = {
31055
+ ...baseRun,
31056
+ text: segmentText,
31057
+ pmStart: pmSliceStart,
31058
+ pmEnd: pmSliceEnd
31059
+ };
31060
+ const elem = this.renderRun(segmentRun, context, trackedConfig);
31061
+ if (elem) {
31062
+ if (styleId) {
31063
+ elem.setAttribute("styleid", styleId);
31064
+ }
31065
+ const xPos = segment.x !== void 0 ? segment.x : cumulativeX;
31066
+ elem.style.position = "absolute";
31067
+ elem.style.left = `${xPos}px`;
31068
+ el.appendChild(elem);
31069
+ let width = segment.width ?? 0;
31070
+ if (width <= 0 && this.doc) {
31071
+ const measureEl = elem.cloneNode(true);
31072
+ measureEl.style.position = "absolute";
31073
+ measureEl.style.visibility = "hidden";
31074
+ measureEl.style.left = "-9999px";
31075
+ this.doc.body.appendChild(measureEl);
31076
+ width = measureEl.offsetWidth;
31077
+ this.doc.body.removeChild(measureEl);
31078
+ }
30066
31079
  cumulativeX = xPos + width;
30067
31080
  }
30068
- }
30069
- });
31081
+ });
31082
+ }
30070
31083
  } else {
30071
- runs.forEach((run) => {
31084
+ runsForLine.forEach((run) => {
30072
31085
  const elem = this.renderRun(run, context, trackedConfig);
30073
31086
  if (elem) {
30074
31087
  if (styleId) {
@@ -30126,8 +31139,18 @@ const _DomPainter = class _DomPainter {
30126
31139
  elem.dataset.trackChangeDate = meta.date;
30127
31140
  }
30128
31141
  }
30129
- updateFragmentElement(el, fragment) {
30130
- this.applyFragmentFrame(el, fragment);
31142
+ /**
31143
+ * Updates an existing fragment element's position and dimensions in place.
31144
+ * Used during incremental updates to efficiently reposition fragments without full re-render.
31145
+ *
31146
+ * @param el - The HTMLElement representing the fragment to update
31147
+ * @param fragment - The fragment data containing updated position and dimensions
31148
+ * @param section - The document section ('body', 'header', 'footer') containing this fragment.
31149
+ * Affects PM position validation - only body sections validate PM positions.
31150
+ * If undefined, defaults to 'body' section behavior.
31151
+ */
31152
+ updateFragmentElement(el, fragment, section) {
31153
+ this.applyFragmentFrame(el, fragment, section);
30131
31154
  if (fragment.kind === "image") {
30132
31155
  el.style.height = `${fragment.height}px`;
30133
31156
  }
@@ -30135,12 +31158,27 @@ const _DomPainter = class _DomPainter {
30135
31158
  el.style.height = `${fragment.height}px`;
30136
31159
  }
30137
31160
  }
30138
- applyFragmentFrame(el, fragment) {
31161
+ /**
31162
+ * Applies fragment positioning, dimensions, and metadata to an HTML element.
31163
+ * Sets CSS positioning, block ID, and PM position data attributes for paragraph fragments.
31164
+ *
31165
+ * @param el - The HTMLElement to apply fragment properties to
31166
+ * @param fragment - The fragment data containing position, dimensions, and PM position information
31167
+ * @param section - The document section ('body', 'header', 'footer') containing this fragment.
31168
+ * Controls PM position validation behavior:
31169
+ * - 'body' or undefined: PM positions are validated and required for paragraph fragments
31170
+ * - 'header' or 'footer': PM position validation is skipped (these sections have separate PM coordinate spaces)
31171
+ * When undefined, defaults to 'body' section behavior (validation enabled).
31172
+ */
31173
+ applyFragmentFrame(el, fragment, section) {
30139
31174
  el.style.left = `${fragment.x}px`;
30140
31175
  el.style.top = `${fragment.y}px`;
30141
31176
  el.style.width = `${fragment.width}px`;
30142
31177
  el.dataset.blockId = fragment.blockId;
30143
31178
  if (fragment.kind === "para") {
31179
+ if (section === "body" || section === void 0) {
31180
+ assertFragmentPmPositions(fragment, "paragraph fragment");
31181
+ }
30144
31182
  if (fragment.pmStart != null) {
30145
31183
  el.dataset.pmStart = String(fragment.pmStart);
30146
31184
  } else {
@@ -30387,22 +31425,29 @@ const deriveBlockVersion = (block) => {
30387
31425
  imgRun.pmEnd ?? ""
30388
31426
  ].join(",");
30389
31427
  }
31428
+ if (run.kind === "lineBreak") {
31429
+ return ["linebreak", run.pmStart ?? "", run.pmEnd ?? ""].join(",");
31430
+ }
31431
+ if (run.kind === "tab") {
31432
+ return [run.text ?? "", "tab", run.pmStart ?? "", run.pmEnd ?? ""].join(",");
31433
+ }
31434
+ const textRun = run;
30390
31435
  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 ?? "" : "",
31436
+ textRun.text ?? "",
31437
+ textRun.fontFamily,
31438
+ textRun.fontSize,
31439
+ textRun.bold ? 1 : 0,
31440
+ textRun.italic ? 1 : 0,
31441
+ textRun.color ?? "",
30397
31442
  // 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 ?? "" : ""
31443
+ textRun.underline?.style ?? "",
31444
+ textRun.underline?.color ?? "",
31445
+ textRun.strike ? 1 : 0,
31446
+ textRun.highlight ?? "",
31447
+ textRun.letterSpacing != null ? textRun.letterSpacing : "",
31448
+ textRun.pmStart ?? "",
31449
+ textRun.pmEnd ?? "",
31450
+ textRun.token ?? ""
30406
31451
  ].join(",");
30407
31452
  }).join("|");
30408
31453
  }
@@ -30453,16 +31498,57 @@ const deriveBlockVersion = (block) => {
30453
31498
  }
30454
31499
  if (block.kind === "table") {
30455
31500
  const tableBlock = block;
30456
- return [
30457
- block.id,
30458
- tableBlock.columnWidths ? JSON.stringify(tableBlock.columnWidths) : "",
30459
- tableBlock.rows.length
30460
- ].join("|");
31501
+ const hashString = (seed, value) => {
31502
+ let hash22 = seed >>> 0;
31503
+ for (let i = 0; i < value.length; i++) {
31504
+ hash22 ^= value.charCodeAt(i);
31505
+ hash22 = Math.imul(hash22, 16777619);
31506
+ }
31507
+ return hash22 >>> 0;
31508
+ };
31509
+ const hashNumber = (seed, value) => {
31510
+ const n = Number.isFinite(value) ? value : 0;
31511
+ let hash22 = seed ^ n;
31512
+ hash22 = Math.imul(hash22, 16777619);
31513
+ hash22 ^= hash22 >>> 13;
31514
+ return hash22 >>> 0;
31515
+ };
31516
+ let hash2 = 2166136261;
31517
+ hash2 = hashString(hash2, block.id);
31518
+ hash2 = hashNumber(hash2, tableBlock.rows.length);
31519
+ hash2 = (tableBlock.columnWidths ?? []).reduce((acc, width) => hashNumber(acc, Math.round(width * 1e3)), hash2);
31520
+ const rows = tableBlock.rows ?? [];
31521
+ for (const row of rows) {
31522
+ if (!row || !Array.isArray(row.cells)) continue;
31523
+ hash2 = hashNumber(hash2, row.cells.length);
31524
+ for (const cell of row.cells) {
31525
+ if (!cell) continue;
31526
+ const cellBlocks = cell.blocks ?? (cell.paragraph ? [cell.paragraph] : []);
31527
+ hash2 = hashNumber(hash2, cellBlocks.length);
31528
+ hash2 = hashNumber(hash2, cell.rowSpan ?? 1);
31529
+ hash2 = hashNumber(hash2, cell.colSpan ?? 1);
31530
+ for (const cellBlock of cellBlocks) {
31531
+ hash2 = hashString(hash2, cellBlock?.kind ?? "unknown");
31532
+ if (cellBlock?.kind === "paragraph") {
31533
+ const runs = cellBlock.runs ?? [];
31534
+ hash2 = hashNumber(hash2, runs.length);
31535
+ for (const run of runs) {
31536
+ if ("text" in run && typeof run.text === "string") {
31537
+ hash2 = hashString(hash2, run.text);
31538
+ }
31539
+ hash2 = hashNumber(hash2, run.pmStart ?? -1);
31540
+ hash2 = hashNumber(hash2, run.pmEnd ?? -1);
31541
+ }
31542
+ }
31543
+ }
31544
+ }
31545
+ }
31546
+ return [block.id, tableBlock.rows.length, hash2.toString(16)].join("|");
30461
31547
  }
30462
31548
  return block.id;
30463
31549
  };
30464
31550
  const applyRunStyles = (element, run, isLink = false) => {
30465
- if (run.kind === "tab" || run.kind === "image") {
31551
+ if (run.kind === "tab" || run.kind === "image" || run.kind === "lineBreak") {
30466
31552
  return;
30467
31553
  }
30468
31554
  element.style.fontFamily = run.fontFamily;
@@ -30494,6 +31580,13 @@ const applyRunStyles = (element, run, isLink = false) => {
30494
31580
  element.style.textDecorationLine = decorations.join(" ");
30495
31581
  }
30496
31582
  };
31583
+ const getCommentHighlight = (run) => {
31584
+ const comments = run.comments;
31585
+ if (!comments || comments.length === 0) return void 0;
31586
+ const primary = comments[0];
31587
+ const base2 = primary.internal ? COMMENT_INTERNAL_COLOR : COMMENT_EXTERNAL_COLOR;
31588
+ return `${base2}${COMMENT_INACTIVE_ALPHA}`;
31589
+ };
30497
31590
  const applyRunDataAttributes = (element, dataAttrs) => {
30498
31591
  if (!dataAttrs) return;
30499
31592
  Object.entries(dataAttrs).forEach(([key2, value]) => {
@@ -30585,6 +31678,10 @@ const sliceRunsForLine = (block, line) => {
30585
31678
  result.push(run);
30586
31679
  continue;
30587
31680
  }
31681
+ if (run.kind === "lineBreak") {
31682
+ result.push(run);
31683
+ continue;
31684
+ }
30588
31685
  const text = run.text ?? "";
30589
31686
  const isFirstRun = runIndex === line.fromRun;
30590
31687
  const isLastRun = runIndex === line.toRun;
@@ -30607,7 +31704,8 @@ const sliceRunsForLine = (block, line) => {
30607
31704
  ...run,
30608
31705
  text: slice2,
30609
31706
  pmStart: pmSliceStart,
30610
- pmEnd: pmSliceEnd
31707
+ pmEnd: pmSliceEnd,
31708
+ comments: run.comments ? [...run.comments] : void 0
30611
31709
  };
30612
31710
  result.push(sliced);
30613
31711
  }
@@ -30638,6 +31736,21 @@ const computeLinePmRange = (block, line) => {
30638
31736
  }
30639
31737
  continue;
30640
31738
  }
31739
+ if (run.kind === "lineBreak") {
31740
+ const runPmStart2 = run.pmStart ?? null;
31741
+ const runPmEnd = run.pmEnd ?? null;
31742
+ if (runPmStart2 == null || runPmEnd == null) {
31743
+ continue;
31744
+ }
31745
+ if (pmStart == null) {
31746
+ pmStart = runPmStart2;
31747
+ }
31748
+ pmEnd = runPmEnd;
31749
+ if (runIndex === line.toRun) {
31750
+ break;
31751
+ }
31752
+ continue;
31753
+ }
30641
31754
  const text = run.text ?? "";
30642
31755
  const runLength = text.length;
30643
31756
  const runPmStart = run.pmStart ?? null;
@@ -30676,6 +31789,9 @@ const resolveRunText = (run, context) => {
30676
31789
  if (run.kind === "image") {
30677
31790
  return "";
30678
31791
  }
31792
+ if (run.kind === "lineBreak") {
31793
+ return "";
31794
+ }
30679
31795
  if (!runToken) {
30680
31796
  return run.text ?? "";
30681
31797
  }
@@ -30842,6 +31958,9 @@ function isTabRun(run) {
30842
31958
  function isImageRun(run) {
30843
31959
  return run.kind === "image";
30844
31960
  }
31961
+ function isLineBreakRun(run) {
31962
+ return run.kind === "lineBreak";
31963
+ }
30845
31964
  async function measureBlock(block, constraints) {
30846
31965
  const normalized = normalizeConstraints(constraints);
30847
31966
  if (block.kind === "drawing") {
@@ -30879,7 +31998,7 @@ async function measureParagraphBlock(block, maxWidth) {
30879
31998
  const hanging = indent?.hanging ?? 0;
30880
31999
  const firstLineOffset = firstLine - hanging;
30881
32000
  const contentWidth = Math.max(1, maxWidth - indentLeft - indentRight);
30882
- let availableWidth = Math.max(1, contentWidth - firstLineOffset);
32001
+ const initialAvailableWidth = Math.max(1, contentWidth - firstLineOffset);
30883
32002
  const tabStops = buildTabStopsPx(
30884
32003
  indent,
30885
32004
  block.attrs?.tabs,
@@ -30892,6 +32011,18 @@ async function measureParagraphBlock(block, maxWidth) {
30892
32011
  line.bars = barTabStops.map((stop) => ({ x: stop.pos }));
30893
32012
  }
30894
32013
  };
32014
+ const dropCapDescriptor = block.attrs?.dropCapDescriptor;
32015
+ let dropCapMeasure = null;
32016
+ if (dropCapDescriptor) {
32017
+ if (!dropCapDescriptor.run || !dropCapDescriptor.run.text || !dropCapDescriptor.lines) {
32018
+ console.warn("Invalid drop cap descriptor - missing required fields:", dropCapDescriptor);
32019
+ } else {
32020
+ const dropCapMeasured = measureDropCap(ctx2, dropCapDescriptor, spacing);
32021
+ dropCapMeasure = dropCapMeasured;
32022
+ dropCapDescriptor.measuredWidth = dropCapMeasured.width;
32023
+ dropCapDescriptor.measuredHeight = dropCapMeasured.height;
32024
+ }
32025
+ }
30895
32026
  if (block.runs.length === 0) {
30896
32027
  const metrics = calculateTypographyMetrics(12, spacing);
30897
32028
  const emptyLine = {
@@ -30911,6 +32042,13 @@ async function measureParagraphBlock(block, maxWidth) {
30911
32042
  };
30912
32043
  }
30913
32044
  let currentLine = null;
32045
+ const getEffectiveWidth = (baseWidth) => {
32046
+ if (dropCapMeasure && lines.length < dropCapMeasure.lines && dropCapMeasure.mode === "drop") {
32047
+ return Math.max(1, baseWidth - dropCapMeasure.width);
32048
+ }
32049
+ return baseWidth;
32050
+ };
32051
+ let lastFontSize = 12;
30914
32052
  let tabStopCursor = 0;
30915
32053
  let pendingTabAlignment = null;
30916
32054
  let lastAppliedTabAlign = null;
@@ -30947,6 +32085,47 @@ async function measureParagraphBlock(block, maxWidth) {
30947
32085
  };
30948
32086
  for (let runIndex = 0; runIndex < block.runs.length; runIndex++) {
30949
32087
  const run = block.runs[runIndex];
32088
+ if (isLineBreakRun(run)) {
32089
+ if (currentLine) {
32090
+ const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing);
32091
+ const completedLine = {
32092
+ ...currentLine,
32093
+ ...metrics
32094
+ };
32095
+ addBarTabsToLine(completedLine);
32096
+ lines.push(completedLine);
32097
+ } else {
32098
+ const metrics = calculateTypographyMetrics(lastFontSize, spacing);
32099
+ const emptyLine = {
32100
+ fromRun: runIndex,
32101
+ fromChar: 0,
32102
+ toRun: runIndex,
32103
+ toChar: 0,
32104
+ width: 0,
32105
+ maxWidth: getEffectiveWidth(initialAvailableWidth),
32106
+ segments: [],
32107
+ ...metrics
32108
+ };
32109
+ addBarTabsToLine(emptyLine);
32110
+ lines.push(emptyLine);
32111
+ }
32112
+ const hadPreviousLine = lines.length > 0;
32113
+ const nextLineMaxWidth = hadPreviousLine ? getEffectiveWidth(contentWidth) : getEffectiveWidth(initialAvailableWidth);
32114
+ currentLine = {
32115
+ fromRun: runIndex,
32116
+ fromChar: 0,
32117
+ toRun: runIndex,
32118
+ toChar: 0,
32119
+ width: 0,
32120
+ maxFontSize: lastFontSize,
32121
+ maxWidth: nextLineMaxWidth,
32122
+ segments: []
32123
+ };
32124
+ tabStopCursor = 0;
32125
+ pendingTabAlignment = null;
32126
+ lastAppliedTabAlign = null;
32127
+ continue;
32128
+ }
30950
32129
  if (isTabRun(run)) {
30951
32130
  if (!currentLine) {
30952
32131
  currentLine = {
@@ -30957,7 +32136,7 @@ async function measureParagraphBlock(block, maxWidth) {
30957
32136
  width: 0,
30958
32137
  maxFontSize: 12,
30959
32138
  // Default font size for tabs
30960
- maxWidth: availableWidth,
32139
+ maxWidth: getEffectiveWidth(initialAvailableWidth),
30961
32140
  segments: []
30962
32141
  };
30963
32142
  }
@@ -31002,7 +32181,7 @@ async function measureParagraphBlock(block, maxWidth) {
31002
32181
  width: imageWidth,
31003
32182
  maxFontSize: imageHeight,
31004
32183
  // Use image height for line height calculation
31005
- maxWidth: availableWidth,
32184
+ maxWidth: getEffectiveWidth(initialAvailableWidth),
31006
32185
  segments: [
31007
32186
  {
31008
32187
  runIndex,
@@ -31012,7 +32191,6 @@ async function measureParagraphBlock(block, maxWidth) {
31012
32191
  }
31013
32192
  ]
31014
32193
  };
31015
- availableWidth = contentWidth;
31016
32194
  continue;
31017
32195
  }
31018
32196
  if (currentLine.width + imageWidth > currentLine.maxWidth && currentLine.width > 0) {
@@ -31032,7 +32210,7 @@ async function measureParagraphBlock(block, maxWidth) {
31032
32210
  toChar: 1,
31033
32211
  width: imageWidth,
31034
32212
  maxFontSize: imageHeight,
31035
- maxWidth: contentWidth,
32213
+ maxWidth: getEffectiveWidth(contentWidth),
31036
32214
  segments: [
31037
32215
  {
31038
32216
  runIndex,
@@ -31042,7 +32220,6 @@ async function measureParagraphBlock(block, maxWidth) {
31042
32220
  }
31043
32221
  ]
31044
32222
  };
31045
- availableWidth = contentWidth;
31046
32223
  } else {
31047
32224
  currentLine.toRun = runIndex;
31048
32225
  currentLine.toChar = 1;
@@ -31058,6 +32235,7 @@ async function measureParagraphBlock(block, maxWidth) {
31058
32235
  }
31059
32236
  continue;
31060
32237
  }
32238
+ lastFontSize = run.fontSize;
31061
32239
  const { font } = buildFontString(run);
31062
32240
  const tabSegments = run.text.split(" ");
31063
32241
  let charPosInRun = 0;
@@ -31092,7 +32270,7 @@ async function measureParagraphBlock(block, maxWidth) {
31092
32270
  toChar: wordEndNoSpace,
31093
32271
  width: wordOnlyWidth,
31094
32272
  maxFontSize: run.fontSize,
31095
- maxWidth: availableWidth,
32273
+ maxWidth: getEffectiveWidth(initialAvailableWidth),
31096
32274
  segments: [{ runIndex, fromChar: wordStartChar, toChar: wordEndNoSpace, width: wordOnlyWidth }]
31097
32275
  };
31098
32276
  const ls = run.letterSpacing ?? 0;
@@ -31103,7 +32281,6 @@ async function measureParagraphBlock(block, maxWidth) {
31103
32281
  } else {
31104
32282
  charPosInRun = wordEndNoSpace;
31105
32283
  }
31106
- availableWidth = contentWidth;
31107
32284
  continue;
31108
32285
  }
31109
32286
  const isTocEntry = block.attrs?.isTocEntry;
@@ -31125,7 +32302,7 @@ async function measureParagraphBlock(block, maxWidth) {
31125
32302
  toChar: wordEndNoSpace,
31126
32303
  width: wordOnlyWidth,
31127
32304
  maxFontSize: run.fontSize,
31128
- maxWidth: contentWidth,
32305
+ maxWidth: getEffectiveWidth(contentWidth),
31129
32306
  segments: [{ runIndex, fromChar: wordStartChar, toChar: wordEndNoSpace, width: wordOnlyWidth }]
31130
32307
  };
31131
32308
  if (!isLastWord && currentLine.width + spaceWidth <= currentLine.maxWidth) {
@@ -31181,10 +32358,9 @@ async function measureParagraphBlock(block, maxWidth) {
31181
32358
  toChar: charPosInRun,
31182
32359
  width: 0,
31183
32360
  maxFontSize: run.fontSize,
31184
- maxWidth: availableWidth,
32361
+ maxWidth: getEffectiveWidth(initialAvailableWidth),
31185
32362
  segments: []
31186
32363
  };
31187
- availableWidth = contentWidth;
31188
32364
  }
31189
32365
  const originX = currentLine.width;
31190
32366
  const { target, nextIndex, stop } = getNextTabStopPx(currentLine.width, tabStops, tabStopCursor);
@@ -31257,7 +32433,8 @@ async function measureParagraphBlock(block, maxWidth) {
31257
32433
  kind: "paragraph",
31258
32434
  lines,
31259
32435
  totalHeight,
31260
- ...markerInfo ? { marker: markerInfo } : {}
32436
+ ...markerInfo ? { marker: markerInfo } : {},
32437
+ ...dropCapMeasure ? { dropCap: dropCapMeasure } : {}
31261
32438
  };
31262
32439
  }
31263
32440
  async function measureTableBlock(block, constraints) {
@@ -31537,7 +32714,7 @@ const getPrimaryRun = (paragraph) => {
31537
32714
  };
31538
32715
  };
31539
32716
  const measureRunWidth = (text, font, ctx2, run) => {
31540
- const letterSpacing = run.kind === "text" ? run.letterSpacing || 0 : 0;
32717
+ const letterSpacing = run.kind === "text" || run.kind === void 0 ? run.letterSpacing || 0 : 0;
31541
32718
  const width = getMeasuredTextWidth(text, font, letterSpacing, ctx2);
31542
32719
  return roundValue(width);
31543
32720
  };
@@ -31573,6 +32750,30 @@ const sanitizeDecimalSeparator = (value) => {
31573
32750
  if (value === ",") return ",";
31574
32751
  return DEFAULT_DECIMAL_SEPARATOR;
31575
32752
  };
32753
+ const DROP_CAP_PADDING_PX = 4;
32754
+ const measureDropCap = (ctx2, descriptor, spacing) => {
32755
+ const { run, lines, mode } = descriptor;
32756
+ const { font } = buildFontString({
32757
+ fontFamily: run.fontFamily,
32758
+ fontSize: run.fontSize,
32759
+ bold: run.bold,
32760
+ italic: run.italic
32761
+ });
32762
+ ctx2.font = font;
32763
+ const metrics = ctx2.measureText(run.text);
32764
+ const advanceWidth = metrics.width;
32765
+ const paintedWidth = (metrics.actualBoundingBoxLeft || 0) + (metrics.actualBoundingBoxRight || 0);
32766
+ const textWidth = Math.max(advanceWidth, paintedWidth);
32767
+ const width = roundValue(textWidth + DROP_CAP_PADDING_PX);
32768
+ const baseLineHeight = resolveLineHeight(spacing, run.fontSize * 1.2);
32769
+ const height = roundValue(baseLineHeight * lines);
32770
+ return {
32771
+ width,
32772
+ height,
32773
+ lines,
32774
+ mode
32775
+ };
32776
+ };
31576
32777
  const resolveIndentLeft = (item) => {
31577
32778
  const indentLeft = sanitizePositive(item.paragraph.attrs?.indent?.left);
31578
32779
  if (indentLeft > 0) {
@@ -32711,10 +33912,7 @@ const _PresentationEditor = class _PresentationEditor extends EventEmitter {
32711
33912
  const tr = __privateGet(this, _editor3).state.tr.setSelection(TextSelection.create(__privateGet(this, _editor3).state.doc, hit.pos));
32712
33913
  try {
32713
33914
  __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
- }
33915
+ } catch {
32718
33916
  }
32719
33917
  }
32720
33918
  __privateMethod(this, _PresentationEditor_instances, scheduleSelectionUpdate_fn).call(this);
@@ -35247,10 +36445,10 @@ computeHeaderFooterCaretRect_fn = function(pos) {
35247
36445
  if (!lineInfo) return null;
35248
36446
  const { line, index: index2 } = lineInfo;
35249
36447
  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);
36448
+ if (range.pmStart == null || range.pmEnd == null) return null;
36449
+ const pmCharsInLine = Math.max(1, range.pmEnd - range.pmStart);
36450
+ const pmOffset = Math.max(0, Math.min(pmCharsInLine, pos - range.pmStart));
36451
+ const localX = hit.fragment.x + measureCharacterX(block, line, pmOffset);
35254
36452
  const lineOffset = __privateMethod(this, _PresentationEditor_instances, lineHeightBeforeIndex_fn).call(this, measure.lines, hit.fragment.fromLine, index2);
35255
36453
  const headerPageHeight = context.layout.pageSize?.h ?? context.region.height ?? 1;
35256
36454
  const headerLocalY = hit.pageIndex * headerPageHeight + (hit.fragment.y + lineOffset);
@@ -35390,23 +36588,34 @@ normalizeClientPoint_fn = function(clientX, clientY) {
35390
36588
  computeCaretLayoutRect_fn = function(pos) {
35391
36589
  const layout = __privateGet(this, _layoutState).layout;
35392
36590
  if (!layout) return null;
36591
+ const domResult = __privateMethod(this, _PresentationEditor_instances, computeCaretLayoutRectFromDOM_fn).call(this, pos);
36592
+ if (domResult) {
36593
+ return domResult;
36594
+ }
35393
36595
  const hit = getFragmentAtPosition(layout, __privateGet(this, _layoutState).blocks, __privateGet(this, _layoutState).measures, pos);
35394
- if (!hit) return null;
36596
+ if (!hit) {
36597
+ return null;
36598
+ }
35395
36599
  const block = hit.block;
35396
36600
  const measure = hit.measure;
36601
+ if (hit.fragment.kind === "table" && block?.kind === "table" && measure?.kind === "table") {
36602
+ return __privateMethod(this, _PresentationEditor_instances, computeTableCaretLayoutRect_fn).call(this, pos, hit.fragment, block, measure, hit.pageIndex);
36603
+ }
35397
36604
  if (!block || block.kind !== "paragraph" || measure?.kind !== "paragraph") return null;
35398
36605
  if (hit.fragment.kind !== "para") {
35399
36606
  return null;
35400
36607
  }
35401
36608
  const fragment = hit.fragment;
35402
36609
  const lineInfo = __privateMethod(this, _PresentationEditor_instances, findLineContainingPos_fn).call(this, block, measure, fragment.fromLine, fragment.toLine, pos);
35403
- if (!lineInfo) return null;
36610
+ if (!lineInfo) {
36611
+ return null;
36612
+ }
35404
36613
  const { line, index: index2 } = lineInfo;
35405
36614
  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);
36615
+ if (range.pmStart == null || range.pmEnd == null) return null;
36616
+ const pmCharsInLine = Math.max(1, range.pmEnd - range.pmStart);
36617
+ const pmOffset = Math.max(0, Math.min(pmCharsInLine, pos - range.pmStart));
36618
+ const localX = fragment.x + measureCharacterX(block, line, pmOffset);
35410
36619
  const lineOffset = __privateMethod(this, _PresentationEditor_instances, lineHeightBeforeIndex_fn).call(this, measure.lines, fragment.fromLine, index2);
35411
36620
  const localY = fragment.y + lineOffset;
35412
36621
  return {
@@ -35416,17 +36625,179 @@ computeCaretLayoutRect_fn = function(pos) {
35416
36625
  height: line.lineHeight
35417
36626
  };
35418
36627
  };
36628
+ /**
36629
+ * Computes caret position using DOM-based positioning.
36630
+ * This matches how click-to-position mapping works and correctly handles
36631
+ * segment-based rendering with tab stops.
36632
+ *
36633
+ * Returns page-local coordinates (x, y relative to the page element).
36634
+ */
36635
+ computeCaretLayoutRectFromDOM_fn = function(pos) {
36636
+ const zoom = __privateGet(this, _layoutOptions).zoom ?? 1;
36637
+ let targetPageEl = null;
36638
+ if (__privateGet(this, _layoutState).layout && __privateGet(this, _layoutState).blocks && __privateGet(this, _layoutState).measures) {
36639
+ const fragmentHit = getFragmentAtPosition(
36640
+ __privateGet(this, _layoutState).layout,
36641
+ __privateGet(this, _layoutState).blocks,
36642
+ __privateGet(this, _layoutState).measures,
36643
+ pos
36644
+ );
36645
+ if (fragmentHit) {
36646
+ const pageEl = __privateGet(this, _viewportHost).querySelector(
36647
+ `.superdoc-page[data-page-index="${fragmentHit.pageIndex}"]`
36648
+ );
36649
+ if (pageEl) {
36650
+ targetPageEl = pageEl;
36651
+ }
36652
+ }
36653
+ }
36654
+ const spanEls = Array.from(
36655
+ targetPageEl ? targetPageEl.querySelectorAll("span[data-pm-start][data-pm-end]") : __privateGet(this, _viewportHost).querySelectorAll("span[data-pm-start][data-pm-end]")
36656
+ );
36657
+ for (const spanEl of spanEls) {
36658
+ const pmStart = Number(spanEl.dataset.pmStart ?? "NaN");
36659
+ const pmEnd = Number(spanEl.dataset.pmEnd ?? "NaN");
36660
+ if (!Number.isFinite(pmStart) || !Number.isFinite(pmEnd)) continue;
36661
+ if (pos < pmStart || pos > pmEnd) continue;
36662
+ const pageEl = spanEl.closest(".superdoc-page");
36663
+ if (!pageEl) continue;
36664
+ const pageIndex = Number(pageEl.dataset.pageIndex ?? "0");
36665
+ const pageRect = pageEl.getBoundingClientRect();
36666
+ const textNode = spanEl.firstChild;
36667
+ if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
36668
+ const spanRect2 = spanEl.getBoundingClientRect();
36669
+ return {
36670
+ pageIndex,
36671
+ x: (spanRect2.left - pageRect.left) / zoom,
36672
+ y: (spanRect2.top - pageRect.top) / zoom,
36673
+ height: spanRect2.height / zoom
36674
+ };
36675
+ }
36676
+ const text = textNode.textContent ?? "";
36677
+ const charOffset = Math.max(0, Math.min(text.length, pos - pmStart));
36678
+ const range = document.createRange();
36679
+ try {
36680
+ range.setStart(textNode, charOffset);
36681
+ range.setEnd(textNode, charOffset);
36682
+ } catch (error) {
36683
+ if (process$1.env.NODE_ENV === "development") {
36684
+ console.warn("[PresentationEditor] Range.setStart/setEnd failed:", {
36685
+ error: error instanceof Error ? error.message : String(error),
36686
+ charOffset,
36687
+ textLength: text.length,
36688
+ pos,
36689
+ pmStart,
36690
+ pmEnd
36691
+ });
36692
+ }
36693
+ const spanRect2 = spanEl.getBoundingClientRect();
36694
+ return {
36695
+ pageIndex,
36696
+ x: (spanRect2.left - pageRect.left) / zoom,
36697
+ y: (spanRect2.top - pageRect.top) / zoom,
36698
+ height: spanRect2.height / zoom
36699
+ };
36700
+ }
36701
+ const rangeRect = range.getBoundingClientRect();
36702
+ const spanRect = spanEl.getBoundingClientRect();
36703
+ const lineEl = spanEl.closest(".superdoc-line");
36704
+ const lineRect = lineEl ? lineEl.getBoundingClientRect() : spanRect;
36705
+ const caretHeight = spanRect.height;
36706
+ const verticalOffset = (lineRect.height - caretHeight) / 2;
36707
+ const caretY = lineRect.top + verticalOffset;
36708
+ return {
36709
+ pageIndex,
36710
+ x: (rangeRect.left - pageRect.left) / zoom,
36711
+ y: (caretY - pageRect.top) / zoom,
36712
+ height: caretHeight / zoom
36713
+ };
36714
+ }
36715
+ return null;
36716
+ };
36717
+ /**
36718
+ * Computes caret position within a table cell using DOM-based positioning.
36719
+ * This uses the actual rendered DOM elements to get accurate positions,
36720
+ * matching how click-to-position mapping works.
36721
+ */
36722
+ computeTableCaretLayoutRect_fn = function(pos, _fragment, _tableBlock, _tableMeasure, pageIndex) {
36723
+ const lineEls = Array.from(__privateGet(this, _viewportHost).querySelectorAll(".superdoc-line"));
36724
+ if (lineEls.length === 0) return null;
36725
+ for (const lineEl of lineEls) {
36726
+ const pmStart = Number(lineEl.dataset.pmStart ?? "NaN");
36727
+ const pmEnd = Number(lineEl.dataset.pmEnd ?? "NaN");
36728
+ if (!Number.isFinite(pmStart) || !Number.isFinite(pmEnd)) continue;
36729
+ if (pos < pmStart || pos > pmEnd) continue;
36730
+ const spanEls = Array.from(lineEl.querySelectorAll("span[data-pm-start]"));
36731
+ for (const spanEl of spanEls) {
36732
+ const spanStart = Number(spanEl.dataset.pmStart ?? "NaN");
36733
+ const spanEnd = Number(spanEl.dataset.pmEnd ?? "NaN");
36734
+ if (!Number.isFinite(spanStart) || !Number.isFinite(spanEnd)) continue;
36735
+ if (pos < spanStart || pos > spanEnd) continue;
36736
+ const textNode = spanEl.firstChild;
36737
+ if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
36738
+ const spanRect = spanEl.getBoundingClientRect();
36739
+ const viewportRect3 = __privateGet(this, _viewportHost).getBoundingClientRect();
36740
+ const zoom3 = __privateGet(this, _layoutOptions).zoom ?? 1;
36741
+ return {
36742
+ pageIndex,
36743
+ x: (spanRect.left - viewportRect3.left + __privateGet(this, _visibleHost).scrollLeft) / zoom3,
36744
+ y: (spanRect.top - viewportRect3.top + __privateGet(this, _visibleHost).scrollTop) / zoom3,
36745
+ height: spanRect.height / zoom3
36746
+ };
36747
+ }
36748
+ const text = textNode.textContent ?? "";
36749
+ const charOffset = Math.max(0, Math.min(text.length, pos - spanStart));
36750
+ const range = document.createRange();
36751
+ range.setStart(textNode, charOffset);
36752
+ range.setEnd(textNode, charOffset);
36753
+ const rangeRect = range.getBoundingClientRect();
36754
+ const viewportRect2 = __privateGet(this, _viewportHost).getBoundingClientRect();
36755
+ const zoom2 = __privateGet(this, _layoutOptions).zoom ?? 1;
36756
+ const lineRect2 = lineEl.getBoundingClientRect();
36757
+ return {
36758
+ pageIndex,
36759
+ x: (rangeRect.left - viewportRect2.left + __privateGet(this, _visibleHost).scrollLeft) / zoom2,
36760
+ y: (lineRect2.top - viewportRect2.top + __privateGet(this, _visibleHost).scrollTop) / zoom2,
36761
+ height: lineRect2.height / zoom2
36762
+ };
36763
+ }
36764
+ const lineRect = lineEl.getBoundingClientRect();
36765
+ const viewportRect = __privateGet(this, _viewportHost).getBoundingClientRect();
36766
+ const zoom = __privateGet(this, _layoutOptions).zoom ?? 1;
36767
+ return {
36768
+ pageIndex,
36769
+ x: (lineRect.left - viewportRect.left + __privateGet(this, _visibleHost).scrollLeft) / zoom,
36770
+ y: (lineRect.top - viewportRect.top + __privateGet(this, _visibleHost).scrollTop) / zoom,
36771
+ height: lineRect.height / zoom
36772
+ };
36773
+ }
36774
+ return null;
36775
+ };
35419
36776
  findLineContainingPos_fn = function(block, measure, fromLine, toLine, pos) {
36777
+ const log2 = (...args) => {
36778
+ console.log("[LINE-SEARCH]", ...args);
36779
+ };
36780
+ log2("Searching for pos:", pos, "in lines", fromLine, "to", toLine);
35420
36781
  if (measure.kind !== "paragraph" || block.kind !== "paragraph") return null;
35421
36782
  for (let lineIndex = fromLine; lineIndex < toLine; lineIndex += 1) {
35422
36783
  const line = measure.lines[lineIndex];
35423
36784
  if (!line) continue;
35424
36785
  const range = computeLinePmRange$1(block, line);
36786
+ log2("Line", lineIndex, ":", {
36787
+ pmStart: range.pmStart,
36788
+ pmEnd: range.pmEnd,
36789
+ fromRun: line.fromRun,
36790
+ toRun: line.toRun,
36791
+ fromChar: line.fromChar,
36792
+ toChar: line.toChar
36793
+ });
35425
36794
  if (range.pmStart == null || range.pmEnd == null) continue;
35426
36795
  if (pos >= range.pmStart && pos <= range.pmEnd) {
36796
+ log2("Found line", lineIndex, "for pos", pos);
35427
36797
  return { line, index: lineIndex };
35428
36798
  }
35429
36799
  }
36800
+ log2("No line found for pos", pos);
35430
36801
  return null;
35431
36802
  };
35432
36803
  lineHeightBeforeIndex_fn = function(lines, fromLine, targetIndex) {
@@ -48995,6 +50366,49 @@ const ShapeGroup = Node$1.create({
48995
50366
  };
48996
50367
  }
48997
50368
  });
50369
+ const sharedAttributes = () => ({
50370
+ originalName: {
50371
+ default: null
50372
+ },
50373
+ originalXml: {
50374
+ default: null
50375
+ }
50376
+ });
50377
+ const hiddenRender = (type) => ["sd-passthrough", { "data-sd-passthrough": type, style: "display: none;" }];
50378
+ const PassthroughBlock = Node$1.create({
50379
+ name: "passthroughBlock",
50380
+ group: "block",
50381
+ atom: true,
50382
+ draggable: false,
50383
+ selectable: false,
50384
+ defining: true,
50385
+ parseDOM() {
50386
+ return [{ tag: 'sd-passthrough[data-sd-passthrough="block"]' }];
50387
+ },
50388
+ renderDOM() {
50389
+ return hiddenRender("block");
50390
+ },
50391
+ addAttributes() {
50392
+ return sharedAttributes();
50393
+ }
50394
+ });
50395
+ const PassthroughInline = Node$1.create({
50396
+ name: "passthroughInline",
50397
+ group: "inline",
50398
+ inline: true,
50399
+ atom: true,
50400
+ draggable: false,
50401
+ selectable: false,
50402
+ parseDOM() {
50403
+ return [{ tag: 'sd-passthrough[data-sd-passthrough="inline"]' }];
50404
+ },
50405
+ renderDOM() {
50406
+ return hiddenRender("inline");
50407
+ },
50408
+ addAttributes() {
50409
+ return sharedAttributes();
50410
+ }
50411
+ });
48998
50412
  const TextStyle = Mark.create({
48999
50413
  name: "textStyle",
49000
50414
  addOptions() {
@@ -54905,7 +56319,9 @@ const getRichTextExtensions = () => {
54905
56319
  AiPlugin,
54906
56320
  Image,
54907
56321
  NodeResizer,
54908
- CustomSelection
56322
+ CustomSelection,
56323
+ PassthroughInline,
56324
+ PassthroughBlock
54909
56325
  ];
54910
56326
  };
54911
56327
  const getStarterExtensions = () => {
@@ -54977,7 +56393,9 @@ const getStarterExtensions = () => {
54977
56393
  CustomSelection,
54978
56394
  TextTransform,
54979
56395
  VectorShape,
54980
- ShapeGroup
56396
+ ShapeGroup,
56397
+ PassthroughInline,
56398
+ PassthroughBlock
54981
56399
  ];
54982
56400
  };
54983
56401
  export {