@stll/folio-core 0.22.2 → 0.23.0

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 (62) hide show
  1. package/dist/ai-edits/apply.d.ts +1 -1
  2. package/dist/controller/layoutPipeline.js +28 -0
  3. package/dist/document-operations.d.ts +4 -4
  4. package/dist/document-operations.js +17 -1
  5. package/dist/docx/imageParser.js +6 -9
  6. package/dist/docx/rezip.d.ts +1 -1
  7. package/dist/docx/rezip.js +12 -31
  8. package/dist/docx/server/inspectDocxPackage.js +8 -7
  9. package/dist/docx/styleParser.js +16 -16
  10. package/dist/docx/xmlParser.d.ts +7 -3
  11. package/dist/docx/xmlParser.js +20 -7
  12. package/dist/layout-bridge/convert/toFlowBlocks.js +35 -61
  13. package/dist/layout-engine/measure/measureHelpers.d.ts +2 -1
  14. package/dist/layout-engine/measure/measureHelpers.js +5 -2
  15. package/dist/layout-engine/types.d.ts +6 -0
  16. package/dist/layout-painter/anchoredImagePosition.d.ts +2 -1
  17. package/dist/layout-painter/anchoredImagePosition.js +14 -6
  18. package/dist/layout-painter/imageLayout.d.ts +1 -1
  19. package/dist/layout-painter/renderPage.js +11 -1
  20. package/dist/layout-painter/renderParagraph.js +3 -4
  21. package/dist/managers/AutoSaveManager.js +8 -40
  22. package/dist/managers/TableSelectionManager.d.ts +1 -1
  23. package/dist/managers/autoSaveCodec.d.ts +17 -0
  24. package/dist/managers/autoSaveCodec.js +109 -0
  25. package/dist/prosemirror/attrs/index.js +278 -29
  26. package/dist/prosemirror/commands/comments.js +3 -3
  27. package/dist/prosemirror/commands/formatting.js +19 -19
  28. package/dist/prosemirror/commands/index.d.ts +2 -2
  29. package/dist/prosemirror/commands/paragraph.js +32 -32
  30. package/dist/prosemirror/commands/propertyChangeScope.d.ts +5 -3
  31. package/dist/prosemirror/commands/propertyChangeScope.js +9 -8
  32. package/dist/prosemirror/commands/table.d.ts +10 -52
  33. package/dist/prosemirror/commands/table.js +34 -34
  34. package/dist/prosemirror/conversion/fromProseDoc.js +33 -20
  35. package/dist/prosemirror/conversion/sdtAttrs.d.ts +2 -1
  36. package/dist/prosemirror/conversion/sdtAttrs.js +9 -4
  37. package/dist/prosemirror/conversion/toProseDoc.js +1 -0
  38. package/dist/prosemirror/extensions/ExtensionManager.d.ts +6 -3
  39. package/dist/prosemirror/extensions/ExtensionManager.js +5 -3
  40. package/dist/prosemirror/extensions/core/ParagraphExtension.d.ts +1 -1
  41. package/dist/prosemirror/extensions/core/ParagraphExtension.js +30 -3
  42. package/dist/prosemirror/extensions/features/ListExtension.js +4 -4
  43. package/dist/prosemirror/extensions/marks/UnderlineExtension.js +9 -7
  44. package/dist/prosemirror/extensions/marks/markUtils.d.ts +2 -1
  45. package/dist/prosemirror/extensions/marks/markUtils.js +92 -16
  46. package/dist/prosemirror/extensions/nodes/ShapeExtension.d.ts +1 -1
  47. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +1 -1
  48. package/dist/prosemirror/extensions/types.d.ts +134 -1
  49. package/dist/prosemirror/index.d.ts +2 -2
  50. package/dist/prosemirror/plugins/selectionTracker.js +25 -94
  51. package/dist/prosemirror/revisionCarriers.js +3 -1
  52. package/dist/prosemirror/schema/index.d.ts +2 -2
  53. package/dist/prosemirror/schema/index.js +87 -0
  54. package/dist/prosemirror/schema/nodes.d.ts +14 -2
  55. package/dist/prosemirror/selectionMarks.d.ts +11 -0
  56. package/dist/prosemirror/selectionMarks.js +19 -0
  57. package/dist/prosemirror/selectionState.d.ts +11 -5
  58. package/dist/prosemirror/selectionState.js +99 -74
  59. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  60. package/dist/utils/formatToStyle.js +1 -1
  61. package/dist/utils/tableOperations.d.ts +7 -6
  62. package/package.json +3 -3
@@ -27,6 +27,7 @@ const PPR_CHANGE_SCOPED_ATTR_KEYS = [
27
27
  "spaceAfter",
28
28
  "lineSpacing",
29
29
  "lineSpacingRule",
30
+ "lineSpacingExplicit",
30
31
  "snapToGrid",
31
32
  "spacingExplicit",
32
33
  "indentLeft",
@@ -111,7 +112,7 @@ function paragraphRejectAttrPatch(previousFormatting) {
111
112
  const prev = previousFormatting ?? {};
112
113
  const patch = {};
113
114
  for (const key of PPR_CHANGE_SCOPED_ATTR_KEYS) patch[key] = Object.hasOwn(prev, key) ? prev[key] ?? null : null;
114
- if (!Object.hasOwn(prev, "direction")) patch["direction"] = directionFromBidi(prev["bidi"]);
115
+ if (!Object.hasOwn(prev, "direction")) patch["direction"] = directionFromBidi(prev.bidi);
115
116
  if (!Object.hasOwn(prev, "_autospacingBase")) patch["_autospacingBase"] = autospacingBaseFromFormatting(prev);
116
117
  for (const [key, value] of Object.entries(prev)) {
117
118
  if (PPR_CHANGE_SCOPED_ATTR_KEY_SET.has(key) || PPR_PARSER_ONLY_KEYS.has(key)) continue;
@@ -131,21 +132,21 @@ function paragraphRejectOriginalFormatting(previousFormatting, liveOriginal) {
131
132
  const result = {};
132
133
  for (const key of PPR_CHANGE_SCOPED_FORMATTING_KEYS) {
133
134
  const value = Object.hasOwn(prev, key) ? prev[key] : void 0;
134
- if (value != null) result[key] = value;
135
+ if (value != null) Object.assign(result, { [key]: value });
135
136
  }
136
137
  const live = isRecord(liveOriginal) ? liveOriginal : {};
137
- if (live["runProperties"] != null) result["runProperties"] = live["runProperties"];
138
- if (live["runInWithNext"] != null) result["runInWithNext"] = live["runInWithNext"];
138
+ if (live["runProperties"] != null) Object.assign(result, { runProperties: live["runProperties"] });
139
+ if (live["runInWithNext"] != null) Object.assign(result, { runInWithNext: live["runInWithNext"] });
139
140
  if (Object.keys(result).length === 0) return null;
140
141
  return result;
141
142
  }
142
143
  function autospacingBaseFromFormatting(prev) {
143
- const before = prev["beforeAutospacing"] === true;
144
- const after = prev["afterAutospacing"] === true;
144
+ const before = prev.beforeAutospacing === true;
145
+ const after = prev.afterAutospacing === true;
145
146
  if (!before && !after) return null;
146
147
  const base = {};
147
- if (before) setAutospacingBaseValue(base, "before", prev["spaceBefore"]);
148
- if (after) setAutospacingBaseValue(base, "after", prev["spaceAfter"]);
148
+ if (before) setAutospacingBaseValue(base, "before", prev.spaceBefore);
149
+ if (after) setAutospacingBaseValue(base, "after", prev.spaceAfter);
149
150
  return base;
150
151
  }
151
152
  /**
@@ -1,4 +1,6 @@
1
+ import { TablePropertiesCommand } from "../../utils/tableOperations.js";
1
2
  import { BorderPreset, TableBorderPreset, TableContextInfo, getTableContext, isInTable as isInTableCell } from "../extensions/nodes/TableExtension.js";
3
+ import { TableBorderCommandSpec, TableCellBorderCommandSpec, TableCellMarginsCommand, TableStyleCommand } from "../extensions/types.js";
2
4
  import { EditorState, Transaction } from "prosemirror-state";
3
5
  //#region src/prosemirror/commands/table.d.ts
4
6
  declare function insertTable(rows: number, cols: number): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
@@ -14,67 +16,23 @@ declare function selectRow(state: EditorState, dispatch?: (tr: Transaction) => v
14
16
  declare function selectColumn(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;
15
17
  declare function mergeCells(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;
16
18
  declare function splitCell(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;
17
- declare function setCellBorder(side: "top" | "bottom" | "left" | "right" | "all", spec: {
18
- style: string;
19
- size?: number;
20
- color?: {
21
- rgb: string;
22
- };
23
- } | null, clearOthers?: boolean): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
19
+ declare function setCellBorder(side: "top" | "bottom" | "left" | "right" | "all", spec: TableCellBorderCommandSpec | null, clearOthers?: boolean): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
24
20
  declare function setTableBorderPreset(preset: TableBorderPreset): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
25
- declare function setTableBorders(preset: BorderPreset, borderSpec?: {
26
- style: string;
27
- size: number;
28
- color: {
29
- rgb: string;
30
- };
31
- }): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
21
+ declare function setTableBorders(preset: BorderPreset, borderSpec?: TableBorderCommandSpec): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
32
22
  declare function removeTableBorders(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;
33
- declare function setAllTableBorders(state: EditorState, dispatch?: (tr: Transaction) => void, borderSpec?: {
34
- style: string;
35
- size: number;
36
- color: {
37
- rgb: string;
38
- };
39
- }): boolean;
40
- declare function setOutsideTableBorders(state: EditorState, dispatch?: (tr: Transaction) => void, borderSpec?: {
41
- style: string;
42
- size: number;
43
- color: {
44
- rgb: string;
45
- };
46
- }): boolean;
47
- declare function setInsideTableBorders(state: EditorState, dispatch?: (tr: Transaction) => void, borderSpec?: {
48
- style: string;
49
- size: number;
50
- color: {
51
- rgb: string;
52
- };
53
- }): boolean;
23
+ declare function setAllTableBorders(state: EditorState, dispatch?: (tr: Transaction) => void, borderSpec?: TableBorderCommandSpec): boolean;
24
+ declare function setOutsideTableBorders(state: EditorState, dispatch?: (tr: Transaction) => void, borderSpec?: TableBorderCommandSpec): boolean;
25
+ declare function setInsideTableBorders(state: EditorState, dispatch?: (tr: Transaction) => void, borderSpec?: TableBorderCommandSpec): boolean;
54
26
  declare function setCellVerticalAlign(align: "top" | "center" | "bottom"): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
55
- declare function setCellMargins(margins: {
56
- top?: number;
57
- bottom?: number;
58
- left?: number;
59
- right?: number;
60
- }): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
27
+ declare function setCellMargins(margins: TableCellMarginsCommand): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
61
28
  declare function setCellTextDirection(direction: string | null): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
62
29
  declare function toggleNoWrap(): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
63
30
  declare function setRowHeight(height: number | null, rule?: "auto" | "atLeast" | "exact"): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
64
31
  declare function toggleHeaderRow(): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
65
32
  declare function distributeColumns(): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
66
33
  declare function autoFitContents(): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
67
- declare function setTableProperties(props: {
68
- width?: number | null;
69
- widthType?: string | null;
70
- justification?: "left" | "center" | "right" | null;
71
- }): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
72
- declare function applyTableStyle(styleData: {
73
- styleId: string;
74
- tableBorders?: Record<string, unknown>;
75
- conditionals?: Record<string, unknown>;
76
- look?: Record<string, boolean>;
77
- }): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
34
+ declare function setTableProperties(props: TablePropertiesCommand): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
35
+ declare function applyTableStyle(styleData: TableStyleCommand): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
78
36
  declare function setCellFillColor(color: string | null): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
79
37
  declare function setTableBorderColor(color: string): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
80
38
  declare function setTableBorderWidth(size: number): (state: EditorState, dispatch?: (tr: Transaction) => void) => boolean;
@@ -1,105 +1,105 @@
1
1
  import { getTableContext, isInTable as isInTableCell } from "../extensions/nodes/TableExtension.js";
2
2
  import { singletonManager } from "../schema/index.js";
3
3
  //#region src/prosemirror/commands/table.ts
4
- const cmds = singletonManager.getCommands();
4
+ const cmds = singletonManager;
5
5
  function insertTable(rows, cols) {
6
- return cmds["insertTable"](rows, cols);
6
+ return cmds.requireCommand("insertTable")(rows, cols);
7
7
  }
8
8
  function addRowAbove(state, dispatch) {
9
- return cmds["addRowAbove"]()(state, dispatch);
9
+ return cmds.requireCommand("addRowAbove")()(state, dispatch);
10
10
  }
11
11
  function addRowBelow(state, dispatch) {
12
- return cmds["addRowBelow"]()(state, dispatch);
12
+ return cmds.requireCommand("addRowBelow")()(state, dispatch);
13
13
  }
14
14
  function deleteRow(state, dispatch) {
15
- return cmds["deleteRow"]()(state, dispatch);
15
+ return cmds.requireCommand("deleteRow")()(state, dispatch);
16
16
  }
17
17
  function addColumnLeft(state, dispatch) {
18
- return cmds["addColumnLeft"]()(state, dispatch);
18
+ return cmds.requireCommand("addColumnLeft")()(state, dispatch);
19
19
  }
20
20
  function addColumnRight(state, dispatch) {
21
- return cmds["addColumnRight"]()(state, dispatch);
21
+ return cmds.requireCommand("addColumnRight")()(state, dispatch);
22
22
  }
23
23
  function deleteColumn(state, dispatch) {
24
- return cmds["deleteColumn"]()(state, dispatch);
24
+ return cmds.requireCommand("deleteColumn")()(state, dispatch);
25
25
  }
26
26
  function deleteTable(state, dispatch) {
27
- return cmds["deleteTable"]()(state, dispatch);
27
+ return cmds.requireCommand("deleteTable")()(state, dispatch);
28
28
  }
29
29
  function selectTable(state, dispatch) {
30
- return cmds["selectTable"]()(state, dispatch);
30
+ return cmds.requireCommand("selectTable")()(state, dispatch);
31
31
  }
32
32
  function selectRow(state, dispatch) {
33
- return cmds["selectRow"]()(state, dispatch);
33
+ return cmds.requireCommand("selectRow")()(state, dispatch);
34
34
  }
35
35
  function selectColumn(state, dispatch) {
36
- return cmds["selectColumn"]()(state, dispatch);
36
+ return cmds.requireCommand("selectColumn")()(state, dispatch);
37
37
  }
38
38
  function mergeCells(state, dispatch) {
39
- return cmds["mergeCells"]()(state, dispatch);
39
+ return cmds.requireCommand("mergeCells")()(state, dispatch);
40
40
  }
41
41
  function splitCell(state, dispatch) {
42
- return cmds["splitCell"]()(state, dispatch);
42
+ return cmds.requireCommand("splitCell")()(state, dispatch);
43
43
  }
44
44
  function setCellBorder(side, spec, clearOthers) {
45
- return cmds["setCellBorder"](side, spec, clearOthers);
45
+ return cmds.requireCommand("setCellBorder")(side, spec, clearOthers);
46
46
  }
47
47
  function setTableBorderPreset(preset) {
48
- return cmds["setTableBorderPreset"](preset);
48
+ return cmds.requireCommand("setTableBorderPreset")(preset);
49
49
  }
50
50
  function setTableBorders(preset, borderSpec) {
51
- return cmds["setTableBorders"](preset, borderSpec);
51
+ return cmds.requireCommand("setTableBorders")(preset, borderSpec);
52
52
  }
53
53
  function removeTableBorders(state, dispatch) {
54
- return cmds["removeTableBorders"]()(state, dispatch);
54
+ return cmds.requireCommand("removeTableBorders")()(state, dispatch);
55
55
  }
56
56
  function setAllTableBorders(state, dispatch, borderSpec) {
57
- return cmds["setAllTableBorders"](borderSpec)(state, dispatch);
57
+ return cmds.requireCommand("setAllTableBorders")(borderSpec)(state, dispatch);
58
58
  }
59
59
  function setOutsideTableBorders(state, dispatch, borderSpec) {
60
- return cmds["setOutsideTableBorders"](borderSpec)(state, dispatch);
60
+ return cmds.requireCommand("setOutsideTableBorders")(borderSpec)(state, dispatch);
61
61
  }
62
62
  function setInsideTableBorders(state, dispatch, borderSpec) {
63
- return cmds["setInsideTableBorders"](borderSpec)(state, dispatch);
63
+ return cmds.requireCommand("setInsideTableBorders")(borderSpec)(state, dispatch);
64
64
  }
65
65
  function setCellVerticalAlign(align) {
66
- return cmds["setCellVerticalAlign"](align);
66
+ return cmds.requireCommand("setCellVerticalAlign")(align);
67
67
  }
68
68
  function setCellMargins(margins) {
69
- return cmds["setCellMargins"](margins);
69
+ return cmds.requireCommand("setCellMargins")(margins);
70
70
  }
71
71
  function setCellTextDirection(direction) {
72
- return cmds["setCellTextDirection"](direction);
72
+ return cmds.requireCommand("setCellTextDirection")(direction);
73
73
  }
74
74
  function toggleNoWrap() {
75
- return cmds["toggleNoWrap"]();
75
+ return cmds.requireCommand("toggleNoWrap")();
76
76
  }
77
77
  function setRowHeight(height, rule) {
78
- return cmds["setRowHeight"](height, rule);
78
+ return cmds.requireCommand("setRowHeight")(height, rule);
79
79
  }
80
80
  function toggleHeaderRow() {
81
- return cmds["toggleHeaderRow"]();
81
+ return cmds.requireCommand("toggleHeaderRow")();
82
82
  }
83
83
  function distributeColumns() {
84
- return cmds["distributeColumns"]();
84
+ return cmds.requireCommand("distributeColumns")();
85
85
  }
86
86
  function autoFitContents() {
87
- return cmds["autoFitContents"]();
87
+ return cmds.requireCommand("autoFitContents")();
88
88
  }
89
89
  function setTableProperties(props) {
90
- return cmds["setTableProperties"](props);
90
+ return cmds.requireCommand("setTableProperties")(props);
91
91
  }
92
92
  function applyTableStyle(styleData) {
93
- return cmds["applyTableStyle"](styleData);
93
+ return cmds.requireCommand("applyTableStyle")(styleData);
94
94
  }
95
95
  function setCellFillColor(color) {
96
- return cmds["setCellFillColor"](color);
96
+ return cmds.requireCommand("setCellFillColor")(color);
97
97
  }
98
98
  function setTableBorderColor(color) {
99
- return cmds["setTableBorderColor"](color);
99
+ return cmds.requireCommand("setTableBorderColor")(color);
100
100
  }
101
101
  function setTableBorderWidth(size) {
102
- return cmds["setTableBorderWidth"](size);
102
+ return cmds.requireCommand("setTableBorderWidth")(size);
103
103
  }
104
104
  //#endregion
105
105
  export { addColumnLeft, addColumnRight, addRowAbove, addRowBelow, applyTableStyle, autoFitContents, deleteColumn, deleteRow, deleteTable, distributeColumns, getTableContext, insertTable, isInTableCell as isInTable, mergeCells, removeTableBorders, selectColumn, selectRow, selectTable, setAllTableBorders, setCellBorder, setCellFillColor, setCellMargins, setCellTextDirection, setCellVerticalAlign, setInsideTableBorders, setOutsideTableBorders, setRowHeight, setTableBorderColor, setTableBorderPreset, setTableBorderWidth, setTableBorders, setTableProperties, splitCell, toggleHeaderRow, toggleNoWrap };
@@ -9,7 +9,7 @@ import { RUN_FORMATTING_MARK_NAMES } from "../runFormattingMarkNames.js";
9
9
  import { expectTextBoxAnchorAttrs } from "../textBoxAnchorAttrs.js";
10
10
  import { assertValidProseMirrorDocument } from "../validation.js";
11
11
  import { runShadingAttrsToShading } from "./runShadingMark.js";
12
- import { sdtPropertiesFromAttrs, sdtPropertiesMatchAttrs } from "./sdtAttrs.js";
12
+ import { decodeSdtListItems, sdtPropertiesFromAttrs, sdtPropertiesMatchAttrs } from "./sdtAttrs.js";
13
13
  import { textFormattingToMarks } from "./toProseDoc.js";
14
14
  import { Fragment } from "prosemirror-model";
15
15
  //#region src/prosemirror/conversion/fromProseDoc.ts
@@ -248,10 +248,7 @@ function convertPMBlockSdt(node) {
248
248
  if (attrs.showingPlaceholder !== void 0) properties.showingPlaceholder = attrs.showingPlaceholder;
249
249
  if (attrs.dateFormat) properties.dateFormat = attrs.dateFormat;
250
250
  if (attrs.dateValueISO) properties.dateValueISO = attrs.dateValueISO;
251
- if (attrs.listItems) {
252
- const items = parseListItemsJson(attrs.listItems);
253
- if (items) properties.listItems = items;
254
- }
251
+ if (attrs.listItems) properties.listItems = decodeSdtListItems(attrs.listItems);
255
252
  if (typeof attrs.dropdownLastValue === "string") properties.dropdownLastValue = attrs.dropdownLastValue;
256
253
  if (typeof attrs.checked === "boolean") properties.checked = attrs.checked;
257
254
  if (attrs.rawPropertiesXml) properties.rawPropertiesXml = attrs.rawPropertiesXml;
@@ -276,17 +273,6 @@ function isStillSyntheticFiller(blocks) {
276
273
  if (block.pPrMark !== void 0) return false;
277
274
  return true;
278
275
  }
279
- function parseListItemsJson(raw) {
280
- try {
281
- const parsed = JSON.parse(raw);
282
- if (!Array.isArray(parsed)) return;
283
- const items = [];
284
- for (const entry of parsed) if (entry !== null && typeof entry === "object" && "displayText" in entry && "value" in entry && typeof entry.displayText === "string" && typeof entry.value === "string") items.push(entry);
285
- return items.length > 0 ? items : void 0;
286
- } catch {
287
- return;
288
- }
289
- }
290
276
  function appendTextBoxBlock(blocks, node, options) {
291
277
  const attrs = expectTextBoxAttrs(node);
292
278
  const paragraph = convertPMTextBox(node);
@@ -513,10 +499,27 @@ function convertPMParagraph(node, documentCounts, textBoxAnchorMarkers) {
513
499
  if (attrs.renderedPageBreakBefore) paragraph.renderedPageBreakBefore = true;
514
500
  if (attrs._sectionProperties) paragraph.sectionProperties = attrs._sectionProperties;
515
501
  else if (attrs.sectionBreakType) paragraph.sectionProperties = { sectionStart: attrs.sectionBreakType };
516
- if (Array.isArray(attrs._propertyChanges) && attrs._propertyChanges.length > 0) paragraph.propertyChanges = [...attrs._propertyChanges];
502
+ if (attrs._propertyChanges && attrs._propertyChanges.length > 0) paragraph.propertyChanges = attrs._propertyChanges.map(propertyChangeFromAttrs);
517
503
  if (attrs.pPrMark) paragraph.pPrMark = attrs.pPrMark;
518
504
  return paragraph;
519
505
  }
506
+ const propertyChangeFromAttrs = (change) => {
507
+ const { previousFormatting, currentFormatting, ...info } = change;
508
+ if (previousFormatting === void 0) return currentFormatting === void 0 ? info : {
509
+ ...info,
510
+ currentFormatting
511
+ };
512
+ const { numPr, ...previousWithoutNumPr } = previousFormatting;
513
+ const normalizedPrevious = numPr === null || numPr === void 0 ? previousWithoutNumPr : {
514
+ ...previousWithoutNumPr,
515
+ numPr
516
+ };
517
+ return {
518
+ ...info,
519
+ previousFormatting: normalizedPrevious,
520
+ ...currentFormatting !== void 0 && { currentFormatting }
521
+ };
522
+ };
520
523
  /**
521
524
  * Whether the paragraph's numbering still comes verbatim from its style —
522
525
  * serialize no direct `<w:numPr>` then. The moment a list command changes
@@ -543,6 +546,7 @@ function paragraphAttrsToFormatting(attrs) {
543
546
  const afterAutospacingEdited = afterHasAutospacingBase ? !autospacingMatchesBase(attrs._autospacingBase, "after", spaceAfter) : afterOriginalAutospacing && attrs._autospacingBase == null;
544
547
  const shouldSerializeSpaceBefore = typeof spaceBefore === "number" && (!beforeHasAutospacingBase || beforeAutospacingEdited);
545
548
  const shouldSerializeSpaceAfter = typeof spaceAfter === "number" && (!afterHasAutospacingBase || afterAutospacingEdited);
549
+ const hasDirectLineSpacing = attrs.lineSpacingExplicit === true;
546
550
  if (attrs._originalFormatting) {
547
551
  const orig = attrs._originalFormatting;
548
552
  const result = { ...orig };
@@ -556,6 +560,15 @@ function paragraphAttrsToFormatting(attrs) {
556
560
  if (typeof spaceAfter === "number") result.spaceAfter = spaceAfter;
557
561
  else delete result.spaceAfter;
558
562
  }
563
+ if (attrs.spacingExplicit?.before && typeof spaceBefore === "number") result.spaceBefore = spaceBefore;
564
+ if (attrs.spacingExplicit?.after && typeof spaceAfter === "number") result.spaceAfter = spaceAfter;
565
+ const originalHasDirectLineSpacing = orig.lineSpacing !== void 0 || orig.lineSpacingRule !== void 0;
566
+ if (hasDirectLineSpacing || originalHasDirectLineSpacing) {
567
+ if (typeof attrs.lineSpacing === "number") result.lineSpacing = attrs.lineSpacing;
568
+ else Reflect.deleteProperty(result, "lineSpacing");
569
+ if (attrs.lineSpacingRule) result.lineSpacingRule = attrs.lineSpacingRule;
570
+ else Reflect.deleteProperty(result, "lineSpacingRule");
571
+ }
559
572
  if (attrs.alignment !== (orig.alignment ?? void 0)) if (attrs.alignment) result.alignment = attrs.alignment;
560
573
  else delete result.alignment;
561
574
  if (isStyleSourcedNumPr(attrs)) {
@@ -583,15 +596,15 @@ function paragraphAttrsToFormatting(attrs) {
583
596
  }
584
597
  const outlineLevel = Reflect.get(attrs, "outlineLevel");
585
598
  const bidi = directionToBidi(attrs.direction);
586
- if (!(attrs.alignment || shouldSerializeSpaceBefore || shouldSerializeSpaceAfter || beforeAutospacingEdited || afterAutospacingEdited || attrs.lineSpacing || attrs.snapToGrid != null || attrs.indentLeft || attrs.indentRight || attrs.indentFirstLine || attrs.numPr || attrs.styleId || attrs.borders || attrs.shading || attrs.tabs || typeof outlineLevel === "number" || attrs.contextualSpacing || attrs.spacingExplicit || bidi != null || attrs.pageBreakBefore != null || attrs.widowControl != null || attrs.kinsoku != null || attrs.overflowPunctuation != null || attrs.suppressAutoHyphens != null)) return;
599
+ if (!(attrs.alignment || shouldSerializeSpaceBefore || shouldSerializeSpaceAfter || beforeAutospacingEdited || afterAutospacingEdited || hasDirectLineSpacing || attrs.snapToGrid != null || attrs.indentLeft || attrs.indentRight || attrs.indentFirstLine || attrs.numPr || attrs.styleId || attrs.borders || attrs.shading || attrs.tabs || typeof outlineLevel === "number" || attrs.contextualSpacing || attrs.spacingExplicit || bidi != null || attrs.pageBreakBefore != null || attrs.widowControl != null || attrs.kinsoku != null || attrs.overflowPunctuation != null || attrs.suppressAutoHyphens != null)) return;
587
600
  const f = {};
588
601
  if (attrs.alignment) f.alignment = attrs.alignment;
589
602
  if (shouldSerializeSpaceBefore) f.spaceBefore = spaceBefore;
590
603
  if (beforeAutospacingEdited) f.beforeAutospacing = false;
591
604
  if (shouldSerializeSpaceAfter) f.spaceAfter = spaceAfter;
592
605
  if (afterAutospacingEdited) f.afterAutospacing = false;
593
- if (attrs.lineSpacing) f.lineSpacing = attrs.lineSpacing;
594
- if (attrs.lineSpacingRule) f.lineSpacingRule = attrs.lineSpacingRule;
606
+ if (hasDirectLineSpacing && typeof attrs.lineSpacing === "number") f.lineSpacing = attrs.lineSpacing;
607
+ if (hasDirectLineSpacing && attrs.lineSpacingRule) f.lineSpacingRule = attrs.lineSpacingRule;
595
608
  if (attrs.snapToGrid != null) f.snapToGrid = attrs.snapToGrid;
596
609
  if (attrs.spacingExplicit) f.spacingExplicit = attrs.spacingExplicit;
597
610
  if (attrs.indentLeft) f.indentLeft = attrs.indentLeft;
@@ -4,5 +4,6 @@ import { SdtAttrs } from "../schema/nodes.js";
4
4
  declare const sdtAttrsFromProperties: (properties: document_d_exports.SdtProperties) => SdtAttrs;
5
5
  declare const sdtPropertiesFromAttrs: (attrs: SdtAttrs) => document_d_exports.SdtProperties;
6
6
  declare const sdtPropertiesMatchAttrs: (properties: document_d_exports.SdtProperties, attrs: SdtAttrs) => boolean;
7
+ declare const decodeSdtListItems: (rawItems: string) => NonNullable<document_d_exports.SdtProperties["listItems"]>;
7
8
  //#endregion
8
- export { sdtAttrsFromProperties, sdtPropertiesFromAttrs, sdtPropertiesMatchAttrs };
9
+ export { decodeSdtListItems, sdtAttrsFromProperties, sdtPropertiesFromAttrs, sdtPropertiesMatchAttrs };
@@ -25,7 +25,7 @@ const sdtPropertiesFromAttrs = (attrs) => {
25
25
  if (attrs.showingPlaceholder !== void 0) properties.showingPlaceholder = attrs.showingPlaceholder;
26
26
  if (attrs.dateFormat) properties.dateFormat = attrs.dateFormat;
27
27
  if (attrs.dateValueISO) properties.dateValueISO = attrs.dateValueISO;
28
- if (attrs.listItems) properties.listItems = parseSdtListItems(attrs.listItems);
28
+ if (attrs.listItems) properties.listItems = decodeSdtListItems(attrs.listItems);
29
29
  if (typeof attrs.dropdownLastValue === "string") properties.dropdownLastValue = attrs.dropdownLastValue;
30
30
  if (typeof attrs.checked === "boolean") properties.checked = attrs.checked;
31
31
  if (attrs.rawPropertiesXml) properties.rawPropertiesXml = attrs.rawPropertiesXml;
@@ -33,8 +33,13 @@ const sdtPropertiesFromAttrs = (attrs) => {
33
33
  return properties;
34
34
  };
35
35
  const sdtPropertiesMatchAttrs = (properties, attrs) => JSON.stringify(sdtAttrsFromProperties(properties)) === JSON.stringify(sdtAttrsFromProperties(sdtPropertiesFromAttrs(attrs)));
36
- const parseSdtListItems = (rawItems) => {
37
- const parsed = JSON.parse(rawItems);
36
+ const decodeSdtListItems = (rawItems) => {
37
+ let parsed;
38
+ try {
39
+ parsed = JSON.parse(rawItems);
40
+ } catch {
41
+ throw new TypeError("Invalid ProseMirror sdt attrs: listItems is not valid JSON");
42
+ }
38
43
  if (!Array.isArray(parsed)) throw new TypeError("Invalid ProseMirror sdt attrs: listItems is not an array");
39
44
  return parsed.map((item) => {
40
45
  if (!item || typeof item !== "object" || Array.isArray(item)) throw new TypeError("Invalid ProseMirror sdt attrs: listItems contains an invalid item");
@@ -46,4 +51,4 @@ const parseSdtListItems = (rawItems) => {
46
51
  });
47
52
  };
48
53
  //#endregion
49
- export { sdtAttrsFromProperties, sdtPropertiesFromAttrs, sdtPropertiesMatchAttrs };
54
+ export { decodeSdtListItems, sdtAttrsFromProperties, sdtPropertiesFromAttrs, sdtPropertiesMatchAttrs };
@@ -319,6 +319,7 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
319
319
  set("spaceAfter", formatting?.spaceAfter ?? stylePpr?.spaceAfter);
320
320
  set("lineSpacing", formatting?.lineSpacing ?? stylePpr?.lineSpacing);
321
321
  set("lineSpacingRule", formatting?.lineSpacingRule ?? stylePpr?.lineSpacingRule);
322
+ set("lineSpacingExplicit", formatting?.lineSpacing !== void 0 || formatting?.lineSpacingRule !== void 0 ? true : void 0);
322
323
  set("snapToGrid", formatting?.snapToGrid ?? stylePpr?.snapToGrid);
323
324
  set("spacingExplicit", formatting?.spacingExplicit);
324
325
  const paragraphStyle = styleId ? styleResolver.getStyle(styleId) ?? styleResolver.getDefaultParagraphStyle() : styleResolver.getDefaultParagraphStyle();
@@ -1,5 +1,5 @@
1
- import { AnyExtension, CommandMap } from "./types.js";
2
- import { Command, Plugin } from "prosemirror-state";
1
+ import { AnyExtension, CommandFactory, CommandMap, FolioCommandArguments, FolioCommandName } from "./types.js";
2
+ import { Plugin } from "prosemirror-state";
3
3
  import { Schema } from "prosemirror-model";
4
4
  //#region src/prosemirror/extensions/ExtensionManager.d.ts
5
5
  declare class ExtensionManager {
@@ -32,7 +32,10 @@ declare class ExtensionManager {
32
32
  /**
33
33
  * Get a specific command by name
34
34
  */
35
- getCommand(name: string): ((...args: unknown[]) => Command) | undefined;
35
+ getCommand<Name extends FolioCommandName>(name: Name): CommandFactory<FolioCommandArguments[Name]> | undefined;
36
+ getCommand(name: string): CommandFactory | undefined;
37
+ requireCommand<Name extends FolioCommandName>(name: Name): CommandFactory<FolioCommandArguments[Name]>;
38
+ requireCommand(name: string): CommandFactory;
36
39
  /**
37
40
  * Lifecycle: destroy
38
41
  */
@@ -68,12 +68,14 @@ var ExtensionManager = class {
68
68
  getCommands() {
69
69
  return this.commands;
70
70
  }
71
- /**
72
- * Get a specific command by name
73
- */
74
71
  getCommand(name) {
75
72
  return this.commands[name];
76
73
  }
74
+ requireCommand(name) {
75
+ const command = this.getCommand(name);
76
+ if (!command) panic(`ExtensionManager: command "${name}" is not registered`);
77
+ return command;
78
+ }
77
79
  /**
78
80
  * Lifecycle: destroy
79
81
  */
@@ -1,6 +1,6 @@
1
1
  import { document_d_exports } from "../../../types/document.js";
2
- import { NodeExtension } from "../types.js";
3
2
  import { NumberingMap } from "../../../docx/numberingParser.js";
3
+ import { NodeExtension } from "../types.js";
4
4
  import { EditorState } from "prosemirror-state";
5
5
  //#region src/prosemirror/extensions/core/ParagraphExtension.d.ts
6
6
  type ResolvedStyleAttrs = {
@@ -161,6 +161,7 @@ function extractParagraphAttrsFromStyle(element) {
161
161
  if (spacing) {
162
162
  attrs.lineSpacing = spacing.lineSpacing;
163
163
  attrs.lineSpacingRule = spacing.lineSpacingRule;
164
+ attrs.lineSpacingExplicit = true;
164
165
  }
165
166
  }
166
167
  if (style.marginTop) {
@@ -187,6 +188,7 @@ const paragraphNodeSpec = {
187
188
  spaceAfter: { default: null },
188
189
  lineSpacing: { default: null },
189
190
  lineSpacingRule: { default: null },
191
+ lineSpacingExplicit: { default: null },
190
192
  snapToGrid: { default: null },
191
193
  spacingExplicit: { default: null },
192
194
  spacingFromDocDefaults: { default: null },
@@ -323,6 +325,30 @@ function setParagraphAttr(attr, value) {
323
325
  return true;
324
326
  };
325
327
  }
328
+ function setParagraphSpacingAttr(side, twips) {
329
+ return (state, dispatch) => {
330
+ const { $from, $to } = state.selection;
331
+ if (!dispatch) return true;
332
+ let tr = state.tr;
333
+ const seen = /* @__PURE__ */ new Set();
334
+ state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
335
+ if (node.type.name === "paragraph" && !seen.has(pos)) {
336
+ seen.add(pos);
337
+ const spacingExplicit = {
338
+ ...node.attrs["spacingExplicit"],
339
+ [side]: true
340
+ };
341
+ tr = tr.setNodeMarkup(pos, void 0, {
342
+ ...node.attrs,
343
+ [side === "before" ? "spaceBefore" : "spaceAfter"]: twips,
344
+ spacingExplicit
345
+ });
346
+ }
347
+ });
348
+ dispatch(tr.scrollIntoView());
349
+ return true;
350
+ };
351
+ }
326
352
  function setParagraphAttrsCmd(attrs) {
327
353
  return (state, dispatch) => {
328
354
  const { $from, $to } = state.selection;
@@ -348,7 +374,8 @@ function makeSetAlignment(alignment) {
348
374
  function makeSetLineSpacing(value, rule = "auto") {
349
375
  return (state, dispatch) => setParagraphAttrsCmd({
350
376
  lineSpacing: value,
351
- lineSpacingRule: rule
377
+ lineSpacingRule: rule,
378
+ lineSpacingExplicit: true
352
379
  })(state, dispatch);
353
380
  }
354
381
  function makeIncreaseIndent(amount = 720) {
@@ -498,8 +525,8 @@ const ParagraphExtension = createNodeExtension({
498
525
  singleSpacing: () => makeSetLineSpacing(240),
499
526
  oneAndHalfSpacing: () => makeSetLineSpacing(360),
500
527
  doubleSpacing: () => makeSetLineSpacing(480),
501
- setSpaceBefore: (twips) => setParagraphAttr("spaceBefore", twips),
502
- setSpaceAfter: (twips) => setParagraphAttr("spaceAfter", twips),
528
+ setSpaceBefore: (twips) => setParagraphSpacingAttr("before", twips),
529
+ setSpaceAfter: (twips) => setParagraphSpacingAttr("after", twips),
503
530
  increaseIndent: (amount) => makeIncreaseIndent(amount),
504
531
  decreaseIndent: (amount) => makeDecreaseIndent(amount),
505
532
  setIndentLeft: (twips) => setParagraphAttr("indentLeft", twips > 0 ? twips : null),
@@ -1,3 +1,4 @@
1
+ import { expectParagraphAttrs } from "../../attrs/index.js";
1
2
  import { PPR_CHANGE_SCOPED_ATTR_KEYS } from "../../commands/propertyChangeScope.js";
2
3
  import { SUGGESTION_META, makeRevisionInfo } from "../../plugins/suggestionMode.js";
3
4
  import { createExtension } from "../create.js";
@@ -10,11 +11,10 @@ function chainCommands(...commands) {
10
11
  return false;
11
12
  };
12
13
  }
13
- function appendParagraphPropertyChange(attrs, previousFormatting, rev) {
14
- const existing = Array.isArray(attrs["_propertyChanges"]) ? attrs["_propertyChanges"] : [];
14
+ function appendParagraphPropertyChange(attrs, existing, previousFormatting, rev) {
15
15
  return {
16
16
  ...attrs,
17
- _propertyChanges: [...existing, {
17
+ _propertyChanges: [...existing ?? [], {
18
18
  type: "paragraphPropertyChange",
19
19
  info: {
20
20
  id: rev.id,
@@ -85,7 +85,7 @@ function toggleList(numId) {
85
85
  listMarker: null
86
86
  };
87
87
  }
88
- if (rev) nextAttrs = appendParagraphPropertyChange(nextAttrs, getPreviousListFormatting(node.attrs), rev);
88
+ if (rev) nextAttrs = appendParagraphPropertyChange(nextAttrs, expectParagraphAttrs(node)._propertyChanges, getPreviousListFormatting(node.attrs), rev);
89
89
  tr = tr.setNodeMarkup(pos, void 0, nextAttrs);
90
90
  }
91
91
  });
@@ -1,8 +1,7 @@
1
1
  import { expectUnderlineMarkAttrs } from "../../attrs/index.js";
2
2
  import { createMarkExtension } from "../create.js";
3
- import { setMark } from "./markUtils.js";
3
+ import { setMark, toggleUnderlineMark } from "./markUtils.js";
4
4
  import { panic } from "better-result";
5
- import { toggleMark } from "prosemirror-commands";
6
5
  //#region src/prosemirror/extensions/marks/UnderlineExtension.ts
7
6
  /**
8
7
  * Underline Mark Extension
@@ -17,14 +16,17 @@ const UnderlineExtension = createMarkExtension({
17
16
  },
18
17
  parseDOM: [{ tag: "u" }, {
19
18
  style: "text-decoration",
20
- getAttrs: (value) => value.includes("underline") ? {} : false
19
+ getAttrs: (value) => {
20
+ if (value.includes("underline")) return {};
21
+ return value.includes("none") ? { style: "none" } : false;
22
+ }
21
23
  }],
22
24
  toDOM(mark) {
23
25
  const attrs = expectUnderlineMarkAttrs(mark);
24
26
  const style = attrs.style;
25
27
  const colorRgb = attrs.color?.rgb;
26
- const cssStyle = ["text-decoration: underline"];
27
- if (style && style !== "single") {
28
+ const cssStyle = [`text-decoration: ${style === "none" ? "none" : "underline"}`];
29
+ if (style && style !== "single" && style !== "none") {
28
30
  const cssDecorationStyle = {
29
31
  double: "double",
30
32
  dotted: "dotted",
@@ -46,13 +48,13 @@ const UnderlineExtension = createMarkExtension({
46
48
  if (!underlineType) panic("Missing mark type: underline");
47
49
  return {
48
50
  commands: {
49
- toggleUnderline: () => toggleMark(underlineType),
51
+ toggleUnderline: () => toggleUnderlineMark(underlineType),
50
52
  setUnderlineStyle: (style, color) => setMark(underlineType, {
51
53
  style,
52
54
  color
53
55
  })
54
56
  },
55
- keyboardShortcuts: { "Mod-u": toggleMark(underlineType) }
57
+ keyboardShortcuts: { "Mod-u": toggleUnderlineMark(underlineType) }
56
58
  };
57
59
  }
58
60
  });
@@ -4,6 +4,7 @@ import { Mark, MarkType, Schema } from "prosemirror-model";
4
4
  //#region src/prosemirror/extensions/marks/markUtils.d.ts
5
5
  type MarkAttrs = Record<string, unknown>;
6
6
  declare function setMark(markType: MarkType, attrs: MarkAttrs): Command;
7
+ declare function toggleUnderlineMark(markType: MarkType): Command;
7
8
  declare function removeMark(markType: MarkType): Command;
8
9
  /**
9
10
  * Check if a mark is active in the current selection
@@ -30,4 +31,4 @@ declare function createSetMarkCommand(markType: MarkType, attrs?: Record<string,
30
31
  */
31
32
  declare function createRemoveMarkCommand(markType: MarkType): Command;
32
33
  //#endregion
33
- export { clearFormatting, createRemoveMarkCommand, createSetMarkCommand, getMarkAttr, isMarkActive, removeMark, setMark, textFormattingToMarks };
34
+ export { clearFormatting, createRemoveMarkCommand, createSetMarkCommand, getMarkAttr, isMarkActive, removeMark, setMark, textFormattingToMarks, toggleUnderlineMark };