@harbour-enterprises/superdoc 1.0.0-next.2 → 1.0.0-next.4

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-B-xTd4XY.cjs → PdfViewer-CtSTAdvv.cjs} +1 -1
  2. package/dist/chunks/{PdfViewer-eykNsWyi.es.js → PdfViewer-DtdN17V4.es.js} +2 -2
  3. package/dist/chunks/{eventemitter3-CcXAdeql.es.js → eventemitter3-44XulWQe.es.js} +1 -1
  4. package/dist/chunks/{index-rF5HExWB.cjs → index-Bj1kFbYe.cjs} +465 -228
  5. package/dist/chunks/{index-DpQ8ZYM0.es.js → index-Cxv7dMYN.es.js} +468 -231
  6. package/dist/chunks/{index-BDVXUeCy-7mwhYeJ7.es.js → index-D_KE9gpD-aIqhxcuF.es.js} +1 -1
  7. package/dist/chunks/{index-BDVXUeCy-Di6ozaOM.cjs → index-D_KE9gpD-radGpP4I.cjs} +1 -1
  8. package/dist/chunks/{jszip-5vvIqAEE.es.js → jszip-VP334ufO.es.js} +1 -1
  9. package/dist/chunks/{super-editor.es-CxajnL9u.es.js → super-editor.es-B2fSLkzN.es.js} +1033 -493
  10. package/dist/chunks/{super-editor.es-CcKbh84I.cjs → super-editor.es-BhQu31e4.cjs} +1032 -492
  11. package/dist/chunks/{vue-Dysv_7z5.es.js → vue-BuPTonTJ.es.js} +27 -27
  12. package/dist/chunks/xml-js-LkEmUa9-.es.js +2 -0
  13. package/dist/packages/superdoc/src/composables/useUiFontFamily.d.ts +42 -0
  14. package/dist/packages/superdoc/src/composables/useUiFontFamily.d.ts.map +1 -0
  15. package/dist/packages/superdoc/src/core/SuperDoc.d.ts +3 -3
  16. package/dist/packages/superdoc/src/core/SuperDoc.d.ts.map +1 -1
  17. package/dist/packages/superdoc/src/core/types/index.d.ts +4 -167
  18. package/dist/packages/superdoc/src/core/types/index.d.ts.map +1 -1
  19. package/dist/style.css +88 -86
  20. package/dist/super-editor/ai-writer.es.js +3 -3
  21. package/dist/super-editor/chunks/{converter-DN_dhslo.js → converter-CVqUjX24.js} +1 -1
  22. package/dist/super-editor/chunks/{docx-zipper-Bhl_yBjL.js → docx-zipper-CsWjoVKD.js} +1 -1
  23. package/dist/super-editor/chunks/{editor-3klx7hyV.js → editor-Cc8nus2C.js} +429 -129
  24. package/dist/super-editor/chunks/{index-BDVXUeCy.js → index-D_KE9gpD.js} +1 -1
  25. package/dist/super-editor/chunks/{toolbar-8YA9ltNC.js → toolbar-CoOpR1xE.js} +804 -568
  26. package/dist/super-editor/converter.es.js +1 -1
  27. package/dist/super-editor/docx-zipper.es.js +2 -2
  28. package/dist/super-editor/editor.es.js +3 -3
  29. package/dist/super-editor/file-zipper.es.js +1 -1
  30. package/dist/super-editor/style.css +22 -21
  31. package/dist/super-editor/super-editor.es.js +10 -6
  32. package/dist/super-editor/toolbar.es.js +2 -2
  33. package/dist/super-editor.cjs +1 -1
  34. package/dist/super-editor.es.js +2 -2
  35. package/dist/superdoc.cjs +2 -2
  36. package/dist/superdoc.es.js +2 -2
  37. package/dist/superdoc.umd.js +1495 -718
  38. package/dist/superdoc.umd.js.map +1 -1
  39. package/package.json +1 -1
  40. package/dist/chunks/xml-js-ClO_jHnq.es.js +0 -2
@@ -12,8 +12,8 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
12
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, _selectionOverlay, _activeEditorHost, _activeDecorationContainer, _activeRegion, _borderLine, _dimmingOverlay, _EditorOverlayManager_instances, findDecorationContainer_fn, ensureEditorHost_fn, positionEditorHost_fn, hideDimmingOverlay_fn, showHeaderFooterBorder_fn, hideHeaderFooterBorder_fn, _instances, _options, _editor3, _visibleHost, _viewportHost, _painterHost, _selectionOverlay2, _hiddenHost, _layoutOptions, _layoutState, _domPainter, _pageGeometryHelper, _dragHandlerCleanup, _layoutError, _layoutErrorState, _errorBanner, _errorBannerMessage, _telemetryEmitter, _renderScheduled, _pendingDocChange, _isRerendering, _selectionUpdateScheduled, _remoteCursorUpdateScheduled, _rafHandle, _editorListeners, _sectionMetadata, _documentMode, _inputBridge, _trackedChangesMode, _trackedChangesEnabled, _trackedChangesOverrides, _headerFooterManager, _headerFooterAdapter, _headerFooterIdentifier, _multiSectionIdentifier, _headerLayoutResults, _footerLayoutResults, _headerLayoutsByRId, _footerLayoutsByRId, _headerDecorationProvider, _footerDecorationProvider, _headerFooterManagerCleanups, _headerRegions, _footerRegions, _session, _activeHeaderFooterEditor, _overlayManager, _hoverOverlay, _hoverTooltip, _modeBanner, _ariaLiveRegion, _hoverRegion, _clickCount, _lastClickTime, _lastClickPosition, _lastSelectedImageBlockId, _dragAnchor, _isDragging, _dragExtensionMode, _cellAnchor, _cellDragMode, _remoteCursorState, _remoteCursorElements, _remoteCursorDirty, _remoteCursorOverlay, _localSelectionLayer, _awarenessCleanup, _scrollCleanup, _scrollTimeout, _lastRemoteCursorRenderTime, _remoteCursorThrottleTimeout, _PresentationEditor_instances, collectCommentPositions_fn, aggregateLayoutBounds_fn, safeCleanup_fn, setupEditorListeners_fn, setupCollaborationCursors_fn, updateLocalAwarenessCursor_fn, normalizeAwarenessStates_fn, getFallbackColor_fn, getValidatedColor_fn, scheduleRemoteCursorUpdate_fn, scheduleRemoteCursorReRender_fn, updateRemoteCursors_fn, renderRemoteCursors_fn, renderRemoteCaret_fn, renderRemoteCursorLabel_fn, renderRemoteSelection_fn, setupPointerHandlers_fn, setupDragHandlers_fn, focusEditorAfterImageSelection_fn, setupInputBridge_fn, initHeaderFooterRegistry_fn, _handlePointerDown, getFirstTextPosition_fn, registerPointerClick_fn, getCellPosFromTableHit_fn, getTablePosFromHit_fn, shouldUseCellSelection_fn, setCellAnchor_fn, clearCellAnchor_fn, hitTestTable_fn, selectWordAt_fn, selectParagraphAt_fn, calculateExtendedSelection_fn, isWordCharacter_fn, _handlePointerMove, _handlePointerLeave, _handlePointerUp, _handleDragOver, _handleDrop, _handleDoubleClick, _handleKeyDown, focusHeaderFooterShortcut_fn, scheduleRerender_fn, flushRerenderQueue_fn, rerender_fn, ensurePainter_fn, scheduleSelectionUpdate_fn, updateSelection_fn, resolveLayoutOptions_fn, buildHeaderFooterInput_fn, computeHeaderFooterConstraints_fn, layoutPerRIdHeaderFooters_fn, updateDecorationProviders_fn, createDecorationProvider_fn, findHeaderFooterPageForPageNumber_fn, computeDecorationBox_fn, computeExpectedSectionType_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, createDefaultHeaderFooter_fn, getPageElement_fn, scrollPageIntoView_fn, computeAnchorMap_fn, waitForPageMount_fn, getEffectivePageGap_fn, getBodyPageHeight_fn, getHeaderFooterPageHeight_fn, applyDomCorrectionToRects_fn, renderCellSelectionOverlay_fn, renderSelectionRects_fn, renderHoverRegion_fn, clearHoverRegion_fn, renderCaretOverlay_fn, getHeaderFooterContext_fn, computeHeaderFooterSelectionRects_fn, syncTrackedChangesPreferences_fn, deriveTrackedChangesMode_fn, deriveTrackedChangesEnabled_fn, getTrackChangesPluginState_fn, computeDefaultLayoutDefaults_fn, parseColumns_fn, inchesToPx_fn, applyZoom_fn, createLayoutMetrics_fn, getPageOffsetX_fn, convertPageLocalToOverlayCoords_fn, computeDomCaretPageLocal_fn, normalizeClientPoint_fn, computeCaretLayoutRectGeometry_fn, computeCaretLayoutRect_fn, computeCaretLayoutRectFromDOM_fn, computeTableCaretLayoutRect_fn, findLineContainingPos_fn, lineHeightBeforeIndex_fn, getCurrentPageIndex_fn, findRegionForPage_fn, handleLayoutError_fn, decorateError_fn, showLayoutErrorBanner_fn, dismissErrorBanner_fn, createHiddenHost_fn, _windowRoot, _layoutSurfaces, _getTargetDom, _isEditable, _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 changeListLevel, O as findParentNode, Q as isList, U as updateNumberingProperties, V as ListHelpers, W as isMacOS, X as isIOS, Y as getSchemaTypeByName, Z as inputRulesPlugin, _ as TrackDeleteMarkName$1, $ as TrackInsertMarkName$1, a0 as v4, a1 as TrackFormatMarkName$1, 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 SelectionRange, ak as Transform, al as resolveParagraphProperties, am as _getReferencedTableStyles, an as parseSizeUnit, ao as minMax, ap as updateDOMAttributes, aq as findChildren$5, ar as generateRandomSigned32BitIntStrId, as as decodeRPrFromMarks, at as calculateResolvedParagraphProperties, au as resolveRunProperties, av as encodeCSSFromPPr, aw as twipsToPixels$2, ax as encodeCSSFromRPr, ay as generateOrderedListIndex, az as docxNumberingHelpers, aA as InputRule, aB as convertSizeToCSS, aC as findParentNodeClosestToPos, aD as isInTable$1, aE as generateDocxRandomId, aF as insertNewRelationship, aG as inchesToPixels, aH as kebabCase, aI as getUnderlineCssString } from "./converter-DN_dhslo.js";
16
- import { D as DocxZipper } from "./docx-zipper-Bhl_yBjL.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 changeListLevel, O as findParentNode, Q as isList, U as updateNumberingProperties, V as ListHelpers, W as isMacOS, X as isIOS, Y as getSchemaTypeByName, Z as inputRulesPlugin, _ as TrackDeleteMarkName$1, $ as TrackInsertMarkName$1, a0 as v4, a1 as TrackFormatMarkName$1, 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 SelectionRange, ak as Transform, al as resolveParagraphProperties, am as _getReferencedTableStyles, an as parseSizeUnit, ao as minMax, ap as updateDOMAttributes, aq as findChildren$5, ar as generateRandomSigned32BitIntStrId, as as decodeRPrFromMarks, at as calculateResolvedParagraphProperties, au as resolveRunProperties, av as encodeCSSFromPPr, aw as twipsToPixels$2, ax as encodeCSSFromRPr, ay as generateOrderedListIndex, az as docxNumberingHelpers, aA as InputRule, aB as convertSizeToCSS, aC as findParentNodeClosestToPos, aD as isInTable$1, aE as generateDocxRandomId, aF as insertNewRelationship, aG as inchesToPixels, aH as kebabCase, aI as getUnderlineCssString } from "./converter-CVqUjX24.js";
16
+ import { D as DocxZipper } from "./docx-zipper-CsWjoVKD.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() {
@@ -12850,28 +12850,28 @@ const setImageNodeSelection = (view, pos) => {
12850
12850
  }
12851
12851
  return false;
12852
12852
  };
12853
- function canRenderFont(fontName, fallbackFont = "sans-serif") {
12854
- const _canRenderFont = (fontName2, fallbackFont2) => {
12853
+ function canRenderFont(fontName, uiDisplayFallbackFont = "sans-serif") {
12854
+ const _canRenderFont = (fontName2, uiDisplayFallbackFont2) => {
12855
12855
  const canvas2 = document.createElement("canvas");
12856
12856
  const ctx2 = canvas2.getContext("2d");
12857
12857
  ctx2.textBaseline = "top";
12858
12858
  const text = "abcdefghijklmnopqrstuvwxyz0123456789";
12859
- ctx2.font = `72px ${fallbackFont2}`;
12859
+ ctx2.font = `72px ${uiDisplayFallbackFont2}`;
12860
12860
  const initialTextMeasurement = ctx2.measureText(text);
12861
12861
  const fallbackWidth = initialTextMeasurement.width;
12862
12862
  const fallbackHeight = initialTextMeasurement.actualBoundingBoxDescent;
12863
- ctx2.font = `72px "${fontName2}", ${fallbackFont2}`;
12863
+ ctx2.font = `72px "${fontName2}", ${uiDisplayFallbackFont2}`;
12864
12864
  const customTextMeasurement = ctx2.measureText(text);
12865
12865
  const customFontWidth = customTextMeasurement.width;
12866
12866
  const customFontHeight = customTextMeasurement.actualBoundingBoxDescent;
12867
12867
  const isAvailable = customFontWidth !== fallbackWidth || customFontHeight !== fallbackHeight;
12868
12868
  return isAvailable;
12869
12869
  };
12870
- if (_canRenderFont(fontName, fallbackFont)) {
12870
+ if (_canRenderFont(fontName, uiDisplayFallbackFont)) {
12871
12871
  return true;
12872
12872
  }
12873
- const oppositeFallbackFont = fallbackFont === "sans-serif" ? "serif" : "sans-serif";
12874
- return _canRenderFont(fontName, oppositeFallbackFont);
12873
+ const oppositeUiDisplayFallbackFont = uiDisplayFallbackFont === "sans-serif" ? "serif" : "sans-serif";
12874
+ return _canRenderFont(fontName, oppositeUiDisplayFallbackFont);
12875
12875
  }
12876
12876
  const { findChildren: findChildren$3 } = helpers;
12877
12877
  function getAllFieldAnnotations(state) {
@@ -13962,7 +13962,7 @@ const isHeadless = (editor) => {
13962
13962
  const shouldSkipNodeView = (editor) => {
13963
13963
  return isHeadless(editor);
13964
13964
  };
13965
- const summaryVersion = "1.0.0-next.2";
13965
+ const summaryVersion = "1.0.0-next.4";
13966
13966
  const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
13967
13967
  const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
13968
13968
  function mapAttributes(attrs) {
@@ -14754,7 +14754,7 @@ const _Editor = class _Editor extends EventEmitter {
14754
14754
  { default: remarkStringify },
14755
14755
  { default: remarkGfm }
14756
14756
  ] = await Promise.all([
14757
- import("./index-BDVXUeCy.js"),
14757
+ import("./index-D_KE9gpD.js"),
14758
14758
  import("./index-DRCvimau.js"),
14759
14759
  import("./index-C_x_N6Uh.js"),
14760
14760
  import("./index-D_sWOSiG.js"),
@@ -14959,7 +14959,7 @@ const _Editor = class _Editor extends EventEmitter {
14959
14959
  * Process collaboration migrations
14960
14960
  */
14961
14961
  processCollaborationMigrations() {
14962
- console.debug("[checkVersionMigrations] Current editor version", "1.0.0-next.2");
14962
+ console.debug("[checkVersionMigrations] Current editor version", "1.0.0-next.4");
14963
14963
  if (!this.options.ydoc) return;
14964
14964
  const metaMap = this.options.ydoc.getMap("meta");
14965
14965
  let docVersion = metaMap.get("version");
@@ -20652,6 +20652,34 @@ function calculateTabWidth(params2) {
20652
20652
  tabStopPosUsed: nextStop.pos
20653
20653
  };
20654
20654
  }
20655
+ const SPACE_CHARS$1 = /* @__PURE__ */ new Set([" ", " "]);
20656
+ function shouldApplyJustify(params2) {
20657
+ const { alignment, hasExplicitPositioning, isLastLineOfParagraph, paragraphEndsWithLineBreak, skipJustifyOverride } = params2;
20658
+ if (alignment !== "justify" && alignment !== "both") {
20659
+ return false;
20660
+ }
20661
+ if (skipJustifyOverride === true) {
20662
+ return false;
20663
+ }
20664
+ if (hasExplicitPositioning) {
20665
+ return false;
20666
+ }
20667
+ if (isLastLineOfParagraph && !paragraphEndsWithLineBreak) {
20668
+ return false;
20669
+ }
20670
+ return true;
20671
+ }
20672
+ function calculateJustifySpacing(params2) {
20673
+ const { lineWidth, availableWidth, spaceCount, shouldJustify } = params2;
20674
+ if (!shouldJustify) {
20675
+ return 0;
20676
+ }
20677
+ if (spaceCount <= 0) {
20678
+ return 0;
20679
+ }
20680
+ const slack = availableWidth - lineWidth;
20681
+ return slack / spaceCount;
20682
+ }
20655
20683
  function resolveSpacingIndent$1(style, numbering) {
20656
20684
  const spacing = {
20657
20685
  before: style.spacing?.before ?? 0,
@@ -20918,13 +20946,14 @@ const hydrateParagraphStyleAttrs = (para, context, preResolved) => {
20918
20946
  };
20919
20947
  }
20920
20948
  }
20949
+ const normalizedAlign = normalizeAlignment(resolvedExtended.justification);
20921
20950
  const hydrated = {
20922
20951
  resolved,
20923
20952
  spacing: resolvedSpacing,
20924
20953
  indent: resolvedIndent,
20925
20954
  borders: cloneIfObject(resolvedExtended.borders),
20926
20955
  shading: cloneIfObject(resolvedExtended.shading),
20927
- alignment: resolvedExtended.justification,
20956
+ alignment: normalizedAlign,
20928
20957
  tabStops: cloneIfObject(resolvedExtended.tabStops),
20929
20958
  keepLines: resolvedExtended.keepLines,
20930
20959
  keepNext: resolvedExtended.keepNext,
@@ -21625,7 +21654,7 @@ const computeParagraphAttrs = (para, styleContext, listCounterContext, converter
21625
21654
  } else if (styleAlignment) {
21626
21655
  paragraphAttrs.alignment = styleAlignment;
21627
21656
  } else if (computed2.paragraph.alignment) {
21628
- paragraphAttrs.alignment = computed2.paragraph.alignment;
21657
+ paragraphAttrs.alignment = normalizeAlignment(computed2.paragraph.alignment);
21629
21658
  }
21630
21659
  const spacingPx = spacingPtToPx(spacing, normalizedSpacing);
21631
21660
  if (spacingPx) paragraphAttrs.spacing = spacingPx;
@@ -22777,9 +22806,9 @@ const extractFirstTextRunFont = (para) => {
22777
22806
  const font = findFirstTextFont(para.content);
22778
22807
  return font;
22779
22808
  };
22780
- const applyBaseRunDefaults = (run, defaults, fallbackFont, fallbackSize) => {
22809
+ const applyBaseRunDefaults = (run, defaults, uiDisplayFallbackFont, fallbackSize) => {
22781
22810
  if (!run) return;
22782
- if (defaults.fontFamily && run.fontFamily === fallbackFont) {
22811
+ if (defaults.fontFamily && run.fontFamily === uiDisplayFallbackFont) {
22783
22812
  run.fontFamily = defaults.fontFamily;
22784
22813
  }
22785
22814
  if (defaults.fontSizePx != null && run.fontSize === fallbackSize) {
@@ -24582,7 +24611,7 @@ function tableNodeToBlock(node, nextBlockId, positions, defaultFont, defaultSize
24582
24611
  let measurementCanvas = null;
24583
24612
  let measurementCtx = null;
24584
24613
  const TAB_CHAR_LENGTH = 1;
24585
- const SPACE_CHARS = /* @__PURE__ */ new Set([" ", " "]);
24614
+ const SPACE_CHARS = SPACE_CHARS$1;
24586
24615
  const isTabRun$1 = (run) => run?.kind === "tab";
24587
24616
  function getMeasurementContext() {
24588
24617
  if (measurementCtx) return measurementCtx;
@@ -24608,29 +24637,50 @@ const countSpaces = (text) => {
24608
24637
  }
24609
24638
  return spaces;
24610
24639
  };
24611
- const getJustifyAdjustment = (block, line, availableWidthOverride, alignmentOverride) => {
24640
+ const getJustifyAdjustment = (block, line, availableWidthOverride, alignmentOverride, isLastLineOfParagraph, paragraphEndsWithLineBreak, skipJustifyOverride) => {
24612
24641
  if (block.kind !== "paragraph") {
24613
24642
  return { extraPerSpace: 0, totalSpaces: 0 };
24614
24643
  }
24615
- const alignment = alignmentOverride ?? block.attrs?.alignment;
24616
- const hasExplicitPositioning = line.segments?.some((seg) => seg.x !== void 0);
24617
- const availableWidth = availableWidthOverride ?? line.maxWidth ?? line.width;
24618
- const slack = Math.max(0, availableWidth - line.width);
24619
- if (alignment !== "justify" || hasExplicitPositioning || slack <= 0) {
24644
+ if (block.runs.length === 0) {
24620
24645
  return { extraPerSpace: 0, totalSpaces: 0 };
24621
24646
  }
24622
- const runs = sliceRunsForLine$1(block, line);
24623
- const totalSpaces = runs.reduce((sum, run) => {
24624
- if (isTabRun$1(run) || "src" in run || run.kind === "lineBreak" || run.kind === "break" || run.kind === "fieldAnnotation") {
24625
- return sum;
24626
- }
24627
- return sum + countSpaces(run.text ?? "");
24628
- }, 0);
24629
- if (totalSpaces <= 0) {
24647
+ const alignment = alignmentOverride ?? block.attrs?.alignment;
24648
+ const hasExplicitPositioning = line.segments?.some((seg) => seg.x !== void 0) ?? false;
24649
+ const lastRunIndex = block.runs.length - 1;
24650
+ const lastRun = block.runs[lastRunIndex];
24651
+ const derivedIsLastLine = line.toRun >= lastRunIndex;
24652
+ const derivedEndsWithLineBreak = lastRun ? lastRun.kind === "lineBreak" : false;
24653
+ const shouldJustify = shouldApplyJustify({
24654
+ alignment,
24655
+ hasExplicitPositioning,
24656
+ isLastLineOfParagraph: derivedIsLastLine,
24657
+ paragraphEndsWithLineBreak: derivedEndsWithLineBreak,
24658
+ skipJustifyOverride
24659
+ });
24660
+ if (!shouldJustify) {
24630
24661
  return { extraPerSpace: 0, totalSpaces: 0 };
24631
24662
  }
24663
+ let totalSpaces = line.spaceCount ?? 0;
24664
+ if (totalSpaces === 0) {
24665
+ const runs = sliceRunsForLine$1(block, line);
24666
+ totalSpaces = runs.reduce((sum, run) => {
24667
+ if (isTabRun$1(run) || "src" in run || run.kind === "lineBreak" || run.kind === "break" || run.kind === "fieldAnnotation") {
24668
+ return sum;
24669
+ }
24670
+ return sum + countSpaces(run.text ?? "");
24671
+ }, 0);
24672
+ }
24673
+ const availableWidth = availableWidthOverride ?? line.maxWidth ?? line.width;
24674
+ const lineWidth = line.naturalWidth ?? line.width;
24675
+ const extraPerSpace = calculateJustifySpacing({
24676
+ lineWidth,
24677
+ availableWidth,
24678
+ spaceCount: totalSpaces,
24679
+ shouldJustify: true
24680
+ // Already checked above
24681
+ });
24632
24682
  return {
24633
- extraPerSpace: slack / totalSpaces,
24683
+ extraPerSpace,
24634
24684
  totalSpaces
24635
24685
  };
24636
24686
  };
@@ -24697,7 +24747,7 @@ function measureCharacterX(block, line, charOffset, availableWidthOverride, alig
24697
24747
  line.width;
24698
24748
  const justify = getJustifyAdjustment(block, line, availableWidth, alignmentOverride);
24699
24749
  const alignment = alignmentOverride ?? (block.kind === "paragraph" ? block.attrs?.alignment : void 0);
24700
- const renderedLineWidth = alignment === "justify" ? line.width + Math.max(0, availableWidth - line.width) : line.width;
24750
+ const renderedLineWidth = alignment === "justify" && justify.extraPerSpace !== 0 ? availableWidth : line.width;
24701
24751
  const hasExplicitPositioning = line.segments?.some((seg) => seg.x !== void 0);
24702
24752
  const alignmentOffset = !hasExplicitPositioning && alignment === "center" ? Math.max(0, (availableWidth - renderedLineWidth) / 2) : !hasExplicitPositioning && alignment === "right" ? Math.max(0, availableWidth - renderedLineWidth) : 0;
24703
24753
  if (hasExplicitPositioning && line.segments && ctx2) {
@@ -24740,13 +24790,13 @@ function measureCharacterX(block, line, charOffset, availableWidthOverride, alig
24740
24790
  const textUpToTarget = text.slice(0, offsetInRun);
24741
24791
  const measured2 = ctx2.measureText(textUpToTarget);
24742
24792
  const spacingWidth = computeLetterSpacingWidth(run, offsetInRun, runLength);
24743
- const spacesInPortion = justify.extraPerSpace > 0 ? countSpaces(textUpToTarget) : 0;
24793
+ const spacesInPortion = justify.extraPerSpace !== 0 ? countSpaces(textUpToTarget) : 0;
24744
24794
  return alignmentOffset + currentX + measured2.width + spacingWidth + justify.extraPerSpace * (spaceTally + spacesInPortion);
24745
24795
  }
24746
24796
  ctx2.font = getRunFontString(run);
24747
24797
  const measured = ctx2.measureText(text);
24748
24798
  const runLetterSpacing = computeLetterSpacingWidth(run, runLength, runLength);
24749
- const spacesInRun = justify.extraPerSpace > 0 ? countSpaces(text) : 0;
24799
+ const spacesInRun = justify.extraPerSpace !== 0 ? countSpaces(text) : 0;
24750
24800
  currentX += measured.width + runLetterSpacing + justify.extraPerSpace * spacesInRun;
24751
24801
  spaceTally += spacesInRun;
24752
24802
  currentCharOffset += runLength;
@@ -29898,9 +29948,66 @@ const _DomPainter = class _DomPainter {
29898
29948
  const firstLineOffset = suppressFirstLineIndent ? 0 : (paraIndent?.firstLine ?? 0) - (paraIndent?.hanging ?? 0);
29899
29949
  const lastRun = block.runs.length > 0 ? block.runs[block.runs.length - 1] : null;
29900
29950
  const paragraphEndsWithLineBreak = lastRun?.kind === "lineBreak";
29951
+ let listFirstLineMarkerTabWidth;
29952
+ if (!fragment.continuesFromPrev && fragment.markerWidth && wordLayout?.marker) {
29953
+ const markerBoxWidth = fragment.markerWidth;
29954
+ const markerTextWidth = fragment.markerTextWidth != null && isFinite(fragment.markerTextWidth) && fragment.markerTextWidth >= 0 ? fragment.markerTextWidth : markerBoxWidth;
29955
+ const suffix = wordLayout.marker.suffix ?? "tab";
29956
+ if (suffix === "tab") {
29957
+ const markerJustification = wordLayout.marker.justification ?? "left";
29958
+ const isFirstLineIndentMode = wordLayout.firstLineIndentMode === true;
29959
+ let markerStartPos;
29960
+ if (isFirstLineIndentMode && wordLayout.marker.markerX !== void 0 && Number.isFinite(wordLayout.marker.markerX)) {
29961
+ markerStartPos = wordLayout.marker.markerX;
29962
+ } else {
29963
+ const hanging = paraIndent?.hanging ?? 0;
29964
+ const firstLine = paraIndent?.firstLine ?? 0;
29965
+ markerStartPos = paraIndentLeft - hanging + firstLine;
29966
+ }
29967
+ const validMarkerStartPos = Number.isFinite(markerStartPos) ? markerStartPos : 0;
29968
+ let tabWidth;
29969
+ if (markerJustification === "left") {
29970
+ const currentPos = validMarkerStartPos + markerTextWidth;
29971
+ if (isFirstLineIndentMode) {
29972
+ const textStartTarget = wordLayout.marker.textStartX !== void 0 && Number.isFinite(wordLayout.marker.textStartX) ? wordLayout.marker.textStartX : wordLayout.textStartPx;
29973
+ if (textStartTarget !== void 0 && Number.isFinite(textStartTarget) && textStartTarget > currentPos) {
29974
+ tabWidth = textStartTarget - currentPos;
29975
+ } else {
29976
+ tabWidth = LIST_MARKER_GAP$1;
29977
+ }
29978
+ } else {
29979
+ const firstLine = paraIndent?.firstLine ?? 0;
29980
+ const textStart = paraIndentLeft + firstLine;
29981
+ tabWidth = textStart - currentPos;
29982
+ if (tabWidth <= 0) {
29983
+ tabWidth = DEFAULT_TAB_INTERVAL_PX$1 - currentPos % DEFAULT_TAB_INTERVAL_PX$1;
29984
+ } else if (tabWidth < LIST_MARKER_GAP$1) {
29985
+ tabWidth = LIST_MARKER_GAP$1;
29986
+ }
29987
+ }
29988
+ } else {
29989
+ const gutterWidth = fragment.markerGutter ?? wordLayout.marker.gutterWidthPx;
29990
+ tabWidth = gutterWidth !== void 0 && Number.isFinite(gutterWidth) && gutterWidth > 0 ? gutterWidth : LIST_MARKER_GAP$1;
29991
+ }
29992
+ if (tabWidth < LIST_MARKER_GAP$1) {
29993
+ tabWidth = LIST_MARKER_GAP$1;
29994
+ }
29995
+ listFirstLineMarkerTabWidth = validMarkerStartPos + markerTextWidth + tabWidth;
29996
+ } else if (suffix === "space") {
29997
+ const hanging = paraIndent?.hanging ?? 0;
29998
+ const firstLine = paraIndent?.firstLine ?? 0;
29999
+ const markerStartPos = paraIndentLeft - hanging + firstLine;
30000
+ const validMarkerStartPos = Number.isFinite(markerStartPos) ? markerStartPos : 0;
30001
+ listFirstLineMarkerTabWidth = validMarkerStartPos + markerTextWidth + 4;
30002
+ }
30003
+ }
29901
30004
  lines.forEach((line, index2) => {
29902
- const fallbackAvailableWidth = Math.max(0, fragment.width - (paraIndentLeft + paraIndentRight));
29903
- const availableWidthOverride = line.maxWidth ?? fallbackAvailableWidth;
30005
+ const positiveIndentReduction = Math.max(0, paraIndentLeft) + Math.max(0, paraIndentRight);
30006
+ const fallbackAvailableWidth = Math.max(0, fragment.width - positiveIndentReduction);
30007
+ let availableWidthOverride = line.maxWidth != null ? Math.min(line.maxWidth, fallbackAvailableWidth) : fallbackAvailableWidth;
30008
+ if (index2 === 0 && listFirstLineMarkerTabWidth != null) {
30009
+ availableWidthOverride = fragment.width - listFirstLineMarkerTabWidth - Math.max(0, paraIndentRight);
30010
+ }
29904
30011
  const isLastLineOfFragment = index2 === lines.length - 1;
29905
30012
  const isLastLineOfParagraph = isLastLineOfFragment && !fragment.continuesOnNext;
29906
30013
  const shouldSkipJustifyForLastLine = isLastLineOfParagraph && !paragraphEndsWithLineBreak;
@@ -29926,7 +30033,10 @@ const _DomPainter = class _DomPainter {
29926
30033
  }
29927
30034
  } else if (paraIndentLeft && paraIndentLeft > 0) {
29928
30035
  lineEl.style.paddingLeft = `${paraIndentLeft}px`;
29929
- } else if (!isFirstLine && paraIndent?.hanging && paraIndent.hanging > 0) {
30036
+ } else if (!isFirstLine && paraIndent?.hanging && paraIndent.hanging > 0 && // Only apply hanging padding when left indent is NOT negative.
30037
+ // When left indent is negative, the fragment position already accounts for it.
30038
+ // Adding padding here would shift body lines right, causing right-side overflow.
30039
+ !(paraIndentLeft != null && paraIndentLeft < 0)) {
29930
30040
  lineEl.style.paddingLeft = `${paraIndent.hanging}px`;
29931
30041
  }
29932
30042
  }
@@ -29955,6 +30065,7 @@ const _DomPainter = class _DomPainter {
29955
30065
  lineEl.style.paddingLeft = `${validMarkerStartPos}px`;
29956
30066
  const markerContainer = this.doc.createElement("span");
29957
30067
  markerContainer.style.display = "inline-block";
30068
+ markerContainer.style.wordSpacing = "0px";
29958
30069
  const markerEl = this.doc.createElement("span");
29959
30070
  markerEl.classList.add("superdoc-paragraph-marker");
29960
30071
  markerEl.textContent = wordLayout.marker.markerText ?? "";
@@ -30040,10 +30151,15 @@ const _DomPainter = class _DomPainter {
30040
30151
  }
30041
30152
  }
30042
30153
  tabEl.style.display = "inline-block";
30154
+ tabEl.style.wordSpacing = "0px";
30043
30155
  tabEl.style.width = `${tabWidth}px`;
30044
30156
  lineEl.prepend(tabEl);
30045
30157
  } else if (suffix === "space") {
30046
- lineEl.prepend(this.doc.createTextNode(" "));
30158
+ const spaceEl = this.doc.createElement("span");
30159
+ spaceEl.classList.add("superdoc-marker-suffix-space");
30160
+ spaceEl.style.wordSpacing = "0px";
30161
+ spaceEl.textContent = " ";
30162
+ lineEl.prepend(spaceEl);
30047
30163
  }
30048
30164
  lineEl.prepend(markerContainer);
30049
30165
  }
@@ -31335,11 +31451,8 @@ const _DomPainter = class _DomPainter {
31335
31451
  el.setAttribute("styleid", styleId);
31336
31452
  }
31337
31453
  const alignment = block.attrs?.alignment;
31338
- const effectiveAlignment = alignment;
31339
- if (effectiveAlignment === "center" || effectiveAlignment === "right") {
31340
- el.style.textAlign = effectiveAlignment;
31341
- } else if (effectiveAlignment === "justify") {
31342
- el.style.textAlign = "left";
31454
+ if (alignment === "center" || alignment === "right") {
31455
+ el.style.textAlign = alignment;
31343
31456
  } else {
31344
31457
  el.style.textAlign = "left";
31345
31458
  }
@@ -31350,9 +31463,8 @@ const _DomPainter = class _DomPainter {
31350
31463
  if (lineRange.pmEnd != null) {
31351
31464
  el.dataset.pmEnd = String(lineRange.pmEnd);
31352
31465
  }
31353
- const runsForLine = sliceRunsForLine(block, line);
31466
+ let runsForLine = sliceRunsForLine(block, line);
31354
31467
  const trackedConfig = this.resolveTrackedChangesConfig(block);
31355
- runsForLine.length > 0 ? runsForLine.filter((r2) => (r2.kind === "text" || r2.kind === void 0) && "text" in r2 && r2.text != null).map((r2) => r2.text) : gatherTextSlicesForLine(block, line);
31356
31468
  if (runsForLine.length === 0) {
31357
31469
  const span = this.doc.createElement("span");
31358
31470
  span.innerHTML = "&nbsp;";
@@ -31399,7 +31511,156 @@ const _DomPainter = class _DomPainter {
31399
31511
  });
31400
31512
  }
31401
31513
  const hasExplicitPositioning = line.segments?.some((seg) => seg.x !== void 0);
31402
- availableWidthOverride ?? line.maxWidth ?? line.width;
31514
+ const availableWidth = availableWidthOverride ?? line.maxWidth ?? line.width;
31515
+ const justifyShouldApply = shouldApplyJustify({
31516
+ alignment: block.attrs?.alignment,
31517
+ hasExplicitPositioning: hasExplicitPositioning ?? false,
31518
+ // Caller already folds last-line + trailing lineBreak behavior into skipJustify.
31519
+ isLastLineOfParagraph: false,
31520
+ paragraphEndsWithLineBreak: false,
31521
+ skipJustifyOverride: skipJustify
31522
+ });
31523
+ const countSpaces2 = (text) => {
31524
+ let count = 0;
31525
+ for (let i = 0; i < text.length; i += 1) {
31526
+ if (SPACE_CHARS$1.has(text[i])) count += 1;
31527
+ }
31528
+ return count;
31529
+ };
31530
+ if (justifyShouldApply) {
31531
+ const stableDataAttrs = (attrs) => {
31532
+ if (!attrs) return void 0;
31533
+ const keys2 = Object.keys(attrs).sort();
31534
+ const out = {};
31535
+ keys2.forEach((key2) => {
31536
+ out[key2] = attrs[key2];
31537
+ });
31538
+ return out;
31539
+ };
31540
+ const mergeSignature = (run) => JSON.stringify({
31541
+ kind: run.kind ?? "text",
31542
+ fontFamily: run.fontFamily,
31543
+ fontSize: run.fontSize,
31544
+ bold: run.bold ?? false,
31545
+ italic: run.italic ?? false,
31546
+ letterSpacing: run.letterSpacing ?? null,
31547
+ color: run.color ?? null,
31548
+ underline: run.underline ?? null,
31549
+ strike: run.strike ?? false,
31550
+ highlight: run.highlight ?? null,
31551
+ textTransform: run.textTransform ?? null,
31552
+ token: run.token ?? null,
31553
+ pageRefMetadata: run.pageRefMetadata ?? null,
31554
+ trackedChange: run.trackedChange ?? null,
31555
+ sdt: run.sdt ?? null,
31556
+ link: run.link ?? null,
31557
+ comments: run.comments ?? null,
31558
+ dataAttrs: stableDataAttrs(run.dataAttrs) ?? null
31559
+ });
31560
+ const isWhitespaceOnly = (text) => {
31561
+ if (text.length === 0) return false;
31562
+ for (let i = 0; i < text.length; i += 1) {
31563
+ if (!SPACE_CHARS$1.has(text[i])) return false;
31564
+ }
31565
+ return true;
31566
+ };
31567
+ const cloneTextRun = (run) => ({
31568
+ ...run,
31569
+ comments: run.comments ? [...run.comments] : void 0,
31570
+ dataAttrs: run.dataAttrs ? { ...run.dataAttrs } : void 0,
31571
+ underline: run.underline ? { ...run.underline } : void 0,
31572
+ pageRefMetadata: run.pageRefMetadata ? { ...run.pageRefMetadata } : void 0
31573
+ });
31574
+ const normalized = runsForLine.map((run) => {
31575
+ if (run.kind !== "text" && run.kind !== void 0 || !("text" in run)) return run;
31576
+ return cloneTextRun(run);
31577
+ });
31578
+ const merged = [];
31579
+ for (let i = 0; i < normalized.length; i += 1) {
31580
+ const run = normalized[i];
31581
+ if (run.kind !== "text" && run.kind !== void 0 || !("text" in run)) {
31582
+ merged.push(run);
31583
+ continue;
31584
+ }
31585
+ const textRun = run;
31586
+ if (!isWhitespaceOnly(textRun.text ?? "")) {
31587
+ merged.push(textRun);
31588
+ continue;
31589
+ }
31590
+ const prev = merged[merged.length - 1];
31591
+ if (prev && (prev.kind === "text" || prev.kind === void 0) && "text" in prev) {
31592
+ const prevTextRun = prev;
31593
+ if (mergeSignature(prevTextRun) === mergeSignature(textRun)) {
31594
+ const extra = textRun.text ?? "";
31595
+ prevTextRun.text = (prevTextRun.text ?? "") + extra;
31596
+ if (prevTextRun.pmStart != null) {
31597
+ prevTextRun.pmEnd = prevTextRun.pmStart + prevTextRun.text.length;
31598
+ } else if (prevTextRun.pmEnd != null) {
31599
+ prevTextRun.pmEnd = prevTextRun.pmEnd + extra.length;
31600
+ }
31601
+ continue;
31602
+ }
31603
+ }
31604
+ const next = normalized[i + 1];
31605
+ if (next && (next.kind === "text" || next.kind === void 0) && "text" in next) {
31606
+ const nextTextRun = next;
31607
+ if (mergeSignature(nextTextRun) === mergeSignature(textRun)) {
31608
+ const extra = textRun.text ?? "";
31609
+ nextTextRun.text = extra + (nextTextRun.text ?? "");
31610
+ if (textRun.pmStart != null) {
31611
+ nextTextRun.pmStart = textRun.pmStart;
31612
+ } else if (nextTextRun.pmStart != null) {
31613
+ nextTextRun.pmStart = nextTextRun.pmStart - extra.length;
31614
+ }
31615
+ if (nextTextRun.pmStart != null && nextTextRun.pmEnd == null) {
31616
+ nextTextRun.pmEnd = nextTextRun.pmStart + nextTextRun.text.length;
31617
+ }
31618
+ continue;
31619
+ }
31620
+ }
31621
+ merged.push(textRun);
31622
+ }
31623
+ runsForLine = merged;
31624
+ const hasNonSpaceText = runsForLine.some(
31625
+ (run) => (run.kind === "text" || run.kind === void 0) && "text" in run && (run.text ?? "").trim().length > 0
31626
+ );
31627
+ if (hasNonSpaceText) {
31628
+ for (let i = runsForLine.length - 1; i >= 0; i -= 1) {
31629
+ const run = runsForLine[i];
31630
+ if (run.kind !== "text" && run.kind !== void 0 || !("text" in run)) continue;
31631
+ const text = run.text ?? "";
31632
+ let trimCount = 0;
31633
+ for (let j = text.length - 1; j >= 0 && text[j] === " "; j -= 1) {
31634
+ trimCount += 1;
31635
+ }
31636
+ if (trimCount === 0) break;
31637
+ const nextText = text.slice(0, Math.max(0, text.length - trimCount));
31638
+ if (nextText.length === 0) {
31639
+ runsForLine.splice(i, 1);
31640
+ continue;
31641
+ }
31642
+ run.text = nextText;
31643
+ if (run.pmEnd != null) {
31644
+ run.pmEnd = run.pmEnd - trimCount;
31645
+ }
31646
+ break;
31647
+ }
31648
+ }
31649
+ }
31650
+ const spaceCount = line.spaceCount ?? runsForLine.reduce((sum, run) => {
31651
+ if (run.kind !== "text" && run.kind !== void 0 || !("text" in run) || run.text == null) return sum;
31652
+ return sum + countSpaces2(run.text);
31653
+ }, 0);
31654
+ const lineWidth = line.naturalWidth ?? line.width;
31655
+ const spacingPerSpace = calculateJustifySpacing({
31656
+ lineWidth,
31657
+ availableWidth,
31658
+ spaceCount,
31659
+ shouldJustify: justifyShouldApply
31660
+ });
31661
+ if (spacingPerSpace !== 0) {
31662
+ el.style.wordSpacing = `${spacingPerSpace}px`;
31663
+ }
31403
31664
  if (hasExplicitPositioning && line.segments) {
31404
31665
  const paraIndent = block.attrs?.indent;
31405
31666
  const indentLeft = paraIndent?.left ?? 0;
@@ -32271,7 +32532,7 @@ const applyParagraphBlockStyles = (element, attrs) => {
32271
32532
  element.setAttribute("styleid", attrs.styleId);
32272
32533
  }
32273
32534
  if (attrs.alignment) {
32274
- element.style.textAlign = attrs.alignment;
32535
+ element.style.textAlign = attrs.alignment === "justify" || attrs.alignment === "both" ? "left" : attrs.alignment;
32275
32536
  }
32276
32537
  if (attrs.dropCap) {
32277
32538
  element.classList.add("sd-editor-dropcap");
@@ -32336,23 +32597,6 @@ const applyParagraphShadingStyles = (element, shading) => {
32336
32597
  if (!shading?.fill) return;
32337
32598
  element.style.backgroundColor = shading.fill;
32338
32599
  };
32339
- const gatherTextSlicesForLine = (block, line) => {
32340
- const slices = [];
32341
- const startRun = line.fromRun ?? 0;
32342
- const endRun = line.toRun ?? startRun;
32343
- for (let runIndex = startRun; runIndex <= endRun; runIndex += 1) {
32344
- const run = block.runs[runIndex];
32345
- if (!run || run.kind !== "text" && run.kind !== void 0 || !("text" in run) || !run.text) continue;
32346
- const isFirst = runIndex === startRun;
32347
- const isLast = runIndex === endRun;
32348
- const start2 = isFirst ? line.fromChar ?? 0 : 0;
32349
- const end2 = isLast ? line.toChar ?? run.text.length : run.text.length;
32350
- if (start2 >= end2) continue;
32351
- const slice2 = run.text.slice(start2, end2);
32352
- if (slice2) slices.push(slice2);
32353
- }
32354
- return slices;
32355
- };
32356
32600
  const sliceRunsForLine = (block, line) => {
32357
32601
  const result = [];
32358
32602
  for (let runIndex = line.fromRun; runIndex <= line.toRun; runIndex += 1) {
@@ -32974,7 +33218,8 @@ function calculateTextStartIndent(params2) {
32974
33218
  let indentAdjust = paraIndentLeft;
32975
33219
  if (isListItem2 && isFirstLine && isFirstLineIndentMode) {
32976
33220
  const textStartFallback = paraIndentLeft + Math.max(firstLineIndent, 0) + markerWidth;
32977
- indentAdjust = typeof wordLayout?.textStartPx === "number" && Number.isFinite(wordLayout.textStartPx) ? wordLayout.textStartPx : textStartFallback;
33221
+ const markerTextStartX = wordLayout?.marker?.textStartX;
33222
+ indentAdjust = typeof markerTextStartX === "number" && Number.isFinite(markerTextStartX) ? markerTextStartX : typeof wordLayout?.textStartPx === "number" && Number.isFinite(wordLayout.textStartPx) ? wordLayout.textStartPx : textStartFallback;
32978
33223
  } else if (isFirstLine && !isListItem2) {
32979
33224
  indentAdjust += firstLineOffset;
32980
33225
  }
@@ -33664,6 +33909,20 @@ const asSafeNumber = (value) => {
33664
33909
  }
33665
33910
  return value;
33666
33911
  };
33912
+ function calculateFirstLineIndent(block, measure) {
33913
+ const wordLayout = block.attrs?.wordLayout;
33914
+ if (!wordLayout?.firstLineIndentMode) {
33915
+ return 0;
33916
+ }
33917
+ if (!wordLayout.marker || !measure.marker) {
33918
+ return 0;
33919
+ }
33920
+ const markerWidthRaw = measure.marker.markerWidth ?? wordLayout.marker.markerBoxWidthPx ?? 0;
33921
+ const markerWidth = Number.isFinite(markerWidthRaw) && markerWidthRaw >= 0 ? markerWidthRaw : 0;
33922
+ const gutterWidthRaw = measure.marker.gutterWidth ?? 0;
33923
+ const gutterWidth = Number.isFinite(gutterWidthRaw) && gutterWidthRaw >= 0 ? gutterWidthRaw : 0;
33924
+ return markerWidth + gutterWidth;
33925
+ }
33667
33926
  function layoutParagraphBlock(ctx2, anchors) {
33668
33927
  const { block, measure, columnWidth, ensurePage, advanceColumn, columnX, floatManager } = ctx2;
33669
33928
  const remeasureParagraph2 = ctx2.remeasureParagraph;
@@ -33793,17 +34052,8 @@ function layoutParagraphBlock(ctx2, anchors) {
33793
34052
  const remeasureWidth = Math.max(1, columnWidth - indentLeft - indentRight);
33794
34053
  let didRemeasureForColumnWidth = false;
33795
34054
  if (typeof remeasureParagraph2 === "function" && typeof measurementWidth === "number" && measurementWidth > remeasureWidth) {
33796
- let firstLineIndent = 0;
33797
- const wordLayout = block.attrs?.wordLayout;
33798
- if (wordLayout?.marker && measure.marker) {
33799
- const markerJustification = wordLayout.marker.justification ?? "left";
33800
- if (markerJustification === "left") {
33801
- const markerWidth = measure.marker.markerWidth ?? 0;
33802
- const gutterWidth = measure.marker.gutterWidth ?? wordLayout.marker.gutterWidthPx ?? 0;
33803
- firstLineIndent = markerWidth + gutterWidth;
33804
- }
33805
- }
33806
- const newMeasure = remeasureParagraph2(block, remeasureWidth, firstLineIndent);
34055
+ const firstLineIndent = calculateFirstLineIndent(block, measure);
34056
+ const newMeasure = remeasureParagraph2(block, columnWidth, firstLineIndent);
33807
34057
  lines = normalizeLines(newMeasure);
33808
34058
  didRemeasureForColumnWidth = true;
33809
34059
  }
@@ -33884,16 +34134,7 @@ function layoutParagraphBlock(ctx2, anchors) {
33884
34134
  }
33885
34135
  const narrowestRemeasureWidth = Math.max(1, narrowestWidth - indentLeft - indentRight);
33886
34136
  if (narrowestRemeasureWidth < remeasureWidth) {
33887
- let firstLineIndent = 0;
33888
- const wordLayout = block.attrs?.wordLayout;
33889
- if (wordLayout?.marker && measure.marker) {
33890
- const markerJustification = wordLayout.marker.justification ?? "left";
33891
- if (markerJustification === "left") {
33892
- const markerWidth = measure.marker.markerWidth ?? 0;
33893
- const gutterWidth = measure.marker.gutterWidth ?? wordLayout.marker.gutterWidthPx ?? 0;
33894
- firstLineIndent = markerWidth + gutterWidth;
33895
- }
33896
- }
34137
+ const firstLineIndent = calculateFirstLineIndent(block, measure);
33897
34138
  const newMeasure = remeasureParagraph2(block, narrowestRemeasureWidth, firstLineIndent);
33898
34139
  lines = normalizeLines(newMeasure);
33899
34140
  didRemeasureForFloats = true;
@@ -37040,7 +37281,8 @@ function remeasureParagraph(block, maxWidth, firstLineIndent = 0) {
37040
37281
  const indentHanging = Math.max(0, indent?.hanging ?? 0);
37041
37282
  const rawFirstLineOffset = Math.max(0, firstLineIndent || indentFirstLine - indentHanging);
37042
37283
  const contentWidth = Math.max(1, maxWidth - indentLeft - indentRight);
37043
- const textStartPx = wordLayout?.textStartPx;
37284
+ const markerTextStartX = wordLayout?.marker?.textStartX;
37285
+ const textStartPx = typeof markerTextStartX === "number" && Number.isFinite(markerTextStartX) ? markerTextStartX : typeof wordLayout?.textStartPx === "number" && Number.isFinite(wordLayout.textStartPx) ? wordLayout.textStartPx : void 0;
37044
37286
  const treatAsHanging = textStartPx && indentLeft === 0 && indentHanging === 0;
37045
37287
  const firstLineWidth = typeof textStartPx === "number" && textStartPx > indentLeft && !treatAsHanging ? Math.max(1, maxWidth - textStartPx - indentRight) : Math.max(1, contentWidth - rawFirstLineOffset);
37046
37288
  const tabStops = buildTabStopsPx$1(indent, attrs?.tabs, attrs?.tabIntervalTwips);
@@ -37052,6 +37294,7 @@ function remeasureParagraph(block, maxWidth, firstLineIndent = 0) {
37052
37294
  const startRun = currentRun;
37053
37295
  const startChar = currentChar;
37054
37296
  let width = 0;
37297
+ let widthAtLastBreak = -1;
37055
37298
  let lastBreakRun = -1;
37056
37299
  let lastBreakChar = -1;
37057
37300
  let endRun = currentRun;
@@ -37069,6 +37312,7 @@ function remeasureParagraph(block, maxWidth, firstLineIndent = 0) {
37069
37312
  endChar = 1;
37070
37313
  lastBreakRun = r2;
37071
37314
  lastBreakChar = 1;
37315
+ widthAtLastBreak = width;
37072
37316
  continue;
37073
37317
  }
37074
37318
  const text = runText(run);
@@ -37079,6 +37323,7 @@ function remeasureParagraph(block, maxWidth, firstLineIndent = 0) {
37079
37323
  if (lastBreakRun >= 0) {
37080
37324
  endRun = lastBreakRun;
37081
37325
  endChar = lastBreakChar;
37326
+ width = widthAtLastBreak >= 0 ? widthAtLastBreak : width;
37082
37327
  } else {
37083
37328
  endRun = r2;
37084
37329
  endChar = c;
@@ -37093,6 +37338,7 @@ function remeasureParagraph(block, maxWidth, firstLineIndent = 0) {
37093
37338
  if (ch === " " || ch === " " || ch === "-") {
37094
37339
  lastBreakRun = r2;
37095
37340
  lastBreakChar = c + 1;
37341
+ widthAtLastBreak = width;
37096
37342
  }
37097
37343
  }
37098
37344
  if (didBreakInThisLine) break;
@@ -38803,7 +39049,9 @@ function clickToPosition(layout, blocks, measures, containerPoint, domContainer,
38803
39049
  }
38804
39050
  const markerWidth = fragment.markerWidth ?? measure.marker?.markerWidth ?? 0;
38805
39051
  const isListItem3 = markerWidth > 0;
38806
- const alignmentOverride = isListItem3 ? "left" : void 0;
39052
+ const paraAlignment = block.attrs?.alignment;
39053
+ const isJustified = paraAlignment === "justify" || paraAlignment === "both";
39054
+ const alignmentOverride = isListItem3 && !isJustified ? "left" : void 0;
38807
39055
  const pos = mapPointToPm(block, line, pageRelativePoint.x - fragment.x, isRTL, availableWidth, alignmentOverride);
38808
39056
  if (pos == null) {
38809
39057
  logClickStage("warn", "no-position", {
@@ -38870,7 +39118,9 @@ function clickToPosition(layout, blocks, measures, containerPoint, domContainer,
38870
39118
  }
38871
39119
  const cellMarkerWidth = cellMeasure.marker?.markerWidth ?? 0;
38872
39120
  const isListItem3 = cellMarkerWidth > 0;
38873
- const alignmentOverride = isListItem3 ? "left" : void 0;
39121
+ const cellAlignment = cellBlock.attrs?.alignment;
39122
+ const isJustified = cellAlignment === "justify" || cellAlignment === "both";
39123
+ const alignmentOverride = isListItem3 && !isJustified ? "left" : void 0;
38874
39124
  const pos = mapPointToPm(cellBlock, line, localX, isRTL, availableWidth, alignmentOverride);
38875
39125
  if (pos != null) {
38876
39126
  logClickStage("log", "success", {
@@ -39039,7 +39289,9 @@ function selectionToRects(layout, blocks, measures, from2, to, geometryHelper) {
39039
39289
  const charOffsetTo = pmPosToCharOffset(block, line, sliceTo);
39040
39290
  const markerWidth = fragment.markerWidth ?? measure.marker?.markerWidth ?? 0;
39041
39291
  const isListItemFlag = isListItem(markerWidth, block);
39042
- const alignmentOverride = isListItemFlag ? "left" : void 0;
39292
+ const blockAlignment = block.attrs?.alignment;
39293
+ const isJustified = blockAlignment === "justify" || blockAlignment === "both";
39294
+ const alignmentOverride = isListItemFlag && !isJustified ? "left" : void 0;
39043
39295
  const startX = mapPmToX(block, line, charOffsetFrom, fragment.width, alignmentOverride);
39044
39296
  const endX = mapPmToX(block, line, charOffsetTo, fragment.width, alignmentOverride);
39045
39297
  const indent = extractParagraphIndent(block.attrs?.indent);
@@ -39781,7 +40033,9 @@ async function measureParagraphBlock(block, maxWidth) {
39781
40033
  const contentWidth = Math.max(1, maxWidth - indentLeft - indentRight);
39782
40034
  const bodyContentWidth = contentWidth;
39783
40035
  let initialAvailableWidth;
39784
- const textStartPx = wordLayout?.textStartPx;
40036
+ const rawTextStartPx = wordLayout?.textStartPx;
40037
+ const markerTextStartX = wordLayout?.marker?.textStartX;
40038
+ const textStartPx = typeof markerTextStartX === "number" && Number.isFinite(markerTextStartX) ? markerTextStartX : typeof rawTextStartPx === "number" && Number.isFinite(rawTextStartPx) ? rawTextStartPx : void 0;
39785
40039
  if (typeof textStartPx === "number" && textStartPx > indentLeft) {
39786
40040
  initialAvailableWidth = Math.max(1, maxWidth - textStartPx - indentRight);
39787
40041
  } else {
@@ -39916,12 +40170,40 @@ async function measureParagraphBlock(block, maxWidth) {
39916
40170
  runsToProcess.push(run);
39917
40171
  }
39918
40172
  }
40173
+ const trimTrailingWrapSpaces = (lineToTrim) => {
40174
+ const lastRun = runsToProcess[lineToTrim.toRun];
40175
+ if (!lastRun || !("text" in lastRun) || typeof lastRun.text !== "string") return;
40176
+ const sliceStart = lineToTrim.toRun === lineToTrim.fromRun ? lineToTrim.fromChar : 0;
40177
+ const sliceEnd = lineToTrim.toChar;
40178
+ if (sliceEnd <= sliceStart) return;
40179
+ const sliceText = lastRun.text.slice(sliceStart, sliceEnd);
40180
+ let trimCount = 0;
40181
+ for (let i = sliceText.length - 1; i >= 0 && sliceText[i] === " "; i -= 1) {
40182
+ trimCount += 1;
40183
+ }
40184
+ if (trimCount === 0) return;
40185
+ if (lineToTrim.fromRun === lineToTrim.toRun && sliceText.trim().length === 0) {
40186
+ return;
40187
+ }
40188
+ const keptText = sliceText.slice(0, Math.max(0, sliceText.length - trimCount));
40189
+ const { font } = buildFontString(
40190
+ lastRun
40191
+ );
40192
+ const fullWidth = measureRunWidth(sliceText, font, ctx2, lastRun);
40193
+ const keptWidth = keptText.length > 0 ? measureRunWidth(keptText, font, ctx2, lastRun) : 0;
40194
+ const delta = Math.max(0, fullWidth - keptWidth);
40195
+ lineToTrim.width = roundValue(Math.max(0, lineToTrim.width - delta));
40196
+ lineToTrim.spaceCount = Math.max(0, lineToTrim.spaceCount - trimCount);
40197
+ if (lineToTrim.naturalWidth != null && typeof lineToTrim.naturalWidth === "number") {
40198
+ lineToTrim.naturalWidth = roundValue(Math.max(0, lineToTrim.naturalWidth - delta));
40199
+ }
40200
+ };
39919
40201
  for (let runIndex = 0; runIndex < runsToProcess.length; runIndex++) {
39920
40202
  const run = runsToProcess[runIndex];
39921
40203
  if (run.kind === "break") {
39922
40204
  if (currentLine) {
39923
40205
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
39924
- const { spaceCount: _sc, ...lineBase } = currentLine;
40206
+ const lineBase = currentLine;
39925
40207
  const completedLine = { ...lineBase, ...metrics };
39926
40208
  addBarTabsToLine(completedLine);
39927
40209
  lines.push(completedLine);
@@ -39966,7 +40248,7 @@ async function measureParagraphBlock(block, maxWidth) {
39966
40248
  toRun: runIndex,
39967
40249
  toChar: 0,
39968
40250
  width: 0,
39969
- maxWidth: getEffectiveWidth(initialAvailableWidth),
40251
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
39970
40252
  segments: [],
39971
40253
  ...metrics
39972
40254
  };
@@ -40001,7 +40283,7 @@ async function measureParagraphBlock(block, maxWidth) {
40001
40283
  width: 0,
40002
40284
  maxFontSize: 12,
40003
40285
  // Default font size for tabs
40004
- maxWidth: getEffectiveWidth(initialAvailableWidth),
40286
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
40005
40287
  segments: [],
40006
40288
  spaceCount: 0
40007
40289
  };
@@ -40051,7 +40333,7 @@ async function measureParagraphBlock(block, maxWidth) {
40051
40333
  width: imageWidth,
40052
40334
  maxFontSize: imageHeight,
40053
40335
  // Use image height for line height calculation
40054
- maxWidth: getEffectiveWidth(initialAvailableWidth),
40336
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
40055
40337
  spaceCount: 0,
40056
40338
  segments: [
40057
40339
  {
@@ -40067,8 +40349,9 @@ async function measureParagraphBlock(block, maxWidth) {
40067
40349
  }
40068
40350
  const appliedTabAlign = lastAppliedTabAlign;
40069
40351
  if (currentLine.width + imageWidth > currentLine.maxWidth && currentLine.width > 0) {
40352
+ trimTrailingWrapSpaces(currentLine);
40070
40353
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
40071
- const { spaceCount: _sc, ...lineBase } = currentLine;
40354
+ const lineBase = currentLine;
40072
40355
  const completedLine = {
40073
40356
  ...lineBase,
40074
40357
  ...metrics
@@ -40141,7 +40424,7 @@ async function measureParagraphBlock(block, maxWidth) {
40141
40424
  // Field annotations are atomic units
40142
40425
  width: annotationWidth,
40143
40426
  maxFontSize: annotationHeight,
40144
- maxWidth: getEffectiveWidth(initialAvailableWidth),
40427
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
40145
40428
  spaceCount: 0,
40146
40429
  segments: [
40147
40430
  {
@@ -40156,8 +40439,9 @@ async function measureParagraphBlock(block, maxWidth) {
40156
40439
  continue;
40157
40440
  }
40158
40441
  if (currentLine.width + annotationWidth > currentLine.maxWidth && currentLine.width > 0) {
40442
+ trimTrailingWrapSpaces(currentLine);
40159
40443
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
40160
- const { spaceCount: _sc, ...lineBase } = currentLine;
40444
+ const lineBase = currentLine;
40161
40445
  const completedLine = {
40162
40446
  ...lineBase,
40163
40447
  ...metrics
@@ -40230,15 +40514,16 @@ async function measureParagraphBlock(block, maxWidth) {
40230
40514
  width: spacesWidth,
40231
40515
  maxFontSize: run.fontSize,
40232
40516
  maxFontInfo: getFontInfoFromRun(run),
40233
- maxWidth: getEffectiveWidth(initialAvailableWidth),
40517
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
40234
40518
  segments: [{ runIndex, fromChar: spacesStartChar, toChar: spacesEndChar, width: spacesWidth }],
40235
40519
  spaceCount: spacesLength
40236
40520
  };
40237
40521
  } else {
40238
40522
  const boundarySpacing = currentLine.width > 0 ? run.letterSpacing ?? 0 : 0;
40239
40523
  if (currentLine.width + boundarySpacing + spacesWidth > currentLine.maxWidth - WIDTH_FUDGE_PX2 && currentLine.width > 0) {
40524
+ trimTrailingWrapSpaces(currentLine);
40240
40525
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
40241
- const { spaceCount: _sc, ...lineBase } = currentLine;
40526
+ const lineBase = currentLine;
40242
40527
  const completedLine = {
40243
40528
  ...lineBase,
40244
40529
  ...metrics
@@ -40274,6 +40559,13 @@ async function measureParagraphBlock(block, maxWidth) {
40274
40559
  continue;
40275
40560
  }
40276
40561
  const words = segment.split(" ");
40562
+ let lastNonEmptyWordIndex = -1;
40563
+ for (let i = words.length - 1; i >= 0; i -= 1) {
40564
+ if (words[i] !== "") {
40565
+ lastNonEmptyWordIndex = i;
40566
+ break;
40567
+ }
40568
+ }
40277
40569
  let segmentStartX;
40278
40570
  if (currentLine && pendingTabAlignment) {
40279
40571
  segmentStartX = alignSegmentAtTab(segment, font, run);
@@ -40296,15 +40588,16 @@ async function measureParagraphBlock(block, maxWidth) {
40296
40588
  width: singleSpaceWidth,
40297
40589
  maxFontSize: run.fontSize,
40298
40590
  maxFontInfo: getFontInfoFromRun(run),
40299
- maxWidth: getEffectiveWidth(initialAvailableWidth),
40591
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
40300
40592
  segments: [{ runIndex, fromChar: spaceStartChar, toChar: spaceEndChar, width: singleSpaceWidth }],
40301
40593
  spaceCount: 1
40302
40594
  };
40303
40595
  } else {
40304
40596
  const boundarySpacing2 = currentLine.width > 0 ? run.letterSpacing ?? 0 : 0;
40305
40597
  if (currentLine.width + boundarySpacing2 + singleSpaceWidth > currentLine.maxWidth - WIDTH_FUDGE_PX2 && currentLine.width > 0) {
40598
+ trimTrailingWrapSpaces(currentLine);
40306
40599
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
40307
- const { spaceCount: _sc, ...lineBase } = currentLine;
40600
+ const lineBase = currentLine;
40308
40601
  const completedLine = { ...lineBase, ...metrics };
40309
40602
  addBarTabsToLine(completedLine);
40310
40603
  lines.push(completedLine);
@@ -40336,19 +40629,19 @@ async function measureParagraphBlock(block, maxWidth) {
40336
40629
  charPosInRun = spaceEndChar;
40337
40630
  continue;
40338
40631
  }
40339
- const isLastWordInSegment = wordIndex === words.length - 1;
40340
- const isLastWord = isLastWordInSegment && isLastSegment;
40341
40632
  const wordOnlyWidth = measureRunWidth(word, font, ctx2, run);
40342
- const spaceWidth = isLastWord ? 0 : measureRunWidth(" ", font, ctx2, run);
40343
- const wordCommitWidth = isLastWord ? wordOnlyWidth : wordOnlyWidth + spaceWidth;
40633
+ const shouldIncludeDelimiterSpace = wordIndex < lastNonEmptyWordIndex;
40634
+ const spaceWidth = shouldIncludeDelimiterSpace ? measureRunWidth(" ", font, ctx2, run) : 0;
40635
+ const wordCommitWidth = wordOnlyWidth + spaceWidth;
40344
40636
  const wordStartChar = charPosInRun;
40345
40637
  const wordEndNoSpace = charPosInRun + word.length;
40346
- const wordEndWithSpace = charPosInRun + (isLastWord ? word.length : word.length + 1);
40638
+ const wordEndWithSpace = wordEndNoSpace + (shouldIncludeDelimiterSpace ? 1 : 0);
40347
40639
  const effectiveMaxWidth = currentLine ? currentLine.maxWidth : getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : contentWidth);
40348
40640
  if (wordOnlyWidth > effectiveMaxWidth && word.length > 1) {
40349
40641
  if (currentLine && currentLine.width > 0 && currentLine.segments && currentLine.segments.length > 0) {
40642
+ trimTrailingWrapSpaces(currentLine);
40350
40643
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
40351
- const { spaceCount: _sc, ...lineBase } = currentLine;
40644
+ const lineBase = currentLine;
40352
40645
  const completedLine = { ...lineBase, ...metrics };
40353
40646
  addBarTabsToLine(completedLine);
40354
40647
  lines.push(completedLine);
@@ -40382,7 +40675,7 @@ async function measureParagraphBlock(block, maxWidth) {
40382
40675
  });
40383
40676
  if (isLastChunk) {
40384
40677
  const ls = run.letterSpacing ?? 0;
40385
- if (!isLastWord && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
40678
+ if (shouldIncludeDelimiterSpace && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
40386
40679
  currentLine.toChar = wordEndWithSpace;
40387
40680
  currentLine.width = roundValue(currentLine.width + spaceWidth + ls);
40388
40681
  charPosInRun = wordEndWithSpace;
@@ -40391,8 +40684,9 @@ async function measureParagraphBlock(block, maxWidth) {
40391
40684
  charPosInRun = wordEndWithSpace;
40392
40685
  }
40393
40686
  } else {
40687
+ trimTrailingWrapSpaces(currentLine);
40394
40688
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
40395
- const { spaceCount: _sc, ...lineBase } = currentLine;
40689
+ const lineBase = currentLine;
40396
40690
  const completedLine = { ...lineBase, ...metrics };
40397
40691
  addBarTabsToLine(completedLine);
40398
40692
  lines.push(completedLine);
@@ -40414,7 +40708,7 @@ async function measureParagraphBlock(block, maxWidth) {
40414
40708
  spaceCount: 0
40415
40709
  };
40416
40710
  const ls = run.letterSpacing ?? 0;
40417
- if (!isLastWord && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
40711
+ if (shouldIncludeDelimiterSpace && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
40418
40712
  currentLine.toChar = wordEndWithSpace;
40419
40713
  currentLine.width = roundValue(currentLine.width + spaceWidth + ls);
40420
40714
  charPosInRun = wordEndWithSpace;
@@ -40451,12 +40745,12 @@ async function measureParagraphBlock(block, maxWidth) {
40451
40745
  width: wordOnlyWidth,
40452
40746
  maxFontSize: run.fontSize,
40453
40747
  maxFontInfo: getFontInfoFromRun(run),
40454
- maxWidth: getEffectiveWidth(initialAvailableWidth),
40748
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
40455
40749
  segments: [{ runIndex, fromChar: wordStartChar, toChar: wordEndNoSpace, width: wordOnlyWidth }],
40456
40750
  spaceCount: 0
40457
40751
  };
40458
40752
  const ls = run.letterSpacing ?? 0;
40459
- if (!isLastWord && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
40753
+ if (shouldIncludeDelimiterSpace && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
40460
40754
  currentLine.toChar = wordEndWithSpace;
40461
40755
  currentLine.width = roundValue(currentLine.width + spaceWidth + ls);
40462
40756
  charPosInRun = wordEndWithSpace;
@@ -40470,15 +40764,16 @@ async function measureParagraphBlock(block, maxWidth) {
40470
40764
  const boundarySpacing = currentLine.width > 0 ? run.letterSpacing ?? 0 : 0;
40471
40765
  const justifyAlignment = block.attrs?.alignment === "justify";
40472
40766
  const totalWidthWithWord = currentLine.width + boundarySpacing + wordCommitWidth + // Safe cast: only TextRuns produce word segments from split(), other run types are handled earlier
40473
- (isLastWord ? 0 : run.letterSpacing ?? 0);
40767
+ (shouldIncludeDelimiterSpace ? run.letterSpacing ?? 0 : 0);
40474
40768
  const availableWidth = currentLine.maxWidth - WIDTH_FUDGE_PX2;
40475
40769
  let shouldBreak = currentLine.width + boundarySpacing + wordOnlyWidth > availableWidth && currentLine.width > 0 && !isTocEntry;
40476
40770
  let compressedWidth = null;
40477
40771
  if (shouldBreak && justifyAlignment) {
40478
- const isParagraphLastWord = isLastWord && runIndex === runsToProcess.length - 1;
40772
+ const isLastNonEmptyWordInSegment = wordIndex === lastNonEmptyWordIndex;
40773
+ const isParagraphLastWord = isLastSegment && isLastNonEmptyWordInSegment && runIndex === runsToProcess.length - 1;
40479
40774
  if (!isParagraphLastWord) {
40480
40775
  const existingSpaces = currentLine.spaceCount ?? 0;
40481
- const candidateSpaces = existingSpaces + (isLastWord ? 0 : 1);
40776
+ const candidateSpaces = existingSpaces + (shouldIncludeDelimiterSpace ? 1 : 0);
40482
40777
  if (candidateSpaces > 0) {
40483
40778
  const overflow = totalWidthWithWord - availableWidth;
40484
40779
  if (overflow > 0) {
@@ -40494,8 +40789,9 @@ async function measureParagraphBlock(block, maxWidth) {
40494
40789
  }
40495
40790
  }
40496
40791
  if (shouldBreak) {
40792
+ trimTrailingWrapSpaces(currentLine);
40497
40793
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
40498
- const { spaceCount: _sc, ...lineBase } = currentLine;
40794
+ const lineBase = currentLine;
40499
40795
  const completedLine = {
40500
40796
  ...lineBase,
40501
40797
  ...metrics
@@ -40516,7 +40812,7 @@ async function measureParagraphBlock(block, maxWidth) {
40516
40812
  segments: [{ runIndex, fromChar: wordStartChar, toChar: wordEndNoSpace, width: wordOnlyWidth }],
40517
40813
  spaceCount: 0
40518
40814
  };
40519
- if (!isLastWord && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
40815
+ if (shouldIncludeDelimiterSpace && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
40520
40816
  currentLine.toChar = wordEndWithSpace;
40521
40817
  currentLine.width = roundValue(currentLine.width + spaceWidth + (run.letterSpacing ?? 0));
40522
40818
  charPosInRun = wordEndWithSpace;
@@ -40526,7 +40822,7 @@ async function measureParagraphBlock(block, maxWidth) {
40526
40822
  }
40527
40823
  } else {
40528
40824
  currentLine.toRun = runIndex;
40529
- if (!isLastWord && currentLine.width + boundarySpacing + wordOnlyWidth + spaceWidth > currentLine.maxWidth - WIDTH_FUDGE_PX2) {
40825
+ if (shouldIncludeDelimiterSpace && currentLine.width + boundarySpacing + wordOnlyWidth + spaceWidth > currentLine.maxWidth - WIDTH_FUDGE_PX2) {
40530
40826
  currentLine.toChar = wordEndNoSpace;
40531
40827
  currentLine.width = roundValue(currentLine.width + boundarySpacing + wordOnlyWidth);
40532
40828
  currentLine.maxFontInfo = updateMaxFontInfo(currentLine.maxFontSize, currentLine.maxFontInfo, run);
@@ -40540,8 +40836,9 @@ async function measureParagraphBlock(block, maxWidth) {
40540
40836
  wordOnlyWidth,
40541
40837
  useExplicitXHere ? segmentStartX : void 0
40542
40838
  );
40839
+ trimTrailingWrapSpaces(currentLine);
40543
40840
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
40544
- const { spaceCount: _sc, ...lineBase } = currentLine;
40841
+ const lineBase = currentLine;
40545
40842
  const completedLine = { ...lineBase, ...metrics };
40546
40843
  addBarTabsToLine(completedLine);
40547
40844
  lines.push(completedLine);
@@ -40551,20 +40848,23 @@ async function measureParagraphBlock(block, maxWidth) {
40551
40848
  charPosInRun = wordEndNoSpace + 1;
40552
40849
  continue;
40553
40850
  }
40554
- const newToChar = isLastWord ? wordEndNoSpace : wordEndWithSpace;
40851
+ const newToChar = shouldIncludeDelimiterSpace ? wordEndWithSpace : wordEndNoSpace;
40555
40852
  currentLine.toChar = newToChar;
40556
40853
  const useExplicitX = wordIndex === 0 && segmentStartX !== void 0;
40557
40854
  const explicitX = useExplicitX ? segmentStartX : void 0;
40558
- const targetWidth = compressedWidth != null ? compressedWidth : currentLine.width + boundarySpacing + wordCommitWidth + (isLastWord ? 0 : run.letterSpacing ?? 0);
40855
+ const targetWidth = compressedWidth != null ? compressedWidth : currentLine.width + boundarySpacing + wordCommitWidth + (shouldIncludeDelimiterSpace ? run.letterSpacing ?? 0 : 0);
40856
+ if (compressedWidth != null) {
40857
+ currentLine.naturalWidth = roundValue(totalWidthWithWord);
40858
+ }
40559
40859
  currentLine.width = roundValue(targetWidth);
40560
40860
  currentLine.maxFontInfo = updateMaxFontInfo(currentLine.maxFontSize, currentLine.maxFontInfo, run);
40561
40861
  currentLine.maxFontSize = Math.max(currentLine.maxFontSize, run.fontSize);
40562
40862
  appendSegment(currentLine.segments, runIndex, wordStartChar, newToChar, wordCommitWidth, explicitX);
40563
- if (!isLastWord) {
40863
+ if (shouldIncludeDelimiterSpace) {
40564
40864
  currentLine.spaceCount += 1;
40565
40865
  }
40566
40866
  }
40567
- charPosInRun = isLastWord ? wordEndNoSpace : wordEndWithSpace;
40867
+ charPosInRun = shouldIncludeDelimiterSpace ? wordEndWithSpace : wordEndNoSpace;
40568
40868
  }
40569
40869
  if (lastAppliedTabAlign && currentLine) {
40570
40870
  const appliedTab = lastAppliedTabAlign;
@@ -40584,7 +40884,7 @@ async function measureParagraphBlock(block, maxWidth) {
40584
40884
  width: 0,
40585
40885
  maxFontSize: run.fontSize,
40586
40886
  maxFontInfo: getFontInfoFromRun(run),
40587
- maxWidth: getEffectiveWidth(initialAvailableWidth),
40887
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
40588
40888
  segments: [],
40589
40889
  spaceCount: 0
40590
40890
  };
@@ -40616,8 +40916,8 @@ async function measureParagraphBlock(block, maxWidth) {
40616
40916
  }
40617
40917
  }
40618
40918
  if (!currentLine && lines.length === 0) {
40619
- const fallbackFontSize = (block.runs[0]?.kind === "text" ? block.runs[0].fontSize : void 0) ?? 12;
40620
- const metrics = calculateTypographyMetrics(fallbackFontSize, spacing);
40919
+ const uiDisplayFallbackFontSize = (block.runs[0]?.kind === "text" ? block.runs[0].fontSize : void 0) ?? 12;
40920
+ const metrics = calculateTypographyMetrics(uiDisplayFallbackFontSize, spacing);
40621
40921
  const fallbackLine = {
40622
40922
  fromRun: 0,
40623
40923
  fromChar: 0,
@@ -40632,7 +40932,7 @@ async function measureParagraphBlock(block, maxWidth) {
40632
40932
  }
40633
40933
  if (currentLine) {
40634
40934
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
40635
- const { spaceCount: _sc, ...lineBase } = currentLine;
40935
+ const lineBase = currentLine;
40636
40936
  const finalLine = {
40637
40937
  ...lineBase,
40638
40938
  ...metrics