@beyondwork/docx-react-component 1.0.36 → 1.0.38

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 (107) hide show
  1. package/README.md +103 -13
  2. package/package.json +1 -1
  3. package/src/api/package-version.ts +13 -0
  4. package/src/api/public-types.ts +402 -1
  5. package/src/core/commands/index.ts +18 -1
  6. package/src/core/commands/section-layout-commands.ts +58 -0
  7. package/src/core/commands/table-grid.ts +431 -0
  8. package/src/core/commands/table-structure-commands.ts +815 -55
  9. package/src/core/selection/mapping.ts +6 -0
  10. package/src/io/docx-session.ts +24 -9
  11. package/src/io/export/build-app-properties-xml.ts +88 -0
  12. package/src/io/export/serialize-comments.ts +6 -1
  13. package/src/io/export/serialize-footnotes.ts +10 -9
  14. package/src/io/export/serialize-headers-footers.ts +11 -10
  15. package/src/io/export/serialize-main-document.ts +328 -50
  16. package/src/io/export/serialize-numbering.ts +114 -24
  17. package/src/io/export/serialize-tables.ts +87 -11
  18. package/src/io/export/table-properties-xml.ts +174 -20
  19. package/src/io/export/twip.ts +66 -0
  20. package/src/io/normalize/normalize-text.ts +20 -0
  21. package/src/io/ooxml/parse-footnotes.ts +62 -1
  22. package/src/io/ooxml/parse-headers-footers.ts +62 -1
  23. package/src/io/ooxml/parse-main-document.ts +158 -1
  24. package/src/io/ooxml/parse-tables.ts +249 -0
  25. package/src/legal/bookmarks.ts +78 -0
  26. package/src/model/canonical-document.ts +45 -0
  27. package/src/review/store/scope-tag-diff.ts +130 -0
  28. package/src/runtime/document-layout.ts +4 -2
  29. package/src/runtime/document-navigation.ts +2 -306
  30. package/src/runtime/document-runtime.ts +287 -11
  31. package/src/runtime/layout/default-page-format.ts +96 -0
  32. package/src/runtime/layout/docx-font-loader.ts +143 -0
  33. package/src/runtime/layout/index.ts +233 -0
  34. package/src/runtime/layout/inert-layout-facet.ts +59 -0
  35. package/src/runtime/layout/layout-engine-instance.ts +628 -0
  36. package/src/runtime/layout/layout-invalidation.ts +257 -0
  37. package/src/runtime/layout/layout-measurement-provider.ts +175 -0
  38. package/src/runtime/layout/margin-preset-catalog.ts +178 -0
  39. package/src/runtime/layout/measurement-backend-canvas.ts +307 -0
  40. package/src/runtime/layout/measurement-backend-empirical.ts +208 -0
  41. package/src/runtime/layout/page-format-catalog.ts +233 -0
  42. package/src/runtime/layout/page-fragment-mapper.ts +179 -0
  43. package/src/runtime/layout/page-graph.ts +452 -0
  44. package/src/runtime/layout/page-layout-snapshot-adapter.ts +70 -0
  45. package/src/runtime/layout/page-story-resolver.ts +195 -0
  46. package/src/runtime/layout/paginated-layout-engine.ts +921 -0
  47. package/src/runtime/layout/project-block-fragments.ts +91 -0
  48. package/src/runtime/layout/public-facet.ts +1398 -0
  49. package/src/runtime/layout/resolved-formatting-document.ts +317 -0
  50. package/src/runtime/layout/resolved-formatting-state.ts +430 -0
  51. package/src/runtime/layout/table-render-plan.ts +229 -0
  52. package/src/runtime/render/block-fragment-projection.ts +35 -0
  53. package/src/runtime/render/decoration-resolver.ts +189 -0
  54. package/src/runtime/render/index.ts +57 -0
  55. package/src/runtime/render/pending-op-delta-reader.ts +129 -0
  56. package/src/runtime/render/render-frame-types.ts +317 -0
  57. package/src/runtime/render/render-kernel.ts +755 -0
  58. package/src/runtime/scope-tag-registry.ts +95 -0
  59. package/src/runtime/surface-projection.ts +1 -0
  60. package/src/runtime/text-ack-range.ts +49 -0
  61. package/src/runtime/view-state.ts +67 -0
  62. package/src/runtime/workflow-markup.ts +1 -5
  63. package/src/runtime/workflow-rail-segments.ts +280 -0
  64. package/src/ui/WordReviewEditor.tsx +99 -15
  65. package/src/ui/editor-runtime-boundary.ts +10 -1
  66. package/src/ui/editor-shell-view.tsx +6 -0
  67. package/src/ui/editor-surface-controller.tsx +3 -0
  68. package/src/ui/headless/chrome-registry.ts +501 -0
  69. package/src/ui/headless/scoped-chrome-policy.ts +183 -0
  70. package/src/ui/headless/selection-tool-context.ts +2 -0
  71. package/src/ui/headless/selection-tool-resolver.ts +36 -17
  72. package/src/ui/headless/selection-tool-types.ts +10 -0
  73. package/src/ui-tailwind/chrome/chrome-preset-model.ts +23 -2
  74. package/src/ui-tailwind/chrome/role-action-sets.ts +74 -0
  75. package/src/ui-tailwind/chrome/tw-detach-handle.tsx +147 -0
  76. package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +163 -0
  77. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +57 -92
  78. package/src/ui-tailwind/chrome/tw-selection-tool-placement.ts +149 -0
  79. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +15 -4
  80. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +274 -138
  81. package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +90 -0
  82. package/src/ui-tailwind/chrome-overlay/index.ts +22 -0
  83. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +86 -0
  84. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +178 -0
  85. package/src/ui-tailwind/chrome-overlay/tw-workspace-view-switcher.tsx +95 -0
  86. package/src/ui-tailwind/editor-surface/fast-text-edit-lane.ts +337 -0
  87. package/src/ui-tailwind/editor-surface/local-edit-session-state.ts +100 -0
  88. package/src/ui-tailwind/editor-surface/perf-probe.ts +27 -1
  89. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +20 -2
  90. package/src/ui-tailwind/editor-surface/pm-decorations.ts +93 -23
  91. package/src/ui-tailwind/editor-surface/predicted-position-map.ts +78 -0
  92. package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +63 -0
  93. package/src/ui-tailwind/editor-surface/predicted-tx-gate.ts +39 -0
  94. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +176 -6
  95. package/src/ui-tailwind/index.ts +33 -0
  96. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +2 -2
  97. package/src/ui-tailwind/review/tw-rail-card.tsx +150 -0
  98. package/src/ui-tailwind/review/tw-review-rail-footer.tsx +52 -0
  99. package/src/ui-tailwind/review/tw-review-rail.tsx +166 -11
  100. package/src/ui-tailwind/review/tw-workflow-tab.tsx +108 -0
  101. package/src/ui-tailwind/theme/editor-theme.css +505 -144
  102. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +559 -0
  103. package/src/ui-tailwind/toolbar/tw-scope-posture-menu.tsx +182 -0
  104. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +162 -0
  105. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +2 -2
  106. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +304 -166
  107. package/src/ui-tailwind/tw-review-workspace.tsx +163 -2
@@ -48,6 +48,8 @@ import type {
48
48
  StyleCatalogSnapshot,
49
49
  SurfaceBlockSnapshot,
50
50
  SurfaceInlineSegment,
51
+ TableOp,
52
+ TableOpResult,
51
53
  TrackedChangeEntrySnapshot,
52
54
  TocRefreshResult,
53
55
  UpdateFieldsResult,
@@ -113,6 +115,7 @@ import {
113
115
  import {
114
116
  applyTableStructureOperation,
115
117
  getTableStructureContext,
118
+ type TableStructureOperation,
116
119
  } from "../core/commands/table-structure-commands.ts";
117
120
  import {
118
121
  deleteSelectionOrBackward,
@@ -165,6 +168,7 @@ import {
165
168
  useRuntimeValue,
166
169
  } from "./runtime-snapshot-selectors.ts";
167
170
  import type { MarkupDisplay } from "./headless/comment-decoration-model";
171
+ import { resolveScopedChromePolicy } from "./headless/scoped-chrome-policy";
168
172
  import type {
169
173
  SelectionToolbarAnchor,
170
174
  SelectionToolbarModel,
@@ -577,6 +581,8 @@ export function __createWordReviewEditorRefBridge(
577
581
  getRuntimeContextAnalytics: (query) => {
578
582
  return clonePublicValue(runtime.getRuntimeContextAnalytics(query));
579
583
  },
584
+ layout: runtime.layout,
585
+ tables: buildTablesFacet(runtime, mountedSurface ?? null),
580
586
  goToNextReviewItem: () => {
581
587
  return clonePublicValue(navigateReviewQueue(runtime, "next"));
582
588
  },
@@ -1391,6 +1397,8 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
1391
1397
  getRuntimeContextAnalytics: (query) => {
1392
1398
  return clonePublicValue(activeRuntime.getRuntimeContextAnalytics(query));
1393
1399
  },
1400
+ layout: activeRuntime.layout,
1401
+ tables: buildTablesFacet(activeRuntime, surfaceRef.current ?? null),
1394
1402
  goToNextReviewItem: () => {
1395
1403
  return clonePublicValue(navigateMountedReviewQueue("next"));
1396
1404
  },
@@ -1670,6 +1678,14 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
1670
1678
  snapshot.comments.activeCommentId
1671
1679
  ? snapshot.comments.threads.find((thread) => thread.commentId === snapshot.comments.activeCommentId)
1672
1680
  : undefined;
1681
+ const scopedChromePolicy = resolveScopedChromePolicy({
1682
+ preset: effectiveChromePreset,
1683
+ compactMode: false,
1684
+ capabilities,
1685
+ interactionGuardSnapshot,
1686
+ workflowScopeSnapshot,
1687
+ activeListContext: viewState.activeListContext,
1688
+ });
1673
1689
  const selectionToolbar = buildSelectionToolbarModel({
1674
1690
  snapshot,
1675
1691
  viewState,
@@ -1736,6 +1752,7 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
1736
1752
  preferListStructureContext: viewState.workspaceMode === "page",
1737
1753
  addCommentDisabledReason,
1738
1754
  suppressedSuggestionRevisionId,
1755
+ scopedChromePolicy,
1739
1756
  });
1740
1757
  const selectionToolbarSelectionKey = useMemo(
1741
1758
  () =>
@@ -2241,6 +2258,9 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
2241
2258
  workflowMetadata={workflowMarkupSnapshot?.metadata}
2242
2259
  onSelectionToolbarAnchorChange={handleSelectionToolbarAnchorChange}
2243
2260
  {...editorCallbacks}
2261
+ dispatchRuntimeCommand={(command) =>
2262
+ activeRuntime.applyActiveStoryTextCommand(command as never)
2263
+ }
2244
2264
  onCommentActivated={(commentId) => {
2245
2265
  activeRuntime.openComment(commentId);
2246
2266
  setActiveRailTab("comments");
@@ -2285,6 +2305,7 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
2285
2305
  activeRevisionId={activeRevisionId}
2286
2306
  showTrackedChanges={showTrackedChanges}
2287
2307
  workflowScopeSnapshot={workflowScopeSnapshot}
2308
+ layoutFacet={activeRuntime.layout}
2288
2309
  interactionGuardSnapshot={interactionGuardSnapshot}
2289
2310
  chromePreset={effectiveChromePreset}
2290
2311
  chromeOptions={chromeOptions}
@@ -3373,28 +3394,19 @@ function applyRuntimeImageReposition(
3373
3394
  function applyRuntimeTableStructureOperation(
3374
3395
  runtime: WordReviewEditorRuntime,
3375
3396
  mountedSurface: TwProseMirrorSurfaceRef | null | undefined,
3376
- operation:
3377
- | { type: "add-row-before" }
3378
- | { type: "add-row-after" }
3379
- | { type: "add-column-before" }
3380
- | { type: "add-column-after" }
3381
- | { type: "delete-row" }
3382
- | { type: "delete-column" }
3383
- | { type: "delete-table" }
3384
- | { type: "merge-cells" }
3385
- | { type: "split-cell" }
3386
- | { type: "set-cell-background"; color: string },
3387
- ): void {
3397
+ operation: TableStructureOperation,
3398
+ ): { changed: boolean; coercedReason: string | null } {
3388
3399
  if (isSelectionSuggesting(runtime)) {
3400
+ const coercedReason = `Table operation "${operation.type}" is not supported in suggesting mode.`;
3389
3401
  runtime.emitBlockedCommand(`table.${operation.type}`, [{
3390
3402
  code: "unsupported_surface",
3391
- message: `Table operation "${operation.type}" is not supported in suggesting mode.`,
3403
+ message: coercedReason,
3392
3404
  }]);
3393
- return;
3405
+ return { changed: false, coercedReason };
3394
3406
  }
3395
3407
  const context = getStoryMutationContext(runtime, `table.${operation.type}`);
3396
3408
  if (!context) {
3397
- return;
3409
+ return { changed: false, coercedReason: "No active mutation context." };
3398
3410
  }
3399
3411
 
3400
3412
  const result = applyTableStructureOperation(
@@ -3404,6 +3416,78 @@ function applyRuntimeTableStructureOperation(
3404
3416
  operation,
3405
3417
  );
3406
3418
  dispatchStoryMutationResult(runtime, context, result, context.timestamp);
3419
+ return {
3420
+ changed: result.changed,
3421
+ coercedReason: result.changed ? null : "Op was a no-op against the active selection.",
3422
+ };
3423
+ }
3424
+
3425
+ /**
3426
+ * Translate a public-API `TableOp` (kebab-case `kind` discriminator) to
3427
+ * the internal `TableStructureOperation` (`type` discriminator). The
3428
+ * shape values are identical aside from the discriminator name.
3429
+ */
3430
+ export function __publicTableOpToInternal(op: TableOp): TableStructureOperation {
3431
+ return publicTableOpToInternal(op);
3432
+ }
3433
+
3434
+ function publicTableOpToInternal(op: TableOp): TableStructureOperation {
3435
+ const { kind, ...rest } = op as { kind: string } & Record<string, unknown>;
3436
+ if (kind === "insert") {
3437
+ throw new Error(
3438
+ "TableOp kind \"insert\" is not routed through ref.tables.apply; use ref.insertTable(...).",
3439
+ );
3440
+ }
3441
+ return { type: kind, ...rest } as TableStructureOperation;
3442
+ }
3443
+
3444
+ /**
3445
+ * Build the `ref.tables` facet: a typed dispatch boundary + capability
3446
+ * read. Delegates every op through the same `applyRuntimeTableStructureOperation`
3447
+ * helper the flat ref verbs use, so there is exactly one server-side path
3448
+ * for every table mutation.
3449
+ */
3450
+ function buildTablesFacet(
3451
+ runtime: WordReviewEditorRuntime,
3452
+ mountedSurface: TwProseMirrorSurfaceRef | null,
3453
+ ) {
3454
+ const getCapabilities = () => {
3455
+ const snapshot = runtime.getRenderSnapshot();
3456
+ const document = runtime.getSessionState().canonicalDocument;
3457
+ return (
3458
+ clonePublicValue(
3459
+ getTableStructureContext(
3460
+ document,
3461
+ snapshot,
3462
+ mountedSurface?.getTableSelection() ?? null,
3463
+ ),
3464
+ ) ?? null
3465
+ );
3466
+ };
3467
+ return {
3468
+ apply(op: TableOp): TableOpResult {
3469
+ if (op.kind === "insert") {
3470
+ applyRuntimeInsertTable(runtime, { rows: op.rows, columns: op.columns });
3471
+ return {
3472
+ changed: true,
3473
+ coercedReason: null,
3474
+ capabilities: getCapabilities(),
3475
+ };
3476
+ }
3477
+ const internal = publicTableOpToInternal(op);
3478
+ const outcome = applyRuntimeTableStructureOperation(
3479
+ runtime,
3480
+ mountedSurface,
3481
+ internal,
3482
+ );
3483
+ return {
3484
+ changed: outcome.changed,
3485
+ coercedReason: outcome.coercedReason,
3486
+ capabilities: getCapabilities(),
3487
+ };
3488
+ },
3489
+ getCapabilities,
3490
+ };
3407
3491
  }
3408
3492
 
3409
3493
  function applyRuntimeTextCommand(
@@ -34,6 +34,7 @@ import {
34
34
  type DocumentRuntimeEvent,
35
35
  type DocumentRuntime,
36
36
  } from "../runtime/document-runtime.ts";
37
+ import { createInertLayoutFacet } from "../runtime/layout/index.ts";
37
38
  import { loadDocxEditorSession } from "../io/docx-session.ts";
38
39
  import {
39
40
  decodePersistedSourcePackageBytes,
@@ -738,6 +739,7 @@ function createLoadingRuntimeBridge(input: {
738
739
  viewState: EditorViewStateSnapshot;
739
740
  navigation: DocumentNavigationSnapshot;
740
741
  }): WordReviewEditorRuntime {
742
+ const inertLayoutFacet = createInertLayoutFacet();
741
743
  const emptyFieldSnapshot: FieldSnapshot = {
742
744
  totalCount: 0,
743
745
  supportedCount: 0,
@@ -772,7 +774,13 @@ function createLoadingRuntimeBridge(input: {
772
774
  getCanonicalDocument: () => input.sessionState.canonicalDocument,
773
775
  getSourcePackage: () => input.sessionState.sourcePackage,
774
776
  replaceText: () => undefined,
775
- applyActiveStoryTextCommand: () => undefined,
777
+ applyActiveStoryTextCommand: () => ({
778
+ kind: "rejected",
779
+ newRevisionToken: "",
780
+ blockedReasons: [
781
+ { code: "runtime_loading", message: "Document runtime is not ready yet." },
782
+ ],
783
+ }),
776
784
  dispatch: () => undefined,
777
785
  undo: () => undefined,
778
786
  redo: () => undefined,
@@ -801,6 +809,7 @@ function createLoadingRuntimeBridge(input: {
801
809
  setZoom: () => undefined,
802
810
  getPageLayoutSnapshot: () => null,
803
811
  getDocumentNavigationSnapshot: () => input.navigation,
812
+ layout: inertLayoutFacet,
804
813
  getCurrentLocation: () => null,
805
814
  getLocationForSelection: () => null,
806
815
  getLocationForAnchor: () => null,
@@ -55,6 +55,12 @@ export interface EditorShellViewProps {
55
55
  activeRevisionId?: string;
56
56
  showTrackedChanges: boolean;
57
57
  workflowScopeSnapshot?: WorkflowScopeSnapshot | null;
58
+ /**
59
+ * Runtime-owned layout facet passed through to the workspace so the
60
+ * ChromeOverlay (scope rail, workflow dock, etc.) can render over the
61
+ * document column and the review rail's Workflow tab can read segments.
62
+ */
63
+ layoutFacet?: import("../runtime/layout/index.ts").WordReviewEditorLayoutFacet;
58
64
  interactionGuardSnapshot?: InteractionGuardSnapshot;
59
65
  chromePreset?: WordReviewEditorChromePreset;
60
66
  chromeOptions?: Partial<WordReviewEditorChromeOptions>;
@@ -60,6 +60,9 @@ export interface EditorSurfaceControllerProps {
60
60
  activeWorkflowWorkItemId?: string | null;
61
61
  activeWorkflowScopeIds?: readonly string[];
62
62
  workflowMetadata?: readonly WorkflowMetadataMarkup[];
63
+ dispatchRuntimeCommand?: (
64
+ command: import("../ui-tailwind/editor-surface/fast-text-edit-lane.ts").LaneRuntimeCommand,
65
+ ) => import("../api/public-types.ts").TextCommandAck;
63
66
  }
64
67
 
65
68
  export const EditorSurfaceController = forwardRef<