@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,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Barrel for the Layer-01 export helpers.
|
|
3
|
+
*
|
|
4
|
+
* Slice 4 ships these as pure, P6-clean pieces of the export pipeline.
|
|
5
|
+
* Slice 5 will wire them together into a single export entry point on
|
|
6
|
+
* `DocxSession.export()`; until then the legacy
|
|
7
|
+
* `exportDocxEditorSession` in `src/io/docx-session.ts` continues to
|
|
8
|
+
* orchestrate while consuming these helpers one-by-one.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
ExportBlockedError,
|
|
13
|
+
formatBlockedExportMessage,
|
|
14
|
+
type ExportBlockedCode,
|
|
15
|
+
type ExportBlockedDetails,
|
|
16
|
+
} from "./export-diagnostics.ts";
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
assertExportNotBlockedByCompatibility,
|
|
20
|
+
assertNoBlockingPreservedComments,
|
|
21
|
+
assertNoBlockingSkippedComments,
|
|
22
|
+
assertNoSkippedRevisions,
|
|
23
|
+
} from "./export-validation.ts";
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
groupFooterRevisionsByPartPath,
|
|
27
|
+
groupHeaderRevisionsByPartPath,
|
|
28
|
+
groupNoteRevisionsByNoteId,
|
|
29
|
+
} from "./serialize-dispatch.ts";
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
reattachPreservedParts,
|
|
33
|
+
reattachPreservedPackageParts,
|
|
34
|
+
} from "./preservation-reattach.ts";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-layer facade for preserved-part reattachment.
|
|
3
|
+
*
|
|
4
|
+
* Slice 4c of refactor/01 package-session. The actual reattach body
|
|
5
|
+
* lives under `src/io/export/reattach-preserved-parts.ts` (already in
|
|
6
|
+
* the `src/io/export/**` home that stays put per the target
|
|
7
|
+
* architecture). This module exposes it under the session-layer
|
|
8
|
+
* naming convention so consumers importing from
|
|
9
|
+
* `src/session/export/**` see one coherent entry surface.
|
|
10
|
+
*
|
|
11
|
+
* P6 clean — transits only `src/io/**`, which is allowed.
|
|
12
|
+
*
|
|
13
|
+
* What "reattach" means (architecture §P2 — preserve-first):
|
|
14
|
+
* Every package part that was present on input but not owned by
|
|
15
|
+
* the runtime export pipeline is copied back verbatim into the
|
|
16
|
+
* output package, along with its relationships. This is the
|
|
17
|
+
* round-trip byte-identity guarantee for parts the runtime does
|
|
18
|
+
* not understand (opaque-fragment store-backed parts, unknown
|
|
19
|
+
* OOXML, vendor extensions).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
reattachPreservedParts,
|
|
24
|
+
/**
|
|
25
|
+
* Expose the reattach entry point under a more session-shaped name
|
|
26
|
+
* for import sites that prefer it. The legacy name stays live for
|
|
27
|
+
* existing in-tree callers.
|
|
28
|
+
*/
|
|
29
|
+
reattachPreservedParts as reattachPreservedPackageParts,
|
|
30
|
+
} from "../../io/export/reattach-preserved-parts.ts";
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export-side serializer dispatch helpers.
|
|
3
|
+
*
|
|
4
|
+
* Slice 4b of refactor/01 package-session. Extracted from the inline
|
|
5
|
+
* sub-part serialization loops inside `exportDocxEditorSession`
|
|
6
|
+
* (`src/io/docx-session.ts:2360–2482`).
|
|
7
|
+
*
|
|
8
|
+
* Each helper takes pre-filtered inputs (the actionable revision list
|
|
9
|
+
* from the review layer, the canonical sub-part record) and returns a
|
|
10
|
+
* deterministic grouping by part path or note id. This lets the legacy
|
|
11
|
+
* file call these instead of the inline loops + keeps the session
|
|
12
|
+
* layer free of review-store reads (P6 clean).
|
|
13
|
+
*
|
|
14
|
+
* The helpers are explicitly generic over the revision record shape
|
|
15
|
+
* so they can be consumed by the legacy file without importing review
|
|
16
|
+
* types into `src/session/**`. Callers pass a `getStoryTarget`
|
|
17
|
+
* accessor that projects the revision's story target.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { EditorStoryTarget } from "../../api/public-types.ts";
|
|
21
|
+
import type { SubPartsCatalog } from "../../model/canonical-document.ts";
|
|
22
|
+
|
|
23
|
+
type HeaderPart = SubPartsCatalog["headers"][number];
|
|
24
|
+
type FooterPart = SubPartsCatalog["footers"][number];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Structural identity for `EditorStoryTarget` — matches the shape that
|
|
28
|
+
* `src/core/selection/mapping.ts` exports, inlined here to keep the
|
|
29
|
+
* session layer P6 clean. Behavior is identical: `main` matches `main`
|
|
30
|
+
* regardless of other fields; `header` / `footer` match on
|
|
31
|
+
* `relationshipId` + `variant` + (`sectionIndex` when both sides
|
|
32
|
+
* define it); notes match on `noteId`; everything else is !=.
|
|
33
|
+
*/
|
|
34
|
+
function storyTargetEquals(
|
|
35
|
+
left: EditorStoryTarget | undefined,
|
|
36
|
+
right: EditorStoryTarget | undefined,
|
|
37
|
+
): boolean {
|
|
38
|
+
if (left === right) return true;
|
|
39
|
+
if (!left || !right) return left === right;
|
|
40
|
+
if (left.kind !== right.kind) return false;
|
|
41
|
+
if (left.kind === "main") return true;
|
|
42
|
+
if (left.kind === "header" && right.kind === "header") {
|
|
43
|
+
return (
|
|
44
|
+
left.relationshipId === right.relationshipId &&
|
|
45
|
+
left.variant === right.variant &&
|
|
46
|
+
(left.sectionIndex === undefined || right.sectionIndex === undefined
|
|
47
|
+
? true
|
|
48
|
+
: left.sectionIndex === right.sectionIndex)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
if (left.kind === "footer" && right.kind === "footer") {
|
|
52
|
+
return (
|
|
53
|
+
left.relationshipId === right.relationshipId &&
|
|
54
|
+
left.variant === right.variant &&
|
|
55
|
+
(left.sectionIndex === undefined || right.sectionIndex === undefined
|
|
56
|
+
? true
|
|
57
|
+
: left.sectionIndex === right.sectionIndex)
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (left.kind === "footnote" && right.kind === "footnote") {
|
|
61
|
+
return left.noteId === right.noteId;
|
|
62
|
+
}
|
|
63
|
+
if (left.kind === "endnote" && right.kind === "endnote") {
|
|
64
|
+
return left.noteId === right.noteId;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Group the actionable revisions by header part path.
|
|
71
|
+
*
|
|
72
|
+
* Each entry stores the canonical header record AND the revisions
|
|
73
|
+
* that target it. When multiple header records share a part path
|
|
74
|
+
* (unusual but possible), the entry is seeded with the first record
|
|
75
|
+
* seen; if a later record carries matching revisions, the entry's
|
|
76
|
+
* `header` reference is overwritten so the serializer uses the record
|
|
77
|
+
* that owns those revisions.
|
|
78
|
+
*/
|
|
79
|
+
export function groupHeaderRevisionsByPartPath<R>(
|
|
80
|
+
headers: readonly HeaderPart[],
|
|
81
|
+
actionableRevisions: readonly R[],
|
|
82
|
+
getStoryTarget: (revision: R) => EditorStoryTarget | undefined,
|
|
83
|
+
): Map<string, { header: HeaderPart; revisions: R[] }> {
|
|
84
|
+
const result = new Map<string, { header: HeaderPart; revisions: R[] }>();
|
|
85
|
+
for (const header of headers) {
|
|
86
|
+
const entry = result.get(header.partPath) ?? { header, revisions: [] };
|
|
87
|
+
const matching = actionableRevisions.filter((revision) =>
|
|
88
|
+
storyTargetEquals(
|
|
89
|
+
getStoryTarget(revision) ?? { kind: "main" },
|
|
90
|
+
{
|
|
91
|
+
kind: "header",
|
|
92
|
+
relationshipId: header.relationshipId,
|
|
93
|
+
variant: header.variant,
|
|
94
|
+
...(header.sectionIndex !== undefined
|
|
95
|
+
? { sectionIndex: header.sectionIndex }
|
|
96
|
+
: {}),
|
|
97
|
+
},
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
entry.revisions.push(...matching);
|
|
101
|
+
if (matching.length > 0) {
|
|
102
|
+
entry.header = header;
|
|
103
|
+
}
|
|
104
|
+
result.set(header.partPath, entry);
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Symmetric with `groupHeaderRevisionsByPartPath`.
|
|
111
|
+
*/
|
|
112
|
+
export function groupFooterRevisionsByPartPath<R>(
|
|
113
|
+
footers: readonly FooterPart[],
|
|
114
|
+
actionableRevisions: readonly R[],
|
|
115
|
+
getStoryTarget: (revision: R) => EditorStoryTarget | undefined,
|
|
116
|
+
): Map<string, { footer: FooterPart; revisions: R[] }> {
|
|
117
|
+
const result = new Map<string, { footer: FooterPart; revisions: R[] }>();
|
|
118
|
+
for (const footer of footers) {
|
|
119
|
+
const entry = result.get(footer.partPath) ?? { footer, revisions: [] };
|
|
120
|
+
const matching = actionableRevisions.filter((revision) =>
|
|
121
|
+
storyTargetEquals(
|
|
122
|
+
getStoryTarget(revision) ?? { kind: "main" },
|
|
123
|
+
{
|
|
124
|
+
kind: "footer",
|
|
125
|
+
relationshipId: footer.relationshipId,
|
|
126
|
+
variant: footer.variant,
|
|
127
|
+
...(footer.sectionIndex !== undefined
|
|
128
|
+
? { sectionIndex: footer.sectionIndex }
|
|
129
|
+
: {}),
|
|
130
|
+
},
|
|
131
|
+
),
|
|
132
|
+
);
|
|
133
|
+
entry.revisions.push(...matching);
|
|
134
|
+
if (matching.length > 0) {
|
|
135
|
+
entry.footer = footer;
|
|
136
|
+
}
|
|
137
|
+
result.set(footer.partPath, entry);
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Group actionable revisions by `storyTarget.noteId` for the given
|
|
144
|
+
* note kind (`footnote` or `endnote`). The return value is a
|
|
145
|
+
* `Record<noteId, R[]>` suitable for passing directly into
|
|
146
|
+
* `serializeFootnotesXml` / `serializeEndnotesXml`.
|
|
147
|
+
*
|
|
148
|
+
* Revisions whose story target is not a note of the given kind are
|
|
149
|
+
* excluded.
|
|
150
|
+
*/
|
|
151
|
+
export function groupNoteRevisionsByNoteId<R>(
|
|
152
|
+
actionableRevisions: readonly R[],
|
|
153
|
+
kind: "footnote" | "endnote",
|
|
154
|
+
getStoryTarget: (revision: R) => EditorStoryTarget | undefined,
|
|
155
|
+
): Record<string, R[]> {
|
|
156
|
+
const result: Record<string, R[]> = {};
|
|
157
|
+
for (const revision of actionableRevisions) {
|
|
158
|
+
const target = getStoryTarget(revision);
|
|
159
|
+
if (!target || target.kind !== kind) continue;
|
|
160
|
+
const noteId = target.noteId;
|
|
161
|
+
if (!(noteId in result)) result[noteId] = [];
|
|
162
|
+
result[noteId]!.push(revision);
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stateful-export-pipeline helpers extracted from `src/io/docx-session.ts`
|
|
3
|
+
* (slice 5e-6f.1). These functions run on the export side only — they
|
|
4
|
+
* decide whether source bytes can be reused, ensure host-owned metadata
|
|
5
|
+
* parts (core/app properties + workflow payload) are present, build the
|
|
6
|
+
* core-properties XML, and weave `w:permStart` / `w:permEnd` protection
|
|
7
|
+
* anchors into the serialized document XML.
|
|
8
|
+
*
|
|
9
|
+
* Export-only by contract: every caller lives inside
|
|
10
|
+
* `exportDocxEditorSession`. Protection-extraction helpers
|
|
11
|
+
* (`extractDocumentProtection` / `buildProtectionSnapshot` / …) are
|
|
12
|
+
* load-path-shared and stay in docx-session.ts until slice 5e-7 rewires
|
|
13
|
+
* `DocxSession.open` directly.
|
|
14
|
+
*
|
|
15
|
+
* P6-clean: imports reach into src/io/ooxml, src/io/export,
|
|
16
|
+
* src/api/public-types, and src/model/canonical-document. Nothing from
|
|
17
|
+
* src/runtime, src/review, src/core, or src/ui-tailwind.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { EditorSessionState, ProtectionSnapshot } from "../../api/public-types.ts";
|
|
21
|
+
import type { CanonicalDocument } from "../../model/canonical-document.ts";
|
|
22
|
+
import { buildAppPropertiesXml } from "../../io/export/build-app-properties-xml.ts";
|
|
23
|
+
import { ExportSession } from "../../io/export/export-session.ts";
|
|
24
|
+
import { mapParagraphBoundaries } from "../../io/export/serialize-comments.ts";
|
|
25
|
+
import type { OpcPackage } from "../../io/opc/package-reader.ts";
|
|
26
|
+
import {
|
|
27
|
+
CONTENT_TYPES_PATH,
|
|
28
|
+
PACKAGE_RELATIONSHIPS_PATH,
|
|
29
|
+
getRelationshipsPartPath,
|
|
30
|
+
resolveRelationshipTarget,
|
|
31
|
+
} from "../../io/ooxml/part-manifest.ts";
|
|
32
|
+
import {
|
|
33
|
+
buildWorkflowPayloadParts,
|
|
34
|
+
WORKFLOW_PAYLOAD_CONTENT_TYPE,
|
|
35
|
+
WORKFLOW_PAYLOAD_CUSTOM_PROPS_CONTENT_TYPE,
|
|
36
|
+
WORKFLOW_PAYLOAD_CUSTOM_PROPS_PART_PATH,
|
|
37
|
+
WORKFLOW_PAYLOAD_CUSTOM_PROPS_RELATIONSHIP_TYPE,
|
|
38
|
+
WORKFLOW_PAYLOAD_ITEM_PROPS_CONTENT_TYPE,
|
|
39
|
+
type EditorStatePayload,
|
|
40
|
+
} from "../../io/ooxml/workflow-payload.ts";
|
|
41
|
+
|
|
42
|
+
type CanonicalDocumentEnvelope = CanonicalDocument;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Minimal shape of the session-import state object that the export pipeline
|
|
46
|
+
* consumes. The full ImportedDocxState interface lives in docx-session.ts;
|
|
47
|
+
* these are the subset of fields this module actually reads.
|
|
48
|
+
*/
|
|
49
|
+
export interface ExportPipelineState {
|
|
50
|
+
sourcePackage: OpcPackage;
|
|
51
|
+
sourceDocumentPartPath: string;
|
|
52
|
+
sourceCommentsPartPath?: string;
|
|
53
|
+
sourceCommentsExtendedPartPath?: string;
|
|
54
|
+
sourceCommentsIdsPartPath?: string;
|
|
55
|
+
sourcePeoplePartPath?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Core + app properties OPC addresses. Moved here from docx-session.ts
|
|
59
|
+
// alongside the helpers that consume them; the legacy file re-imports.
|
|
60
|
+
export const APP_PROPERTIES_PART_PATH = "/docProps/app.xml";
|
|
61
|
+
export const CORE_PROPERTIES_PART_PATH = "/docProps/core.xml";
|
|
62
|
+
export const APP_PROPERTIES_CONTENT_TYPE =
|
|
63
|
+
"application/vnd.openxmlformats-officedocument.extended-properties+xml";
|
|
64
|
+
export const CORE_PROPERTIES_CONTENT_TYPE =
|
|
65
|
+
"application/vnd.openxmlformats-package.core-properties+xml";
|
|
66
|
+
export const APP_PROPERTIES_RELATIONSHIP_TYPE =
|
|
67
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties";
|
|
68
|
+
export const CORE_PROPERTIES_RELATIONSHIP_TYPE =
|
|
69
|
+
"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties";
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Read a Node-style environment variable without referencing the Node-only
|
|
73
|
+
* `process` global in the production build (which excludes @types/node).
|
|
74
|
+
* Returns `undefined` in browser environments or when the var is unset.
|
|
75
|
+
*/
|
|
76
|
+
export function readNodeEnvVar(name: string): string | undefined {
|
|
77
|
+
const proc = (globalThis as unknown as {
|
|
78
|
+
process?: { env?: Record<string, string | undefined> };
|
|
79
|
+
}).process;
|
|
80
|
+
return proc?.env?.[name];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function canReuseSourceBytesForCurrentDocument(
|
|
84
|
+
state: ExportPipelineState,
|
|
85
|
+
document: CanonicalDocumentEnvelope,
|
|
86
|
+
): boolean {
|
|
87
|
+
// The validator-loop CI harness (§1 / §7) needs to exercise the full
|
|
88
|
+
// serializer pipeline — including A.1 / A.2 / A.3 / A.6 / A.7 / A.8 /
|
|
89
|
+
// A.9 fixes — so it sets DOCX_VALIDATOR_FORCE_REGEN=1 to disable the
|
|
90
|
+
// fast-path byte-reuse. Never set this flag in production; it forces a
|
|
91
|
+
// full XML regeneration on every export and is only used by the CCEP
|
|
92
|
+
// validator harness to prove our serializer's correctness.
|
|
93
|
+
if (readNodeEnvVar("DOCX_VALIDATOR_FORCE_REGEN") === "1") {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
if (requiresHostMetadataNormalization(state.sourcePackage, state.sourceDocumentPartPath)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const commentThreads = Object.values(document.review.comments);
|
|
101
|
+
const hasLiveComments = commentThreads.some((thread) => thread.anchor.kind !== "detached");
|
|
102
|
+
const hasRuntimeAuthoredActiveRevisions = Object.values(document.review.revisions).some((revision) =>
|
|
103
|
+
revision.status === "open" && revision.metadata?.source === "runtime"
|
|
104
|
+
);
|
|
105
|
+
if (hasRuntimeAuthoredActiveRevisions) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
if (!hasLiveComments) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return Boolean(
|
|
113
|
+
state.sourceCommentsPartPath &&
|
|
114
|
+
state.sourceCommentsExtendedPartPath &&
|
|
115
|
+
state.sourceCommentsIdsPartPath &&
|
|
116
|
+
state.sourcePeoplePartPath,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function requiresHostMetadataNormalization(
|
|
121
|
+
sourcePackage: OpcPackage,
|
|
122
|
+
sourceDocumentPartPath: string,
|
|
123
|
+
): boolean {
|
|
124
|
+
return (
|
|
125
|
+
isSuspiciouslySkeletalWordPackage(sourcePackage, sourceDocumentPartPath) &&
|
|
126
|
+
!hasHostSafeMetadataPackageStructure(sourcePackage)
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function ensureHostMetadataParts(
|
|
131
|
+
exportSession: ExportSession,
|
|
132
|
+
sourcePackage: OpcPackage,
|
|
133
|
+
document: CanonicalDocumentEnvelope,
|
|
134
|
+
): void {
|
|
135
|
+
const corePropertiesPart = sourcePackage.parts.get(CORE_PROPERTIES_PART_PATH);
|
|
136
|
+
if (!corePropertiesPart || corePropertiesPart.contentType !== CORE_PROPERTIES_CONTENT_TYPE) {
|
|
137
|
+
exportSession.replaceOwnedPart({
|
|
138
|
+
path: CORE_PROPERTIES_PART_PATH,
|
|
139
|
+
bytes: corePropertiesPart?.bytes ?? new TextEncoder().encode(buildCorePropertiesXml(document)),
|
|
140
|
+
contentType: CORE_PROPERTIES_CONTENT_TYPE,
|
|
141
|
+
compression: corePropertiesPart?.compression,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const appPropertiesPart = sourcePackage.parts.get(APP_PROPERTIES_PART_PATH);
|
|
146
|
+
if (!appPropertiesPart || appPropertiesPart.contentType !== APP_PROPERTIES_CONTENT_TYPE) {
|
|
147
|
+
exportSession.replaceOwnedPart({
|
|
148
|
+
path: APP_PROPERTIES_PART_PATH,
|
|
149
|
+
bytes: appPropertiesPart?.bytes ?? new TextEncoder().encode(buildAppPropertiesXml()),
|
|
150
|
+
contentType: APP_PROPERTIES_CONTENT_TYPE,
|
|
151
|
+
compression: appPropertiesPart?.compression,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
exportSession.ensurePackageRelationship({
|
|
156
|
+
type: CORE_PROPERTIES_RELATIONSHIP_TYPE,
|
|
157
|
+
target: CORE_PROPERTIES_PART_PATH,
|
|
158
|
+
preferredId: "rIdDocPropsCore",
|
|
159
|
+
});
|
|
160
|
+
exportSession.ensurePackageRelationship({
|
|
161
|
+
type: APP_PROPERTIES_RELATIONSHIP_TYPE,
|
|
162
|
+
target: APP_PROPERTIES_PART_PATH,
|
|
163
|
+
preferredId: "rIdDocPropsApp",
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function ensureWorkflowPayloadParts(
|
|
168
|
+
exportSession: ExportSession,
|
|
169
|
+
sessionState: EditorSessionState,
|
|
170
|
+
document: CanonicalDocumentEnvelope,
|
|
171
|
+
sourcePackage: OpcPackage,
|
|
172
|
+
resolvedPartPaths: {
|
|
173
|
+
payloadPartPath: string;
|
|
174
|
+
itemPropsPartPath: string;
|
|
175
|
+
},
|
|
176
|
+
editorState?: EditorStatePayload,
|
|
177
|
+
): void {
|
|
178
|
+
const payloadParts = buildWorkflowPayloadParts({
|
|
179
|
+
sourcePackage,
|
|
180
|
+
workflowMetadata: sessionState.workflowMetadata,
|
|
181
|
+
workflowOverlay: sessionState.workflowOverlay,
|
|
182
|
+
visibilityPolicies: sessionState.visibilityPolicies,
|
|
183
|
+
markupModePolicy: sessionState.markupModePolicy ?? null,
|
|
184
|
+
editorState,
|
|
185
|
+
documentId: sessionState.documentId,
|
|
186
|
+
createdAt: document.createdAt,
|
|
187
|
+
updatedAt: document.updatedAt,
|
|
188
|
+
producerVersion: sessionState.editorBuild,
|
|
189
|
+
});
|
|
190
|
+
if (!payloadParts) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (
|
|
194
|
+
payloadParts.payloadPartPath !== resolvedPartPaths.payloadPartPath ||
|
|
195
|
+
payloadParts.itemPropsPartPath !== resolvedPartPaths.itemPropsPartPath
|
|
196
|
+
) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
"Workflow payload export resolved inconsistent customXml paths; export session ownership no longer matches payload serialization.",
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const payloadPart = sourcePackage.parts.get(payloadParts.payloadPartPath);
|
|
203
|
+
const itemPropsPart = sourcePackage.parts.get(payloadParts.itemPropsPartPath);
|
|
204
|
+
const customPropsPart = sourcePackage.parts.get(WORKFLOW_PAYLOAD_CUSTOM_PROPS_PART_PATH);
|
|
205
|
+
|
|
206
|
+
exportSession.replaceOwnedPart({
|
|
207
|
+
path: payloadParts.payloadPartPath,
|
|
208
|
+
bytes: new TextEncoder().encode(payloadParts.payloadPartXml),
|
|
209
|
+
contentType: payloadPart?.contentType ?? WORKFLOW_PAYLOAD_CONTENT_TYPE,
|
|
210
|
+
relationships: payloadParts.payloadRelationships,
|
|
211
|
+
compression: payloadPart?.compression,
|
|
212
|
+
});
|
|
213
|
+
exportSession.replaceOwnedPart({
|
|
214
|
+
path: payloadParts.itemPropsPartPath,
|
|
215
|
+
bytes: new TextEncoder().encode(payloadParts.itemPropsXml),
|
|
216
|
+
contentType: itemPropsPart?.contentType ?? WORKFLOW_PAYLOAD_ITEM_PROPS_CONTENT_TYPE,
|
|
217
|
+
compression: itemPropsPart?.compression,
|
|
218
|
+
});
|
|
219
|
+
exportSession.replaceOwnedPart({
|
|
220
|
+
path: WORKFLOW_PAYLOAD_CUSTOM_PROPS_PART_PATH,
|
|
221
|
+
bytes: new TextEncoder().encode(payloadParts.customPropertiesXml),
|
|
222
|
+
contentType: customPropsPart?.contentType ?? WORKFLOW_PAYLOAD_CUSTOM_PROPS_CONTENT_TYPE,
|
|
223
|
+
compression: customPropsPart?.compression,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
exportSession.ensurePackageRelationship({
|
|
227
|
+
type: WORKFLOW_PAYLOAD_CUSTOM_PROPS_RELATIONSHIP_TYPE,
|
|
228
|
+
target: WORKFLOW_PAYLOAD_CUSTOM_PROPS_PART_PATH,
|
|
229
|
+
preferredId: "rIdBwWorkflowCustomProps",
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function hasHostSafeMetadataPackageStructure(sourcePackage: OpcPackage): boolean {
|
|
234
|
+
const corePropertiesPart = sourcePackage.parts.get(CORE_PROPERTIES_PART_PATH);
|
|
235
|
+
const appPropertiesPart = sourcePackage.parts.get(APP_PROPERTIES_PART_PATH);
|
|
236
|
+
return (
|
|
237
|
+
corePropertiesPart?.contentType === CORE_PROPERTIES_CONTENT_TYPE &&
|
|
238
|
+
appPropertiesPart?.contentType === APP_PROPERTIES_CONTENT_TYPE &&
|
|
239
|
+
hasPackageRelationshipTarget(
|
|
240
|
+
sourcePackage,
|
|
241
|
+
CORE_PROPERTIES_RELATIONSHIP_TYPE,
|
|
242
|
+
CORE_PROPERTIES_PART_PATH,
|
|
243
|
+
) &&
|
|
244
|
+
hasPackageRelationshipTarget(
|
|
245
|
+
sourcePackage,
|
|
246
|
+
APP_PROPERTIES_RELATIONSHIP_TYPE,
|
|
247
|
+
APP_PROPERTIES_PART_PATH,
|
|
248
|
+
)
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function hasPackageRelationshipTarget(
|
|
253
|
+
sourcePackage: OpcPackage,
|
|
254
|
+
relationshipType: string,
|
|
255
|
+
targetPartPath: string,
|
|
256
|
+
): boolean {
|
|
257
|
+
return sourcePackage.manifest.packageRelationships.some(
|
|
258
|
+
(relationship) =>
|
|
259
|
+
relationship.type === relationshipType &&
|
|
260
|
+
relationship.targetMode === "internal" &&
|
|
261
|
+
resolveRelationshipTarget(null, relationship) === targetPartPath,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function isSuspiciouslySkeletalWordPackage(
|
|
266
|
+
sourcePackage: OpcPackage,
|
|
267
|
+
sourceDocumentPartPath: string,
|
|
268
|
+
): boolean {
|
|
269
|
+
const allowedPaths = new Set<string>([
|
|
270
|
+
CONTENT_TYPES_PATH,
|
|
271
|
+
PACKAGE_RELATIONSHIPS_PATH,
|
|
272
|
+
sourceDocumentPartPath,
|
|
273
|
+
]);
|
|
274
|
+
const relationshipsPartPath = getRelationshipsPartPath(sourceDocumentPartPath);
|
|
275
|
+
if (relationshipsPartPath) {
|
|
276
|
+
allowedPaths.add(relationshipsPartPath);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return [...sourcePackage.parts.keys()].every((partPath) => allowedPaths.has(partPath));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function buildCorePropertiesXml(document: CanonicalDocumentEnvelope): string {
|
|
283
|
+
const { metadata } = document;
|
|
284
|
+
const keywords =
|
|
285
|
+
Array.isArray(metadata.keywords) && metadata.keywords.length > 0
|
|
286
|
+
? metadata.keywords.join(", ")
|
|
287
|
+
: undefined;
|
|
288
|
+
const propertyLines = [
|
|
289
|
+
xmlNode("dc:title", metadata.title),
|
|
290
|
+
xmlNode("dc:subject", metadata.subject),
|
|
291
|
+
xmlNode("dc:description", metadata.description),
|
|
292
|
+
xmlNode("dc:language", metadata.language),
|
|
293
|
+
xmlNode("cp:keywords", keywords),
|
|
294
|
+
xmlNode("cp:category", metadata.category),
|
|
295
|
+
xmlNode('dcterms:created xsi:type="dcterms:W3CDTF"', document.createdAt),
|
|
296
|
+
xmlNode('dcterms:modified xsi:type="dcterms:W3CDTF"', document.updatedAt),
|
|
297
|
+
].filter((line): line is string => Boolean(line));
|
|
298
|
+
|
|
299
|
+
return [
|
|
300
|
+
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',
|
|
301
|
+
'<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">',
|
|
302
|
+
...propertyLines.map((line) => ` ${line}`),
|
|
303
|
+
"</cp:coreProperties>",
|
|
304
|
+
].join("\n");
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export function xmlNode(tagName: string, value: string | undefined): string | undefined {
|
|
308
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
309
|
+
return undefined;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return `<${tagName}>${escapeXml(value)}</${tagName.split(" ", 1)[0]}>`;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export function serializeProtectionRangesIntoDocumentXml(
|
|
316
|
+
documentXml: string,
|
|
317
|
+
protection: ProtectionSnapshot,
|
|
318
|
+
paragraphs = mapParagraphBoundaries(documentXml),
|
|
319
|
+
): string {
|
|
320
|
+
if (protection.ranges.length === 0) {
|
|
321
|
+
return documentXml;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const insertions = new Map<number, string[]>();
|
|
325
|
+
|
|
326
|
+
for (const range of protection.ranges) {
|
|
327
|
+
if (typeof range.start !== "number" || typeof range.end !== "number") {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const rangeStart = range.start;
|
|
331
|
+
const rangeEnd = range.end;
|
|
332
|
+
|
|
333
|
+
const startParagraph = paragraphs.find(
|
|
334
|
+
(candidate) => rangeStart >= candidate.start && rangeStart <= candidate.end,
|
|
335
|
+
);
|
|
336
|
+
const endParagraph = paragraphs.find(
|
|
337
|
+
(candidate) => rangeEnd >= candidate.start && rangeEnd <= candidate.end,
|
|
338
|
+
);
|
|
339
|
+
if (!startParagraph || !endParagraph) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const startIndex =
|
|
344
|
+
startParagraph.boundaries.get(rangeStart) ??
|
|
345
|
+
findNearestBoundaryIndex(startParagraph.boundaries, rangeStart, "backward");
|
|
346
|
+
const endIndex =
|
|
347
|
+
endParagraph.boundaries.get(rangeEnd) ??
|
|
348
|
+
findNearestBoundaryIndex(endParagraph.boundaries, rangeEnd, "forward");
|
|
349
|
+
if (startIndex === undefined || endIndex === undefined) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const permStartXml = [
|
|
354
|
+
`<w:permStart`,
|
|
355
|
+
` w:id="${escapeXmlAttribute(range.rangeId)}"`,
|
|
356
|
+
range.editorGroup ? ` w:edGrp="${escapeXmlAttribute(range.editorGroup)}"` : "",
|
|
357
|
+
range.editor ? ` w:ed="${escapeXmlAttribute(range.editor)}"` : "",
|
|
358
|
+
`/>`,
|
|
359
|
+
].join("");
|
|
360
|
+
const permEndXml = `<w:permEnd w:id="${escapeXmlAttribute(range.rangeId)}"/>`;
|
|
361
|
+
|
|
362
|
+
pushProtectionInsertion(insertions, startIndex, permStartXml);
|
|
363
|
+
pushProtectionInsertion(insertions, endIndex, permEndXml);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (insertions.size === 0) {
|
|
367
|
+
return documentXml;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const parts: string[] = [];
|
|
371
|
+
let cursor = 0;
|
|
372
|
+
for (const [index, snippets] of [...insertions.entries()].sort(([left], [right]) => left - right)) {
|
|
373
|
+
parts.push(documentXml.slice(cursor, index));
|
|
374
|
+
parts.push(...snippets);
|
|
375
|
+
cursor = index;
|
|
376
|
+
}
|
|
377
|
+
parts.push(documentXml.slice(cursor));
|
|
378
|
+
return parts.join("");
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function pushProtectionInsertion(
|
|
382
|
+
insertions: Map<number, string[]>,
|
|
383
|
+
index: number,
|
|
384
|
+
xml: string,
|
|
385
|
+
): void {
|
|
386
|
+
const existing = insertions.get(index);
|
|
387
|
+
if (existing) {
|
|
388
|
+
existing.push(xml);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
insertions.set(index, [xml]);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function findNearestBoundaryIndex(
|
|
395
|
+
boundaries: Map<number, number>,
|
|
396
|
+
position: number,
|
|
397
|
+
direction: "backward" | "forward",
|
|
398
|
+
): number | undefined {
|
|
399
|
+
const ordered = [...boundaries.entries()].sort(([left], [right]) => left - right);
|
|
400
|
+
if (direction === "backward") {
|
|
401
|
+
for (let index = ordered.length - 1; index >= 0; index -= 1) {
|
|
402
|
+
const [boundaryPos, boundaryIndex] = ordered[index]!;
|
|
403
|
+
if (boundaryPos <= position) {
|
|
404
|
+
return boundaryIndex;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return undefined;
|
|
408
|
+
}
|
|
409
|
+
for (const [boundaryPos, boundaryIndex] of ordered) {
|
|
410
|
+
if (boundaryPos >= position) {
|
|
411
|
+
return boundaryIndex;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return undefined;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export function escapeXml(value: string): string {
|
|
418
|
+
return value
|
|
419
|
+
.replace(/&/g, "&")
|
|
420
|
+
.replace(/</g, "<")
|
|
421
|
+
.replace(/>/g, ">")
|
|
422
|
+
.replace(/\"/g, """)
|
|
423
|
+
.replace(/'/g, "'");
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export function escapeXmlAttribute(value: string): string {
|
|
427
|
+
return value
|
|
428
|
+
.replace(/&/g, "&")
|
|
429
|
+
.replace(/</g, "<")
|
|
430
|
+
.replace(/>/g, ">")
|
|
431
|
+
.replace(/"/g, """);
|
|
432
|
+
}
|