@stll/folio-core 0.33.1 → 0.33.2

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 (91) hide show
  1. package/dist/ai-edits/apply.d.ts +15 -0
  2. package/dist/ai-edits/apply.js +134 -25
  3. package/dist/ai-edits/headless.d.ts +47 -3
  4. package/dist/ai-edits/headless.js +53 -3
  5. package/dist/ai-edits/index.d.ts +1 -1
  6. package/dist/ai-edits/snapshot.d.ts +19 -1
  7. package/dist/ai-edits/snapshot.js +25 -3
  8. package/dist/ai-edits/table-geometry.d.ts +56 -0
  9. package/dist/ai-edits/table-geometry.js +217 -0
  10. package/dist/ai-edits/table-row-column-mutations.d.ts +34 -16
  11. package/dist/ai-edits/table-row-column-mutations.js +12 -4
  12. package/dist/ai-edits/table-template.d.ts +60 -0
  13. package/dist/ai-edits/table-template.js +176 -0
  14. package/dist/ai-edits/types.d.ts +7 -0
  15. package/dist/compare/__fixtures__/body-sequence.d.ts +61 -2
  16. package/dist/compare/__fixtures__/body-sequence.js +94 -6
  17. package/dist/compare/compare.d.ts +14 -7
  18. package/dist/compare/compare.js +136 -38
  19. package/dist/compare/plan.d.ts +31 -1
  20. package/dist/compare/plan.js +233 -66
  21. package/dist/compare/types.d.ts +9 -7
  22. package/dist/compare/types.js +7 -5
  23. package/dist/compare/verification.d.ts +31 -12
  24. package/dist/compare/verification.js +88 -17
  25. package/dist/compat/eigenpal.d.ts +4 -4
  26. package/dist/compat/eigenpal.js +2 -2
  27. package/dist/display-list/primitives.d.ts +1 -1
  28. package/dist/document-operations.d.ts +10 -1
  29. package/dist/document-operations.js +3 -2
  30. package/dist/docx/appVersionNormalization.d.ts +49 -0
  31. package/dist/docx/appVersionNormalization.js +74 -0
  32. package/dist/docx/blockContentParser.js +3 -2
  33. package/dist/docx/drawingUtils.js +4 -3
  34. package/dist/docx/paragraphParser.js +3 -2
  35. package/dist/docx/rezip.d.ts +10 -4
  36. package/dist/docx/rezip.js +54 -18
  37. package/dist/docx/runParser.js +9 -8
  38. package/dist/docx/sdtProperties.js +4 -3
  39. package/dist/docx/selectiveSave.js +7 -6
  40. package/dist/docx/serializer/commentSerializer.d.ts +1 -1
  41. package/dist/docx/serializer/commentSerializer.js +43 -19
  42. package/dist/docx/serializer/documentSerializer.d.ts +3 -1
  43. package/dist/docx/serializer/documentSerializer.js +42 -78
  44. package/dist/docx/serializer/fontTableSerializer.js +8 -5
  45. package/dist/docx/serializer/headerFooterSerializer.d.ts +4 -1
  46. package/dist/docx/serializer/headerFooterSerializer.js +34 -29
  47. package/dist/docx/serializer/noteSerializer.js +34 -30
  48. package/dist/docx/serializer/numberingSerializer.js +8 -4
  49. package/dist/docx/serializer/paragraphSerializer.js +3 -3
  50. package/dist/docx/serializer/partNamespaces.d.ts +78 -0
  51. package/dist/docx/serializer/partNamespaces.js +324 -0
  52. package/dist/docx/serializer/runSerializer.js +1 -1
  53. package/dist/docx/serializer/settingsSerializer.js +8 -2
  54. package/dist/docx/serializer/stylesSerializer.js +8 -5
  55. package/dist/docx/serializer/tableSerializer.js +83 -18
  56. package/dist/docx/serializer/themeSerializer.js +10 -2
  57. package/dist/docx/server/build.d.ts +1 -1
  58. package/dist/docx/strictValueEncodings.gen.d.ts +15 -0
  59. package/dist/docx/strictValueEncodings.gen.js +275 -0
  60. package/dist/docx/tableParser.d.ts +0 -6
  61. package/dist/docx/tableParser.js +52 -7
  62. package/dist/docx/transitionalSpelling.d.ts +23 -0
  63. package/dist/docx/transitionalSpelling.js +36 -0
  64. package/dist/docx/universalMeasure.d.ts +20 -0
  65. package/dist/docx/universalMeasure.js +32 -0
  66. package/dist/docx/verbatimCapture.d.ts +20 -0
  67. package/dist/docx/verbatimCapture.js +131 -0
  68. package/dist/docx/vmlImageParser.js +4 -3
  69. package/dist/docx/watermarkParser.js +4 -3
  70. package/dist/docx/xmlParser.d.ts +23 -2
  71. package/dist/docx/xmlParser.js +43 -11
  72. package/dist/index.d.ts +4 -4
  73. package/dist/index.js +2 -2
  74. package/dist/model.d.ts +3 -3
  75. package/dist/model.js +2 -2
  76. package/dist/prosemirror/commands/comments.js +2 -1
  77. package/dist/prosemirror/containerFinalParagraph.d.ts +47 -0
  78. package/dist/prosemirror/containerFinalParagraph.js +92 -0
  79. package/dist/prosemirror/conversion/fromProseDoc.d.ts +8 -1
  80. package/dist/prosemirror/conversion/fromProseDoc.js +15 -4
  81. package/dist/prosemirror/conversion/toProseDoc.js +12 -3
  82. package/dist/prosemirror/extensions/nodes/TableExtension.js +5 -0
  83. package/dist/prosemirror/schema/marks.d.ts +1 -1
  84. package/dist/prosemirror/schema/nodes.d.ts +22 -0
  85. package/dist/server.d.ts +1 -1
  86. package/dist/types/block-id.d.ts +12 -1
  87. package/dist/types/block-id.js +17 -1
  88. package/dist/utils/canonicalJson.d.ts +14 -0
  89. package/dist/utils/canonicalJson.js +19 -0
  90. package/dist/utils/formatToStyle.d.ts +1 -1
  91. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -26,9 +26,9 @@ import { clearTemplateSlashMenu, consumeTemplateSlashQuery, getTemplateSlashMenu
26
26
  import { extractDocumentStyleSet, extractDocumentStyleSetFromDocx, inspectDocumentStyles, inspectDocumentStylesFromDocx } from "./style-sets/extract.js";
27
27
  import { STELLA_STYLE_SET_NAME, createStellaStyleDocumentPreset, createStellaStyleSet } from "./style-sets/stellaStyle.js";
28
28
  import { DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION } from "./style-sets/types.js";
29
- import { deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
29
+ import { currentFolioBlockId, deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
30
30
  import { createEmptyDocument } from "./utils/createDocument.js";
31
31
  import { getGoogleFontsEnabled, setEmbeddedFontFamilyMap, setGoogleFontsEnabled } from "./utils/fontResolver.js";
32
32
  import { mergeDocumentContent } from "./utils/mergeDocumentContent.js";
33
33
  import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
34
- export { COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, CompareDocxApplyError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, 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, InvalidCompareDocxOptionsError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, 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 };
34
+ export { COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, CompareDocxApplyError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, 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, InvalidCompareDocxOptionsError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, 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 };
package/dist/model.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { document_d_exports } from "./types/document.js";
2
- import { DeriveBlockIdInput, FolioBlockId, deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
2
+ import { DeriveBlockIdInput, FolioBlockId, currentFolioBlockId, deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
3
3
  import { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, OutlineStyleAttr, OutlineStyleCssAlias, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TEXT_ANCHOR_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES, normalizeShapeTextAnchor } from "./types/documentEnumValues.js";
4
4
  import { types_d_exports } from "./layout-engine/types.js";
5
5
  import { measureTypes_d_exports } from "./layout-engine/measure/measureTypes.js";
6
6
  export * from "@stll/docx-core/model";
7
7
  declare namespace model_d_exports {
8
- export { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, DeriveBlockIdInput, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, FolioBlockId, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, types_d_exports as Layout, measureTypes_d_exports as Measure, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, OutlineStyleAttr, OutlineStyleCssAlias, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TEXT_ANCHOR_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES, deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId, normalizeShapeTextAnchor };
8
+ export { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, DeriveBlockIdInput, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, FolioBlockId, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, types_d_exports as Layout, measureTypes_d_exports as Measure, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, OutlineStyleAttr, OutlineStyleCssAlias, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TEXT_ANCHOR_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES, currentFolioBlockId, deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId, normalizeShapeTextAnchor };
9
9
  }
10
10
  //#endregion
11
- export { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, DeriveBlockIdInput, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, FolioBlockId, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, types_d_exports as Layout, type measureTypes_d_exports as Measure, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, OutlineStyleAttr, OutlineStyleCssAlias, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TEXT_ANCHOR_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES, deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId, normalizeShapeTextAnchor };
11
+ export { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, DeriveBlockIdInput, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, FolioBlockId, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, types_d_exports as Layout, type measureTypes_d_exports as Measure, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, OutlineStyleAttr, OutlineStyleCssAlias, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TEXT_ANCHOR_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES, currentFolioBlockId, deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId, normalizeShapeTextAnchor };
package/dist/model.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { types_exports } from "./layout-engine/types.js";
2
- import { deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
2
+ import { currentFolioBlockId, deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
3
3
  import { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TEXT_ANCHOR_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES, normalizeShapeTextAnchor } from "./types/documentEnumValues.js";
4
- export { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, types_exports as Layout, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TEXT_ANCHOR_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES, deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId, normalizeShapeTextAnchor };
4
+ export { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, types_exports as Layout, NUMBER_FORMAT_VALUES, OUTLINE_STYLE_ATTR_VALUES, OUTLINE_STYLE_CSS_ALIASES, OUTLINE_STYLE_CSS_ALIAS_VALUES, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TEXT_ANCHOR_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES, currentFolioBlockId, deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId, normalizeShapeTextAnchor };
@@ -1,4 +1,5 @@
1
1
  import { expectParagraphAttrs, expectRunPropertyChangeMarkAttrs } from "../attrs/index.js";
2
+ import { paragraphEndsItsContainer } from "../containerFinalParagraph.js";
2
3
  import { textFormattingToMarks } from "../conversion/toProseDoc.js";
3
4
  import { markStructuralChange } from "../extensions/features/ParagraphChangeTrackerExtension.js";
4
5
  import { getFolioNodeRevisionCarriers } from "../revisionCarriers.js";
@@ -157,7 +158,7 @@ function resolveChange(from, to, mode, revisionIds) {
157
158
  const resolved = tr.doc.resolve(mappedPos);
158
159
  const previous = resolved.nodeBefore;
159
160
  const sectionCanMoveBack = !carriesSection(paragraph) || previous?.type.name === paragraph.type.name && !carriesSection(previous);
160
- const endsItsContainer = resolved.index() === resolved.parent.childCount - 1;
161
+ const endsItsContainer = paragraphEndsItsContainer(resolved, paragraph.type.name);
161
162
  const canGo = op.markWasAdded || !endsItsContainer;
162
163
  if (sectionCanMoveBack && holdsNoContent(paragraph) && canGo && resolved.parent.childCount > 1) {
163
164
  if (carriesSection(paragraph) && previous) tr.setNodeMarkup(mappedPos - previous.nodeSize, void 0, {
@@ -0,0 +1,47 @@
1
+ import { Node, ResolvedPos } from "prosemirror-model";
2
+ //#region src/prosemirror/containerFinalParagraph.d.ts
3
+ /**
4
+ * Whether the paragraph starting at `at` is the one its container ENDS with,
5
+ * which is the one place a paragraph mark cannot carry a revision.
6
+ *
7
+ * A deleted mark asks a reader to join the paragraph it ends with the one
8
+ * after it, and an inserted mark says that break was added, so rejecting it
9
+ * closes the paragraph back over the next one. A body, a table cell, a header
10
+ * or footer, a note and a text box each end with a paragraph that nothing
11
+ * follows, so neither direction can be resolved there: a consumer is left with
12
+ * a revision it can only ignore, and the redline no longer resolves to the two
13
+ * documents it was written from.
14
+ *
15
+ * A paragraph before a TABLE is not one of those: the table is a following
16
+ * sibling, so the paragraph does not end anything and carries its own mark
17
+ * like any other.
18
+ */
19
+ declare const paragraphEndsItsContainer: (at: ResolvedPos, paragraphTypeName: string) => boolean;
20
+ /**
21
+ * Every paragraph a container ENDS with in `doc`, with its position.
22
+ *
23
+ * One walk for the whole document: asking the question of each paragraph in
24
+ * turn costs a scan of its siblings each time, which is quadratic in a body
25
+ * whose paragraphs a batch has just filled.
26
+ */
27
+ declare const finalParagraphsOf: (doc: Node, paragraphTypeName: string) => {
28
+ position: number;
29
+ node: Node;
30
+ }[];
31
+ /**
32
+ * The paragraph an added break can rotate BACK onto from the one at `at`: the
33
+ * paragraph the run of inserted ones was appended after.
34
+ *
35
+ * The walk crosses the inserted paragraphs — their own marks stay where they
36
+ * are, and only which paragraph is left markless changes — and stops at the
37
+ * first one whose mark is free. `null` when there is none: a table in between,
38
+ * which no mark joins across; a paragraph whose mark says something else,
39
+ * which the rotation must not overwrite; or a container whose every paragraph
40
+ * is new.
41
+ */
42
+ declare const addedBreakCarrierBefore: (at: ResolvedPos, paragraphTypeName: string) => {
43
+ position: number;
44
+ node: Node;
45
+ } | null;
46
+ //#endregion
47
+ export { addedBreakCarrierBefore, finalParagraphsOf, paragraphEndsItsContainer };
@@ -0,0 +1,92 @@
1
+ //#region src/prosemirror/containerFinalParagraph.ts
2
+ /**
3
+ * A node the FILE holds as a child of the container it appears in.
4
+ *
5
+ * The editor keeps a text box as a block-level sibling of the paragraph that
6
+ * anchors it; the file hangs it off that paragraph's own run, so it is not a
7
+ * child of the container at all. A paragraph and a table are children in both.
8
+ */
9
+ const isAContainerChild = (node, paragraphTypeName) => node.type.name === paragraphTypeName || node.type.spec["tableRole"] === "table";
10
+ /** Whether every child from `fromIndex` on is one the file does not hold here. */
11
+ const nothingFollowsIn = (parent, fromIndex, paragraphTypeName) => {
12
+ for (let index = fromIndex; index < parent.childCount; index++) if (isAContainerChild(parent.child(index), paragraphTypeName)) return false;
13
+ return true;
14
+ };
15
+ /**
16
+ * Whether the paragraph starting at `at` is the one its container ENDS with,
17
+ * which is the one place a paragraph mark cannot carry a revision.
18
+ *
19
+ * A deleted mark asks a reader to join the paragraph it ends with the one
20
+ * after it, and an inserted mark says that break was added, so rejecting it
21
+ * closes the paragraph back over the next one. A body, a table cell, a header
22
+ * or footer, a note and a text box each end with a paragraph that nothing
23
+ * follows, so neither direction can be resolved there: a consumer is left with
24
+ * a revision it can only ignore, and the redline no longer resolves to the two
25
+ * documents it was written from.
26
+ *
27
+ * A paragraph before a TABLE is not one of those: the table is a following
28
+ * sibling, so the paragraph does not end anything and carries its own mark
29
+ * like any other.
30
+ */
31
+ const paragraphEndsItsContainer = (at, paragraphTypeName) => nothingFollowsIn(at.parent, at.index() + 1, paragraphTypeName);
32
+ /**
33
+ * Every paragraph a container ENDS with in `doc`, with its position.
34
+ *
35
+ * One walk for the whole document: asking the question of each paragraph in
36
+ * turn costs a scan of its siblings each time, which is quadratic in a body
37
+ * whose paragraphs a batch has just filled.
38
+ */
39
+ const finalParagraphsOf = (doc, paragraphTypeName) => {
40
+ const found = [];
41
+ const collect = (node, contentStart) => {
42
+ let last = null;
43
+ node.forEach((child, offset) => {
44
+ const position = contentStart + offset;
45
+ if (child.type.name === paragraphTypeName) last = {
46
+ position,
47
+ node: child
48
+ };
49
+ else if (isAContainerChild(child, paragraphTypeName)) last = null;
50
+ if (!child.isTextblock && child.childCount > 0) collect(child, position + 1);
51
+ });
52
+ if (last !== null) found.push(last);
53
+ };
54
+ collect(doc, 0);
55
+ return found;
56
+ };
57
+ /** A paragraph's mark, when it says the break was ADDED. */
58
+ const marksAnAddedBreak = (node) => {
59
+ const mark = node.attrs["pPrMark"];
60
+ if (typeof mark !== "object" || mark === null || !("kind" in mark)) return false;
61
+ return mark.kind === "ins" || mark.kind === "moveTo";
62
+ };
63
+ /**
64
+ * The paragraph an added break can rotate BACK onto from the one at `at`: the
65
+ * paragraph the run of inserted ones was appended after.
66
+ *
67
+ * The walk crosses the inserted paragraphs — their own marks stay where they
68
+ * are, and only which paragraph is left markless changes — and stops at the
69
+ * first one whose mark is free. `null` when there is none: a table in between,
70
+ * which no mark joins across; a paragraph whose mark says something else,
71
+ * which the rotation must not overwrite; or a container whose every paragraph
72
+ * is new.
73
+ */
74
+ const addedBreakCarrierBefore = (at, paragraphTypeName) => {
75
+ let position = at.pos;
76
+ for (let index = at.index() - 1; index >= 0; index--) {
77
+ const sibling = at.parent.child(index);
78
+ position -= sibling.nodeSize;
79
+ if (sibling.type.name !== paragraphTypeName) {
80
+ if (isAContainerChild(sibling, paragraphTypeName)) return null;
81
+ continue;
82
+ }
83
+ if (sibling.attrs["pPrMark"] == null) return {
84
+ position,
85
+ node: sibling
86
+ };
87
+ if (!marksAnAddedBreak(sibling)) return null;
88
+ }
89
+ return null;
90
+ };
91
+ //#endregion
92
+ export { addedBreakCarrierBefore, finalParagraphsOf, paragraphEndsItsContainer };
@@ -1,4 +1,5 @@
1
1
  import { document_d_exports } from "../../types/document.js";
2
+ import { TableAttrs, TableCellAttrs, TableRowAttrs } from "../schema/nodes.js";
2
3
  import { Mark, Node } from "prosemirror-model";
3
4
  //#region src/prosemirror/conversion/fromProseDoc.d.ts
4
5
  /**
@@ -12,7 +13,13 @@ type MarksToTextFormattingOptions = {
12
13
  inheritedFormatting: document_d_exports.TextFormatting | undefined;
13
14
  };
14
15
  declare function marksToTextFormatting(marks: readonly Mark[], options?: MarksToTextFormattingOptions): document_d_exports.TextFormatting;
16
+ declare function tableAttrsToFormatting(attrs: TableAttrs): document_d_exports.TableFormatting | undefined;
17
+ /**
18
+ * Convert ProseMirror table row attrs to TableRowFormatting
19
+ */
20
+ declare function tableRowAttrsToFormatting(attrs: TableRowAttrs): document_d_exports.TableRowFormatting | undefined;
15
21
  declare const standaloneTableCellFromProseMirror: (node: Node) => document_d_exports.TableCell;
22
+ declare function tableCellAttrsToFormatting(attrs: TableCellAttrs): document_d_exports.TableCellFormatting | undefined;
16
23
  /**
17
24
  * Update a Document with content from a ProseMirror document
18
25
  * Preserves all non-content parts of the original document
@@ -27,4 +34,4 @@ declare function updateDocumentContent(originalDocument: document_d_exports.Docu
27
34
  */
28
35
  declare function proseDocToBlocks(pmDoc: Node): document_d_exports.BlockContent[];
29
36
  //#endregion
30
- export { fromProseDoc, marksToTextFormatting, proseDocToBlocks, standaloneTableCellFromProseMirror, updateDocumentContent };
37
+ export { fromProseDoc, marksToTextFormatting, proseDocToBlocks, standaloneTableCellFromProseMirror, tableAttrsToFormatting, tableCellAttrsToFormatting, tableRowAttrsToFormatting, updateDocumentContent };
@@ -1,6 +1,7 @@
1
1
  import { numPrEqual } from "../../docx/numberingParser.js";
2
2
  import { ShapeOutlineStyleSchema, narrowEnum } from "../../docx/parserEnums.js";
3
3
  import { OUTLINE_STYLE_CSS_ALIASES, normalizeShapeTextAnchor } from "../../types/documentEnumValues.js";
4
+ import { canonicalJson } from "../../utils/canonicalJson.js";
4
5
  import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
5
6
  import { pixelsToEmu } from "../../utils/units.js";
6
7
  import { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunPropertyChangeMarkAttrs, expectRunShadingMarkAttrs, expectSdtAttrs, expectShapeAttrs, expectStrikeMarkAttrs, expectSymbolAttrs, expectTabAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs } from "../attrs/index.js";
@@ -1801,6 +1802,16 @@ function buildCellMarginsFromAttrs(m) {
1801
1802
  /**
1802
1803
  * Convert ProseMirror table attrs to TableFormatting
1803
1804
  */
1805
+ /**
1806
+ * Whether a live attr still holds what the style cascade resolved to.
1807
+ *
1808
+ * `toProseDoc` seeds an attr with the EFFECTIVE value — a border a table
1809
+ * style supplied, a margin a table declared — because that is what the editor
1810
+ * renders with. A save must write only what the node itself states, or the
1811
+ * inherited value becomes a direct override that outranks the style it came
1812
+ * from on reload. The resolved companion is what tells the two apart.
1813
+ */
1814
+ const sameResolvedValue = (live, resolved) => resolved !== void 0 && resolved !== null && canonicalJson(live) === canonicalJson(resolved);
1804
1815
  function tableAttrsToFormatting(attrs) {
1805
1816
  if (attrs._originalFormatting) {
1806
1817
  const orig = attrs._originalFormatting;
@@ -1824,7 +1835,7 @@ function tableAttrsToFormatting(attrs) {
1824
1835
  type: tableWidthType ?? "dxa"
1825
1836
  };
1826
1837
  else delete result.width;
1827
- if (attrs.cellMargins) result.cellMargins = buildCellMarginsFromAttrs(attrs.cellMargins);
1838
+ if (attrs.cellMargins && !sameResolvedValue(attrs.cellMargins, attrs._resolvedCellMargins)) result.cellMargins = buildCellMarginsFromAttrs(attrs.cellMargins);
1828
1839
  return result;
1829
1840
  }
1830
1841
  const tableWidth = attrs.width;
@@ -2022,8 +2033,8 @@ function tableCellAttrsToFormatting(attrs) {
2022
2033
  if (attrs.verticalAlign !== (orig.verticalAlign ?? void 0)) if (attrs.verticalAlign) result.verticalAlign = attrs.verticalAlign;
2023
2034
  else delete result.verticalAlign;
2024
2035
  if (backgroundChanged) result.shading = cellShadingFromAttrs(attrs);
2025
- if (attrs.borders) result.borders = attrs.borders;
2026
- if (attrs.margins) result.margins = buildCellMarginsFromAttrs(attrs.margins);
2036
+ if (attrs.borders && !sameResolvedValue(attrs.borders, attrs._resolvedBorders)) result.borders = attrs.borders;
2037
+ if (attrs.margins && !sameResolvedValue(attrs.margins, attrs._resolvedMargins)) result.margins = buildCellMarginsFromAttrs(attrs.margins);
2027
2038
  if (attrs.textDirection !== (orig.textDirection ?? void 0)) if (attrs.textDirection) result.textDirection = attrs.textDirection;
2028
2039
  else delete result.textDirection;
2029
2040
  return result;
@@ -2154,4 +2165,4 @@ function proseDocToBlocks(pmDoc) {
2154
2165
  return extractBlocks(pmDoc);
2155
2166
  }
2156
2167
  //#endregion
2157
- export { fromProseDoc, marksToTextFormatting, proseDocToBlocks, standaloneTableCellFromProseMirror, updateDocumentContent };
2168
+ export { fromProseDoc, marksToTextFormatting, proseDocToBlocks, standaloneTableCellFromProseMirror, tableAttrsToFormatting, tableCellAttrsToFormatting, tableRowAttrsToFormatting, updateDocumentContent };
@@ -850,7 +850,10 @@ function convertTable(table, styleResolver, context) {
850
850
  if (table.formatting?.justification) attrs.justification = table.formatting.justification;
851
851
  if (columnWidths) attrs.columnWidths = columnWidths;
852
852
  if (table.formatting?.floating) attrs.floating = table.formatting.floating;
853
- if (cellMarginsAttr) attrs.cellMargins = cellMarginsAttr;
853
+ if (cellMarginsAttr) {
854
+ attrs.cellMargins = cellMarginsAttr;
855
+ attrs._resolvedCellMargins = cellMarginsAttr;
856
+ }
854
857
  if (table.formatting?.look) attrs.look = table.formatting.look;
855
858
  if (table.formatting?.borders) attrs.borders = table.formatting.borders;
856
859
  if (resolvedTableIndent) attrs._resolvedIndent = resolvedTableIndent;
@@ -1059,8 +1062,14 @@ function convertTableCell({ cell, styleResolver, context, isHeader, gridWidthPer
1059
1062
  if (formatting?.noWrap !== void 0) attrs.noWrap = formatting.noWrap;
1060
1063
  const hideMark = formatting?.hideMark ?? conditionalStyle?.tcPr?.hideMark;
1061
1064
  if (hideMark !== void 0) attrs.hideMark = hideMark;
1062
- if (effectiveFormatting.borders) attrs.borders = effectiveFormatting.borders;
1063
- if (effectiveFormatting.margins) attrs.margins = effectiveFormatting.margins;
1065
+ if (effectiveFormatting.borders) {
1066
+ attrs.borders = effectiveFormatting.borders;
1067
+ attrs._resolvedBorders = effectiveFormatting.borders;
1068
+ }
1069
+ if (effectiveFormatting.margins) {
1070
+ attrs.margins = effectiveFormatting.margins;
1071
+ attrs._resolvedMargins = effectiveFormatting.margins;
1072
+ }
1064
1073
  if (formatting) attrs._originalFormatting = formatting;
1065
1074
  if (cell.propertyChanges && cell.propertyChanges.length > 0) attrs.tcPrChange = [...cell.propertyChanges];
1066
1075
  if (cell.structuralChange?.type === "tableCellInsertion") attrs.cellMarker = {
@@ -151,6 +151,7 @@ const tableSpec = {
151
151
  columnWidths: { default: null },
152
152
  floating: { default: null },
153
153
  cellMargins: { default: null },
154
+ _resolvedCellMargins: { default: null },
154
155
  look: { default: null },
155
156
  borders: { default: null },
156
157
  _resolvedIndent: { default: null },
@@ -319,7 +320,9 @@ const tableCellSpec = {
319
320
  backgroundColor: { default: null },
320
321
  _resolvedBackgroundColor: { default: null },
321
322
  borders: { default: null },
323
+ _resolvedBorders: { default: null },
322
324
  margins: { default: null },
325
+ _resolvedMargins: { default: null },
323
326
  textDirection: { default: null },
324
327
  noWrap: { default: false },
325
328
  hideMark: { default: false },
@@ -375,7 +378,9 @@ const tableHeaderSpec = {
375
378
  backgroundColor: { default: null },
376
379
  _resolvedBackgroundColor: { default: null },
377
380
  borders: { default: null },
381
+ _resolvedBorders: { default: null },
378
382
  margins: { default: null },
383
+ _resolvedMargins: { default: null },
379
384
  textDirection: { default: null },
380
385
  noWrap: { default: false },
381
386
  hideMark: { default: false },
@@ -1,5 +1,5 @@
1
- import { document_d_exports } from "../../types/document.js";
2
1
  import { ShadingProperties } from "../../types/colors.js";
2
+ import { document_d_exports } from "../../types/document.js";
3
3
  //#region src/prosemirror/schema/marks.d.ts
4
4
  /**
5
5
  * Text color mark attributes
@@ -569,6 +569,13 @@ type TableAttrs = {
569
569
  look?: document_d_exports.TableLook;
570
570
  /** Table-level borders (w:tblBorders) — full BorderSpec per side */
571
571
  borders?: document_d_exports.TableBorders;
572
+ /**
573
+ * Effective default cell margins after the table-style cascade. PM-only;
574
+ * never serialized. What `cellMargins` resolves to when the table declares
575
+ * none of its own, so a save can tell a value a style supplied from one the
576
+ * table states — and stop writing the style's into the table's `w:tblPr`.
577
+ */
578
+ _resolvedCellMargins?: TableAttrs["cellMargins"];
572
579
  /** Effective table indent after style resolution. PM-only; never serialized. */
573
580
  _resolvedIndent?: NonNullable<document_d_exports.TableFormatting["indent"]>;
574
581
  /** Style-derived table justification fallback. PM-only; never serialized. */
@@ -677,6 +684,14 @@ type TableCellAttrs = {
677
684
  hideMark?: boolean;
678
685
  /** Cell borders — full BorderSpec per side (style, color, size) */
679
686
  borders?: document_d_exports.TableCellBorders;
687
+ /**
688
+ * Effective borders after the table and table-style cascade. PM-only; never
689
+ * serialized. The companion to `borders` that `_resolvedBackgroundColor` is
690
+ * to `backgroundColor`: what the cell renders with when it declares none of
691
+ * its own, so a save can tell a border a style supplied from one the cell
692
+ * states.
693
+ */
694
+ _resolvedBorders?: document_d_exports.TableCellBorders;
680
695
  /** Cell margins/padding in twips per side */
681
696
  margins?: {
682
697
  top?: number;
@@ -684,6 +699,13 @@ type TableCellAttrs = {
684
699
  left?: number;
685
700
  right?: number;
686
701
  };
702
+ /** Effective margins after the cascade. PM-only; never serialized. */
703
+ _resolvedMargins?: {
704
+ top?: number;
705
+ bottom?: number;
706
+ left?: number;
707
+ right?: number;
708
+ };
687
709
  /** Original cell formatting from DOCX for lossless round-trip serialization */
688
710
  _originalFormatting?: document_d_exports.TableCellFormatting;
689
711
  /** Tracked cell property changes (w:tcPrChange) for round-trip + accept/reject */
package/dist/server.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIComment, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditNormalization, FolioAIEditNormalizationCode, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditSnapshot, FolioAIInlineFormatting, FolioAITextRangeHandle, FolioDocumentNavigationTarget, FolioDocumentOutline, FolioDocumentOutlineEntry, FolioDocumentSection, FolioDocumentSectionHandle, FolioDocumentSectionReadResult } from "./ai-edits/types.js";
2
2
  import { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationBatchPrecondition, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationQueuedOperation, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "./document-operations.js";
3
3
  import { FolioReviewChange, FolioReviewChangeKind } from "./ai-edits/read.js";
4
- import { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsOptions, FolioApplyDocumentOperationsToStoryOptions, FolioApplyOperationsOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioDocxReviewer, FolioDocxReviewerOptions, FolioEditableDocumentStoryHandle, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, FolioReviewChangeFilter, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, FolioReviewedStory, FolioReviewedView, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, isFolioResolvedReviewedView, isFolioReviewedView } from "./ai-edits/headless.js";
5
4
  import { createFolioAITextRangeHandle, hashFolioAIBlockText, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./ai-edits/snapshot.js";
5
+ import { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsOptions, FolioApplyDocumentOperationsToStoryOptions, FolioApplyOperationsOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioDocxReviewer, FolioDocxReviewerOptions, FolioEditableDocumentStoryHandle, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, FolioReviewChangeFilter, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, FolioReviewedStory, FolioReviewedView, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, isFolioResolvedReviewedView, isFolioReviewedView } from "./ai-edits/headless.js";
6
6
  import { getFolioDocumentOutline, readFolioDocumentSection } from "./ai-edits/scoped-reading.js";
7
7
  import { DeriveBlockIdInput, FolioBlockId, deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
8
8
  import { DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DocumentPreset, DocumentStyleSet } from "./style-sets/types.js";
@@ -61,6 +61,17 @@ declare const isBlankFolioBlockId: (id: string) => boolean;
61
61
  * without an upfront brand check.
62
62
  */
63
63
  declare const getFolioParaIdFromBlockId: (id: string) => string | null;
64
+ /**
65
+ * The id a block recorded earlier answers to in the document as parsed now.
66
+ *
67
+ * A paragraph id above the 31-bit bound the schema sets is brought into range
68
+ * on parse, so an id recorded from such a paragraph before that no longer
69
+ * names it. The mapping is a pure function of the id alone, so the recorded id
70
+ * resolves to the current one with no document in hand. Sequential and blank
71
+ * ids carry no paragraph id, and an id the parser would not have mapped is
72
+ * returned as it is.
73
+ */
74
+ declare const currentFolioBlockId: (id: string) => string;
64
75
  /**
65
76
  * The 1-based document position encoded in a sequential fallback id
66
77
  * (`seq-0011` -> 11), or `null` for paraId-backed ids. The number is
@@ -76,4 +87,4 @@ declare const getSequentialFolioBlockIdIndex: (id: string) => number | null;
76
87
  */
77
88
  declare const isFolioBlockId: (value: unknown) => value is FolioBlockId;
78
89
  //#endregion
79
- export { DeriveBlockIdInput, FolioBlockId, deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId };
90
+ export { DeriveBlockIdInput, FolioBlockId, currentFolioBlockId, deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId };
@@ -1,3 +1,5 @@
1
+ import { paraIdInRange } from "../docx/paraIdRangeNormalization.js";
2
+ import { isValidHexId } from "../utils/hexId.js";
1
3
  //#region src/types/block-id.ts
2
4
  /**
3
5
  * Single source of truth for folio block ids.
@@ -91,6 +93,20 @@ const isBlankFolioBlockId = (id) => BLANK_BLOCK_ID_PATTERN.test(id);
91
93
  */
92
94
  const getFolioParaIdFromBlockId = (id) => isSequentialFolioBlockId(id) || isBlankFolioBlockId(id) ? null : id;
93
95
  /**
96
+ * The id a block recorded earlier answers to in the document as parsed now.
97
+ *
98
+ * A paragraph id above the 31-bit bound the schema sets is brought into range
99
+ * on parse, so an id recorded from such a paragraph before that no longer
100
+ * names it. The mapping is a pure function of the id alone, so the recorded id
101
+ * resolves to the current one with no document in hand. Sequential and blank
102
+ * ids carry no paragraph id, and an id the parser would not have mapped is
103
+ * returned as it is.
104
+ */
105
+ const currentFolioBlockId = (id) => {
106
+ const paraId = getFolioParaIdFromBlockId(id);
107
+ return paraId !== null && isValidHexId(paraId) ? paraIdInRange(paraId) : id;
108
+ };
109
+ /**
94
110
  * The 1-based document position encoded in a sequential fallback id
95
111
  * (`seq-0011` -> 11), or `null` for paraId-backed ids. The number is
96
112
  * the block's position in the same non-empty-block walk both the
@@ -114,4 +130,4 @@ const isFolioBlockId = (value) => {
114
130
  return true;
115
131
  };
116
132
  //#endregion
117
- export { deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId };
133
+ export { currentFolioBlockId, deriveBlankBlockId, deriveBlockId, getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex, isBlankFolioBlockId, isFolioBlockId, isSequentialFolioBlockId };
@@ -0,0 +1,14 @@
1
+ //#region src/utils/canonicalJson.d.ts
2
+ /**
3
+ * JSON with object keys in a fixed order, for comparing two values that were
4
+ * built by different code paths.
5
+ *
6
+ * `JSON.stringify` writes keys in insertion order, so two objects holding the
7
+ * same properties compare unequal whenever one was assembled in a different
8
+ * sequence — which is what happens whenever a parsed record is compared with a
9
+ * rebuilt one. `undefined` members are omitted, so an absent property and one
10
+ * explicitly set to `undefined` read alike.
11
+ */
12
+ declare const canonicalJson: (value: unknown) => string;
13
+ //#endregion
14
+ export { canonicalJson };
@@ -0,0 +1,19 @@
1
+ //#region src/utils/canonicalJson.ts
2
+ /**
3
+ * JSON with object keys in a fixed order, for comparing two values that were
4
+ * built by different code paths.
5
+ *
6
+ * `JSON.stringify` writes keys in insertion order, so two objects holding the
7
+ * same properties compare unequal whenever one was assembled in a different
8
+ * sequence — which is what happens whenever a parsed record is compared with a
9
+ * rebuilt one. `undefined` members are omitted, so an absent property and one
10
+ * explicitly set to `undefined` read alike.
11
+ */
12
+ const canonicalJson = (value) => {
13
+ if (value === null || value === void 0) return "null";
14
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
15
+ if (typeof value !== "object") return JSON.stringify(value) ?? "null";
16
+ return `{${Object.entries(value).filter(([, entry]) => entry !== void 0).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(",")}}`;
17
+ };
18
+ //#endregion
19
+ export { canonicalJson };
@@ -1,5 +1,5 @@
1
- import { document_d_exports } from "../types/document.js";
2
1
  import { ColorValue } from "../types/colors.js";
2
+ import { document_d_exports } from "../types/document.js";
3
3
  import { Properties } from "csstype";
4
4
  //#region src/utils/formatToStyle.d.ts
5
5
  type CSSProperties = Properties<string | number>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/folio-core",
3
- "version": "0.33.1",
3
+ "version": "0.33.2",
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",
@@ -115,7 +115,7 @@
115
115
  "perf": "bun scripts/profile-editor.ts"
116
116
  },
117
117
  "dependencies": {
118
- "@stll/docx-core": "^0.19.0",
118
+ "@stll/docx-core": "^0.19.1",
119
119
  "@stll/docx-utils": "^0.1.0",
120
120
  "@stll/template-conditions": "^0.1.0",
121
121
  "better-result": "3.0.1",