@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,331 @@
1
+ import type { TransactionMapping } from "../../core/selection/mapping.ts";
2
+ import {
3
+ isDetachedCommentAnchor,
4
+ remapCommentAnchor,
5
+ summarizeCommentAnchor,
6
+ type CommentAnchor,
7
+ } from "./comment-anchors.ts";
8
+
9
+ export interface CommentEntry {
10
+ entryId: string;
11
+ authorId: string;
12
+ body: string;
13
+ createdAt: string;
14
+ metadata?: CommentEntryMetadata;
15
+ }
16
+
17
+ export interface CommentEntryMetadata {
18
+ ooxmlCommentId?: string;
19
+ paraId?: string;
20
+ parentParaId?: string;
21
+ durableId?: string;
22
+ initials?: string;
23
+ }
24
+
25
+ export interface CommentResolution {
26
+ resolvedAt: string;
27
+ resolvedBy: string;
28
+ }
29
+
30
+ export type CommentThreadStatus = "open" | "resolved" | "detached";
31
+
32
+ export interface CommentThread {
33
+ commentId: string;
34
+ anchor: CommentAnchor;
35
+ status: CommentThreadStatus;
36
+ entries: CommentEntry[];
37
+ createdBy: string;
38
+ createdAt: string;
39
+ resolution?: CommentResolution;
40
+ warningIds: string[];
41
+ metadata?: CommentThreadMetadata;
42
+ }
43
+
44
+ export interface CommentThreadMetadata {
45
+ source?: "runtime" | "import";
46
+ rootOoxmlCommentId?: string;
47
+ rootParaId?: string;
48
+ }
49
+
50
+ export interface CommentStore {
51
+ version: "comment-store/1";
52
+ threads: Record<string, CommentThread>;
53
+ }
54
+
55
+ export interface CreateCommentThreadParams {
56
+ commentId: string;
57
+ anchor: CommentAnchor;
58
+ createdBy: string;
59
+ createdAt: string;
60
+ body: string;
61
+ entryId?: string;
62
+ warningIds?: string[];
63
+ }
64
+
65
+ export interface AppendCommentEntryParams {
66
+ commentId: string;
67
+ entry: CommentEntry;
68
+ }
69
+
70
+ export interface ResolveCommentThreadParams {
71
+ commentId: string;
72
+ resolvedAt: string;
73
+ resolvedBy: string;
74
+ }
75
+
76
+ export interface CommentSidebarThread {
77
+ commentId: string;
78
+ status: CommentThreadStatus;
79
+ excerpt: string;
80
+ entryCount: number;
81
+ createdAt: string;
82
+ createdBy: string;
83
+ warningCount: number;
84
+ anchorLabel: string;
85
+ isActive: boolean;
86
+ resolvedAt?: string;
87
+ resolvedBy?: string;
88
+ }
89
+
90
+ export interface CommentSidebarProjection {
91
+ totalCount: number;
92
+ openCommentIds: string[];
93
+ resolvedCommentIds: string[];
94
+ detachedCommentIds: string[];
95
+ threads: CommentSidebarThread[];
96
+ }
97
+
98
+ export function createCommentStore(
99
+ threads: Record<string, CommentThread> = {},
100
+ ): CommentStore {
101
+ return {
102
+ version: "comment-store/1",
103
+ threads,
104
+ };
105
+ }
106
+
107
+ export function createCommentThread(
108
+ params: CreateCommentThreadParams,
109
+ ): CommentThread {
110
+ return {
111
+ commentId: params.commentId,
112
+ anchor: params.anchor,
113
+ status: isDetachedCommentAnchor(params.anchor) ? "detached" : "open",
114
+ entries: [
115
+ {
116
+ entryId: params.entryId ?? `${params.commentId}-entry-1`,
117
+ authorId: params.createdBy,
118
+ body: params.body,
119
+ createdAt: params.createdAt,
120
+ },
121
+ ],
122
+ createdBy: params.createdBy,
123
+ createdAt: params.createdAt,
124
+ warningIds: [...(params.warningIds ?? [])],
125
+ };
126
+ }
127
+
128
+ export function upsertCommentThread(
129
+ store: CommentStore,
130
+ thread: CommentThread,
131
+ ): CommentStore {
132
+ return {
133
+ ...store,
134
+ threads: {
135
+ ...store.threads,
136
+ [thread.commentId]: normalizeThread(thread),
137
+ },
138
+ };
139
+ }
140
+
141
+ export function appendCommentEntry(
142
+ store: CommentStore,
143
+ params: AppendCommentEntryParams,
144
+ ): CommentStore {
145
+ const existing = store.threads[params.commentId];
146
+ if (!existing) {
147
+ return store;
148
+ }
149
+
150
+ return upsertCommentThread(store, {
151
+ ...existing,
152
+ entries: [...existing.entries, params.entry],
153
+ });
154
+ }
155
+
156
+ export function resolveCommentThread(
157
+ store: CommentStore,
158
+ params: ResolveCommentThreadParams,
159
+ ): CommentStore {
160
+ const existing = store.threads[params.commentId];
161
+ if (!existing) {
162
+ return store;
163
+ }
164
+
165
+ return upsertCommentThread(store, {
166
+ ...existing,
167
+ status: existing.status === "detached" ? "detached" : "resolved",
168
+ resolution: {
169
+ resolvedAt: params.resolvedAt,
170
+ resolvedBy: params.resolvedBy,
171
+ },
172
+ });
173
+ }
174
+
175
+ export function reopenCommentThread(
176
+ store: CommentStore,
177
+ commentId: string,
178
+ ): CommentStore {
179
+ const existing = store.threads[commentId];
180
+ if (!existing) {
181
+ return store;
182
+ }
183
+
184
+ return upsertCommentThread(store, {
185
+ ...existing,
186
+ status: isDetachedCommentAnchor(existing.anchor) ? "detached" : "open",
187
+ resolution: undefined,
188
+ });
189
+ }
190
+
191
+ export function setCommentThreadWarnings(
192
+ store: CommentStore,
193
+ commentId: string,
194
+ warningIds: string[],
195
+ ): CommentStore {
196
+ const existing = store.threads[commentId];
197
+ if (!existing) {
198
+ return store;
199
+ }
200
+
201
+ return upsertCommentThread(store, {
202
+ ...existing,
203
+ warningIds: [...warningIds],
204
+ });
205
+ }
206
+
207
+ export function remapCommentStore(
208
+ store: CommentStore,
209
+ mapping: TransactionMapping,
210
+ ): CommentStore {
211
+ return createCommentStore(
212
+ Object.fromEntries(
213
+ Object.entries(store.threads).map(([commentId, thread]) => {
214
+ const anchor = remapCommentAnchor(thread.anchor, mapping);
215
+ const nextStatus =
216
+ anchor.kind === "detached"
217
+ ? "detached"
218
+ : thread.status === "resolved"
219
+ ? "resolved"
220
+ : "open";
221
+
222
+ return [
223
+ commentId,
224
+ normalizeThread({
225
+ ...thread,
226
+ anchor,
227
+ status: nextStatus,
228
+ }),
229
+ ];
230
+ }),
231
+ ),
232
+ );
233
+ }
234
+
235
+ export function createCommentSidebarProjection(
236
+ store: CommentStore,
237
+ activeCommentId?: string,
238
+ ): CommentSidebarProjection {
239
+ const threads = Object.values(store.threads)
240
+ .map((thread) => toSidebarThread(thread, activeCommentId))
241
+ .sort(compareSidebarThreads);
242
+
243
+ return {
244
+ totalCount: threads.length,
245
+ openCommentIds: threads
246
+ .filter((thread) => thread.status === "open")
247
+ .map((thread) => thread.commentId),
248
+ resolvedCommentIds: threads
249
+ .filter((thread) => thread.status === "resolved")
250
+ .map((thread) => thread.commentId),
251
+ detachedCommentIds: threads
252
+ .filter((thread) => thread.status === "detached")
253
+ .map((thread) => thread.commentId),
254
+ threads,
255
+ };
256
+ }
257
+
258
+ function toSidebarThread(
259
+ thread: CommentThread,
260
+ activeCommentId?: string,
261
+ ): CommentSidebarThread {
262
+ const firstEntry = thread.entries[0];
263
+ const anchorSummary = summarizeCommentAnchor(thread.anchor);
264
+
265
+ return {
266
+ commentId: thread.commentId,
267
+ status: thread.status,
268
+ excerpt: firstEntry ? summarizeBody(firstEntry.body) : "Empty thread",
269
+ entryCount: thread.entries.length,
270
+ createdAt: thread.createdAt,
271
+ createdBy: thread.createdBy,
272
+ warningCount: thread.warningIds.length,
273
+ anchorLabel:
274
+ anchorSummary.state === "detached"
275
+ ? `Detached ${anchorSummary.range.from}-${anchorSummary.range.to}`
276
+ : `Range ${anchorSummary.range.from}-${anchorSummary.range.to}`,
277
+ isActive: activeCommentId === thread.commentId,
278
+ resolvedAt: thread.resolution?.resolvedAt,
279
+ resolvedBy: thread.resolution?.resolvedBy,
280
+ };
281
+ }
282
+
283
+ function normalizeThread(thread: CommentThread): CommentThread {
284
+ if (isDetachedCommentAnchor(thread.anchor)) {
285
+ return {
286
+ ...thread,
287
+ status: "detached",
288
+ };
289
+ }
290
+
291
+ if (thread.status === "detached") {
292
+ return {
293
+ ...thread,
294
+ status: thread.resolution ? "resolved" : "open",
295
+ };
296
+ }
297
+
298
+ return thread;
299
+ }
300
+
301
+ function compareSidebarThreads(
302
+ left: CommentSidebarThread,
303
+ right: CommentSidebarThread,
304
+ ): number {
305
+ const statusDelta = priorityForStatus(left.status) - priorityForStatus(right.status);
306
+ if (statusDelta !== 0) {
307
+ return statusDelta;
308
+ }
309
+
310
+ return left.createdAt.localeCompare(right.createdAt);
311
+ }
312
+
313
+ function priorityForStatus(status: CommentThreadStatus): number {
314
+ switch (status) {
315
+ case "open":
316
+ return 0;
317
+ case "detached":
318
+ return 1;
319
+ case "resolved":
320
+ return 2;
321
+ }
322
+ }
323
+
324
+ function summarizeBody(body: string): string {
325
+ const collapsed = body.replace(/\s+/g, " ").trim();
326
+ if (!collapsed) {
327
+ return "Empty thread";
328
+ }
329
+
330
+ return collapsed.length > 80 ? `${collapsed.slice(0, 77)}...` : collapsed;
331
+ }
@@ -0,0 +1,109 @@
1
+ import { createDetachedAnchor } from "../../core/selection/mapping.ts";
2
+ import type {
3
+ CommentEntry,
4
+ CommentEntryMetadata,
5
+ CommentThread,
6
+ CommentThreadMetadata,
7
+ } from "./comment-store.ts";
8
+ import { createCommentRangeAnchor } from "./comment-anchors.ts";
9
+
10
+ export interface ImportedCommentEntryInput {
11
+ entryId?: string;
12
+ authorId?: string;
13
+ body: string;
14
+ createdAt?: string;
15
+ metadata?: CommentEntryMetadata;
16
+ }
17
+
18
+ export interface ImportedCommentThreadInput {
19
+ commentId: string;
20
+ body: string;
21
+ createdBy?: string;
22
+ createdAt?: string;
23
+ range?:
24
+ | {
25
+ from: number;
26
+ to: number;
27
+ }
28
+ | undefined;
29
+ warningIds?: string[];
30
+ entries?: CommentEntry[];
31
+ status?: CommentThread["status"];
32
+ resolution?: CommentThread["resolution"];
33
+ metadata?: CommentThreadMetadata;
34
+ }
35
+
36
+ export function createImportedCommentThread(
37
+ input: ImportedCommentThreadInput,
38
+ ): CommentThread {
39
+ const createdAt = input.createdAt ?? "1970-01-01T00:00:00.000Z";
40
+ const createdBy = input.createdBy ?? "unknown";
41
+ const entries =
42
+ input.entries && input.entries.length > 0
43
+ ? input.entries
44
+ : [
45
+ {
46
+ entryId: `${input.commentId}-entry-1`,
47
+ authorId: createdBy,
48
+ body: input.body,
49
+ createdAt,
50
+ },
51
+ ];
52
+ const status =
53
+ input.status ??
54
+ (input.range ? "open" : "detached");
55
+
56
+ return {
57
+ commentId: input.commentId,
58
+ anchor:
59
+ status === "detached"
60
+ ? createDetachedAnchor(input.range ?? { from: 0, to: 0 }, "importAmbiguity")
61
+ : input.range
62
+ ? createCommentRangeAnchor(input.range.from, input.range.to)
63
+ : createDetachedAnchor({ from: 0, to: 0 }, "importAmbiguity"),
64
+ status,
65
+ entries,
66
+ createdBy,
67
+ createdAt,
68
+ resolution: input.resolution,
69
+ warningIds: [...(input.warningIds ?? [])],
70
+ metadata: input.metadata,
71
+ };
72
+ }
73
+
74
+ export function createImportedCommentReply(
75
+ commentId: string,
76
+ input: ImportedCommentEntryInput,
77
+ ) {
78
+ return {
79
+ entryId: input.entryId ?? `${commentId}-entry-reply`,
80
+ authorId: input.authorId ?? "unknown",
81
+ body: input.body,
82
+ createdAt: input.createdAt ?? "1970-01-01T00:00:00.000Z",
83
+ };
84
+ }
85
+
86
+ export function updateCommentThreadBody(
87
+ thread: CommentThread,
88
+ body: string,
89
+ ): CommentThread {
90
+ const firstEntry = thread.entries[0];
91
+ if (!firstEntry) {
92
+ return thread;
93
+ }
94
+
95
+ return {
96
+ ...thread,
97
+ entries: [
98
+ {
99
+ ...firstEntry,
100
+ body,
101
+ },
102
+ ...thread.entries.slice(1),
103
+ ],
104
+ };
105
+ }
106
+
107
+ export function getCommentThreadBody(thread: CommentThread): string {
108
+ return thread.entries.map((entry) => entry.body).join("\n");
109
+ }