@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
@@ -1,5 +1,20 @@
1
+ import { FONT_THEME_VALUES } from "../../../types/documentEnumValues.js";
2
+ import { mergeFontFamily } from "../../../utils/fontFamilyMerge.js";
3
+ import { expectFontFamilyMarkAttrs } from "../../attrs/index.js";
1
4
  import { applyRunFormattingOverrideMark, buildRunFormattingOverrideAttrs } from "./RunFormattingOverrideExtension.js";
2
5
  //#region src/prosemirror/extensions/marks/markUtils.ts
6
+ const isFontTheme = (value) => value !== void 0 && FONT_THEME_VALUES.some((theme) => theme === value);
7
+ const fontFamilyAttrsToFormatting = ({ ascii, hAnsi, eastAsia, cs, hint, asciiTheme, hAnsiTheme, eastAsiaTheme, csTheme }) => ({
8
+ ...ascii !== void 0 ? { ascii } : {},
9
+ ...hAnsi !== void 0 ? { hAnsi } : {},
10
+ ...eastAsia !== void 0 ? { eastAsia } : {},
11
+ ...cs !== void 0 ? { cs } : {},
12
+ ...hint !== void 0 ? { hint } : {},
13
+ ...isFontTheme(asciiTheme) ? { asciiTheme } : {},
14
+ ...hAnsiTheme !== void 0 ? { hAnsiTheme } : {},
15
+ ...eastAsiaTheme !== void 0 ? { eastAsiaTheme } : {},
16
+ ...csTheme !== void 0 ? { csTheme } : {}
17
+ });
3
18
  function marksToTextFormatting(marks) {
4
19
  const formatting = {};
5
20
  for (const mark of marks) switch (mark.type.name) {
@@ -10,7 +25,10 @@ function marksToTextFormatting(marks) {
10
25
  formatting.italic = true;
11
26
  break;
12
27
  case "underline":
13
- formatting.underline = { style: typeof mark.attrs["style"] === "string" ? mark.attrs["style"] : "single" };
28
+ formatting.underline = {
29
+ style: typeof mark.attrs["style"] === "string" ? mark.attrs["style"] : "single",
30
+ ...mark.attrs["color"] !== null && mark.attrs["color"] !== void 0 ? { color: mark.attrs["color"] } : {}
31
+ };
14
32
  break;
15
33
  case "strike":
16
34
  formatting.strike = true;
@@ -34,17 +52,9 @@ function marksToTextFormatting(marks) {
34
52
  case "fontSize":
35
53
  formatting.fontSize = Number(mark.attrs["size"]);
36
54
  break;
37
- case "fontFamily": {
38
- const ascii = mark.attrs["ascii"] !== null && mark.attrs["ascii"] !== void 0 ? String(mark.attrs["ascii"]) : void 0;
39
- const hAnsi = mark.attrs["hAnsi"] !== null && mark.attrs["hAnsi"] !== void 0 ? String(mark.attrs["hAnsi"]) : void 0;
40
- const hint = mark.attrs["hint"];
41
- formatting.fontFamily = {
42
- ...ascii !== void 0 ? { ascii } : {},
43
- ...hAnsi !== void 0 ? { hAnsi } : {},
44
- ...hint === "default" || hint === "eastAsia" || hint === "cs" ? { hint } : {}
45
- };
55
+ case "fontFamily":
56
+ formatting.fontFamily = fontFamilyAttrsToFormatting(expectFontFamilyMarkAttrs(mark));
46
57
  break;
47
- }
48
58
  case "language": {
49
59
  const val = mark.attrs["val"];
50
60
  const eastAsia = mark.attrs["eastAsia"];
@@ -93,21 +103,82 @@ function dispatchStoredMarks(state, dispatch, marks) {
93
103
  tr.setStoredMarks(marks);
94
104
  dispatch(tr);
95
105
  }
106
+ function compactAttrs(attrs) {
107
+ if (!attrs) return {};
108
+ const result = {};
109
+ for (const [key, value] of Object.entries(attrs)) if (value !== null && value !== void 0) result[key] = value;
110
+ return result;
111
+ }
112
+ function mergeMarkAttrs(markType, currentMark, nextAttrs) {
113
+ const next = compactAttrs(nextAttrs);
114
+ switch (markType.name) {
115
+ case "fontFamily": return mergeFontFamily(currentMark ? fontFamilyAttrsToFormatting(expectFontFamilyMarkAttrs(currentMark)) : void 0, fontFamilyAttrsToFormatting(expectFontFamilyMarkAttrs(markType.create(next))));
116
+ case "underline": return {
117
+ ...compactAttrs(currentMark?.attrs),
118
+ ...next
119
+ };
120
+ default: return nextAttrs;
121
+ }
122
+ }
123
+ function markRequiresAttrMerge(markType) {
124
+ return markType.name === "fontFamily" || markType.name === "underline";
125
+ }
126
+ function createMarkWithMergedAttrs(markType, currentMark, nextAttrs) {
127
+ if (!markRequiresAttrMerge(markType)) return markType.create(nextAttrs);
128
+ return markType.create(mergeMarkAttrs(markType, currentMark, nextAttrs));
129
+ }
96
130
  function setMark(markType, attrs) {
97
131
  return (state, dispatch) => {
98
132
  const { from, to, empty } = state.selection;
99
- const mark = markType.create(attrs);
100
133
  if (empty) {
101
134
  if (dispatch) {
102
135
  const current = state.storedMarks ?? state.selection.$from.marks();
103
- dispatchStoredMarks(state, dispatch, [...markType.isInSet(current) ? current.filter((m) => m.type !== markType) : current, mark]);
136
+ const currentMark = markType.isInSet(current);
137
+ const marks = markType.isInSet(current) ? current.filter((m) => m.type !== markType) : current;
138
+ const mark = createMarkWithMergedAttrs(markType, currentMark, attrs);
139
+ dispatchStoredMarks(state, dispatch, [...marks, mark]);
104
140
  }
105
141
  return true;
106
142
  }
107
- if (dispatch) dispatch(state.tr.addMark(from, to, mark).scrollIntoView());
143
+ if (dispatch) {
144
+ if (!markRequiresAttrMerge(markType)) {
145
+ dispatch(state.tr.addMark(from, to, markType.create(attrs)).scrollIntoView());
146
+ return true;
147
+ }
148
+ let tr = state.tr;
149
+ state.doc.nodesBetween(from, to, (node, pos) => {
150
+ if (!node.isText) return;
151
+ const start = Math.max(from, pos);
152
+ const end = Math.min(to, pos + node.nodeSize);
153
+ const mark = createMarkWithMergedAttrs(markType, markType.isInSet(node.marks), attrs);
154
+ tr = tr.addMark(start, end, mark);
155
+ });
156
+ dispatch(tr.scrollIntoView());
157
+ }
108
158
  return true;
109
159
  };
110
160
  }
161
+ function selectionHasVisibleUnderline(state, markType) {
162
+ const { from, to, empty, $from } = state.selection;
163
+ if (empty) {
164
+ const mark = markType.isInSet(state.storedMarks ?? $from.marks());
165
+ return mark !== void 0 && mark.attrs["style"] !== "none";
166
+ }
167
+ let hasVisibleUnderline = false;
168
+ state.doc.nodesBetween(from, to, (node) => {
169
+ if (!node.isText) return true;
170
+ const mark = markType.isInSet(node.marks);
171
+ if (mark && mark.attrs["style"] !== "none") {
172
+ hasVisibleUnderline = true;
173
+ return false;
174
+ }
175
+ return true;
176
+ });
177
+ return hasVisibleUnderline;
178
+ }
179
+ function toggleUnderlineMark(markType) {
180
+ return (state, dispatch) => setMark(markType, { style: selectionHasVisibleUnderline(state, markType) ? "none" : "single" })(state, dispatch);
181
+ }
111
182
  function removeMark(markType) {
112
183
  return (state, dispatch) => {
113
184
  const { from, to, empty } = state.selection;
@@ -197,8 +268,13 @@ function textFormattingToMarks(formatting, schema) {
197
268
  if (formatting.fontFamily && schema.marks["fontFamily"]) marks.push(schema.marks["fontFamily"].create({
198
269
  ascii: formatting.fontFamily.ascii,
199
270
  hAnsi: formatting.fontFamily.hAnsi,
271
+ eastAsia: formatting.fontFamily.eastAsia,
272
+ cs: formatting.fontFamily.cs,
200
273
  hint: formatting.fontFamily.hint,
201
- asciiTheme: formatting.fontFamily.asciiTheme
274
+ asciiTheme: formatting.fontFamily.asciiTheme,
275
+ hAnsiTheme: formatting.fontFamily.hAnsiTheme,
276
+ eastAsiaTheme: formatting.fontFamily.eastAsiaTheme,
277
+ csTheme: formatting.fontFamily.csTheme
202
278
  }));
203
279
  if (formatting.language && schema.marks["language"]) marks.push(schema.marks["language"].create(formatting.language));
204
280
  if (formatting.vertAlign === "superscript" && schema.marks["superscript"]) marks.push(schema.marks["superscript"].create());
@@ -245,4 +321,4 @@ function createRemoveMarkCommand(markType) {
245
321
  return removeMark(markType);
246
322
  }
247
323
  //#endregion
248
- export { clearFormatting, createRemoveMarkCommand, createSetMarkCommand, getMarkAttr, isMarkActive, removeMark, setMark, textFormattingToMarks };
324
+ export { clearFormatting, createRemoveMarkCommand, createSetMarkCommand, getMarkAttr, isMarkActive, removeMark, setMark, textFormattingToMarks, toggleUnderlineMark };
@@ -1,5 +1,5 @@
1
- import { NodeExtension } from "../types.js";
2
1
  import { ShapeAttrs as ShapeAttrs$1 } from "../../schema/nodes.js";
2
+ import { NodeExtension } from "../types.js";
3
3
  //#region src/prosemirror/extensions/nodes/ShapeExtension.d.ts
4
4
  type ShapeAttrs = ShapeAttrs$1;
5
5
  declare function sanitizeColor(value: string | null | undefined): string | null;
@@ -1,7 +1,7 @@
1
1
  import { document_d_exports } from "../../../types/document.js";
2
2
  import { OutlineStyleAttr } from "../../../types/documentEnumValues.js";
3
- import { NodeExtension } from "../types.js";
4
3
  import { ImagePositionAttrs, TextBoxAttrs as TextBoxAttrs$1 } from "../../schema/nodes.js";
4
+ import { NodeExtension } from "../types.js";
5
5
  //#region src/prosemirror/extensions/nodes/TextBoxExtension.d.ts
6
6
  type TextBoxAttrs = {
7
7
  /** Width in pixels */
@@ -1,6 +1,50 @@
1
+ import { document_d_exports } from "../../types/document.js";
2
+ import { TextColorAttrs } from "../schema/marks.js";
3
+ import "../schema/index.js";
4
+ import { TablePropertiesCommand } from "../../utils/tableOperations.js";
5
+ import { ResolvedStyleAttrs } from "./core/ParagraphExtension.js";
6
+ import { BorderPreset, TableBorderPreset } from "./nodes/TableExtension.js";
1
7
  import { Command, Plugin } from "prosemirror-state";
2
8
  import { MarkSpec, NodeSpec, Schema } from "prosemirror-model";
3
9
  //#region src/prosemirror/extensions/types.d.ts
10
+ type TableCellBorderCommandSpec = {
11
+ style: string;
12
+ size?: number;
13
+ color?: {
14
+ rgb: string;
15
+ };
16
+ };
17
+ type TableBorderCommandSpec = {
18
+ style: string;
19
+ size: number;
20
+ color: {
21
+ rgb: string;
22
+ };
23
+ };
24
+ type TableCellMarginsCommand = {
25
+ top?: number;
26
+ bottom?: number;
27
+ left?: number;
28
+ right?: number;
29
+ };
30
+ type TableStyleCommand = {
31
+ styleId: string;
32
+ tableBorders?: Partial<Record<"top" | "bottom" | "left" | "right" | "insideH" | "insideV", TableCellBorderCommandSpec>>;
33
+ conditionals?: Record<string, {
34
+ backgroundColor?: string;
35
+ borders?: Partial<Record<"top" | "bottom" | "left" | "right", TableCellBorderCommandSpec | null>>;
36
+ bold?: boolean;
37
+ color?: string;
38
+ }>;
39
+ look?: {
40
+ firstRow?: boolean;
41
+ lastRow?: boolean;
42
+ firstCol?: boolean;
43
+ lastCol?: boolean;
44
+ noHBand?: boolean;
45
+ noVBand?: boolean;
46
+ };
47
+ };
4
48
  type ExtensionPriority = number;
5
49
  declare const Priority: {
6
50
  readonly Highest: 0;
@@ -12,7 +56,96 @@ declare const Priority: {
12
56
  type ExtensionContext = {
13
57
  schema: Schema;
14
58
  };
59
+ type FolioCommandArguments = {
60
+ toggleBold: [];
61
+ toggleItalic: [];
62
+ toggleUnderline: [];
63
+ toggleStrike: [];
64
+ toggleSuperscript: [];
65
+ toggleSubscript: [];
66
+ setTextColor: [attrs: TextColorAttrs];
67
+ clearTextColor: [];
68
+ setHighlight: [color: string];
69
+ clearHighlight: [];
70
+ setFontSize: [size: number];
71
+ clearFontSize: [];
72
+ setFontFamily: [fontName: string];
73
+ clearFontFamily: [];
74
+ setUnderlineStyle: [style: string, color?: TextColorAttrs];
75
+ setHyperlink: [href: string, tooltip?: string];
76
+ removeHyperlink: [];
77
+ insertHyperlink: [text: string, href: string, tooltip?: string];
78
+ setAlignment: [alignment: document_d_exports.ParagraphAlignment];
79
+ alignLeft: [];
80
+ alignCenter: [];
81
+ alignRight: [];
82
+ alignJustify: [];
83
+ setLineSpacing: [value: number, rule?: document_d_exports.LineSpacingRule];
84
+ singleSpacing: [];
85
+ oneAndHalfSpacing: [];
86
+ doubleSpacing: [];
87
+ increaseIndent: [amount?: number];
88
+ decreaseIndent: [amount?: number];
89
+ setIndentLeft: [twips: number];
90
+ setIndentRight: [twips: number];
91
+ setIndentFirstLine: [twips: number, hanging?: boolean];
92
+ toggleBulletList: [];
93
+ toggleNumberedList: [];
94
+ increaseListLevel: [];
95
+ decreaseListLevel: [];
96
+ removeList: [];
97
+ setSpaceBefore: [twips: number];
98
+ setSpaceAfter: [twips: number];
99
+ applyStyle: [styleId: string, resolvedAttrs?: ResolvedStyleAttrs];
100
+ clearStyle: [];
101
+ insertSectionBreak: [breakType: "nextPage" | "continuous" | "oddPage" | "evenPage"];
102
+ removeSectionBreak: [];
103
+ addTabStop: [position: number, alignment?: document_d_exports.TabStopAlignment, leader?: document_d_exports.TabLeader];
104
+ removeTabStop: [position: number];
105
+ toggleBidi: [];
106
+ setRtl: [];
107
+ setLtr: [];
108
+ setTabs: [tabs: document_d_exports.TabStop[]];
109
+ generateTOC: [];
110
+ insertTable: [rows: number, cols: number];
111
+ addRowAbove: [];
112
+ addRowBelow: [];
113
+ deleteRow: [];
114
+ addColumnLeft: [];
115
+ addColumnRight: [];
116
+ deleteColumn: [];
117
+ deleteTable: [];
118
+ selectTable: [];
119
+ selectRow: [];
120
+ selectColumn: [];
121
+ mergeCells: [];
122
+ splitCell: [];
123
+ setCellBorder: [side: "top" | "bottom" | "left" | "right" | "all", spec: TableCellBorderCommandSpec | null, clearOthers?: boolean];
124
+ setTableBorderPreset: [preset: TableBorderPreset];
125
+ setTableBorders: [preset: BorderPreset, borderSpec?: TableBorderCommandSpec];
126
+ removeTableBorders: [];
127
+ setAllTableBorders: [borderSpec?: TableBorderCommandSpec];
128
+ setOutsideTableBorders: [borderSpec?: TableBorderCommandSpec];
129
+ setInsideTableBorders: [borderSpec?: TableBorderCommandSpec];
130
+ setCellVerticalAlign: [align: "top" | "center" | "bottom"];
131
+ setCellMargins: [margins: TableCellMarginsCommand];
132
+ setCellTextDirection: [direction: string | null];
133
+ toggleNoWrap: [];
134
+ setRowHeight: [height: number | null, rule?: "auto" | "atLeast" | "exact"];
135
+ toggleHeaderRow: [];
136
+ distributeColumns: [];
137
+ autoFitContents: [];
138
+ setTableProperties: [props: TablePropertiesCommand];
139
+ applyTableStyle: [styleData: TableStyleCommand];
140
+ setCellFillColor: [color: string | null];
141
+ setTableBorderColor: [color: string];
142
+ setTableBorderWidth: [size: number];
143
+ };
144
+ type FolioCommandName = keyof FolioCommandArguments;
145
+ type CommandFactory<Args extends readonly unknown[] = readonly unknown[]> = (...args: Args) => Command;
146
+ type FolioCommandMap = Partial<{ [Name in FolioCommandName]: CommandFactory<FolioCommandArguments[Name]>; }>;
15
147
  type CommandMap = Record<string, (...args: any[]) => Command>;
148
+ type ExtensionCommandMap = CommandMap & FolioCommandMap;
16
149
  type KeyboardShortcutMap = Record<string, Command>;
17
150
  type ExtensionRuntime = {
18
151
  commands?: CommandMap;
@@ -97,4 +230,4 @@ type MarkExtensionDefinitionWithoutDefaults<TOptions extends ExtensionOptions =
97
230
  };
98
231
  type MarkExtensionDefinition<TOptions extends ExtensionOptions = ExtensionOptions> = MarkExtensionDefinitionWithDefaults<TOptions> | MarkExtensionDefinitionWithoutDefaults<TOptions>;
99
232
  //#endregion
100
- export { AnyExtension, CommandMap, Extension, ExtensionConfig, ExtensionContext, ExtensionDefinition, ExtensionPriority, ExtensionRuntime, KeyboardShortcutMap, MarkExtension, MarkExtensionConfig, MarkExtensionDefinition, NodeExtension, NodeExtensionConfig, NodeExtensionDefinition, Priority };
233
+ export { AnyExtension, CommandFactory, CommandMap, Extension, ExtensionCommandMap, ExtensionConfig, ExtensionContext, ExtensionDefinition, ExtensionPriority, ExtensionRuntime, FolioCommandArguments, FolioCommandMap, FolioCommandName, KeyboardShortcutMap, MarkExtension, MarkExtensionConfig, MarkExtensionDefinition, NodeExtension, NodeExtensionConfig, NodeExtensionDefinition, Priority, TableBorderCommandSpec, TableCellBorderCommandSpec, TableCellMarginsCommand, type TablePropertiesCommand, TableStyleCommand };
@@ -1,15 +1,15 @@
1
1
  import { FontFamilyAttrs, FontSizeAttrs, HyperlinkAttrs, TextColorAttrs, UnderlineAttrs } from "./schema/marks.js";
2
2
  import { ImageAttrs, ParagraphAttrs } from "./schema/nodes.js";
3
3
  import { schema, singletonManager } from "./schema/index.js";
4
+ import { getParagraphAlignment, getParagraphBidi, getStyleId } from "./extensions/core/ParagraphExtension.js";
5
+ import { BorderPreset, TableBorderPreset, TableContextInfo, getTableContext, isInTable as isInTableCell } from "./extensions/nodes/TableExtension.js";
4
6
  import { PAINTABLE_MARK_NAMES, applyFormatMarks, captureFormatMarks } from "./commands/formatPainter.js";
5
7
  import { clearFormatting, getMarkAttr, isMarkActive } from "./extensions/marks/markUtils.js";
6
8
  import { getHyperlinkAttrs, getSelectedText, isHyperlinkActive } from "./extensions/marks/HyperlinkExtension.js";
7
9
  import { clearFontFamily, clearFontSize, clearHighlight, clearTextColor, insertHyperlink, removeHyperlink, setFontFamily, setFontSize, setHighlight, setHyperlink, setTextColor, toggleBold, toggleItalic, toggleStrike, toggleSubscript, toggleSuperscript, toggleUnderline } from "./commands/formatting.js";
8
10
  import { insertImageFromFile } from "./commands/image.js";
9
- import { getParagraphAlignment, getParagraphBidi, getStyleId } from "./extensions/core/ParagraphExtension.js";
10
11
  import { getListInfo, isInList } from "./extensions/features/ListExtension.js";
11
12
  import { addTabStop, alignCenter, alignJustify, alignLeft, alignRight, applyStyle, clearStyle, decreaseIndent, decreaseListLevel, generateTOC, increaseIndent, increaseListLevel, removeList, removeTabStop, setAlignment, setIndentFirstLine, setIndentLeft, setIndentRight, setLineSpacing, setLtr, setRtl, toggleBidi, toggleBulletList, toggleNumberedList } from "./commands/paragraph.js";
12
- import { BorderPreset, TableBorderPreset, TableContextInfo, getTableContext, isInTable as isInTableCell } from "./extensions/nodes/TableExtension.js";
13
13
  import { addColumnLeft, addColumnRight, addRowAbove, addRowBelow, applyTableStyle, autoFitContents, deleteColumn, deleteRow, deleteTable, distributeColumns, insertTable, mergeCells, removeTableBorders, selectColumn, selectRow, selectTable, setAllTableBorders, setCellBorder, setCellFillColor, setCellMargins, setCellTextDirection, setCellVerticalAlign, setInsideTableBorders, setOutsideTableBorders, setRowHeight, setTableBorderColor, setTableBorderPreset, setTableBorderWidth, setTableBorders, setTableProperties, splitCell, toggleHeaderRow, toggleNoWrap } from "./commands/table.js";
14
14
  import { insertPageBreak } from "./commands/pageBreak.js";
15
15
  import "./commands/index.js";
@@ -1,3 +1,5 @@
1
+ import { expectCommentMarkAttrs, expectTrackedChangeMarkAttrs } from "../attrs/index.js";
2
+ import { extractSelectionSnapshot } from "../selectionState.js";
1
3
  import { Plugin, PluginKey } from "prosemirror-state";
2
4
  //#region src/prosemirror/plugins/selectionTracker.ts
3
5
  /**
@@ -17,56 +19,41 @@ const selectionTrackerKey = new PluginKey("selectionTracker");
17
19
  * Extract selection context from editor state
18
20
  */
19
21
  function extractSelectionContext(state) {
20
- const { selection, doc } = state;
21
- const { from, to, empty } = selection;
22
- const $from = doc.resolve(from);
23
- let startParagraphIndex = 0;
24
- let endParagraphIndex = 0;
25
- doc.forEach((_node, offset, index) => {
26
- if (offset > to) return;
27
- if (offset <= from) startParagraphIndex = index;
28
- if (offset <= to) endParagraphIndex = index;
29
- });
30
- const textFormatting = extractTextFormatting(state);
31
- const paragraph = $from.parent;
32
- const paragraphFormatting = {};
33
- if (paragraph.type.name === "paragraph") {
34
- if (paragraph.attrs["alignment"]) paragraphFormatting.alignment = paragraph.attrs["alignment"];
35
- if (paragraph.attrs["lineSpacing"]) {
36
- paragraphFormatting.lineSpacing = paragraph.attrs["lineSpacing"];
37
- paragraphFormatting.lineSpacingRule = paragraph.attrs["lineSpacingRule"];
38
- }
39
- if (typeof paragraph.attrs["snapToGrid"] === "boolean") paragraphFormatting.snapToGrid = paragraph.attrs["snapToGrid"];
40
- if (paragraph.attrs["indentLeft"]) paragraphFormatting.indentLeft = paragraph.attrs["indentLeft"];
41
- if (paragraph.attrs["indentRight"]) paragraphFormatting.indentRight = paragraph.attrs["indentRight"];
42
- if (paragraph.attrs["indentFirstLine"]) paragraphFormatting.indentFirstLine = paragraph.attrs["indentFirstLine"];
43
- if (paragraph.attrs["hangingIndent"]) paragraphFormatting.hangingIndent = paragraph.attrs["hangingIndent"];
44
- if (paragraph.attrs["tabs"]) paragraphFormatting.tabs = paragraph.attrs["tabs"];
45
- if (paragraph.attrs["numPr"]) paragraphFormatting.numPr = paragraph.attrs["numPr"];
46
- if (paragraph.attrs["styleId"]) paragraphFormatting.styleId = paragraph.attrs["styleId"];
47
- }
48
- const numPr = paragraph.attrs["numPr"];
22
+ const { selection } = state;
23
+ const { empty } = selection;
24
+ const snapshot = extractSelectionSnapshot(state);
25
+ const paragraphFormatting = snapshot.styleId === null ? snapshot.paragraphFormatting : {
26
+ ...snapshot.paragraphFormatting,
27
+ styleId: snapshot.styleId
28
+ };
29
+ const numPr = snapshot.paragraphFormatting.numPr;
49
30
  const inList = !!numPr?.numId;
50
31
  let listType;
51
32
  if (numPr?.numId === 1) listType = "bullet";
52
33
  else if (numPr?.numId) listType = "numbered";
53
34
  const listLevel = numPr?.ilvl;
54
- const allMarks = state.storedMarks || (empty ? $from.marks() : []);
35
+ const allMarks = state.storedMarks || (empty ? selection.$from.marks() : []);
55
36
  const activeCommentIds = [];
56
37
  let inInsertion = false;
57
38
  let inDeletion = false;
58
39
  for (const mark of allMarks) {
59
- if (mark.type.name === "comment" && mark.attrs["commentId"]) activeCommentIds.push(mark.attrs["commentId"]);
60
- if (mark.type.name === "insertion") inInsertion = true;
61
- if (mark.type.name === "deletion") inDeletion = true;
40
+ if (mark.type.name === "comment") activeCommentIds.push(expectCommentMarkAttrs(mark).commentId);
41
+ if (mark.type.name === "insertion") {
42
+ expectTrackedChangeMarkAttrs(mark);
43
+ inInsertion = true;
44
+ }
45
+ if (mark.type.name === "deletion") {
46
+ expectTrackedChangeMarkAttrs(mark);
47
+ inDeletion = true;
48
+ }
62
49
  }
63
50
  return {
64
- hasSelection: !empty,
65
- isMultiParagraph: startParagraphIndex !== endParagraphIndex,
66
- textFormatting,
51
+ hasSelection: snapshot.hasSelection,
52
+ isMultiParagraph: snapshot.isMultiParagraph,
53
+ textFormatting: snapshot.textFormatting,
67
54
  paragraphFormatting,
68
- startParagraphIndex,
69
- endParagraphIndex,
55
+ startParagraphIndex: snapshot.startParagraphIndex,
56
+ endParagraphIndex: snapshot.endParagraphIndex,
70
57
  inList,
71
58
  ...listType !== void 0 ? { listType } : {},
72
59
  ...listLevel !== void 0 ? { listLevel } : {},
@@ -76,62 +63,6 @@ function extractSelectionContext(state) {
76
63
  };
77
64
  }
78
65
  /**
79
- * Extract text formatting from current selection/cursor marks
80
- */
81
- function extractTextFormatting(state) {
82
- const { selection } = state;
83
- const { empty, $from } = selection;
84
- const marks = state.storedMarks || (empty ? $from.marks() : []);
85
- const formatting = {};
86
- for (const mark of marks) switch (mark.type.name) {
87
- case "bold":
88
- formatting.bold = true;
89
- break;
90
- case "italic":
91
- formatting.italic = true;
92
- break;
93
- case "underline":
94
- formatting.underline = {
95
- style: mark.attrs["style"] || "single",
96
- color: mark.attrs["color"]
97
- };
98
- break;
99
- case "strike":
100
- if (mark.attrs["double"]) formatting.doubleStrike = true;
101
- else formatting.strike = true;
102
- break;
103
- case "textColor":
104
- formatting.color = {
105
- rgb: mark.attrs["rgb"],
106
- themeColor: mark.attrs["themeColor"],
107
- themeTint: mark.attrs["themeTint"],
108
- themeShade: mark.attrs["themeShade"]
109
- };
110
- break;
111
- case "highlight":
112
- formatting.highlight = mark.attrs["color"];
113
- break;
114
- case "fontSize":
115
- formatting.fontSize = mark.attrs["size"];
116
- break;
117
- case "fontFamily":
118
- formatting.fontFamily = {
119
- ascii: mark.attrs["ascii"],
120
- hAnsi: mark.attrs["hAnsi"],
121
- asciiTheme: mark.attrs["asciiTheme"]
122
- };
123
- break;
124
- case "superscript":
125
- formatting.vertAlign = "superscript";
126
- break;
127
- case "subscript":
128
- formatting.vertAlign = "subscript";
129
- break;
130
- default: break;
131
- }
132
- return formatting;
133
- }
134
- /**
135
66
  * Create selection tracker plugin
136
67
  */
137
68
  function createSelectionTrackerPlugin(onSelectionChange) {
@@ -1,3 +1,4 @@
1
+ import { expectParagraphAttrs } from "./attrs/index.js";
1
2
  //#region src/prosemirror/revisionCarriers.ts
2
3
  const isObjectRecord = (value) => typeof value === "object" && value !== null;
3
4
  const revisionMetadata = (value) => {
@@ -34,6 +35,7 @@ const getFolioNodeRevisionCarriers = (node, nodePos) => {
34
35
  if (node.type.name === "paragraph") {
35
36
  const from = nodePos + node.nodeSize - 1;
36
37
  const to = nodePos + node.nodeSize;
38
+ const paragraphAttrs = expectParagraphAttrs(node);
37
39
  const paragraphMark = node.attrs["pPrMark"];
38
40
  if (isObjectRecord(paragraphMark) && (paragraphMark["kind"] === "ins" || paragraphMark["kind"] === "del")) {
39
41
  const metadata = revisionMetadata(paragraphMark["info"]);
@@ -47,7 +49,7 @@ const getFolioNodeRevisionCarriers = (node, nodePos) => {
47
49
  }
48
50
  appendPropertyCarriers({
49
51
  carriers,
50
- changes: node.attrs["_propertyChanges"],
52
+ changes: paragraphAttrs._propertyChanges,
51
53
  type: "paragraphPropertiesChanged",
52
54
  node,
53
55
  from,
@@ -1,6 +1,6 @@
1
1
  import { CharacterSpacingAttrs, CharacterStyleAttrs, CommentAttrs, EmphasisMarkAttrs, FontFamilyAttrs, FontSizeAttrs, FootnoteRefAttrs, HighlightAttrs, HyperlinkAttrs, LanguageAttrs, RunFormattingOverrideAttrs, RunPropertyChangeMarkAttrs, RunShadingAttrs, StrikeAttrs, TextColorAttrs, TextEffectAttrs, TrackedChangeMarkAttrs, UnderlineAttrs } from "./marks.js";
2
+ import { BlockSdtAttrs, FieldAttrs, HardBreakAttrs, ImageAttrs, ImagePositionAttrs, MathAttrs, ParagraphAttrs, ParagraphPropertyChangeAttrs, SdtAttrs, ShapeAttrs, SymbolAttrs, TabAttrs, TableAttrs, TableCellAttrs, TableRowAttrs, TextBoxAttrs } from "./nodes.js";
2
3
  import { ExtensionManager } from "../extensions/ExtensionManager.js";
3
- import { BlockSdtAttrs, FieldAttrs, HardBreakAttrs, ImageAttrs, ImagePositionAttrs, MathAttrs, ParagraphAttrs, SdtAttrs, ShapeAttrs, SymbolAttrs, TabAttrs, TableAttrs, TableCellAttrs, TableRowAttrs, TextBoxAttrs } from "./nodes.js";
4
4
  //#region src/prosemirror/schema/index.d.ts
5
5
  declare const singletonManager: ExtensionManager;
6
6
  declare const schema: import("prosemirror-model").Schema<any, any>;
@@ -11,4 +11,4 @@ type DocxSchema = typeof schema;
11
11
  type DocxNode = ReturnType<typeof schema.node>;
12
12
  type DocxMark = ReturnType<typeof schema.mark>;
13
13
  //#endregion
14
- export { type BlockSdtAttrs, type CharacterSpacingAttrs, type CharacterStyleAttrs, type CommentAttrs, DocxMark, DocxNode, DocxSchema, type EmphasisMarkAttrs, type FieldAttrs, type FontFamilyAttrs, type FontSizeAttrs, type FootnoteRefAttrs, type HardBreakAttrs, type HighlightAttrs, type HyperlinkAttrs, type ImageAttrs, type ImagePositionAttrs, type LanguageAttrs, type MathAttrs, type ParagraphAttrs, type RunFormattingOverrideAttrs, type RunPropertyChangeMarkAttrs, type RunShadingAttrs, type SdtAttrs, type ShapeAttrs, type StrikeAttrs, type SymbolAttrs, type TabAttrs, type TableAttrs, type TableCellAttrs, type TableRowAttrs, type TextBoxAttrs, type TextColorAttrs, type TextEffectAttrs, type TrackedChangeMarkAttrs, type UnderlineAttrs, schema, singletonManager };
14
+ export { type BlockSdtAttrs, type CharacterSpacingAttrs, type CharacterStyleAttrs, type CommentAttrs, DocxMark, DocxNode, DocxSchema, type EmphasisMarkAttrs, type FieldAttrs, type FontFamilyAttrs, type FontSizeAttrs, type FootnoteRefAttrs, type HardBreakAttrs, type HighlightAttrs, type HyperlinkAttrs, type ImageAttrs, type ImagePositionAttrs, type LanguageAttrs, type MathAttrs, type ParagraphAttrs, type ParagraphPropertyChangeAttrs, type RunFormattingOverrideAttrs, type RunPropertyChangeMarkAttrs, type RunShadingAttrs, type SdtAttrs, type ShapeAttrs, type StrikeAttrs, type SymbolAttrs, type TabAttrs, type TableAttrs, type TableCellAttrs, type TableRowAttrs, type TextBoxAttrs, type TextColorAttrs, type TextEffectAttrs, type TrackedChangeMarkAttrs, type UnderlineAttrs, schema, singletonManager };
@@ -14,6 +14,93 @@ import { createStarterKit } from "../extensions/StarterKit.js";
14
14
  const mgr = new ExtensionManager(createStarterKit());
15
15
  mgr.buildSchema();
16
16
  mgr.initializeRuntime();
17
+ const completeCommandNames = (names) => names;
18
+ const requiredCommands = completeCommandNames([
19
+ "toggleBold",
20
+ "toggleItalic",
21
+ "toggleUnderline",
22
+ "toggleStrike",
23
+ "toggleSuperscript",
24
+ "toggleSubscript",
25
+ "setTextColor",
26
+ "clearTextColor",
27
+ "setHighlight",
28
+ "clearHighlight",
29
+ "setFontSize",
30
+ "clearFontSize",
31
+ "setFontFamily",
32
+ "clearFontFamily",
33
+ "setUnderlineStyle",
34
+ "setHyperlink",
35
+ "removeHyperlink",
36
+ "insertHyperlink",
37
+ "setAlignment",
38
+ "alignLeft",
39
+ "alignCenter",
40
+ "alignRight",
41
+ "alignJustify",
42
+ "setLineSpacing",
43
+ "singleSpacing",
44
+ "oneAndHalfSpacing",
45
+ "doubleSpacing",
46
+ "increaseIndent",
47
+ "decreaseIndent",
48
+ "setIndentLeft",
49
+ "setIndentRight",
50
+ "setIndentFirstLine",
51
+ "toggleBulletList",
52
+ "toggleNumberedList",
53
+ "increaseListLevel",
54
+ "decreaseListLevel",
55
+ "removeList",
56
+ "setSpaceBefore",
57
+ "setSpaceAfter",
58
+ "applyStyle",
59
+ "clearStyle",
60
+ "insertSectionBreak",
61
+ "removeSectionBreak",
62
+ "addTabStop",
63
+ "removeTabStop",
64
+ "toggleBidi",
65
+ "setRtl",
66
+ "setLtr",
67
+ "setTabs",
68
+ "generateTOC",
69
+ "insertTable",
70
+ "addRowAbove",
71
+ "addRowBelow",
72
+ "deleteRow",
73
+ "addColumnLeft",
74
+ "addColumnRight",
75
+ "deleteColumn",
76
+ "deleteTable",
77
+ "selectTable",
78
+ "selectRow",
79
+ "selectColumn",
80
+ "mergeCells",
81
+ "splitCell",
82
+ "setCellBorder",
83
+ "setTableBorderPreset",
84
+ "setTableBorders",
85
+ "removeTableBorders",
86
+ "setAllTableBorders",
87
+ "setOutsideTableBorders",
88
+ "setInsideTableBorders",
89
+ "setCellVerticalAlign",
90
+ "setCellMargins",
91
+ "setCellTextDirection",
92
+ "toggleNoWrap",
93
+ "setRowHeight",
94
+ "toggleHeaderRow",
95
+ "distributeColumns",
96
+ "autoFitContents",
97
+ "setTableProperties",
98
+ "applyTableStyle",
99
+ "setCellFillColor",
100
+ "setTableBorderColor",
101
+ "setTableBorderWidth"
102
+ ]);
103
+ for (const commandName of requiredCommands) mgr.requireCommand(commandName);
17
104
  const singletonManager = mgr;
18
105
  const schema = mgr.getSchema();
19
106
  //#endregion
@@ -32,6 +32,7 @@ type ParagraphAttrs = {
32
32
  spaceAfter?: number;
33
33
  lineSpacing?: number;
34
34
  lineSpacingRule?: document_d_exports.LineSpacingRule;
35
+ lineSpacingExplicit?: boolean;
35
36
  snapToGrid?: boolean;
36
37
  spacingExplicit?: SpacingExplicit;
37
38
  /** Layout provenance: document defaults survive on empty paragraphs. */
@@ -176,7 +177,7 @@ type ParagraphAttrs = {
176
177
  * them in UI today, but stripping them on every edit would corrupt the
177
178
  * `w:pPrChange` history Word relies on for "show previous formatting"
178
179
  * and for reverting an accepted property change. */
179
- _propertyChanges?: document_d_exports.ParagraphPropertyChange[];
180
+ _propertyChanges?: ParagraphPropertyChangeAttrs[];
180
181
  /** Paragraph-mark insertion / deletion (`<w:pPr><w:rPr><w:ins/>` /
181
182
  * `<w:del/>`). Word emits this when the paragraph break itself was
182
183
  * authored in track-changes mode — pressing Enter mid-paragraph
@@ -191,6 +192,17 @@ type ParagraphAttrs = {
191
192
  */
192
193
  _suggestedInsert?: SuggestedStructuralMarker | null;
193
194
  };
195
+ /**
196
+ * ProseMirror property-change attrs may also carry the editor's list-marker
197
+ * snapshot fields alongside the canonical paragraph formatting fields.
198
+ * Keeping that shape typed here lets layout consume validated attrs directly.
199
+ */
200
+ type ParagraphPropertyChangeAttrs = Omit<document_d_exports.ParagraphPropertyChange, "previousFormatting" | "currentFormatting"> & {
201
+ previousFormatting?: Omit<document_d_exports.ParagraphFormatting, "numPr"> & {
202
+ numPr?: document_d_exports.ParagraphFormatting["numPr"] | null;
203
+ } & Partial<Pick<ParagraphAttrs, "listIsBullet" | "listIsLegal" | "listNumFmt" | "listMarker" | "listMarkerHidden" | "listMarkerFontFamily" | "listMarkerFontSize" | "listMarkerBold" | "listMarkerAlignment" | "listMarkerSuffix" | "listLevelNumFmts" | "listLevelStarts" | "listAbstractNumId" | "listStartOverride" | "lineSpacingExplicit" | "direction" | "_autospacingBase">>;
204
+ currentFormatting?: document_d_exports.ParagraphFormatting;
205
+ };
194
206
  /**
195
207
  * Image position for floating images (horizontal and vertical positioning)
196
208
  */
@@ -688,4 +700,4 @@ type TableCellAttrs = {
688
700
  _docxVMergeContinuationCells?: document_d_exports.TableCell[];
689
701
  };
690
702
  //#endregion
691
- export { BlockSdtAttrs, FieldAttrs, HardBreakAttrs, ImageAttrs, ImagePositionAttrs, MathAttrs, ParagraphAttrs, SdtAttrs, ShapeAttrs, SuggestedStructuralMarker, SymbolAttrs, TabAttrs, TableAttrs, TableCellAttrs, TableRowAttrs, TextBoxAnchorAttrs, TextBoxAttrs };
703
+ export { BlockSdtAttrs, FieldAttrs, HardBreakAttrs, ImageAttrs, ImagePositionAttrs, MathAttrs, ParagraphAttrs, ParagraphPropertyChangeAttrs, SdtAttrs, ShapeAttrs, SuggestedStructuralMarker, SymbolAttrs, TabAttrs, TableAttrs, TableCellAttrs, TableRowAttrs, TextBoxAnchorAttrs, TextBoxAttrs };