@stll/folio-core 0.37.2 → 0.37.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 (52) hide show
  1. package/dist/ai-edits/table-cell-mutations.js +10 -5
  2. package/dist/ai-edits/table-template.js +19 -5
  3. package/dist/controller/hiddenEditorManager.js +11 -0
  4. package/dist/controller/layoutPipeline.js +1 -1
  5. package/dist/display-list/build/textBoxPrimitives.js +34 -1
  6. package/dist/display-list/dom/renderDisplayListToDom.js +5 -2
  7. package/dist/display-list/types.d.ts +8 -4
  8. package/dist/docx/paragraphPropertySource.d.ts +82 -2
  9. package/dist/docx/paragraphPropertySource.js +569 -3
  10. package/dist/docx/parser.js +9 -0
  11. package/dist/docx/server/materializeYjsDocx.d.ts +1 -1
  12. package/dist/docx/server/materializeYjsDocx.js +21 -2
  13. package/dist/headless-layout.js +1 -1
  14. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +7 -1
  15. package/dist/layout-bridge/convert/headerFooterLayout.js +20 -3
  16. package/dist/layout-bridge/convert/toFlowBlocks.js +43 -12
  17. package/dist/layout-engine/index.js +20 -10
  18. package/dist/layout-engine/measure/measureBlocks.d.ts +7 -3
  19. package/dist/layout-engine/measure/measureBlocks.js +10 -7
  20. package/dist/layout-engine/measure/measureParagraph.d.ts +2 -0
  21. package/dist/layout-engine/measure/measureParagraph.js +1 -1
  22. package/dist/layout-engine/paginator.d.ts +1 -1
  23. package/dist/layout-engine/paginator.js +41 -4
  24. package/dist/layout-engine/textBoxFlow.d.ts +2 -0
  25. package/dist/layout-engine/textBoxFlow.js +9 -5
  26. package/dist/layout-engine/types.d.ts +6 -0
  27. package/dist/layout-painter/documentColors.d.ts +10 -1
  28. package/dist/layout-painter/documentColors.js +16 -1
  29. package/dist/layout-painter/renderParagraph.js +9 -24
  30. package/dist/layout-painter/renderTable.js +3 -3
  31. package/dist/layout-painter/renderTextBox.js +2 -1
  32. package/dist/pdf/pageSpace.d.ts +12 -1
  33. package/dist/pdf/pageSpace.js +24 -1
  34. package/dist/pdf/paint.js +2 -2
  35. package/dist/prosemirror/attrs/index.js +32 -8
  36. package/dist/prosemirror/commands/comments.js +12 -3
  37. package/dist/prosemirror/commands/tableCellMergeResolution.js +18 -11
  38. package/dist/prosemirror/conversion/fromProseDoc.js +114 -25
  39. package/dist/prosemirror/conversion/toProseDoc.js +101 -8
  40. package/dist/prosemirror/extensions/core/DocExtension.js +2 -0
  41. package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -0
  42. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +5 -1
  43. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +132 -41
  44. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -61
  45. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +16 -0
  46. package/dist/prosemirror/schema/nodes.d.ts +14 -2
  47. package/dist/prosemirror/schema/nodes.js +7 -0
  48. package/dist/prosemirror/yjsParagraphSourceContract.d.ts +9 -0
  49. package/dist/prosemirror/yjsParagraphSourceContract.js +26 -0
  50. package/dist/utils/rotationBoundingBox.d.ts +5 -1
  51. package/dist/utils/rotationBoundingBox.js +9 -1
  52. package/package.json +2 -2
@@ -604,6 +604,8 @@ type TableRow = {
604
604
  isHeader?: boolean;
605
605
  /** `w:cantSplit`: keep this row in one flow region. */
606
606
  cantSplit?: boolean;
607
+ /** A leading authored page break in a cell advances the whole row. */
608
+ breakBefore?: "page";
607
609
  hidden?: boolean;
608
610
  };
609
611
  /**
@@ -802,6 +804,8 @@ type TextBoxBlock = {
802
804
  outlineColor?: string;
803
805
  /** Outline dash style, or `"none"` for an explicit no-outline. */
804
806
  outlineStyle?: OutlineStyleAttr;
807
+ /** DrawingML rotation and/or flips, serialized as CSS transform functions. */
808
+ transform?: string;
805
809
  /** Internal padding */
806
810
  margins?: {
807
811
  top: number;
@@ -1032,6 +1036,8 @@ type FragmentBase = {
1032
1036
  */
1033
1037
  type ParagraphFragment = FragmentBase & {
1034
1038
  kind: "paragraph";
1039
+ /** Structural PM carrier that must not make an otherwise blank page visible. */
1040
+ paginationRole?: "empty-carrier";
1035
1041
  /** First line index (inclusive) from the measure. */
1036
1042
  fromLine: number;
1037
1043
  /** Last line index (exclusive) from the measure. */
@@ -1,4 +1,13 @@
1
1
  //#region src/layout-painter/documentColors.d.ts
2
+ declare const AUTHORED_BACKGROUND_COLOR_VAR = "--doc-authored-background-color";
3
+ declare const AUTHORED_TEXT_COLOR_VAR = "--doc-run-color";
4
+ /** Paint a document text color and expose it to the dark-mode color transform. */
5
+ declare const setAuthoredTextColor: (style: CSSStyleDeclaration, color: string) => void;
6
+ /**
7
+ * Paint an OOXML background and retain its authored color for dark-mode adaptation.
8
+ * The custom property is presentation-only; serialization still uses the model value.
9
+ */
10
+ declare const setAuthoredBackgroundColor: (style: CSSStyleDeclaration, color: string) => void;
2
11
  declare function getAutomaticTextColorForBackground(backgroundColor: string | undefined): string | undefined;
3
12
  //#endregion
4
- export { getAutomaticTextColorForBackground };
13
+ export { AUTHORED_BACKGROUND_COLOR_VAR, AUTHORED_TEXT_COLOR_VAR, getAutomaticTextColorForBackground, setAuthoredBackgroundColor, setAuthoredTextColor };
@@ -5,6 +5,21 @@ const WHITE_TEXT_COLOR = "#FFFFFF";
5
5
  const BLACK_LUMINANCE = 0;
6
6
  const WHITE_LUMINANCE = 1;
7
7
  const CONTRAST_OFFSET = .05;
8
+ const AUTHORED_BACKGROUND_COLOR_VAR = "--doc-authored-background-color";
9
+ const AUTHORED_TEXT_COLOR_VAR = "--doc-run-color";
10
+ /** Paint a document text color and expose it to the dark-mode color transform. */
11
+ const setAuthoredTextColor = (style, color) => {
12
+ style.color = color;
13
+ style.setProperty(AUTHORED_TEXT_COLOR_VAR, color);
14
+ };
15
+ /**
16
+ * Paint an OOXML background and retain its authored color for dark-mode adaptation.
17
+ * The custom property is presentation-only; serialization still uses the model value.
18
+ */
19
+ const setAuthoredBackgroundColor = (style, color) => {
20
+ style.backgroundColor = color;
21
+ style.setProperty(AUTHORED_BACKGROUND_COLOR_VAR, color);
22
+ };
8
23
  function normalizeHexColor(color) {
9
24
  const trimmed = color.trim();
10
25
  if (!HEX_COLOR_RE.test(trimmed)) return null;
@@ -37,4 +52,4 @@ function getAutomaticTextColorForBackground(backgroundColor) {
37
52
  return contrastRatio(luminance, BLACK_LUMINANCE) >= contrastRatio(luminance, WHITE_LUMINANCE) ? BLACK_TEXT_COLOR : WHITE_TEXT_COLOR;
38
53
  }
39
54
  //#endregion
40
- export { getAutomaticTextColorForBackground };
55
+ export { AUTHORED_BACKGROUND_COLOR_VAR, AUTHORED_TEXT_COLOR_VAR, getAutomaticTextColorForBackground, setAuthoredBackgroundColor, setAuthoredTextColor };
@@ -19,7 +19,7 @@ import { SCRIPT_CLASS, hasCjk, hasComplexScript, segmentByScript } from "../util
19
19
  import { sanitizeExternalUrl } from "../utils/urlSecurity.js";
20
20
  import { borderStrokeToCss, resolveParagraphBorderHorizontalOutsets } from "./borderStroke.js";
21
21
  import { planCursiveJoiners, withCursiveJoiners } from "./cursiveJoiners.js";
22
- import { getAutomaticTextColorForBackground } from "./documentColors.js";
22
+ import { getAutomaticTextColorForBackground, setAuthoredBackgroundColor, setAuthoredTextColor } from "./documentColors.js";
23
23
  import { applyImageBorder, applyImageVisualAttrs, hasImageCrop, hasImageVisualAttrs, wrapImageWithCrop } from "./renderImage.js";
24
24
  import { resolveImageLineAlign } from "./renderUtils.js";
25
25
  import { applySdtDataAttrs } from "./sdtBoundary.js";
@@ -124,12 +124,6 @@ const DEFAULT_BLACK_TEXT_COLOR_VALUES = /* @__PURE__ */ new Set(["000000", "000"
124
124
  const SUGGESTION_COLOR_CSS = "var(--suggestion-color, #6d3bd6)";
125
125
  const SUGGESTION_TINT_CSS = "var(--suggestion-bg, color-mix(in oklch, #6d3bd6 12%, transparent))";
126
126
  const SUGGESTION_TINT_LAYER_CSS = `linear-gradient(${SUGGESTION_TINT_CSS}, ${SUGGESTION_TINT_CSS})`;
127
- const RUN_BACKGROUND_TEXT_COLOR_VAR = "--doc-run-background-text-color";
128
- const setRunBackgroundTextColor = (element, color) => {
129
- element.classList.add("docx-run-background-text");
130
- element.style.setProperty(RUN_BACKGROUND_TEXT_COLOR_VAR, color);
131
- };
132
- const hasRunBackgroundTextSurface = (run) => Boolean(run.highlight ?? run.shading) && !run.isInsertion && !run.isDeletion && !(run.commentIds !== void 0 && run.commentIds.length > 0);
133
127
  function normalizeTextColorValue(color) {
134
128
  return color.trim().toLowerCase().replace(/^#/u, "");
135
129
  }
@@ -180,8 +174,7 @@ function applyRunStyles(element, run) {
180
174
  let hasExplicitTextColor = false;
181
175
  const textColor = getRenderableTextColor(run);
182
176
  if (textColor) {
183
- element.style.color = textColor;
184
- element.style.setProperty("--doc-run-color", textColor);
177
+ setAuthoredTextColor(element.style, textColor);
185
178
  hasExplicitTextColor = true;
186
179
  }
187
180
  if (run.letterSpacing) element.style.letterSpacing = `${run.letterSpacing}px`;
@@ -219,13 +212,11 @@ function applyRunStyles(element, run) {
219
212
  }
220
213
  const runBackground = run.highlight ?? run.shading;
221
214
  if (runBackground) {
222
- element.style.backgroundColor = runBackground;
215
+ setAuthoredBackgroundColor(element.style, runBackground);
223
216
  const hasTrackedChangeColor = run.isInsertion || run.isDeletion;
224
217
  const hasCommentHighlight = run.commentIds !== void 0 && run.commentIds.length > 0;
225
218
  const automaticTextColor = hasExplicitTextColor || hasTrackedChangeColor || hasCommentHighlight ? void 0 : getAutomaticTextColorForBackground(runBackground);
226
- if (automaticTextColor) element.style.color = automaticTextColor;
227
- const backgroundTextColor = hasExplicitTextColor ? textColor : automaticTextColor;
228
- if (backgroundTextColor && hasRunBackgroundTextSurface(run)) setRunBackgroundTextColor(element, backgroundTextColor);
219
+ if (automaticTextColor) setAuthoredTextColor(element.style, automaticTextColor);
229
220
  }
230
221
  const decorations = [];
231
222
  let explicitDecorationStyle = false;
@@ -378,15 +369,9 @@ function renderTextRun(run, doc, options) {
378
369
  });
379
370
  if (!run.hyperlink.noDefaultStyle) {
380
371
  const hyperlinkColor = getHyperlinkTextColor(run, span.style.color);
381
- anchor.style.color = hyperlinkColor;
372
+ setAuthoredTextColor(anchor.style, hyperlinkColor);
382
373
  anchor.style.textDecoration = "underline";
383
- span.style.color = hyperlinkColor;
384
- anchor.style.setProperty("--doc-run-color", hyperlinkColor);
385
- span.style.setProperty("--doc-run-color", hyperlinkColor);
386
- if (hasRunBackgroundTextSurface(run)) {
387
- setRunBackgroundTextColor(span, hyperlinkColor);
388
- setRunBackgroundTextColor(anchor, hyperlinkColor);
389
- }
374
+ setAuthoredTextColor(span.style, hyperlinkColor);
390
375
  }
391
376
  span.append(anchor);
392
377
  } else appendPaintedText({
@@ -1295,7 +1280,7 @@ function renderLine(block, line, alignment, doc, options) {
1295
1280
  }
1296
1281
  const authoredEndpoint = currentX + tabResult.width + followingWidthForCheck;
1297
1282
  const activeContentRightEdge = options?.contentWidthPx === void 0 ? void 0 : options.contentWidthPx - (options.floatingMargins?.rightMargin ?? 0);
1298
- const preservesAuthoredEndStop = activeContentRightEdge !== void 0 && tabResult.alignment === "end" && authoredEndpoint <= activeContentRightEdge + RIGHT_EDGE_EPSILON_PX;
1283
+ const preservesAuthoredEndStop = activeContentRightEdge !== void 0 && tabResult.alignment === "end" && (options?.context?.section === "header" || options?.context?.section === "footer" || authoredEndpoint <= activeContentRightEdge + RIGHT_EDGE_EPSILON_PX);
1299
1284
  const preservesAuthoredEndStopPastIndent = lineRightEdgeX !== void 0 && preservesAuthoredEndStop && authoredEndpoint > lineRightEdgeX + RIGHT_EDGE_EPSILON_PX;
1300
1285
  if (lineRightEdgeX !== void 0 && options?.isRtl !== true && tabResult.alignment === "end" && !hasFollowingTab && !preservesAuthoredEndStopPastIndent && authoredEndpoint >= lineRightEdgeX - RIGHT_EDGE_EPSILON_PX) {
1301
1286
  lineEl.style.display = "flex";
@@ -1534,9 +1519,9 @@ function renderParagraphFragment(fragment, block, measure, context, options = {}
1534
1519
  }
1535
1520
  }
1536
1521
  if (block.attrs?.shading) {
1537
- fragmentEl.style.backgroundColor = block.attrs.shading;
1522
+ setAuthoredBackgroundColor(fragmentEl.style, block.attrs.shading);
1538
1523
  const automaticTextColor = getAutomaticTextColorForBackground(block.attrs.shading);
1539
- if (automaticTextColor) fragmentEl.style.color = automaticTextColor;
1524
+ if (automaticTextColor) setAuthoredTextColor(fragmentEl.style, automaticTextColor);
1540
1525
  }
1541
1526
  const availableWidth = fragment.width - indentLeft - indentRight;
1542
1527
  const paragraphEndsWithLineBreak = block.runs.at(-1)?.kind === "lineBreak";
@@ -9,7 +9,7 @@ import { applySanitizedImageSrc } from "../utils/sanitizeImageSrc.js";
9
9
  import { emuToPixels } from "../utils/units.js";
10
10
  import { resolveAnchoredImagePosition } from "./anchoredImagePosition.js";
11
11
  import { borderStrokeToCss, resolveCssBorderStroke } from "./borderStroke.js";
12
- import { getAutomaticTextColorForBackground } from "./documentColors.js";
12
+ import { getAutomaticTextColorForBackground, setAuthoredBackgroundColor, setAuthoredTextColor } from "./documentColors.js";
13
13
  import { applyImageVisualAttrs, hasImageCrop, hasImageVisualAttrs } from "./renderImage.js";
14
14
  import { renderParagraphFragment } from "./renderParagraph.js";
15
15
  import { renderTextBoxFragment } from "./renderTextBox.js";
@@ -394,9 +394,9 @@ function renderTableCell({ cell, cellMeasure, x, width, rowHeight, borderFlags,
394
394
  if (borderFlags.drawLeft) applyBorder(cellEl, "left", cell.borders.left);
395
395
  }
396
396
  if (cell.background) {
397
- cellEl.style.backgroundColor = cell.background;
397
+ setAuthoredBackgroundColor(cellEl.style, cell.background);
398
398
  const automaticTextColor = getAutomaticTextColorForBackground(cell.background);
399
- if (automaticTextColor) cellEl.style.color = automaticTextColor;
399
+ if (automaticTextColor) setAuthoredTextColor(cellEl.style, automaticTextColor);
400
400
  }
401
401
  if (cell.noWrap && !columnsPinned) cellEl.style.whiteSpace = "nowrap";
402
402
  if (cell.verticalAlign) {
@@ -1,5 +1,6 @@
1
1
  import { layoutTextBoxContent } from "../layout-engine/measure/textBoxParagraphLayout.js";
2
2
  import { DEFAULT_TEXTBOX_MARGINS } from "../layout-engine/types.js";
3
+ import { setAuthoredBackgroundColor } from "./documentColors.js";
3
4
  import { renderParagraphFragment } from "./renderParagraph.js";
4
5
  import { panic } from "better-result";
5
6
  //#region src/layout-painter/renderTextBox.ts
@@ -28,7 +29,7 @@ function renderTextBoxFragment(fragment, block, measure, context, options = {})
28
29
  containerEl.style.height = `${fragment.height}px`;
29
30
  containerEl.style.overflow = block.textWrap === "none" ? "visible" : "hidden";
30
31
  containerEl.style.boxSizing = "border-box";
31
- if (block.fillColor) containerEl.style.backgroundColor = block.fillColor;
32
+ if (block.fillColor) setAuthoredBackgroundColor(containerEl.style, block.fillColor);
32
33
  if (block.outlineWidth && block.outlineWidth > 0) {
33
34
  const style = block.outlineStyle || "solid";
34
35
  const color = block.outlineColor || "#000000";
@@ -39,5 +39,16 @@ declare const displayPointToPdf: (pageHeightPx: number, xPx: number, yPx: number
39
39
  * screen, which is what a positive CSS/OOXML angle means.
40
40
  */
41
41
  declare const rotationMatrix: (degrees: number, originXPx: number, originYPx: number) => PdfMatrix;
42
+ /**
43
+ * A DrawingML flip and rotation about one display-list point. CSS applies the
44
+ * rightmost scale first, then the rotation; this matrix does the same.
45
+ */
46
+ declare const transformMatrix: ({ degrees, originXPx, originYPx, scaleX, scaleY }: {
47
+ readonly degrees: number;
48
+ readonly originXPx: number;
49
+ readonly originYPx: number;
50
+ readonly scaleX?: -1;
51
+ readonly scaleY?: -1;
52
+ }) => PdfMatrix;
42
53
  //#endregion
43
- export { POINTS_PER_PIXEL, PdfMatrix, applyMatrix, basePageMatrix, displayPointToPdf, pxToPt, rotationMatrix };
54
+ export { POINTS_PER_PIXEL, PdfMatrix, applyMatrix, basePageMatrix, displayPointToPdf, pxToPt, rotationMatrix, transformMatrix };
@@ -53,5 +53,28 @@ const rotationMatrix = (degrees, originXPx, originYPx) => {
53
53
  originYPx - originXPx * sin - originYPx * cos
54
54
  ];
55
55
  };
56
+ /**
57
+ * A DrawingML flip and rotation about one display-list point. CSS applies the
58
+ * rightmost scale first, then the rotation; this matrix does the same.
59
+ */
60
+ const transformMatrix = ({ degrees, originXPx, originYPx, scaleX, scaleY }) => {
61
+ const radians = degrees * Math.PI / 180;
62
+ const cos = Math.cos(radians);
63
+ const sin = Math.sin(radians);
64
+ const horizontalScale = scaleX ?? 1;
65
+ const verticalScale = scaleY ?? 1;
66
+ const a = cos * horizontalScale;
67
+ const b = sin * horizontalScale;
68
+ const c = -sin * verticalScale;
69
+ const d = cos * verticalScale;
70
+ return [
71
+ a,
72
+ b,
73
+ c,
74
+ d,
75
+ originXPx - originXPx * a - originYPx * c,
76
+ originYPx - originXPx * b - originYPx * d
77
+ ];
78
+ };
56
79
  //#endregion
57
- export { POINTS_PER_PIXEL, applyMatrix, basePageMatrix, displayPointToPdf, pxToPt, rotationMatrix };
80
+ export { POINTS_PER_PIXEL, applyMatrix, basePageMatrix, displayPointToPdf, pxToPt, rotationMatrix, transformMatrix };
package/dist/pdf/paint.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { STROKE_DASH_FACTORS, glyphCellOffsetsPx } from "../display-list/primitives.js";
2
2
  import { needsShaping } from "../shaping/placeRun.js";
3
3
  import { TEXT_RENDER_MODE, createContentStream } from "./contentStream.js";
4
- import { basePageMatrix, rotationMatrix } from "./pageSpace.js";
4
+ import { basePageMatrix, transformMatrix } from "./pageSpace.js";
5
5
  import { Result, TaggedError, panic } from "better-result";
6
6
  //#region src/pdf/paint.ts
7
7
  /**
@@ -358,7 +358,7 @@ const paintPrimitive = (context, primitive) => {
358
358
  return;
359
359
  case "rotateGroup":
360
360
  context.stream.save();
361
- context.stream.concat(rotationMatrix(primitive.degrees, primitive.originXPx, primitive.originYPx));
361
+ context.stream.concat(transformMatrix(primitive));
362
362
  for (const child of primitive.children) paintPrimitive(context, child);
363
363
  context.stream.restore();
364
364
  return;
@@ -2,6 +2,7 @@ import { EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FONT_HINT_VALUES, FONT_THEME_V
2
2
  import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
3
3
  import { isParagraphDirection } from "../paragraphDirection.js";
4
4
  import { COMPLEX_SCRIPT_RUN_PROPERTY_KEYS, RUN_FORMATTING_BOOLEAN_PROPERTIES, RUN_FORMATTING_VALUE_PROPERTIES, TRACKED_CHANGE_PROVENANCE_VALUES } from "../schema/marks.js";
5
+ import { TEXT_BOX_TEXT_BODY_CONTENT_STATE_TYPES } from "../schema/nodes.js";
5
6
  import { panic } from "better-result";
6
7
  import { DRAWING_RAW_XML_MODES, isOoxmlSymbolCharacter } from "@stll/docx-core/model";
7
8
  //#region src/prosemirror/attrs/index.ts
@@ -27,6 +28,8 @@ const TEXT_BOX_AUTO_FIT_VALUES = [
27
28
  "shape"
28
29
  ];
29
30
  const TEXT_BOX_TEXT_WRAP_VALUES = ["square", "none"];
31
+ /** The complete DrawingML transform subset carried by text boxes. */
32
+ const TEXT_BOX_TRANSFORM_PATTERN = /^(?=rotate|scaleX|scaleY)(?:rotate\(-?\d+(?:\.\d+)?deg\)(?: )?)?(?:scaleX\(-1\)(?: )?)?(?:scaleY\(-1\))?$/u;
30
33
  const FIELD_KINDS = ["simple", "complex"];
31
34
  const MATH_DISPLAYS = ["inline", "block"];
32
35
  const SHAPE_FILL_TYPES = [
@@ -168,6 +171,7 @@ const readParagraphAttrs = (node) => {
168
171
  const issues = [];
169
172
  expectNodeType(node, "paragraph", issues);
170
173
  optionalString(attrs, "paraId", "paragraph.attrs.paraId", issues);
174
+ optionalString(attrs, "_docxParagraphSourceToken", "paragraph.attrs._docxParagraphSourceToken", issues);
171
175
  optionalString(attrs, "textId", "paragraph.attrs.textId", issues);
172
176
  optionalOneOf(attrs, "alignment", "paragraph.attrs.alignment", issues, PARAGRAPH_ALIGNMENT_VALUES);
173
177
  optionalOneOf(attrs, "alignmentFromStyle", "paragraph.attrs.alignmentFromStyle", issues, PARAGRAPH_ALIGNMENT_VALUES);
@@ -367,7 +371,6 @@ const readTableCellAttrs = (node) => {
367
371
  optionalRecord(attrs, "_originalFormatting", "tableCell.attrs._originalFormatting", issues);
368
372
  optionalTableCellRevision(attrs, issues);
369
373
  optionalBoolean(attrs, "_preserveVMergeRestart", "tableCell.attrs._preserveVMergeRestart", issues);
370
- optionalArray(attrs, "_docxVMergeContinuationCells", "tableCell.attrs._docxVMergeContinuationCells", issues);
371
374
  return attrsResult(attrs, issues);
372
375
  };
373
376
  const expectTableCellAttrs = (node) => expectCachedNodeAttrs(node, tableCellAttrsCache, readTableCellAttrs, "table cell attrs");
@@ -520,6 +523,7 @@ const readTextBoxAttrs = (node) => {
520
523
  optionalNumber(attrs, "outlineWidth", "textBox.attrs.outlineWidth", issues);
521
524
  optionalString(attrs, "outlineColor", "textBox.attrs.outlineColor", issues);
522
525
  optionalOneOf(attrs, "outlineStyle", "textBox.attrs.outlineStyle", issues, OUTLINE_STYLE_ATTR_VALUES);
526
+ optionalTextBoxTransform(attrs, "transform", "textBox.attrs.transform", issues);
523
527
  optionalNumber(attrs, "marginTop", "textBox.attrs.marginTop", issues);
524
528
  optionalNumber(attrs, "marginBottom", "textBox.attrs.marginBottom", issues);
525
529
  optionalNumber(attrs, "marginLeft", "textBox.attrs.marginLeft", issues);
@@ -537,6 +541,7 @@ const readTextBoxAttrs = (node) => {
537
541
  optionalOneOf(attrs, "_docxPlacement", "textBox.attrs._docxPlacement", issues, TEXT_BOX_DOCX_PLACEMENTS);
538
542
  optionalString(attrs, "_docxGroupId", "textBox.attrs._docxGroupId", issues);
539
543
  optionalString(attrs, "_docxAnchorId", "textBox.attrs._docxAnchorId", issues);
544
+ requiredTextBoxBodyContentState(attrs, issues);
540
545
  optionalTextBoxTrackedChange(attrs, issues);
541
546
  optionalTextBoxInlineSdts(attrs, issues);
542
547
  return attrsResult(attrs, issues);
@@ -905,6 +910,21 @@ const optionalString = (attrs, key, path, issues) => {
905
910
  message: "Expected a string."
906
911
  });
907
912
  };
913
+ const optionalTextBoxTransform = (attrs, key, path, issues) => {
914
+ const value = attrs[key];
915
+ if (value === void 0 || value === null) return;
916
+ if (typeof value !== "string") {
917
+ issues.push({
918
+ path,
919
+ message: "Expected a string."
920
+ });
921
+ return;
922
+ }
923
+ if (!TEXT_BOX_TRANSFORM_PATTERN.test(value)) issues.push({
924
+ path,
925
+ message: "Expected DrawingML rotation and/or horizontal or vertical flips."
926
+ });
927
+ };
908
928
  const optionalSdtListItems = (attrs, key, path, issues) => {
909
929
  const value = attrs[key];
910
930
  if (value === void 0 || value === null) return;
@@ -1185,6 +1205,17 @@ const optionalTextBoxTrackedChange = (attrs, issues) => {
1185
1205
  requiredString(info, "author", "textBox.attrs._docxTrackedChange.info.author", issues);
1186
1206
  optionalString(info, "date", "textBox.attrs._docxTrackedChange.info.date", issues);
1187
1207
  };
1208
+ const requiredTextBoxBodyContentState = (attrs, issues) => {
1209
+ const value = attrs["_docxTextBodyContentState"];
1210
+ if (!isRecord(value)) {
1211
+ issues.push({
1212
+ path: "textBox.attrs._docxTextBodyContentState",
1213
+ message: "Expected an object."
1214
+ });
1215
+ return;
1216
+ }
1217
+ requiredOneOf(value, "type", "textBox.attrs._docxTextBodyContentState.type", issues, TEXT_BOX_TEXT_BODY_CONTENT_STATE_TYPES);
1218
+ };
1188
1219
  const optionalTextBoxInlineSdts = (attrs, issues) => {
1189
1220
  const value = attrs["_docxInlineSdts"];
1190
1221
  if (value === void 0 || value === null) return;
@@ -1236,13 +1267,6 @@ const optionalAutospacingBase = (attrs, path, issues) => {
1236
1267
  optionalNumber(value, "before", `${path}.before`, issues);
1237
1268
  optionalNumber(value, "after", `${path}.after`, issues);
1238
1269
  };
1239
- const optionalArray = (attrs, key, path, issues) => {
1240
- const value = attrs[key];
1241
- if (value !== void 0 && value !== null && !Array.isArray(value)) issues.push({
1242
- path,
1243
- message: "Expected an array."
1244
- });
1245
- };
1246
1270
  const optionalBorderMap = (attrs, key, path, issues, sides) => {
1247
1271
  const value = attrs[key];
1248
1272
  if (value === void 0 || value === null) return;
@@ -1,3 +1,4 @@
1
+ import { joinProseParagraphsWithRightPropertySource } from "../../docx/paragraphPropertySource.js";
1
2
  import { appendHeadlessInlineResolution } from "../../internal/headlessRevisionResolution.js";
2
3
  import { stateAllowsHeadlessRevisionResolution } from "../../internal/headlessRevisionResolutionGuard.js";
3
4
  import { expectParagraphAttrs, expectRunPropertyChangeMarkAttrs } from "../attrs/index.js";
@@ -232,15 +233,23 @@ function resolveChange(from, to, mode, revisionIds, execution = "legacy") {
232
233
  tr.setNodeAttribute(mappedPos, "pPrMark", null);
233
234
  continue;
234
235
  }
236
+ const emptyFirstParagraph = holdsNoContent(paragraph);
235
237
  const joinedAttrs = {
236
- ...(holdsNoContent(paragraph) ? nextNode : paragraph).attrs,
238
+ ...(emptyFirstParagraph ? nextNode : paragraph).attrs,
237
239
  pPrMark: nextNode.attrs["pPrMark"],
238
240
  sectionBreakType: nextNode.attrs["sectionBreakType"],
239
241
  _sectionProperties: nextNode.attrs["_sectionProperties"]
240
242
  };
241
243
  try {
242
- tr.join(joinPos);
243
- tr.setNodeMarkup(mappedPos, void 0, joinedAttrs);
244
+ if (emptyFirstParagraph) joinProseParagraphsWithRightPropertySource({
245
+ attrs: joinedAttrs,
246
+ pos: joinPos,
247
+ transaction: tr
248
+ });
249
+ else {
250
+ tr.join(joinPos);
251
+ tr.setNodeMarkup(mappedPos, void 0, joinedAttrs);
252
+ }
244
253
  if (ownsSectionEndpoint(paragraph)) {
245
254
  removedSectionEndpointCount++;
246
255
  removedSectionReferences.push(...sectionReferencesOf(paragraph));
@@ -1,13 +1,17 @@
1
+ import { decodeTableCellParagraphSourcePayload, restoreTableCellsWithParagraphPropertySources, transportTableCellsWithParagraphPropertySources } from "../../docx/paragraphPropertySource.js";
1
2
  import { standaloneTableCellFromProseMirror } from "../conversion/fromProseDoc.js";
2
3
  import { standaloneTableCellToProseMirror } from "../conversion/toProseDoc.js";
3
4
  import { getTableCellMergeChange } from "../tableCellMergeRevision.js";
4
5
  import { Result } from "better-result";
5
6
  import { TableMap } from "prosemirror-tables";
6
7
  //#region src/prosemirror/commands/tableCellMergeResolution.ts
8
+ const tableCellContinuationPayload = (node) => {
9
+ const value = node.attrs["_docxVMergeContinuationCells"];
10
+ return value === void 0 || value === null ? null : decodeTableCellParagraphSourcePayload(value);
11
+ };
7
12
  const hasMatchingCollapsedTableCellMerge = (node, revisionSet) => {
8
- const continuationCells = node.attrs["_docxVMergeContinuationCells"];
9
- if (!Array.isArray(continuationCells)) return false;
10
- return continuationCells.some((cell) => {
13
+ const payload = tableCellContinuationPayload(node);
14
+ return payload !== null && payload.cells.some((cell) => {
11
15
  const change = getTableCellMergeChange(cell);
12
16
  return change !== null && (revisionSet === null || revisionSet.has(change.info.id));
13
17
  });
@@ -71,19 +75,19 @@ const mergeTableCellWithCellAbove = (tr, cellPos) => {
71
75
  if (!aboveCell || typeof aboveRowspan !== "number" || typeof cellRowspan !== "number" || aboveRowspan < 1 || cellRowspan < 1) return false;
72
76
  const continuationCells = tableCellContinuationCells(aboveCell, aboveRowspan);
73
77
  continuationCells.push(tableCellContinuationFromNode(cell));
74
- const nestedContinuations = cell.attrs["_docxVMergeContinuationCells"];
75
- if (Array.isArray(nestedContinuations)) continuationCells.push(...nestedContinuations);
78
+ const nestedPayload = tableCellContinuationPayload(cell);
79
+ if (nestedPayload) continuationCells.push(...nestedPayload.cells);
76
80
  tr.delete(cellPos, cellPos + cell.nodeSize);
77
81
  tr.setNodeMarkup(abovePos, void 0, {
78
82
  ...aboveCell.attrs,
79
83
  rowspan: aboveRowspan + cellRowspan,
80
- _docxVMergeContinuationCells: continuationCells
84
+ _docxVMergeContinuationCells: transportTableCellsWithParagraphPropertySources(continuationCells)
81
85
  });
82
86
  return true;
83
87
  };
84
88
  const tableCellContinuationCells = (cell, rowspan) => {
85
- const stored = cell.attrs["_docxVMergeContinuationCells"];
86
- const cells = Array.isArray(stored) ? [...stored] : [];
89
+ const payload = tableCellContinuationPayload(cell);
90
+ const cells = payload ? [...payload.cells] : [];
87
91
  while (cells.length < rowspan - 1) cells.push(emptyVerticalMergeContinuation());
88
92
  return cells;
89
93
  };
@@ -109,8 +113,10 @@ const emptyVerticalMergeContinuation = () => ({
109
113
  });
110
114
  const resolveCollapsedTableCellMerge = (tr, cellPos, mode, revisionSet) => {
111
115
  const cell = tr.doc.nodeAt(cellPos);
112
- const stored = cell?.attrs["_docxVMergeContinuationCells"];
113
- if (!cell || !Array.isArray(stored)) return false;
116
+ if (!cell) return false;
117
+ const payload = tableCellContinuationPayload(cell);
118
+ if (!payload) return false;
119
+ const stored = payload.cells;
114
120
  const matchingIndices = [];
115
121
  const nextCells = stored.map((continuationCell, index) => {
116
122
  const change = getTableCellMergeChange(continuationCell);
@@ -142,8 +148,9 @@ const resolveCollapsedTableCellMerge = (tr, cellPos, mode, revisionSet) => {
142
148
  const rowspan = cell.attrs["rowspan"];
143
149
  if (typeof rowspan !== "number" || rowspan !== stored.length + 1 || rectangle.bottom - rectangle.top !== rowspan) return false;
144
150
  const restorations = [];
151
+ const restoredCells = restoreTableCellsWithParagraphPropertySources(payload);
145
152
  for (const index of splitIndices) {
146
- const source = nextCells[index];
153
+ const source = restoredCells[index];
147
154
  if (!source) return false;
148
155
  const restoredCell = createRestoredTableCell(cell, source);
149
156
  if (!restoredCell) return false;