@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
|
@@ -48,24 +48,27 @@ import {
|
|
|
48
48
|
isBlockedImportFeatureKey,
|
|
49
49
|
} from "../preservation/store.ts";
|
|
50
50
|
import { getStoryBlocks } from "./story-targeting.ts";
|
|
51
|
-
import {
|
|
52
|
-
createNumberingPrefixResolver,
|
|
53
|
-
type NumberingPrefixResult,
|
|
54
|
-
type NumberingPrefixResolver,
|
|
55
|
-
} from "./numbering-prefix.ts";
|
|
56
51
|
import {
|
|
57
52
|
collectSectionContexts,
|
|
58
53
|
findHeaderFooterDocumentEntry,
|
|
59
54
|
resolveSectionVariants,
|
|
60
55
|
} from "./story-context.ts";
|
|
61
|
-
|
|
56
|
+
// L03 boundary: surface-projection owns PROJECTION, not formatting semantics.
|
|
57
|
+
// Every formatting resolution goes through `FormattingContext` — the
|
|
58
|
+
// production hot-path entry point for Layer 03. Scattered sub-resolver
|
|
59
|
+
// calls are rejected by `scripts/ci-check-formatting-production-boundary.mjs`.
|
|
62
60
|
import {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
} from "./
|
|
67
|
-
import {
|
|
68
|
-
import {
|
|
61
|
+
createFormattingContext,
|
|
62
|
+
type FormattingContext,
|
|
63
|
+
type NumberingResolution,
|
|
64
|
+
} from "./formatting/formatting-context.ts";
|
|
65
|
+
import { isSupportedNumberingFormat } from "./formatting/numbering/prefix.ts";
|
|
66
|
+
import type { FormattingTelemetryEvent } from "./formatting/resolve-effective.ts";
|
|
67
|
+
// The ThemeColorResolver type is still exposed to a handful of helper
|
|
68
|
+
// callsites below (shading, cell fill) that accept a pre-built resolver.
|
|
69
|
+
// Surface-projection no longer constructs one — it reads
|
|
70
|
+
// `formattingContext.theme` instead.
|
|
71
|
+
import type { ThemeColorResolver } from "./formatting/theme-color.ts";
|
|
69
72
|
import type { CanonicalParagraphFormatting, CanonicalRunFormatting } from "../model/canonical-document.ts";
|
|
70
73
|
|
|
71
74
|
const SAFE_CSS_HEX_COLOR_RE = /^#?(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
|
|
@@ -93,6 +96,48 @@ interface ParagraphAccumulator {
|
|
|
93
96
|
|
|
94
97
|
export interface SurfaceProjectionOptions {
|
|
95
98
|
viewportBlockRange?: { start: number; end: number } | null;
|
|
99
|
+
/**
|
|
100
|
+
* Active markup mode. When set together with the document's
|
|
101
|
+
* `review.revisions`, surface-projection tags each text segment whose
|
|
102
|
+
* offset falls inside a revision's anchor range with
|
|
103
|
+
* `segment.revisionDisplay` flags via `FormattingContext`.
|
|
104
|
+
*
|
|
105
|
+
* Architecture §F6: the layer applies the markup-mode decision (from
|
|
106
|
+
* UI / session capabilities); it does not decide it. When absent,
|
|
107
|
+
* segments carry no `revisionDisplay` field and the legacy decoration
|
|
108
|
+
* path continues to own rendering (back-compat during consumer
|
|
109
|
+
* migration).
|
|
110
|
+
*/
|
|
111
|
+
revisionMarkupMode?: "clean" | "simple" | "all";
|
|
112
|
+
/**
|
|
113
|
+
* Live markup-mode callback. When set, wins over `revisionMarkupMode`
|
|
114
|
+
* on each projection pass — the composition site (L10
|
|
115
|
+
* `ui.viewport.getEffectiveMarkupMode()`) updates the rendered posture
|
|
116
|
+
* without re-parsing the document. Returning `undefined` means "no
|
|
117
|
+
* posture this pass". See `03-formatting-semantics.md` §F6.
|
|
118
|
+
*/
|
|
119
|
+
getEffectiveMarkupMode?: () => "clean" | "simple" | "all" | undefined;
|
|
120
|
+
/**
|
|
121
|
+
* Author-id → color palette for revision-display posture. Consumed by
|
|
122
|
+
* `FormattingContext.resolveRunFromMarks({revision})` via the layer's
|
|
123
|
+
* `applyRevisionDisplay` function when the active revision carries an
|
|
124
|
+
* `authorId`.
|
|
125
|
+
*/
|
|
126
|
+
authorColorPalette?: ReadonlyMap<string, string>;
|
|
127
|
+
/**
|
|
128
|
+
* Emit callback for L03 formatting telemetry. Bridges the numbering
|
|
129
|
+
* format-coverage gap (`formatting.numbering_format_unsupported`) onto
|
|
130
|
+
* the runtime telemetry bus when the document resolves a numbering
|
|
131
|
+
* level whose format is not in `SUPPORTED_NUMBERING_FORMATS` — the
|
|
132
|
+
* fallback path still renders `String(value)` so output is unchanged,
|
|
133
|
+
* but agents + the debug pane see the fidelity gap.
|
|
134
|
+
*
|
|
135
|
+
* Typically wired via `buildFormattingTelemetryBridge(handle.debug.bus)`
|
|
136
|
+
* in `src/runtime/formatting/telemetry-bridge.ts`. Surface-projection
|
|
137
|
+
* deduplicates emissions per `(numberingInstanceId, level, format)`
|
|
138
|
+
* tuple within a single projection pass.
|
|
139
|
+
*/
|
|
140
|
+
emitFormattingTelemetry?: (event: FormattingTelemetryEvent) => void;
|
|
96
141
|
}
|
|
97
142
|
|
|
98
143
|
export function createEditorSurfaceSnapshot(
|
|
@@ -108,7 +153,25 @@ export function createEditorSurfaceSnapshot(
|
|
|
108
153
|
});
|
|
109
154
|
const blocks: SurfaceBlockSnapshot[] = [];
|
|
110
155
|
const lockedFragmentIds: string[] = [];
|
|
111
|
-
|
|
156
|
+
// L03 boundary: construct one formatting context per projection pass.
|
|
157
|
+
// The context owns the theme resolver + numbering prefix counter +
|
|
158
|
+
// style catalog walks. Surface-projection delegates every formatting
|
|
159
|
+
// semantics question to it and keeps only projection concerns
|
|
160
|
+
// (id synthesis, viewport culling, chart/image metadata).
|
|
161
|
+
const formattingContext = createFormattingContext(document, {
|
|
162
|
+
...(options.revisionMarkupMode ? { revisionMarkupMode: options.revisionMarkupMode } : {}),
|
|
163
|
+
...(options.getEffectiveMarkupMode
|
|
164
|
+
? { getEffectiveMarkupMode: options.getEffectiveMarkupMode }
|
|
165
|
+
: {}),
|
|
166
|
+
...(options.authorColorPalette ? { authorColorPalette: options.authorColorPalette } : {}),
|
|
167
|
+
});
|
|
168
|
+
// L03 §F6: revision range index is owned by `FormattingContext`
|
|
169
|
+
// itself when `revisionMarkupMode` is set. Surface-projection calls
|
|
170
|
+
// `formattingContext.findRevisionAtRange(from, to)` per segment to
|
|
171
|
+
// look up the attached revision + passes it to
|
|
172
|
+
// `formattingContext.resolveRunFromMarks({revision})`. The layer
|
|
173
|
+
// applies the posture; surface-projection just carries the flags
|
|
174
|
+
// through to `segment.revisionDisplay`.
|
|
112
175
|
// Open a chartModelStore build pass tagged with the document envelope.
|
|
113
176
|
// Every `chart_preview` node populated during the pass records its id
|
|
114
177
|
// in the pass's seen-set; `endBuildPass()` below evicts store entries
|
|
@@ -116,6 +179,14 @@ export function createEditorSurfaceSnapshot(
|
|
|
116
179
|
// earlier builds (same owner, not seen this pass). Previously the
|
|
117
180
|
// store grew unbounded across document loads — documented v1 gap.
|
|
118
181
|
chartModelStore.beginBuildPass(document);
|
|
182
|
+
// Per-pass dedup set for L03 `formatting.numbering_format_unsupported`
|
|
183
|
+
// emissions. Keys are `"${numberingInstanceId}|${level}|${format}"`.
|
|
184
|
+
// Only populated when the caller wires `emitFormattingTelemetry` —
|
|
185
|
+
// otherwise zero cost.
|
|
186
|
+
const unsupportedNumberingFormatsSeen: Set<string> | null = options
|
|
187
|
+
.emitFormattingTelemetry
|
|
188
|
+
? new Set<string>()
|
|
189
|
+
: null;
|
|
119
190
|
let cursor = 0;
|
|
120
191
|
const counters = {
|
|
121
192
|
paragraph: 0,
|
|
@@ -143,7 +214,7 @@ export function createEditorSurfaceSnapshot(
|
|
|
143
214
|
document,
|
|
144
215
|
cursor,
|
|
145
216
|
counters,
|
|
146
|
-
|
|
217
|
+
formattingContext,
|
|
147
218
|
activeStory.kind !== "main",
|
|
148
219
|
!isInViewport,
|
|
149
220
|
);
|
|
@@ -178,7 +249,7 @@ export function createEditorSurfaceSnapshot(
|
|
|
178
249
|
}
|
|
179
250
|
}
|
|
180
251
|
|
|
181
|
-
const secondaryStories = createSecondaryStorySurfaces(document,
|
|
252
|
+
const secondaryStories = createSecondaryStorySurfaces(document, formattingContext);
|
|
182
253
|
|
|
183
254
|
// Close the chartModelStore build pass. Evicts entries from previous
|
|
184
255
|
// documents + stale entries whose ids no longer appear in the current
|
|
@@ -186,6 +257,36 @@ export function createEditorSurfaceSnapshot(
|
|
|
186
257
|
// chart_preview nodes).
|
|
187
258
|
chartModelStore.endBuildPass();
|
|
188
259
|
|
|
260
|
+
// L03 Task A3 item 5 — emit `formatting.numbering_format_unsupported`
|
|
261
|
+
// once per unique `(numberingInstanceId, level, format)` tuple when
|
|
262
|
+
// the resolved format is outside `SUPPORTED_NUMBERING_FORMATS`. The
|
|
263
|
+
// rendered output is unchanged (fallback is `String(value)`); this
|
|
264
|
+
// signal exposes the fidelity gap to agents + the debug pane without
|
|
265
|
+
// changing behavior. Zero cost when the caller does not wire
|
|
266
|
+
// `emitFormattingTelemetry`.
|
|
267
|
+
if (
|
|
268
|
+
options.emitFormattingTelemetry !== undefined &&
|
|
269
|
+
unsupportedNumberingFormatsSeen !== null
|
|
270
|
+
) {
|
|
271
|
+
for (const block of blocks) {
|
|
272
|
+
if (block.kind !== "paragraph") continue;
|
|
273
|
+
if (!block.resolvedNumbering || !block.numbering) continue;
|
|
274
|
+
const fmt = block.resolvedNumbering.format;
|
|
275
|
+
if (isSupportedNumberingFormat(fmt)) continue;
|
|
276
|
+
const key = `${block.numbering.numberingInstanceId}|${block.resolvedNumbering.level}|${fmt}`;
|
|
277
|
+
if (unsupportedNumberingFormatsSeen.has(key)) continue;
|
|
278
|
+
unsupportedNumberingFormatsSeen.add(key);
|
|
279
|
+
options.emitFormattingTelemetry({
|
|
280
|
+
type: "formatting.numbering_format_unsupported",
|
|
281
|
+
payload: {
|
|
282
|
+
format: fmt,
|
|
283
|
+
numberingInstanceId: block.numbering.numberingInstanceId,
|
|
284
|
+
level: block.resolvedNumbering.level,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
189
290
|
return {
|
|
190
291
|
storySize: cursor,
|
|
191
292
|
plainText: createPlainText(blocks),
|
|
@@ -208,7 +309,7 @@ function createSurfaceBlock(
|
|
|
208
309
|
customXml: number;
|
|
209
310
|
altChunk: number;
|
|
210
311
|
},
|
|
211
|
-
|
|
312
|
+
formattingContext: FormattingContext,
|
|
212
313
|
promoteSecondaryStoryTextBoxes: boolean,
|
|
213
314
|
cullBuild: boolean = false,
|
|
214
315
|
): { block: SurfaceBlockSnapshot; lockedFragmentIds: string[]; nextCursor: number } {
|
|
@@ -249,7 +350,7 @@ function createSurfaceBlock(
|
|
|
249
350
|
document,
|
|
250
351
|
cursor,
|
|
251
352
|
counters,
|
|
252
|
-
|
|
353
|
+
formattingContext,
|
|
253
354
|
promoteSecondaryStoryTextBoxes,
|
|
254
355
|
cullBuild,
|
|
255
356
|
);
|
|
@@ -289,7 +390,7 @@ function createSurfaceBlock(
|
|
|
289
390
|
document,
|
|
290
391
|
cursor,
|
|
291
392
|
counters,
|
|
292
|
-
|
|
393
|
+
formattingContext,
|
|
293
394
|
promoteSecondaryStoryTextBoxes,
|
|
294
395
|
cullBuild,
|
|
295
396
|
);
|
|
@@ -382,7 +483,7 @@ function createSurfaceBlock(
|
|
|
382
483
|
block,
|
|
383
484
|
document,
|
|
384
485
|
cursor,
|
|
385
|
-
|
|
486
|
+
formattingContext,
|
|
386
487
|
promoteSecondaryStoryTextBoxes,
|
|
387
488
|
cullBuild,
|
|
388
489
|
);
|
|
@@ -401,7 +502,7 @@ function createTableBlock(
|
|
|
401
502
|
customXml: number;
|
|
402
503
|
altChunk: number;
|
|
403
504
|
},
|
|
404
|
-
|
|
505
|
+
formattingContext: FormattingContext,
|
|
405
506
|
promoteSecondaryStoryTextBoxes: boolean,
|
|
406
507
|
cullBuild: boolean = false,
|
|
407
508
|
): { block: SurfaceBlockSnapshot; lockedFragmentIds: string[]; nextCursor: number } {
|
|
@@ -409,13 +510,11 @@ function createTableBlock(
|
|
|
409
510
|
let innerCursor = cursor;
|
|
410
511
|
const rows: SurfaceTableRowSnapshot[] = [];
|
|
411
512
|
const rowSpans = computeTableRowSpans(table);
|
|
412
|
-
|
|
413
|
-
//
|
|
414
|
-
//
|
|
415
|
-
|
|
416
|
-
const tableThemeResolver =
|
|
417
|
-
? new ThemeColorResolver(document.subParts.canonicalTheme)
|
|
418
|
-
: undefined;
|
|
513
|
+
// L03 boundary: the table-style cascade + theme resolver come from the
|
|
514
|
+
// projection-pass-scoped formatting context so every block in the pass
|
|
515
|
+
// shares one cached theme resolver.
|
|
516
|
+
const resolvedTable = formattingContext.resolveTable(table);
|
|
517
|
+
const tableThemeResolver = formattingContext.theme;
|
|
419
518
|
|
|
420
519
|
// SOW gap G5 — fold table-level borders into per-cell rendering. The CCEP
|
|
421
520
|
// SOW form tables ship only `w:tblBorders` (single sz=4 on every side + insideH
|
|
@@ -449,7 +548,7 @@ function createTableBlock(
|
|
|
449
548
|
document,
|
|
450
549
|
innerCursor,
|
|
451
550
|
counters,
|
|
452
|
-
|
|
551
|
+
formattingContext,
|
|
453
552
|
promoteSecondaryStoryTextBoxes,
|
|
454
553
|
cullBuild,
|
|
455
554
|
);
|
|
@@ -841,7 +940,7 @@ function createSdtBlock(
|
|
|
841
940
|
customXml: number;
|
|
842
941
|
altChunk: number;
|
|
843
942
|
},
|
|
844
|
-
|
|
943
|
+
formattingContext: FormattingContext,
|
|
845
944
|
promoteSecondaryStoryTextBoxes: boolean,
|
|
846
945
|
cullBuild: boolean = false,
|
|
847
946
|
): { block: SurfaceBlockSnapshot; lockedFragmentIds: string[]; nextCursor: number } {
|
|
@@ -855,7 +954,7 @@ function createSdtBlock(
|
|
|
855
954
|
document,
|
|
856
955
|
innerCursor,
|
|
857
956
|
counters,
|
|
858
|
-
|
|
957
|
+
formattingContext,
|
|
859
958
|
promoteSecondaryStoryTextBoxes,
|
|
860
959
|
cullBuild,
|
|
861
960
|
);
|
|
@@ -891,7 +990,7 @@ function createParagraphBlock(
|
|
|
891
990
|
paragraph: ParagraphNode,
|
|
892
991
|
document: CanonicalDocumentEnvelope,
|
|
893
992
|
start: number,
|
|
894
|
-
|
|
993
|
+
formattingContext: FormattingContext,
|
|
895
994
|
promoteSecondaryStoryTextBoxes: boolean,
|
|
896
995
|
cullBuild: boolean = false,
|
|
897
996
|
): {
|
|
@@ -899,51 +998,40 @@ function createParagraphBlock(
|
|
|
899
998
|
nextCursor: number;
|
|
900
999
|
lockedFragmentIds: string[];
|
|
901
1000
|
} {
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1001
|
+
// L03 boundary: the formatting context owns the theme resolver +
|
|
1002
|
+
// numbering counter + style-catalog walks. Surface-projection reads
|
|
1003
|
+
// `formattingContext.theme` (constructed once per projection pass)
|
|
1004
|
+
// and delegates every formatting semantics question to the context.
|
|
1005
|
+
const themeResolver = formattingContext.theme;
|
|
905
1006
|
// L7 Phase 2.9 — viewport bail. When the paragraph is outside the
|
|
906
1007
|
// viewport, the returned block is discarded (the outer caller in
|
|
907
1008
|
// `createEditorSurfaceSnapshot` replaces it with a placeholder-culled
|
|
908
|
-
// entry and only consumes `nextCursor`).
|
|
909
|
-
//
|
|
910
|
-
//
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
// Task 11: compute cascaded paragraph formatting (expensive — styles-catalog walk).
|
|
924
|
-
const stylesCatalog = document.styles;
|
|
925
|
-
const directParagraphFormatting = cullBuild
|
|
926
|
-
? undefined
|
|
927
|
-
: buildDirectParagraphFormattingFromNode(paragraph);
|
|
1009
|
+
// entry and only consumes `nextCursor`). The context's numbering
|
|
1010
|
+
// counter is always advanced so state stays correct for culled
|
|
1011
|
+
// paragraphs; the paragraph cascade + marker rPr walks are skipped.
|
|
1012
|
+
const effectiveNumbering = formattingContext.resolveEffectiveParagraphNumbering(paragraph);
|
|
1013
|
+
let resolvedNumbering: NumberingResolution | null = null;
|
|
1014
|
+
if (effectiveNumbering) {
|
|
1015
|
+
if (!cullBuild) {
|
|
1016
|
+
resolvedNumbering = formattingContext.resolveParagraphNumbering(paragraph);
|
|
1017
|
+
} else {
|
|
1018
|
+
// Counter-advance only for the culled path.
|
|
1019
|
+
formattingContext.numbering.resolve(effectiveNumbering);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
928
1023
|
const resolvedParagraphFormatting = cullBuild
|
|
929
1024
|
? undefined
|
|
930
|
-
:
|
|
931
|
-
{ styleId: paragraph.styleId, direct: directParagraphFormatting },
|
|
932
|
-
stylesCatalog,
|
|
933
|
-
);
|
|
1025
|
+
: formattingContext.resolveParagraphCascade(paragraph);
|
|
934
1026
|
const surfaceResolvedParagraphFormatting = resolvedParagraphFormatting
|
|
935
1027
|
? resolveSurfaceParagraphFormatting(resolvedParagraphFormatting, themeResolver)
|
|
936
1028
|
: undefined;
|
|
937
1029
|
|
|
938
|
-
// Task 11: compute cascaded marker run formatting (expensive).
|
|
939
1030
|
const markerRunProperties =
|
|
940
1031
|
!cullBuild && effectiveNumbering
|
|
941
|
-
? resolveNumberingMarkerRunFormatting(
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
levelRunProperties: resolvedNumbering?.markerRunProperties,
|
|
945
|
-
},
|
|
946
|
-
stylesCatalog,
|
|
1032
|
+
? formattingContext.resolveNumberingMarkerRunFormatting(
|
|
1033
|
+
paragraph.styleId,
|
|
1034
|
+
resolvedNumbering?.markerRunProperties,
|
|
947
1035
|
)
|
|
948
1036
|
: undefined;
|
|
949
1037
|
|
|
@@ -1009,6 +1097,7 @@ function createParagraphBlock(
|
|
|
1009
1097
|
undefined,
|
|
1010
1098
|
cullBuild,
|
|
1011
1099
|
themeResolver,
|
|
1100
|
+
formattingContext,
|
|
1012
1101
|
);
|
|
1013
1102
|
cursor = result.nextCursor;
|
|
1014
1103
|
lockedFragmentIds.push(...result.lockedFragmentIds);
|
|
@@ -1022,155 +1111,14 @@ function createParagraphBlock(
|
|
|
1022
1111
|
};
|
|
1023
1112
|
}
|
|
1024
1113
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
function resolveEffectiveParagraphNumbering(
|
|
1034
|
-
document: CanonicalDocumentEnvelope,
|
|
1035
|
-
paragraph: ParagraphNode,
|
|
1036
|
-
): ParagraphNode["numbering"] | undefined {
|
|
1037
|
-
if (paragraph.numbering) {
|
|
1038
|
-
return paragraph.numbering;
|
|
1039
|
-
}
|
|
1040
|
-
if (!paragraph.styleId) {
|
|
1041
|
-
return undefined;
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
const paragraphStyles = document.styles?.paragraphs ?? {};
|
|
1045
|
-
const styleChain = collectParagraphStyleChain(document, paragraph.styleId);
|
|
1046
|
-
let styleNumbering:
|
|
1047
|
-
| CanonicalDocumentEnvelope["styles"]["paragraphs"][string]["numbering"]
|
|
1048
|
-
| undefined;
|
|
1049
|
-
for (const styleId of styleChain) {
|
|
1050
|
-
const style = paragraphStyles[styleId];
|
|
1051
|
-
if (style?.numbering) {
|
|
1052
|
-
styleNumbering = style.numbering;
|
|
1053
|
-
break;
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
if (!styleNumbering) {
|
|
1057
|
-
return undefined;
|
|
1058
|
-
}
|
|
1059
|
-
if (styleNumbering.level !== undefined) {
|
|
1060
|
-
return {
|
|
1061
|
-
numberingInstanceId: styleNumbering.numberingInstanceId,
|
|
1062
|
-
level: styleNumbering.level,
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
const resolvedLevel = resolveStyleLinkedNumberingLevel(
|
|
1067
|
-
document,
|
|
1068
|
-
styleNumbering.numberingInstanceId,
|
|
1069
|
-
styleChain,
|
|
1070
|
-
);
|
|
1071
|
-
return resolvedLevel !== undefined
|
|
1072
|
-
? {
|
|
1073
|
-
numberingInstanceId: styleNumbering.numberingInstanceId,
|
|
1074
|
-
level: resolvedLevel,
|
|
1075
|
-
}
|
|
1076
|
-
: undefined;
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
function collectParagraphStyleChain(
|
|
1080
|
-
document: CanonicalDocumentEnvelope,
|
|
1081
|
-
styleId: string,
|
|
1082
|
-
): string[] {
|
|
1083
|
-
const paragraphStyles = document.styles?.paragraphs ?? {};
|
|
1084
|
-
const chain: string[] = [];
|
|
1085
|
-
const visited = new Set<string>();
|
|
1086
|
-
let currentStyleId: string | undefined = styleId;
|
|
1087
|
-
|
|
1088
|
-
while (currentStyleId && !visited.has(currentStyleId)) {
|
|
1089
|
-
visited.add(currentStyleId);
|
|
1090
|
-
chain.push(currentStyleId);
|
|
1091
|
-
currentStyleId = paragraphStyles[currentStyleId]?.basedOn;
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
return chain;
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
function resolveStyleLinkedNumberingLevel(
|
|
1098
|
-
document: CanonicalDocumentEnvelope,
|
|
1099
|
-
numberingInstanceId: string,
|
|
1100
|
-
styleChain: readonly string[],
|
|
1101
|
-
): number | undefined {
|
|
1102
|
-
const instance = document.numbering.instances[numberingInstanceId];
|
|
1103
|
-
if (!instance) {
|
|
1104
|
-
return undefined;
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
for (const styleId of styleChain) {
|
|
1108
|
-
const overrideMatch = instance.overrides.find(
|
|
1109
|
-
(override) => override.levelDefinition?.paragraphStyleId === styleId,
|
|
1110
|
-
);
|
|
1111
|
-
if (overrideMatch) {
|
|
1112
|
-
return overrideMatch.level;
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
const abstractDefinition = document.numbering.abstractDefinitions[instance.abstractNumberingId];
|
|
1117
|
-
if (!abstractDefinition) {
|
|
1118
|
-
return undefined;
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
for (const styleId of styleChain) {
|
|
1122
|
-
const levelMatch = abstractDefinition.levels.find(
|
|
1123
|
-
(levelDefinition) => levelDefinition.paragraphStyleId === styleId,
|
|
1124
|
-
);
|
|
1125
|
-
if (levelMatch) {
|
|
1126
|
-
return levelMatch.level;
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
return undefined;
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
function buildDirectRunFormattingFromMarks(
|
|
1134
|
-
marks: SurfaceTextMark[] | undefined,
|
|
1135
|
-
markAttrs: {
|
|
1136
|
-
backgroundColor?: string;
|
|
1137
|
-
charSpacing?: number;
|
|
1138
|
-
kerning?: number;
|
|
1139
|
-
textFill?: string;
|
|
1140
|
-
fontFamily?: string;
|
|
1141
|
-
fontSize?: number;
|
|
1142
|
-
textColor?: string;
|
|
1143
|
-
} | undefined,
|
|
1144
|
-
): CanonicalRunFormatting | undefined {
|
|
1145
|
-
const direct: CanonicalRunFormatting = {};
|
|
1146
|
-
if (marks) {
|
|
1147
|
-
if (marks.includes("bold")) direct.bold = true;
|
|
1148
|
-
if (marks.includes("italic")) direct.italic = true;
|
|
1149
|
-
if (marks.includes("underline")) direct.underline = "single";
|
|
1150
|
-
if (marks.includes("strikethrough")) direct.strikethrough = true;
|
|
1151
|
-
if (marks.includes("doubleStrikethrough")) direct.doubleStrikethrough = true;
|
|
1152
|
-
if (marks.includes("vanish")) direct.vanish = true;
|
|
1153
|
-
if (marks.includes("allCaps")) direct.allCaps = true;
|
|
1154
|
-
if (marks.includes("smallCaps")) direct.smallCaps = true;
|
|
1155
|
-
}
|
|
1156
|
-
if (markAttrs) {
|
|
1157
|
-
if (markAttrs.fontFamily) {
|
|
1158
|
-
direct.fontFamily = markAttrs.fontFamily;
|
|
1159
|
-
direct.fontFamilyAscii = markAttrs.fontFamily;
|
|
1160
|
-
}
|
|
1161
|
-
if (typeof markAttrs.fontSize === "number") {
|
|
1162
|
-
// markAttrs.fontSize is already in half-points from cloneMarks
|
|
1163
|
-
direct.fontSizeHalfPoints = markAttrs.fontSize;
|
|
1164
|
-
}
|
|
1165
|
-
if (markAttrs.textColor) {
|
|
1166
|
-
direct.colorHex = markAttrs.textColor.replace(/^#/, "");
|
|
1167
|
-
}
|
|
1168
|
-
if (markAttrs.backgroundColor) {
|
|
1169
|
-
direct.highlight = markAttrs.backgroundColor;
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
return Object.keys(direct).length > 0 ? direct : undefined;
|
|
1173
|
-
}
|
|
1114
|
+
// `advanceNumberingCounterOnly`, `resolveEffectiveParagraphNumbering`,
|
|
1115
|
+
// `collectParagraphStyleChain`, `resolveStyleLinkedNumberingLevel`, and
|
|
1116
|
+
// `buildDirectRunFormattingFromMarks` were removed from surface-projection
|
|
1117
|
+
// in the Layer-03 production-boundary migration. Their behavior now lives
|
|
1118
|
+
// on `FormattingContext` (`resolveEffectiveParagraphNumbering`,
|
|
1119
|
+
// `resolveParagraphNumbering`, `resolveRunFromMarks`, and the internal
|
|
1120
|
+
// `resolveStyleLinkedNumberingLevel` in `formatting-context.ts`). Surface-
|
|
1121
|
+
// projection owns projection concerns only.
|
|
1174
1122
|
|
|
1175
1123
|
function appendInlineSegments(
|
|
1176
1124
|
paragraph: ParagraphAccumulator,
|
|
@@ -1181,58 +1129,70 @@ function appendInlineSegments(
|
|
|
1181
1129
|
hyperlinkHref?: string,
|
|
1182
1130
|
cullBuild: boolean = false,
|
|
1183
1131
|
themeResolver?: ThemeColorResolver,
|
|
1132
|
+
formattingContext?: FormattingContext,
|
|
1184
1133
|
): { nextCursor: number; lockedFragmentIds: string[] } {
|
|
1185
1134
|
switch (node.type) {
|
|
1186
1135
|
case "text": {
|
|
1187
1136
|
const cloned = node.marks ? cloneMarks(node.marks) : { marks: [] as SurfaceTextMark[] };
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
//
|
|
1191
|
-
// placeholder path does not read `resolvedRunFormatting`.
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
//
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1137
|
+
const segmentFrom = start;
|
|
1138
|
+
const segmentTo = start + Array.from(node.text).length;
|
|
1139
|
+
// L7 Phase 2.9 — skip the run-cascade walk when the block will be
|
|
1140
|
+
// culled. The placeholder path does not read `resolvedRunFormatting`.
|
|
1141
|
+
//
|
|
1142
|
+
// L03 boundary: the run cascade + theme concretization + hyperlink
|
|
1143
|
+
// four-tier fallback + F6 revision-display posture ALL flow through
|
|
1144
|
+
// `formattingContext.resolveRunFromMarks`. Surface-projection hands
|
|
1145
|
+
// the context the projected marks + the hyperlink-context flag +
|
|
1146
|
+
// the attached revision (looked up via L03's per-context revision
|
|
1147
|
+
// index); the context owns every cascade decision.
|
|
1148
|
+
const attachedRevision =
|
|
1149
|
+
!cullBuild && formattingContext
|
|
1150
|
+
? formattingContext.findRevisionAtRange(segmentFrom, segmentTo)
|
|
1151
|
+
: undefined;
|
|
1152
|
+
const resolvedRunFormatting =
|
|
1153
|
+
cullBuild || !formattingContext
|
|
1154
|
+
? {}
|
|
1155
|
+
: formattingContext.resolveRunFromMarks({
|
|
1156
|
+
paragraphStyleId: paragraph.styleId,
|
|
1157
|
+
marks: {
|
|
1158
|
+
marks: cloned.marks.length > 0 ? cloned.marks : undefined,
|
|
1159
|
+
markAttrs: cloned.markAttrs,
|
|
1160
|
+
},
|
|
1161
|
+
inHyperlink: hyperlinkHref !== undefined,
|
|
1162
|
+
...(attachedRevision ? { revision: attachedRevision } : {}),
|
|
1163
|
+
});
|
|
1164
|
+
// Pull the F6 revision-display flags off the resolved record so
|
|
1165
|
+
// they ride the surface segment for render consumers. Research
|
|
1166
|
+
// doc §F6: the surface is the single place that maps segment →
|
|
1167
|
+
// revision; render reads `segment.revisionDisplay` directly.
|
|
1168
|
+
const revisionDisplay =
|
|
1169
|
+
(resolvedRunFormatting as { revisionDisplay?: unknown }).revisionDisplay;
|
|
1170
|
+
// Strip `revisionDisplay` from the object that lands on
|
|
1171
|
+
// `resolvedRunFormatting` — the snapshot surface carries it as a
|
|
1172
|
+
// top-level segment field (`segment.revisionDisplay`), not as a
|
|
1173
|
+
// sub-field of the cascade. This keeps legacy consumers of
|
|
1174
|
+
// `resolvedRunFormatting` stable while exposing the flags at a
|
|
1175
|
+
// discoverable site.
|
|
1176
|
+
const resolvedRunFormattingWithoutRevisionDisplay:
|
|
1177
|
+
Record<string, unknown> = { ...(resolvedRunFormatting as Record<string, unknown>) };
|
|
1178
|
+
delete resolvedRunFormattingWithoutRevisionDisplay.revisionDisplay;
|
|
1224
1179
|
paragraph.segments.push({
|
|
1225
1180
|
segmentId: `${paragraph.blockId}-segment-${paragraph.segments.length}`,
|
|
1226
1181
|
kind: "text",
|
|
1227
|
-
from:
|
|
1228
|
-
to:
|
|
1182
|
+
from: segmentFrom,
|
|
1183
|
+
to: segmentTo,
|
|
1229
1184
|
text: node.text,
|
|
1230
1185
|
...(cloned.marks.length > 0 ? { marks: cloned.marks } : {}),
|
|
1231
1186
|
...(cloned.markAttrs ? { markAttrs: cloned.markAttrs } : {}),
|
|
1232
|
-
...(Object.keys(
|
|
1187
|
+
...(Object.keys(resolvedRunFormattingWithoutRevisionDisplay).length > 0
|
|
1188
|
+
? { resolvedRunFormatting: resolvedRunFormattingWithoutRevisionDisplay as CanonicalRunFormatting }
|
|
1189
|
+
: {}),
|
|
1233
1190
|
...(hyperlinkHref ? { hyperlinkHref } : {}),
|
|
1191
|
+
...(revisionDisplay
|
|
1192
|
+
? { revisionDisplay: revisionDisplay as Extract<SurfaceInlineSegment, { kind: "text" }>["revisionDisplay"] }
|
|
1193
|
+
: {}),
|
|
1234
1194
|
});
|
|
1235
|
-
return { nextCursor:
|
|
1195
|
+
return { nextCursor: segmentTo, lockedFragmentIds: [] };
|
|
1236
1196
|
}
|
|
1237
1197
|
case "tab":
|
|
1238
1198
|
paragraph.segments.push({
|
|
@@ -1264,6 +1224,7 @@ function appendInlineSegments(
|
|
|
1264
1224
|
node.href,
|
|
1265
1225
|
cullBuild,
|
|
1266
1226
|
themeResolver,
|
|
1227
|
+
formattingContext,
|
|
1267
1228
|
);
|
|
1268
1229
|
cursor = result.nextCursor;
|
|
1269
1230
|
}
|
|
@@ -1479,6 +1440,7 @@ function appendInlineSegments(
|
|
|
1479
1440
|
refHyperlinkHref ?? hyperlinkHref,
|
|
1480
1441
|
cullBuild,
|
|
1481
1442
|
themeResolver,
|
|
1443
|
+
formattingContext,
|
|
1482
1444
|
);
|
|
1483
1445
|
cursor = result.nextCursor;
|
|
1484
1446
|
lockedIds.push(...result.lockedFragmentIds);
|
|
@@ -1844,7 +1806,7 @@ function createPlainText(
|
|
|
1844
1806
|
|
|
1845
1807
|
function createSecondaryStorySurfaces(
|
|
1846
1808
|
document: CanonicalDocumentEnvelope,
|
|
1847
|
-
|
|
1809
|
+
formattingContext: FormattingContext,
|
|
1848
1810
|
): SecondaryStorySurface[] {
|
|
1849
1811
|
const surfaces: SecondaryStorySurface[] = [];
|
|
1850
1812
|
const subParts = document.subParts;
|
|
@@ -1876,7 +1838,7 @@ function createSecondaryStorySurfaces(
|
|
|
1876
1838
|
`Header · ${headerVariant.variant}`,
|
|
1877
1839
|
header.blocks,
|
|
1878
1840
|
document,
|
|
1879
|
-
|
|
1841
|
+
formattingContext,
|
|
1880
1842
|
),
|
|
1881
1843
|
);
|
|
1882
1844
|
}
|
|
@@ -1904,7 +1866,7 @@ function createSecondaryStorySurfaces(
|
|
|
1904
1866
|
`Footer · ${footerVariant.variant}`,
|
|
1905
1867
|
footer.blocks,
|
|
1906
1868
|
document,
|
|
1907
|
-
|
|
1869
|
+
formattingContext,
|
|
1908
1870
|
),
|
|
1909
1871
|
);
|
|
1910
1872
|
}
|
|
@@ -1913,13 +1875,13 @@ function createSecondaryStorySurfaces(
|
|
|
1913
1875
|
const footnotes = Object.values(subParts.footnoteCollection?.footnotes ?? {}).sort(compareNoteIds);
|
|
1914
1876
|
for (const note of footnotes) {
|
|
1915
1877
|
const target: EditorStoryTarget = { kind: "footnote", noteId: note.noteId };
|
|
1916
|
-
surfaces.push(createStorySurface(target, `Footnote ${note.noteId}`, note.blocks, document,
|
|
1878
|
+
surfaces.push(createStorySurface(target, `Footnote ${note.noteId}`, note.blocks, document, formattingContext));
|
|
1917
1879
|
}
|
|
1918
1880
|
|
|
1919
1881
|
const endnotes = Object.values(subParts.footnoteCollection?.endnotes ?? {}).sort(compareNoteIds);
|
|
1920
1882
|
for (const note of endnotes) {
|
|
1921
1883
|
const target: EditorStoryTarget = { kind: "endnote", noteId: note.noteId };
|
|
1922
|
-
surfaces.push(createStorySurface(target, `Endnote ${note.noteId}`, note.blocks, document,
|
|
1884
|
+
surfaces.push(createStorySurface(target, `Endnote ${note.noteId}`, note.blocks, document, formattingContext));
|
|
1923
1885
|
}
|
|
1924
1886
|
|
|
1925
1887
|
return surfaces;
|
|
@@ -1930,7 +1892,7 @@ function createStorySurface(
|
|
|
1930
1892
|
label: string,
|
|
1931
1893
|
blocks: readonly BlockNode[],
|
|
1932
1894
|
document: CanonicalDocumentEnvelope,
|
|
1933
|
-
|
|
1895
|
+
formattingContext: FormattingContext,
|
|
1934
1896
|
): SecondaryStorySurface {
|
|
1935
1897
|
const surfaceBlocks: SurfaceBlockSnapshot[] = [];
|
|
1936
1898
|
let cursor = 0;
|
|
@@ -1949,7 +1911,7 @@ function createStorySurface(
|
|
|
1949
1911
|
document,
|
|
1950
1912
|
cursor,
|
|
1951
1913
|
counters,
|
|
1952
|
-
|
|
1914
|
+
formattingContext,
|
|
1953
1915
|
true,
|
|
1954
1916
|
);
|
|
1955
1917
|
surfaceBlocks.push(surfaceBlock.block);
|
|
@@ -2167,7 +2129,7 @@ function buildDirectParagraphFormattingFromNode(
|
|
|
2167
2129
|
}
|
|
2168
2130
|
|
|
2169
2131
|
function toSurfaceResolvedNumbering(
|
|
2170
|
-
numbering:
|
|
2132
|
+
numbering: NumberingResolution,
|
|
2171
2133
|
): NonNullable<Extract<SurfaceBlockSnapshot, { kind: "paragraph" }>["resolvedNumbering"]> {
|
|
2172
2134
|
return {
|
|
2173
2135
|
level: numbering.level,
|
|
@@ -2508,3 +2470,11 @@ function createFloatingImageDetail(
|
|
|
2508
2470
|
function hasMediaItem(media: MediaCatalog, mediaId: string): boolean {
|
|
2509
2471
|
return mediaId in media.items;
|
|
2510
2472
|
}
|
|
2473
|
+
|
|
2474
|
+
// Revision range index was moved into `FormattingContext` itself in the
|
|
2475
|
+
// §A production-boundary pass. Surface-projection now calls
|
|
2476
|
+
// `formattingContext.findRevisionAtRange(from, to)` at each text segment
|
|
2477
|
+
// push-point and hands the attached revision to
|
|
2478
|
+
// `formattingContext.resolveRunFromMarks({revision})` (which invokes
|
|
2479
|
+
// `applyRevisionDisplay` per F6). The result flows onto
|
|
2480
|
+
// `segment.revisionDisplay` for render consumers.
|