@beyondwork/docx-react-component 1.0.110 → 1.0.111
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 +3 -0
- package/src/model/layout/page-graph-types.ts +33 -0
- package/src/runtime/geometry/adjacent-geometry-intake.ts +373 -5
- package/src/runtime/geometry/caret-geometry.ts +219 -7
- package/src/runtime/geometry/geometry-index.ts +35 -10
- package/src/runtime/geometry/object-handles.ts +42 -1
- package/src/runtime/layout/index.ts +3 -0
- package/src/runtime/layout/inert-layout-facet.ts +13 -0
- package/src/runtime/layout/layout-engine-instance.ts +2 -0
- package/src/runtime/layout/layout-engine-version.ts +32 -2
- package/src/runtime/layout/layout-facet-types.ts +3 -0
- package/src/runtime/layout/page-graph.ts +81 -7
- package/src/runtime/layout/project-block-fragments.ts +144 -1
- package/src/runtime/layout/public-facet.ts +160 -0
- package/src/runtime/scopes/adjacent-geometry-evidence.ts +456 -0
- package/src/runtime/scopes/compile-scope-bundle.ts +8 -0
- package/src/runtime/scopes/evidence.ts +16 -0
- package/src/runtime/scopes/index.ts +13 -0
- package/src/runtime/scopes/semantic-scope-types.ts +67 -0
- package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +104 -0
- package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +50 -5
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +26 -0
- package/src/README.md +0 -85
- package/src/api/README.md +0 -26
- package/src/api/v3/README.md +0 -91
- package/src/component-inventory.md +0 -99
- package/src/core/README.md +0 -10
- package/src/core/commands/README.md +0 -3
- package/src/core/schema/README.md +0 -3
- package/src/core/selection/README.md +0 -3
- package/src/core/state/README.md +0 -3
- package/src/io/README.md +0 -10
- package/src/io/export/README.md +0 -3
- package/src/io/normalize/README.md +0 -3
- package/src/io/ooxml/README.md +0 -3
- package/src/io/opc/README.md +0 -3
- package/src/model/README.md +0 -3
- package/src/preservation/README.md +0 -3
- package/src/review/README.md +0 -16
- package/src/review/store/README.md +0 -3
- package/src/runtime/README.md +0 -3
- package/src/ui/README.md +0 -30
- package/src/ui/comments/README.md +0 -3
- package/src/ui/compatibility/README.md +0 -3
- package/src/ui/editor-surface/README.md +0 -3
- package/src/ui/review/README.md +0 -3
- package/src/ui/status/README.md +0 -3
- package/src/ui/theme/README.md +0 -3
- package/src/ui/toolbar/README.md +0 -3
- package/src/ui-tailwind/debug/README.md +0 -22
- package/src/validation/README.md +0 -3
|
@@ -215,6 +215,38 @@ export interface Layer11RenderCalibrationSummary {
|
|
|
215
215
|
route: "presentation-check" | "lower-layer-fact-required" | "insufficient-evidence";
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
export type Layer11AdjacentGeometryAxis = "numbering-marker" | "field-region";
|
|
219
|
+
|
|
220
|
+
export interface Layer11AdjacentGeometryIntakeLike {
|
|
221
|
+
readonly schemaVersion?: unknown;
|
|
222
|
+
readonly totals?: {
|
|
223
|
+
readonly numberingCompositorReadyRows?: unknown;
|
|
224
|
+
readonly fieldRegionCompositorReadyRows?: unknown;
|
|
225
|
+
};
|
|
226
|
+
readonly pageLocalNormalization?: {
|
|
227
|
+
readonly framePixelCoordinateSpace?: unknown;
|
|
228
|
+
readonly framePixelPrecision?: unknown;
|
|
229
|
+
readonly compositorReady?: unknown;
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export interface Layer11AdjacentGeometryAxisReadiness {
|
|
234
|
+
axis: Layer11AdjacentGeometryAxis;
|
|
235
|
+
compositorReadyRows: number;
|
|
236
|
+
assertionReady: boolean;
|
|
237
|
+
route: "presentation-consumer-ready" | "lower-layer-fact-required";
|
|
238
|
+
reason: string;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export interface Layer11AdjacentGeometryConsumerSummary {
|
|
242
|
+
schemaVersion: string | null;
|
|
243
|
+
framePixelCoordinateSpace: string | null;
|
|
244
|
+
framePixelPrecision: string | null;
|
|
245
|
+
assertionReadyCount: number;
|
|
246
|
+
lowerLayerFactRequiredCount: number;
|
|
247
|
+
entries: readonly Layer11AdjacentGeometryAxisReadiness[];
|
|
248
|
+
}
|
|
249
|
+
|
|
218
250
|
const PRIMARY_RENDER_EVIDENCE = new Set<Layer11RenderEvidenceKind>([
|
|
219
251
|
"word-oracle",
|
|
220
252
|
"render-word",
|
|
@@ -270,3 +302,75 @@ export function summarizeWordFirstRenderCalibration(
|
|
|
270
302
|
route: "presentation-check",
|
|
271
303
|
};
|
|
272
304
|
}
|
|
305
|
+
|
|
306
|
+
export function summarizeLayer11AdjacentGeometryConsumer(
|
|
307
|
+
intake: Layer11AdjacentGeometryIntakeLike,
|
|
308
|
+
): Layer11AdjacentGeometryConsumerSummary {
|
|
309
|
+
const schemaVersion =
|
|
310
|
+
typeof intake.schemaVersion === "string" ? intake.schemaVersion : null;
|
|
311
|
+
const normalization = intake.pageLocalNormalization;
|
|
312
|
+
const framePixelCoordinateSpace =
|
|
313
|
+
typeof normalization?.framePixelCoordinateSpace === "string"
|
|
314
|
+
? normalization.framePixelCoordinateSpace
|
|
315
|
+
: null;
|
|
316
|
+
const framePixelPrecision =
|
|
317
|
+
typeof normalization?.framePixelPrecision === "string"
|
|
318
|
+
? normalization.framePixelPrecision
|
|
319
|
+
: null;
|
|
320
|
+
const normalizationReady =
|
|
321
|
+
schemaVersion === "layer-05-adjacent-geometry-intake/v2" &&
|
|
322
|
+
framePixelCoordinateSpace === "frame-px" &&
|
|
323
|
+
normalization?.compositorReady === true;
|
|
324
|
+
const entries: Layer11AdjacentGeometryAxisReadiness[] = [
|
|
325
|
+
summarizeAdjacentGeometryAxis(
|
|
326
|
+
"numbering-marker",
|
|
327
|
+
numberValue(intake.totals?.numberingCompositorReadyRows),
|
|
328
|
+
normalizationReady,
|
|
329
|
+
),
|
|
330
|
+
summarizeAdjacentGeometryAxis(
|
|
331
|
+
"field-region",
|
|
332
|
+
numberValue(intake.totals?.fieldRegionCompositorReadyRows),
|
|
333
|
+
normalizationReady,
|
|
334
|
+
),
|
|
335
|
+
];
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
schemaVersion,
|
|
339
|
+
framePixelCoordinateSpace,
|
|
340
|
+
framePixelPrecision,
|
|
341
|
+
assertionReadyCount: entries.filter((entry) => entry.assertionReady).length,
|
|
342
|
+
lowerLayerFactRequiredCount: entries.filter((entry) => !entry.assertionReady)
|
|
343
|
+
.length,
|
|
344
|
+
entries,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function summarizeAdjacentGeometryAxis(
|
|
349
|
+
axis: Layer11AdjacentGeometryAxis,
|
|
350
|
+
compositorReadyRows: number,
|
|
351
|
+
normalizationReady: boolean,
|
|
352
|
+
): Layer11AdjacentGeometryAxisReadiness {
|
|
353
|
+
const assertionReady = normalizationReady && compositorReadyRows > 0;
|
|
354
|
+
if (assertionReady) {
|
|
355
|
+
return {
|
|
356
|
+
axis,
|
|
357
|
+
compositorReadyRows,
|
|
358
|
+
assertionReady,
|
|
359
|
+
route: "presentation-consumer-ready",
|
|
360
|
+
reason:
|
|
361
|
+
"L05 published frame-pixel rows with compositorReady provenance; L11 may consume this bounded subset without reconstructing geometry.",
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
axis,
|
|
366
|
+
compositorReadyRows,
|
|
367
|
+
assertionReady,
|
|
368
|
+
route: "lower-layer-fact-required",
|
|
369
|
+
reason:
|
|
370
|
+
"L11 must wait for L05 frame-pixel rows marked compositorReady before treating this axis as a presentation assertion.",
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function numberValue(value: unknown): number {
|
|
375
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
376
|
+
}
|
|
@@ -89,6 +89,15 @@ export interface PageBreakDecorationInput {
|
|
|
89
89
|
* heuristic.
|
|
90
90
|
*/
|
|
91
91
|
blockIndexRangeByPageIndex?: ReadonlyMap<number, { first: number; last: number }>;
|
|
92
|
+
/**
|
|
93
|
+
* Page indexes whose boundary/content widgets would be inserted at a document
|
|
94
|
+
* position inside a table. PM widgets inherit that table-cell containing
|
|
95
|
+
* block, so visible chrome there paints page numbers inside the cell and
|
|
96
|
+
* block-level invisible anchors can perturb table layout. Keep the diagnostic
|
|
97
|
+
* markers but suppress the visible spacer/seam and shrink anchors to inline
|
|
98
|
+
* zero-size markers.
|
|
99
|
+
*/
|
|
100
|
+
suppressChromeForPageIndex?: ReadonlySet<number>;
|
|
92
101
|
}
|
|
93
102
|
|
|
94
103
|
export function buildPageBreakDecorations(
|
|
@@ -111,7 +120,13 @@ export function buildPageBreakDecorations(
|
|
|
111
120
|
// (coord-11 §19) are emitted in a second pass at the end of this
|
|
112
121
|
// function.
|
|
113
122
|
if (graph.pages.length < 2) {
|
|
114
|
-
return buildPageAnchorDecorationsInto(
|
|
123
|
+
return buildPageAnchorDecorationsInto(
|
|
124
|
+
decorations,
|
|
125
|
+
graph,
|
|
126
|
+
posture,
|
|
127
|
+
runtimeToPmOffset,
|
|
128
|
+
input.suppressChromeForPageIndex,
|
|
129
|
+
);
|
|
115
130
|
}
|
|
116
131
|
|
|
117
132
|
for (let i = 1; i < graph.pages.length; i += 1) {
|
|
@@ -133,6 +148,8 @@ export function buildPageBreakDecorations(
|
|
|
133
148
|
input.headerPreviewByPageId?.get(next.pageId) ?? "";
|
|
134
149
|
|
|
135
150
|
const nextBlockRange = input.blockIndexRangeByPageIndex?.get(next.pageIndex);
|
|
151
|
+
const suppressVisibleChrome =
|
|
152
|
+
input.suppressChromeForPageIndex?.has(next.pageIndex) === true;
|
|
136
153
|
|
|
137
154
|
decorations.push(
|
|
138
155
|
Decoration.widget(
|
|
@@ -155,6 +172,7 @@ export function buildPageBreakDecorations(
|
|
|
155
172
|
nextHeaderPreview,
|
|
156
173
|
nextPageFirstBlockIndex: nextBlockRange?.first ?? -1,
|
|
157
174
|
nextPageLastBlockIndex: nextBlockRange?.last ?? -1,
|
|
175
|
+
suppressVisibleChrome,
|
|
158
176
|
}),
|
|
159
177
|
{
|
|
160
178
|
side: -1,
|
|
@@ -171,7 +189,13 @@ export function buildPageBreakDecorations(
|
|
|
171
189
|
),
|
|
172
190
|
);
|
|
173
191
|
}
|
|
174
|
-
return buildPageAnchorDecorationsInto(
|
|
192
|
+
return buildPageAnchorDecorationsInto(
|
|
193
|
+
decorations,
|
|
194
|
+
graph,
|
|
195
|
+
posture,
|
|
196
|
+
runtimeToPmOffset,
|
|
197
|
+
input.suppressChromeForPageIndex,
|
|
198
|
+
);
|
|
175
199
|
}
|
|
176
200
|
|
|
177
201
|
/**
|
|
@@ -197,6 +221,7 @@ function buildPageAnchorDecorationsInto(
|
|
|
197
221
|
graph: RuntimePageGraph,
|
|
198
222
|
posture: "page" | "canvas",
|
|
199
223
|
runtimeToPmOffset: ((runtimeOffset: number) => number | null) | undefined,
|
|
224
|
+
tableInteriorPageIndex?: ReadonlySet<number>,
|
|
200
225
|
): Decoration[] {
|
|
201
226
|
let contentPageOrdinal = 0;
|
|
202
227
|
for (const page of graph.pages) {
|
|
@@ -214,6 +239,7 @@ function buildPageAnchorDecorationsInto(
|
|
|
214
239
|
() => buildPageAnchorWidgetDom({
|
|
215
240
|
pageNumber: anchorPageNumber,
|
|
216
241
|
pageId: anchorPageId,
|
|
242
|
+
tableInterior: tableInteriorPageIndex?.has(page.pageIndex) === true,
|
|
217
243
|
}),
|
|
218
244
|
{
|
|
219
245
|
side: 1,
|
|
@@ -268,6 +294,7 @@ interface ChromeWidgetInput {
|
|
|
268
294
|
* -1 when block-index info was not supplied to the decoration builder.
|
|
269
295
|
*/
|
|
270
296
|
nextPageLastBlockIndex: number;
|
|
297
|
+
suppressVisibleChrome?: boolean;
|
|
271
298
|
}
|
|
272
299
|
|
|
273
300
|
// P14.c — cache the widget DOM by input identity. PM rebuilds the
|
|
@@ -299,6 +326,7 @@ function widgetCacheKey(input: ChromeWidgetInput): string {
|
|
|
299
326
|
input.nextHeaderPreview,
|
|
300
327
|
input.nextPageFirstBlockIndex,
|
|
301
328
|
input.nextPageLastBlockIndex,
|
|
329
|
+
input.suppressVisibleChrome ? "1" : "0",
|
|
302
330
|
].join("\x1f");
|
|
303
331
|
}
|
|
304
332
|
|
|
@@ -337,22 +365,29 @@ export function __resetPageBreakWidgetCache(): void {
|
|
|
337
365
|
*
|
|
338
366
|
* Emitted as a PM widget via `buildPageBreakDecorations` so it lives
|
|
339
367
|
* on the content layer (present under chrome=none) rather than on an
|
|
340
|
-
* absolute-positioned chrome overlay.
|
|
368
|
+
* absolute-positioned chrome overlay. When the page starts inside a table,
|
|
369
|
+
* the marker stays inline and zero-width so it does not create a block box
|
|
370
|
+
* inside the table cell.
|
|
341
371
|
*/
|
|
342
372
|
function buildPageAnchorWidgetDom(input: {
|
|
343
373
|
pageNumber: number;
|
|
344
374
|
pageId: string;
|
|
375
|
+
tableInterior?: boolean;
|
|
345
376
|
}): HTMLElement {
|
|
346
377
|
const root = document.createElement("span");
|
|
347
378
|
root.setAttribute("data-kind", "page-content-anchor");
|
|
348
379
|
root.setAttribute("data-page-content-wrapper", "");
|
|
349
380
|
root.setAttribute("data-page-number", String(input.pageNumber));
|
|
350
381
|
root.setAttribute("data-page-id", input.pageId);
|
|
382
|
+
if (input.tableInterior) {
|
|
383
|
+
root.setAttribute("data-page-anchor-suppressed", "table-interior");
|
|
384
|
+
}
|
|
351
385
|
root.setAttribute("aria-hidden", "true");
|
|
352
386
|
root.contentEditable = "false";
|
|
353
|
-
root.style.display = "block";
|
|
387
|
+
root.style.display = input.tableInterior ? "inline-block" : "block";
|
|
354
388
|
root.style.height = "0";
|
|
355
|
-
root.style.width = "100%";
|
|
389
|
+
root.style.width = input.tableInterior ? "0" : "100%";
|
|
390
|
+
root.style.overflow = "hidden";
|
|
356
391
|
root.style.userSelect = "none";
|
|
357
392
|
return root;
|
|
358
393
|
}
|
|
@@ -391,6 +426,16 @@ function buildChromeWidgetDomUncached(input: ChromeWidgetInput): HTMLElement {
|
|
|
391
426
|
root.style.width = "100%";
|
|
392
427
|
root.style.userSelect = "none";
|
|
393
428
|
|
|
429
|
+
if (input.suppressVisibleChrome) {
|
|
430
|
+
root.setAttribute("data-page-chrome-suppressed", "table-interior");
|
|
431
|
+
root.setAttribute("aria-hidden", "true");
|
|
432
|
+
root.style.height = "0";
|
|
433
|
+
root.style.width = "0";
|
|
434
|
+
root.style.overflow = "hidden";
|
|
435
|
+
root.style.pointerEvents = "none";
|
|
436
|
+
return root;
|
|
437
|
+
}
|
|
438
|
+
|
|
394
439
|
if (input.posture === "canvas") {
|
|
395
440
|
// Single dotted horizontal line with an unframed page-number label.
|
|
396
441
|
root.style.height = `${input.interGapPx + 1}px`;
|
|
@@ -185,6 +185,7 @@ function buildPageBreakDecorationsFromProps(
|
|
|
185
185
|
// carry `data-page-first-block-index` / `data-page-last-block-index`
|
|
186
186
|
// attributes needed by `useVisibleBlockRange`.
|
|
187
187
|
let blockIndexRangeByPageIndex: Map<number, { first: number; last: number }> | undefined;
|
|
188
|
+
let suppressChromeForPageIndex: Set<number> | undefined;
|
|
188
189
|
if (surfaceBlocks && surfaceBlocks.length > 0 && frame.pages.length > 0) {
|
|
189
190
|
blockIndexRangeByPageIndex = new Map();
|
|
190
191
|
for (let pi = 0; pi < frame.pages.length; pi++) {
|
|
@@ -194,6 +195,13 @@ function buildPageBreakDecorationsFromProps(
|
|
|
194
195
|
if (range) {
|
|
195
196
|
blockIndexRangeByPageIndex.set(page.page.pageIndex, range);
|
|
196
197
|
}
|
|
198
|
+
if (
|
|
199
|
+
pi > 0 &&
|
|
200
|
+
isRuntimeOffsetInsideTableBlock(surfaceBlocks, page.page.startOffset)
|
|
201
|
+
) {
|
|
202
|
+
if (!suppressChromeForPageIndex) suppressChromeForPageIndex = new Set();
|
|
203
|
+
suppressChromeForPageIndex.add(page.page.pageIndex);
|
|
204
|
+
}
|
|
197
205
|
}
|
|
198
206
|
}
|
|
199
207
|
|
|
@@ -207,9 +215,27 @@ function buildPageBreakDecorationsFromProps(
|
|
|
207
215
|
headerPreviewByPageId: previews?.headerPreviewByPageId,
|
|
208
216
|
footerPreviewByPageId: previews?.footerPreviewByPageId,
|
|
209
217
|
blockIndexRangeByPageIndex,
|
|
218
|
+
suppressChromeForPageIndex,
|
|
210
219
|
});
|
|
211
220
|
}
|
|
212
221
|
|
|
222
|
+
function isRuntimeOffsetInsideTableBlock(
|
|
223
|
+
blocks: readonly import("../../api/public-types.ts").SurfaceBlockSnapshot[],
|
|
224
|
+
offset: number,
|
|
225
|
+
): boolean {
|
|
226
|
+
for (const block of blocks) {
|
|
227
|
+
if (offset <= block.from || offset >= block.to) continue;
|
|
228
|
+
if (block.kind === "table") return true;
|
|
229
|
+
if (
|
|
230
|
+
block.kind === "sdt_block" &&
|
|
231
|
+
isRuntimeOffsetInsideTableBlock(block.children, offset)
|
|
232
|
+
) {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
|
|
213
239
|
function extractDecorations(
|
|
214
240
|
set: DecorationSet,
|
|
215
241
|
_doc: unknown,
|
package/src/README.md
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# Source Layout
|
|
2
|
-
|
|
3
|
-
The landed source tree is still organized around the active docx implementation, not around the future target office-wide layout.
|
|
4
|
-
|
|
5
|
-
## Current Landed Layout
|
|
6
|
-
|
|
7
|
-
```text
|
|
8
|
-
src/
|
|
9
|
-
api/
|
|
10
|
-
model/
|
|
11
|
-
core/
|
|
12
|
-
schema/
|
|
13
|
-
state/
|
|
14
|
-
commands/
|
|
15
|
-
selection/
|
|
16
|
-
review/
|
|
17
|
-
store/
|
|
18
|
-
io/
|
|
19
|
-
opc/
|
|
20
|
-
ooxml/
|
|
21
|
-
normalize/
|
|
22
|
-
export/
|
|
23
|
-
preservation/
|
|
24
|
-
validation/
|
|
25
|
-
runtime/
|
|
26
|
-
ui/
|
|
27
|
-
headless/ # Shared pure logic (framework-free)
|
|
28
|
-
shared/ # Shared utilities (revision-filters)
|
|
29
|
-
WordReviewEditor.tsx # Entry point
|
|
30
|
-
ui-tailwind/ # Default rendering (Tailwind + Radix + Lucide)
|
|
31
|
-
editor-surface/
|
|
32
|
-
toolbar/
|
|
33
|
-
review/
|
|
34
|
-
status/
|
|
35
|
-
chrome/
|
|
36
|
-
theme/
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
This is the truthful current layout for implementation work today.
|
|
40
|
-
|
|
41
|
-
## Target Broader Layout
|
|
42
|
-
|
|
43
|
-
The broader repo story now targets a future layout like this:
|
|
44
|
-
|
|
45
|
-
```text
|
|
46
|
-
src/
|
|
47
|
-
platform/
|
|
48
|
-
formats/
|
|
49
|
-
docx/
|
|
50
|
-
xlsx/
|
|
51
|
-
pdf/
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
That layout is not landed yet. Use it as a planning direction, not as evidence that the current code has already been reorganized.
|
|
55
|
-
|
|
56
|
-
## UI Layer Strategy
|
|
57
|
-
|
|
58
|
-
- `ui/headless/` — Pure logic: keyboard handling, decoration models, selection utilities. No React dependencies.
|
|
59
|
-
- `ui-tailwind/` — Default rendering: Tailwind CSS, Radix UI primitives, Lucide icons. All styling via CSS custom properties.
|
|
60
|
-
- `ui/WordReviewEditor.tsx` — Public entry point that bridges the runtime to the Tailwind layer.
|
|
61
|
-
|
|
62
|
-
Legacy inline-CSSProperties components have been removed. See `docs/reference/word-review-editor-frontend-architecture.md` for the canonical frontend architecture.
|
|
63
|
-
|
|
64
|
-
Keep business rules close to the subsystem that owns them. Do not centralize unrelated logic into generic utility layers.
|
|
65
|
-
|
|
66
|
-
Ownership rules:
|
|
67
|
-
|
|
68
|
-
- `api` exposes public contracts only.
|
|
69
|
-
- `model`, `core`, `review`, `io`, `preservation`, and `validation` should remain React-free when possible.
|
|
70
|
-
- `runtime` is the only mutation boundary the UI calls into.
|
|
71
|
-
- `ui` consumes runtime contracts and design tokens; it does not own canonical document truth.
|
|
72
|
-
|
|
73
|
-
## Broader Repo Direction
|
|
74
|
-
|
|
75
|
-
As the repo broadens:
|
|
76
|
-
|
|
77
|
-
- shared package and preservation concerns should move toward `src/platform/`
|
|
78
|
-
- current docx runtime code remains the active implementation track until a deliberate source move lands
|
|
79
|
-
- future xlsx work should gain its own source area rather than widening docx-specific modules by implication
|
|
80
|
-
- future pdf work should remain separate from the first OOXML platform layer unless architecture decisions intentionally broaden it
|
|
81
|
-
|
|
82
|
-
Wave 0 inventory references:
|
|
83
|
-
|
|
84
|
-
- `component-inventory.md`
|
|
85
|
-
Wave-owned inventory for the major editor subsystems, their boundaries, and the promotion target for this scaffold phase.
|
package/src/api/README.md
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# API
|
|
2
|
-
|
|
3
|
-
Public TypeScript contracts for `WordReviewEditor` belong here.
|
|
4
|
-
|
|
5
|
-
This layer should expose:
|
|
6
|
-
|
|
7
|
-
- component props and ref types
|
|
8
|
-
- session-state and snapshot compatibility types
|
|
9
|
-
- host adapter and datastore adapter interfaces
|
|
10
|
-
- runtime-derived position and selection projection types used by the public API
|
|
11
|
-
- discriminated event unions
|
|
12
|
-
- warning and error payloads
|
|
13
|
-
- persisted snapshot contracts
|
|
14
|
-
- export and compatibility report types
|
|
15
|
-
|
|
16
|
-
Do not place runtime logic here.
|
|
17
|
-
|
|
18
|
-
Frozen naming and boundary rules:
|
|
19
|
-
|
|
20
|
-
- the shipped component name is `WordReviewEditor`
|
|
21
|
-
- `DocumentRuntime` is an internal runtime contract, not a public React component name
|
|
22
|
-
- `EditorSessionState` is the canonical host-facing live-session contract
|
|
23
|
-
- `PersistedEditorSnapshot` is the legacy/store compatibility envelope
|
|
24
|
-
- `EditorHostAdapter` is the preferred persistence boundary; `EditorDatastoreAdapter` remains the snapshot-compatible bridge
|
|
25
|
-
- public range references are canonical-position projections, not DOM-path handles
|
|
26
|
-
- render snapshots stay in `src/runtime`; `src/api` only exposes persisted host-facing snapshot types
|
package/src/api/v3/README.md
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
# `src/api/v3/` — End-state public API surface
|
|
2
|
-
|
|
3
|
-
> **@endStateApi v3** — the canonical Beyond Work build-team contract.
|
|
4
|
-
> Do NOT treat this as a rename of `WordReviewEditorRef`. It is a
|
|
5
|
-
> separate, additive API that mirrors the target architecture in
|
|
6
|
-
> [`docs/architecture/00-overview.md`](../../../docs/architecture/00-overview.md)
|
|
7
|
-
> (Runtime API + AI API split per §9 + §10).
|
|
8
|
-
|
|
9
|
-
## What this directory is
|
|
10
|
-
|
|
11
|
-
- The shape every new Beyond Work consumer builds against, today.
|
|
12
|
-
- Each function carries `ApiV3FnMetadata` declaring
|
|
13
|
-
`status: "mock" | "partial" | "live-with-adapter" | "live"`, its
|
|
14
|
-
target layer, UX intent, and agent metadata.
|
|
15
|
-
- Every UX-visible function emits one `UxResponse` event on the
|
|
16
|
-
`api` telemetry channel per invocation so the future debug UX
|
|
17
|
-
(Phase Q) and agent-facing audit can observe it identically in
|
|
18
|
-
mock and live modes.
|
|
19
|
-
|
|
20
|
-
### Four-status taxonomy (Phase P')
|
|
21
|
-
|
|
22
|
-
| Status | Meaning |
|
|
23
|
-
|---|---|
|
|
24
|
-
| `mock` | No live path attempted; returns a `mockPayload(...)` / `mockArray(...)` carrying `__mock: true`. |
|
|
25
|
-
| `partial` | Live path attempted; falls back to a mock-shaped payload when the runtime cannot satisfy it. `mockShape` is required. |
|
|
26
|
-
| `live-with-adapter` | Delegates to a real runtime/facet method through a nontrivial adapter or composition layer. `mockShape` optional. |
|
|
27
|
-
| `live` | Direct delegation to a single runtime/facet method. `mockShape` is forbidden. |
|
|
28
|
-
|
|
29
|
-
The current distribution (as reported by the authoritative
|
|
30
|
-
`test/runtime/debug/api-contract.test.ts` runner) is `9 live / 18
|
|
31
|
-
live-with-adapter / 1 partial / 7 mock / 35 total`. See the Changelog
|
|
32
|
-
in [`docs/reference/public-api.md`](../../../docs/reference/public-api.md)
|
|
33
|
-
for the latest entry.
|
|
34
|
-
|
|
35
|
-
## What this directory is NOT
|
|
36
|
-
|
|
37
|
-
- NOT a re-export of the existing `WordReviewEditorRef`. The two CI
|
|
38
|
-
guards `scripts/ci-check-api-v3-no-ref-reexport.mjs` and
|
|
39
|
-
`scripts/ci-check-api-v3-metadata.mjs` enforce this. Adding a
|
|
40
|
-
convenience re-export of a ref method trips the guard.
|
|
41
|
-
- NOT the place for internal runtime refactoring (`src/session/`,
|
|
42
|
-
`src/runtime/formatting/`, etc.). That work lands behind this
|
|
43
|
-
surface — v3 stays stable while internals move.
|
|
44
|
-
- NOT a sandbox. `status: "live"` functions MUST parity-match the
|
|
45
|
-
underlying runtime behavior (asserted in per-family tests).
|
|
46
|
-
|
|
47
|
-
## Consumers
|
|
48
|
-
|
|
49
|
-
- **Beyond Work build team** (canonical). Import via
|
|
50
|
-
`import { createApiV3 } from "@beyondwork/docx-react-component/api/v3"`.
|
|
51
|
-
- **Debug UX (Phase Q)**. Uses `UxResponse` events to render mock-or-live
|
|
52
|
-
visualizations.
|
|
53
|
-
- **Phase D runners** (Layer-level validators). Use `createApiV3` as the
|
|
54
|
-
single entry point so runners don't depend on internal runtime shape.
|
|
55
|
-
|
|
56
|
-
## Graduating a function's status
|
|
57
|
-
|
|
58
|
-
The intended progression is `mock → partial → live-with-adapter → live`.
|
|
59
|
-
Skipping a stage is allowed when justified, but each flip requires
|
|
60
|
-
evidence:
|
|
61
|
-
|
|
62
|
-
1. Implement the live path (delegate to the appropriate runtime
|
|
63
|
-
facet or layer extractor). For `live-with-adapter`, write the
|
|
64
|
-
adapter/composition in the family file; for `live`, the body
|
|
65
|
-
must be a single delegation call.
|
|
66
|
-
2. Add a validator log entry under `services/debug/docs/` with the
|
|
67
|
-
new evidence.
|
|
68
|
-
3. Update the function's `<fnName>Metadata.liveEvidence` field to
|
|
69
|
-
point at the log + the commit SHA; update `mockShape` per the
|
|
70
|
-
taxonomy rules above (required for `mock` and `partial`, optional
|
|
71
|
-
for `live-with-adapter`, forbidden for `live`).
|
|
72
|
-
4. Update the per-family test's parity assertion and the matching
|
|
73
|
-
inline `// @endStateApi — <status>.` comment on the function
|
|
74
|
-
body so the status-drift canary stays green.
|
|
75
|
-
|
|
76
|
-
Silent flips — changing `status` without updating `liveEvidence` or
|
|
77
|
-
the inline annotation — are a review-time reject.
|
|
78
|
-
|
|
79
|
-
## Canonical reference doc
|
|
80
|
-
|
|
81
|
-
[`docs/reference/public-api.md`](../../../docs/reference/public-api.md).
|
|
82
|
-
That's the URL the BW team bookmarks. This README is the in-repo
|
|
83
|
-
maintainer-facing companion.
|
|
84
|
-
|
|
85
|
-
## Phase Q (debug UX refactor) dependency
|
|
86
|
-
|
|
87
|
-
Phase Q ships a runtime-embedded debug UX (`src/ui-tailwind/debug/**`)
|
|
88
|
-
that subscribes to the `UxResponse` event stream emitted from this
|
|
89
|
-
directory. Phase Q does NOT re-import the existing harness debug
|
|
90
|
-
panel — it renders against `UxResponse` + `runtime.debug.getSnapshot`
|
|
91
|
-
directly. See the Phase P plan for the full Phase Q spec.
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
# Component Inventory
|
|
2
|
-
|
|
3
|
-
This file is the source-owned inventory for the major subsystems in the active docx implementation.
|
|
4
|
-
|
|
5
|
-
Wave 0 established the scaffold and ownership boundaries. Wave 1 froze the contract surface for the editor-side components listed below. That promotion does not claim implementation completeness; it means the subsystem boundaries, contract artifacts, and proof expectations are now explicit enough for later docx waves to build against without reopening the architecture.
|
|
6
|
-
|
|
7
|
-
This inventory does not yet track:
|
|
8
|
-
|
|
9
|
-
- shared OOXML platform extraction as a separate source area
|
|
10
|
-
- a future xlsx runtime inventory
|
|
11
|
-
- future pdf work
|
|
12
|
-
|
|
13
|
-
## Inventory Table
|
|
14
|
-
|
|
15
|
-
| Component | Current target | Primary paths | Scope in this repo phase | Contract and proof surfaces |
|
|
16
|
-
| --- | --- | --- | --- | --- |
|
|
17
|
-
| `canonical-document-model` | `contract-frozen` | `src/model/`, `src/api/` | Versioned canonical envelope, persisted snapshot shape, migration boundary, document metadata ownership | `docs/plans/waves/specs/wave-1-component-boundaries.md`, `docs/plans/waves/specs/wave-1-runtime-contracts.md`, schema docs, canonical JSON examples, model tests |
|
|
18
|
-
| `command-and-transaction-runtime` | `contract-frozen` | `src/core/`, `src/runtime/` | Deterministic commands, transactions, mapping, selection semantics, runtime mutation boundary | `docs/plans/waves/specs/wave-1-component-boundaries.md`, `docs/plans/waves/specs/wave-1-runtime-contracts.md`, command tests, mapping tests, state-transition proofs |
|
|
19
|
-
| `comments-and-anchor-mapping` | `contract-frozen` | `src/review/`, `src/core/selection/` | Comment threads, anchor remapping, review selectors, edit-safe anchor mapping policy | `docs/plans/waves/specs/wave-1-review-and-ui-contracts.md`, `docs/plans/waves/specs/wave-1-runtime-contracts.md`, anchor remap tests, threaded comment fixtures, review model docs |
|
|
20
|
-
| `tracked-changes-and-review-actions` | `contract-frozen` | `src/review/`, `src/ui/review/` | Revision records, display modes, accept/reject boundaries, review-oriented commands | `docs/plans/waves/specs/wave-1-review-and-ui-contracts.md`, revision fixtures, accept/reject tests, review-state mapping tests |
|
|
21
|
-
| `docx-import-and-normalization` | `contract-frozen` | `src/io/opc/`, `src/io/ooxml/`, `src/io/normalize/` | OPC opening, WordprocessingML parsing, normalization into canonical state, support classification inputs | `docs/plans/waves/specs/wave-1-ooxml-contracts.md`, `.docx` import fixtures, normalization tests, supported-feature mapping docs |
|
|
22
|
-
| `docx-export-and-serialization` | `contract-frozen` | `src/io/export/`, `src/io/opc/` | Regenerated OOXML parts, package writing, relationship integrity, supported-feature serialization | `docs/plans/waves/specs/wave-1-ooxml-contracts.md`, round-trip fixtures, export validation tests, serializer docs |
|
|
23
|
-
| `unsupported-ooxml-preservation` | `contract-frozen` | `src/preservation/`, `src/io/` | Opaque fragment capture, untouched package-part retention, reattachment policy, locked editing regions | `docs/plans/waves/specs/wave-1-ooxml-contracts.md`, preservation fixtures, fragment retention tests, export reattachment tests |
|
|
24
|
-
| `compatibility-and-word-validation` | `contract-frozen` | `src/validation/`, `src/ui/compatibility/`, `services/openxml-validator/` | Editor-side compatibility reporting, support classification, export-risk reporting, validator-service integration boundary | `docs/plans/waves/specs/wave-1-ooxml-contracts.md`, `docs/plans/waves/specs/wave-1-review-and-ui-contracts.md`, feature matrix, compatibility tests, Word reopen evidence |
|
|
25
|
-
| `react-editor-ui` | `contract-frozen` | `src/ui-tailwind/`, `src/ui/headless/`, `src/runtime/`, `src/api/` | Tailwind-based React shell with Radix UI primitives. Headless logic in `src/ui/headless/`. Legacy inline-CSSProperties components removed. | `docs/plans/waves/specs/wave-1-review-and-ui-contracts.md`, component tests, selection behavior checks, review-surface checks, token and theme checks |
|
|
26
|
-
|
|
27
|
-
## Boundary Notes
|
|
28
|
-
|
|
29
|
-
### Canonical data stays outside React
|
|
30
|
-
|
|
31
|
-
- `src/model/`, `src/core/`, `src/review/`, `src/io/`, `src/preservation/`, and `src/validation/` should remain framework-light and serializable.
|
|
32
|
-
- The DOM is a rendering target, not canonical state.
|
|
33
|
-
|
|
34
|
-
### Runtime is the mutation choke point
|
|
35
|
-
|
|
36
|
-
- UI surfaces call into `src/runtime/`.
|
|
37
|
-
- Commands and transactions are owned by `src/core/`.
|
|
38
|
-
- Review actions remain explicit state transitions rather than implicit UI side effects.
|
|
39
|
-
|
|
40
|
-
### DOCX handling is package-first
|
|
41
|
-
|
|
42
|
-
- `src/io/opc/` owns ZIP/package and relationship handling.
|
|
43
|
-
- `src/io/ooxml/` owns WordprocessingML parsing.
|
|
44
|
-
- `src/preservation/` owns unsupported-content retention and untouched package parts.
|
|
45
|
-
|
|
46
|
-
### Broader repo work is planned, not landed
|
|
47
|
-
|
|
48
|
-
- The repo now documents a target source layout under `src/platform/` and `src/formats/*`, but this file still inventories the landed docx-first tree.
|
|
49
|
-
- Shared platform and xlsx architecture are currently defined in docs, not in source ownership tables here.
|
|
50
|
-
|
|
51
|
-
### Review semantics are first-class
|
|
52
|
-
|
|
53
|
-
- Comments and tracked changes are not annotations layered loosely on top of text rendering.
|
|
54
|
-
- Anchors and revisions must stay mappable through edits and export.
|
|
55
|
-
|
|
56
|
-
## Service-Backed Wave 0 Components
|
|
57
|
-
|
|
58
|
-
The following components have a higher Wave 0 target because they already have repo-landed scaffolds:
|
|
59
|
-
|
|
60
|
-
| Component | Wave 0 target | Primary paths | Landed scaffold evidence |
|
|
61
|
-
| --- | --- | --- | --- |
|
|
62
|
-
| `railway-test-harness` | `repo-landed` | `services/react-word-editor/` | Next.js service package, layout shell, harness dashboard stub, Dockerfile, Railway-oriented README |
|
|
63
|
-
| `openxml-sdk-validator-service` | `repo-landed` | `services/openxml-validator/` | .NET 8 minimal API, `GET /health`, `POST /validate`, Dockerfile, SDK-backed validation README |
|
|
64
|
-
|
|
65
|
-
## Out Of Scope For Wave 0
|
|
66
|
-
|
|
67
|
-
- No canonical schema implementation yet
|
|
68
|
-
- No command engine implementation yet
|
|
69
|
-
- No comment or revision persistence yet
|
|
70
|
-
- No OOXML transformer or serializer yet
|
|
71
|
-
- No production `WordReviewEditor` React runtime yet
|
|
72
|
-
- No live Railway persistence or private networking integration yet
|
|
73
|
-
|
|
74
|
-
Those surfaces move in later waves. This inventory only establishes where they belong and what proof each promotion will require.
|
|
75
|
-
|
|
76
|
-
## Contract Freeze References
|
|
77
|
-
|
|
78
|
-
Wave 1 froze the owned architecture in the following durable spec set:
|
|
79
|
-
|
|
80
|
-
- `docs/plans/waves/design/wave-1-a1.md`
|
|
81
|
-
- `docs/plans/waves/specs/wave-1-component-boundaries.md`
|
|
82
|
-
- `docs/plans/waves/specs/wave-1-runtime-contracts.md`
|
|
83
|
-
- `docs/plans/waves/specs/wave-1-ooxml-contracts.md`
|
|
84
|
-
- `docs/plans/waves/specs/wave-1-review-and-ui-contracts.md`
|
|
85
|
-
|
|
86
|
-
## Future Source Direction
|
|
87
|
-
|
|
88
|
-
Target broader layout:
|
|
89
|
-
|
|
90
|
-
```text
|
|
91
|
-
src/
|
|
92
|
-
platform/
|
|
93
|
-
formats/
|
|
94
|
-
docx/
|
|
95
|
-
xlsx/
|
|
96
|
-
pdf/
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
That future layout is a planning direction only. Do not treat it as a landed source move until the actual implementation is reorganized.
|
package/src/core/README.md
DELETED
package/src/core/state/README.md
DELETED
package/src/io/README.md
DELETED
package/src/io/export/README.md
DELETED
package/src/io/ooxml/README.md
DELETED
package/src/io/opc/README.md
DELETED