@beyondwork/docx-react-component 1.0.67 → 1.0.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -932
- package/package.json +26 -27
- package/src/api/anchor-conversion.ts +43 -0
- package/src/api/editor-state-types.ts +2 -1
- package/src/api/public-types.ts +504 -101
- package/src/api/session-state.ts +4 -0
- package/src/api/v3/README.md +91 -0
- package/src/api/v3/_create.ts +146 -0
- package/src/api/v3/_layer-metadata.ts +362 -0
- package/src/api/v3/_mocks.ts +84 -0
- package/src/api/v3/_runtime-handle.ts +162 -0
- package/src/api/v3/_ux-response.ts +73 -0
- package/src/api/v3/ai/_metadata-audit.ts +225 -0
- package/src/api/v3/ai/attach.ts +235 -0
- package/src/api/v3/ai/bundle.ts +132 -0
- package/src/api/v3/ai/explain.ts +144 -0
- package/src/api/v3/ai/export.ts +54 -0
- package/src/api/v3/ai/inspect.ts +118 -0
- package/src/api/v3/ai/policy.ts +77 -0
- package/src/api/v3/ai/replacement.ts +341 -0
- package/src/api/v3/ai/resolve.ts +133 -0
- package/src/api/v3/index.ts +79 -0
- package/src/api/v3/runtime/chart.ts +310 -0
- package/src/api/v3/runtime/clipboard.ts +81 -0
- package/src/api/v3/runtime/collab.ts +331 -0
- package/src/api/v3/runtime/content.ts +236 -0
- package/src/api/v3/runtime/document.ts +282 -0
- package/src/api/v3/runtime/formatting.ts +186 -0
- package/src/api/v3/runtime/geometry.ts +349 -0
- package/src/api/v3/runtime/layout.ts +108 -0
- package/src/api/v3/runtime/review.ts +129 -0
- package/src/api/v3/runtime/search.ts +74 -0
- package/src/api/v3/runtime/table.ts +63 -0
- package/src/api/v3/runtime/workflow.ts +434 -0
- package/src/api/v3/ui/_context.ts +86 -0
- package/src/api/v3/ui/_create.ts +65 -0
- package/src/api/v3/ui/_types.ts +520 -0
- package/src/api/v3/ui/chrome-composition.ts +342 -0
- package/src/{ui-tailwind/chrome → api/v3/ui}/chrome-preset-model.ts +11 -1
- package/src/api/v3/ui/chrome.ts +476 -0
- package/src/api/v3/ui/debug.ts +124 -0
- package/src/api/v3/ui/index.ts +64 -0
- package/src/api/v3/ui/overlays-visibility.ts +170 -0
- package/src/api/v3/ui/overlays.ts +427 -0
- package/src/api/v3/ui/scope.ts +71 -0
- package/src/api/v3/ui/session.ts +100 -0
- package/src/api/v3/ui/surface.ts +170 -0
- package/src/api/v3/ui/viewport.ts +303 -0
- package/src/core/commands/index.ts +28 -6
- package/src/core/commands/list-commands.ts +3 -2
- package/src/core/commands/section-layout-commands.ts +9 -8
- package/src/core/schema/text-schema.ts +16 -0
- package/src/core/selection/mapping.ts +33 -72
- package/src/core/state/editor-state.ts +96 -189
- package/src/index.ts +23 -4
- package/src/io/chart-preview-resolver.ts +1 -1
- package/src/io/docx-session.ts +36 -4797
- package/src/io/export/build-app-properties-xml.ts +1 -1
- package/src/io/export/serialize-comments.ts +1 -1
- package/src/io/export/serialize-headers-footers.ts +6 -1
- package/src/io/export/serialize-main-document.ts +45 -0
- package/src/io/export/serialize-run-formatting.ts +17 -2
- package/src/io/export/twip.ts +1 -1
- package/src/io/normalize/normalize-text.ts +27 -20
- package/src/io/ooxml/chart/parse-series.ts +1 -1
- package/src/io/ooxml/chart/resolve-color.ts +2 -2
- package/src/io/ooxml/chart/types.ts +1 -1
- package/src/io/ooxml/classify-embedding.ts +83 -33
- package/src/io/ooxml/parse-fill.ts +1 -1
- package/src/io/ooxml/parse-main-document.ts +71 -1
- package/src/io/ooxml/parse-object.ts +14 -10
- package/src/io/ooxml/parse-run-formatting.ts +47 -1
- package/src/io/ooxml/property-grab-bag.ts +2 -2
- package/src/io/ooxml/units.ts +11 -0
- package/src/io/ooxml/workflow-payload.ts +282 -7
- package/src/model/anchor.ts +85 -0
- package/src/model/canonical-document.ts +351 -15
- package/src/model/chart-types.ts +1 -1
- package/src/model/layout/index.ts +83 -0
- package/src/model/layout/page-graph-types.ts +181 -0
- package/src/model/layout/page-layout-snapshot.ts +105 -0
- package/src/model/layout/resolved-layout-types.ts +47 -0
- package/src/model/layout/runtime-page-graph-types.ts +102 -0
- package/src/model/paragraph-scope-ids.ts +72 -0
- package/src/model/review/comment-types.ts +112 -0
- package/src/model/review/index.ts +2 -0
- package/src/model/review/revision-types.ts +215 -0
- package/src/model/snapshot.ts +32 -0
- package/src/review/store/comment-store.ts +21 -47
- package/src/review/store/revision-types.ts +40 -198
- package/src/runtime/collab/base-doc-fingerprint.ts +6 -1
- package/src/runtime/collab/runtime-collab-sync.ts +13 -3
- package/src/runtime/collab-session.ts +1 -1
- package/src/runtime/debug/build-debug-inspector-snapshot.ts +686 -0
- package/src/runtime/debug/event-ring-buffer.ts +64 -0
- package/src/runtime/debug/probability-sampler.ts +18 -0
- package/src/runtime/debug/runtime-debug-facet.ts +67 -0
- package/src/runtime/debug/stage-tokens.ts +31 -0
- package/src/runtime/debug/telemetry-bus.ts +271 -0
- package/src/runtime/debug/types.ts +275 -0
- package/src/runtime/debug/wrap-ref-for-telemetry.ts +118 -0
- package/src/runtime/document-layout.ts +8 -6
- package/src/runtime/document-runtime.ts +843 -1141
- package/src/runtime/document-search.ts +1 -1
- package/src/runtime/edit-ops/index.ts +1 -1
- package/src/runtime/external-send-runtime.ts +1 -1
- package/src/runtime/formatting/document-lookup.ts +235 -0
- package/src/runtime/formatting/field/registry.ts +41 -0
- package/src/runtime/{field-resolver.ts → formatting/field/resolver.ts} +27 -2
- package/src/runtime/formatting/font-resolution.ts +83 -0
- package/src/runtime/formatting/formatting-context.ts +903 -0
- package/src/runtime/formatting/formatting-types.ts +157 -0
- package/src/runtime/{hyperlink-color-resolver.ts → formatting/hyperlink-color.ts} +2 -2
- package/src/runtime/formatting/index.ts +125 -0
- package/src/runtime/{resolved-numbering-geometry.ts → formatting/numbering/geometry.ts} +1 -1
- package/src/runtime/{numbering-prefix.ts → formatting/numbering/prefix.ts} +170 -3
- package/src/runtime/formatting/paragraph-style-resolver.ts +92 -0
- package/src/runtime/formatting/projector.ts +75 -0
- package/src/runtime/formatting/resolve-effective.ts +407 -0
- package/src/runtime/formatting/revision-display.ts +105 -0
- package/src/runtime/{paragraph-style-resolver.ts → formatting/style-cascade.ts} +84 -141
- package/src/runtime/{table-style-resolver.ts → formatting/table-style-resolver.ts} +1 -1
- package/src/runtime/formatting/telemetry-bridge.ts +106 -0
- package/src/runtime/{theme-color-resolver.ts → formatting/theme-color.ts} +2 -30
- package/src/runtime/geometry/caret-geometry.ts +164 -0
- package/src/runtime/geometry/geometry-facet.ts +364 -0
- package/src/runtime/geometry/geometry-types.ts +256 -0
- package/src/runtime/geometry/hit-test.ts +125 -0
- package/src/runtime/geometry/index.ts +71 -0
- package/src/runtime/geometry/inert-geometry-facet.ts +43 -0
- package/src/runtime/geometry/invalidation.ts +35 -0
- package/src/runtime/geometry/object-handles.ts +77 -0
- package/src/runtime/geometry/overlay-rects.ts +85 -0
- package/src/runtime/geometry/project-anchors.ts +100 -0
- package/src/runtime/geometry/project-fragments.ts +216 -0
- package/src/runtime/geometry/projector.ts +129 -0
- package/src/runtime/geometry/replacement-envelope.ts +130 -0
- package/src/runtime/geometry/viewport.ts +218 -0
- package/src/runtime/layout/compat-input-ledger.ts +211 -0
- package/src/runtime/layout/index.ts +6 -1
- package/src/runtime/layout/inert-layout-facet.ts +12 -7
- package/src/runtime/layout/layout-engine-instance.ts +189 -11
- package/src/runtime/layout/layout-engine-version.ts +450 -1
- package/src/runtime/layout/layout-facet-types.ts +60 -0
- package/src/runtime/layout/layout-measurement-provider.ts +13 -0
- package/src/runtime/layout/measurement-backend-canvas.ts +14 -2
- package/src/runtime/layout/measurement-backend-empirical.ts +23 -4
- package/src/runtime/layout/page-graph.ts +62 -209
- package/src/runtime/layout/page-story-resolver.ts +7 -12
- package/src/runtime/layout/paginated-layout-engine.ts +186 -11
- package/src/runtime/layout/project-block-fragments.ts +11 -0
- package/src/runtime/layout/projector.ts +90 -0
- package/src/runtime/layout/public-facet.ts +187 -442
- package/src/runtime/layout/resolved-formatting-state.ts +158 -26
- package/src/runtime/layout/table-render-plan.ts +1 -1
- package/src/runtime/prerender/cache-envelope.ts +6 -1
- package/src/runtime/prerender/prerender-document.ts +18 -23
- package/src/runtime/render/decoration-resolver.ts +1 -1
- package/src/runtime/render/render-frame-types.ts +20 -0
- package/src/runtime/render/render-kernel.ts +94 -25
- package/src/runtime/scopes/_formatting-seam.ts +262 -0
- package/src/runtime/scopes/_scope-dependencies.ts +49 -0
- package/src/runtime/scopes/action-validation.ts +356 -0
- package/src/runtime/scopes/attach-explanation.ts +102 -0
- package/src/runtime/scopes/audit-bundle.ts +71 -0
- package/src/runtime/scopes/compile-scope-bundle.ts +163 -0
- package/src/runtime/scopes/compile-scope.ts +262 -0
- package/src/runtime/scopes/compiler-service.ts +431 -0
- package/src/runtime/scopes/create-issue.ts +107 -0
- package/src/runtime/scopes/enumerate-scopes.ts +543 -0
- package/src/runtime/scopes/evidence.ts +233 -0
- package/src/runtime/scopes/index.ts +150 -0
- package/src/runtime/scopes/position-map.ts +214 -0
- package/src/runtime/scopes/preservation-boundary.ts +91 -0
- package/src/runtime/scopes/projector.ts +49 -0
- package/src/runtime/scopes/replaceability.ts +87 -0
- package/src/runtime/scopes/replacement/apply.ts +228 -0
- package/src/runtime/scopes/replacement/compile.ts +59 -0
- package/src/runtime/scopes/replacement/propose.ts +42 -0
- package/src/runtime/scopes/resolve-reference.ts +347 -0
- package/src/runtime/scopes/review-bundle.ts +141 -0
- package/src/runtime/scopes/scope-kinds/_paragraph-text.ts +57 -0
- package/src/runtime/scopes/scope-kinds/_table-text.ts +42 -0
- package/src/runtime/scopes/scope-kinds/comment-thread.ts +59 -0
- package/src/runtime/scopes/scope-kinds/field.ts +65 -0
- package/src/runtime/scopes/scope-kinds/heading.ts +84 -0
- package/src/runtime/scopes/scope-kinds/list-item.ts +77 -0
- package/src/runtime/scopes/scope-kinds/paragraph.ts +182 -0
- package/src/runtime/scopes/scope-kinds/revision.ts +62 -0
- package/src/runtime/scopes/scope-kinds/table-cell.ts +57 -0
- package/src/runtime/scopes/scope-kinds/table-row.ts +61 -0
- package/src/runtime/scopes/scope-kinds/table.ts +55 -0
- package/src/runtime/scopes/scope-range.ts +208 -0
- package/src/runtime/scopes/semantic-scope-types.ts +454 -0
- package/src/runtime/scopes/workflow-overlap.ts +92 -0
- package/src/runtime/selection/index.ts +1 -1
- package/src/runtime/structure-ops/fragment-insert.ts +1 -1
- package/src/runtime/structure-ops/index.ts +1 -1
- package/src/runtime/surface-projection.ts +232 -262
- package/src/runtime/units.ts +4 -2
- package/src/runtime/workflow/coordinator.ts +1348 -0
- package/src/runtime/workflow/derived-scope-resolver.ts +125 -0
- package/src/runtime/workflow/index.ts +25 -0
- package/src/runtime/workflow/markup-mode-policy.ts +98 -0
- package/src/runtime/{workflow-markup.ts → workflow/markup.ts} +6 -6
- package/src/runtime/workflow/metadata-persistence.ts +306 -0
- package/src/runtime/workflow/metadata-writer.ts +123 -0
- package/src/runtime/workflow/overlay-store.ts +690 -0
- package/src/runtime/workflow/projector.ts +127 -0
- package/src/runtime/{query-scopes.ts → workflow/query-scopes.ts} +3 -3
- package/src/runtime/{workflow-rail-segments.ts → workflow/rail/compose.ts} +60 -165
- package/src/runtime/workflow/rail/types.ts +198 -0
- package/src/runtime/workflow/scope-rail-composer.ts +39 -0
- package/src/runtime/{scope-resolver.ts → workflow/scope-resolver.ts} +3 -3
- package/src/runtime/workflow/scope-writer.ts +188 -0
- package/src/runtime/{tamper-gate.ts → workflow/tamper-gate.ts} +1 -1
- package/src/runtime/workflow/visibility-policy.ts +129 -0
- package/src/session/_sync-legacy.ts +66 -0
- package/src/session/export/embedded-reconstitute.ts +104 -0
- package/src/session/export/export-diagnostics.ts +85 -0
- package/src/session/export/export-validation.ts +110 -0
- package/src/session/export/index.ts +34 -0
- package/src/session/export/preservation-reattach.ts +30 -0
- package/src/session/export/serialize-dispatch.ts +165 -0
- package/src/session/export/stateful-export-pipeline.ts +432 -0
- package/src/session/export/stateful-export.ts +684 -0
- package/src/session/import/canonical-assembly.ts +227 -0
- package/src/session/import/diagnostics-session.ts +54 -0
- package/src/session/import/embedded-discovery.ts +225 -0
- package/src/session/import/embedded-offload.ts +337 -0
- package/src/session/import/import-diagnostics.ts +69 -0
- package/src/session/import/loader-types.ts +313 -0
- package/src/session/import/loader.ts +1834 -0
- package/src/session/import/normalize.ts +195 -0
- package/src/session/import/package-parts.ts +217 -0
- package/src/session/import/package-read.ts +195 -0
- package/src/session/import/parse-orchestration.ts +105 -0
- package/src/session/import/part-constants.ts +70 -0
- package/src/session/import/part-discovery.ts +94 -0
- package/src/session/import/preservation-index.ts +46 -0
- package/src/{runtime/read-only-diagnostics-runtime.ts → session/import/read-only-diagnostics.ts} +24 -3
- package/src/session/import/review-import.ts +508 -0
- package/src/session/import/styles-consolidation.ts +281 -0
- package/src/session/import/workflow-scope-import.ts +256 -0
- package/src/session/index.ts +37 -0
- package/src/session/session-state.ts +69 -0
- package/src/session/session.ts +532 -0
- package/src/session/shared/protection.ts +228 -0
- package/src/session/shared/session-utils.ts +82 -0
- package/src/session/types.ts +499 -0
- package/src/shell/chart-snapshots.ts +96 -0
- package/src/shell/media-previews.ts +85 -0
- package/src/shell/overlay-anchor-bridge.ts +53 -0
- package/src/shell/paste-adapter.ts +23 -0
- package/src/shell/ref-commands.ts +1697 -0
- package/src/shell/ref-utilities.ts +48 -0
- package/src/shell/search.ts +51 -0
- package/src/{ui/editor-runtime-boundary.ts → shell/session-bootstrap.ts} +243 -67
- package/src/shell/ui-subscriber-channels.ts +81 -0
- package/src/shell/use-collab-sync.ts +116 -0
- package/src/ui/WordReviewEditor.tsx +496 -2051
- package/src/ui/editor-shell-view.tsx +30 -1
- package/src/ui/editor-surface-controller.tsx +49 -1
- package/src/ui/headless/revision-decoration-model.ts +83 -0
- package/src/{ui-tailwind/chrome → ui/headless}/role-action-sets.ts +1 -1
- package/src/ui/headless/scoped-chrome-policy.ts +2 -2
- package/src/ui/headless/selection-tool-context.ts +1 -1
- package/src/ui/headless/selection-tool-resolver.ts +1 -1
- package/src/ui/runtime-shortcut-dispatch.ts +46 -1
- package/src/ui/ui-controller-factory.ts +221 -0
- package/src/ui-tailwind/chart/ChartSurface.tsx +2 -2
- package/src/ui-tailwind/chart/layout/legend-layout.ts +1 -1
- package/src/ui-tailwind/chart/layout/plot-area.ts +2 -2
- package/src/ui-tailwind/chart/layout/title-layout.ts +1 -1
- package/src/ui-tailwind/chart/render/area.tsx +3 -3
- package/src/ui-tailwind/chart/render/bar-column.tsx +3 -3
- package/src/ui-tailwind/chart/render/bubble.tsx +3 -3
- package/src/ui-tailwind/chart/render/combo.tsx +2 -2
- package/src/ui-tailwind/chart/render/data-labels.tsx +2 -2
- package/src/ui-tailwind/chart/render/font-metrics.ts +2 -2
- package/src/ui-tailwind/chart/render/line.tsx +3 -3
- package/src/ui-tailwind/chart/render/pie.tsx +6 -6
- package/src/ui-tailwind/chart/render/scatter.tsx +3 -3
- package/src/ui-tailwind/chart/render/svg-primitives.ts +3 -3
- package/src/ui-tailwind/chart/render/unsupported.tsx +2 -2
- package/src/ui-tailwind/chrome/build-context-menu-entries.ts +88 -0
- package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +1 -1
- package/src/ui-tailwind/chrome/collab-send-to-supplier-button.tsx +1 -1
- package/src/ui-tailwind/chrome/collab-tamper-banner.tsx +1 -1
- package/src/ui-tailwind/chrome/collab-top-nav-container.tsx +1 -1
- package/src/ui-tailwind/chrome/editor-action-registry.ts +553 -0
- package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +182 -0
- package/src/ui-tailwind/chrome/local-surface-arbiter.ts +534 -0
- package/src/ui-tailwind/chrome/resolve-target-kind.ts +226 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +38 -4
- package/src/ui-tailwind/chrome/tw-context-band.tsx +125 -0
- package/src/ui-tailwind/chrome/tw-context-menu-portal.tsx +248 -0
- package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +42 -1
- package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +8 -7
- package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +38 -4
- package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +104 -6
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +66 -7
- package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +54 -8
- package/src/ui-tailwind/chrome/tw-shortcut-hint.tsx +7 -1
- package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +33 -0
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +78 -1
- package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +16 -8
- package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +276 -0
- package/src/ui-tailwind/chrome/use-context-menu-controller.ts +201 -0
- package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +22 -4
- package/src/ui-tailwind/chrome-overlay/tw-comment-balloon-layer.tsx +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-locked-block-layer.tsx +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +11 -5
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +197 -3
- package/src/ui-tailwind/chrome-overlay/tw-revision-margin-bar-layer.tsx +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +35 -6
- package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +24 -16
- package/src/ui-tailwind/chrome-overlay/tw-table-continuation-header.tsx +1 -1
- package/src/ui-tailwind/debug/README.md +57 -0
- package/src/ui-tailwind/debug/index.ts +3 -0
- package/src/ui-tailwind/debug/tw-debug-overlay.tsx +186 -0
- package/src/ui-tailwind/debug/tw-debug-presentation.tsx +80 -0
- package/src/ui-tailwind/debug/tw-debug-top-bar.tsx +83 -0
- package/src/ui-tailwind/editor-surface/chart-node-view.tsx +2 -2
- package/src/ui-tailwind/editor-surface/float-wrap-resolver.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +135 -10
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +40 -13
- package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-schema.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +3 -3
- package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +1 -1
- package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +2 -2
- package/src/ui-tailwind/editor-surface/scroll-anchor.ts +91 -9
- package/src/ui-tailwind/editor-surface/shape-renderer.ts +1 -1
- package/src/ui-tailwind/editor-surface/surface-layer.ts +1 -1
- package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +1 -1
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +23 -6
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +132 -22
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +1 -1
- package/src/ui-tailwind/index.ts +0 -5
- package/src/ui-tailwind/overlay-anchor-bridge-context.tsx +33 -0
- package/src/ui-tailwind/page-stack/floating-image-overlay-model.ts +66 -29
- package/src/ui-tailwind/page-stack/tw-floating-image-layer.tsx +25 -2
- package/src/ui-tailwind/review/comment-markdown-renderer.tsx +15 -0
- package/src/ui-tailwind/review/tw-review-rail.tsx +92 -4
- package/src/ui-tailwind/review/tw-workflow-tab.tsx +1 -1
- package/src/ui-tailwind/review-workspace/page-chrome.ts +210 -0
- package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +101 -0
- package/src/ui-tailwind/review-workspace/paragraph-layout.ts +115 -0
- package/src/ui-tailwind/review-workspace/selection-toolbar-placement.ts +97 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-navigator.tsx +130 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-page-toolbar.tsx +240 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +59 -0
- package/src/ui-tailwind/review-workspace/types.ts +408 -0
- package/src/ui-tailwind/review-workspace/use-chrome-policy.ts +104 -0
- package/src/ui-tailwind/review-workspace/use-derived-view-state.ts +151 -0
- package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +70 -0
- package/src/ui-tailwind/review-workspace/use-grabbed-segment-offsets.ts +40 -0
- package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +55 -0
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +130 -0
- package/src/ui-tailwind/review-workspace/use-pm-surface-capture.ts +60 -0
- package/src/ui-tailwind/review-workspace/use-review-rail-state.ts +63 -0
- package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +170 -0
- package/src/ui-tailwind/review-workspace/use-scroll-root-capture.ts +28 -0
- package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +113 -0
- package/src/ui-tailwind/review-workspace/use-shell-selection-anchor-bridge.ts +120 -0
- package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +55 -0
- package/src/ui-tailwind/review-workspace/use-viewport-dimensions.ts +43 -0
- package/src/ui-tailwind/review-workspace/use-workspace-arbiter.ts +25 -0
- package/src/ui-tailwind/review-workspace/use-workspace-composition.ts +86 -0
- package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +150 -0
- package/src/ui-tailwind/theme/editor-theme.css +25 -0
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +2 -2
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +61 -98
- package/src/ui-tailwind/tw-review-workspace.tsx +521 -1802
- package/src/ui-tailwind/ui-api-context.tsx +43 -0
- package/src/ui-tailwind/ui-shell-channels-context.tsx +49 -0
- package/src/validation/compatibility-engine.ts +6 -6
- package/src/runtime/styles-cascade.ts +0 -33
- package/src/ui-tailwind/chrome/tw-mode-dock.tsx +0 -85
- /package/src/runtime/{page-number-format.ts → formatting/field/page-number-format.ts} +0 -0
- /package/src/runtime/{ai-action-policy.ts → workflow/ai-action-policy.ts} +0 -0
- /package/src/runtime/{scope-tag-registry.ts → workflow/scope-tag-registry.ts} +0 -0
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `DocxSession` — the reserved Layer-01 public entry class.
|
|
3
|
+
*
|
|
4
|
+
* Shape matches `docs/architecture/01-package-session.md` § Public
|
|
5
|
+
* entry points:
|
|
6
|
+
*
|
|
7
|
+
* open(bytes, opts?) → Promise<OpenResult>
|
|
8
|
+
* export(doc, opts?) → Promise<ExportResult>
|
|
9
|
+
* validate(doc, opts?) → ValidationReport
|
|
10
|
+
*
|
|
11
|
+
* Slice-4 state: `open()` is wired and additionally attaches a
|
|
12
|
+
* module-private legacy view (accessed via
|
|
13
|
+
* `src/session/_internal-legacy.ts`) for in-tree services that still
|
|
14
|
+
* need the stateful `exportDocx` closure. `export()` and `validate()`
|
|
15
|
+
* throw `SessionNotWiredError` — the architecture reserves them as
|
|
16
|
+
* stateless, which requires a new pipeline that lands in Slice 5.
|
|
17
|
+
*
|
|
18
|
+
* P6 discipline: `src/session` must not import from `src/runtime`,
|
|
19
|
+
* `src/ui` / `src/ui-tailwind`, `src/core`, or `src/review`. This file transits
|
|
20
|
+
* `src/io/docx-session.ts` (legacy integration orchestrator) under a
|
|
21
|
+
* documented debt exception — the legacy file itself still imports
|
|
22
|
+
* forbidden runtime/review surfaces (see `src/io/docx-session.ts:35–43`
|
|
23
|
+
* and architecture doc §P6 "debt exception"). The exception closes in
|
|
24
|
+
* Slice 5 when the legacy file is deleted; the purity checker warns
|
|
25
|
+
* (not errors) on this specific transit, and a session-transit test
|
|
26
|
+
* pins the exception so any accidental reintroduction is caught.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import type { CanonicalDocument } from "../model/canonical-document.ts";
|
|
30
|
+
import { buildCompatibilityReport } from "../validation/compatibility-engine.ts";
|
|
31
|
+
// Slice 5e-8.a: session.ts now reaches the load pipeline directly
|
|
32
|
+
// via `src/session/import/loader.ts` (in-layer). The legacy transit
|
|
33
|
+
// through `src/io/docx-session.ts` is retired along with the
|
|
34
|
+
// debt-exception entry for this file in the purity CI guard.
|
|
35
|
+
import { loadDocxSessionAsync } from "./import/loader.ts";
|
|
36
|
+
import type { LoadDocxEditorSessionAsyncOptions } from "./import/loader-types.ts";
|
|
37
|
+
import { createLoadScheduler } from "../io/load-scheduler.ts";
|
|
38
|
+
// P6 debt exception: the laycache probe lives in
|
|
39
|
+
// `src/runtime/prerender/` because its `CacheEnvelope` type
|
|
40
|
+
// transitively references `CompatibilityReport` (core) and
|
|
41
|
+
// `RuntimePageGraph` (runtime/layout). Relocating those shared
|
|
42
|
+
// types is a layer-04/05 concern. The transit is pinned in
|
|
43
|
+
// `scripts/ci-check-session-layer-purity.mjs::DEBT_EXCEPTIONS` so
|
|
44
|
+
// CI surfaces the debt on every run.
|
|
45
|
+
// Slice 5e-11 item-1: the laycache probe is now dependency-injected
|
|
46
|
+
// via `OpenOptions.laycacheProbe` — session no longer imports from
|
|
47
|
+
// `src/runtime/prerender/**`. This retires the last P6 debt
|
|
48
|
+
// exception for Layer 01. See `docs/plans/cross-layer-coord-01.md §1`.
|
|
49
|
+
// Slice 5e-11 item-4: the WeakMap-backed `_internal-legacy.ts`
|
|
50
|
+
// accessor has been retired. `initialSnapshot`,
|
|
51
|
+
// `initialEditorStatePayload?`, `exportDocx`, and `dispose?` are
|
|
52
|
+
// public fields on `OpenResult` now; `DocxSession.export` + `dispose`
|
|
53
|
+
// route through the captured `lastOpenResult` directly.
|
|
54
|
+
import type { EditorSessionState } from "./session-state.ts";
|
|
55
|
+
import type {
|
|
56
|
+
EmbeddedDocumentManifest,
|
|
57
|
+
ExportOptions,
|
|
58
|
+
ExportResult,
|
|
59
|
+
OpenOptions,
|
|
60
|
+
OpenResult,
|
|
61
|
+
PreservationSnapshot,
|
|
62
|
+
ReopenBarrier,
|
|
63
|
+
ValidateOptions,
|
|
64
|
+
ValidationFinding,
|
|
65
|
+
ValidationReport,
|
|
66
|
+
} from "./types.ts";
|
|
67
|
+
import type { PersistedEditorSnapshot } from "../api/public-types.ts";
|
|
68
|
+
import {
|
|
69
|
+
decodePersistedSourcePackageBytes,
|
|
70
|
+
hasValidPersistedSourcePackageDigest,
|
|
71
|
+
} from "../io/source-package-provenance.ts";
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Thrown from `DocxSession.export` when called on an instance that has
|
|
75
|
+
* not yet called `open()` (or whose capture has been cleared by
|
|
76
|
+
* `dispose()`).
|
|
77
|
+
*
|
|
78
|
+
* Layer-01 export is session-bound: `open()` captures the
|
|
79
|
+
* `ImportedDocxState` (source bytes, `OpcPackage`, per-part
|
|
80
|
+
* relationships, preserved-part manifest) that the export pipeline
|
|
81
|
+
* needs in order to round-trip. Calling `export(sessionState)` on an
|
|
82
|
+
* instance that never ran `open()` has no state to dispatch through.
|
|
83
|
+
*
|
|
84
|
+
* The architecture doc (`docs/architecture/01-package-session.md` §
|
|
85
|
+
* "Public entry points") deliberately reserves `export(sessionState)`
|
|
86
|
+
* rather than `export(doc)`: `EditorSessionState` carries the
|
|
87
|
+
* session-level fields (`workflowOverlay`, `workflowMetadata`,
|
|
88
|
+
* `warningLog`, `protectionSnapshot`) that the layer bakes into
|
|
89
|
+
* `customXml/itemN.xml` on export, which are not part of
|
|
90
|
+
* `CanonicalDocument`. A cross-session `export(doc)` overload is
|
|
91
|
+
* deliberately omitted from this phase — it requires a byte-free
|
|
92
|
+
* preservation contract (Layer-02 extension) that has not shipped.
|
|
93
|
+
*/
|
|
94
|
+
export class SessionNotOpenError extends Error {
|
|
95
|
+
constructor() {
|
|
96
|
+
super(
|
|
97
|
+
"DocxSession.export(sessionState) requires DocxSession.open(bytes) to have been called on this session instance first. " +
|
|
98
|
+
"Open the source package through this session, then call export(sessionState) with the runtime's current session state.",
|
|
99
|
+
);
|
|
100
|
+
this.name = "SessionNotOpenError";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Legacy alias for `SessionNotOpenError`. Kept for one release window
|
|
106
|
+
* so callers that were catching the 5e-4 `SessionNotWiredError` for
|
|
107
|
+
* `export` continue to work.
|
|
108
|
+
*
|
|
109
|
+
* @deprecated use `SessionNotOpenError` — this alias goes away with
|
|
110
|
+
* Slice 5e-9 when the legacy file is deleted.
|
|
111
|
+
*/
|
|
112
|
+
export const SessionNotWiredError = SessionNotOpenError;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Reserved Layer-01 entry class. `open()` and `export()` are async per
|
|
116
|
+
* the architecture reservation; `validate()` is synchronous because it
|
|
117
|
+
* runs against an already-loaded canonical document.
|
|
118
|
+
*
|
|
119
|
+
* Session-bound export model: `open()` captures the `OpenResult`
|
|
120
|
+
* handle (which carries the `LegacyOpenView.exportDocx` closure +
|
|
121
|
+
* `ImportedDocxState`). `export(sessionState, opts)` dispatches
|
|
122
|
+
* through that captured closure with the caller-supplied session
|
|
123
|
+
* state — the runtime's current state, carrying any
|
|
124
|
+
* `workflowOverlay` / `workflowMetadata` edits made between open and
|
|
125
|
+
* export. A session instance that has not been `open`ed throws
|
|
126
|
+
* `SessionNotOpenError`.
|
|
127
|
+
*
|
|
128
|
+
* Re-opening on the same instance overwrites the capture; `export()`
|
|
129
|
+
* always dispatches through the most recent open. In practice hosts
|
|
130
|
+
* construct one `DocxSession` per document and keep it alive for the
|
|
131
|
+
* runtime's lifetime (see `services/debug/lib/doc-loader.ts` for the
|
|
132
|
+
* reference pattern).
|
|
133
|
+
*/
|
|
134
|
+
export class DocxSession {
|
|
135
|
+
/**
|
|
136
|
+
* Most recent `open()` result — captured so `export(sessionState)`
|
|
137
|
+
* can reach the `LegacyOpenView.exportDocx` closure via the
|
|
138
|
+
* `src/session/_internal-legacy.ts` WeakMap. Cleared on `dispose()`.
|
|
139
|
+
*
|
|
140
|
+
* The caller's `sessionState` argument to `export()` is passed
|
|
141
|
+
* through verbatim; this slot is only the dispatch anchor, not a
|
|
142
|
+
* source of truth for export data.
|
|
143
|
+
*/
|
|
144
|
+
private lastOpenResult?: OpenResult;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Open a DOCX package from bytes. Returns the canonical document,
|
|
148
|
+
* session state, compatibility report, preservation snapshot, and
|
|
149
|
+
* the embedded-document manifest list populated from
|
|
150
|
+
* `collectEmbeddedDocuments` (P8).
|
|
151
|
+
*
|
|
152
|
+
* Implementation delegates to the legacy async loader
|
|
153
|
+
* (`loadDocxEditorSessionAsync`) while Slices 2–4 extract parse/
|
|
154
|
+
* serialize orchestration into `src/session/import/**` +
|
|
155
|
+
* `src/session/export/**`. The signature here is stable — the body
|
|
156
|
+
* can swap to the new orchestrator without consumers noticing.
|
|
157
|
+
*/
|
|
158
|
+
async open(
|
|
159
|
+
bytes: Uint8Array | ArrayBuffer,
|
|
160
|
+
opts: OpenOptions = {},
|
|
161
|
+
): Promise<OpenResult> {
|
|
162
|
+
// CLAUDE.md Performance Invariant 10: colocate the probe with the
|
|
163
|
+
// loader so nothing blocking can be inserted between them. When
|
|
164
|
+
// `opts.tryLaycacheEnvelope` is true AND `opts.laycacheProbe` is
|
|
165
|
+
// supplied, run the probe here; the result becomes
|
|
166
|
+
// `laycacheEnvelope` on the legacy loader options. An explicitly
|
|
167
|
+
// supplied `opts.laycacheEnvelope` wins over the probe so custom
|
|
168
|
+
// caching layers still work.
|
|
169
|
+
//
|
|
170
|
+
// The probe itself is dependency-injected (Slice 5e-11 item-1) so
|
|
171
|
+
// session does not import from `src/runtime/prerender/**`. The
|
|
172
|
+
// shell / prerender entry point passes `tryReadLaycacheEnvelope`
|
|
173
|
+
// as the probe.
|
|
174
|
+
let resolvedLaycacheEnvelope: unknown = opts.laycacheEnvelope;
|
|
175
|
+
if (
|
|
176
|
+
resolvedLaycacheEnvelope === undefined &&
|
|
177
|
+
opts.tryLaycacheEnvelope === true &&
|
|
178
|
+
opts.laycacheProbe
|
|
179
|
+
) {
|
|
180
|
+
const probeResult = await opts.laycacheProbe(bytes);
|
|
181
|
+
if (probeResult) {
|
|
182
|
+
resolvedLaycacheEnvelope = probeResult.envelope;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Scheduler created here so its `dispose` can be handed back to
|
|
187
|
+
// the caller via `LegacyOpenView.dispose`. Interactive hosts
|
|
188
|
+
// (editor-runtime-boundary in Slice 5c) call it on unmount to
|
|
189
|
+
// cancel pending idle chart-preview callbacks; one-shot callers
|
|
190
|
+
// like `prerenderDocument` can omit the call because the sync
|
|
191
|
+
// backend has nothing to cancel.
|
|
192
|
+
const scheduler =
|
|
193
|
+
opts.schedulerBackend !== undefined
|
|
194
|
+
? createLoadScheduler({ backendOverride: opts.schedulerBackend })
|
|
195
|
+
: createLoadScheduler();
|
|
196
|
+
|
|
197
|
+
const legacyOptions: LoadDocxEditorSessionAsyncOptions = {
|
|
198
|
+
bytes,
|
|
199
|
+
documentId: opts.documentId ?? mintTransientDocumentId(),
|
|
200
|
+
...(opts.sourceLabel !== undefined
|
|
201
|
+
? { sourceLabel: opts.sourceLabel }
|
|
202
|
+
: {}),
|
|
203
|
+
...(opts.editorBuild !== undefined
|
|
204
|
+
? { editorBuild: opts.editorBuild }
|
|
205
|
+
: {}),
|
|
206
|
+
...(opts.hostAdapter !== undefined
|
|
207
|
+
? { hostAdapter: opts.hostAdapter }
|
|
208
|
+
: {}),
|
|
209
|
+
...(opts.offloadEmbeddedDocuments !== undefined
|
|
210
|
+
? { offloadEmbeddedDocuments: opts.offloadEmbeddedDocuments }
|
|
211
|
+
: {}),
|
|
212
|
+
...(opts.onLoadStage !== undefined
|
|
213
|
+
? { onLoadStage: opts.onLoadStage }
|
|
214
|
+
: {}),
|
|
215
|
+
...(opts.telemetryBus !== undefined
|
|
216
|
+
? {
|
|
217
|
+
// The public `OpenOptions.telemetryBus` is typed `unknown`
|
|
218
|
+
// so this barrel does not drag runtime-layer types into
|
|
219
|
+
// the session surface. The legacy loader narrows it back
|
|
220
|
+
// to the concrete `TelemetryBus` shape internally.
|
|
221
|
+
telemetryBus:
|
|
222
|
+
opts.telemetryBus as LoadDocxEditorSessionAsyncOptions["telemetryBus"],
|
|
223
|
+
}
|
|
224
|
+
: {}),
|
|
225
|
+
...(resolvedLaycacheEnvelope !== undefined
|
|
226
|
+
? {
|
|
227
|
+
laycacheEnvelope:
|
|
228
|
+
resolvedLaycacheEnvelope as LoadDocxEditorSessionAsyncOptions["laycacheEnvelope"],
|
|
229
|
+
}
|
|
230
|
+
: {}),
|
|
231
|
+
...(opts.onChartPreviewsReady !== undefined
|
|
232
|
+
? { onChartPreviewsReady: opts.onChartPreviewsReady }
|
|
233
|
+
: {}),
|
|
234
|
+
...(opts.onProgressiveSnapshot !== undefined
|
|
235
|
+
? { onProgressiveSnapshot: opts.onProgressiveSnapshot }
|
|
236
|
+
: {}),
|
|
237
|
+
...(opts.surfaceProjector !== undefined
|
|
238
|
+
? { surfaceProjector: opts.surfaceProjector }
|
|
239
|
+
: {}),
|
|
240
|
+
// Session class picks the scheduler so callers do not have to.
|
|
241
|
+
// DOM + Node both work: `createLoadScheduler()` auto-detects the
|
|
242
|
+
// best backend (scheduler.yield → MessageChannel → setTimeout →
|
|
243
|
+
// sync). `opts.schedulerBackend` pins the backend when the caller
|
|
244
|
+
// needs determinism (e.g. `prerenderDocument` forces `"sync"`).
|
|
245
|
+
//
|
|
246
|
+
// Scheduler disposal: carried through `LegacyOpenView.dispose`
|
|
247
|
+
// so interactive hosts can cancel pending idle chart-preview
|
|
248
|
+
// callbacks on unmount (Performance Invariant 9). Disposing
|
|
249
|
+
// here would race with the loader's own post-return idle work.
|
|
250
|
+
scheduler,
|
|
251
|
+
};
|
|
252
|
+
const legacy = await loadDocxSessionAsync(legacyOptions);
|
|
253
|
+
|
|
254
|
+
const sessionState = legacy.initialSessionState;
|
|
255
|
+
const canonicalDocument =
|
|
256
|
+
sessionState.canonicalDocument as CanonicalDocument;
|
|
257
|
+
const preservation: PreservationSnapshot =
|
|
258
|
+
canonicalDocument.preservation;
|
|
259
|
+
|
|
260
|
+
// P8 Step 6: embedded-document discovery runs inside the legacy
|
|
261
|
+
// loader and surfaces here via `legacy.embeddedDocumentManifests`.
|
|
262
|
+
// The array is empty when the package has no embeddings or the
|
|
263
|
+
// loader short-circuited on a laycache envelope.
|
|
264
|
+
const embeddedDocuments: readonly EmbeddedDocumentManifest[] =
|
|
265
|
+
legacy.embeddedDocumentManifests ?? [];
|
|
266
|
+
|
|
267
|
+
// Slice 5e-11 item-4: the remaining `LegacyOpenView` fields
|
|
268
|
+
// (`initialSnapshot`, `initialEditorStatePayload`, `exportDocx`,
|
|
269
|
+
// `dispose`) graduate to `OpenResult` directly per L06's
|
|
270
|
+
// `OpenResult` shape decision (`docs/architecture/06-workflow-
|
|
271
|
+
// review.md` §"Open-time surface"). The WeakMap-backed
|
|
272
|
+
// `_internal-legacy.ts` is retired.
|
|
273
|
+
const result: OpenResult = {
|
|
274
|
+
sessionState,
|
|
275
|
+
canonicalDocument,
|
|
276
|
+
compatibility: sessionState.compatibility,
|
|
277
|
+
preservation,
|
|
278
|
+
embeddedDocuments,
|
|
279
|
+
// Slice 5e-1 legacy-view graduates.
|
|
280
|
+
readOnly: legacy.readOnly,
|
|
281
|
+
...(legacy.fatalError !== undefined
|
|
282
|
+
? { fatalError: legacy.fatalError }
|
|
283
|
+
: {}),
|
|
284
|
+
protectionSnapshot: legacy.protectionSnapshot,
|
|
285
|
+
...(resolvedLaycacheEnvelope !== undefined
|
|
286
|
+
? { laycacheEnvelope: resolvedLaycacheEnvelope }
|
|
287
|
+
: {}),
|
|
288
|
+
// Slice 5e-11 item-4 graduates.
|
|
289
|
+
initialSnapshot: legacy.initialSnapshot,
|
|
290
|
+
...(legacy.initialEditorStatePayload !== undefined
|
|
291
|
+
? { initialEditorStatePayload: legacy.initialEditorStatePayload }
|
|
292
|
+
: {}),
|
|
293
|
+
exportDocx: legacy.exportDocx,
|
|
294
|
+
dispose: () => scheduler.dispose(),
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// Capture the open result so `export(sessionState)` can reach the
|
|
298
|
+
// legacy view's stateful `exportDocx` closure without the caller
|
|
299
|
+
// threading the `OpenResult` back through. Overwritten on re-open;
|
|
300
|
+
// cleared by `dispose()`.
|
|
301
|
+
this.lastOpenResult = result;
|
|
302
|
+
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Serialize an editor session state to a DOCX package.
|
|
308
|
+
*
|
|
309
|
+
* Per the architecture (`docs/architecture/01-package-session.md` §
|
|
310
|
+
* "Public entry points"), `export` takes the full `EditorSessionState`
|
|
311
|
+
* — not just a `CanonicalDocument` — because the session-level fields
|
|
312
|
+
* `workflowOverlay`, `workflowMetadata`, `warningLog`, and
|
|
313
|
+
* `protectionSnapshot` are bakery-inputs to `customXml/itemN.xml` on
|
|
314
|
+
* export, and live outside the canonical model. The runtime calls
|
|
315
|
+
* `session.export(runtime.getSessionState(), opts)` and the session
|
|
316
|
+
* passes `sessionState` straight through to the stateful export
|
|
317
|
+
* pipeline — no splice, no hidden state-carry.
|
|
318
|
+
*
|
|
319
|
+
* Dispatch: the method reaches the `LegacyOpenView.exportDocx`
|
|
320
|
+
* closure captured during the most recent `open()` on this instance.
|
|
321
|
+
* That closure holds the `ImportedDocxState` (source bytes,
|
|
322
|
+
* `OpcPackage`, per-part relationships, preserved-part manifest)
|
|
323
|
+
* that the pipeline needs in order to round-trip. When
|
|
324
|
+
* `sessionState.canonicalDocument` is referentially the same object
|
|
325
|
+
* as was produced at open time and every other field is unchanged,
|
|
326
|
+
* the legacy pipeline hits its byte-reuse fast path (identical
|
|
327
|
+
* `initialCanonicalSignature` + `canReuse`) and returns the source
|
|
328
|
+
* bytes directly.
|
|
329
|
+
*
|
|
330
|
+
* Throws `SessionNotOpenError` when called before `open()` on this
|
|
331
|
+
* instance. Cross-session export (a session state whose source bytes
|
|
332
|
+
* this instance never saw) is a later Layer-01 follow-up: it needs a
|
|
333
|
+
* byte-free preservation contract that has not shipped.
|
|
334
|
+
*
|
|
335
|
+
* P6 discipline: dispatch goes through `getLegacyOpenView(result).
|
|
336
|
+
* exportDocx(...)` — the WeakMap-backed accessor at
|
|
337
|
+
* `src/session/_internal-legacy.ts`. No direct import of
|
|
338
|
+
* `exportDocxEditorSession` from `src/io/docx-session.ts` is added
|
|
339
|
+
* beyond the `session.ts → docx-session.ts` debt exception already
|
|
340
|
+
* present for `open()`'s legacy transit.
|
|
341
|
+
*/
|
|
342
|
+
async export(
|
|
343
|
+
sessionState: EditorSessionState,
|
|
344
|
+
opts: ExportOptions = {},
|
|
345
|
+
): Promise<ExportResult> {
|
|
346
|
+
const captured = this.lastOpenResult;
|
|
347
|
+
if (!captured) {
|
|
348
|
+
throw new SessionNotOpenError();
|
|
349
|
+
}
|
|
350
|
+
// Slice 5e-11 item-4: `exportDocx` is a public field on
|
|
351
|
+
// `OpenResult` now. The WeakMap-backed accessor was retired when
|
|
352
|
+
// the remaining legacy-view fields graduated.
|
|
353
|
+
return captured.exportDocx(sessionState, opts);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Release resources captured by the most recent `open()`. Clears
|
|
358
|
+
* the session's internal capture and forwards to `OpenResult.dispose`
|
|
359
|
+
* (which cancels pending idle chart-preview callbacks scheduled by
|
|
360
|
+
* the load scheduler — CLAUDE.md Performance Invariant 9).
|
|
361
|
+
*
|
|
362
|
+
* After `dispose()`, a subsequent `export(sessionState)` on this
|
|
363
|
+
* instance throws `SessionNotOpenError` until `open()` is called
|
|
364
|
+
* again.
|
|
365
|
+
*/
|
|
366
|
+
dispose(): void {
|
|
367
|
+
const captured = this.lastOpenResult;
|
|
368
|
+
if (!captured) return;
|
|
369
|
+
captured.dispose?.();
|
|
370
|
+
this.lastOpenResult = undefined;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Rehydrate a Layer-01 session from a previously-persisted
|
|
375
|
+
* `EditorSessionState` or `PersistedEditorSnapshot`.
|
|
376
|
+
*
|
|
377
|
+
* Shipped in Slice 5e-11 item-2 in response to the L07 Option-B
|
|
378
|
+
* decision (`docs/architecture/07-runtime-api.md` §Seam-reality).
|
|
379
|
+
* Replaces the manual decode-bytes-and-call-loadDocxSessionSync
|
|
380
|
+
* dance that `src/shell/session-bootstrap.ts::resolvePackageBackedExportSession`
|
|
381
|
+
* had to do by reaching across the Layer-01 boundary.
|
|
382
|
+
*
|
|
383
|
+
* Behaviour:
|
|
384
|
+
* 1. Extract the persisted `sourcePackage` envelope from the
|
|
385
|
+
* snapshot. If absent → return `ReopenBarrier { reason: "no-source-package" }`.
|
|
386
|
+
* 2. Base64-decode the bytes via `decodePersistedSourcePackageBytes`.
|
|
387
|
+
* If decode throws → return `ReopenBarrier { reason: "corrupted-bytes" }`.
|
|
388
|
+
* 3. Digest-check via `hasValidPersistedSourcePackageDigest`. On
|
|
389
|
+
* mismatch → return `ReopenBarrier { reason: "invalid-digest" }`.
|
|
390
|
+
* 4. Forward the bytes to `loadDocxSessionAsync`. Any fatal parse
|
|
391
|
+
* error surfaces on the returned `OpenResult.fatalError` as
|
|
392
|
+
* normal — NOT as a barrier (the rehydrate path produced a
|
|
393
|
+
* valid `OpenResult`; the LOAD decided it was unusable).
|
|
394
|
+
*
|
|
395
|
+
* Never throws. Barriers are always returned as data so callers
|
|
396
|
+
* can diff / display / persist without try-catch envelopes.
|
|
397
|
+
*
|
|
398
|
+
* On success, the returned `OpenResult` is captured on this
|
|
399
|
+
* instance the same way `open()` does — subsequent `export()` and
|
|
400
|
+
* `dispose()` calls operate on the rehydrated session.
|
|
401
|
+
*/
|
|
402
|
+
async reopenFromSnapshot(
|
|
403
|
+
snapshot: PersistedEditorSnapshot | EditorSessionState,
|
|
404
|
+
opts: OpenOptions = {},
|
|
405
|
+
): Promise<OpenResult | ReopenBarrier> {
|
|
406
|
+
const sourcePackage = snapshot.sourcePackage;
|
|
407
|
+
if (!sourcePackage) {
|
|
408
|
+
return {
|
|
409
|
+
reason: "no-source-package",
|
|
410
|
+
message:
|
|
411
|
+
"Cannot reopen from snapshot — the persisted session carries no embedded source-package envelope.",
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
let bytes: Uint8Array;
|
|
415
|
+
try {
|
|
416
|
+
bytes = decodePersistedSourcePackageBytes(sourcePackage);
|
|
417
|
+
} catch (err) {
|
|
418
|
+
return {
|
|
419
|
+
reason: "corrupted-bytes",
|
|
420
|
+
message: `Failed to decode source-package bytes: ${err instanceof Error ? err.message : String(err)}`,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
if (!hasValidPersistedSourcePackageDigest(sourcePackage, bytes)) {
|
|
424
|
+
return {
|
|
425
|
+
reason: "invalid-digest",
|
|
426
|
+
message:
|
|
427
|
+
"Source-package digest check failed — the persisted snapshot has been tampered with or the schema drifted.",
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
return this.open(bytes, {
|
|
431
|
+
...opts,
|
|
432
|
+
...(opts.documentId === undefined
|
|
433
|
+
? { documentId: snapshot.documentId }
|
|
434
|
+
: {}),
|
|
435
|
+
...(opts.sourceLabel === undefined && sourcePackage.sourceLabel
|
|
436
|
+
? { sourceLabel: sourcePackage.sourceLabel }
|
|
437
|
+
: {}),
|
|
438
|
+
...(opts.editorBuild === undefined && "editorBuild" in snapshot
|
|
439
|
+
? { editorBuild: snapshot.editorBuild }
|
|
440
|
+
: {}),
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Validate a canonical document against the compatibility engine.
|
|
446
|
+
*
|
|
447
|
+
* Slice 5e-4 wired this through the existing
|
|
448
|
+
* `buildCompatibilityReport` machinery. The method is synchronous
|
|
449
|
+
* per the architecture reservation and is a pure function of the
|
|
450
|
+
* canonical document — no session state is read.
|
|
451
|
+
*
|
|
452
|
+
* `ok` is `false` when the report's `blockExport` flag is set OR
|
|
453
|
+
* any finding carries `severity === "error"`. Consumer surfaces
|
|
454
|
+
* (agents, CI gates) can rely on this single boolean to decide
|
|
455
|
+
* whether the document is export-blocked.
|
|
456
|
+
*/
|
|
457
|
+
validate(
|
|
458
|
+
doc: CanonicalDocument,
|
|
459
|
+
_opts: ValidateOptions = {},
|
|
460
|
+
): ValidationReport {
|
|
461
|
+
const report = buildCompatibilityReport({
|
|
462
|
+
// `buildCompatibilityReport` accepts the richer
|
|
463
|
+
// `CanonicalDocumentEnvelope` shape; a `CanonicalDocument` is a
|
|
464
|
+
// structural subset, so the cast is safe at the boundary.
|
|
465
|
+
document: doc as unknown as Parameters<
|
|
466
|
+
typeof buildCompatibilityReport
|
|
467
|
+
>[0]["document"],
|
|
468
|
+
generatedAt: new Date().toISOString(),
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const findings: ValidationFinding[] = [
|
|
472
|
+
...report.featureEntries.map<ValidationFinding>((entry) => ({
|
|
473
|
+
code: entry.featureEntryId,
|
|
474
|
+
severity: featureClassToSeverity(entry.featureClass),
|
|
475
|
+
message: entry.message,
|
|
476
|
+
...(entry.featureKey !== undefined
|
|
477
|
+
? { featureKey: entry.featureKey }
|
|
478
|
+
: {}),
|
|
479
|
+
})),
|
|
480
|
+
...report.warnings.map<ValidationFinding>((warning) => ({
|
|
481
|
+
code: warning.code,
|
|
482
|
+
severity: "warning" as const,
|
|
483
|
+
message: warning.message,
|
|
484
|
+
})),
|
|
485
|
+
...report.errors.map<ValidationFinding>((error) => ({
|
|
486
|
+
code: error.code,
|
|
487
|
+
severity: "error" as const,
|
|
488
|
+
message: error.message,
|
|
489
|
+
})),
|
|
490
|
+
];
|
|
491
|
+
|
|
492
|
+
const ok =
|
|
493
|
+
!report.blockExport &&
|
|
494
|
+
!findings.some((finding) => finding.severity === "error");
|
|
495
|
+
|
|
496
|
+
return { ok, findings };
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Map `CompatibilityFeatureClass` onto the narrower severity tri-state
|
|
502
|
+
* the `ValidationFinding` contract exposes. Fatal / blocking classes
|
|
503
|
+
* become `"error"`; partially-supported / warning-esque classes become
|
|
504
|
+
* `"warning"`; everything else is informational.
|
|
505
|
+
*/
|
|
506
|
+
function featureClassToSeverity(
|
|
507
|
+
featureClass: string,
|
|
508
|
+
): ValidationFinding["severity"] {
|
|
509
|
+
if (
|
|
510
|
+
featureClass === "unsupported-fatal" ||
|
|
511
|
+
featureClass === "unsupported-blocking" ||
|
|
512
|
+
featureClass === "unsupported-blocking-export"
|
|
513
|
+
) {
|
|
514
|
+
return "error";
|
|
515
|
+
}
|
|
516
|
+
if (
|
|
517
|
+
featureClass === "unsupported-partial" ||
|
|
518
|
+
featureClass === "supported-partial" ||
|
|
519
|
+
featureClass === "preserve-only"
|
|
520
|
+
) {
|
|
521
|
+
return "warning";
|
|
522
|
+
}
|
|
523
|
+
return "info";
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function mintTransientDocumentId(): string {
|
|
527
|
+
// The session class does not own stable document identity; hosts
|
|
528
|
+
// that need round-trip-stable ids pass `opts.documentId`. This
|
|
529
|
+
// fallback is for ad-hoc opens (tests, one-off conversions).
|
|
530
|
+
const rand = Math.random().toString(36).slice(2, 10);
|
|
531
|
+
return `docx-session-${Date.now().toString(36)}-${rand}`;
|
|
532
|
+
}
|