@beyondwork/docx-react-component 1.0.19 → 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 (70) hide show
  1. package/package.json +1 -1
  2. package/src/api/public-types.ts +336 -0
  3. package/src/api/session-state.ts +2 -0
  4. package/src/core/commands/formatting-commands.ts +1 -1
  5. package/src/core/commands/index.ts +14 -2
  6. package/src/core/search/search-text.ts +28 -0
  7. package/src/core/state/editor-state.ts +3 -0
  8. package/src/index.ts +21 -0
  9. package/src/io/docx-session.ts +363 -17
  10. package/src/io/export/serialize-comments.ts +104 -34
  11. package/src/io/export/serialize-footnotes.ts +198 -1
  12. package/src/io/export/serialize-headers-footers.ts +203 -10
  13. package/src/io/export/serialize-main-document.ts +83 -3
  14. package/src/io/export/split-review-boundaries.ts +181 -19
  15. package/src/io/normalize/normalize-text.ts +82 -8
  16. package/src/io/ooxml/highlight-colors.ts +39 -0
  17. package/src/io/ooxml/parse-comments.ts +85 -19
  18. package/src/io/ooxml/parse-fields.ts +396 -0
  19. package/src/io/ooxml/parse-footnotes.ts +240 -2
  20. package/src/io/ooxml/parse-headers-footers.ts +431 -7
  21. package/src/io/ooxml/parse-inline-media.ts +15 -1
  22. package/src/io/ooxml/parse-main-document.ts +396 -14
  23. package/src/io/ooxml/parse-revisions.ts +317 -38
  24. package/src/legal/bookmarks.ts +44 -0
  25. package/src/legal/cross-references.ts +59 -1
  26. package/src/model/canonical-document.ts +117 -1
  27. package/src/model/snapshot.ts +85 -1
  28. package/src/review/store/revision-store.ts +6 -0
  29. package/src/review/store/revision-types.ts +1 -0
  30. package/src/runtime/document-navigation.ts +52 -13
  31. package/src/runtime/document-runtime.ts +1521 -75
  32. package/src/runtime/read-only-diagnostics-runtime.ts +8 -0
  33. package/src/runtime/session-capabilities.ts +33 -3
  34. package/src/runtime/surface-projection.ts +86 -25
  35. package/src/runtime/table-schema.ts +2 -2
  36. package/src/runtime/view-state.ts +24 -6
  37. package/src/runtime/workflow-markup.ts +349 -0
  38. package/src/ui/WordReviewEditor.tsx +850 -1315
  39. package/src/ui/editor-command-bag.ts +120 -0
  40. package/src/ui/editor-runtime-boundary.ts +1422 -0
  41. package/src/ui/editor-shell-view.tsx +134 -0
  42. package/src/ui/editor-surface-controller.tsx +51 -0
  43. package/src/ui/headless/revision-decoration-model.ts +4 -4
  44. package/src/ui/runtime-snapshot-selectors.ts +197 -0
  45. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +18 -2
  46. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +129 -0
  47. package/src/ui-tailwind/chrome/tw-layout-panel.tsx +114 -0
  48. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +34 -0
  49. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +27 -2
  50. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +128 -0
  51. package/src/ui-tailwind/editor-surface/perf-probe.ts +86 -14
  52. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +2 -2
  53. package/src/ui-tailwind/editor-surface/pm-decorations.ts +35 -0
  54. package/src/ui-tailwind/editor-surface/pm-position-map.ts +1 -1
  55. package/src/ui-tailwind/editor-surface/pm-schema.ts +139 -8
  56. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +98 -48
  57. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +51 -0
  58. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +7 -1
  59. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +174 -48
  60. package/src/ui-tailwind/page-chrome-model.ts +27 -0
  61. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +7 -7
  62. package/src/ui-tailwind/review/tw-health-panel.tsx +31 -2
  63. package/src/ui-tailwind/review/tw-review-rail.tsx +3 -3
  64. package/src/ui-tailwind/review/tw-revision-sidebar.tsx +15 -15
  65. package/src/ui-tailwind/theme/editor-theme.css +4 -0
  66. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +543 -5
  67. package/src/ui-tailwind/tw-review-workspace.tsx +316 -19
  68. package/src/validation/compatibility-engine.ts +27 -4
  69. package/src/validation/compatibility-report.ts +1 -0
  70. package/src/validation/docx-comment-proof.ts +220 -0
@@ -1,6 +1,8 @@
1
1
  import type {
2
2
  CompatibilityReport,
3
3
  DocumentNavigationSnapshot,
4
+ FieldSnapshot,
5
+ ProtectionSnapshot,
4
6
  RuntimeRenderSnapshot,
5
7
  } from "../api/public-types.ts";
6
8
  import { parseCommentsFromOoxml } from "../io/ooxml/parse-comments.ts";
@@ -19,6 +21,32 @@ export interface ClosureValidationContext {
19
21
  navigation?: Pick<DocumentNavigationSnapshot, "pages">;
20
22
  compatibility: Pick<CompatibilityReport, "errors" | "featureEntries">;
21
23
  surface: RuntimeRenderSnapshot["surface"];
24
+ fieldSnapshot?: FieldSnapshot;
25
+ protectionSnapshot?: ProtectionSnapshot;
26
+ harnessShowcase?: HarnessShowcaseValidationContext;
27
+ }
28
+
29
+ export type HarnessShowcaseArea =
30
+ | "workflow-overlay"
31
+ | "blocked-commands"
32
+ | "preserve-only"
33
+ | "search-candidates"
34
+ | "right-rail"
35
+ | "multi-scope"
36
+ | "public-api"
37
+ | "sidebar-integration"
38
+ | "policy-yaml"
39
+ | "harness-lifecycle"
40
+ | "export-continuity";
41
+
42
+ export interface HarnessShowcaseAreaProof {
43
+ demonstrated: boolean;
44
+ detail?: string;
45
+ proofArtifacts?: string[];
46
+ }
47
+
48
+ export interface HarnessShowcaseValidationContext {
49
+ areas: Partial<Record<HarnessShowcaseArea, HarnessShowcaseAreaProof>>;
22
50
  }
23
51
 
24
52
  export type ClosureValidationCheck =
@@ -31,6 +59,31 @@ export type ClosureValidationCheck =
31
59
  | { type: "surfaceBlockKind"; kind: string; count?: number }
32
60
  | { type: "opaqueBlockLabelPrefix"; value: string }
33
61
  | { type: "opaqueInlineLabelPrefix"; value: string }
62
+ | {
63
+ type: "trackedChangeMatch";
64
+ kind?: "insertion" | "deletion" | "formatting" | "move" | "property-change";
65
+ actionability?: "actionable" | "preserve-only";
66
+ status?: "active" | "accepted" | "rejected" | "detached";
67
+ preserveOnlyReasonIncludes?: string[];
68
+ }
69
+ | {
70
+ type: "fieldEntryMatch";
71
+ fieldFamily?: string;
72
+ supported?: boolean;
73
+ refreshStatus?: string;
74
+ displayTextIncludes?: string[];
75
+ fieldTarget?: string;
76
+ }
77
+ | {
78
+ type: "protectionMatch";
79
+ hasDocumentProtection?: boolean;
80
+ editType?: string;
81
+ enforcementActive?: boolean;
82
+ enforcedRangeCountAtLeast?: number;
83
+ preservedRangeCountAtLeast?: number;
84
+ rangeId?: string;
85
+ rangeEnforced?: boolean;
86
+ }
34
87
  | {
35
88
  type: "secondaryStoryMatch";
36
89
  kind: "header" | "footer" | "footnote" | "endnote";
@@ -59,6 +112,11 @@ export type ClosureValidationCheck =
59
112
  entryBodies?: string[];
60
113
  entryAuthorIds?: string[];
61
114
  authorId?: string;
115
+ }
116
+ | {
117
+ type: "harnessShowcase";
118
+ area: HarnessShowcaseArea;
119
+ claim: string;
62
120
  };
63
121
 
64
122
  export interface ClosureValidationResult {
@@ -87,6 +145,28 @@ export interface DocxCommentProof {
87
145
  }>;
88
146
  }
89
147
 
148
+ export function evaluateHarnessShowcaseCheck(
149
+ check: Extract<ClosureValidationCheck, { type: "harnessShowcase" }>,
150
+ context: HarnessShowcaseValidationContext,
151
+ ): ClosureValidationResult {
152
+ const areaProof = context.areas[check.area];
153
+ if (!areaProof) {
154
+ return {
155
+ type: check.type,
156
+ passed: false,
157
+ reason: `missing harness showcase proof for area ${check.area}: ${check.claim}`,
158
+ };
159
+ }
160
+
161
+ return {
162
+ type: check.type,
163
+ passed: areaProof.demonstrated,
164
+ reason:
165
+ areaProof.detail ??
166
+ `expected harness showcase proof for area ${check.area}: ${check.claim}`,
167
+ };
168
+ }
169
+
90
170
  export function evaluateClosureCheck(
91
171
  check: ClosureValidationCheck,
92
172
  context: ClosureValidationContext,
@@ -163,6 +243,87 @@ export function evaluateClosureCheck(
163
243
  passed: hasOpaqueInlineLabelPrefix(context.surface, check.value),
164
244
  reason: `expected an opaque inline label starting with ${check.value}`,
165
245
  };
246
+ case "trackedChangeMatch": {
247
+ const passed = context.renderSnapshot.trackedChanges.revisions.some((revision) => {
248
+ if (check.kind && revision.kind !== check.kind) {
249
+ return false;
250
+ }
251
+ if (check.actionability && revision.actionability !== check.actionability) {
252
+ return false;
253
+ }
254
+ if (check.status && revision.status !== check.status) {
255
+ return false;
256
+ }
257
+ if (
258
+ check.preserveOnlyReasonIncludes &&
259
+ !check.preserveOnlyReasonIncludes.every((fragment) =>
260
+ (revision.preserveOnlyReason ?? "").includes(fragment),
261
+ )
262
+ ) {
263
+ return false;
264
+ }
265
+ return true;
266
+ });
267
+ return {
268
+ type: check.type,
269
+ passed,
270
+ reason: `expected a tracked change matching ${describeTrackedChangeMatch(check)}`,
271
+ };
272
+ }
273
+ case "fieldEntryMatch": {
274
+ const passed =
275
+ context.fieldSnapshot?.fields.some((field) => {
276
+ if (check.fieldFamily && field.fieldFamily !== check.fieldFamily) {
277
+ return false;
278
+ }
279
+ if (check.supported !== undefined && field.supported !== check.supported) {
280
+ return false;
281
+ }
282
+ if (check.refreshStatus && field.refreshStatus !== check.refreshStatus) {
283
+ return false;
284
+ }
285
+ if (check.fieldTarget && field.fieldTarget !== check.fieldTarget) {
286
+ return false;
287
+ }
288
+ if (
289
+ check.displayTextIncludes &&
290
+ !check.displayTextIncludes.every((fragment) => field.displayText.includes(fragment))
291
+ ) {
292
+ return false;
293
+ }
294
+ return true;
295
+ }) ?? false;
296
+ return {
297
+ type: check.type,
298
+ passed,
299
+ reason: `expected a field entry matching ${describeFieldEntryMatch(check)}`,
300
+ };
301
+ }
302
+ case "protectionMatch": {
303
+ const snapshot = context.protectionSnapshot;
304
+ const passed = Boolean(
305
+ snapshot &&
306
+ (check.hasDocumentProtection === undefined ||
307
+ snapshot.hasDocumentProtection === check.hasDocumentProtection) &&
308
+ (check.editType === undefined || snapshot.editType === check.editType) &&
309
+ (check.enforcementActive === undefined ||
310
+ snapshot.enforcementActive === check.enforcementActive) &&
311
+ (check.enforcedRangeCountAtLeast === undefined ||
312
+ snapshot.enforcedRangeCount >= check.enforcedRangeCountAtLeast) &&
313
+ (check.preservedRangeCountAtLeast === undefined ||
314
+ snapshot.preservedRangeCount >= check.preservedRangeCountAtLeast) &&
315
+ (!check.rangeId ||
316
+ snapshot.ranges.some((range) =>
317
+ range.rangeId === check.rangeId &&
318
+ (check.rangeEnforced === undefined || range.enforced === check.rangeEnforced),
319
+ ))
320
+ );
321
+ return {
322
+ type: check.type,
323
+ passed,
324
+ reason: `expected protection snapshot matching ${describeProtectionMatch(check)}`,
325
+ };
326
+ }
166
327
  case "secondaryStoryMatch": {
167
328
  const passed =
168
329
  context.surface?.secondaryStories.some((story) => {
@@ -229,6 +390,8 @@ export function evaluateClosureCheck(
229
390
  reason: `expected a comment thread matching ${describeCommentThreadMatch(check)}`,
230
391
  };
231
392
  }
393
+ case "harnessShowcase":
394
+ return evaluateHarnessShowcaseCheck(check, context.harnessShowcase ?? { areas: {} });
232
395
  default:
233
396
  return {
234
397
  type: (check as { type: string }).type,
@@ -364,6 +527,63 @@ function matchesCommentThread(
364
527
  return true;
365
528
  }
366
529
 
530
+ function describeTrackedChangeMatch(
531
+ check: Extract<ClosureValidationCheck, { type: "trackedChangeMatch" }>,
532
+ ): string {
533
+ return [
534
+ check.kind ? `kind=${check.kind}` : undefined,
535
+ check.actionability ? `actionability=${check.actionability}` : undefined,
536
+ check.status ? `status=${check.status}` : undefined,
537
+ check.preserveOnlyReasonIncludes?.length
538
+ ? `preserveOnlyReasonIncludes=${check.preserveOnlyReasonIncludes.join(",")}`
539
+ : undefined,
540
+ ]
541
+ .filter(Boolean)
542
+ .join(" ");
543
+ }
544
+
545
+ function describeFieldEntryMatch(
546
+ check: Extract<ClosureValidationCheck, { type: "fieldEntryMatch" }>,
547
+ ): string {
548
+ return [
549
+ check.fieldFamily ? `fieldFamily=${check.fieldFamily}` : undefined,
550
+ check.supported !== undefined ? `supported=${String(check.supported)}` : undefined,
551
+ check.refreshStatus ? `refreshStatus=${check.refreshStatus}` : undefined,
552
+ check.fieldTarget ? `fieldTarget=${check.fieldTarget}` : undefined,
553
+ check.displayTextIncludes?.length
554
+ ? `displayTextIncludes=${check.displayTextIncludes.join(",")}`
555
+ : undefined,
556
+ ]
557
+ .filter(Boolean)
558
+ .join(" ");
559
+ }
560
+
561
+ function describeProtectionMatch(
562
+ check: Extract<ClosureValidationCheck, { type: "protectionMatch" }>,
563
+ ): string {
564
+ return [
565
+ check.hasDocumentProtection !== undefined
566
+ ? `hasDocumentProtection=${String(check.hasDocumentProtection)}`
567
+ : undefined,
568
+ check.editType ? `editType=${check.editType}` : undefined,
569
+ check.enforcementActive !== undefined
570
+ ? `enforcementActive=${String(check.enforcementActive)}`
571
+ : undefined,
572
+ check.enforcedRangeCountAtLeast !== undefined
573
+ ? `enforcedRangeCountAtLeast=${String(check.enforcedRangeCountAtLeast)}`
574
+ : undefined,
575
+ check.preservedRangeCountAtLeast !== undefined
576
+ ? `preservedRangeCountAtLeast=${String(check.preservedRangeCountAtLeast)}`
577
+ : undefined,
578
+ check.rangeId ? `rangeId=${check.rangeId}` : undefined,
579
+ check.rangeEnforced !== undefined
580
+ ? `rangeEnforced=${String(check.rangeEnforced)}`
581
+ : undefined,
582
+ ]
583
+ .filter(Boolean)
584
+ .join(" ");
585
+ }
586
+
367
587
  function getLayoutsForCheck(
368
588
  context: ClosureValidationContext,
369
589
  check: Extract<ClosureValidationCheck, { type: "pageLayoutMatch" }>,