@stll/folio-core 0.37.3 → 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.
@@ -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
@@ -540,6 +541,7 @@ const readTextBoxAttrs = (node) => {
540
541
  optionalOneOf(attrs, "_docxPlacement", "textBox.attrs._docxPlacement", issues, TEXT_BOX_DOCX_PLACEMENTS);
541
542
  optionalString(attrs, "_docxGroupId", "textBox.attrs._docxGroupId", issues);
542
543
  optionalString(attrs, "_docxAnchorId", "textBox.attrs._docxAnchorId", issues);
544
+ requiredTextBoxBodyContentState(attrs, issues);
543
545
  optionalTextBoxTrackedChange(attrs, issues);
544
546
  optionalTextBoxInlineSdts(attrs, issues);
545
547
  return attrsResult(attrs, issues);
@@ -1203,6 +1205,17 @@ const optionalTextBoxTrackedChange = (attrs, issues) => {
1203
1205
  requiredString(info, "author", "textBox.attrs._docxTrackedChange.info.author", issues);
1204
1206
  optionalString(info, "date", "textBox.attrs._docxTrackedChange.info.date", issues);
1205
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
+ };
1206
1219
  const optionalTextBoxInlineSdts = (attrs, issues) => {
1207
1220
  const value = attrs["_docxInlineSdts"];
1208
1221
  if (value === void 0 || value === null) return;
@@ -2862,6 +2862,24 @@ function tableCellAttrsToFormatting(attrs) {
2862
2862
  if (attrs.margins) f.margins = buildCellMarginsFromAttrs(attrs.margins);
2863
2863
  return f;
2864
2864
  }
2865
+ const isUnchangedSourceEmptyPlaceholder = (blocks) => {
2866
+ const paragraph = blocks.at(0);
2867
+ if (blocks.length !== 1 || paragraph?.type !== "paragraph" || paragraph.content.length !== 0) return false;
2868
+ return Object.keys(paragraph).every((key) => key === "type" || key === "content" || key === "paraId" || key === "textId");
2869
+ };
2870
+ const projectTextBoxBodyContent = (state, blocks) => {
2871
+ switch (state.type) {
2872
+ case "source-empty": return isUnchangedSourceEmptyPlaceholder(blocks) ? { type: "source-empty" } : {
2873
+ type: "authored",
2874
+ content: blocks
2875
+ };
2876
+ case "authored": return {
2877
+ type: "authored",
2878
+ content: blocks
2879
+ };
2880
+ default: return state;
2881
+ }
2882
+ };
2865
2883
  /**
2866
2884
  * Convert a ProseMirror textBox node back to a Paragraph wrapping a ShapeContent run.
2867
2885
  * The text box content becomes a Shape with textBody.
@@ -2874,6 +2892,7 @@ function convertPMTextBox(node, styleResolver = null) {
2874
2892
  if (child.type.name === "paragraph") childBlocks.push(convertPMParagraph(child, void 0, void 0, styleResolver));
2875
2893
  else if (child.type.name === "table") childBlocks.push(convertPMTable(child, void 0, styleResolver));
2876
2894
  });
2895
+ const textBodyContent = projectTextBoxBodyContent(attrs._docxTextBodyContentState, childBlocks);
2877
2896
  const shape = {
2878
2897
  type: "shape",
2879
2898
  shapeType: "textBox",
@@ -2882,10 +2901,7 @@ function convertPMTextBox(node, styleResolver = null) {
2882
2901
  height: attrs.height ? pixelsToEmu(attrs.height) : 0
2883
2902
  },
2884
2903
  textBody: {
2885
- content: childBlocks.length > 0 ? childBlocks : [{
2886
- type: "paragraph",
2887
- content: []
2888
- }],
2904
+ content: textBodyContent.type === "source-empty" ? [] : textBodyContent.content,
2889
2905
  ...attrs.autoFit !== void 0 ? { autoFit: attrs.autoFit } : {},
2890
2906
  ...attrs.textWrap !== void 0 ? { textWrap: attrs.textWrap } : {},
2891
2907
  ...verticalAlign !== void 0 ? { anchor: verticalAlign } : {},
@@ -2259,6 +2259,7 @@ function convertTextBox(textBox, styleResolver, options) {
2259
2259
  _docxPlacement: options.placement,
2260
2260
  _docxGroupId: options.groupId,
2261
2261
  _docxAnchorId: options.anchorId,
2262
+ _docxTextBodyContentState: textBox.content.length === 0 ? { type: "source-empty" } : { type: "authored" },
2262
2263
  _docxTrackedChange: options.trackedChange,
2263
2264
  _docxInlineSdts: options.inlineSdts.length > 0 ? options.inlineSdts : void 0
2264
2265
  }, contentNodes);
@@ -1,68 +1,7 @@
1
- import { document_d_exports } from "../../../types/document.js";
2
- import { OutlineStyleAttr } from "../../../types/documentEnumValues.js";
3
- import { ImagePositionAttrs, TextBoxAttrs as TextBoxAttrs$1 } from "../../schema/nodes.js";
1
+ import { TextBoxAttrs as TextBoxAttrs$1 } from "../../schema/nodes.js";
4
2
  import { NodeExtension } from "../types.js";
5
3
  //#region src/prosemirror/extensions/nodes/TextBoxExtension.d.ts
6
- type TextBoxAttrs = {
7
- /** Width in pixels */
8
- width?: number;
9
- /** Height in pixels */
10
- height?: number;
11
- /** Text fitting behavior */
12
- autoFit?: document_d_exports.ShapeTextBody["autoFit"];
13
- /** Horizontal text wrapping inside the box */
14
- textWrap?: document_d_exports.ShapeTextBody["textWrap"];
15
- /** Unique identifier */
16
- textBoxId?: string;
17
- /** Fill color as CSS color */
18
- fillColor?: string;
19
- /** Outline width in pixels */
20
- outlineWidth?: number;
21
- /** Outline color as CSS color */
22
- outlineColor?: string;
23
- /** Outline dash style, or `"none"` for an explicit no-outline. */
24
- outlineStyle?: OutlineStyleAttr;
25
- /** DrawingML rotation and/or flips, serialized as CSS transform functions. */
26
- transform?: string;
27
- /** Internal margin top in pixels */
28
- marginTop?: number;
29
- /** Internal margin bottom in pixels */
30
- marginBottom?: number;
31
- /** Internal margin left in pixels */
32
- marginLeft?: number;
33
- /** Internal margin right in pixels */
34
- marginRight?: number;
35
- /** Vertical text alignment */
36
- verticalAlign?: string;
37
- /** Display mode */
38
- displayMode?: "inline" | "float" | "block";
39
- /** CSS float direction */
40
- cssFloat?: "left" | "right" | "none";
41
- /** Wrap type */
42
- wrapType?: document_d_exports.ImageWrap["type"];
43
- /** OOXML wrapText direction for anchored text boxes (eigenpal #474). */
44
- wrapText?: "bothSides" | "left" | "right" | "largest";
45
- /** Wrap distance from top edge, in pixels (OOXML distT, EMU-converted). */
46
- distTop?: number;
47
- /** Wrap distance from bottom edge, in pixels. */
48
- distBottom?: number;
49
- /** Wrap distance from left edge, in pixels. */
50
- distLeft?: number;
51
- /** Wrap distance from right edge, in pixels. */
52
- distRight?: number;
53
- /** Position for floating/anchored text boxes. */
54
- position?: ImagePositionAttrs;
55
- /** Original DOCX placement hint for save-path reconstruction. */
56
- _docxPlacement?: "standalone" | "inlineWithPrevious";
57
- /** Original DOCX paragraph group for standalone text-box reconstruction. */
58
- _docxGroupId?: string;
59
- /** Inline anchor linking this block node to its source run position. */
60
- _docxAnchorId?: string;
61
- /** Original run-level revision wrapper for save-path reconstruction. */
62
- _docxTrackedChange?: TextBoxAttrs$1["_docxTrackedChange"];
63
- /** Original inline content-control ancestry for save-path reconstruction. */
64
- _docxInlineSdts?: TextBoxAttrs$1["_docxInlineSdts"];
65
- };
4
+ type TextBoxAttrs = TextBoxAttrs$1;
66
5
  declare const TextBoxExtension: (options?: Partial<Record<string, unknown>> | undefined) => NodeExtension;
67
6
  //#endregion
68
7
  export { TextBoxAttrs, TextBoxExtension };
@@ -2,6 +2,13 @@ import { IMAGE_WRAP_TYPE_VALUES, normalizeShapeTextAnchor } from "../../../types
2
2
  import { expectTextBoxAttrs } from "../../attrs/index.js";
3
3
  import { createNodeExtension } from "../create.js";
4
4
  //#region src/prosemirror/extensions/nodes/TextBoxExtension.ts
5
+ /**
6
+ * TextBox Extension — editable text box node
7
+ *
8
+ * An isolating block node that contains paragraphs (and tables).
9
+ * Rendered as a positioned container with optional fill, outline, and margins.
10
+ * Supports inline and floating positioning.
11
+ */
5
12
  function parseTextBoxPosition(raw) {
6
13
  if (!raw) return;
7
14
  try {
@@ -60,6 +67,7 @@ const TextBoxExtension = createNodeExtension({
60
67
  _docxPlacement: { default: null },
61
68
  _docxGroupId: { default: null },
62
69
  _docxAnchorId: { default: null },
70
+ _docxTextBodyContentState: { default: { type: "authored" } },
63
71
  _docxTrackedChange: { default: null },
64
72
  _docxInlineSdts: { default: null }
65
73
  },
@@ -74,6 +82,7 @@ const TextBoxExtension = createNodeExtension({
74
82
  const textWrap = parseTextBoxTextWrap(d["textWrap"]);
75
83
  const verticalAlign = parseTextBoxVerticalAlign(d["verticalAlign"]);
76
84
  return {
85
+ _docxTextBodyContentState: { type: "authored" },
77
86
  ...d["width"] ? { width: Number(d["width"]) } : {},
78
87
  ...d["height"] ? { height: Number(d["height"]) } : {},
79
88
  ...autoFit ? { autoFit } : {},
@@ -488,6 +488,10 @@ type ShapeAttrs = {
488
488
  /**
489
489
  * Text box node attributes
490
490
  */
491
+ declare const TEXT_BOX_TEXT_BODY_CONTENT_STATE_TYPES: readonly ["source-empty", "authored"];
492
+ type TextBoxTextBodyContentState = { [Type in (typeof TEXT_BOX_TEXT_BODY_CONTENT_STATE_TYPES)[number]]: {
493
+ readonly type: Type;
494
+ }; }[(typeof TEXT_BOX_TEXT_BODY_CONTENT_STATE_TYPES)[number]];
491
495
  type TextBoxAttrs = {
492
496
  /** Width in pixels */
493
497
  width?: number;
@@ -543,6 +547,12 @@ type TextBoxAttrs = {
543
547
  _docxGroupId?: string;
544
548
  /** Inline anchor linking this block node to its source run position. */
545
549
  _docxAnchorId?: string;
550
+ /**
551
+ * Ownership of the schema-required placeholder paragraph. A source text
552
+ * body with no children needs one paragraph while it is editable, but that
553
+ * paragraph is not authored document content.
554
+ */
555
+ _docxTextBodyContentState: TextBoxTextBodyContentState;
546
556
  /** Original run-level revision wrapper for save-path reconstruction. */
547
557
  _docxTrackedChange?: {
548
558
  type: "insertion";
@@ -770,4 +780,4 @@ type TableCellAttrs = {
770
780
  _docxVMergeContinuationCells?: unknown;
771
781
  };
772
782
  //#endregion
773
- export { BlockSdtAttrs, BookmarkBoundaryAttrs, FieldAttrs, HardBreakAttrs, ImageAttrs, ImagePositionAttrs, MathAttrs, PageBreakRunAttrs, ParagraphAttrs, ParagraphPropertyChangeAttrs, SdtAttrs, ShapeAttrs, SuggestedStructuralMarker, SymbolAttrs, TabAttrs, TableAttrs, TableCellAttrs, TableRowAttrs, TextBoxAnchorAttrs, TextBoxAttrs };
783
+ export { BlockSdtAttrs, BookmarkBoundaryAttrs, FieldAttrs, HardBreakAttrs, ImageAttrs, ImagePositionAttrs, MathAttrs, PageBreakRunAttrs, ParagraphAttrs, ParagraphPropertyChangeAttrs, SdtAttrs, ShapeAttrs, SuggestedStructuralMarker, SymbolAttrs, TEXT_BOX_TEXT_BODY_CONTENT_STATE_TYPES, TabAttrs, TableAttrs, TableCellAttrs, TableRowAttrs, TextBoxAnchorAttrs, TextBoxAttrs, TextBoxTextBodyContentState };
@@ -0,0 +1,7 @@
1
+ //#region src/prosemirror/schema/nodes.ts
2
+ /**
3
+ * Text box node attributes
4
+ */
5
+ const TEXT_BOX_TEXT_BODY_CONTENT_STATE_TYPES = Object.freeze(["source-empty", "authored"]);
6
+ //#endregion
7
+ export { TEXT_BOX_TEXT_BODY_CONTENT_STATE_TYPES };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/folio-core",
3
- "version": "0.37.3",
3
+ "version": "0.37.4",
4
4
  "description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
5
5
  "keywords": [
6
6
  "document-model",