@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
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
expectString,
|
|
11
11
|
expectStringAllowEmpty,
|
|
12
12
|
expectUuid,
|
|
13
|
+
isUuid,
|
|
13
14
|
stableStringify,
|
|
14
15
|
} from "./cds-1.0.0.ts";
|
|
15
16
|
import type {
|
|
@@ -57,25 +58,132 @@ const ID_PATTERNS = {
|
|
|
57
58
|
type StableIdDomain = keyof typeof ID_PATTERNS;
|
|
58
59
|
|
|
59
60
|
export interface CanonicalDocument {
|
|
60
|
-
schemaVersion: typeof CDS_SCHEMA_VERSION;
|
|
61
|
-
docId: UUID;
|
|
62
|
-
createdAt: ISO8601DateTime;
|
|
63
|
-
updatedAt: ISO8601DateTime;
|
|
64
|
-
metadata: DocumentMetadata;
|
|
65
|
-
styles: StylesCatalog;
|
|
66
|
-
numbering: NumberingCatalog;
|
|
67
|
-
media: MediaCatalog;
|
|
68
|
-
content: DocumentRootNode;
|
|
69
|
-
review: ReviewStore;
|
|
70
|
-
preservation: PreservationStore;
|
|
71
|
-
diagnostics: DiagnosticStore;
|
|
72
|
-
subParts?: SubPartsCatalog;
|
|
61
|
+
readonly schemaVersion: typeof CDS_SCHEMA_VERSION;
|
|
62
|
+
readonly docId: UUID;
|
|
63
|
+
readonly createdAt: ISO8601DateTime;
|
|
64
|
+
readonly updatedAt: ISO8601DateTime;
|
|
65
|
+
readonly metadata: DocumentMetadata;
|
|
66
|
+
readonly styles: StylesCatalog;
|
|
67
|
+
readonly numbering: NumberingCatalog;
|
|
68
|
+
readonly media: MediaCatalog;
|
|
69
|
+
readonly content: DocumentRootNode;
|
|
70
|
+
readonly review: ReviewStore;
|
|
71
|
+
readonly preservation: PreservationStore;
|
|
72
|
+
readonly diagnostics: DiagnosticStore;
|
|
73
|
+
readonly subParts?: SubPartsCatalog;
|
|
73
74
|
/** Package-backed field registry for supported field families. */
|
|
74
|
-
fieldRegistry?: FieldRegistry;
|
|
75
|
+
readonly fieldRegistry?: FieldRegistry;
|
|
75
76
|
/** Parsed fontTable.xml. Undefined when the package has no fontTable part. */
|
|
76
|
-
fontTable?: CanonicalFontTable;
|
|
77
|
+
readonly fontTable?: CanonicalFontTable;
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
/**
|
|
81
|
+
* A detachable slice of canonical document content. The inverse of
|
|
82
|
+
* `DocumentRootNode` in the narrow sense — a fragment is block-level
|
|
83
|
+
* children that can be spliced into a target range on the main story or
|
|
84
|
+
* on any secondary story (header, footer, footnote, endnote) without
|
|
85
|
+
* carrying the enclosing envelope.
|
|
86
|
+
*
|
|
87
|
+
* Owned by Layer 02 because it is a shape of canonical content, not a
|
|
88
|
+
* layout or runtime concern. Consumed by the Layer 08 scope compiler's
|
|
89
|
+
* structured-replacement path (`paragraph.compileReplacement` with
|
|
90
|
+
* `proposedContent.kind === "structured"`) and by any future command
|
|
91
|
+
* that performs block-level splicing. The fragment does not carry its
|
|
92
|
+
* own catalogs / stores / registries — those are resolved against the
|
|
93
|
+
* destination document at splice time.
|
|
94
|
+
*
|
|
95
|
+
* Structural invariants:
|
|
96
|
+
* - Every `BlockNode` inside `blocks` is structurally valid in
|
|
97
|
+
* isolation (passes `validateDocumentNode` when walked).
|
|
98
|
+
* - The fragment carries no anchors to the source document; splicing
|
|
99
|
+
* is by target range supplied at apply time, not by fragment
|
|
100
|
+
* identity.
|
|
101
|
+
* - Styles / numbering / media referenced by inline children must
|
|
102
|
+
* resolve in the destination document; the fragment itself does not
|
|
103
|
+
* own catalog entries. A splice that introduces unresolved
|
|
104
|
+
* references is a caller error, not a fragment shape violation.
|
|
105
|
+
*/
|
|
106
|
+
export interface CanonicalDocumentFragment {
|
|
107
|
+
/** Block-level children of the fragment, in document order. */
|
|
108
|
+
readonly blocks: ReadonlyArray<BlockNode>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Draft-only mutable view of `CanonicalDocumentFragment`. Same
|
|
113
|
+
* semantics as {@link Mutable}. Used at fragment-construction sites;
|
|
114
|
+
* callers widen back to `CanonicalDocumentFragment` at the function
|
|
115
|
+
* boundary.
|
|
116
|
+
*/
|
|
117
|
+
export type MutableCanonicalDocumentFragment = Mutable<CanonicalDocumentFragment>;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Strip every `readonly` modifier from an interface / type. The sanctioned
|
|
121
|
+
* "local draft → freeze" escape hatch for constructing a new canonical value:
|
|
122
|
+
* declare the draft as `Mutable<CanonicalType>`, populate it by in-place
|
|
123
|
+
* assignment, and return it widened back to the readonly type. Widening is
|
|
124
|
+
* identity at runtime; TypeScript preserves the immutability contract at the
|
|
125
|
+
* function boundary.
|
|
126
|
+
*
|
|
127
|
+
* Contract D2 (architecture §02) forbids consumers from mutating a
|
|
128
|
+
* `CanonicalDocument` or any nested canonical value received from elsewhere.
|
|
129
|
+
* This helper is the ONE acceptable cast for locally-owned drafts:
|
|
130
|
+
*
|
|
131
|
+
* function refreshStyles(doc: CanonicalDocument, fresh: StylesCatalog): CanonicalDocument {
|
|
132
|
+
* const draft: MutableCanonicalDocument = { ...doc };
|
|
133
|
+
* draft.styles = fresh;
|
|
134
|
+
* draft.updatedAt = new Date().toISOString();
|
|
135
|
+
* return draft; // widens back to CanonicalDocument
|
|
136
|
+
* }
|
|
137
|
+
*
|
|
138
|
+
* Never cast a value received as a parameter. Only cast values you just
|
|
139
|
+
* created via object literal, spread, `structuredClone`, or `createCanonicalDocument`.
|
|
140
|
+
*
|
|
141
|
+
* **Shallow only.** `Mutable<T>` strips `readonly` at the top level of `T`;
|
|
142
|
+
* nested `ReadonlyArray<…>`, `Readonly<…>`, and inner `readonly` modifiers
|
|
143
|
+
* declared inside referenced types are preserved. For example,
|
|
144
|
+
* `Mutable<CanonicalDocument>` does NOT make `draft.content.children`
|
|
145
|
+
* mutable — `content` is `DocumentRootNode`, whose `children` field is
|
|
146
|
+
* declared as it is in the source type. If you need to mutate nested
|
|
147
|
+
* structure, build the nested value first (also as a `Mutable<…>` of the
|
|
148
|
+
* nested type) and assign it onto the draft as a complete replacement.
|
|
149
|
+
* Deep-readonly propagation across nested catalogs/stores/nodes is a
|
|
150
|
+
* deferred D1 follow-up (see `docs/plans/refactor/02-canonical-document-deferred.md`).
|
|
151
|
+
*/
|
|
152
|
+
export type Mutable<T> = { -readonly [K in keyof T]: T[K] };
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Draft-only mutable view of `CanonicalDocument`. Equivalent to
|
|
156
|
+
* `Mutable<CanonicalDocument>`; kept as a named alias for discoverability at
|
|
157
|
+
* the most common call site. See {@link Mutable} for the general pattern and
|
|
158
|
+
* usage constraints.
|
|
159
|
+
*/
|
|
160
|
+
export type MutableCanonicalDocument = Mutable<CanonicalDocument>;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Named `Mutable*` aliases for the top-level catalogs, stores, and
|
|
164
|
+
* registries referenced from `CanonicalDocument`. Same semantics as
|
|
165
|
+
* `Mutable<T>` — present as named exports so a consumer can write
|
|
166
|
+
* `const catalog: MutableStylesCatalog = { … }` without reaching for the
|
|
167
|
+
* generic. For deeply-nested shapes (node types, formatting records, table
|
|
168
|
+
* cells, chart models) use `Mutable<T>` directly.
|
|
169
|
+
*
|
|
170
|
+
* These aliases are the foundation layer for the deferred D1 deep-readonly
|
|
171
|
+
* propagation (see `docs/plans/refactor/02-canonical-document-deferred.md`
|
|
172
|
+
* § D1). They are type-only additions with zero runtime cost and zero
|
|
173
|
+
* consumer impact; they only unlock the migration path for later slices.
|
|
174
|
+
*/
|
|
175
|
+
export type MutableDocumentMetadata = Mutable<DocumentMetadata>;
|
|
176
|
+
export type MutableStylesCatalog = Mutable<StylesCatalog>;
|
|
177
|
+
export type MutableNumberingCatalog = Mutable<NumberingCatalog>;
|
|
178
|
+
export type MutableMediaCatalog = Mutable<MediaCatalog>;
|
|
179
|
+
export type MutableDocumentRootNode = Mutable<DocumentRootNode>;
|
|
180
|
+
export type MutableReviewStore = Mutable<ReviewStore>;
|
|
181
|
+
export type MutablePreservationStore = Mutable<PreservationStore>;
|
|
182
|
+
export type MutableDiagnosticStore = Mutable<DiagnosticStore>;
|
|
183
|
+
export type MutableSubPartsCatalog = Mutable<SubPartsCatalog>;
|
|
184
|
+
export type MutableFieldRegistry = Mutable<FieldRegistry>;
|
|
185
|
+
export type MutableCanonicalFontTable = Mutable<CanonicalFontTable>;
|
|
186
|
+
|
|
79
187
|
|
|
80
188
|
export interface DocumentMetadata {
|
|
81
189
|
title?: string;
|
|
@@ -382,6 +490,34 @@ export interface ThemeFontScheme {
|
|
|
382
490
|
minorFont?: string;
|
|
383
491
|
}
|
|
384
492
|
|
|
493
|
+
/**
|
|
494
|
+
* ECMA-376 §17.18.94 `<w:rFonts>` theme-slot values. The set is a small
|
|
495
|
+
* closed enumeration; every attribute value outside this list is dropped
|
|
496
|
+
* at parse (preserving the "canonical reflects modeled state only"
|
|
497
|
+
* invariant — unknown values would be data the layer cannot round-trip).
|
|
498
|
+
*
|
|
499
|
+
* Slot semantics: `majorAscii` = major-font Latin glyph typeface,
|
|
500
|
+
* `minorEastAsia` = minor-font East-Asian typeface, etc. Word uses
|
|
501
|
+
* `majorFont` for headings and `minorFont` for body text; the script
|
|
502
|
+
* suffix picks the typeface within that major/minor family.
|
|
503
|
+
*
|
|
504
|
+
* Canonical `ThemeFontScheme` today captures only a single typeface
|
|
505
|
+
* per major/minor — all script variants of the same major/minor family
|
|
506
|
+
* resolve to the same concrete font. Extending the scheme with
|
|
507
|
+
* per-script (latin / ea / cs) typefaces is L02 territory and is
|
|
508
|
+
* tracked as a follow-up; the run-side field preserves the full
|
|
509
|
+
* attribute for byte-identical export regardless.
|
|
510
|
+
*/
|
|
511
|
+
export type ThemeFontSlot =
|
|
512
|
+
| "majorAscii"
|
|
513
|
+
| "minorAscii"
|
|
514
|
+
| "majorHAnsi"
|
|
515
|
+
| "minorHAnsi"
|
|
516
|
+
| "majorEastAsia"
|
|
517
|
+
| "minorEastAsia"
|
|
518
|
+
| "majorBidi"
|
|
519
|
+
| "minorBidi";
|
|
520
|
+
|
|
385
521
|
export interface ThemeDefinition {
|
|
386
522
|
name?: string;
|
|
387
523
|
colorScheme?: ThemeColorScheme;
|
|
@@ -664,6 +800,24 @@ export interface CanonicalRunFormatting {
|
|
|
664
800
|
fontFamilyHAnsi?: string;
|
|
665
801
|
fontFamilyEastAsia?: string;
|
|
666
802
|
fontFamilyCs?: string;
|
|
803
|
+
/**
|
|
804
|
+
* ECMA-376 §17.3.2.26 theme-slot bindings on `<w:rFonts>`
|
|
805
|
+
* (`w:asciiTheme`, `w:hAnsiTheme`, `w:eastAsiaTheme`, `w:cstheme`).
|
|
806
|
+
* When present, the run asks for the typeface named by the
|
|
807
|
+
* corresponding slot on the document's `theme.fontScheme` — Word
|
|
808
|
+
* then resolves the concrete font through the theme. Preserved for
|
|
809
|
+
* round-trip; resolution lives in
|
|
810
|
+
* `src/runtime/formatting/font-resolution.ts`.
|
|
811
|
+
*
|
|
812
|
+
* Concrete names (`fontFamilyAscii`, etc.) take precedence over
|
|
813
|
+
* theme slots per ECMA-376 §17.3.2.26; when both are present the
|
|
814
|
+
* concrete name wins. Keep the theme slot on the canonical so the
|
|
815
|
+
* export path can re-emit it byte-identically.
|
|
816
|
+
*/
|
|
817
|
+
asciiTheme?: ThemeFontSlot;
|
|
818
|
+
hAnsiTheme?: ThemeFontSlot;
|
|
819
|
+
eastAsiaTheme?: ThemeFontSlot;
|
|
820
|
+
csTheme?: ThemeFontSlot;
|
|
667
821
|
fontSizeHalfPoints?: number;
|
|
668
822
|
fontSizeCsHalfPoints?: number;
|
|
669
823
|
/**
|
|
@@ -1930,6 +2084,151 @@ export function createCanonicalDocument(
|
|
|
1930
2084
|
return document;
|
|
1931
2085
|
}
|
|
1932
2086
|
|
|
2087
|
+
/**
|
|
2088
|
+
* Layer 02 default-envelope factory. Produces a fully-valid
|
|
2089
|
+
* {@link CanonicalDocument} with a single empty paragraph and empty
|
|
2090
|
+
* catalogs/stores. Used by:
|
|
2091
|
+
* - the session layer's default-new-document path (when a host requests
|
|
2092
|
+
* an editor without supplying source bytes)
|
|
2093
|
+
* - the persisted-snapshot repair path (as the "base" onto which a
|
|
2094
|
+
* malformed envelope is merged by {@link repairCanonicalDocumentEnvelope})
|
|
2095
|
+
*
|
|
2096
|
+
* The function is pure: no runtime, io, or session imports. Every field
|
|
2097
|
+
* populated here is a structural constant, so the resulting document
|
|
2098
|
+
* passes {@link assertCanonicalDocument} by construction.
|
|
2099
|
+
*/
|
|
2100
|
+
export function createEmptyCanonicalDocument(params: {
|
|
2101
|
+
docId: UUID;
|
|
2102
|
+
createdAt: ISO8601DateTime;
|
|
2103
|
+
updatedAt?: ISO8601DateTime;
|
|
2104
|
+
}): CanonicalDocument {
|
|
2105
|
+
return createCanonicalDocument({
|
|
2106
|
+
docId: params.docId,
|
|
2107
|
+
createdAt: params.createdAt,
|
|
2108
|
+
updatedAt: params.updatedAt ?? params.createdAt,
|
|
2109
|
+
metadata: { customProperties: {} },
|
|
2110
|
+
styles: { paragraphs: {}, characters: {}, tables: {} },
|
|
2111
|
+
numbering: { abstractDefinitions: {}, instances: {} },
|
|
2112
|
+
media: { items: {} },
|
|
2113
|
+
content: {
|
|
2114
|
+
type: "doc",
|
|
2115
|
+
children: [{ type: "paragraph", children: [] }],
|
|
2116
|
+
},
|
|
2117
|
+
review: { comments: {}, revisions: {} },
|
|
2118
|
+
preservation: { opaqueFragments: {}, packageParts: {} },
|
|
2119
|
+
diagnostics: { warnings: [], errors: [] },
|
|
2120
|
+
});
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
/**
|
|
2124
|
+
* Layer 02 repair-envelope factory. Coerces an unknown value that claims
|
|
2125
|
+
* to be a persisted {@link CanonicalDocument} into a structurally-valid
|
|
2126
|
+
* envelope by merging the value's recognizable fields onto a freshly
|
|
2127
|
+
* constructed empty envelope (see {@link createEmptyCanonicalDocument}).
|
|
2128
|
+
*
|
|
2129
|
+
* The repair preserves optional fields — `subParts`, `fieldRegistry`,
|
|
2130
|
+
* `fontTable` — unmodified when present as objects. The session layer's
|
|
2131
|
+
* previous copy of this function silently dropped `fieldRegistry` and
|
|
2132
|
+
* `fontTable`, which caused a round-trip of (load → persist → reload)
|
|
2133
|
+
* to lose canonical data. Having the function live on the model layer
|
|
2134
|
+
* ensures every repair path recognises every optional field the model
|
|
2135
|
+
* actually owns.
|
|
2136
|
+
*
|
|
2137
|
+
* Inputs that are not plain objects are treated as completely missing,
|
|
2138
|
+
* producing a document with default content. Inputs whose `docId` is
|
|
2139
|
+
* not a valid UUID are given {@link params.fallbackDocId} instead.
|
|
2140
|
+
*
|
|
2141
|
+
* This function is pure and synchronous. It does not throw; the caller
|
|
2142
|
+
* decides whether to pass the output through {@link assertCanonicalDocument}
|
|
2143
|
+
* for strict validation (the session-layer repair path does; the rehydration
|
|
2144
|
+
* path does too, after merging in the live session's canonicalDocument
|
|
2145
|
+
* snapshot). The output is always structurally valid — the final shape is
|
|
2146
|
+
* `createEmptyCanonicalDocument(fallback) ∪ filtered(input)`.
|
|
2147
|
+
*/
|
|
2148
|
+
export function repairCanonicalDocumentEnvelope(
|
|
2149
|
+
value: unknown,
|
|
2150
|
+
params: {
|
|
2151
|
+
fallbackDocId: UUID;
|
|
2152
|
+
fallbackCreatedAt: ISO8601DateTime;
|
|
2153
|
+
fallbackUpdatedAt?: ISO8601DateTime;
|
|
2154
|
+
},
|
|
2155
|
+
): CanonicalDocument {
|
|
2156
|
+
const base = createEmptyCanonicalDocument({
|
|
2157
|
+
docId: params.fallbackDocId,
|
|
2158
|
+
createdAt: params.fallbackCreatedAt,
|
|
2159
|
+
updatedAt: params.fallbackUpdatedAt ?? params.fallbackCreatedAt,
|
|
2160
|
+
});
|
|
2161
|
+
const record = isPlainRecord(value) ? value : {};
|
|
2162
|
+
const metadata = isPlainRecord(record.metadata) ? record.metadata : {};
|
|
2163
|
+
const customProperties = isPlainRecord(metadata.customProperties)
|
|
2164
|
+
? (Object.fromEntries(
|
|
2165
|
+
Object.entries(metadata.customProperties).filter(
|
|
2166
|
+
([, entry]) => typeof entry === "string",
|
|
2167
|
+
),
|
|
2168
|
+
) as Record<string, string>)
|
|
2169
|
+
: {};
|
|
2170
|
+
|
|
2171
|
+
const envelope: Mutable<CanonicalDocument> = {
|
|
2172
|
+
...base,
|
|
2173
|
+
schemaVersion: CDS_SCHEMA_VERSION,
|
|
2174
|
+
docId: isUuid(record.docId) ? record.docId : params.fallbackDocId,
|
|
2175
|
+
createdAt:
|
|
2176
|
+
typeof record.createdAt === "string" && record.createdAt.length > 0
|
|
2177
|
+
? record.createdAt
|
|
2178
|
+
: base.createdAt,
|
|
2179
|
+
updatedAt:
|
|
2180
|
+
typeof record.updatedAt === "string" && record.updatedAt.length > 0
|
|
2181
|
+
? record.updatedAt
|
|
2182
|
+
: base.updatedAt,
|
|
2183
|
+
metadata: {
|
|
2184
|
+
...base.metadata,
|
|
2185
|
+
...metadata,
|
|
2186
|
+
customProperties,
|
|
2187
|
+
} as CanonicalDocument["metadata"],
|
|
2188
|
+
styles:
|
|
2189
|
+
record.styles === undefined
|
|
2190
|
+
? base.styles
|
|
2191
|
+
: (record.styles as CanonicalDocument["styles"]),
|
|
2192
|
+
numbering:
|
|
2193
|
+
record.numbering === undefined
|
|
2194
|
+
? base.numbering
|
|
2195
|
+
: (record.numbering as CanonicalDocument["numbering"]),
|
|
2196
|
+
media:
|
|
2197
|
+
record.media === undefined
|
|
2198
|
+
? base.media
|
|
2199
|
+
: (record.media as CanonicalDocument["media"]),
|
|
2200
|
+
content: isPlainRecord(record.content)
|
|
2201
|
+
? (record.content as unknown as CanonicalDocument["content"])
|
|
2202
|
+
: base.content,
|
|
2203
|
+
review:
|
|
2204
|
+
record.review === undefined
|
|
2205
|
+
? base.review
|
|
2206
|
+
: (record.review as CanonicalDocument["review"]),
|
|
2207
|
+
preservation:
|
|
2208
|
+
record.preservation === undefined
|
|
2209
|
+
? base.preservation
|
|
2210
|
+
: (record.preservation as CanonicalDocument["preservation"]),
|
|
2211
|
+
diagnostics:
|
|
2212
|
+
record.diagnostics === undefined
|
|
2213
|
+
? base.diagnostics
|
|
2214
|
+
: (record.diagnostics as CanonicalDocument["diagnostics"]),
|
|
2215
|
+
};
|
|
2216
|
+
if (isPlainRecord(record.subParts)) {
|
|
2217
|
+
envelope.subParts = record.subParts as unknown as CanonicalDocument["subParts"];
|
|
2218
|
+
}
|
|
2219
|
+
if (isPlainRecord(record.fieldRegistry)) {
|
|
2220
|
+
envelope.fieldRegistry = record.fieldRegistry as unknown as CanonicalDocument["fieldRegistry"];
|
|
2221
|
+
}
|
|
2222
|
+
if (isPlainRecord(record.fontTable)) {
|
|
2223
|
+
envelope.fontTable = record.fontTable as unknown as CanonicalDocument["fontTable"];
|
|
2224
|
+
}
|
|
2225
|
+
return envelope;
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
|
2229
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2230
|
+
}
|
|
2231
|
+
|
|
1933
2232
|
export function serializeCanonicalDocument(document: CanonicalDocument): string {
|
|
1934
2233
|
assertCanonicalDocument(document);
|
|
1935
2234
|
return stableStringify(document);
|
|
@@ -3621,3 +3920,40 @@ function expectDomainString(
|
|
|
3621
3920
|
|
|
3622
3921
|
return stableId;
|
|
3623
3922
|
}
|
|
3923
|
+
|
|
3924
|
+
/**
|
|
3925
|
+
* Content-addressable canonical document id — a pseudo-UUIDv4
|
|
3926
|
+
* derived deterministically from an opaque `documentId` string
|
|
3927
|
+
* via four 32-bit FNV-1a hashes. Stable across load/export
|
|
3928
|
+
* (same `documentId` → same `docId`), zero I/O, no wall clock.
|
|
3929
|
+
*
|
|
3930
|
+
* Lives here (model) rather than `src/core/state/editor-state.ts`
|
|
3931
|
+
* because `CanonicalDocument.docId` is a model concern — the
|
|
3932
|
+
* editor-state layer consumes it, doesn't define it. The legacy
|
|
3933
|
+
* export at `src/core/state/editor-state.ts::createCanonicalDocumentId`
|
|
3934
|
+
* re-exports this function for back-compat.
|
|
3935
|
+
*/
|
|
3936
|
+
export function createCanonicalDocumentId(documentId: string): string {
|
|
3937
|
+
const hashWord = (seed: string): string => {
|
|
3938
|
+
let hash = 0x811c9dc5;
|
|
3939
|
+
for (const char of seed) {
|
|
3940
|
+
hash ^= char.charCodeAt(0);
|
|
3941
|
+
hash = Math.imul(hash, 0x01000193);
|
|
3942
|
+
}
|
|
3943
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
3944
|
+
};
|
|
3945
|
+
|
|
3946
|
+
const raw =
|
|
3947
|
+
hashWord(`${documentId}:0`) +
|
|
3948
|
+
hashWord(`${documentId}:1`) +
|
|
3949
|
+
hashWord(`${documentId}:2`) +
|
|
3950
|
+
hashWord(`${documentId}:3`);
|
|
3951
|
+
|
|
3952
|
+
return [
|
|
3953
|
+
raw.slice(0, 8),
|
|
3954
|
+
raw.slice(8, 12),
|
|
3955
|
+
`4${raw.slice(13, 16)}`,
|
|
3956
|
+
`8${raw.slice(17, 20)}`,
|
|
3957
|
+
raw.slice(20, 32),
|
|
3958
|
+
].join("-");
|
|
3959
|
+
}
|
package/src/model/chart-types.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* references; consumers of Stage 1 models should treat color resolution as
|
|
14
14
|
* "not yet available."
|
|
15
15
|
*
|
|
16
|
-
* See
|
|
16
|
+
* See CLAUDE.md (lane status table) §3 Task 1.1 for the full specification.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 02 · shared layout types (barrel).
|
|
3
|
+
*
|
|
4
|
+
* Neutral home for *data-only* layout types that multiple layers
|
|
5
|
+
* consume but nobody else should own — `RuntimePageGraph`,
|
|
6
|
+
* `RuntimeLayoutFacet`, `RuntimeLineBox`, `RuntimeNoteAllocation`,
|
|
7
|
+
* `RuntimePageAnchor`, etc. Proposed by Layer 04 at `cross-layer-
|
|
8
|
+
* coord-04.md §2` and accepted by Layer 02 at `cross-layer-coord-02.md §9`.
|
|
9
|
+
*
|
|
10
|
+
* # Acceptance rule (what can land in `src/model/layout/**`)
|
|
11
|
+
*
|
|
12
|
+
* This directory is inside `src/model/**` and therefore subject to the
|
|
13
|
+
* same architecture §D2 (quasi-immutability) and §D3 (no upward
|
|
14
|
+
* imports) contracts as the rest of Layer 02. The import-purity guard
|
|
15
|
+
* at `scripts/ci-check-model-import-purity.mjs` already scans this
|
|
16
|
+
* subdirectory recursively — no guard change needed when types land.
|
|
17
|
+
*
|
|
18
|
+
* Accepts:
|
|
19
|
+
* ✓ Plain data interfaces / type aliases for layout-graph + geometry
|
|
20
|
+
* shapes (e.g. `RuntimePageGraph`, `RuntimeLineBox`,
|
|
21
|
+
* `RuntimeNoteAllocation`, `RuntimePageAnchor`).
|
|
22
|
+
* ✓ Readonly / discriminated-union shapes.
|
|
23
|
+
* ✓ `Mutable<T>` draft aliases (same construction-pattern discipline
|
|
24
|
+
* as the rest of `src/model/`).
|
|
25
|
+
* ✓ Type-level utility aliases and branded-id types that describe a
|
|
26
|
+
* layout shape.
|
|
27
|
+
*
|
|
28
|
+
* Rejects (these belong elsewhere — typically `src/runtime/layout/`):
|
|
29
|
+
* ✗ Classes with methods, or interfaces that describe a stateful
|
|
30
|
+
* object.
|
|
31
|
+
* ✗ Factory functions or builders (`createPageGraph()`, etc.).
|
|
32
|
+
* ✗ Visitors / walkers / transformers that operate on a layout tree.
|
|
33
|
+
* ✗ Imports from any forbidden prefix (see guard).
|
|
34
|
+
* ✗ Non-trivial top-level statements (module side-effects).
|
|
35
|
+
*
|
|
36
|
+
* # Who edits here
|
|
37
|
+
*
|
|
38
|
+
* Layer 04 owns the content. Layer 02 owns the boundary (purity guard
|
|
39
|
+
* + acceptance rule). A type relocation from `src/runtime/layout/**`
|
|
40
|
+
* into this tree lands as an L04 commit; L02 reviews and, if the
|
|
41
|
+
* types pass the acceptance rule, the guard scan stays green on the
|
|
42
|
+
* next CI run. No L02 commit is needed per relocation.
|
|
43
|
+
*
|
|
44
|
+
* # Pass 1 — 2026-04-22
|
|
45
|
+
*
|
|
46
|
+
* 6 pure data types relocated from `src/runtime/layout/page-graph.ts`
|
|
47
|
+
* per `docs/plans/cross-layer-coord-04.md §1.12` (L04 purity audit).
|
|
48
|
+
*
|
|
49
|
+
* # Pass 2 — 2026-04-22
|
|
50
|
+
*
|
|
51
|
+
* `PageLayoutSnapshot`, `EditorStoryTarget`, and `DocumentPageSnapshot`
|
|
52
|
+
* relocated out of `src/api/public-types.ts`. They are pure data shapes
|
|
53
|
+
* derived from `CanonicalDocument` inputs; living in the model tree lets
|
|
54
|
+
* `RuntimePageGraph` (next step) drop its transitive api dependency.
|
|
55
|
+
* `src/api/public-types.ts` continues to re-export them as a stability
|
|
56
|
+
* guarantee for external consumers.
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
export type {
|
|
60
|
+
RuntimePageRegions,
|
|
61
|
+
RuntimePageRegion,
|
|
62
|
+
RuntimeBlockFragment,
|
|
63
|
+
RuntimeLineBox,
|
|
64
|
+
RuntimeNoteAllocation,
|
|
65
|
+
RuntimePageAnchor,
|
|
66
|
+
} from "./page-graph-types.ts";
|
|
67
|
+
|
|
68
|
+
export type {
|
|
69
|
+
PageLayoutSnapshot,
|
|
70
|
+
EditorStoryTarget,
|
|
71
|
+
DocumentPageSnapshot,
|
|
72
|
+
} from "./page-layout-snapshot.ts";
|
|
73
|
+
|
|
74
|
+
export type {
|
|
75
|
+
ResolvedPageStories,
|
|
76
|
+
ResolvedDocumentSection,
|
|
77
|
+
} from "./resolved-layout-types.ts";
|
|
78
|
+
|
|
79
|
+
export type {
|
|
80
|
+
RuntimePageGraph,
|
|
81
|
+
RuntimePageNode,
|
|
82
|
+
BuildPageGraphInput,
|
|
83
|
+
} from "./runtime-page-graph-types.ts";
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 02 · shared layout data types — Pass 1 of the L04/L02
|
|
3
|
+
* `src/model/layout/` seam (see `docs/plans/cross-layer-coord-04.md
|
|
4
|
+
* §1.12` and `cross-layer-coord-02.md §9`).
|
|
5
|
+
*
|
|
6
|
+
* These six interfaces are the pure-data subset of the layout graph.
|
|
7
|
+
* They describe how the paginated layout engine exposes region /
|
|
8
|
+
* fragment / line-box / footnote / anchor state — no methods, no
|
|
9
|
+
* imports from higher layers, no runtime-scoped types. They live
|
|
10
|
+
* here so external consumers (Layer 01 `CacheEnvelope`, future debug
|
|
11
|
+
* runners, stateless services) can import the shapes without pulling
|
|
12
|
+
* in `src/runtime/layout/`.
|
|
13
|
+
*
|
|
14
|
+
* Runtime-scoped companions (`RuntimePageGraph`, `RuntimePageNode`,
|
|
15
|
+
* `BuildPageGraphInput`) remain in `src/runtime/layout/page-graph.ts`
|
|
16
|
+
* because they reference `ResolvedPageStories` / `ResolvedDocumentSection`
|
|
17
|
+
* which carry runtime identity today. Pass 2 will relocate the parent
|
|
18
|
+
* graph types once those references are narrowed (see L04's coord-04
|
|
19
|
+
* §1.12 "Pass 2 (full closure)").
|
|
20
|
+
*
|
|
21
|
+
* Acceptance rule: see `src/model/layout/index.ts`. Every type here is
|
|
22
|
+
* plain data (no classes / methods / factories / higher-layer imports).
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Region types
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
export interface RuntimePageRegions {
|
|
30
|
+
body: RuntimePageRegion;
|
|
31
|
+
header?: RuntimePageRegion;
|
|
32
|
+
footer?: RuntimePageRegion;
|
|
33
|
+
/**
|
|
34
|
+
* For multi-column bodies, `body.columns` is populated. For single-column
|
|
35
|
+
* pages, the top-level body is used and `columns` is undefined.
|
|
36
|
+
*/
|
|
37
|
+
columns?: RuntimePageRegion[];
|
|
38
|
+
/**
|
|
39
|
+
* P4 — footnote regions reserved at the bottom of the page (above
|
|
40
|
+
* the footer band) when `noteAllocations` reserved space for one or
|
|
41
|
+
* more footnote bodies. Empty until the page-graph builder
|
|
42
|
+
* populates them, which lands alongside the P8 per-page region
|
|
43
|
+
* rendering work. The shape exists now so consumers
|
|
44
|
+
* (`getStoryRegionsOnPage` / `getLineBoxesForRegion("footnote-area")`)
|
|
45
|
+
* have a stable seam to read from when the builder catches up —
|
|
46
|
+
* pre-P8 reads return `[]` because no entries are populated.
|
|
47
|
+
*/
|
|
48
|
+
footnotes?: RuntimePageRegion[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface RuntimePageRegion {
|
|
52
|
+
kind: "body" | "header" | "footer" | "column" | "footnote-area";
|
|
53
|
+
/** Twips offset from page top (header) or similar region-specific origin. */
|
|
54
|
+
originTwips: number;
|
|
55
|
+
/** Width in twips. */
|
|
56
|
+
widthTwips: number;
|
|
57
|
+
/** Height in twips. */
|
|
58
|
+
heightTwips: number;
|
|
59
|
+
/** IDs of block fragments rendered in this region, in order. */
|
|
60
|
+
fragmentIds: string[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Fragment types
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
export interface RuntimeBlockFragment {
|
|
68
|
+
fragmentId: string;
|
|
69
|
+
/** Canonical block id the fragment slices. */
|
|
70
|
+
blockId: string;
|
|
71
|
+
/** Page this fragment lives on. */
|
|
72
|
+
pageId: string;
|
|
73
|
+
/** Zero-based order within the page region. */
|
|
74
|
+
orderInRegion: number;
|
|
75
|
+
/** Region id the fragment sits in (matches RuntimePageRegion.kind). */
|
|
76
|
+
regionKind: RuntimePageRegion["kind"];
|
|
77
|
+
/** Inclusive from / exclusive to offsets within the main story. */
|
|
78
|
+
from: number;
|
|
79
|
+
to: number;
|
|
80
|
+
/** Height consumed on this page (twips). */
|
|
81
|
+
heightTwips: number;
|
|
82
|
+
/**
|
|
83
|
+
* Fragment classification.
|
|
84
|
+
* - `"whole"` (default): the fragment represents the entire block; no slicing.
|
|
85
|
+
* - `"paragraph-slice"`: one of several fragments produced by intra-paragraph
|
|
86
|
+
* line-box splitting. `paragraphLineRange` identifies which lines this
|
|
87
|
+
* slice renders.
|
|
88
|
+
* - `"table-slice"`: one of several fragments produced by row-boundary table
|
|
89
|
+
* splitting (emitted by the table-fidelity workstream).
|
|
90
|
+
* `tableRowRange` identifies which canonical rows this slice renders.
|
|
91
|
+
* Consumers that predate multi-fragment blocks may treat an absent `kind`
|
|
92
|
+
* as `"whole"`.
|
|
93
|
+
*/
|
|
94
|
+
kind?: "whole" | "paragraph-slice" | "table-slice";
|
|
95
|
+
/**
|
|
96
|
+
* For `kind === "paragraph-slice"`, the inclusive-exclusive line-box index
|
|
97
|
+
* range rendered by this slice plus the total line count for the source
|
|
98
|
+
* paragraph. `from`/`to` still span the full paragraph offset range on
|
|
99
|
+
* every slice — only the visible lines differ.
|
|
100
|
+
*/
|
|
101
|
+
paragraphLineRange?: {
|
|
102
|
+
from: number;
|
|
103
|
+
to: number;
|
|
104
|
+
totalLines: number;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* For `kind === "table-slice"`, the inclusive-exclusive row-index range
|
|
108
|
+
* rendered by this slice. Repeated header rows (when the table has
|
|
109
|
+
* `isHeader` rows and `continuation` pages) are implied by the owning
|
|
110
|
+
* table's canonical row list — consumers prepend header rows for slices
|
|
111
|
+
* whose `from > 0`.
|
|
112
|
+
*/
|
|
113
|
+
tableRowRange?: {
|
|
114
|
+
from: number;
|
|
115
|
+
to: number;
|
|
116
|
+
totalRows: number;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Slice 5 — opaque style-chain ref derived from the block's `styleId`.
|
|
120
|
+
* Used by `analyzeStylesChange` to bound invalidation to the first page
|
|
121
|
+
* that carries a fragment referencing a dirty style.
|
|
122
|
+
*/
|
|
123
|
+
resolvedStyleChainRef?: string;
|
|
124
|
+
/**
|
|
125
|
+
* Slice 5 — numbering instance id copied from the block's `numbering`
|
|
126
|
+
* field. Used by `analyzeNumberingChange` to bound invalidation to the
|
|
127
|
+
* first page that carries a fragment from the affected list instance.
|
|
128
|
+
*/
|
|
129
|
+
numberingInstanceId?: string;
|
|
130
|
+
/**
|
|
131
|
+
* Refactor/04 Slice 4 — zero-based column index on the page, set only
|
|
132
|
+
* when the containing section paginated under a multi-column layout.
|
|
133
|
+
* Absent on single-column pages; absent on footnote-area + header/footer
|
|
134
|
+
* fragments. `buildRegions` uses this to route fragment ids into
|
|
135
|
+
* `RuntimePageRegions.columns[i].fragmentIds`.
|
|
136
|
+
*/
|
|
137
|
+
columnIndex?: number;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Line box + note + anchor types
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
export interface RuntimeLineBox {
|
|
145
|
+
/** Fragment this line belongs to. */
|
|
146
|
+
fragmentId: string;
|
|
147
|
+
/** Zero-based line index inside the fragment. */
|
|
148
|
+
lineIndex: number;
|
|
149
|
+
/** Baseline twips from the region's origin. */
|
|
150
|
+
baselineTwips: number;
|
|
151
|
+
/** Line height twips. */
|
|
152
|
+
heightTwips: number;
|
|
153
|
+
/** Approximate inline width consumed on this line. */
|
|
154
|
+
widthTwips: number;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface RuntimeNoteAllocation {
|
|
158
|
+
noteKind: "footnote" | "endnote";
|
|
159
|
+
noteId: string;
|
|
160
|
+
/** Twips reserved at the bottom of the page for this note's content. */
|
|
161
|
+
reservedHeightTwips: number;
|
|
162
|
+
/**
|
|
163
|
+
* P8 — fragment ID of this note's body. The corresponding
|
|
164
|
+
* `RuntimeBlockFragment` lives in `RuntimePageGraph.fragments` with
|
|
165
|
+
* `regionKind: "footnote-area"`. Undefined when the engine hasn't
|
|
166
|
+
* yet emitted a fragment for the allocation (back-compat for pre-P8
|
|
167
|
+
* callers).
|
|
168
|
+
*/
|
|
169
|
+
fragmentId?: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface RuntimePageAnchor {
|
|
173
|
+
/** Story target the anchor is for. Main story is the common case. */
|
|
174
|
+
storyKey: string;
|
|
175
|
+
/** Offset the anchor represents. */
|
|
176
|
+
offset: number;
|
|
177
|
+
/** Page id resolved for this offset. */
|
|
178
|
+
pageId: string;
|
|
179
|
+
/** Fragment id resolved for this offset, if available. */
|
|
180
|
+
fragmentId?: string;
|
|
181
|
+
}
|