@beyondwork/docx-react-component 1.0.11 → 1.0.13

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 (40) hide show
  1. package/README.md +8 -2
  2. package/package.json +35 -21
  3. package/src/api/public-types.ts +103 -1
  4. package/src/core/commands/formatting-commands.ts +742 -0
  5. package/src/core/commands/image-commands.ts +84 -2
  6. package/src/core/commands/structural-helpers.ts +309 -0
  7. package/src/core/commands/table-structure-commands.ts +721 -0
  8. package/src/core/commands/text-commands.ts +166 -1
  9. package/src/core/state/editor-state.ts +318 -9
  10. package/src/formats/xlsx/io/parse-sheet.ts +177 -7
  11. package/src/formats/xlsx/io/parse-styles.ts +2 -0
  12. package/src/formats/xlsx/io/xlsx-session.ts +18 -12
  13. package/src/formats/xlsx/model/sheet.ts +81 -1
  14. package/src/formats/xlsx/model/workbook.ts +10 -6
  15. package/src/io/docx-session.ts +392 -22
  16. package/src/io/export/export-session.ts +55 -0
  17. package/src/io/export/serialize-footnotes.ts +5 -20
  18. package/src/io/export/serialize-headers-footers.ts +5 -31
  19. package/src/io/export/serialize-main-document.ts +78 -5
  20. package/src/io/normalize/normalize-text.ts +90 -1
  21. package/src/io/ooxml/parse-footnotes.ts +68 -5
  22. package/src/io/ooxml/parse-headers-footers.ts +67 -9
  23. package/src/io/ooxml/parse-main-document.ts +169 -6
  24. package/src/io/opc/package-reader.ts +3 -3
  25. package/src/io/source-package-provenance.ts +241 -0
  26. package/src/model/canonical-document.ts +450 -2
  27. package/src/model/cds-1.0.0.ts +5 -2
  28. package/src/model/snapshot.ts +190 -19
  29. package/src/preservation/package-preservation.ts +0 -7
  30. package/src/runtime/document-runtime.ts +7 -1
  31. package/src/runtime/read-only-diagnostics-runtime.ts +1 -1
  32. package/src/runtime/surface-projection.ts +200 -17
  33. package/src/runtime/table-commands.ts +79 -0
  34. package/src/runtime/table-schema.ts +9 -0
  35. package/src/ui/WordReviewEditor.tsx +708 -16
  36. package/src/ui-tailwind/editor-surface/pm-schema.ts +121 -5
  37. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +73 -7
  38. package/src/ui-tailwind/editor-surface/search-plugin.ts +76 -16
  39. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +162 -14
  40. package/src/validation/compatibility-engine.ts +208 -0
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/bwllaming/React-OOXML-Office/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/bwllaming/React-OOXML-Office/actions/workflows/ci.yml)
4
4
 
5
- `@beyondwork/docx-react-component` is the shipped product in this repository: `WordReviewEditor`, a fidelity-first React editor for legal-review-safe `docx` workflows. Wave 21 lands the package-facing docs, npm metadata, publish workflow, and `pnpm pack` proof for this surface.
5
+ `@beyondwork/docx-react-component` is the shipped product in this repository: `WordReviewEditor`, a fidelity-first React editor for legal-review-safe `docx` workflows. Waves 20 through 23 land live table editing, package distribution, validator-backed CI, and legal workflow helpers. Wave 24 is the next production-hardening packet.
6
6
 
7
7
  The broader repository is still evolving toward a layered `react-ooxml-office` platform, but the source reality is unchanged:
8
8
 
@@ -43,6 +43,11 @@ import { WordReviewEditor } from "@beyondwork/docx-react-component";
43
43
  - runtime owns the live working session
44
44
  - host receives events, warnings, errors, snapshots, and exported artifacts
45
45
 
46
+ Snapshot/export note:
47
+
48
+ - when a session starts from a real `.docx`, persisted snapshots now carry embedded source-package provenance so later snapshot-origin `.docx` export can use the same package-backed exporter
49
+ - legacy snapshots without that provenance still load, but `.docx` export is intentionally blocked rather than falling back to a lossy minimal package
50
+
46
51
  The current public ESM exports are:
47
52
 
48
53
  - `@beyondwork/docx-react-component` -> `WordReviewEditor`
@@ -87,7 +92,7 @@ Start here:
87
92
  Current shipped docx contracts:
88
93
 
89
94
  - `docs/reference/public-api.md`
90
- This doc separates the shipped Wave 21 surface from the future Waves 25 through 27 ref expansion.
95
+ This doc separates the shipped Waves 20-23 surface from the future Waves 25 through 27 ref expansion.
91
96
  - `docs/reference/ooxml-compliance.md`
92
97
  - `docs/reference/word-review-editor-frontend-architecture.md`
93
98
  - `docs/reference/word-review-editor-ux-guide.md`
@@ -117,6 +122,7 @@ Shared platform and planned xlsx docs:
117
122
  - do not silently drop unknown OOXML
118
123
  - keep docs honest about shipped versus planned behavior
119
124
  - add or extend fixtures for compatibility-critical changes
125
+ - `bash scripts/validate-fixtures.sh` now uses the Railway validator service and requires `OPENXML_VALIDATOR_AUTH_TOKEN` when hitting the public domain
120
126
 
121
127
  ## Guiding Principle
122
128
 
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@beyondwork/docx-react-component",
3
3
  "publisher": "beyondwork",
4
- "version": "1.0.11",
4
+ "version": "1.0.13",
5
5
  "description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
6
+ "packageManager": "pnpm@10.30.3",
6
7
  "type": "module",
7
8
  "sideEffects": [
8
9
  "**/*.css"
@@ -45,6 +46,29 @@
45
46
  "./package.json": "./package.json"
46
47
  },
47
48
  "types": "./src/index.ts",
49
+ "scripts": {
50
+ "build": "tsup",
51
+ "test": "bash scripts/run-workspace-tests.sh",
52
+ "test:repo": "node scripts/run-repo-tests.mjs core",
53
+ "test:repo:all": "node scripts/run-repo-tests.mjs all",
54
+ "test:repo:optional": "node scripts/run-repo-tests.mjs optional",
55
+ "test:wcag-audit": "node scripts/run-repo-tests.mjs wcag-audit",
56
+ "test:harness": "pnpm --filter @docx-react-component/react-word-editor-harness test",
57
+ "lint": "pnpm run lint:no-authored-js && pnpm run lint:tsgo && pnpm run lint:tsgo:harness",
58
+ "lint:no-authored-js": "bash scripts/check-no-authored-js.sh",
59
+ "lint:tsgo": "tsgo --noEmit -p tsconfig.build.json",
60
+ "lint:tsgo:harness": "pnpm --filter @docx-react-component/react-word-editor-harness lint:tsgo",
61
+ "context7:api-check": "bash scripts/context7-export-env.sh run bash scripts/context7-api-check.sh",
62
+ "wave:doctor": "bash scripts/context7-export-env.sh run pnpm exec wave doctor --json",
63
+ "wave:dry-run": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main --dry-run --no-dashboard",
64
+ "wave:launch": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main",
65
+ "wave:launch:managed": "bash scripts/wave-launch.sh",
66
+ "wave:status": "bash scripts/wave-status.sh",
67
+ "wave:watch": "bash scripts/wave-watch.sh --follow",
68
+ "wave:dashboard:current": "bash scripts/wave-dashboard-attach.sh current",
69
+ "wave:dashboard:global": "bash scripts/wave-dashboard-attach.sh global",
70
+ "harness:dev": "pnpm --filter @docx-react-component/react-word-editor-harness dev"
71
+ },
48
72
  "keywords": [
49
73
  "docx",
50
74
  "word",
@@ -90,7 +114,7 @@
90
114
  "tailwindcss": "^4.2.2"
91
115
  },
92
116
  "devDependencies": {
93
- "@chllming/wave-orchestration": "^0.9.12",
117
+ "@chllming/wave-orchestration": "^0.9.13",
94
118
  "@types/react": "19.2.14",
95
119
  "@types/react-dom": "19.2.3",
96
120
  "@typescript/native-preview": "7.0.0-dev.20260409.1",
@@ -107,24 +131,14 @@
107
131
  "tsup": "^8.3.0",
108
132
  "tsx": "^4.21.0"
109
133
  },
110
- "scripts": {
111
- "build": "tsup",
112
- "test": "bash scripts/run-workspace-tests.sh",
113
- "test:repo": "pnpm exec tsx --test $(find test -type f \\( -name '*.test.ts' -o -name '*.test.tsx' \\) | sort)",
114
- "test:harness": "pnpm --filter @docx-react-component/react-word-editor-harness test",
115
- "lint": "pnpm run lint:no-authored-js && pnpm run lint:tsgo && pnpm run lint:tsgo:harness",
116
- "lint:no-authored-js": "bash scripts/check-no-authored-js.sh",
117
- "lint:tsgo": "tsgo --noEmit -p tsconfig.build.json",
118
- "lint:tsgo:harness": "pnpm --filter @docx-react-component/react-word-editor-harness lint:tsgo",
119
- "context7:api-check": "bash scripts/context7-export-env.sh run bash scripts/context7-api-check.sh",
120
- "wave:doctor": "bash scripts/context7-export-env.sh run pnpm exec wave doctor --json",
121
- "wave:dry-run": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main --dry-run --no-dashboard",
122
- "wave:launch": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main",
123
- "wave:launch:managed": "bash scripts/wave-launch.sh",
124
- "wave:status": "bash scripts/wave-status.sh",
125
- "wave:watch": "bash scripts/wave-watch.sh --follow",
126
- "wave:dashboard:current": "bash scripts/wave-dashboard-attach.sh current",
127
- "wave:dashboard:global": "bash scripts/wave-dashboard-attach.sh global",
128
- "harness:dev": "pnpm --filter @docx-react-component/react-word-editor-harness dev"
134
+ "pnpm": {
135
+ "onlyBuiltDependencies": [
136
+ "esbuild",
137
+ "sharp"
138
+ ],
139
+ "overrides": {
140
+ "react": "19.2.4",
141
+ "react-dom": "19.2.4"
142
+ }
129
143
  }
130
144
  }
@@ -123,11 +123,62 @@ export interface TrackedChangesSnapshot {
123
123
  revisions: TrackedChangeEntrySnapshot[];
124
124
  }
125
125
 
126
+ export type FormattingAlignment = "left" | "center" | "right" | "justify";
127
+
128
+ export interface FormattingStateSnapshot {
129
+ bold: boolean;
130
+ italic: boolean;
131
+ underline: boolean;
132
+ strikethrough: boolean;
133
+ superscript: boolean;
134
+ subscript: boolean;
135
+ fontFamily?: string;
136
+ fontSize?: number;
137
+ textColor?: string;
138
+ highlightColor?: string | null;
139
+ alignment?: FormattingAlignment;
140
+ }
141
+
142
+ export interface SearchOptions {
143
+ matchCase?: boolean;
144
+ wholeWord?: boolean;
145
+ limit?: number;
146
+ }
147
+
148
+ export interface SearchResultSnapshot {
149
+ resultId: string;
150
+ anchor: EditorAnchorProjection;
151
+ excerpt: string;
152
+ isActive: boolean;
153
+ }
154
+
155
+ export interface InsertTableOptions {
156
+ rows: number;
157
+ columns: number;
158
+ }
159
+
160
+ export interface InsertImageOptions {
161
+ data: Uint8Array;
162
+ mimeType: string;
163
+ width?: number;
164
+ height?: number;
165
+ altText?: string;
166
+ }
167
+
126
168
  export type SurfaceTextMark =
127
169
  | "bold"
128
170
  | "italic"
129
171
  | "underline"
130
- | "strikethrough";
172
+ | "strikethrough"
173
+ | "doubleStrikethrough"
174
+ | "superscript"
175
+ | "subscript"
176
+ | "vanish"
177
+ | "emboss"
178
+ | "imprint"
179
+ | "shadow"
180
+ | "smallCaps"
181
+ | "allCaps";
131
182
 
132
183
  export type SurfaceInlineSegment =
133
184
  | {
@@ -137,6 +188,15 @@ export type SurfaceInlineSegment =
137
188
  to: number;
138
189
  text: string;
139
190
  marks?: SurfaceTextMark[];
191
+ markAttrs?: {
192
+ backgroundColor?: string;
193
+ charSpacing?: number;
194
+ kerning?: number;
195
+ textFill?: string;
196
+ fontFamily?: string;
197
+ fontSize?: number;
198
+ textColor?: string;
199
+ };
140
200
  hyperlinkHref?: string;
141
201
  }
142
202
  | {
@@ -174,6 +234,7 @@ export interface SurfaceTableCellSnapshot {
174
234
  verticalMerge: "restart" | "continue" | null;
175
235
  colspan: number;
176
236
  rowspan: number;
237
+ backgroundColor?: string | null;
177
238
  content: SurfaceBlockSnapshot[];
178
239
  }
179
240
 
@@ -192,6 +253,17 @@ export type SurfaceBlockSnapshot =
192
253
  numberingInstanceId: string;
193
254
  level: number;
194
255
  };
256
+ alignment?: "left" | "center" | "right" | "both" | "distribute";
257
+ spacing?: { before?: number; after?: number; line?: number; lineRule?: string };
258
+ indentation?: { left?: number; right?: number; firstLine?: number; hanging?: number };
259
+ borders?: { top?: unknown; left?: unknown; bottom?: unknown; right?: unknown; bar?: unknown; between?: unknown };
260
+ shading?: { fill?: string; color?: string; val?: string };
261
+ tabStops?: Array<{ pos: number; val?: string; leader?: string }>;
262
+ keepNext?: boolean;
263
+ keepLines?: boolean;
264
+ pageBreakBefore?: boolean;
265
+ outlineLevel?: number;
266
+ bidi?: boolean;
195
267
  segments: SurfaceInlineSegment[];
196
268
  }
197
269
  | {
@@ -346,6 +418,7 @@ export interface PersistedEditorSnapshot {
346
418
  canonicalDocument: RuntimePersistedEditorSnapshot["canonicalDocument"];
347
419
  compatibility: CompatibilityReport;
348
420
  warningLog: EditorWarning[];
421
+ sourcePackage?: RuntimePersistedEditorSnapshot["sourcePackage"];
349
422
  }
350
423
 
351
424
  export interface AddCommentParams {
@@ -508,6 +581,35 @@ export interface WordReviewEditorRef {
508
581
  getWarnings(): EditorWarning[];
509
582
  getComments(): CommentSidebarSnapshot;
510
583
  getTrackedChanges(): TrackedChangesSnapshot;
584
+ getFormattingState(): FormattingStateSnapshot;
585
+ toggleBold(): void;
586
+ toggleItalic(): void;
587
+ toggleUnderline(): void;
588
+ toggleStrikethrough(): void;
589
+ toggleSuperscript(): void;
590
+ toggleSubscript(): void;
591
+ setFontFamily(fontFamily: string | null): void;
592
+ setFontSize(size: number | null): void;
593
+ setTextColor(color: string | null): void;
594
+ setHighlightColor(color: string | null): void;
595
+ setAlignment(alignment: FormattingAlignment): void;
596
+ indent(): void;
597
+ outdent(): void;
598
+ insertPageBreak(): void;
599
+ insertTable(options: InsertTableOptions): void;
600
+ insertImage(options: InsertImageOptions): void;
601
+ addRowBefore(): void;
602
+ addRowAfter(): void;
603
+ addColumnBefore(): void;
604
+ addColumnAfter(): void;
605
+ deleteRow(): void;
606
+ deleteColumn(): void;
607
+ deleteTable(): void;
608
+ mergeCells(): void;
609
+ splitCell(): void;
610
+ setCellBackground(color: string): void;
611
+ search(query: string, options?: SearchOptions): SearchResultSnapshot[];
612
+ clearSearch(): void;
511
613
  scrollToRevision(revisionId: string): void;
512
614
  scrollToComment(commentId: string): void;
513
615
  }