@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.
- package/package.json +1 -1
- package/src/api/public-types.ts +319 -1
- package/src/core/commands/section-layout-commands.ts +58 -0
- package/src/core/commands/table-grid.ts +431 -0
- package/src/core/commands/table-structure-commands.ts +815 -55
- package/src/io/export/serialize-main-document.ts +2 -11
- package/src/io/export/serialize-numbering.ts +1 -2
- package/src/io/export/serialize-tables.ts +74 -0
- package/src/io/export/table-properties-xml.ts +139 -4
- package/src/io/normalize/normalize-text.ts +15 -0
- package/src/io/ooxml/parse-footnotes.ts +60 -0
- package/src/io/ooxml/parse-headers-footers.ts +60 -0
- package/src/io/ooxml/parse-main-document.ts +137 -0
- package/src/io/ooxml/parse-tables.ts +249 -0
- package/src/model/canonical-document.ts +34 -0
- package/src/runtime/document-layout.ts +4 -2
- package/src/runtime/document-navigation.ts +1 -1
- package/src/runtime/document-runtime.ts +114 -0
- package/src/runtime/layout/default-page-format.ts +96 -0
- package/src/runtime/layout/index.ts +45 -0
- package/src/runtime/layout/inert-layout-facet.ts +14 -0
- package/src/runtime/layout/layout-engine-instance.ts +33 -23
- package/src/runtime/layout/margin-preset-catalog.ts +178 -0
- package/src/runtime/layout/page-format-catalog.ts +233 -0
- package/src/runtime/layout/page-graph.ts +19 -0
- package/src/runtime/layout/paginated-layout-engine.ts +142 -9
- package/src/runtime/layout/project-block-fragments.ts +91 -0
- package/src/runtime/layout/public-facet.ts +709 -16
- package/src/runtime/layout/table-render-plan.ts +229 -0
- package/src/runtime/render/block-fragment-projection.ts +35 -0
- package/src/runtime/render/decoration-resolver.ts +189 -0
- package/src/runtime/render/index.ts +57 -0
- package/src/runtime/render/pending-op-delta-reader.ts +129 -0
- package/src/runtime/render/render-frame-types.ts +317 -0
- package/src/runtime/render/render-kernel.ts +755 -0
- package/src/runtime/view-state.ts +67 -0
- package/src/runtime/workflow-markup.ts +1 -5
- package/src/runtime/workflow-rail-segments.ts +280 -0
- package/src/ui/WordReviewEditor.tsx +84 -15
- package/src/ui/editor-shell-view.tsx +6 -0
- package/src/ui/headless/chrome-registry.ts +280 -14
- package/src/ui/headless/scoped-chrome-policy.ts +20 -1
- package/src/ui/headless/selection-tool-types.ts +10 -0
- package/src/ui-tailwind/chrome/chrome-preset-model.ts +23 -2
- package/src/ui-tailwind/chrome/role-action-sets.ts +74 -0
- package/src/ui-tailwind/chrome/tw-detach-handle.tsx +147 -0
- package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +163 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +57 -92
- package/src/ui-tailwind/chrome/tw-selection-tool-placement.ts +149 -0
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +15 -4
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +274 -138
- package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +90 -0
- package/src/ui-tailwind/chrome-overlay/index.ts +22 -0
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +86 -0
- package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +178 -0
- package/src/ui-tailwind/chrome-overlay/tw-workspace-view-switcher.tsx +95 -0
- package/src/ui-tailwind/editor-surface/fast-text-edit-lane.ts +4 -0
- package/src/ui-tailwind/editor-surface/local-edit-session-state.ts +11 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +7 -1
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +12 -1
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +22 -12
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +3 -0
- package/src/ui-tailwind/index.ts +33 -0
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +2 -2
- package/src/ui-tailwind/review/tw-rail-card.tsx +150 -0
- package/src/ui-tailwind/review/tw-review-rail-footer.tsx +52 -0
- package/src/ui-tailwind/review/tw-review-rail.tsx +166 -11
- package/src/ui-tailwind/review/tw-workflow-tab.tsx +108 -0
- package/src/ui-tailwind/theme/editor-theme.css +498 -163
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +559 -0
- package/src/ui-tailwind/toolbar/tw-scope-posture-menu.tsx +182 -0
- package/src/ui-tailwind/toolbar/tw-shell-header.tsx +162 -0
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +69 -0
- 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
|
+
}
|