@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.
Files changed (52) hide show
  1. package/package.json +1 -1
  2. package/src/api/public-types.ts +3 -0
  3. package/src/model/layout/page-graph-types.ts +33 -0
  4. package/src/runtime/geometry/adjacent-geometry-intake.ts +373 -5
  5. package/src/runtime/geometry/caret-geometry.ts +219 -7
  6. package/src/runtime/geometry/geometry-index.ts +35 -10
  7. package/src/runtime/geometry/object-handles.ts +42 -1
  8. package/src/runtime/layout/index.ts +3 -0
  9. package/src/runtime/layout/inert-layout-facet.ts +13 -0
  10. package/src/runtime/layout/layout-engine-instance.ts +2 -0
  11. package/src/runtime/layout/layout-engine-version.ts +32 -2
  12. package/src/runtime/layout/layout-facet-types.ts +3 -0
  13. package/src/runtime/layout/page-graph.ts +81 -7
  14. package/src/runtime/layout/project-block-fragments.ts +144 -1
  15. package/src/runtime/layout/public-facet.ts +160 -0
  16. package/src/runtime/scopes/adjacent-geometry-evidence.ts +456 -0
  17. package/src/runtime/scopes/compile-scope-bundle.ts +8 -0
  18. package/src/runtime/scopes/evidence.ts +16 -0
  19. package/src/runtime/scopes/index.ts +13 -0
  20. package/src/runtime/scopes/semantic-scope-types.ts +67 -0
  21. package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +104 -0
  22. package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +50 -5
  23. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +26 -0
  24. package/src/README.md +0 -85
  25. package/src/api/README.md +0 -26
  26. package/src/api/v3/README.md +0 -91
  27. package/src/component-inventory.md +0 -99
  28. package/src/core/README.md +0 -10
  29. package/src/core/commands/README.md +0 -3
  30. package/src/core/schema/README.md +0 -3
  31. package/src/core/selection/README.md +0 -3
  32. package/src/core/state/README.md +0 -3
  33. package/src/io/README.md +0 -10
  34. package/src/io/export/README.md +0 -3
  35. package/src/io/normalize/README.md +0 -3
  36. package/src/io/ooxml/README.md +0 -3
  37. package/src/io/opc/README.md +0 -3
  38. package/src/model/README.md +0 -3
  39. package/src/preservation/README.md +0 -3
  40. package/src/review/README.md +0 -16
  41. package/src/review/store/README.md +0 -3
  42. package/src/runtime/README.md +0 -3
  43. package/src/ui/README.md +0 -30
  44. package/src/ui/comments/README.md +0 -3
  45. package/src/ui/compatibility/README.md +0 -3
  46. package/src/ui/editor-surface/README.md +0 -3
  47. package/src/ui/review/README.md +0 -3
  48. package/src/ui/status/README.md +0 -3
  49. package/src/ui/theme/README.md +0 -3
  50. package/src/ui/toolbar/README.md +0 -3
  51. package/src/ui-tailwind/debug/README.md +0 -22
  52. 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(decorations, graph, posture, runtimeToPmOffset);
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(decorations, graph, posture, runtimeToPmOffset);
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
@@ -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.
@@ -1,10 +0,0 @@
1
- # Core
2
-
3
- Commands, transactions, selection, mapping, schema, and deterministic state transitions belong here.
4
-
5
- Key subdirectories:
6
-
7
- - `schema/`
8
- - `state/`
9
- - `commands/`
10
- - `selection/`
@@ -1,3 +0,0 @@
1
- # Core Commands
2
-
3
- Command handlers, transaction builders, and editor mutation policies belong here.
@@ -1,3 +0,0 @@
1
- # Core Schema
2
-
3
- Canonical node, mark, and attribute definitions belong here.
@@ -1,3 +0,0 @@
1
- # Core Selection
2
-
3
- Selections, ranges, position mapping, and anchor remapping helpers belong here.
@@ -1,3 +0,0 @@
1
- # Core State
2
-
3
- Canonical state containers, selectors, and structural invariants belong here.
package/src/io/README.md DELETED
@@ -1,10 +0,0 @@
1
- # IO
2
-
3
- OPC package handling, OOXML parsing, normalization, and serialization belong here.
4
-
5
- Key subdirectories:
6
-
7
- - `opc/`
8
- - `ooxml/`
9
- - `normalize/`
10
- - `export/`
@@ -1,3 +0,0 @@
1
- # IO Export
2
-
3
- OOXML serialization, part regeneration, and package writing helpers belong here.
@@ -1,3 +0,0 @@
1
- # IO Normalize
2
-
3
- Normalization from OOXML structures into canonical state belongs here.
@@ -1,3 +0,0 @@
1
- # IO OOXML
2
-
3
- WordprocessingML parsing and intermediate OOXML structures belong here.
@@ -1,3 +0,0 @@
1
- # IO OPC
2
-
3
- Open Packaging Conventions package reading, part enumeration, and relationship handling belong here.