@beyondwork/docx-react-component 1.0.66 → 1.0.69
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 -931
- 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 -4795
- 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
|
@@ -42,33 +42,50 @@ export interface LayoutTabStop {
|
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Average character width in twips per half-point of font size.
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
*
|
|
46
|
+
* Calibrated 2026-04-23 against published OpenType font-metrics average
|
|
47
|
+
* advance widths (xAvgCharWidth / (unitsPerEm × 2)) rather than the
|
|
48
|
+
* pre-calibration "empirical" values that were systematically ~80%
|
|
49
|
+
* higher than real Word measurements. The pre-calibration values
|
|
50
|
+
* produced a 50-73% page overcount vs LibreOffice oracle baselines on
|
|
51
|
+
* CCEP contracts; trusted-oracle verification post truth-baseline's
|
|
52
|
+
* 2026-04-23 re-extraction confirmed the runtime-side miscalibration
|
|
53
|
+
* (cross-layer-coord-04.md §1.15 / §1.18).
|
|
54
|
+
*
|
|
55
|
+
* Unit: twips per half-point of font size. At an `effectiveSize` of 22
|
|
56
|
+
* (11-pt text), Calibri's factor of 5.0 yields 110 twips per character —
|
|
57
|
+
* which matches Calibri's published `xAvgCharWidth = 1037 / 2048 em`
|
|
58
|
+
* (~0.506 em = 5.57 pt = 111 twips at 11 pt). The same derivation gives
|
|
59
|
+
* the other values.
|
|
60
|
+
*
|
|
61
|
+
* The canvas backend does not consult this table — it measures real
|
|
62
|
+
* glyph widths via Canvas2D `measureText`. This table is only the
|
|
63
|
+
* empirical / SSR fallback used by the layout engine when a canvas
|
|
64
|
+
* context is unavailable.
|
|
48
65
|
*/
|
|
49
66
|
const FONT_AVG_CHAR_WIDTH: Record<string, number> = {
|
|
50
|
-
// Proportional serif
|
|
51
|
-
"times new roman":
|
|
52
|
-
"georgia":
|
|
53
|
-
"garamond":
|
|
54
|
-
"book antiqua":
|
|
55
|
-
"palatino linotype":
|
|
56
|
-
"cambria":
|
|
57
|
-
// Proportional sans-serif
|
|
58
|
-
"arial":
|
|
59
|
-
"calibri":
|
|
60
|
-
"helvetica":
|
|
61
|
-
"verdana":
|
|
62
|
-
"tahoma":
|
|
63
|
-
"segoe ui":
|
|
64
|
-
"trebuchet ms":
|
|
65
|
-
// Monospace
|
|
66
|
-
"courier new":
|
|
67
|
-
"consolas":
|
|
68
|
-
"lucida console":
|
|
67
|
+
// Proportional serif (xAvgCharWidth ≈ 0.48-0.52 em for body text)
|
|
68
|
+
"times new roman": 5.1,
|
|
69
|
+
"georgia": 5.6,
|
|
70
|
+
"garamond": 4.9,
|
|
71
|
+
"book antiqua": 5.3,
|
|
72
|
+
"palatino linotype": 5.5,
|
|
73
|
+
"cambria": 5.3,
|
|
74
|
+
// Proportional sans-serif (xAvgCharWidth ≈ 0.50-0.60 em)
|
|
75
|
+
"arial": 5.8,
|
|
76
|
+
"calibri": 5.0,
|
|
77
|
+
"helvetica": 5.8,
|
|
78
|
+
"verdana": 6.7,
|
|
79
|
+
"tahoma": 5.9,
|
|
80
|
+
"segoe ui": 5.4,
|
|
81
|
+
"trebuchet ms": 5.6,
|
|
82
|
+
// Monospace (every glyph ≈ 0.60-0.67 em)
|
|
83
|
+
"courier new": 6.7,
|
|
84
|
+
"consolas": 6.1,
|
|
85
|
+
"lucida console": 6.7,
|
|
69
86
|
};
|
|
70
87
|
|
|
71
|
-
const DEFAULT_FONT_AVG_CHAR_WIDTH =
|
|
88
|
+
const DEFAULT_FONT_AVG_CHAR_WIDTH = 5.5; // reasonable fallback (between serif + sans proportional)
|
|
72
89
|
const DEFAULT_FONT_SIZE_HALF_POINTS = 24; // 12pt
|
|
73
90
|
const DEFAULT_LINE_HEIGHT_FACTOR = 1.15; // Word default for Calibri body
|
|
74
91
|
const TWIPS_PER_POINT = 20;
|
|
@@ -277,15 +294,34 @@ function resolveIndentation(
|
|
|
277
294
|
function resolveDominantFont(
|
|
278
295
|
block: Extract<SurfaceBlockSnapshot, { kind: "paragraph" }>,
|
|
279
296
|
): { fontSizeHalfPoints: number; avgCharWidth: number; fontFamily: string | undefined } {
|
|
280
|
-
//
|
|
297
|
+
// L03 → L04 contract: read formatting from the canonical L03 output
|
|
298
|
+
// (`segment.resolvedRunFormatting`) first; fall back to the surface
|
|
299
|
+
// projection intermediate (`segment.markAttrs`) only when the
|
|
300
|
+
// resolved value is absent (viewport-culled block).
|
|
301
|
+
//
|
|
302
|
+
// Architecture §C2: layout must consume formatting from L03, not
|
|
303
|
+
// re-derive it from a rendering intermediate. Research doc §"Where
|
|
304
|
+
// to combine layers for performance" permits layout to ride the
|
|
305
|
+
// surface snapshot for locality, but the authoritative values must
|
|
306
|
+
// come from L03.
|
|
281
307
|
let fontFamily: string | undefined;
|
|
282
308
|
let fontSizeHalfPoints: number | undefined;
|
|
283
309
|
let maxTextLength = 0;
|
|
284
310
|
|
|
285
311
|
for (const segment of block.segments) {
|
|
286
312
|
if (segment.kind !== "text") continue;
|
|
287
|
-
const
|
|
288
|
-
|
|
313
|
+
const resolved = segment.resolvedRunFormatting;
|
|
314
|
+
// Prefer L03 resolved output over projected markAttrs. The cascade
|
|
315
|
+
// already layered docDefaults → paragraph style → character style →
|
|
316
|
+
// direct overrides; the markAttrs fallback only applies when the
|
|
317
|
+
// block was viewport-culled (resolvedRunFormatting absent).
|
|
318
|
+
const segFontFamily =
|
|
319
|
+
resolved?.fontFamilyAscii ??
|
|
320
|
+
resolved?.fontFamily ??
|
|
321
|
+
resolved?.fontFamilyHAnsi ??
|
|
322
|
+
segment.markAttrs?.fontFamily;
|
|
323
|
+
const segFontSize =
|
|
324
|
+
resolved?.fontSizeHalfPoints ?? segment.markAttrs?.fontSize;
|
|
289
325
|
const textLength = segment.text.length;
|
|
290
326
|
|
|
291
327
|
if (textLength > maxTextLength) {
|
|
@@ -457,3 +493,99 @@ export function resolveNumberingPrefixLength(
|
|
|
457
493
|
: 0;
|
|
458
494
|
return prefix.length + suffix;
|
|
459
495
|
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Build a `Map<RunId, ResolvedRunFormatting>` for a paragraph block so
|
|
499
|
+
* the measurement provider's `measureLineFragments` call receives real
|
|
500
|
+
* per-run metrics instead of a placeholder empty map. Task 3 of
|
|
501
|
+
* refactor/04 post-closure (2026-04-23).
|
|
502
|
+
*
|
|
503
|
+
* Stable key convention: `${blockId}:${segmentId}`, matching the
|
|
504
|
+
* canvas backend's lookup (`measurement-backend-canvas.ts:156`).
|
|
505
|
+
*
|
|
506
|
+
* Source precedence per text segment:
|
|
507
|
+
* 1. `segment.resolvedRunFormatting` (L03 canonical cascade output)
|
|
508
|
+
* 2. `segment.markAttrs` + `segment.marks` (surface-projection intermediate)
|
|
509
|
+
*
|
|
510
|
+
* The shape emitted matches `ResolvedRunFormatting` from
|
|
511
|
+
* `resolved-formatting-document.ts` (structural compatibility — no
|
|
512
|
+
* import cycle). Canvas backend consumes `fontFamily`,
|
|
513
|
+
* `fontSizeHalfPoints`, `bold`, `italic`. The rest are populated
|
|
514
|
+
* best-effort for future consumers.
|
|
515
|
+
*/
|
|
516
|
+
export function buildRunFormattingMap(
|
|
517
|
+
block: Extract<SurfaceBlockSnapshot, { kind: "paragraph" }>,
|
|
518
|
+
): Map<
|
|
519
|
+
string,
|
|
520
|
+
{
|
|
521
|
+
fontFamily?: string;
|
|
522
|
+
fontSizeHalfPoints?: number;
|
|
523
|
+
bold: boolean;
|
|
524
|
+
italic: boolean;
|
|
525
|
+
underline: boolean;
|
|
526
|
+
strikethrough: boolean;
|
|
527
|
+
color?: string;
|
|
528
|
+
highlight?: string;
|
|
529
|
+
verticalAlign: "baseline" | "superscript" | "subscript";
|
|
530
|
+
}
|
|
531
|
+
> {
|
|
532
|
+
const runs = new Map<
|
|
533
|
+
string,
|
|
534
|
+
{
|
|
535
|
+
fontFamily?: string;
|
|
536
|
+
fontSizeHalfPoints?: number;
|
|
537
|
+
bold: boolean;
|
|
538
|
+
italic: boolean;
|
|
539
|
+
underline: boolean;
|
|
540
|
+
strikethrough: boolean;
|
|
541
|
+
color?: string;
|
|
542
|
+
highlight?: string;
|
|
543
|
+
verticalAlign: "baseline" | "superscript" | "subscript";
|
|
544
|
+
}
|
|
545
|
+
>();
|
|
546
|
+
for (const segment of block.segments) {
|
|
547
|
+
if (segment.kind !== "text") continue;
|
|
548
|
+
const resolved = segment.resolvedRunFormatting;
|
|
549
|
+
const markAttrs = segment.markAttrs ?? {};
|
|
550
|
+
const markSet = new Set(segment.marks ?? []);
|
|
551
|
+
const fontFamily =
|
|
552
|
+
resolved?.fontFamilyAscii ??
|
|
553
|
+
resolved?.fontFamily ??
|
|
554
|
+
resolved?.fontFamilyHAnsi ??
|
|
555
|
+
(typeof markAttrs.fontFamily === "string" ? markAttrs.fontFamily : undefined);
|
|
556
|
+
const fontSizeHalfPoints =
|
|
557
|
+
resolved?.fontSizeHalfPoints ??
|
|
558
|
+
(typeof markAttrs.fontSize === "number" ? markAttrs.fontSize : undefined);
|
|
559
|
+
const bold = resolved?.bold ?? markSet.has("bold");
|
|
560
|
+
const italic = resolved?.italic ?? markSet.has("italic");
|
|
561
|
+
const underline =
|
|
562
|
+
(resolved?.underline !== undefined && resolved.underline !== "none") ||
|
|
563
|
+
markSet.has("underline");
|
|
564
|
+
const strikethrough =
|
|
565
|
+
resolved?.strikethrough === true ||
|
|
566
|
+
markSet.has("strikethrough") ||
|
|
567
|
+
markSet.has("doubleStrikethrough");
|
|
568
|
+
const verticalAlign: "baseline" | "superscript" | "subscript" =
|
|
569
|
+
resolved?.verticalAlign === "superscript" || markSet.has("superscript")
|
|
570
|
+
? "superscript"
|
|
571
|
+
: resolved?.verticalAlign === "subscript" || markSet.has("subscript")
|
|
572
|
+
? "subscript"
|
|
573
|
+
: "baseline";
|
|
574
|
+
runs.set(`${block.blockId}:${segment.segmentId}`, {
|
|
575
|
+
...(fontFamily ? { fontFamily } : {}),
|
|
576
|
+
...(fontSizeHalfPoints !== undefined ? { fontSizeHalfPoints } : {}),
|
|
577
|
+
bold: Boolean(bold),
|
|
578
|
+
italic: Boolean(italic),
|
|
579
|
+
underline: Boolean(underline),
|
|
580
|
+
strikethrough: Boolean(strikethrough),
|
|
581
|
+
...(typeof markAttrs.textColor === "string"
|
|
582
|
+
? { color: markAttrs.textColor }
|
|
583
|
+
: {}),
|
|
584
|
+
...(typeof markAttrs.backgroundColor === "string"
|
|
585
|
+
? { highlight: markAttrs.backgroundColor }
|
|
586
|
+
: {}),
|
|
587
|
+
verticalAlign,
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
return runs;
|
|
591
|
+
}
|
|
@@ -23,7 +23,7 @@ import type {
|
|
|
23
23
|
SurfaceTableRowSnapshot,
|
|
24
24
|
} from "../../api/public-types";
|
|
25
25
|
import type { TableStyleConditionalRegion } from "../../model/canonical-document.ts";
|
|
26
|
-
import type { ResolvedTableStyleResolution } from "../table-style-resolver.ts";
|
|
26
|
+
import type { ResolvedTableStyleResolution } from "../formatting/table-style-resolver.ts";
|
|
27
27
|
|
|
28
28
|
// ─── Public shapes ───────────────────────────────────────────────────────────
|
|
29
29
|
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { EditorSurfaceSnapshot } from "../../api/public-types";
|
|
2
2
|
import type { CanonicalDocument } from "../../model/canonical-document.ts";
|
|
3
3
|
import type { CompatibilityReport } from "../../core/state/editor-state.ts";
|
|
4
|
-
|
|
4
|
+
// Pass 2 (2026-04-22) — `RuntimePageGraph` relocated out of
|
|
5
|
+
// `src/runtime/layout/` to `src/model/layout/runtime-page-graph-types.ts`
|
|
6
|
+
// so this envelope (and the `tryReadLaycacheEnvelope` probe that
|
|
7
|
+
// produces it) can be consumed from `src/session/**` without a
|
|
8
|
+
// `src/runtime/**` transit. See `docs/plans/cross-layer-coord-04.md §1.12`.
|
|
9
|
+
import type { RuntimePageGraph } from "../../model/layout/runtime-page-graph-types.ts";
|
|
5
10
|
|
|
6
11
|
/**
|
|
7
12
|
* L7 Phase 2.5 — prerender cache envelope shape.
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { EditorSurfaceSnapshot } from "../../api/public-types";
|
|
2
2
|
import { createSelectionSnapshot } from "../../core/state/editor-state.ts";
|
|
3
|
-
import { loadDocxEditorSessionAsync } from "../../io/docx-session.ts";
|
|
4
|
-
import { createLoadScheduler } from "../../io/load-scheduler.ts";
|
|
5
3
|
import { readOpcPackage } from "../../io/opc/package-reader.ts";
|
|
6
4
|
import { writeOpcPackage } from "../../io/opc/package-writer.ts";
|
|
5
|
+
import { DocxSession } from "../../session/index.ts";
|
|
7
6
|
import {
|
|
8
7
|
LAYCACHE_SCHEMA_VERSION,
|
|
9
8
|
LAYOUT_ENGINE_VERSION,
|
|
@@ -44,8 +43,8 @@ import { canonicalizeGraph } from "./graph-canonicalize.ts";
|
|
|
44
43
|
* but do not enter the envelope.
|
|
45
44
|
*
|
|
46
45
|
* Node-safety:
|
|
47
|
-
* -
|
|
48
|
-
* async loader runs without DOM scheduler.yield.
|
|
46
|
+
* - Forwards `schedulerBackend: "sync"` to `DocxSession.open()` so
|
|
47
|
+
* the async loader runs without DOM scheduler.yield.
|
|
49
48
|
* - Layout engine is constructed with
|
|
50
49
|
* `autoUpgradeToCanvasBackend: false` so the empirical measurement
|
|
51
50
|
* backend is used (no Canvas2D, no node-canvas).
|
|
@@ -118,26 +117,21 @@ export async function prerenderDocument(
|
|
|
118
117
|
const bytes = toUint8Array(input);
|
|
119
118
|
const fontFingerprint = options.fontFingerprint ?? resolveFontFingerprint();
|
|
120
119
|
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
bytes,
|
|
127
|
-
editorBuild: "prerender",
|
|
128
|
-
scheduler,
|
|
129
|
-
});
|
|
130
|
-
} finally {
|
|
131
|
-
scheduler.dispose();
|
|
132
|
-
}
|
|
120
|
+
const openResult = await new DocxSession().open(bytes, {
|
|
121
|
+
documentId: PRERENDER_DOCUMENT_ID,
|
|
122
|
+
editorBuild: "prerender",
|
|
123
|
+
schedulerBackend: "sync",
|
|
124
|
+
});
|
|
133
125
|
|
|
134
|
-
|
|
126
|
+
// Slice 5e-1 promoted `fatalError` onto `OpenResult`, so no legacy
|
|
127
|
+
// view lookup is needed on this path anymore.
|
|
128
|
+
if (openResult.fatalError) {
|
|
135
129
|
throw new Error(
|
|
136
|
-
`prerenderDocument: failed to load input — ${
|
|
130
|
+
`prerenderDocument: failed to load input — ${openResult.fatalError.message ?? "fatal error"}`,
|
|
137
131
|
);
|
|
138
132
|
}
|
|
139
133
|
|
|
140
|
-
// Normalize session-birth timestamps. `
|
|
134
|
+
// Normalize session-birth timestamps. `DocxSession.open()` sets
|
|
141
135
|
// `createdAt`/`updatedAt` from `new Date().toISOString()`; without this
|
|
142
136
|
// override, two sequential prerender calls on identical bytes would
|
|
143
137
|
// produce different envelopes → different customXml bytes → determinism
|
|
@@ -146,8 +140,8 @@ export async function prerenderDocument(
|
|
|
146
140
|
// eliminating the only remaining source of non-determinism. The live
|
|
147
141
|
// session's updatedAt is re-populated by runtime mutations anyway, so
|
|
148
142
|
// the normalized value is irrelevant at runtime.
|
|
149
|
-
const envelope: typeof
|
|
150
|
-
...
|
|
143
|
+
const envelope: typeof openResult.canonicalDocument = {
|
|
144
|
+
...openResult.canonicalDocument,
|
|
151
145
|
createdAt: PRERENDER_NORMALIZED_TIMESTAMP,
|
|
152
146
|
updatedAt: PRERENDER_NORMALIZED_TIMESTAMP,
|
|
153
147
|
};
|
|
@@ -172,7 +166,8 @@ export async function prerenderDocument(
|
|
|
172
166
|
});
|
|
173
167
|
|
|
174
168
|
// Phase 2 Finale C3: pre-compute `compatibilityReport` so the warm Plan B
|
|
175
|
-
// short-circuit
|
|
169
|
+
// short-circuit inside `DocxSession.open()` (which still transits the
|
|
170
|
+
// legacy async loader) can skip the live
|
|
176
171
|
// `buildCompatibilityReport` call (~60–100 ms on extra-large). The report
|
|
177
172
|
// is a pure function of `canonicalDocument` + `generatedAt`; pinning
|
|
178
173
|
// `generatedAt` to `CACHE_NORMALIZED_GENERATED_AT` keeps the envelope
|
|
@@ -196,7 +191,7 @@ export async function prerenderDocument(
|
|
|
196
191
|
|
|
197
192
|
// Plan B B.5 — persistToCustomXml: inject the envelope into the docx's
|
|
198
193
|
// workflow-payload part. Re-parses the OPC package from bytes (~17 ms on
|
|
199
|
-
// extra-large) because `
|
|
194
|
+
// extra-large) because `DocxSession.open()` does not expose
|
|
200
195
|
// `sourcePackage` publicly. Acceptable cost on the one-shot ingest path.
|
|
201
196
|
let docWithCustomXml = bytes;
|
|
202
197
|
let persistedToCustomXml = false;
|
|
@@ -23,7 +23,7 @@ import type {
|
|
|
23
23
|
RevisionDecorationModel,
|
|
24
24
|
RevisionDecorationEntry,
|
|
25
25
|
} from "../../ui/headless/revision-decoration-model.ts";
|
|
26
|
-
import type { ScopeRailSegment } from "../workflow
|
|
26
|
+
import type { ScopeRailSegment } from "../workflow/rail/types.ts";
|
|
27
27
|
import type {
|
|
28
28
|
DecorationIndex,
|
|
29
29
|
RenderAnchorIndex,
|
|
@@ -244,6 +244,26 @@ export interface RenderAnchorIndex {
|
|
|
244
244
|
byScopeId(scopeId: string): readonly RenderFrameRect[];
|
|
245
245
|
byCommentId(commentId: string): RenderFrameRect | null;
|
|
246
246
|
byRevisionId(revisionId: string): RenderFrameRect | null;
|
|
247
|
+
/**
|
|
248
|
+
* Locate an object's exact bounding rect given its object id.
|
|
249
|
+
*
|
|
250
|
+
* Object ids discriminate **the object itself** from **the block that
|
|
251
|
+
* contains it** — for inline images embedded in a paragraph,
|
|
252
|
+
* `byObjectId(imageId)` returns the image's own bbox, not the
|
|
253
|
+
* enclosing paragraph's rect. For block-level drawings (floating
|
|
254
|
+
* anchors resolved to a page slot) the rect matches the drawing's
|
|
255
|
+
* slot.
|
|
256
|
+
*
|
|
257
|
+
* Refactor/05 Slice 7c substrate: the kernel does not yet carry
|
|
258
|
+
* per-object metadata on anchor frames, so this method returns
|
|
259
|
+
* `null` today for every id except those that also happen to be
|
|
260
|
+
* block ids (preserving the Slice-5 `object-handles.ts` fallback).
|
|
261
|
+
* When the layout facet grows an object-specific index (`byObjectId`
|
|
262
|
+
* on the layout graph, Slice 7d follow-up), this method starts
|
|
263
|
+
* returning real object rects automatically — consumers that gate
|
|
264
|
+
* on `result !== null` pick up the upgrade without an API break.
|
|
265
|
+
*/
|
|
266
|
+
byObjectId(objectId: string): RenderFrameRect | null;
|
|
247
267
|
}
|
|
248
268
|
|
|
249
269
|
// ---------------------------------------------------------------------------
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import { MAIN_STORY_TARGET } from "../../core/selection/mapping.ts";
|
|
17
17
|
import { recordPerfSample } from "../../ui-tailwind/editor-surface/perf-probe.ts";
|
|
18
|
+
import { storyTargetKey } from "../story-targeting.ts";
|
|
18
19
|
import type { EditorStoryTarget } from "../../api/public-types";
|
|
19
20
|
import type {
|
|
20
21
|
PublicBlockFragment,
|
|
@@ -25,7 +26,7 @@ import type {
|
|
|
25
26
|
} from "../layout/public-facet.ts";
|
|
26
27
|
import type { CommentDecorationModel } from "../../ui/headless/comment-decoration-model.ts";
|
|
27
28
|
import type { RevisionDecorationModel } from "../../ui/headless/revision-decoration-model.ts";
|
|
28
|
-
import type { ScopeRailSegment } from "../workflow
|
|
29
|
+
import type { ScopeRailSegment } from "../workflow/rail/types.ts";
|
|
29
30
|
import {
|
|
30
31
|
resolveDecorationIndex,
|
|
31
32
|
type LockedRangeInput,
|
|
@@ -719,7 +720,14 @@ function buildAnchorIndex(
|
|
|
719
720
|
pxPerTwip = 1,
|
|
720
721
|
decorationIndex: DecorationIndex = EMPTY_DECORATION_INDEX,
|
|
721
722
|
): RenderAnchorIndex {
|
|
723
|
+
// Main-document runtime offsets flow through `byRuntimeOffset` (body
|
|
724
|
+
// region). Header / footer / footnote stories have offsets LOCAL to
|
|
725
|
+
// their story — they can't share the `byRuntimeOffset` keyspace with
|
|
726
|
+
// the body without collisions. Refactor/05 Slice 7d indexes non-body
|
|
727
|
+
// blocks via `byRuntimeOffsetByStory`, a `storyTargetKey → offset →
|
|
728
|
+
// rect` map consulted when the caller passes a `story` argument.
|
|
722
729
|
const byRuntimeOffset = new Map<number, RenderFrameRect>();
|
|
730
|
+
const byRuntimeOffsetByStory = new Map<string, Map<number, RenderFrameRect>>();
|
|
723
731
|
const byFragmentId = new Map<string, RenderFrameRect>();
|
|
724
732
|
const byBlockId = new Map<string, RenderFrameRect>();
|
|
725
733
|
const byPageIndex = new Map<number, RenderFrameRect>();
|
|
@@ -730,28 +738,61 @@ function buildAnchorIndex(
|
|
|
730
738
|
const tableColumnEdges = new Map<string, RenderFrameRect>();
|
|
731
739
|
const tableRowEdges = new Map<string, RenderFrameRect>();
|
|
732
740
|
|
|
741
|
+
const recordBlock = (
|
|
742
|
+
block: import("./render-frame-types.ts").RenderBlock,
|
|
743
|
+
offsetMap: Map<number, RenderFrameRect>,
|
|
744
|
+
): void => {
|
|
745
|
+
byFragmentId.set(block.fragment.fragmentId, block.frame);
|
|
746
|
+
byBlockId.set(block.fragment.blockId, block.frame);
|
|
747
|
+
offsetMap.set(block.fragment.from, block.frame);
|
|
748
|
+
for (const line of block.lines) {
|
|
749
|
+
for (const anchor of line.anchors) {
|
|
750
|
+
offsetMap.set(anchor.runtimeOffset, anchor.frame);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
if (block.kind === "table" && block.tablePlan) {
|
|
754
|
+
recordTableAnchors(
|
|
755
|
+
block.fragment.blockId,
|
|
756
|
+
block.frame,
|
|
757
|
+
block.tablePlan,
|
|
758
|
+
pxPerTwip,
|
|
759
|
+
tableCellRects,
|
|
760
|
+
tableColumnEdges,
|
|
761
|
+
tableRowEdges,
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
const recordStoryRegion = (
|
|
767
|
+
region: import("./render-frame-types.ts").RenderStoryRegion,
|
|
768
|
+
): void => {
|
|
769
|
+
const storyKey = storyTargetKey(region.storyTarget);
|
|
770
|
+
let storyMap = byRuntimeOffsetByStory.get(storyKey);
|
|
771
|
+
if (!storyMap) {
|
|
772
|
+
storyMap = new Map<number, RenderFrameRect>();
|
|
773
|
+
byRuntimeOffsetByStory.set(storyKey, storyMap);
|
|
774
|
+
}
|
|
775
|
+
for (const block of region.blocks) {
|
|
776
|
+
recordBlock(block, storyMap);
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
|
|
733
780
|
for (const page of pages) {
|
|
734
781
|
byPageIndex.set(page.page.pageIndex, page.frame);
|
|
782
|
+
// Body region feeds the shared `byRuntimeOffset` (main-story offsets).
|
|
735
783
|
for (const block of page.regions.body.blocks) {
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
block.frame,
|
|
749
|
-
block.tablePlan,
|
|
750
|
-
pxPerTwip,
|
|
751
|
-
tableCellRects,
|
|
752
|
-
tableColumnEdges,
|
|
753
|
-
tableRowEdges,
|
|
754
|
-
);
|
|
784
|
+
recordBlock(block, byRuntimeOffset);
|
|
785
|
+
}
|
|
786
|
+
// Refactor/05 Slice 7d: index non-body regions so header / footer /
|
|
787
|
+
// footnote chrome consumers can resolve `byFragmentId` /
|
|
788
|
+
// `byBlockId` / story-scoped `byRuntimeOffset` without falling
|
|
789
|
+
// back to body lookups. Each region's offsets are keyed on its
|
|
790
|
+
// own story (header offsets are header-local, etc.).
|
|
791
|
+
if (page.regions.header) recordStoryRegion(page.regions.header);
|
|
792
|
+
if (page.regions.footer) recordStoryRegion(page.regions.footer);
|
|
793
|
+
if (page.regions.footnotes) {
|
|
794
|
+
for (const footnote of page.regions.footnotes) {
|
|
795
|
+
recordStoryRegion(footnote);
|
|
755
796
|
}
|
|
756
797
|
}
|
|
757
798
|
}
|
|
@@ -771,15 +812,22 @@ function buildAnchorIndex(
|
|
|
771
812
|
|
|
772
813
|
const resolveByRuntimeOffset = (
|
|
773
814
|
offset: number,
|
|
774
|
-
|
|
815
|
+
story?: EditorStoryTarget,
|
|
775
816
|
): RenderFrameRect | null => {
|
|
776
|
-
void _story;
|
|
777
817
|
const lookup = shiftForDeltas(offset);
|
|
778
|
-
|
|
818
|
+
// Refactor/05 Slice 7d: pick the per-story offset map when the
|
|
819
|
+
// caller names a non-main story. Delta-shift is currently
|
|
820
|
+
// main-only — non-body stories don't receive predicted-dispatch
|
|
821
|
+
// deltas today, so the shift is a no-op for them.
|
|
822
|
+
const targetMap =
|
|
823
|
+
story && story.kind !== "main"
|
|
824
|
+
? byRuntimeOffsetByStory.get(storyTargetKey(story)) ?? byRuntimeOffset
|
|
825
|
+
: byRuntimeOffset;
|
|
826
|
+
const exact = targetMap.get(lookup);
|
|
779
827
|
if (exact) return exact;
|
|
780
828
|
let best: RenderFrameRect | null = null;
|
|
781
829
|
let bestDistance = Number.POSITIVE_INFINITY;
|
|
782
|
-
for (const [key, rect] of
|
|
830
|
+
for (const [key, rect] of targetMap) {
|
|
783
831
|
const distance = Math.abs(key - lookup);
|
|
784
832
|
if (distance < bestDistance) {
|
|
785
833
|
best = rect;
|
|
@@ -812,8 +860,16 @@ function buildAnchorIndex(
|
|
|
812
860
|
// the anchor maps (see `shiftForDeltas` rationale above).
|
|
813
861
|
const loShifted = shiftForDeltas(lo);
|
|
814
862
|
const hiShifted = shiftForDeltas(hi);
|
|
863
|
+
// Refactor/05 Slice 7d: pick the per-story offset map when the
|
|
864
|
+
// selection lives in a non-main story (header / footer /
|
|
865
|
+
// footnote). Offsets are story-local so iterating the main map
|
|
866
|
+
// would never match.
|
|
867
|
+
const targetMap =
|
|
868
|
+
story && story.kind !== "main"
|
|
869
|
+
? byRuntimeOffsetByStory.get(storyTargetKey(story)) ?? byRuntimeOffset
|
|
870
|
+
: byRuntimeOffset;
|
|
815
871
|
let union: RenderFrameRect | null = null;
|
|
816
|
-
for (const [key, rect] of
|
|
872
|
+
for (const [key, rect] of targetMap) {
|
|
817
873
|
if (key < loShifted || key >= hiShifted) continue;
|
|
818
874
|
union = unionRects(union, rect);
|
|
819
875
|
}
|
|
@@ -855,6 +911,19 @@ function buildAnchorIndex(
|
|
|
855
911
|
);
|
|
856
912
|
return match?.frame ?? null;
|
|
857
913
|
},
|
|
914
|
+
// Refactor/05 Slice 7c substrate: object-specific index. Falls back
|
|
915
|
+
// to `byBlockId` because the render kernel does not yet carry
|
|
916
|
+
// per-object metadata on anchor frames — the layout facet would
|
|
917
|
+
// need to emit `byObjectId` entries on the page graph. When that
|
|
918
|
+
// follow-up ships, populate a dedicated `byObjectIdMap` here and
|
|
919
|
+
// consult it before the `byBlockId` fallback. The fallback is
|
|
920
|
+
// correct for block-level drawings (whose object id equals their
|
|
921
|
+
// block id); it is `heuristic` for inline images (which share
|
|
922
|
+
// their block id with the enclosing paragraph). `object-handles.ts`
|
|
923
|
+
// tags those handles `precision: "heuristic"` accordingly.
|
|
924
|
+
byObjectId(objectId) {
|
|
925
|
+
return byBlockId.get(objectId) ?? null;
|
|
926
|
+
},
|
|
858
927
|
};
|
|
859
928
|
}
|
|
860
929
|
|