@beyondwork/docx-react-component 1.0.37 → 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 (74) hide show
  1. package/package.json +1 -1
  2. package/src/api/public-types.ts +319 -1
  3. package/src/core/commands/section-layout-commands.ts +58 -0
  4. package/src/core/commands/table-grid.ts +431 -0
  5. package/src/core/commands/table-structure-commands.ts +815 -55
  6. package/src/io/export/serialize-main-document.ts +2 -11
  7. package/src/io/export/serialize-numbering.ts +1 -2
  8. package/src/io/export/serialize-tables.ts +74 -0
  9. package/src/io/export/table-properties-xml.ts +139 -4
  10. package/src/io/normalize/normalize-text.ts +15 -0
  11. package/src/io/ooxml/parse-footnotes.ts +60 -0
  12. package/src/io/ooxml/parse-headers-footers.ts +60 -0
  13. package/src/io/ooxml/parse-main-document.ts +137 -0
  14. package/src/io/ooxml/parse-tables.ts +249 -0
  15. package/src/model/canonical-document.ts +34 -0
  16. package/src/runtime/document-layout.ts +4 -2
  17. package/src/runtime/document-navigation.ts +1 -1
  18. package/src/runtime/document-runtime.ts +114 -0
  19. package/src/runtime/layout/default-page-format.ts +96 -0
  20. package/src/runtime/layout/index.ts +45 -0
  21. package/src/runtime/layout/inert-layout-facet.ts +14 -0
  22. package/src/runtime/layout/layout-engine-instance.ts +33 -23
  23. package/src/runtime/layout/margin-preset-catalog.ts +178 -0
  24. package/src/runtime/layout/page-format-catalog.ts +233 -0
  25. package/src/runtime/layout/page-graph.ts +19 -0
  26. package/src/runtime/layout/paginated-layout-engine.ts +142 -9
  27. package/src/runtime/layout/project-block-fragments.ts +91 -0
  28. package/src/runtime/layout/public-facet.ts +709 -16
  29. package/src/runtime/layout/table-render-plan.ts +229 -0
  30. package/src/runtime/render/block-fragment-projection.ts +35 -0
  31. package/src/runtime/render/decoration-resolver.ts +189 -0
  32. package/src/runtime/render/index.ts +57 -0
  33. package/src/runtime/render/pending-op-delta-reader.ts +129 -0
  34. package/src/runtime/render/render-frame-types.ts +317 -0
  35. package/src/runtime/render/render-kernel.ts +755 -0
  36. package/src/runtime/view-state.ts +67 -0
  37. package/src/runtime/workflow-markup.ts +1 -5
  38. package/src/runtime/workflow-rail-segments.ts +280 -0
  39. package/src/ui/WordReviewEditor.tsx +84 -15
  40. package/src/ui/editor-shell-view.tsx +6 -0
  41. package/src/ui/headless/chrome-registry.ts +280 -14
  42. package/src/ui/headless/scoped-chrome-policy.ts +20 -1
  43. package/src/ui/headless/selection-tool-types.ts +10 -0
  44. package/src/ui-tailwind/chrome/chrome-preset-model.ts +23 -2
  45. package/src/ui-tailwind/chrome/role-action-sets.ts +74 -0
  46. package/src/ui-tailwind/chrome/tw-detach-handle.tsx +147 -0
  47. package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +163 -0
  48. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +57 -92
  49. package/src/ui-tailwind/chrome/tw-selection-tool-placement.ts +149 -0
  50. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +15 -4
  51. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +274 -138
  52. package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +90 -0
  53. package/src/ui-tailwind/chrome-overlay/index.ts +22 -0
  54. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +86 -0
  55. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +178 -0
  56. package/src/ui-tailwind/chrome-overlay/tw-workspace-view-switcher.tsx +95 -0
  57. package/src/ui-tailwind/editor-surface/fast-text-edit-lane.ts +4 -0
  58. package/src/ui-tailwind/editor-surface/local-edit-session-state.ts +11 -0
  59. package/src/ui-tailwind/editor-surface/perf-probe.ts +7 -1
  60. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +12 -1
  61. package/src/ui-tailwind/editor-surface/pm-decorations.ts +22 -12
  62. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +3 -0
  63. package/src/ui-tailwind/index.ts +33 -0
  64. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +2 -2
  65. package/src/ui-tailwind/review/tw-rail-card.tsx +150 -0
  66. package/src/ui-tailwind/review/tw-review-rail-footer.tsx +52 -0
  67. package/src/ui-tailwind/review/tw-review-rail.tsx +166 -11
  68. package/src/ui-tailwind/review/tw-workflow-tab.tsx +108 -0
  69. package/src/ui-tailwind/theme/editor-theme.css +498 -163
  70. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +559 -0
  71. package/src/ui-tailwind/toolbar/tw-scope-posture-menu.tsx +182 -0
  72. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +162 -0
  73. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +69 -0
  74. package/src/ui-tailwind/tw-review-workspace.tsx +136 -1
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Shape definitions for the runtime-owned render kernel (Phase R1).
3
+ *
4
+ * Per runtime-rendering-and-chrome-phase.md §1 the render kernel projects the
5
+ * layout engine's page graph plus the decoration sources into a stable
6
+ * `RenderFrame` that every chrome surface reads. Today the kernel emits a
7
+ * minimum-viable frame so consumers can start wiring against the shape;
8
+ * richer decoration resolution, wrap rects, and table render plans land as
9
+ * the chrome phase continues.
10
+ */
11
+
12
+ import type {
13
+ EditorStoryTarget,
14
+ PageLayoutSnapshot,
15
+ } from "../../api/public-types";
16
+ import type {
17
+ PublicBlockFragment,
18
+ PublicLineBox,
19
+ PublicPageNode,
20
+ PublicPageRegion,
21
+ PublicMeasurementFidelity,
22
+ } from "../layout/public-facet.ts";
23
+
24
+ // ---------------------------------------------------------------------------
25
+ // Core frame shape
26
+ // ---------------------------------------------------------------------------
27
+
28
+ export interface RenderFrame {
29
+ /** Revision stamp the kernel used to build this frame. */
30
+ revision: number;
31
+ /** Measurement fidelity the frame was computed against. */
32
+ measurementFidelity: PublicMeasurementFidelity;
33
+ /** Story the mounted surface renders. */
34
+ activeStory: EditorStoryTarget;
35
+ /** Zoom the frame was composed for. */
36
+ zoom: RenderZoom;
37
+ /** Page frames in document order. */
38
+ pages: RenderPage[];
39
+ /** Decoration index (per-frame, keyed by canonical runtime ranges). */
40
+ decorationIndex: DecorationIndex;
41
+ /** Anchor index for chrome placement. */
42
+ anchorIndex: RenderAnchorIndex;
43
+ }
44
+
45
+ export interface RenderPage {
46
+ page: PublicPageNode;
47
+ /** Top-left in frame-local pixel coordinates (post-zoom). */
48
+ frame: RenderFrameRect;
49
+ regions: RenderPageRegions;
50
+ chromeReservations: PageChromeReservations;
51
+ }
52
+
53
+ export interface RenderPageRegions {
54
+ body: RenderStoryRegion;
55
+ header?: RenderStoryRegion;
56
+ footer?: RenderStoryRegion;
57
+ columns?: readonly RenderStoryRegion[];
58
+ footnoteArea?: RenderStoryRegion;
59
+ }
60
+
61
+ export interface RenderStoryRegion {
62
+ storyTarget: EditorStoryTarget;
63
+ region: PublicPageRegion;
64
+ frame: RenderFrameRect;
65
+ blocks: RenderBlock[];
66
+ }
67
+
68
+ export interface RenderBlock {
69
+ fragment: PublicBlockFragment;
70
+ frame: RenderFrameRect;
71
+ kind: "paragraph" | "table" | "opaque" | "image-float" | "synthetic";
72
+ lines: RenderLine[];
73
+ blockDecorations: RenderBlockDecoration[];
74
+ /**
75
+ * Table geometry + band classes, attached only when `kind === "table"`.
76
+ * Populated by the layout facet's `getTableRenderPlan(blockId)` during
77
+ * frame construction so chrome can read columns, merges, and resize-
78
+ * handle origins without walking the canonical model.
79
+ */
80
+ tablePlan?: import("../layout/table-render-plan.ts").TableRenderPlan | null;
81
+ }
82
+
83
+ export interface RenderLine {
84
+ line: PublicLineBox;
85
+ frame: RenderFrameRect;
86
+ /**
87
+ * Anchor metadata for chrome hit-testing and balloon placement. Today
88
+ * every line carries a single anchor covering its fragment; richer per-
89
+ * run anchors land with the kernel's full line-box projection.
90
+ */
91
+ anchors: RenderLineAnchor[];
92
+ }
93
+
94
+ export interface RenderLineAnchor {
95
+ /** Runtime offset this anchor represents. */
96
+ runtimeOffset: number;
97
+ /** Screen-relative rect the anchor occupies. */
98
+ frame: RenderFrameRect;
99
+ /** Optional fragment or block id hint. */
100
+ fragmentId?: string;
101
+ blockId?: string;
102
+ }
103
+
104
+ export interface RenderBlockDecoration {
105
+ kind: "workflow" | "comment" | "revision" | "search" | "locked";
106
+ /** Decoration identifier (scope id, comment id, revision id, etc.). */
107
+ refId: string;
108
+ frame: RenderFrameRect;
109
+ }
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // Geometry
113
+ // ---------------------------------------------------------------------------
114
+
115
+ /**
116
+ * All rects are in frame-local pixels at the kernel's chosen zoom. The
117
+ * kernel applies `RenderZoom.pxPerTwip` to the page graph's twip values
118
+ * before returning a frame.
119
+ */
120
+ export interface RenderFrameRect {
121
+ leftPx: number;
122
+ topPx: number;
123
+ widthPx: number;
124
+ heightPx: number;
125
+ }
126
+
127
+ export interface RenderZoom {
128
+ /** Pixels per twip (e.g. 96 dpi at 100% = 0.0667). */
129
+ pxPerTwip: number;
130
+ /** Viewport width in px. */
131
+ viewportWidthPx: number;
132
+ /** Fit mode the zoom was resolved from. */
133
+ fitMode: "fixed" | "page-width" | "one-page";
134
+ }
135
+
136
+ export interface PageChromeReservations {
137
+ /** Twips reserved to the left of the page frame for the workflow rail. */
138
+ railLaneTwips: number;
139
+ /** Twips reserved to the right for comment balloons. */
140
+ balloonLaneTwips: number;
141
+ /** Twips reserved at the bottom of the body for footnotes. */
142
+ footnoteAreaTwips: number;
143
+ /** Cached post-zoom page frame in pixels. */
144
+ pageFrameWidthPx: number;
145
+ pageFrameHeightPx: number;
146
+ }
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // Decoration and anchor indexes
150
+ // ---------------------------------------------------------------------------
151
+
152
+ export interface DecorationIndex {
153
+ workflow: readonly RenderBlockDecoration[];
154
+ comments: readonly RenderBlockDecoration[];
155
+ revisions: readonly RenderBlockDecoration[];
156
+ search: readonly RenderBlockDecoration[];
157
+ locked: readonly RenderBlockDecoration[];
158
+ }
159
+
160
+ export interface RenderAnchorIndex {
161
+ byRuntimeOffset(
162
+ offset: number,
163
+ story?: EditorStoryTarget,
164
+ ): RenderFrameRect | null;
165
+ byBlockId(blockId: string): RenderFrameRect | null;
166
+ byFragmentId(fragmentId: string): RenderFrameRect | null;
167
+ byPageIndex(pageIndex: number): RenderFrameRect | null;
168
+ /**
169
+ * Union rect covering a selection range. Resolves one rect per runtime
170
+ * offset and unions them, so chrome surfaces anchoring on selections
171
+ * (selection toolbar, suggestion card, comment thread tool) get a single
172
+ * tight rect without repeating the union math per consumer. Returns
173
+ * `null` when no offset in the range resolves to an anchor.
174
+ *
175
+ * Inclusive `fromOffset`, exclusive `toOffset` — matches the selection
176
+ * model used by `SelectionSnapshot`. When `fromOffset === toOffset`,
177
+ * falls back to `byRuntimeOffset(fromOffset)` so collapsed carets
178
+ * resolve to their line-box rect.
179
+ */
180
+ bySelection(
181
+ fromOffset: number,
182
+ toOffset: number,
183
+ story?: EditorStoryTarget,
184
+ ): RenderFrameRect | null;
185
+ /**
186
+ * Locate a table cell's rect given its owning table block id and logical
187
+ * (rowIndex, columnIndex). Reads from the attached `TableRenderPlan` on
188
+ * the render block so chrome tools (border picker, cell context bar)
189
+ * position without re-measuring the DOM. Returns `null` when the block
190
+ * is not a table, the plan is absent, or the indices fall outside the
191
+ * cell matrix.
192
+ */
193
+ byTableCell(
194
+ tableBlockId: string,
195
+ rowIndex: number,
196
+ columnIndex: number,
197
+ ): RenderFrameRect | null;
198
+ /**
199
+ * Locate the vertical grip at the right edge of `columnIndex` on a
200
+ * table. Returns a zero-width rect whose `leftPx` is the grip origin
201
+ * and whose `heightPx` spans the table's visible height on the page.
202
+ */
203
+ byTableColumnEdge(
204
+ tableBlockId: string,
205
+ columnIndex: number,
206
+ ): RenderFrameRect | null;
207
+ /**
208
+ * Locate the horizontal grip at the bottom edge of `rowIndex`. Returns
209
+ * a zero-height rect whose `topPx` is the grip origin and whose
210
+ * `widthPx` spans the table's visible width.
211
+ */
212
+ byTableRowEdge(
213
+ tableBlockId: string,
214
+ rowIndex: number,
215
+ ): RenderFrameRect | null;
216
+ }
217
+
218
+ // ---------------------------------------------------------------------------
219
+ // Queries + events
220
+ // ---------------------------------------------------------------------------
221
+
222
+ export interface RenderFrameQueryOptions {
223
+ story?: EditorStoryTarget;
224
+ pageRange?: { fromPageIndex: number; toPageIndex: number };
225
+ includeDecorations?: boolean;
226
+ }
227
+
228
+ export interface RenderAnchorQuery {
229
+ kind:
230
+ | "runtime-offset"
231
+ | "block-id"
232
+ | "fragment-id"
233
+ | "scope-id"
234
+ | "comment-id"
235
+ | "revision-id"
236
+ | "page-index";
237
+ value: string | number;
238
+ story?: EditorStoryTarget;
239
+ }
240
+
241
+ export interface RenderHitResult {
242
+ pageIndex: number;
243
+ regionKind: PublicPageRegion["kind"];
244
+ blockId: string;
245
+ fragmentId: string;
246
+ lineIndex: number;
247
+ runtimeOffset: number;
248
+ }
249
+
250
+ /**
251
+ * Point in the shell's overlay coordinate space (not the viewport, not
252
+ * the document CSS). Chrome surfaces compute this from a pointer event
253
+ * via `chrome-overlay-projector.ts` (same projector that maps rects out
254
+ * of the overlay) and pass it into `facet.hitTest`.
255
+ */
256
+ export interface RenderPoint {
257
+ xPx: number;
258
+ yPx: number;
259
+ }
260
+
261
+ export type RenderKernelEvent =
262
+ | { kind: "frame_built"; revision: number; reason: "full" | "incremental" }
263
+ | {
264
+ kind: "frame_diff";
265
+ revision: number;
266
+ pageRange: { fromPageIndex: number; toPageIndex: number };
267
+ }
268
+ | { kind: "decoration_resolved"; revision: number };
269
+
270
+ export interface DefaultZoomInput {
271
+ pxPerTwip?: number;
272
+ viewportWidthPx?: number;
273
+ fitMode?: RenderZoom["fitMode"];
274
+ }
275
+
276
+ /**
277
+ * Conservative default zoom: 96 dpi at 100% = 1 inch per 96 pixels, and
278
+ * 1 inch = 1440 twips, so 1 twip = 96/1440 ≈ 0.0667 px. Consumers pass a
279
+ * real zoom when they have a viewport; this keeps the kernel testable
280
+ * without a DOM.
281
+ */
282
+ export const DEFAULT_PX_PER_TWIP = 96 / 1440;
283
+
284
+ export function resolveDefaultZoom(input: DefaultZoomInput = {}): RenderZoom {
285
+ return {
286
+ pxPerTwip: input.pxPerTwip ?? DEFAULT_PX_PER_TWIP,
287
+ viewportWidthPx: input.viewportWidthPx ?? 900,
288
+ fitMode: input.fitMode ?? "fixed",
289
+ };
290
+ }
291
+
292
+ export const EMPTY_DECORATION_INDEX: DecorationIndex = Object.freeze({
293
+ workflow: Object.freeze([]) as readonly RenderBlockDecoration[],
294
+ comments: Object.freeze([]) as readonly RenderBlockDecoration[],
295
+ revisions: Object.freeze([]) as readonly RenderBlockDecoration[],
296
+ search: Object.freeze([]) as readonly RenderBlockDecoration[],
297
+ locked: Object.freeze([]) as readonly RenderBlockDecoration[],
298
+ });
299
+
300
+ /**
301
+ * Default chrome reservations. Ship Letter-compatible defaults so the
302
+ * kernel can be instantiated without a host injecting chrome sizes; chrome
303
+ * consumers override via `RenderPage.chromeReservations` once they know
304
+ * their own layout.
305
+ */
306
+ export function defaultChromeReservations(
307
+ layout: PageLayoutSnapshot,
308
+ zoom: RenderZoom,
309
+ ): PageChromeReservations {
310
+ return {
311
+ railLaneTwips: 360,
312
+ balloonLaneTwips: 2160,
313
+ footnoteAreaTwips: 0,
314
+ pageFrameWidthPx: layout.pageWidth * zoom.pxPerTwip,
315
+ pageFrameHeightPx: layout.pageHeight * zoom.pxPerTwip,
316
+ };
317
+ }