@stll/folio-core 0.11.0 → 0.12.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 (194) hide show
  1. package/dist/ai-edits/apply.js +602 -50
  2. package/dist/ai-edits/block-identity.d.ts +4 -0
  3. package/dist/ai-edits/block-identity.js +9 -0
  4. package/dist/ai-edits/blockRange.d.ts +28 -1
  5. package/dist/ai-edits/blockRange.js +67 -1
  6. package/dist/ai-edits/headless.d.ts +4 -4
  7. package/dist/ai-edits/headless.js +9 -9
  8. package/dist/ai-edits/index.d.ts +2 -2
  9. package/dist/ai-edits/index.js +2 -2
  10. package/dist/ai-edits/read.d.ts +7 -7
  11. package/dist/ai-edits/read.js +110 -3
  12. package/dist/ai-edits/table-cell-mutations.d.ts +48 -0
  13. package/dist/ai-edits/table-cell-mutations.js +237 -0
  14. package/dist/ai-edits/table-mutation-plan.d.ts +38 -0
  15. package/dist/ai-edits/table-mutation-plan.js +90 -0
  16. package/dist/ai-edits/table-row-column-mutations.d.ts +100 -0
  17. package/dist/ai-edits/table-row-column-mutations.js +300 -0
  18. package/dist/ai-edits/table-targets.d.ts +32 -0
  19. package/dist/ai-edits/table-targets.js +70 -0
  20. package/dist/ai-edits/types.d.ts +34 -0
  21. package/dist/compat/eigenpal.js +1 -1
  22. package/dist/content-controls/mutateContentControls.js +1 -1
  23. package/dist/controller/activeEditorStory.d.ts +36 -0
  24. package/dist/controller/activeEditorStory.js +30 -0
  25. package/dist/controller/contentControlWidgetController.d.ts +55 -0
  26. package/dist/controller/contentControlWidgetController.js +141 -0
  27. package/dist/controller/fontReadiness.js +2 -0
  28. package/dist/controller/headerFooterEditorManager.js +1 -1
  29. package/dist/controller/hiddenEditorApi.js +2 -2
  30. package/dist/controller/hiddenEditorManager.d.ts +12 -1
  31. package/dist/controller/hiddenEditorManager.js +32 -11
  32. package/dist/controller/layoutPipeline.d.ts +2 -0
  33. package/dist/controller/layoutPipeline.js +16 -1
  34. package/dist/controller/noteEditorManager.d.ts +36 -0
  35. package/dist/controller/noteEditorManager.js +210 -0
  36. package/dist/document-operations.d.ts +35 -3
  37. package/dist/document-operations.js +161 -3
  38. package/dist/docx/blockContentParser.js +2 -2
  39. package/dist/docx/commentParser.js +6 -1
  40. package/dist/docx/conformance.js +8 -1
  41. package/dist/docx/documentGrid.d.ts +8 -0
  42. package/dist/docx/documentGrid.js +8 -0
  43. package/dist/docx/drawingUtils.js +1 -1
  44. package/dist/docx/ensureParaIds.js +1 -1
  45. package/dist/docx/fieldParser.js +1 -1
  46. package/dist/docx/notePropertiesParser.js +1 -1
  47. package/dist/docx/numberingParser.js +1 -1
  48. package/dist/docx/paragraphParser.js +63 -58
  49. package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
  50. package/dist/docx/paragraphTextBoxEnrichment.js +67 -13
  51. package/dist/docx/paragraphTraversal.js +1 -1
  52. package/dist/docx/parser.js +3 -3
  53. package/dist/docx/rezip.js +1 -1
  54. package/dist/docx/runParser.js +4 -2
  55. package/dist/docx/sdtProperties.js +1 -1
  56. package/dist/docx/sectionParser.js +1 -1
  57. package/dist/docx/selectiveSave.js +1 -1
  58. package/dist/docx/serializer/commentSerializer.js +1 -2
  59. package/dist/docx/serializer/documentSerializer.js +2 -2
  60. package/dist/docx/serializer/headerFooterSerializer.js +2 -2
  61. package/dist/docx/serializer/noteSerializer.js +2 -2
  62. package/dist/docx/serializer/paragraphSerializer.js +7 -1
  63. package/dist/docx/serializer/runSerializer.js +4 -2
  64. package/dist/docx/serializer/stylesSerializer.js +1 -1
  65. package/dist/docx/serializer/tableSerializer.d.ts +5 -4
  66. package/dist/docx/serializer/tableSerializer.js +17 -13
  67. package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +87 -0
  68. package/dist/docx/server/applyDocxXmlPatchProposal.js +204 -0
  69. package/dist/docx/server/boundedArchive.d.ts +14 -3
  70. package/dist/docx/server/boundedArchive.js +56 -11
  71. package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +85 -0
  72. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +361 -0
  73. package/dist/docx/server/inspectDocxPackage.d.ts +70 -0
  74. package/dist/docx/server/inspectDocxPackage.js +217 -0
  75. package/dist/docx/server/validateDocxConformance.d.ts +41 -0
  76. package/dist/docx/server/validateDocxConformance.js +336 -0
  77. package/dist/docx/settingsParser.js +11 -1
  78. package/dist/docx/shapeParser.js +1 -1
  79. package/dist/docx/styleParser.js +4 -2
  80. package/dist/docx/tableParser.js +18 -7
  81. package/dist/docx/textBoxParser.d.ts +3 -3
  82. package/dist/docx/textBoxParser.js +16 -12
  83. package/dist/docx/xmlSafety.d.ts +5 -0
  84. package/dist/docx/xmlSafety.js +9 -0
  85. package/dist/fields/resolveFieldValues.js +1 -3
  86. package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
  87. package/dist/i18n/messages/catalogs.gen.js +17 -0
  88. package/dist/i18n/messages/messages.gen.d.ts +1 -0
  89. package/dist/index.js +1 -1
  90. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  91. package/dist/layout-bridge/convert/footnoteLayout.js +47 -40
  92. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  93. package/dist/layout-bridge/convert/headerFooterLayout.js +3 -0
  94. package/dist/layout-bridge/convert/templatePreviewFlow.js +3 -3
  95. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
  96. package/dist/layout-bridge/convert/toFlowBlocks.js +136 -20
  97. package/dist/layout-bridge/dom/noteStoryDom.d.ts +6 -0
  98. package/dist/layout-bridge/dom/noteStoryDom.js +17 -0
  99. package/dist/layout-bridge/engine/clickToPosition.js +2 -2
  100. package/dist/layout-engine/index.d.ts +2 -2
  101. package/dist/layout-engine/index.js +43 -28
  102. package/dist/layout-engine/justificationCompatibility.d.ts +6 -0
  103. package/dist/layout-engine/justificationCompatibility.js +8 -0
  104. package/dist/layout-engine/keep-together.js +11 -1
  105. package/dist/layout-engine/measure/cache.js +21 -5
  106. package/dist/layout-engine/measure/lineBreakProvider.js +53 -34
  107. package/dist/layout-engine/measure/measureBlocks.js +6 -3
  108. package/dist/layout-engine/measure/measureContainer.js +1 -1
  109. package/dist/layout-engine/measure/measureHelpers.d.ts +1 -1
  110. package/dist/layout-engine/measure/measureHelpers.js +3 -3
  111. package/dist/layout-engine/measure/measureParagraph.js +69 -25
  112. package/dist/layout-engine/measure/measureProvider.js +4 -3
  113. package/dist/layout-engine/measure/tableCellFlow.js +5 -2
  114. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +9 -7
  115. package/dist/layout-engine/measure/textBoxParagraphLayout.js +22 -6
  116. package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +3 -1
  117. package/dist/layout-engine/measure/textMeasurementPolicy.js +7 -1
  118. package/dist/layout-engine/paginator.js +5 -1
  119. package/dist/layout-engine/paragraphSpacing.d.ts +28 -4
  120. package/dist/layout-engine/paragraphSpacing.js +107 -13
  121. package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
  122. package/dist/layout-engine/types.d.ts +26 -13
  123. package/dist/layout-painter/registry/modules/textBox.js +5 -1
  124. package/dist/layout-painter/renderPage.d.ts +5 -2
  125. package/dist/layout-painter/renderPage.js +56 -12
  126. package/dist/layout-painter/renderParagraph.js +22 -6
  127. package/dist/layout-painter/renderTable.d.ts +5 -1
  128. package/dist/layout-painter/renderTable.js +5 -2
  129. package/dist/layout-painter/renderTextBox.d.ts +2 -1
  130. package/dist/layout-painter/renderTextBox.js +25 -14
  131. package/dist/managers/DocumentLoaderManager.js +1 -1
  132. package/dist/model.js +1 -1
  133. package/dist/prosemirror/attrs/index.d.ts +4 -2
  134. package/dist/prosemirror/attrs/index.js +154 -18
  135. package/dist/prosemirror/commands/comments.d.ts +3 -3
  136. package/dist/prosemirror/commands/comments.js +248 -12
  137. package/dist/prosemirror/commands/contentControls.js +1 -1
  138. package/dist/prosemirror/commands/index.js +1 -1
  139. package/dist/prosemirror/commands/pageBreak.js +1 -1
  140. package/dist/prosemirror/commands/propertyChangeScope.d.ts +2 -2
  141. package/dist/prosemirror/commands/propertyChangeScope.js +3 -2
  142. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +9 -0
  143. package/dist/prosemirror/commands/tableCellMergeResolution.js +181 -0
  144. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -1
  145. package/dist/prosemirror/conversion/fromProseDoc.js +286 -61
  146. package/dist/prosemirror/conversion/sdtAttrs.d.ts +9 -0
  147. package/dist/prosemirror/conversion/sdtAttrs.js +49 -0
  148. package/dist/prosemirror/conversion/toProseDoc.d.ts +2 -1
  149. package/dist/prosemirror/conversion/toProseDoc.js +347 -98
  150. package/dist/prosemirror/extensions/ExtensionManager.js +1 -1
  151. package/dist/prosemirror/extensions/StarterKit.js +6 -1
  152. package/dist/prosemirror/extensions/core/ParagraphExtension.js +4 -1
  153. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -0
  154. package/dist/prosemirror/extensions/features/ListExtension.js +1 -1
  155. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +10 -1
  156. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +93 -2
  157. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +2 -1
  158. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +6 -2
  159. package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +1 -1
  160. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +9 -1
  161. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +24 -1
  162. package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
  163. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -0
  164. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +22 -0
  165. package/dist/prosemirror/extensions/nodes/TableExtension.js +9 -3
  166. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +13 -0
  167. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +41 -0
  168. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +5 -2
  169. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +4 -1
  170. package/dist/prosemirror/plugins/contentControlWidgets.d.ts +34 -8
  171. package/dist/prosemirror/plugins/contentControlWidgets.js +125 -91
  172. package/dist/prosemirror/plugins/selectionTracker.js +1 -0
  173. package/dist/prosemirror/plugins/suggestionMode.js +1 -1
  174. package/dist/prosemirror/schema/index.d.ts +2 -2
  175. package/dist/prosemirror/schema/marks.d.ts +5 -1
  176. package/dist/prosemirror/schema/nodes.d.ts +64 -7
  177. package/dist/prosemirror/selectionState.js +1 -0
  178. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  179. package/dist/prosemirror/tableCellMergeRevision.d.ts +9 -0
  180. package/dist/prosemirror/tableCellMergeRevision.js +22 -0
  181. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +9 -0
  182. package/dist/prosemirror/textBoxAnchorAttrs.js +33 -0
  183. package/dist/prosemirror/utils/extractTrackedChanges.d.ts +1 -1
  184. package/dist/prosemirror/utils/extractTrackedChanges.js +62 -14
  185. package/dist/prosemirror/validation.js +9 -1
  186. package/dist/server.d.ts +6 -2
  187. package/dist/server.js +5 -1
  188. package/dist/utils/fontFamilyMerge.js +9 -11
  189. package/dist/utils/fontLoader.js +3 -2
  190. package/dist/utils/fontResolver.js +22 -4
  191. package/dist/utils/fontWeights.d.ts +1 -1
  192. package/dist/utils/fontWeights.js +1 -1
  193. package/dist/utils/paragraphFormattingMerge.js +1 -0
  194. package/package.json +1 -1
@@ -0,0 +1,300 @@
1
+ import { stripBlockIdentityAttrs } from "./block-identity.js";
2
+ import { markStructuralChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
3
+ import { findEnclosingTableCell, findEnclosingTableRow } from "./table-targets.js";
4
+ import { TableMap, columnIsHeader, removeColumn, removeRow, rowIsHeader, tableNodeTypes } from "prosemirror-tables";
5
+ //#region src/ai-edits/table-row-column-mutations.ts
6
+ const getTableColumnCoordinateKey = ({ tablePosition, columnIndex }) => `${tablePosition}:${columnIndex}`;
7
+ const findTableColumnInsertion = ({ doc, blockFrom, position }) => {
8
+ const target = findEnclosingTableCell(doc, blockFrom);
9
+ if (!target) return null;
10
+ return {
11
+ tablePosition: target.tablePosition,
12
+ columnIndex: position === "before" ? target.leftColumnIndex : target.rightColumnIndex,
13
+ boundaryPosition: position === "before" ? target.cellPosition : target.cellEndPosition
14
+ };
15
+ };
16
+ const findTableRowInsertion = ({ doc, blockFrom, position }) => {
17
+ const target = findEnclosingTableRow(doc, blockFrom);
18
+ if (!target) return null;
19
+ const map = TableMap.get(target.table);
20
+ const rowIndex = position === "before" ? target.rowIndex : target.rowIndex + 1;
21
+ return buildTableRowInsertion({
22
+ map,
23
+ table: target.table,
24
+ tableStart: target.tableStart,
25
+ rowIndex
26
+ });
27
+ };
28
+ const applyTableRowInsertion = ({ tr, insertion, cellTexts, revision }) => {
29
+ if (revision && insertion.rowspanUpdates.length > 0) return { type: "unsupported" };
30
+ const liveRowspanUpdates = [];
31
+ for (const updatePosition of insertion.rowspanUpdates) {
32
+ const cellPosition = insertion.tableStart + updatePosition;
33
+ const liveCell = tr.doc.nodeAt(cellPosition);
34
+ const rowspan = liveCell?.attrs["rowspan"];
35
+ if (!liveCell || typeof rowspan !== "number") return { type: "unsupported" };
36
+ liveRowspanUpdates.push({
37
+ position: cellPosition,
38
+ cell: liveCell,
39
+ rowspan
40
+ });
41
+ }
42
+ for (const update of liveRowspanUpdates) tr.setNodeMarkup(update.position, void 0, {
43
+ ...update.cell.attrs,
44
+ rowspan: update.rowspan + 1
45
+ });
46
+ const rowAttrs = revision ? { trIns: revision } : null;
47
+ const row = insertion.rowType.create(rowAttrs, insertion.cells);
48
+ tr.insert(insertion.rowPosition, populateTableRow(row, cellTexts));
49
+ return applied(tr, revision);
50
+ };
51
+ const applyTableColumnInsertion = ({ tr, insertion, cellTexts, revision }) => {
52
+ const tablePosition = tr.mapping.map(insertion.tablePosition, 1);
53
+ const table = tr.doc.nodeAt(tablePosition);
54
+ if (!table || table.type.spec["tableRole"] !== "table") return { type: "unsupported" };
55
+ const actions = buildTableColumnInsertionActions({
56
+ map: TableMap.get(table),
57
+ table,
58
+ columnIndex: insertion.columnIndex,
59
+ cellTexts
60
+ });
61
+ if (!actions || revision && actions.some(({ type }) => type === "setColspan")) return { type: "unsupported" };
62
+ const mapFrom = tr.mapping.maps.length;
63
+ for (const action of actions) {
64
+ const position = tr.mapping.slice(mapFrom).map(tablePosition + 1 + action.position);
65
+ if (action.type === "setColspan") {
66
+ tr.setNodeMarkup(position, void 0, action.attrs);
67
+ continue;
68
+ }
69
+ const cell = revision ? action.cell.type.create({
70
+ ...action.cell.attrs,
71
+ cellMarker: {
72
+ kind: "ins",
73
+ info: revision
74
+ }
75
+ }, action.cell.content) : action.cell;
76
+ tr.insert(position, cell);
77
+ }
78
+ return applied(tr, revision);
79
+ };
80
+ const applyTableColumnDeletion = ({ tr, deletion, insertedColumnCount, revision }) => {
81
+ const tablePosition = tr.mapping.map(deletion.tablePosition, 1);
82
+ const table = tr.doc.nodeAt(tablePosition);
83
+ if (!table || table.type.spec["tableRole"] !== "table") return { type: "unsupported" };
84
+ const map = TableMap.get(table);
85
+ const columnIndex = deletion.columnIndex + insertedColumnCount;
86
+ if (columnIndex < 0 || columnIndex >= map.width) return { type: "unsupported" };
87
+ if (revision) {
88
+ const cellPositions = getTableColumnDeletionCellPositions({
89
+ map,
90
+ table,
91
+ columnIndex
92
+ });
93
+ if (!cellPositions) return { type: "unsupported" };
94
+ for (const cellPosition of cellPositions) tr.setNodeAttribute(tablePosition + 1 + cellPosition, "cellMarker", {
95
+ kind: "del",
96
+ info: revision
97
+ });
98
+ markStructuralChange(tr);
99
+ return applied(tr, revision);
100
+ }
101
+ if (map.width === 1) {
102
+ const transaction = deleteTableNode({
103
+ tr,
104
+ tablePosition,
105
+ table
106
+ });
107
+ return transaction ? applied(transaction, null) : { type: "unsupported" };
108
+ }
109
+ removeColumn(tr, {
110
+ map,
111
+ table,
112
+ tableStart: tablePosition + 1,
113
+ left: columnIndex,
114
+ top: 0,
115
+ right: columnIndex + 1,
116
+ bottom: map.height
117
+ }, columnIndex);
118
+ return applied(tr, null);
119
+ };
120
+ const applyTableRowDeletion = ({ tr, deletion, revision }) => {
121
+ if (revision) {
122
+ const rowPosition = tr.mapping.map(deletion.rowPosition);
123
+ const row = tr.doc.nodeAt(rowPosition);
124
+ if (!row || row.type.spec["tableRole"] !== "row" || row.attrs["trIns"] != null || row.attrs["trDel"] != null) return { type: "unsupported" };
125
+ tr.setNodeAttribute(rowPosition, "trDel", revision);
126
+ markStructuralChange(tr);
127
+ return applied(tr, revision);
128
+ }
129
+ const table = tr.doc.nodeAt(deletion.tablePosition);
130
+ if (!table || table.type.spec["tableRole"] !== "table" || deletion.rowIndex >= table.childCount) return { type: "unsupported" };
131
+ if (table.childCount === 1) {
132
+ const transaction = deleteTableNode({
133
+ tr,
134
+ tablePosition: deletion.tablePosition,
135
+ table
136
+ });
137
+ return transaction ? applied(transaction, null) : { type: "unsupported" };
138
+ }
139
+ const map = TableMap.get(table);
140
+ removeRow(tr, {
141
+ map,
142
+ table,
143
+ tableStart: deletion.tableStart,
144
+ left: 0,
145
+ top: deletion.rowIndex,
146
+ right: map.width,
147
+ bottom: deletion.rowIndex + 1
148
+ }, deletion.rowIndex);
149
+ return applied(tr, null);
150
+ };
151
+ const applied = (transaction, revision) => ({
152
+ type: "applied",
153
+ transaction,
154
+ revisionId: revision?.revisionId ?? null
155
+ });
156
+ const deleteTableNode = ({ tr, tablePosition, table }) => {
157
+ const tableEnd = tablePosition + table.nodeSize;
158
+ const resolved = tr.doc.resolve(tablePosition);
159
+ const parent = resolved.parent;
160
+ const tableIndex = resolved.index();
161
+ if (parent.canReplace(tableIndex, tableIndex + 1)) return tr.delete(tablePosition, tableEnd);
162
+ const emptyParagraph = tr.doc.type.schema.nodes["paragraph"]?.createAndFill();
163
+ if (!emptyParagraph || !parent.canReplaceWith(tableIndex, tableIndex + 1, emptyParagraph.type, emptyParagraph.marks)) return null;
164
+ return tr.replaceWith(tablePosition, tableEnd, emptyParagraph);
165
+ };
166
+ const buildTableRowInsertion = ({ map, table, tableStart, rowIndex }) => {
167
+ const cells = [];
168
+ const rowspanUpdates = [];
169
+ let referenceRow = rowIndex > 0 ? -1 : 0;
170
+ if (rowIsHeader(map, table, rowIndex + referenceRow)) referenceRow = rowIndex === 0 || rowIndex === map.height ? null : 0;
171
+ for (let col = 0, index = map.width * rowIndex; col < map.width; col++, index++) {
172
+ if (rowIndex > 0 && rowIndex < map.height && map.map[index] === map.map[index - map.width]) {
173
+ const cellPosition = map.map[index];
174
+ if (cellPosition === void 0) return null;
175
+ const cell = table.nodeAt(cellPosition);
176
+ if (!cell) return null;
177
+ const colspan = cell.attrs["colspan"];
178
+ if (typeof colspan !== "number" || colspan < 1) return null;
179
+ rowspanUpdates.push(cellPosition);
180
+ col += colspan - 1;
181
+ index += colspan - 1;
182
+ continue;
183
+ }
184
+ const referencePosition = referenceRow === null ? void 0 : map.map[index + referenceRow * map.width];
185
+ const cell = (referencePosition === void 0 ? tableNodeTypes(table.type.schema).cell : table.nodeAt(referencePosition)?.type)?.createAndFill();
186
+ if (!cell) return null;
187
+ cells.push(cell);
188
+ }
189
+ if (cells.length === 0) return null;
190
+ let rowPosition = tableStart;
191
+ for (let index = 0; index < rowIndex; index++) rowPosition += table.child(index).nodeSize;
192
+ return {
193
+ tableStart,
194
+ rowPosition,
195
+ rowType: tableNodeTypes(table.type.schema).row,
196
+ cells,
197
+ rowspanUpdates
198
+ };
199
+ };
200
+ const populateTableRow = (row, cellTexts) => {
201
+ if (cellTexts === void 0) return row;
202
+ const cells = [];
203
+ row.forEach((cell, _offset, index) => {
204
+ const paragraph = cell.firstChild;
205
+ if (!paragraph?.isTextblock) {
206
+ cells.push(cell);
207
+ return;
208
+ }
209
+ const text = cellTexts[index] ?? "";
210
+ const content = text.length > 0 ? row.type.schema.text(text) : null;
211
+ const nextParagraph = paragraph.type.create(stripBlockIdentityAttrs(paragraph.attrs), content);
212
+ cells.push(cell.type.create(cell.attrs, nextParagraph));
213
+ });
214
+ return row.type.create(row.attrs, cells);
215
+ };
216
+ const getInsertedPhysicalColumnRows = (map, columnIndex) => {
217
+ const rows = [];
218
+ for (let row = 0; row < map.height; row++) {
219
+ const index = row * map.width + columnIndex;
220
+ if (!(columnIndex > 0 && columnIndex < map.width && map.map[index - 1] === map.map[index])) rows.push(row);
221
+ }
222
+ return rows;
223
+ };
224
+ const expandTableCellColspan = (cell, colspanOffset) => {
225
+ const colspan = cell.attrs["colspan"];
226
+ const rowspan = cell.attrs["rowspan"];
227
+ const colwidth = cell.attrs["colwidth"];
228
+ if (typeof colspan !== "number" || !Number.isInteger(colspan) || colspan < 1 || typeof rowspan !== "number" || !Number.isInteger(rowspan) || rowspan < 1 || !Number.isInteger(colspanOffset) || colspanOffset < 0 || colspanOffset > colspan) return null;
229
+ if (colwidth !== null && !Array.isArray(colwidth)) return null;
230
+ if (Array.isArray(colwidth) && !colwidth.every((width) => typeof width === "number")) return null;
231
+ const nextColwidth = Array.isArray(colwidth) ? [...colwidth] : null;
232
+ nextColwidth?.splice(colspanOffset, 0, 0);
233
+ return {
234
+ ...cell.attrs,
235
+ colspan: colspan + 1,
236
+ colwidth: nextColwidth
237
+ };
238
+ };
239
+ const populateTableCell = (cell, text) => {
240
+ const paragraph = cell.firstChild;
241
+ if (!paragraph?.isTextblock) return null;
242
+ const content = text.length > 0 ? cell.type.schema.text(text) : null;
243
+ const nextParagraph = paragraph.type.create(stripBlockIdentityAttrs(paragraph.attrs), content);
244
+ return cell.type.create(cell.attrs, nextParagraph);
245
+ };
246
+ const buildTableColumnInsertionActions = ({ map, table, columnIndex, cellTexts }) => {
247
+ if (columnIndex < 0 || columnIndex > map.width) return null;
248
+ const physicalRows = getInsertedPhysicalColumnRows(map, columnIndex);
249
+ if ((cellTexts?.length ?? 0) > physicalRows.length) return null;
250
+ let referenceColumn = columnIndex > 0 ? -1 : 0;
251
+ if (columnIsHeader(map, table, columnIndex + referenceColumn)) referenceColumn = columnIndex === 0 || columnIndex === map.width ? null : 0;
252
+ const actions = [];
253
+ let physicalCellIndex = 0;
254
+ for (let row = 0; row < map.height; row++) {
255
+ const index = row * map.width + columnIndex;
256
+ if (columnIndex > 0 && columnIndex < map.width && map.map[index - 1] === map.map[index]) {
257
+ const position = map.map[index];
258
+ if (position === void 0) return null;
259
+ const cell = table.nodeAt(position);
260
+ if (!cell) return null;
261
+ const colspanOffset = columnIndex - map.colCount(position);
262
+ const attrs = expandTableCellColspan(cell, colspanOffset);
263
+ if (!attrs) return null;
264
+ actions.push({
265
+ type: "setColspan",
266
+ position,
267
+ attrs
268
+ });
269
+ row += Number(cell.attrs["rowspan"]) - 1;
270
+ continue;
271
+ }
272
+ const referencePosition = referenceColumn === null ? void 0 : map.map[index + referenceColumn];
273
+ const cell = (referencePosition === void 0 ? tableNodeTypes(table.type.schema).cell : table.nodeAt(referencePosition)?.type)?.createAndFill();
274
+ if (!cell) return null;
275
+ const populatedCell = populateTableCell(cell, cellTexts?.[physicalCellIndex] ?? "");
276
+ if (!populatedCell) return null;
277
+ actions.push({
278
+ type: "insertCell",
279
+ position: map.positionAt(row, columnIndex, table),
280
+ cell: populatedCell
281
+ });
282
+ physicalCellIndex++;
283
+ }
284
+ return actions;
285
+ };
286
+ const getTableColumnDeletionCellPositions = ({ map, table, columnIndex }) => {
287
+ if (columnIndex < 0 || columnIndex >= map.width) return null;
288
+ const positions = /* @__PURE__ */ new Set();
289
+ for (let row = 0; row < map.height; row++) {
290
+ const position = map.map[row * map.width + columnIndex];
291
+ if (position === void 0) return null;
292
+ const cell = table.nodeAt(position);
293
+ const tableRole = cell?.type.spec["tableRole"];
294
+ if (!cell || tableRole !== "cell" && tableRole !== "header_cell" || cell.attrs["colspan"] !== 1 || cell.attrs["cellMarker"] != null) return null;
295
+ positions.add(position);
296
+ }
297
+ return [...positions];
298
+ };
299
+ //#endregion
300
+ export { applyTableColumnDeletion, applyTableColumnInsertion, applyTableRowDeletion, applyTableRowInsertion, findTableColumnInsertion, findTableRowInsertion, getTableColumnCoordinateKey };
@@ -0,0 +1,32 @@
1
+ import { TableRectangle } from "./table-mutation-plan.js";
2
+ import { Node } from "prosemirror-model";
3
+ import { TableMap } from "prosemirror-tables";
4
+
5
+ //#region src/ai-edits/table-targets.d.ts
6
+ type TableBoundary = {
7
+ before: number;
8
+ after: number;
9
+ };
10
+ type TableRowTarget = {
11
+ table: Node;
12
+ tableStart: number;
13
+ tablePosition: number;
14
+ rowIndex: number;
15
+ rowPosition: number;
16
+ };
17
+ type TableCellTarget = {
18
+ tablePosition: number;
19
+ cellPosition: number;
20
+ cellEndPosition: number;
21
+ leftColumnIndex: number;
22
+ rightColumnIndex: number;
23
+ topRowIndex: number;
24
+ bottomRowIndex: number;
25
+ };
26
+ /** Resolve the nearest enclosing table boundary for block-adjacent insertions. */
27
+ declare const findEnclosingTableBoundary: (doc: Node, blockFrom: number) => TableBoundary | null;
28
+ declare const findEnclosingTableRow: (doc: Node, blockFrom: number) => TableRowTarget | null;
29
+ declare const findEnclosingTableCell: (doc: Node, blockFrom: number) => TableCellTarget | null;
30
+ declare const tableRectangleCutsMergedCell: (map: TableMap, rectangle: TableRectangle) => boolean;
31
+ //#endregion
32
+ export { TableBoundary, TableCellTarget, TableRowTarget, findEnclosingTableBoundary, findEnclosingTableCell, findEnclosingTableRow, tableRectangleCutsMergedCell };
@@ -0,0 +1,70 @@
1
+ import { TableMap } from "prosemirror-tables";
2
+ //#region src/ai-edits/table-targets.ts
3
+ /** Resolve the nearest enclosing table boundary for block-adjacent insertions. */
4
+ const findEnclosingTableBoundary = (doc, blockFrom) => {
5
+ const resolved = doc.resolve(blockFrom);
6
+ for (let depth = resolved.depth; depth > 0; depth--) if (resolved.node(depth).type.name === "table") return {
7
+ before: resolved.before(depth),
8
+ after: resolved.after(depth)
9
+ };
10
+ return null;
11
+ };
12
+ const findEnclosingTableRow = (doc, blockFrom) => {
13
+ const resolved = doc.resolve(blockFrom);
14
+ for (let rowDepth = resolved.depth; rowDepth > 0; rowDepth--) {
15
+ if (resolved.node(rowDepth).type.spec["tableRole"] !== "row") continue;
16
+ const tableDepth = rowDepth - 1;
17
+ const table = resolved.node(tableDepth);
18
+ if (table.type.spec["tableRole"] !== "table") return null;
19
+ return {
20
+ table,
21
+ tableStart: resolved.start(tableDepth),
22
+ tablePosition: resolved.before(tableDepth),
23
+ rowIndex: resolved.index(tableDepth),
24
+ rowPosition: resolved.before(rowDepth)
25
+ };
26
+ }
27
+ return null;
28
+ };
29
+ const findEnclosingTableCell = (doc, blockFrom) => {
30
+ const resolved = doc.resolve(blockFrom);
31
+ for (let cellDepth = resolved.depth; cellDepth > 0; cellDepth--) {
32
+ const cell = resolved.node(cellDepth);
33
+ const tableRole = cell.type.spec["tableRole"];
34
+ if (tableRole !== "cell" && tableRole !== "header_cell") continue;
35
+ const rowDepth = cellDepth - 1;
36
+ const tableDepth = rowDepth - 1;
37
+ if (tableDepth < 0) return null;
38
+ if (resolved.node(rowDepth).type.spec["tableRole"] !== "row" || resolved.node(tableDepth).type.spec["tableRole"] !== "table") return null;
39
+ const table = resolved.node(tableDepth);
40
+ const tableStart = resolved.start(tableDepth);
41
+ const cellPosition = resolved.before(cellDepth);
42
+ const cellRectangle = TableMap.get(table).findCell(cellPosition - tableStart);
43
+ return {
44
+ tablePosition: resolved.before(tableDepth),
45
+ cellPosition,
46
+ cellEndPosition: cellPosition + cell.nodeSize,
47
+ leftColumnIndex: cellRectangle.left,
48
+ rightColumnIndex: cellRectangle.right,
49
+ topRowIndex: cellRectangle.top,
50
+ bottomRowIndex: cellRectangle.bottom
51
+ };
52
+ }
53
+ return null;
54
+ };
55
+ const tableRectangleCutsMergedCell = (map, rectangle) => {
56
+ if (rectangle.left < 0 || rectangle.top < 0 || rectangle.right > map.width || rectangle.bottom > map.height) return false;
57
+ for (let row = rectangle.top; row < rectangle.bottom; row++) {
58
+ const leftIndex = row * map.width + rectangle.left;
59
+ const rightIndex = row * map.width + rectangle.right - 1;
60
+ if (rectangle.left > 0 && map.map[leftIndex] === map.map[leftIndex - 1] || rectangle.right < map.width && map.map[rightIndex] === map.map[rightIndex + 1]) return true;
61
+ }
62
+ for (let column = rectangle.left; column < rectangle.right; column++) {
63
+ const topIndex = rectangle.top * map.width + column;
64
+ const bottomIndex = (rectangle.bottom - 1) * map.width + column;
65
+ if (rectangle.top > 0 && map.map[topIndex] === map.map[topIndex - map.width] || rectangle.bottom < map.height && map.map[bottomIndex] === map.map[bottomIndex + map.width]) return true;
66
+ }
67
+ return false;
68
+ };
69
+ //#endregion
70
+ export { findEnclosingTableBoundary, findEnclosingTableCell, findEnclosingTableRow, tableRectangleCutsMergedCell };
@@ -196,6 +196,40 @@ type FolioAIEditOperation = FolioAIEditReviewMeta & {
196
196
  position?: "after" | "before";
197
197
  parties: FolioAISignatureParty[];
198
198
  comment?: FolioAIComment;
199
+ } | {
200
+ id: string;
201
+ type: "insertTableRow"; /** Stable paragraph anchor inside the row that receives the new sibling. */
202
+ blockId: string;
203
+ position?: "after" | "before"; /** Initial text for each physical cell in source order; omitted cells stay empty. */
204
+ cellTexts?: string[];
205
+ } | {
206
+ id: string;
207
+ type: "deleteTableRow"; /** Stable paragraph anchor inside the row to delete. */
208
+ blockId: string;
209
+ } | {
210
+ id: string;
211
+ type: "insertTableColumn"; /** Stable paragraph anchor inside the cell that receives the new sibling column. */
212
+ blockId: string;
213
+ position?: "after" | "before"; /** Initial text for newly created physical cells in row order. */
214
+ cellTexts?: string[];
215
+ } | {
216
+ id: string;
217
+ type: "deleteTableColumn"; /** Stable paragraph anchor inside the column to delete. */
218
+ blockId: string;
219
+ } | ({
220
+ id: string;
221
+ type: "mergeTableCells"; /** Stable paragraph anchor inside the first cell. */
222
+ blockId: string;
223
+ } & ({
224
+ /** Stable paragraph anchor inside the opposite corner cell. */endBlockId: string;
225
+ rowCount?: never;
226
+ } | {
227
+ /** Number of grid rows to merge downward from the anchored cell. */rowCount: number;
228
+ endBlockId?: never;
229
+ })) | {
230
+ id: string;
231
+ type: "splitTableCell"; /** Stable paragraph anchor inside the cell to split. */
232
+ blockId: string;
199
233
  });
200
234
  type FolioAIEditApplyMode = "direct" | "tracked-changes";
201
235
  type FolioAIEditSkipReason = "missingBlock" | "changedBlock" | "ambiguousFind" | "missingFind" | "unsupportedBlock" | "unsupportedMode" | "atomicBatchRejected" | "preconditionFailed" | "staleRange" | "emptyOperation"
@@ -1,5 +1,6 @@
1
1
  import { deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "../types/block-id.js";
2
2
  import { createFolioAIEditSnapshot, hashFolioAIBlockText, normalizeFolioAIBlockText } from "../ai-edits/snapshot.js";
3
+ import { getGoogleFontsEnabled, setGoogleFontsEnabled } from "../utils/fontResolver.js";
3
4
  import { diffWordSegments } from "../ai-edits/word-diff.js";
4
5
  import { applyFolioAIEditOperations } from "../ai-edits/apply.js";
5
6
  import { 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, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "../document-operations.js";
@@ -13,7 +14,6 @@ import { DEFAULT_AI_SUGGESTION_PRESETS } from "../ai-suggestions/types.js";
13
14
  import { buildPositionalText } from "../ai-suggestions/text-positions.js";
14
15
  import { isSuggestionStale, resolveSuggestionAnchor } from "../ai-suggestions/conflict.js";
15
16
  import { applySuggestions } from "../ai-suggestions/apply.js";
16
- import { getGoogleFontsEnabled, setGoogleFontsEnabled } from "../utils/fontResolver.js";
17
17
  import { setAISuggestionsMeta, setFocusedSuggestionMeta } from "../prosemirror/plugins/aiSuggestionDecorations.js";
18
18
  import { scrollFolioPositionIntoView } from "../paged-layout/scrollToPmPosition.js";
19
19
  import { getFolioCaretViewportRect, getFolioSelectionViewportRect } from "../paged-layout/selectionViewportRect.js";
@@ -1,5 +1,5 @@
1
- import { formatDate } from "../docx/fieldParser.js";
2
1
  import { ContentControlBoundError, ContentControlLockedError, ContentControlTypeError } from "./errors.js";
2
+ import { formatDate } from "../docx/fieldParser.js";
3
3
  //#region src/content-controls/mutateContentControls.ts
4
4
  /**
5
5
  * Pure (immutable) write helpers for block-level content controls.
@@ -0,0 +1,36 @@
1
+ import { EditorView } from "prosemirror-view";
2
+
3
+ //#region src/controller/activeEditorStory.d.ts
4
+ type ActiveEditorStoryTarget = {
5
+ type: "note";
6
+ view: EditorView;
7
+ } | {
8
+ type: "headerFooter";
9
+ view: EditorView;
10
+ } | {
11
+ type: "body";
12
+ view: EditorView;
13
+ } | {
14
+ type: "none";
15
+ view: null;
16
+ };
17
+ type ActiveEditorStoryCandidates = {
18
+ bodyView: EditorView | null;
19
+ headerFooterView: EditorView | null;
20
+ noteView: EditorView | null;
21
+ };
22
+ /**
23
+ * Resolve the single command target across editable document stories.
24
+ *
25
+ * A visible note panel takes precedence over header/footer mode, which takes
26
+ * precedence over the body. Keeping this order in core prevents framework
27
+ * adapters from sending formatting, focus, or history commands to a story
28
+ * hidden behind another active editor surface during an async UI transition.
29
+ */
30
+ declare const resolveActiveEditorStory: ({
31
+ bodyView,
32
+ headerFooterView,
33
+ noteView
34
+ }: ActiveEditorStoryCandidates) => ActiveEditorStoryTarget;
35
+ //#endregion
36
+ export { ActiveEditorStoryCandidates, ActiveEditorStoryTarget, resolveActiveEditorStory };
@@ -0,0 +1,30 @@
1
+ //#region src/controller/activeEditorStory.ts
2
+ const NO_ACTIVE_STORY = {
3
+ type: "none",
4
+ view: null
5
+ };
6
+ /**
7
+ * Resolve the single command target across editable document stories.
8
+ *
9
+ * A visible note panel takes precedence over header/footer mode, which takes
10
+ * precedence over the body. Keeping this order in core prevents framework
11
+ * adapters from sending formatting, focus, or history commands to a story
12
+ * hidden behind another active editor surface during an async UI transition.
13
+ */
14
+ const resolveActiveEditorStory = ({ bodyView, headerFooterView, noteView }) => {
15
+ if (noteView) return {
16
+ type: "note",
17
+ view: noteView
18
+ };
19
+ if (headerFooterView) return {
20
+ type: "headerFooter",
21
+ view: headerFooterView
22
+ };
23
+ if (bodyView) return {
24
+ type: "body",
25
+ view: bodyView
26
+ };
27
+ return NO_ACTIVE_STORY;
28
+ };
29
+ //#endregion
30
+ export { resolveActiveEditorStory };
@@ -0,0 +1,55 @@
1
+ import { ContentControlWidgetEvent } from "../prosemirror/plugins/contentControlWidgets.js";
2
+ import { Subscribable } from "../managers/Subscribable.js";
3
+ import { EditorView } from "prosemirror-view";
4
+
5
+ //#region src/controller/contentControlWidgetController.d.ts
6
+ type ContentControlListItem = {
7
+ displayText: string;
8
+ value: string;
9
+ };
10
+ type ContentControlPickerPosition = {
11
+ x: number;
12
+ y: number;
13
+ };
14
+ type ContentControlWidgetSnapshot = {
15
+ status: "closed";
16
+ } | {
17
+ status: "dropdown";
18
+ items: readonly ContentControlListItem[];
19
+ pmPos: number;
20
+ position: ContentControlPickerPosition;
21
+ } | {
22
+ status: "date";
23
+ currentValue: string | null;
24
+ pmPos: number;
25
+ position: ContentControlPickerPosition;
26
+ };
27
+ type ContentControlWidgetControllerOptions = {
28
+ onRefused?: ((event: Extract<ContentControlWidgetEvent, {
29
+ kind: "refused";
30
+ }>) => void) | undefined;
31
+ };
32
+ declare const parseContentControlListItems: (raw: string | undefined) => ContentControlListItem[];
33
+ declare class ContentControlWidgetController extends Subscribable<ContentControlWidgetSnapshot> {
34
+ private readonly options;
35
+ private view;
36
+ private viewDom;
37
+ private eventRoot;
38
+ private ownerWindow;
39
+ constructor(options?: ContentControlWidgetControllerOptions);
40
+ bind(view: EditorView | null, eventRoot?: HTMLElement | null): void;
41
+ handleWidgetEvent(event: ContentControlWidgetEvent): void;
42
+ pickDropdown(value: string): boolean;
43
+ pickDate(value: string): boolean;
44
+ close(): void;
45
+ destroy(): void;
46
+ private readonly onDomEvent;
47
+ private readonly onPaintedClick;
48
+ private readonly onKeyDown;
49
+ private readonly onScroll;
50
+ private open;
51
+ private removeDismissListeners;
52
+ private unbindView;
53
+ }
54
+ //#endregion
55
+ export { ContentControlListItem, ContentControlPickerPosition, ContentControlWidgetController, ContentControlWidgetControllerOptions, ContentControlWidgetSnapshot, parseContentControlListItems };