@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,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @endStateApi v3 — `ui.session` family (layer 10).
|
|
3
|
+
*
|
|
4
|
+
* Slice 2 — live. Pure state management inside the UI API instance: bind
|
|
5
|
+
* stores the controller on the shared context, getController reads it,
|
|
6
|
+
* release clears it. No runtime coupling at this layer; the controller is
|
|
7
|
+
* the seam.
|
|
8
|
+
*
|
|
9
|
+
* Contract U2 — UI API is bound to an opaque controller, not React
|
|
10
|
+
* internals. The same shape works for the React editor, a Playwright test,
|
|
11
|
+
* and a headless debug session.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ApiV3FnMetadata } from "../_layer-metadata.ts";
|
|
15
|
+
import type { UiController, UiBinding } from "./_types.ts";
|
|
16
|
+
import type { UiApiContext, BindingState } from "./_context.ts";
|
|
17
|
+
|
|
18
|
+
export const bindMetadata: ApiV3FnMetadata = {
|
|
19
|
+
name: "ui.session.bind",
|
|
20
|
+
status: "live",
|
|
21
|
+
sourceLayer: "presentation",
|
|
22
|
+
liveEvidence: {
|
|
23
|
+
runnerTest: "test/api/v3/ui/session-bind.test.ts",
|
|
24
|
+
commit: "refactor-10-slice-2",
|
|
25
|
+
},
|
|
26
|
+
uxIntent: { uiVisible: false },
|
|
27
|
+
agentMetadata: { readOrMutate: "compound", boundedScope: "session", auditCategory: "ui-session-bind" },
|
|
28
|
+
stateClass: "C-local",
|
|
29
|
+
persistsTo: "none",
|
|
30
|
+
rwdReference: "§UI API § ui.session.bind",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const getControllerMetadata: ApiV3FnMetadata = {
|
|
34
|
+
name: "ui.session.getController",
|
|
35
|
+
status: "live",
|
|
36
|
+
sourceLayer: "presentation",
|
|
37
|
+
liveEvidence: {
|
|
38
|
+
runnerTest: "test/api/v3/ui/session-bind.test.ts",
|
|
39
|
+
commit: "refactor-10-slice-2",
|
|
40
|
+
},
|
|
41
|
+
uxIntent: { uiVisible: false },
|
|
42
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "session", auditCategory: "ui-session-read" },
|
|
43
|
+
stateClass: "C-local",
|
|
44
|
+
persistsTo: "none",
|
|
45
|
+
rwdReference: "§UI API § ui.session.getController",
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const releaseMetadata: ApiV3FnMetadata = {
|
|
49
|
+
name: "ui.session.release",
|
|
50
|
+
status: "live",
|
|
51
|
+
sourceLayer: "presentation",
|
|
52
|
+
liveEvidence: {
|
|
53
|
+
runnerTest: "test/api/v3/ui/session-bind.test.ts",
|
|
54
|
+
commit: "refactor-10-slice-2",
|
|
55
|
+
},
|
|
56
|
+
uxIntent: { uiVisible: false },
|
|
57
|
+
agentMetadata: { readOrMutate: "compound", boundedScope: "session", auditCategory: "ui-session-release" },
|
|
58
|
+
stateClass: "C-local",
|
|
59
|
+
persistsTo: "none",
|
|
60
|
+
rwdReference: "§UI API § ui.session.release",
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export function createSessionFamily(ctx: UiApiContext) {
|
|
64
|
+
function makeBinding(state: BindingState): UiBinding {
|
|
65
|
+
return {
|
|
66
|
+
controller: state.controller,
|
|
67
|
+
release(): void {
|
|
68
|
+
if (state.released) return;
|
|
69
|
+
state.released = true;
|
|
70
|
+
// Only clear the root binding if it still points to this state —
|
|
71
|
+
// calling release() on a stale binding must NOT unbind a newer one.
|
|
72
|
+
if (ctx.binding === state) {
|
|
73
|
+
ctx.binding = null;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
bind(controller: UiController): UiBinding {
|
|
80
|
+
// Replace any prior binding. The prior binding's own release() is a
|
|
81
|
+
// no-op after this (its released flag will be set the next time it
|
|
82
|
+
// is invoked — and it can no longer affect ctx.binding).
|
|
83
|
+
if (ctx.binding) {
|
|
84
|
+
ctx.binding.released = true;
|
|
85
|
+
}
|
|
86
|
+
const state: BindingState = { controller, released: false };
|
|
87
|
+
ctx.binding = state;
|
|
88
|
+
return makeBinding(state);
|
|
89
|
+
},
|
|
90
|
+
getController(): UiController | null {
|
|
91
|
+
return ctx.binding?.controller ?? null;
|
|
92
|
+
},
|
|
93
|
+
release(): void {
|
|
94
|
+
if (ctx.binding) {
|
|
95
|
+
ctx.binding.released = true;
|
|
96
|
+
ctx.binding = null;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @endStateApi v3 — `ui.surface` family (layer 10).
|
|
3
|
+
*
|
|
4
|
+
* Slice 2 wiring:
|
|
5
|
+
* - getSelection — live. Reads `handle.getRenderSnapshot().selection`;
|
|
6
|
+
* returns null when the runtime has no snapshot.
|
|
7
|
+
* - setSelection — live-with-adapter. Delegates to the bound
|
|
8
|
+
* controller's `dispatchSelection` hook. Bidirectional
|
|
9
|
+
* channel U3.
|
|
10
|
+
* - getViewport — partial. Delegates to the controller's
|
|
11
|
+
* `getViewport` hook if provided; otherwise returns a
|
|
12
|
+
* mock default. Slice 3 replaces with a
|
|
13
|
+
* `GeometryFacet.getViewport()` read.
|
|
14
|
+
* - scrollTo — live-with-adapter. Delegates to the bound
|
|
15
|
+
* controller's `dispatchScroll`. Slice 3 adds
|
|
16
|
+
* geometry-backed resolution for block/scope/comment/
|
|
17
|
+
* revision targets before dispatch.
|
|
18
|
+
*
|
|
19
|
+
* Contract U3 — selection + viewport are the only bidirectional channels.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { ApiV3FnMetadata } from "../_layer-metadata.ts";
|
|
23
|
+
import type { SelectionSnapshot } from "../../public-types.ts";
|
|
24
|
+
import type {
|
|
25
|
+
SelectionRangeInput,
|
|
26
|
+
ScrollTarget,
|
|
27
|
+
ViewportState,
|
|
28
|
+
} from "./_types.ts";
|
|
29
|
+
import type { UiApiContext } from "./_context.ts";
|
|
30
|
+
import { readComposedViewport } from "./_context.ts";
|
|
31
|
+
import { emitUxResponse } from "../_ux-response.ts";
|
|
32
|
+
|
|
33
|
+
export const getSelectionMetadata: ApiV3FnMetadata = {
|
|
34
|
+
name: "ui.surface.getSelection",
|
|
35
|
+
status: "live",
|
|
36
|
+
sourceLayer: "presentation",
|
|
37
|
+
liveEvidence: {
|
|
38
|
+
runnerTest: "test/api/v3/ui/selection-bidirectional.test.ts",
|
|
39
|
+
commit: "refactor-10-slice-2",
|
|
40
|
+
},
|
|
41
|
+
uxIntent: { uiVisible: false },
|
|
42
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "selection", auditCategory: "ui-surface-read" },
|
|
43
|
+
stateClass: "C-local",
|
|
44
|
+
persistsTo: "none",
|
|
45
|
+
rwdReference: "§UI API § ui.surface.getSelection",
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const setSelectionMetadata: ApiV3FnMetadata = {
|
|
49
|
+
name: "ui.surface.setSelection",
|
|
50
|
+
status: "live-with-adapter",
|
|
51
|
+
sourceLayer: "presentation",
|
|
52
|
+
liveEvidence: {
|
|
53
|
+
runnerTest: "test/api/v3/ui/selection-bidirectional.test.ts",
|
|
54
|
+
commit: "refactor-10-slice-2",
|
|
55
|
+
},
|
|
56
|
+
uxIntent: { uiVisible: true, expectsUxResponse: "selection-change", expectedDelta: "selection moves to requested range" },
|
|
57
|
+
agentMetadata: { readOrMutate: "mutate", boundedScope: "selection", auditCategory: "ui-surface-set-selection" },
|
|
58
|
+
stateClass: "C-local",
|
|
59
|
+
persistsTo: "none",
|
|
60
|
+
// Dispatch-form bidirectional channel (U3): request flows UI → runtime
|
|
61
|
+
// via controller.dispatchSelection; the runtime's follow-up
|
|
62
|
+
// `selection.changed` emission is observed by the viewport / surface
|
|
63
|
+
// event stream, not by this call. No subscriptionShape — single-shot.
|
|
64
|
+
bidirectional: true,
|
|
65
|
+
rwdReference: "§UI API § ui.surface.setSelection. Adapter delegates to UiController.dispatchSelection; throws when the active binding has no dispatch hook.",
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const getViewportMetadata: ApiV3FnMetadata = {
|
|
69
|
+
name: "ui.surface.getViewport",
|
|
70
|
+
status: "partial",
|
|
71
|
+
sourceLayer: "presentation",
|
|
72
|
+
liveEvidence: {
|
|
73
|
+
runnerTest: "test/api/v3/ui/selection-bidirectional.test.ts",
|
|
74
|
+
commit: "refactor-10-slice-2",
|
|
75
|
+
},
|
|
76
|
+
mockShape: {
|
|
77
|
+
deterministic: true,
|
|
78
|
+
seededFrom: "fixed",
|
|
79
|
+
shapeDescription: "Default zeroed ViewportState (width/height/scroll=0, zoom/dpr=1) when no controller.getViewport hook is provided. Slice 3 promotes to live via GeometryFacet.getViewport().",
|
|
80
|
+
carriesMockFlag: true,
|
|
81
|
+
},
|
|
82
|
+
uxIntent: { uiVisible: false },
|
|
83
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "session", auditCategory: "ui-surface-read" },
|
|
84
|
+
stateClass: "C-local",
|
|
85
|
+
persistsTo: "none",
|
|
86
|
+
rwdReference: "§UI API § ui.surface.getViewport",
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const scrollToMetadata: ApiV3FnMetadata = {
|
|
90
|
+
name: "ui.surface.scrollTo",
|
|
91
|
+
status: "live-with-adapter",
|
|
92
|
+
sourceLayer: "presentation",
|
|
93
|
+
liveEvidence: {
|
|
94
|
+
runnerTest: "test/api/v3/ui/scroll-to.test.ts",
|
|
95
|
+
commit: "refactor-10-slice-2",
|
|
96
|
+
},
|
|
97
|
+
uxIntent: { uiVisible: true, expectsUxResponse: "surface-refresh", expectedDelta: "viewport scrolls to target" },
|
|
98
|
+
agentMetadata: { readOrMutate: "mutate", boundedScope: "session", auditCategory: "ui-surface-scroll" },
|
|
99
|
+
stateClass: "C-local",
|
|
100
|
+
persistsTo: "none",
|
|
101
|
+
// Dispatch-form bidirectional channel (U3): request flows UI → runtime
|
|
102
|
+
// via controller.dispatchScroll; viewport settles via rAF + the
|
|
103
|
+
// `ux.response.ui.surface.scrollTo` event announces the request.
|
|
104
|
+
bidirectional: true,
|
|
105
|
+
rwdReference: "§UI API § ui.surface.scrollTo. Adapter delegates to UiController.dispatchScroll. Slice 3 adds geometry-backed target resolution for block/scope/comment/revision kinds prior to dispatch.",
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export function createSurfaceFamily(ctx: UiApiContext) {
|
|
109
|
+
return {
|
|
110
|
+
getSelection(): SelectionSnapshot | null {
|
|
111
|
+
// Per DS §8.3 (runtime owns truth), trust the handle contract.
|
|
112
|
+
// A thrown `getRenderSnapshot` is a runtime regression, not a
|
|
113
|
+
// pre-mount state — silent null would let the UI claim "no
|
|
114
|
+
// selection" while substrate is broken. Pre-mount handles use
|
|
115
|
+
// the inert-facet pattern (`loading-runtime-bridge.ts`).
|
|
116
|
+
const snapshot = ctx.handle.getRenderSnapshot();
|
|
117
|
+
return snapshot?.selection ?? null;
|
|
118
|
+
},
|
|
119
|
+
setSelection(range: SelectionRangeInput): void {
|
|
120
|
+
const controller = ctx.binding?.controller;
|
|
121
|
+
if (!controller) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
"ui.surface.setSelection: no controller bound — call ui.session.bind(controller) first",
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
if (!controller.dispatchSelection) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`ui.surface.setSelection: controller of kind "${controller.kind}" did not provide a dispatchSelection hook`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
controller.dispatchSelection(range);
|
|
132
|
+
emitUxResponse(ctx.handle, {
|
|
133
|
+
apiFn: setSelectionMetadata.name,
|
|
134
|
+
intent: setSelectionMetadata.uxIntent.expectedDelta ?? "",
|
|
135
|
+
mockOrLive: "live-with-adapter",
|
|
136
|
+
uiVisible: true,
|
|
137
|
+
expectedDelta: setSelectionMetadata.uxIntent.expectedDelta,
|
|
138
|
+
actualDelta: { kind: "selection-change", payload: { anchor: range.anchor, head: range.head } },
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
getViewport(): ViewportState {
|
|
142
|
+
// Shared composition with `ui.viewport.get` (Slice 11) — reads
|
|
143
|
+
// scroll/dpr/zoom from handle.geometry, width/height from the
|
|
144
|
+
// bound controller.
|
|
145
|
+
return readComposedViewport(ctx);
|
|
146
|
+
},
|
|
147
|
+
async scrollTo(target: ScrollTarget): Promise<void> {
|
|
148
|
+
const controller = ctx.binding?.controller;
|
|
149
|
+
if (!controller) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"ui.surface.scrollTo: no controller bound — call ui.session.bind(controller) first",
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
if (!controller.dispatchScroll) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
`ui.surface.scrollTo: controller of kind "${controller.kind}" did not provide a dispatchScroll hook`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
await controller.dispatchScroll(target);
|
|
160
|
+
emitUxResponse(ctx.handle, {
|
|
161
|
+
apiFn: scrollToMetadata.name,
|
|
162
|
+
intent: scrollToMetadata.uxIntent.expectedDelta ?? "",
|
|
163
|
+
mockOrLive: "live-with-adapter",
|
|
164
|
+
uiVisible: true,
|
|
165
|
+
expectedDelta: scrollToMetadata.uxIntent.expectedDelta,
|
|
166
|
+
actualDelta: { kind: "surface-refresh", payload: { kind: target.kind, value: target.kind === "scope" || target.kind === "comment" || target.kind === "revision" || target.kind === "block" ? target.value : undefined } },
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @endStateApi v3 — `ui.viewport` family (layer 10).
|
|
3
|
+
*
|
|
4
|
+
* Slice 11 wiring (2026-04-22 — refactor/10 adversarial closure):
|
|
5
|
+
* - get — live. Reads scroll / dpr / zoom directly from
|
|
6
|
+
* `handle.geometry.getViewport()` (available since
|
|
7
|
+
* refactor/05 adversarial closure). Width + height
|
|
8
|
+
* still come from the controller hook when present,
|
|
9
|
+
* because those are DOM-measured container dims and
|
|
10
|
+
* not in the geometry primitive. When neither geometry
|
|
11
|
+
* nor controller is available, returns a mock-flagged
|
|
12
|
+
* zeroed default.
|
|
13
|
+
* - subscribe — live-with-adapter. Delegates to the bound controller's
|
|
14
|
+
* `subscribeViewport` hook. Bind-side is responsible for
|
|
15
|
+
* rAF coalescing (U7). Throws when no controller is
|
|
16
|
+
* bound or the controller lacks the hook — consumers
|
|
17
|
+
* learn about misconfiguration rather than silently
|
|
18
|
+
* missing events.
|
|
19
|
+
*
|
|
20
|
+
* Contract U7 — subscribers receive runtime-time events plus coalesced
|
|
21
|
+
* rAF ticks for viewport. Never on raw PM transaction emission.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { ApiV3FnMetadata } from "../_layer-metadata.ts";
|
|
25
|
+
import type {
|
|
26
|
+
ViewportState,
|
|
27
|
+
UiListener,
|
|
28
|
+
UiUnsubscribe,
|
|
29
|
+
WorkflowMarkupMode,
|
|
30
|
+
} from "./_types.ts";
|
|
31
|
+
import type { UiApiContext } from "./_context.ts";
|
|
32
|
+
import { readComposedViewport } from "./_context.ts";
|
|
33
|
+
import { emitUxResponse } from "../_ux-response.ts";
|
|
34
|
+
|
|
35
|
+
export const getMetadata: ApiV3FnMetadata = {
|
|
36
|
+
name: "ui.viewport.get",
|
|
37
|
+
status: "live-with-adapter",
|
|
38
|
+
sourceLayer: "presentation",
|
|
39
|
+
liveEvidence: {
|
|
40
|
+
runnerTest: "test/api/v3/ui/viewport-subscribe.test.ts",
|
|
41
|
+
commit: "refactor-10-slice-11",
|
|
42
|
+
},
|
|
43
|
+
mockShape: {
|
|
44
|
+
deterministic: true,
|
|
45
|
+
seededFrom: "fixed",
|
|
46
|
+
shapeDescription: "Default zeroed ViewportState (width/height=0, scroll=0, zoom/dpr=1) when neither handle.geometry nor a controller.getViewport hook is available (headless pre-paint). Slice 11 sources scroll/dpr/zoom from handle.geometry.getViewport() and width/height from the controller hook when present.",
|
|
47
|
+
carriesMockFlag: true,
|
|
48
|
+
},
|
|
49
|
+
uxIntent: { uiVisible: false },
|
|
50
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "session", auditCategory: "ui-viewport-read" },
|
|
51
|
+
stateClass: "C-local",
|
|
52
|
+
persistsTo: "none",
|
|
53
|
+
rwdReference: "§UI API § ui.viewport.get. Reads scroll/dpr/zoom through handle.geometry.getViewport() directly (refactor/05 geometry facet). width/height come from controller.getViewport() when bound, since container-dim measurement is DOM-local and not in the geometry primitive.",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const subscribeMetadata: ApiV3FnMetadata = {
|
|
57
|
+
name: "ui.viewport.subscribe",
|
|
58
|
+
status: "live-with-adapter",
|
|
59
|
+
sourceLayer: "presentation",
|
|
60
|
+
liveEvidence: {
|
|
61
|
+
runnerTest: "test/api/v3/ui/viewport-subscribe.test.ts",
|
|
62
|
+
commit: "refactor-07-slice-2",
|
|
63
|
+
},
|
|
64
|
+
// Stream-form bidirectional channel (U3 + U7). `uiVisible: true`
|
|
65
|
+
// because the subscribe call IS observable UX — registering a
|
|
66
|
+
// listener affects what the mounted surface paints on the next
|
|
67
|
+
// viewport change. The single `surface-refresh` ack fires on the
|
|
68
|
+
// establishing call; the rAF-coalesced stream is announced via
|
|
69
|
+
// `subscriptionShape`.
|
|
70
|
+
uxIntent: {
|
|
71
|
+
uiVisible: true,
|
|
72
|
+
expectsUxResponse: "surface-refresh",
|
|
73
|
+
expectedDelta: "viewport subscriber attached; future scroll/zoom/dpr changes propagate through the listener",
|
|
74
|
+
},
|
|
75
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "session", auditCategory: "ui-viewport-subscribe" },
|
|
76
|
+
stateClass: "C-local",
|
|
77
|
+
persistsTo: "none",
|
|
78
|
+
bidirectional: true,
|
|
79
|
+
subscriptionShape: {
|
|
80
|
+
eventType: "ui.viewport.changed",
|
|
81
|
+
payloadType: "ViewportState",
|
|
82
|
+
coalescing: "raf",
|
|
83
|
+
},
|
|
84
|
+
rwdReference: "§UI API § ui.viewport.subscribe. Adapter delegates to UiController.subscribeViewport; throws when the active binding has no hook. Subscribe call emits one `ux.response.ui.viewport.subscribe` acknowledgement; per-tick ViewportState deliveries flow through the listener (rAF-coalesced, U7).",
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// ----- X5 markup-mode metadata (state-classes cross-cutting Slice X5) -----
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Intrinsic default when no policy has been authored AND no local
|
|
91
|
+
* preference is set. "all" mirrors the shipped shell default: show
|
|
92
|
+
* insertions + deletions + suggestions so a user who hasn't touched
|
|
93
|
+
* the control sees changes.
|
|
94
|
+
*/
|
|
95
|
+
const INTRINSIC_DEFAULT_MARKUP_MODE: WorkflowMarkupMode = "all";
|
|
96
|
+
|
|
97
|
+
export const getEffectiveMarkupModeMetadata: ApiV3FnMetadata = {
|
|
98
|
+
name: "ui.viewport.getEffectiveMarkupMode",
|
|
99
|
+
status: "live",
|
|
100
|
+
sourceLayer: "presentation",
|
|
101
|
+
liveEvidence: {
|
|
102
|
+
runnerTest: "test/api/v3/ui/markup-mode-composition.test.ts",
|
|
103
|
+
commit: "refactor-10-slice-x5",
|
|
104
|
+
},
|
|
105
|
+
uxIntent: { uiVisible: false },
|
|
106
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "session", auditCategory: "ui-viewport-read" },
|
|
107
|
+
stateClass: "C-local",
|
|
108
|
+
persistsTo: "none",
|
|
109
|
+
rwdReference: "§UI API § ui.viewport.getEffectiveMarkupMode (X5). Composes class-A policy (handle.getMarkupModePolicy) with class-C local preference. enforcement 'always' hard-overrides the preference; 'authored-default' cedes to local preference.",
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const setLocalMarkupModeMetadata: ApiV3FnMetadata = {
|
|
113
|
+
name: "ui.viewport.setLocalMarkupMode",
|
|
114
|
+
status: "live",
|
|
115
|
+
sourceLayer: "presentation",
|
|
116
|
+
liveEvidence: {
|
|
117
|
+
runnerTest: "test/api/v3/ui/markup-mode-composition.test.ts",
|
|
118
|
+
commit: "refactor-10-slice-x5",
|
|
119
|
+
},
|
|
120
|
+
uxIntent: {
|
|
121
|
+
uiVisible: true,
|
|
122
|
+
expectsUxResponse: "surface-refresh",
|
|
123
|
+
expectedDelta: "markup-mode local preference set; subscribers receive the new composed mode",
|
|
124
|
+
},
|
|
125
|
+
agentMetadata: { readOrMutate: "mutate", boundedScope: "session", auditCategory: "ui-viewport-set-markup-mode" },
|
|
126
|
+
stateClass: "C-local",
|
|
127
|
+
persistsTo: "none",
|
|
128
|
+
rwdReference: "§UI API § ui.viewport.setLocalMarkupMode (X5). Class-C write.",
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const resetLocalMarkupModeMetadata: ApiV3FnMetadata = {
|
|
132
|
+
name: "ui.viewport.resetLocalMarkupMode",
|
|
133
|
+
status: "live",
|
|
134
|
+
sourceLayer: "presentation",
|
|
135
|
+
liveEvidence: {
|
|
136
|
+
runnerTest: "test/api/v3/ui/markup-mode-composition.test.ts",
|
|
137
|
+
commit: "refactor-10-slice-x5",
|
|
138
|
+
},
|
|
139
|
+
uxIntent: {
|
|
140
|
+
uiVisible: true,
|
|
141
|
+
expectsUxResponse: "surface-refresh",
|
|
142
|
+
expectedDelta: "markup-mode local preference cleared; subsequent reads fall back to policy",
|
|
143
|
+
},
|
|
144
|
+
agentMetadata: { readOrMutate: "mutate", boundedScope: "session", auditCategory: "ui-viewport-reset-markup-mode" },
|
|
145
|
+
stateClass: "C-local",
|
|
146
|
+
persistsTo: "none",
|
|
147
|
+
rwdReference: "§UI API § ui.viewport.resetLocalMarkupMode (X5).",
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const subscribeEffectiveMarkupModeMetadata: ApiV3FnMetadata = {
|
|
151
|
+
name: "ui.viewport.subscribeEffectiveMarkupMode",
|
|
152
|
+
status: "live",
|
|
153
|
+
sourceLayer: "presentation",
|
|
154
|
+
liveEvidence: {
|
|
155
|
+
runnerTest: "test/api/v3/ui/markup-mode-composition.test.ts",
|
|
156
|
+
commit: "refactor-10-slice-x5",
|
|
157
|
+
},
|
|
158
|
+
uxIntent: {
|
|
159
|
+
uiVisible: true,
|
|
160
|
+
expectsUxResponse: "surface-refresh",
|
|
161
|
+
expectedDelta: "markup-mode subscriber attached; future local-pref changes + L06 policy changes propagate through the listener",
|
|
162
|
+
},
|
|
163
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "session", auditCategory: "ui-viewport-subscribe" },
|
|
164
|
+
stateClass: "C-local",
|
|
165
|
+
persistsTo: "none",
|
|
166
|
+
bidirectional: true,
|
|
167
|
+
subscriptionShape: {
|
|
168
|
+
eventType: "ui.viewport.markup_mode_changed",
|
|
169
|
+
payloadType: "WorkflowMarkupMode",
|
|
170
|
+
coalescing: "microtask",
|
|
171
|
+
},
|
|
172
|
+
rwdReference: "§UI API § ui.viewport.subscribeEffectiveMarkupMode (X5). Fires on local-pref mutations AND on L06 class-A policy changes (chained through handle.subscribeMarkupModePolicy).",
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export function createViewportFamily(ctx: UiApiContext) {
|
|
176
|
+
// Closure-local class-C markup-mode state (X5). Per-createUiApi-
|
|
177
|
+
// instance — two instances over the same handle hold independent
|
|
178
|
+
// preferences (same isolation pattern as U9 + rail-state).
|
|
179
|
+
let localMarkupMode: WorkflowMarkupMode | null = null;
|
|
180
|
+
const markupModeSubscribers = new Set<UiListener<WorkflowMarkupMode>>();
|
|
181
|
+
|
|
182
|
+
function readEffectiveMarkupMode(): WorkflowMarkupMode {
|
|
183
|
+
const policy = ctx.handle.getMarkupModePolicy?.() ?? null;
|
|
184
|
+
// enforcement "always" hard-overrides local preference.
|
|
185
|
+
if (policy?.enforcement === "always") {
|
|
186
|
+
return policy.mode;
|
|
187
|
+
}
|
|
188
|
+
// authored-default / no policy: local preference wins if expressed.
|
|
189
|
+
if (localMarkupMode !== null) {
|
|
190
|
+
return localMarkupMode;
|
|
191
|
+
}
|
|
192
|
+
// No preference → policy.mode (authored default) or intrinsic "all".
|
|
193
|
+
return policy?.mode ?? INTRINSIC_DEFAULT_MARKUP_MODE;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function notifyMarkupModeSubscribers(): void {
|
|
197
|
+
if (markupModeSubscribers.size === 0) return;
|
|
198
|
+
const mode = readEffectiveMarkupMode();
|
|
199
|
+
for (const listener of markupModeSubscribers) {
|
|
200
|
+
try {
|
|
201
|
+
listener(mode);
|
|
202
|
+
} catch {
|
|
203
|
+
// Isolate listener errors.
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Chain L06 policy changes into our subscribers so consumers get
|
|
209
|
+
// notified on class-A authoring (same pattern as U9 X1⇄X3).
|
|
210
|
+
// TODO(ui.dispose): retain the unsubscribe if a family-level dispose
|
|
211
|
+
// hook lands. The family instance lives for the lifetime of the
|
|
212
|
+
// handle today.
|
|
213
|
+
ctx.handle.subscribeMarkupModePolicy?.(() => {
|
|
214
|
+
notifyMarkupModeSubscribers();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
// Slice 11 — composes scroll/dpr/zoom from `handle.geometry` with
|
|
219
|
+
// width/height from the bound controller (see
|
|
220
|
+
// `readComposedViewport` in `_context.ts` for the shared
|
|
221
|
+
// implementation).
|
|
222
|
+
get(): ViewportState {
|
|
223
|
+
return readComposedViewport(ctx);
|
|
224
|
+
},
|
|
225
|
+
subscribe(listener: UiListener<ViewportState>): UiUnsubscribe {
|
|
226
|
+
const controller = ctx.binding?.controller;
|
|
227
|
+
if (!controller) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
"ui.viewport.subscribe: no controller bound — call ui.session.bind(controller) first",
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
if (!controller.subscribeViewport) {
|
|
233
|
+
throw new Error(
|
|
234
|
+
`ui.viewport.subscribe: controller of kind "${controller.kind}" did not provide a subscribeViewport hook`,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
const unsubscribe = controller.subscribeViewport(listener);
|
|
238
|
+
// Stream-form bidirectional ack: one UxResponse per subscribe call
|
|
239
|
+
// announcing "channel established". Per-tick ViewportState updates
|
|
240
|
+
// flow through the listener, not this event stream.
|
|
241
|
+
emitUxResponse(ctx.handle, {
|
|
242
|
+
apiFn: subscribeMetadata.name,
|
|
243
|
+
intent: subscribeMetadata.uxIntent.expectedDelta ?? "",
|
|
244
|
+
mockOrLive: "live-with-adapter",
|
|
245
|
+
uiVisible: true,
|
|
246
|
+
expectedDelta: subscribeMetadata.uxIntent.expectedDelta,
|
|
247
|
+
actualDelta: { kind: "surface-refresh", payload: { subscribed: "ui.viewport" } },
|
|
248
|
+
});
|
|
249
|
+
return unsubscribe;
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
// ----- X5 markup-mode (state-classes cross-cutting Slice X5) -----
|
|
253
|
+
|
|
254
|
+
getEffectiveMarkupMode(): WorkflowMarkupMode {
|
|
255
|
+
return readEffectiveMarkupMode();
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
setLocalMarkupMode(mode: WorkflowMarkupMode): void {
|
|
259
|
+
if (localMarkupMode === mode) return; // idempotent.
|
|
260
|
+
localMarkupMode = mode;
|
|
261
|
+
emitUxResponse(ctx.handle, {
|
|
262
|
+
apiFn: setLocalMarkupModeMetadata.name,
|
|
263
|
+
intent: setLocalMarkupModeMetadata.uxIntent.expectedDelta ?? "",
|
|
264
|
+
mockOrLive: "live",
|
|
265
|
+
uiVisible: true,
|
|
266
|
+
expectedDelta: setLocalMarkupModeMetadata.uxIntent.expectedDelta,
|
|
267
|
+
actualDelta: { kind: "surface-refresh", payload: { markupMode: mode } },
|
|
268
|
+
});
|
|
269
|
+
notifyMarkupModeSubscribers();
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
resetLocalMarkupMode(): void {
|
|
273
|
+
if (localMarkupMode === null) return; // idempotent.
|
|
274
|
+
localMarkupMode = null;
|
|
275
|
+
emitUxResponse(ctx.handle, {
|
|
276
|
+
apiFn: resetLocalMarkupModeMetadata.name,
|
|
277
|
+
intent: resetLocalMarkupModeMetadata.uxIntent.expectedDelta ?? "",
|
|
278
|
+
mockOrLive: "live",
|
|
279
|
+
uiVisible: true,
|
|
280
|
+
expectedDelta: resetLocalMarkupModeMetadata.uxIntent.expectedDelta,
|
|
281
|
+
actualDelta: { kind: "surface-refresh", payload: { markupMode: "reset" } },
|
|
282
|
+
});
|
|
283
|
+
notifyMarkupModeSubscribers();
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
subscribeEffectiveMarkupMode(
|
|
287
|
+
listener: UiListener<WorkflowMarkupMode>,
|
|
288
|
+
): UiUnsubscribe {
|
|
289
|
+
markupModeSubscribers.add(listener);
|
|
290
|
+
emitUxResponse(ctx.handle, {
|
|
291
|
+
apiFn: subscribeEffectiveMarkupModeMetadata.name,
|
|
292
|
+
intent: subscribeEffectiveMarkupModeMetadata.uxIntent.expectedDelta ?? "",
|
|
293
|
+
mockOrLive: "live",
|
|
294
|
+
uiVisible: true,
|
|
295
|
+
expectedDelta: subscribeEffectiveMarkupModeMetadata.uxIntent.expectedDelta,
|
|
296
|
+
actualDelta: { kind: "surface-refresh", payload: { subscribed: "ui.viewport.markupMode" } },
|
|
297
|
+
});
|
|
298
|
+
return () => {
|
|
299
|
+
markupModeSubscribers.delete(listener);
|
|
300
|
+
};
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
}
|
|
@@ -21,6 +21,27 @@ import {
|
|
|
21
21
|
type MappingStep,
|
|
22
22
|
type TransactionMapping,
|
|
23
23
|
} from "../selection/mapping.ts";
|
|
24
|
+
import {
|
|
25
|
+
toInternalAnchorProjection,
|
|
26
|
+
toPublicAnchorProjection,
|
|
27
|
+
} from "../selection/anchor-conversion.ts";
|
|
28
|
+
import type { EditorAnchorProjection as PublicEditorAnchorProjection } from "../../api/public-types.ts";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Apply a transaction mapping to a record-attached (public flat) anchor.
|
|
32
|
+
* Record-attached anchors (on `EditorWarning` / `CompatibilityFeatureEntry`)
|
|
33
|
+
* carry the flat public shape; `mapAnchor` operates on the internal nested
|
|
34
|
+
* shape. Convert flat → nested → map → flat so the mapping composition
|
|
35
|
+
* logic stays in `mapping.ts` while records stay flat.
|
|
36
|
+
*
|
|
37
|
+
* See `docs/plans/cross-layer-coord-02.md §8`.
|
|
38
|
+
*/
|
|
39
|
+
function mapRecordAnchor(
|
|
40
|
+
anchor: PublicEditorAnchorProjection,
|
|
41
|
+
mapping: TransactionMapping,
|
|
42
|
+
): PublicEditorAnchorProjection {
|
|
43
|
+
return toPublicAnchorProjection(mapAnchor(toInternalAnchorProjection(anchor), mapping));
|
|
44
|
+
}
|
|
24
45
|
import {
|
|
25
46
|
createAcceptRevisionCommand,
|
|
26
47
|
createRejectRevisionCommand,
|
|
@@ -37,6 +58,7 @@ import {
|
|
|
37
58
|
} from "./text-commands.ts";
|
|
38
59
|
import type {
|
|
39
60
|
BlockNode,
|
|
61
|
+
MutableCanonicalDocument,
|
|
40
62
|
RevisionRecord as CanonicalRevisionRecord,
|
|
41
63
|
} from "../../model/canonical-document.ts";
|
|
42
64
|
import { remapCommentThreads } from "../../review/store/comment-remapping.ts";
|
|
@@ -95,7 +117,7 @@ import { insertPageBreak, insertTable } from "./text-commands.ts";
|
|
|
95
117
|
// is a **diamond** dependency (A → B, A → C, B → C), NOT a module-level
|
|
96
118
|
// cycle — `text-commands.ts` does not re-enter `core/commands/index.ts`
|
|
97
119
|
// or `runtime/edit-ops/`. Verified 2026-04-20 by static DFS; see
|
|
98
|
-
//
|
|
120
|
+
// CLAUDE.md (lane status table) §F3. Keep it this way — don't add
|
|
99
121
|
// imports from `runtime/` that route through this file, as that could
|
|
100
122
|
// introduce a real cycle.
|
|
101
123
|
import { editLayer } from "../../runtime/edit-ops/index.ts";
|
|
@@ -527,7 +549,7 @@ export function executeEditorCommand(
|
|
|
527
549
|
entry.affectedAnchor
|
|
528
550
|
? {
|
|
529
551
|
...entry,
|
|
530
|
-
affectedAnchor:
|
|
552
|
+
affectedAnchor: mapRecordAnchor(entry.affectedAnchor, mapping),
|
|
531
553
|
}
|
|
532
554
|
: entry,
|
|
533
555
|
),
|
|
@@ -574,7 +596,7 @@ export function executeEditorCommand(
|
|
|
574
596
|
entry.affectedAnchor
|
|
575
597
|
? {
|
|
576
598
|
...entry,
|
|
577
|
-
affectedAnchor:
|
|
599
|
+
affectedAnchor: mapRecordAnchor(entry.affectedAnchor, mapping),
|
|
578
600
|
}
|
|
579
601
|
: entry,
|
|
580
602
|
),
|
|
@@ -1363,7 +1385,7 @@ export function applyDocumentPatch(
|
|
|
1363
1385
|
contentChildren?: ContentChildrenPatch,
|
|
1364
1386
|
): CanonicalDocumentEnvelope {
|
|
1365
1387
|
const nextContent = resolvePatchedContent(base, patch, contentChildren);
|
|
1366
|
-
const merged:
|
|
1388
|
+
const merged: MutableCanonicalDocument = { ...base, ...patch };
|
|
1367
1389
|
if (nextContent) {
|
|
1368
1390
|
merged.content = nextContent;
|
|
1369
1391
|
}
|
|
@@ -1445,7 +1467,7 @@ function buildDocumentReplaceTransaction(
|
|
|
1445
1467
|
entry.affectedAnchor
|
|
1446
1468
|
? {
|
|
1447
1469
|
...entry,
|
|
1448
|
-
affectedAnchor:
|
|
1470
|
+
affectedAnchor: mapRecordAnchor(entry.affectedAnchor, mapping),
|
|
1449
1471
|
}
|
|
1450
1472
|
: entry,
|
|
1451
1473
|
),
|
|
@@ -2094,7 +2116,7 @@ function mapWarningsThroughMapping(
|
|
|
2094
2116
|
warning.affectedAnchor
|
|
2095
2117
|
? {
|
|
2096
2118
|
...warning,
|
|
2097
|
-
affectedAnchor:
|
|
2119
|
+
affectedAnchor: mapRecordAnchor(warning.affectedAnchor, mapping),
|
|
2098
2120
|
}
|
|
2099
2121
|
: warning,
|
|
2100
2122
|
);
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
AbstractNumberingDefinition,
|
|
3
3
|
BlockNode,
|
|
4
4
|
CanonicalDocument as CanonicalDocumentEnvelope,
|
|
5
|
+
MutableCanonicalDocument,
|
|
5
6
|
NumberingCatalog,
|
|
6
7
|
NumberingInstance,
|
|
7
8
|
ParagraphNode,
|
|
@@ -498,14 +499,14 @@ function ensureNumberingCatalog(value: NumberingCatalog): NumberingCatalog {
|
|
|
498
499
|
function cloneEnvelope(
|
|
499
500
|
document: CanonicalDocumentEnvelope,
|
|
500
501
|
timestamp: string,
|
|
501
|
-
):
|
|
502
|
+
): MutableCanonicalDocument {
|
|
502
503
|
return {
|
|
503
504
|
...structuredClone(document),
|
|
504
505
|
updatedAt: timestamp,
|
|
505
506
|
};
|
|
506
507
|
}
|
|
507
508
|
|
|
508
|
-
function captureEditableParagraphs(document:
|
|
509
|
+
function captureEditableParagraphs(document: MutableCanonicalDocument): ParagraphNode[] {
|
|
509
510
|
if (isDocumentRoot(document.content)) {
|
|
510
511
|
const paragraphs: ParagraphNode[] = [];
|
|
511
512
|
collectEditableParagraphs(document.content.children as BlockNode[], paragraphs);
|