superdoc 1.0.0-beta.13 → 1.0.0-beta.14

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 (40) hide show
  1. package/dist/chunks/{PdfViewer-D_E86Mtr.cjs → PdfViewer-Cdc_EOZ9.cjs} +2 -2
  2. package/dist/chunks/{PdfViewer-D0cPi5hG.es.js → PdfViewer-DrsLL5TV.es.js} +2 -2
  3. package/dist/chunks/{eventemitter3-ByBH0NYV.es.js → eventemitter3-CcXAdeql.es.js} +1 -1
  4. package/dist/chunks/{eventemitter3-CFCpOk3d.cjs → eventemitter3-DQmQUge-.cjs} +1 -1
  5. package/dist/chunks/{index-CL4VptDO.cjs → index-9ZvcPlCg.cjs} +6 -6
  6. package/dist/chunks/{index-BHmLKAul.es.js → index-DB1DyDmZ.es.js} +6 -6
  7. package/dist/chunks/{index-BMAfbNel-CvPc3jnD.es.js → index-I4Ew0HDV-Bht7-IGi.es.js} +1 -1
  8. package/dist/chunks/{index-BMAfbNel-DBZCkkMj.cjs → index-I4Ew0HDV-EPhjcu7T.cjs} +1 -1
  9. package/dist/chunks/{jszip-BwsONqK5.es.js → jszip-5vvIqAEE.es.js} +1 -1
  10. package/dist/chunks/{jszip-B99MTu59.cjs → jszip-BdEez1WM.cjs} +1 -1
  11. package/dist/chunks/{super-editor.es-riuOlaxm.cjs → super-editor.es-DuS77THX.cjs} +3958 -531
  12. package/dist/chunks/{super-editor.es-5gtBhP8I.es.js → super-editor.es-DxAG4BVh.es.js} +3959 -532
  13. package/dist/chunks/{vue-CztqUvm1.es.js → vue-Dysv_7z5.es.js} +101 -12
  14. package/dist/chunks/{vue-ARQSyfaw.cjs → vue-jWLMl8Ts.cjs} +89 -0
  15. package/dist/chunks/xml-js-ClO_jHnq.es.js +2 -0
  16. package/dist/chunks/xml-js-Dz51sEbr.cjs +3 -0
  17. package/dist/packages/superdoc/src/core/SuperDoc.d.ts.map +1 -1
  18. package/dist/style.css +50 -11
  19. package/dist/super-editor/ai-writer.es.js +2 -2
  20. package/dist/super-editor/chunks/{converter-D9KiDgXx.js → converter-CfmIU--8.js} +511 -95
  21. package/dist/super-editor/chunks/{docx-zipper-DQmUj-ef.js → docx-zipper-B-CXiNSb.js} +1 -1
  22. package/dist/super-editor/chunks/{editor-ByPqJ2k8.js → editor-wC_gs8Bl.js} +2898 -347
  23. package/dist/super-editor/chunks/{index-BMAfbNel.js → index-I4Ew0HDV.js} +1 -1
  24. package/dist/super-editor/chunks/{toolbar-Da2cOhJ-.js → toolbar-BR1GYvwW.js} +36 -22
  25. package/dist/super-editor/converter.es.js +1 -1
  26. package/dist/super-editor/docx-zipper.es.js +2 -2
  27. package/dist/super-editor/editor.es.js +3 -3
  28. package/dist/super-editor/file-zipper.es.js +1 -1
  29. package/dist/super-editor/style.css +50 -11
  30. package/dist/super-editor/super-editor.es.js +559 -67
  31. package/dist/super-editor/toolbar.es.js +2 -2
  32. package/dist/super-editor.cjs +4 -4
  33. package/dist/super-editor.es.js +2 -2
  34. package/dist/superdoc.cjs +2 -2
  35. package/dist/superdoc.es.js +2 -2
  36. package/dist/superdoc.umd.js +4023 -520
  37. package/dist/superdoc.umd.js.map +1 -1
  38. package/package.json +1 -1
  39. package/dist/chunks/xml-js-BZPSMmVo.es.js +0 -2
  40. package/dist/chunks/xml-js-DQa4Ye5C.cjs +0 -3
@@ -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, _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, 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;
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;
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-D9KiDgXx.js";
16
- import { D as DocxZipper } from "./docx-zipper-DQmUj-ef.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-CfmIU--8.js";
16
+ import { D as DocxZipper } from "./docx-zipper-B-CXiNSb.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() {
@@ -13553,7 +13553,7 @@ const isHeadless = (editor) => {
13553
13553
  const shouldSkipNodeView = (editor) => {
13554
13554
  return isHeadless(editor);
13555
13555
  };
13556
- const summaryVersion = "1.0.0-beta.13";
13556
+ const summaryVersion = "1.0.0-beta.14";
13557
13557
  const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
13558
13558
  const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
13559
13559
  function mapAttributes(attrs) {
@@ -14335,7 +14335,7 @@ const _Editor = class _Editor extends EventEmitter {
14335
14335
  { default: remarkStringify },
14336
14336
  { default: remarkGfm }
14337
14337
  ] = await Promise.all([
14338
- import("./index-BMAfbNel.js"),
14338
+ import("./index-I4Ew0HDV.js"),
14339
14339
  import("./index-DRCvimau.js"),
14340
14340
  import("./index-C_x_N6Uh.js"),
14341
14341
  import("./index-D_sWOSiG.js"),
@@ -14540,7 +14540,7 @@ const _Editor = class _Editor extends EventEmitter {
14540
14540
  * Process collaboration migrations
14541
14541
  */
14542
14542
  processCollaborationMigrations() {
14543
- console.debug("[checkVersionMigrations] Current editor version", "1.0.0-beta.13");
14543
+ console.debug("[checkVersionMigrations] Current editor version", "1.0.0-beta.14");
14544
14544
  if (!this.options.ydoc) return;
14545
14545
  const metaMap = this.options.ydoc.getMap("meta");
14546
14546
  let docVersion = metaMap.get("version");
@@ -15872,22 +15872,15 @@ function hydrateImageBlocks(blocks, mediaFiles) {
15872
15872
  if (normalizedMedia.size === 0) {
15873
15873
  return blocks;
15874
15874
  }
15875
- return blocks.map((block) => {
15876
- if (block.kind !== "image") {
15877
- return block;
15878
- }
15879
- if (!block.src || block.src.startsWith("data:")) {
15880
- return block;
15875
+ const resolveImageSrc = (src, relId, attrSrc, extension) => {
15876
+ if (!src || src.startsWith("data:")) {
15877
+ return void 0;
15881
15878
  }
15882
- const attrs = block.attrs ?? {};
15883
- const relId = typeof attrs.rId === "string" ? attrs.rId : void 0;
15884
- const attrSrc = typeof attrs.src === "string" ? attrs.src : void 0;
15885
- const extension = typeof attrs.extension === "string" ? attrs.extension.toLowerCase() : void 0;
15886
15879
  const candidates = /* @__PURE__ */ new Set();
15887
- candidates.add(block.src);
15880
+ candidates.add(src);
15888
15881
  if (attrSrc) candidates.add(attrSrc);
15889
15882
  if (relId) {
15890
- const inferredExt = extension ?? inferExtensionFromPath(block.src) ?? "jpeg";
15883
+ const inferredExt = extension ?? inferExtensionFromPath(src) ?? "jpeg";
15891
15884
  candidates.add(`word/media/${relId}.${inferredExt}`);
15892
15885
  candidates.add(`media/${relId}.${inferredExt}`);
15893
15886
  }
@@ -15897,14 +15890,128 @@ function hydrateImageBlocks(blocks, mediaFiles) {
15897
15890
  const base64 = normalizedMedia.get(normalized);
15898
15891
  if (!base64) continue;
15899
15892
  const finalExt = extension ?? inferExtensionFromPath(normalized) ?? "jpeg";
15900
- return {
15901
- ...block,
15902
- src: `data:image/${finalExt};base64,${base64}`
15903
- };
15893
+ return base64.startsWith("data:") ? base64 : `data:image/${finalExt};base64,${base64}`;
15894
+ }
15895
+ return void 0;
15896
+ };
15897
+ const hydrateRuns = (runs) => {
15898
+ let hasChanges = false;
15899
+ const hydratedRuns = runs.map((run) => {
15900
+ if (run.kind !== "image") {
15901
+ return run;
15902
+ }
15903
+ const imageRun = run;
15904
+ if (!imageRun.src || imageRun.src.startsWith("data:")) {
15905
+ return run;
15906
+ }
15907
+ const resolvedSrc = resolveImageSrc(imageRun.src);
15908
+ if (resolvedSrc) {
15909
+ hasChanges = true;
15910
+ return { ...imageRun, src: resolvedSrc };
15911
+ }
15912
+ return run;
15913
+ });
15914
+ return hasChanges ? hydratedRuns : runs;
15915
+ };
15916
+ return blocks.map((block) => {
15917
+ if (block.kind === "image") {
15918
+ if (!block.src || block.src.startsWith("data:")) {
15919
+ return block;
15920
+ }
15921
+ const attrs = block.attrs ?? {};
15922
+ const relId = typeof attrs.rId === "string" ? attrs.rId : void 0;
15923
+ const attrSrc = typeof attrs.src === "string" ? attrs.src : void 0;
15924
+ const extension = typeof attrs.extension === "string" ? attrs.extension.toLowerCase() : void 0;
15925
+ const resolvedSrc = resolveImageSrc(block.src, relId, attrSrc, extension);
15926
+ if (resolvedSrc) {
15927
+ return { ...block, src: resolvedSrc };
15928
+ }
15929
+ return block;
15930
+ }
15931
+ if (block.kind === "paragraph") {
15932
+ const paragraphBlock = block;
15933
+ if (!paragraphBlock.runs || paragraphBlock.runs.length === 0) {
15934
+ return block;
15935
+ }
15936
+ const hydratedRuns = hydrateRuns(paragraphBlock.runs);
15937
+ if (hydratedRuns !== paragraphBlock.runs) {
15938
+ return { ...paragraphBlock, runs: hydratedRuns };
15939
+ }
15940
+ return block;
15904
15941
  }
15905
15942
  return block;
15906
15943
  });
15907
15944
  }
15945
+ function isGradientFill(value) {
15946
+ if (!isPlainObject$2(value)) return false;
15947
+ if (value.type !== "gradient") return false;
15948
+ const gradientType = value.gradientType;
15949
+ if (gradientType !== "linear" && gradientType !== "radial") return false;
15950
+ if (gradientType === "linear") {
15951
+ if (typeof value.angle !== "number" || !Number.isFinite(value.angle)) {
15952
+ return false;
15953
+ }
15954
+ }
15955
+ if (!Array.isArray(value.stops) || value.stops.length === 0) return false;
15956
+ return value.stops.every((stop) => {
15957
+ if (!isPlainObject$2(stop)) return false;
15958
+ return typeof stop.position === "number" && Number.isFinite(stop.position) && typeof stop.color === "string" && (stop.alpha === void 0 || typeof stop.alpha === "number" && Number.isFinite(stop.alpha));
15959
+ });
15960
+ }
15961
+ function isSolidFillWithAlpha(value) {
15962
+ return isPlainObject$2(value) && value.type === "solidWithAlpha" && typeof value.color === "string" && typeof value.alpha === "number";
15963
+ }
15964
+ function normalizeFillColor(value) {
15965
+ if (value === null) return null;
15966
+ if (typeof value === "string") return value;
15967
+ if (isGradientFill(value)) return value;
15968
+ if (isSolidFillWithAlpha(value)) return value;
15969
+ return void 0;
15970
+ }
15971
+ function normalizeStrokeColor(value) {
15972
+ if (value === null) return null;
15973
+ if (typeof value === "string") return value;
15974
+ return void 0;
15975
+ }
15976
+ function normalizeTextContent(value) {
15977
+ if (!isPlainObject$2(value)) return void 0;
15978
+ if (!Array.isArray(value.parts)) return void 0;
15979
+ if (value.parts.length === 0) return void 0;
15980
+ const validParts = value.parts.filter((p) => isPlainObject$2(p) && typeof p.text === "string");
15981
+ if (validParts.length === 0) return void 0;
15982
+ const result = {
15983
+ parts: validParts
15984
+ };
15985
+ if (["left", "center", "right"].includes(value.horizontalAlign)) {
15986
+ result.horizontalAlign = value.horizontalAlign;
15987
+ }
15988
+ return result;
15989
+ }
15990
+ function normalizeTextVerticalAlign(value) {
15991
+ if (typeof value !== "string") return void 0;
15992
+ if (value === "top" || value === "center" || value === "bottom") {
15993
+ return value;
15994
+ }
15995
+ return void 0;
15996
+ }
15997
+ function normalizeTextInsets(value) {
15998
+ if (!isPlainObject$2(value)) return void 0;
15999
+ const top2 = pickNumber(value.top);
16000
+ const right2 = pickNumber(value.right);
16001
+ const bottom2 = pickNumber(value.bottom);
16002
+ const left2 = pickNumber(value.left);
16003
+ if (top2 == null || right2 == null || bottom2 == null || left2 == null) {
16004
+ return void 0;
16005
+ }
16006
+ return { top: top2, right: right2, bottom: bottom2, left: left2 };
16007
+ }
16008
+ const OOXML_Z_INDEX_BASE = 251658240;
16009
+ function normalizeZIndex(originalAttributes) {
16010
+ if (!isPlainObject$2(originalAttributes)) return void 0;
16011
+ const relativeHeight = originalAttributes.relativeHeight;
16012
+ if (typeof relativeHeight !== "number") return void 0;
16013
+ return Math.max(0, relativeHeight - OOXML_Z_INDEX_BASE);
16014
+ }
15908
16015
  const DEFAULT_ALLOWED_PROTOCOLS = ["http", "https", "mailto", "tel", "sms"];
15909
16016
  const OPTIONAL_PROTOCOLS = ["ftp", "sftp", "irc"];
15910
16017
  const BLOCKED_PROTOCOLS = ["javascript", "data", "vbscript", "file", "ssh", "ws", "wss"];
@@ -16798,6 +16905,7 @@ const normalizeBorderSide = (value) => {
16798
16905
  if (!value || typeof value !== "object") return void 0;
16799
16906
  const raw = value;
16800
16907
  const style = mapBorderStyle(raw.val);
16908
+ if (style === "none") return void 0;
16801
16909
  const width = pickNumber(raw.size);
16802
16910
  const widthPx = borderSizeToPx(width);
16803
16911
  const color = normalizeColor(raw.color);
@@ -16806,7 +16914,9 @@ const normalizeBorderSide = (value) => {
16806
16914
  return void 0;
16807
16915
  }
16808
16916
  const border = {};
16809
- if (style && style !== "none") border.style = style;
16917
+ if (style) {
16918
+ border.style = style;
16919
+ }
16810
16920
  if (widthPx != null) border.width = Math.max(0, widthPx);
16811
16921
  if (color) border.color = color;
16812
16922
  if (space != null) border.space = Math.max(0, space);
@@ -17917,7 +18027,13 @@ function resolveMarkerRunProperties(input) {
17917
18027
  return input.cached;
17918
18028
  }
17919
18029
  const numberingResolved = input.numbering?.resolvedMarkerRpr || input.resolvedParagraphProps.numberingProperties?.resolvedMarkerRpr;
17920
- return mergeRunProperties(DEFAULT_MARKER_RUN, input.docDefaults.run, numberingResolved, input.inlineMarkerRpr);
18030
+ const result = mergeRunProperties(
18031
+ DEFAULT_MARKER_RUN,
18032
+ input.docDefaults.run,
18033
+ numberingResolved,
18034
+ input.inlineMarkerRpr
18035
+ );
18036
+ return result;
17921
18037
  }
17922
18038
  const DEFAULT_MARKER_RUN = {
17923
18039
  fontFamily: "Times New Roman",
@@ -18661,7 +18777,7 @@ const normalizeResolvedTabAlignment = (value) => {
18661
18777
  return void 0;
18662
18778
  }
18663
18779
  };
18664
- const computeWordLayoutForParagraph = (paragraphAttrs, numberingProps, styleContext) => {
18780
+ const computeWordLayoutForParagraph = (paragraphAttrs, numberingProps, styleContext, _paragraphNode) => {
18665
18781
  if (numberingProps === null) {
18666
18782
  return null;
18667
18783
  }
@@ -18684,12 +18800,14 @@ const computeWordLayoutForParagraph = (paragraphAttrs, numberingProps, styleCont
18684
18800
  decimalSeparator: paragraphAttrs.decimalSeparator,
18685
18801
  numberingProperties: numberingProps
18686
18802
  };
18803
+ const defaultFontFamily = styleContext.defaults?.paragraphFont ?? styleContext.defaults?.paragraphFontFamily ?? "Times New Roman";
18804
+ const defaultFontSize = styleContext.defaults?.fontSize ?? 12;
18687
18805
  const docDefaults = {
18688
18806
  defaultTabIntervalTwips: styleContext.defaults?.defaultTabIntervalTwips ?? 720,
18689
18807
  decimalSeparator: styleContext.defaults?.decimalSeparator ?? ".",
18690
18808
  run: {
18691
- fontFamily: "Times New Roman",
18692
- fontSize: 12
18809
+ fontFamily: defaultFontFamily,
18810
+ fontSize: defaultFontSize
18693
18811
  },
18694
18812
  paragraph: {
18695
18813
  indent: {},
@@ -18710,12 +18828,26 @@ const computeWordLayoutForParagraph = (paragraphAttrs, numberingProps, styleCont
18710
18828
  };
18711
18829
  }
18712
18830
  }
18713
- return computeWordParagraphLayout({
18831
+ if (!markerRun) {
18832
+ markerRun = {
18833
+ fontFamily: "Times New Roman",
18834
+ fontSize: 12,
18835
+ color: "#000000"
18836
+ };
18837
+ }
18838
+ if (markerRun.fontSize != null) {
18839
+ const fontSizePx = ptToPx(markerRun.fontSize);
18840
+ if (fontSizePx != null) {
18841
+ markerRun = { ...markerRun, fontSize: fontSizePx };
18842
+ }
18843
+ }
18844
+ const layout = computeWordParagraphLayout({
18714
18845
  paragraph: resolvedParagraph,
18715
18846
  numbering: numberingProps,
18716
18847
  markerRun,
18717
18848
  docDefaults
18718
18849
  });
18850
+ return layout;
18719
18851
  } catch {
18720
18852
  return null;
18721
18853
  }
@@ -18879,26 +19011,58 @@ const computeParagraphAttrs = (para, styleContext, listCounterContext, converter
18879
19011
  paragraphAttrs.tabs = normalizedTabs;
18880
19012
  }
18881
19013
  }
18882
- let framePr = attrs.framePr;
18883
- if (!framePr && attrs.paragraphProperties && typeof attrs.paragraphProperties === "object") {
18884
- const pPr = attrs.paragraphProperties;
18885
- if (pPr.elements && Array.isArray(pPr.elements)) {
18886
- const framePrElement = pPr.elements.find((el) => el.name === "w:framePr");
18887
- if (framePrElement?.attributes) {
18888
- framePr = {
18889
- xAlign: framePrElement.attributes["w:xAlign"],
18890
- dropCap: framePrElement.attributes["w:dropCap"]
18891
- };
18892
- }
19014
+ const asString = (value) => {
19015
+ return typeof value === "string" ? value : void 0;
19016
+ };
19017
+ const normalizeFramePr = (value) => {
19018
+ if (!value || typeof value !== "object") return void 0;
19019
+ const record = value;
19020
+ if (record.attributes && typeof record.attributes === "object") {
19021
+ return record.attributes;
18893
19022
  }
18894
- }
18895
- if (framePr?.xAlign) {
18896
- const xAlign = framePr.xAlign.toLowerCase();
19023
+ return record;
19024
+ };
19025
+ const extractFramePrFromElements = (paragraphProperties) => {
19026
+ if (!paragraphProperties || typeof paragraphProperties !== "object") return void 0;
19027
+ const pPr = paragraphProperties;
19028
+ if (!Array.isArray(pPr.elements)) return void 0;
19029
+ const framePrElement = pPr.elements.find((el) => el.name === "w:framePr");
19030
+ if (framePrElement?.attributes && typeof framePrElement.attributes === "object") {
19031
+ return framePrElement.attributes;
19032
+ }
19033
+ return void 0;
19034
+ };
19035
+ const framePr = normalizeFramePr(attrs.framePr) ?? normalizeFramePr(attrs.paragraphProperties?.framePr) ?? extractFramePrFromElements(attrs.paragraphProperties);
19036
+ if (framePr) {
19037
+ const rawXAlign = asString(framePr["w:xAlign"] ?? framePr.xAlign);
19038
+ const xAlign = typeof rawXAlign === "string" ? rawXAlign.toLowerCase() : void 0;
18897
19039
  if (xAlign === "left" || xAlign === "right" || xAlign === "center") {
18898
19040
  paragraphAttrs.floatAlignment = xAlign;
18899
19041
  }
18900
- if (framePr.dropCap != null) {
18901
- paragraphAttrs.dropCap = framePr.dropCap;
19042
+ const dropCap = framePr["w:dropCap"] ?? framePr.dropCap;
19043
+ if (dropCap != null && (typeof dropCap === "string" || typeof dropCap === "number" || typeof dropCap === "boolean")) {
19044
+ paragraphAttrs.dropCap = dropCap;
19045
+ }
19046
+ const frame = {};
19047
+ const wrap = asString(framePr["w:wrap"] ?? framePr.wrap);
19048
+ if (wrap) frame.wrap = wrap;
19049
+ if (xAlign) {
19050
+ frame.xAlign = xAlign;
19051
+ }
19052
+ const rawYAlign = asString(framePr["w:yAlign"] ?? framePr.yAlign);
19053
+ if (rawYAlign) {
19054
+ frame.yAlign = rawYAlign;
19055
+ }
19056
+ const hAnchor = asString(framePr["w:hAnchor"] ?? framePr.hAnchor);
19057
+ if (hAnchor) frame.hAnchor = hAnchor;
19058
+ const vAnchor = asString(framePr["w:vAnchor"] ?? framePr.vAnchor);
19059
+ if (vAnchor) frame.vAnchor = vAnchor;
19060
+ const xTwips = pickNumber(framePr["w:x"] ?? framePr.x);
19061
+ if (xTwips != null) frame.x = twipsToPx$1(xTwips);
19062
+ const yTwips = pickNumber(framePr["w:y"] ?? framePr.y);
19063
+ if (yTwips != null) frame.y = twipsToPx$1(yTwips);
19064
+ if (Object.keys(frame).length > 0) {
19065
+ paragraphAttrs.frame = frame;
18902
19066
  }
18903
19067
  }
18904
19068
  const numberingSource = attrs.numberingProperties ?? paragraphProps.numberingProperties ?? hydrated?.numberingProperties;
@@ -18943,13 +19107,6 @@ const computeParagraphAttrs = (para, styleContext, listCounterContext, converter
18943
19107
  enrichedNumberingProps.resolvedMarkerRpr = marker.run;
18944
19108
  }
18945
19109
  }
18946
- if (!enrichedNumberingProps.resolvedMarkerRpr) {
18947
- enrichedNumberingProps.resolvedMarkerRpr = {
18948
- fontFamily: "Times New Roman",
18949
- fontSize: 12,
18950
- color: "#000000"
18951
- };
18952
- }
18953
19110
  }
18954
19111
  const wordLayout = computeWordLayoutForParagraph(paragraphAttrs, enrichedNumberingProps, styleContext);
18955
19112
  if (wordLayout) {
@@ -19386,7 +19543,9 @@ function handleDocumentPartObjectNode(node, context) {
19386
19543
  styleContext,
19387
19544
  bookmarks,
19388
19545
  hyperlinkConfig,
19389
- converters
19546
+ converters,
19547
+ listCounterContext,
19548
+ trackedChangesConfig
19390
19549
  } = context;
19391
19550
  const docPartGallery = getDocPartGallery(node);
19392
19551
  const docPartObjectId = getDocPartObjectId(node);
@@ -19409,6 +19568,27 @@ function handleDocumentPartObjectNode(node, context) {
19409
19568
  { blocks, recordBlockKind },
19410
19569
  paragraphToFlowBlocks2
19411
19570
  );
19571
+ } else if (paragraphToFlowBlocks2) {
19572
+ for (const child of node.content) {
19573
+ if (child.type === "paragraph") {
19574
+ const childBlocks = paragraphToFlowBlocks2(
19575
+ child,
19576
+ nextBlockId,
19577
+ positions,
19578
+ defaultFont,
19579
+ defaultSize,
19580
+ styleContext,
19581
+ listCounterContext,
19582
+ trackedChangesConfig,
19583
+ bookmarks,
19584
+ hyperlinkConfig
19585
+ );
19586
+ for (const block of childBlocks) {
19587
+ blocks.push(block);
19588
+ recordBlockKind(block.kind);
19589
+ }
19590
+ }
19591
+ }
19412
19592
  }
19413
19593
  }
19414
19594
  const extractValue = (value) => {
@@ -19569,6 +19749,55 @@ const extractRunStyleId = (runProperties) => {
19569
19749
  }
19570
19750
  return null;
19571
19751
  };
19752
+ const DEFAULT_IMAGE_DIMENSION_PX = 100;
19753
+ function isInlineImage(node) {
19754
+ const attrs = node.attrs ?? {};
19755
+ const wrap = attrs.wrap;
19756
+ const rawWrapType = wrap?.type;
19757
+ if (rawWrapType === "Inline") return true;
19758
+ if (rawWrapType && rawWrapType !== "Inline") return false;
19759
+ if (attrs.inline === true) return true;
19760
+ if (attrs.display === "inline") return true;
19761
+ return false;
19762
+ }
19763
+ function imageNodeToRun(node, positions, activeSdt) {
19764
+ const attrs = node.attrs ?? {};
19765
+ const src = typeof attrs.src === "string" ? attrs.src : "";
19766
+ if (!src) {
19767
+ return null;
19768
+ }
19769
+ const size = attrs.size ?? {};
19770
+ const width = typeof size.width === "number" && Number.isFinite(size.width) && size.width > 0 ? size.width : DEFAULT_IMAGE_DIMENSION_PX;
19771
+ const height = typeof size.height === "number" && Number.isFinite(size.height) && size.height > 0 ? size.height : DEFAULT_IMAGE_DIMENSION_PX;
19772
+ const wrap = isPlainObject$2(attrs.wrap) ? attrs.wrap : {};
19773
+ const wrapAttrs = isPlainObject$2(wrap.attrs) ? wrap.attrs : {};
19774
+ const run = {
19775
+ kind: "image",
19776
+ src,
19777
+ width,
19778
+ height
19779
+ };
19780
+ if (typeof attrs.alt === "string") run.alt = attrs.alt;
19781
+ if (typeof attrs.title === "string") run.title = attrs.title;
19782
+ const distTop = pickNumber(wrapAttrs.distTop ?? wrapAttrs.distT);
19783
+ if (distTop != null) run.distTop = distTop;
19784
+ const distBottom = pickNumber(wrapAttrs.distBottom ?? wrapAttrs.distB);
19785
+ if (distBottom != null) run.distBottom = distBottom;
19786
+ const distLeft = pickNumber(wrapAttrs.distLeft ?? wrapAttrs.distL);
19787
+ if (distLeft != null) run.distLeft = distLeft;
19788
+ const distRight = pickNumber(wrapAttrs.distRight ?? wrapAttrs.distR);
19789
+ if (distRight != null) run.distRight = distRight;
19790
+ run.verticalAlign = "bottom";
19791
+ const pos = positions.get(node);
19792
+ if (pos) {
19793
+ run.pmStart = pos.start;
19794
+ run.pmEnd = pos.end;
19795
+ }
19796
+ if (activeSdt) {
19797
+ run.sdt = activeSdt;
19798
+ }
19799
+ return run;
19800
+ }
19572
19801
  const isTextRun$1 = (run) => run.kind !== "tab";
19573
19802
  const dataAttrsCompatible = (a, b) => {
19574
19803
  const aAttrs = a.dataAttrs;
@@ -19609,6 +19838,62 @@ function mergeAdjacentRuns(runs) {
19609
19838
  merged.push(current);
19610
19839
  return merged;
19611
19840
  }
19841
+ const extractFirstTextRunFont = (para) => {
19842
+ if (!para.content || !Array.isArray(para.content) || para.content.length === 0) {
19843
+ return void 0;
19844
+ }
19845
+ const extractFontFromMarks = (marks) => {
19846
+ if (!marks || !Array.isArray(marks)) return void 0;
19847
+ const result = {};
19848
+ for (const mark of marks) {
19849
+ if (!mark || typeof mark !== "object") continue;
19850
+ if (mark.type === "textStyle" && mark.attrs) {
19851
+ const attrs = mark.attrs;
19852
+ if (attrs.fontSize != null) {
19853
+ const fontSizeStr = String(attrs.fontSize);
19854
+ const size = parseFloat(fontSizeStr);
19855
+ if (Number.isFinite(size)) {
19856
+ if (fontSizeStr.endsWith("pt")) {
19857
+ result.fontSizePx = ptToPx(size);
19858
+ } else {
19859
+ result.fontSizePx = size;
19860
+ }
19861
+ }
19862
+ }
19863
+ if (typeof attrs.fontFamily === "string") {
19864
+ result.fontFamily = attrs.fontFamily;
19865
+ }
19866
+ }
19867
+ }
19868
+ return Object.keys(result).length > 0 ? result : void 0;
19869
+ };
19870
+ const findFirstTextFont = (nodes) => {
19871
+ for (const node of nodes) {
19872
+ if (!node) continue;
19873
+ if (node.type === "text") {
19874
+ const font2 = extractFontFromMarks(node.marks);
19875
+ if (font2) return font2;
19876
+ }
19877
+ if (node.type === "run" && Array.isArray(node.content)) {
19878
+ const runFont = extractFontFromMarks(node.marks);
19879
+ const childFont = findFirstTextFont(node.content);
19880
+ if (runFont || childFont) {
19881
+ return {
19882
+ fontSizePx: childFont?.fontSizePx ?? runFont?.fontSizePx,
19883
+ fontFamily: childFont?.fontFamily ?? runFont?.fontFamily
19884
+ };
19885
+ }
19886
+ }
19887
+ if (Array.isArray(node.content)) {
19888
+ const font2 = findFirstTextFont(node.content);
19889
+ if (font2) return font2;
19890
+ }
19891
+ }
19892
+ return void 0;
19893
+ };
19894
+ const font = findFirstTextFont(para.content);
19895
+ return font;
19896
+ };
19612
19897
  const applyBaseRunDefaults = (run, defaults, fallbackFont, fallbackSize) => {
19613
19898
  if (!run) return;
19614
19899
  if (defaults.fontFamily && run.fontFamily === fallbackFont) {
@@ -19684,6 +19969,22 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
19684
19969
  }
19685
19970
  paragraphAttrs.spacing = spacing;
19686
19971
  }
19972
+ if (paragraphAttrs?.numberingProperties && paragraphAttrs?.wordLayout) {
19973
+ const firstRunFont = extractFirstTextRunFont(para);
19974
+ if (firstRunFont) {
19975
+ const wordLayout = paragraphAttrs.wordLayout;
19976
+ const marker = wordLayout.marker;
19977
+ if (marker?.run) {
19978
+ const markerRun = marker.run;
19979
+ if (firstRunFont.fontSizePx != null && Number.isFinite(firstRunFont.fontSizePx)) {
19980
+ markerRun.fontSize = firstRunFont.fontSizePx;
19981
+ }
19982
+ if (firstRunFont.fontFamily) {
19983
+ markerRun.fontFamily = firstRunFont.fontFamily;
19984
+ }
19985
+ }
19986
+ }
19987
+ }
19687
19988
  const linkedStyleResolver = createLinkedStyleResolver(converterContext?.linkedStyles);
19688
19989
  const blocks = [];
19689
19990
  if (hasPageBreakBefore(para)) {
@@ -19900,6 +20201,13 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
19900
20201
  return;
19901
20202
  }
19902
20203
  if (node.type === "image") {
20204
+ if (isInlineImage(node)) {
20205
+ const imageRun = imageNodeToRun(node, positions, activeSdt);
20206
+ if (imageRun) {
20207
+ currentRuns.push(imageRun);
20208
+ }
20209
+ return;
20210
+ }
19903
20211
  flushParagraph();
19904
20212
  const mergedMarks = [...node.marks ?? [], ...inheritedMarks ?? []];
19905
20213
  const trackedMeta = trackedChanges?.enabled ? collectTrackedChangeFromMarks(mergedMarks) : void 0;
@@ -19999,7 +20307,9 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
19999
20307
  return;
20000
20308
  }
20001
20309
  };
20002
- para.content.forEach((child) => visitNode(child, [], void 0, null));
20310
+ para.content.forEach((child) => {
20311
+ visitNode(child, [], void 0, null);
20312
+ });
20003
20313
  flushParagraph();
20004
20314
  const hasParagraphBlock = blocks.some((block) => block.kind === "paragraph");
20005
20315
  if (!hasParagraphBlock) {
@@ -20167,7 +20477,7 @@ const normalizeWrap$2 = (value) => {
20167
20477
  return void 0;
20168
20478
  }
20169
20479
  const type = normalizeWrapType$1(value.type);
20170
- if (!type || type === "Inline") {
20480
+ if (!type) {
20171
20481
  return void 0;
20172
20482
  }
20173
20483
  const wrap = { type };
@@ -20392,6 +20702,8 @@ const buildDrawingBlock = (rawAttrs, nextBlockId, positions, node, geometry, dra
20392
20702
  attrsWithPm.pmStart = pos.start;
20393
20703
  attrsWithPm.pmEnd = pos.end;
20394
20704
  }
20705
+ const zIndexFromRelativeHeight = normalizeZIndex(rawAttrs.originalAttributes);
20706
+ const finalZIndex = zIndexFromRelativeHeight ?? coerceNumber(rawAttrs.zIndex);
20395
20707
  return {
20396
20708
  kind: "drawing",
20397
20709
  id: nextBlockId("drawing"),
@@ -20400,15 +20712,19 @@ const buildDrawingBlock = (rawAttrs, nextBlockId, positions, node, geometry, dra
20400
20712
  margin: toBoxSpacing$1(rawAttrs.marginOffset) ?? toBoxSpacing$1(rawAttrs.margin),
20401
20713
  anchor: baseAnchor,
20402
20714
  wrap: normalizedWrap,
20403
- zIndex: coerceNumber(rawAttrs.zIndex),
20715
+ zIndex: finalZIndex,
20404
20716
  drawingContentId: typeof rawAttrs.drawingContentId === "string" ? rawAttrs.drawingContentId : void 0,
20405
20717
  drawingContent: toDrawingContentSnapshot(rawAttrs.drawingContent),
20406
20718
  attrs: attrsWithPm,
20407
20719
  geometry,
20408
20720
  shapeKind: typeof rawAttrs.kind === "string" ? rawAttrs.kind : void 0,
20409
- fillColor: typeof rawAttrs.fillColor === "string" ? rawAttrs.fillColor : void 0,
20410
- strokeColor: typeof rawAttrs.strokeColor === "string" ? rawAttrs.strokeColor : void 0,
20721
+ fillColor: normalizeFillColor(rawAttrs.fillColor),
20722
+ strokeColor: normalizeStrokeColor(rawAttrs.strokeColor),
20411
20723
  strokeWidth: coerceNumber(rawAttrs.strokeWidth),
20724
+ textContent: normalizeTextContent(rawAttrs.textContent),
20725
+ textAlign: typeof rawAttrs.textAlign === "string" ? rawAttrs.textAlign : void 0,
20726
+ textVerticalAlign: normalizeTextVerticalAlign(rawAttrs.textVerticalAlign),
20727
+ textInsets: normalizeTextInsets(rawAttrs.textInsets),
20412
20728
  ...extraProps
20413
20729
  };
20414
20730
  };
@@ -20585,26 +20901,32 @@ const parseTableCell = (args) => {
20585
20901
  if (!isTableCellNode(cellNode) || !Array.isArray(cellNode.content)) {
20586
20902
  return null;
20587
20903
  }
20588
- const paraNode = cellNode.content.find((c) => c.type === "paragraph");
20589
- if (!paraNode) {
20904
+ const blocks = [];
20905
+ for (const childNode of cellNode.content) {
20906
+ if (childNode.type === "paragraph") {
20907
+ const paragraphBlocks = context.paragraphToFlowBlocks(
20908
+ childNode,
20909
+ context.nextBlockId,
20910
+ context.positions,
20911
+ context.defaultFont,
20912
+ context.defaultSize,
20913
+ context.styleContext,
20914
+ void 0,
20915
+ context.trackedChanges,
20916
+ context.bookmarks,
20917
+ context.hyperlinkConfig,
20918
+ context.themeColors,
20919
+ context.converterContext
20920
+ );
20921
+ const paragraph = paragraphBlocks.find((b) => b.kind === "paragraph");
20922
+ if (paragraph) {
20923
+ blocks.push(paragraph);
20924
+ }
20925
+ }
20926
+ }
20927
+ if (blocks.length === 0) {
20590
20928
  return null;
20591
20929
  }
20592
- const paragraphBlocks = context.paragraphToFlowBlocks(
20593
- paraNode,
20594
- context.nextBlockId,
20595
- context.positions,
20596
- context.defaultFont,
20597
- context.defaultSize,
20598
- context.styleContext,
20599
- void 0,
20600
- context.trackedChanges,
20601
- context.bookmarks,
20602
- context.hyperlinkConfig,
20603
- context.themeColors,
20604
- context.converterContext
20605
- );
20606
- const paragraph = paragraphBlocks.find((b) => b.kind === "paragraph");
20607
- if (!paragraph) return null;
20608
20930
  const cellAttrs = {};
20609
20931
  const borders = extractCellBorders(cellNode.attrs ?? {});
20610
20932
  if (borders) cellAttrs.borders = borders;
@@ -20627,7 +20949,9 @@ const parseTableCell = (args) => {
20627
20949
  const colSpan = pickNumber(cellNode.attrs?.colspan);
20628
20950
  return {
20629
20951
  id: context.nextBlockId(`cell-${rowIndex}-${cellIndex}`),
20630
- paragraph,
20952
+ blocks,
20953
+ // Backward compatibility: set paragraph to first block if it's a paragraph
20954
+ paragraph: blocks[0]?.kind === "paragraph" ? blocks[0] : void 0,
20631
20955
  rowSpan: rowSpan ?? void 0,
20632
20956
  colSpan: colSpan ?? void 0,
20633
20957
  attrs: Object.keys(cellAttrs).length > 0 ? cellAttrs : void 0
@@ -20722,6 +21046,10 @@ function tableNodeToBlock$1(node, nextBlockId, positions, defaultFont, defaultSi
20722
21046
  } else if (hydratedTableStyle?.tableWidth) {
20723
21047
  tableAttrs.tableWidth = hydratedTableStyle.tableWidth;
20724
21048
  }
21049
+ const tableLayout = node.attrs?.tableLayout;
21050
+ if (tableLayout) {
21051
+ tableAttrs.tableLayout = tableLayout;
21052
+ }
20725
21053
  let columnWidths = void 0;
20726
21054
  const twipsToPixels2 = (twips) => {
20727
21055
  const PIXELS_PER_INCH2 = 96;
@@ -21074,7 +21402,7 @@ function getMeasurementContext() {
21074
21402
  return measurementCtx;
21075
21403
  }
21076
21404
  function getRunFontString(run) {
21077
- if (run.kind === "tab") {
21405
+ if (run.kind === "tab" || run.kind === "image") {
21078
21406
  return "normal normal 16px Arial";
21079
21407
  }
21080
21408
  const style = run.italic ? "italic" : "normal";
@@ -21093,6 +21421,10 @@ function sliceRunsForLine$1(block, line) {
21093
21421
  result.push(run);
21094
21422
  continue;
21095
21423
  }
21424
+ if (run.kind === "image") {
21425
+ result.push(run);
21426
+ continue;
21427
+ }
21096
21428
  const text = run.text ?? "";
21097
21429
  const isFirstRun = runIndex === line.fromRun;
21098
21430
  const isLastRun = runIndex === line.toRun;
@@ -21120,7 +21452,11 @@ function measureCharacterX(block, line, charOffset) {
21120
21452
  const runs2 = sliceRunsForLine$1(block, line);
21121
21453
  const charsInLine = Math.max(
21122
21454
  1,
21123
- runs2.reduce((sum, run) => sum + (isTabRun$1(run) ? TAB_CHAR_LENGTH : (run.text ?? "").length), 0)
21455
+ runs2.reduce((sum, run) => {
21456
+ if (isTabRun$1(run)) return sum + TAB_CHAR_LENGTH;
21457
+ if (run.kind === "image") return sum;
21458
+ return sum + (run.text ?? "").length;
21459
+ }, 0)
21124
21460
  );
21125
21461
  return charOffset / charsInLine * line.width;
21126
21462
  }
@@ -21139,7 +21475,7 @@ function measureCharacterX(block, line, charOffset) {
21139
21475
  currentCharOffset += runLength2;
21140
21476
  continue;
21141
21477
  }
21142
- const text = run.text ?? "";
21478
+ const text = run.kind === "image" ? "" : run.text ?? "";
21143
21479
  const runLength = text.length;
21144
21480
  if (currentCharOffset + runLength >= charOffset) {
21145
21481
  const offsetInRun = charOffset - currentCharOffset;
@@ -21162,7 +21498,11 @@ function findCharacterAtX(block, line, x, pmStart) {
21162
21498
  const runs2 = sliceRunsForLine$1(block, line);
21163
21499
  const charsInLine = Math.max(
21164
21500
  1,
21165
- runs2.reduce((sum, run) => sum + (isTabRun$1(run) ? TAB_CHAR_LENGTH : (run.text ?? "").length), 0)
21501
+ runs2.reduce((sum, run) => {
21502
+ if (isTabRun$1(run)) return sum + TAB_CHAR_LENGTH;
21503
+ if (run.kind === "image") return sum;
21504
+ return sum + (run.text ?? "").length;
21505
+ }, 0)
21166
21506
  );
21167
21507
  const ratio = Math.max(0, Math.min(1, x / line.width));
21168
21508
  const charOffset = Math.round(ratio * charsInLine);
@@ -21195,7 +21535,7 @@ function findCharacterAtX(block, line, x, pmStart) {
21195
21535
  currentCharOffset += TAB_CHAR_LENGTH;
21196
21536
  continue;
21197
21537
  }
21198
- const text = run.text ?? "";
21538
+ const text = run.kind === "image" ? "" : run.text ?? "";
21199
21539
  const runLength = text.length;
21200
21540
  if (runLength === 0) continue;
21201
21541
  ctx2.font = getRunFontString(run);
@@ -21232,7 +21572,7 @@ function findCharacterAtX(block, line, x, pmStart) {
21232
21572
  };
21233
21573
  }
21234
21574
  const computeLetterSpacingWidth = (run, precedingChars, runLength) => {
21235
- if (run.kind === "tab" || !run.letterSpacing) {
21575
+ if (isTabRun$1(run) || run.kind === "image" || !run.letterSpacing) {
21236
21576
  return 0;
21237
21577
  }
21238
21578
  const maxGaps = Math.max(runLength - 1, 0);
@@ -21598,14 +21938,8 @@ function computeAnchorX(anchor, columnIndex, columns, imageWidth, margins, pageW
21598
21938
  baseX = columnLeft;
21599
21939
  availableWidth = columns.width;
21600
21940
  }
21601
- if (alignH === "left") {
21602
- return baseX + offsetH;
21603
- } else if (alignH === "right") {
21604
- return baseX + availableWidth - imageWidth - offsetH;
21605
- } else if (alignH === "center") {
21606
- return baseX + (availableWidth - imageWidth) / 2 + offsetH;
21607
- }
21608
- return baseX;
21941
+ const result = alignH === "left" ? baseX + offsetH : alignH === "right" ? baseX + availableWidth - imageWidth - offsetH : alignH === "center" ? baseX + (availableWidth - imageWidth) / 2 + offsetH : baseX;
21942
+ return result;
21609
21943
  }
21610
21944
  function computeWrapMode(wrap, _anchor) {
21611
21945
  if (!wrap) return "none";
@@ -21834,6 +22168,18 @@ const computeLinePmRange$2 = (block, line) => {
21834
22168
  for (let runIndex = line.fromRun; runIndex <= line.toRun; runIndex += 1) {
21835
22169
  const run = block.runs[runIndex];
21836
22170
  if (!run) continue;
22171
+ if (run.kind === "image") {
22172
+ const runPmStart2 = run.pmStart ?? void 0;
22173
+ const runPmEnd = run.pmEnd ?? void 0;
22174
+ if (runPmStart2 == null || runPmEnd == null) {
22175
+ continue;
22176
+ }
22177
+ if (pmStart == null) {
22178
+ pmStart = runPmStart2;
22179
+ }
22180
+ pmEnd = runPmEnd;
22181
+ continue;
22182
+ }
21837
22183
  const runWithPm = run;
21838
22184
  const text = runWithPm.text ?? "";
21839
22185
  const runLength = text.length;
@@ -21867,16 +22213,34 @@ const extractBlockPmRange = (block) => {
21867
22213
  pmEnd: end2 ?? (start2 != null ? start2 + 1 : void 0)
21868
22214
  };
21869
22215
  };
21870
- const spacingDebugLog = (..._args) => {
22216
+ const anchorDebugLog = (...args) => {
22217
+ return;
21871
22218
  };
21872
22219
  function layoutParagraphBlock(ctx2, anchors) {
21873
22220
  const { block, measure, columnWidth, ensurePage, advanceColumn, columnX, floatManager } = ctx2;
21874
22221
  const remeasureParagraph2 = ctx2.remeasureParagraph;
22222
+ const frame = block.attrs?.frame;
21875
22223
  if (anchors?.anchoredDrawings?.length) {
21876
22224
  for (const entry of anchors.anchoredDrawings) {
21877
22225
  if (anchors.placedAnchoredIds.has(entry.block.id)) continue;
21878
22226
  const state = ensurePage();
21879
- const anchorY = state.cursorY;
22227
+ const baseAnchorY = state.cursorY;
22228
+ const firstLineHeight = measure.lines?.[0]?.lineHeight ?? 0;
22229
+ 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
+ });
21880
22244
  floatManager.registerDrawing(entry.block, entry.measure, anchorY, state.columnIndex, state.page.number);
21881
22245
  const anchorX = entry.block.anchor ? computeAnchorX(
21882
22246
  entry.block.anchor,
@@ -21888,6 +22252,30 @@ function layoutParagraphBlock(ctx2, anchors) {
21888
22252
  ) : columnX(state.columnIndex);
21889
22253
  const pmRange = extractBlockPmRange(entry.block);
21890
22254
  if (entry.block.kind === "image" && entry.measure.kind === "image") {
22255
+ const pageContentHeight = Math.max(0, state.contentBottom - state.topMargin);
22256
+ const relativeFrom = entry.block.anchor?.hRelativeFrom ?? "column";
22257
+ const marginLeft = anchors.pageMargins.left ?? 0;
22258
+ const marginRight = anchors.pageMargins.right ?? 0;
22259
+ let maxWidth;
22260
+ if (relativeFrom === "page") {
22261
+ maxWidth = anchors.columns.count === 1 ? anchors.pageWidth - marginLeft - marginRight : anchors.pageWidth;
22262
+ } else if (relativeFrom === "margin") {
22263
+ maxWidth = anchors.pageWidth - marginLeft - marginRight;
22264
+ } else {
22265
+ maxWidth = anchors.columns.width;
22266
+ }
22267
+ const aspectRatio = entry.measure.width > 0 && entry.measure.height > 0 ? entry.measure.width / entry.measure.height : 1;
22268
+ const minWidth = 20;
22269
+ const minHeight = minWidth / aspectRatio;
22270
+ const metadata = {
22271
+ originalWidth: entry.measure.width,
22272
+ originalHeight: entry.measure.height,
22273
+ maxWidth,
22274
+ maxHeight: pageContentHeight,
22275
+ aspectRatio,
22276
+ minWidth,
22277
+ minHeight
22278
+ };
21891
22279
  const fragment = {
21892
22280
  kind: "image",
21893
22281
  blockId: entry.block.id,
@@ -21896,7 +22284,8 @@ function layoutParagraphBlock(ctx2, anchors) {
21896
22284
  width: entry.measure.width,
21897
22285
  height: entry.measure.height,
21898
22286
  isAnchored: true,
21899
- zIndex: entry.block.anchor?.behindDoc ? 0 : 1
22287
+ zIndex: entry.block.anchor?.behindDoc ? 0 : 1,
22288
+ metadata
21900
22289
  };
21901
22290
  if (pmRange.pmStart != null) fragment.pmStart = pmRange.pmStart;
21902
22291
  if (pmRange.pmEnd != null) fragment.pmEnd = pmRange.pmEnd;
@@ -21932,13 +22321,41 @@ function layoutParagraphBlock(ctx2, anchors) {
21932
22321
  const spacingAfter = Math.max(0, Number(spacing.after ?? spacing.lineSpaceAfter ?? 0));
21933
22322
  let appliedSpacingBefore = spacingBefore === 0;
21934
22323
  let lastState = null;
21935
- {
21936
- spacingDebugLog("paragraph spacing attrs", {
22324
+ const isPositionedFrame = frame?.wrap === "none";
22325
+ if (isPositionedFrame) {
22326
+ let state = ensurePage();
22327
+ if (state.cursorY >= state.contentBottom) {
22328
+ state = advanceColumn(state);
22329
+ }
22330
+ const maxLineWidth = lines.reduce((max2, line) => Math.max(max2, line.width ?? 0), 0);
22331
+ const fragmentWidth = maxLineWidth || columnWidth;
22332
+ let x = columnX(state.columnIndex);
22333
+ if (frame.xAlign === "right") {
22334
+ x += columnWidth - fragmentWidth;
22335
+ } else if (frame.xAlign === "center") {
22336
+ x += (columnWidth - fragmentWidth) / 2;
22337
+ }
22338
+ if (typeof frame.x === "number" && Number.isFinite(frame.x)) {
22339
+ x += frame.x;
22340
+ }
22341
+ const yOffset = typeof frame.y === "number" && Number.isFinite(frame.y) ? frame.y : 0;
22342
+ const fragment = {
22343
+ kind: "para",
21937
22344
  blockId: block.id,
21938
- spacingAttrs: spacing,
21939
- spacingBefore,
21940
- spacingAfter
21941
- });
22345
+ fromLine: 0,
22346
+ toLine: lines.length,
22347
+ x,
22348
+ y: state.cursorY + yOffset,
22349
+ width: fragmentWidth,
22350
+ ...computeFragmentPmRange(block, lines, 0, lines.length)
22351
+ };
22352
+ if (measure.marker) {
22353
+ fragment.markerWidth = measure.marker.markerWidth;
22354
+ }
22355
+ state.page.fragments.push(fragment);
22356
+ state.trailingSpacing = 0;
22357
+ state.lastParagraphStyleId = styleId;
22358
+ return;
21942
22359
  }
21943
22360
  let didRemeasureForFloats = false;
21944
22361
  while (fromLine < lines.length) {
@@ -21957,54 +22374,13 @@ function layoutParagraphBlock(ctx2, anchors) {
21957
22374
  while (!appliedSpacingBefore) {
21958
22375
  const prevTrailing = state.trailingSpacing ?? 0;
21959
22376
  const neededSpacingBefore = Math.max(spacingBefore - prevTrailing, 0);
21960
- {
21961
- spacingDebugLog("spacingBefore pending", {
21962
- blockId: block.id,
21963
- cursorY: state.cursorY,
21964
- contentBottom: state.contentBottom,
21965
- spacingBefore,
21966
- prevTrailing,
21967
- neededSpacingBefore,
21968
- column: state.columnIndex,
21969
- page: state.page.number
21970
- });
21971
- }
21972
22377
  if (state.cursorY + neededSpacingBefore > state.contentBottom) {
21973
- {
21974
- spacingDebugLog("spacingBefore triggers column advance", {
21975
- blockId: block.id,
21976
- cursorY: state.cursorY,
21977
- spacingBefore,
21978
- neededSpacingBefore,
21979
- prevTrailing,
21980
- column: state.columnIndex,
21981
- page: state.page.number
21982
- });
21983
- }
21984
22378
  state = advanceColumn(state);
21985
22379
  if (state.trailingSpacing == null) state.trailingSpacing = 0;
21986
22380
  continue;
21987
22381
  }
21988
22382
  if (neededSpacingBefore > 0) {
21989
22383
  state.cursorY += neededSpacingBefore;
21990
- {
21991
- spacingDebugLog("spacingBefore applied", {
21992
- blockId: block.id,
21993
- added: neededSpacingBefore,
21994
- prevTrailing,
21995
- newCursorY: state.cursorY,
21996
- column: state.columnIndex,
21997
- page: state.page.number
21998
- });
21999
- }
22000
- } else if (prevTrailing > 0) {
22001
- spacingDebugLog("spacingBefore collapsed by trailing spacing", {
22002
- blockId: block.id,
22003
- prevTrailing,
22004
- spacingBefore,
22005
- column: state.columnIndex,
22006
- page: state.page.number
22007
- });
22008
22384
  }
22009
22385
  state.trailingSpacing = 0;
22010
22386
  appliedSpacingBefore = true;
@@ -22056,18 +22432,18 @@ function layoutParagraphBlock(ctx2, anchors) {
22056
22432
  width: effectiveColumnWidth,
22057
22433
  ...computeFragmentPmRange(block, lines, fromLine, slice2.toLine)
22058
22434
  };
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
+ });
22059
22444
  if (measure.marker && fromLine === 0) {
22060
22445
  fragment.markerWidth = measure.marker.markerWidth;
22061
22446
  }
22062
- if (process$1.env.NODE_ENV !== "production" && measure.marker && fromLine === 0) {
22063
- console.log("[layoutParagraphBlock] fragment marker info", {
22064
- blockId: block.id,
22065
- indent: block.attrs?.indent,
22066
- markerWidth: fragment.markerWidth,
22067
- textWidth: fragment.width,
22068
- marker: measure.marker
22069
- });
22070
- }
22071
22447
  if (fromLine > 0) fragment.continuesFromPrev = true;
22072
22448
  if (slice2.toLine < lines.length) fragment.continuesOnNext = true;
22073
22449
  const floatAlignment = block.attrs?.floatAlignment;
@@ -22094,30 +22470,12 @@ function layoutParagraphBlock(ctx2, anchors) {
22094
22470
  let targetState = lastState;
22095
22471
  let appliedSpacingAfter = spacingAfter;
22096
22472
  if (targetState.cursorY + spacingAfter > targetState.contentBottom) {
22097
- {
22098
- spacingDebugLog("spacingAfter triggers column advance", {
22099
- blockId: block.id,
22100
- cursorY: targetState.cursorY,
22101
- spacingAfter,
22102
- column: targetState.columnIndex,
22103
- page: targetState.page.number
22104
- });
22105
- }
22106
22473
  targetState = advanceColumn(targetState);
22107
22474
  appliedSpacingAfter = 0;
22108
22475
  } else {
22109
22476
  targetState.cursorY += spacingAfter;
22110
22477
  }
22111
22478
  targetState.trailingSpacing = appliedSpacingAfter;
22112
- {
22113
- spacingDebugLog("spacingAfter applied", {
22114
- blockId: block.id,
22115
- appliedSpacingAfter,
22116
- newCursorY: targetState.cursorY,
22117
- column: targetState.columnIndex,
22118
- page: targetState.page.number
22119
- });
22120
- }
22121
22479
  } else {
22122
22480
  lastState.trailingSpacing = 0;
22123
22481
  }
@@ -22159,6 +22517,18 @@ function layoutImageBlock({
22159
22517
  state = advanceColumn(state);
22160
22518
  }
22161
22519
  const pmRange = extractBlockPmRange(block);
22520
+ const aspectRatio = measure.width > 0 && measure.height > 0 ? measure.width / measure.height : 1;
22521
+ const minWidth = 20;
22522
+ const minHeight = minWidth / aspectRatio;
22523
+ const metadata = {
22524
+ originalWidth: measure.width,
22525
+ originalHeight: measure.height,
22526
+ maxWidth,
22527
+ maxHeight: pageContentHeight,
22528
+ aspectRatio,
22529
+ minWidth,
22530
+ minHeight
22531
+ };
22162
22532
  const fragment = {
22163
22533
  kind: "image",
22164
22534
  blockId: block.id,
@@ -22167,7 +22537,8 @@ function layoutImageBlock({
22167
22537
  width,
22168
22538
  height,
22169
22539
  pmStart: pmRange.pmStart,
22170
- pmEnd: pmRange.pmEnd
22540
+ pmEnd: pmRange.pmEnd,
22541
+ metadata
22171
22542
  };
22172
22543
  state.page.fragments.push(fragment);
22173
22544
  state.cursorY += requiredHeight;
@@ -22398,6 +22769,175 @@ function createPaginator(opts) {
22398
22769
  getActiveColumnsForState
22399
22770
  };
22400
22771
  }
22772
+ function toUpperRoman(num) {
22773
+ if (num < 1 || num > 3999) {
22774
+ return String(num);
22775
+ }
22776
+ const values = [1e3, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
22777
+ const numerals = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"];
22778
+ let result = "";
22779
+ let remaining = num;
22780
+ for (let i = 0; i < values.length; i++) {
22781
+ while (remaining >= values[i]) {
22782
+ result += numerals[i];
22783
+ remaining -= values[i];
22784
+ }
22785
+ }
22786
+ return result;
22787
+ }
22788
+ function toLowerRoman(num) {
22789
+ return toUpperRoman(num).toLowerCase();
22790
+ }
22791
+ function toUpperLetter(num) {
22792
+ if (num < 1) {
22793
+ return "A";
22794
+ }
22795
+ let result = "";
22796
+ let n = num;
22797
+ while (n > 0) {
22798
+ const remainder = (n - 1) % 26;
22799
+ result = String.fromCharCode(65 + remainder) + result;
22800
+ n = Math.floor((n - 1) / 26);
22801
+ }
22802
+ return result;
22803
+ }
22804
+ function toLowerLetter(num) {
22805
+ return toUpperLetter(num).toLowerCase();
22806
+ }
22807
+ function formatPageNumber$1(pageNumber, format) {
22808
+ const num = Math.max(1, pageNumber);
22809
+ switch (format) {
22810
+ case "decimal":
22811
+ return String(num);
22812
+ case "upperRoman":
22813
+ return toUpperRoman(num);
22814
+ case "lowerRoman":
22815
+ return toLowerRoman(num);
22816
+ case "upperLetter":
22817
+ return toUpperLetter(num);
22818
+ case "lowerLetter":
22819
+ return toLowerLetter(num);
22820
+ default:
22821
+ return String(num);
22822
+ }
22823
+ }
22824
+ function computeDisplayPageNumber(pages, sections) {
22825
+ const result = [];
22826
+ if (pages.length === 0) {
22827
+ return result;
22828
+ }
22829
+ const sectionMap = /* @__PURE__ */ new Map();
22830
+ for (const section of sections) {
22831
+ sectionMap.set(section.sectionIndex, section);
22832
+ }
22833
+ let runningCounter = 1;
22834
+ let currentSectionIndex = -1;
22835
+ for (let i = 0; i < pages.length; i++) {
22836
+ const page = pages[i];
22837
+ const pageSectionIndex = 0;
22838
+ if (pageSectionIndex !== currentSectionIndex) {
22839
+ const sectionMetadata2 = sectionMap.get(pageSectionIndex);
22840
+ if (sectionMetadata2?.numbering?.start !== void 0) {
22841
+ runningCounter = sectionMetadata2.numbering.start;
22842
+ }
22843
+ currentSectionIndex = pageSectionIndex;
22844
+ }
22845
+ const sectionMetadata = sectionMap.get(pageSectionIndex);
22846
+ const format = sectionMetadata?.numbering?.format ?? "decimal";
22847
+ const displayNumber = runningCounter;
22848
+ const displayText = formatPageNumber$1(displayNumber, format);
22849
+ result.push({
22850
+ physicalPage: page.number,
22851
+ displayNumber,
22852
+ displayText,
22853
+ sectionIndex: pageSectionIndex
22854
+ });
22855
+ runningCounter++;
22856
+ }
22857
+ return result;
22858
+ }
22859
+ function resolvePageNumberTokens(layout, blocks, measures, numberingCtx) {
22860
+ const affectedBlockIds = /* @__PURE__ */ new Set();
22861
+ const updatedBlocks = /* @__PURE__ */ new Map();
22862
+ if (!layout?.pages || layout.pages.length === 0) {
22863
+ return { affectedBlockIds, updatedBlocks };
22864
+ }
22865
+ if (!numberingCtx || !numberingCtx.displayPages || numberingCtx.totalPages < 1) {
22866
+ console.warn("[resolvePageTokens] Invalid numbering context - skipping resolution");
22867
+ return { affectedBlockIds, updatedBlocks };
22868
+ }
22869
+ const blockMap = /* @__PURE__ */ new Map();
22870
+ const blockHasTokensFlags = /* @__PURE__ */ new Map();
22871
+ for (let i = 0; i < blocks.length; i++) {
22872
+ const block = blocks[i];
22873
+ blockMap.set(block.id, block);
22874
+ if (block.kind === "paragraph" && block.attrs && "hasPageTokens" in block.attrs) {
22875
+ blockHasTokensFlags.set(block.id, Boolean(block.attrs.hasPageTokens));
22876
+ }
22877
+ }
22878
+ const totalPagesStr = String(numberingCtx.totalPages);
22879
+ const processedBlocks = /* @__PURE__ */ new Set();
22880
+ for (const page of layout.pages) {
22881
+ const pageIndex = page.number - 1;
22882
+ const displayPageInfo = numberingCtx.displayPages[pageIndex];
22883
+ if (!displayPageInfo) {
22884
+ console.warn(`[resolvePageTokens] No display page info for page ${page.number} - skipping`);
22885
+ continue;
22886
+ }
22887
+ const displayPageText = displayPageInfo.displayText;
22888
+ for (const fragment of page.fragments) {
22889
+ if (fragment.kind !== "para") continue;
22890
+ const blockId = fragment.blockId;
22891
+ if (processedBlocks.has(blockId)) continue;
22892
+ const hasTokensFlag = blockHasTokensFlags.get(blockId);
22893
+ if (hasTokensFlag === false) continue;
22894
+ const block = blockMap.get(blockId);
22895
+ if (!block || block.kind !== "paragraph") continue;
22896
+ const wasModified = hasPageTokens$1(block);
22897
+ if (!wasModified) {
22898
+ processedBlocks.add(blockId);
22899
+ continue;
22900
+ }
22901
+ const clonedBlock = cloneBlockWithResolvedTokens(block, displayPageText, totalPagesStr);
22902
+ updatedBlocks.set(blockId, clonedBlock);
22903
+ affectedBlockIds.add(blockId);
22904
+ processedBlocks.add(blockId);
22905
+ }
22906
+ }
22907
+ return { affectedBlockIds, updatedBlocks };
22908
+ }
22909
+ function hasPageTokens$1(block) {
22910
+ for (const run of block.runs) {
22911
+ if ("token" in run && (run.token === "pageNumber" || run.token === "totalPageCount")) {
22912
+ return true;
22913
+ }
22914
+ }
22915
+ return false;
22916
+ }
22917
+ function cloneBlockWithResolvedTokens(block, displayPageText, totalPagesStr) {
22918
+ const clonedRuns = block.runs.map((run) => {
22919
+ if ("token" in run && run.token) {
22920
+ if (run.token === "pageNumber") {
22921
+ const { token: _token, ...runWithoutToken } = run;
22922
+ return {
22923
+ ...runWithoutToken,
22924
+ text: displayPageText
22925
+ };
22926
+ } else if (run.token === "totalPageCount") {
22927
+ const { token: _token, ...runWithoutToken } = run;
22928
+ return {
22929
+ ...runWithoutToken,
22930
+ text: totalPagesStr
22931
+ };
22932
+ }
22933
+ }
22934
+ return run;
22935
+ });
22936
+ return {
22937
+ ...block,
22938
+ runs: clonedRuns
22939
+ };
22940
+ }
22401
22941
  const DEFAULT_PAGE_SIZE$2 = { w: 612, h: 792 };
22402
22942
  const DEFAULT_MARGINS$2 = { top: 72, right: 72, bottom: 72, left: 72 };
22403
22943
  const COLUMN_EPSILON = 1e-4;
@@ -22854,7 +23394,7 @@ function layoutDocument(blocks, measures, options = {}) {
22854
23394
  throw new Error(`layoutDocument: expected paragraph measure for block ${block.id}`);
22855
23395
  }
22856
23396
  const paraBlock = block;
22857
- const isEmpty2 = !paraBlock.runs || paraBlock.runs.length === 0 || paraBlock.runs.length === 1 && (!paraBlock.runs[0].text || paraBlock.runs[0].text === "");
23397
+ const isEmpty2 = !paraBlock.runs || paraBlock.runs.length === 0 || paraBlock.runs.length === 1 && (!paraBlock.runs[0].kind || paraBlock.runs[0].kind === "text") && (!paraBlock.runs[0].text || paraBlock.runs[0].text === "");
22858
23398
  if (isEmpty2) {
22859
23399
  const prevBlock = index2 > 0 ? blocks[index2 - 1] : null;
22860
23400
  const nextBlock = index2 < blocks.length - 1 ? blocks[index2 + 1] : null;
@@ -22980,7 +23520,21 @@ function layoutHeaderFooter(blocks, measures, constraints) {
22980
23520
  if (!Number.isFinite(height) || height <= 0) {
22981
23521
  throw new Error("layoutHeaderFooter: height must be positive");
22982
23522
  }
22983
- const layout = layoutDocument(blocks, measures, {
23523
+ const marginLeft = constraints.margins?.left ?? 0;
23524
+ const transformedBlocks = marginLeft > 0 ? blocks.map((block) => {
23525
+ const hasPageRelativeAnchor = (block.kind === "image" || block.kind === "drawing") && block.anchor?.hRelativeFrom === "page" && block.anchor.offsetH != null;
23526
+ if (hasPageRelativeAnchor) {
23527
+ return {
23528
+ ...block,
23529
+ anchor: {
23530
+ ...block.anchor,
23531
+ offsetH: block.anchor.offsetH - marginLeft
23532
+ }
23533
+ };
23534
+ }
23535
+ return block;
23536
+ }) : blocks;
23537
+ const layout = layoutDocument(transformedBlocks, measures, {
22984
23538
  pageSize: { w: width, h: height },
22985
23539
  margins: { top: 0, right: 0, bottom: 0, left: 0 }
22986
23540
  });
@@ -23074,7 +23628,7 @@ const hashRuns = (block) => {
23074
23628
  const trackedMode = block.attrs && "trackedChangesMode" in block.attrs && block.attrs.trackedChangesMode || "review";
23075
23629
  const trackedEnabled = resolveTrackedChangesEnabled(block.attrs, true);
23076
23630
  const runsHash = block.runs.map((run) => {
23077
- const text = normalizeText(run.text ?? "");
23631
+ const text = normalizeText(run.kind === "image" ? "" : run.text ?? "");
23078
23632
  const bold = "bold" in run ? run.bold : false;
23079
23633
  const italic = "italic" in run ? run.italic : false;
23080
23634
  const color = "color" in run ? run.color : void 0;
@@ -23253,6 +23807,358 @@ class MeasureCache {
23253
23807
  return `${block.id}@${safeWidth}x${safeHeight}:${hash2}`;
23254
23808
  }
23255
23809
  }
23810
+ function resolveHeaderFooterTokens(blocks, pageNumber, totalPages) {
23811
+ if (!blocks || blocks.length === 0) {
23812
+ return;
23813
+ }
23814
+ if (!Number.isFinite(pageNumber) || pageNumber < 1) {
23815
+ console.warn("[resolveHeaderFooterTokens] Invalid pageNumber:", pageNumber, "- using 1 as fallback");
23816
+ pageNumber = 1;
23817
+ }
23818
+ if (!Number.isFinite(totalPages) || totalPages < 1) {
23819
+ console.warn("[resolveHeaderFooterTokens] Invalid totalPages:", totalPages, "- using 1 as fallback");
23820
+ totalPages = 1;
23821
+ }
23822
+ const pageNumberStr = String(pageNumber);
23823
+ const totalPagesStr = String(totalPages);
23824
+ for (const block of blocks) {
23825
+ if (block.kind !== "paragraph") continue;
23826
+ const paraBlock = block;
23827
+ for (const run of paraBlock.runs) {
23828
+ if ("token" in run && run.token) {
23829
+ if (run.token === "pageNumber") {
23830
+ run.text = pageNumberStr;
23831
+ } else if (run.token === "totalPageCount") {
23832
+ run.text = totalPagesStr;
23833
+ }
23834
+ }
23835
+ }
23836
+ }
23837
+ }
23838
+ function cloneHeaderFooterBlocks(blocks) {
23839
+ if (!blocks || blocks.length === 0) {
23840
+ return [];
23841
+ }
23842
+ return blocks.map((block) => {
23843
+ if (block.kind === "paragraph") {
23844
+ const paraBlock = block;
23845
+ return {
23846
+ ...paraBlock,
23847
+ runs: paraBlock.runs.map((run) => ({ ...run })),
23848
+ attrs: paraBlock.attrs ? { ...paraBlock.attrs } : void 0
23849
+ };
23850
+ }
23851
+ return { ...block };
23852
+ });
23853
+ }
23854
+ function isEnabled(envVar, defaultValue) {
23855
+ if (typeof process$1 === "undefined" || typeof process$1.env === "undefined") {
23856
+ return defaultValue;
23857
+ }
23858
+ const value = process$1.env[envVar];
23859
+ if (value === "true" || value === "1") {
23860
+ return true;
23861
+ }
23862
+ if (value === "false" || value === "0") {
23863
+ return false;
23864
+ }
23865
+ return defaultValue;
23866
+ }
23867
+ const FeatureFlags = {
23868
+ /**
23869
+ * Enable section-aware numbering with restarts and format changes.
23870
+ * When disabled, falls back to simple 1-N sequential numbering.
23871
+ */
23872
+ NUMBERING_SECTION_AWARE: isEnabled("SD_NUMBERING_SECTION_AWARE", true),
23873
+ /**
23874
+ * Enable body page token resolution (PAGE/NUMPAGES in document content).
23875
+ * When disabled, tokens remain as placeholders in body content.
23876
+ */
23877
+ BODY_PAGE_TOKENS: isEnabled("SD_BODY_PAGE_TOKENS", true),
23878
+ /**
23879
+ * Enable header/footer page token resolution.
23880
+ * When disabled, headers/footers use painter-time token rendering fallback.
23881
+ */
23882
+ HEADER_FOOTER_PAGE_TOKENS: isEnabled("SD_HEADER_FOOTER_PAGE_TOKENS", true),
23883
+ /**
23884
+ * Enable digit bucketing for header/footer caching in large documents.
23885
+ * When disabled, creates per-page layouts for all documents (no bucketing).
23886
+ * Recommended to keep enabled for documents with 100+ pages.
23887
+ */
23888
+ HF_DIGIT_BUCKETING: isEnabled("SD_HF_DIGIT_BUCKETING", true),
23889
+ /**
23890
+ * Enable debug logging for page token resolution.
23891
+ * Logs token resolution details, affected blocks, and convergence iteration info.
23892
+ * Should be disabled in production (only enabled for debugging).
23893
+ */
23894
+ DEBUG_PAGE_TOKENS: isEnabled("SD_DEBUG_PAGE_TOKENS", false),
23895
+ /**
23896
+ * Enable debug logging for header/footer cache operations.
23897
+ * Logs cache hits, misses, invalidations, and bucket selection.
23898
+ * Should be disabled in production (only enabled for debugging).
23899
+ */
23900
+ DEBUG_HF_CACHE: isEnabled("SD_DEBUG_HF_CACHE", false)
23901
+ };
23902
+ const PageTokenLogger = {
23903
+ /**
23904
+ * Logs the start of token resolution.
23905
+ *
23906
+ * @param iteration - Current iteration number (0-based)
23907
+ * @param totalPages - Total number of pages in the document
23908
+ */
23909
+ logIterationStart(iteration, totalPages) {
23910
+ if (!FeatureFlags.DEBUG_PAGE_TOKENS) return;
23911
+ console.log(`[PageTokens] Iteration ${iteration}: Resolving tokens for ${totalPages} pages`);
23912
+ },
23913
+ /**
23914
+ * Logs affected blocks during token resolution.
23915
+ *
23916
+ * @param iteration - Current iteration number (0-based)
23917
+ * @param affectedBlockIds - Set of affected block IDs
23918
+ * @param blockSamples - Sample block IDs for debugging (first 5)
23919
+ */
23920
+ logAffectedBlocks(iteration, affectedBlockIds, blockSamples = []) {
23921
+ if (!FeatureFlags.DEBUG_PAGE_TOKENS) return;
23922
+ const count = affectedBlockIds.size;
23923
+ const samples = blockSamples.slice(0, 5).join(", ");
23924
+ console.log(
23925
+ `[PageTokens] Iteration ${iteration}: ${count} blocks affected`,
23926
+ samples ? `(samples: ${samples})` : ""
23927
+ );
23928
+ },
23929
+ /**
23930
+ * Logs convergence status.
23931
+ *
23932
+ * @param iteration - Final iteration number
23933
+ * @param converged - Whether convergence was achieved
23934
+ * @param totalTimeMs - Total time spent in token resolution
23935
+ */
23936
+ logConvergence(iteration, converged, totalTimeMs) {
23937
+ if (!FeatureFlags.DEBUG_PAGE_TOKENS) return;
23938
+ if (converged) {
23939
+ console.log(`[PageTokens] Converged after ${iteration} iterations in ${totalTimeMs.toFixed(2)}ms`);
23940
+ } else {
23941
+ console.warn(`[PageTokens] Did NOT converge after ${iteration} iterations (${totalTimeMs.toFixed(2)}ms)`);
23942
+ }
23943
+ },
23944
+ /**
23945
+ * Logs token resolution error.
23946
+ *
23947
+ * @param blockId - Block ID where error occurred
23948
+ * @param error - Error object
23949
+ */
23950
+ logError(blockId, error) {
23951
+ if (!FeatureFlags.DEBUG_PAGE_TOKENS) return;
23952
+ console.error(`[PageTokens] Error resolving tokens in block ${blockId}:`, error);
23953
+ },
23954
+ /**
23955
+ * Logs re-measurement details.
23956
+ *
23957
+ * @param blockCount - Number of blocks being re-measured
23958
+ * @param timeMs - Time spent re-measuring
23959
+ */
23960
+ logRemeasure(blockCount, timeMs) {
23961
+ if (!FeatureFlags.DEBUG_PAGE_TOKENS) return;
23962
+ console.log(`[PageTokens] Re-measured ${blockCount} blocks in ${timeMs.toFixed(2)}ms`);
23963
+ }
23964
+ };
23965
+ const HeaderFooterCacheLogger = {
23966
+ /**
23967
+ * Logs cache hit for a header/footer variant.
23968
+ *
23969
+ * @param variantType - Variant type (default, first, even, odd)
23970
+ * @param pageNumber - Page number being cached
23971
+ * @param bucket - Digit bucket (d1, d2, d3, d4) or 'exact'
23972
+ */
23973
+ logCacheHit(variantType, pageNumber, bucket) {
23974
+ if (!FeatureFlags.DEBUG_HF_CACHE) return;
23975
+ console.log(`[HF Cache] HIT: variant=${variantType}, page=${pageNumber}, bucket=${bucket}`);
23976
+ },
23977
+ /**
23978
+ * Logs cache miss for a header/footer variant.
23979
+ *
23980
+ * @param variantType - Variant type (default, first, even, odd)
23981
+ * @param pageNumber - Page number being cached
23982
+ * @param bucket - Digit bucket (d1, d2, d3, d4) or 'exact'
23983
+ */
23984
+ logCacheMiss(variantType, pageNumber, bucket) {
23985
+ if (!FeatureFlags.DEBUG_HF_CACHE) return;
23986
+ console.log(`[HF Cache] MISS: variant=${variantType}, page=${pageNumber}, bucket=${bucket}`);
23987
+ },
23988
+ /**
23989
+ * Logs cache invalidation.
23990
+ *
23991
+ * @param reason - Reason for invalidation
23992
+ * @param affectedBlockIds - Block IDs being invalidated
23993
+ */
23994
+ logInvalidation(reason, affectedBlockIds) {
23995
+ if (!FeatureFlags.DEBUG_HF_CACHE) return;
23996
+ console.log(`[HF Cache] INVALIDATE: reason=${reason}, blocks=${affectedBlockIds.length}`);
23997
+ },
23998
+ /**
23999
+ * Logs cache statistics.
24000
+ *
24001
+ * @param stats - Cache statistics object
24002
+ */
24003
+ logStats(stats) {
24004
+ if (!FeatureFlags.DEBUG_HF_CACHE) return;
24005
+ const hitRate = stats.hits + stats.misses > 0 ? (stats.hits / (stats.hits + stats.misses) * 100).toFixed(1) : "0.0";
24006
+ console.log(
24007
+ `[HF Cache] Stats: hits=${stats.hits}, misses=${stats.misses}, hitRate=${hitRate}%, size=${stats.size}, evictions=${stats.evictions}`
24008
+ );
24009
+ },
24010
+ /**
24011
+ * Logs bucketing decision for large documents.
24012
+ *
24013
+ * @param totalPages - Total number of pages
24014
+ * @param useBucketing - Whether bucketing is being used
24015
+ * @param buckets - Buckets needed (if bucketing enabled)
24016
+ */
24017
+ logBucketingDecision(totalPages, useBucketing, buckets) {
24018
+ if (!FeatureFlags.DEBUG_HF_CACHE) return;
24019
+ if (useBucketing && buckets) {
24020
+ console.log(`[HF Cache] Bucketing enabled: ${totalPages} pages, buckets=${buckets.join(", ")}`);
24021
+ } else {
24022
+ console.log(`[HF Cache] Bucketing disabled: ${totalPages} pages (per-page layouts)`);
24023
+ }
24024
+ }
24025
+ };
24026
+ class MetricsCollector {
24027
+ constructor() {
24028
+ this.pageTokenMetrics = null;
24029
+ this.headerFooterCacheMetrics = null;
24030
+ this.layoutMetrics = null;
24031
+ }
24032
+ /**
24033
+ * Records page token resolution metrics.
24034
+ *
24035
+ * @param metrics - Page token metrics
24036
+ */
24037
+ recordPageTokenMetrics(metrics) {
24038
+ this.pageTokenMetrics = { ...metrics };
24039
+ this.checkPageTokenRollbackTriggers(metrics);
24040
+ }
24041
+ /**
24042
+ * Records header/footer cache metrics.
24043
+ *
24044
+ * @param stats - Cache statistics
24045
+ */
24046
+ recordHeaderFooterCacheMetrics(stats) {
24047
+ const hitRate = stats.hits + stats.misses > 0 ? stats.hits / (stats.hits + stats.misses) * 100 : 0;
24048
+ this.headerFooterCacheMetrics = {
24049
+ hits: stats.hits,
24050
+ misses: stats.misses,
24051
+ hitRate,
24052
+ cacheSize: stats.size,
24053
+ memoryEstimate: stats.memorySizeEstimate,
24054
+ evictions: stats.evictions
24055
+ };
24056
+ this.checkCacheRollbackTriggers(this.headerFooterCacheMetrics);
24057
+ }
24058
+ /**
24059
+ * Records overall layout metrics.
24060
+ *
24061
+ * @param metrics - Layout metrics
24062
+ */
24063
+ recordLayoutMetrics(metrics) {
24064
+ this.layoutMetrics = { ...metrics };
24065
+ }
24066
+ /**
24067
+ * Gets all collected metrics.
24068
+ *
24069
+ * @returns Object with all metrics or null if not collected
24070
+ */
24071
+ getMetrics() {
24072
+ return {
24073
+ pageTokens: this.pageTokenMetrics,
24074
+ headerFooterCache: this.headerFooterCacheMetrics,
24075
+ layout: this.layoutMetrics
24076
+ };
24077
+ }
24078
+ /**
24079
+ * Resets all collected metrics.
24080
+ */
24081
+ reset() {
24082
+ this.pageTokenMetrics = null;
24083
+ this.headerFooterCacheMetrics = null;
24084
+ this.layoutMetrics = null;
24085
+ }
24086
+ /**
24087
+ * Checks for page token rollback triggers and logs warnings.
24088
+ *
24089
+ * Rollback triggers (from plan):
24090
+ * - Convergence > 2 iterations
24091
+ * - Token resolution time > 100ms per layout run
24092
+ *
24093
+ * @param metrics - Page token metrics
24094
+ */
24095
+ checkPageTokenRollbackTriggers(metrics) {
24096
+ if (metrics.iterations > 2 && !metrics.converged) {
24097
+ console.warn(
24098
+ `[Rollback Trigger] Page token resolution did not converge after ${metrics.iterations} iterations. Consider disabling SD_BODY_PAGE_TOKENS if this persists.`
24099
+ );
24100
+ }
24101
+ if (metrics.totalTimeMs > 100 && metrics.iterations > 0) {
24102
+ console.warn(
24103
+ `[Rollback Trigger] Page token resolution took ${metrics.totalTimeMs.toFixed(2)}ms (>100ms threshold). Consider disabling SD_BODY_PAGE_TOKENS if performance is unacceptable.`
24104
+ );
24105
+ }
24106
+ }
24107
+ /**
24108
+ * Checks for cache rollback triggers and logs warnings.
24109
+ *
24110
+ * Rollback triggers (from plan):
24111
+ * - Cache thrash (hit rate below 30%)
24112
+ * - Excessive memory usage (>1MB per 100 pages)
24113
+ *
24114
+ * @param metrics - Header/footer cache metrics
24115
+ */
24116
+ checkCacheRollbackTriggers(metrics) {
24117
+ const MIN_HIT_RATE = 30;
24118
+ const MAX_MEMORY_PER_100_PAGES = 1e6;
24119
+ if (metrics.hits + metrics.misses > 10 && metrics.hitRate < MIN_HIT_RATE) {
24120
+ console.warn(
24121
+ `[Rollback Trigger] Header/footer cache hit rate is low (${metrics.hitRate.toFixed(1)}% < ${MIN_HIT_RATE}%). Consider disabling SD_HF_DIGIT_BUCKETING if cache thrashing persists.`
24122
+ );
24123
+ }
24124
+ if (metrics.memoryEstimate > MAX_MEMORY_PER_100_PAGES) {
24125
+ console.warn(
24126
+ `[Rollback Trigger] Header/footer cache memory usage is high (${(metrics.memoryEstimate / 1e6).toFixed(2)}MB). Monitor for excessive growth.`
24127
+ );
24128
+ }
24129
+ }
24130
+ }
24131
+ const globalMetrics = new MetricsCollector();
24132
+ const MIN_PAGES_FOR_BUCKETING = 100;
24133
+ function getBucketForPageNumber(pageNumber) {
24134
+ if (pageNumber < 10) return "d1";
24135
+ if (pageNumber < 100) return "d2";
24136
+ if (pageNumber < 1e3) return "d3";
24137
+ return "d4";
24138
+ }
24139
+ function getBucketRepresentative(bucket) {
24140
+ switch (bucket) {
24141
+ case "d1":
24142
+ return 5;
24143
+ case "d2":
24144
+ return 50;
24145
+ case "d3":
24146
+ return 500;
24147
+ case "d4":
24148
+ return 5e3;
24149
+ }
24150
+ }
24151
+ function hasPageTokens(blocks) {
24152
+ for (const block of blocks) {
24153
+ if (block.kind !== "paragraph") continue;
24154
+ for (const run of block.runs) {
24155
+ if ("token" in run && (run.token === "pageNumber" || run.token === "totalPageCount")) {
24156
+ return true;
24157
+ }
24158
+ }
24159
+ }
24160
+ return false;
24161
+ }
23256
24162
  class HeaderFooterLayoutCache {
23257
24163
  constructor() {
23258
24164
  this.cache = new MeasureCache();
@@ -23277,15 +24183,81 @@ class HeaderFooterLayoutCache {
23277
24183
  invalidate(blockIds) {
23278
24184
  this.cache.invalidate(blockIds);
23279
24185
  }
24186
+ /**
24187
+ * Gets cache statistics for monitoring and debugging.
24188
+ *
24189
+ * @returns Cache statistics object
24190
+ */
24191
+ getStats() {
24192
+ return this.cache.getStats();
24193
+ }
23280
24194
  }
23281
24195
  const sharedHeaderFooterCache = new HeaderFooterLayoutCache();
23282
- async function layoutHeaderFooterWithCache(sections, constraints, measureBlock2, cache2 = sharedHeaderFooterCache) {
24196
+ async function layoutHeaderFooterWithCache(sections, constraints, measureBlock2, cache2 = sharedHeaderFooterCache, totalPages, pageResolver) {
23283
24197
  const result = {};
24198
+ if (!pageResolver) {
24199
+ const numPages = totalPages ?? 1;
24200
+ for (const [type, blocks] of Object.entries(sections)) {
24201
+ if (!blocks || blocks.length === 0) continue;
24202
+ const clonedBlocks = cloneHeaderFooterBlocks(blocks);
24203
+ resolveHeaderFooterTokens(clonedBlocks, 1, numPages);
24204
+ const measures = await cache2.measureBlocks(clonedBlocks, constraints, measureBlock2);
24205
+ const layout = layoutHeaderFooter(clonedBlocks, measures, constraints);
24206
+ result[type] = { blocks: clonedBlocks, measures, layout };
24207
+ }
24208
+ return result;
24209
+ }
24210
+ const { totalPages: docTotalPages } = pageResolver(1);
24211
+ const useBucketing = FeatureFlags.HF_DIGIT_BUCKETING && docTotalPages >= MIN_PAGES_FOR_BUCKETING;
23284
24212
  for (const [type, blocks] of Object.entries(sections)) {
23285
24213
  if (!blocks || blocks.length === 0) continue;
23286
- const measures = await cache2.measureBlocks(blocks, constraints, measureBlock2);
23287
- const layout = layoutHeaderFooter(blocks, measures, constraints);
23288
- result[type] = { blocks, measures, layout };
24214
+ if (!hasPageTokens(blocks)) {
24215
+ const measures = await cache2.measureBlocks(blocks, constraints, measureBlock2);
24216
+ const layout = layoutHeaderFooter(blocks, measures, constraints);
24217
+ result[type] = { blocks, measures, layout };
24218
+ continue;
24219
+ }
24220
+ let pagesToLayout;
24221
+ if (!useBucketing) {
24222
+ pagesToLayout = Array.from({ length: docTotalPages }, (_, i) => i + 1);
24223
+ HeaderFooterCacheLogger.logBucketingDecision(docTotalPages, false);
24224
+ } else {
24225
+ const bucketsNeeded = /* @__PURE__ */ new Set();
24226
+ for (let p = 1; p <= docTotalPages; p++) {
24227
+ bucketsNeeded.add(getBucketForPageNumber(p));
24228
+ }
24229
+ pagesToLayout = Array.from(bucketsNeeded).map((bucket) => getBucketRepresentative(bucket));
24230
+ HeaderFooterCacheLogger.logBucketingDecision(docTotalPages, true, Array.from(bucketsNeeded));
24231
+ }
24232
+ const pages = [];
24233
+ for (const pageNum of pagesToLayout) {
24234
+ const clonedBlocks = cloneHeaderFooterBlocks(blocks);
24235
+ const { displayText, totalPages: totalPagesForPage } = pageResolver(pageNum);
24236
+ resolveHeaderFooterTokens(clonedBlocks, parseInt(displayText, 10) || pageNum, totalPagesForPage);
24237
+ const measures = await cache2.measureBlocks(clonedBlocks, constraints, measureBlock2);
24238
+ const pageLayout = layoutHeaderFooter(clonedBlocks, measures, constraints);
24239
+ pages.push({
24240
+ number: pageNum,
24241
+ blocks: clonedBlocks,
24242
+ measures,
24243
+ fragments: pageLayout.pages[0]?.fragments ?? []
24244
+ });
24245
+ }
24246
+ const firstPageLayout = pages[0] ? layoutHeaderFooter(pages[0].blocks, pages[0].measures, constraints) : { height: 0 };
24247
+ const finalLayout = {
24248
+ height: firstPageLayout.height,
24249
+ minY: firstPageLayout.minY,
24250
+ maxY: firstPageLayout.maxY,
24251
+ pages: pages.map((p) => ({
24252
+ number: p.number,
24253
+ fragments: p.fragments
24254
+ }))
24255
+ };
24256
+ result[type] = {
24257
+ blocks: pages[0]?.blocks ?? blocks,
24258
+ measures: pages[0]?.measures ?? [],
24259
+ layout: finalLayout
24260
+ };
23289
24261
  }
23290
24262
  return result;
23291
24263
  }
@@ -23310,7 +24282,7 @@ function fontString(run) {
23310
24282
  return `${italic}${bold}${size}px ${family}`.trim();
23311
24283
  }
23312
24284
  function runText(run) {
23313
- return run.text ?? "";
24285
+ return run.kind === "image" ? "" : run.text ?? "";
23314
24286
  }
23315
24287
  function measureRunSliceWidth(run, fromChar, toChar) {
23316
24288
  const context = getCtx();
@@ -23472,7 +24444,7 @@ const paragraphBlocksEqual = (a, b) => {
23472
24444
  for (let i = 0; i < a.runs.length; i += 1) {
23473
24445
  const runA = a.runs[i];
23474
24446
  const runB = b.runs[i];
23475
- if (runA.text !== 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)) {
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)) {
23476
24448
  return false;
23477
24449
  }
23478
24450
  }
@@ -23583,8 +24555,195 @@ const jsonEqual = (a, b) => {
23583
24555
  return false;
23584
24556
  }
23585
24557
  };
24558
+ function computeHeaderFooterContentHash(blocks) {
24559
+ if (!blocks || blocks.length === 0) {
24560
+ return "";
24561
+ }
24562
+ const parts = [];
24563
+ for (const block of blocks) {
24564
+ parts.push(block.id);
24565
+ if (block.kind === "paragraph") {
24566
+ for (const run of block.runs) {
24567
+ if (run.kind !== "image") {
24568
+ parts.push(run.text ?? "");
24569
+ }
24570
+ if ("bold" in run && run.bold) parts.push("b");
24571
+ if ("italic" in run && run.italic) parts.push("i");
24572
+ if ("token" in run && run.token) parts.push(`token:${run.token}`);
24573
+ }
24574
+ }
24575
+ }
24576
+ return parts.join("|");
24577
+ }
24578
+ function computeSectionMetadataHash(sections) {
24579
+ if (!sections || sections.length === 0) {
24580
+ return "";
24581
+ }
24582
+ const parts = [];
24583
+ for (const section of sections) {
24584
+ parts.push(`section:${section.sectionIndex}`);
24585
+ if (section.numbering) {
24586
+ const num = section.numbering;
24587
+ parts.push(`num:${num.format ?? "decimal"}:${num.start ?? 1}`);
24588
+ }
24589
+ if (section.headerRefs) {
24590
+ const refs = section.headerRefs;
24591
+ if (refs.default) parts.push(`hdr-def:${refs.default}`);
24592
+ if (refs.first) parts.push(`hdr-first:${refs.first}`);
24593
+ if (refs.even) parts.push(`hdr-even:${refs.even}`);
24594
+ if (refs.odd) parts.push(`hdr-odd:${refs.odd}`);
24595
+ }
24596
+ if (section.footerRefs) {
24597
+ const refs = section.footerRefs;
24598
+ if (refs.default) parts.push(`ftr-def:${refs.default}`);
24599
+ if (refs.first) parts.push(`ftr-first:${refs.first}`);
24600
+ if (refs.even) parts.push(`ftr-even:${refs.even}`);
24601
+ if (refs.odd) parts.push(`ftr-odd:${refs.odd}`);
24602
+ }
24603
+ }
24604
+ return parts.join("|");
24605
+ }
24606
+ function computeConstraintsHash(constraints) {
24607
+ const { width, height, pageWidth, margins } = constraints;
24608
+ const parts = [`w:${width}`, `h:${height}`];
24609
+ if (pageWidth !== void 0) {
24610
+ parts.push(`pw:${pageWidth}`);
24611
+ }
24612
+ if (margins) {
24613
+ parts.push(`ml:${margins.left}`, `mr:${margins.right}`);
24614
+ }
24615
+ return parts.join("|");
24616
+ }
24617
+ class HeaderFooterCacheState {
24618
+ constructor() {
24619
+ this.contentHashes = /* @__PURE__ */ new Map();
24620
+ this.constraintsHash = "";
24621
+ this.sectionMetadataHash = "";
24622
+ }
24623
+ /**
24624
+ * Checks if header/footer content has changed for a variant.
24625
+ *
24626
+ * @param variantKey - Unique key for the variant (e.g., 'header-default')
24627
+ * @param blocks - Current blocks for the variant
24628
+ * @returns True if content has changed
24629
+ */
24630
+ hasContentChanged(variantKey, blocks) {
24631
+ const currentHash = computeHeaderFooterContentHash(blocks);
24632
+ const previousHash = this.contentHashes.get(variantKey);
24633
+ if (previousHash === void 0) {
24634
+ this.contentHashes.set(variantKey, currentHash);
24635
+ return false;
24636
+ }
24637
+ const changed = currentHash !== previousHash;
24638
+ if (changed) {
24639
+ this.contentHashes.set(variantKey, currentHash);
24640
+ }
24641
+ return changed;
24642
+ }
24643
+ /**
24644
+ * Checks if constraints have changed.
24645
+ *
24646
+ * @param constraints - Current constraints
24647
+ * @returns True if constraints have changed
24648
+ */
24649
+ hasConstraintsChanged(constraints) {
24650
+ const currentHash = computeConstraintsHash(constraints);
24651
+ if (this.constraintsHash === "") {
24652
+ this.constraintsHash = currentHash;
24653
+ return false;
24654
+ }
24655
+ const changed = currentHash !== this.constraintsHash;
24656
+ if (changed) {
24657
+ this.constraintsHash = currentHash;
24658
+ }
24659
+ return changed;
24660
+ }
24661
+ /**
24662
+ * Checks if section metadata has changed.
24663
+ *
24664
+ * @param sections - Current section metadata
24665
+ * @returns True if metadata has changed
24666
+ */
24667
+ hasSectionMetadataChanged(sections) {
24668
+ const currentHash = computeSectionMetadataHash(sections);
24669
+ if (this.sectionMetadataHash === "") {
24670
+ this.sectionMetadataHash = currentHash;
24671
+ return false;
24672
+ }
24673
+ const changed = currentHash !== this.sectionMetadataHash;
24674
+ if (changed) {
24675
+ this.sectionMetadataHash = currentHash;
24676
+ }
24677
+ return changed;
24678
+ }
24679
+ /**
24680
+ * Resets all cached state.
24681
+ * Called when performing a full cache clear.
24682
+ */
24683
+ reset() {
24684
+ this.contentHashes.clear();
24685
+ this.constraintsHash = "";
24686
+ this.sectionMetadataHash = "";
24687
+ }
24688
+ }
24689
+ function invalidateHeaderFooterCache(cache2, cacheState, headerBlocks, footerBlocks, constraints, sections) {
24690
+ const invalidationReasons = [];
24691
+ const affectedBlockIds = [];
24692
+ if (constraints && cacheState.hasConstraintsChanged(constraints)) {
24693
+ invalidationReasons.push("constraints changed");
24694
+ if (headerBlocks) {
24695
+ Object.values(headerBlocks).forEach((blocks) => {
24696
+ if (blocks) affectedBlockIds.push(...blocks.map((b) => b.id));
24697
+ });
24698
+ }
24699
+ if (footerBlocks) {
24700
+ Object.values(footerBlocks).forEach((blocks) => {
24701
+ if (blocks) affectedBlockIds.push(...blocks.map((b) => b.id));
24702
+ });
24703
+ }
24704
+ }
24705
+ if (sections && cacheState.hasSectionMetadataChanged(sections)) {
24706
+ invalidationReasons.push("section metadata changed");
24707
+ if (headerBlocks) {
24708
+ Object.values(headerBlocks).forEach((blocks) => {
24709
+ if (blocks) affectedBlockIds.push(...blocks.map((b) => b.id));
24710
+ });
24711
+ }
24712
+ if (footerBlocks) {
24713
+ Object.values(footerBlocks).forEach((blocks) => {
24714
+ if (blocks) affectedBlockIds.push(...blocks.map((b) => b.id));
24715
+ });
24716
+ }
24717
+ }
24718
+ if (headerBlocks) {
24719
+ for (const [variant, blocks] of Object.entries(headerBlocks)) {
24720
+ if (!blocks) continue;
24721
+ const variantKey = `header-${variant}`;
24722
+ if (cacheState.hasContentChanged(variantKey, blocks)) {
24723
+ invalidationReasons.push(`header ${variant} content changed`);
24724
+ affectedBlockIds.push(...blocks.map((b) => b.id));
24725
+ }
24726
+ }
24727
+ }
24728
+ if (footerBlocks) {
24729
+ for (const [variant, blocks] of Object.entries(footerBlocks)) {
24730
+ if (!blocks) continue;
24731
+ const variantKey = `footer-${variant}`;
24732
+ if (cacheState.hasContentChanged(variantKey, blocks)) {
24733
+ invalidationReasons.push(`footer ${variant} content changed`);
24734
+ affectedBlockIds.push(...blocks.map((b) => b.id));
24735
+ }
24736
+ }
24737
+ }
24738
+ if (affectedBlockIds.length > 0) {
24739
+ const uniqueBlockIds = Array.from(new Set(affectedBlockIds));
24740
+ cache2.invalidate(uniqueBlockIds);
24741
+ HeaderFooterCacheLogger.logInvalidation(invalidationReasons.join(", "), uniqueBlockIds);
24742
+ }
24743
+ }
23586
24744
  const measureCache = new MeasureCache();
23587
24745
  const headerMeasureCache = new HeaderFooterLayoutCache();
24746
+ const headerFooterCacheState = new HeaderFooterCacheState();
23588
24747
  const layoutDebugEnabled = typeof process$1 !== "undefined" && typeof process$1.env !== "undefined" && Boolean(process$1.env.SD_DEBUG_LAYOUT);
23589
24748
  const perfLog = (...args) => {
23590
24749
  if (!layoutDebugEnabled) return;
@@ -23626,22 +24785,121 @@ async function incrementalLayout(previousBlocks, _previousLayout, nextBlocks, op
23626
24785
  `[Perf] 4.1 Measure all blocks: ${(measureEnd - measureStart).toFixed(2)}ms (${cacheMisses} measured, ${cacheHits} cached)`
23627
24786
  );
23628
24787
  const layoutStart = performance.now();
23629
- const layout = layoutDocument(nextBlocks, measures, {
24788
+ let layout = layoutDocument(nextBlocks, measures, {
23630
24789
  ...options,
23631
24790
  remeasureParagraph: (block, maxWidth) => remeasureParagraph(block, maxWidth)
23632
24791
  });
23633
24792
  const layoutEnd = performance.now();
23634
24793
  perfLog(`[Perf] 4.2 Layout document (pagination): ${(layoutEnd - layoutStart).toFixed(2)}ms`);
24794
+ const maxIterations = 3;
24795
+ let currentBlocks = nextBlocks;
24796
+ let currentMeasures = measures;
24797
+ let iteration = 0;
24798
+ const pageTokenStart = performance.now();
24799
+ let totalAffectedBlocks = 0;
24800
+ let totalRemeasureTime = 0;
24801
+ let totalRelayoutTime = 0;
24802
+ let converged = true;
24803
+ if (FeatureFlags.BODY_PAGE_TOKENS) {
24804
+ while (iteration < maxIterations) {
24805
+ const sections = options.sectionMetadata ?? [];
24806
+ const numberingCtx = buildNumberingContext(layout, sections);
24807
+ PageTokenLogger.logIterationStart(iteration, layout.pages.length);
24808
+ const tokenResult = resolvePageNumberTokens(layout, currentBlocks, currentMeasures, numberingCtx);
24809
+ if (tokenResult.affectedBlockIds.size === 0) {
24810
+ perfLog(`[Perf] 4.3 Page token resolution converged after ${iteration} iterations`);
24811
+ break;
24812
+ }
24813
+ perfLog(`[Perf] 4.3.${iteration + 1} Page tokens resolved: ${tokenResult.affectedBlockIds.size} blocks affected`);
24814
+ const blockSamples = Array.from(tokenResult.affectedBlockIds).slice(0, 5);
24815
+ PageTokenLogger.logAffectedBlocks(iteration, tokenResult.affectedBlockIds, blockSamples);
24816
+ totalAffectedBlocks += tokenResult.affectedBlockIds.size;
24817
+ currentBlocks = currentBlocks.map((block) => tokenResult.updatedBlocks.get(block.id) ?? block);
24818
+ measureCache.invalidate(Array.from(tokenResult.affectedBlockIds));
24819
+ const remeasureStart = performance.now();
24820
+ currentMeasures = await remeasureAffectedBlocks(
24821
+ currentBlocks,
24822
+ currentMeasures,
24823
+ tokenResult.affectedBlockIds,
24824
+ constraints,
24825
+ measureBlock2
24826
+ );
24827
+ const remeasureEnd = performance.now();
24828
+ const remeasureTime = remeasureEnd - remeasureStart;
24829
+ totalRemeasureTime += remeasureTime;
24830
+ perfLog(`[Perf] 4.3.${iteration + 1}.1 Re-measure: ${remeasureTime.toFixed(2)}ms`);
24831
+ PageTokenLogger.logRemeasure(tokenResult.affectedBlockIds.size, remeasureTime);
24832
+ const oldPageCount = layout.pages.length;
24833
+ const relayoutStart = performance.now();
24834
+ layout = layoutDocument(currentBlocks, currentMeasures, {
24835
+ ...options,
24836
+ remeasureParagraph: (block, maxWidth) => remeasureParagraph(block, maxWidth)
24837
+ });
24838
+ const relayoutEnd = performance.now();
24839
+ const relayoutTime = relayoutEnd - relayoutStart;
24840
+ totalRelayoutTime += relayoutTime;
24841
+ perfLog(`[Perf] 4.3.${iteration + 1}.2 Re-layout: ${relayoutTime.toFixed(2)}ms`);
24842
+ const newPageCount = layout.pages.length;
24843
+ if (newPageCount === oldPageCount && iteration > 0) {
24844
+ perfLog(`[Perf] 4.3 Page count stable at ${newPageCount} - breaking convergence loop`);
24845
+ break;
24846
+ }
24847
+ iteration++;
24848
+ }
24849
+ if (iteration >= maxIterations) {
24850
+ converged = false;
24851
+ console.warn(
24852
+ `[incrementalLayout] Page token resolution did not converge after ${maxIterations} iterations - stopping`
24853
+ );
24854
+ }
24855
+ }
24856
+ const pageTokenEnd = performance.now();
24857
+ const totalTokenTime = pageTokenEnd - pageTokenStart;
24858
+ if (iteration > 0) {
24859
+ perfLog(`[Perf] 4.3 Total page token resolution time: ${totalTokenTime.toFixed(2)}ms`);
24860
+ PageTokenLogger.logConvergence(iteration, converged, totalTokenTime);
24861
+ globalMetrics.recordPageTokenMetrics({
24862
+ totalTimeMs: totalTokenTime,
24863
+ iterations: iteration,
24864
+ affectedBlocks: totalAffectedBlocks,
24865
+ remeasureTimeMs: totalRemeasureTime,
24866
+ relayoutTimeMs: totalRelayoutTime,
24867
+ converged
24868
+ });
24869
+ }
23635
24870
  let headers;
23636
24871
  let footers;
23637
24872
  if (headerFooter?.constraints && (headerFooter.headerBlocks || headerFooter.footerBlocks)) {
24873
+ const hfStart = performance.now();
23638
24874
  const measureFn = headerFooter.measure ?? measureBlock2;
24875
+ invalidateHeaderFooterCache(
24876
+ headerMeasureCache,
24877
+ headerFooterCacheState,
24878
+ headerFooter.headerBlocks,
24879
+ headerFooter.footerBlocks,
24880
+ headerFooter.constraints,
24881
+ options.sectionMetadata
24882
+ );
24883
+ const sections = options.sectionMetadata ?? [];
24884
+ const numberingCtx = buildNumberingContext(layout, sections);
24885
+ const pageResolver = FeatureFlags.HEADER_FOOTER_PAGE_TOKENS ? (pageNumber) => {
24886
+ const pageIndex = pageNumber - 1;
24887
+ const displayInfo = numberingCtx.displayPages[pageIndex];
24888
+ return {
24889
+ displayText: displayInfo?.displayText ?? String(pageNumber),
24890
+ totalPages: numberingCtx.totalPages
24891
+ };
24892
+ } : void 0;
23639
24893
  if (headerFooter.headerBlocks) {
23640
24894
  const headerLayouts = await layoutHeaderFooterWithCache(
23641
24895
  headerFooter.headerBlocks,
23642
24896
  headerFooter.constraints,
23643
24897
  measureFn,
23644
- headerMeasureCache
24898
+ headerMeasureCache,
24899
+ FeatureFlags.HEADER_FOOTER_PAGE_TOKENS ? void 0 : numberingCtx.totalPages,
24900
+ // Fallback for backward compat
24901
+ pageResolver
24902
+ // Use page resolver for section-aware numbering
23645
24903
  );
23646
24904
  headers = serializeHeaderFooterResults("header", headerLayouts);
23647
24905
  }
@@ -23650,14 +24908,23 @@ async function incrementalLayout(previousBlocks, _previousLayout, nextBlocks, op
23650
24908
  headerFooter.footerBlocks,
23651
24909
  headerFooter.constraints,
23652
24910
  measureFn,
23653
- headerMeasureCache
24911
+ headerMeasureCache,
24912
+ FeatureFlags.HEADER_FOOTER_PAGE_TOKENS ? void 0 : numberingCtx.totalPages,
24913
+ // Fallback for backward compat
24914
+ pageResolver
24915
+ // Use page resolver for section-aware numbering
23654
24916
  );
23655
24917
  footers = serializeHeaderFooterResults("footer", footerLayouts);
23656
24918
  }
24919
+ const hfEnd = performance.now();
24920
+ perfLog(`[Perf] 4.4 Header/footer layout: ${(hfEnd - hfStart).toFixed(2)}ms`);
24921
+ const cacheStats = headerMeasureCache.getStats();
24922
+ globalMetrics.recordHeaderFooterCacheMetrics(cacheStats);
24923
+ HeaderFooterCacheLogger.logStats(cacheStats);
23657
24924
  }
23658
24925
  return {
23659
24926
  layout,
23660
- measures,
24927
+ measures: currentMeasures,
23661
24928
  dirty,
23662
24929
  headers,
23663
24930
  footers
@@ -23701,6 +24968,31 @@ const serializeHeaderFooterResults = (kind, batch) => {
23701
24968
  });
23702
24969
  return results;
23703
24970
  };
24971
+ function buildNumberingContext(layout, sections) {
24972
+ const totalPages = layout.pages.length;
24973
+ const displayPages = computeDisplayPageNumber(layout.pages, sections);
24974
+ return {
24975
+ totalPages,
24976
+ displayPages
24977
+ };
24978
+ }
24979
+ async function remeasureAffectedBlocks(blocks, measures, affectedBlockIds, constraints, measureBlock2) {
24980
+ const updatedMeasures = [...measures];
24981
+ for (let i = 0; i < blocks.length; i++) {
24982
+ const block = blocks[i];
24983
+ if (!affectedBlockIds.has(block.id)) {
24984
+ continue;
24985
+ }
24986
+ try {
24987
+ const newMeasure = await measureBlock2(block, constraints);
24988
+ updatedMeasures[i] = newMeasure;
24989
+ measureCache.set(block, constraints.maxWidth, constraints.maxHeight, newMeasure);
24990
+ } catch (error) {
24991
+ console.warn(`[incrementalLayout] Failed to re-measure block ${block.id} after token resolution:`, error);
24992
+ }
24993
+ }
24994
+ return updatedMeasures;
24995
+ }
23704
24996
  const isAtomicFragment = (fragment) => {
23705
24997
  return fragment.kind === "drawing" || fragment.kind === "image";
23706
24998
  };
@@ -24040,7 +25332,7 @@ function computeLinePmRange$1(block, line) {
24040
25332
  for (let runIndex = line.fromRun; runIndex <= line.toRun; runIndex += 1) {
24041
25333
  const run = block.runs[runIndex];
24042
25334
  if (!run) continue;
24043
- const text = run.text ?? "";
25335
+ const text = run.kind === "image" ? "" : run.text ?? "";
24044
25336
  const runLength = text.length;
24045
25337
  const runPmStart = run.pmStart ?? null;
24046
25338
  const runPmEnd = run.pmEnd ?? (runPmStart != null ? runPmStart + runLength : null);
@@ -26221,6 +27513,104 @@ function k(t) {
26221
27513
  ${l}
26222
27514
  </svg>`;
26223
27515
  }
27516
+ function validateHexColor(color) {
27517
+ if (typeof color !== "string") return void 0;
27518
+ const trimmed = color.trim();
27519
+ if (!trimmed) return void 0;
27520
+ const withoutHash = trimmed.startsWith("#") ? trimmed.slice(1) : trimmed;
27521
+ const hexPattern = /^[0-9A-Fa-f]{3}$|^[0-9A-Fa-f]{6}$/;
27522
+ if (!hexPattern.test(withoutHash)) {
27523
+ return void 0;
27524
+ }
27525
+ return `#${withoutHash}`;
27526
+ }
27527
+ function addValidatedGradientStops(gradient, stops) {
27528
+ stops.forEach((stop) => {
27529
+ const position = typeof stop.position === "number" && Number.isFinite(stop.position) ? Math.max(0, Math.min(1, stop.position)) : 0;
27530
+ const validatedColor = validateHexColor(stop.color);
27531
+ if (!validatedColor) {
27532
+ return;
27533
+ }
27534
+ const alpha = typeof stop.alpha === "number" && Number.isFinite(stop.alpha) ? Math.max(0, Math.min(1, stop.alpha)) : 1;
27535
+ const stopElement = document.createElementNS("http://www.w3.org/2000/svg", "stop");
27536
+ stopElement.setAttribute("offset", `${position * 100}%`);
27537
+ stopElement.setAttribute("stop-color", validatedColor);
27538
+ if (alpha < 1) {
27539
+ stopElement.setAttribute("stop-opacity", alpha.toString());
27540
+ }
27541
+ gradient.appendChild(stopElement);
27542
+ });
27543
+ }
27544
+ function createGradient$1(gradientData, gradientId) {
27545
+ const { gradientType, stops, angle } = gradientData;
27546
+ if (!stops || stops.length === 0) {
27547
+ return null;
27548
+ }
27549
+ let gradient;
27550
+ if (gradientType === "linear") {
27551
+ gradient = document.createElementNS("http://www.w3.org/2000/svg", "linearGradient");
27552
+ gradient.setAttribute("id", gradientId);
27553
+ const radians = angle * Math.PI / 180;
27554
+ const x1 = 50 - 50 * Math.cos(radians);
27555
+ const y1 = 50 + 50 * Math.sin(radians);
27556
+ const x2 = 50 + 50 * Math.cos(radians);
27557
+ const y2 = 50 - 50 * Math.sin(radians);
27558
+ gradient.setAttribute("x1", `${x1}%`);
27559
+ gradient.setAttribute("y1", `${y1}%`);
27560
+ gradient.setAttribute("x2", `${x2}%`);
27561
+ gradient.setAttribute("y2", `${y2}%`);
27562
+ } else {
27563
+ gradient = document.createElementNS("http://www.w3.org/2000/svg", "radialGradient");
27564
+ gradient.setAttribute("id", gradientId);
27565
+ gradient.setAttribute("cx", "50%");
27566
+ gradient.setAttribute("cy", "50%");
27567
+ gradient.setAttribute("r", "50%");
27568
+ }
27569
+ addValidatedGradientStops(gradient, stops);
27570
+ return gradient;
27571
+ }
27572
+ function applyGradientToSVG$1(svg, gradientData) {
27573
+ try {
27574
+ const gradientId = generateGradientId("gradient");
27575
+ const gradient = createGradient$1(gradientData, gradientId);
27576
+ if (!gradient) {
27577
+ return;
27578
+ }
27579
+ let defs = svg.querySelector("defs");
27580
+ if (!defs) {
27581
+ defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
27582
+ svg.insertBefore(defs, svg.firstChild);
27583
+ }
27584
+ defs.appendChild(gradient);
27585
+ const filledElements = svg.querySelectorAll('[fill]:not([fill="none"])');
27586
+ filledElements.forEach((el) => {
27587
+ el.setAttribute("fill", `url(#${gradientId})`);
27588
+ });
27589
+ } catch (error) {
27590
+ console.error("Failed to apply gradient to SVG:", error);
27591
+ }
27592
+ }
27593
+ function applyAlphaToSVG$1(svg, alphaData) {
27594
+ try {
27595
+ const { color, alpha } = alphaData;
27596
+ const validatedColor = validateHexColor(color);
27597
+ if (!validatedColor) {
27598
+ return;
27599
+ }
27600
+ const clampedAlpha = typeof alpha === "number" && Number.isFinite(alpha) ? Math.max(0, Math.min(1, alpha)) : 1;
27601
+ const filledElements = svg.querySelectorAll('[fill]:not([fill="none"])');
27602
+ filledElements.forEach((el) => {
27603
+ el.setAttribute("fill", validatedColor);
27604
+ el.setAttribute("fill-opacity", clampedAlpha.toString());
27605
+ });
27606
+ } catch (error) {
27607
+ console.error("Failed to apply alpha to SVG:", error);
27608
+ }
27609
+ }
27610
+ let gradientIdCounter = 0;
27611
+ function generateGradientId(prefix = "gradient") {
27612
+ return `${prefix}-${Date.now()}-${gradientIdCounter++}-${Math.random().toString(36).substring(2, 11)}`;
27613
+ }
26224
27614
  const CLASS_NAMES = {
26225
27615
  container: "superdoc-layout",
26226
27616
  page: "superdoc-page",
@@ -26555,47 +27945,20 @@ const borderValueToSpec = (value) => {
26555
27945
  }
26556
27946
  return void 0;
26557
27947
  };
26558
- const resolveTableBorderValue = (explicit, fallback) => {
26559
- const explicitSpec = borderValueToSpec(explicit);
26560
- if (explicitSpec) {
26561
- return explicitSpec;
26562
- }
26563
- return borderValueToSpec(fallback);
26564
- };
26565
- const createTableBorderOverlay = (doc2, fragment, tableBorders) => {
26566
- const top2 = borderValueToSpec(tableBorders.top ?? null);
26567
- const right2 = borderValueToSpec(tableBorders.right ?? null);
26568
- const bottom2 = borderValueToSpec(tableBorders.bottom ?? null);
26569
- const left2 = borderValueToSpec(tableBorders.left ?? null);
26570
- if (!top2 && !right2 && !bottom2 && !left2) {
26571
- return null;
26572
- }
26573
- const overlay = doc2.createElement("div");
26574
- overlay.classList.add("superdoc-table-border");
26575
- overlay.style.position = "absolute";
26576
- overlay.style.left = "0";
26577
- overlay.style.top = "0";
26578
- overlay.style.width = `${fragment.width}px`;
26579
- overlay.style.height = `${fragment.height}px`;
26580
- overlay.style.boxSizing = "border-box";
26581
- overlay.style.pointerEvents = "none";
26582
- overlay.style.zIndex = "1";
26583
- applyBorder(overlay, "Top", top2);
26584
- applyBorder(overlay, "Right", right2);
26585
- applyBorder(overlay, "Bottom", bottom2);
26586
- applyBorder(overlay, "Left", left2);
26587
- return overlay;
26588
- };
26589
27948
  const resolveTableCellBorders = (tableBorders, rowIndex, colIndex, totalRows, totalCols) => {
26590
27949
  const isFirstRow = rowIndex === 0;
26591
27950
  const isLastRow = rowIndex === totalRows - 1;
26592
27951
  const isFirstCol = colIndex === 0;
26593
27952
  const isLastCol = colIndex === totalCols - 1;
26594
27953
  return {
26595
- top: resolveTableBorderValue(isFirstRow ? tableBorders?.top : null, tableBorders?.insideH),
26596
- bottom: resolveTableBorderValue(isLastRow ? tableBorders?.bottom : null, tableBorders?.insideH),
26597
- left: resolveTableBorderValue(isFirstCol ? tableBorders?.left : null, tableBorders?.insideV),
26598
- right: resolveTableBorderValue(isLastCol ? tableBorders?.right : null, tableBorders?.insideV)
27954
+ // Top: first row gets table.top, interior rows get insideH
27955
+ top: borderValueToSpec(isFirstRow ? tableBorders?.top : tableBorders?.insideH),
27956
+ // Bottom: ONLY last row gets table.bottom (interior cells don't render bottom - it comes from cell below's top)
27957
+ bottom: borderValueToSpec(isLastRow ? tableBorders?.bottom : null),
27958
+ // Left: first col gets table.left, interior cols get insideV
27959
+ left: borderValueToSpec(isFirstCol ? tableBorders?.left : tableBorders?.insideV),
27960
+ // Right: ONLY last col gets table.right (interior cells don't render right - it comes from cell to right's left)
27961
+ right: borderValueToSpec(isLastCol ? tableBorders?.right : null)
26599
27962
  };
26600
27963
  };
26601
27964
  const renderTableCell = (deps) => {
@@ -26621,21 +27984,37 @@ const renderTableCell = (deps) => {
26621
27984
  let contentElement;
26622
27985
  const attrs = cell?.attrs;
26623
27986
  const padding = attrs?.padding || { top: 2, left: 4, right: 4 };
26624
- if (cell && cellMeasure.paragraph.lines.length > 0) {
26625
- const lines = cellMeasure.paragraph.lines;
27987
+ const paddingLeft = padding.left ?? 4;
27988
+ const paddingTop = padding.top ?? 2;
27989
+ const paddingRight = padding.right ?? 4;
27990
+ const cellBlocks = cell?.blocks ?? (cell?.paragraph ? [cell.paragraph] : []);
27991
+ const blockMeasures = cellMeasure.blocks ?? (cellMeasure.paragraph ? [cellMeasure.paragraph] : []);
27992
+ if (cellBlocks.length > 0 && blockMeasures.length > 0) {
26626
27993
  const content = doc2.createElement("div");
26627
27994
  content.style.position = "absolute";
26628
- applySdtDataset(content, cell.paragraph.attrs?.sdt);
26629
- const paddingLeft = padding.left ?? 4;
26630
- const paddingTop = padding.top ?? 2;
26631
- const paddingRight = padding.right ?? 4;
26632
27995
  content.style.left = `${x + paddingLeft}px`;
26633
27996
  content.style.top = `${y + paddingTop}px`;
26634
27997
  content.style.width = `${Math.max(0, cellMeasure.width - paddingLeft - paddingRight)}px`;
26635
- lines.forEach((line) => {
26636
- const lineEl = renderLine(cell.paragraph, line, { ...context, section: "body" });
26637
- content.appendChild(lineEl);
26638
- });
27998
+ let blockY = 0;
27999
+ for (let i = 0; i < Math.min(blockMeasures.length, cellBlocks.length); i++) {
28000
+ const blockMeasure = blockMeasures[i];
28001
+ const block = cellBlocks[i];
28002
+ if (blockMeasure.kind === "paragraph" && block?.kind === "paragraph") {
28003
+ const paraWrapper = doc2.createElement("div");
28004
+ paraWrapper.style.position = "absolute";
28005
+ paraWrapper.style.top = `${blockY}px`;
28006
+ paraWrapper.style.left = "0";
28007
+ paraWrapper.style.width = "100%";
28008
+ applySdtDataset(paraWrapper, block.attrs?.sdt);
28009
+ const lines = blockMeasure.lines;
28010
+ lines.forEach((line) => {
28011
+ const lineEl = renderLine(block, line, { ...context, section: "body" });
28012
+ paraWrapper.appendChild(lineEl);
28013
+ });
28014
+ content.appendChild(paraWrapper);
28015
+ blockY += blockMeasure.totalHeight;
28016
+ }
28017
+ }
26639
28018
  contentElement = content;
26640
28019
  }
26641
28020
  return { cellElement: cellEl, contentElement };
@@ -26680,10 +28059,25 @@ const renderTableRow = (deps) => {
26680
28059
  const gridColIndex = cellMeasure.gridColumnStart ?? cellIndex;
26681
28060
  const totalCols = columnWidths.length;
26682
28061
  let resolvedBorders;
26683
- if (hasExplicitBorders) {
26684
- resolvedBorders = cellBordersAttr;
26685
- } else if (hasBordersAttribute) {
28062
+ if (hasBordersAttribute && !hasExplicitBorders) {
26686
28063
  resolvedBorders = void 0;
28064
+ } else if (hasExplicitBorders && tableBorders) {
28065
+ const isFirstRow = rowIndex === 0;
28066
+ const isLastRow = rowIndex === totalRows - 1;
28067
+ const isFirstCol = gridColIndex === 0;
28068
+ const isLastCol = gridColIndex === totalCols - 1;
28069
+ resolvedBorders = {
28070
+ // For top: use cell's if defined, otherwise use table's top for first row
28071
+ top: cellBordersAttr.top ?? borderValueToSpec(isFirstRow ? tableBorders.top : tableBorders.insideH),
28072
+ // For bottom: use cell's if defined, otherwise use table's bottom for last row only
28073
+ bottom: cellBordersAttr.bottom ?? borderValueToSpec(isLastRow ? tableBorders.bottom : void 0),
28074
+ // For left: use cell's if defined, otherwise use table's left for first col
28075
+ left: cellBordersAttr.left ?? borderValueToSpec(isFirstCol ? tableBorders.left : tableBorders.insideV),
28076
+ // For right: use cell's if defined, otherwise use table's right for last col only
28077
+ right: cellBordersAttr.right ?? borderValueToSpec(isLastCol ? tableBorders.right : void 0)
28078
+ };
28079
+ } else if (hasExplicitBorders) {
28080
+ resolvedBorders = cellBordersAttr;
26687
28081
  } else if (tableBorders) {
26688
28082
  resolvedBorders = resolveTableCellBorders(tableBorders, rowIndex, gridColIndex, totalRows, totalCols);
26689
28083
  } else {
@@ -26739,7 +28133,6 @@ const renderTableFragment = (deps) => {
26739
28133
  const block = lookup.block;
26740
28134
  const measure = lookup.measure;
26741
28135
  const tableBorders = block.attrs?.borders;
26742
- const borderOverlay = tableBorders ? createTableBorderOverlay(doc2, fragment, tableBorders) : null;
26743
28136
  const container = doc2.createElement("div");
26744
28137
  container.classList.add(CLASS_NAMES.fragment);
26745
28138
  applyStyles2(container, fragmentStyles);
@@ -26838,9 +28231,6 @@ const renderTableFragment = (deps) => {
26838
28231
  });
26839
28232
  y += rowMeasure.height;
26840
28233
  }
26841
- if (borderOverlay) {
26842
- container.appendChild(borderOverlay);
26843
- }
26844
28234
  return container;
26845
28235
  };
26846
28236
  const LIST_MARKER_GAP$1 = 8;
@@ -26853,6 +28243,8 @@ const LINK_DATASET_KEYS = {
26853
28243
  };
26854
28244
  const MAX_HREF_LENGTH = 2048;
26855
28245
  const SAFE_ANCHOR_PATTERN = /^[A-Za-z0-9._-]+$/;
28246
+ const MAX_DATA_URL_LENGTH = 10 * 1024 * 1024;
28247
+ const VALID_IMAGE_DATA_URL = /^data:image\/(png|jpeg|jpg|gif|svg\+xml|webp|bmp|ico|tiff?);base64,/i;
26856
28248
  const AMBIGUOUS_LINK_PATTERNS = /^(click here|read more|more|link|here|this|download|view)$/i;
26857
28249
  const TRACK_CHANGE_BASE_CLASS = {
26858
28250
  insert: "track-insert-dec",
@@ -26879,6 +28271,11 @@ const TRACK_CHANGE_MODIFIER_CLASS = {
26879
28271
  off: void 0
26880
28272
  }
26881
28273
  };
28274
+ function sanitizeUrl(href) {
28275
+ if (typeof href !== "string") return null;
28276
+ const sanitized = sanitizeHref(href);
28277
+ return sanitized?.href ?? null;
28278
+ }
26882
28279
  const LINK_TARGET_SET = /* @__PURE__ */ new Set(["_blank", "_self", "_parent", "_top"]);
26883
28280
  const normalizeAnchor = (value) => {
26884
28281
  if (typeof value !== "string") return null;
@@ -27773,7 +29170,7 @@ const _DomPainter = class _DomPainter {
27773
29170
  }
27774
29171
  const block = lookup.block;
27775
29172
  const fragmentEl = this.doc.createElement("div");
27776
- fragmentEl.classList.add(CLASS_NAMES.fragment);
29173
+ fragmentEl.classList.add(CLASS_NAMES.fragment, "superdoc-image-fragment");
27777
29174
  applyStyles$2(fragmentEl, fragmentStyles);
27778
29175
  this.applyFragmentFrame(fragmentEl, fragment);
27779
29176
  fragmentEl.style.height = `${fragment.height}px`;
@@ -27782,6 +29179,18 @@ const _DomPainter = class _DomPainter {
27782
29179
  if (fragment.isAnchored && fragment.zIndex != null) {
27783
29180
  fragmentEl.style.zIndex = String(fragment.zIndex);
27784
29181
  }
29182
+ if (block.id) {
29183
+ fragmentEl.setAttribute("data-sd-block-id", block.id);
29184
+ }
29185
+ if (fragment.pmStart != null) {
29186
+ fragmentEl.dataset.pmStart = String(fragment.pmStart);
29187
+ }
29188
+ if (fragment.pmEnd != null) {
29189
+ fragmentEl.dataset.pmEnd = String(fragment.pmEnd);
29190
+ }
29191
+ if (fragment.metadata) {
29192
+ fragmentEl.setAttribute("data-image-metadata", JSON.stringify(fragment.metadata));
29193
+ }
27785
29194
  const img = this.doc.createElement("img");
27786
29195
  if (block.src) {
27787
29196
  img.src = block.src;
@@ -27815,6 +29224,7 @@ const _DomPainter = class _DomPainter {
27815
29224
  this.applyFragmentFrame(fragmentEl, fragment);
27816
29225
  fragmentEl.style.height = `${fragment.height}px`;
27817
29226
  fragmentEl.style.position = "absolute";
29227
+ fragmentEl.style.overflow = "hidden";
27818
29228
  if (fragment.isAnchored && fragment.zIndex != null) {
27819
29229
  fragmentEl.style.zIndex = String(fragment.zIndex);
27820
29230
  }
@@ -27872,12 +29282,13 @@ const _DomPainter = class _DomPainter {
27872
29282
  img.style.display = "block";
27873
29283
  return img;
27874
29284
  }
27875
- createVectorShapeElement(block, geometry, applyTransforms = false) {
29285
+ createVectorShapeElement(block, geometry, applyTransforms = false, groupScaleX = 1, groupScaleY = 1) {
27876
29286
  const container = this.doc.createElement("div");
27877
29287
  container.classList.add("superdoc-vector-shape");
27878
29288
  container.style.width = "100%";
27879
29289
  container.style.height = "100%";
27880
29290
  container.style.position = "relative";
29291
+ container.style.overflow = "hidden";
27881
29292
  const svgMarkup = block.shapeKind ? this.tryCreatePresetSvg(block) : null;
27882
29293
  if (svgMarkup) {
27883
29294
  const svgElement = this.parseSafeSvg(svgMarkup);
@@ -27885,27 +29296,193 @@ const _DomPainter = class _DomPainter {
27885
29296
  svgElement.setAttribute("width", "100%");
27886
29297
  svgElement.setAttribute("height", "100%");
27887
29298
  svgElement.style.display = "block";
29299
+ if (block.fillColor && typeof block.fillColor === "object") {
29300
+ if ("type" in block.fillColor && block.fillColor.type === "gradient") {
29301
+ applyGradientToSVG$1(svgElement, block.fillColor);
29302
+ } else if ("type" in block.fillColor && block.fillColor.type === "solidWithAlpha") {
29303
+ applyAlphaToSVG$1(svgElement, block.fillColor);
29304
+ }
29305
+ }
27888
29306
  if (applyTransforms && geometry) {
27889
29307
  this.applyVectorShapeTransforms(svgElement, geometry);
27890
29308
  }
27891
29309
  container.appendChild(svgElement);
29310
+ if (block.textContent && block.textContent.parts.length > 0) {
29311
+ const textDiv = this.createFallbackTextElement(
29312
+ block.textContent,
29313
+ block.textAlign ?? "center",
29314
+ block.textVerticalAlign,
29315
+ block.textInsets,
29316
+ groupScaleX,
29317
+ groupScaleY
29318
+ );
29319
+ container.appendChild(textDiv);
29320
+ }
27892
29321
  return container;
27893
29322
  }
27894
29323
  }
27895
- container.style.background = block.fillColor ?? "rgba(15, 23, 42, 0.1)";
27896
- container.style.border = `1px solid ${block.strokeColor ?? "rgba(15, 23, 42, 0.3)"}`;
29324
+ this.applyFallbackShapeStyle(container, block);
29325
+ if (block.textContent && block.textContent.parts.length > 0) {
29326
+ const textDiv = this.createFallbackTextElement(
29327
+ block.textContent,
29328
+ block.textAlign ?? "center",
29329
+ block.textVerticalAlign,
29330
+ block.textInsets,
29331
+ groupScaleX,
29332
+ groupScaleY
29333
+ );
29334
+ container.appendChild(textDiv);
29335
+ }
27897
29336
  if (applyTransforms && geometry) {
27898
29337
  this.applyVectorShapeTransforms(container, geometry);
27899
29338
  }
27900
29339
  return container;
27901
29340
  }
29341
+ /**
29342
+ * Apply fill and stroke styles to a fallback shape container
29343
+ */
29344
+ applyFallbackShapeStyle(container, block) {
29345
+ if (block.fillColor === null) {
29346
+ container.style.background = "none";
29347
+ } else if (typeof block.fillColor === "string") {
29348
+ container.style.background = block.fillColor;
29349
+ } else if (typeof block.fillColor === "object" && "type" in block.fillColor) {
29350
+ if (block.fillColor.type === "solidWithAlpha") {
29351
+ const alpha = block.fillColor.alpha;
29352
+ const color = block.fillColor.color;
29353
+ container.style.background = color;
29354
+ container.style.opacity = alpha.toString();
29355
+ } else if (block.fillColor.type === "gradient") {
29356
+ container.style.background = "rgba(15, 23, 42, 0.1)";
29357
+ }
29358
+ } else {
29359
+ container.style.background = "rgba(15, 23, 42, 0.1)";
29360
+ }
29361
+ if (block.strokeColor === null) {
29362
+ container.style.border = "none";
29363
+ } else if (typeof block.strokeColor === "string") {
29364
+ const strokeWidth = block.strokeWidth ?? 1;
29365
+ container.style.border = `${strokeWidth}px solid ${block.strokeColor}`;
29366
+ } else {
29367
+ container.style.border = "1px solid rgba(15, 23, 42, 0.3)";
29368
+ }
29369
+ }
29370
+ /**
29371
+ * Create a fallback text element for shapes without SVG
29372
+ * @param textContent - Text content with formatting
29373
+ * @param textAlign - Horizontal text alignment
29374
+ * @param textVerticalAlign - Vertical text alignment (top, center, bottom)
29375
+ * @param textInsets - Text insets in pixels (top, right, bottom, left)
29376
+ * @param groupScaleX - Scale factor applied by parent group (for counter-scaling)
29377
+ * @param groupScaleY - Scale factor applied by parent group (for counter-scaling)
29378
+ */
29379
+ createFallbackTextElement(textContent2, textAlign, textVerticalAlign, textInsets, groupScaleX = 1, groupScaleY = 1) {
29380
+ const textDiv = this.doc.createElement("div");
29381
+ textDiv.style.position = "absolute";
29382
+ textDiv.style.top = "0";
29383
+ textDiv.style.left = "0";
29384
+ textDiv.style.width = "100%";
29385
+ textDiv.style.height = "100%";
29386
+ textDiv.style.display = "flex";
29387
+ textDiv.style.flexDirection = "column";
29388
+ const verticalAlign = textVerticalAlign ?? "center";
29389
+ if (verticalAlign === "top") {
29390
+ textDiv.style.justifyContent = "flex-start";
29391
+ } else if (verticalAlign === "bottom") {
29392
+ textDiv.style.justifyContent = "flex-end";
29393
+ } else {
29394
+ textDiv.style.justifyContent = "center";
29395
+ }
29396
+ if (textInsets) {
29397
+ textDiv.style.padding = `${textInsets.top}px ${textInsets.right}px ${textInsets.bottom}px ${textInsets.left}px`;
29398
+ } else {
29399
+ textDiv.style.padding = "10px";
29400
+ }
29401
+ textDiv.style.boxSizing = "border-box";
29402
+ textDiv.style.wordWrap = "break-word";
29403
+ textDiv.style.overflowWrap = "break-word";
29404
+ textDiv.style.overflow = "hidden";
29405
+ textDiv.style.minWidth = "0";
29406
+ textDiv.style.fontSize = "12px";
29407
+ textDiv.style.lineHeight = "1.2";
29408
+ if (groupScaleX !== 1 || groupScaleY !== 1) {
29409
+ const counterScaleX = 1 / groupScaleX;
29410
+ const counterScaleY = 1 / groupScaleY;
29411
+ textDiv.style.transform = `scale(${counterScaleX}, ${counterScaleY})`;
29412
+ textDiv.style.transformOrigin = "top left";
29413
+ textDiv.style.width = `${100 * groupScaleX}%`;
29414
+ textDiv.style.height = `${100 * groupScaleY}%`;
29415
+ }
29416
+ if (textAlign === "center") {
29417
+ textDiv.style.textAlign = "center";
29418
+ } else if (textAlign === "right" || textAlign === "r") {
29419
+ textDiv.style.textAlign = "right";
29420
+ } else {
29421
+ textDiv.style.textAlign = "left";
29422
+ }
29423
+ let currentParagraph = this.doc.createElement("div");
29424
+ currentParagraph.style.width = "100%";
29425
+ currentParagraph.style.minWidth = "0";
29426
+ currentParagraph.style.whiteSpace = "normal";
29427
+ textContent2.parts.forEach((part) => {
29428
+ if (part.isLineBreak) {
29429
+ textDiv.appendChild(currentParagraph);
29430
+ currentParagraph = this.doc.createElement("div");
29431
+ currentParagraph.style.width = "100%";
29432
+ currentParagraph.style.minWidth = "0";
29433
+ currentParagraph.style.whiteSpace = "normal";
29434
+ if (part.isEmptyParagraph) {
29435
+ currentParagraph.style.minHeight = "1em";
29436
+ }
29437
+ } else {
29438
+ const span = this.doc.createElement("span");
29439
+ span.textContent = part.text;
29440
+ if (part.formatting) {
29441
+ if (part.formatting.bold) {
29442
+ span.style.fontWeight = "bold";
29443
+ }
29444
+ if (part.formatting.italic) {
29445
+ span.style.fontStyle = "italic";
29446
+ }
29447
+ if (part.formatting.color) {
29448
+ const validatedColor = validateHexColor(part.formatting.color);
29449
+ if (validatedColor) {
29450
+ span.style.color = validatedColor;
29451
+ }
29452
+ }
29453
+ if (part.formatting.fontSize) {
29454
+ span.style.fontSize = `${part.formatting.fontSize}px`;
29455
+ }
29456
+ }
29457
+ currentParagraph.appendChild(span);
29458
+ }
29459
+ });
29460
+ textDiv.appendChild(currentParagraph);
29461
+ return textDiv;
29462
+ }
27902
29463
  tryCreatePresetSvg(block) {
27903
29464
  try {
29465
+ let fillColor;
29466
+ if (block.fillColor === null) {
29467
+ fillColor = "none";
29468
+ } else if (typeof block.fillColor === "string") {
29469
+ fillColor = block.fillColor;
29470
+ }
29471
+ const strokeColor = block.strokeColor === null ? "none" : typeof block.strokeColor === "string" ? block.strokeColor : void 0;
29472
+ if (block.shapeKind === "line") {
29473
+ const width = block.geometry.width;
29474
+ const height = block.geometry.height;
29475
+ const stroke = strokeColor ?? "#000000";
29476
+ const strokeWidth = block.strokeWidth ?? 1;
29477
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
29478
+ <line x1="0" y1="0" x2="${width}" y2="${height}" stroke="${stroke}" stroke-width="${strokeWidth}" />
29479
+ </svg>`;
29480
+ }
27904
29481
  return k({
27905
29482
  preset: block.shapeKind ?? "",
27906
29483
  styleOverrides: () => ({
27907
- fill: block.fillColor ?? void 0,
27908
- stroke: block.strokeColor ?? void 0,
29484
+ fill: fillColor,
29485
+ stroke: strokeColor,
27909
29486
  strokeWidth: block.strokeWidth ?? void 0
27910
29487
  })
27911
29488
  });
@@ -27971,6 +29548,8 @@ const _DomPainter = class _DomPainter {
27971
29548
  groupEl.style.height = "100%";
27972
29549
  const groupTransform = block.groupTransform;
27973
29550
  let contentContainer = groupEl;
29551
+ let groupScaleX = 1;
29552
+ let groupScaleY = 1;
27974
29553
  if (groupTransform) {
27975
29554
  const inner = this.doc.createElement("div");
27976
29555
  inner.style.position = "absolute";
@@ -27988,10 +29567,10 @@ const _DomPainter = class _DomPainter {
27988
29567
  }
27989
29568
  const targetWidth = groupTransform.width ?? block.geometry.width ?? childWidth;
27990
29569
  const targetHeight = groupTransform.height ?? block.geometry.height ?? childHeight;
27991
- const scaleX = childWidth ? targetWidth / childWidth : 1;
27992
- const scaleY = childHeight ? targetHeight / childHeight : 1;
27993
- if (scaleX !== 1 || scaleY !== 1) {
27994
- transforms.push(`scale(${scaleX}, ${scaleY})`);
29570
+ groupScaleX = childWidth ? targetWidth / childWidth : 1;
29571
+ groupScaleY = childHeight ? targetHeight / childHeight : 1;
29572
+ if (groupScaleX !== 1 || groupScaleY !== 1) {
29573
+ transforms.push(`scale(${groupScaleX}, ${groupScaleY})`);
27995
29574
  }
27996
29575
  if (transforms.length > 0) {
27997
29576
  inner.style.transformOrigin = "top left";
@@ -28001,7 +29580,7 @@ const _DomPainter = class _DomPainter {
28001
29580
  contentContainer = inner;
28002
29581
  }
28003
29582
  block.shapes.forEach((child) => {
28004
- const childContent = this.createGroupChildContent(child);
29583
+ const childContent = this.createGroupChildContent(child, groupScaleX, groupScaleY);
28005
29584
  if (!childContent) return;
28006
29585
  const attrs = child.attrs ?? {};
28007
29586
  const wrapper = this.doc.createElement("div");
@@ -28034,20 +29613,21 @@ const _DomPainter = class _DomPainter {
28034
29613
  });
28035
29614
  return groupEl;
28036
29615
  }
28037
- createGroupChildContent(child) {
29616
+ createGroupChildContent(child, groupScaleX = 1, groupScaleY = 1) {
28038
29617
  if (child.shapeType === "vectorShape" && "fillColor" in child.attrs) {
28039
29618
  const attrs = child.attrs;
29619
+ const childGeometry = {
29620
+ width: attrs.width ?? 0,
29621
+ height: attrs.height ?? 0,
29622
+ rotation: attrs.rotation ?? 0,
29623
+ flipH: attrs.flipH ?? false,
29624
+ flipV: attrs.flipV ?? false
29625
+ };
28040
29626
  const vectorChild = {
28041
29627
  drawingKind: "vectorShape",
28042
29628
  kind: "drawing",
28043
29629
  id: `${attrs.shapeId ?? child.shapeType}`,
28044
- geometry: {
28045
- width: attrs.width ?? 0,
28046
- height: attrs.height ?? 0,
28047
- rotation: attrs.rotation ?? 0,
28048
- flipH: attrs.flipH ?? false,
28049
- flipV: attrs.flipV ?? false
28050
- },
29630
+ geometry: childGeometry,
28051
29631
  padding: void 0,
28052
29632
  margin: void 0,
28053
29633
  anchor: void 0,
@@ -28058,9 +29638,11 @@ const _DomPainter = class _DomPainter {
28058
29638
  shapeKind: attrs.kind,
28059
29639
  fillColor: attrs.fillColor,
28060
29640
  strokeColor: attrs.strokeColor,
28061
- strokeWidth: attrs.strokeWidth
29641
+ strokeWidth: attrs.strokeWidth,
29642
+ textContent: attrs.textContent,
29643
+ textAlign: attrs.textAlign
28062
29644
  };
28063
- return this.createVectorShapeElement(vectorChild);
29645
+ return this.createVectorShapeElement(vectorChild, childGeometry, false, groupScaleX, groupScaleY);
28064
29646
  }
28065
29647
  if (child.shapeType === "image" && "src" in child.attrs) {
28066
29648
  const attrs = child.attrs;
@@ -28250,7 +29832,16 @@ const _DomPainter = class _DomPainter {
28250
29832
  /**
28251
29833
  * Render a single run as an HTML element (span or anchor).
28252
29834
  */
29835
+ /**
29836
+ * Type guard to check if a run is an image run.
29837
+ */
29838
+ isImageRun(run) {
29839
+ return run.kind === "image";
29840
+ }
28253
29841
  renderRun(run, context, trackedConfig) {
29842
+ if (this.isImageRun(run)) {
29843
+ return this.renderImageRun(run);
29844
+ }
28254
29845
  if (!run.text || !this.doc) {
28255
29846
  return null;
28256
29847
  }
@@ -28285,6 +29876,88 @@ const _DomPainter = class _DomPainter {
28285
29876
  this.applySdtDataset(elem, run.sdt);
28286
29877
  return elem;
28287
29878
  }
29879
+ /**
29880
+ * Renders an ImageRun as an inline <img> element.
29881
+ *
29882
+ * SECURITY NOTES:
29883
+ * - Data URLs are validated against VALID_IMAGE_DATA_URL regex to ensure proper format
29884
+ * - Size limit (MAX_DATA_URL_LENGTH) prevents DoS attacks from extremely large images
29885
+ * - Only allows safe image MIME types (png, jpeg, gif, etc.) with base64 encoding
29886
+ * - Non-data URLs are sanitized through sanitizeUrl to prevent XSS
29887
+ *
29888
+ * @param run - The ImageRun to render containing image source, dimensions, and spacing
29889
+ * @returns HTMLElement (img) or null if src is missing or invalid
29890
+ *
29891
+ * @example
29892
+ * ```typescript
29893
+ * // Valid data URL
29894
+ * renderImageRun({ kind: 'image', src: 'data:image/png;base64,iVBORw...', width: 100, height: 100 })
29895
+ * // Returns: <img> element
29896
+ *
29897
+ * // Invalid MIME type
29898
+ * renderImageRun({ kind: 'image', src: 'data:text/html;base64,PHNjcmlwdD4...', width: 100, height: 100 })
29899
+ * // Returns: null (blocked)
29900
+ *
29901
+ * // HTTP URL
29902
+ * renderImageRun({ kind: 'image', src: 'https://example.com/image.png', width: 100, height: 100 })
29903
+ * // Returns: <img> element (after sanitization)
29904
+ * ```
29905
+ */
29906
+ renderImageRun(run) {
29907
+ if (!this.doc || !run.src) {
29908
+ return null;
29909
+ }
29910
+ const img = this.doc.createElement("img");
29911
+ const isDataUrl = typeof run.src === "string" && run.src.startsWith("data:");
29912
+ if (isDataUrl) {
29913
+ if (run.src.length > MAX_DATA_URL_LENGTH) {
29914
+ return null;
29915
+ }
29916
+ if (!VALID_IMAGE_DATA_URL.test(run.src)) {
29917
+ return null;
29918
+ }
29919
+ img.src = run.src;
29920
+ } else {
29921
+ const sanitized = sanitizeUrl(run.src);
29922
+ if (sanitized) {
29923
+ img.src = sanitized;
29924
+ } else {
29925
+ return null;
29926
+ }
29927
+ }
29928
+ img.width = run.width;
29929
+ img.height = run.height;
29930
+ img.alt = run.alt ?? "";
29931
+ if (run.title) {
29932
+ img.title = run.title;
29933
+ }
29934
+ img.style.display = "inline-block";
29935
+ img.style.verticalAlign = run.verticalAlign ?? "bottom";
29936
+ if (run.distTop) {
29937
+ img.style.marginTop = `${run.distTop}px`;
29938
+ }
29939
+ if (run.distBottom) {
29940
+ img.style.marginBottom = `${run.distBottom}px`;
29941
+ }
29942
+ if (run.distLeft) {
29943
+ img.style.marginLeft = `${run.distLeft}px`;
29944
+ }
29945
+ if (run.distRight) {
29946
+ img.style.marginRight = `${run.distRight}px`;
29947
+ }
29948
+ img.style.zIndex = "1";
29949
+ if (run.pmStart != null) {
29950
+ img.dataset.pmStart = String(run.pmStart);
29951
+ }
29952
+ if (run.pmEnd != null) {
29953
+ img.dataset.pmEnd = String(run.pmEnd);
29954
+ }
29955
+ this.applySdtDataset(img, run.sdt);
29956
+ if (run.dataAttrs) {
29957
+ applyRunDataAttributes(img, run.dataAttrs);
29958
+ }
29959
+ return img;
29960
+ }
28288
29961
  renderLine(block, line, context) {
28289
29962
  if (!this.doc) {
28290
29963
  throw new Error("DomPainter: document is not available");
@@ -28356,6 +30029,16 @@ const _DomPainter = class _DomPainter {
28356
30029
  line.segments.forEach((segment, _segIdx) => {
28357
30030
  const baseRun = runs[segment.runIndex];
28358
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);
30039
+ }
30040
+ return;
30041
+ }
28359
30042
  const segmentText = baseRun.text.slice(segment.fromChar, segment.toChar);
28360
30043
  const segmentRun = { ...baseRun, text: segmentText };
28361
30044
  const elem = this.renderRun(segmentRun, context, trackedConfig);
@@ -28686,8 +30369,25 @@ const fragmentSignature = (fragment, lookup) => {
28686
30369
  };
28687
30370
  const deriveBlockVersion = (block) => {
28688
30371
  if (block.kind === "paragraph") {
28689
- return block.runs.map(
28690
- (run) => [
30372
+ return block.runs.map((run) => {
30373
+ if (run.kind === "image") {
30374
+ const imgRun = run;
30375
+ return [
30376
+ "img",
30377
+ imgRun.src,
30378
+ imgRun.width,
30379
+ imgRun.height,
30380
+ imgRun.alt ?? "",
30381
+ imgRun.title ?? "",
30382
+ imgRun.distTop ?? "",
30383
+ imgRun.distBottom ?? "",
30384
+ imgRun.distLeft ?? "",
30385
+ imgRun.distRight ?? "",
30386
+ imgRun.pmStart ?? "",
30387
+ imgRun.pmEnd ?? ""
30388
+ ].join(",");
30389
+ }
30390
+ return [
28691
30391
  run.text ?? "",
28692
30392
  run.kind !== "tab" ? run.fontFamily : "",
28693
30393
  run.kind !== "tab" ? run.fontSize : "",
@@ -28703,8 +30403,8 @@ const deriveBlockVersion = (block) => {
28703
30403
  run.pmStart ?? "",
28704
30404
  run.pmEnd ?? "",
28705
30405
  run.kind !== "tab" ? run.token ?? "" : ""
28706
- ].join(",")
28707
- ).join("|");
30406
+ ].join(",");
30407
+ }).join("|");
28708
30408
  }
28709
30409
  if (block.kind === "list") {
28710
30410
  return block.items.map((item) => `${item.id}:${item.marker.text}:${deriveBlockVersion(item.paragraph)}`).join("|");
@@ -28762,7 +30462,7 @@ const deriveBlockVersion = (block) => {
28762
30462
  return block.id;
28763
30463
  };
28764
30464
  const applyRunStyles = (element, run, isLink = false) => {
28765
- if (run.kind === "tab") {
30465
+ if (run.kind === "tab" || run.kind === "image") {
28766
30466
  return;
28767
30467
  }
28768
30468
  element.style.fontFamily = run.fontFamily;
@@ -28881,6 +30581,10 @@ const sliceRunsForLine = (block, line) => {
28881
30581
  for (let runIndex = line.fromRun; runIndex <= line.toRun; runIndex += 1) {
28882
30582
  const run = block.runs[runIndex];
28883
30583
  if (!run) continue;
30584
+ if (run.kind === "image") {
30585
+ result.push(run);
30586
+ continue;
30587
+ }
28884
30588
  const text = run.text ?? "";
28885
30589
  const isFirstRun = runIndex === line.fromRun;
28886
30590
  const isLastRun = runIndex === line.toRun;
@@ -28919,6 +30623,21 @@ const computeLinePmRange = (block, line) => {
28919
30623
  for (let runIndex = line.fromRun; runIndex <= line.toRun; runIndex += 1) {
28920
30624
  const run = block.runs[runIndex];
28921
30625
  if (!run) continue;
30626
+ if (run.kind === "image") {
30627
+ const runPmStart2 = run.pmStart ?? null;
30628
+ const runPmEnd = run.pmEnd ?? null;
30629
+ if (runPmStart2 == null || runPmEnd == null) {
30630
+ continue;
30631
+ }
30632
+ if (pmStart == null) {
30633
+ pmStart = runPmStart2;
30634
+ }
30635
+ pmEnd = runPmEnd;
30636
+ if (runIndex === line.toRun) {
30637
+ break;
30638
+ }
30639
+ continue;
30640
+ }
28922
30641
  const text = run.text ?? "";
28923
30642
  const runLength = text.length;
28924
30643
  const runPmStart = run.pmStart ?? null;
@@ -28950,27 +30669,21 @@ const applyStyles$2 = (el, styles) => {
28950
30669
  });
28951
30670
  };
28952
30671
  const resolveRunText = (run, context) => {
30672
+ const runToken = "token" in run ? run.token : void 0;
28953
30673
  if (run.kind === "tab") {
28954
30674
  return run.text;
28955
30675
  }
28956
- if (!run.token) {
30676
+ if (run.kind === "image") {
30677
+ return "";
30678
+ }
30679
+ if (!runToken) {
28957
30680
  return run.text ?? "";
28958
30681
  }
28959
- if (run.token === "pageNumber") {
28960
- const resolved = context.pageNumberText ?? String(context.pageNumber);
28961
- if (typeof process$1 !== "undefined" && process$1.env?.NODE_ENV === "development" && context.section) {
28962
- console.debug(
28963
- `[Page Number] ${context.section}: page ${context.pageNumber} of ${context.totalPages} → "${resolved}"`
28964
- );
28965
- }
28966
- return resolved;
30682
+ if (runToken === "pageNumber") {
30683
+ return context.pageNumberText ?? String(context.pageNumber);
28967
30684
  }
28968
- if (run.token === "totalPageCount") {
28969
- const resolved = context.totalPages ? String(context.totalPages) : run.text ?? "";
28970
- if (typeof process$1 !== "undefined" && process$1.env?.NODE_ENV === "development" && context.section) {
28971
- console.debug(`[Total Pages] ${context.section}: ${resolved}`);
28972
- }
28973
- return resolved;
30685
+ if (runToken === "totalPageCount") {
30686
+ return context.totalPages ? String(context.totalPages) : run.text ?? "";
28974
30687
  }
28975
30688
  return run.text ?? "";
28976
30689
  };
@@ -29126,6 +30839,9 @@ function calculateTypographyMetrics(fontSize, spacing) {
29126
30839
  function isTabRun(run) {
29127
30840
  return run.kind === "tab";
29128
30841
  }
30842
+ function isImageRun(run) {
30843
+ return run.kind === "image";
30844
+ }
29129
30845
  async function measureBlock(block, constraints) {
29130
30846
  const normalized = normalizeConstraints(constraints);
29131
30847
  if (block.kind === "drawing") {
@@ -29269,6 +30985,79 @@ async function measureParagraphBlock(block, maxWidth) {
29269
30985
  }
29270
30986
  continue;
29271
30987
  }
30988
+ if (isImageRun(run)) {
30989
+ const leftSpace = run.distLeft ?? 0;
30990
+ const rightSpace = run.distRight ?? 0;
30991
+ const imageWidth = run.width + leftSpace + rightSpace;
30992
+ const topSpace = run.distTop ?? 0;
30993
+ const bottomSpace = run.distBottom ?? 0;
30994
+ const imageHeight = run.height + topSpace + bottomSpace;
30995
+ if (!currentLine) {
30996
+ currentLine = {
30997
+ fromRun: runIndex,
30998
+ fromChar: 0,
30999
+ toRun: runIndex,
31000
+ toChar: 1,
31001
+ // Images are treated as single atomic units
31002
+ width: imageWidth,
31003
+ maxFontSize: imageHeight,
31004
+ // Use image height for line height calculation
31005
+ maxWidth: availableWidth,
31006
+ segments: [
31007
+ {
31008
+ runIndex,
31009
+ fromChar: 0,
31010
+ toChar: 1,
31011
+ width: imageWidth
31012
+ }
31013
+ ]
31014
+ };
31015
+ availableWidth = contentWidth;
31016
+ continue;
31017
+ }
31018
+ if (currentLine.width + imageWidth > currentLine.maxWidth && currentLine.width > 0) {
31019
+ const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing);
31020
+ const completedLine = {
31021
+ ...currentLine,
31022
+ ...metrics
31023
+ };
31024
+ addBarTabsToLine(completedLine);
31025
+ lines.push(completedLine);
31026
+ tabStopCursor = 0;
31027
+ pendingTabAlignment = null;
31028
+ currentLine = {
31029
+ fromRun: runIndex,
31030
+ fromChar: 0,
31031
+ toRun: runIndex,
31032
+ toChar: 1,
31033
+ width: imageWidth,
31034
+ maxFontSize: imageHeight,
31035
+ maxWidth: contentWidth,
31036
+ segments: [
31037
+ {
31038
+ runIndex,
31039
+ fromChar: 0,
31040
+ toChar: 1,
31041
+ width: imageWidth
31042
+ }
31043
+ ]
31044
+ };
31045
+ availableWidth = contentWidth;
31046
+ } else {
31047
+ currentLine.toRun = runIndex;
31048
+ currentLine.toChar = 1;
31049
+ currentLine.width = roundValue(currentLine.width + imageWidth);
31050
+ currentLine.maxFontSize = Math.max(currentLine.maxFontSize, imageHeight);
31051
+ if (!currentLine.segments) currentLine.segments = [];
31052
+ currentLine.segments.push({
31053
+ runIndex,
31054
+ fromChar: 0,
31055
+ toChar: 1,
31056
+ width: imageWidth
31057
+ });
31058
+ }
31059
+ continue;
31060
+ }
29272
31061
  const { font } = buildFontString(run);
29273
31062
  const tabSegments = run.text.split(" ");
29274
31063
  let charPosInRun = 0;
@@ -29423,7 +31212,7 @@ async function measureParagraphBlock(block, maxWidth) {
29423
31212
  }
29424
31213
  }
29425
31214
  if (!currentLine && lines.length === 0) {
29426
- const fallbackFontSize = (block.runs[0]?.kind !== "tab" ? block.runs[0]?.fontSize : void 0) ?? 12;
31215
+ const fallbackFontSize = (block.runs[0]?.kind === "text" ? block.runs[0].fontSize : void 0) ?? 12;
29427
31216
  const metrics = calculateTypographyMetrics(fallbackFontSize, spacing);
29428
31217
  const fallbackLine = {
29429
31218
  fromRun: 0,
@@ -29474,16 +31263,34 @@ async function measureParagraphBlock(block, maxWidth) {
29474
31263
  async function measureTableBlock(block, constraints) {
29475
31264
  const maxWidth = typeof constraints === "number" ? constraints : constraints.maxWidth;
29476
31265
  let columnWidths;
31266
+ const maxCellCount = Math.max(1, Math.max(...block.rows.map((r2) => r2.cells.length)));
29477
31267
  if (block.columnWidths && block.columnWidths.length > 0) {
29478
31268
  columnWidths = [...block.columnWidths];
29479
- const totalWidth2 = columnWidths.reduce((a, b) => a + b, 0);
29480
31269
  const hasExplicitWidth = block.attrs?.tableWidth != null;
29481
- if (!hasExplicitWidth && totalWidth2 > maxWidth) {
29482
- const scale = maxWidth / totalWidth2;
29483
- columnWidths = columnWidths.map((w) => Math.max(1, Math.floor(w * scale)));
31270
+ const hasFixedLayout = block.attrs?.tableLayout === "fixed";
31271
+ if (hasExplicitWidth || hasFixedLayout) {
31272
+ const totalWidth2 = columnWidths.reduce((a, b) => a + b, 0);
31273
+ if (totalWidth2 > maxWidth) {
31274
+ const scale = maxWidth / totalWidth2;
31275
+ columnWidths = columnWidths.map((w) => Math.max(1, Math.floor(w * scale)));
31276
+ }
31277
+ } else {
31278
+ if (columnWidths.length < maxCellCount) {
31279
+ const usedWidth = columnWidths.reduce((a, b) => a + b, 0);
31280
+ const remainingWidth = Math.max(0, maxWidth - usedWidth);
31281
+ const missingColumns = maxCellCount - columnWidths.length;
31282
+ const paddingWidth = Math.max(1, Math.floor(remainingWidth / missingColumns));
31283
+ columnWidths.push(...Array.from({ length: missingColumns }, () => paddingWidth));
31284
+ } else if (columnWidths.length > maxCellCount) {
31285
+ columnWidths = columnWidths.slice(0, maxCellCount);
31286
+ }
31287
+ const totalWidth2 = columnWidths.reduce((a, b) => a + b, 0);
31288
+ if (totalWidth2 > maxWidth) {
31289
+ const scale = maxWidth / totalWidth2;
31290
+ columnWidths = columnWidths.map((w) => Math.max(1, Math.floor(w * scale)));
31291
+ }
29484
31292
  }
29485
31293
  } else {
29486
- const maxCellCount = Math.max(1, Math.max(...block.rows.map((r2) => r2.cells.length)));
29487
31294
  const columnWidth = Math.max(1, Math.floor(maxWidth / maxCellCount));
29488
31295
  columnWidths = Array.from({ length: maxCellCount }, () => columnWidth);
29489
31296
  }
@@ -29517,12 +31324,28 @@ async function measureTableBlock(block, constraints) {
29517
31324
  rowspanTracker[gridColIndex + c] = rowspan - 1;
29518
31325
  }
29519
31326
  }
29520
- const paraMeasure = await measureParagraphBlock(cell.paragraph, cellWidth);
29521
- const height = paraMeasure.totalHeight;
31327
+ const cellPadding = cell.attrs?.padding ?? { top: 2, left: 4, right: 4, bottom: 2 };
31328
+ const paddingTop = cellPadding.top ?? 2;
31329
+ const paddingBottom = cellPadding.bottom ?? 2;
31330
+ const paddingLeft = cellPadding.left ?? 4;
31331
+ const paddingRight = cellPadding.right ?? 4;
31332
+ const contentWidth = Math.max(1, cellWidth - paddingLeft - paddingRight);
31333
+ const blockMeasures = [];
31334
+ let contentHeight = 0;
31335
+ const cellBlocks = cell.blocks ?? (cell.paragraph ? [cell.paragraph] : []);
31336
+ for (const block2 of cellBlocks) {
31337
+ const measure = await measureBlock(block2, { maxWidth: contentWidth, maxHeight: Infinity });
31338
+ blockMeasures.push(measure);
31339
+ const blockHeight = "totalHeight" in measure ? measure.totalHeight : "height" in measure ? measure.height : 0;
31340
+ contentHeight += blockHeight;
31341
+ }
31342
+ const totalCellHeight = contentHeight + paddingTop + paddingBottom;
29522
31343
  cellMeasures.push({
29523
- paragraph: paraMeasure,
31344
+ blocks: blockMeasures,
31345
+ // Backward compatibility
31346
+ paragraph: blockMeasures[0]?.kind === "paragraph" ? blockMeasures[0] : void 0,
29524
31347
  width: cellWidth,
29525
- height,
31348
+ height: totalCellHeight,
29526
31349
  gridColumnStart: gridColIndex,
29527
31350
  colSpan: colspan,
29528
31351
  rowSpan: rowspan
@@ -29550,7 +31373,8 @@ async function measureTableBlock(block, constraints) {
29550
31373
  async function measureImageBlock(block, constraints) {
29551
31374
  const intrinsic = getIntrinsicImageSize(block, constraints.maxWidth);
29552
31375
  const maxWidth = constraints.maxWidth > 0 ? constraints.maxWidth : intrinsic.width;
29553
- const maxHeight = constraints.maxHeight && constraints.maxHeight > 0 ? constraints.maxHeight : Infinity;
31376
+ const hasNegativeVerticalPosition = block.anchor?.isAnchored && (typeof block.anchor?.offsetV === "number" && block.anchor.offsetV < 0 || typeof block.margin?.top === "number" && block.margin.top < 0);
31377
+ const maxHeight = hasNegativeVerticalPosition || !constraints.maxHeight || constraints.maxHeight <= 0 ? Infinity : constraints.maxHeight;
29554
31378
  const widthScale = maxWidth / intrinsic.width;
29555
31379
  const heightScale = maxHeight / intrinsic.height;
29556
31380
  const scale = Math.min(1, widthScale, heightScale);
@@ -29594,7 +31418,8 @@ async function measureDrawingBlock(block, constraints) {
29594
31418
  const naturalWidth = Math.max(1, rotatedBounds.width);
29595
31419
  const naturalHeight = Math.max(1, rotatedBounds.height);
29596
31420
  const maxWidth = constraints.maxWidth > 0 ? constraints.maxWidth : naturalWidth;
29597
- const maxHeight = constraints.maxHeight && constraints.maxHeight > 0 ? constraints.maxHeight : Infinity;
31421
+ const hasNegativeVerticalPosition = block.anchor?.isAnchored && (typeof block.anchor?.offsetV === "number" && block.anchor.offsetV < 0 || typeof block.margin?.top === "number" && block.margin.top < 0);
31422
+ const maxHeight = hasNegativeVerticalPosition || !constraints.maxHeight || constraints.maxHeight <= 0 ? Infinity : constraints.maxHeight;
29598
31423
  const widthScale = maxWidth / naturalWidth;
29599
31424
  const heightScale = maxHeight / naturalHeight;
29600
31425
  const normalizedScale = Math.min(1, widthScale, heightScale);
@@ -29705,14 +31530,14 @@ async function measureListBlock(block, constraints) {
29705
31530
  };
29706
31531
  }
29707
31532
  const getPrimaryRun = (paragraph) => {
29708
- return paragraph.runs.find((run) => run.kind !== "tab" && Boolean(run.fontFamily && run.fontSize)) || {
31533
+ return paragraph.runs.find((run) => run.kind === "text" && Boolean(run.fontFamily && run.fontSize)) || {
29709
31534
  text: "",
29710
31535
  fontFamily: "Arial",
29711
31536
  fontSize: 16
29712
31537
  };
29713
31538
  };
29714
31539
  const measureRunWidth = (text, font, ctx2, run) => {
29715
- const letterSpacing = run.kind !== "tab" ? run.letterSpacing || 0 : 0;
31540
+ const letterSpacing = run.kind === "text" ? run.letterSpacing || 0 : 0;
29716
31541
  const width = getMeasuredTextWidth(text, font, letterSpacing, ctx2);
29717
31542
  return roundValue(width);
29718
31543
  };
@@ -30607,8 +32432,12 @@ getBlocks_fn = function(descriptor) {
30607
32432
  }
30608
32433
  const blockIdPrefix = `hf-${descriptor.kind}-${descriptor.id}-`;
30609
32434
  const converterContext = __privateMethod(this, _HeaderFooterLayoutAdapter_instances, getConverterContext_fn).call(this);
32435
+ const rootConverter = __privateGet(this, _manager).rootEditor?.converter;
32436
+ const providedMedia = __privateGet(this, _mediaFiles);
32437
+ const fallbackMedia = rootConverter?.media;
32438
+ const mediaFiles = providedMedia && Object.keys(providedMedia).length > 0 ? providedMedia : fallbackMedia;
30610
32439
  const result = toFlowBlocks(doc2, {
30611
- mediaFiles: __privateGet(this, _mediaFiles),
32440
+ mediaFiles,
30612
32441
  blockIdPrefix,
30613
32442
  converterContext
30614
32443
  });
@@ -30722,6 +32551,7 @@ const _PresentationEditor = class _PresentationEditor extends EventEmitter {
30722
32551
  __privateAdd(this, _clickCount, 0);
30723
32552
  __privateAdd(this, _lastClickTime, 0);
30724
32553
  __privateAdd(this, _lastClickPosition, { x: 0, y: 0 });
32554
+ __privateAdd(this, _lastSelectedImageBlockId, null);
30725
32555
  // Remote cursor/presence state management
30726
32556
  /** Map of clientId -> normalized remote cursor state */
30727
32557
  __privateAdd(this, _remoteCursorState, /* @__PURE__ */ new Map());
@@ -30821,6 +32651,53 @@ const _PresentationEditor = class _PresentationEditor extends EventEmitter {
30821
32651
  }
30822
32652
  return;
30823
32653
  }
32654
+ const fragmentHit = getFragmentAtPosition(
32655
+ __privateGet(this, _layoutState).layout,
32656
+ __privateGet(this, _layoutState).blocks,
32657
+ __privateGet(this, _layoutState).measures,
32658
+ hit.pos
32659
+ );
32660
+ if (fragmentHit && (fragmentHit.fragment.kind === "image" || fragmentHit.fragment.kind === "drawing")) {
32661
+ const doc2 = __privateGet(this, _editor3).state.doc;
32662
+ try {
32663
+ const tr = __privateGet(this, _editor3).state.tr.setSelection(NodeSelection.create(doc2, hit.pos));
32664
+ __privateGet(this, _editor3).view?.dispatch(tr);
32665
+ if (__privateGet(this, _lastSelectedImageBlockId) && __privateGet(this, _lastSelectedImageBlockId) !== fragmentHit.fragment.blockId) {
32666
+ this.emit("imageDeselected", { blockId: __privateGet(this, _lastSelectedImageBlockId) });
32667
+ }
32668
+ if (fragmentHit.fragment.kind === "image") {
32669
+ const targetElement = __privateGet(this, _viewportHost).querySelector(
32670
+ `.superdoc-image-fragment[data-pm-start="${fragmentHit.fragment.pmStart}"]`
32671
+ );
32672
+ if (targetElement) {
32673
+ this.emit("imageSelected", {
32674
+ element: targetElement,
32675
+ blockId: fragmentHit.fragment.blockId,
32676
+ pmStart: fragmentHit.fragment.pmStart
32677
+ });
32678
+ __privateSet(this, _lastSelectedImageBlockId, fragmentHit.fragment.blockId);
32679
+ }
32680
+ }
32681
+ } catch (error) {
32682
+ if (process$1.env.NODE_ENV === "development") {
32683
+ console.warn("[PresentationEditor] Failed to create NodeSelection for atomic fragment:", error);
32684
+ }
32685
+ }
32686
+ __privateMethod(this, _PresentationEditor_instances, scheduleSelectionUpdate_fn).call(this);
32687
+ if (document.activeElement instanceof HTMLElement) {
32688
+ document.activeElement.blur();
32689
+ }
32690
+ const editorDom2 = __privateGet(this, _editor3).view?.dom;
32691
+ if (editorDom2) {
32692
+ editorDom2.focus();
32693
+ __privateGet(this, _editor3).view?.focus();
32694
+ }
32695
+ return;
32696
+ }
32697
+ if (__privateGet(this, _lastSelectedImageBlockId)) {
32698
+ this.emit("imageDeselected", { blockId: __privateGet(this, _lastSelectedImageBlockId) });
32699
+ __privateSet(this, _lastSelectedImageBlockId, null);
32700
+ }
30824
32701
  const clickDepth = __privateMethod(this, _PresentationEditor_instances, registerPointerClick_fn).call(this, event);
30825
32702
  let handledByDepth = false;
30826
32703
  if (__privateGet(this, _session).mode === "body") {
@@ -31888,6 +33765,7 @@ _hoverRegion = new WeakMap();
31888
33765
  _clickCount = new WeakMap();
31889
33766
  _lastClickTime = new WeakMap();
31890
33767
  _lastClickPosition = new WeakMap();
33768
+ _lastSelectedImageBlockId = new WeakMap();
31891
33769
  _remoteCursorState = new WeakMap();
31892
33770
  _remoteCursorDirty = new WeakMap();
31893
33771
  _remoteCursorOverlay = new WeakMap();
@@ -32367,7 +34245,9 @@ initHeaderFooterRegistry_fn = function() {
32367
34245
  const converter = __privateGet(this, _editor3).converter;
32368
34246
  __privateSet(this, _headerFooterIdentifier, extractIdentifierFromConverter(converter));
32369
34247
  __privateSet(this, _headerFooterManager, new HeaderFooterEditorManager(__privateGet(this, _editor3)));
32370
- const mediaFiles = __privateGet(this, _options)?.mediaFiles ?? __privateGet(this, _editor3).storage?.image?.media;
34248
+ const optionsMedia = __privateGet(this, _options)?.mediaFiles;
34249
+ const storageMedia = __privateGet(this, _editor3).storage?.image?.media;
34250
+ const mediaFiles = optionsMedia ?? storageMedia;
32371
34251
  __privateSet(this, _headerFooterAdapter, new HeaderFooterLayoutAdapter(
32372
34252
  __privateGet(this, _headerFooterManager),
32373
34253
  mediaFiles
@@ -32862,7 +34742,9 @@ buildHeaderFooterInput_fn = function() {
32862
34742
  computeHeaderFooterConstraints_fn = function() {
32863
34743
  const pageSize = __privateGet(this, _layoutOptions).pageSize ?? DEFAULT_PAGE_SIZE;
32864
34744
  const margins = __privateGet(this, _layoutOptions).margins ?? DEFAULT_MARGINS;
32865
- const width = pageSize.w - ((margins.left ?? DEFAULT_MARGINS.left) + (margins.right ?? DEFAULT_MARGINS.right));
34745
+ const marginLeft = margins.left ?? DEFAULT_MARGINS.left;
34746
+ const marginRight = margins.right ?? DEFAULT_MARGINS.right;
34747
+ const width = pageSize.w - (marginLeft + marginRight);
32866
34748
  if (!Number.isFinite(width) || width <= 0) {
32867
34749
  return null;
32868
34750
  }
@@ -32870,7 +34752,10 @@ computeHeaderFooterConstraints_fn = function() {
32870
34752
  const height = Math.max(headerSpace, footerSpace, 1);
32871
34753
  return {
32872
34754
  width,
32873
- height
34755
+ height,
34756
+ // Pass actual page dimensions for page-relative anchor positioning in headers/footers
34757
+ pageWidth: pageSize.w,
34758
+ margins: { left: marginLeft, right: marginRight }
32874
34759
  };
32875
34760
  };
32876
34761
  updateDecorationProviders_fn = function(layout) {
@@ -32893,10 +34778,11 @@ createDecorationProvider_fn = function(kind, layout) {
32893
34778
  if (!variant || !variant.layout?.pages?.length) {
32894
34779
  return null;
32895
34780
  }
32896
- const slotPage = variant.layout.pages.find((candidate) => candidate.number === pageNumber) ?? variant.layout.pages[0];
34781
+ const slotPage = __privateMethod(this, _PresentationEditor_instances, findHeaderFooterPageForPageNumber_fn).call(this, variant.layout.pages, pageNumber);
32897
34782
  if (!slotPage) {
32898
34783
  return null;
32899
34784
  }
34785
+ const fragments = slotPage.fragments ?? [];
32900
34786
  const pageHeight = page?.size?.h ?? layout.pageSize?.h ?? __privateGet(this, _layoutOptions).pageSize?.h ?? DEFAULT_PAGE_SIZE.h;
32901
34787
  const margins = pageMargins ?? layout.pages[0]?.margins ?? __privateGet(this, _layoutOptions).margins ?? DEFAULT_MARGINS;
32902
34788
  const box = __privateMethod(this, _PresentationEditor_instances, computeDecorationBox_fn).call(this, kind, margins, pageHeight);
@@ -32904,7 +34790,7 @@ createDecorationProvider_fn = function(kind, layout) {
32904
34790
  const fallbackId = __privateGet(this, _headerFooterManager)?.getVariantId(kind, headerFooterType);
32905
34791
  const finalHeaderId = headerId ?? fallbackId ?? void 0;
32906
34792
  return {
32907
- fragments: slotPage.fragments,
34793
+ fragments,
32908
34794
  height: box.height,
32909
34795
  contentHeight: variant.layout.height ?? box.height,
32910
34796
  offset: box.offset,
@@ -32927,6 +34813,40 @@ createDecorationProvider_fn = function(kind, layout) {
32927
34813
  };
32928
34814
  };
32929
34815
  };
34816
+ /**
34817
+ * Finds the header/footer page layout for a given page number with bucket fallback.
34818
+ *
34819
+ * Lookup strategy:
34820
+ * 1. Try exact match first (find page with matching number)
34821
+ * 2. If bucketing is used, fall back to the bucket's representative page
34822
+ * 3. Finally, fall back to the first available page
34823
+ *
34824
+ * Digit buckets (for large documents):
34825
+ * - d1: pages 1-9 → representative page 5
34826
+ * - d2: pages 10-99 → representative page 50
34827
+ * - d3: pages 100-999 → representative page 500
34828
+ * - d4: pages 1000+ → representative page 5000
34829
+ *
34830
+ * @param pages - Array of header/footer layout pages from the variant
34831
+ * @param pageNumber - Physical page number to find layout for (1-indexed)
34832
+ * @returns Header/footer page layout, or undefined if no suitable page found
34833
+ */
34834
+ findHeaderFooterPageForPageNumber_fn = function(pages, pageNumber) {
34835
+ if (!pages || pages.length === 0) {
34836
+ return void 0;
34837
+ }
34838
+ const exactMatch = pages.find((p) => p.number === pageNumber);
34839
+ if (exactMatch) {
34840
+ return exactMatch;
34841
+ }
34842
+ const bucket = getBucketForPageNumber(pageNumber);
34843
+ const representative = getBucketRepresentative(bucket);
34844
+ const bucketMatch = pages.find((p) => p.number === representative);
34845
+ if (bucketMatch) {
34846
+ return bucketMatch;
34847
+ }
34848
+ return pages[0];
34849
+ };
32930
34850
  computeDecorationBox_fn = function(kind, pageMargins, pageHeight) {
32931
34851
  const margins = pageMargins ?? __privateGet(this, _layoutOptions).margins ?? DEFAULT_MARGINS;
32932
34852
  const pageSize = __privateGet(this, _layoutOptions).pageSize ?? DEFAULT_PAGE_SIZE;
@@ -44560,15 +46480,21 @@ const Image = Node$1.create({
44560
46480
  */
44561
46481
  simplePos: { rendered: false },
44562
46482
  extension: { rendered: false },
46483
+ shouldStretch: {
46484
+ default: false,
46485
+ rendered: false
46486
+ },
44563
46487
  size: {
44564
46488
  default: {},
44565
- renderDOM: ({ size, extension }) => {
46489
+ renderDOM: ({ size, extension, shouldStretch }) => {
44566
46490
  let style = "";
44567
46491
  let { width, height } = size ?? {};
44568
46492
  if (width) style += `width: ${width}px;`;
44569
46493
  if (height && ["emf", "wmf"].includes(extension))
44570
46494
  style += `height: ${height}px; border: 1px solid black; position: absolute;`;
44571
- else if (height) style += "height: auto;";
46495
+ else if (height && shouldStretch) {
46496
+ style += `height: ${height}px; object-fit: fill;`;
46497
+ } else if (height) style += "height: auto;";
44572
46498
  return { style };
44573
46499
  }
44574
46500
  },
@@ -44632,7 +46558,11 @@ const Image = Node$1.create({
44632
46558
  switch (type) {
44633
46559
  case "None":
44634
46560
  style += "position: absolute;";
44635
- if (attrs.behindDoc) {
46561
+ const relativeHeight = node.attrs.originalAttributes?.relativeHeight;
46562
+ if (relativeHeight != null) {
46563
+ const zIndex = Math.floor(relativeHeight / 1e6);
46564
+ style += `z-index: ${zIndex};`;
46565
+ } else if (attrs.behindDoc) {
44636
46566
  style += "z-index: -1;";
44637
46567
  } else {
44638
46568
  style += "z-index: 1;";
@@ -44738,12 +46668,20 @@ const Image = Node$1.create({
44738
46668
  if (!style.includes("float: left;")) {
44739
46669
  style += "float: left;";
44740
46670
  }
46671
+ } else if (!anchorData.alignH && marginOffset?.horizontal != null) {
46672
+ const isAbsolutelyPositioned = style.includes("position: absolute;");
46673
+ if (isAbsolutelyPositioned) {
46674
+ style += `left: ${baseHorizontal}px;`;
46675
+ style += "max-width: none;";
46676
+ baseHorizontal = 0;
46677
+ }
44741
46678
  }
44742
46679
  break;
44743
46680
  }
44744
46681
  }
44745
46682
  if (hasAnchorData || hasMarginOffsets) {
44746
46683
  const relativeFromPageV = anchorData?.vRelativeFrom === "page";
46684
+ const relativeFromMarginV = anchorData?.vRelativeFrom === "margin";
44747
46685
  const maxMarginV = 500;
44748
46686
  const baseTop = Math.max(0, marginOffset?.top ?? 0);
44749
46687
  let rotationHorizontal = 0;
@@ -44764,7 +46702,7 @@ const Image = Node$1.create({
44764
46702
  margin.left += horizontal;
44765
46703
  }
44766
46704
  }
44767
- if (top2) {
46705
+ if (top2 && !relativeFromMarginV) {
44768
46706
  if (relativeFromPageV && top2 >= maxMarginV) margin.top += maxMarginV;
44769
46707
  else margin.top += top2;
44770
46708
  }
@@ -45596,15 +47534,24 @@ const ContentBlock = Node$1.create({
45596
47534
  },
45597
47535
  size: {
45598
47536
  default: null,
45599
- renderDOM: ({ size }) => {
45600
- if (!size) return {};
47537
+ renderDOM: (attrs) => {
47538
+ if (!attrs.size) return {};
45601
47539
  let style = "";
45602
- if (size.top) style += `top: ${size.top}px; `;
45603
- if (size.left) style += `left: ${size.left}px; `;
45604
- if (size.width) style += `width: ${size.width.toString().endsWith("%") ? size.width : `${size.width}px`}; `;
45605
- if (size.height) {
45606
- const heightValue = size.height.toString().endsWith("%") ? size.height : `${size.height}px`;
45607
- style += `height: ${heightValue}; `;
47540
+ if (attrs.size.top) style += `top: ${attrs.size.top}px; `;
47541
+ if (attrs.size.left) style += `left: ${attrs.size.left}px; `;
47542
+ if (attrs.size.width)
47543
+ style += `width: ${attrs.size.width.toString().endsWith("%") ? attrs.size.width : `${attrs.size.width}px`}; `;
47544
+ if (attrs.size.height)
47545
+ style += `height: ${attrs.size.height.toString().endsWith("%") ? attrs.size.height : `${attrs.size.height}px`}; `;
47546
+ if (attrs.marginOffset?.horizontal != null || attrs.marginOffset?.top != null) {
47547
+ style += "position: absolute; ";
47548
+ const relativeHeight = attrs.originalAttributes?.relativeHeight;
47549
+ if (relativeHeight != null) {
47550
+ const zIndex = Math.floor(relativeHeight / 1e6);
47551
+ style += `z-index: ${zIndex}; `;
47552
+ } else {
47553
+ style += "z-index: 1; ";
47554
+ }
45608
47555
  }
45609
47556
  return { style };
45610
47557
  }
@@ -45623,6 +47570,13 @@ const ContentBlock = Node$1.create({
45623
47570
  },
45624
47571
  attributes: {
45625
47572
  rendered: false
47573
+ },
47574
+ originalAttributes: {
47575
+ rendered: false
47576
+ },
47577
+ marginOffset: {
47578
+ default: null,
47579
+ rendered: false
45626
47580
  }
45627
47581
  };
45628
47582
  },
@@ -46012,8 +47966,167 @@ const TableOfContents = Node$1.create({
46012
47966
  };
46013
47967
  }
46014
47968
  });
47969
+ function createGradient(gradientData, gradientId) {
47970
+ const { gradientType, stops, angle } = gradientData;
47971
+ if (!stops || stops.length === 0) {
47972
+ return null;
47973
+ }
47974
+ let gradient;
47975
+ if (gradientType === "linear") {
47976
+ gradient = document.createElementNS("http://www.w3.org/2000/svg", "linearGradient");
47977
+ gradient.setAttribute("id", gradientId);
47978
+ const radians = angle * Math.PI / 180;
47979
+ const x1 = 50 - 50 * Math.cos(radians);
47980
+ const y1 = 50 + 50 * Math.sin(radians);
47981
+ const x2 = 50 + 50 * Math.cos(radians);
47982
+ const y2 = 50 - 50 * Math.sin(radians);
47983
+ gradient.setAttribute("x1", `${x1}%`);
47984
+ gradient.setAttribute("y1", `${y1}%`);
47985
+ gradient.setAttribute("x2", `${x2}%`);
47986
+ gradient.setAttribute("y2", `${y2}%`);
47987
+ } else {
47988
+ gradient = document.createElementNS("http://www.w3.org/2000/svg", "radialGradient");
47989
+ gradient.setAttribute("id", gradientId);
47990
+ gradient.setAttribute("cx", "50%");
47991
+ gradient.setAttribute("cy", "50%");
47992
+ gradient.setAttribute("r", "50%");
47993
+ }
47994
+ stops.forEach((stop) => {
47995
+ const stopElement = document.createElementNS("http://www.w3.org/2000/svg", "stop");
47996
+ stopElement.setAttribute("offset", `${stop.position * 100}%`);
47997
+ stopElement.setAttribute("stop-color", stop.color);
47998
+ if (stop.alpha != null && stop.alpha < 1) {
47999
+ stopElement.setAttribute("stop-opacity", stop.alpha.toString());
48000
+ }
48001
+ gradient.appendChild(stopElement);
48002
+ });
48003
+ return gradient;
48004
+ }
48005
+ function createTextElement(textContent2, textAlign, width, height) {
48006
+ const foreignObject = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
48007
+ foreignObject.setAttribute("x", "0");
48008
+ foreignObject.setAttribute("y", "0");
48009
+ foreignObject.setAttribute("width", width.toString());
48010
+ foreignObject.setAttribute("height", height.toString());
48011
+ const div2 = document.createElement("div");
48012
+ div2.style.width = "100%";
48013
+ div2.style.height = "100%";
48014
+ div2.style.display = "flex";
48015
+ div2.style.flexDirection = "column";
48016
+ div2.style.justifyContent = "center";
48017
+ div2.style.padding = "10px";
48018
+ div2.style.boxSizing = "border-box";
48019
+ div2.style.wordWrap = "break-word";
48020
+ div2.style.overflowWrap = "break-word";
48021
+ div2.style.fontSize = "12px";
48022
+ div2.style.lineHeight = "1.2";
48023
+ if (textAlign === "center") {
48024
+ div2.style.textAlign = "center";
48025
+ } else if (textAlign === "right" || textAlign === "r") {
48026
+ div2.style.textAlign = "right";
48027
+ } else {
48028
+ div2.style.textAlign = "left";
48029
+ }
48030
+ let currentParagraph = document.createElement("div");
48031
+ textContent2.parts.forEach((part) => {
48032
+ if (part.isLineBreak) {
48033
+ div2.appendChild(currentParagraph);
48034
+ currentParagraph = document.createElement("div");
48035
+ if (part.isEmptyParagraph) {
48036
+ currentParagraph.style.minHeight = "1em";
48037
+ }
48038
+ } else {
48039
+ const span = document.createElement("span");
48040
+ span.textContent = part.text;
48041
+ if (part.formatting) {
48042
+ if (part.formatting.bold) {
48043
+ span.style.fontWeight = "bold";
48044
+ }
48045
+ if (part.formatting.italic) {
48046
+ span.style.fontStyle = "italic";
48047
+ }
48048
+ if (part.formatting.color) {
48049
+ span.style.color = `#${part.formatting.color}`;
48050
+ }
48051
+ if (part.formatting.fontSize) {
48052
+ span.style.fontSize = `${part.formatting.fontSize}px`;
48053
+ }
48054
+ }
48055
+ currentParagraph.appendChild(span);
48056
+ }
48057
+ });
48058
+ div2.appendChild(currentParagraph);
48059
+ foreignObject.appendChild(div2);
48060
+ return foreignObject;
48061
+ }
48062
+ function applyGradientToSVG(svg, gradientData) {
48063
+ const { gradientType, stops, angle } = gradientData;
48064
+ const gradientId = `gradient-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
48065
+ let defs = svg.querySelector("defs");
48066
+ if (!defs) {
48067
+ defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
48068
+ svg.insertBefore(defs, svg.firstChild);
48069
+ }
48070
+ let gradient;
48071
+ if (gradientType === "linear") {
48072
+ gradient = document.createElementNS("http://www.w3.org/2000/svg", "linearGradient");
48073
+ gradient.setAttribute("id", gradientId);
48074
+ const radians = angle * Math.PI / 180;
48075
+ const x1 = 50 - 50 * Math.cos(radians);
48076
+ const y1 = 50 + 50 * Math.sin(radians);
48077
+ const x2 = 50 + 50 * Math.cos(radians);
48078
+ const y2 = 50 - 50 * Math.sin(radians);
48079
+ gradient.setAttribute("x1", `${x1}%`);
48080
+ gradient.setAttribute("y1", `${y1}%`);
48081
+ gradient.setAttribute("x2", `${x2}%`);
48082
+ gradient.setAttribute("y2", `${y2}%`);
48083
+ } else {
48084
+ gradient = document.createElementNS("http://www.w3.org/2000/svg", "radialGradient");
48085
+ gradient.setAttribute("id", gradientId);
48086
+ gradient.setAttribute("cx", "50%");
48087
+ gradient.setAttribute("cy", "50%");
48088
+ gradient.setAttribute("r", "50%");
48089
+ }
48090
+ stops.forEach((stop) => {
48091
+ const stopElement = document.createElementNS("http://www.w3.org/2000/svg", "stop");
48092
+ stopElement.setAttribute("offset", `${stop.position * 100}%`);
48093
+ stopElement.setAttribute("stop-color", stop.color);
48094
+ if (stop.alpha != null && stop.alpha < 1) {
48095
+ stopElement.setAttribute("stop-opacity", stop.alpha.toString());
48096
+ }
48097
+ gradient.appendChild(stopElement);
48098
+ });
48099
+ defs.appendChild(gradient);
48100
+ const filledElements = svg.querySelectorAll('[fill]:not([fill="none"])');
48101
+ filledElements.forEach((el) => {
48102
+ el.setAttribute("fill", `url(#${gradientId})`);
48103
+ });
48104
+ }
48105
+ function applyAlphaToSVG(svg, alphaData) {
48106
+ const { color, alpha } = alphaData;
48107
+ const filledElements = svg.querySelectorAll('[fill]:not([fill="none"])');
48108
+ filledElements.forEach((el) => {
48109
+ el.setAttribute("fill", color);
48110
+ el.setAttribute("fill-opacity", alpha.toString());
48111
+ });
48112
+ }
48113
+ function generateTransforms(attrs) {
48114
+ const transforms = [];
48115
+ if (attrs.rotation != null) {
48116
+ transforms.push(`rotate(${attrs.rotation}deg)`);
48117
+ }
48118
+ if (attrs.flipH) {
48119
+ transforms.push(`scaleX(-1)`);
48120
+ }
48121
+ if (attrs.flipV) {
48122
+ transforms.push(`scaleY(-1)`);
48123
+ }
48124
+ return transforms;
48125
+ }
48126
+ const Z_INDEX_SCALE_FACTOR = 1e6;
46015
48127
  class VectorShapeView {
46016
48128
  constructor(props) {
48129
+ __privateAdd(this, _VectorShapeView_instances);
46017
48130
  __publicField(this, "node");
46018
48131
  __publicField(this, "view");
46019
48132
  __publicField(this, "getPos");
@@ -46035,6 +48148,7 @@ class VectorShapeView {
46035
48148
  }
46036
48149
  mount() {
46037
48150
  this.buildView();
48151
+ __privateMethod(this, _VectorShapeView_instances, ensureParentPositioned_fn).call(this);
46038
48152
  }
46039
48153
  get dom() {
46040
48154
  return this.root;
@@ -46049,49 +48163,264 @@ class VectorShapeView {
46049
48163
  element.setAttribute("data-vector-shape", "");
46050
48164
  element.style.width = `${attrs.width}px`;
46051
48165
  element.style.height = `${attrs.height}px`;
48166
+ const positioningStyle = this.getPositioningStyle(attrs);
48167
+ if (positioningStyle) {
48168
+ element.style.cssText += positioningStyle;
48169
+ }
46052
48170
  const transforms = this.generateTransform();
46053
- if (transforms.length > 0) {
46054
- element.style.transform = transforms.join(" ");
48171
+ const positioningTransform = element.style.transform;
48172
+ const combinedTransforms = [];
48173
+ if (positioningTransform && positioningTransform.trim() !== "") {
48174
+ combinedTransforms.push(positioningTransform.trim());
48175
+ }
48176
+ if (Array.isArray(transforms) && transforms.length > 0) {
48177
+ const validTransforms = transforms.filter(
48178
+ (t) => t !== null && t !== void 0 && typeof t === "string" && t.trim() !== ""
48179
+ );
48180
+ if (validTransforms.length > 0) {
48181
+ combinedTransforms.push(...validTransforms);
48182
+ }
46055
48183
  }
46056
- const svgTemplate = this.generateSVG({
46057
- kind: attrs.kind,
46058
- fillColor: attrs.fillColor,
46059
- strokeColor: attrs.strokeColor,
46060
- strokeWidth: attrs.strokeWidth
46061
- });
46062
- if (svgTemplate) {
46063
- element.innerHTML = svgTemplate;
48184
+ if (combinedTransforms.length > 0) {
48185
+ element.style.transform = combinedTransforms.join(" ");
48186
+ }
48187
+ const svg = this.createSVGElement(attrs);
48188
+ if (svg) {
48189
+ element.appendChild(svg);
48190
+ if (attrs.textContent && attrs.textContent.parts) {
48191
+ const textElement = this.createTextElement(attrs.textContent, attrs.textAlign, attrs.width, attrs.height);
48192
+ if (textElement) {
48193
+ svg.appendChild(textElement);
48194
+ }
48195
+ }
46064
48196
  }
46065
48197
  return { element };
46066
48198
  }
46067
- generateTransform() {
46068
- const attrs = this.node.attrs;
46069
- const transforms = [];
46070
- if (attrs.rotation != null) {
46071
- transforms.push(`rotate(${attrs.rotation}deg)`);
48199
+ getPositioningStyle(attrs) {
48200
+ const { anchorData, marginOffset, wrap, originalAttributes } = attrs;
48201
+ if (!anchorData && !marginOffset?.horizontal && !marginOffset?.top) {
48202
+ return "";
46072
48203
  }
46073
- if (attrs.flipH) {
46074
- transforms.push(`scaleX(-1)`);
48204
+ let style = "";
48205
+ const margin = { left: 0, right: 0, top: 0 };
48206
+ let centered = false;
48207
+ let floatRight = false;
48208
+ let baseHorizontal = marginOffset?.horizontal || 0;
48209
+ if (wrap?.type === "None") {
48210
+ style += "position: absolute;";
48211
+ const relativeHeight = originalAttributes?.relativeHeight;
48212
+ if (relativeHeight != null) {
48213
+ const zIndex = Math.floor(relativeHeight / Z_INDEX_SCALE_FACTOR);
48214
+ style += `z-index: ${zIndex};`;
48215
+ } else if (wrap?.attrs?.behindDoc) {
48216
+ style += "z-index: -1;";
48217
+ } else {
48218
+ style += "z-index: 1;";
48219
+ }
46075
48220
  }
46076
- if (attrs.flipV) {
46077
- transforms.push(`scaleY(-1)`);
48221
+ if (anchorData) {
48222
+ switch (anchorData.hRelativeFrom) {
48223
+ case "page":
48224
+ const pageStyles2 = this.editor?.converter?.pageStyles || this.editor?.options?.parentEditor?.converter?.pageStyles;
48225
+ margin.left -= inchesToPixels(pageStyles2?.pageMargins?.left) || 0;
48226
+ break;
48227
+ case "margin":
48228
+ if (anchorData.alignH === "center") {
48229
+ style += "position: absolute; left: 50%; transform: translateX(-50%);";
48230
+ }
48231
+ if (anchorData.alignH === "left" || anchorData.alignH === "right") {
48232
+ style += `position: absolute; ${anchorData.alignH}: 0;`;
48233
+ }
48234
+ break;
48235
+ case "column":
48236
+ if (anchorData.alignH === "center") {
48237
+ centered = true;
48238
+ } else if (anchorData.alignH === "right") {
48239
+ floatRight = true;
48240
+ if (!style.includes("float: right;")) {
48241
+ style += "float: right;";
48242
+ }
48243
+ } else if (anchorData.alignH === "left") {
48244
+ if (!style.includes("float: left;")) {
48245
+ style += "float: left;";
48246
+ }
48247
+ } else if (!anchorData.alignH && marginOffset?.horizontal != null) {
48248
+ const isAbsolutelyPositioned2 = style.includes("position: absolute;");
48249
+ if (isAbsolutelyPositioned2) {
48250
+ style += `left: ${baseHorizontal}px;`;
48251
+ baseHorizontal = 0;
48252
+ }
48253
+ }
48254
+ break;
48255
+ }
48256
+ }
48257
+ const isAbsolutelyPositioned = style.includes("position: absolute;");
48258
+ if (anchorData || marginOffset?.horizontal != null || marginOffset?.top != null) {
48259
+ const horizontal = baseHorizontal;
48260
+ const top2 = marginOffset?.top ?? 0;
48261
+ if (isAbsolutelyPositioned) {
48262
+ if (horizontal && !style.includes("left:")) {
48263
+ style += `left: ${horizontal}px;`;
48264
+ }
48265
+ if (top2 != null) {
48266
+ style += `top: ${top2}px;`;
48267
+ }
48268
+ } else {
48269
+ if (horizontal) {
48270
+ if (floatRight) {
48271
+ margin.right += horizontal;
48272
+ } else {
48273
+ margin.left += horizontal;
48274
+ }
48275
+ }
48276
+ if (top2 > 0) {
48277
+ margin.top += top2;
48278
+ }
48279
+ }
48280
+ }
48281
+ if (centered) {
48282
+ style += "margin-left: auto; margin-right: auto;";
48283
+ } else if (!isAbsolutelyPositioned) {
48284
+ if (margin.left) style += `margin-left: ${margin.left}px;`;
48285
+ if (margin.right) style += `margin-right: ${margin.right}px;`;
46078
48286
  }
46079
- return transforms;
48287
+ if (!isAbsolutelyPositioned && margin.top) style += `margin-top: ${margin.top}px;`;
48288
+ return style;
46080
48289
  }
46081
- generateSVG({ kind, fillColor, strokeColor, strokeWidth }) {
48290
+ generateTransform() {
48291
+ return generateTransforms(this.node.attrs);
48292
+ }
48293
+ createSVGElement(attrs) {
48294
+ const { kind, fillColor, strokeColor, strokeWidth, width, height } = attrs;
48295
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
48296
+ svg.setAttribute("width", width.toString());
48297
+ svg.setAttribute("height", height.toString());
48298
+ svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
48299
+ svg.style.display = "block";
48300
+ const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
48301
+ svg.appendChild(defs);
48302
+ let fill = "none";
48303
+ let fillOpacity = 1;
48304
+ if (fillColor) {
48305
+ if (typeof fillColor === "object") {
48306
+ if (fillColor.type === "gradient") {
48307
+ const gradientId = `gradient-${Math.random().toString(36).slice(2, 11)}-${Date.now()}`;
48308
+ const gradient = this.createGradient(fillColor, gradientId);
48309
+ if (gradient) {
48310
+ defs.appendChild(gradient);
48311
+ fill = `url(#${gradientId})`;
48312
+ }
48313
+ } else if (fillColor.type === "solidWithAlpha") {
48314
+ fill = fillColor.color;
48315
+ fillOpacity = fillColor.alpha;
48316
+ }
48317
+ } else {
48318
+ fill = fillColor;
48319
+ }
48320
+ }
48321
+ const stroke = strokeColor === null ? "none" : strokeColor || "none";
48322
+ const strokeW = strokeColor === null ? 0 : strokeColor ? strokeWidth || 1 : 0;
48323
+ let shapeElement;
48324
+ switch (kind) {
48325
+ case "rect":
48326
+ shapeElement = document.createElementNS("http://www.w3.org/2000/svg", "rect");
48327
+ shapeElement.setAttribute("x", "0");
48328
+ shapeElement.setAttribute("y", "0");
48329
+ shapeElement.setAttribute("width", width.toString());
48330
+ shapeElement.setAttribute("height", height.toString());
48331
+ break;
48332
+ case "roundRect":
48333
+ shapeElement = document.createElementNS("http://www.w3.org/2000/svg", "rect");
48334
+ shapeElement.setAttribute("x", "0");
48335
+ shapeElement.setAttribute("y", "0");
48336
+ shapeElement.setAttribute("width", width.toString());
48337
+ shapeElement.setAttribute("height", height.toString());
48338
+ const radius = Math.min(width, height) * 0.05;
48339
+ shapeElement.setAttribute("rx", radius.toString());
48340
+ shapeElement.setAttribute("ry", radius.toString());
48341
+ break;
48342
+ case "ellipse":
48343
+ shapeElement = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");
48344
+ shapeElement.setAttribute("cx", (width / 2).toString());
48345
+ shapeElement.setAttribute("cy", (height / 2).toString());
48346
+ shapeElement.setAttribute("rx", (width / 2).toString());
48347
+ shapeElement.setAttribute("ry", (height / 2).toString());
48348
+ break;
48349
+ case "circle":
48350
+ shapeElement = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");
48351
+ shapeElement.setAttribute("cx", (width / 2).toString());
48352
+ shapeElement.setAttribute("cy", (height / 2).toString());
48353
+ shapeElement.setAttribute("rx", (width / 2).toString());
48354
+ shapeElement.setAttribute("ry", (height / 2).toString());
48355
+ break;
48356
+ default:
48357
+ try {
48358
+ const svgTemplate = this.generateSVG({ kind, fillColor, strokeColor, strokeWidth, width, height });
48359
+ if (svgTemplate) {
48360
+ const tempDiv = document.createElement("div");
48361
+ tempDiv.innerHTML = svgTemplate;
48362
+ const tempSvg = tempDiv.querySelector("svg");
48363
+ if (tempSvg) {
48364
+ tempSvg.setAttribute("width", width.toString());
48365
+ tempSvg.setAttribute("height", height.toString());
48366
+ tempSvg.setAttribute("preserveAspectRatio", "none");
48367
+ tempSvg.style.width = `${width}px`;
48368
+ tempSvg.style.height = `${height}px`;
48369
+ tempSvg.style.display = "block";
48370
+ return tempSvg;
48371
+ }
48372
+ }
48373
+ } catch (error) {
48374
+ console.warn("Failed to generate SVG for shape:", kind, error);
48375
+ return null;
48376
+ }
48377
+ return null;
48378
+ }
48379
+ shapeElement.setAttribute("fill", fill);
48380
+ if (fillOpacity < 1) {
48381
+ shapeElement.setAttribute("fill-opacity", fillOpacity.toString());
48382
+ }
48383
+ shapeElement.setAttribute("stroke", stroke);
48384
+ shapeElement.setAttribute("stroke-width", strokeW.toString());
48385
+ svg.appendChild(shapeElement);
48386
+ return svg;
48387
+ }
48388
+ createGradient(gradientData, gradientId) {
48389
+ return createGradient(gradientData, gradientId);
48390
+ }
48391
+ generateSVG({ kind, fillColor, strokeColor, strokeWidth, width, height }) {
46082
48392
  try {
48393
+ let fill = fillColor || "none";
48394
+ if (fillColor && typeof fillColor === "object") {
48395
+ if (fillColor.type === "gradient") {
48396
+ fill = "#cccccc";
48397
+ } else if (fillColor.type === "solidWithAlpha") {
48398
+ fill = fillColor.color;
48399
+ }
48400
+ }
46083
48401
  return k({
46084
48402
  preset: kind,
46085
48403
  styleOverrides: {
46086
- fill: fillColor || "none",
48404
+ fill,
46087
48405
  stroke: strokeColor || "none",
46088
48406
  strokeWidth: strokeWidth || 0
46089
- }
48407
+ },
48408
+ width,
48409
+ height
46090
48410
  });
46091
48411
  } catch {
46092
48412
  return null;
46093
48413
  }
46094
48414
  }
48415
+ applyGradientToSVG(svg, gradientData) {
48416
+ applyGradientToSVG(svg, gradientData);
48417
+ }
48418
+ applyAlphaToSVG(svg, alphaData) {
48419
+ applyAlphaToSVG(svg, alphaData);
48420
+ }
48421
+ createTextElement(textContent2, textAlign, width, height) {
48422
+ return createTextElement(textContent2, textAlign, width, height);
48423
+ }
46095
48424
  buildView() {
46096
48425
  const { element } = this.createElement();
46097
48426
  this.root = element;
@@ -46100,6 +48429,37 @@ class VectorShapeView {
46100
48429
  return false;
46101
48430
  }
46102
48431
  }
48432
+ _VectorShapeView_instances = new WeakSet();
48433
+ /**
48434
+ * Ensures the parent paragraph element is positioned for absolute-positioned vector shapes.
48435
+ *
48436
+ * For vector shapes with wrap type 'None' (absolutely positioned), the parent paragraph
48437
+ * element must have `position: relative` to establish a containing block for CSS absolute
48438
+ * positioning. This allows the vector shape's `top` and `left` offsets to position correctly
48439
+ * relative to the paragraph.
48440
+ *
48441
+ * Uses requestAnimationFrame to defer the DOM manipulation until after the element is fully
48442
+ * mounted in the DOM tree. This prevents race conditions where the parent element might not
48443
+ * yet be available during the initial render phase.
48444
+ *
48445
+ * Only applies to wrap type 'None' - inline and floated elements do not require this setup.
48446
+ */
48447
+ ensureParentPositioned_fn = function() {
48448
+ const wrapType = this.node.attrs.wrap?.type;
48449
+ if (wrapType !== "None") return;
48450
+ if (typeof globalThis !== "undefined" && globalThis.requestAnimationFrame) {
48451
+ globalThis.requestAnimationFrame(() => {
48452
+ try {
48453
+ const parent = this.root?.parentElement;
48454
+ if (parent && parent.tagName === "P") {
48455
+ parent.style.position = "relative";
48456
+ }
48457
+ } catch (error) {
48458
+ console.warn("Failed to position parent element for vector shape:", error);
48459
+ }
48460
+ });
48461
+ }
48462
+ };
46103
48463
  const VectorShape = Node$1.create({
46104
48464
  name: "vectorShape",
46105
48465
  group: "inline",
@@ -46175,8 +48535,42 @@ const VectorShape = Node$1.create({
46175
48535
  return { "data-flip-v": attrs.flipV };
46176
48536
  }
46177
48537
  },
48538
+ wrap: {
48539
+ default: { type: "Inline" },
48540
+ rendered: false
48541
+ },
48542
+ anchorData: {
48543
+ default: null,
48544
+ rendered: false
48545
+ },
48546
+ isAnchor: {
48547
+ rendered: false
48548
+ },
48549
+ marginOffset: {
48550
+ default: {},
48551
+ rendered: false
48552
+ },
46178
48553
  drawingContent: {
46179
48554
  rendered: false
48555
+ },
48556
+ originalAttributes: {
48557
+ rendered: false
48558
+ },
48559
+ textContent: {
48560
+ default: null,
48561
+ rendered: false
48562
+ },
48563
+ textAlign: {
48564
+ default: "center",
48565
+ rendered: false
48566
+ },
48567
+ textVerticalAlign: {
48568
+ default: "center",
48569
+ rendered: false
48570
+ },
48571
+ textInsets: {
48572
+ default: null,
48573
+ rendered: false
46180
48574
  }
46181
48575
  };
46182
48576
  },
@@ -46197,6 +48591,7 @@ const VectorShape = Node$1.create({
46197
48591
  });
46198
48592
  class ShapeGroupView {
46199
48593
  constructor(props) {
48594
+ __privateAdd(this, _ShapeGroupView_instances);
46200
48595
  __publicField(this, "node");
46201
48596
  __publicField(this, "view");
46202
48597
  __publicField(this, "getPos");
@@ -46218,6 +48613,7 @@ class ShapeGroupView {
46218
48613
  }
46219
48614
  mount() {
46220
48615
  this.buildView();
48616
+ __privateMethod(this, _ShapeGroupView_instances, ensureParentPositioned_fn2).call(this);
46221
48617
  }
46222
48618
  get dom() {
46223
48619
  return this.root;
@@ -46227,7 +48623,7 @@ class ShapeGroupView {
46227
48623
  }
46228
48624
  createElement() {
46229
48625
  const attrs = this.node.attrs;
46230
- const { groupTransform, shapes, size } = attrs;
48626
+ const { groupTransform, shapes, size, marginOffset, originalAttributes, wrap, anchorData } = attrs;
46231
48627
  const container = document.createElement("div");
46232
48628
  container.classList.add("sd-shape-group");
46233
48629
  container.setAttribute("data-shape-group", "");
@@ -46237,6 +48633,55 @@ class ShapeGroupView {
46237
48633
  container.style.height = `${height}px`;
46238
48634
  container.style.position = "relative";
46239
48635
  container.style.display = "inline-block";
48636
+ const wrapType = wrap?.type || "Inline";
48637
+ if (wrapType === "None") {
48638
+ container.style.position = "absolute";
48639
+ if (marginOffset?.horizontal != null) {
48640
+ container.style.left = `${marginOffset.horizontal}px`;
48641
+ }
48642
+ const isColumnRelative = anchorData?.hRelativeFrom === "column";
48643
+ if (isColumnRelative && !anchorData?.alignH && marginOffset?.horizontal != null) {
48644
+ container.style.maxWidth = "none";
48645
+ }
48646
+ if (marginOffset?.top != null) {
48647
+ container.style.top = `${marginOffset.top}px`;
48648
+ }
48649
+ const relativeHeight = originalAttributes?.relativeHeight;
48650
+ if (relativeHeight != null) {
48651
+ const zIndex = Math.floor(relativeHeight / 1e6);
48652
+ container.style.zIndex = zIndex.toString();
48653
+ } else {
48654
+ container.style.zIndex = "1";
48655
+ }
48656
+ } else if (wrapType === "Square") {
48657
+ container.style.float = "left";
48658
+ container.style.clear = "both";
48659
+ if (marginOffset?.horizontal != null) {
48660
+ container.style.marginLeft = `${marginOffset.horizontal}px`;
48661
+ }
48662
+ if (marginOffset?.top != null) {
48663
+ container.style.marginTop = `${marginOffset.top}px`;
48664
+ }
48665
+ if (wrap?.attrs?.distLeft) {
48666
+ container.style.marginLeft = `${(marginOffset?.horizontal || 0) + wrap.attrs.distLeft}px`;
48667
+ }
48668
+ if (wrap?.attrs?.distRight) {
48669
+ container.style.marginRight = `${wrap.attrs.distRight}px`;
48670
+ }
48671
+ if (wrap?.attrs?.distTop) {
48672
+ container.style.marginTop = `${(marginOffset?.top || 0) + wrap.attrs.distTop}px`;
48673
+ }
48674
+ if (wrap?.attrs?.distBottom) {
48675
+ container.style.marginBottom = `${wrap.attrs.distBottom}px`;
48676
+ }
48677
+ } else {
48678
+ if (marginOffset?.horizontal != null) {
48679
+ container.style.marginLeft = `${marginOffset.horizontal}px`;
48680
+ }
48681
+ if (marginOffset?.top != null) {
48682
+ container.style.marginTop = `${marginOffset.top}px`;
48683
+ }
48684
+ }
46240
48685
  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
46241
48686
  svg.setAttribute("version", "1.1");
46242
48687
  svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
@@ -46244,26 +48689,33 @@ class ShapeGroupView {
46244
48689
  svg.setAttribute("height", height.toString());
46245
48690
  svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
46246
48691
  svg.style.display = "block";
48692
+ const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
48693
+ svg.appendChild(defs);
46247
48694
  if (shapes && Array.isArray(shapes)) {
46248
- shapes.forEach((shape) => {
48695
+ shapes.forEach((shape, index2) => {
46249
48696
  if (shape.shapeType === "vectorShape") {
46250
- const shapeElement = this.createShapeElement(shape, groupTransform);
48697
+ const shapeElement = this.createShapeElement(shape, groupTransform, defs, index2);
46251
48698
  if (shapeElement) {
46252
48699
  svg.appendChild(shapeElement);
46253
48700
  }
48701
+ } else if (shape.shapeType === "image") {
48702
+ const imageElement = this.createImageElement(shape, groupTransform);
48703
+ if (imageElement) {
48704
+ svg.appendChild(imageElement);
48705
+ }
46254
48706
  }
46255
48707
  });
46256
48708
  }
46257
48709
  container.appendChild(svg);
46258
48710
  return { element: container };
46259
48711
  }
46260
- createShapeElement(shape) {
48712
+ createShapeElement(shape, groupTransform, defs, shapeIndex) {
46261
48713
  const attrs = shape.attrs;
46262
48714
  if (!attrs) return null;
46263
- const x = attrs.x || 0;
46264
- const y = attrs.y || 0;
46265
- const width = attrs.width || 100;
46266
- const height = attrs.height || 100;
48715
+ const x = attrs.x ?? 0;
48716
+ const y = attrs.y ?? 0;
48717
+ const width = attrs.width ?? 100;
48718
+ const height = attrs.height ?? 100;
46267
48719
  const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
46268
48720
  const transforms = [];
46269
48721
  transforms.push(`translate(${x}, ${y})`);
@@ -46280,16 +48732,44 @@ class ShapeGroupView {
46280
48732
  g.setAttribute("transform", transforms.join(" "));
46281
48733
  }
46282
48734
  const shapeKind = attrs.kind || "rect";
46283
- const fillColor = attrs.fillColor || "#5b9bd5";
46284
- const strokeColor = attrs.strokeColor || "#000000";
46285
- const strokeWidth = attrs.strokeWidth || 1;
48735
+ const fillColor = attrs.fillColor === null ? null : attrs.fillColor ?? "#5b9bd5";
48736
+ const strokeColor = attrs.strokeColor === null ? null : attrs.strokeColor ?? "#000000";
48737
+ const strokeWidth = attrs.strokeWidth ?? 1;
48738
+ let fillValue = fillColor;
48739
+ if (fillColor && typeof fillColor === "object" && fillColor.type === "gradient") {
48740
+ const gradientId = `gradient-${shapeIndex}-${Date.now()}-${Math.floor(Math.random() * 1e9)}`;
48741
+ const gradient = this.createGradient(fillColor, gradientId);
48742
+ defs.appendChild(gradient);
48743
+ fillValue = `url(#${gradientId})`;
48744
+ } else if (fillColor === null) {
48745
+ fillValue = "none";
48746
+ } else if (typeof fillColor === "string") {
48747
+ fillValue = fillColor;
48748
+ }
48749
+ if (shapeKind === "line") {
48750
+ const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
48751
+ line.setAttribute("x1", "0");
48752
+ line.setAttribute("y1", "0");
48753
+ line.setAttribute("x2", width.toString());
48754
+ line.setAttribute("y2", height.toString());
48755
+ line.setAttribute("stroke", strokeColor === null ? "none" : strokeColor);
48756
+ line.setAttribute("stroke-width", (strokeColor === null ? 0 : strokeWidth).toString());
48757
+ g.appendChild(line);
48758
+ if (attrs.textContent && attrs.textContent.parts) {
48759
+ const textGroup = this.createTextElement(attrs.textContent, attrs.textAlign, width, height);
48760
+ if (textGroup) {
48761
+ g.appendChild(textGroup);
48762
+ }
48763
+ }
48764
+ return g;
48765
+ }
46286
48766
  try {
46287
48767
  const svgContent = k({
46288
48768
  preset: shapeKind,
46289
48769
  styleOverrides: {
46290
- fill: fillColor || "none",
46291
- stroke: strokeColor || "none",
46292
- strokeWidth: strokeWidth || 0
48770
+ fill: fillValue || "none",
48771
+ stroke: strokeColor === null ? "none" : strokeColor,
48772
+ strokeWidth: strokeColor === null ? 0 : strokeWidth
46293
48773
  },
46294
48774
  width,
46295
48775
  height
@@ -46358,13 +48838,42 @@ class ShapeGroupView {
46358
48838
  const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
46359
48839
  rect.setAttribute("width", width.toString());
46360
48840
  rect.setAttribute("height", height.toString());
46361
- rect.setAttribute("fill", fillColor);
46362
- rect.setAttribute("stroke", strokeColor);
46363
- rect.setAttribute("stroke-width", strokeWidth.toString());
48841
+ rect.setAttribute("fill", fillColor === null ? "none" : typeof fillColor === "string" ? fillColor : "#cccccc");
48842
+ rect.setAttribute("stroke", strokeColor === null ? "none" : strokeColor);
48843
+ rect.setAttribute("stroke-width", strokeColor === null ? "0" : strokeWidth.toString());
46364
48844
  g.appendChild(rect);
46365
48845
  }
48846
+ if (attrs.textContent && attrs.textContent.parts) {
48847
+ const textGroup = this.createTextElement(attrs.textContent, attrs.textAlign, width, height);
48848
+ if (textGroup) {
48849
+ g.appendChild(textGroup);
48850
+ }
48851
+ }
46366
48852
  return g;
46367
48853
  }
48854
+ createTextElement(textContent2, textAlign, width, height) {
48855
+ return createTextElement(textContent2, textAlign, width, height);
48856
+ }
48857
+ createGradient(gradientData, gradientId) {
48858
+ return createGradient(gradientData, gradientId);
48859
+ }
48860
+ createImageElement(shape, _groupTransform) {
48861
+ const attrs = shape.attrs;
48862
+ if (!attrs) return null;
48863
+ const x = attrs.x || 0;
48864
+ const y = attrs.y || 0;
48865
+ const width = attrs.width || 100;
48866
+ const height = attrs.height || 100;
48867
+ const image = document.createElementNS("http://www.w3.org/2000/svg", "image");
48868
+ image.setAttribute("x", x.toString());
48869
+ image.setAttribute("y", y.toString());
48870
+ image.setAttribute("width", width.toString());
48871
+ image.setAttribute("height", height.toString());
48872
+ const src = this.editor?.storage?.image?.media?.[attrs.src] ?? attrs.src;
48873
+ image.setAttribute("href", src);
48874
+ image.setAttribute("preserveAspectRatio", "none");
48875
+ return image;
48876
+ }
46368
48877
  buildView() {
46369
48878
  const { element } = this.createElement();
46370
48879
  this.root = element;
@@ -46373,6 +48882,37 @@ class ShapeGroupView {
46373
48882
  return false;
46374
48883
  }
46375
48884
  }
48885
+ _ShapeGroupView_instances = new WeakSet();
48886
+ /**
48887
+ * Ensures the parent paragraph element is positioned for absolute-positioned shape groups.
48888
+ *
48889
+ * For shape groups with wrap type 'None' (absolutely positioned), the parent paragraph
48890
+ * element must have `position: relative` to establish a containing block for CSS absolute
48891
+ * positioning. This allows the shape group's `top` and `left` offsets to position correctly
48892
+ * relative to the paragraph.
48893
+ *
48894
+ * Uses requestAnimationFrame to defer the DOM manipulation until after the element is fully
48895
+ * mounted in the DOM tree. This prevents race conditions where the parent element might not
48896
+ * yet be available during the initial render phase.
48897
+ *
48898
+ * Only applies to wrap type 'None' - inline and floated elements do not require this setup.
48899
+ */
48900
+ ensureParentPositioned_fn2 = function() {
48901
+ const wrapType = this.node.attrs.wrap?.type || "Inline";
48902
+ if (wrapType !== "None") return;
48903
+ if (typeof globalThis !== "undefined" && globalThis.requestAnimationFrame) {
48904
+ globalThis.requestAnimationFrame(() => {
48905
+ try {
48906
+ const parent = this.root?.parentElement;
48907
+ if (parent && parent.tagName === "P") {
48908
+ parent.style.position = "relative";
48909
+ }
48910
+ } catch (error) {
48911
+ console.warn("Failed to position parent element for shape group:", error);
48912
+ }
48913
+ });
48914
+ }
48915
+ };
46376
48916
  const ShapeGroup = Node$1.create({
46377
48917
  name: "shapeGroup",
46378
48918
  group: "inline",
@@ -46429,6 +48969,17 @@ const ShapeGroup = Node$1.create({
46429
48969
  },
46430
48970
  drawingContent: {
46431
48971
  rendered: false
48972
+ },
48973
+ wrap: {
48974
+ default: { type: "Inline" },
48975
+ rendered: false
48976
+ },
48977
+ anchorData: {
48978
+ default: null,
48979
+ rendered: false
48980
+ },
48981
+ originalAttributes: {
48982
+ rendered: false
46432
48983
  }
46433
48984
  };
46434
48985
  },