@stll/folio-core 0.37.3 → 0.37.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 (33) hide show
  1. package/dist/ai-edits/index.d.ts +2 -1
  2. package/dist/ai-edits/types.d.ts +2 -2
  3. package/dist/compare/content-alignment.js +213 -135
  4. package/dist/compare/content-types.d.ts +3 -1
  5. package/dist/compat/eigenpal.d.ts +2 -2
  6. package/dist/docx/numberingParser.d.ts +1 -13
  7. package/dist/docx/numberingParser.js +1 -10
  8. package/dist/docx/paragraphParser.js +1 -5
  9. package/dist/docx/paragraphPropertySource.d.ts +8 -5
  10. package/dist/docx/paragraphPropertySource.js +16 -4
  11. package/dist/docx/serializer/borderSerializer.d.ts +4 -1
  12. package/dist/docx/serializer/borderSerializer.js +20 -18
  13. package/dist/docx/serializer/commentSerializer.js +1 -1
  14. package/dist/docx/serializer/numberingSerializer.js +1 -1
  15. package/dist/docx/serializer/paragraphSerializer.js +11 -177
  16. package/dist/docx/serializer/runSerializer.d.ts +1 -5
  17. package/dist/docx/serializer/runSerializer.js +2 -129
  18. package/dist/docx/serializer/stylesSerializer.js +1 -1
  19. package/dist/docx/serializer/textFormattingSerializer.d.ts +17 -0
  20. package/dist/docx/serializer/textFormattingSerializer.js +142 -0
  21. package/dist/index.d.ts +2 -2
  22. package/dist/internal/paragraphFormattingSerialization.d.ts +23 -0
  23. package/dist/internal/paragraphFormattingSerialization.js +157 -0
  24. package/dist/prosemirror/attrs/index.js +13 -0
  25. package/dist/prosemirror/conversion/fromProseDoc.js +28 -20
  26. package/dist/prosemirror/conversion/toProseDoc.js +1 -0
  27. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -63
  28. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +9 -0
  29. package/dist/prosemirror/schema/nodes.d.ts +11 -1
  30. package/dist/prosemirror/schema/nodes.js +7 -0
  31. package/dist/server.d.ts +2 -1
  32. package/dist/version-comparison.js +23 -27
  33. package/package.json +1 -1
@@ -0,0 +1,142 @@
1
+ import { HIGHLIGHT_COLOR_VALUES } from "../../types/documentEnumValues.js";
2
+ import { isValidHexColor } from "../../utils/colorResolver.js";
3
+ import { roundHorizontalScalePercentForSerialization } from "../../utils/horizontalScale.js";
4
+ import { escapeXml, intAttr } from "./xmlUtils.js";
5
+ //#region src/docx/serializer/textFormattingSerializer.ts
6
+ const VALID_HIGHLIGHT_COLORS = new Set(HIGHLIGHT_COLOR_VALUES);
7
+ /**
8
+ * Serialize a color element (w:color)
9
+ */
10
+ function serializeColorElement(color) {
11
+ if (!color) return "";
12
+ const { auto, rgb, themeColor, themeTint, themeShade } = color;
13
+ const attrs = [];
14
+ if (auto) attrs.push("w:val=\"auto\"");
15
+ else if (rgb && isValidHexColor(rgb)) attrs.push(`w:val="${escapeXml(rgb)}"`);
16
+ if (themeColor) attrs.push(`w:themeColor="${escapeXml(themeColor)}"`);
17
+ if (themeTint) attrs.push(`w:themeTint="${escapeXml(themeTint)}"`);
18
+ if (themeShade) attrs.push(`w:themeShade="${escapeXml(themeShade)}"`);
19
+ return attrs.length === 0 ? "" : `<w:color ${attrs.join(" ")}/>`;
20
+ }
21
+ /**
22
+ * Serialize shading properties (w:shd)
23
+ */
24
+ function serializeShading(shading) {
25
+ if (!shading) return "";
26
+ const { pattern, color, fill } = shading;
27
+ const attrs = [];
28
+ if (pattern) attrs.push(`w:val="${escapeXml(pattern)}"`);
29
+ else attrs.push("w:val=\"clear\"");
30
+ if (color) {
31
+ const { rgb, auto, themeColor: _themeColor, themeTint: _themeTint, themeShade: _themeShade } = color;
32
+ if (rgb && isValidHexColor(rgb)) attrs.push(`w:color="${escapeXml(rgb)}"`);
33
+ else if (auto) attrs.push("w:color=\"auto\"");
34
+ }
35
+ if (fill) {
36
+ const { rgb, auto, themeColor, themeTint, themeShade } = fill;
37
+ if (rgb && isValidHexColor(rgb)) attrs.push(`w:fill="${escapeXml(rgb)}"`);
38
+ else if (auto) attrs.push("w:fill=\"auto\"");
39
+ if (themeColor) attrs.push(`w:themeFill="${escapeXml(themeColor)}"`);
40
+ if (themeTint) attrs.push(`w:themeFillTint="${escapeXml(themeTint)}"`);
41
+ if (themeShade) attrs.push(`w:themeFillShade="${escapeXml(themeShade)}"`);
42
+ }
43
+ return attrs.length === 0 ? "" : `<w:shd ${attrs.join(" ")}/>`;
44
+ }
45
+ /**
46
+ * Serialize text formatting properties to w:rPr XML
47
+ */
48
+ function serializeTextFormatting(input) {
49
+ if (!input) return "";
50
+ const { bold, boldCs, italic, italicCs, underline, strike, doubleStrike, vertAlign, smallCaps, allCaps, hidden, color, highlight, shading, fontSize, fontSizeCs, fontFamily, language, spacing, position, scale, kerning, effect, emphasisMark, emboss, imprint, outline, shadow, rtl, cs, styleId } = input;
51
+ const parts = [];
52
+ if (styleId) parts.push(`<w:rStyle w:val="${escapeXml(styleId)}"/>`);
53
+ if (fontFamily) {
54
+ const { ascii, hAnsi, eastAsia, cs: complexScript, hint, asciiTheme, hAnsiTheme, eastAsiaTheme, csTheme } = fontFamily;
55
+ const fontAttrs = [];
56
+ if (ascii) fontAttrs.push(`w:ascii="${escapeXml(ascii)}"`);
57
+ if (hAnsi) fontAttrs.push(`w:hAnsi="${escapeXml(hAnsi)}"`);
58
+ if (eastAsia) fontAttrs.push(`w:eastAsia="${escapeXml(eastAsia)}"`);
59
+ if (complexScript) fontAttrs.push(`w:cs="${escapeXml(complexScript)}"`);
60
+ if (hint) fontAttrs.push(`w:hint="${escapeXml(hint)}"`);
61
+ if (asciiTheme) fontAttrs.push(`w:asciiTheme="${escapeXml(asciiTheme)}"`);
62
+ if (hAnsiTheme) fontAttrs.push(`w:hAnsiTheme="${escapeXml(hAnsiTheme)}"`);
63
+ if (eastAsiaTheme) fontAttrs.push(`w:eastAsiaTheme="${escapeXml(eastAsiaTheme)}"`);
64
+ if (csTheme) fontAttrs.push(`w:cstheme="${escapeXml(csTheme)}"`);
65
+ if (fontAttrs.length > 0) parts.push(`<w:rFonts ${fontAttrs.join(" ")}/>`);
66
+ }
67
+ if (bold === true) parts.push("<w:b/>");
68
+ else if (bold === false) parts.push("<w:b w:val=\"0\"/>");
69
+ if (boldCs === true) parts.push("<w:bCs/>");
70
+ else if (boldCs === false) parts.push("<w:bCs w:val=\"0\"/>");
71
+ if (italic === true) parts.push("<w:i/>");
72
+ else if (italic === false) parts.push("<w:i w:val=\"0\"/>");
73
+ if (italicCs === true) parts.push("<w:iCs/>");
74
+ else if (italicCs === false) parts.push("<w:iCs w:val=\"0\"/>");
75
+ if (allCaps === true) parts.push("<w:caps/>");
76
+ else if (allCaps === false) parts.push("<w:caps w:val=\"0\"/>");
77
+ if (smallCaps === true) parts.push("<w:smallCaps/>");
78
+ else if (smallCaps === false) parts.push("<w:smallCaps w:val=\"0\"/>");
79
+ if (strike === true) parts.push("<w:strike/>");
80
+ else if (strike === false) parts.push("<w:strike w:val=\"0\"/>");
81
+ if (doubleStrike === true) parts.push("<w:dstrike/>");
82
+ else if (doubleStrike === false) parts.push("<w:dstrike w:val=\"0\"/>");
83
+ if (outline === true) parts.push("<w:outline/>");
84
+ else if (outline === false) parts.push("<w:outline w:val=\"0\"/>");
85
+ if (shadow === true) parts.push("<w:shadow/>");
86
+ else if (shadow === false) parts.push("<w:shadow w:val=\"0\"/>");
87
+ if (emboss === true) parts.push("<w:emboss/>");
88
+ else if (emboss === false) parts.push("<w:emboss w:val=\"0\"/>");
89
+ if (imprint === true) parts.push("<w:imprint/>");
90
+ else if (imprint === false) parts.push("<w:imprint w:val=\"0\"/>");
91
+ if (hidden === true) parts.push("<w:vanish/>");
92
+ else if (hidden === false) parts.push("<w:vanish w:val=\"0\"/>");
93
+ const colorXml = serializeColorElement(color);
94
+ if (colorXml) parts.push(colorXml);
95
+ if (spacing !== void 0) parts.push(`<w:spacing w:val="${intAttr(spacing)}"/>`);
96
+ const horizontalScale = roundHorizontalScalePercentForSerialization(scale);
97
+ if (horizontalScale !== void 0) parts.push(`<w:w w:val="${intAttr(horizontalScale)}"/>`);
98
+ if (kerning !== void 0) parts.push(`<w:kern w:val="${intAttr(kerning)}"/>`);
99
+ if (position !== void 0) parts.push(`<w:position w:val="${intAttr(position)}"/>`);
100
+ if (fontSize !== void 0) parts.push(`<w:sz w:val="${intAttr(fontSize)}"/>`);
101
+ if (fontSizeCs !== void 0) parts.push(`<w:szCs w:val="${intAttr(fontSizeCs)}"/>`);
102
+ let customHighlightShadingXml = "";
103
+ if (highlight) {
104
+ if (VALID_HIGHLIGHT_COLORS.has(highlight)) parts.push(`<w:highlight w:val="${highlight}"/>`);
105
+ else if (!shading) {
106
+ const hex = highlight.replace(/^#/u, "");
107
+ if (/^[0-9a-fA-F]{6}$/u.test(hex)) customHighlightShadingXml = `<w:shd w:val="clear" w:color="auto" w:fill="${hex}"/>`;
108
+ }
109
+ }
110
+ if (underline) {
111
+ const { style, color: underlineColor } = underline;
112
+ const uAttrs = [`w:val="${style}"`];
113
+ if (underlineColor) {
114
+ const { rgb, themeColor, themeTint, themeShade, auto: _auto } = underlineColor;
115
+ if (rgb && isValidHexColor(rgb)) uAttrs.push(`w:color="${escapeXml(rgb)}"`);
116
+ if (themeColor) uAttrs.push(`w:themeColor="${escapeXml(themeColor)}"`);
117
+ if (themeTint) uAttrs.push(`w:themeTint="${escapeXml(themeTint)}"`);
118
+ if (themeShade) uAttrs.push(`w:themeShade="${escapeXml(themeShade)}"`);
119
+ }
120
+ parts.push(`<w:u ${uAttrs.join(" ")}/>`);
121
+ }
122
+ if (effect) parts.push(`<w:effect w:val="${effect}"/>`);
123
+ const shadingXml = serializeShading(shading) || customHighlightShadingXml;
124
+ if (shadingXml) parts.push(shadingXml);
125
+ if (vertAlign) parts.push(`<w:vertAlign w:val="${vertAlign}"/>`);
126
+ if (rtl === true) parts.push("<w:rtl/>");
127
+ else if (rtl === false) parts.push("<w:rtl w:val=\"0\"/>");
128
+ if (cs === true) parts.push("<w:cs/>");
129
+ else if (cs === false) parts.push("<w:cs w:val=\"0\"/>");
130
+ if (emphasisMark) parts.push(`<w:em w:val="${emphasisMark}"/>`);
131
+ if (language) {
132
+ const { val, eastAsia, bidi } = language;
133
+ const languageAttrs = [];
134
+ if (val) languageAttrs.push(`w:val="${escapeXml(val)}"`);
135
+ if (eastAsia) languageAttrs.push(`w:eastAsia="${escapeXml(eastAsia)}"`);
136
+ if (bidi) languageAttrs.push(`w:bidi="${escapeXml(bidi)}"`);
137
+ if (languageAttrs.length > 0) parts.push(`<w:lang ${languageAttrs.join(" ")}/>`);
138
+ }
139
+ return parts.length === 0 ? "" : `<w:rPr>${parts.join("")}</w:rPr>`;
140
+ }
141
+ //#endregion
142
+ export { serializeShading, serializeTextFormatting };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { document_d_exports } from "./types/document.js";
2
- import { FolioContentBlock, FolioContentContainerPathEntry, FolioContentIdStability, FolioContentInlineBooleanProperty, FolioContentInlineFormatting, FolioContentInlineFormattingPatch, FolioContentLineSpacingRule, FolioContentParagraphAlignment, FolioContentParagraphSpacing, FolioContentRun, FolioContentSnapshot, FolioContentTableLocation } from "./compare/content-types.js";
2
+ import { FolioContentBlock, FolioContentContainerPathEntry, FolioContentIdStability, FolioContentInlineBooleanProperty, FolioContentInlineFormatting, FolioContentInlineFormattingPatch, FolioContentLineSpacingRule, FolioContentParagraphAlignment, FolioContentParagraphKind, FolioContentParagraphSpacing, FolioContentRun, FolioContentSnapshot, FolioContentTableLocation } from "./compare/content-types.js";
3
3
  import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIBlockStructuralBoundary, FolioAIBlockTableLocation, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditNormalization, FolioAIEditNormalizationCode, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAIInlineBooleanProperty, FolioAIInlineFormatting, FolioAIInlineFormattingPatch, FolioAIParagraphSpacing, FolioAISignatureParty } from "./ai-edits/types.js";
4
4
  import { WORD_DIFF_GRANULARITIES, WordDiffGranularity, WordDiffNormalization, WordDiffOptions, WordDiffSegment, diffWordSegments } from "./ai-edits/word-diff.js";
5
5
  import { FolioAIEditApplyOutcome, FolioRevisionStamp, FolioWordDiffOptions, applyFolioAIEditOperations } from "./ai-edits/apply.js";
@@ -40,4 +40,4 @@ import { getGoogleFontsEnabled, setEmbeddedFontFamilyMap, setGoogleFontsEnabled
40
40
  import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
41
41
  type Document = document_d_exports.Document;
42
42
  type DocxConformanceClass = document_d_exports.DocxConformanceClass;
43
- export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, type CompareChange, type CompareChangeLocation, type CompareContentOptions, CompareDocxApplyError, type CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, type CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, type CompareFormatRange, type CompareResult, type CompareUnsupportedPart, type CompareUnsupportedReason, type CompareVerification, type CompareVerificationCause, type CompareVerificationFailure, type CompareVerificationInvariant, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, type FinalParagraphMarkRevision, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockStructuralBoundary, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIInlineBooleanProperty, type FolioAIInlineFormatting, type FolioAIInlineFormattingPatch, type FolioAIParagraphSpacing, type FolioAISignatureParty, type FolioBlockId, type FolioContentBlock, type FolioContentBlockProperty, type FolioContentComparison, type FolioContentComparisonError, type FolioContentComparisonEvent, type FolioContentComparisonLimit, FolioContentComparisonLimitError, type FolioContentContainerPathEntry, type FolioContentFormatRange, type FolioContentFormattingChange, type FolioContentIdStability, type FolioContentInlineBooleanProperty, type FolioContentInlineFormatting, type FolioContentInlineFormattingPatch, type FolioContentLineSpacingRule, type FolioContentParagraphAlignment, type FolioContentParagraphFormattingPatch, type FolioContentParagraphSpacing, type FolioContentRun, type FolioContentSnapshot, type FolioContentStructuralChange, type FolioContentTableLocation, type FolioContentTextSegment, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type FolioRevisionStamp, type FolioWordDiffOptions, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
43
+ export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, type CompareChange, type CompareChangeLocation, type CompareContentOptions, CompareDocxApplyError, type CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, type CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, type CompareFormatRange, type CompareResult, type CompareUnsupportedPart, type CompareUnsupportedReason, type CompareVerification, type CompareVerificationCause, type CompareVerificationFailure, type CompareVerificationInvariant, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, type FinalParagraphMarkRevision, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockStructuralBoundary, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIInlineBooleanProperty, type FolioAIInlineFormatting, type FolioAIInlineFormattingPatch, type FolioAIParagraphSpacing, type FolioAISignatureParty, type FolioBlockId, type FolioContentBlock, type FolioContentBlockProperty, type FolioContentComparison, type FolioContentComparisonError, type FolioContentComparisonEvent, type FolioContentComparisonLimit, FolioContentComparisonLimitError, type FolioContentContainerPathEntry, type FolioContentFormatRange, type FolioContentFormattingChange, type FolioContentIdStability, type FolioContentInlineBooleanProperty, type FolioContentInlineFormatting, type FolioContentInlineFormattingPatch, type FolioContentLineSpacingRule, type FolioContentParagraphAlignment, type FolioContentParagraphFormattingPatch, type FolioContentParagraphKind, type FolioContentParagraphSpacing, type FolioContentRun, type FolioContentSnapshot, type FolioContentStructuralChange, type FolioContentTableLocation, type FolioContentTextSegment, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type FolioRevisionStamp, type FolioWordDiffOptions, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
@@ -0,0 +1,23 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ //#region src/internal/paragraphFormattingSerialization.d.ts
3
+ type ExhaustiveFields<Source, Classified extends keyof Source> = Exclude<keyof Source, Classified> extends never ? Source : never;
4
+ type ParagraphNumberingReference = document_d_exports.ParagraphFormatting["numPr"] | null;
5
+ type ClassifiedParagraphFormattingField = "alignment" | "bidi" | "kinsoku" | "overflowPunctuation" | "spaceBefore" | "spaceAfter" | "lineSpacing" | "lineSpacingRule" | "snapToGrid" | "beforeAutospacing" | "afterAutospacing" | "spacingExplicit" | "indentLeft" | "indentRight" | "indentFirstLine" | "hangingIndent" | "borders" | "shading" | "tabs" | "keepNext" | "keepLines" | "widowControl" | "pageBreakBefore" | "contextualSpacing" | "numPr" | "numPrFromStyle" | "outlineLevel" | "styleId" | "frame" | "suppressLineNumbers" | "suppressAutoHyphens" | "runProperties" | "runInWithNext";
6
+ type ExhaustiveParagraphFormatting = ExhaustiveFields<document_d_exports.ParagraphFormatting, ClassifiedParagraphFormattingField>;
7
+ /** Compare numbering references by their emitted id and effective level. */
8
+ declare const paragraphNumberingReferencesEqual: (left: ParagraphNumberingReference, right: ParagraphNumberingReference) => boolean;
9
+ /** Whether resolved numbering still belongs to the paragraph's style tier. */
10
+ declare const isStyleSourcedParagraphNumbering: (numPr: ParagraphNumberingReference, numPrFromStyle: ParagraphNumberingReference) => boolean;
11
+ /** Exact fallback-emission instructions for the modeled part of `w:pPr`. */
12
+ type ModeledParagraphFormattingEmission = Readonly<{
13
+ propertiesXml?: string;
14
+ paragraphMarkPropertiesInnerXml?: string;
15
+ }>;
16
+ /**
17
+ * Resolve paragraph formatting into its exact fallback `w:pPr` instructions.
18
+ * Every input field is classified here so parser capture fingerprints and the
19
+ * serializer cannot drift apart when paragraph formatting grows.
20
+ */
21
+ declare const modelParagraphFormattingEmission: (input: ExhaustiveParagraphFormatting | undefined) => ModeledParagraphFormattingEmission;
22
+ //#endregion
23
+ export { ModeledParagraphFormattingEmission, isStyleSourcedParagraphNumbering, modelParagraphFormattingEmission, paragraphNumberingReferencesEqual };
@@ -0,0 +1,157 @@
1
+ import { serializeBorder } from "../docx/serializer/borderSerializer.js";
2
+ import { serializeShading, serializeTextFormatting } from "../docx/serializer/textFormattingSerializer.js";
3
+ import { escapeXml, intAttr } from "../docx/serializer/xmlUtils.js";
4
+ //#region src/internal/paragraphFormattingSerialization.ts
5
+ const modelParagraphNumberingReference = (reference) => {
6
+ if (!reference) return null;
7
+ const { numId, ilvl } = reference;
8
+ return {
9
+ ...numId !== void 0 ? { numId } : {},
10
+ ...ilvl !== void 0 ? { ilvl } : {}
11
+ };
12
+ };
13
+ /** Compare numbering references by their emitted id and effective level. */
14
+ const paragraphNumberingReferencesEqual = (left, right) => {
15
+ const modeledLeft = modelParagraphNumberingReference(left);
16
+ const modeledRight = modelParagraphNumberingReference(right);
17
+ if (modeledLeft === null || modeledRight === null) return modeledLeft === null && modeledRight === null;
18
+ return modeledLeft.numId === modeledRight.numId && (modeledLeft.ilvl ?? 0) === (modeledRight.ilvl ?? 0);
19
+ };
20
+ /** Whether resolved numbering still belongs to the paragraph's style tier. */
21
+ const isStyleSourcedParagraphNumbering = (numPr, numPrFromStyle) => numPr != null && numPrFromStyle != null && paragraphNumberingReferencesEqual(numPr, numPrFromStyle);
22
+ const serializeToggle = (name, value) => {
23
+ if (value === true) return `<w:${name}/>`;
24
+ if (value === false) return `<w:${name} w:val="0"/>`;
25
+ return "";
26
+ };
27
+ const serializeParagraphBorders = (borders) => {
28
+ if (!borders) return "";
29
+ const { top, left, bottom, right, between, bar } = borders;
30
+ const parts = [
31
+ serializeBorder(top, "top"),
32
+ serializeBorder(left, "left"),
33
+ serializeBorder(bottom, "bottom"),
34
+ serializeBorder(right, "right"),
35
+ serializeBorder(between, "between"),
36
+ serializeBorder(bar, "bar")
37
+ ].filter(Boolean);
38
+ return parts.length === 0 ? "" : `<w:pBdr>${parts.join("")}</w:pBdr>`;
39
+ };
40
+ const serializeTabStops = (tabs) => {
41
+ if (!tabs || tabs.length === 0) return "";
42
+ return `<w:tabs>${tabs.map((tab) => {
43
+ const { alignment, position, leader } = tab;
44
+ const attrs = [`w:val="${alignment}"`, `w:pos="${intAttr(position)}"`];
45
+ if (leader && leader !== "none") attrs.push(`w:leader="${leader}"`);
46
+ return `<w:tab ${attrs.join(" ")}/>`;
47
+ }).join("")}</w:tabs>`;
48
+ };
49
+ const serializeSpacing = (formatting) => {
50
+ const attrs = [];
51
+ if (formatting.spaceBefore !== void 0) attrs.push(`w:before="${intAttr(formatting.spaceBefore)}"`);
52
+ if (formatting.spaceAfter !== void 0) attrs.push(`w:after="${intAttr(formatting.spaceAfter)}"`);
53
+ if (formatting.lineSpacing !== void 0) attrs.push(`w:line="${intAttr(formatting.lineSpacing)}"`);
54
+ if (formatting.lineSpacingRule) attrs.push(`w:lineRule="${formatting.lineSpacingRule}"`);
55
+ if (formatting.beforeAutospacing !== void 0) attrs.push(`w:beforeAutospacing="${formatting.beforeAutospacing ? "1" : "0"}"`);
56
+ if (formatting.afterAutospacing !== void 0) attrs.push(`w:afterAutospacing="${formatting.afterAutospacing ? "1" : "0"}"`);
57
+ return attrs.length === 0 ? "" : `<w:spacing ${attrs.join(" ")}/>`;
58
+ };
59
+ const serializeIndentation = (formatting) => {
60
+ const attrs = [];
61
+ if (formatting.indentLeft !== void 0) attrs.push(`w:left="${intAttr(formatting.indentLeft)}"`);
62
+ if (formatting.indentRight !== void 0) attrs.push(`w:right="${intAttr(formatting.indentRight)}"`);
63
+ if (formatting.indentFirstLine !== void 0) {
64
+ const attribute = formatting.hangingIndent ? "hanging" : "firstLine";
65
+ const value = formatting.hangingIndent ? Math.abs(formatting.indentFirstLine) : formatting.indentFirstLine;
66
+ attrs.push(`w:${attribute}="${intAttr(value)}"`);
67
+ }
68
+ return attrs.length === 0 ? "" : `<w:ind ${attrs.join(" ")}/>`;
69
+ };
70
+ const serializeNumbering = (numPr) => {
71
+ const modeled = modelParagraphNumberingReference(numPr ?? null);
72
+ if (!modeled) return "";
73
+ const parts = [];
74
+ if (modeled.ilvl !== void 0) parts.push(`<w:ilvl w:val="${intAttr(modeled.ilvl)}"/>`);
75
+ if (modeled.numId !== void 0) parts.push(`<w:numId w:val="${intAttr(modeled.numId)}"/>`);
76
+ return parts.length === 0 ? "" : `<w:numPr>${parts.join("")}</w:numPr>`;
77
+ };
78
+ const serializeFrameProperties = (frame) => {
79
+ if (!frame) return "";
80
+ const { dropCap, lines, width, height, hSpace, vSpace, hAnchor, vAnchor, x, y, xAlign, yAlign, wrap } = frame;
81
+ const attrs = [];
82
+ if (dropCap) attrs.push(`w:dropCap="${dropCap}"`);
83
+ if (lines !== void 0) attrs.push(`w:lines="${intAttr(lines)}"`);
84
+ if (width !== void 0) attrs.push(`w:w="${intAttr(width)}"`);
85
+ if (height !== void 0) attrs.push(`w:h="${intAttr(height)}"`);
86
+ if (hSpace !== void 0) attrs.push(`w:hSpace="${intAttr(hSpace)}"`);
87
+ if (vSpace !== void 0) attrs.push(`w:vSpace="${intAttr(vSpace)}"`);
88
+ if (hAnchor) attrs.push(`w:hAnchor="${hAnchor}"`);
89
+ if (vAnchor) attrs.push(`w:vAnchor="${vAnchor}"`);
90
+ if (x !== void 0) attrs.push(`w:x="${x}"`);
91
+ if (y !== void 0) attrs.push(`w:y="${y}"`);
92
+ if (xAlign) attrs.push(`w:xAlign="${xAlign}"`);
93
+ if (yAlign) attrs.push(`w:yAlign="${yAlign}"`);
94
+ if (wrap) attrs.push(`w:wrap="${wrap}"`);
95
+ return attrs.length === 0 ? "" : `<w:framePr ${attrs.join(" ")}/>`;
96
+ };
97
+ const modelSpacingProvenance = (spacingExplicit) => {
98
+ if (spacingExplicit) {
99
+ const { before: _before, after: _after } = spacingExplicit;
100
+ }
101
+ };
102
+ const extractRunPropertiesInnerXml = (runPropertiesXml) => {
103
+ if (!runPropertiesXml.startsWith("<w:rPr>") || !runPropertiesXml.endsWith("</w:rPr>")) return "";
104
+ return runPropertiesXml.slice(7, -8);
105
+ };
106
+ /**
107
+ * Resolve paragraph formatting into its exact fallback `w:pPr` instructions.
108
+ * Every input field is classified here so parser capture fingerprints and the
109
+ * serializer cannot drift apart when paragraph formatting grows.
110
+ */
111
+ const modelParagraphFormattingEmission = (input) => {
112
+ if (!input) return {};
113
+ const { alignment, bidi, kinsoku, overflowPunctuation, spaceBefore, spaceAfter, lineSpacing, lineSpacingRule, snapToGrid, beforeAutospacing, afterAutospacing, spacingExplicit, indentLeft, indentRight, indentFirstLine, hangingIndent, borders, shading, tabs, keepNext, keepLines, widowControl, pageBreakBefore, contextualSpacing, numPr, numPrFromStyle, outlineLevel, styleId, frame, suppressLineNumbers, suppressAutoHyphens, runProperties, runInWithNext } = input;
114
+ modelSpacingProvenance(spacingExplicit);
115
+ const propertiesXml = [
116
+ styleId ? `<w:pStyle w:val="${escapeXml(styleId)}"/>` : "",
117
+ serializeToggle("keepNext", keepNext),
118
+ serializeToggle("keepLines", keepLines),
119
+ serializeToggle("pageBreakBefore", pageBreakBefore),
120
+ serializeFrameProperties(frame),
121
+ serializeToggle("widowControl", widowControl),
122
+ isStyleSourcedParagraphNumbering(numPr, numPrFromStyle) ? "" : serializeNumbering(numPr),
123
+ serializeToggle("suppressLineNumbers", suppressLineNumbers),
124
+ serializeParagraphBorders(borders),
125
+ serializeShading(shading),
126
+ serializeTabStops(tabs),
127
+ serializeToggle("suppressAutoHyphens", suppressAutoHyphens),
128
+ serializeToggle("kinsoku", kinsoku),
129
+ serializeToggle("overflowPunct", overflowPunctuation),
130
+ serializeToggle("bidi", bidi),
131
+ serializeToggle("snapToGrid", snapToGrid),
132
+ serializeSpacing({
133
+ spaceBefore,
134
+ spaceAfter,
135
+ lineSpacing,
136
+ lineSpacingRule,
137
+ beforeAutospacing,
138
+ afterAutospacing
139
+ }),
140
+ serializeIndentation({
141
+ indentLeft,
142
+ indentRight,
143
+ indentFirstLine,
144
+ hangingIndent
145
+ }),
146
+ serializeToggle("contextualSpacing", contextualSpacing),
147
+ alignment ? `<w:jc w:val="${alignment}"/>` : "",
148
+ outlineLevel !== void 0 ? `<w:outlineLvl w:val="${outlineLevel}"/>` : ""
149
+ ].join("");
150
+ const paragraphMarkPropertiesInnerXml = `${extractRunPropertiesInnerXml(serializeTextFormatting(runProperties))}${runInWithNext === true ? "<w:specVanish/>" : ""}`;
151
+ const emission = {};
152
+ if (propertiesXml) emission.propertiesXml = propertiesXml;
153
+ if (paragraphMarkPropertiesInnerXml) emission.paragraphMarkPropertiesInnerXml = paragraphMarkPropertiesInnerXml;
154
+ return emission;
155
+ };
156
+ //#endregion
157
+ export { isStyleSourcedParagraphNumbering, modelParagraphFormattingEmission, paragraphNumberingReferencesEqual };
@@ -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;
@@ -1,8 +1,8 @@
1
- import { numPrEqual } from "../../docx/numberingParser.js";
2
1
  import { PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, ParagraphPropertySourceValidationError, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphPropertyCapture, copyParagraphPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, isParagraphPropertySourceToken, linkParagraphPropertySourceCandidate, paragraphPropertySourceBelongsToDocument, paragraphPropertySourceTokenMatchesContract, recreateProseNodeWithParagraphPropertySource, restoreTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings } from "../../docx/paragraphPropertySource.js";
3
2
  import { visitDocxParagraphs } from "../../docx/paragraphTraversal.js";
4
3
  import { ShapeOutlineStyleSchema, narrowEnum } from "../../docx/parserEnums.js";
5
4
  import { DATE_UTC_ATTRIBUTE } from "../../docx/trackedChangeInfo.js";
5
+ import { isStyleSourcedParagraphNumbering, modelParagraphFormattingEmission, paragraphNumberingReferencesEqual } from "../../internal/paragraphFormattingSerialization.js";
6
6
  import { createStyleEngine } from "../../style-engine/styleEngine.js";
7
7
  import { OUTLINE_STYLE_CSS_ALIASES, normalizeShapeTextAnchor } from "../../types/documentEnumValues.js";
8
8
  import { canonicalJson } from "../../utils/canonicalJson.js";
@@ -106,9 +106,10 @@ const uniqueParagraphsById = (content, includeTransferIds = false) => {
106
106
  const restoreParagraphPropertySource = (paragraph, baseParagraph) => {
107
107
  copyParagraphPropertySource(paragraph, baseParagraph);
108
108
  const baseFormatting = baseParagraph.formatting;
109
- if (!baseFormatting?.numPr || !baseFormatting.numPrFromStyle || !numPrEqual(baseFormatting.numPr, baseFormatting.numPrFromStyle)) return;
110
- const { numPr, numPrFromStyle, ...authoredFormatting } = baseFormatting;
111
- if (canonicalJson(paragraph.formatting ?? {}) !== canonicalJson(authoredFormatting)) return;
109
+ if (!baseFormatting) return;
110
+ const { numPr, numPrFromStyle } = baseFormatting;
111
+ if (!numPr || !numPrFromStyle || !isStyleSourcedParagraphNumbering(numPr, numPrFromStyle)) return;
112
+ if (canonicalJson(modelParagraphFormattingEmission(paragraph.formatting)) !== canonicalJson(modelParagraphFormattingEmission(baseFormatting))) return;
112
113
  paragraph.formatting = {
113
114
  ...paragraph.formatting,
114
115
  numPr,
@@ -777,15 +778,6 @@ const propertyChangeFromAttrs = (change) => {
777
778
  ...currentFormatting !== void 0 && { currentFormatting }
778
779
  };
779
780
  };
780
- /**
781
- * Whether the paragraph's numbering still comes verbatim from its style —
782
- * serialize no direct `<w:numPr>` then. The moment a list command changes
783
- * `numPr` the values diverge and the numbering serializes as direct
784
- * formatting, so a stale provenance value can never swallow a user edit.
785
- */
786
- function isStyleSourcedNumPr(attrs) {
787
- return attrs.numPrFromStyle != null && attrs.numPr != null && numPrEqual(attrs.numPr, attrs.numPrFromStyle);
788
- }
789
781
  function assignBooleanToggle(result, attrs, orig, key) {
790
782
  const value = attrs[key] ?? void 0;
791
783
  if (value === (orig[key] ?? void 0)) return;
@@ -834,10 +826,10 @@ function paragraphAttrsToFormatting(attrs) {
834
826
  else Reflect.deleteProperty(result, "lineSpacingRule");
835
827
  if (directAlignment === void 0) Reflect.deleteProperty(result, "alignment");
836
828
  else result.alignment = directAlignment;
837
- if (isStyleSourcedNumPr(attrs)) {
829
+ if (isStyleSourcedParagraphNumbering(attrs.numPr, attrs.numPrFromStyle)) {
838
830
  delete result.numPr;
839
831
  delete result.numPrFromStyle;
840
- } else if (attrs.numPr !== orig.numPr && !numPrEqual(attrs.numPr, orig.numPr)) {
832
+ } else if (attrs.numPr !== orig.numPr && !paragraphNumberingReferencesEqual(attrs.numPr, orig.numPr)) {
841
833
  if (attrs.numPr) result.numPr = attrs.numPr;
842
834
  else delete result.numPr;
843
835
  delete result.numPrFromStyle;
@@ -874,7 +866,7 @@ function paragraphAttrsToFormatting(attrs) {
874
866
  if (attrs.indentRight) f.indentRight = attrs.indentRight;
875
867
  if (attrs.indentFirstLine) f.indentFirstLine = attrs.indentFirstLine;
876
868
  if (attrs.hangingIndent) f.hangingIndent = attrs.hangingIndent;
877
- if (attrs.numPr && !isStyleSourcedNumPr(attrs)) f.numPr = attrs.numPr;
869
+ if (attrs.numPr && !isStyleSourcedParagraphNumbering(attrs.numPr, attrs.numPrFromStyle)) f.numPr = attrs.numPr;
878
870
  if (attrs.styleId) f.styleId = attrs.styleId;
879
871
  if (attrs.borders) f.borders = attrs.borders;
880
872
  if (attrs.shading) f.shading = attrs.shading;
@@ -2862,6 +2854,24 @@ function tableCellAttrsToFormatting(attrs) {
2862
2854
  if (attrs.margins) f.margins = buildCellMarginsFromAttrs(attrs.margins);
2863
2855
  return f;
2864
2856
  }
2857
+ const isUnchangedSourceEmptyPlaceholder = (blocks) => {
2858
+ const paragraph = blocks.at(0);
2859
+ if (blocks.length !== 1 || paragraph?.type !== "paragraph" || paragraph.content.length !== 0) return false;
2860
+ return Object.keys(paragraph).every((key) => key === "type" || key === "content" || key === "paraId" || key === "textId");
2861
+ };
2862
+ const projectTextBoxBodyContent = (state, blocks) => {
2863
+ switch (state.type) {
2864
+ case "source-empty": return isUnchangedSourceEmptyPlaceholder(blocks) ? { type: "source-empty" } : {
2865
+ type: "authored",
2866
+ content: blocks
2867
+ };
2868
+ case "authored": return {
2869
+ type: "authored",
2870
+ content: blocks
2871
+ };
2872
+ default: return state;
2873
+ }
2874
+ };
2865
2875
  /**
2866
2876
  * Convert a ProseMirror textBox node back to a Paragraph wrapping a ShapeContent run.
2867
2877
  * The text box content becomes a Shape with textBody.
@@ -2874,6 +2884,7 @@ function convertPMTextBox(node, styleResolver = null) {
2874
2884
  if (child.type.name === "paragraph") childBlocks.push(convertPMParagraph(child, void 0, void 0, styleResolver));
2875
2885
  else if (child.type.name === "table") childBlocks.push(convertPMTable(child, void 0, styleResolver));
2876
2886
  });
2887
+ const textBodyContent = projectTextBoxBodyContent(attrs._docxTextBodyContentState, childBlocks);
2877
2888
  const shape = {
2878
2889
  type: "shape",
2879
2890
  shapeType: "textBox",
@@ -2882,10 +2893,7 @@ function convertPMTextBox(node, styleResolver = null) {
2882
2893
  height: attrs.height ? pixelsToEmu(attrs.height) : 0
2883
2894
  },
2884
2895
  textBody: {
2885
- content: childBlocks.length > 0 ? childBlocks : [{
2886
- type: "paragraph",
2887
- content: []
2888
- }],
2896
+ content: textBodyContent.type === "source-empty" ? [] : textBodyContent.content,
2889
2897
  ...attrs.autoFit !== void 0 ? { autoFit: attrs.autoFit } : {},
2890
2898
  ...attrs.textWrap !== void 0 ? { textWrap: attrs.textWrap } : {},
2891
2899
  ...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 } : {},