@harbour-enterprises/superdoc 1.0.0-next.3 → 1.0.0-next.5

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 (54) hide show
  1. package/dist/chunks/{PdfViewer-PUn0q_lG.es.js → PdfViewer-BQ7d08ku.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-cVGt7Ne7.cjs → PdfViewer-D8FKTxiO.cjs} +1 -1
  3. package/dist/chunks/{index-Djmhr-R6.cjs → index-BOkSE8Li.cjs} +6 -5
  4. package/dist/chunks/{index-DLO-SgUP-BNtYQDPq.cjs → index-BUoOdDUj-CF4U3xYL.cjs} +1 -1
  5. package/dist/chunks/{index-DLO-SgUP-c73otQJ5.es.js → index-BUoOdDUj-CLmpIPsb.es.js} +1 -1
  6. package/dist/chunks/{index-mHanL4xN.es.js → index-D3wrs960.es.js} +6 -5
  7. package/dist/chunks/{super-editor.es-Cve8WKfM.cjs → super-editor.es-Bj_Ga0sp.cjs} +417 -117
  8. package/dist/chunks/{super-editor.es-S9Kp_7dy.es.js → super-editor.es-vynpc_1U.es.js} +417 -117
  9. package/dist/style.css +6 -6
  10. package/dist/super-editor/ai-writer.es.js +2 -2
  11. package/dist/super-editor/chunks/{converter-ByybZRFp.js → converter-D6Kvbivo.js} +1 -1
  12. package/dist/super-editor/chunks/{docx-zipper-CFQANTLI.js → docx-zipper-DAS45uKP.js} +1 -1
  13. package/dist/super-editor/chunks/{editor-Cu4hD14N.js → editor-BMEPaiVS.js} +418 -118
  14. package/dist/super-editor/chunks/{index-DLO-SgUP.js → index-BUoOdDUj.js} +1 -1
  15. package/dist/super-editor/chunks/{toolbar-5MMla0sj.js → toolbar-CUWKY5Cx.js} +2 -2
  16. package/dist/super-editor/converter.es.js +1 -1
  17. package/dist/super-editor/docx-zipper.es.js +2 -2
  18. package/dist/super-editor/editor.es.js +3 -3
  19. package/dist/super-editor/file-zipper.es.js +1 -1
  20. package/dist/super-editor/super-editor.es.js +6 -6
  21. package/dist/super-editor/toolbar.es.js +2 -2
  22. package/dist/super-editor.cjs +1 -1
  23. package/dist/super-editor.es.js +1 -1
  24. package/dist/superdoc.cjs +2 -2
  25. package/dist/superdoc.es.js +2 -2
  26. package/dist/superdoc.umd.js +422 -121
  27. package/dist/superdoc.umd.js.map +1 -1
  28. package/package.json +1 -1
  29. package/dist/images/altText_add.svg +0 -3
  30. package/dist/images/altText_disclaimer.svg +0 -3
  31. package/dist/images/altText_done.svg +0 -3
  32. package/dist/images/altText_spinner.svg +0 -30
  33. package/dist/images/altText_warning.svg +0 -3
  34. package/dist/images/annotation-check.svg +0 -11
  35. package/dist/images/annotation-comment.svg +0 -16
  36. package/dist/images/annotation-help.svg +0 -26
  37. package/dist/images/annotation-insert.svg +0 -10
  38. package/dist/images/annotation-key.svg +0 -11
  39. package/dist/images/annotation-newparagraph.svg +0 -11
  40. package/dist/images/annotation-noicon.svg +0 -7
  41. package/dist/images/annotation-note.svg +0 -42
  42. package/dist/images/annotation-paperclip.svg +0 -6
  43. package/dist/images/annotation-paragraph.svg +0 -16
  44. package/dist/images/annotation-pushpin.svg +0 -7
  45. package/dist/images/cursor-editorFreeHighlight.svg +0 -6
  46. package/dist/images/cursor-editorFreeText.svg +0 -3
  47. package/dist/images/cursor-editorInk.svg +0 -4
  48. package/dist/images/cursor-editorTextHighlight.svg +0 -8
  49. package/dist/images/editor-toolbar-delete.svg +0 -5
  50. package/dist/images/loading-icon.gif +0 -0
  51. package/dist/images/messageBar_closingButton.svg +0 -3
  52. package/dist/images/messageBar_warning.svg +0 -3
  53. package/dist/images/toolbarButton-editorHighlight.svg +0 -6
  54. package/dist/images/toolbarButton-menuArrow.svg +0 -3
@@ -42410,7 +42410,7 @@ const _SuperConverter = class _SuperConverter2 {
42410
42410
  static getStoredSuperdocVersion(docx) {
42411
42411
  return _SuperConverter2.getStoredCustomProperty(docx, "SuperdocVersion");
42412
42412
  }
42413
- static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "1.0.0-next.3") {
42413
+ static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "1.0.0-next.5") {
42414
42414
  return _SuperConverter2.setStoredCustomProperty(docx, "SuperdocVersion", version2, false);
42415
42415
  }
42416
42416
  /**
@@ -59638,7 +59638,7 @@ const isHeadless = (editor) => {
59638
59638
  const shouldSkipNodeView = (editor) => {
59639
59639
  return isHeadless(editor);
59640
59640
  };
59641
- const summaryVersion = "1.0.0-next.3";
59641
+ const summaryVersion = "1.0.0-next.5";
59642
59642
  const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
59643
59643
  const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
59644
59644
  function mapAttributes(attrs) {
@@ -60427,7 +60427,7 @@ const _Editor = class _Editor2 extends EventEmitter$1 {
60427
60427
  { default: remarkStringify },
60428
60428
  { default: remarkGfm }
60429
60429
  ] = await Promise.all([
60430
- import("./index-DLO-SgUP-c73otQJ5.es.js"),
60430
+ import("./index-BUoOdDUj-CLmpIPsb.es.js"),
60431
60431
  import("./index-DRCvimau-Cw339678.es.js"),
60432
60432
  import("./index-C_x_N6Uh-DJn8hIEt.es.js"),
60433
60433
  import("./index-D_sWOSiG-DE96TaT5.es.js"),
@@ -60632,7 +60632,7 @@ const _Editor = class _Editor2 extends EventEmitter$1 {
60632
60632
  * Process collaboration migrations
60633
60633
  */
60634
60634
  processCollaborationMigrations() {
60635
- console.debug("[checkVersionMigrations] Current editor version", "1.0.0-next.3");
60635
+ console.debug("[checkVersionMigrations] Current editor version", "1.0.0-next.5");
60636
60636
  if (!this.options.ydoc) return;
60637
60637
  const metaMap = this.options.ydoc.getMap("meta");
60638
60638
  let docVersion = metaMap.get("version");
@@ -66237,6 +66237,34 @@ function calculateTabWidth(params2) {
66237
66237
  tabStopPosUsed: nextStop.pos
66238
66238
  };
66239
66239
  }
66240
+ const SPACE_CHARS$1 = /* @__PURE__ */ new Set([" ", " "]);
66241
+ function shouldApplyJustify(params2) {
66242
+ const { alignment: alignment2, hasExplicitPositioning, isLastLineOfParagraph, paragraphEndsWithLineBreak, skipJustifyOverride } = params2;
66243
+ if (alignment2 !== "justify" && alignment2 !== "both") {
66244
+ return false;
66245
+ }
66246
+ if (skipJustifyOverride === true) {
66247
+ return false;
66248
+ }
66249
+ if (hasExplicitPositioning) {
66250
+ return false;
66251
+ }
66252
+ if (isLastLineOfParagraph && !paragraphEndsWithLineBreak) {
66253
+ return false;
66254
+ }
66255
+ return true;
66256
+ }
66257
+ function calculateJustifySpacing(params2) {
66258
+ const { lineWidth, availableWidth, spaceCount, shouldJustify } = params2;
66259
+ if (!shouldJustify) {
66260
+ return 0;
66261
+ }
66262
+ if (spaceCount <= 0) {
66263
+ return 0;
66264
+ }
66265
+ const slack = availableWidth - lineWidth;
66266
+ return slack / spaceCount;
66267
+ }
66240
66268
  function resolveSpacingIndent$1(style2, numbering) {
66241
66269
  const spacing = {
66242
66270
  before: style2.spacing?.before ?? 0,
@@ -66503,13 +66531,14 @@ const hydrateParagraphStyleAttrs = (para, context, preResolved) => {
66503
66531
  };
66504
66532
  }
66505
66533
  }
66534
+ const normalizedAlign = normalizeAlignment(resolvedExtended.justification);
66506
66535
  const hydrated = {
66507
66536
  resolved,
66508
66537
  spacing: resolvedSpacing,
66509
66538
  indent: resolvedIndent,
66510
66539
  borders: cloneIfObject(resolvedExtended.borders),
66511
66540
  shading: cloneIfObject(resolvedExtended.shading),
66512
- alignment: resolvedExtended.justification,
66541
+ alignment: normalizedAlign,
66513
66542
  tabStops: cloneIfObject(resolvedExtended.tabStops),
66514
66543
  keepLines: resolvedExtended.keepLines,
66515
66544
  keepNext: resolvedExtended.keepNext,
@@ -67210,7 +67239,7 @@ const computeParagraphAttrs = (para, styleContext, listCounterContext, converter
67210
67239
  } else if (styleAlignment) {
67211
67240
  paragraphAttrs.alignment = styleAlignment;
67212
67241
  } else if (computed2.paragraph.alignment) {
67213
- paragraphAttrs.alignment = computed2.paragraph.alignment;
67242
+ paragraphAttrs.alignment = normalizeAlignment(computed2.paragraph.alignment);
67214
67243
  }
67215
67244
  const spacingPx = spacingPtToPx(spacing, normalizedSpacing);
67216
67245
  if (spacingPx) paragraphAttrs.spacing = spacingPx;
@@ -70167,7 +70196,7 @@ function tableNodeToBlock(node, nextBlockId, positions, defaultFont, defaultSize
70167
70196
  let measurementCanvas = null;
70168
70197
  let measurementCtx = null;
70169
70198
  const TAB_CHAR_LENGTH = 1;
70170
- const SPACE_CHARS = /* @__PURE__ */ new Set([" ", " "]);
70199
+ const SPACE_CHARS = SPACE_CHARS$1;
70171
70200
  const isTabRun$1 = (run2) => run2?.kind === "tab";
70172
70201
  function getMeasurementContext() {
70173
70202
  if (measurementCtx) return measurementCtx;
@@ -70193,29 +70222,50 @@ const countSpaces = (text) => {
70193
70222
  }
70194
70223
  return spaces;
70195
70224
  };
70196
- const getJustifyAdjustment = (block, line, availableWidthOverride, alignmentOverride) => {
70225
+ const getJustifyAdjustment = (block, line, availableWidthOverride, alignmentOverride, isLastLineOfParagraph, paragraphEndsWithLineBreak, skipJustifyOverride) => {
70197
70226
  if (block.kind !== "paragraph") {
70198
70227
  return { extraPerSpace: 0, totalSpaces: 0 };
70199
70228
  }
70200
- const alignment2 = alignmentOverride ?? block.attrs?.alignment;
70201
- const hasExplicitPositioning = line.segments?.some((seg) => seg.x !== void 0);
70202
- const availableWidth = availableWidthOverride ?? line.maxWidth ?? line.width;
70203
- const slack = Math.max(0, availableWidth - line.width);
70204
- if (alignment2 !== "justify" || hasExplicitPositioning || slack <= 0) {
70229
+ if (block.runs.length === 0) {
70205
70230
  return { extraPerSpace: 0, totalSpaces: 0 };
70206
70231
  }
70207
- const runs = sliceRunsForLine$1(block, line);
70208
- const totalSpaces = runs.reduce((sum, run2) => {
70209
- if (isTabRun$1(run2) || "src" in run2 || run2.kind === "lineBreak" || run2.kind === "break" || run2.kind === "fieldAnnotation") {
70210
- return sum;
70211
- }
70212
- return sum + countSpaces(run2.text ?? "");
70213
- }, 0);
70214
- if (totalSpaces <= 0) {
70232
+ const alignment2 = alignmentOverride ?? block.attrs?.alignment;
70233
+ const hasExplicitPositioning = line.segments?.some((seg) => seg.x !== void 0) ?? false;
70234
+ const lastRunIndex = block.runs.length - 1;
70235
+ const lastRun = block.runs[lastRunIndex];
70236
+ const derivedIsLastLine = line.toRun >= lastRunIndex;
70237
+ const derivedEndsWithLineBreak = lastRun ? lastRun.kind === "lineBreak" : false;
70238
+ const shouldJustify = shouldApplyJustify({
70239
+ alignment: alignment2,
70240
+ hasExplicitPositioning,
70241
+ isLastLineOfParagraph: derivedIsLastLine,
70242
+ paragraphEndsWithLineBreak: derivedEndsWithLineBreak,
70243
+ skipJustifyOverride
70244
+ });
70245
+ if (!shouldJustify) {
70215
70246
  return { extraPerSpace: 0, totalSpaces: 0 };
70216
70247
  }
70248
+ let totalSpaces = line.spaceCount ?? 0;
70249
+ if (totalSpaces === 0) {
70250
+ const runs = sliceRunsForLine$1(block, line);
70251
+ totalSpaces = runs.reduce((sum, run2) => {
70252
+ if (isTabRun$1(run2) || "src" in run2 || run2.kind === "lineBreak" || run2.kind === "break" || run2.kind === "fieldAnnotation") {
70253
+ return sum;
70254
+ }
70255
+ return sum + countSpaces(run2.text ?? "");
70256
+ }, 0);
70257
+ }
70258
+ const availableWidth = availableWidthOverride ?? line.maxWidth ?? line.width;
70259
+ const lineWidth = line.naturalWidth ?? line.width;
70260
+ const extraPerSpace = calculateJustifySpacing({
70261
+ lineWidth,
70262
+ availableWidth,
70263
+ spaceCount: totalSpaces,
70264
+ shouldJustify: true
70265
+ // Already checked above
70266
+ });
70217
70267
  return {
70218
- extraPerSpace: slack / totalSpaces,
70268
+ extraPerSpace,
70219
70269
  totalSpaces
70220
70270
  };
70221
70271
  };
@@ -70282,7 +70332,7 @@ function measureCharacterX(block, line, charOffset, availableWidthOverride, alig
70282
70332
  line.width;
70283
70333
  const justify = getJustifyAdjustment(block, line, availableWidth, alignmentOverride);
70284
70334
  const alignment2 = alignmentOverride ?? (block.kind === "paragraph" ? block.attrs?.alignment : void 0);
70285
- const renderedLineWidth = alignment2 === "justify" ? line.width + Math.max(0, availableWidth - line.width) : line.width;
70335
+ const renderedLineWidth = alignment2 === "justify" && justify.extraPerSpace !== 0 ? availableWidth : line.width;
70286
70336
  const hasExplicitPositioning = line.segments?.some((seg) => seg.x !== void 0);
70287
70337
  const alignmentOffset = !hasExplicitPositioning && alignment2 === "center" ? Math.max(0, (availableWidth - renderedLineWidth) / 2) : !hasExplicitPositioning && alignment2 === "right" ? Math.max(0, availableWidth - renderedLineWidth) : 0;
70288
70338
  if (hasExplicitPositioning && line.segments && ctx2) {
@@ -70325,13 +70375,13 @@ function measureCharacterX(block, line, charOffset, availableWidthOverride, alig
70325
70375
  const textUpToTarget = text.slice(0, offsetInRun);
70326
70376
  const measured2 = ctx2.measureText(textUpToTarget);
70327
70377
  const spacingWidth = computeLetterSpacingWidth(run2, offsetInRun, runLength);
70328
- const spacesInPortion = justify.extraPerSpace > 0 ? countSpaces(textUpToTarget) : 0;
70378
+ const spacesInPortion = justify.extraPerSpace !== 0 ? countSpaces(textUpToTarget) : 0;
70329
70379
  return alignmentOffset + currentX + measured2.width + spacingWidth + justify.extraPerSpace * (spaceTally + spacesInPortion);
70330
70380
  }
70331
70381
  ctx2.font = getRunFontString(run2);
70332
70382
  const measured = ctx2.measureText(text);
70333
70383
  const runLetterSpacing = computeLetterSpacingWidth(run2, runLength, runLength);
70334
- const spacesInRun = justify.extraPerSpace > 0 ? countSpaces(text) : 0;
70384
+ const spacesInRun = justify.extraPerSpace !== 0 ? countSpaces(text) : 0;
70335
70385
  currentX += measured.width + runLetterSpacing + justify.extraPerSpace * spacesInRun;
70336
70386
  spaceTally += spacesInRun;
70337
70387
  currentCharOffset += runLength;
@@ -75483,9 +75533,66 @@ const _DomPainter = class _DomPainter2 {
75483
75533
  const firstLineOffset = suppressFirstLineIndent ? 0 : (paraIndent?.firstLine ?? 0) - (paraIndent?.hanging ?? 0);
75484
75534
  const lastRun = block.runs.length > 0 ? block.runs[block.runs.length - 1] : null;
75485
75535
  const paragraphEndsWithLineBreak = lastRun?.kind === "lineBreak";
75536
+ let listFirstLineMarkerTabWidth;
75537
+ if (!fragment.continuesFromPrev && fragment.markerWidth && wordLayout?.marker) {
75538
+ const markerBoxWidth = fragment.markerWidth;
75539
+ const markerTextWidth = fragment.markerTextWidth != null && isFinite(fragment.markerTextWidth) && fragment.markerTextWidth >= 0 ? fragment.markerTextWidth : markerBoxWidth;
75540
+ const suffix2 = wordLayout.marker.suffix ?? "tab";
75541
+ if (suffix2 === "tab") {
75542
+ const markerJustification = wordLayout.marker.justification ?? "left";
75543
+ const isFirstLineIndentMode = wordLayout.firstLineIndentMode === true;
75544
+ let markerStartPos;
75545
+ if (isFirstLineIndentMode && wordLayout.marker.markerX !== void 0 && Number.isFinite(wordLayout.marker.markerX)) {
75546
+ markerStartPos = wordLayout.marker.markerX;
75547
+ } else {
75548
+ const hanging = paraIndent?.hanging ?? 0;
75549
+ const firstLine = paraIndent?.firstLine ?? 0;
75550
+ markerStartPos = paraIndentLeft - hanging + firstLine;
75551
+ }
75552
+ const validMarkerStartPos = Number.isFinite(markerStartPos) ? markerStartPos : 0;
75553
+ let tabWidth;
75554
+ if (markerJustification === "left") {
75555
+ const currentPos = validMarkerStartPos + markerTextWidth;
75556
+ if (isFirstLineIndentMode) {
75557
+ const textStartTarget = wordLayout.marker.textStartX !== void 0 && Number.isFinite(wordLayout.marker.textStartX) ? wordLayout.marker.textStartX : wordLayout.textStartPx;
75558
+ if (textStartTarget !== void 0 && Number.isFinite(textStartTarget) && textStartTarget > currentPos) {
75559
+ tabWidth = textStartTarget - currentPos;
75560
+ } else {
75561
+ tabWidth = LIST_MARKER_GAP$1;
75562
+ }
75563
+ } else {
75564
+ const firstLine = paraIndent?.firstLine ?? 0;
75565
+ const textStart = paraIndentLeft + firstLine;
75566
+ tabWidth = textStart - currentPos;
75567
+ if (tabWidth <= 0) {
75568
+ tabWidth = DEFAULT_TAB_INTERVAL_PX$1 - currentPos % DEFAULT_TAB_INTERVAL_PX$1;
75569
+ } else if (tabWidth < LIST_MARKER_GAP$1) {
75570
+ tabWidth = LIST_MARKER_GAP$1;
75571
+ }
75572
+ }
75573
+ } else {
75574
+ const gutterWidth = fragment.markerGutter ?? wordLayout.marker.gutterWidthPx;
75575
+ tabWidth = gutterWidth !== void 0 && Number.isFinite(gutterWidth) && gutterWidth > 0 ? gutterWidth : LIST_MARKER_GAP$1;
75576
+ }
75577
+ if (tabWidth < LIST_MARKER_GAP$1) {
75578
+ tabWidth = LIST_MARKER_GAP$1;
75579
+ }
75580
+ listFirstLineMarkerTabWidth = validMarkerStartPos + markerTextWidth + tabWidth;
75581
+ } else if (suffix2 === "space") {
75582
+ const hanging = paraIndent?.hanging ?? 0;
75583
+ const firstLine = paraIndent?.firstLine ?? 0;
75584
+ const markerStartPos = paraIndentLeft - hanging + firstLine;
75585
+ const validMarkerStartPos = Number.isFinite(markerStartPos) ? markerStartPos : 0;
75586
+ listFirstLineMarkerTabWidth = validMarkerStartPos + markerTextWidth + 4;
75587
+ }
75588
+ }
75486
75589
  lines.forEach((line, index2) => {
75487
- const fallbackAvailableWidth = Math.max(0, fragment.width - (paraIndentLeft + paraIndentRight));
75488
- const availableWidthOverride = line.maxWidth ?? fallbackAvailableWidth;
75590
+ const positiveIndentReduction = Math.max(0, paraIndentLeft) + Math.max(0, paraIndentRight);
75591
+ const fallbackAvailableWidth = Math.max(0, fragment.width - positiveIndentReduction);
75592
+ let availableWidthOverride = line.maxWidth != null ? Math.min(line.maxWidth, fallbackAvailableWidth) : fallbackAvailableWidth;
75593
+ if (index2 === 0 && listFirstLineMarkerTabWidth != null) {
75594
+ availableWidthOverride = fragment.width - listFirstLineMarkerTabWidth - Math.max(0, paraIndentRight);
75595
+ }
75489
75596
  const isLastLineOfFragment = index2 === lines.length - 1;
75490
75597
  const isLastLineOfParagraph = isLastLineOfFragment && !fragment.continuesOnNext;
75491
75598
  const shouldSkipJustifyForLastLine = isLastLineOfParagraph && !paragraphEndsWithLineBreak;
@@ -75511,7 +75618,10 @@ const _DomPainter = class _DomPainter2 {
75511
75618
  }
75512
75619
  } else if (paraIndentLeft && paraIndentLeft > 0) {
75513
75620
  lineEl.style.paddingLeft = `${paraIndentLeft}px`;
75514
- } else if (!isFirstLine && paraIndent?.hanging && paraIndent.hanging > 0) {
75621
+ } else if (!isFirstLine && paraIndent?.hanging && paraIndent.hanging > 0 && // Only apply hanging padding when left indent is NOT negative.
75622
+ // When left indent is negative, the fragment position already accounts for it.
75623
+ // Adding padding here would shift body lines right, causing right-side overflow.
75624
+ !(paraIndentLeft != null && paraIndentLeft < 0)) {
75515
75625
  lineEl.style.paddingLeft = `${paraIndent.hanging}px`;
75516
75626
  }
75517
75627
  }
@@ -75540,6 +75650,7 @@ const _DomPainter = class _DomPainter2 {
75540
75650
  lineEl.style.paddingLeft = `${validMarkerStartPos}px`;
75541
75651
  const markerContainer = this.doc.createElement("span");
75542
75652
  markerContainer.style.display = "inline-block";
75653
+ markerContainer.style.wordSpacing = "0px";
75543
75654
  const markerEl = this.doc.createElement("span");
75544
75655
  markerEl.classList.add("superdoc-paragraph-marker");
75545
75656
  markerEl.textContent = wordLayout.marker.markerText ?? "";
@@ -75625,10 +75736,15 @@ const _DomPainter = class _DomPainter2 {
75625
75736
  }
75626
75737
  }
75627
75738
  tabEl.style.display = "inline-block";
75739
+ tabEl.style.wordSpacing = "0px";
75628
75740
  tabEl.style.width = `${tabWidth}px`;
75629
75741
  lineEl.prepend(tabEl);
75630
75742
  } else if (suffix2 === "space") {
75631
- lineEl.prepend(this.doc.createTextNode(" "));
75743
+ const spaceEl = this.doc.createElement("span");
75744
+ spaceEl.classList.add("superdoc-marker-suffix-space");
75745
+ spaceEl.style.wordSpacing = "0px";
75746
+ spaceEl.textContent = " ";
75747
+ lineEl.prepend(spaceEl);
75632
75748
  }
75633
75749
  lineEl.prepend(markerContainer);
75634
75750
  }
@@ -76920,11 +77036,8 @@ const _DomPainter = class _DomPainter2 {
76920
77036
  el.setAttribute("styleid", styleId);
76921
77037
  }
76922
77038
  const alignment2 = block.attrs?.alignment;
76923
- const effectiveAlignment = alignment2;
76924
- if (effectiveAlignment === "center" || effectiveAlignment === "right") {
76925
- el.style.textAlign = effectiveAlignment;
76926
- } else if (effectiveAlignment === "justify") {
76927
- el.style.textAlign = "left";
77039
+ if (alignment2 === "center" || alignment2 === "right") {
77040
+ el.style.textAlign = alignment2;
76928
77041
  } else {
76929
77042
  el.style.textAlign = "left";
76930
77043
  }
@@ -76935,9 +77048,8 @@ const _DomPainter = class _DomPainter2 {
76935
77048
  if (lineRange.pmEnd != null) {
76936
77049
  el.dataset.pmEnd = String(lineRange.pmEnd);
76937
77050
  }
76938
- const runsForLine = sliceRunsForLine(block, line);
77051
+ let runsForLine = sliceRunsForLine(block, line);
76939
77052
  const trackedConfig = this.resolveTrackedChangesConfig(block);
76940
- 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);
76941
77053
  if (runsForLine.length === 0) {
76942
77054
  const span = this.doc.createElement("span");
76943
77055
  span.innerHTML = "&nbsp;";
@@ -76984,7 +77096,156 @@ const _DomPainter = class _DomPainter2 {
76984
77096
  });
76985
77097
  }
76986
77098
  const hasExplicitPositioning = line.segments?.some((seg) => seg.x !== void 0);
76987
- availableWidthOverride ?? line.maxWidth ?? line.width;
77099
+ const availableWidth = availableWidthOverride ?? line.maxWidth ?? line.width;
77100
+ const justifyShouldApply = shouldApplyJustify({
77101
+ alignment: block.attrs?.alignment,
77102
+ hasExplicitPositioning: hasExplicitPositioning ?? false,
77103
+ // Caller already folds last-line + trailing lineBreak behavior into skipJustify.
77104
+ isLastLineOfParagraph: false,
77105
+ paragraphEndsWithLineBreak: false,
77106
+ skipJustifyOverride: skipJustify
77107
+ });
77108
+ const countSpaces2 = (text) => {
77109
+ let count = 0;
77110
+ for (let i = 0; i < text.length; i += 1) {
77111
+ if (SPACE_CHARS$1.has(text[i])) count += 1;
77112
+ }
77113
+ return count;
77114
+ };
77115
+ if (justifyShouldApply) {
77116
+ const stableDataAttrs = (attrs) => {
77117
+ if (!attrs) return void 0;
77118
+ const keys2 = Object.keys(attrs).sort();
77119
+ const out = {};
77120
+ keys2.forEach((key2) => {
77121
+ out[key2] = attrs[key2];
77122
+ });
77123
+ return out;
77124
+ };
77125
+ const mergeSignature = (run2) => JSON.stringify({
77126
+ kind: run2.kind ?? "text",
77127
+ fontFamily: run2.fontFamily,
77128
+ fontSize: run2.fontSize,
77129
+ bold: run2.bold ?? false,
77130
+ italic: run2.italic ?? false,
77131
+ letterSpacing: run2.letterSpacing ?? null,
77132
+ color: run2.color ?? null,
77133
+ underline: run2.underline ?? null,
77134
+ strike: run2.strike ?? false,
77135
+ highlight: run2.highlight ?? null,
77136
+ textTransform: run2.textTransform ?? null,
77137
+ token: run2.token ?? null,
77138
+ pageRefMetadata: run2.pageRefMetadata ?? null,
77139
+ trackedChange: run2.trackedChange ?? null,
77140
+ sdt: run2.sdt ?? null,
77141
+ link: run2.link ?? null,
77142
+ comments: run2.comments ?? null,
77143
+ dataAttrs: stableDataAttrs(run2.dataAttrs) ?? null
77144
+ });
77145
+ const isWhitespaceOnly = (text) => {
77146
+ if (text.length === 0) return false;
77147
+ for (let i = 0; i < text.length; i += 1) {
77148
+ if (!SPACE_CHARS$1.has(text[i])) return false;
77149
+ }
77150
+ return true;
77151
+ };
77152
+ const cloneTextRun = (run2) => ({
77153
+ ...run2,
77154
+ comments: run2.comments ? [...run2.comments] : void 0,
77155
+ dataAttrs: run2.dataAttrs ? { ...run2.dataAttrs } : void 0,
77156
+ underline: run2.underline ? { ...run2.underline } : void 0,
77157
+ pageRefMetadata: run2.pageRefMetadata ? { ...run2.pageRefMetadata } : void 0
77158
+ });
77159
+ const normalized = runsForLine.map((run2) => {
77160
+ if (run2.kind !== "text" && run2.kind !== void 0 || !("text" in run2)) return run2;
77161
+ return cloneTextRun(run2);
77162
+ });
77163
+ const merged = [];
77164
+ for (let i = 0; i < normalized.length; i += 1) {
77165
+ const run2 = normalized[i];
77166
+ if (run2.kind !== "text" && run2.kind !== void 0 || !("text" in run2)) {
77167
+ merged.push(run2);
77168
+ continue;
77169
+ }
77170
+ const textRun = run2;
77171
+ if (!isWhitespaceOnly(textRun.text ?? "")) {
77172
+ merged.push(textRun);
77173
+ continue;
77174
+ }
77175
+ const prev = merged[merged.length - 1];
77176
+ if (prev && (prev.kind === "text" || prev.kind === void 0) && "text" in prev) {
77177
+ const prevTextRun = prev;
77178
+ if (mergeSignature(prevTextRun) === mergeSignature(textRun)) {
77179
+ const extra = textRun.text ?? "";
77180
+ prevTextRun.text = (prevTextRun.text ?? "") + extra;
77181
+ if (prevTextRun.pmStart != null) {
77182
+ prevTextRun.pmEnd = prevTextRun.pmStart + prevTextRun.text.length;
77183
+ } else if (prevTextRun.pmEnd != null) {
77184
+ prevTextRun.pmEnd = prevTextRun.pmEnd + extra.length;
77185
+ }
77186
+ continue;
77187
+ }
77188
+ }
77189
+ const next = normalized[i + 1];
77190
+ if (next && (next.kind === "text" || next.kind === void 0) && "text" in next) {
77191
+ const nextTextRun = next;
77192
+ if (mergeSignature(nextTextRun) === mergeSignature(textRun)) {
77193
+ const extra = textRun.text ?? "";
77194
+ nextTextRun.text = extra + (nextTextRun.text ?? "");
77195
+ if (textRun.pmStart != null) {
77196
+ nextTextRun.pmStart = textRun.pmStart;
77197
+ } else if (nextTextRun.pmStart != null) {
77198
+ nextTextRun.pmStart = nextTextRun.pmStart - extra.length;
77199
+ }
77200
+ if (nextTextRun.pmStart != null && nextTextRun.pmEnd == null) {
77201
+ nextTextRun.pmEnd = nextTextRun.pmStart + nextTextRun.text.length;
77202
+ }
77203
+ continue;
77204
+ }
77205
+ }
77206
+ merged.push(textRun);
77207
+ }
77208
+ runsForLine = merged;
77209
+ const hasNonSpaceText = runsForLine.some(
77210
+ (run2) => (run2.kind === "text" || run2.kind === void 0) && "text" in run2 && (run2.text ?? "").trim().length > 0
77211
+ );
77212
+ if (hasNonSpaceText) {
77213
+ for (let i = runsForLine.length - 1; i >= 0; i -= 1) {
77214
+ const run2 = runsForLine[i];
77215
+ if (run2.kind !== "text" && run2.kind !== void 0 || !("text" in run2)) continue;
77216
+ const text = run2.text ?? "";
77217
+ let trimCount = 0;
77218
+ for (let j2 = text.length - 1; j2 >= 0 && text[j2] === " "; j2 -= 1) {
77219
+ trimCount += 1;
77220
+ }
77221
+ if (trimCount === 0) break;
77222
+ const nextText = text.slice(0, Math.max(0, text.length - trimCount));
77223
+ if (nextText.length === 0) {
77224
+ runsForLine.splice(i, 1);
77225
+ continue;
77226
+ }
77227
+ run2.text = nextText;
77228
+ if (run2.pmEnd != null) {
77229
+ run2.pmEnd = run2.pmEnd - trimCount;
77230
+ }
77231
+ break;
77232
+ }
77233
+ }
77234
+ }
77235
+ const spaceCount = line.spaceCount ?? runsForLine.reduce((sum, run2) => {
77236
+ if (run2.kind !== "text" && run2.kind !== void 0 || !("text" in run2) || run2.text == null) return sum;
77237
+ return sum + countSpaces2(run2.text);
77238
+ }, 0);
77239
+ const lineWidth = line.naturalWidth ?? line.width;
77240
+ const spacingPerSpace = calculateJustifySpacing({
77241
+ lineWidth,
77242
+ availableWidth,
77243
+ spaceCount,
77244
+ shouldJustify: justifyShouldApply
77245
+ });
77246
+ if (spacingPerSpace !== 0) {
77247
+ el.style.wordSpacing = `${spacingPerSpace}px`;
77248
+ }
76988
77249
  if (hasExplicitPositioning && line.segments) {
76989
77250
  const paraIndent = block.attrs?.indent;
76990
77251
  const indentLeft = paraIndent?.left ?? 0;
@@ -77856,7 +78117,7 @@ const applyParagraphBlockStyles = (element, attrs) => {
77856
78117
  element.setAttribute("styleid", attrs.styleId);
77857
78118
  }
77858
78119
  if (attrs.alignment) {
77859
- element.style.textAlign = attrs.alignment;
78120
+ element.style.textAlign = attrs.alignment === "justify" || attrs.alignment === "both" ? "left" : attrs.alignment;
77860
78121
  }
77861
78122
  if (attrs.dropCap) {
77862
78123
  element.classList.add("sd-editor-dropcap");
@@ -77921,23 +78182,6 @@ const applyParagraphShadingStyles = (element, shading) => {
77921
78182
  if (!shading?.fill) return;
77922
78183
  element.style.backgroundColor = shading.fill;
77923
78184
  };
77924
- const gatherTextSlicesForLine = (block, line) => {
77925
- const slices = [];
77926
- const startRun = line.fromRun ?? 0;
77927
- const endRun = line.toRun ?? startRun;
77928
- for (let runIndex = startRun; runIndex <= endRun; runIndex += 1) {
77929
- const run2 = block.runs[runIndex];
77930
- if (!run2 || run2.kind !== "text" && run2.kind !== void 0 || !("text" in run2) || !run2.text) continue;
77931
- const isFirst = runIndex === startRun;
77932
- const isLast = runIndex === endRun;
77933
- const start2 = isFirst ? line.fromChar ?? 0 : 0;
77934
- const end2 = isLast ? line.toChar ?? run2.text.length : run2.text.length;
77935
- if (start2 >= end2) continue;
77936
- const slice2 = run2.text.slice(start2, end2);
77937
- if (slice2) slices.push(slice2);
77938
- }
77939
- return slices;
77940
- };
77941
78185
  const sliceRunsForLine = (block, line) => {
77942
78186
  const result = [];
77943
78187
  for (let runIndex = line.fromRun; runIndex <= line.toRun; runIndex += 1) {
@@ -78559,7 +78803,8 @@ function calculateTextStartIndent(params2) {
78559
78803
  let indentAdjust = paraIndentLeft;
78560
78804
  if (isListItem2 && isFirstLine && isFirstLineIndentMode) {
78561
78805
  const textStartFallback = paraIndentLeft + Math.max(firstLineIndent, 0) + markerWidth;
78562
- indentAdjust = typeof wordLayout?.textStartPx === "number" && Number.isFinite(wordLayout.textStartPx) ? wordLayout.textStartPx : textStartFallback;
78806
+ const markerTextStartX = wordLayout?.marker?.textStartX;
78807
+ indentAdjust = typeof markerTextStartX === "number" && Number.isFinite(markerTextStartX) ? markerTextStartX : typeof wordLayout?.textStartPx === "number" && Number.isFinite(wordLayout.textStartPx) ? wordLayout.textStartPx : textStartFallback;
78563
78808
  } else if (isFirstLine && !isListItem2) {
78564
78809
  indentAdjust += firstLineOffset;
78565
78810
  }
@@ -79249,6 +79494,20 @@ const asSafeNumber = (value) => {
79249
79494
  }
79250
79495
  return value;
79251
79496
  };
79497
+ function calculateFirstLineIndent(block, measure) {
79498
+ const wordLayout = block.attrs?.wordLayout;
79499
+ if (!wordLayout?.firstLineIndentMode) {
79500
+ return 0;
79501
+ }
79502
+ if (!wordLayout.marker || !measure.marker) {
79503
+ return 0;
79504
+ }
79505
+ const markerWidthRaw = measure.marker.markerWidth ?? wordLayout.marker.markerBoxWidthPx ?? 0;
79506
+ const markerWidth = Number.isFinite(markerWidthRaw) && markerWidthRaw >= 0 ? markerWidthRaw : 0;
79507
+ const gutterWidthRaw = measure.marker.gutterWidth ?? 0;
79508
+ const gutterWidth = Number.isFinite(gutterWidthRaw) && gutterWidthRaw >= 0 ? gutterWidthRaw : 0;
79509
+ return markerWidth + gutterWidth;
79510
+ }
79252
79511
  function layoutParagraphBlock(ctx2, anchors) {
79253
79512
  const { block, measure, columnWidth, ensurePage, advanceColumn, columnX, floatManager } = ctx2;
79254
79513
  const remeasureParagraph2 = ctx2.remeasureParagraph;
@@ -79378,17 +79637,8 @@ function layoutParagraphBlock(ctx2, anchors) {
79378
79637
  const remeasureWidth = Math.max(1, columnWidth - indentLeft - indentRight);
79379
79638
  let didRemeasureForColumnWidth = false;
79380
79639
  if (typeof remeasureParagraph2 === "function" && typeof measurementWidth === "number" && measurementWidth > remeasureWidth) {
79381
- let firstLineIndent = 0;
79382
- const wordLayout = block.attrs?.wordLayout;
79383
- if (wordLayout?.marker && measure.marker) {
79384
- const markerJustification = wordLayout.marker.justification ?? "left";
79385
- if (markerJustification === "left") {
79386
- const markerWidth = measure.marker.markerWidth ?? 0;
79387
- const gutterWidth = measure.marker.gutterWidth ?? wordLayout.marker.gutterWidthPx ?? 0;
79388
- firstLineIndent = markerWidth + gutterWidth;
79389
- }
79390
- }
79391
- const newMeasure = remeasureParagraph2(block, remeasureWidth, firstLineIndent);
79640
+ const firstLineIndent = calculateFirstLineIndent(block, measure);
79641
+ const newMeasure = remeasureParagraph2(block, columnWidth, firstLineIndent);
79392
79642
  lines = normalizeLines(newMeasure);
79393
79643
  didRemeasureForColumnWidth = true;
79394
79644
  }
@@ -79469,16 +79719,7 @@ function layoutParagraphBlock(ctx2, anchors) {
79469
79719
  }
79470
79720
  const narrowestRemeasureWidth = Math.max(1, narrowestWidth - indentLeft - indentRight);
79471
79721
  if (narrowestRemeasureWidth < remeasureWidth) {
79472
- let firstLineIndent = 0;
79473
- const wordLayout = block.attrs?.wordLayout;
79474
- if (wordLayout?.marker && measure.marker) {
79475
- const markerJustification = wordLayout.marker.justification ?? "left";
79476
- if (markerJustification === "left") {
79477
- const markerWidth = measure.marker.markerWidth ?? 0;
79478
- const gutterWidth = measure.marker.gutterWidth ?? wordLayout.marker.gutterWidthPx ?? 0;
79479
- firstLineIndent = markerWidth + gutterWidth;
79480
- }
79481
- }
79722
+ const firstLineIndent = calculateFirstLineIndent(block, measure);
79482
79723
  const newMeasure = remeasureParagraph2(block, narrowestRemeasureWidth, firstLineIndent);
79483
79724
  lines = normalizeLines(newMeasure);
79484
79725
  didRemeasureForFloats = true;
@@ -82625,7 +82866,8 @@ function remeasureParagraph(block, maxWidth, firstLineIndent = 0) {
82625
82866
  const indentHanging = Math.max(0, indent?.hanging ?? 0);
82626
82867
  const rawFirstLineOffset = Math.max(0, firstLineIndent || indentFirstLine - indentHanging);
82627
82868
  const contentWidth = Math.max(1, maxWidth - indentLeft - indentRight);
82628
- const textStartPx = wordLayout?.textStartPx;
82869
+ const markerTextStartX = wordLayout?.marker?.textStartX;
82870
+ const textStartPx = typeof markerTextStartX === "number" && Number.isFinite(markerTextStartX) ? markerTextStartX : typeof wordLayout?.textStartPx === "number" && Number.isFinite(wordLayout.textStartPx) ? wordLayout.textStartPx : void 0;
82629
82871
  const treatAsHanging = textStartPx && indentLeft === 0 && indentHanging === 0;
82630
82872
  const firstLineWidth = typeof textStartPx === "number" && textStartPx > indentLeft && !treatAsHanging ? Math.max(1, maxWidth - textStartPx - indentRight) : Math.max(1, contentWidth - rawFirstLineOffset);
82631
82873
  const tabStops = buildTabStopsPx$1(indent, attrs?.tabs, attrs?.tabIntervalTwips);
@@ -82637,6 +82879,7 @@ function remeasureParagraph(block, maxWidth, firstLineIndent = 0) {
82637
82879
  const startRun = currentRun;
82638
82880
  const startChar = currentChar;
82639
82881
  let width = 0;
82882
+ let widthAtLastBreak = -1;
82640
82883
  let lastBreakRun = -1;
82641
82884
  let lastBreakChar = -1;
82642
82885
  let endRun = currentRun;
@@ -82654,6 +82897,7 @@ function remeasureParagraph(block, maxWidth, firstLineIndent = 0) {
82654
82897
  endChar = 1;
82655
82898
  lastBreakRun = r2;
82656
82899
  lastBreakChar = 1;
82900
+ widthAtLastBreak = width;
82657
82901
  continue;
82658
82902
  }
82659
82903
  const text = runText(run2);
@@ -82664,6 +82908,7 @@ function remeasureParagraph(block, maxWidth, firstLineIndent = 0) {
82664
82908
  if (lastBreakRun >= 0) {
82665
82909
  endRun = lastBreakRun;
82666
82910
  endChar = lastBreakChar;
82911
+ width = widthAtLastBreak >= 0 ? widthAtLastBreak : width;
82667
82912
  } else {
82668
82913
  endRun = r2;
82669
82914
  endChar = c2;
@@ -82678,6 +82923,7 @@ function remeasureParagraph(block, maxWidth, firstLineIndent = 0) {
82678
82923
  if (ch === " " || ch === " " || ch === "-") {
82679
82924
  lastBreakRun = r2;
82680
82925
  lastBreakChar = c2 + 1;
82926
+ widthAtLastBreak = width;
82681
82927
  }
82682
82928
  }
82683
82929
  if (didBreakInThisLine) break;
@@ -84388,7 +84634,9 @@ function clickToPosition(layout, blocks, measures, containerPoint, domContainer,
84388
84634
  }
84389
84635
  const markerWidth = fragment.markerWidth ?? measure.marker?.markerWidth ?? 0;
84390
84636
  const isListItem3 = markerWidth > 0;
84391
- const alignmentOverride = isListItem3 ? "left" : void 0;
84637
+ const paraAlignment = block.attrs?.alignment;
84638
+ const isJustified = paraAlignment === "justify" || paraAlignment === "both";
84639
+ const alignmentOverride = isListItem3 && !isJustified ? "left" : void 0;
84392
84640
  const pos = mapPointToPm(block, line, pageRelativePoint.x - fragment.x, isRTL, availableWidth, alignmentOverride);
84393
84641
  if (pos == null) {
84394
84642
  logClickStage("warn", "no-position", {
@@ -84455,7 +84703,9 @@ function clickToPosition(layout, blocks, measures, containerPoint, domContainer,
84455
84703
  }
84456
84704
  const cellMarkerWidth = cellMeasure.marker?.markerWidth ?? 0;
84457
84705
  const isListItem3 = cellMarkerWidth > 0;
84458
- const alignmentOverride = isListItem3 ? "left" : void 0;
84706
+ const cellAlignment = cellBlock.attrs?.alignment;
84707
+ const isJustified = cellAlignment === "justify" || cellAlignment === "both";
84708
+ const alignmentOverride = isListItem3 && !isJustified ? "left" : void 0;
84459
84709
  const pos = mapPointToPm(cellBlock, line, localX, isRTL, availableWidth, alignmentOverride);
84460
84710
  if (pos != null) {
84461
84711
  logClickStage("log", "success", {
@@ -84624,7 +84874,9 @@ function selectionToRects(layout, blocks, measures, from2, to, geometryHelper) {
84624
84874
  const charOffsetTo = pmPosToCharOffset(block, line, sliceTo);
84625
84875
  const markerWidth = fragment.markerWidth ?? measure.marker?.markerWidth ?? 0;
84626
84876
  const isListItemFlag = isListItem(markerWidth, block);
84627
- const alignmentOverride = isListItemFlag ? "left" : void 0;
84877
+ const blockAlignment = block.attrs?.alignment;
84878
+ const isJustified = blockAlignment === "justify" || blockAlignment === "both";
84879
+ const alignmentOverride = isListItemFlag && !isJustified ? "left" : void 0;
84628
84880
  const startX = mapPmToX(block, line, charOffsetFrom, fragment.width, alignmentOverride);
84629
84881
  const endX = mapPmToX(block, line, charOffsetTo, fragment.width, alignmentOverride);
84630
84882
  const indent = extractParagraphIndent(block.attrs?.indent);
@@ -85366,7 +85618,9 @@ async function measureParagraphBlock(block, maxWidth) {
85366
85618
  const contentWidth = Math.max(1, maxWidth - indentLeft - indentRight);
85367
85619
  const bodyContentWidth = contentWidth;
85368
85620
  let initialAvailableWidth;
85369
- const textStartPx = wordLayout?.textStartPx;
85621
+ const rawTextStartPx = wordLayout?.textStartPx;
85622
+ const markerTextStartX = wordLayout?.marker?.textStartX;
85623
+ const textStartPx = typeof markerTextStartX === "number" && Number.isFinite(markerTextStartX) ? markerTextStartX : typeof rawTextStartPx === "number" && Number.isFinite(rawTextStartPx) ? rawTextStartPx : void 0;
85370
85624
  if (typeof textStartPx === "number" && textStartPx > indentLeft) {
85371
85625
  initialAvailableWidth = Math.max(1, maxWidth - textStartPx - indentRight);
85372
85626
  } else {
@@ -85501,12 +85755,40 @@ async function measureParagraphBlock(block, maxWidth) {
85501
85755
  runsToProcess.push(run2);
85502
85756
  }
85503
85757
  }
85758
+ const trimTrailingWrapSpaces = (lineToTrim) => {
85759
+ const lastRun = runsToProcess[lineToTrim.toRun];
85760
+ if (!lastRun || !("text" in lastRun) || typeof lastRun.text !== "string") return;
85761
+ const sliceStart = lineToTrim.toRun === lineToTrim.fromRun ? lineToTrim.fromChar : 0;
85762
+ const sliceEnd = lineToTrim.toChar;
85763
+ if (sliceEnd <= sliceStart) return;
85764
+ const sliceText = lastRun.text.slice(sliceStart, sliceEnd);
85765
+ let trimCount = 0;
85766
+ for (let i = sliceText.length - 1; i >= 0 && sliceText[i] === " "; i -= 1) {
85767
+ trimCount += 1;
85768
+ }
85769
+ if (trimCount === 0) return;
85770
+ if (lineToTrim.fromRun === lineToTrim.toRun && sliceText.trim().length === 0) {
85771
+ return;
85772
+ }
85773
+ const keptText = sliceText.slice(0, Math.max(0, sliceText.length - trimCount));
85774
+ const { font } = buildFontString(
85775
+ lastRun
85776
+ );
85777
+ const fullWidth = measureRunWidth(sliceText, font, ctx2, lastRun);
85778
+ const keptWidth = keptText.length > 0 ? measureRunWidth(keptText, font, ctx2, lastRun) : 0;
85779
+ const delta = Math.max(0, fullWidth - keptWidth);
85780
+ lineToTrim.width = roundValue(Math.max(0, lineToTrim.width - delta));
85781
+ lineToTrim.spaceCount = Math.max(0, lineToTrim.spaceCount - trimCount);
85782
+ if (lineToTrim.naturalWidth != null && typeof lineToTrim.naturalWidth === "number") {
85783
+ lineToTrim.naturalWidth = roundValue(Math.max(0, lineToTrim.naturalWidth - delta));
85784
+ }
85785
+ };
85504
85786
  for (let runIndex = 0; runIndex < runsToProcess.length; runIndex++) {
85505
85787
  const run2 = runsToProcess[runIndex];
85506
85788
  if (run2.kind === "break") {
85507
85789
  if (currentLine) {
85508
85790
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
85509
- const { spaceCount: _sc, ...lineBase } = currentLine;
85791
+ const lineBase = currentLine;
85510
85792
  const completedLine = { ...lineBase, ...metrics };
85511
85793
  addBarTabsToLine(completedLine);
85512
85794
  lines.push(completedLine);
@@ -85551,7 +85833,7 @@ async function measureParagraphBlock(block, maxWidth) {
85551
85833
  toRun: runIndex,
85552
85834
  toChar: 0,
85553
85835
  width: 0,
85554
- maxWidth: getEffectiveWidth(initialAvailableWidth),
85836
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
85555
85837
  segments: [],
85556
85838
  ...metrics
85557
85839
  };
@@ -85586,7 +85868,7 @@ async function measureParagraphBlock(block, maxWidth) {
85586
85868
  width: 0,
85587
85869
  maxFontSize: 12,
85588
85870
  // Default font size for tabs
85589
- maxWidth: getEffectiveWidth(initialAvailableWidth),
85871
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
85590
85872
  segments: [],
85591
85873
  spaceCount: 0
85592
85874
  };
@@ -85636,7 +85918,7 @@ async function measureParagraphBlock(block, maxWidth) {
85636
85918
  width: imageWidth,
85637
85919
  maxFontSize: imageHeight,
85638
85920
  // Use image height for line height calculation
85639
- maxWidth: getEffectiveWidth(initialAvailableWidth),
85921
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
85640
85922
  spaceCount: 0,
85641
85923
  segments: [
85642
85924
  {
@@ -85652,8 +85934,9 @@ async function measureParagraphBlock(block, maxWidth) {
85652
85934
  }
85653
85935
  const appliedTabAlign = lastAppliedTabAlign;
85654
85936
  if (currentLine.width + imageWidth > currentLine.maxWidth && currentLine.width > 0) {
85937
+ trimTrailingWrapSpaces(currentLine);
85655
85938
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
85656
- const { spaceCount: _sc, ...lineBase } = currentLine;
85939
+ const lineBase = currentLine;
85657
85940
  const completedLine = {
85658
85941
  ...lineBase,
85659
85942
  ...metrics
@@ -85726,7 +86009,7 @@ async function measureParagraphBlock(block, maxWidth) {
85726
86009
  // Field annotations are atomic units
85727
86010
  width: annotationWidth,
85728
86011
  maxFontSize: annotationHeight,
85729
- maxWidth: getEffectiveWidth(initialAvailableWidth),
86012
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
85730
86013
  spaceCount: 0,
85731
86014
  segments: [
85732
86015
  {
@@ -85741,8 +86024,9 @@ async function measureParagraphBlock(block, maxWidth) {
85741
86024
  continue;
85742
86025
  }
85743
86026
  if (currentLine.width + annotationWidth > currentLine.maxWidth && currentLine.width > 0) {
86027
+ trimTrailingWrapSpaces(currentLine);
85744
86028
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
85745
- const { spaceCount: _sc, ...lineBase } = currentLine;
86029
+ const lineBase = currentLine;
85746
86030
  const completedLine = {
85747
86031
  ...lineBase,
85748
86032
  ...metrics
@@ -85815,15 +86099,16 @@ async function measureParagraphBlock(block, maxWidth) {
85815
86099
  width: spacesWidth,
85816
86100
  maxFontSize: run2.fontSize,
85817
86101
  maxFontInfo: getFontInfoFromRun(run2),
85818
- maxWidth: getEffectiveWidth(initialAvailableWidth),
86102
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
85819
86103
  segments: [{ runIndex, fromChar: spacesStartChar, toChar: spacesEndChar, width: spacesWidth }],
85820
86104
  spaceCount: spacesLength
85821
86105
  };
85822
86106
  } else {
85823
86107
  const boundarySpacing = currentLine.width > 0 ? run2.letterSpacing ?? 0 : 0;
85824
86108
  if (currentLine.width + boundarySpacing + spacesWidth > currentLine.maxWidth - WIDTH_FUDGE_PX2 && currentLine.width > 0) {
86109
+ trimTrailingWrapSpaces(currentLine);
85825
86110
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
85826
- const { spaceCount: _sc, ...lineBase } = currentLine;
86111
+ const lineBase = currentLine;
85827
86112
  const completedLine = {
85828
86113
  ...lineBase,
85829
86114
  ...metrics
@@ -85859,6 +86144,13 @@ async function measureParagraphBlock(block, maxWidth) {
85859
86144
  continue;
85860
86145
  }
85861
86146
  const words = segment.split(" ");
86147
+ let lastNonEmptyWordIndex = -1;
86148
+ for (let i = words.length - 1; i >= 0; i -= 1) {
86149
+ if (words[i] !== "") {
86150
+ lastNonEmptyWordIndex = i;
86151
+ break;
86152
+ }
86153
+ }
85862
86154
  let segmentStartX;
85863
86155
  if (currentLine && pendingTabAlignment) {
85864
86156
  segmentStartX = alignSegmentAtTab(segment, font, run2);
@@ -85881,15 +86173,16 @@ async function measureParagraphBlock(block, maxWidth) {
85881
86173
  width: singleSpaceWidth,
85882
86174
  maxFontSize: run2.fontSize,
85883
86175
  maxFontInfo: getFontInfoFromRun(run2),
85884
- maxWidth: getEffectiveWidth(initialAvailableWidth),
86176
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
85885
86177
  segments: [{ runIndex, fromChar: spaceStartChar, toChar: spaceEndChar, width: singleSpaceWidth }],
85886
86178
  spaceCount: 1
85887
86179
  };
85888
86180
  } else {
85889
86181
  const boundarySpacing2 = currentLine.width > 0 ? run2.letterSpacing ?? 0 : 0;
85890
86182
  if (currentLine.width + boundarySpacing2 + singleSpaceWidth > currentLine.maxWidth - WIDTH_FUDGE_PX2 && currentLine.width > 0) {
86183
+ trimTrailingWrapSpaces(currentLine);
85891
86184
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
85892
- const { spaceCount: _sc, ...lineBase } = currentLine;
86185
+ const lineBase = currentLine;
85893
86186
  const completedLine = { ...lineBase, ...metrics };
85894
86187
  addBarTabsToLine(completedLine);
85895
86188
  lines.push(completedLine);
@@ -85921,19 +86214,19 @@ async function measureParagraphBlock(block, maxWidth) {
85921
86214
  charPosInRun = spaceEndChar;
85922
86215
  continue;
85923
86216
  }
85924
- const isLastWordInSegment = wordIndex === words.length - 1;
85925
- const isLastWord = isLastWordInSegment && isLastSegment;
85926
86217
  const wordOnlyWidth = measureRunWidth(word, font, ctx2, run2);
85927
- const spaceWidth = isLastWord ? 0 : measureRunWidth(" ", font, ctx2, run2);
85928
- const wordCommitWidth = isLastWord ? wordOnlyWidth : wordOnlyWidth + spaceWidth;
86218
+ const shouldIncludeDelimiterSpace = wordIndex < lastNonEmptyWordIndex;
86219
+ const spaceWidth = shouldIncludeDelimiterSpace ? measureRunWidth(" ", font, ctx2, run2) : 0;
86220
+ const wordCommitWidth = wordOnlyWidth + spaceWidth;
85929
86221
  const wordStartChar = charPosInRun;
85930
86222
  const wordEndNoSpace = charPosInRun + word.length;
85931
- const wordEndWithSpace = charPosInRun + (isLastWord ? word.length : word.length + 1);
86223
+ const wordEndWithSpace = wordEndNoSpace + (shouldIncludeDelimiterSpace ? 1 : 0);
85932
86224
  const effectiveMaxWidth = currentLine ? currentLine.maxWidth : getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : contentWidth);
85933
86225
  if (wordOnlyWidth > effectiveMaxWidth && word.length > 1) {
85934
86226
  if (currentLine && currentLine.width > 0 && currentLine.segments && currentLine.segments.length > 0) {
86227
+ trimTrailingWrapSpaces(currentLine);
85935
86228
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
85936
- const { spaceCount: _sc, ...lineBase } = currentLine;
86229
+ const lineBase = currentLine;
85937
86230
  const completedLine = { ...lineBase, ...metrics };
85938
86231
  addBarTabsToLine(completedLine);
85939
86232
  lines.push(completedLine);
@@ -85967,7 +86260,7 @@ async function measureParagraphBlock(block, maxWidth) {
85967
86260
  });
85968
86261
  if (isLastChunk) {
85969
86262
  const ls = run2.letterSpacing ?? 0;
85970
- if (!isLastWord && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
86263
+ if (shouldIncludeDelimiterSpace && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
85971
86264
  currentLine.toChar = wordEndWithSpace;
85972
86265
  currentLine.width = roundValue(currentLine.width + spaceWidth + ls);
85973
86266
  charPosInRun = wordEndWithSpace;
@@ -85976,8 +86269,9 @@ async function measureParagraphBlock(block, maxWidth) {
85976
86269
  charPosInRun = wordEndWithSpace;
85977
86270
  }
85978
86271
  } else {
86272
+ trimTrailingWrapSpaces(currentLine);
85979
86273
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
85980
- const { spaceCount: _sc, ...lineBase } = currentLine;
86274
+ const lineBase = currentLine;
85981
86275
  const completedLine = { ...lineBase, ...metrics };
85982
86276
  addBarTabsToLine(completedLine);
85983
86277
  lines.push(completedLine);
@@ -85999,7 +86293,7 @@ async function measureParagraphBlock(block, maxWidth) {
85999
86293
  spaceCount: 0
86000
86294
  };
86001
86295
  const ls = run2.letterSpacing ?? 0;
86002
- if (!isLastWord && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
86296
+ if (shouldIncludeDelimiterSpace && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
86003
86297
  currentLine.toChar = wordEndWithSpace;
86004
86298
  currentLine.width = roundValue(currentLine.width + spaceWidth + ls);
86005
86299
  charPosInRun = wordEndWithSpace;
@@ -86036,12 +86330,12 @@ async function measureParagraphBlock(block, maxWidth) {
86036
86330
  width: wordOnlyWidth,
86037
86331
  maxFontSize: run2.fontSize,
86038
86332
  maxFontInfo: getFontInfoFromRun(run2),
86039
- maxWidth: getEffectiveWidth(initialAvailableWidth),
86333
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
86040
86334
  segments: [{ runIndex, fromChar: wordStartChar, toChar: wordEndNoSpace, width: wordOnlyWidth }],
86041
86335
  spaceCount: 0
86042
86336
  };
86043
86337
  const ls = run2.letterSpacing ?? 0;
86044
- if (!isLastWord && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
86338
+ if (shouldIncludeDelimiterSpace && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
86045
86339
  currentLine.toChar = wordEndWithSpace;
86046
86340
  currentLine.width = roundValue(currentLine.width + spaceWidth + ls);
86047
86341
  charPosInRun = wordEndWithSpace;
@@ -86055,15 +86349,16 @@ async function measureParagraphBlock(block, maxWidth) {
86055
86349
  const boundarySpacing = currentLine.width > 0 ? run2.letterSpacing ?? 0 : 0;
86056
86350
  const justifyAlignment = block.attrs?.alignment === "justify";
86057
86351
  const totalWidthWithWord = currentLine.width + boundarySpacing + wordCommitWidth + // Safe cast: only TextRuns produce word segments from split(), other run types are handled earlier
86058
- (isLastWord ? 0 : run2.letterSpacing ?? 0);
86352
+ (shouldIncludeDelimiterSpace ? run2.letterSpacing ?? 0 : 0);
86059
86353
  const availableWidth = currentLine.maxWidth - WIDTH_FUDGE_PX2;
86060
86354
  let shouldBreak = currentLine.width + boundarySpacing + wordOnlyWidth > availableWidth && currentLine.width > 0 && !isTocEntry;
86061
86355
  let compressedWidth = null;
86062
86356
  if (shouldBreak && justifyAlignment) {
86063
- const isParagraphLastWord = isLastWord && runIndex === runsToProcess.length - 1;
86357
+ const isLastNonEmptyWordInSegment = wordIndex === lastNonEmptyWordIndex;
86358
+ const isParagraphLastWord = isLastSegment && isLastNonEmptyWordInSegment && runIndex === runsToProcess.length - 1;
86064
86359
  if (!isParagraphLastWord) {
86065
86360
  const existingSpaces = currentLine.spaceCount ?? 0;
86066
- const candidateSpaces = existingSpaces + (isLastWord ? 0 : 1);
86361
+ const candidateSpaces = existingSpaces + (shouldIncludeDelimiterSpace ? 1 : 0);
86067
86362
  if (candidateSpaces > 0) {
86068
86363
  const overflow = totalWidthWithWord - availableWidth;
86069
86364
  if (overflow > 0) {
@@ -86079,8 +86374,9 @@ async function measureParagraphBlock(block, maxWidth) {
86079
86374
  }
86080
86375
  }
86081
86376
  if (shouldBreak) {
86377
+ trimTrailingWrapSpaces(currentLine);
86082
86378
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
86083
- const { spaceCount: _sc, ...lineBase } = currentLine;
86379
+ const lineBase = currentLine;
86084
86380
  const completedLine = {
86085
86381
  ...lineBase,
86086
86382
  ...metrics
@@ -86101,7 +86397,7 @@ async function measureParagraphBlock(block, maxWidth) {
86101
86397
  segments: [{ runIndex, fromChar: wordStartChar, toChar: wordEndNoSpace, width: wordOnlyWidth }],
86102
86398
  spaceCount: 0
86103
86399
  };
86104
- if (!isLastWord && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
86400
+ if (shouldIncludeDelimiterSpace && currentLine.width + spaceWidth <= currentLine.maxWidth - WIDTH_FUDGE_PX2) {
86105
86401
  currentLine.toChar = wordEndWithSpace;
86106
86402
  currentLine.width = roundValue(currentLine.width + spaceWidth + (run2.letterSpacing ?? 0));
86107
86403
  charPosInRun = wordEndWithSpace;
@@ -86111,7 +86407,7 @@ async function measureParagraphBlock(block, maxWidth) {
86111
86407
  }
86112
86408
  } else {
86113
86409
  currentLine.toRun = runIndex;
86114
- if (!isLastWord && currentLine.width + boundarySpacing + wordOnlyWidth + spaceWidth > currentLine.maxWidth - WIDTH_FUDGE_PX2) {
86410
+ if (shouldIncludeDelimiterSpace && currentLine.width + boundarySpacing + wordOnlyWidth + spaceWidth > currentLine.maxWidth - WIDTH_FUDGE_PX2) {
86115
86411
  currentLine.toChar = wordEndNoSpace;
86116
86412
  currentLine.width = roundValue(currentLine.width + boundarySpacing + wordOnlyWidth);
86117
86413
  currentLine.maxFontInfo = updateMaxFontInfo(currentLine.maxFontSize, currentLine.maxFontInfo, run2);
@@ -86125,8 +86421,9 @@ async function measureParagraphBlock(block, maxWidth) {
86125
86421
  wordOnlyWidth,
86126
86422
  useExplicitXHere ? segmentStartX : void 0
86127
86423
  );
86424
+ trimTrailingWrapSpaces(currentLine);
86128
86425
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
86129
- const { spaceCount: _sc, ...lineBase } = currentLine;
86426
+ const lineBase = currentLine;
86130
86427
  const completedLine = { ...lineBase, ...metrics };
86131
86428
  addBarTabsToLine(completedLine);
86132
86429
  lines.push(completedLine);
@@ -86136,20 +86433,23 @@ async function measureParagraphBlock(block, maxWidth) {
86136
86433
  charPosInRun = wordEndNoSpace + 1;
86137
86434
  continue;
86138
86435
  }
86139
- const newToChar = isLastWord ? wordEndNoSpace : wordEndWithSpace;
86436
+ const newToChar = shouldIncludeDelimiterSpace ? wordEndWithSpace : wordEndNoSpace;
86140
86437
  currentLine.toChar = newToChar;
86141
86438
  const useExplicitX = wordIndex === 0 && segmentStartX !== void 0;
86142
86439
  const explicitX = useExplicitX ? segmentStartX : void 0;
86143
- const targetWidth = compressedWidth != null ? compressedWidth : currentLine.width + boundarySpacing + wordCommitWidth + (isLastWord ? 0 : run2.letterSpacing ?? 0);
86440
+ const targetWidth = compressedWidth != null ? compressedWidth : currentLine.width + boundarySpacing + wordCommitWidth + (shouldIncludeDelimiterSpace ? run2.letterSpacing ?? 0 : 0);
86441
+ if (compressedWidth != null) {
86442
+ currentLine.naturalWidth = roundValue(totalWidthWithWord);
86443
+ }
86144
86444
  currentLine.width = roundValue(targetWidth);
86145
86445
  currentLine.maxFontInfo = updateMaxFontInfo(currentLine.maxFontSize, currentLine.maxFontInfo, run2);
86146
86446
  currentLine.maxFontSize = Math.max(currentLine.maxFontSize, run2.fontSize);
86147
86447
  appendSegment(currentLine.segments, runIndex, wordStartChar, newToChar, wordCommitWidth, explicitX);
86148
- if (!isLastWord) {
86448
+ if (shouldIncludeDelimiterSpace) {
86149
86449
  currentLine.spaceCount += 1;
86150
86450
  }
86151
86451
  }
86152
- charPosInRun = isLastWord ? wordEndNoSpace : wordEndWithSpace;
86452
+ charPosInRun = shouldIncludeDelimiterSpace ? wordEndWithSpace : wordEndNoSpace;
86153
86453
  }
86154
86454
  if (lastAppliedTabAlign && currentLine) {
86155
86455
  const appliedTab = lastAppliedTabAlign;
@@ -86169,7 +86469,7 @@ async function measureParagraphBlock(block, maxWidth) {
86169
86469
  width: 0,
86170
86470
  maxFontSize: run2.fontSize,
86171
86471
  maxFontInfo: getFontInfoFromRun(run2),
86172
- maxWidth: getEffectiveWidth(initialAvailableWidth),
86472
+ maxWidth: getEffectiveWidth(lines.length === 0 ? initialAvailableWidth : bodyContentWidth),
86173
86473
  segments: [],
86174
86474
  spaceCount: 0
86175
86475
  };
@@ -86217,7 +86517,7 @@ async function measureParagraphBlock(block, maxWidth) {
86217
86517
  }
86218
86518
  if (currentLine) {
86219
86519
  const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
86220
- const { spaceCount: _sc, ...lineBase } = currentLine;
86520
+ const lineBase = currentLine;
86221
86521
  const finalLine = {
86222
86522
  ...lineBase,
86223
86523
  ...metrics