@beyondwork/docx-react-component 1.0.1 → 1.0.3

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 (172) hide show
  1. package/README.md +44 -104
  2. package/package.json +50 -30
  3. package/src/README.md +85 -0
  4. package/src/api/README.md +22 -0
  5. package/src/api/public-types.ts +525 -0
  6. package/src/compare/diff-engine.ts +530 -0
  7. package/src/compare/export-redlines.ts +162 -0
  8. package/src/compare/snapshot.ts +37 -0
  9. package/src/component-inventory.md +99 -0
  10. package/src/core/README.md +10 -0
  11. package/src/core/commands/README.md +3 -0
  12. package/src/core/commands/formatting-commands.ts +161 -0
  13. package/src/core/commands/image-commands.ts +144 -0
  14. package/src/core/commands/index.ts +1013 -0
  15. package/src/core/commands/list-commands.ts +370 -0
  16. package/src/core/commands/review-commands.ts +108 -0
  17. package/src/core/commands/text-commands.ts +119 -0
  18. package/src/core/schema/README.md +3 -0
  19. package/src/core/schema/text-schema.ts +512 -0
  20. package/src/core/selection/README.md +3 -0
  21. package/src/core/selection/mapping.ts +238 -0
  22. package/src/core/selection/review-anchors.ts +94 -0
  23. package/src/core/state/README.md +3 -0
  24. package/src/core/state/editor-state.ts +580 -0
  25. package/src/core/state/text-transaction.ts +276 -0
  26. package/src/formats/xlsx/io/parse-shared-strings.ts +41 -0
  27. package/src/formats/xlsx/io/parse-sheet.ts +289 -0
  28. package/src/formats/xlsx/io/parse-styles.ts +57 -0
  29. package/src/formats/xlsx/io/parse-workbook.ts +75 -0
  30. package/src/formats/xlsx/io/xlsx-session.ts +306 -0
  31. package/src/formats/xlsx/model/cell.ts +189 -0
  32. package/src/formats/xlsx/model/sheet.ts +244 -0
  33. package/src/formats/xlsx/model/styles.ts +118 -0
  34. package/src/formats/xlsx/model/workbook.ts +449 -0
  35. package/src/index.ts +45 -0
  36. package/src/io/README.md +10 -0
  37. package/src/io/docx-session.ts +1763 -0
  38. package/src/io/export/README.md +3 -0
  39. package/src/io/export/export-session.ts +165 -0
  40. package/src/io/export/minimal-docx.ts +115 -0
  41. package/src/io/export/reattach-preserved-parts.ts +54 -0
  42. package/src/io/export/serialize-comments.ts +876 -0
  43. package/src/io/export/serialize-footnotes.ts +217 -0
  44. package/src/io/export/serialize-headers-footers.ts +200 -0
  45. package/src/io/export/serialize-main-document.ts +982 -0
  46. package/src/io/export/serialize-numbering.ts +97 -0
  47. package/src/io/export/serialize-revisions.ts +389 -0
  48. package/src/io/export/serialize-runtime-revisions.ts +265 -0
  49. package/src/io/export/serialize-tables.ts +147 -0
  50. package/src/io/export/split-review-boundaries.ts +194 -0
  51. package/src/io/normalize/README.md +3 -0
  52. package/src/io/normalize/normalize-text.ts +437 -0
  53. package/src/io/ooxml/README.md +3 -0
  54. package/src/io/ooxml/parse-comments.ts +779 -0
  55. package/src/io/ooxml/parse-complex-content.ts +287 -0
  56. package/src/io/ooxml/parse-fields.ts +438 -0
  57. package/src/io/ooxml/parse-footnotes.ts +403 -0
  58. package/src/io/ooxml/parse-headers-footers.ts +483 -0
  59. package/src/io/ooxml/parse-inline-media.ts +431 -0
  60. package/src/io/ooxml/parse-main-document.ts +1846 -0
  61. package/src/io/ooxml/parse-numbering.ts +425 -0
  62. package/src/io/ooxml/parse-revisions.ts +658 -0
  63. package/src/io/ooxml/parse-shapes.ts +271 -0
  64. package/src/io/ooxml/parse-tables.ts +568 -0
  65. package/src/io/ooxml/parse-theme.ts +314 -0
  66. package/src/io/ooxml/part-manifest.ts +136 -0
  67. package/src/io/ooxml/revision-boundaries.ts +351 -0
  68. package/src/io/opc/README.md +3 -0
  69. package/src/io/opc/corrupt-package.ts +166 -0
  70. package/src/io/opc/docx-package.ts +74 -0
  71. package/src/io/opc/package-reader.ts +325 -0
  72. package/src/io/opc/package-writer.ts +273 -0
  73. package/src/legal/bookmarks.ts +196 -0
  74. package/src/legal/cross-references.ts +356 -0
  75. package/src/legal/defined-terms.ts +203 -0
  76. package/src/model/README.md +3 -0
  77. package/src/model/canonical-document.ts +1911 -0
  78. package/src/model/cds-1.0.0.ts +196 -0
  79. package/src/model/snapshot.ts +393 -0
  80. package/src/preservation/README.md +3 -0
  81. package/src/preservation/markup-compatibility.ts +48 -0
  82. package/src/preservation/opaque-fragment-store.ts +89 -0
  83. package/src/preservation/opaque-region.ts +233 -0
  84. package/src/preservation/package-preservation.ts +120 -0
  85. package/src/preservation/preserved-part-manifest.ts +56 -0
  86. package/src/preservation/relationship-retention.ts +57 -0
  87. package/src/preservation/store.ts +185 -0
  88. package/src/review/README.md +16 -0
  89. package/src/review/store/README.md +3 -0
  90. package/src/review/store/comment-anchors.ts +70 -0
  91. package/src/review/store/comment-remapping.ts +154 -0
  92. package/src/review/store/comment-store.ts +331 -0
  93. package/src/review/store/comment-thread.ts +109 -0
  94. package/src/review/store/revision-actions.ts +394 -0
  95. package/src/review/store/revision-store.ts +303 -0
  96. package/src/review/store/revision-types.ts +168 -0
  97. package/src/review/store/runtime-comment-store.ts +43 -0
  98. package/src/runtime/README.md +3 -0
  99. package/src/runtime/ai-action-policy.ts +764 -0
  100. package/src/runtime/document-runtime.ts +967 -0
  101. package/src/runtime/read-only-diagnostics-runtime.ts +232 -0
  102. package/src/runtime/review-runtime.ts +44 -0
  103. package/src/runtime/revision-runtime.ts +107 -0
  104. package/src/runtime/session-capabilities.ts +138 -0
  105. package/src/runtime/surface-projection.ts +570 -0
  106. package/src/runtime/table-commands.ts +87 -0
  107. package/src/runtime/table-schema.ts +140 -0
  108. package/src/runtime/virtualized-rendering.ts +258 -0
  109. package/src/ui/README.md +30 -0
  110. package/src/ui/WordReviewEditor.tsx +1506 -0
  111. package/src/ui/comments/README.md +3 -0
  112. package/src/ui/compatibility/README.md +3 -0
  113. package/src/ui/editor-surface/README.md +3 -0
  114. package/src/ui/headless/comment-decoration-model.ts +124 -0
  115. package/src/ui/headless/revision-decoration-model.ts +128 -0
  116. package/src/ui/headless/selection-helpers.ts +34 -0
  117. package/src/ui/headless/use-editor-keyboard.ts +98 -0
  118. package/src/ui/review/README.md +3 -0
  119. package/src/ui/shared/revision-filters.ts +31 -0
  120. package/src/ui/status/README.md +3 -0
  121. package/src/ui/theme/README.md +3 -0
  122. package/src/ui/toolbar/README.md +3 -0
  123. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +48 -0
  124. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +44 -0
  125. package/src/ui-tailwind/chrome/tw-unsaved-modal.tsx +58 -0
  126. package/src/ui-tailwind/chrome/use-before-unload.ts +20 -0
  127. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +139 -0
  128. package/src/ui-tailwind/editor-surface/pm-decorations.ts +98 -0
  129. package/src/ui-tailwind/editor-surface/pm-position-map.ts +123 -0
  130. package/src/ui-tailwind/editor-surface/pm-schema.ts +452 -0
  131. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +327 -0
  132. package/src/ui-tailwind/editor-surface/search-plugin.ts +157 -0
  133. package/src/ui-tailwind/editor-surface/tw-caret.tsx +12 -0
  134. package/src/ui-tailwind/editor-surface/tw-editor-surface.tsx +150 -0
  135. package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +118 -0
  136. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +52 -0
  137. package/src/ui-tailwind/editor-surface/tw-paragraph-block.tsx +151 -0
  138. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +215 -0
  139. package/src/ui-tailwind/editor-surface/tw-segment-view.tsx +111 -0
  140. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +122 -0
  141. package/src/ui-tailwind/index.ts +61 -0
  142. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +276 -0
  143. package/src/ui-tailwind/review/tw-health-panel.tsx +120 -0
  144. package/src/ui-tailwind/review/tw-review-rail.tsx +120 -0
  145. package/src/ui-tailwind/review/tw-revision-sidebar.tsx +164 -0
  146. package/src/ui-tailwind/status/tw-status-bar.tsx +58 -0
  147. package/src/ui-tailwind/theme/editor-theme.css +190 -0
  148. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +48 -0
  149. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +231 -0
  150. package/src/ui-tailwind/tw-review-workspace.tsx +140 -0
  151. package/src/validation/README.md +3 -0
  152. package/src/validation/compatibility-engine.ts +317 -0
  153. package/src/validation/compatibility-report.ts +160 -0
  154. package/src/validation/diagnostics.ts +203 -0
  155. package/src/validation/import-diagnostics.ts +128 -0
  156. package/src/validation/low-priority-word-surfaces.ts +373 -0
  157. package/dist/chunk-32W6IVQE.js +0 -7725
  158. package/dist/chunk-32W6IVQE.js.map +0 -1
  159. package/dist/index.cjs +0 -23722
  160. package/dist/index.cjs.map +0 -1
  161. package/dist/index.d.cts +0 -7
  162. package/dist/index.d.ts +0 -7
  163. package/dist/index.js +0 -16011
  164. package/dist/index.js.map +0 -1
  165. package/dist/public-types-DqCURAz8.d.cts +0 -1152
  166. package/dist/public-types-DqCURAz8.d.ts +0 -1152
  167. package/dist/tailwind.cjs +0 -8295
  168. package/dist/tailwind.cjs.map +0 -1
  169. package/dist/tailwind.d.cts +0 -323
  170. package/dist/tailwind.d.ts +0 -323
  171. package/dist/tailwind.js +0 -553
  172. package/dist/tailwind.js.map +0 -1
@@ -0,0 +1,512 @@
1
+ import type {
2
+ DocumentRootNode,
3
+ HyperlinkNode,
4
+ InlineNode,
5
+ OpaqueBlockNode,
6
+ OpaqueInlineNode,
7
+ ParagraphNode,
8
+ TextMark,
9
+ } from "../../model/canonical-document.ts";
10
+
11
+ export interface ParagraphProperties {
12
+ styleId?: string;
13
+ numbering?: ParagraphNode["numbering"];
14
+ }
15
+
16
+ export interface TextStory {
17
+ firstParagraph: ParagraphProperties;
18
+ units: StoryUnit[];
19
+ size: number;
20
+ }
21
+
22
+ export type StoryUnit =
23
+ | TextCharacterUnit
24
+ | TabUnit
25
+ | HardBreakUnit
26
+ | ImageUnit
27
+ | OpaqueInlineUnit
28
+ | OpaqueBlockUnit
29
+ | ParagraphBreakUnit;
30
+
31
+ export interface TextCharacterUnit {
32
+ kind: "text";
33
+ value: string;
34
+ marks?: TextMark[];
35
+ hyperlinkHref?: string;
36
+ }
37
+
38
+ export interface TabUnit {
39
+ kind: "tab";
40
+ hyperlinkHref?: string;
41
+ }
42
+
43
+ export interface HardBreakUnit {
44
+ kind: "hard_break";
45
+ hyperlinkHref?: string;
46
+ }
47
+
48
+ export interface ImageUnit {
49
+ kind: "image";
50
+ mediaId: string;
51
+ altText?: string;
52
+ }
53
+
54
+ export interface OpaqueInlineUnit {
55
+ kind: "opaque_inline";
56
+ fragmentId: string;
57
+ warningId: string;
58
+ }
59
+
60
+ export interface OpaqueBlockUnit {
61
+ kind: "opaque_block";
62
+ fragmentId: string;
63
+ warningId: string;
64
+ nextParagraph?: ParagraphProperties;
65
+ }
66
+
67
+ export interface ParagraphBreakUnit {
68
+ kind: "paragraph_break";
69
+ nextParagraph: ParagraphProperties;
70
+ }
71
+
72
+ export function parseTextStory(content: unknown): TextStory {
73
+ const root = normalizeDocumentRoot(content);
74
+ const firstParagraphNode = root.children.find(isParagraphNode);
75
+ const firstParagraph = firstParagraphNode
76
+ ? extractParagraphProperties(firstParagraphNode)
77
+ : cloneParagraphProperties(EMPTY_PARAGRAPH_PROPERTIES);
78
+ const units: StoryUnit[] = [];
79
+
80
+ for (let index = 0; index < root.children.length; index += 1) {
81
+ const block = root.children[index];
82
+ const nextBlock = root.children[index + 1];
83
+
84
+ if (isParagraphNode(block)) {
85
+ units.push(...flattenInlineNodes(block.children));
86
+
87
+ if (isParagraphNode(nextBlock)) {
88
+ units.push({
89
+ kind: "paragraph_break",
90
+ nextParagraph: extractParagraphProperties(nextBlock),
91
+ });
92
+ }
93
+
94
+ continue;
95
+ }
96
+
97
+ units.push({
98
+ kind: "opaque_block",
99
+ fragmentId: block.fragmentId,
100
+ warningId: block.warningId,
101
+ ...(isParagraphNode(nextBlock)
102
+ ? { nextParagraph: extractParagraphProperties(nextBlock) }
103
+ : {}),
104
+ });
105
+ }
106
+
107
+ return {
108
+ firstParagraph,
109
+ units,
110
+ size: units.length,
111
+ };
112
+ }
113
+
114
+ export function serializeTextStory(story: TextStory): DocumentRootNode {
115
+ const blocks: Array<ParagraphNode | OpaqueBlockNode> = [];
116
+ let currentParagraph: ParagraphNode | undefined = createParagraph(story.firstParagraph);
117
+ let currentHyperlink: HyperlinkNode | undefined;
118
+ let activeTextBuffer:
119
+ | {
120
+ text: string;
121
+ marks?: TextMark[];
122
+ hyperlinkHref?: string;
123
+ }
124
+ | undefined;
125
+
126
+ const ensureCurrentParagraph = () => {
127
+ if (!currentParagraph) {
128
+ currentParagraph = createParagraph(EMPTY_PARAGRAPH_PROPERTIES);
129
+ }
130
+ };
131
+
132
+ const flushTextBuffer = () => {
133
+ if (!activeTextBuffer || activeTextBuffer.text.length === 0) {
134
+ activeTextBuffer = undefined;
135
+ return;
136
+ }
137
+
138
+ const textNode = {
139
+ type: "text" as const,
140
+ text: activeTextBuffer.text,
141
+ ...(activeTextBuffer.marks ? { marks: cloneMarks(activeTextBuffer.marks) } : {}),
142
+ };
143
+
144
+ ensureCurrentParagraph();
145
+
146
+ if (activeTextBuffer.hyperlinkHref) {
147
+ if (!currentHyperlink || currentHyperlink.href !== activeTextBuffer.hyperlinkHref) {
148
+ flushHyperlink();
149
+ currentHyperlink = {
150
+ type: "hyperlink",
151
+ href: activeTextBuffer.hyperlinkHref,
152
+ children: [],
153
+ };
154
+ }
155
+
156
+ currentHyperlink.children.push(textNode);
157
+ } else {
158
+ flushHyperlink();
159
+ currentParagraph!.children.push(textNode);
160
+ }
161
+
162
+ activeTextBuffer = undefined;
163
+ };
164
+
165
+ const flushHyperlink = () => {
166
+ if (currentHyperlink) {
167
+ ensureCurrentParagraph();
168
+ currentParagraph!.children.push(currentHyperlink);
169
+ currentHyperlink = undefined;
170
+ }
171
+ };
172
+
173
+ const flushParagraph = () => {
174
+ flushTextBuffer();
175
+ flushHyperlink();
176
+
177
+ if (!currentParagraph) {
178
+ return;
179
+ }
180
+
181
+ blocks.push(currentParagraph);
182
+ currentParagraph = undefined;
183
+ };
184
+
185
+ const pushInlineNode = (
186
+ node: Exclude<InlineNode, { type: "text" }>,
187
+ hyperlinkHref?: string,
188
+ ) => {
189
+ flushTextBuffer();
190
+ ensureCurrentParagraph();
191
+
192
+ if (hyperlinkHref) {
193
+ if (!currentHyperlink || currentHyperlink.href !== hyperlinkHref) {
194
+ flushHyperlink();
195
+ currentHyperlink = {
196
+ type: "hyperlink",
197
+ href: hyperlinkHref,
198
+ children: [],
199
+ };
200
+ }
201
+
202
+ currentHyperlink.children.push(node as HyperlinkNode["children"][number]);
203
+ return;
204
+ }
205
+
206
+ flushHyperlink();
207
+ currentParagraph!.children.push(node);
208
+ };
209
+
210
+ for (const unit of story.units) {
211
+ if (unit.kind === "paragraph_break") {
212
+ flushParagraph();
213
+ currentParagraph = createParagraph(unit.nextParagraph);
214
+ continue;
215
+ }
216
+
217
+ if (unit.kind === "opaque_block") {
218
+ flushParagraph();
219
+ blocks.push({
220
+ type: "opaque_block",
221
+ fragmentId: unit.fragmentId,
222
+ warningId: unit.warningId,
223
+ });
224
+ currentParagraph = unit.nextParagraph
225
+ ? createParagraph(unit.nextParagraph)
226
+ : undefined;
227
+ continue;
228
+ }
229
+
230
+ if (unit.kind === "text") {
231
+ const shouldExtendBuffer =
232
+ activeTextBuffer &&
233
+ activeTextBuffer.hyperlinkHref === unit.hyperlinkHref &&
234
+ haveEqualMarks(activeTextBuffer.marks, unit.marks);
235
+
236
+ if (shouldExtendBuffer && activeTextBuffer) {
237
+ activeTextBuffer.text += unit.value;
238
+ } else {
239
+ flushTextBuffer();
240
+ activeTextBuffer = {
241
+ text: unit.value,
242
+ ...(unit.marks ? { marks: cloneMarks(unit.marks) } : {}),
243
+ ...(unit.hyperlinkHref ? { hyperlinkHref: unit.hyperlinkHref } : {}),
244
+ };
245
+ }
246
+
247
+ continue;
248
+ }
249
+
250
+ switch (unit.kind) {
251
+ case "tab":
252
+ pushInlineNode({ type: "tab" }, unit.hyperlinkHref);
253
+ break;
254
+ case "hard_break":
255
+ pushInlineNode({ type: "hard_break" }, unit.hyperlinkHref);
256
+ break;
257
+ case "image":
258
+ pushInlineNode({
259
+ type: "image",
260
+ mediaId: unit.mediaId,
261
+ ...(unit.altText ? { altText: unit.altText } : {}),
262
+ });
263
+ break;
264
+ case "opaque_inline":
265
+ pushInlineNode({
266
+ type: "opaque_inline",
267
+ fragmentId: unit.fragmentId,
268
+ warningId: unit.warningId,
269
+ });
270
+ break;
271
+ }
272
+ }
273
+
274
+ flushParagraph();
275
+
276
+ if (blocks.length === 0) {
277
+ blocks.push(createParagraph(story.firstParagraph));
278
+ }
279
+
280
+ return {
281
+ type: "doc",
282
+ children: blocks,
283
+ };
284
+ }
285
+
286
+ export function createPlainText(story: TextStory): string {
287
+ return story.units
288
+ .map((unit) => {
289
+ switch (unit.kind) {
290
+ case "text":
291
+ return unit.value;
292
+ case "tab":
293
+ return "\t";
294
+ case "hard_break":
295
+ return "\n";
296
+ case "paragraph_break":
297
+ return "\n";
298
+ case "image":
299
+ return "\uFFFC";
300
+ case "opaque_inline":
301
+ return "\uFFF9";
302
+ case "opaque_block":
303
+ return "\uFFFA";
304
+ }
305
+ })
306
+ .join("");
307
+ }
308
+
309
+ export function cloneStoryUnit(unit: StoryUnit): StoryUnit {
310
+ switch (unit.kind) {
311
+ case "text":
312
+ return {
313
+ kind: "text",
314
+ value: unit.value,
315
+ ...(unit.marks ? { marks: cloneMarks(unit.marks) } : {}),
316
+ ...(unit.hyperlinkHref ? { hyperlinkHref: unit.hyperlinkHref } : {}),
317
+ };
318
+ case "tab":
319
+ return {
320
+ kind: "tab",
321
+ ...(unit.hyperlinkHref ? { hyperlinkHref: unit.hyperlinkHref } : {}),
322
+ };
323
+ case "hard_break":
324
+ return {
325
+ kind: "hard_break",
326
+ ...(unit.hyperlinkHref ? { hyperlinkHref: unit.hyperlinkHref } : {}),
327
+ };
328
+ case "image":
329
+ return {
330
+ kind: "image",
331
+ mediaId: unit.mediaId,
332
+ ...(unit.altText ? { altText: unit.altText } : {}),
333
+ };
334
+ case "opaque_inline":
335
+ return {
336
+ kind: "opaque_inline",
337
+ fragmentId: unit.fragmentId,
338
+ warningId: unit.warningId,
339
+ };
340
+ case "opaque_block":
341
+ return {
342
+ kind: "opaque_block",
343
+ fragmentId: unit.fragmentId,
344
+ warningId: unit.warningId,
345
+ ...(unit.nextParagraph
346
+ ? { nextParagraph: cloneParagraphProperties(unit.nextParagraph) }
347
+ : {}),
348
+ };
349
+ case "paragraph_break":
350
+ return {
351
+ kind: "paragraph_break",
352
+ nextParagraph: cloneParagraphProperties(unit.nextParagraph),
353
+ };
354
+ }
355
+ }
356
+
357
+ export function cloneParagraphProperties(
358
+ properties: ParagraphProperties,
359
+ ): ParagraphProperties {
360
+ return {
361
+ ...(properties.styleId ? { styleId: properties.styleId } : {}),
362
+ ...(properties.numbering
363
+ ? {
364
+ numbering: {
365
+ numberingInstanceId: properties.numbering.numberingInstanceId,
366
+ level: properties.numbering.level,
367
+ },
368
+ }
369
+ : {}),
370
+ };
371
+ }
372
+
373
+ function normalizeDocumentRoot(content: unknown): DocumentRootNode {
374
+ if (isDocumentRootNode(content)) {
375
+ return content;
376
+ }
377
+
378
+ if (Array.isArray(content)) {
379
+ return {
380
+ type: "doc",
381
+ children: content.filter(
382
+ (block): block is DocumentRootNode["children"][number] =>
383
+ isParagraphNode(block) || isOpaqueBlockNode(block),
384
+ ),
385
+ };
386
+ }
387
+
388
+ return {
389
+ type: "doc",
390
+ children: [createEmptyParagraph()],
391
+ };
392
+ }
393
+
394
+ function flattenInlineNodes(
395
+ nodes: ParagraphNode["children"],
396
+ hyperlinkHref?: string,
397
+ ): StoryUnit[] {
398
+ const units: StoryUnit[] = [];
399
+
400
+ for (const node of nodes) {
401
+ switch (node.type) {
402
+ case "text":
403
+ for (const character of Array.from(node.text)) {
404
+ units.push({
405
+ kind: "text",
406
+ value: character,
407
+ ...(node.marks ? { marks: cloneMarks(node.marks) } : {}),
408
+ ...(hyperlinkHref ? { hyperlinkHref } : {}),
409
+ });
410
+ }
411
+ break;
412
+ case "tab":
413
+ units.push({
414
+ kind: "tab",
415
+ ...(hyperlinkHref ? { hyperlinkHref } : {}),
416
+ });
417
+ break;
418
+ case "hard_break":
419
+ units.push({
420
+ kind: "hard_break",
421
+ ...(hyperlinkHref ? { hyperlinkHref } : {}),
422
+ });
423
+ break;
424
+ case "hyperlink":
425
+ units.push(...flattenInlineNodes(node.children, node.href));
426
+ break;
427
+ case "image":
428
+ units.push({
429
+ kind: "image",
430
+ mediaId: node.mediaId,
431
+ ...(node.altText ? { altText: node.altText } : {}),
432
+ });
433
+ break;
434
+ case "opaque_inline":
435
+ units.push({
436
+ kind: "opaque_inline",
437
+ fragmentId: node.fragmentId,
438
+ warningId: node.warningId,
439
+ });
440
+ break;
441
+ }
442
+ }
443
+
444
+ return units;
445
+ }
446
+
447
+ function extractParagraphProperties(paragraph: ParagraphNode): ParagraphProperties {
448
+ return {
449
+ ...(paragraph.styleId ? { styleId: paragraph.styleId } : {}),
450
+ ...(paragraph.numbering
451
+ ? {
452
+ numbering: {
453
+ numberingInstanceId: paragraph.numbering.numberingInstanceId,
454
+ level: paragraph.numbering.level,
455
+ },
456
+ }
457
+ : {}),
458
+ };
459
+ }
460
+
461
+ function createParagraph(properties: ParagraphProperties): ParagraphNode {
462
+ return {
463
+ type: "paragraph",
464
+ ...(properties.styleId ? { styleId: properties.styleId } : {}),
465
+ ...(properties.numbering
466
+ ? {
467
+ numbering: {
468
+ numberingInstanceId: properties.numbering.numberingInstanceId,
469
+ level: properties.numbering.level,
470
+ },
471
+ }
472
+ : {}),
473
+ children: [],
474
+ };
475
+ }
476
+
477
+ function createEmptyParagraph(): ParagraphNode {
478
+ return {
479
+ type: "paragraph",
480
+ children: [],
481
+ };
482
+ }
483
+
484
+ const EMPTY_PARAGRAPH_PROPERTIES: ParagraphProperties = {};
485
+
486
+ function cloneMarks(marks: TextMark[]): TextMark[] {
487
+ return marks.map((mark) => ({ type: mark.type }));
488
+ }
489
+
490
+ function haveEqualMarks(left?: TextMark[], right?: TextMark[]): boolean {
491
+ if (!left && !right) {
492
+ return true;
493
+ }
494
+
495
+ if (!left || !right || left.length !== right.length) {
496
+ return false;
497
+ }
498
+
499
+ return left.every((mark, index) => mark.type === right[index]?.type);
500
+ }
501
+
502
+ function isDocumentRootNode(value: unknown): value is DocumentRootNode {
503
+ return Boolean(value) && typeof value === "object" && (value as { type?: string }).type === "doc";
504
+ }
505
+
506
+ function isParagraphNode(value: unknown): value is ParagraphNode {
507
+ return Boolean(value) && typeof value === "object" && (value as { type?: string }).type === "paragraph";
508
+ }
509
+
510
+ function isOpaqueBlockNode(value: unknown): value is OpaqueBlockNode {
511
+ return Boolean(value) && typeof value === "object" && (value as { type?: string }).type === "opaque_block";
512
+ }
@@ -0,0 +1,3 @@
1
+ # Core Selection
2
+
3
+ Selections, ranges, position mapping, and anchor remapping helpers belong here.