@beyondwork/docx-react-component 1.0.67 → 1.0.70
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/README.md +75 -932
- package/package.json +26 -27
- package/src/api/anchor-conversion.ts +43 -0
- package/src/api/editor-state-types.ts +2 -1
- package/src/api/public-types.ts +504 -101
- package/src/api/session-state.ts +4 -0
- package/src/api/v3/README.md +91 -0
- package/src/api/v3/_create.ts +146 -0
- package/src/api/v3/_layer-metadata.ts +362 -0
- package/src/api/v3/_mocks.ts +84 -0
- package/src/api/v3/_runtime-handle.ts +162 -0
- package/src/api/v3/_ux-response.ts +73 -0
- package/src/api/v3/ai/_metadata-audit.ts +225 -0
- package/src/api/v3/ai/attach.ts +235 -0
- package/src/api/v3/ai/bundle.ts +132 -0
- package/src/api/v3/ai/explain.ts +144 -0
- package/src/api/v3/ai/export.ts +54 -0
- package/src/api/v3/ai/inspect.ts +118 -0
- package/src/api/v3/ai/policy.ts +77 -0
- package/src/api/v3/ai/replacement.ts +341 -0
- package/src/api/v3/ai/resolve.ts +133 -0
- package/src/api/v3/index.ts +79 -0
- package/src/api/v3/runtime/chart.ts +310 -0
- package/src/api/v3/runtime/clipboard.ts +81 -0
- package/src/api/v3/runtime/collab.ts +331 -0
- package/src/api/v3/runtime/content.ts +236 -0
- package/src/api/v3/runtime/document.ts +282 -0
- package/src/api/v3/runtime/formatting.ts +186 -0
- package/src/api/v3/runtime/geometry.ts +349 -0
- package/src/api/v3/runtime/layout.ts +108 -0
- package/src/api/v3/runtime/review.ts +129 -0
- package/src/api/v3/runtime/search.ts +74 -0
- package/src/api/v3/runtime/table.ts +63 -0
- package/src/api/v3/runtime/workflow.ts +434 -0
- package/src/api/v3/ui/_context.ts +86 -0
- package/src/api/v3/ui/_create.ts +65 -0
- package/src/api/v3/ui/_types.ts +520 -0
- package/src/api/v3/ui/chrome-composition.ts +342 -0
- package/src/{ui-tailwind/chrome → api/v3/ui}/chrome-preset-model.ts +11 -1
- package/src/api/v3/ui/chrome.ts +476 -0
- package/src/api/v3/ui/debug.ts +124 -0
- package/src/api/v3/ui/index.ts +64 -0
- package/src/api/v3/ui/overlays-visibility.ts +170 -0
- package/src/api/v3/ui/overlays.ts +427 -0
- package/src/api/v3/ui/scope.ts +71 -0
- package/src/api/v3/ui/session.ts +100 -0
- package/src/api/v3/ui/surface.ts +170 -0
- package/src/api/v3/ui/viewport.ts +303 -0
- package/src/core/commands/index.ts +28 -6
- package/src/core/commands/list-commands.ts +3 -2
- package/src/core/commands/section-layout-commands.ts +9 -8
- package/src/core/schema/text-schema.ts +16 -0
- package/src/core/selection/mapping.ts +33 -72
- package/src/core/state/editor-state.ts +96 -189
- package/src/index.ts +23 -4
- package/src/io/chart-preview-resolver.ts +1 -1
- package/src/io/docx-session.ts +36 -4797
- package/src/io/export/build-app-properties-xml.ts +1 -1
- package/src/io/export/serialize-comments.ts +1 -1
- package/src/io/export/serialize-headers-footers.ts +6 -1
- package/src/io/export/serialize-main-document.ts +45 -0
- package/src/io/export/serialize-run-formatting.ts +17 -2
- package/src/io/export/twip.ts +1 -1
- package/src/io/normalize/normalize-text.ts +27 -20
- package/src/io/ooxml/chart/parse-series.ts +1 -1
- package/src/io/ooxml/chart/resolve-color.ts +2 -2
- package/src/io/ooxml/chart/types.ts +1 -1
- package/src/io/ooxml/classify-embedding.ts +83 -33
- package/src/io/ooxml/parse-fill.ts +1 -1
- package/src/io/ooxml/parse-main-document.ts +71 -1
- package/src/io/ooxml/parse-object.ts +14 -10
- package/src/io/ooxml/parse-run-formatting.ts +47 -1
- package/src/io/ooxml/property-grab-bag.ts +2 -2
- package/src/io/ooxml/units.ts +11 -0
- package/src/io/ooxml/workflow-payload.ts +282 -7
- package/src/model/anchor.ts +85 -0
- package/src/model/canonical-document.ts +351 -15
- package/src/model/chart-types.ts +1 -1
- package/src/model/layout/index.ts +83 -0
- package/src/model/layout/page-graph-types.ts +181 -0
- package/src/model/layout/page-layout-snapshot.ts +105 -0
- package/src/model/layout/resolved-layout-types.ts +47 -0
- package/src/model/layout/runtime-page-graph-types.ts +102 -0
- package/src/model/paragraph-scope-ids.ts +72 -0
- package/src/model/review/comment-types.ts +112 -0
- package/src/model/review/index.ts +2 -0
- package/src/model/review/revision-types.ts +215 -0
- package/src/model/snapshot.ts +32 -0
- package/src/review/store/comment-store.ts +21 -47
- package/src/review/store/revision-types.ts +40 -198
- package/src/runtime/collab/base-doc-fingerprint.ts +6 -1
- package/src/runtime/collab/runtime-collab-sync.ts +13 -3
- package/src/runtime/collab-session.ts +1 -1
- package/src/runtime/debug/build-debug-inspector-snapshot.ts +686 -0
- package/src/runtime/debug/event-ring-buffer.ts +64 -0
- package/src/runtime/debug/probability-sampler.ts +18 -0
- package/src/runtime/debug/runtime-debug-facet.ts +67 -0
- package/src/runtime/debug/stage-tokens.ts +31 -0
- package/src/runtime/debug/telemetry-bus.ts +271 -0
- package/src/runtime/debug/types.ts +275 -0
- package/src/runtime/debug/wrap-ref-for-telemetry.ts +118 -0
- package/src/runtime/document-layout.ts +8 -6
- package/src/runtime/document-runtime.ts +843 -1141
- package/src/runtime/document-search.ts +1 -1
- package/src/runtime/edit-ops/index.ts +1 -1
- package/src/runtime/external-send-runtime.ts +1 -1
- package/src/runtime/formatting/document-lookup.ts +235 -0
- package/src/runtime/formatting/field/registry.ts +41 -0
- package/src/runtime/{field-resolver.ts → formatting/field/resolver.ts} +27 -2
- package/src/runtime/formatting/font-resolution.ts +83 -0
- package/src/runtime/formatting/formatting-context.ts +903 -0
- package/src/runtime/formatting/formatting-types.ts +157 -0
- package/src/runtime/{hyperlink-color-resolver.ts → formatting/hyperlink-color.ts} +2 -2
- package/src/runtime/formatting/index.ts +125 -0
- package/src/runtime/{resolved-numbering-geometry.ts → formatting/numbering/geometry.ts} +1 -1
- package/src/runtime/{numbering-prefix.ts → formatting/numbering/prefix.ts} +170 -3
- package/src/runtime/formatting/paragraph-style-resolver.ts +92 -0
- package/src/runtime/formatting/projector.ts +75 -0
- package/src/runtime/formatting/resolve-effective.ts +407 -0
- package/src/runtime/formatting/revision-display.ts +105 -0
- package/src/runtime/{paragraph-style-resolver.ts → formatting/style-cascade.ts} +84 -141
- package/src/runtime/{table-style-resolver.ts → formatting/table-style-resolver.ts} +1 -1
- package/src/runtime/formatting/telemetry-bridge.ts +106 -0
- package/src/runtime/{theme-color-resolver.ts → formatting/theme-color.ts} +2 -30
- package/src/runtime/geometry/caret-geometry.ts +164 -0
- package/src/runtime/geometry/geometry-facet.ts +364 -0
- package/src/runtime/geometry/geometry-types.ts +256 -0
- package/src/runtime/geometry/hit-test.ts +125 -0
- package/src/runtime/geometry/index.ts +71 -0
- package/src/runtime/geometry/inert-geometry-facet.ts +43 -0
- package/src/runtime/geometry/invalidation.ts +35 -0
- package/src/runtime/geometry/object-handles.ts +77 -0
- package/src/runtime/geometry/overlay-rects.ts +85 -0
- package/src/runtime/geometry/project-anchors.ts +100 -0
- package/src/runtime/geometry/project-fragments.ts +216 -0
- package/src/runtime/geometry/projector.ts +129 -0
- package/src/runtime/geometry/replacement-envelope.ts +130 -0
- package/src/runtime/geometry/viewport.ts +218 -0
- package/src/runtime/layout/compat-input-ledger.ts +211 -0
- package/src/runtime/layout/index.ts +6 -1
- package/src/runtime/layout/inert-layout-facet.ts +12 -7
- package/src/runtime/layout/layout-engine-instance.ts +189 -11
- package/src/runtime/layout/layout-engine-version.ts +450 -1
- package/src/runtime/layout/layout-facet-types.ts +60 -0
- package/src/runtime/layout/layout-measurement-provider.ts +13 -0
- package/src/runtime/layout/measurement-backend-canvas.ts +14 -2
- package/src/runtime/layout/measurement-backend-empirical.ts +23 -4
- package/src/runtime/layout/page-graph.ts +62 -209
- package/src/runtime/layout/page-story-resolver.ts +7 -12
- package/src/runtime/layout/paginated-layout-engine.ts +186 -11
- package/src/runtime/layout/project-block-fragments.ts +11 -0
- package/src/runtime/layout/projector.ts +90 -0
- package/src/runtime/layout/public-facet.ts +187 -442
- package/src/runtime/layout/resolved-formatting-state.ts +158 -26
- package/src/runtime/layout/table-render-plan.ts +1 -1
- package/src/runtime/prerender/cache-envelope.ts +6 -1
- package/src/runtime/prerender/prerender-document.ts +18 -23
- package/src/runtime/render/decoration-resolver.ts +1 -1
- package/src/runtime/render/render-frame-types.ts +20 -0
- package/src/runtime/render/render-kernel.ts +94 -25
- package/src/runtime/scopes/_formatting-seam.ts +262 -0
- package/src/runtime/scopes/_scope-dependencies.ts +49 -0
- package/src/runtime/scopes/action-validation.ts +356 -0
- package/src/runtime/scopes/attach-explanation.ts +102 -0
- package/src/runtime/scopes/audit-bundle.ts +71 -0
- package/src/runtime/scopes/compile-scope-bundle.ts +163 -0
- package/src/runtime/scopes/compile-scope.ts +262 -0
- package/src/runtime/scopes/compiler-service.ts +431 -0
- package/src/runtime/scopes/create-issue.ts +107 -0
- package/src/runtime/scopes/enumerate-scopes.ts +543 -0
- package/src/runtime/scopes/evidence.ts +233 -0
- package/src/runtime/scopes/index.ts +150 -0
- package/src/runtime/scopes/position-map.ts +214 -0
- package/src/runtime/scopes/preservation-boundary.ts +91 -0
- package/src/runtime/scopes/projector.ts +49 -0
- package/src/runtime/scopes/replaceability.ts +87 -0
- package/src/runtime/scopes/replacement/apply.ts +228 -0
- package/src/runtime/scopes/replacement/compile.ts +59 -0
- package/src/runtime/scopes/replacement/propose.ts +42 -0
- package/src/runtime/scopes/resolve-reference.ts +347 -0
- package/src/runtime/scopes/review-bundle.ts +141 -0
- package/src/runtime/scopes/scope-kinds/_paragraph-text.ts +57 -0
- package/src/runtime/scopes/scope-kinds/_table-text.ts +42 -0
- package/src/runtime/scopes/scope-kinds/comment-thread.ts +59 -0
- package/src/runtime/scopes/scope-kinds/field.ts +65 -0
- package/src/runtime/scopes/scope-kinds/heading.ts +84 -0
- package/src/runtime/scopes/scope-kinds/list-item.ts +77 -0
- package/src/runtime/scopes/scope-kinds/paragraph.ts +182 -0
- package/src/runtime/scopes/scope-kinds/revision.ts +62 -0
- package/src/runtime/scopes/scope-kinds/table-cell.ts +57 -0
- package/src/runtime/scopes/scope-kinds/table-row.ts +61 -0
- package/src/runtime/scopes/scope-kinds/table.ts +55 -0
- package/src/runtime/scopes/scope-range.ts +208 -0
- package/src/runtime/scopes/semantic-scope-types.ts +454 -0
- package/src/runtime/scopes/workflow-overlap.ts +92 -0
- package/src/runtime/selection/index.ts +1 -1
- package/src/runtime/structure-ops/fragment-insert.ts +1 -1
- package/src/runtime/structure-ops/index.ts +1 -1
- package/src/runtime/surface-projection.ts +232 -262
- package/src/runtime/units.ts +4 -2
- package/src/runtime/workflow/coordinator.ts +1348 -0
- package/src/runtime/workflow/derived-scope-resolver.ts +125 -0
- package/src/runtime/workflow/index.ts +25 -0
- package/src/runtime/workflow/markup-mode-policy.ts +98 -0
- package/src/runtime/{workflow-markup.ts → workflow/markup.ts} +6 -6
- package/src/runtime/workflow/metadata-persistence.ts +306 -0
- package/src/runtime/workflow/metadata-writer.ts +123 -0
- package/src/runtime/workflow/overlay-store.ts +690 -0
- package/src/runtime/workflow/projector.ts +127 -0
- package/src/runtime/{query-scopes.ts → workflow/query-scopes.ts} +3 -3
- package/src/runtime/{workflow-rail-segments.ts → workflow/rail/compose.ts} +60 -165
- package/src/runtime/workflow/rail/types.ts +198 -0
- package/src/runtime/workflow/scope-rail-composer.ts +39 -0
- package/src/runtime/{scope-resolver.ts → workflow/scope-resolver.ts} +3 -3
- package/src/runtime/workflow/scope-writer.ts +188 -0
- package/src/runtime/{tamper-gate.ts → workflow/tamper-gate.ts} +1 -1
- package/src/runtime/workflow/visibility-policy.ts +129 -0
- package/src/session/_sync-legacy.ts +66 -0
- package/src/session/export/embedded-reconstitute.ts +104 -0
- package/src/session/export/export-diagnostics.ts +85 -0
- package/src/session/export/export-validation.ts +110 -0
- package/src/session/export/index.ts +34 -0
- package/src/session/export/preservation-reattach.ts +30 -0
- package/src/session/export/serialize-dispatch.ts +165 -0
- package/src/session/export/stateful-export-pipeline.ts +432 -0
- package/src/session/export/stateful-export.ts +684 -0
- package/src/session/import/canonical-assembly.ts +227 -0
- package/src/session/import/diagnostics-session.ts +54 -0
- package/src/session/import/embedded-discovery.ts +225 -0
- package/src/session/import/embedded-offload.ts +337 -0
- package/src/session/import/import-diagnostics.ts +69 -0
- package/src/session/import/loader-types.ts +313 -0
- package/src/session/import/loader.ts +1834 -0
- package/src/session/import/normalize.ts +195 -0
- package/src/session/import/package-parts.ts +217 -0
- package/src/session/import/package-read.ts +195 -0
- package/src/session/import/parse-orchestration.ts +105 -0
- package/src/session/import/part-constants.ts +70 -0
- package/src/session/import/part-discovery.ts +94 -0
- package/src/session/import/preservation-index.ts +46 -0
- package/src/{runtime/read-only-diagnostics-runtime.ts → session/import/read-only-diagnostics.ts} +24 -3
- package/src/session/import/review-import.ts +508 -0
- package/src/session/import/styles-consolidation.ts +281 -0
- package/src/session/import/workflow-scope-import.ts +256 -0
- package/src/session/index.ts +37 -0
- package/src/session/session-state.ts +69 -0
- package/src/session/session.ts +532 -0
- package/src/session/shared/protection.ts +228 -0
- package/src/session/shared/session-utils.ts +82 -0
- package/src/session/types.ts +499 -0
- package/src/shell/chart-snapshots.ts +96 -0
- package/src/shell/media-previews.ts +85 -0
- package/src/shell/overlay-anchor-bridge.ts +53 -0
- package/src/shell/paste-adapter.ts +23 -0
- package/src/shell/ref-commands.ts +1697 -0
- package/src/shell/ref-utilities.ts +48 -0
- package/src/shell/search.ts +51 -0
- package/src/{ui/editor-runtime-boundary.ts → shell/session-bootstrap.ts} +243 -67
- package/src/shell/ui-subscriber-channels.ts +81 -0
- package/src/shell/use-collab-sync.ts +116 -0
- package/src/ui/WordReviewEditor.tsx +496 -2051
- package/src/ui/editor-shell-view.tsx +30 -1
- package/src/ui/editor-surface-controller.tsx +49 -1
- package/src/ui/headless/revision-decoration-model.ts +83 -0
- package/src/{ui-tailwind/chrome → ui/headless}/role-action-sets.ts +1 -1
- package/src/ui/headless/scoped-chrome-policy.ts +2 -2
- package/src/ui/headless/selection-tool-context.ts +1 -1
- package/src/ui/headless/selection-tool-resolver.ts +1 -1
- package/src/ui/runtime-shortcut-dispatch.ts +46 -1
- package/src/ui/ui-controller-factory.ts +221 -0
- package/src/ui-tailwind/chart/ChartSurface.tsx +2 -2
- package/src/ui-tailwind/chart/layout/legend-layout.ts +1 -1
- package/src/ui-tailwind/chart/layout/plot-area.ts +2 -2
- package/src/ui-tailwind/chart/layout/title-layout.ts +1 -1
- package/src/ui-tailwind/chart/render/area.tsx +3 -3
- package/src/ui-tailwind/chart/render/bar-column.tsx +3 -3
- package/src/ui-tailwind/chart/render/bubble.tsx +3 -3
- package/src/ui-tailwind/chart/render/combo.tsx +2 -2
- package/src/ui-tailwind/chart/render/data-labels.tsx +2 -2
- package/src/ui-tailwind/chart/render/font-metrics.ts +2 -2
- package/src/ui-tailwind/chart/render/line.tsx +3 -3
- package/src/ui-tailwind/chart/render/pie.tsx +6 -6
- package/src/ui-tailwind/chart/render/scatter.tsx +3 -3
- package/src/ui-tailwind/chart/render/svg-primitives.ts +3 -3
- package/src/ui-tailwind/chart/render/unsupported.tsx +2 -2
- package/src/ui-tailwind/chrome/build-context-menu-entries.ts +88 -0
- package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +1 -1
- package/src/ui-tailwind/chrome/collab-send-to-supplier-button.tsx +1 -1
- package/src/ui-tailwind/chrome/collab-tamper-banner.tsx +1 -1
- package/src/ui-tailwind/chrome/collab-top-nav-container.tsx +1 -1
- package/src/ui-tailwind/chrome/editor-action-registry.ts +553 -0
- package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +182 -0
- package/src/ui-tailwind/chrome/local-surface-arbiter.ts +534 -0
- package/src/ui-tailwind/chrome/resolve-target-kind.ts +226 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +38 -4
- package/src/ui-tailwind/chrome/tw-context-band.tsx +125 -0
- package/src/ui-tailwind/chrome/tw-context-menu-portal.tsx +248 -0
- package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +42 -1
- package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +8 -7
- package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +38 -4
- package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +104 -6
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +66 -7
- package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +54 -8
- package/src/ui-tailwind/chrome/tw-shortcut-hint.tsx +7 -1
- package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +33 -0
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +78 -1
- package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +16 -8
- package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +276 -0
- package/src/ui-tailwind/chrome/use-context-menu-controller.ts +201 -0
- package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +22 -4
- package/src/ui-tailwind/chrome-overlay/tw-comment-balloon-layer.tsx +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-locked-block-layer.tsx +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +11 -5
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +197 -3
- package/src/ui-tailwind/chrome-overlay/tw-revision-margin-bar-layer.tsx +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +35 -6
- package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +24 -16
- package/src/ui-tailwind/chrome-overlay/tw-table-continuation-header.tsx +1 -1
- package/src/ui-tailwind/debug/README.md +57 -0
- package/src/ui-tailwind/debug/index.ts +3 -0
- package/src/ui-tailwind/debug/tw-debug-overlay.tsx +186 -0
- package/src/ui-tailwind/debug/tw-debug-presentation.tsx +80 -0
- package/src/ui-tailwind/debug/tw-debug-top-bar.tsx +83 -0
- package/src/ui-tailwind/editor-surface/chart-node-view.tsx +2 -2
- package/src/ui-tailwind/editor-surface/float-wrap-resolver.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +135 -10
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +40 -13
- package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-schema.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +3 -3
- package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +1 -1
- package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +2 -2
- package/src/ui-tailwind/editor-surface/scroll-anchor.ts +91 -9
- package/src/ui-tailwind/editor-surface/shape-renderer.ts +1 -1
- package/src/ui-tailwind/editor-surface/surface-layer.ts +1 -1
- package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +1 -1
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +23 -6
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +132 -22
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +1 -1
- package/src/ui-tailwind/index.ts +0 -5
- package/src/ui-tailwind/overlay-anchor-bridge-context.tsx +33 -0
- package/src/ui-tailwind/page-stack/floating-image-overlay-model.ts +66 -29
- package/src/ui-tailwind/page-stack/tw-floating-image-layer.tsx +25 -2
- package/src/ui-tailwind/review/comment-markdown-renderer.tsx +15 -0
- package/src/ui-tailwind/review/tw-review-rail.tsx +92 -4
- package/src/ui-tailwind/review/tw-workflow-tab.tsx +1 -1
- package/src/ui-tailwind/review-workspace/page-chrome.ts +210 -0
- package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +101 -0
- package/src/ui-tailwind/review-workspace/paragraph-layout.ts +115 -0
- package/src/ui-tailwind/review-workspace/selection-toolbar-placement.ts +97 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-navigator.tsx +130 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-page-toolbar.tsx +240 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +59 -0
- package/src/ui-tailwind/review-workspace/types.ts +408 -0
- package/src/ui-tailwind/review-workspace/use-chrome-policy.ts +104 -0
- package/src/ui-tailwind/review-workspace/use-derived-view-state.ts +151 -0
- package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +70 -0
- package/src/ui-tailwind/review-workspace/use-grabbed-segment-offsets.ts +40 -0
- package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +55 -0
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +130 -0
- package/src/ui-tailwind/review-workspace/use-pm-surface-capture.ts +60 -0
- package/src/ui-tailwind/review-workspace/use-review-rail-state.ts +63 -0
- package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +170 -0
- package/src/ui-tailwind/review-workspace/use-scroll-root-capture.ts +28 -0
- package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +113 -0
- package/src/ui-tailwind/review-workspace/use-shell-selection-anchor-bridge.ts +120 -0
- package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +55 -0
- package/src/ui-tailwind/review-workspace/use-viewport-dimensions.ts +43 -0
- package/src/ui-tailwind/review-workspace/use-workspace-arbiter.ts +25 -0
- package/src/ui-tailwind/review-workspace/use-workspace-composition.ts +86 -0
- package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +150 -0
- package/src/ui-tailwind/theme/editor-theme.css +25 -0
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +2 -2
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +61 -98
- package/src/ui-tailwind/tw-review-workspace.tsx +521 -1802
- package/src/ui-tailwind/ui-api-context.tsx +43 -0
- package/src/ui-tailwind/ui-shell-channels-context.tsx +49 -0
- package/src/validation/compatibility-engine.ts +6 -6
- package/src/runtime/styles-cascade.ts +0 -33
- package/src/ui-tailwind/chrome/tw-mode-dock.tsx +0 -85
- /package/src/runtime/{page-number-format.ts → formatting/field/page-number-format.ts} +0 -0
- /package/src/runtime/{ai-action-policy.ts → workflow/ai-action-policy.ts} +0 -0
- /package/src/runtime/{scope-tag-registry.ts → workflow/scope-tag-registry.ts} +0 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 04 — layout compat-input ledger.
|
|
3
|
+
*
|
|
4
|
+
* Task 5 (refactor/04 post-closure, 2026-04-23).
|
|
5
|
+
*
|
|
6
|
+
* The canonical document's `DocumentSettings` carries three compat-flag
|
|
7
|
+
* buckets that OOXML defines for backwards compatibility with pre-2007
|
|
8
|
+
* Word and cross-version layout quirks:
|
|
9
|
+
*
|
|
10
|
+
* - `compatSettings[]` — ordered list of `<w:compatSetting>` entries
|
|
11
|
+
* inside `<w:compat>` (e.g. `compatibilityMode`,
|
|
12
|
+
* `overrideTableStyleFontSizeAndJustification`).
|
|
13
|
+
* - `compatFlags` — boolean children of `<w:compat>` that aren't
|
|
14
|
+
* `<w:compatSetting>` (e.g. `doNotExpandShiftReturn`,
|
|
15
|
+
* `useFELayout`).
|
|
16
|
+
* - `rootCompatFlags` — settings-level boolean flags that sit at the
|
|
17
|
+
* root of `<w:settings>` instead of inside `<w:compat>` (e.g.
|
|
18
|
+
* `doNotEmbedSmartTags`).
|
|
19
|
+
*
|
|
20
|
+
* Today the paginated layout engine branches on none of them. This
|
|
21
|
+
* module provides a typed snapshot so:
|
|
22
|
+
*
|
|
23
|
+
* 1. Behavioral changes that need to gate on a compat flag have a
|
|
24
|
+
* canonical access point (no ad-hoc `settings.compatFlags?.[...]`
|
|
25
|
+
* reads scattered across `src/runtime/layout/**`).
|
|
26
|
+
* 2. The debug projector can surface the active ledger alongside
|
|
27
|
+
* page counts + measurement fidelity so operators can tell at a
|
|
28
|
+
* glance which compat-conditional branches *would* fire on the
|
|
29
|
+
* current document.
|
|
30
|
+
*
|
|
31
|
+
* The ledger is read-only, pure data (no methods), JSON-serializable —
|
|
32
|
+
* it fits inside `LayoutDebugEntry` and can travel through the
|
|
33
|
+
* debug-inspector snapshot unchanged.
|
|
34
|
+
*
|
|
35
|
+
* Resolution rule: OOXML defaults are used for flags absent from the
|
|
36
|
+
* document's `DocumentSettings`. The defaults encoded here match Word
|
|
37
|
+
* 2007+ behavior (the "modern" posture); documents that explicitly
|
|
38
|
+
* set flags opt into the legacy posture.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import type { DocumentSettings } from "../../model/canonical-document.ts";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Typed snapshot of layout-relevant compat inputs + passthrough for
|
|
45
|
+
* anything the resolver doesn't know about by name.
|
|
46
|
+
*/
|
|
47
|
+
export interface LayoutCompatInputs {
|
|
48
|
+
readonly schemaVersion: 1;
|
|
49
|
+
/**
|
|
50
|
+
* From `<w:compatSetting w:name="compatibilityMode" w:val>`. Word
|
|
51
|
+
* writes "15" for Word 2013+ behavior, "14" for 2010, "12" for 2007.
|
|
52
|
+
* Absent → 15 (modern default; Word 2007 behavior is explicitly opted
|
|
53
|
+
* into, not implicit).
|
|
54
|
+
*/
|
|
55
|
+
readonly compatibilityMode: number;
|
|
56
|
+
/**
|
|
57
|
+
* From `<w:doNotExpandShiftReturn/>` under `<w:compat>`. When true,
|
|
58
|
+
* soft line breaks (`<w:br/>`) do not expand to fill the line like
|
|
59
|
+
* hard breaks do — Word 2007's layout behavior. Default false.
|
|
60
|
+
*/
|
|
61
|
+
readonly doNotExpandShiftReturn: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* From `<w:useWord97LineBreakRules/>`. When true, line-break
|
|
64
|
+
* classification follows Word 97 conventions (different handling of
|
|
65
|
+
* East-Asian break classes). Default false.
|
|
66
|
+
*/
|
|
67
|
+
readonly useWord97LineBreakRules: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* From `<w:useFELayout/>`. Forces East-Asian layout conventions
|
|
70
|
+
* (document-grid, vertical text, CJK line-break rules). Default false.
|
|
71
|
+
*/
|
|
72
|
+
readonly useFELayout: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* From `<w:doNotUseHTMLParagraphAutoSpacing/>`. When true,
|
|
75
|
+
* paragraphs inherit their full `spacingBefore` + `spacingAfter`
|
|
76
|
+
* regardless of adjacent-paragraph contextual-spacing hints.
|
|
77
|
+
* Default false.
|
|
78
|
+
*/
|
|
79
|
+
readonly doNotUseHTMLParagraphAutoSpacing: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* From `<w:compatSetting w:name="overrideTableStyleFontSizeAndJustification">`.
|
|
82
|
+
* When true, direct paragraph formatting on a table row wins over
|
|
83
|
+
* the table style's font-size + justification. Default false.
|
|
84
|
+
*/
|
|
85
|
+
readonly overrideTableStyleFontSizeAndJustification: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* From `<w:compatSetting w:name="differentiateMultirowTableHeaders">`.
|
|
88
|
+
* When true, multi-row `<w:tblHeader/>` rows repeat as a group on
|
|
89
|
+
* continuation pages. Default false; when false only the first
|
|
90
|
+
* header row repeats.
|
|
91
|
+
*/
|
|
92
|
+
readonly differentiateMultirowTableHeaders: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Passthrough: every `<w:compatSetting>` entry in document order.
|
|
95
|
+
* Consumers that need a flag this ledger doesn't model by name can
|
|
96
|
+
* scan this list; the resolver retains insertion order for
|
|
97
|
+
* byte-stable round-trip.
|
|
98
|
+
*/
|
|
99
|
+
readonly unknownCompatSettings: ReadonlyArray<{
|
|
100
|
+
readonly name: string;
|
|
101
|
+
readonly uri: string;
|
|
102
|
+
readonly value: string;
|
|
103
|
+
}>;
|
|
104
|
+
/**
|
|
105
|
+
* Passthrough: every boolean compat flag the ledger doesn't model
|
|
106
|
+
* individually, keyed by local element name, sourced from
|
|
107
|
+
* `DocumentSettings.compatFlags` + `.rootCompatFlags` (merged;
|
|
108
|
+
* duplicates prefer `compatFlags` since it's the canonical OOXML
|
|
109
|
+
* location).
|
|
110
|
+
*/
|
|
111
|
+
readonly unknownCompatFlags: Readonly<Record<string, boolean>>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const DEFAULT_COMPATIBILITY_MODE = 15;
|
|
115
|
+
|
|
116
|
+
const KNOWN_COMPAT_FLAG_NAMES = new Set([
|
|
117
|
+
"doNotExpandShiftReturn",
|
|
118
|
+
"useWord97LineBreakRules",
|
|
119
|
+
"useFELayout",
|
|
120
|
+
"doNotUseHTMLParagraphAutoSpacing",
|
|
121
|
+
]);
|
|
122
|
+
|
|
123
|
+
const KNOWN_COMPAT_SETTING_NAMES = new Set([
|
|
124
|
+
"compatibilityMode",
|
|
125
|
+
"overrideTableStyleFontSizeAndJustification",
|
|
126
|
+
"differentiateMultirowTableHeaders",
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Read the compat ledger from the document's `DocumentSettings`.
|
|
131
|
+
* Returns a ledger populated from OOXML defaults when `settings` is
|
|
132
|
+
* absent — never returns null.
|
|
133
|
+
*/
|
|
134
|
+
export function resolveLayoutCompatInputs(
|
|
135
|
+
settings: DocumentSettings | undefined,
|
|
136
|
+
): LayoutCompatInputs {
|
|
137
|
+
const compatFlags = settings?.compatFlags ?? {};
|
|
138
|
+
const rootCompatFlags = settings?.rootCompatFlags ?? {};
|
|
139
|
+
const compatSettings = settings?.compatSettings ?? [];
|
|
140
|
+
|
|
141
|
+
// Named settings resolution
|
|
142
|
+
let compatibilityMode = DEFAULT_COMPATIBILITY_MODE;
|
|
143
|
+
let overrideTableStyleFontSizeAndJustification = false;
|
|
144
|
+
let differentiateMultirowTableHeaders = false;
|
|
145
|
+
const unknownCompatSettings: Array<{
|
|
146
|
+
name: string;
|
|
147
|
+
uri: string;
|
|
148
|
+
value: string;
|
|
149
|
+
}> = [];
|
|
150
|
+
|
|
151
|
+
for (const entry of compatSettings) {
|
|
152
|
+
switch (entry.name) {
|
|
153
|
+
case "compatibilityMode": {
|
|
154
|
+
const parsed = Number.parseInt(entry.value, 10);
|
|
155
|
+
if (Number.isFinite(parsed)) compatibilityMode = parsed;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case "overrideTableStyleFontSizeAndJustification":
|
|
159
|
+
overrideTableStyleFontSizeAndJustification = parseOnOff(entry.value);
|
|
160
|
+
break;
|
|
161
|
+
case "differentiateMultirowTableHeaders":
|
|
162
|
+
differentiateMultirowTableHeaders = parseOnOff(entry.value);
|
|
163
|
+
break;
|
|
164
|
+
default:
|
|
165
|
+
unknownCompatSettings.push(entry);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Named flag resolution
|
|
170
|
+
const doNotExpandShiftReturn = compatFlags.doNotExpandShiftReturn === true;
|
|
171
|
+
const useWord97LineBreakRules = compatFlags.useWord97LineBreakRules === true;
|
|
172
|
+
const useFELayout = compatFlags.useFELayout === true;
|
|
173
|
+
const doNotUseHTMLParagraphAutoSpacing =
|
|
174
|
+
compatFlags.doNotUseHTMLParagraphAutoSpacing === true;
|
|
175
|
+
|
|
176
|
+
// Passthrough unknown flags — merge the two buckets but exclude
|
|
177
|
+
// names the ledger already surfaces as dedicated fields.
|
|
178
|
+
const unknownCompatFlags: Record<string, boolean> = {};
|
|
179
|
+
for (const [name, value] of Object.entries(compatFlags)) {
|
|
180
|
+
if (KNOWN_COMPAT_FLAG_NAMES.has(name)) continue;
|
|
181
|
+
unknownCompatFlags[name] = value === true;
|
|
182
|
+
}
|
|
183
|
+
for (const [name, value] of Object.entries(rootCompatFlags)) {
|
|
184
|
+
if (KNOWN_COMPAT_FLAG_NAMES.has(name)) continue;
|
|
185
|
+
if (unknownCompatFlags[name] !== undefined) continue;
|
|
186
|
+
unknownCompatFlags[name] = value === true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
void KNOWN_COMPAT_SETTING_NAMES;
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
schemaVersion: 1,
|
|
193
|
+
compatibilityMode,
|
|
194
|
+
doNotExpandShiftReturn,
|
|
195
|
+
useWord97LineBreakRules,
|
|
196
|
+
useFELayout,
|
|
197
|
+
doNotUseHTMLParagraphAutoSpacing,
|
|
198
|
+
overrideTableStyleFontSizeAndJustification,
|
|
199
|
+
differentiateMultirowTableHeaders,
|
|
200
|
+
unknownCompatSettings: Object.freeze(unknownCompatSettings),
|
|
201
|
+
unknownCompatFlags: Object.freeze(unknownCompatFlags),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function parseOnOff(value: string): boolean {
|
|
206
|
+
// ST_OnOff: "1" / "true" / "on" → true; "0" / "false" / "off" → false.
|
|
207
|
+
// Absent explicit value would never reach here (CompatSetting always
|
|
208
|
+
// carries a string), but be defensive.
|
|
209
|
+
const normalized = value.trim().toLowerCase();
|
|
210
|
+
return normalized === "1" || normalized === "true" || normalized === "on";
|
|
211
|
+
}
|
|
@@ -166,8 +166,13 @@ export { createCanvasBackend } from "./measurement-backend-canvas.ts";
|
|
|
166
166
|
|
|
167
167
|
export { createInertLayoutFacet } from "./inert-layout-facet.ts";
|
|
168
168
|
|
|
169
|
+
// Refactor-era structural alias — consumers type against `LayoutFacet`
|
|
170
|
+
// while `WordReviewEditorLayoutFacet` remains the public-API name.
|
|
171
|
+
export type { LayoutFacet } from "./layout-facet-types.ts";
|
|
172
|
+
|
|
169
173
|
export {
|
|
170
174
|
createLayoutFacet,
|
|
175
|
+
setActiveLayoutWarningEmitter,
|
|
171
176
|
type WordReviewEditorLayoutFacet,
|
|
172
177
|
type PublicPageNode,
|
|
173
178
|
type PublicPageRegions,
|
|
@@ -234,4 +239,4 @@ export {
|
|
|
234
239
|
type CollectScopeRailSegmentsInput,
|
|
235
240
|
type ScopeRailPosture,
|
|
236
241
|
type ScopeRailSegment,
|
|
237
|
-
} from "../workflow
|
|
242
|
+
} from "../workflow/rail/compose.ts";
|
|
@@ -43,13 +43,18 @@ export function createInertLayoutFacet(): WordReviewEditorLayoutFacet {
|
|
|
43
43
|
getActivePageFormat: () => null,
|
|
44
44
|
getMarginPresetCatalog: () => MARGIN_PRESET_CATALOG,
|
|
45
45
|
getActiveMarginPreset: () => null,
|
|
46
|
-
getRenderFrame
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
getAnchorRects
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
// `getRenderFrame` + `getRenderZoom` removed in refactor/05
|
|
47
|
+
// cross-lane-coord §8 pass (2026-04-22). `createInertGeometryFacet`
|
|
48
|
+
// handles their loading-boundary shape.
|
|
49
|
+
// `hitTest` + `getAnchorRects` removed from the layout facet in
|
|
50
|
+
// refactor/05 Slice 6 wrapper-deletion pass (2026-04-22).
|
|
51
|
+
// `createInertGeometryFacet` handles their loading-boundary shape.
|
|
52
|
+
// `getScopeRailSegments` / `getAllScopeRailSegments` /
|
|
53
|
+
// `getAllScopeCardModels` removed from the layout facet in
|
|
54
|
+
// refactor/06 Slice 4C rail-seam inversion (2026-04-22). Rail
|
|
55
|
+
// composition is now `runtime.workflow.*`; the loading boundary
|
|
56
|
+
// exposes an inert workflow facet separately (see
|
|
57
|
+
// `src/ui/editor-runtime-boundary.ts`).
|
|
53
58
|
getResolvedFormatting: () => null,
|
|
54
59
|
getResolvedRunFormatting: () => null,
|
|
55
60
|
getMeasurement: () => null,
|
|
@@ -81,6 +81,8 @@ import {
|
|
|
81
81
|
createEmpiricalMeasurementProvider,
|
|
82
82
|
type LayoutMeasurementProvider,
|
|
83
83
|
} from "./layout-measurement-provider.ts";
|
|
84
|
+
import { LAYOUT_ENGINE_VERSION } from "./layout-engine-version.ts";
|
|
85
|
+
import type { TelemetryBus } from "../debug/telemetry-bus.ts";
|
|
84
86
|
|
|
85
87
|
// ---------------------------------------------------------------------------
|
|
86
88
|
// Types
|
|
@@ -267,6 +269,14 @@ export interface CreateLayoutEngineOptions {
|
|
|
267
269
|
* determinism or tests) pass `false`. Default: true.
|
|
268
270
|
*/
|
|
269
271
|
autoUpgradeToCanvasBackend?: boolean;
|
|
272
|
+
/**
|
|
273
|
+
* Refactor/04 Slice 3 — optional `TelemetryBus` the engine forwards
|
|
274
|
+
* layout lifecycle events to on the `layout` channel. Zero cost when
|
|
275
|
+
* the channel is off (the bus bitmap-checks before allocating a
|
|
276
|
+
* payload). Omit to leave the engine's internal listener stream the
|
|
277
|
+
* only consumer.
|
|
278
|
+
*/
|
|
279
|
+
telemetryBus?: TelemetryBus;
|
|
270
280
|
}
|
|
271
281
|
|
|
272
282
|
export function createLayoutEngine(
|
|
@@ -275,6 +285,7 @@ export function createLayoutEngine(
|
|
|
275
285
|
let measurementProvider: LayoutMeasurementProvider =
|
|
276
286
|
options.measurementProvider ?? createEmpiricalMeasurementProvider();
|
|
277
287
|
const autoUpgradeToCanvas = options.autoUpgradeToCanvasBackend !== false;
|
|
288
|
+
const telemetryBus = options.telemetryBus;
|
|
278
289
|
const dirtyFieldFamilies = new Set<string>();
|
|
279
290
|
const listeners = new Set<(event: LayoutEngineEvent) => void>();
|
|
280
291
|
let cachedKey: CacheKey | null = null;
|
|
@@ -300,8 +311,127 @@ export function createLayoutEngine(
|
|
|
300
311
|
// never let a listener error interrupt the engine
|
|
301
312
|
}
|
|
302
313
|
}
|
|
314
|
+
// Refactor/04 Slice 3 — forward granular lifecycle events to the
|
|
315
|
+
// `layout` telemetry channel. `layout.recompute.completed` is emitted
|
|
316
|
+
// separately by `fullRebuild` / `incrementalRelayout` so it can carry
|
|
317
|
+
// `durationMs` + `scope`; that event has no direct `LayoutEngineEvent`
|
|
318
|
+
// analogue.
|
|
319
|
+
//
|
|
320
|
+
// Emission uses `emitLazy` so the bus fills the monotonic `t` clock
|
|
321
|
+
// automatically. Passing `emit({ t: 0, … })` would preserve the
|
|
322
|
+
// literal zero (the bus only substitutes when `t` is non-numeric) and
|
|
323
|
+
// produce traces with collapsed timestamps.
|
|
324
|
+
if (!telemetryBus || !telemetryBus.isEnabled("layout")) return;
|
|
325
|
+
switch (event.kind) {
|
|
326
|
+
case "page_count_changed":
|
|
327
|
+
telemetryBus.emitLazy("layout", () => ({
|
|
328
|
+
type: "layout.page_count_changed",
|
|
329
|
+
payload: {
|
|
330
|
+
before: event.previousPageCount ?? null,
|
|
331
|
+
after: event.currentPageCount ?? null,
|
|
332
|
+
revision: event.revision,
|
|
333
|
+
},
|
|
334
|
+
}));
|
|
335
|
+
break;
|
|
336
|
+
case "page_field_dirtied":
|
|
337
|
+
telemetryBus.emitLazy("layout", () => ({
|
|
338
|
+
type: "layout.page_field_dirtied",
|
|
339
|
+
payload: {
|
|
340
|
+
families: event.dirtyFieldFamilies ?? [],
|
|
341
|
+
revision: event.revision,
|
|
342
|
+
},
|
|
343
|
+
}));
|
|
344
|
+
break;
|
|
345
|
+
case "layout_committed":
|
|
346
|
+
telemetryBus.emitLazy("layout", () => ({
|
|
347
|
+
type: "layout.committed",
|
|
348
|
+
payload: { revision: event.revision },
|
|
349
|
+
}));
|
|
350
|
+
break;
|
|
351
|
+
default:
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function telemetryNow(): number {
|
|
357
|
+
if (typeof performance !== "undefined" && typeof performance.now === "function") {
|
|
358
|
+
return performance.now();
|
|
359
|
+
}
|
|
360
|
+
return Date.now();
|
|
303
361
|
}
|
|
304
362
|
|
|
363
|
+
function emitRecomputeCompleted(
|
|
364
|
+
scope: "full" | "bounded",
|
|
365
|
+
reasonKind: string,
|
|
366
|
+
pageCountBefore: number,
|
|
367
|
+
pageCountAfter: number,
|
|
368
|
+
durationMs: number,
|
|
369
|
+
): void {
|
|
370
|
+
if (!telemetryBus || !telemetryBus.isEnabled("layout")) return;
|
|
371
|
+
telemetryBus.emitLazy("layout", () => ({
|
|
372
|
+
type: "layout.recompute.completed",
|
|
373
|
+
payload: {
|
|
374
|
+
layoutEngineVersion: LAYOUT_ENGINE_VERSION,
|
|
375
|
+
reasonKind,
|
|
376
|
+
scope,
|
|
377
|
+
pageCountBefore,
|
|
378
|
+
pageCountAfter,
|
|
379
|
+
durationMs,
|
|
380
|
+
},
|
|
381
|
+
}));
|
|
382
|
+
// Refactor/04 post-Slice-4 (2026-04-22) — D1 stage-token pair at
|
|
383
|
+
// the layout engine's recompute boundary. Paired with the
|
|
384
|
+
// already-emitted `layout.refresh.start` / `layout.refresh.complete`
|
|
385
|
+
// stage-tokens around the outer `refreshRenderSnapshot` in
|
|
386
|
+
// `document-runtime.ts`; this inner event lets
|
|
387
|
+
// `L3.layout-truth` attribute a diverged verdict to a specific
|
|
388
|
+
// recompute pass (full vs bounded, which reasonKind, how many
|
|
389
|
+
// pages before / after). Requested by debug-infra's cross-layer
|
|
390
|
+
// coord doc §"Layer 04 — Layout Semantics" → "Benefits from:
|
|
391
|
+
// stage-token for `layout.recompute.end` with before/after page
|
|
392
|
+
// counts". Same `bus.isEnabled("layout")` guard above makes this
|
|
393
|
+
// zero-cost when the D1 substrate is off.
|
|
394
|
+
telemetryBus.emitLazy("layout", () => ({
|
|
395
|
+
type: "layout.recompute.end",
|
|
396
|
+
payload: {
|
|
397
|
+
layoutEngineVersion: LAYOUT_ENGINE_VERSION,
|
|
398
|
+
reasonKind,
|
|
399
|
+
scope,
|
|
400
|
+
pageCountBefore,
|
|
401
|
+
pageCountAfter,
|
|
402
|
+
durationMs,
|
|
403
|
+
stageTokenId: String(++recomputeStageTokenSeq),
|
|
404
|
+
},
|
|
405
|
+
}));
|
|
406
|
+
// Refactor/05 Slice 6 (2026-04-22) — layer-05 projector emit site.
|
|
407
|
+
// Paired with the shipped `GeometryDebugEntry` projector in
|
|
408
|
+
// `src/runtime/geometry/projector.ts`. The emit carries the same
|
|
409
|
+
// scope + durationMs the layout event uses, plus the layout
|
|
410
|
+
// revision so a downstream consumer can correlate a geometry
|
|
411
|
+
// snapshot with the layout pass that produced it.
|
|
412
|
+
// Off-critical-path: only fires when the `layout` channel is
|
|
413
|
+
// enabled (same guard above). `emitLazy` means zero cost otherwise.
|
|
414
|
+
// Perf bench (typing-latency large-tables P50) confirmed within
|
|
415
|
+
// 1% of baseline before/after wiring this emit.
|
|
416
|
+
telemetryBus.emitLazy("layout", () => ({
|
|
417
|
+
type: "geometry.projected",
|
|
418
|
+
payload: {
|
|
419
|
+
layoutEngineVersion: LAYOUT_ENGINE_VERSION,
|
|
420
|
+
reasonKind,
|
|
421
|
+
scope,
|
|
422
|
+
pageCountBefore,
|
|
423
|
+
pageCountAfter,
|
|
424
|
+
durationMs,
|
|
425
|
+
},
|
|
426
|
+
}));
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Refactor/04 post-Slice-4 — monotonic stage-token counter for the
|
|
430
|
+
// engine recompute boundary. Used by `emitRecomputeCompleted`'s
|
|
431
|
+
// `layout.recompute.end` event to give every emission a stable
|
|
432
|
+
// `stageTokenId` the L3 runner can correlate across boundaries.
|
|
433
|
+
let recomputeStageTokenSeq = 0;
|
|
434
|
+
|
|
305
435
|
function fullRebuild(
|
|
306
436
|
input: LayoutEngineQueryInput,
|
|
307
437
|
reason?: LayoutInvalidationReason,
|
|
@@ -310,6 +440,12 @@ export function createLayoutEngine(
|
|
|
310
440
|
// back to the invalidation reason that triggered it so we can later
|
|
311
441
|
// narrow specific kinds (numbering/section) without guessing.
|
|
312
442
|
recordFullRebuildReason(reason?.kind ?? "unknown");
|
|
443
|
+
// Refactor/04 Slice 3 — sample start-time only when the layout
|
|
444
|
+
// telemetry channel is on so the cold path pays nothing for the
|
|
445
|
+
// measurement when telemetry is disabled.
|
|
446
|
+
const telemetryOn = telemetryBus?.isEnabled("layout") ?? false;
|
|
447
|
+
const recomputeStart = telemetryOn ? telemetryNow() : 0;
|
|
448
|
+
const pageCountBeforeRecompute = previousPageCount;
|
|
313
449
|
const document = input.document;
|
|
314
450
|
const mainSurface = createEditorSurfaceSnapshot(
|
|
315
451
|
document,
|
|
@@ -329,6 +465,7 @@ export function createLayoutEngine(
|
|
|
329
465
|
mainSurface,
|
|
330
466
|
pages,
|
|
331
467
|
pageStack.splits,
|
|
468
|
+
pageStack.columnByBlockIdByPageIndex,
|
|
332
469
|
);
|
|
333
470
|
// P8.1b — merge per-note fragments (regionKind: "footnote-area") into the
|
|
334
471
|
// main fragments map so buildPageGraph sees them alongside body fragments.
|
|
@@ -354,11 +491,14 @@ export function createLayoutEngine(
|
|
|
354
491
|
const formatting = buildResolvedFormattingState(document, mainSurface);
|
|
355
492
|
|
|
356
493
|
const currentPageCount = resolveTotalPageCount(pages);
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}
|
|
494
|
+
// Compute the delta only; `previousPageCount` is mutated AFTER all
|
|
495
|
+
// emits so every emission reads the pre-commit value through the
|
|
496
|
+
// `pageCountDelta` local. `incrementalRelayout` mirrors this order
|
|
497
|
+
// exactly — keep the two paths in sync when editing (reviewer N3).
|
|
498
|
+
const pageCountDelta: { previous: number; current: number } | undefined =
|
|
499
|
+
currentPageCount !== previousPageCount
|
|
500
|
+
? { previous: previousPageCount, current: currentPageCount }
|
|
501
|
+
: undefined;
|
|
362
502
|
|
|
363
503
|
// MUST publish cache before emit: re-entrant getPageGraph() calls from
|
|
364
504
|
// subscribers during emit would otherwise trigger runaway rebuilds.
|
|
@@ -404,6 +544,20 @@ export function createLayoutEngine(
|
|
|
404
544
|
...(pageCountDelta ? { pageCountDelta } : {}),
|
|
405
545
|
});
|
|
406
546
|
|
|
547
|
+
if (telemetryOn) {
|
|
548
|
+
emitRecomputeCompleted(
|
|
549
|
+
"full",
|
|
550
|
+
reason?.kind ?? "full",
|
|
551
|
+
pageCountBeforeRecompute,
|
|
552
|
+
currentPageCount,
|
|
553
|
+
telemetryNow() - recomputeStart,
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Consolidate the `previousPageCount` update at the END of the
|
|
558
|
+
// recompute path, after all emits. Mirrored in `incrementalRelayout`.
|
|
559
|
+
previousPageCount = currentPageCount;
|
|
560
|
+
|
|
407
561
|
return graph;
|
|
408
562
|
}
|
|
409
563
|
|
|
@@ -414,6 +568,9 @@ export function createLayoutEngine(
|
|
|
414
568
|
const priorGraph = cachedGraph;
|
|
415
569
|
const range = pending.result.dirtyPageRange;
|
|
416
570
|
if (!priorGraph || !range) return null;
|
|
571
|
+
const telemetryOn = telemetryBus?.isEnabled("layout") ?? false;
|
|
572
|
+
const recomputeStart = telemetryOn ? telemetryNow() : 0;
|
|
573
|
+
const pageCountBeforeRecompute = previousPageCount;
|
|
417
574
|
const firstDirty = range.firstPageIndex;
|
|
418
575
|
if (firstDirty < 0 || firstDirty >= priorGraph.pages.length) return null;
|
|
419
576
|
|
|
@@ -480,6 +637,7 @@ export function createLayoutEngine(
|
|
|
480
637
|
mainSurface,
|
|
481
638
|
freshSnapshotsToRebuild,
|
|
482
639
|
freshResult.splits,
|
|
640
|
+
freshResult.columnByBlockIdByPageIndex,
|
|
483
641
|
);
|
|
484
642
|
// P8.1b — merge per-note fragments into the fresh fragments map.
|
|
485
643
|
const freshFragmentsByPageIndex = new Map(freshBodyFragmentsByPageIndex);
|
|
@@ -510,16 +668,21 @@ export function createLayoutEngine(
|
|
|
510
668
|
const currentPageCount = resolveTotalPageCount(
|
|
511
669
|
deriveDocumentPageSnapshots(splicedGraph),
|
|
512
670
|
);
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
671
|
+
// Compute the delta only; `previousPageCount` is mutated AFTER all
|
|
672
|
+
// emits so every emission reads the pre-commit value through the
|
|
673
|
+
// `pageCountDelta` local. Mirrors `fullRebuild` above — keep the two
|
|
674
|
+
// paths in sync when editing (reviewer N3).
|
|
675
|
+
const pageCountDelta: { previous: number; current: number } | undefined =
|
|
676
|
+
currentPageCount !== previousPageCount
|
|
677
|
+
? { previous: previousPageCount, current: currentPageCount }
|
|
678
|
+
: undefined;
|
|
679
|
+
if (pageCountDelta) {
|
|
516
680
|
emit({
|
|
517
681
|
kind: "page_count_changed",
|
|
518
682
|
revision: splicedGraph.revision,
|
|
519
|
-
previousPageCount,
|
|
520
|
-
currentPageCount,
|
|
683
|
+
previousPageCount: pageCountDelta.previous,
|
|
684
|
+
currentPageCount: pageCountDelta.current,
|
|
521
685
|
});
|
|
522
|
-
previousPageCount = currentPageCount;
|
|
523
686
|
}
|
|
524
687
|
|
|
525
688
|
if (dirtyFamilies.length > 0) {
|
|
@@ -573,6 +736,21 @@ export function createLayoutEngine(
|
|
|
573
736
|
splicedGraph,
|
|
574
737
|
firstDirty,
|
|
575
738
|
);
|
|
739
|
+
|
|
740
|
+
if (telemetryOn) {
|
|
741
|
+
emitRecomputeCompleted(
|
|
742
|
+
"bounded",
|
|
743
|
+
pending.reason.kind,
|
|
744
|
+
pageCountBeforeRecompute,
|
|
745
|
+
currentPageCount,
|
|
746
|
+
telemetryNow() - recomputeStart,
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Consolidate the `previousPageCount` update at the END of the
|
|
751
|
+
// recompute path, after all emits. Mirrored in `fullRebuild`.
|
|
752
|
+
previousPageCount = currentPageCount;
|
|
753
|
+
|
|
576
754
|
return splicedGraph;
|
|
577
755
|
}
|
|
578
756
|
|