@beyondwork/docx-react-component 1.0.1 → 1.0.2

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 +76 -46
  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 +320 -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 +1504 -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,370 @@
1
+ import type {
2
+ AbstractNumberingDefinition,
3
+ CanonicalDocument as CanonicalDocumentEnvelope,
4
+ NumberingCatalog,
5
+ NumberingInstance,
6
+ ParagraphNode,
7
+ } from "../../model/canonical-document.ts";
8
+
9
+ export interface ListCommandContext {
10
+ timestamp: string;
11
+ }
12
+
13
+ export interface ListCommandResult {
14
+ document: CanonicalDocumentEnvelope;
15
+ affectedParagraphIndexes: number[];
16
+ createdNumberingInstanceId?: string;
17
+ }
18
+
19
+ type OpaqueBlockLike = { type: "opaque_block"; [key: string]: unknown };
20
+ type BlockLike = ParagraphNode | OpaqueBlockLike;
21
+
22
+ export function toggleNumberedList(
23
+ document: CanonicalDocumentEnvelope,
24
+ paragraphIndexes: readonly number[],
25
+ context: ListCommandContext,
26
+ ): ListCommandResult {
27
+ return toggleListKind(document, paragraphIndexes, "numbered", context);
28
+ }
29
+
30
+ export function toggleBulletedList(
31
+ document: CanonicalDocumentEnvelope,
32
+ paragraphIndexes: readonly number[],
33
+ context: ListCommandContext,
34
+ ): ListCommandResult {
35
+ return toggleListKind(document, paragraphIndexes, "bulleted", context);
36
+ }
37
+
38
+ export function indentListItems(
39
+ document: CanonicalDocumentEnvelope,
40
+ paragraphIndexes: readonly number[],
41
+ context: ListCommandContext,
42
+ ): ListCommandResult {
43
+ return adjustListLevels(document, paragraphIndexes, 1, context);
44
+ }
45
+
46
+ export function outdentListItems(
47
+ document: CanonicalDocumentEnvelope,
48
+ paragraphIndexes: readonly number[],
49
+ context: ListCommandContext,
50
+ ): ListCommandResult {
51
+ return adjustListLevels(document, paragraphIndexes, -1, context);
52
+ }
53
+
54
+ export function restartNumbering(
55
+ document: CanonicalDocumentEnvelope,
56
+ paragraphIndex: number,
57
+ context: ListCommandContext,
58
+ startAt = 1,
59
+ ): ListCommandResult {
60
+ const working = cloneEnvelope(document, context.timestamp);
61
+ const blocks = captureBlocks(working);
62
+ const target = blocks[paragraphIndex];
63
+
64
+ if (!isParagraphNode(target) || !target.numbering) {
65
+ return {
66
+ document: working,
67
+ affectedParagraphIndexes: [],
68
+ };
69
+ }
70
+
71
+ const catalog = ensureNumberingCatalog(working.numbering);
72
+ const existingInstance = catalog.instances[target.numbering.numberingInstanceId];
73
+ if (!existingInstance) {
74
+ return {
75
+ document: working,
76
+ affectedParagraphIndexes: [],
77
+ };
78
+ }
79
+
80
+ const numberingInstanceId = createNumberingInstanceId(catalog);
81
+ catalog.instances[numberingInstanceId] = {
82
+ numberingInstanceId,
83
+ abstractNumberingId: existingInstance.abstractNumberingId,
84
+ overrides: mergeOverride(existingInstance.overrides, {
85
+ level: target.numbering.level,
86
+ startAt,
87
+ }),
88
+ };
89
+
90
+ const affectedParagraphIndexes: number[] = [];
91
+ for (let index = paragraphIndex; index < blocks.length; index += 1) {
92
+ const block = blocks[index];
93
+ if (!isParagraphNode(block)) {
94
+ break;
95
+ }
96
+ if (block.numbering?.numberingInstanceId !== existingInstance.numberingInstanceId) {
97
+ break;
98
+ }
99
+
100
+ block.numbering = {
101
+ numberingInstanceId,
102
+ level: block.numbering.level,
103
+ };
104
+ affectedParagraphIndexes.push(index);
105
+ }
106
+
107
+ working.numbering = catalog;
108
+
109
+ return {
110
+ document: working,
111
+ affectedParagraphIndexes,
112
+ createdNumberingInstanceId: numberingInstanceId,
113
+ };
114
+ }
115
+
116
+ function toggleListKind(
117
+ document: CanonicalDocumentEnvelope,
118
+ paragraphIndexes: readonly number[],
119
+ kind: "numbered" | "bulleted",
120
+ context: ListCommandContext,
121
+ ): ListCommandResult {
122
+ const working = cloneEnvelope(document, context.timestamp);
123
+ const blocks = captureBlocks(working);
124
+ const normalizedIndexes = normalizeParagraphIndexes(blocks, paragraphIndexes);
125
+
126
+ if (normalizedIndexes.length === 0) {
127
+ return {
128
+ document: working,
129
+ affectedParagraphIndexes: [],
130
+ };
131
+ }
132
+
133
+ const catalog = ensureNumberingCatalog(working.numbering);
134
+ const allAlreadyKind = normalizedIndexes.every((index) => {
135
+ const paragraph = blocks[index] as ParagraphNode;
136
+ return paragraph.numbering
137
+ ? getListKind(catalog, paragraph.numbering.numberingInstanceId) === kind
138
+ : false;
139
+ });
140
+
141
+ if (allAlreadyKind) {
142
+ for (const index of normalizedIndexes) {
143
+ delete (blocks[index] as ParagraphNode).numbering;
144
+ }
145
+ working.numbering = catalog;
146
+ return {
147
+ document: working,
148
+ affectedParagraphIndexes: normalizedIndexes,
149
+ };
150
+ }
151
+
152
+ const numberingInstanceId =
153
+ findAdjacentCompatibleInstance(blocks, catalog, normalizedIndexes[0]!, kind) ??
154
+ ensureDefaultInstance(catalog, kind);
155
+
156
+ for (const index of normalizedIndexes) {
157
+ const paragraph = blocks[index] as ParagraphNode;
158
+ paragraph.numbering = {
159
+ numberingInstanceId,
160
+ level: clampLevel(paragraph.numbering?.level ?? 0),
161
+ };
162
+ }
163
+
164
+ working.numbering = catalog;
165
+
166
+ return {
167
+ document: working,
168
+ affectedParagraphIndexes: normalizedIndexes,
169
+ createdNumberingInstanceId: numberingInstanceId,
170
+ };
171
+ }
172
+
173
+ function adjustListLevels(
174
+ document: CanonicalDocumentEnvelope,
175
+ paragraphIndexes: readonly number[],
176
+ delta: -1 | 1,
177
+ context: ListCommandContext,
178
+ ): ListCommandResult {
179
+ const working = cloneEnvelope(document, context.timestamp);
180
+ const blocks = captureBlocks(working);
181
+ const affectedParagraphIndexes = normalizeParagraphIndexes(blocks, paragraphIndexes).filter(
182
+ (index) => isParagraphNode(blocks[index]) && Boolean((blocks[index] as ParagraphNode).numbering),
183
+ );
184
+
185
+ for (const index of affectedParagraphIndexes) {
186
+ const paragraph = blocks[index] as ParagraphNode;
187
+ if (!paragraph.numbering) {
188
+ continue;
189
+ }
190
+
191
+ paragraph.numbering = {
192
+ numberingInstanceId: paragraph.numbering.numberingInstanceId,
193
+ level: clampLevel(paragraph.numbering.level + delta),
194
+ };
195
+ }
196
+
197
+ return {
198
+ document: working,
199
+ affectedParagraphIndexes,
200
+ };
201
+ }
202
+
203
+ function ensureDefaultInstance(
204
+ catalog: NumberingCatalog,
205
+ kind: "numbered" | "bulleted",
206
+ ): string {
207
+ const existing = Object.values(catalog.instances).find(
208
+ (instance) => getListKind(catalog, instance.numberingInstanceId) === kind,
209
+ );
210
+ if (existing) {
211
+ return existing.numberingInstanceId;
212
+ }
213
+
214
+ const abstractNumberingId = createAbstractNumberingId(catalog);
215
+ catalog.abstractDefinitions[abstractNumberingId] =
216
+ kind === "bulleted" ? createDefaultBulletedDefinition(abstractNumberingId) : createDefaultNumberedDefinition(abstractNumberingId);
217
+
218
+ const numberingInstanceId = createNumberingInstanceId(catalog);
219
+ catalog.instances[numberingInstanceId] = {
220
+ numberingInstanceId,
221
+ abstractNumberingId,
222
+ overrides: [],
223
+ };
224
+
225
+ return numberingInstanceId;
226
+ }
227
+
228
+ function findAdjacentCompatibleInstance(
229
+ blocks: BlockLike[],
230
+ catalog: NumberingCatalog,
231
+ fromIndex: number,
232
+ kind: "numbered" | "bulleted",
233
+ ): string | undefined {
234
+ const previous = blocks[fromIndex - 1];
235
+ if (isParagraphNode(previous) && previous.numbering) {
236
+ const previousKind = getListKind(catalog, previous.numbering.numberingInstanceId);
237
+ if (previousKind === kind) {
238
+ return previous.numbering.numberingInstanceId;
239
+ }
240
+ }
241
+
242
+ return undefined;
243
+ }
244
+
245
+ function getListKind(
246
+ catalog: NumberingCatalog,
247
+ numberingInstanceId: string,
248
+ ): "numbered" | "bulleted" | undefined {
249
+ const instance = catalog.instances[numberingInstanceId];
250
+ const definition = instance
251
+ ? catalog.abstractDefinitions[instance.abstractNumberingId]
252
+ : undefined;
253
+ const levelZero = definition?.levels.find((level) => level.level === 0);
254
+ if (!levelZero) {
255
+ return undefined;
256
+ }
257
+
258
+ return levelZero.format === "bullet" ? "bulleted" : "numbered";
259
+ }
260
+
261
+ function mergeOverride(
262
+ overrides: NumberingInstance["overrides"],
263
+ nextOverride: NumberingInstance["overrides"][number],
264
+ ): NumberingInstance["overrides"] {
265
+ const filtered = overrides.filter((override) => override.level !== nextOverride.level);
266
+ filtered.push(nextOverride);
267
+ return filtered.sort((left, right) => left.level - right.level);
268
+ }
269
+
270
+ function createDefaultNumberedDefinition(abstractNumberingId: string): AbstractNumberingDefinition {
271
+ return {
272
+ abstractNumberingId,
273
+ levels: [
274
+ { level: 0, format: "decimal", text: "%1." },
275
+ { level: 1, format: "decimal", text: "%1.%2." },
276
+ { level: 2, format: "lowerLetter", text: "(%3)" },
277
+ { level: 3, format: "lowerRoman", text: "(%4)" },
278
+ { level: 4, format: "decimal", text: "%5." },
279
+ { level: 5, format: "lowerLetter", text: "(%6)" },
280
+ { level: 6, format: "lowerRoman", text: "(%7)" },
281
+ { level: 7, format: "decimal", text: "%8." },
282
+ { level: 8, format: "lowerLetter", text: "(%9)" },
283
+ ],
284
+ };
285
+ }
286
+
287
+ function createDefaultBulletedDefinition(abstractNumberingId: string): AbstractNumberingDefinition {
288
+ return {
289
+ abstractNumberingId,
290
+ levels: [
291
+ { level: 0, format: "bullet", text: "•" },
292
+ { level: 1, format: "bullet", text: "o" },
293
+ { level: 2, format: "bullet", text: "■" },
294
+ { level: 3, format: "bullet", text: "•" },
295
+ { level: 4, format: "bullet", text: "o" },
296
+ { level: 5, format: "bullet", text: "■" },
297
+ { level: 6, format: "bullet", text: "•" },
298
+ { level: 7, format: "bullet", text: "o" },
299
+ { level: 8, format: "bullet", text: "■" },
300
+ ],
301
+ };
302
+ }
303
+
304
+ function createAbstractNumberingId(catalog: NumberingCatalog): string {
305
+ let index = 1;
306
+ while (catalog.abstractDefinitions[`abstract-num:generated-${index}`]) {
307
+ index += 1;
308
+ }
309
+
310
+ return `abstract-num:generated-${index}`;
311
+ }
312
+
313
+ function createNumberingInstanceId(catalog: NumberingCatalog): string {
314
+ let index = 1;
315
+ while (catalog.instances[`num:generated-${index}`]) {
316
+ index += 1;
317
+ }
318
+
319
+ return `num:generated-${index}`;
320
+ }
321
+
322
+ function ensureNumberingCatalog(value: NumberingCatalog): NumberingCatalog {
323
+ return structuredClone(value);
324
+ }
325
+
326
+ function cloneEnvelope(
327
+ document: CanonicalDocumentEnvelope,
328
+ timestamp: string,
329
+ ): CanonicalDocumentEnvelope {
330
+ return {
331
+ ...structuredClone(document),
332
+ updatedAt: timestamp,
333
+ };
334
+ }
335
+
336
+ function captureBlocks(document: CanonicalDocumentEnvelope): BlockLike[] {
337
+ if (isDocumentRoot(document.content)) {
338
+ return document.content.children as BlockLike[];
339
+ }
340
+
341
+ const fallback = {
342
+ type: "doc" as const,
343
+ children: [{ type: "paragraph" as const, children: [] }],
344
+ };
345
+ document.content = fallback;
346
+ return fallback.children as BlockLike[];
347
+ }
348
+
349
+ function normalizeParagraphIndexes(
350
+ blocks: BlockLike[],
351
+ paragraphIndexes: readonly number[],
352
+ ): number[] {
353
+ return [...new Set(paragraphIndexes)]
354
+ .filter((index) => Number.isInteger(index) && index >= 0 && index < blocks.length)
355
+ .filter((index) => isParagraphNode(blocks[index]))
356
+ .sort((left, right) => left - right);
357
+ }
358
+
359
+ function clampLevel(level: number): number {
360
+ return Math.max(0, Math.min(8, level));
361
+ }
362
+
363
+ function isDocumentRoot(value: unknown): value is { type: "doc"; children: unknown[] } {
364
+ return Boolean(value) && typeof value === "object" && (value as { type?: string }).type === "doc";
365
+ }
366
+
367
+ function isParagraphNode(value: unknown): value is ParagraphNode {
368
+ return Boolean(value) && typeof value === "object" && (value as { type?: string }).type === "paragraph";
369
+ }
370
+
@@ -0,0 +1,108 @@
1
+ export interface ReviewCommandOrigin {
2
+ source:
3
+ | "keyboard"
4
+ | "toolbar"
5
+ | "context_menu"
6
+ | "comment_panel"
7
+ | "review_panel"
8
+ | "api"
9
+ | "runtime";
10
+ timestamp: string;
11
+ }
12
+
13
+ export type ReviewCommand =
14
+ | {
15
+ type: "review.accept-revision";
16
+ revisionId: string;
17
+ origin?: ReviewCommandOrigin;
18
+ }
19
+ | {
20
+ type: "review.reject-revision";
21
+ revisionId: string;
22
+ origin?: ReviewCommandOrigin;
23
+ }
24
+ | {
25
+ type: "review.accept-all-revisions";
26
+ origin?: ReviewCommandOrigin;
27
+ }
28
+ | {
29
+ type: "review.reject-all-revisions";
30
+ origin?: ReviewCommandOrigin;
31
+ };
32
+
33
+ export type SingleRevisionReviewCommand = Extract<
34
+ ReviewCommand,
35
+ { revisionId: string }
36
+ >;
37
+
38
+ export type BatchRevisionReviewCommand = Exclude<
39
+ ReviewCommand,
40
+ SingleRevisionReviewCommand
41
+ >;
42
+
43
+ export type ReviewCommandIntent = "accept" | "reject";
44
+
45
+ export function isSingleRevisionReviewCommand(
46
+ command: ReviewCommand,
47
+ ): command is SingleRevisionReviewCommand {
48
+ return "revisionId" in command;
49
+ }
50
+
51
+ export function isBatchRevisionReviewCommand(
52
+ command: ReviewCommand,
53
+ ): command is BatchRevisionReviewCommand {
54
+ return !isSingleRevisionReviewCommand(command);
55
+ }
56
+
57
+ export function getReviewCommandIntent(
58
+ command: ReviewCommand,
59
+ ): ReviewCommandIntent {
60
+ switch (command.type) {
61
+ case "review.accept-revision":
62
+ case "review.accept-all-revisions":
63
+ return "accept";
64
+ case "review.reject-revision":
65
+ case "review.reject-all-revisions":
66
+ return "reject";
67
+ }
68
+ }
69
+
70
+ export function createAcceptRevisionCommand(
71
+ revisionId: string,
72
+ origin?: ReviewCommandOrigin,
73
+ ): SingleRevisionReviewCommand {
74
+ return {
75
+ type: "review.accept-revision",
76
+ revisionId,
77
+ ...(origin ? { origin } : {}),
78
+ };
79
+ }
80
+
81
+ export function createRejectRevisionCommand(
82
+ revisionId: string,
83
+ origin?: ReviewCommandOrigin,
84
+ ): SingleRevisionReviewCommand {
85
+ return {
86
+ type: "review.reject-revision",
87
+ revisionId,
88
+ ...(origin ? { origin } : {}),
89
+ };
90
+ }
91
+
92
+ export function createAcceptAllRevisionsCommand(
93
+ origin?: ReviewCommandOrigin,
94
+ ): BatchRevisionReviewCommand {
95
+ return {
96
+ type: "review.accept-all-revisions",
97
+ ...(origin ? { origin } : {}),
98
+ };
99
+ }
100
+
101
+ export function createRejectAllRevisionsCommand(
102
+ origin?: ReviewCommandOrigin,
103
+ ): BatchRevisionReviewCommand {
104
+ return {
105
+ type: "review.reject-all-revisions",
106
+ ...(origin ? { origin } : {}),
107
+ };
108
+ }
@@ -0,0 +1,119 @@
1
+ import type { CanonicalDocumentEnvelope, SelectionSnapshot } from "../state/editor-state.ts";
2
+ import {
3
+ applyTextTransaction,
4
+ type TextTransactionResult,
5
+ } from "../state/text-transaction.ts";
6
+
7
+ export interface TextCommandContext {
8
+ timestamp: string;
9
+ }
10
+
11
+ export function insertText(
12
+ document: CanonicalDocumentEnvelope,
13
+ selection: SelectionSnapshot,
14
+ text: string,
15
+ context: TextCommandContext,
16
+ ): TextTransactionResult {
17
+ return applyTextTransaction(
18
+ document,
19
+ selection,
20
+ {
21
+ type: "replace",
22
+ insertion: [
23
+ {
24
+ type: "text",
25
+ text,
26
+ },
27
+ ],
28
+ },
29
+ context,
30
+ );
31
+ }
32
+
33
+ export function deleteSelectionOrBackward(
34
+ document: CanonicalDocumentEnvelope,
35
+ selection: SelectionSnapshot,
36
+ context: TextCommandContext,
37
+ ): TextTransactionResult {
38
+ return applyTextTransaction(
39
+ document,
40
+ selection,
41
+ selection.isCollapsed
42
+ ? {
43
+ type: "delete_backward",
44
+ }
45
+ : {
46
+ type: "replace",
47
+ insertion: [],
48
+ },
49
+ context,
50
+ );
51
+ }
52
+
53
+ export function deleteSelectionOrForward(
54
+ document: CanonicalDocumentEnvelope,
55
+ selection: SelectionSnapshot,
56
+ context: TextCommandContext,
57
+ ): TextTransactionResult {
58
+ return applyTextTransaction(
59
+ document,
60
+ selection,
61
+ selection.isCollapsed
62
+ ? {
63
+ type: "delete_forward",
64
+ }
65
+ : {
66
+ type: "replace",
67
+ insertion: [],
68
+ },
69
+ context,
70
+ );
71
+ }
72
+
73
+ export function insertTab(
74
+ document: CanonicalDocumentEnvelope,
75
+ selection: SelectionSnapshot,
76
+ context: TextCommandContext,
77
+ ): TextTransactionResult {
78
+ return applyTextTransaction(
79
+ document,
80
+ selection,
81
+ {
82
+ type: "replace",
83
+ insertion: [{ type: "tab" }],
84
+ },
85
+ context,
86
+ );
87
+ }
88
+
89
+ export function insertHardBreak(
90
+ document: CanonicalDocumentEnvelope,
91
+ selection: SelectionSnapshot,
92
+ context: TextCommandContext,
93
+ ): TextTransactionResult {
94
+ return applyTextTransaction(
95
+ document,
96
+ selection,
97
+ {
98
+ type: "replace",
99
+ insertion: [{ type: "hard_break" }],
100
+ },
101
+ context,
102
+ );
103
+ }
104
+
105
+ export function splitParagraph(
106
+ document: CanonicalDocumentEnvelope,
107
+ selection: SelectionSnapshot,
108
+ context: TextCommandContext,
109
+ ): TextTransactionResult {
110
+ return applyTextTransaction(
111
+ document,
112
+ selection,
113
+ {
114
+ type: "replace",
115
+ insertion: [{ type: "paragraph_break" }],
116
+ },
117
+ context,
118
+ );
119
+ }
@@ -0,0 +1,3 @@
1
+ # Core Schema
2
+
3
+ Canonical node, mark, and attribute definitions belong here.