@beyondwork/docx-react-component 1.0.105 → 1.0.108

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 (193) hide show
  1. package/package.json +19 -5
  2. package/src/api/geometry-overlay-rects.ts +5 -0
  3. package/src/api/package-version.ts +1 -1
  4. package/src/api/page-anchor-id.ts +5 -0
  5. package/src/api/public-types.ts +16 -9
  6. package/src/api/table-node-specs.ts +6 -0
  7. package/src/api/v3/_create.ts +10 -2
  8. package/src/api/v3/_page-anchor-id.ts +52 -0
  9. package/src/api/v3/_runtime-handle.ts +92 -1
  10. package/src/api/v3/ai/_audit-reference.ts +28 -0
  11. package/src/api/v3/ai/_audit-time.ts +5 -0
  12. package/src/api/v3/ai/_pe2-evidence.ts +310 -6
  13. package/src/api/v3/ai/attach.ts +29 -4
  14. package/src/api/v3/ai/bundle.ts +6 -2
  15. package/src/api/v3/ai/inspect.ts +6 -2
  16. package/src/api/v3/ai/replacement.ts +112 -18
  17. package/src/api/v3/ai/resolve.ts +2 -2
  18. package/src/api/v3/ai/review.ts +177 -3
  19. package/src/api/v3/index.ts +8 -0
  20. package/src/api/v3/runtime/collab.ts +462 -0
  21. package/src/api/v3/runtime/document.ts +503 -20
  22. package/src/api/v3/runtime/geometry.ts +97 -0
  23. package/src/api/v3/runtime/layout.ts +744 -0
  24. package/src/api/v3/runtime/perf-probe.ts +14 -0
  25. package/src/api/v3/runtime/viewport.ts +9 -8
  26. package/src/api/v3/ui/_types.ts +202 -55
  27. package/src/api/v3/ui/chrome-preset-model.ts +5 -5
  28. package/src/api/v3/ui/debug.ts +115 -2
  29. package/src/api/v3/ui/index.ts +17 -0
  30. package/src/api/v3/ui/overlays.ts +0 -8
  31. package/src/api/v3/ui/surface.ts +56 -0
  32. package/src/api/v3/ui/viewport.ts +119 -9
  33. package/src/core/commands/image-commands.ts +1 -0
  34. package/src/core/commands/index.ts +6 -0
  35. package/src/core/schema/text-schema.ts +43 -5
  36. package/src/core/selection/mapping.ts +8 -1
  37. package/src/core/selection/review-anchors.ts +5 -1
  38. package/src/core/state/text-transaction.ts +8 -2
  39. package/src/io/export/serialize-revisions.ts +149 -1
  40. package/src/io/normalize/normalize-text.ts +6 -0
  41. package/src/io/ooxml/parse-bookmark-references.ts +55 -0
  42. package/src/io/ooxml/parse-fields.ts +24 -2
  43. package/src/io/ooxml/parse-headers-footers.ts +38 -5
  44. package/src/io/ooxml/parse-main-document.ts +153 -9
  45. package/src/io/ooxml/parse-numbering.ts +20 -0
  46. package/src/io/ooxml/parse-revisions.ts +19 -8
  47. package/src/io/opc/package-reader.ts +98 -8
  48. package/src/model/anchor.ts +4 -3
  49. package/src/model/canonical-document.ts +220 -2
  50. package/src/model/canonical-hash.ts +221 -0
  51. package/src/model/canonical-layout-inputs.ts +245 -6
  52. package/src/model/layout/index.ts +1 -0
  53. package/src/model/layout/page-graph-types.ts +147 -1
  54. package/src/model/review/revision-types.ts +14 -3
  55. package/src/preservation/store.ts +20 -4
  56. package/src/review/README.md +1 -1
  57. package/src/review/store/revision-actions.ts +14 -2
  58. package/src/runtime/collab/event-types.ts +67 -1
  59. package/src/runtime/collab/runtime-collab-sync.ts +177 -5
  60. package/src/runtime/diagnostics/layout-guard-warning.ts +18 -0
  61. package/src/runtime/document-heading-outline.ts +147 -0
  62. package/src/runtime/document-navigation.ts +8 -243
  63. package/src/runtime/document-runtime.ts +279 -115
  64. package/src/runtime/edit-dispatch/dispatch-text-command.ts +11 -0
  65. package/src/runtime/formatting/layout-inputs.ts +38 -5
  66. package/src/runtime/formatting/numbering/geometry.ts +28 -2
  67. package/src/runtime/geometry/adjacent-geometry-intake.ts +835 -0
  68. package/src/runtime/geometry/caret-geometry.ts +5 -6
  69. package/src/runtime/geometry/geometry-facet.ts +60 -10
  70. package/src/runtime/geometry/geometry-index.ts +661 -16
  71. package/src/runtime/geometry/geometry-types.ts +59 -0
  72. package/src/runtime/geometry/hit-test.ts +11 -1
  73. package/src/runtime/geometry/overlay-rects.ts +5 -3
  74. package/src/runtime/geometry/project-anchors.ts +1 -1
  75. package/src/runtime/geometry/word-layout-v2-line-intake.ts +323 -0
  76. package/src/runtime/layout/index.ts +6 -0
  77. package/src/runtime/layout/layout-engine-instance.ts +6 -1
  78. package/src/runtime/layout/layout-engine-version.ts +188 -16
  79. package/src/runtime/layout/layout-facet-types.ts +6 -0
  80. package/src/runtime/layout/page-graph.ts +23 -4
  81. package/src/runtime/layout/paginated-layout-engine.ts +149 -15
  82. package/src/runtime/layout/project-block-fragments.ts +351 -14
  83. package/src/runtime/layout/public-facet.ts +162 -24
  84. package/src/runtime/layout/table-row-continuation-contract.ts +107 -0
  85. package/src/runtime/layout/table-row-split.ts +92 -35
  86. package/src/runtime/prerender/cache-envelope.ts +2 -2
  87. package/src/runtime/prerender/cache-key.ts +5 -4
  88. package/src/runtime/prerender/customxml-cache.ts +0 -1
  89. package/src/runtime/render/render-kernel.ts +1 -1
  90. package/src/runtime/revision-runtime.ts +112 -10
  91. package/src/runtime/scopes/_scope-dependencies.ts +1 -0
  92. package/src/runtime/scopes/action-validation.ts +22 -2
  93. package/src/runtime/scopes/capabilities.ts +316 -0
  94. package/src/runtime/scopes/compile-scope-bundle.ts +14 -0
  95. package/src/runtime/scopes/compiler-service.ts +108 -4
  96. package/src/runtime/scopes/content-control-evidence.ts +79 -0
  97. package/src/runtime/scopes/create-issue.ts +5 -5
  98. package/src/runtime/scopes/evidence.ts +91 -0
  99. package/src/runtime/scopes/formatting/apply.ts +2 -0
  100. package/src/runtime/scopes/geometry-evidence.ts +130 -0
  101. package/src/runtime/scopes/index.ts +54 -0
  102. package/src/runtime/scopes/issue-lifecycle.ts +224 -0
  103. package/src/runtime/scopes/layout-evidence.ts +374 -0
  104. package/src/runtime/scopes/multi-paragraph-refusal.ts +37 -0
  105. package/src/runtime/scopes/preservation-boundary.ts +7 -1
  106. package/src/runtime/scopes/replacement/apply.ts +97 -34
  107. package/src/runtime/scopes/scope-kinds/paragraph.ts +108 -12
  108. package/src/runtime/scopes/semantic-scope-types.ts +242 -3
  109. package/src/runtime/scopes/visualization.ts +28 -0
  110. package/src/runtime/surface-projection.ts +44 -5
  111. package/src/runtime/telemetry/perf-probe.ts +216 -0
  112. package/src/runtime/virtualized-rendering.ts +36 -1
  113. package/src/runtime/workflow/ai-issue-lifecycle.ts +253 -0
  114. package/src/runtime/workflow/coordinator.ts +39 -11
  115. package/src/runtime/workflow/derived-scope-resolver.ts +63 -9
  116. package/src/runtime/workflow/index.ts +4 -0
  117. package/src/runtime/workflow/overlay-lane-types.ts +58 -0
  118. package/src/runtime/workflow/overlay-lanes.ts +386 -0
  119. package/src/runtime/workflow/overlay-store.ts +2 -2
  120. package/src/runtime/workflow/redline-posture-calibration.ts +257 -0
  121. package/src/runtime/workflow/word-field-matrix-calibration.ts +231 -0
  122. package/src/session/_sync-legacy.ts +17 -27
  123. package/src/session/import/loader.ts +6 -4
  124. package/src/session/import/source-package-evidence.ts +186 -2
  125. package/src/session/index.ts +5 -6
  126. package/src/session/session.ts +30 -56
  127. package/src/session/types.ts +8 -13
  128. package/src/shell/session-bootstrap.ts +155 -81
  129. package/src/ui/WordReviewEditor.tsx +520 -12
  130. package/src/ui/editor-shell-view.tsx +14 -4
  131. package/src/ui/editor-surface-controller.tsx +5 -3
  132. package/src/ui/headless/selection-tool-resolver.ts +1 -2
  133. package/src/ui/presence-overlay-lane.ts +130 -0
  134. package/src/ui/ui-controller-factory.ts +17 -0
  135. package/src/ui-tailwind/chrome/build-context-menu-entries.ts +5 -1
  136. package/src/ui-tailwind/chrome/editor-action-registry.ts +105 -5
  137. package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +7 -0
  138. package/src/ui-tailwind/chrome/layer-debug-contracts.ts +208 -0
  139. package/src/ui-tailwind/chrome/resolve-target-kind.ts +13 -0
  140. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +11 -3
  141. package/src/ui-tailwind/chrome/tw-command-palette.tsx +36 -6
  142. package/src/ui-tailwind/chrome/tw-context-menu.tsx +6 -1
  143. package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +42 -109
  144. package/src/ui-tailwind/chrome/tw-inline-find-bar.tsx +26 -6
  145. package/src/ui-tailwind/chrome/tw-navigation-command-bar.tsx +328 -0
  146. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +8 -4
  147. package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +129 -1
  148. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +19 -5
  149. package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
  150. package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +28 -12
  151. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +30 -3
  152. package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +116 -10
  153. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +223 -94
  154. package/src/ui-tailwind/chrome-overlay/tw-presence-overlay-lane.tsx +157 -0
  155. package/src/ui-tailwind/chrome-overlay/tw-review-overlay-lane-markers.tsx +259 -0
  156. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +5 -2
  157. package/src/ui-tailwind/chrome-overlay/tw-substrate-overlay-lanes.tsx +314 -0
  158. package/src/ui-tailwind/debug/README.md +4 -1
  159. package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +272 -0
  160. package/src/ui-tailwind/debug/layer11-word-field-matrix-evidence.ts +160 -0
  161. package/src/ui-tailwind/editor-surface/perf-probe.ts +14 -215
  162. package/src/ui-tailwind/editor-surface/pm-decorations.ts +42 -0
  163. package/src/ui-tailwind/editor-surface/pm-position-map.ts +38 -2
  164. package/src/ui-tailwind/editor-surface/pm-schema.ts +14 -4
  165. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +34 -5
  166. package/src/ui-tailwind/editor-surface/runtime-decoration-plugin.ts +9 -19
  167. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -2
  168. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +145 -0
  169. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +16 -11
  170. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +8 -10
  171. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +3 -0
  172. package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +4 -2
  173. package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +60 -20
  174. package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +16 -11
  175. package/src/ui-tailwind/review/tw-health-panel.tsx +36 -17
  176. package/src/ui-tailwind/review/tw-review-rail.tsx +7 -4
  177. package/src/ui-tailwind/review-workspace/diagnostics-visibility.ts +44 -0
  178. package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +11 -0
  179. package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +16 -1
  180. package/src/ui-tailwind/review-workspace/types.ts +26 -12
  181. package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +40 -11
  182. package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +2 -1
  183. package/src/ui-tailwind/review-workspace/use-page-markers.ts +15 -26
  184. package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +35 -18
  185. package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +41 -32
  186. package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +2 -1
  187. package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +2 -1
  188. package/src/ui-tailwind/status/tw-status-bar.tsx +6 -5
  189. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +52 -80
  190. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +12 -48
  191. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +9 -4
  192. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +328 -361
  193. package/src/ui-tailwind/tw-review-workspace.tsx +152 -286
@@ -257,6 +257,19 @@ export interface AIIssueSummary {
257
257
  readonly severity: "info" | "warning" | "error";
258
258
  readonly status: "open" | "resolved";
259
259
  readonly createdAtUtc?: string;
260
+ readonly statusUpdatedAtUtc?: string;
261
+ readonly resolvedAtUtc?: string;
262
+ readonly resolvedBy?: string;
263
+ readonly reopenedAtUtc?: string;
264
+ readonly reopenedBy?: string;
265
+ readonly lastTransition?: {
266
+ readonly action: "resolve" | "reopen";
267
+ readonly actorId: string;
268
+ readonly at: string;
269
+ readonly origin: string;
270
+ readonly fromStatus: "open" | "resolved";
271
+ readonly toStatus: "open" | "resolved";
272
+ };
260
273
  }
261
274
 
262
275
  export interface ScopeBundleEvidence {
@@ -264,6 +277,37 @@ export interface ScopeBundleEvidence {
264
277
  readonly reviewItemIds?: readonly string[];
265
278
  readonly overlappingWorkflowScopeIds?: readonly string[];
266
279
  readonly compatibilityFlags?: readonly string[];
280
+ /**
281
+ * Layout evidence projected from L04/L05 join surfaces when present.
282
+ * Missing layout truth is represented explicitly; Layer 08 does not infer
283
+ * page slices, continuation, or divergence rows from content or DOM state.
284
+ */
285
+ readonly layout?: ScopeLayoutEvidence;
286
+ /**
287
+ * Geometry envelope evidence projected from Layer 05 when a geometry facet
288
+ * is wired. Missing/cold geometry is represented explicitly as
289
+ * `requires-rehydration` or `unavailable`; Layer 08 never fabricates rects.
290
+ */
291
+ readonly geometry?: ScopeGeometryEvidence;
292
+ /**
293
+ * Presentation hint only. Consumers may use this to choose a cheap inline
294
+ * treatment for field-like scopes versus a broader overlay treatment for
295
+ * everything else, but it is not layout or geometry authority.
296
+ */
297
+ readonly visualization?: ScopeVisualizationHint;
298
+ /**
299
+ * Current action capability verdicts for this scope. These are evidence
300
+ * only: they explain what the shipped compiler/runtime can lower today
301
+ * without broadening the mutation surface.
302
+ */
303
+ readonly capabilities?: ScopeCapabilities;
304
+ /**
305
+ * Content-control / SDT overlap evidence. These are preserve-first wrapper
306
+ * facts: when present on a target scope, replacement capability verdicts and
307
+ * validation must surface blockers instead of treating the wrapper like
308
+ * ordinary editable text.
309
+ */
310
+ readonly contentControls?: ScopeContentControlEvidence;
267
311
  /**
268
312
  * Agent-authored explanations on this scope, read back from Layer-06
269
313
  * metadata entries keyed by `metadataId: "ai.explanation"`. Adversarial-
@@ -279,6 +323,192 @@ export interface ScopeBundleEvidence {
279
323
  readonly aiIssues?: readonly AIIssueSummary[];
280
324
  }
281
325
 
326
+ export type ScopeEvidenceAvailability =
327
+ | "available"
328
+ | "partial"
329
+ | "degraded"
330
+ | "requires-rehydration"
331
+ | "unavailable";
332
+
333
+ export interface ScopeLayoutContinuationEvidence {
334
+ readonly pageIds?: readonly string[];
335
+ readonly pageCount?: number;
336
+ readonly crossesPageBoundary?: boolean;
337
+ readonly continuedFromPreviousPage?: boolean;
338
+ readonly continuesToNextPage?: boolean;
339
+ }
340
+
341
+ export interface ScopeTableFramePageEvidence {
342
+ readonly pageId: string;
343
+ readonly pageIndex: number;
344
+ readonly fragmentId: string;
345
+ readonly rowRange?: {
346
+ readonly from: number;
347
+ readonly to: number;
348
+ readonly totalRows: number;
349
+ };
350
+ readonly continuesFromPreviousPage?: boolean;
351
+ readonly continuesToNextPage?: boolean;
352
+ readonly repeatedHeaderRowIndexes?: readonly number[];
353
+ readonly splitRowCarry?: readonly {
354
+ readonly rowIndex: number;
355
+ readonly continuesFromPreviousPage: boolean;
356
+ readonly continuesToNextPage: boolean;
357
+ }[];
358
+ readonly verticalMergeCarry?: readonly {
359
+ readonly columnIndex: number;
360
+ readonly restartRowIndex: number;
361
+ }[];
362
+ }
363
+
364
+ export interface ScopeTableFrameEvidence {
365
+ readonly source: "runtime.layout.table-frame-continuation";
366
+ readonly blockId: string;
367
+ readonly scopeKind: "table" | "table-row" | "table-cell";
368
+ readonly rowIndex?: number;
369
+ readonly cellIndex?: number;
370
+ readonly pageIds?: readonly string[];
371
+ readonly pageSliceIds?: readonly string[];
372
+ readonly layoutObjectIds?: readonly string[];
373
+ readonly rowRangesByPage?: readonly ScopeTableFramePageEvidence[];
374
+ readonly repeatedHeaderRowIndexes?: readonly number[];
375
+ readonly splitRowCarry?: readonly {
376
+ readonly rowIndex: number;
377
+ readonly continuesFromPreviousPage: boolean;
378
+ readonly continuesToNextPage: boolean;
379
+ }[];
380
+ readonly verticalMergeCarry?: readonly {
381
+ readonly columnIndex: number;
382
+ readonly restartRowIndex: number;
383
+ }[];
384
+ }
385
+
386
+ export interface ScopeLayoutEvidence {
387
+ readonly status: ScopeEvidenceAvailability;
388
+ readonly completeness:
389
+ | "complete"
390
+ | "partial"
391
+ | "degraded"
392
+ | "requires-rehydration"
393
+ | "unavailable";
394
+ readonly reason?: string;
395
+ readonly pageSliceIds?: readonly string[];
396
+ readonly layoutObjectIds?: readonly string[];
397
+ readonly continuationState?: ScopeLayoutContinuationEvidence;
398
+ readonly divergenceIds?: readonly string[];
399
+ readonly tableFrame?: ScopeTableFrameEvidence;
400
+ }
401
+
402
+ export type ScopeGeometryEvidenceStatus =
403
+ | "available"
404
+ | "requires-rehydration"
405
+ | "unavailable";
406
+
407
+ export type ScopeGeometryEvidencePrecision =
408
+ | "exact"
409
+ | "within-tolerance"
410
+ | "heuristic";
411
+
412
+ export interface ScopeGeometryEvidenceRect {
413
+ readonly x: number;
414
+ readonly y: number;
415
+ readonly width: number;
416
+ readonly height: number;
417
+ readonly space: "twips" | "frame" | "overlay";
418
+ readonly precision?: ScopeGeometryEvidencePrecision;
419
+ }
420
+
421
+ export interface ScopeGeometryEvidence {
422
+ readonly status: ScopeGeometryEvidenceStatus;
423
+ readonly requiresRehydration: boolean;
424
+ readonly reason?: string;
425
+ readonly pageIds?: readonly string[];
426
+ readonly confidence?: "exact" | "medium" | "detached";
427
+ readonly precision?: ScopeGeometryEvidencePrecision;
428
+ readonly rects?: readonly ScopeGeometryEvidenceRect[];
429
+ readonly attachPoint?: {
430
+ readonly x: number;
431
+ readonly y: number;
432
+ readonly space: "twips" | "frame" | "overlay";
433
+ };
434
+ readonly linesCrossed?: number;
435
+ readonly continuationState?: ScopeLayoutContinuationEvidence;
436
+ readonly sourceIdentity?: {
437
+ readonly storyKey?: string;
438
+ readonly blockPath?: string;
439
+ readonly tableKey?: string;
440
+ readonly rowKey?: string;
441
+ readonly cellKey?: string;
442
+ readonly scopeKey?: string;
443
+ readonly scopeId?: string;
444
+ readonly objectKey?: string;
445
+ readonly inlinePath?: string;
446
+ readonly objectKind?: string;
447
+ readonly editPosture?: string;
448
+ readonly joinKind?: string;
449
+ };
450
+ }
451
+
452
+ export type ScopeVisualizationClass = "field" | "broad";
453
+
454
+ export interface ScopeVisualizationHint {
455
+ readonly class: ScopeVisualizationClass;
456
+ readonly reason: string;
457
+ }
458
+
459
+ export type ScopeCapabilityStatus =
460
+ | "supported"
461
+ | "blocked"
462
+ | "unsupported"
463
+ | "degraded";
464
+
465
+ /**
466
+ * Action posture for compiler consumers that need to distinguish "warn and
467
+ * proceed" from a real refusal. L08 must prefer `warn-and-proceed` whenever
468
+ * the mutation can be lowered without losing preserved truth or fabricating
469
+ * layout/geometry. `hard-refusal` means the action must not dispatch.
470
+ */
471
+ export type ScopeActionPosture =
472
+ | "supported"
473
+ | "warn-and-proceed"
474
+ | "hard-refusal";
475
+
476
+ export interface ScopeCapabilityVerdict {
477
+ readonly supported: boolean;
478
+ readonly status: ScopeCapabilityStatus;
479
+ readonly posture?: ScopeActionPosture;
480
+ readonly reason?: string;
481
+ readonly blockers?: readonly string[];
482
+ readonly warnings?: readonly string[];
483
+ }
484
+
485
+ export interface ScopeCapabilities {
486
+ readonly canReplaceText: ScopeCapabilityVerdict;
487
+ readonly canReplaceFragment: ScopeCapabilityVerdict;
488
+ readonly canInsertBefore: ScopeCapabilityVerdict;
489
+ readonly canInsertAfter: ScopeCapabilityVerdict;
490
+ readonly canApplyFormatting: ScopeCapabilityVerdict;
491
+ readonly canClearFormattingLayer: ScopeCapabilityVerdict;
492
+ readonly canAttachMetadata: ScopeCapabilityVerdict;
493
+ }
494
+
495
+ export type ScopeContentControlEvidenceStatus = "none" | "present";
496
+
497
+ export interface ScopeContentControlEvidenceEntry {
498
+ readonly evidenceId: string;
499
+ readonly blockIndex: number;
500
+ readonly sdtType?: string;
501
+ readonly alias?: string;
502
+ readonly tag?: string;
503
+ readonly lock?: string;
504
+ }
505
+
506
+ export interface ScopeContentControlEvidence {
507
+ readonly status: ScopeContentControlEvidenceStatus;
508
+ readonly count: number;
509
+ readonly entries: readonly ScopeContentControlEvidenceEntry[];
510
+ }
511
+
282
512
  export interface ScopeBundle {
283
513
  readonly scope: SemanticScope;
284
514
  readonly neighborhood: ScopeBundleNeighborhood;
@@ -286,11 +516,19 @@ export interface ScopeBundle {
286
516
  readonly generatedAtUtc: string;
287
517
  }
288
518
 
289
- export type ReplacementOperationKind =
519
+ export type ScopeReplacementOperationKind =
290
520
  | "replace"
291
521
  | "insert-before"
292
- | "insert-after"
293
- | "split"
522
+ | "insert-after";
523
+
524
+ /**
525
+ * Compiler-internal proposal operation. Public replacement surfaces accept
526
+ * `ScopeReplacementOperationKind`; `annotate` is retained only for metadata
527
+ * write audit records (`ai.attachExplanation` / `ai.createIssue`) that reuse
528
+ * the `ScopeActionAudit` shape without going through text replacement.
529
+ */
530
+ export type ReplacementOperationKind =
531
+ | ScopeReplacementOperationKind
294
532
  | "annotate";
295
533
 
296
534
  export interface ReplacementPreservePolicy {
@@ -426,6 +664,7 @@ export interface ValidationApproval {
426
664
  */
427
665
  export interface ValidationResult {
428
666
  readonly safe: boolean;
667
+ readonly posture?: ScopeActionPosture;
429
668
  readonly blockedReasons: readonly string[];
430
669
  readonly warnings: readonly ValidationIssue[];
431
670
  readonly approval?: ValidationApproval;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Scope visualization hint projection.
3
+ *
4
+ * This is deliberately evidence, not geometry. Layer 08 can tell consumers
5
+ * whether a scope is structurally field-like or broad; Layer 05 remains the
6
+ * authority for rects, line spans, and overlay envelopes.
7
+ */
8
+
9
+ import type {
10
+ ScopeVisualizationHint,
11
+ SemanticScope,
12
+ } from "./semantic-scope-types.ts";
13
+
14
+ export function deriveScopeVisualization(
15
+ scope: SemanticScope,
16
+ ): ScopeVisualizationHint {
17
+ if (scope.kind === "field") {
18
+ return {
19
+ class: "field",
20
+ reason: "kind:field",
21
+ };
22
+ }
23
+
24
+ return {
25
+ class: "broad",
26
+ reason: `kind:${scope.kind}`,
27
+ };
28
+ }
@@ -149,7 +149,7 @@ export interface SurfaceProjectionOptions {
149
149
  * if you call `createEditorSurfaceSnapshot` directly, normalize first.
150
150
  */
151
151
  viewportBlockRanges?: readonly { start: number; end: number }[] | null;
152
- /** @deprecated use `viewportBlockRanges`. Kept for back-compat; wrapped into a 1-element array when supplied alone. */
152
+ /** @deprecated use `viewportBlockRanges`. Kept for back-compat; remove after the next published API pin refresh. */
153
153
  viewportBlockRange?: { start: number; end: number } | null;
154
154
  /**
155
155
  * Active markup mode. When set together with the document's
@@ -444,7 +444,9 @@ function createSurfaceBlock(
444
444
  }
445
445
 
446
446
  if (block.type === "sdt") {
447
- const descriptor = describeStructuredWrapperBlock(block);
447
+ const descriptor = describeStructuredWrapperBlock(block, {
448
+ projectVisibleTocContentControls: true,
449
+ });
448
450
  if (descriptor) {
449
451
  const blockId = `sdt-wrapper-${counters.sdt}`;
450
452
  counters.sdt += 1;
@@ -1086,6 +1088,12 @@ function createSdtBlock(
1086
1088
  const children: SurfaceBlockSnapshot[] = [];
1087
1089
  const lockedFragmentIds: string[] = [];
1088
1090
  let innerCursor = cursor;
1091
+ const descriptor = describeStructuredWrapperBlock(block);
1092
+ const recursableBlockedReasonCode = descriptor
1093
+ ? isBlockedImportFeatureKey(descriptor.featureKey)
1094
+ ? "workflow_blocked_import"
1095
+ : "workflow_preserve_only"
1096
+ : getRecursableSdtBlockedReasonCode(block);
1089
1097
 
1090
1098
  for (const child of block.children) {
1091
1099
  const result = createSurfaceBlock(
@@ -1112,11 +1120,21 @@ function createSdtBlock(
1112
1120
  ...(block.properties.alias ? { alias: block.properties.alias } : {}),
1113
1121
  ...(block.properties.tag ? { tag: block.properties.tag } : {}),
1114
1122
  ...(block.properties.lock ? { lock: block.properties.lock } : {}),
1123
+ ...(recursableBlockedReasonCode
1124
+ ? { blockedReasonCode: recursableBlockedReasonCode }
1125
+ : {}),
1115
1126
  ...(block.properties.checkbox ? { checkboxChecked: block.properties.checkbox.checked } : {}),
1116
1127
  ...(block.properties.datePicker?.fullDate ? { dateValue: block.properties.datePicker.fullDate } : {}),
1117
1128
  ...(block.properties.dropdownList ? { dropdownItems: block.properties.dropdownList } : {}),
1118
1129
  ...(block.properties.comboBox ? { comboBoxItems: block.properties.comboBox } : {}),
1119
1130
  ...(block.properties.showingPlcHdr ? { showingPlcHdr: true } : {}),
1131
+ ...(descriptor
1132
+ ? {
1133
+ label: descriptor.label,
1134
+ detail: descriptor.detail,
1135
+ featureKey: descriptor.featureKey,
1136
+ }
1137
+ : {}),
1120
1138
  children,
1121
1139
  },
1122
1140
  lockedFragmentIds,
@@ -1124,6 +1142,24 @@ function createSdtBlock(
1124
1142
  };
1125
1143
  }
1126
1144
 
1145
+ function getRecursableSdtBlockedReasonCode(
1146
+ block: Mutable<SdtNode>,
1147
+ ): "workflow_preserve_only" | null {
1148
+ const searchText = [
1149
+ block.properties.alias,
1150
+ block.properties.tag,
1151
+ block.properties.sdtType,
1152
+ block.properties.propertiesXml,
1153
+ ]
1154
+ .filter(Boolean)
1155
+ .join(" ")
1156
+ .toLowerCase();
1157
+
1158
+ return searchText.includes("table of contents") || /\btoc\b/u.test(searchText)
1159
+ ? "workflow_preserve_only"
1160
+ : null;
1161
+ }
1162
+
1127
1163
  function createParagraphBlock(
1128
1164
  paragraphIndex: number,
1129
1165
  paragraph: Mutable<ParagraphNode>,
@@ -1562,6 +1598,7 @@ function appendInlineSegments(
1562
1598
  });
1563
1599
  return { nextCursor: start + 1, lockedFragmentIds: [] };
1564
1600
  }
1601
+ const objectAnchor = surfaceAnchorFromGeometry(node.anchor);
1565
1602
  if (c.type === "chart_preview") {
1566
1603
  const parsedChartId = registerParsedChartPreview(c, document);
1567
1604
  return appendComplexPreviewSegment(
@@ -1573,7 +1610,7 @@ function appendInlineSegments(
1573
1610
  {
1574
1611
  previewMediaId: c.previewMediaId,
1575
1612
  parsedChartId,
1576
- anchor: surfaceAnchorFromGeometry(node.anchor),
1613
+ ...(objectAnchor ? { anchor: objectAnchor } : {}),
1577
1614
  },
1578
1615
  );
1579
1616
  }
@@ -1586,7 +1623,7 @@ function appendInlineSegments(
1586
1623
  `DrawingFrame smartart_preview (${node.anchor.wrapMode}).`,
1587
1624
  {
1588
1625
  previewMediaId: c.previewMediaId,
1589
- anchor: surfaceAnchorFromGeometry(node.anchor),
1626
+ ...(objectAnchor ? { anchor: objectAnchor } : {}),
1590
1627
  },
1591
1628
  );
1592
1629
  }
@@ -1596,7 +1633,9 @@ function appendInlineSegments(
1596
1633
  start,
1597
1634
  "Drawing frame",
1598
1635
  `DrawingFrame ${c.type} (${node.anchor.wrapMode}).`,
1599
- { anchor: surfaceAnchorFromGeometry(node.anchor) },
1636
+ {
1637
+ ...(objectAnchor ? { anchor: objectAnchor } : {}),
1638
+ },
1600
1639
  );
1601
1640
  }
1602
1641
  case "symbol":
@@ -0,0 +1,216 @@
1
+ export type PerfProbeKind =
2
+ | "typing"
3
+ | "typing.predicted"
4
+ | "typing.reconcile"
5
+ | "typing.divergence"
6
+ | "selection"
7
+ | "runtime.create"
8
+ | "loadSession.laycacheProbe"
9
+ | "loadSession.compatReportCached"
10
+ | "snapshot.surface"
11
+ | "snapshot.compatibility"
12
+ | "snapshot.navigation"
13
+ | "pm.rebuild"
14
+ | "pm.decorations"
15
+ | "pm.decorations.apply"
16
+ | "pm.decorations.comments"
17
+ | "pm.decorations.revisions"
18
+ | "pm.decorations.workflow"
19
+ | "pm.mount"
20
+ | "shell.render"
21
+ | "workspace.chrome"
22
+ | "selection.sync"
23
+ | "layout.incremental"
24
+ | "layout.full"
25
+ | "render.frame_build"
26
+ | "render.frame_diff"
27
+ | "render.decoration_resolve"
28
+ | "chrome.overlay_reposition"
29
+ | "chrome.hit_test"
30
+ | "rail.segment_project";
31
+
32
+ /**
33
+ * Counter names the FastTextEditLane emits via `incrementInvalidationCounter`.
34
+ * Expose them as a const so integrators can read the shape without duplicating
35
+ * strings.
36
+ */
37
+ export const PREDICTED_LANE_COUNTERS = {
38
+ applied: "predictions.applied",
39
+ equivalent: "predictions.equivalent",
40
+ adjusted: "predictions.adjusted",
41
+ rejected: "predictions.rejected",
42
+ rollback: "predictions.rollback",
43
+ structuralDivergence: "predictions.structuralDivergence",
44
+ bailBeforePredict: "predictions.bailBeforePredict",
45
+ refreshSelectionOnly: "predictions.refresh.selectionOnly",
46
+ refreshLocalTextEquivalent: "predictions.refresh.localTextEquivalent",
47
+ refreshSurfaceOnly: "predictions.refresh.surfaceOnly",
48
+ refreshFullProjection: "predictions.refresh.fullProjection",
49
+ refreshBlocked: "predictions.refresh.blocked",
50
+ } as const;
51
+
52
+ export interface PerfProbeSample {
53
+ token: string;
54
+ kind: PerfProbeKind;
55
+ durationMs: number;
56
+ recordedAt: number;
57
+ }
58
+
59
+ interface PendingProbe {
60
+ kind: PerfProbeKind;
61
+ startedAt: number;
62
+ }
63
+
64
+ interface PerfProbeState {
65
+ enabled?: boolean;
66
+ nextToken?: number;
67
+ pending?: Record<string, PendingProbe>;
68
+ samples?: PerfProbeSample[];
69
+ maxSamples?: number;
70
+ invalidationCounts?: Record<string, number>;
71
+ }
72
+
73
+ export interface PerfProbeSummary {
74
+ samples: PerfProbeSample[];
75
+ latest: Partial<Record<PerfProbeKind, PerfProbeSample | null>>;
76
+ invalidationCounts: Record<string, number>;
77
+ }
78
+
79
+ declare global {
80
+ interface Window {
81
+ __DOCX_REACT_PERF_PROBE__?: PerfProbeState;
82
+ }
83
+ }
84
+
85
+ export function startPerfProbe(kind: PerfProbeKind): string | null {
86
+ const state = getEnabledState();
87
+ if (!state) {
88
+ return null;
89
+ }
90
+
91
+ const token = `${kind}-${state.nextToken ?? 0}`;
92
+ state.nextToken = (state.nextToken ?? 0) + 1;
93
+ state.pending ??= {};
94
+ state.pending[token] = {
95
+ kind,
96
+ startedAt: performance.now(),
97
+ };
98
+ return token;
99
+ }
100
+
101
+ export function finishPerfProbe(token: string | null | undefined): PerfProbeSample | null {
102
+ if (!token) {
103
+ return null;
104
+ }
105
+ const state = getEnabledState();
106
+ if (!state?.pending?.[token]) {
107
+ return null;
108
+ }
109
+
110
+ const pending = state.pending[token];
111
+ delete state.pending[token];
112
+
113
+ const sample: PerfProbeSample = {
114
+ token,
115
+ kind: pending.kind,
116
+ durationMs: performance.now() - pending.startedAt,
117
+ recordedAt: Date.now(),
118
+ };
119
+
120
+ pushSample(state, sample);
121
+
122
+ return sample;
123
+ }
124
+
125
+ export function recordPerfSample(
126
+ kind: PerfProbeKind,
127
+ durationMs = 0,
128
+ ): PerfProbeSample | null {
129
+ const state = getEnabledState();
130
+ if (!state) {
131
+ return null;
132
+ }
133
+
134
+ const token = `${kind}-${state.nextToken ?? 0}`;
135
+ state.nextToken = (state.nextToken ?? 0) + 1;
136
+ const sample: PerfProbeSample = {
137
+ token,
138
+ kind,
139
+ durationMs,
140
+ recordedAt: Date.now(),
141
+ };
142
+ pushSample(state, sample);
143
+ return sample;
144
+ }
145
+
146
+ export function incrementInvalidationCounter(
147
+ counter: string,
148
+ amount = 1,
149
+ ): number {
150
+ const state = getEnabledState();
151
+ if (!state) {
152
+ return 0;
153
+ }
154
+
155
+ state.invalidationCounts ??= {};
156
+ state.invalidationCounts[counter] =
157
+ (state.invalidationCounts[counter] ?? 0) + amount;
158
+ return state.invalidationCounts[counter]!;
159
+ }
160
+
161
+ export function getLatestPerfSummary(): PerfProbeSummary | null {
162
+ const state = getEnabledState();
163
+ const samples = state?.samples ?? [];
164
+ if (!state || samples.length === 0) {
165
+ return null;
166
+ }
167
+
168
+ return {
169
+ samples: [...samples],
170
+ latest: buildLatestSampleMap(samples),
171
+ invalidationCounts: { ...(state.invalidationCounts ?? {}) },
172
+ };
173
+ }
174
+
175
+ export function resetPerfProbeState(): void {
176
+ const state = getEnabledState();
177
+ if (!state) {
178
+ return;
179
+ }
180
+ state.nextToken = 0;
181
+ state.pending = {};
182
+ state.samples = [];
183
+ state.invalidationCounts = {};
184
+ }
185
+
186
+ function getEnabledState(): PerfProbeState | null {
187
+ if (typeof window === "undefined") {
188
+ return null;
189
+ }
190
+ const state = window.__DOCX_REACT_PERF_PROBE__;
191
+ if (!state?.enabled) {
192
+ return null;
193
+ }
194
+ return state;
195
+ }
196
+
197
+ function pushSample(state: PerfProbeState, sample: PerfProbeSample): void {
198
+ state.samples ??= [];
199
+ state.samples.push(sample);
200
+ const maxSamples = state.maxSamples ?? 20;
201
+ if (state.samples.length > maxSamples) {
202
+ state.samples.splice(0, state.samples.length - maxSamples);
203
+ }
204
+ }
205
+
206
+ function buildLatestSampleMap(
207
+ samples: PerfProbeSample[],
208
+ ): Partial<Record<PerfProbeKind, PerfProbeSample | null>> {
209
+ const latest: Partial<Record<PerfProbeKind, PerfProbeSample | null>> = {};
210
+ for (const sample of [...samples].reverse()) {
211
+ if (latest[sample.kind] === undefined) {
212
+ latest[sample.kind] = sample;
213
+ }
214
+ }
215
+ return latest;
216
+ }
@@ -35,6 +35,21 @@ export interface VirtualizedViewport {
35
35
  overscanHeight?: number;
36
36
  }
37
37
 
38
+ export interface VirtualizedBlockGeometryProvider {
39
+ getBlock(blockId: string): { rects: readonly { topPx: number }[] } | null;
40
+ }
41
+
42
+ export interface ComputeVirtualizedWindowOptions {
43
+ /**
44
+ * Optional L05 geometry source. When present, `offsetTop` is derived from
45
+ * `GeometryFacet.getBlock(firstVisibleBlockId).rects[0].topPx` instead of
46
+ * the synthetic virtualization estimate. This keeps DOM-free consumers on
47
+ * geometry truth while preserving the legacy estimate for unwired tests and
48
+ * pre-paint sessions.
49
+ */
50
+ geometryFacet?: VirtualizedBlockGeometryProvider;
51
+ }
52
+
38
53
  export interface VirtualizedWindow {
39
54
  startIndex: number;
40
55
  endIndex: number;
@@ -121,6 +136,7 @@ export function createVirtualizedRenderingSession(
121
136
  export function computeVirtualizedWindow(
122
137
  session: VirtualizedRenderingSession,
123
138
  viewport: VirtualizedViewport,
139
+ options: ComputeVirtualizedWindowOptions = {},
124
140
  ): VirtualizedWindow {
125
141
  if (session.metrics.length === 0) {
126
142
  return {
@@ -145,19 +161,38 @@ export function computeVirtualizedWindow(
145
161
  const safeEndIndex = Math.max(startIndex + 1, endIndexExclusive);
146
162
  const visibleMetrics = session.metrics.slice(startIndex, safeEndIndex);
147
163
  const visibleBlocks = session.blocks.slice(startIndex, safeEndIndex);
164
+ const firstMetric = visibleMetrics[0];
165
+ const firstBlock = visibleBlocks[0];
148
166
  const lastMetric = visibleMetrics[visibleMetrics.length - 1]!;
149
167
 
150
168
  return {
151
169
  startIndex,
152
170
  endIndex: safeEndIndex,
153
171
  totalHeight: session.totalHeight,
154
- offsetTop: visibleMetrics[0]?.top ?? 0,
172
+ offsetTop: resolveWindowOffsetTop(
173
+ firstBlock?.blockId ?? firstMetric?.blockId,
174
+ firstMetric,
175
+ options,
176
+ ),
155
177
  offsetBottom: Math.max(0, session.totalHeight - lastMetric.bottom),
156
178
  visibleBlocks,
157
179
  visibleMetrics,
158
180
  };
159
181
  }
160
182
 
183
+ function resolveWindowOffsetTop(
184
+ blockId: string | undefined,
185
+ fallbackMetric: VirtualizedRenderingMetrics | undefined,
186
+ options: ComputeVirtualizedWindowOptions,
187
+ ): number {
188
+ const geometryTop = blockId
189
+ ? options.geometryFacet?.getBlock(blockId)?.rects[0]?.topPx
190
+ : undefined;
191
+ return typeof geometryTop === "number" && Number.isFinite(geometryTop)
192
+ ? geometryTop
193
+ : (fallbackMetric?.top ?? 0);
194
+ }
195
+
161
196
  function findFirstVisibleIndex(
162
197
  metrics: VirtualizedRenderingMetrics[],
163
198
  top: number,