@beyondwork/docx-react-component 1.0.67 → 1.0.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -932
- package/package.json +26 -27
- package/src/api/anchor-conversion.ts +43 -0
- package/src/api/editor-state-types.ts +2 -1
- package/src/api/public-types.ts +504 -101
- package/src/api/session-state.ts +4 -0
- package/src/api/v3/README.md +91 -0
- package/src/api/v3/_create.ts +146 -0
- package/src/api/v3/_layer-metadata.ts +362 -0
- package/src/api/v3/_mocks.ts +84 -0
- package/src/api/v3/_runtime-handle.ts +162 -0
- package/src/api/v3/_ux-response.ts +73 -0
- package/src/api/v3/ai/_metadata-audit.ts +225 -0
- package/src/api/v3/ai/attach.ts +235 -0
- package/src/api/v3/ai/bundle.ts +132 -0
- package/src/api/v3/ai/explain.ts +144 -0
- package/src/api/v3/ai/export.ts +54 -0
- package/src/api/v3/ai/inspect.ts +118 -0
- package/src/api/v3/ai/policy.ts +77 -0
- package/src/api/v3/ai/replacement.ts +341 -0
- package/src/api/v3/ai/resolve.ts +133 -0
- package/src/api/v3/index.ts +79 -0
- package/src/api/v3/runtime/chart.ts +310 -0
- package/src/api/v3/runtime/clipboard.ts +81 -0
- package/src/api/v3/runtime/collab.ts +331 -0
- package/src/api/v3/runtime/content.ts +236 -0
- package/src/api/v3/runtime/document.ts +282 -0
- package/src/api/v3/runtime/formatting.ts +186 -0
- package/src/api/v3/runtime/geometry.ts +349 -0
- package/src/api/v3/runtime/layout.ts +108 -0
- package/src/api/v3/runtime/review.ts +129 -0
- package/src/api/v3/runtime/search.ts +74 -0
- package/src/api/v3/runtime/table.ts +63 -0
- package/src/api/v3/runtime/workflow.ts +434 -0
- package/src/api/v3/ui/_context.ts +86 -0
- package/src/api/v3/ui/_create.ts +65 -0
- package/src/api/v3/ui/_types.ts +520 -0
- package/src/api/v3/ui/chrome-composition.ts +342 -0
- package/src/{ui-tailwind/chrome → api/v3/ui}/chrome-preset-model.ts +11 -1
- package/src/api/v3/ui/chrome.ts +476 -0
- package/src/api/v3/ui/debug.ts +124 -0
- package/src/api/v3/ui/index.ts +64 -0
- package/src/api/v3/ui/overlays-visibility.ts +170 -0
- package/src/api/v3/ui/overlays.ts +427 -0
- package/src/api/v3/ui/scope.ts +71 -0
- package/src/api/v3/ui/session.ts +100 -0
- package/src/api/v3/ui/surface.ts +170 -0
- package/src/api/v3/ui/viewport.ts +303 -0
- package/src/core/commands/index.ts +28 -6
- package/src/core/commands/list-commands.ts +3 -2
- package/src/core/commands/section-layout-commands.ts +9 -8
- package/src/core/schema/text-schema.ts +16 -0
- package/src/core/selection/mapping.ts +33 -72
- package/src/core/state/editor-state.ts +96 -189
- package/src/index.ts +23 -4
- package/src/io/chart-preview-resolver.ts +1 -1
- package/src/io/docx-session.ts +36 -4797
- package/src/io/export/build-app-properties-xml.ts +1 -1
- package/src/io/export/serialize-comments.ts +1 -1
- package/src/io/export/serialize-headers-footers.ts +6 -1
- package/src/io/export/serialize-main-document.ts +45 -0
- package/src/io/export/serialize-run-formatting.ts +17 -2
- package/src/io/export/twip.ts +1 -1
- package/src/io/normalize/normalize-text.ts +27 -20
- package/src/io/ooxml/chart/parse-series.ts +1 -1
- package/src/io/ooxml/chart/resolve-color.ts +2 -2
- package/src/io/ooxml/chart/types.ts +1 -1
- package/src/io/ooxml/classify-embedding.ts +83 -33
- package/src/io/ooxml/parse-fill.ts +1 -1
- package/src/io/ooxml/parse-main-document.ts +71 -1
- package/src/io/ooxml/parse-object.ts +14 -10
- package/src/io/ooxml/parse-run-formatting.ts +47 -1
- package/src/io/ooxml/property-grab-bag.ts +2 -2
- package/src/io/ooxml/units.ts +11 -0
- package/src/io/ooxml/workflow-payload.ts +282 -7
- package/src/model/anchor.ts +85 -0
- package/src/model/canonical-document.ts +351 -15
- package/src/model/chart-types.ts +1 -1
- package/src/model/layout/index.ts +83 -0
- package/src/model/layout/page-graph-types.ts +181 -0
- package/src/model/layout/page-layout-snapshot.ts +105 -0
- package/src/model/layout/resolved-layout-types.ts +47 -0
- package/src/model/layout/runtime-page-graph-types.ts +102 -0
- package/src/model/paragraph-scope-ids.ts +72 -0
- package/src/model/review/comment-types.ts +112 -0
- package/src/model/review/index.ts +2 -0
- package/src/model/review/revision-types.ts +215 -0
- package/src/model/snapshot.ts +32 -0
- package/src/review/store/comment-store.ts +21 -47
- package/src/review/store/revision-types.ts +40 -198
- package/src/runtime/collab/base-doc-fingerprint.ts +6 -1
- package/src/runtime/collab/runtime-collab-sync.ts +13 -3
- package/src/runtime/collab-session.ts +1 -1
- package/src/runtime/debug/build-debug-inspector-snapshot.ts +686 -0
- package/src/runtime/debug/event-ring-buffer.ts +64 -0
- package/src/runtime/debug/probability-sampler.ts +18 -0
- package/src/runtime/debug/runtime-debug-facet.ts +67 -0
- package/src/runtime/debug/stage-tokens.ts +31 -0
- package/src/runtime/debug/telemetry-bus.ts +271 -0
- package/src/runtime/debug/types.ts +275 -0
- package/src/runtime/debug/wrap-ref-for-telemetry.ts +118 -0
- package/src/runtime/document-layout.ts +8 -6
- package/src/runtime/document-runtime.ts +843 -1141
- package/src/runtime/document-search.ts +1 -1
- package/src/runtime/edit-ops/index.ts +1 -1
- package/src/runtime/external-send-runtime.ts +1 -1
- package/src/runtime/formatting/document-lookup.ts +235 -0
- package/src/runtime/formatting/field/registry.ts +41 -0
- package/src/runtime/{field-resolver.ts → formatting/field/resolver.ts} +27 -2
- package/src/runtime/formatting/font-resolution.ts +83 -0
- package/src/runtime/formatting/formatting-context.ts +903 -0
- package/src/runtime/formatting/formatting-types.ts +157 -0
- package/src/runtime/{hyperlink-color-resolver.ts → formatting/hyperlink-color.ts} +2 -2
- package/src/runtime/formatting/index.ts +125 -0
- package/src/runtime/{resolved-numbering-geometry.ts → formatting/numbering/geometry.ts} +1 -1
- package/src/runtime/{numbering-prefix.ts → formatting/numbering/prefix.ts} +170 -3
- package/src/runtime/formatting/paragraph-style-resolver.ts +92 -0
- package/src/runtime/formatting/projector.ts +75 -0
- package/src/runtime/formatting/resolve-effective.ts +407 -0
- package/src/runtime/formatting/revision-display.ts +105 -0
- package/src/runtime/{paragraph-style-resolver.ts → formatting/style-cascade.ts} +84 -141
- package/src/runtime/{table-style-resolver.ts → formatting/table-style-resolver.ts} +1 -1
- package/src/runtime/formatting/telemetry-bridge.ts +106 -0
- package/src/runtime/{theme-color-resolver.ts → formatting/theme-color.ts} +2 -30
- package/src/runtime/geometry/caret-geometry.ts +164 -0
- package/src/runtime/geometry/geometry-facet.ts +364 -0
- package/src/runtime/geometry/geometry-types.ts +256 -0
- package/src/runtime/geometry/hit-test.ts +125 -0
- package/src/runtime/geometry/index.ts +71 -0
- package/src/runtime/geometry/inert-geometry-facet.ts +43 -0
- package/src/runtime/geometry/invalidation.ts +35 -0
- package/src/runtime/geometry/object-handles.ts +77 -0
- package/src/runtime/geometry/overlay-rects.ts +85 -0
- package/src/runtime/geometry/project-anchors.ts +100 -0
- package/src/runtime/geometry/project-fragments.ts +216 -0
- package/src/runtime/geometry/projector.ts +129 -0
- package/src/runtime/geometry/replacement-envelope.ts +130 -0
- package/src/runtime/geometry/viewport.ts +218 -0
- package/src/runtime/layout/compat-input-ledger.ts +211 -0
- package/src/runtime/layout/index.ts +6 -1
- package/src/runtime/layout/inert-layout-facet.ts +12 -7
- package/src/runtime/layout/layout-engine-instance.ts +189 -11
- package/src/runtime/layout/layout-engine-version.ts +450 -1
- package/src/runtime/layout/layout-facet-types.ts +60 -0
- package/src/runtime/layout/layout-measurement-provider.ts +13 -0
- package/src/runtime/layout/measurement-backend-canvas.ts +14 -2
- package/src/runtime/layout/measurement-backend-empirical.ts +23 -4
- package/src/runtime/layout/page-graph.ts +62 -209
- package/src/runtime/layout/page-story-resolver.ts +7 -12
- package/src/runtime/layout/paginated-layout-engine.ts +186 -11
- package/src/runtime/layout/project-block-fragments.ts +11 -0
- package/src/runtime/layout/projector.ts +90 -0
- package/src/runtime/layout/public-facet.ts +187 -442
- package/src/runtime/layout/resolved-formatting-state.ts +158 -26
- package/src/runtime/layout/table-render-plan.ts +1 -1
- package/src/runtime/prerender/cache-envelope.ts +6 -1
- package/src/runtime/prerender/prerender-document.ts +18 -23
- package/src/runtime/render/decoration-resolver.ts +1 -1
- package/src/runtime/render/render-frame-types.ts +20 -0
- package/src/runtime/render/render-kernel.ts +94 -25
- package/src/runtime/scopes/_formatting-seam.ts +262 -0
- package/src/runtime/scopes/_scope-dependencies.ts +49 -0
- package/src/runtime/scopes/action-validation.ts +356 -0
- package/src/runtime/scopes/attach-explanation.ts +102 -0
- package/src/runtime/scopes/audit-bundle.ts +71 -0
- package/src/runtime/scopes/compile-scope-bundle.ts +163 -0
- package/src/runtime/scopes/compile-scope.ts +262 -0
- package/src/runtime/scopes/compiler-service.ts +431 -0
- package/src/runtime/scopes/create-issue.ts +107 -0
- package/src/runtime/scopes/enumerate-scopes.ts +543 -0
- package/src/runtime/scopes/evidence.ts +233 -0
- package/src/runtime/scopes/index.ts +150 -0
- package/src/runtime/scopes/position-map.ts +214 -0
- package/src/runtime/scopes/preservation-boundary.ts +91 -0
- package/src/runtime/scopes/projector.ts +49 -0
- package/src/runtime/scopes/replaceability.ts +87 -0
- package/src/runtime/scopes/replacement/apply.ts +228 -0
- package/src/runtime/scopes/replacement/compile.ts +59 -0
- package/src/runtime/scopes/replacement/propose.ts +42 -0
- package/src/runtime/scopes/resolve-reference.ts +347 -0
- package/src/runtime/scopes/review-bundle.ts +141 -0
- package/src/runtime/scopes/scope-kinds/_paragraph-text.ts +57 -0
- package/src/runtime/scopes/scope-kinds/_table-text.ts +42 -0
- package/src/runtime/scopes/scope-kinds/comment-thread.ts +59 -0
- package/src/runtime/scopes/scope-kinds/field.ts +65 -0
- package/src/runtime/scopes/scope-kinds/heading.ts +84 -0
- package/src/runtime/scopes/scope-kinds/list-item.ts +77 -0
- package/src/runtime/scopes/scope-kinds/paragraph.ts +182 -0
- package/src/runtime/scopes/scope-kinds/revision.ts +62 -0
- package/src/runtime/scopes/scope-kinds/table-cell.ts +57 -0
- package/src/runtime/scopes/scope-kinds/table-row.ts +61 -0
- package/src/runtime/scopes/scope-kinds/table.ts +55 -0
- package/src/runtime/scopes/scope-range.ts +208 -0
- package/src/runtime/scopes/semantic-scope-types.ts +454 -0
- package/src/runtime/scopes/workflow-overlap.ts +92 -0
- package/src/runtime/selection/index.ts +1 -1
- package/src/runtime/structure-ops/fragment-insert.ts +1 -1
- package/src/runtime/structure-ops/index.ts +1 -1
- package/src/runtime/surface-projection.ts +232 -262
- package/src/runtime/units.ts +4 -2
- package/src/runtime/workflow/coordinator.ts +1348 -0
- package/src/runtime/workflow/derived-scope-resolver.ts +125 -0
- package/src/runtime/workflow/index.ts +25 -0
- package/src/runtime/workflow/markup-mode-policy.ts +98 -0
- package/src/runtime/{workflow-markup.ts → workflow/markup.ts} +6 -6
- package/src/runtime/workflow/metadata-persistence.ts +306 -0
- package/src/runtime/workflow/metadata-writer.ts +123 -0
- package/src/runtime/workflow/overlay-store.ts +690 -0
- package/src/runtime/workflow/projector.ts +127 -0
- package/src/runtime/{query-scopes.ts → workflow/query-scopes.ts} +3 -3
- package/src/runtime/{workflow-rail-segments.ts → workflow/rail/compose.ts} +60 -165
- package/src/runtime/workflow/rail/types.ts +198 -0
- package/src/runtime/workflow/scope-rail-composer.ts +39 -0
- package/src/runtime/{scope-resolver.ts → workflow/scope-resolver.ts} +3 -3
- package/src/runtime/workflow/scope-writer.ts +188 -0
- package/src/runtime/{tamper-gate.ts → workflow/tamper-gate.ts} +1 -1
- package/src/runtime/workflow/visibility-policy.ts +129 -0
- package/src/session/_sync-legacy.ts +66 -0
- package/src/session/export/embedded-reconstitute.ts +104 -0
- package/src/session/export/export-diagnostics.ts +85 -0
- package/src/session/export/export-validation.ts +110 -0
- package/src/session/export/index.ts +34 -0
- package/src/session/export/preservation-reattach.ts +30 -0
- package/src/session/export/serialize-dispatch.ts +165 -0
- package/src/session/export/stateful-export-pipeline.ts +432 -0
- package/src/session/export/stateful-export.ts +684 -0
- package/src/session/import/canonical-assembly.ts +227 -0
- package/src/session/import/diagnostics-session.ts +54 -0
- package/src/session/import/embedded-discovery.ts +225 -0
- package/src/session/import/embedded-offload.ts +337 -0
- package/src/session/import/import-diagnostics.ts +69 -0
- package/src/session/import/loader-types.ts +313 -0
- package/src/session/import/loader.ts +1834 -0
- package/src/session/import/normalize.ts +195 -0
- package/src/session/import/package-parts.ts +217 -0
- package/src/session/import/package-read.ts +195 -0
- package/src/session/import/parse-orchestration.ts +105 -0
- package/src/session/import/part-constants.ts +70 -0
- package/src/session/import/part-discovery.ts +94 -0
- package/src/session/import/preservation-index.ts +46 -0
- package/src/{runtime/read-only-diagnostics-runtime.ts → session/import/read-only-diagnostics.ts} +24 -3
- package/src/session/import/review-import.ts +508 -0
- package/src/session/import/styles-consolidation.ts +281 -0
- package/src/session/import/workflow-scope-import.ts +256 -0
- package/src/session/index.ts +37 -0
- package/src/session/session-state.ts +69 -0
- package/src/session/session.ts +532 -0
- package/src/session/shared/protection.ts +228 -0
- package/src/session/shared/session-utils.ts +82 -0
- package/src/session/types.ts +499 -0
- package/src/shell/chart-snapshots.ts +96 -0
- package/src/shell/media-previews.ts +85 -0
- package/src/shell/overlay-anchor-bridge.ts +53 -0
- package/src/shell/paste-adapter.ts +23 -0
- package/src/shell/ref-commands.ts +1697 -0
- package/src/shell/ref-utilities.ts +48 -0
- package/src/shell/search.ts +51 -0
- package/src/{ui/editor-runtime-boundary.ts → shell/session-bootstrap.ts} +243 -67
- package/src/shell/ui-subscriber-channels.ts +81 -0
- package/src/shell/use-collab-sync.ts +116 -0
- package/src/ui/WordReviewEditor.tsx +496 -2051
- package/src/ui/editor-shell-view.tsx +30 -1
- package/src/ui/editor-surface-controller.tsx +49 -1
- package/src/ui/headless/revision-decoration-model.ts +83 -0
- package/src/{ui-tailwind/chrome → ui/headless}/role-action-sets.ts +1 -1
- package/src/ui/headless/scoped-chrome-policy.ts +2 -2
- package/src/ui/headless/selection-tool-context.ts +1 -1
- package/src/ui/headless/selection-tool-resolver.ts +1 -1
- package/src/ui/runtime-shortcut-dispatch.ts +46 -1
- package/src/ui/ui-controller-factory.ts +221 -0
- package/src/ui-tailwind/chart/ChartSurface.tsx +2 -2
- package/src/ui-tailwind/chart/layout/legend-layout.ts +1 -1
- package/src/ui-tailwind/chart/layout/plot-area.ts +2 -2
- package/src/ui-tailwind/chart/layout/title-layout.ts +1 -1
- package/src/ui-tailwind/chart/render/area.tsx +3 -3
- package/src/ui-tailwind/chart/render/bar-column.tsx +3 -3
- package/src/ui-tailwind/chart/render/bubble.tsx +3 -3
- package/src/ui-tailwind/chart/render/combo.tsx +2 -2
- package/src/ui-tailwind/chart/render/data-labels.tsx +2 -2
- package/src/ui-tailwind/chart/render/font-metrics.ts +2 -2
- package/src/ui-tailwind/chart/render/line.tsx +3 -3
- package/src/ui-tailwind/chart/render/pie.tsx +6 -6
- package/src/ui-tailwind/chart/render/scatter.tsx +3 -3
- package/src/ui-tailwind/chart/render/svg-primitives.ts +3 -3
- package/src/ui-tailwind/chart/render/unsupported.tsx +2 -2
- package/src/ui-tailwind/chrome/build-context-menu-entries.ts +88 -0
- package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +1 -1
- package/src/ui-tailwind/chrome/collab-send-to-supplier-button.tsx +1 -1
- package/src/ui-tailwind/chrome/collab-tamper-banner.tsx +1 -1
- package/src/ui-tailwind/chrome/collab-top-nav-container.tsx +1 -1
- package/src/ui-tailwind/chrome/editor-action-registry.ts +553 -0
- package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +182 -0
- package/src/ui-tailwind/chrome/local-surface-arbiter.ts +534 -0
- package/src/ui-tailwind/chrome/resolve-target-kind.ts +226 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +38 -4
- package/src/ui-tailwind/chrome/tw-context-band.tsx +125 -0
- package/src/ui-tailwind/chrome/tw-context-menu-portal.tsx +248 -0
- package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +42 -1
- package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +8 -7
- package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +38 -4
- package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +104 -6
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +66 -7
- package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +54 -8
- package/src/ui-tailwind/chrome/tw-shortcut-hint.tsx +7 -1
- package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +33 -0
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +78 -1
- package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +16 -8
- package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +276 -0
- package/src/ui-tailwind/chrome/use-context-menu-controller.ts +201 -0
- package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +22 -4
- package/src/ui-tailwind/chrome-overlay/tw-comment-balloon-layer.tsx +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-locked-block-layer.tsx +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +11 -5
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +197 -3
- package/src/ui-tailwind/chrome-overlay/tw-revision-margin-bar-layer.tsx +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +35 -6
- package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +24 -16
- package/src/ui-tailwind/chrome-overlay/tw-table-continuation-header.tsx +1 -1
- package/src/ui-tailwind/debug/README.md +57 -0
- package/src/ui-tailwind/debug/index.ts +3 -0
- package/src/ui-tailwind/debug/tw-debug-overlay.tsx +186 -0
- package/src/ui-tailwind/debug/tw-debug-presentation.tsx +80 -0
- package/src/ui-tailwind/debug/tw-debug-top-bar.tsx +83 -0
- package/src/ui-tailwind/editor-surface/chart-node-view.tsx +2 -2
- package/src/ui-tailwind/editor-surface/float-wrap-resolver.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +135 -10
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +40 -13
- package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-schema.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +3 -3
- package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +1 -1
- package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +2 -2
- package/src/ui-tailwind/editor-surface/scroll-anchor.ts +91 -9
- package/src/ui-tailwind/editor-surface/shape-renderer.ts +1 -1
- package/src/ui-tailwind/editor-surface/surface-layer.ts +1 -1
- package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +1 -1
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +23 -6
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +132 -22
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +1 -1
- package/src/ui-tailwind/index.ts +0 -5
- package/src/ui-tailwind/overlay-anchor-bridge-context.tsx +33 -0
- package/src/ui-tailwind/page-stack/floating-image-overlay-model.ts +66 -29
- package/src/ui-tailwind/page-stack/tw-floating-image-layer.tsx +25 -2
- package/src/ui-tailwind/review/comment-markdown-renderer.tsx +15 -0
- package/src/ui-tailwind/review/tw-review-rail.tsx +92 -4
- package/src/ui-tailwind/review/tw-workflow-tab.tsx +1 -1
- package/src/ui-tailwind/review-workspace/page-chrome.ts +210 -0
- package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +101 -0
- package/src/ui-tailwind/review-workspace/paragraph-layout.ts +115 -0
- package/src/ui-tailwind/review-workspace/selection-toolbar-placement.ts +97 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-navigator.tsx +130 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-page-toolbar.tsx +240 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +59 -0
- package/src/ui-tailwind/review-workspace/types.ts +408 -0
- package/src/ui-tailwind/review-workspace/use-chrome-policy.ts +104 -0
- package/src/ui-tailwind/review-workspace/use-derived-view-state.ts +151 -0
- package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +70 -0
- package/src/ui-tailwind/review-workspace/use-grabbed-segment-offsets.ts +40 -0
- package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +55 -0
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +130 -0
- package/src/ui-tailwind/review-workspace/use-pm-surface-capture.ts +60 -0
- package/src/ui-tailwind/review-workspace/use-review-rail-state.ts +63 -0
- package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +170 -0
- package/src/ui-tailwind/review-workspace/use-scroll-root-capture.ts +28 -0
- package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +113 -0
- package/src/ui-tailwind/review-workspace/use-shell-selection-anchor-bridge.ts +120 -0
- package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +55 -0
- package/src/ui-tailwind/review-workspace/use-viewport-dimensions.ts +43 -0
- package/src/ui-tailwind/review-workspace/use-workspace-arbiter.ts +25 -0
- package/src/ui-tailwind/review-workspace/use-workspace-composition.ts +86 -0
- package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +150 -0
- package/src/ui-tailwind/theme/editor-theme.css +25 -0
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +2 -2
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +61 -98
- package/src/ui-tailwind/tw-review-workspace.tsx +521 -1802
- package/src/ui-tailwind/ui-api-context.tsx +43 -0
- package/src/ui-tailwind/ui-shell-channels-context.tsx +49 -0
- package/src/validation/compatibility-engine.ts +6 -6
- package/src/runtime/styles-cascade.ts +0 -33
- package/src/ui-tailwind/chrome/tw-mode-dock.tsx +0 -85
- /package/src/runtime/{page-number-format.ts → formatting/field/page-number-format.ts} +0 -0
- /package/src/runtime/{ai-action-policy.ts → workflow/ai-action-policy.ts} +0 -0
- /package/src/runtime/{scope-tag-registry.ts → workflow/scope-tag-registry.ts} +0 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 03 — `resolveEffectiveFormatting` façade over `FormattingContext`.
|
|
3
|
+
*
|
|
4
|
+
* Architecture contract F1: there is exactly one Layer-03 production
|
|
5
|
+
* boundary. That boundary is `FormattingContext` (see
|
|
6
|
+
* `./formatting-context.ts`) — production consumers (`surface-projection`,
|
|
7
|
+
* layout public-facet, etc.) construct one context per projection pass
|
|
8
|
+
* and call its methods directly for amortized per-document resolver
|
|
9
|
+
* reuse.
|
|
10
|
+
*
|
|
11
|
+
* `resolveEffectiveFormatting(doc, nodeRef, opts?)` is the external-API
|
|
12
|
+
* façade over that boundary. It constructs a per-call context from the
|
|
13
|
+
* inputs and delegates every dispatch branch to the context. Tests +
|
|
14
|
+
* agents + any caller that does not have a long-lived projection pass
|
|
15
|
+
* use this entry; production hot paths go through the context directly.
|
|
16
|
+
*
|
|
17
|
+
* The façade is PURE with respect to `doc` — it never mutates the
|
|
18
|
+
* document, never reads DOM geometry, and never schedules side effects
|
|
19
|
+
* outside the optional `opts.emitFormattingTelemetry` callback.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type {
|
|
23
|
+
CanonicalDocument,
|
|
24
|
+
CanonicalFontTable,
|
|
25
|
+
FieldNode,
|
|
26
|
+
FieldRegistryEntry,
|
|
27
|
+
ParagraphNode,
|
|
28
|
+
RevisionRecord,
|
|
29
|
+
} from "../../model/canonical-document.ts";
|
|
30
|
+
import type { FieldPageGraph, FieldResolver } from "./field/resolver.ts";
|
|
31
|
+
import type { ThemeColorResolver } from "./theme-color.ts";
|
|
32
|
+
import type { NumberingPrefixResolver } from "./numbering/prefix.ts";
|
|
33
|
+
import {
|
|
34
|
+
createFormattingContext,
|
|
35
|
+
type FormattingContext,
|
|
36
|
+
} from "./formatting-context.ts";
|
|
37
|
+
import {
|
|
38
|
+
findParagraphByBlockId,
|
|
39
|
+
findTableByBlockId,
|
|
40
|
+
findInlineBySegmentIndex,
|
|
41
|
+
parseSegmentIndex,
|
|
42
|
+
resolveDirectRunFormattingAtSegment,
|
|
43
|
+
} from "./document-lookup.ts";
|
|
44
|
+
import type {
|
|
45
|
+
EffectiveFieldDisplay,
|
|
46
|
+
EffectiveFormatting,
|
|
47
|
+
} from "./formatting-types.ts";
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Telemetry event shape emitted on the `formatting` channel when
|
|
51
|
+
* `opts.emitFormattingTelemetry` is provided.
|
|
52
|
+
*/
|
|
53
|
+
export type FormattingTelemetryEvent =
|
|
54
|
+
| {
|
|
55
|
+
readonly type: "formatting.resolution_sampled";
|
|
56
|
+
readonly payload: {
|
|
57
|
+
readonly kind: NodeRef["kind"];
|
|
58
|
+
readonly blockId: string;
|
|
59
|
+
readonly hasDirect: boolean;
|
|
60
|
+
readonly hasTheme: boolean;
|
|
61
|
+
readonly hasNumbering: boolean;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
| {
|
|
65
|
+
readonly type: "formatting.cache.invalidated";
|
|
66
|
+
readonly payload: {
|
|
67
|
+
readonly reason:
|
|
68
|
+
| "theme-resolver-built"
|
|
69
|
+
| "numbering-resolver-built"
|
|
70
|
+
| "field-resolver-built";
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
| {
|
|
74
|
+
/**
|
|
75
|
+
* Emitted when numbering resolution resolves a paragraph to a level
|
|
76
|
+
* whose `format` is not in `SUPPORTED_NUMBERING_FORMATS`. Exposes
|
|
77
|
+
* the L03 Task-A3 format-coverage gap observably without changing
|
|
78
|
+
* rendered output (the fallback path still produces `String(value)`
|
|
79
|
+
* so users see digits). Deduplication is expected to happen at the
|
|
80
|
+
* emit site — a surface-projection pass emits at most once per
|
|
81
|
+
* `(numberingInstanceId, level, format)` tuple.
|
|
82
|
+
*/
|
|
83
|
+
readonly type: "formatting.numbering_format_unsupported";
|
|
84
|
+
readonly payload: {
|
|
85
|
+
readonly format: string;
|
|
86
|
+
readonly numberingInstanceId: string;
|
|
87
|
+
readonly level: number;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export type NodeRef =
|
|
92
|
+
| { kind: "paragraph"; blockId: string }
|
|
93
|
+
| { kind: "run"; blockId: string; runId: string }
|
|
94
|
+
| { kind: "table"; blockId: string }
|
|
95
|
+
| {
|
|
96
|
+
kind: "table-cell";
|
|
97
|
+
blockId: string;
|
|
98
|
+
rowIndex: number;
|
|
99
|
+
cellIndex: number;
|
|
100
|
+
}
|
|
101
|
+
| {
|
|
102
|
+
kind: "field";
|
|
103
|
+
blockId: string;
|
|
104
|
+
runId: string;
|
|
105
|
+
pageGraph?: FieldPageGraph;
|
|
106
|
+
}
|
|
107
|
+
| { kind: "drawing-frame"; blockId: string; inlineId: string };
|
|
108
|
+
|
|
109
|
+
export interface ResolveEffectiveFormattingOptions {
|
|
110
|
+
/** Active markup mode. When set together with `revision`, the
|
|
111
|
+
* returned run is decorated with `revisionDisplay` flags. */
|
|
112
|
+
readonly revisionMarkupMode?: "clean" | "simple" | "all";
|
|
113
|
+
/** Pre-built theme resolver. Passing one through avoids building
|
|
114
|
+
* a fresh `ThemeColorResolver` inside the façade; pass `undefined`
|
|
115
|
+
* to let the façade build from `doc.subParts.canonicalTheme`. */
|
|
116
|
+
readonly themeResolver?: ThemeColorResolver;
|
|
117
|
+
/** Pre-built numbering prefix resolver. When absent, the façade
|
|
118
|
+
* constructs one from `doc.numbering` per call. */
|
|
119
|
+
readonly numberingPrefixResolver?: NumberingPrefixResolver;
|
|
120
|
+
/** Font table reference (for ECMA-376 §17.3.2.26 substitution). */
|
|
121
|
+
readonly fontTable?: CanonicalFontTable;
|
|
122
|
+
/** Pre-built field resolver. Required for PAGE / NUMPAGES / REF /
|
|
123
|
+
* STYLEREF resolution. */
|
|
124
|
+
readonly fieldResolver?: FieldResolver;
|
|
125
|
+
/** Attached revision for `run` / `field` dispatch. */
|
|
126
|
+
readonly revision?: RevisionRecord;
|
|
127
|
+
/** Author-id → color palette for revision-display posture. */
|
|
128
|
+
readonly authorColorPalette?: ReadonlyMap<string, string>;
|
|
129
|
+
/** Probability-gated telemetry emit callback. */
|
|
130
|
+
readonly emitFormattingTelemetry?: (event: FormattingTelemetryEvent) => void;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function resolveEffectiveFormatting(
|
|
134
|
+
doc: CanonicalDocument,
|
|
135
|
+
nodeRef: NodeRef,
|
|
136
|
+
opts?: ResolveEffectiveFormattingOptions,
|
|
137
|
+
): EffectiveFormatting {
|
|
138
|
+
// Build a per-call façade context. Production consumers with a
|
|
139
|
+
// long-lived projection pass construct their own `FormattingContext`
|
|
140
|
+
// once and use it directly; the façade exists for external callers
|
|
141
|
+
// + tests that prefer the `(doc, nodeRef, opts)` entry shape.
|
|
142
|
+
const ctx = buildFacadeContext(doc, nodeRef, opts);
|
|
143
|
+
emitSampled(opts, {
|
|
144
|
+
type: "formatting.resolution_sampled",
|
|
145
|
+
payload: {
|
|
146
|
+
kind: nodeRef.kind,
|
|
147
|
+
blockId: nodeRef.blockId,
|
|
148
|
+
hasDirect: nodeRef.kind === "run" || nodeRef.kind === "field" || nodeRef.kind === "drawing-frame",
|
|
149
|
+
hasTheme: Boolean(opts?.themeResolver ?? doc.subParts?.canonicalTheme),
|
|
150
|
+
hasNumbering: Boolean(opts?.numberingPrefixResolver ?? doc.numbering),
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
switch (nodeRef.kind) {
|
|
155
|
+
case "paragraph":
|
|
156
|
+
return resolveParagraph(ctx, nodeRef.blockId);
|
|
157
|
+
case "run":
|
|
158
|
+
return resolveRun(ctx, doc, nodeRef.blockId, nodeRef.runId, opts);
|
|
159
|
+
case "table":
|
|
160
|
+
return resolveTable(ctx, nodeRef.blockId);
|
|
161
|
+
case "table-cell": {
|
|
162
|
+
const found = findTableByBlockId(doc, nodeRef.blockId);
|
|
163
|
+
if (!found) return { kind: "table-cell", paragraph: {}, run: {} };
|
|
164
|
+
// Resolve conditional regions for this cell so the floor cascade
|
|
165
|
+
// layers tblStylePr pPr/rPr for header rows, banded rows, first
|
|
166
|
+
// columns, etc. (L03 A1). Out-of-bounds row/cell indices fall
|
|
167
|
+
// back to no region info (same output as pre-A1).
|
|
168
|
+
const resolved = ctx.resolveTable(found.table);
|
|
169
|
+
const activeRegions =
|
|
170
|
+
resolved.rows[nodeRef.rowIndex]?.cells[nodeRef.cellIndex]
|
|
171
|
+
?.activeConditionalRegions;
|
|
172
|
+
const { paragraph, run } = ctx.resolveTableCellFloor(
|
|
173
|
+
found.table.styleId,
|
|
174
|
+
activeRegions,
|
|
175
|
+
);
|
|
176
|
+
return { kind: "table-cell", paragraph, run };
|
|
177
|
+
}
|
|
178
|
+
case "field":
|
|
179
|
+
return resolveField(ctx, doc, nodeRef, opts);
|
|
180
|
+
case "drawing-frame":
|
|
181
|
+
return resolveDrawingFrame(ctx, doc, nodeRef.blockId, nodeRef.inlineId, opts);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function buildFacadeContext(
|
|
186
|
+
doc: CanonicalDocument,
|
|
187
|
+
nodeRef: NodeRef,
|
|
188
|
+
opts: ResolveEffectiveFormattingOptions | undefined,
|
|
189
|
+
): FormattingContext {
|
|
190
|
+
const pageGraph =
|
|
191
|
+
nodeRef.kind === "field" ? nodeRef.pageGraph : undefined;
|
|
192
|
+
return createFormattingContext(doc, {
|
|
193
|
+
revisionMarkupMode: opts?.revisionMarkupMode,
|
|
194
|
+
authorColorPalette: opts?.authorColorPalette,
|
|
195
|
+
fontTable: opts?.fontTable,
|
|
196
|
+
pageGraph,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function resolveParagraph(
|
|
201
|
+
ctx: FormattingContext,
|
|
202
|
+
blockId: string,
|
|
203
|
+
): Extract<EffectiveFormatting, { kind: "paragraph" }> {
|
|
204
|
+
const found = findParagraphByBlockId(ctx.doc, blockId);
|
|
205
|
+
if (!found) return { kind: "paragraph", paragraph: {} };
|
|
206
|
+
return { kind: "paragraph", paragraph: ctx.resolveParagraph(found.paragraph) };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function resolveRun(
|
|
210
|
+
ctx: FormattingContext,
|
|
211
|
+
doc: CanonicalDocument,
|
|
212
|
+
blockId: string,
|
|
213
|
+
runId: string,
|
|
214
|
+
opts: ResolveEffectiveFormattingOptions | undefined,
|
|
215
|
+
): Extract<EffectiveFormatting, { kind: "run" }> {
|
|
216
|
+
void doc;
|
|
217
|
+
const found = findParagraphByBlockId(ctx.doc, blockId);
|
|
218
|
+
if (!found) return { kind: "run", run: {} };
|
|
219
|
+
const para = found.paragraph;
|
|
220
|
+
const direct = resolveDirectRunFormattingAtSegment(para, runId);
|
|
221
|
+
const run = ctx.resolveRunFromCanonicalInline({
|
|
222
|
+
paragraphStyleId: para.styleId,
|
|
223
|
+
characterStyleId: direct?.characterStyleId,
|
|
224
|
+
direct,
|
|
225
|
+
inHyperlink: detectHyperlinkContext(para, runId),
|
|
226
|
+
...(opts?.revision ? { revision: opts.revision } : {}),
|
|
227
|
+
});
|
|
228
|
+
return { kind: "run", run };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function resolveTable(
|
|
232
|
+
ctx: FormattingContext,
|
|
233
|
+
blockId: string,
|
|
234
|
+
): Extract<EffectiveFormatting, { kind: "table" }> {
|
|
235
|
+
const found = findTableByBlockId(ctx.doc, blockId);
|
|
236
|
+
if (!found) {
|
|
237
|
+
return {
|
|
238
|
+
kind: "table",
|
|
239
|
+
table: {
|
|
240
|
+
effectiveTblLook: {},
|
|
241
|
+
tableResolved: {},
|
|
242
|
+
rows: [],
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
return { kind: "table", table: ctx.resolveTable(found.table) };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function resolveField(
|
|
250
|
+
ctx: FormattingContext,
|
|
251
|
+
doc: CanonicalDocument,
|
|
252
|
+
nodeRef: Extract<NodeRef, { kind: "field" }>,
|
|
253
|
+
opts: ResolveEffectiveFormattingOptions | undefined,
|
|
254
|
+
): Extract<EffectiveFormatting, { kind: "field" }> {
|
|
255
|
+
const found = findParagraphByBlockId(doc, nodeRef.blockId);
|
|
256
|
+
if (!found) {
|
|
257
|
+
return {
|
|
258
|
+
kind: "field",
|
|
259
|
+
run: {},
|
|
260
|
+
field: { refreshStatus: "unresolved" },
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const para = found.paragraph;
|
|
265
|
+
const direct = resolveDirectRunFormattingAtSegment(para, nodeRef.runId);
|
|
266
|
+
const run = ctx.resolveRunFromCanonicalInline({
|
|
267
|
+
paragraphStyleId: para.styleId,
|
|
268
|
+
characterStyleId: undefined,
|
|
269
|
+
direct,
|
|
270
|
+
inHyperlink: false,
|
|
271
|
+
...(opts?.revision ? { revision: opts.revision } : {}),
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const fieldInline = findFieldNodeAtSegment(para, nodeRef.runId);
|
|
275
|
+
const registryEntry = fieldInline
|
|
276
|
+
? findFieldRegistryEntryForParagraph(doc, found.paragraphIndex, fieldInline)
|
|
277
|
+
: undefined;
|
|
278
|
+
|
|
279
|
+
// The façade resolver was built with `nodeRef.pageGraph` threaded in,
|
|
280
|
+
// so `ctx.field` is present iff the caller supplied a page graph.
|
|
281
|
+
let field: EffectiveFieldDisplay;
|
|
282
|
+
if (!registryEntry) {
|
|
283
|
+
field = { refreshStatus: "unresolved" };
|
|
284
|
+
} else if (opts?.fieldResolver) {
|
|
285
|
+
// Honor an explicit pre-built fieldResolver over the context's own
|
|
286
|
+
// (preserves the opts-threading contract for external callers).
|
|
287
|
+
const resolved = opts.fieldResolver.resolve(registryEntry);
|
|
288
|
+
field = mapResolvedField(resolved, registryEntry);
|
|
289
|
+
} else {
|
|
290
|
+
field = ctx.resolveFieldDisplay(registryEntry, registryEntry.displayText);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return { kind: "field", run, field };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function resolveDrawingFrame(
|
|
297
|
+
ctx: FormattingContext,
|
|
298
|
+
doc: CanonicalDocument,
|
|
299
|
+
blockId: string,
|
|
300
|
+
inlineId: string,
|
|
301
|
+
opts: ResolveEffectiveFormattingOptions | undefined,
|
|
302
|
+
): Extract<EffectiveFormatting, { kind: "drawing-frame" }> {
|
|
303
|
+
void doc;
|
|
304
|
+
const found = findParagraphByBlockId(ctx.doc, blockId);
|
|
305
|
+
if (!found) return { kind: "drawing-frame", run: {} };
|
|
306
|
+
const para = found.paragraph;
|
|
307
|
+
const direct = resolveDirectRunFormattingAtSegment(para, inlineId);
|
|
308
|
+
const run = ctx.resolveRunFromCanonicalInline({
|
|
309
|
+
paragraphStyleId: para.styleId,
|
|
310
|
+
characterStyleId: undefined,
|
|
311
|
+
direct,
|
|
312
|
+
inHyperlink: false,
|
|
313
|
+
...(opts?.revision ? { revision: opts.revision } : {}),
|
|
314
|
+
});
|
|
315
|
+
return { kind: "drawing-frame", run };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ---------------------------------------------------------------------------
|
|
319
|
+
// Helpers
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
|
|
322
|
+
function detectHyperlinkContext(para: ParagraphNode, runId: string): boolean {
|
|
323
|
+
const segmentIndex = parseSegmentIndex(runId);
|
|
324
|
+
if (segmentIndex === undefined) return false;
|
|
325
|
+
let s = 0;
|
|
326
|
+
for (const inline of para.children) {
|
|
327
|
+
if (inline.type === "hyperlink") {
|
|
328
|
+
for (let i = 0; i < inline.children.length; i += 1) {
|
|
329
|
+
if (s === segmentIndex) return true;
|
|
330
|
+
s += 1;
|
|
331
|
+
}
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (
|
|
335
|
+
inline.type === "bookmark_start" ||
|
|
336
|
+
inline.type === "bookmark_end" ||
|
|
337
|
+
inline.type === "scope_marker_start" ||
|
|
338
|
+
inline.type === "scope_marker_end"
|
|
339
|
+
) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
if (s === segmentIndex) return false;
|
|
343
|
+
s += 1;
|
|
344
|
+
}
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function findFieldNodeAtSegment(
|
|
349
|
+
para: ParagraphNode,
|
|
350
|
+
runId: string,
|
|
351
|
+
): FieldNode | undefined {
|
|
352
|
+
const segmentIndex = parseSegmentIndex(runId);
|
|
353
|
+
if (segmentIndex === undefined) return undefined;
|
|
354
|
+
const found = findInlineBySegmentIndex(para, segmentIndex);
|
|
355
|
+
if (!found) return undefined;
|
|
356
|
+
return found.inline.type === "field" ? (found.inline as FieldNode) : undefined;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function findFieldRegistryEntryForParagraph(
|
|
360
|
+
doc: CanonicalDocument,
|
|
361
|
+
paragraphIndex: number,
|
|
362
|
+
fieldInline: FieldNode,
|
|
363
|
+
): FieldRegistryEntry | undefined {
|
|
364
|
+
const registry = doc.fieldRegistry;
|
|
365
|
+
if (!registry) return undefined;
|
|
366
|
+
const all = [...registry.supported, ...registry.preserveOnly];
|
|
367
|
+
return all.find(
|
|
368
|
+
(entry) =>
|
|
369
|
+
entry.paragraphIndex === paragraphIndex &&
|
|
370
|
+
entry.fieldFamily === fieldInline.fieldFamily &&
|
|
371
|
+
entry.instruction === fieldInline.instruction,
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function mapResolvedField(
|
|
376
|
+
resolved: { displayText?: string; refreshStatus: string } | undefined,
|
|
377
|
+
registry: FieldRegistryEntry,
|
|
378
|
+
): EffectiveFieldDisplay {
|
|
379
|
+
if (!resolved) {
|
|
380
|
+
return {
|
|
381
|
+
refreshStatus: "unresolved",
|
|
382
|
+
...(registry.displayText ? { displayText: registry.displayText } : {}),
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
const status: EffectiveFieldDisplay["refreshStatus"] =
|
|
386
|
+
resolved.refreshStatus === "current" || resolved.refreshStatus === "stale"
|
|
387
|
+
? "resolved"
|
|
388
|
+
: resolved.refreshStatus === "unresolvable"
|
|
389
|
+
? "unresolvable"
|
|
390
|
+
: "unresolved";
|
|
391
|
+
return {
|
|
392
|
+
refreshStatus: status,
|
|
393
|
+
...(resolved.displayText !== undefined
|
|
394
|
+
? { displayText: resolved.displayText }
|
|
395
|
+
: registry.displayText
|
|
396
|
+
? { displayText: registry.displayText }
|
|
397
|
+
: {}),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function emitSampled(
|
|
402
|
+
opts: ResolveEffectiveFormattingOptions | undefined,
|
|
403
|
+
event: FormattingTelemetryEvent,
|
|
404
|
+
): void {
|
|
405
|
+
if (!opts?.emitFormattingTelemetry) return;
|
|
406
|
+
opts.emitFormattingTelemetry(event);
|
|
407
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 03 — revision-display posture application.
|
|
3
|
+
*
|
|
4
|
+
* UI / session capabilities decide which markup mode is active. This
|
|
5
|
+
* module owns the *application* of that mode to a run with an attached
|
|
6
|
+
* revision; it does NOT decide the mode. Reference: contract F6 in
|
|
7
|
+
* `docs/architecture/03-formatting-semantics.md` §52.
|
|
8
|
+
*
|
|
9
|
+
* Mode semantics:
|
|
10
|
+
* - "clean" — open deletions are hidden; insertions + accepted/rejected
|
|
11
|
+
* revisions render as final content.
|
|
12
|
+
* - "simple" — content visible but de-emphasized; simple inline markup.
|
|
13
|
+
* - "all" — strikethrough on open deletions; insertion-underline on
|
|
14
|
+
* insertions; formatting/property/move changes surface only
|
|
15
|
+
* via author color + change bars (owned by paragraph
|
|
16
|
+
* projection).
|
|
17
|
+
*
|
|
18
|
+
* `applyRevisionDisplay(run, revision, markupMode, authorColor?)` is pure
|
|
19
|
+
* and deterministic: same inputs → same output, no hidden state. That
|
|
20
|
+
* determinism lets downstream render code memoize on
|
|
21
|
+
* `(run, revision, markupMode)` identity.
|
|
22
|
+
*
|
|
23
|
+
* The `run: CanonicalRunFormatting` parameter lets this function inspect
|
|
24
|
+
* the already-cascaded run state to avoid redundant flags — e.g. a
|
|
25
|
+
* deletion run that already carries `strikethrough: true` via direct
|
|
26
|
+
* formatting skips the `all`-mode strikethrough flag so the rendered
|
|
27
|
+
* glyph is not double-struck.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import type {
|
|
31
|
+
CanonicalRunFormatting,
|
|
32
|
+
RevisionRecord,
|
|
33
|
+
} from "../../model/canonical-document.ts";
|
|
34
|
+
import type { RevisionDisplayFlags } from "./formatting-types.ts";
|
|
35
|
+
|
|
36
|
+
export type RevisionMarkupMode = "clean" | "simple" | "all";
|
|
37
|
+
|
|
38
|
+
export function applyRevisionDisplay(
|
|
39
|
+
run: CanonicalRunFormatting,
|
|
40
|
+
revision: RevisionRecord,
|
|
41
|
+
markupMode: RevisionMarkupMode,
|
|
42
|
+
authorColor?: string,
|
|
43
|
+
): RevisionDisplayFlags {
|
|
44
|
+
switch (markupMode) {
|
|
45
|
+
case "clean": {
|
|
46
|
+
if (revision.kind === "deletion" && revision.status === "open") {
|
|
47
|
+
return buildFlags(markupMode, authorColor, { hidden: true });
|
|
48
|
+
}
|
|
49
|
+
return buildFlags(markupMode, authorColor);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
case "simple": {
|
|
53
|
+
// Content visible, de-emphasized. No strikethrough / underline
|
|
54
|
+
// markers — the consumer renders a muted style (opacity /
|
|
55
|
+
// secondary color) uniformly.
|
|
56
|
+
return buildFlags(markupMode, authorColor, { deemphasize: true });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
case "all": {
|
|
60
|
+
switch (revision.kind) {
|
|
61
|
+
case "deletion": {
|
|
62
|
+
// Short-circuit: if the run already carries strikethrough via
|
|
63
|
+
// direct formatting (uncommon but possible: a deletion whose
|
|
64
|
+
// source already had strikethrough as authoring style), do NOT
|
|
65
|
+
// add the revision strikethrough flag — avoids double-struck
|
|
66
|
+
// glyphs in render.
|
|
67
|
+
if (run.strikethrough === true) {
|
|
68
|
+
return buildFlags(markupMode, authorColor);
|
|
69
|
+
}
|
|
70
|
+
return buildFlags(markupMode, authorColor, { strikethrough: true });
|
|
71
|
+
}
|
|
72
|
+
case "insertion": {
|
|
73
|
+
// Mirror short-circuit for insertions. If the run already carries
|
|
74
|
+
// a single-underline via direct formatting, skip the insertion
|
|
75
|
+
// underline so the render doesn't over-paint.
|
|
76
|
+
if (run.underline === "single") {
|
|
77
|
+
return buildFlags(markupMode, authorColor);
|
|
78
|
+
}
|
|
79
|
+
return buildFlags(markupMode, authorColor, { insertionUnderline: true });
|
|
80
|
+
}
|
|
81
|
+
case "formatting":
|
|
82
|
+
case "property-change":
|
|
83
|
+
case "move": {
|
|
84
|
+
// Paragraph-level markers (change-bar, move-from/to arrows) are
|
|
85
|
+
// owned by the paragraph projection. For runs that carry these
|
|
86
|
+
// revision kinds, fall through to the author-color-only posture.
|
|
87
|
+
return buildFlags(markupMode, authorColor);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return buildFlags(markupMode, authorColor);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function buildFlags(
|
|
96
|
+
markupMode: RevisionMarkupMode,
|
|
97
|
+
authorColor: string | undefined,
|
|
98
|
+
extras: Omit<RevisionDisplayFlags, "markupMode" | "authorColor"> = {},
|
|
99
|
+
): RevisionDisplayFlags {
|
|
100
|
+
return {
|
|
101
|
+
markupMode,
|
|
102
|
+
...(authorColor !== undefined ? { authorColor } : {}),
|
|
103
|
+
...extras,
|
|
104
|
+
};
|
|
105
|
+
}
|