@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
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer-06 Slice 3 — `createScopeFromBlockId` adapter.
|
|
3
|
+
*
|
|
4
|
+
* Resolves a `blockId` (paragraph/table/sdt-index identifier minted by
|
|
5
|
+
* `src/runtime/surface-projection.ts`) into the `EditorAnchorProjection`
|
|
6
|
+
* that `runtime.addScope` expects, then delegates to the existing
|
|
7
|
+
* marker-backed scope creator.
|
|
8
|
+
*
|
|
9
|
+
* Resolution walks the canonical document directly, not the surface
|
|
10
|
+
* snapshot. Going through `runtime.getRenderSnapshot().surface.blocks`
|
|
11
|
+
* would miss blocks the surface replaces with `placeholder-culled-*`
|
|
12
|
+
* entries under viewport culling — a real reproducibility failure on
|
|
13
|
+
* CCEP-size documents.
|
|
14
|
+
*
|
|
15
|
+
* This adapter unblocks `v3 runtime.workflow.createScope` graduation
|
|
16
|
+
* from `mock` → `live-with-adapter`. Consumers that already carry a
|
|
17
|
+
* full `EditorAnchorProjection` should continue calling
|
|
18
|
+
* `runtime.addScope` directly.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type {
|
|
22
|
+
BlockNode,
|
|
23
|
+
CanonicalDocument,
|
|
24
|
+
DocumentRootNode,
|
|
25
|
+
InlineNode,
|
|
26
|
+
ParagraphNode,
|
|
27
|
+
} from "../../model/canonical-document.ts";
|
|
28
|
+
import type { CanonicalDocumentEnvelope } from "../../core/state/editor-state.ts";
|
|
29
|
+
import type {
|
|
30
|
+
AddScopeParams,
|
|
31
|
+
AddScopeResult,
|
|
32
|
+
EditorAnchorProjection,
|
|
33
|
+
EditorStoryTarget,
|
|
34
|
+
RuntimeRenderSnapshot,
|
|
35
|
+
WorkflowMetadataEntry,
|
|
36
|
+
WorkflowMetadataPersistence,
|
|
37
|
+
WorkflowScopeMode,
|
|
38
|
+
} from "../../api/public-types.ts";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Minimal runtime surface the scope writer needs. Narrowed so layer-06
|
|
42
|
+
* does not import the full `DocumentRuntime` (which would reach up into
|
|
43
|
+
* layer-07/orchestration and violate contract W9).
|
|
44
|
+
*/
|
|
45
|
+
export interface ScopeWriterRuntime {
|
|
46
|
+
getCanonicalDocument(): CanonicalDocumentEnvelope;
|
|
47
|
+
getRenderSnapshot(): RuntimeRenderSnapshot;
|
|
48
|
+
addScope(params: AddScopeParams): AddScopeResult;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface CreateScopeFromBlockIdInput {
|
|
52
|
+
readonly blockId: string;
|
|
53
|
+
readonly mode?: WorkflowScopeMode;
|
|
54
|
+
readonly label?: string;
|
|
55
|
+
readonly scopeId?: string;
|
|
56
|
+
readonly persistence?: WorkflowMetadataPersistence;
|
|
57
|
+
readonly metadata?: Partial<WorkflowMetadataEntry>;
|
|
58
|
+
readonly storyTarget?: EditorStoryTarget;
|
|
59
|
+
/**
|
|
60
|
+
* Coord-06 §13d — per-scope edge stickiness for the range anchor.
|
|
61
|
+
* Defaults to `{ start: 1, end: -1 }` (greedy — absorbs boundary
|
|
62
|
+
* inserts, the pre-§13d shipped shape). Use `{ start: -1, end: 1 }`
|
|
63
|
+
* for a fixed-position anchor (signature-block / template-slot /
|
|
64
|
+
* system-paragraph semantics — rejects boundary inserts; cursor work
|
|
65
|
+
* doesn't leak in). The two diagonal variants `{ 1, 1 }` and
|
|
66
|
+
* `{ -1, -1 }` produce asymmetric edge behaviour (one sticky side,
|
|
67
|
+
* one fixed side) — uncommon in practice but accepted for
|
|
68
|
+
* completeness since `BoundaryAssoc` typing allows them.
|
|
69
|
+
*/
|
|
70
|
+
readonly assoc?: { readonly start: -1 | 1; readonly end: -1 | 1 };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export type CreateScopeFromBlockIdResult =
|
|
74
|
+
| { readonly status: "created"; readonly scopeId: string; readonly anchor: EditorAnchorProjection }
|
|
75
|
+
| { readonly status: "block-not-found"; readonly blockId: string };
|
|
76
|
+
|
|
77
|
+
function inlineLength(node: InlineNode): number {
|
|
78
|
+
switch (node.type) {
|
|
79
|
+
case "text":
|
|
80
|
+
return Array.from(node.text).length;
|
|
81
|
+
case "hyperlink":
|
|
82
|
+
case "field":
|
|
83
|
+
return node.children.reduce(
|
|
84
|
+
(total, child) => total + inlineLength(child as InlineNode),
|
|
85
|
+
0,
|
|
86
|
+
);
|
|
87
|
+
case "bookmark_start":
|
|
88
|
+
case "bookmark_end":
|
|
89
|
+
case "scope_marker_start":
|
|
90
|
+
case "scope_marker_end":
|
|
91
|
+
return 0;
|
|
92
|
+
default:
|
|
93
|
+
return 1;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function paragraphLength(paragraph: ParagraphNode): number {
|
|
98
|
+
return paragraph.children.reduce(
|
|
99
|
+
(total, child) => total + inlineLength(child as InlineNode),
|
|
100
|
+
0,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Resolve a surface-level `blockId` (e.g. `paragraph-3`, `table-0`,
|
|
106
|
+
* `sdt-1`) into an `EditorAnchorProjection` by walking the canonical
|
|
107
|
+
* document in surface-projection order. Returns `null` when no block
|
|
108
|
+
* matches — viewport culling cannot produce false negatives here
|
|
109
|
+
* because the walk is independent of the render snapshot.
|
|
110
|
+
*/
|
|
111
|
+
function resolveBlockAnchorFromCanonical(
|
|
112
|
+
document: CanonicalDocumentEnvelope,
|
|
113
|
+
blockId: string,
|
|
114
|
+
assoc: { readonly start: -1 | 1; readonly end: -1 | 1 } = { start: 1, end: -1 },
|
|
115
|
+
): EditorAnchorProjection | null {
|
|
116
|
+
const root = document.content as DocumentRootNode;
|
|
117
|
+
let paragraphIndex = 0;
|
|
118
|
+
let tableIndex = 0;
|
|
119
|
+
let sdtIndex = 0;
|
|
120
|
+
let cursor = 0;
|
|
121
|
+
const blocks = root.children;
|
|
122
|
+
for (let i = 0; i < blocks.length; i += 1) {
|
|
123
|
+
const block: BlockNode = blocks[i]!;
|
|
124
|
+
let thisBlockLength: number;
|
|
125
|
+
let thisBlockId: string | null = null;
|
|
126
|
+
if (block.type === "paragraph") {
|
|
127
|
+
thisBlockLength = paragraphLength(block);
|
|
128
|
+
thisBlockId = `paragraph-${paragraphIndex}`;
|
|
129
|
+
paragraphIndex += 1;
|
|
130
|
+
} else if (block.type === "table") {
|
|
131
|
+
thisBlockLength = 1;
|
|
132
|
+
thisBlockId = `table-${tableIndex}`;
|
|
133
|
+
tableIndex += 1;
|
|
134
|
+
} else if (block.type === "sdt") {
|
|
135
|
+
thisBlockLength = 1;
|
|
136
|
+
thisBlockId = `sdt-${sdtIndex}`;
|
|
137
|
+
sdtIndex += 1;
|
|
138
|
+
} else {
|
|
139
|
+
thisBlockLength = 1;
|
|
140
|
+
}
|
|
141
|
+
if (thisBlockId === blockId) {
|
|
142
|
+
return {
|
|
143
|
+
kind: "range",
|
|
144
|
+
from: cursor,
|
|
145
|
+
to: cursor + thisBlockLength,
|
|
146
|
+
assoc,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
cursor += thisBlockLength;
|
|
150
|
+
// Surface-projection order inserts a 1-unit gap between sibling
|
|
151
|
+
// blocks except after the last one. Mirror that so paragraph
|
|
152
|
+
// offsets match `surface.blocks[i].{from,to}` for visible rows.
|
|
153
|
+
if (i < blocks.length - 1) cursor += 1;
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Create a workflow scope covering the block identified by `blockId`.
|
|
160
|
+
* Uses the runtime's own `addScope` pipeline so markers, overlay state,
|
|
161
|
+
* audit records, and collab sync all follow the canonical path.
|
|
162
|
+
*/
|
|
163
|
+
export function createScopeFromBlockId(
|
|
164
|
+
runtime: ScopeWriterRuntime,
|
|
165
|
+
input: CreateScopeFromBlockIdInput,
|
|
166
|
+
): CreateScopeFromBlockIdResult {
|
|
167
|
+
const anchor = resolveBlockAnchorFromCanonical(
|
|
168
|
+
runtime.getCanonicalDocument(),
|
|
169
|
+
input.blockId,
|
|
170
|
+
input.assoc,
|
|
171
|
+
);
|
|
172
|
+
if (!anchor) {
|
|
173
|
+
return { status: "block-not-found", blockId: input.blockId };
|
|
174
|
+
}
|
|
175
|
+
const result: AddScopeResult = runtime.addScope({
|
|
176
|
+
anchor,
|
|
177
|
+
mode: input.mode,
|
|
178
|
+
scopeId: input.scopeId,
|
|
179
|
+
persistence: input.persistence,
|
|
180
|
+
metadata: input.metadata,
|
|
181
|
+
storyTarget: input.storyTarget,
|
|
182
|
+
label: input.label,
|
|
183
|
+
});
|
|
184
|
+
return { status: "created", scopeId: result.scopeId, anchor: result.anchor };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Exported for tests that need to verify the resolver independently.
|
|
188
|
+
export { resolveBlockAnchorFromCanonical };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W10 · Overlay-visibility policy (class-A state) — runtime helpers.
|
|
3
|
+
*
|
|
4
|
+
* Document-authored visibility policy for every overlay kind (scope rail,
|
|
5
|
+
* tracked-changes markup, comment threads, suggestion halos, debug panel,
|
|
6
|
+
* presence halos). The policy is the class-A input to the class-C local
|
|
7
|
+
* preference composition that lives in Layer 10 (`ui.overlays.getVisibility`);
|
|
8
|
+
* this module does NOT compose the two — that is strictly L10's job.
|
|
9
|
+
*
|
|
10
|
+
* Spec: `docs/architecture/06-workflow-review.md` §W10 and
|
|
11
|
+
* `docs/plans/refactor/state-classes-cross-cutting.md` §X1. The public
|
|
12
|
+
* types live in `src/api/public-types.ts` (shared vocabulary); the
|
|
13
|
+
* runtime-internal helpers live here.
|
|
14
|
+
*
|
|
15
|
+
* Invariants:
|
|
16
|
+
* - Policies are keyed by `OverlayKind`; at most one policy per kind.
|
|
17
|
+
* - `enforcement: "always"` / `"never"` overrides any local preference.
|
|
18
|
+
* - `enforcement: "authored-default"` cedes to the local preference; the
|
|
19
|
+
* `defaultOn` field is only consulted when the user has not expressed one.
|
|
20
|
+
* - Mutations flow through the coordinator so they persist via
|
|
21
|
+
* `customXml/item1.xml` (see `workflow-payload.ts` `bw:overlayVisibility`
|
|
22
|
+
* block) and broadcast via the Yjs CRDT (shared-workflow state carries them).
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import type {
|
|
26
|
+
OverlayKind,
|
|
27
|
+
OverlayVisibilityAuthorship,
|
|
28
|
+
OverlayVisibilityEnforcement,
|
|
29
|
+
OverlayVisibilityPolicy,
|
|
30
|
+
} from "../../api/public-types.ts";
|
|
31
|
+
|
|
32
|
+
export type {
|
|
33
|
+
OverlayKind,
|
|
34
|
+
OverlayVisibilityAuthorship,
|
|
35
|
+
OverlayVisibilityEnforcement,
|
|
36
|
+
OverlayVisibilityPolicy,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** The closed set — used by the CI guard + the serializer. */
|
|
40
|
+
export const OVERLAY_KINDS: readonly OverlayKind[] = [
|
|
41
|
+
"scope-rail",
|
|
42
|
+
"comments",
|
|
43
|
+
"tracked-changes",
|
|
44
|
+
"suggestions",
|
|
45
|
+
"debug-panel",
|
|
46
|
+
"presence",
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const OVERLAY_KIND_SET: ReadonlySet<OverlayKind> = new Set(OVERLAY_KINDS);
|
|
50
|
+
const ENFORCEMENT_SET: ReadonlySet<OverlayVisibilityEnforcement> = new Set([
|
|
51
|
+
"authored-default",
|
|
52
|
+
"always",
|
|
53
|
+
"never",
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
export function isOverlayKind(value: unknown): value is OverlayKind {
|
|
57
|
+
return typeof value === "string" && OVERLAY_KIND_SET.has(value as OverlayKind);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function isOverlayVisibilityEnforcement(
|
|
61
|
+
value: unknown,
|
|
62
|
+
): value is OverlayVisibilityEnforcement {
|
|
63
|
+
return (
|
|
64
|
+
typeof value === "string" &&
|
|
65
|
+
ENFORCEMENT_SET.has(value as OverlayVisibilityEnforcement)
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Type-guard — shallow validation. Authorship is optional and not checked. */
|
|
70
|
+
export function isOverlayVisibilityPolicy(
|
|
71
|
+
value: unknown,
|
|
72
|
+
): value is OverlayVisibilityPolicy {
|
|
73
|
+
if (value === null || typeof value !== "object") return false;
|
|
74
|
+
const candidate = value as { kind?: unknown; enforcement?: unknown; defaultOn?: unknown };
|
|
75
|
+
return (
|
|
76
|
+
isOverlayKind(candidate.kind) &&
|
|
77
|
+
isOverlayVisibilityEnforcement(candidate.enforcement) &&
|
|
78
|
+
typeof candidate.defaultOn === "boolean"
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Factory. Applies structural clone so callers cannot mutate the record
|
|
84
|
+
* after registration.
|
|
85
|
+
*/
|
|
86
|
+
export function createOverlayVisibilityPolicy(input: {
|
|
87
|
+
kind: OverlayKind;
|
|
88
|
+
enforcement: OverlayVisibilityEnforcement;
|
|
89
|
+
defaultOn: boolean;
|
|
90
|
+
authoredBy?: OverlayVisibilityAuthorship;
|
|
91
|
+
}): OverlayVisibilityPolicy {
|
|
92
|
+
return {
|
|
93
|
+
kind: input.kind,
|
|
94
|
+
enforcement: input.enforcement,
|
|
95
|
+
defaultOn: input.defaultOn,
|
|
96
|
+
...(input.authoredBy ? { authoredBy: { ...input.authoredBy } } : {}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Structural equality — used by the store to short-circuit no-op
|
|
102
|
+
* `setVisibilityPolicy` calls (no spurious subscriber wakeups).
|
|
103
|
+
*/
|
|
104
|
+
export function overlayVisibilityPoliciesEqual(
|
|
105
|
+
left: OverlayVisibilityPolicy | null | undefined,
|
|
106
|
+
right: OverlayVisibilityPolicy | null | undefined,
|
|
107
|
+
): boolean {
|
|
108
|
+
if (left === right) return true;
|
|
109
|
+
if (!left || !right) return false;
|
|
110
|
+
if (left.kind !== right.kind) return false;
|
|
111
|
+
if (left.enforcement !== right.enforcement) return false;
|
|
112
|
+
if (left.defaultOn !== right.defaultOn) return false;
|
|
113
|
+
const la = left.authoredBy;
|
|
114
|
+
const ra = right.authoredBy;
|
|
115
|
+
if (!la && !ra) return true;
|
|
116
|
+
if (!la || !ra) return false;
|
|
117
|
+
return la.actorId === ra.actorId && la.at === ra.at;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Deterministic ordering for serialization — by the fixed `OVERLAY_KINDS`
|
|
122
|
+
* order, so re-export of an unchanged policy set is byte-identical.
|
|
123
|
+
*/
|
|
124
|
+
export function sortOverlayVisibilityPolicies(
|
|
125
|
+
policies: readonly OverlayVisibilityPolicy[],
|
|
126
|
+
): readonly OverlayVisibilityPolicy[] {
|
|
127
|
+
const indexOf = (kind: OverlayKind): number => OVERLAY_KINDS.indexOf(kind);
|
|
128
|
+
return [...policies].sort((a, b) => indexOf(a.kind) - indexOf(b.kind));
|
|
129
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transitional sync entry — wraps `loadDocxEditorSession` from the
|
|
3
|
+
* legacy `src/io/docx-session.ts` for the two sync call sites inside
|
|
4
|
+
* `src/ui/editor-runtime-boundary.ts` that cannot (yet) await.
|
|
5
|
+
*
|
|
6
|
+
* Why this module exists.
|
|
7
|
+
* =======================
|
|
8
|
+
* `DocxSession.open()` is async per the architecture (the loader
|
|
9
|
+
* yields between parse stages so the browser can paint). Two
|
|
10
|
+
* `editor-runtime-boundary` paths need a sync load:
|
|
11
|
+
*
|
|
12
|
+
* 1. `createRuntime`'s SSR / Node fallback — runs when no
|
|
13
|
+
* `preloadedDocxSession` was produced by the async warm path but
|
|
14
|
+
* `initialDocx` bytes are present. `createRuntime` is sync, so a
|
|
15
|
+
* sync loader is the only entry that keeps the mount simple.
|
|
16
|
+
* Slice 5e-9 is expected to eliminate this fallback altogether
|
|
17
|
+
* by requiring the async warm path to run, OR by making
|
|
18
|
+
* `createRuntime` async-capable.
|
|
19
|
+
*
|
|
20
|
+
* 2. `resolvePackageBackedExportSession` — resolves an embedded
|
|
21
|
+
* source-package blob to decide the export barrier for
|
|
22
|
+
* snapshot-only mounts. The result drives the compatibility
|
|
23
|
+
* report that shows on first paint; deferring to first export
|
|
24
|
+
* would regress the blocked-export UX. Slice 5e-9 may thread a
|
|
25
|
+
* preloaded barrier session through the async warm path
|
|
26
|
+
* instead.
|
|
27
|
+
*
|
|
28
|
+
* Isolation.
|
|
29
|
+
* ==========
|
|
30
|
+
* By routing both sync call sites through this one module, the
|
|
31
|
+
* `src/ui/editor-runtime-boundary.ts` file no longer imports directly
|
|
32
|
+
* from `src/io/docx-session.ts`. The transit is pinned as a
|
|
33
|
+
* WARNING-level debt exception in
|
|
34
|
+
* `scripts/ci-check-session-layer-purity.mjs`, so the legacy-import
|
|
35
|
+
* surface shrinks to one narrow file that Slice 5e-9 deletes whole.
|
|
36
|
+
*
|
|
37
|
+
* Not exported from the session barrel.
|
|
38
|
+
* =====================================
|
|
39
|
+
* `src/session/index.ts` intentionally does NOT re-export this
|
|
40
|
+
* module; it is an in-tree implementation seam. External consumers
|
|
41
|
+
* never see it. Agents: do not widen its surface.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
// Slice 5e-8.a: reach the sync loader body directly (in-layer),
|
|
45
|
+
// retiring the `src/io/docx-session.ts` transit for this shim.
|
|
46
|
+
import { loadDocxSessionSync } from "./import/loader.ts";
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Sync docx load — returns the legacy-shaped handle the two
|
|
50
|
+
* `editor-runtime-boundary` sync call sites currently consume.
|
|
51
|
+
*
|
|
52
|
+
* Options + result are typed via `Parameters<>` / `ReturnType<>` so
|
|
53
|
+
* this module does not force its loader-types dependency to widen
|
|
54
|
+
* publicly.
|
|
55
|
+
*
|
|
56
|
+
* Removed in Slice 5e-9 when the sync call sites either flip to
|
|
57
|
+
* `await DocxSession.open(...)` or rely on a preloaded session
|
|
58
|
+
* produced by the async warm path.
|
|
59
|
+
*/
|
|
60
|
+
export type SyncLegacyOpenOptions = Parameters<typeof loadDocxSessionSync>[0];
|
|
61
|
+
|
|
62
|
+
export function openDocxSync(
|
|
63
|
+
opts: SyncLegacyOpenOptions,
|
|
64
|
+
): ReturnType<typeof loadDocxSessionSync> {
|
|
65
|
+
return loadDocxSessionSync(opts);
|
|
66
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P8 Step 7 — export-time reconstitution of offloaded embedded
|
|
3
|
+
* documents back into the OPC package.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors `src/session/import/embedded-offload.ts`. For every
|
|
6
|
+
* `EmbeddingOffloadEntry` the session captured at open time:
|
|
7
|
+
* 1. Call `hostAdapter.loadEmbeddedDocument(entry.storageReference)`
|
|
8
|
+
* → on `Uint8Array` success, use those bytes;
|
|
9
|
+
* → on `null` / rejection / thrown exception, decode the entry's
|
|
10
|
+
* `inlineBytes` base64 fallback and use that.
|
|
11
|
+
* 2. Write the bytes back to the OPC package at `entry.sourcePartPath`
|
|
12
|
+
* via `exportSession.replaceOwnedPart`.
|
|
13
|
+
*
|
|
14
|
+
* The source part path was added to the export session's owned-paths
|
|
15
|
+
* list at construction, so `replaceOwnedPart` is valid for it. The
|
|
16
|
+
* preservation path does NOT carry these parts — `buildImportPreservation`
|
|
17
|
+
* excluded them based on the `ownedPartPaths` the offload module
|
|
18
|
+
* returned at load time.
|
|
19
|
+
*
|
|
20
|
+
* Export never fails on storage unavailability: every entry carries
|
|
21
|
+
* the inline-bytes fallback inline (the whole point of P3). A
|
|
22
|
+
* ubiquitous host-storage outage simply results in the exported
|
|
23
|
+
* package looking identical to the source — bytes just round-trip
|
|
24
|
+
* via customXml instead of via preservation.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import type {
|
|
28
|
+
EditorHostAdapter,
|
|
29
|
+
StorageReference,
|
|
30
|
+
} from "../../api/public-types.ts";
|
|
31
|
+
import { sha256Hex } from "../../io/source-package-provenance.ts";
|
|
32
|
+
import {
|
|
33
|
+
decodeInlineBytes,
|
|
34
|
+
type EmbeddingOffloadEntry,
|
|
35
|
+
} from "../import/embedded-offload.ts";
|
|
36
|
+
import type { createExportSession } from "../../io/export/export-session.ts";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Call-shape of the `exportSession` argument. Typed via `ReturnType`
|
|
40
|
+
* so this module does not drag internal export-session implementation
|
|
41
|
+
* into the public surface.
|
|
42
|
+
*/
|
|
43
|
+
type ExportSession = ReturnType<typeof createExportSession>;
|
|
44
|
+
|
|
45
|
+
export interface ReconstituteEmbeddedParams {
|
|
46
|
+
readonly exportSession: ExportSession;
|
|
47
|
+
readonly entries: readonly EmbeddingOffloadEntry[];
|
|
48
|
+
readonly hostAdapter: EditorHostAdapter | undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Rebuild the OPC parts for every offload entry. No-op when `entries`
|
|
53
|
+
* is empty (no offload was performed on this session, OR the package
|
|
54
|
+
* had no offloadable embeddings).
|
|
55
|
+
*/
|
|
56
|
+
export async function reconstituteEmbeddedDocuments(
|
|
57
|
+
params: ReconstituteEmbeddedParams,
|
|
58
|
+
): Promise<void> {
|
|
59
|
+
const { exportSession, entries, hostAdapter } = params;
|
|
60
|
+
if (entries.length === 0) return;
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
const bytes = await resolveEntryBytes(hostAdapter, entry);
|
|
63
|
+
exportSession.replaceOwnedPart({
|
|
64
|
+
path: entry.sourcePartPath,
|
|
65
|
+
bytes,
|
|
66
|
+
contentType: entry.contentType,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function resolveEntryBytes(
|
|
72
|
+
hostAdapter: EditorHostAdapter | undefined,
|
|
73
|
+
entry: EmbeddingOffloadEntry,
|
|
74
|
+
): Promise<Uint8Array> {
|
|
75
|
+
if (hostAdapter?.loadEmbeddedDocument) {
|
|
76
|
+
try {
|
|
77
|
+
const fetched = await hostAdapter.loadEmbeddedDocument(entry.storageReference);
|
|
78
|
+
if (fetched && fetched.byteLength > 0) {
|
|
79
|
+
// Tamper check: the `StorageReference.sha256` contract in
|
|
80
|
+
// `public-types.ts` promises the session computes the sha
|
|
81
|
+
// so hosts can detect tampering on rehydrate. Verify the
|
|
82
|
+
// loaded payload matches the sha we captured at offload
|
|
83
|
+
// time. On mismatch, treat it identically to a null /
|
|
84
|
+
// failed load and fall through to the inline-bytes
|
|
85
|
+
// fallback — export still ships honest bytes.
|
|
86
|
+
if (sha256Hex(fetched) === entry.sha256) {
|
|
87
|
+
return fetched;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
// Treat identically to `null` — fall through to the inline-
|
|
92
|
+
// bytes fallback. The architecture guarantees export never
|
|
93
|
+
// fails on storage unavailability.
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return decodeInlineBytes(entry.inlineBytes);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Re-export for convenience so consumers of the reconstitute module
|
|
101
|
+
* that need to chain additional logic can reach the `StorageReference`
|
|
102
|
+
* shape without a separate import.
|
|
103
|
+
*/
|
|
104
|
+
export type { StorageReference };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed export diagnostic producers.
|
|
3
|
+
*
|
|
4
|
+
* Slice 4a of refactor/01 package-session. The legacy export path in
|
|
5
|
+
* `src/io/docx-session.ts` currently throws plain `Error` objects when
|
|
6
|
+
* the canonical document cannot be serialized — callers get a string
|
|
7
|
+
* message but no stable code to react against.
|
|
8
|
+
*
|
|
9
|
+
* This module defines a single `ExportBlockedError` class carrying a
|
|
10
|
+
* stable `code` + `featureKey` + `details`, so:
|
|
11
|
+
* - the runtime can surface targeted UX (block dialog, rail badge)
|
|
12
|
+
* - CI / truth-baseline can assert on the code, not the message
|
|
13
|
+
* - export-validation.ts's `assert*` predicates throw these directly
|
|
14
|
+
*
|
|
15
|
+
* Contract P6 clean. Depends on no runtime / ui / core / review code.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Stable classification codes used across the export path.
|
|
20
|
+
* Consumers snapshot on `code` rather than `message`; the message
|
|
21
|
+
* format may change for i18n without breaking callers.
|
|
22
|
+
*/
|
|
23
|
+
export type ExportBlockedCode =
|
|
24
|
+
/** Export is blocked by the active compatibility report. */
|
|
25
|
+
| "compatibility-blocked"
|
|
26
|
+
/** Preserve-only comment anchors cannot be remapped safely. */
|
|
27
|
+
| "preserve-only-comments"
|
|
28
|
+
/** Active revisions overlap unsupported serialization boundaries. */
|
|
29
|
+
| "revision-boundary"
|
|
30
|
+
/** One or more comments no longer map to a serializable range. */
|
|
31
|
+
| "comment-range-lost";
|
|
32
|
+
|
|
33
|
+
export interface ExportBlockedDetails {
|
|
34
|
+
/** Count of items that contributed to the block (when applicable). */
|
|
35
|
+
readonly count?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Arbitrary identifiers (revision ids, comment ids, feature keys)
|
|
38
|
+
* the runtime can cite in its block-dialog UX. Empty when the
|
|
39
|
+
* block reason is a single global flag (compatibility-blocked).
|
|
40
|
+
*/
|
|
41
|
+
readonly ids?: readonly string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Thrown from the export path to signal a structural block. The
|
|
46
|
+
* runtime catches this, reads `code`, and drives UX / telemetry
|
|
47
|
+
* accordingly. Message is human-facing; the machine-readable
|
|
48
|
+
* projection is the `code` + `details` pair.
|
|
49
|
+
*/
|
|
50
|
+
export class ExportBlockedError extends Error {
|
|
51
|
+
readonly code: ExportBlockedCode;
|
|
52
|
+
readonly details: ExportBlockedDetails;
|
|
53
|
+
|
|
54
|
+
constructor(
|
|
55
|
+
code: ExportBlockedCode,
|
|
56
|
+
message: string,
|
|
57
|
+
details: ExportBlockedDetails = {},
|
|
58
|
+
) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.name = "ExportBlockedError";
|
|
61
|
+
this.code = code;
|
|
62
|
+
this.details = details;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Produce a human-facing message with a count prefix. Kept in a
|
|
68
|
+
* shared helper so both the assertion functions in export-validation
|
|
69
|
+
* and downstream diagnostic-panel UX format the string identically.
|
|
70
|
+
*/
|
|
71
|
+
export function formatBlockedExportMessage(
|
|
72
|
+
code: ExportBlockedCode,
|
|
73
|
+
count?: number,
|
|
74
|
+
): string {
|
|
75
|
+
switch (code) {
|
|
76
|
+
case "compatibility-blocked":
|
|
77
|
+
return "DOCX export is blocked by the current compatibility report.";
|
|
78
|
+
case "preserve-only-comments":
|
|
79
|
+
return `DOCX export is blocked because ${count ?? 0} preserve-only comment anchors cannot be safely remapped after runtime edits.`;
|
|
80
|
+
case "revision-boundary":
|
|
81
|
+
return `DOCX export is blocked because ${count ?? 0} active revisions overlap unsupported serialization boundaries.`;
|
|
82
|
+
case "comment-range-lost":
|
|
83
|
+
return `DOCX export is blocked because ${count ?? 0} comments no longer map to serializable ranges.`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export-validation helpers for the session-layer export path.
|
|
3
|
+
*
|
|
4
|
+
* Slice 4a of refactor/01 package-session. Extracted from the four
|
|
5
|
+
* inline `throw new Error(...)` points in
|
|
6
|
+
* `exportDocxEditorSession` (`src/io/docx-session.ts:2021`, `:2066`,
|
|
7
|
+
* `:2109`, `:2148`). Each inline throw becomes a single `assert*`
|
|
8
|
+
* call that routes through the typed `ExportBlockedError` from
|
|
9
|
+
* `export-diagnostics.ts`.
|
|
10
|
+
*
|
|
11
|
+
* Pure functions over primitives + typed value objects; no IO, no
|
|
12
|
+
* runtime imports, no review-store reads. Contract P6 clean.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { CompatibilityReport } from "../../api/public-types.ts";
|
|
16
|
+
import {
|
|
17
|
+
ExportBlockedError,
|
|
18
|
+
formatBlockedExportMessage,
|
|
19
|
+
} from "./export-diagnostics.ts";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Block export when the compatibility report flags the document as
|
|
23
|
+
* unsafe to re-serialize. This is the earliest structural gate — if
|
|
24
|
+
* compatibility blocks, nothing else matters.
|
|
25
|
+
*/
|
|
26
|
+
export function assertExportNotBlockedByCompatibility(
|
|
27
|
+
compatibility: Pick<CompatibilityReport, "blockExport">,
|
|
28
|
+
): void {
|
|
29
|
+
if (compatibility.blockExport) {
|
|
30
|
+
throw new ExportBlockedError(
|
|
31
|
+
"compatibility-blocked",
|
|
32
|
+
formatBlockedExportMessage("compatibility-blocked"),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Block export when the loader captured preserve-only comment anchors
|
|
39
|
+
* that no longer map to serializable ranges after runtime edits.
|
|
40
|
+
*
|
|
41
|
+
* `count` is the greater of:
|
|
42
|
+
* - `state.blockingCommentDiagnostics.length`
|
|
43
|
+
* - `state.preservedCommentDefinitions.length` (deduped)
|
|
44
|
+
*
|
|
45
|
+
* Kept as a scalar count input so callers can decide the combination
|
|
46
|
+
* policy before calling in. Callers that need to cite specific
|
|
47
|
+
* comment ids pass them via `ids` for inclusion in the error's
|
|
48
|
+
* machine-readable payload.
|
|
49
|
+
*/
|
|
50
|
+
export function assertNoBlockingPreservedComments(
|
|
51
|
+
count: number,
|
|
52
|
+
ids?: readonly string[],
|
|
53
|
+
): void {
|
|
54
|
+
if (count > 0) {
|
|
55
|
+
throw new ExportBlockedError(
|
|
56
|
+
"preserve-only-comments",
|
|
57
|
+
formatBlockedExportMessage("preserve-only-comments", count),
|
|
58
|
+
{
|
|
59
|
+
count,
|
|
60
|
+
...(ids !== undefined ? { ids } : {}),
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Block export when runtime revisions overlap unsupported
|
|
68
|
+
* serialization boundaries. `skippedRevisionIds` comes from
|
|
69
|
+
* `serializeRuntimeRevisionsIntoDocumentXml` — the serializer
|
|
70
|
+
* returns the list of ids it could not safely rewrite, and any
|
|
71
|
+
* non-empty list blocks export.
|
|
72
|
+
*/
|
|
73
|
+
export function assertNoSkippedRevisions(
|
|
74
|
+
skippedRevisionIds: readonly string[],
|
|
75
|
+
): void {
|
|
76
|
+
if (skippedRevisionIds.length > 0) {
|
|
77
|
+
throw new ExportBlockedError(
|
|
78
|
+
"revision-boundary",
|
|
79
|
+
formatBlockedExportMessage("revision-boundary", skippedRevisionIds.length),
|
|
80
|
+
{
|
|
81
|
+
count: skippedRevisionIds.length,
|
|
82
|
+
ids: skippedRevisionIds,
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Block export when comment-anchor serialization dropped comments
|
|
90
|
+
* whose anchor is not merely "detached" (detached comments are
|
|
91
|
+
* acceptable; others signal a serializer bug / unexpected runtime
|
|
92
|
+
* state).
|
|
93
|
+
*/
|
|
94
|
+
export function assertNoBlockingSkippedComments(
|
|
95
|
+
blockingSkippedCommentIds: readonly string[],
|
|
96
|
+
): void {
|
|
97
|
+
if (blockingSkippedCommentIds.length > 0) {
|
|
98
|
+
throw new ExportBlockedError(
|
|
99
|
+
"comment-range-lost",
|
|
100
|
+
formatBlockedExportMessage(
|
|
101
|
+
"comment-range-lost",
|
|
102
|
+
blockingSkippedCommentIds.length,
|
|
103
|
+
),
|
|
104
|
+
{
|
|
105
|
+
count: blockingSkippedCommentIds.length,
|
|
106
|
+
ids: blockingSkippedCommentIds,
|
|
107
|
+
},
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|