@stll/folio-core 0.1.2 → 0.2.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 (101) hide show
  1. package/dist/docx/headerFooterParser.js +3 -0
  2. package/dist/docx/headerFooterVerbatim.d.ts +19 -0
  3. package/dist/docx/headerFooterVerbatim.js +25 -0
  4. package/dist/docx/index.d.ts +2 -0
  5. package/dist/docx/index.js +2 -0
  6. package/dist/docx/metafileRaster.d.ts +30 -0
  7. package/dist/docx/metafileRaster.js +80 -0
  8. package/dist/docx/paragraphParser.js +5 -1
  9. package/dist/docx/parser.d.ts +12 -2
  10. package/dist/docx/parser.js +31 -1
  11. package/dist/docx/serializer/headerFooterSerializer.js +3 -0
  12. package/dist/docx/styleParser.js +2 -2
  13. package/dist/docx/tableParser.js +2 -3
  14. package/dist/docx/unzip.js +7 -1
  15. package/dist/docx/wrapTypes.d.ts +8 -1
  16. package/dist/docx/wrapTypes.js +0 -8
  17. package/dist/docx/xmlParser.d.ts +7 -1
  18. package/dist/docx/xmlParser.js +17 -1
  19. package/dist/i18n/messages/catalogs.gen.d.ts +2776 -0
  20. package/dist/i18n/messages/catalogs.gen.js +2742 -0
  21. package/dist/i18n/messages/messages.gen.d.ts +166 -0
  22. package/dist/i18n/messages/messages.gen.js +0 -0
  23. package/dist/i18n/messages.d.ts +13 -0
  24. package/dist/i18n/messages.js +39 -0
  25. package/dist/layout-bridge/cellSelectionHighlight.d.ts +18 -0
  26. package/dist/layout-bridge/cellSelectionHighlight.js +36 -0
  27. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +3 -2
  28. package/dist/layout-bridge/convert/headerFooterLayout.js +39 -15
  29. package/dist/layout-bridge/convert/toFlowBlocks.js +1 -0
  30. package/dist/layout-bridge/headerFooterLayout.d.ts +50 -0
  31. package/dist/layout-bridge/headerFooterLayout.js +216 -0
  32. package/dist/layout-bridge/measuring/measureBlocksPipeline.d.ts +33 -0
  33. package/dist/layout-bridge/measuring/measureBlocksPipeline.js +0 -0
  34. package/dist/layout-bridge/sectionColumns.d.ts +11 -0
  35. package/dist/layout-bridge/sectionColumns.js +21 -0
  36. package/dist/layout-bridge/tableInsertHover.d.ts +42 -0
  37. package/dist/layout-bridge/tableInsertHover.js +102 -0
  38. package/dist/layout-engine/index.d.ts +2 -1
  39. package/dist/layout-engine/index.js +2 -1
  40. package/dist/layout-engine/measure/lineBreaks.d.ts +10 -0
  41. package/dist/layout-engine/measure/lineBreaks.js +30 -0
  42. package/dist/layout-engine/measure/measureContainer.js +1 -1
  43. package/dist/layout-engine/measure/measureParagraph.js +63 -22
  44. package/dist/layout-engine/pmPageIndex.d.ts +24 -0
  45. package/dist/layout-engine/pmPageIndex.js +37 -0
  46. package/dist/layout-engine/types.d.ts +2 -1
  47. package/dist/layout-painter/imageLayout.d.ts +101 -0
  48. package/dist/layout-painter/imageLayout.js +161 -0
  49. package/dist/layout-painter/renderPage.d.ts +16 -1
  50. package/dist/layout-painter/renderPage.js +28 -1
  51. package/dist/layout-painter/renderParagraph.js +2 -1
  52. package/dist/layout-painter/sdtBoundary.d.ts +24 -1
  53. package/dist/layout-painter/sdtBoundary.js +40 -1
  54. package/dist/managers/AutoSaveManager.d.ts +89 -0
  55. package/dist/managers/AutoSaveManager.js +279 -0
  56. package/dist/managers/ClipboardManager.d.ts +31 -0
  57. package/dist/managers/ClipboardManager.js +147 -0
  58. package/dist/paged-layout/paragraphFlash.d.ts +17 -0
  59. package/dist/paged-layout/paragraphFlash.js +56 -0
  60. package/dist/paged-layout/paragraphFlashTypes.d.ts +18 -0
  61. package/dist/paged-layout/paragraphFlashTypes.js +0 -0
  62. package/dist/paged-layout/rangeProjection.d.ts +23 -1
  63. package/dist/paged-layout/rangeProjection.js +34 -1
  64. package/dist/prosemirror/cellDragSelection.d.ts +34 -0
  65. package/dist/prosemirror/cellDragSelection.js +69 -0
  66. package/dist/prosemirror/commands/sectionBreak.d.ts +15 -0
  67. package/dist/prosemirror/commands/sectionBreak.js +54 -0
  68. package/dist/prosemirror/commentIdAllocator.d.ts +32 -0
  69. package/dist/prosemirror/commentIdAllocator.js +35 -0
  70. package/dist/prosemirror/commentOps.d.ts +35 -0
  71. package/dist/prosemirror/commentOps.js +104 -0
  72. package/dist/prosemirror/conversion/toProseDoc.js +29 -6
  73. package/dist/prosemirror/extensions/index.d.ts +3 -0
  74. package/dist/prosemirror/extensions/index.js +3 -0
  75. package/dist/prosemirror/imageCommit.d.ts +42 -0
  76. package/dist/prosemirror/imageCommit.js +118 -0
  77. package/dist/prosemirror/paraText.d.ts +26 -0
  78. package/dist/prosemirror/paraText.js +73 -0
  79. package/dist/prosemirror/plugins/templateDirectives.d.ts +21 -1
  80. package/dist/prosemirror/plugins/templateDirectives.js +40 -1
  81. package/dist/prosemirror/queries.d.ts +46 -0
  82. package/dist/prosemirror/queries.js +74 -0
  83. package/dist/prosemirror/styles/styleResolver.d.ts +43 -1
  84. package/dist/prosemirror/styles/styleResolver.js +34 -0
  85. package/dist/prosemirror/tableResize.d.ts +49 -0
  86. package/dist/prosemirror/tableResize.js +162 -0
  87. package/dist/prosemirror/utils/extractTrackedChanges.d.ts +106 -0
  88. package/dist/prosemirror/utils/extractTrackedChanges.js +379 -0
  89. package/dist/prosemirror/utils/visualLineNavigation.d.ts +27 -0
  90. package/dist/prosemirror/utils/visualLineNavigation.js +217 -0
  91. package/dist/style-engine/index.d.ts +2 -2
  92. package/dist/style-engine/styleEngine.d.ts +14 -1
  93. package/dist/style-engine/styleEngine.js +15 -0
  94. package/dist/utils/colorResolver.d.ts +8 -1
  95. package/dist/utils/colorResolver.js +12 -1
  96. package/dist/utils/findVerticalScrollParent.d.ts +20 -0
  97. package/dist/utils/findVerticalScrollParent.js +30 -0
  98. package/dist/utils/fontResolver.d.ts +22 -3
  99. package/dist/utils/fontResolver.js +216 -31
  100. package/dist/utils/headerFooter.js +2 -0
  101. package/package.json +9 -1
@@ -0,0 +1,106 @@
1
+ import { EditorState } from "prosemirror-state";
2
+
3
+ //#region src/prosemirror/utils/extractTrackedChanges.d.ts
4
+ /**
5
+ * One tracked change surfaced by {@link extractTrackedChanges}. Each entry
6
+ * groups all sites of one revision into a single row that the sidebar
7
+ * renders as one card.
8
+ *
9
+ * @public
10
+ */
11
+ type TrackedChangeEntry = {
12
+ /**
13
+ * Revision shape. Inline shapes (`insertion`, `deletion`, `replacement`)
14
+ * wrap text runs; the rest are structural revisions on node attrs.
15
+ *
16
+ * - `insertion` — text was added (`<w:ins>`).
17
+ * - `deletion` — text was struck through but not removed (`<w:del>`).
18
+ * - `replacement` — a deletion + insertion by the same author at the
19
+ * same position+time; sidebar shows one combined card. `deletedText`
20
+ * and `insertionRevisionId` are set on this variant.
21
+ * - `paragraphMarkInsertion` / `paragraphMarkDeletion` — Enter /
22
+ * Backspace produced a tracked paragraph break (`<w:pPr><w:rPr><w:ins/>` /
23
+ * `<w:del/>`).
24
+ * - `paragraphPropertiesChanged` — formatting (alignment, spacing,
25
+ * etc.) on the paragraph was changed (`<w:pPrChange>`).
26
+ * - `rowInserted` / `rowDeleted` / `rowPropertiesChanged` — table
27
+ * row authored / removed / formatted (`<w:trPr><w:ins/>` / `<w:del/>`
28
+ * / `<w:trPrChange>`).
29
+ * - `cellInserted` / `cellDeleted` / `cellMerged` /
30
+ * `cellPropertiesChanged` — per-cell revisions
31
+ * (`<w:cellIns>` / `<w:cellDel>` / `<w:cellMerge>` / `<w:tcPrChange>`).
32
+ * - `tablePropertiesChanged` — table-level formatting
33
+ * (`<w:tblPrChange>`).
34
+ */
35
+ type: "insertion" | "deletion" | "replacement" | "paragraphMarkInsertion" | "paragraphMarkDeletion" | "paragraphPropertiesChanged" | "rowInserted" | "rowDeleted" | "rowPropertiesChanged" | "cellInserted" | "cellDeleted" | "cellMerged" | "cellPropertiesChanged" | "tableInserted" | "tableDeleted" | "tablePropertiesChanged";
36
+ /**
37
+ * Affected text. For inline types this is the run's text; for
38
+ * structural types it's the surrounding paragraph / cell content
39
+ * (truncated by the sidebar before display).
40
+ */
41
+ text: string;
42
+ /**
43
+ * Only set when `type === 'replacement'` — the text the user removed.
44
+ * The inserted text lives in {@link TrackedChangeEntry.text}.
45
+ */
46
+ deletedText?: string | undefined; /** Author that minted the revision (`w:author`). */
47
+ author: string; /** ISO timestamp the revision was minted (`w:date`). May be undefined for legacy imports. */
48
+ date?: string | undefined;
49
+ /**
50
+ * Document position where the revision starts. For inline types this
51
+ * is the start of the marked text run; for structural types it's the
52
+ * containing paragraph / row / cell / table node's start position.
53
+ */
54
+ from: number;
55
+ /**
56
+ * Document position where the revision ends. For inline coalesced
57
+ * runs that span multiple paragraphs, this is the END position of the
58
+ * LAST run in the group; the intervening structural positions are not
59
+ * preserved.
60
+ */
61
+ to: number; /** The `w:id` of the revision. */
62
+ revisionId: number;
63
+ /**
64
+ * Only set when `type === 'replacement'` — the insertion half carries
65
+ * a DIFFERENT `w:id` from the deletion (sharing would trip the OOXML
66
+ * move-pair serializer).
67
+ */
68
+ insertionRevisionId?: number | undefined;
69
+ /**
70
+ * Extra `w:id`s that map to the same logical revision as this card.
71
+ * Populated when the extractor coalesces a burst of distinct ids by
72
+ * (author, date) — e.g. a foreign document where the source editor
73
+ * minted a fresh id per atomic edit.
74
+ */
75
+ coalescedRevisionIds?: number[] | undefined;
76
+ };
77
+ /**
78
+ * Output of {@link extractTrackedChanges}.
79
+ *
80
+ * @public
81
+ */
82
+ type TrackedChangesResult = {
83
+ /** Tracked-change entries, sorted by document position, with adjacent same-revision entries merged. */entries: TrackedChangeEntry[];
84
+ /**
85
+ * Map of `commentId -> revisionId` for comments whose range overlaps a tracked-change mark.
86
+ * Consumers (DocxEditor's threading effect) use this to thread comments under their tracked change.
87
+ */
88
+ commentToRevision: Map<number, number>;
89
+ };
90
+ /**
91
+ * Walk the PM doc and extract every tracked change as a flat list of
92
+ * `TrackedChangeEntry` plus a comment→revision overlap map. Adjacent
93
+ * inline marks coalesce by `(type, revisionId, author, date)`; a
94
+ * deletion immediately followed by an insertion (same author + same
95
+ * date) collapses into a single `replacement` entry; paragraph-mark
96
+ * cards (`paragraphMarkInsertion` / `paragraphMarkDeletion`) are
97
+ * hidden when an inline entry already covers their revision triple
98
+ * (one Accept clears every site of one conceptual change).
99
+ *
100
+ * Pure and deterministic. Returns `EMPTY_RESULT` on null state.
101
+ *
102
+ * @public
103
+ */
104
+ declare function extractTrackedChanges(state: EditorState | null): TrackedChangesResult;
105
+ //#endregion
106
+ export { TrackedChangeEntry, TrackedChangesResult, extractTrackedChanges };
@@ -0,0 +1,379 @@
1
+ //#region src/prosemirror/utils/extractTrackedChanges.ts
2
+ const EMPTY_RESULT = {
3
+ entries: [],
4
+ commentToRevision: /* @__PURE__ */ new Map()
5
+ };
6
+ /**
7
+ * Walk the PM doc and extract every tracked change as a flat list of
8
+ * `TrackedChangeEntry` plus a comment→revision overlap map. Adjacent
9
+ * inline marks coalesce by `(type, revisionId, author, date)`; a
10
+ * deletion immediately followed by an insertion (same author + same
11
+ * date) collapses into a single `replacement` entry; paragraph-mark
12
+ * cards (`paragraphMarkInsertion` / `paragraphMarkDeletion`) are
13
+ * hidden when an inline entry already covers their revision triple
14
+ * (one Accept clears every site of one conceptual change).
15
+ *
16
+ * Pure and deterministic. Returns `EMPTY_RESULT` on null state.
17
+ *
18
+ * @public
19
+ */
20
+ function extractTrackedChanges(state) {
21
+ if (!state) return EMPTY_RESULT;
22
+ const { doc, schema } = state;
23
+ const insertionType = schema.marks["insertion"];
24
+ const deletionType = schema.marks["deletion"];
25
+ const commentType = schema.marks["comment"];
26
+ if (!insertionType && !deletionType) return EMPTY_RESULT;
27
+ const raw = [];
28
+ const commentToRevision = /* @__PURE__ */ new Map();
29
+ const commentMetadata = /* @__PURE__ */ new Map();
30
+ doc.descendants((node, pos) => {
31
+ if (node.type.name === "paragraph") {
32
+ const ins = node.attrs["pPrIns"];
33
+ const del = node.attrs["pPrDel"];
34
+ if (ins) raw.push({
35
+ type: "paragraphMarkInsertion",
36
+ text: node.textContent || "",
37
+ author: ins.author || "",
38
+ date: ins.date ?? void 0,
39
+ from: pos,
40
+ to: pos + node.nodeSize,
41
+ revisionId: ins.revisionId
42
+ });
43
+ if (del) raw.push({
44
+ type: "paragraphMarkDeletion",
45
+ text: node.textContent || "",
46
+ author: del.author || "",
47
+ date: del.date ?? void 0,
48
+ from: pos,
49
+ to: pos + node.nodeSize,
50
+ revisionId: del.revisionId
51
+ });
52
+ const pPrChange = node.attrs["pPrChange"];
53
+ if (Array.isArray(pPrChange)) for (const entry of pPrChange) {
54
+ if (!entry?.info || typeof entry.info.id !== "number") continue;
55
+ raw.push({
56
+ type: "paragraphPropertiesChanged",
57
+ text: node.textContent || "",
58
+ author: entry.info.author || "",
59
+ date: entry.info.date ?? void 0,
60
+ from: pos,
61
+ to: pos + node.nodeSize,
62
+ revisionId: entry.info.id
63
+ });
64
+ }
65
+ }
66
+ if (node.type.name === "tableRow") {
67
+ const trIns = node.attrs["trIns"];
68
+ const trDel = node.attrs["trDel"];
69
+ if (trIns) raw.push({
70
+ type: "rowInserted",
71
+ text: node.textContent || "",
72
+ author: trIns.author || "",
73
+ date: trIns.date ?? void 0,
74
+ from: pos,
75
+ to: pos + node.nodeSize,
76
+ revisionId: trIns.revisionId
77
+ });
78
+ if (trDel) raw.push({
79
+ type: "rowDeleted",
80
+ text: node.textContent || "",
81
+ author: trDel.author || "",
82
+ date: trDel.date ?? void 0,
83
+ from: pos,
84
+ to: pos + node.nodeSize,
85
+ revisionId: trDel.revisionId
86
+ });
87
+ const trPrChange = node.attrs["trPrChange"];
88
+ if (Array.isArray(trPrChange)) for (const entry of trPrChange) {
89
+ if (!entry?.info || typeof entry.info.id !== "number") continue;
90
+ raw.push({
91
+ type: "rowPropertiesChanged",
92
+ text: node.textContent || "",
93
+ author: entry.info.author || "",
94
+ date: entry.info.date ?? void 0,
95
+ from: pos,
96
+ to: pos + node.nodeSize,
97
+ revisionId: entry.info.id
98
+ });
99
+ }
100
+ }
101
+ if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
102
+ const cellMarker = node.attrs["cellMarker"];
103
+ if (cellMarker?.info && typeof cellMarker.info.revisionId === "number") {
104
+ const resolvedType = {
105
+ ins: "cellInserted",
106
+ del: "cellDeleted",
107
+ merge: "cellMerged"
108
+ }[cellMarker.kind];
109
+ if (resolvedType) raw.push({
110
+ type: resolvedType,
111
+ text: node.textContent || "",
112
+ author: cellMarker.info.author || "",
113
+ date: cellMarker.info.date ?? void 0,
114
+ from: pos,
115
+ to: pos + node.nodeSize,
116
+ revisionId: cellMarker.info.revisionId
117
+ });
118
+ }
119
+ const tcPrChange = node.attrs["tcPrChange"];
120
+ if (Array.isArray(tcPrChange)) for (const entry of tcPrChange) {
121
+ if (!entry?.info || typeof entry.info.id !== "number") continue;
122
+ raw.push({
123
+ type: "cellPropertiesChanged",
124
+ text: node.textContent || "",
125
+ author: entry.info.author || "",
126
+ date: entry.info.date ?? void 0,
127
+ from: pos,
128
+ to: pos + node.nodeSize,
129
+ revisionId: entry.info.id
130
+ });
131
+ }
132
+ }
133
+ if (node.type.name === "table") {
134
+ const tblPrChange = node.attrs["tblPrChange"];
135
+ if (Array.isArray(tblPrChange)) for (const entry of tblPrChange) {
136
+ if (!entry?.info || typeof entry.info.id !== "number") continue;
137
+ raw.push({
138
+ type: "tablePropertiesChanged",
139
+ text: "",
140
+ author: entry.info.author || "",
141
+ date: entry.info.date ?? void 0,
142
+ from: pos,
143
+ to: pos + node.nodeSize,
144
+ revisionId: entry.info.id
145
+ });
146
+ }
147
+ const firstRow = node.firstChild;
148
+ const firstIns = firstRow?.attrs["trIns"];
149
+ const firstDel = firstRow?.attrs["trDel"];
150
+ let sharedAttr = null;
151
+ if (firstIns) sharedAttr = "trIns";
152
+ else if (firstDel) sharedAttr = "trDel";
153
+ if (sharedAttr) {
154
+ const sharedRev = firstIns ?? firstDel;
155
+ let allShare = true;
156
+ const rowRevIds = [];
157
+ node.forEach((row) => {
158
+ if (row.type.name !== "tableRow") {
159
+ allShare = false;
160
+ return;
161
+ }
162
+ const v = row.attrs[sharedAttr];
163
+ if (!v || (v.author ?? "") !== (sharedRev.author ?? "") || (v.date ?? null) !== (sharedRev.date ?? null)) {
164
+ allShare = false;
165
+ return;
166
+ }
167
+ rowRevIds.push(v.revisionId);
168
+ });
169
+ if (allShare) {
170
+ let visibleText = "";
171
+ if (deletionType) node.descendants((child) => {
172
+ if (child.isText && !child.marks.some((m) => m.type === deletionType)) visibleText += child.text || "";
173
+ });
174
+ else visibleText = node.textContent || "";
175
+ const primaryId = sharedRev.revisionId;
176
+ const extraIds = rowRevIds.filter((id, idx) => id !== primaryId && rowRevIds.indexOf(id) === idx);
177
+ raw.push({
178
+ type: sharedAttr === "trIns" ? "tableInserted" : "tableDeleted",
179
+ text: visibleText,
180
+ author: sharedRev.author || "",
181
+ date: sharedRev.date ?? void 0,
182
+ from: pos,
183
+ to: pos + node.nodeSize,
184
+ revisionId: primaryId,
185
+ ...extraIds.length > 0 ? { coalescedRevisionIds: extraIds } : {}
186
+ });
187
+ }
188
+ }
189
+ }
190
+ if (!node.isInline) return;
191
+ let inlineText;
192
+ if (node.isText) inlineText = node.text || "";
193
+ else if (node.type.name === "image") inlineText = node.attrs["alt"] || "image";
194
+ else inlineText = node.type.name;
195
+ let tcMark = null;
196
+ for (const mark of node.marks) if (mark.type === insertionType || mark.type === deletionType) {
197
+ raw.push({
198
+ type: mark.type === insertionType ? "insertion" : "deletion",
199
+ text: inlineText,
200
+ author: mark.attrs["author"] || "",
201
+ date: mark.attrs["date"],
202
+ from: pos,
203
+ to: pos + node.nodeSize,
204
+ revisionId: mark.attrs["revisionId"]
205
+ });
206
+ tcMark = mark;
207
+ }
208
+ if (commentType) {
209
+ const commentMark = node.marks.find((m) => m.type === commentType);
210
+ if (commentMark) {
211
+ const cid = commentMark.attrs["commentId"];
212
+ let meta = commentMetadata.get(cid);
213
+ if (!meta) {
214
+ meta = {
215
+ revisions: /* @__PURE__ */ new Set(),
216
+ hasCleanText: false
217
+ };
218
+ commentMetadata.set(cid, meta);
219
+ }
220
+ if (tcMark) meta.revisions.add(tcMark.attrs["revisionId"]);
221
+ else meta.hasCleanText = true;
222
+ }
223
+ }
224
+ });
225
+ for (const [cid, meta] of commentMetadata.entries()) if (!meta.hasCleanText && meta.revisions.size === 1) {
226
+ const rid = meta.revisions.values().next().value;
227
+ commentToRevision.set(cid, rid);
228
+ }
229
+ const STRUCTURAL_PRIORITY = {
230
+ tableInserted: 6,
231
+ tableDeleted: 6,
232
+ tablePropertiesChanged: 5,
233
+ rowInserted: 4,
234
+ rowDeleted: 4,
235
+ rowPropertiesChanged: 4,
236
+ cellInserted: 3,
237
+ cellDeleted: 3,
238
+ cellMerged: 3,
239
+ cellPropertiesChanged: 3,
240
+ paragraphMarkInsertion: 2,
241
+ paragraphMarkDeletion: 2,
242
+ paragraphPropertiesChanged: 2
243
+ };
244
+ const isStructuralType = (t) => t in STRUCTURAL_PRIORITY;
245
+ const slotByKey = /* @__PURE__ */ new Map();
246
+ const ordered = [];
247
+ const mergeIds = (survivor, dropped) => {
248
+ const ids = new Set(survivor.coalescedRevisionIds ?? []);
249
+ for (const id of dropped.coalescedRevisionIds ?? []) ids.add(id);
250
+ ids.add(dropped.revisionId);
251
+ ids.delete(survivor.revisionId);
252
+ return [...ids];
253
+ };
254
+ for (const entry of raw) {
255
+ if (!isStructuralType(entry.type)) {
256
+ ordered.push(entry);
257
+ continue;
258
+ }
259
+ const key = `${entry.author}|${entry.date ?? ""}`;
260
+ const slot = slotByKey.get(key);
261
+ if (slot === void 0) {
262
+ slotByKey.set(key, ordered.push(entry) - 1);
263
+ continue;
264
+ }
265
+ const existing = ordered[slot];
266
+ if ((STRUCTURAL_PRIORITY[entry.type] ?? 0) > (STRUCTURAL_PRIORITY[existing.type] ?? 0)) ordered[slot] = {
267
+ ...entry,
268
+ coalescedRevisionIds: mergeIds(entry, existing)
269
+ };
270
+ else ordered[slot] = {
271
+ ...existing,
272
+ coalescedRevisionIds: mergeIds(existing, entry)
273
+ };
274
+ }
275
+ const inlineGroups = /* @__PURE__ */ new Map();
276
+ const merged = [];
277
+ for (const entry of ordered) {
278
+ if (!(entry.type === "insertion" || entry.type === "deletion")) {
279
+ merged.push({ ...entry });
280
+ continue;
281
+ }
282
+ const key = `${entry.type}|${entry.author}|${entry.date ?? ""}`;
283
+ const group = inlineGroups.get(key);
284
+ if (group) {
285
+ const sep = group.to === entry.from ? "" : " ";
286
+ group.text += sep + entry.text;
287
+ group.to = entry.to;
288
+ if (entry.revisionId !== group.revisionId) {
289
+ const ids = new Set(group.coalescedRevisionIds ?? []);
290
+ for (const id of entry.coalescedRevisionIds ?? []) ids.add(id);
291
+ ids.add(entry.revisionId);
292
+ ids.delete(group.revisionId);
293
+ group.coalescedRevisionIds = ids.size > 0 ? [...ids] : void 0;
294
+ }
295
+ } else {
296
+ const copy = { ...entry };
297
+ inlineGroups.set(key, copy);
298
+ merged.push(copy);
299
+ }
300
+ }
301
+ const final = [];
302
+ for (let i = 0; i < merged.length; i++) {
303
+ const curr = merged[i];
304
+ const next = merged[i + 1];
305
+ if (curr.type === "deletion" && next && next.type === "insertion" && curr.author === next.author && curr.date === next.date && curr.to === next.from) {
306
+ final.push({
307
+ type: "replacement",
308
+ text: next.text,
309
+ deletedText: curr.text,
310
+ author: curr.author,
311
+ date: curr.date,
312
+ from: curr.from,
313
+ to: next.to,
314
+ revisionId: curr.revisionId,
315
+ insertionRevisionId: next.revisionId
316
+ });
317
+ i++;
318
+ } else final.push(curr);
319
+ }
320
+ const inlineByKey = /* @__PURE__ */ new Map();
321
+ for (const e of final) if (e.type === "insertion" || e.type === "deletion" || e.type === "replacement") {
322
+ const k = `${e.author}|${e.date ?? ""}`;
323
+ if (!inlineByKey.has(k)) inlineByKey.set(k, e);
324
+ }
325
+ for (const e of final) {
326
+ if (e.type !== "paragraphMarkInsertion" && e.type !== "paragraphMarkDeletion") continue;
327
+ const survivor = inlineByKey.get(`${e.author}|${e.date ?? ""}`);
328
+ if (!survivor) continue;
329
+ const ids = new Set(survivor.coalescedRevisionIds ?? []);
330
+ for (const id of e.coalescedRevisionIds ?? []) ids.add(id);
331
+ ids.add(e.revisionId);
332
+ ids.delete(survivor.revisionId);
333
+ if (survivor.type === "replacement" && survivor.insertionRevisionId != null) ids.delete(survivor.insertionRevisionId);
334
+ survivor.coalescedRevisionIds = ids.size > 0 ? [...ids] : void 0;
335
+ }
336
+ const emptyTableEntries = /* @__PURE__ */ new Set();
337
+ const tableByKey = /* @__PURE__ */ new Map();
338
+ for (const e of final) {
339
+ if (e.type !== "tableInserted" && e.type !== "tableDeleted") continue;
340
+ const key = `${e.author}|${e.date ?? ""}`;
341
+ if (e.text.trim().length > 0) continue;
342
+ emptyTableEntries.add(e);
343
+ if (!tableByKey.has(key)) tableByKey.set(key, e);
344
+ }
345
+ for (const e of final) {
346
+ if (e.type !== "paragraphMarkInsertion" && e.type !== "paragraphMarkDeletion") continue;
347
+ const survivor = tableByKey.get(`${e.author}|${e.date ?? ""}`);
348
+ if (!survivor) continue;
349
+ const ids = new Set(survivor.coalescedRevisionIds ?? []);
350
+ for (const id of e.coalescedRevisionIds ?? []) ids.add(id);
351
+ ids.add(e.revisionId);
352
+ ids.delete(survivor.revisionId);
353
+ survivor.coalescedRevisionIds = ids.size > 0 ? [...ids] : void 0;
354
+ }
355
+ const inlineHosts = final.filter((e) => e.type === "insertion" || e.type === "replacement");
356
+ const foldedPropChanges = /* @__PURE__ */ new Set();
357
+ for (const e of final) {
358
+ if (e.type !== "paragraphPropertiesChanged") continue;
359
+ const host = inlineHosts.find((c) => c.author === e.author && c.from < e.to && e.from < c.to);
360
+ if (!host) continue;
361
+ const ids = new Set(mergeIds(host, e));
362
+ if (host.type === "replacement" && host.insertionRevisionId != null) ids.delete(host.insertionRevisionId);
363
+ host.coalescedRevisionIds = ids.size > 0 ? [...ids] : void 0;
364
+ foldedPropChanges.add(e);
365
+ }
366
+ return {
367
+ entries: final.filter((e) => {
368
+ const key = `${e.author}|${e.date ?? ""}`;
369
+ if (e.type === "tableInserted" || e.type === "tableDeleted") return emptyTableEntries.has(e);
370
+ if (e.type === "paragraphMarkInsertion" || e.type === "paragraphMarkDeletion") return !inlineByKey.has(key) && !tableByKey.has(key);
371
+ if (e.type === "paragraphPropertiesChanged") return !foldedPropChanges.has(e);
372
+ if (e.type === "insertion" || e.type === "deletion" || e.type === "replacement") return !tableByKey.has(key);
373
+ return true;
374
+ }),
375
+ commentToRevision
376
+ };
377
+ }
378
+ //#endregion
379
+ export { extractTrackedChanges };
@@ -0,0 +1,27 @@
1
+ import { EditorView } from "prosemirror-view";
2
+
3
+ //#region src/prosemirror/utils/visualLineNavigation.d.ts
4
+ /** @internal */
5
+ type VisualLineState = {
6
+ stickyX: number | null;
7
+ lastVisualLineIndex: number;
8
+ };
9
+ /** @internal */
10
+ declare function createVisualLineState(): VisualLineState;
11
+ /** @internal */
12
+ declare function getCaretClientX(container: HTMLElement, pmPos: number): number | null;
13
+ /** @internal */
14
+ declare function findLineElementAtPosition(container: HTMLElement, pmPos: number): HTMLElement | null;
15
+ /** @internal */
16
+ declare function findPositionOnLineAtClientX(lineEl: HTMLElement, clientX: number): number | null;
17
+ /**
18
+ * Handle PM ArrowUp / ArrowDown with visual-line awareness + sticky
19
+ * X. Returns true if the event was handled and PM should not run
20
+ * its default behaviour. Mutates `state` so consecutive presses
21
+ * keep the same sticky X.
22
+ *
23
+ * @internal
24
+ */
25
+ declare function handleVisualLineKeyDown(state: VisualLineState, view: EditorView, event: KeyboardEvent, container: HTMLElement | null): boolean;
26
+ //#endregion
27
+ export { VisualLineState, createVisualLineState, findLineElementAtPosition, findPositionOnLineAtClientX, getCaretClientX, handleVisualLineKeyDown };