@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,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical-assembly helpers — the layer that turns parsed OOXML
|
|
3
|
+
* primitives into a `CanonicalDocument` envelope + `PersistedEditorSnapshot`
|
|
4
|
+
* + `EditorSessionState` triple.
|
|
5
|
+
*
|
|
6
|
+
* Slice 5e-6c of refactor/01 package-session. Extracted from
|
|
7
|
+
* `src/io/docx-session.ts`:
|
|
8
|
+
*
|
|
9
|
+
* - `toEditorSessionState` — lift a `PersistedEditorSnapshot` to an
|
|
10
|
+
* `EditorSessionState` via the api/session-state helper, or pass a
|
|
11
|
+
* live `EditorSessionState` through unchanged.
|
|
12
|
+
* - `createImportedCanonicalDocument` — compose the canonical document
|
|
13
|
+
* from parsed content + styles + numbering + fieldRegistry + media
|
|
14
|
+
* + preservation + diagnostics + review.
|
|
15
|
+
* - `createImportedSnapshot` — wrap a `CanonicalDocument` + session-
|
|
16
|
+
* level state (compatibility / protection / sourcePackage /
|
|
17
|
+
* workflow overlay + metadata) into a `PersistedEditorSnapshot`.
|
|
18
|
+
* - `ensureImportedNumberingCatalogSupportsContent` — back-fill the
|
|
19
|
+
* synthetic `num:0` numbering instance when the package omitted it
|
|
20
|
+
* but content references it (DOCX-null-numbering sentinel).
|
|
21
|
+
* - `collectReferencedNumberingInstanceIds` — walk the canonical
|
|
22
|
+
* content tree and collect every referenced `numberingInstanceId`.
|
|
23
|
+
*
|
|
24
|
+
* Contract P6 clean: imports only from `src/model/**`, `src/io/**`,
|
|
25
|
+
* `src/api/**` (public types + session-state factory). No runtime,
|
|
26
|
+
* UI, core, or review imports.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import type {
|
|
30
|
+
BlockNode,
|
|
31
|
+
CanonicalDocument,
|
|
32
|
+
NumberingCatalog,
|
|
33
|
+
SubPartsCatalog,
|
|
34
|
+
} from "../../model/canonical-document.ts";
|
|
35
|
+
import { createCanonicalDocumentId } from "../../model/canonical-document.ts";
|
|
36
|
+
import type {
|
|
37
|
+
EditorSessionState,
|
|
38
|
+
PersistedEditorSnapshot,
|
|
39
|
+
ProtectionSnapshot,
|
|
40
|
+
} from "../../api/public-types.ts";
|
|
41
|
+
import { editorSessionStateFromPersistedSnapshot } from "../../api/session-state.ts";
|
|
42
|
+
import { buildFieldRegistry } from "../../io/ooxml/parse-fields.ts";
|
|
43
|
+
import type { ParseStylesResult } from "../../io/ooxml/parse-styles.ts";
|
|
44
|
+
import {
|
|
45
|
+
createSyntheticDocxNullNumberingCatalog,
|
|
46
|
+
DOCX_NULL_NUMBERING_INSTANCE_ID,
|
|
47
|
+
} from "../../io/ooxml/numbering-sentinels.ts";
|
|
48
|
+
import { buildStylesCatalog } from "./styles-consolidation.ts";
|
|
49
|
+
|
|
50
|
+
/** Lift a `PersistedEditorSnapshot` to an `EditorSessionState`, or pass
|
|
51
|
+
* a live `EditorSessionState` through unchanged. Used by export paths
|
|
52
|
+
* that accept either shape from their caller. */
|
|
53
|
+
export function toEditorSessionState(
|
|
54
|
+
sessionStateOrSnapshot: EditorSessionState | PersistedEditorSnapshot,
|
|
55
|
+
): EditorSessionState {
|
|
56
|
+
return "sessionVersion" in sessionStateOrSnapshot
|
|
57
|
+
? sessionStateOrSnapshot
|
|
58
|
+
: editorSessionStateFromPersistedSnapshot(sessionStateOrSnapshot);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Walk the canonical content tree and collect every referenced
|
|
62
|
+
* `numberingInstanceId`. Used to determine whether the DOCX-null
|
|
63
|
+
* numbering sentinel (`num:0`) must be back-filled. */
|
|
64
|
+
export function collectReferencedNumberingInstanceIds(
|
|
65
|
+
content: CanonicalDocument["content"],
|
|
66
|
+
): Set<string> {
|
|
67
|
+
const numberingInstanceIds = new Set<string>();
|
|
68
|
+
|
|
69
|
+
const visitBlocks = (blocks: ReadonlyArray<BlockNode>) => {
|
|
70
|
+
for (const block of blocks) {
|
|
71
|
+
if (block.type === "paragraph" && block.numbering?.numberingInstanceId) {
|
|
72
|
+
numberingInstanceIds.add(block.numbering.numberingInstanceId);
|
|
73
|
+
}
|
|
74
|
+
if (block.type === "table") {
|
|
75
|
+
for (const row of block.rows) {
|
|
76
|
+
for (const cell of row.cells) {
|
|
77
|
+
visitBlocks(cell.children);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} else if (block.type === "sdt" || block.type === "custom_xml") {
|
|
81
|
+
visitBlocks(block.children);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
visitBlocks(content.children);
|
|
87
|
+
return numberingInstanceIds;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Back-fill the synthetic DOCX-null numbering instance when the
|
|
91
|
+
* package's `numbering.xml` omitted it but the content references
|
|
92
|
+
* `num:0`. No-op when either (a) the catalog already defines it, or
|
|
93
|
+
* (b) the content does not reference it. */
|
|
94
|
+
export function ensureImportedNumberingCatalogSupportsContent(
|
|
95
|
+
catalog: NumberingCatalog,
|
|
96
|
+
content: CanonicalDocument["content"],
|
|
97
|
+
): NumberingCatalog {
|
|
98
|
+
if (
|
|
99
|
+
catalog.instances[DOCX_NULL_NUMBERING_INSTANCE_ID] ||
|
|
100
|
+
!collectReferencedNumberingInstanceIds(content).has(DOCX_NULL_NUMBERING_INSTANCE_ID)
|
|
101
|
+
) {
|
|
102
|
+
return catalog;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const syntheticNullCatalog = createSyntheticDocxNullNumberingCatalog();
|
|
106
|
+
return {
|
|
107
|
+
abstractDefinitions: {
|
|
108
|
+
...catalog.abstractDefinitions,
|
|
109
|
+
...syntheticNullCatalog.abstractDefinitions,
|
|
110
|
+
},
|
|
111
|
+
instances: {
|
|
112
|
+
...catalog.instances,
|
|
113
|
+
...syntheticNullCatalog.instances,
|
|
114
|
+
},
|
|
115
|
+
...(catalog.numPicBullets !== undefined ? { numPicBullets: catalog.numPicBullets } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Compose the canonical document envelope from the parsed OOXML
|
|
120
|
+
* primitives. Produces a `docId` via the deterministic hash of
|
|
121
|
+
* `documentId`, builds the styles catalog + fieldRegistry, and
|
|
122
|
+
* back-fills the DOCX-null numbering sentinel if referenced.
|
|
123
|
+
*
|
|
124
|
+
* Pure function over the inputs — no I/O, no wall-clock reads. The
|
|
125
|
+
* caller supplies the `timestamp` so import-time provenance is
|
|
126
|
+
* captured once at the outer orchestrator. */
|
|
127
|
+
export function createImportedCanonicalDocument(input: {
|
|
128
|
+
documentId: string;
|
|
129
|
+
timestamp: string;
|
|
130
|
+
numbering: CanonicalDocument["numbering"];
|
|
131
|
+
media: CanonicalDocument["media"];
|
|
132
|
+
content: CanonicalDocument["content"];
|
|
133
|
+
subParts?: SubPartsCatalog;
|
|
134
|
+
parsedStyles?: ParseStylesResult;
|
|
135
|
+
fontTable?: CanonicalDocument["fontTable"];
|
|
136
|
+
preservation: CanonicalDocument["preservation"];
|
|
137
|
+
diagnostics: CanonicalDocument["diagnostics"];
|
|
138
|
+
review: CanonicalDocument["review"];
|
|
139
|
+
}): CanonicalDocument {
|
|
140
|
+
const numbering = ensureImportedNumberingCatalogSupportsContent(
|
|
141
|
+
input.numbering,
|
|
142
|
+
input.content,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const styles = buildStylesCatalog(input.parsedStyles, input.content, input.subParts);
|
|
146
|
+
|
|
147
|
+
// Build the canonical field registry from the assembled {content,
|
|
148
|
+
// styles, subParts}. The registry is deterministic given those three
|
|
149
|
+
// inputs, so it belongs on the canonical envelope (architecture §02
|
|
150
|
+
// D1). Earlier call sites relied on the runtime to recompute the
|
|
151
|
+
// registry on the first content-changing command; that left
|
|
152
|
+
// `canonicalDocument.fieldRegistry` undefined immediately after
|
|
153
|
+
// import, forcing every read-model that consumes fields (document
|
|
154
|
+
// outline, TOC snapshot, collab fingerprint) to maintain a fallback
|
|
155
|
+
// walk. Computing here closes the import-truth drop.
|
|
156
|
+
const fieldRegistry = buildFieldRegistry({
|
|
157
|
+
content: input.content,
|
|
158
|
+
styles,
|
|
159
|
+
...(input.subParts !== undefined ? { subParts: input.subParts } : {}),
|
|
160
|
+
});
|
|
161
|
+
const hasFields =
|
|
162
|
+
fieldRegistry.supported.length > 0 || fieldRegistry.preserveOnly.length > 0;
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
schemaVersion: "cds/1.0.0",
|
|
166
|
+
docId: createCanonicalDocumentId(input.documentId),
|
|
167
|
+
createdAt: input.timestamp,
|
|
168
|
+
updatedAt: input.timestamp,
|
|
169
|
+
metadata: {
|
|
170
|
+
customProperties: {},
|
|
171
|
+
},
|
|
172
|
+
styles,
|
|
173
|
+
numbering,
|
|
174
|
+
media: input.media,
|
|
175
|
+
content: input.content,
|
|
176
|
+
review: input.review,
|
|
177
|
+
preservation: input.preservation,
|
|
178
|
+
diagnostics: input.diagnostics,
|
|
179
|
+
...(input.subParts !== undefined ? { subParts: input.subParts } : {}),
|
|
180
|
+
...(input.fontTable !== undefined ? { fontTable: input.fontTable } : {}),
|
|
181
|
+
...(hasFields ? { fieldRegistry } : {}),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Wrap a `CanonicalDocument` + session-level state into a
|
|
186
|
+
* `PersistedEditorSnapshot`. Session-level = compatibility report,
|
|
187
|
+
* protection snapshot, source-package provenance, workflow overlay,
|
|
188
|
+
* workflow metadata. */
|
|
189
|
+
export function createImportedSnapshot(input: {
|
|
190
|
+
documentId: string;
|
|
191
|
+
editorBuild: string;
|
|
192
|
+
timestamp: string;
|
|
193
|
+
document: CanonicalDocument;
|
|
194
|
+
compatibility: PersistedEditorSnapshot["compatibility"];
|
|
195
|
+
protectionSnapshot: ProtectionSnapshot;
|
|
196
|
+
sourcePackage?: PersistedEditorSnapshot["sourcePackage"];
|
|
197
|
+
workflowOverlay?: PersistedEditorSnapshot["workflowOverlay"];
|
|
198
|
+
workflowMetadata?: PersistedEditorSnapshot["workflowMetadata"];
|
|
199
|
+
/** W10 — class-A overlay-visibility policies parsed from customXml. */
|
|
200
|
+
visibilityPolicies?: PersistedEditorSnapshot["visibilityPolicies"];
|
|
201
|
+
/** X5 — class-A markup-mode policy parsed from customXml. */
|
|
202
|
+
markupModePolicy?: PersistedEditorSnapshot["markupModePolicy"];
|
|
203
|
+
}): PersistedEditorSnapshot {
|
|
204
|
+
return {
|
|
205
|
+
snapshotVersion: "persisted-editor-snapshot/2",
|
|
206
|
+
schemaVersion: input.document.schemaVersion,
|
|
207
|
+
documentId: input.documentId,
|
|
208
|
+
docId: input.document.docId,
|
|
209
|
+
createdAt: input.document.createdAt,
|
|
210
|
+
updatedAt: input.document.updatedAt,
|
|
211
|
+
savedAt: input.timestamp,
|
|
212
|
+
editorBuild: input.editorBuild,
|
|
213
|
+
canonicalDocument: input.document,
|
|
214
|
+
compatibility: input.compatibility,
|
|
215
|
+
warningLog: input.compatibility.warnings,
|
|
216
|
+
protectionSnapshot: input.protectionSnapshot,
|
|
217
|
+
sourcePackage: input.sourcePackage,
|
|
218
|
+
workflowOverlay: input.workflowOverlay,
|
|
219
|
+
workflowMetadata: input.workflowMetadata,
|
|
220
|
+
...(input.visibilityPolicies && input.visibilityPolicies.length > 0
|
|
221
|
+
? { visibilityPolicies: input.visibilityPolicies }
|
|
222
|
+
: {}),
|
|
223
|
+
...(input.markupModePolicy
|
|
224
|
+
? { markupModePolicy: input.markupModePolicy }
|
|
225
|
+
: {}),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createDiagnosticsSession` — builds a fatal-error read-only session
|
|
3
|
+
* state for the load path's error branches. Invoked from 16 sites
|
|
4
|
+
* inside the sync + async loaders when parse / OPC / validation
|
|
5
|
+
* diagnostics block normal open.
|
|
6
|
+
*
|
|
7
|
+
* Slice 5e-7.d.2 — relocated from `src/io/docx-session.ts`. Pure
|
|
8
|
+
* glue between `createReadOnlyDiagnosticsRuntime` (already at
|
|
9
|
+
* `src/session/import/read-only-diagnostics.ts` from 5e-7.c) and the
|
|
10
|
+
* `LoadedDocxEditorSession` + `LegacyOpenView.exportDocx` shape the
|
|
11
|
+
* legacy load path expects. No new runtime / core / review imports.
|
|
12
|
+
*
|
|
13
|
+
* P6-clean.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { editorSessionStateFromPersistedSnapshot } from "../../api/session-state.ts";
|
|
17
|
+
import type { ImportDiagnosticsResult } from "../../validation/import-diagnostics.ts";
|
|
18
|
+
import { createReadOnlyDiagnosticsRuntime } from "./read-only-diagnostics.ts";
|
|
19
|
+
import type {
|
|
20
|
+
LoadDocxEditorSessionOptions,
|
|
21
|
+
LoadedDocxEditorSession,
|
|
22
|
+
} from "./loader-types.ts";
|
|
23
|
+
import { EMPTY_PROTECTION_SNAPSHOT } from "../shared/protection.ts";
|
|
24
|
+
|
|
25
|
+
export function createDiagnosticsSession(
|
|
26
|
+
options: LoadDocxEditorSessionOptions,
|
|
27
|
+
diagnostics: ImportDiagnosticsResult,
|
|
28
|
+
): LoadedDocxEditorSession {
|
|
29
|
+
const timestamp = new Date().toISOString();
|
|
30
|
+
const editorBuild =
|
|
31
|
+
typeof options.editorBuild === "string" && options.editorBuild.length > 0
|
|
32
|
+
? options.editorBuild
|
|
33
|
+
: "dev";
|
|
34
|
+
const runtime = createReadOnlyDiagnosticsRuntime({
|
|
35
|
+
documentId: options.documentId,
|
|
36
|
+
sourceLabel: options.sourceLabel,
|
|
37
|
+
editorBuild,
|
|
38
|
+
generatedAt: timestamp,
|
|
39
|
+
diagnostics,
|
|
40
|
+
});
|
|
41
|
+
const initialSnapshot = runtime.getPersistedSnapshot();
|
|
42
|
+
const initialSessionState =
|
|
43
|
+
editorSessionStateFromPersistedSnapshot(initialSnapshot);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
initialSessionState,
|
|
47
|
+
initialSnapshot,
|
|
48
|
+
fatalError: diagnostics.fatalError,
|
|
49
|
+
readOnly: true,
|
|
50
|
+
protectionSnapshot: EMPTY_PROTECTION_SNAPSHOT,
|
|
51
|
+
exportDocx: async (_sessionState, exportOptions) =>
|
|
52
|
+
runtime.exportDocx(exportOptions),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedded-document discovery — scaffold for P8 Step 6.
|
|
3
|
+
*
|
|
4
|
+
* Enumerates every embedded part referenced from the main document's
|
|
5
|
+
* relationship list, covering two relationship families per
|
|
6
|
+
* `docs/architecture/01-package-session.md` §P8:
|
|
7
|
+
*
|
|
8
|
+
* - OLE / package embeddings (`relationships/oleObject`,
|
|
9
|
+
* `relationships/package`) — binary OLE streams and native OOXML
|
|
10
|
+
* sub-packages (Word sub-doc, Excel workbook, PowerPoint deck).
|
|
11
|
+
* - `w:altChunk` targets (`relationships/aFChunk`) — alternate-format
|
|
12
|
+
* input chunks: HTML fragments, RTF, plain text, XHTML, or nested
|
|
13
|
+
* OOXML that Word should render in-place.
|
|
14
|
+
*
|
|
15
|
+
* Each discovered entry is classified by `classifyEmbedding()` into
|
|
16
|
+
* one of three kinds (`digestible` / `offloadable` / `store-only`)
|
|
17
|
+
* and packaged into the reserved `EmbeddedDocumentManifest` shape
|
|
18
|
+
* from `src/session/types.ts`.
|
|
19
|
+
*
|
|
20
|
+
* Status: ADDITIVE SCAFFOLD. Nothing in the load path calls this yet.
|
|
21
|
+
* Step 6 promotes the output onto `OpenResult.embeddedDocuments`. The
|
|
22
|
+
* function is exercised by targeted tests to pin the discovery
|
|
23
|
+
* contract before it goes live on the critical path.
|
|
24
|
+
*
|
|
25
|
+
* P6 clean. Depends only on:
|
|
26
|
+
* - src/io/opc/package-reader.ts (allowed)
|
|
27
|
+
* - src/io/ooxml/classify-embedding.ts (allowed)
|
|
28
|
+
* - src/io/ooxml/parse-ole-relationship.ts (allowed)
|
|
29
|
+
* - src/io/ooxml/part-manifest.ts (allowed)
|
|
30
|
+
* - src/session/types.ts (peer session module; type-only)
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import type { OpcPackage } from "../../io/opc/package-reader.ts";
|
|
34
|
+
import {
|
|
35
|
+
classifyEmbedding,
|
|
36
|
+
type EmbeddingKind,
|
|
37
|
+
} from "../../io/ooxml/classify-embedding.ts";
|
|
38
|
+
import { OLE_RELATIONSHIP_TYPES } from "../../io/ooxml/parse-ole-relationship.ts";
|
|
39
|
+
import { resolveRelationshipTarget } from "../../io/ooxml/part-manifest.ts";
|
|
40
|
+
import type { EmbeddedDocumentManifest } from "../types.ts";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Relationship type URIs for `w:altChunk` targets. ECMA-376 §17.17
|
|
44
|
+
* reserves `aFChunk` as the canonical URI; some tooling emits legacy
|
|
45
|
+
* URIs with slightly different casing — kept in the set to stay
|
|
46
|
+
* tolerant of producer drift.
|
|
47
|
+
*/
|
|
48
|
+
export const ALT_CHUNK_RELATIONSHIP_TYPES: ReadonlySet<string> = new Set([
|
|
49
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk",
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Per-discovery provenance — which relationship family the entry came
|
|
54
|
+
* from. Consumers (Step 7 offload, export-side reconstitute, truth
|
|
55
|
+
* comparator) need to distinguish OLE from altChunk even though both
|
|
56
|
+
* flow through the same `EmbeddedDocumentManifest`.
|
|
57
|
+
*/
|
|
58
|
+
export type EmbeddedDocumentSource = "ole-embedding" | "alt-chunk";
|
|
59
|
+
|
|
60
|
+
export interface InternalEmbeddedDocumentEntry {
|
|
61
|
+
/** Provenance of the discovery. */
|
|
62
|
+
readonly source: EmbeddedDocumentSource;
|
|
63
|
+
/** Absolute package part path (e.g. `/word/embeddings/chart.xlsx`). */
|
|
64
|
+
readonly partPath: string;
|
|
65
|
+
/** Content-Type declared in `[Content_Types].xml`, when known. */
|
|
66
|
+
readonly contentType: string | null;
|
|
67
|
+
/** Three-kind classification. */
|
|
68
|
+
readonly kind: EmbeddingKind;
|
|
69
|
+
/** Relationship id pointing at the part from the host story. */
|
|
70
|
+
readonly relationshipId: string;
|
|
71
|
+
/** Full relationship type URI. */
|
|
72
|
+
readonly relationshipType: string;
|
|
73
|
+
/** Path-tail filename (e.g. `chart.xlsx`). */
|
|
74
|
+
readonly filename?: string;
|
|
75
|
+
/** Byte length of the underlying package part. */
|
|
76
|
+
readonly size: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface EmbeddedDiscoveryResult {
|
|
80
|
+
/**
|
|
81
|
+
* Internal discovery records keyed by part path. Useful for test
|
|
82
|
+
* assertions and for consumers that need the relationship-family
|
|
83
|
+
* provenance. Step 6 does not expose this directly.
|
|
84
|
+
*/
|
|
85
|
+
readonly entries: ReadonlyMap<string, InternalEmbeddedDocumentEntry>;
|
|
86
|
+
/**
|
|
87
|
+
* Public manifest list in the architecture-reserved shape. This is
|
|
88
|
+
* what Step 6 surfaces on `OpenResult.embeddedDocuments`.
|
|
89
|
+
*/
|
|
90
|
+
readonly manifests: readonly EmbeddedDocumentManifest[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Walk the main document's relationships, identify every OLE/package
|
|
95
|
+
* and altChunk target, and return both the internal discovery records
|
|
96
|
+
* and the public manifest list.
|
|
97
|
+
*
|
|
98
|
+
* Discovery strategy:
|
|
99
|
+
* 1. Locate the main document part; bail with empty result if absent.
|
|
100
|
+
* 2. For each relationship whose type is in
|
|
101
|
+
* `OLE_RELATIONSHIP_TYPES` or `ALT_CHUNK_RELATIONSHIP_TYPES`,
|
|
102
|
+
* resolve the target to an absolute package path.
|
|
103
|
+
* 3. Look up the target part in the OPC package. Skip external /
|
|
104
|
+
* unresolved targets.
|
|
105
|
+
* 4. Classify via `classifyEmbedding()`.
|
|
106
|
+
* 5. Emit a manifest entry with `bytes()` as a lazy accessor —
|
|
107
|
+
* no eager copy at discovery time (Performance Invariant 9).
|
|
108
|
+
*/
|
|
109
|
+
export function collectEmbeddedDocuments(
|
|
110
|
+
sourcePackage: OpcPackage,
|
|
111
|
+
mainDocumentPath: string,
|
|
112
|
+
): EmbeddedDiscoveryResult {
|
|
113
|
+
const mainDocumentPart = sourcePackage.parts.get(mainDocumentPath);
|
|
114
|
+
if (!mainDocumentPart) {
|
|
115
|
+
return { entries: new Map(), manifests: [] };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const entries = new Map<string, InternalEmbeddedDocumentEntry>();
|
|
119
|
+
const manifests: EmbeddedDocumentManifest[] = [];
|
|
120
|
+
|
|
121
|
+
for (const rel of mainDocumentPart.relationships) {
|
|
122
|
+
if (rel.targetMode !== "internal") continue;
|
|
123
|
+
|
|
124
|
+
const source = classifyRelationshipSource(rel.type);
|
|
125
|
+
if (!source) continue;
|
|
126
|
+
|
|
127
|
+
const targetPath = resolveRelationshipTarget(mainDocumentPath, rel);
|
|
128
|
+
const part = sourcePackage.parts.get(targetPath);
|
|
129
|
+
if (!part) continue;
|
|
130
|
+
// Idempotence — a part might be referenced twice; keep the first
|
|
131
|
+
// relationship record to avoid duplicate manifest entries. The
|
|
132
|
+
// second relationship id is still preserved on the export side
|
|
133
|
+
// through the existing preservation store.
|
|
134
|
+
if (entries.has(targetPath)) continue;
|
|
135
|
+
|
|
136
|
+
const filename = extractFilename(rel.target);
|
|
137
|
+
const size = part.bytes?.byteLength ?? 0;
|
|
138
|
+
const kind = classifyEmbedding({
|
|
139
|
+
contentType: part.contentType ?? undefined,
|
|
140
|
+
relationshipType: rel.type,
|
|
141
|
+
targetPath: targetPath,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const internal: InternalEmbeddedDocumentEntry = {
|
|
145
|
+
source,
|
|
146
|
+
partPath: targetPath,
|
|
147
|
+
contentType: part.contentType,
|
|
148
|
+
kind,
|
|
149
|
+
relationshipId: rel.id,
|
|
150
|
+
relationshipType: rel.type,
|
|
151
|
+
...(filename !== undefined ? { filename } : {}),
|
|
152
|
+
size,
|
|
153
|
+
};
|
|
154
|
+
entries.set(targetPath, internal);
|
|
155
|
+
|
|
156
|
+
const manifest: EmbeddedDocumentManifest = {
|
|
157
|
+
id: mintManifestId(targetPath, rel.id, size),
|
|
158
|
+
kind,
|
|
159
|
+
...(filename !== undefined ? { filename } : {}),
|
|
160
|
+
...(part.contentType ? { mimeType: part.contentType } : {}),
|
|
161
|
+
size,
|
|
162
|
+
relationshipId: rel.id,
|
|
163
|
+
// Lazy byte accessor — required by Performance Invariant 9.
|
|
164
|
+
// Allocates a fresh Uint8Array copy per call so callers cannot
|
|
165
|
+
// mutate the package store.
|
|
166
|
+
bytes: () => {
|
|
167
|
+
const raw = part.bytes;
|
|
168
|
+
if (!raw || raw.byteLength === 0) return new Uint8Array(0);
|
|
169
|
+
return new Uint8Array(raw);
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
manifests.push(manifest);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return { entries, manifests };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function classifyRelationshipSource(
|
|
179
|
+
relationshipType: string,
|
|
180
|
+
): EmbeddedDocumentSource | null {
|
|
181
|
+
if (OLE_RELATIONSHIP_TYPES.has(relationshipType)) return "ole-embedding";
|
|
182
|
+
if (ALT_CHUNK_RELATIONSHIP_TYPES.has(relationshipType)) return "alt-chunk";
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function extractFilename(target: string): string | undefined {
|
|
187
|
+
if (!target) return undefined;
|
|
188
|
+
const slashIdx = target.lastIndexOf("/");
|
|
189
|
+
const backslashIdx = target.lastIndexOf("\\");
|
|
190
|
+
const idx = Math.max(slashIdx, backslashIdx);
|
|
191
|
+
const name = idx >= 0 ? target.slice(idx + 1) : target;
|
|
192
|
+
return name.length > 0 ? name : undefined;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Stable manifest id. Derived from part path + relationship id + size
|
|
197
|
+
* so re-opens of the same fixture produce identical ids — required by
|
|
198
|
+
* architecture §P8 ("id: string; // stable across load/export").
|
|
199
|
+
* Uses a pair of rolling 32-bit hashes (FNV-1a variants) rather than a
|
|
200
|
+
* counter so the id survives load-order changes, and does not depend
|
|
201
|
+
* on `node:crypto` so the session module stays browser-compatible.
|
|
202
|
+
*/
|
|
203
|
+
function mintManifestId(
|
|
204
|
+
partPath: string,
|
|
205
|
+
relationshipId: string,
|
|
206
|
+
size: number,
|
|
207
|
+
): string {
|
|
208
|
+
const material = `${partPath}${relationshipId}${size}`;
|
|
209
|
+
const a = fnv1a(material, 0x811c9dc5);
|
|
210
|
+
const b = fnv1a(material, 0x1b873593);
|
|
211
|
+
return `emb:${toHex8(a)}${toHex8(b)}`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function fnv1a(input: string, seed: number): number {
|
|
215
|
+
let hash = seed >>> 0;
|
|
216
|
+
for (let i = 0; i < input.length; i++) {
|
|
217
|
+
hash ^= input.charCodeAt(i);
|
|
218
|
+
hash = Math.imul(hash, 0x01000193);
|
|
219
|
+
}
|
|
220
|
+
return hash >>> 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function toHex8(value: number): string {
|
|
224
|
+
return (value >>> 0).toString(16).padStart(8, "0");
|
|
225
|
+
}
|