@beyondwork/docx-react-component 1.0.41 → 1.0.43

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 (118) hide show
  1. package/package.json +38 -37
  2. package/src/api/awareness-identity-types.ts +35 -0
  3. package/src/api/comment-negotiation-types.ts +130 -0
  4. package/src/api/comment-presentation-types.ts +106 -0
  5. package/src/api/editor-state-types.ts +110 -0
  6. package/src/api/external-custody-types.ts +74 -0
  7. package/src/api/participants-types.ts +18 -0
  8. package/src/api/public-types.ts +541 -5
  9. package/src/api/scope-metadata-resolver-types.ts +88 -0
  10. package/src/core/commands/formatting-commands.ts +1 -1
  11. package/src/core/commands/index.ts +601 -9
  12. package/src/core/search/search-text.ts +15 -2
  13. package/src/index.ts +131 -1
  14. package/src/io/docx-session.ts +672 -2
  15. package/src/io/export/escape-xml-attribute.ts +26 -0
  16. package/src/io/export/external-send.ts +188 -0
  17. package/src/io/export/serialize-comments.ts +13 -16
  18. package/src/io/export/serialize-footnotes.ts +17 -24
  19. package/src/io/export/serialize-headers-footers.ts +17 -24
  20. package/src/io/export/serialize-main-document.ts +59 -62
  21. package/src/io/export/serialize-numbering.ts +20 -27
  22. package/src/io/export/serialize-runtime-revisions.ts +2 -9
  23. package/src/io/export/serialize-tables.ts +8 -15
  24. package/src/io/export/table-properties-xml.ts +25 -32
  25. package/src/io/import/external-reimport.ts +40 -0
  26. package/src/io/load-scheduler.ts +230 -0
  27. package/src/io/normalize/normalize-text.ts +83 -0
  28. package/src/io/ooxml/bw-xml.ts +244 -0
  29. package/src/io/ooxml/canonicalize-payload.ts +301 -0
  30. package/src/io/ooxml/comment-negotiation-payload.ts +288 -0
  31. package/src/io/ooxml/comment-presentation-payload.ts +311 -0
  32. package/src/io/ooxml/external-custody-payload.ts +102 -0
  33. package/src/io/ooxml/participants-payload.ts +97 -0
  34. package/src/io/ooxml/payload-signature.ts +112 -0
  35. package/src/io/ooxml/workflow-payload-validator.ts +367 -0
  36. package/src/io/ooxml/workflow-payload.ts +317 -7
  37. package/src/runtime/awareness-identity.ts +173 -0
  38. package/src/runtime/collab/event-types.ts +27 -0
  39. package/src/runtime/collab-session-bridge.ts +157 -0
  40. package/src/runtime/collab-session-facet.ts +193 -0
  41. package/src/runtime/collab-session.ts +273 -0
  42. package/src/runtime/comment-negotiation-sync.ts +91 -0
  43. package/src/runtime/comment-negotiation.ts +158 -0
  44. package/src/runtime/comment-presentation.ts +223 -0
  45. package/src/runtime/document-runtime.ts +639 -124
  46. package/src/runtime/editor-state-channel.ts +544 -0
  47. package/src/runtime/editor-state-integration.ts +217 -0
  48. package/src/runtime/external-send-runtime.ts +117 -0
  49. package/src/runtime/layout/docx-font-loader.ts +11 -30
  50. package/src/runtime/layout/index.ts +2 -0
  51. package/src/runtime/layout/inert-layout-facet.ts +4 -0
  52. package/src/runtime/layout/layout-engine-instance.ts +139 -14
  53. package/src/runtime/layout/page-graph.ts +79 -7
  54. package/src/runtime/layout/paginated-layout-engine.ts +441 -48
  55. package/src/runtime/layout/public-facet.ts +585 -14
  56. package/src/runtime/layout/table-row-split.ts +316 -0
  57. package/src/runtime/markdown-sanitizer.ts +132 -0
  58. package/src/runtime/participants.ts +134 -0
  59. package/src/runtime/perf-counters.ts +28 -0
  60. package/src/runtime/render/render-frame-types.ts +17 -0
  61. package/src/runtime/render/render-kernel.ts +172 -29
  62. package/src/runtime/resign-payload.ts +120 -0
  63. package/src/runtime/surface-projection.ts +10 -5
  64. package/src/runtime/tamper-gate.ts +157 -0
  65. package/src/runtime/workflow-markup.ts +80 -16
  66. package/src/runtime/workflow-rail-segments.ts +244 -5
  67. package/src/ui/WordReviewEditor.tsx +654 -45
  68. package/src/ui/editor-command-bag.ts +14 -0
  69. package/src/ui/editor-runtime-boundary.ts +111 -11
  70. package/src/ui/editor-shell-view.tsx +21 -0
  71. package/src/ui/editor-surface-controller.tsx +5 -0
  72. package/src/ui/headless/selection-helpers.ts +10 -0
  73. package/src/ui-tailwind/chrome/chrome-preset-model.ts +28 -0
  74. package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +62 -2
  75. package/src/ui-tailwind/chrome/collab-audience-chip.tsx +73 -0
  76. package/src/ui-tailwind/chrome/collab-negotiation-action-bar.tsx +244 -0
  77. package/src/ui-tailwind/chrome/collab-presence-strip.tsx +150 -0
  78. package/src/ui-tailwind/chrome/collab-role-chip.tsx +62 -0
  79. package/src/ui-tailwind/chrome/collab-send-to-supplier-button.tsx +68 -0
  80. package/src/ui-tailwind/chrome/collab-send-to-supplier-modal.tsx +149 -0
  81. package/src/ui-tailwind/chrome/collab-tamper-banner.tsx +68 -0
  82. package/src/ui-tailwind/chrome/collab-top-nav-container.tsx +281 -0
  83. package/src/ui-tailwind/chrome/forward-non-drag-click.ts +104 -0
  84. package/src/ui-tailwind/chrome/tw-mode-dock.tsx +1 -0
  85. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +7 -1
  86. package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +1 -38
  87. package/src/ui-tailwind/chrome-overlay/index.ts +6 -0
  88. package/src/ui-tailwind/chrome-overlay/scope-card-role-model.ts +78 -0
  89. package/src/ui-tailwind/chrome-overlay/scope-keyboard-cycle.ts +49 -0
  90. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +106 -0
  91. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +527 -0
  92. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +120 -22
  93. package/src/ui-tailwind/chrome-overlay/tw-scope-card.tsx +310 -32
  94. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +93 -14
  95. package/src/ui-tailwind/editor-surface/paste-plain-text.ts +72 -0
  96. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +118 -8
  97. package/src/ui-tailwind/editor-surface/pm-decorations.ts +35 -1
  98. package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +86 -3
  99. package/src/ui-tailwind/editor-surface/pm-schema.ts +167 -17
  100. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +35 -7
  101. package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +20 -3
  102. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +265 -0
  103. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +10 -256
  104. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +9 -0
  105. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +66 -0
  106. package/src/ui-tailwind/index.ts +37 -1
  107. package/src/ui-tailwind/page-stack/tw-endnote-area.tsx +57 -0
  108. package/src/ui-tailwind/page-stack/tw-footnote-area.tsx +71 -0
  109. package/src/ui-tailwind/page-stack/tw-page-footer-band.tsx +73 -0
  110. package/src/ui-tailwind/page-stack/tw-page-header-band.tsx +74 -0
  111. package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +477 -0
  112. package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +374 -0
  113. package/src/ui-tailwind/review/comment-markdown-renderer.tsx +155 -0
  114. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +77 -16
  115. package/src/ui-tailwind/review/tw-review-rail-footer.tsx +29 -3
  116. package/src/ui-tailwind/status/tw-status-bar.tsx +52 -1
  117. package/src/ui-tailwind/theme/editor-theme.css +25 -0
  118. package/src/ui-tailwind/tw-review-workspace.tsx +455 -118
@@ -11,7 +11,11 @@ export { renderTwCaret } from "./editor-surface/tw-caret";
11
11
 
12
12
  // Review rail
13
13
  export { TwReviewRail, type TwReviewRailProps, type ReviewRailTab } from "./review/tw-review-rail";
14
- export { TwCommentSidebar } from "./review/tw-comment-sidebar";
14
+ export { TwCommentSidebar, type TwCommentSidebarProps } from "./review/tw-comment-sidebar";
15
+ export {
16
+ CommentMarkdownRenderer,
17
+ type CommentMarkdownRendererProps,
18
+ } from "./review/comment-markdown-renderer";
15
19
  export { TwRevisionSidebar } from "./review/tw-revision-sidebar";
16
20
  export { TwHealthPanel } from "./review/tw-health-panel";
17
21
  export { TwWorkflowTab, type TwWorkflowTabProps } from "./review/tw-workflow-tab";
@@ -53,6 +57,38 @@ export {
53
57
  type TwModeDockProps,
54
58
  } from "./chrome/tw-mode-dock";
55
59
 
60
+ // Collab chrome (P9) — mount when chromePreset === "collab"; each
61
+ // component is pure presentational and takes snapshots + callbacks.
62
+ export {
63
+ CollabPresenceStrip,
64
+ type CollabPresenceStripProps,
65
+ } from "./chrome/collab-presence-strip";
66
+ export {
67
+ CollabRoleChip,
68
+ type CollabRoleChipProps,
69
+ } from "./chrome/collab-role-chip";
70
+ export {
71
+ CollabAudienceChip,
72
+ type CollabAudienceChipProps,
73
+ } from "./chrome/collab-audience-chip";
74
+ export {
75
+ CollabTamperBanner,
76
+ type CollabTamperBannerProps,
77
+ } from "./chrome/collab-tamper-banner";
78
+ export {
79
+ CollabNegotiationActionBar,
80
+ type CollabNegotiationActionBarProps,
81
+ } from "./chrome/collab-negotiation-action-bar";
82
+ export {
83
+ CollabSendToSupplierButton,
84
+ type CollabSendToSupplierButtonProps,
85
+ } from "./chrome/collab-send-to-supplier-button";
86
+ export {
87
+ CollabSendToSupplierModal,
88
+ type CollabSendToSupplierModalProps,
89
+ type CollabSendToSupplierSubmitArgs,
90
+ } from "./chrome/collab-send-to-supplier-modal";
91
+
56
92
  // Chrome overlay plane (R3a — scope rail layer)
57
93
  export {
58
94
  TwChromeOverlay,
@@ -0,0 +1,57 @@
1
+ import React from "react";
2
+
3
+ import type { SurfaceBlockSnapshot } from "../../api/public-types.ts";
4
+ import { TwRegionBlockRenderer } from "./tw-region-block-renderer.tsx";
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // TwEndnoteArea (P8.7)
8
+ //
9
+ // Read-only area for document-end endnote placement. Unlike footnotes, which
10
+ // the chrome layer positions per-page via an absolute rectangle, endnotes in
11
+ // the default mode sit at the end of the document (Word's `w:pos="docEnd"`
12
+ // behavior). This component is mounted as a sibling of the chrome layer
13
+ // after the last page rect — not as an absolutely positioned chrome overlay.
14
+ //
15
+ // It renders:
16
+ // 1. A default 1px separator (1/3 of the parent width) along the top edge,
17
+ // and
18
+ // 2. The ordered endnote bodies via `TwRegionBlockRenderer` (P8.4).
19
+ //
20
+ // When `blocks` is empty the component returns `null` so the document-end
21
+ // slot stays visually absent.
22
+ //
23
+ // Per-section endnote placement (`w:endnotePr/w:pos` other than `docEnd`)
24
+ // is a follow-up — tracked with the P8.b polish pass.
25
+ // ---------------------------------------------------------------------------
26
+
27
+ export interface TwEndnoteAreaProps {
28
+ blocks: readonly SurfaceBlockSnapshot[];
29
+ "data-testid"?: string;
30
+ }
31
+
32
+ export const TwEndnoteArea: React.FC<TwEndnoteAreaProps> = ({
33
+ blocks,
34
+ "data-testid": testId,
35
+ }) => {
36
+ if (blocks.length === 0) return null;
37
+ return (
38
+ <div
39
+ data-endnote-area
40
+ data-testid={testId}
41
+ style={{ marginTop: "24pt" }}
42
+ >
43
+ <div
44
+ data-endnote-separator
45
+ style={{
46
+ width: "33%",
47
+ height: "1px",
48
+ backgroundColor: "currentColor",
49
+ marginBottom: "8pt",
50
+ }}
51
+ />
52
+ <TwRegionBlockRenderer blocks={blocks} />
53
+ </div>
54
+ );
55
+ };
56
+
57
+ export default TwEndnoteArea;
@@ -0,0 +1,71 @@
1
+ import React from "react";
2
+
3
+ import type { SurfaceBlockSnapshot } from "../../api/public-types.ts";
4
+ import { TwRegionBlockRenderer } from "./tw-region-block-renderer.tsx";
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // TwFootnoteArea (P8.6)
8
+ //
9
+ // Read-only area mounted at an absolute pixel rectangle inside a per-page
10
+ // chrome overlay. Renders:
11
+ // 1. A default 1px separator (1/3 of the parent width) along the top edge,
12
+ // and
13
+ // 2. The ordered footnote bodies via `TwRegionBlockRenderer` (P8.4).
14
+ //
15
+ // The chrome layer (P8.8) computes the `topPx / leftPx / widthPx / heightPx`
16
+ // rectangle from the page graph's `regions.footnotes` geometry and mounts
17
+ // this component above the footer band.
18
+ //
19
+ // The default separator matches Word's implicit footnote separator; reading
20
+ // the `w:separator` / `w:continuationSeparator` parts from the footnotes
21
+ // package is deferred to P8.b polish. No active-slot / portal plumbing in
22
+ // this pass — the P8 plan only reserves portal slots for header / footer.
23
+ // ---------------------------------------------------------------------------
24
+
25
+ export interface TwFootnoteAreaProps {
26
+ pageIndex: number;
27
+ blocks: readonly SurfaceBlockSnapshot[];
28
+ topPx: number;
29
+ leftPx: number;
30
+ widthPx: number;
31
+ heightPx: number;
32
+ "data-testid"?: string;
33
+ }
34
+
35
+ export const TwFootnoteArea: React.FC<TwFootnoteAreaProps> = ({
36
+ pageIndex,
37
+ blocks,
38
+ topPx,
39
+ leftPx,
40
+ widthPx,
41
+ heightPx,
42
+ "data-testid": testId,
43
+ }) => {
44
+ return (
45
+ <div
46
+ data-footnote-area
47
+ data-page-index={pageIndex}
48
+ data-testid={testId}
49
+ style={{
50
+ position: "absolute",
51
+ top: `${topPx}px`,
52
+ left: `${leftPx}px`,
53
+ width: `${widthPx}px`,
54
+ height: `${heightPx}px`,
55
+ }}
56
+ >
57
+ <div
58
+ data-footnote-separator
59
+ style={{
60
+ width: `${Math.round(widthPx / 3)}px`,
61
+ height: "1px",
62
+ backgroundColor: "currentColor",
63
+ marginBottom: "4pt",
64
+ }}
65
+ />
66
+ <TwRegionBlockRenderer blocks={blocks} />
67
+ </div>
68
+ );
69
+ };
70
+
71
+ export default TwFootnoteArea;
@@ -0,0 +1,73 @@
1
+ import React from "react";
2
+
3
+ import type { SurfaceBlockSnapshot } from "../../api/public-types.ts";
4
+ import { TwRegionBlockRenderer } from "./tw-region-block-renderer.tsx";
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // TwPageFooterBand (P8.5)
8
+ //
9
+ // Symmetric counterpart to `TwPageHeaderBand` (see its header for design
10
+ // context). The footer band positions itself via `bottom` rather than
11
+ // `top` so the chrome layer can measure the footer margin up from the
12
+ // page rect's bottom edge and keep footer content pinned correctly when
13
+ // page size / margin changes.
14
+ //
15
+ // When `isActiveSlot` is true, the band emits a single `data-pm-portal-slot`
16
+ // div tagged `data-page-band-slot="footer"` — the chrome layer portals the
17
+ // PM surface into this target in P8.10. Otherwise it renders the footer
18
+ // story's `SurfaceBlockSnapshot[]` through `TwRegionBlockRenderer` (P8.4).
19
+ // ---------------------------------------------------------------------------
20
+
21
+ export interface TwPageFooterBandProps {
22
+ pageIndex: number;
23
+ blocks: readonly SurfaceBlockSnapshot[];
24
+ bandHeightPx: number;
25
+ bottomPx: number;
26
+ leftPx: number;
27
+ widthPx: number;
28
+ /** True when this band is the active-story slot. Renders portal target instead of read-only DOM. */
29
+ isActiveSlot: boolean;
30
+ onClick: () => void;
31
+ "data-testid"?: string;
32
+ }
33
+
34
+ export const TwPageFooterBand: React.FC<TwPageFooterBandProps> = ({
35
+ pageIndex,
36
+ blocks,
37
+ bandHeightPx,
38
+ bottomPx,
39
+ leftPx,
40
+ widthPx,
41
+ isActiveSlot,
42
+ onClick,
43
+ "data-testid": testId,
44
+ }) => {
45
+ return (
46
+ <div
47
+ data-page-band="footer"
48
+ data-page-index={pageIndex}
49
+ data-testid={testId}
50
+ onClick={onClick}
51
+ style={{
52
+ position: "absolute",
53
+ bottom: `${bottomPx}px`,
54
+ left: `${leftPx}px`,
55
+ width: `${widthPx}px`,
56
+ height: `${bandHeightPx}px`,
57
+ cursor: "pointer",
58
+ }}
59
+ >
60
+ {isActiveSlot ? (
61
+ <div
62
+ data-pm-portal-slot
63
+ data-page-band-slot="footer"
64
+ style={{ width: "100%", height: "100%" }}
65
+ />
66
+ ) : (
67
+ <TwRegionBlockRenderer blocks={blocks} />
68
+ )}
69
+ </div>
70
+ );
71
+ };
72
+
73
+ export default TwPageFooterBand;
@@ -0,0 +1,74 @@
1
+ import React from "react";
2
+
3
+ import type { SurfaceBlockSnapshot } from "../../api/public-types.ts";
4
+ import { TwRegionBlockRenderer } from "./tw-region-block-renderer.tsx";
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // TwPageHeaderBand (P8.5)
8
+ //
9
+ // Read-only band mounted at an absolute pixel position inside a per-page
10
+ // chrome overlay. The band either:
11
+ // - Renders the header story's `SurfaceBlockSnapshot[]` through
12
+ // `TwRegionBlockRenderer` (P8.4) as pure presentational DOM, or
13
+ // - Emits a single `data-pm-portal-slot` div when the chrome layer has
14
+ // promoted this band to the active story slot (P8.10 wires the PM
15
+ // surface into this target via React portal).
16
+ //
17
+ // Clicks on the band bubble to the chrome layer's `openStory` dispatch
18
+ // (wired in P8.8 / P8.10) so legal reviewers can promote a header into
19
+ // the active editing surface.
20
+ // ---------------------------------------------------------------------------
21
+
22
+ export interface TwPageHeaderBandProps {
23
+ pageIndex: number;
24
+ blocks: readonly SurfaceBlockSnapshot[];
25
+ bandHeightPx: number;
26
+ topPx: number;
27
+ leftPx: number;
28
+ widthPx: number;
29
+ /** True when this band is the active-story slot. Renders portal target instead of read-only DOM. */
30
+ isActiveSlot: boolean;
31
+ onClick: () => void;
32
+ "data-testid"?: string;
33
+ }
34
+
35
+ export const TwPageHeaderBand: React.FC<TwPageHeaderBandProps> = ({
36
+ pageIndex,
37
+ blocks,
38
+ bandHeightPx,
39
+ topPx,
40
+ leftPx,
41
+ widthPx,
42
+ isActiveSlot,
43
+ onClick,
44
+ "data-testid": testId,
45
+ }) => {
46
+ return (
47
+ <div
48
+ data-page-band="header"
49
+ data-page-index={pageIndex}
50
+ data-testid={testId}
51
+ onClick={onClick}
52
+ style={{
53
+ position: "absolute",
54
+ top: `${topPx}px`,
55
+ left: `${leftPx}px`,
56
+ width: `${widthPx}px`,
57
+ height: `${bandHeightPx}px`,
58
+ cursor: "pointer",
59
+ }}
60
+ >
61
+ {isActiveSlot ? (
62
+ <div
63
+ data-pm-portal-slot
64
+ data-page-band-slot="header"
65
+ style={{ width: "100%", height: "100%" }}
66
+ />
67
+ ) : (
68
+ <TwRegionBlockRenderer blocks={blocks} />
69
+ )}
70
+ </div>
71
+ );
72
+ };
73
+
74
+ export default TwPageHeaderBand;