@beyondwork/docx-react-component 1.0.18 → 1.0.20

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 (105) hide show
  1. package/README.md +8 -2
  2. package/package.json +24 -34
  3. package/src/api/README.md +5 -1
  4. package/src/api/public-types.ts +710 -4
  5. package/src/api/session-state.ts +60 -0
  6. package/src/core/commands/formatting-commands.ts +2 -1
  7. package/src/core/commands/image-commands.ts +147 -0
  8. package/src/core/commands/index.ts +19 -3
  9. package/src/core/commands/list-commands.ts +231 -36
  10. package/src/core/commands/paragraph-layout-commands.ts +339 -0
  11. package/src/core/commands/section-layout-commands.ts +680 -0
  12. package/src/core/commands/style-commands.ts +262 -0
  13. package/src/core/search/search-text.ts +357 -0
  14. package/src/core/selection/mapping.ts +41 -0
  15. package/src/core/state/editor-state.ts +4 -1
  16. package/src/index.ts +51 -0
  17. package/src/io/docx-session.ts +623 -56
  18. package/src/io/export/serialize-comments.ts +104 -34
  19. package/src/io/export/serialize-footnotes.ts +198 -1
  20. package/src/io/export/serialize-headers-footers.ts +203 -10
  21. package/src/io/export/serialize-main-document.ts +285 -8
  22. package/src/io/export/serialize-numbering.ts +28 -7
  23. package/src/io/export/split-review-boundaries.ts +181 -19
  24. package/src/io/normalize/normalize-text.ts +144 -32
  25. package/src/io/ooxml/highlight-colors.ts +39 -0
  26. package/src/io/ooxml/numbering-sentinels.ts +44 -0
  27. package/src/io/ooxml/parse-comments.ts +85 -19
  28. package/src/io/ooxml/parse-fields.ts +396 -0
  29. package/src/io/ooxml/parse-footnotes.ts +452 -22
  30. package/src/io/ooxml/parse-headers-footers.ts +657 -29
  31. package/src/io/ooxml/parse-inline-media.ts +30 -0
  32. package/src/io/ooxml/parse-main-document.ts +807 -20
  33. package/src/io/ooxml/parse-numbering.ts +7 -0
  34. package/src/io/ooxml/parse-revisions.ts +317 -38
  35. package/src/io/ooxml/parse-settings.ts +184 -0
  36. package/src/io/ooxml/parse-shapes.ts +25 -0
  37. package/src/io/ooxml/parse-styles.ts +463 -0
  38. package/src/io/ooxml/parse-theme.ts +32 -0
  39. package/src/legal/bookmarks.ts +44 -0
  40. package/src/legal/cross-references.ts +59 -1
  41. package/src/model/canonical-document.ts +250 -4
  42. package/src/model/cds-1.0.0.ts +13 -0
  43. package/src/model/snapshot.ts +87 -2
  44. package/src/review/store/revision-store.ts +6 -0
  45. package/src/review/store/revision-types.ts +1 -0
  46. package/src/runtime/document-layout.ts +332 -0
  47. package/src/runtime/document-navigation.ts +603 -0
  48. package/src/runtime/document-runtime.ts +1754 -78
  49. package/src/runtime/document-search.ts +145 -0
  50. package/src/runtime/numbering-prefix.ts +47 -26
  51. package/src/runtime/page-layout-estimation.ts +212 -0
  52. package/src/runtime/read-only-diagnostics-runtime.ts +9 -0
  53. package/src/runtime/session-capabilities.ts +35 -3
  54. package/src/runtime/story-context.ts +164 -0
  55. package/src/runtime/story-targeting.ts +162 -0
  56. package/src/runtime/surface-projection.ts +324 -36
  57. package/src/runtime/table-schema.ts +89 -7
  58. package/src/runtime/view-state.ts +477 -0
  59. package/src/runtime/workflow-markup.ts +349 -0
  60. package/src/ui/WordReviewEditor.tsx +2469 -1344
  61. package/src/ui/browser-export.ts +52 -0
  62. package/src/ui/editor-command-bag.ts +120 -0
  63. package/src/ui/editor-runtime-boundary.ts +1422 -0
  64. package/src/ui/editor-shell-view.tsx +134 -0
  65. package/src/ui/editor-surface-controller.tsx +51 -0
  66. package/src/ui/headless/preserve-editor-selection.ts +5 -0
  67. package/src/ui/headless/revision-decoration-model.ts +4 -4
  68. package/src/ui/headless/selection-helpers.ts +20 -0
  69. package/src/ui/headless/selection-toolbar-model.ts +22 -0
  70. package/src/ui/headless/use-editor-keyboard.ts +6 -1
  71. package/src/ui/runtime-snapshot-selectors.ts +197 -0
  72. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +18 -2
  73. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +129 -0
  74. package/src/ui-tailwind/chrome/tw-layout-panel.tsx +114 -0
  75. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +34 -0
  76. package/src/ui-tailwind/chrome/tw-page-ruler.tsx +386 -0
  77. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +150 -14
  78. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +128 -0
  79. package/src/ui-tailwind/editor-surface/perf-probe.ts +179 -0
  80. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +46 -7
  81. package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +31 -0
  82. package/src/ui-tailwind/editor-surface/pm-decorations.ts +35 -0
  83. package/src/ui-tailwind/editor-surface/pm-position-map.ts +3 -3
  84. package/src/ui-tailwind/editor-surface/pm-schema.ts +186 -13
  85. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +191 -68
  86. package/src/ui-tailwind/editor-surface/search-plugin.ts +19 -68
  87. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +51 -0
  88. package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +11 -0
  89. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +7 -1
  90. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +528 -85
  91. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +0 -1
  92. package/src/ui-tailwind/index.ts +2 -1
  93. package/src/ui-tailwind/page-chrome-model.ts +27 -0
  94. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +277 -147
  95. package/src/ui-tailwind/review/tw-health-panel.tsx +31 -2
  96. package/src/ui-tailwind/review/tw-review-rail.tsx +8 -8
  97. package/src/ui-tailwind/review/tw-revision-sidebar.tsx +15 -15
  98. package/src/ui-tailwind/theme/editor-theme.css +127 -0
  99. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +4 -0
  100. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +829 -12
  101. package/src/ui-tailwind/tw-review-workspace.tsx +1238 -42
  102. package/src/validation/compatibility-engine.ts +119 -24
  103. package/src/validation/compatibility-report.ts +1 -0
  104. package/src/validation/diagnostics.ts +1 -0
  105. package/src/validation/docx-comment-proof.ts +707 -0
@@ -0,0 +1,332 @@
1
+ import type {
2
+ EditorStoryTarget,
3
+ PageLayoutSnapshot,
4
+ } from "../api/public-types";
5
+ import { MAIN_STORY_TARGET } from "../core/selection/mapping.ts";
6
+ import {
7
+ createSelectionSnapshot,
8
+ type CanonicalDocumentEnvelope,
9
+ type EditorState,
10
+ } from "../core/state/editor-state.ts";
11
+ import type {
12
+ FooterDocument,
13
+ HeaderDocument,
14
+ SectionProperties,
15
+ SubPartsCatalog,
16
+ } from "../model/canonical-document.ts";
17
+ import { createEditorSurfaceSnapshot } from "./surface-projection.ts";
18
+ import {
19
+ resolveSectionVariants,
20
+ sectionSupportsStoryTarget,
21
+ } from "./story-context.ts";
22
+ import { storyTargetKey } from "./story-targeting.ts";
23
+
24
+ export interface ResolvedDocumentSection {
25
+ index: number;
26
+ start: number;
27
+ end: number;
28
+ properties?: SectionProperties;
29
+ }
30
+
31
+ export function buildResolvedSections(
32
+ document: CanonicalDocumentEnvelope,
33
+ ): ResolvedDocumentSection[] {
34
+ const mainSurface = createEditorSurfaceSnapshot(
35
+ document,
36
+ createSelectionSnapshot(0, 0),
37
+ MAIN_STORY_TARGET,
38
+ );
39
+ const sections: ResolvedDocumentSection[] = [];
40
+ let sectionStart = 0;
41
+ let sectionIndex = 0;
42
+
43
+ for (const [index, block] of document.content.children.entries()) {
44
+ if (block.type !== "section_break") {
45
+ continue;
46
+ }
47
+
48
+ const surfaceBlock = mainSurface.blocks[index];
49
+ sections.push({
50
+ index: sectionIndex,
51
+ start: sectionStart,
52
+ end: surfaceBlock?.from ?? sectionStart,
53
+ properties: block.sectionProperties,
54
+ });
55
+ sectionStart = surfaceBlock?.to ?? sectionStart;
56
+ sectionIndex += 1;
57
+ }
58
+
59
+ sections.push({
60
+ index: sectionIndex,
61
+ start: sectionStart,
62
+ end: mainSurface.storySize,
63
+ properties: document.subParts?.finalSectionProperties,
64
+ });
65
+
66
+ return sections;
67
+ }
68
+
69
+ export function findSectionForPosition(
70
+ sections: ReadonlyArray<ResolvedDocumentSection>,
71
+ position: number,
72
+ ): ResolvedDocumentSection {
73
+ for (const section of sections) {
74
+ if (position < section.end) {
75
+ return section;
76
+ }
77
+ }
78
+
79
+ return sections[sections.length - 1] ?? {
80
+ index: 0,
81
+ start: 0,
82
+ end: 0,
83
+ };
84
+ }
85
+
86
+ export function buildPageLayoutSnapshot(
87
+ sectionIndex: number,
88
+ properties: SectionProperties | undefined,
89
+ subParts: SubPartsCatalog | undefined,
90
+ ): PageLayoutSnapshot {
91
+ const pageSize = properties?.pageSize ?? {
92
+ width: 12240,
93
+ height: 15840,
94
+ orientation: "portrait" as const,
95
+ };
96
+ const margins = properties?.pageMargins ?? {
97
+ top: 1440,
98
+ right: 1440,
99
+ bottom: 1440,
100
+ left: 1440,
101
+ header: 720,
102
+ footer: 720,
103
+ gutter: 0,
104
+ };
105
+ const columns = properties?.columns;
106
+ const explicitColumns = columns?.columns ?? [];
107
+ const differentOddEvenPages = Boolean(subParts?.settings?.evenAndOddHeaders);
108
+
109
+ return {
110
+ sectionIndex,
111
+ ...(properties?.sectionType ? { sectionType: properties.sectionType } : {}),
112
+ pageWidth: pageSize.width,
113
+ pageHeight: pageSize.height,
114
+ marginTop: margins.top,
115
+ marginBottom: margins.bottom,
116
+ marginLeft: margins.left,
117
+ marginRight: margins.right,
118
+ headerMargin: margins.header ?? 720,
119
+ footerMargin: margins.footer ?? 720,
120
+ gutter: margins.gutter ?? 0,
121
+ orientation: pageSize.orientation ?? "portrait",
122
+ columns:
123
+ explicitColumns.length > 0 ? explicitColumns.length : (columns?.count ?? 1),
124
+ differentFirstPage: Boolean(properties?.titlePage),
125
+ differentOddEvenPages,
126
+ ...(properties?.pageNumbering
127
+ ? {
128
+ pageNumbering: {
129
+ ...(properties.pageNumbering.format
130
+ ? { format: properties.pageNumbering.format }
131
+ : {}),
132
+ ...(properties.pageNumbering.start !== undefined
133
+ ? { start: properties.pageNumbering.start }
134
+ : {}),
135
+ ...(properties.pageNumbering.chapStyle
136
+ ? { chapterStyle: properties.pageNumbering.chapStyle }
137
+ : {}),
138
+ ...(properties.pageNumbering.chapSep
139
+ ? { chapterSeparator: properties.pageNumbering.chapSep }
140
+ : {}),
141
+ },
142
+ }
143
+ : {}),
144
+ ...(properties?.lineNumbering
145
+ ? {
146
+ lineNumbering: {
147
+ ...(properties.lineNumbering.countBy !== undefined
148
+ ? { countBy: properties.lineNumbering.countBy }
149
+ : {}),
150
+ ...(properties.lineNumbering.start !== undefined
151
+ ? { start: properties.lineNumbering.start }
152
+ : {}),
153
+ ...(properties.lineNumbering.distance !== undefined
154
+ ? { distance: properties.lineNumbering.distance }
155
+ : {}),
156
+ ...(properties.lineNumbering.restart
157
+ ? { restart: properties.lineNumbering.restart }
158
+ : {}),
159
+ },
160
+ }
161
+ : {}),
162
+ ...(properties?.pageBorders
163
+ ? {
164
+ pageBorders: {
165
+ ...(properties.pageBorders.top
166
+ ? { top: { ...properties.pageBorders.top } }
167
+ : {}),
168
+ ...(properties.pageBorders.left
169
+ ? { left: { ...properties.pageBorders.left } }
170
+ : {}),
171
+ ...(properties.pageBorders.bottom
172
+ ? { bottom: { ...properties.pageBorders.bottom } }
173
+ : {}),
174
+ ...(properties.pageBorders.right
175
+ ? { right: { ...properties.pageBorders.right } }
176
+ : {}),
177
+ ...(properties.pageBorders.offsetFrom
178
+ ? { offsetFrom: properties.pageBorders.offsetFrom }
179
+ : {}),
180
+ ...(properties.pageBorders.display
181
+ ? { display: properties.pageBorders.display }
182
+ : {}),
183
+ ...(properties.pageBorders.zOrder
184
+ ? { zOrder: properties.pageBorders.zOrder }
185
+ : {}),
186
+ },
187
+ }
188
+ : {}),
189
+ ...(properties?.documentGrid
190
+ ? {
191
+ documentGrid: {
192
+ ...(properties.documentGrid.type
193
+ ? { type: properties.documentGrid.type }
194
+ : {}),
195
+ ...(properties.documentGrid.linePitch !== undefined
196
+ ? { linePitch: properties.documentGrid.linePitch }
197
+ : {}),
198
+ ...(properties.documentGrid.charSpace !== undefined
199
+ ? { charSpace: properties.documentGrid.charSpace }
200
+ : {}),
201
+ },
202
+ }
203
+ : {}),
204
+ columnDefinitions: explicitColumns,
205
+ equalWidthColumns:
206
+ columns?.equalWidth ??
207
+ (explicitColumns.length === 0 || explicitColumns.length <= 1),
208
+ columnSeparator: Boolean(columns?.separator),
209
+ headerVariants: resolveSectionVariants(
210
+ "header",
211
+ sectionIndex,
212
+ properties?.headerReferences,
213
+ subParts?.headers ?? [],
214
+ ),
215
+ footerVariants: resolveSectionVariants(
216
+ "footer",
217
+ sectionIndex,
218
+ properties?.footerReferences,
219
+ subParts?.footers ?? [],
220
+ ),
221
+ };
222
+ }
223
+
224
+ export function resolveSectionForStoryTarget(
225
+ document: CanonicalDocumentEnvelope,
226
+ sections: ReadonlyArray<ResolvedDocumentSection>,
227
+ target: EditorStoryTarget,
228
+ ): ResolvedDocumentSection | undefined {
229
+ if (target.kind === "main" || sections.length === 0) {
230
+ return sections[0];
231
+ }
232
+
233
+ if (target.kind === "header") {
234
+ if (target.sectionIndex !== undefined) {
235
+ const section = sections.find((candidate) => candidate.index === target.sectionIndex);
236
+ return section &&
237
+ sectionSupportsStoryTarget(document, target.sectionIndex, target)
238
+ ? section
239
+ : undefined;
240
+ }
241
+ return (
242
+ findSectionByStoryReference(
243
+ document,
244
+ sections,
245
+ target,
246
+ "header",
247
+ ) ?? sections[0]
248
+ );
249
+ }
250
+
251
+ if (target.kind === "footer") {
252
+ if (target.sectionIndex !== undefined) {
253
+ const section = sections.find((candidate) => candidate.index === target.sectionIndex);
254
+ return section &&
255
+ sectionSupportsStoryTarget(document, target.sectionIndex, target)
256
+ ? section
257
+ : undefined;
258
+ }
259
+ return (
260
+ findSectionByStoryReference(
261
+ document,
262
+ sections,
263
+ target,
264
+ "footer",
265
+ ) ?? sections[0]
266
+ );
267
+ }
268
+
269
+ return undefined;
270
+ }
271
+
272
+ export function resolveActiveSection(
273
+ state: EditorState,
274
+ activeStory: EditorStoryTarget,
275
+ sections: ReadonlyArray<ResolvedDocumentSection>,
276
+ storySelections?: ReadonlyMap<string, EditorState["selection"]>,
277
+ ): ResolvedDocumentSection | undefined {
278
+ if (sections.length === 0) {
279
+ return undefined;
280
+ }
281
+
282
+ if (activeStory.kind === "main") {
283
+ return findSectionForPosition(sections, state.selection.head);
284
+ }
285
+
286
+ const referencedSection = resolveSectionForStoryTarget(
287
+ state.document,
288
+ sections,
289
+ activeStory,
290
+ );
291
+ if (referencedSection) {
292
+ return referencedSection;
293
+ }
294
+
295
+ const mainSelection =
296
+ storySelections?.get(storyTargetKey(MAIN_STORY_TARGET)) ?? state.selection;
297
+ return findSectionForPosition(sections, mainSelection.head);
298
+ }
299
+
300
+ function findSectionByStoryReference(
301
+ document: CanonicalDocumentEnvelope,
302
+ sections: ReadonlyArray<ResolvedDocumentSection>,
303
+ target:
304
+ | Extract<EditorStoryTarget, { kind: "header" }>
305
+ | Extract<EditorStoryTarget, { kind: "footer" }>,
306
+ kind: "header" | "footer",
307
+ ): ResolvedDocumentSection | undefined {
308
+ const propertyKey =
309
+ kind === "header" ? "headerReferences" : "footerReferences";
310
+ const documents =
311
+ kind === "header"
312
+ ? document.subParts?.headers ?? []
313
+ : document.subParts?.footers ?? [];
314
+
315
+ return (
316
+ sections.find((section) =>
317
+ section.properties?.[propertyKey]?.some(
318
+ (ref) =>
319
+ ref.relationshipId === target.relationshipId &&
320
+ ref.variant === target.variant,
321
+ ),
322
+ ) ??
323
+ sections.find((section) =>
324
+ documents.some(
325
+ (entry) =>
326
+ entry.relationshipId === target.relationshipId &&
327
+ entry.variant === target.variant &&
328
+ entry.sectionIndex === section.index,
329
+ ),
330
+ )
331
+ );
332
+ }