@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,164 @@
1
+ import type { EditorStoryTarget } from "../api/public-types";
2
+ import type { CanonicalDocumentEnvelope } from "../core/state/editor-state.ts";
3
+ import type {
4
+ FooterDocument,
5
+ HeaderDocument,
6
+ SectionProperties,
7
+ } from "../model/canonical-document.ts";
8
+
9
+ export interface DocumentSectionContext {
10
+ index: number;
11
+ properties?: SectionProperties;
12
+ }
13
+
14
+ export type HeaderFooterStoryTarget =
15
+ | Extract<EditorStoryTarget, { kind: "header" }>
16
+ | Extract<EditorStoryTarget, { kind: "footer" }>;
17
+
18
+ export function collectSectionContexts(
19
+ document: CanonicalDocumentEnvelope,
20
+ ): DocumentSectionContext[] {
21
+ const sections: DocumentSectionContext[] = [];
22
+ let sectionIndex = 0;
23
+
24
+ for (const block of document.content.children) {
25
+ if (block.type !== "section_break") {
26
+ continue;
27
+ }
28
+ sections.push({
29
+ index: sectionIndex,
30
+ properties: block.sectionProperties,
31
+ });
32
+ sectionIndex += 1;
33
+ }
34
+
35
+ sections.push({
36
+ index: sectionIndex,
37
+ properties: document.subParts?.finalSectionProperties,
38
+ });
39
+
40
+ return sections;
41
+ }
42
+
43
+ export function resolveSectionVariants(
44
+ kind: "header" | "footer",
45
+ sectionIndex: number,
46
+ explicitReferences:
47
+ | SectionProperties["headerReferences"]
48
+ | SectionProperties["footerReferences"]
49
+ | undefined,
50
+ documents: HeaderDocument[] | FooterDocument[],
51
+ ): Array<{ variant: "default" | "first" | "even"; relationshipId: string }> {
52
+ if (explicitReferences && explicitReferences.length > 0) {
53
+ return explicitReferences.map((ref) => ({
54
+ variant: ref.variant,
55
+ relationshipId: ref.relationshipId,
56
+ }));
57
+ }
58
+
59
+ return documents
60
+ .filter(
61
+ (entry) =>
62
+ entry.sectionIndex === undefined || entry.sectionIndex === sectionIndex,
63
+ )
64
+ .map((entry) => ({
65
+ variant: entry.variant,
66
+ relationshipId: entry.relationshipId,
67
+ }));
68
+ }
69
+
70
+ export function findHeaderFooterDocumentEntry(
71
+ document: CanonicalDocumentEnvelope,
72
+ target: HeaderFooterStoryTarget,
73
+ ): HeaderDocument | FooterDocument | undefined {
74
+ const documents =
75
+ target.kind === "header"
76
+ ? document.subParts?.headers ?? []
77
+ : document.subParts?.footers ?? [];
78
+ const matches = documents.filter(
79
+ (entry) =>
80
+ entry.relationshipId === target.relationshipId &&
81
+ entry.variant === target.variant,
82
+ );
83
+ if (matches.length === 0) {
84
+ return undefined;
85
+ }
86
+ if (target.sectionIndex !== undefined) {
87
+ return (
88
+ matches.find((entry) => entry.sectionIndex === target.sectionIndex) ??
89
+ matches.find((entry) => entry.sectionIndex === undefined) ??
90
+ matches[0]
91
+ );
92
+ }
93
+ return matches[0];
94
+ }
95
+
96
+ export function sectionSupportsStoryTarget(
97
+ document: CanonicalDocumentEnvelope,
98
+ sectionIndex: number,
99
+ target: HeaderFooterStoryTarget,
100
+ ): boolean {
101
+ if (!findHeaderFooterDocumentEntry(document, target)) {
102
+ return false;
103
+ }
104
+
105
+ const section = collectSectionContexts(document).find(
106
+ (candidate) => candidate.index === sectionIndex,
107
+ );
108
+ if (!section) {
109
+ return false;
110
+ }
111
+
112
+ const variants = resolveSectionVariants(
113
+ target.kind,
114
+ sectionIndex,
115
+ target.kind === "header"
116
+ ? section.properties?.headerReferences
117
+ : section.properties?.footerReferences,
118
+ target.kind === "header"
119
+ ? document.subParts?.headers ?? []
120
+ : document.subParts?.footers ?? [],
121
+ );
122
+
123
+ return variants.some(
124
+ (variant) =>
125
+ variant.relationshipId === target.relationshipId &&
126
+ variant.variant === target.variant,
127
+ );
128
+ }
129
+
130
+ export function normalizeHeaderFooterTarget(
131
+ document: CanonicalDocumentEnvelope,
132
+ target: HeaderFooterStoryTarget,
133
+ preferredSectionIndex?: number,
134
+ ): HeaderFooterStoryTarget | undefined {
135
+ if (!findHeaderFooterDocumentEntry(document, target)) {
136
+ return undefined;
137
+ }
138
+
139
+ if (target.sectionIndex !== undefined) {
140
+ return sectionSupportsStoryTarget(document, target.sectionIndex, target)
141
+ ? target
142
+ : undefined;
143
+ }
144
+
145
+ const candidateIndexes = collectSectionContexts(document)
146
+ .map((section) => section.index)
147
+ .filter((sectionIndex) =>
148
+ sectionSupportsStoryTarget(document, sectionIndex, target),
149
+ );
150
+ if (candidateIndexes.length === 0) {
151
+ return undefined;
152
+ }
153
+
154
+ const resolvedSectionIndex =
155
+ preferredSectionIndex !== undefined &&
156
+ candidateIndexes.includes(preferredSectionIndex)
157
+ ? preferredSectionIndex
158
+ : candidateIndexes[0];
159
+
160
+ return {
161
+ ...target,
162
+ sectionIndex: resolvedSectionIndex,
163
+ };
164
+ }
@@ -0,0 +1,162 @@
1
+ import type { EditorStoryTarget } from "../api/public-types";
2
+ import type { CanonicalDocumentEnvelope } from "../core/state/editor-state.ts";
3
+ import type { BlockNode } from "../model/canonical-document.ts";
4
+ import {
5
+ findHeaderFooterDocumentEntry,
6
+ normalizeHeaderFooterTarget,
7
+ } from "./story-context.ts";
8
+
9
+ export function storyTargetKey(target: EditorStoryTarget): string {
10
+ switch (target.kind) {
11
+ case "main":
12
+ return "main";
13
+ case "header":
14
+ return `header:${target.relationshipId}:${target.variant}:${target.sectionIndex ?? "*"}`;
15
+ case "footer":
16
+ return `footer:${target.relationshipId}:${target.variant}:${target.sectionIndex ?? "*"}`;
17
+ case "footnote":
18
+ return `footnote:${target.noteId}`;
19
+ case "endnote":
20
+ return `endnote:${target.noteId}`;
21
+ }
22
+ }
23
+
24
+ export function getStoryBlocks(
25
+ document: CanonicalDocumentEnvelope,
26
+ target: EditorStoryTarget,
27
+ ): readonly BlockNode[] {
28
+ if (target.kind === "main") {
29
+ return document.content.children;
30
+ }
31
+
32
+ const subParts = document.subParts;
33
+ if (!subParts) {
34
+ return [];
35
+ }
36
+
37
+ switch (target.kind) {
38
+ case "header": {
39
+ const resolvedTarget = normalizeHeaderFooterTarget(document, target);
40
+ return (
41
+ resolvedTarget &&
42
+ findHeaderFooterDocumentEntry(document, resolvedTarget)?.blocks
43
+ ) ?? [];
44
+ }
45
+ case "footer": {
46
+ const resolvedTarget = normalizeHeaderFooterTarget(document, target);
47
+ return (
48
+ resolvedTarget &&
49
+ findHeaderFooterDocumentEntry(document, resolvedTarget)?.blocks
50
+ ) ?? [];
51
+ }
52
+ case "footnote":
53
+ return subParts.footnoteCollection?.footnotes[target.noteId]?.blocks ?? [];
54
+ case "endnote":
55
+ return subParts.footnoteCollection?.endnotes[target.noteId]?.blocks ?? [];
56
+ }
57
+ }
58
+
59
+ export function replaceStoryBlocks(
60
+ document: CanonicalDocumentEnvelope,
61
+ target: EditorStoryTarget,
62
+ blocks: readonly BlockNode[],
63
+ ): CanonicalDocumentEnvelope {
64
+ if (target.kind === "main") {
65
+ return {
66
+ ...document,
67
+ content: {
68
+ ...document.content,
69
+ children: [...blocks],
70
+ },
71
+ };
72
+ }
73
+
74
+ if (!document.subParts) {
75
+ return document;
76
+ }
77
+
78
+ switch (target.kind) {
79
+ case "header": {
80
+ const resolvedTarget = normalizeHeaderFooterTarget(document, target);
81
+ const matchedEntry = resolvedTarget
82
+ ? findHeaderFooterDocumentEntry(document, resolvedTarget)
83
+ : undefined;
84
+ if (!matchedEntry) {
85
+ return document;
86
+ }
87
+ return {
88
+ ...document,
89
+ subParts: {
90
+ ...document.subParts,
91
+ headers: document.subParts.headers.map((header) =>
92
+ header.relationshipId === matchedEntry.relationshipId &&
93
+ header.variant === matchedEntry.variant &&
94
+ header.partPath === matchedEntry.partPath
95
+ ? { ...header, blocks: [...blocks] }
96
+ : header,
97
+ ),
98
+ },
99
+ };
100
+ }
101
+ case "footer": {
102
+ const resolvedTarget = normalizeHeaderFooterTarget(document, target);
103
+ const matchedEntry = resolvedTarget
104
+ ? findHeaderFooterDocumentEntry(document, resolvedTarget)
105
+ : undefined;
106
+ if (!matchedEntry) {
107
+ return document;
108
+ }
109
+ return {
110
+ ...document,
111
+ subParts: {
112
+ ...document.subParts,
113
+ footers: document.subParts.footers.map((footer) =>
114
+ footer.relationshipId === matchedEntry.relationshipId &&
115
+ footer.variant === matchedEntry.variant &&
116
+ footer.partPath === matchedEntry.partPath
117
+ ? { ...footer, blocks: [...blocks] }
118
+ : footer,
119
+ ),
120
+ },
121
+ };
122
+ }
123
+ case "footnote":
124
+ return !document.subParts.footnoteCollection?.footnotes[target.noteId]
125
+ ? document
126
+ : {
127
+ ...document,
128
+ subParts: {
129
+ ...document.subParts,
130
+ footnoteCollection: {
131
+ ...document.subParts.footnoteCollection,
132
+ footnotes: {
133
+ ...document.subParts.footnoteCollection.footnotes,
134
+ [target.noteId]: {
135
+ ...document.subParts.footnoteCollection.footnotes[target.noteId],
136
+ blocks: [...blocks],
137
+ },
138
+ },
139
+ },
140
+ },
141
+ };
142
+ case "endnote":
143
+ return !document.subParts.footnoteCollection?.endnotes[target.noteId]
144
+ ? document
145
+ : {
146
+ ...document,
147
+ subParts: {
148
+ ...document.subParts,
149
+ footnoteCollection: {
150
+ ...document.subParts.footnoteCollection,
151
+ endnotes: {
152
+ ...document.subParts.footnoteCollection.endnotes,
153
+ [target.noteId]: {
154
+ ...document.subParts.footnoteCollection.endnotes[target.noteId],
155
+ blocks: [...blocks],
156
+ },
157
+ },
158
+ },
159
+ },
160
+ };
161
+ }
162
+ }