@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,476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @endStateApi v3 — `ui.chrome` family (layer 10).
|
|
3
|
+
*
|
|
4
|
+
* Slice 4 wiring:
|
|
5
|
+
* - getPosture — live-with-adapter. Single composition site for
|
|
6
|
+
* ChromePosture. Reads the guard verdict from
|
|
7
|
+
* `handle.getInteractionGuardSnapshot()` and the
|
|
8
|
+
* mode/readOnly from `handle.getRenderSnapshot()`;
|
|
9
|
+
* overlays the host-provided slice (reviewMode,
|
|
10
|
+
* markupDisplay, debugMode, chromePreset) via
|
|
11
|
+
* `controller.getHostPosture()`. Host defaults
|
|
12
|
+
* applied at this layer — debugMode defaults to
|
|
13
|
+
* "off" per U6 + CLAUDE.md Protected Invariants.
|
|
14
|
+
* - getPinnedSurfaces — live-with-adapter. Delegates to
|
|
15
|
+
* `controller.getPinnedSurfaces()`; returns [] when
|
|
16
|
+
* unbound or the hook is absent. The existing
|
|
17
|
+
* `chrome-preset-model` lives in the presentation
|
|
18
|
+
* layer; the controller adapts it.
|
|
19
|
+
* - subscribe — live-with-adapter. Delegates to
|
|
20
|
+
* `controller.subscribeChrome`.
|
|
21
|
+
*
|
|
22
|
+
* Contract U5 — chrome posture is composed once per read at this single
|
|
23
|
+
* site. Components that call getPosture() do not re-derive from individual
|
|
24
|
+
* inputs. `refactor/10 Slice 6` migrates chrome components off their
|
|
25
|
+
* direct `getInteractionGuardSnapshot()` reads.
|
|
26
|
+
*
|
|
27
|
+
* Contract U6 — debug mode defaults "off". The default MUST survive every
|
|
28
|
+
* code path in this file. CLAUDE.md flags this has regressed multiple
|
|
29
|
+
* times; `test/api/v3/ui/chrome-posture-shape.test.ts` asserts it.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import type { ApiV3FnMetadata } from "../_layer-metadata.ts";
|
|
33
|
+
import type {
|
|
34
|
+
ChromePosture,
|
|
35
|
+
ChromeSurface,
|
|
36
|
+
ChromeDocumentMode,
|
|
37
|
+
UiListener,
|
|
38
|
+
UiUnsubscribe,
|
|
39
|
+
} from "./_types.ts";
|
|
40
|
+
import type {
|
|
41
|
+
ChromeComposition,
|
|
42
|
+
ChromeCompositionInput,
|
|
43
|
+
EditorRailTab,
|
|
44
|
+
} from "./chrome-composition.ts";
|
|
45
|
+
import { resolveChromeComposition } from "./chrome-composition.ts";
|
|
46
|
+
import type { UiApiContext } from "./_context.ts";
|
|
47
|
+
import { emitUxResponse } from "../_ux-response.ts";
|
|
48
|
+
|
|
49
|
+
export const getPostureMetadata: ApiV3FnMetadata = {
|
|
50
|
+
name: "ui.chrome.getPosture",
|
|
51
|
+
status: "live-with-adapter",
|
|
52
|
+
sourceLayer: "presentation",
|
|
53
|
+
liveEvidence: {
|
|
54
|
+
runnerTest: "test/api/v3/ui/chrome-posture-shape.test.ts",
|
|
55
|
+
commit: "refactor-10-slice-4",
|
|
56
|
+
},
|
|
57
|
+
uxIntent: { uiVisible: false },
|
|
58
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "session", auditCategory: "ui-chrome-read" },
|
|
59
|
+
stateClass: "C-local",
|
|
60
|
+
persistsTo: "none",
|
|
61
|
+
rwdReference:
|
|
62
|
+
"§UI API § ui.chrome.getPosture. Composes handle.getInteractionGuardSnapshot() + handle.getRenderSnapshot() with controller.getHostPosture() at a single site. debugMode defaults 'off' per U6.",
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const getCompositionMetadata: ApiV3FnMetadata = {
|
|
66
|
+
name: "ui.chrome.getComposition",
|
|
67
|
+
status: "live",
|
|
68
|
+
sourceLayer: "presentation",
|
|
69
|
+
liveEvidence: {
|
|
70
|
+
runnerTest: "test/api/v3/ui/chrome-composition-shape.test.ts",
|
|
71
|
+
commit: "refactor-10-slice-9",
|
|
72
|
+
},
|
|
73
|
+
uxIntent: { uiVisible: false },
|
|
74
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "session", auditCategory: "ui-chrome-read" },
|
|
75
|
+
stateClass: "C-local",
|
|
76
|
+
persistsTo: "none",
|
|
77
|
+
rwdReference:
|
|
78
|
+
"§UI API § ui.chrome.getComposition (U5.b). Pure function — wraps resolveChromeComposition from src/api/v3/ui/chrome-composition.ts. Input is host-supplied; output is the single ChromeComposition record every chrome surface reads. Caller memoizes.",
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const getPinnedSurfacesMetadata: ApiV3FnMetadata = {
|
|
82
|
+
name: "ui.chrome.getPinnedSurfaces",
|
|
83
|
+
status: "live-with-adapter",
|
|
84
|
+
sourceLayer: "presentation",
|
|
85
|
+
liveEvidence: {
|
|
86
|
+
runnerTest: "test/api/v3/ui/chrome-pinned-surfaces.test.ts",
|
|
87
|
+
commit: "refactor-10-slice-4",
|
|
88
|
+
},
|
|
89
|
+
uxIntent: { uiVisible: false },
|
|
90
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "session", auditCategory: "ui-chrome-read" },
|
|
91
|
+
stateClass: "C-local",
|
|
92
|
+
persistsTo: "none",
|
|
93
|
+
rwdReference:
|
|
94
|
+
"§UI API § ui.chrome.getPinnedSurfaces. Adapter delegates to controller.getPinnedSurfaces which wraps chrome-preset-model on the bind-side.",
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const subscribeMetadata: ApiV3FnMetadata = {
|
|
98
|
+
name: "ui.chrome.subscribe",
|
|
99
|
+
status: "live-with-adapter",
|
|
100
|
+
sourceLayer: "presentation",
|
|
101
|
+
liveEvidence: {
|
|
102
|
+
runnerTest: "test/api/v3/ui/chrome-posture-coherence.test.ts",
|
|
103
|
+
commit: "refactor-07-slice-2",
|
|
104
|
+
},
|
|
105
|
+
// Stream-form bidirectional channel (U5 + U7). Posture changes
|
|
106
|
+
// (guard-snapshot updates, host posture flips like debugMode toggle)
|
|
107
|
+
// fan out through this listener. Coalescing is microtask — posture
|
|
108
|
+
// composes at most once per render tick, no rAF batching needed.
|
|
109
|
+
uxIntent: {
|
|
110
|
+
uiVisible: true,
|
|
111
|
+
expectsUxResponse: "surface-refresh",
|
|
112
|
+
expectedDelta: "chrome subscriber attached; posture changes propagate through the listener",
|
|
113
|
+
},
|
|
114
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "session", auditCategory: "ui-chrome-subscribe" },
|
|
115
|
+
stateClass: "C-local",
|
|
116
|
+
persistsTo: "none",
|
|
117
|
+
bidirectional: true,
|
|
118
|
+
subscriptionShape: {
|
|
119
|
+
eventType: "ui.chrome.posture-changed",
|
|
120
|
+
payloadType: "ChromePosture",
|
|
121
|
+
coalescing: "microtask",
|
|
122
|
+
},
|
|
123
|
+
rwdReference: "§UI API § ui.chrome.subscribe. Adapter delegates to controller.subscribeChrome. Subscribe call emits one `ux.response.ui.chrome.subscribe` acknowledgement; per-change ChromePosture deliveries flow through the listener (microtask-coalesced).",
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Normalize a `DocumentMode` (long form — "editing" / "suggesting" /
|
|
128
|
+
* "viewing" / "commenting") OR a `WorkflowScopeMode` (short form —
|
|
129
|
+
* "edit" / "suggest" / "view" / "comment") to the ChromePosture short
|
|
130
|
+
* form. Unknown values fall back to "edit" — the most permissive posture
|
|
131
|
+
* is the safest default.
|
|
132
|
+
*/
|
|
133
|
+
function normalizeDocumentMode(raw: string | undefined | null): ChromeDocumentMode {
|
|
134
|
+
switch (raw) {
|
|
135
|
+
case "editing":
|
|
136
|
+
case "edit":
|
|
137
|
+
return "edit";
|
|
138
|
+
case "suggesting":
|
|
139
|
+
case "suggest":
|
|
140
|
+
return "suggest";
|
|
141
|
+
case "commenting":
|
|
142
|
+
case "comment":
|
|
143
|
+
return "comment";
|
|
144
|
+
case "viewing":
|
|
145
|
+
case "view":
|
|
146
|
+
return "view";
|
|
147
|
+
default:
|
|
148
|
+
return "edit";
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function normalizeEffectiveMode(
|
|
153
|
+
mode: string | undefined | null,
|
|
154
|
+
): ChromePosture["effectiveMode"] {
|
|
155
|
+
if (mode === "blocked") return "blocked";
|
|
156
|
+
return normalizeDocumentMode(mode);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Rail-state metadata — class-C per-session local state, owned by L10
|
|
160
|
+
// per the three-way state model (§9.1).
|
|
161
|
+
|
|
162
|
+
export const getActiveRailTabMetadata: ApiV3FnMetadata = {
|
|
163
|
+
name: "ui.chrome.getActiveRailTab",
|
|
164
|
+
status: "live",
|
|
165
|
+
sourceLayer: "presentation",
|
|
166
|
+
liveEvidence: {
|
|
167
|
+
runnerTest: "test/api/v3/ui/chrome-rail-state.test.ts",
|
|
168
|
+
commit: "refactor-10-task-2-rail-state",
|
|
169
|
+
},
|
|
170
|
+
uxIntent: { uiVisible: false },
|
|
171
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "session", auditCategory: "ui-chrome-read" },
|
|
172
|
+
stateClass: "C-local",
|
|
173
|
+
persistsTo: "none",
|
|
174
|
+
rwdReference: "§UI API § ui.chrome.getActiveRailTab. Class-C rail state read — null when no tab is active. UI API owns the state; hosts persist across reloads via beforeunload save/restore if needed (no localStorage inside L10).",
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export const setActiveRailTabMetadata: ApiV3FnMetadata = {
|
|
178
|
+
name: "ui.chrome.setActiveRailTab",
|
|
179
|
+
status: "live",
|
|
180
|
+
sourceLayer: "presentation",
|
|
181
|
+
liveEvidence: {
|
|
182
|
+
runnerTest: "test/api/v3/ui/chrome-rail-state.test.ts",
|
|
183
|
+
commit: "refactor-10-task-2-rail-state",
|
|
184
|
+
},
|
|
185
|
+
uxIntent: {
|
|
186
|
+
uiVisible: true,
|
|
187
|
+
expectsUxResponse: "surface-refresh",
|
|
188
|
+
expectedDelta: "rail active tab updated; subscribers receive the new value; next getComposition reflects the change",
|
|
189
|
+
},
|
|
190
|
+
agentMetadata: { readOrMutate: "mutate", boundedScope: "session", auditCategory: "ui-chrome-set-rail-state" },
|
|
191
|
+
stateClass: "C-local",
|
|
192
|
+
persistsTo: "none",
|
|
193
|
+
rwdReference: "§UI API § ui.chrome.setActiveRailTab (null closes the rail).",
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export const getPinnedRailTabsMetadata: ApiV3FnMetadata = {
|
|
197
|
+
name: "ui.chrome.getPinnedRailTabs",
|
|
198
|
+
status: "live",
|
|
199
|
+
sourceLayer: "presentation",
|
|
200
|
+
liveEvidence: {
|
|
201
|
+
runnerTest: "test/api/v3/ui/chrome-rail-state.test.ts",
|
|
202
|
+
commit: "refactor-10-task-2-rail-state",
|
|
203
|
+
},
|
|
204
|
+
uxIntent: { uiVisible: false },
|
|
205
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "session", auditCategory: "ui-chrome-read" },
|
|
206
|
+
stateClass: "C-local",
|
|
207
|
+
persistsTo: "none",
|
|
208
|
+
rwdReference: "§UI API § ui.chrome.getPinnedRailTabs.",
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export const pinRailTabMetadata: ApiV3FnMetadata = {
|
|
212
|
+
name: "ui.chrome.pinRailTab",
|
|
213
|
+
status: "live",
|
|
214
|
+
sourceLayer: "presentation",
|
|
215
|
+
liveEvidence: {
|
|
216
|
+
runnerTest: "test/api/v3/ui/chrome-rail-state.test.ts",
|
|
217
|
+
commit: "refactor-10-task-2-rail-state",
|
|
218
|
+
},
|
|
219
|
+
uxIntent: {
|
|
220
|
+
uiVisible: true,
|
|
221
|
+
expectsUxResponse: "surface-refresh",
|
|
222
|
+
expectedDelta: "rail tab pinned; next getComposition reflects the pinned set",
|
|
223
|
+
},
|
|
224
|
+
agentMetadata: { readOrMutate: "mutate", boundedScope: "session", auditCategory: "ui-chrome-set-rail-state" },
|
|
225
|
+
stateClass: "C-local",
|
|
226
|
+
persistsTo: "none",
|
|
227
|
+
rwdReference: "§UI API § ui.chrome.pinRailTab. Idempotent.",
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
export const unpinRailTabMetadata: ApiV3FnMetadata = {
|
|
231
|
+
name: "ui.chrome.unpinRailTab",
|
|
232
|
+
status: "live",
|
|
233
|
+
sourceLayer: "presentation",
|
|
234
|
+
liveEvidence: {
|
|
235
|
+
runnerTest: "test/api/v3/ui/chrome-rail-state.test.ts",
|
|
236
|
+
commit: "refactor-10-task-2-rail-state",
|
|
237
|
+
},
|
|
238
|
+
uxIntent: {
|
|
239
|
+
uiVisible: true,
|
|
240
|
+
expectsUxResponse: "surface-refresh",
|
|
241
|
+
expectedDelta: "rail tab unpinned; next getComposition reflects the pinned set",
|
|
242
|
+
},
|
|
243
|
+
agentMetadata: { readOrMutate: "mutate", boundedScope: "session", auditCategory: "ui-chrome-set-rail-state" },
|
|
244
|
+
stateClass: "C-local",
|
|
245
|
+
persistsTo: "none",
|
|
246
|
+
rwdReference: "§UI API § ui.chrome.unpinRailTab. Idempotent.",
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
export const subscribeRailStateMetadata: ApiV3FnMetadata = {
|
|
250
|
+
name: "ui.chrome.subscribeRailState",
|
|
251
|
+
status: "live",
|
|
252
|
+
sourceLayer: "presentation",
|
|
253
|
+
liveEvidence: {
|
|
254
|
+
runnerTest: "test/api/v3/ui/chrome-rail-state.test.ts",
|
|
255
|
+
commit: "refactor-10-task-2-rail-state",
|
|
256
|
+
},
|
|
257
|
+
uxIntent: {
|
|
258
|
+
uiVisible: true,
|
|
259
|
+
expectsUxResponse: "surface-refresh",
|
|
260
|
+
expectedDelta: "rail-state subscriber attached; receives the new active tab on every set/pin/unpin that mutated state",
|
|
261
|
+
},
|
|
262
|
+
agentMetadata: { readOrMutate: "read", boundedScope: "session", auditCategory: "ui-chrome-subscribe" },
|
|
263
|
+
stateClass: "C-local",
|
|
264
|
+
persistsTo: "none",
|
|
265
|
+
bidirectional: true,
|
|
266
|
+
subscriptionShape: {
|
|
267
|
+
eventType: "ui.chrome.rail_state_changed",
|
|
268
|
+
payloadType: "EditorRailTab | null",
|
|
269
|
+
coalescing: "microtask",
|
|
270
|
+
},
|
|
271
|
+
rwdReference: "§UI API § ui.chrome.subscribeRailState. Fires on any mutation to active-tab or pinned-set. Payload is the new active tab; consumers read the pinned set via getPinnedRailTabs.",
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
export function createChromeFamily(ctx: UiApiContext) {
|
|
275
|
+
// Closure-local class-C rail state. Per-createUiApi-instance — two
|
|
276
|
+
// instances over the same handle hold independent rail state (same
|
|
277
|
+
// isolation pattern as U9 overlay-visibility).
|
|
278
|
+
//
|
|
279
|
+
// `activeRailTabSet` distinguishes "user explicitly set activeRailTab"
|
|
280
|
+
// (including to `null`, meaning "close the rail") from "user hasn't
|
|
281
|
+
// expressed an opinion" (composer picks a mode-default). Without this
|
|
282
|
+
// flag, getComposition would force `null` for every caller that
|
|
283
|
+
// previously relied on the composer's mode-appropriate default.
|
|
284
|
+
let activeRailTab: EditorRailTab | null = null;
|
|
285
|
+
let activeRailTabSet = false;
|
|
286
|
+
const pinnedRailTabs = new Set<EditorRailTab>();
|
|
287
|
+
const railStateSubscribers = new Set<UiListener<EditorRailTab | null>>();
|
|
288
|
+
|
|
289
|
+
function notifyRailStateSubscribers(): void {
|
|
290
|
+
for (const listener of railStateSubscribers) {
|
|
291
|
+
try {
|
|
292
|
+
listener(activeRailTab);
|
|
293
|
+
} catch {
|
|
294
|
+
// Isolate listener errors.
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
getPosture(): ChromePosture {
|
|
301
|
+
// Runtime-sourced slice — the guard + mode + readOnly. Per DS §8.3
|
|
302
|
+
// (runtime owns truth) we trust the RuntimeApiHandle contract —
|
|
303
|
+
// `getInteractionGuardSnapshot` and `getRenderSnapshot` are
|
|
304
|
+
// unconditional on the handle interface. A throw here is a runtime
|
|
305
|
+
// regression, not a "pre-mount" state, and masking it with a
|
|
306
|
+
// silent fallback would let the UI declare `effectiveMode: "edit"`
|
|
307
|
+
// while the substrate is broken. Pre-mount handles use the
|
|
308
|
+
// inert-facet pattern from `loading-runtime-bridge.ts`.
|
|
309
|
+
const guard = ctx.handle.getInteractionGuardSnapshot() as
|
|
310
|
+
| { effectiveMode?: string; blockedReasons?: ReadonlyArray<{ code?: string; reason?: string } | string> }
|
|
311
|
+
| undefined
|
|
312
|
+
| null;
|
|
313
|
+
const effectiveMode: ChromePosture["effectiveMode"] = guard
|
|
314
|
+
? normalizeEffectiveMode(guard.effectiveMode)
|
|
315
|
+
: "edit";
|
|
316
|
+
const blockedReasons: readonly string[] =
|
|
317
|
+
guard && Array.isArray(guard.blockedReasons)
|
|
318
|
+
? guard.blockedReasons.map((r) =>
|
|
319
|
+
typeof r === "string" ? r : (r?.code ?? r?.reason ?? "blocked"),
|
|
320
|
+
)
|
|
321
|
+
: [];
|
|
322
|
+
|
|
323
|
+
const render = ctx.handle.getRenderSnapshot() as
|
|
324
|
+
| { documentMode?: string; readOnly?: boolean }
|
|
325
|
+
| undefined
|
|
326
|
+
| null;
|
|
327
|
+
const documentMode: ChromeDocumentMode = render
|
|
328
|
+
? normalizeDocumentMode(render.documentMode)
|
|
329
|
+
: "edit";
|
|
330
|
+
const readOnly = render?.readOnly === true;
|
|
331
|
+
|
|
332
|
+
// Host-provided slice — reviewMode / markupDisplay / debugMode /
|
|
333
|
+
// chromePreset. Defaults applied here; U6 — debugMode MUST default
|
|
334
|
+
// to "off" (CLAUDE.md Protected Invariants).
|
|
335
|
+
const host = ctx.binding?.controller.getHostPosture?.() ?? undefined;
|
|
336
|
+
const reviewMode = host?.reviewMode ?? "observer";
|
|
337
|
+
const markupDisplay = host?.markupDisplay ?? "final";
|
|
338
|
+
const debugMode = host?.debugMode ?? "off";
|
|
339
|
+
const chromePreset = host?.chromePreset;
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
effectiveMode,
|
|
343
|
+
documentMode,
|
|
344
|
+
readOnly,
|
|
345
|
+
reviewMode,
|
|
346
|
+
markupDisplay,
|
|
347
|
+
debugMode,
|
|
348
|
+
chromePreset,
|
|
349
|
+
blockedReasons,
|
|
350
|
+
};
|
|
351
|
+
},
|
|
352
|
+
getComposition(input: ChromeCompositionInput): ChromeComposition {
|
|
353
|
+
// U5.b — compose with class-C rail state the UI API owns. The
|
|
354
|
+
// caller can override `activeRailTab` / `pinnedRailTabs` via input;
|
|
355
|
+
// when omitted AND the UI API's internal state has been set,
|
|
356
|
+
// internal state fills in. When the caller omits AND internal state
|
|
357
|
+
// is untouched (the initial-mount path), the composer's mode-default
|
|
358
|
+
// kicks in (e.g. rail open on "advanced" preset picks a default
|
|
359
|
+
// active tab per mode). This preserves back-compat for callers that
|
|
360
|
+
// never interacted with rail-state methods.
|
|
361
|
+
const merged: ChromeCompositionInput = {
|
|
362
|
+
...input,
|
|
363
|
+
...(input.activeRailTab !== undefined
|
|
364
|
+
? {}
|
|
365
|
+
: activeRailTabSet
|
|
366
|
+
? { activeRailTab }
|
|
367
|
+
: {}),
|
|
368
|
+
...(input.pinnedRailTabs !== undefined
|
|
369
|
+
? {}
|
|
370
|
+
: pinnedRailTabs.size > 0
|
|
371
|
+
? { pinnedRailTabs }
|
|
372
|
+
: {}),
|
|
373
|
+
};
|
|
374
|
+
return resolveChromeComposition(merged);
|
|
375
|
+
},
|
|
376
|
+
getPinnedSurfaces(): readonly ChromeSurface[] {
|
|
377
|
+
const hook = ctx.binding?.controller.getPinnedSurfaces;
|
|
378
|
+
return hook ? hook() : [];
|
|
379
|
+
},
|
|
380
|
+
subscribe(listener: UiListener<ChromePosture>): UiUnsubscribe {
|
|
381
|
+
const controller = ctx.binding?.controller;
|
|
382
|
+
if (!controller) {
|
|
383
|
+
throw new Error(
|
|
384
|
+
"ui.chrome.subscribe: no controller bound — call ui.session.bind(controller) first",
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
if (!controller.subscribeChrome) {
|
|
388
|
+
throw new Error(
|
|
389
|
+
`ui.chrome.subscribe: controller of kind "${controller.kind}" did not provide a subscribeChrome hook`,
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
const unsubscribe = controller.subscribeChrome(listener);
|
|
393
|
+
emitUxResponse(ctx.handle, {
|
|
394
|
+
apiFn: subscribeMetadata.name,
|
|
395
|
+
intent: subscribeMetadata.uxIntent.expectedDelta ?? "",
|
|
396
|
+
mockOrLive: "live-with-adapter",
|
|
397
|
+
uiVisible: true,
|
|
398
|
+
expectedDelta: subscribeMetadata.uxIntent.expectedDelta,
|
|
399
|
+
actualDelta: { kind: "surface-refresh", payload: { subscribed: "ui.chrome" } },
|
|
400
|
+
});
|
|
401
|
+
return unsubscribe;
|
|
402
|
+
},
|
|
403
|
+
|
|
404
|
+
// ----- Class-C rail state (Task 2) -----
|
|
405
|
+
|
|
406
|
+
getActiveRailTab(): EditorRailTab | null {
|
|
407
|
+
return activeRailTab;
|
|
408
|
+
},
|
|
409
|
+
|
|
410
|
+
setActiveRailTab(tab: EditorRailTab | null): void {
|
|
411
|
+
if (activeRailTabSet && activeRailTab === tab) return; // idempotent.
|
|
412
|
+
activeRailTab = tab;
|
|
413
|
+
activeRailTabSet = true;
|
|
414
|
+
emitUxResponse(ctx.handle, {
|
|
415
|
+
apiFn: setActiveRailTabMetadata.name,
|
|
416
|
+
intent: setActiveRailTabMetadata.uxIntent.expectedDelta ?? "",
|
|
417
|
+
mockOrLive: "live",
|
|
418
|
+
uiVisible: true,
|
|
419
|
+
expectedDelta: setActiveRailTabMetadata.uxIntent.expectedDelta,
|
|
420
|
+
actualDelta: { kind: "surface-refresh", payload: { activeRailTab: tab } },
|
|
421
|
+
});
|
|
422
|
+
notifyRailStateSubscribers();
|
|
423
|
+
},
|
|
424
|
+
|
|
425
|
+
getPinnedRailTabs(): ReadonlySet<EditorRailTab> {
|
|
426
|
+
// Return a defensive snapshot — mutation on the caller side must
|
|
427
|
+
// not leak into UI API state.
|
|
428
|
+
return new Set(pinnedRailTabs);
|
|
429
|
+
},
|
|
430
|
+
|
|
431
|
+
pinRailTab(tab: EditorRailTab): void {
|
|
432
|
+
if (pinnedRailTabs.has(tab)) return;
|
|
433
|
+
pinnedRailTabs.add(tab);
|
|
434
|
+
emitUxResponse(ctx.handle, {
|
|
435
|
+
apiFn: pinRailTabMetadata.name,
|
|
436
|
+
intent: pinRailTabMetadata.uxIntent.expectedDelta ?? "",
|
|
437
|
+
mockOrLive: "live",
|
|
438
|
+
uiVisible: true,
|
|
439
|
+
expectedDelta: pinRailTabMetadata.uxIntent.expectedDelta,
|
|
440
|
+
actualDelta: { kind: "surface-refresh", payload: { pinned: tab } },
|
|
441
|
+
});
|
|
442
|
+
notifyRailStateSubscribers();
|
|
443
|
+
},
|
|
444
|
+
|
|
445
|
+
unpinRailTab(tab: EditorRailTab): void {
|
|
446
|
+
if (!pinnedRailTabs.has(tab)) return;
|
|
447
|
+
pinnedRailTabs.delete(tab);
|
|
448
|
+
emitUxResponse(ctx.handle, {
|
|
449
|
+
apiFn: unpinRailTabMetadata.name,
|
|
450
|
+
intent: unpinRailTabMetadata.uxIntent.expectedDelta ?? "",
|
|
451
|
+
mockOrLive: "live",
|
|
452
|
+
uiVisible: true,
|
|
453
|
+
expectedDelta: unpinRailTabMetadata.uxIntent.expectedDelta,
|
|
454
|
+
actualDelta: { kind: "surface-refresh", payload: { unpinned: tab } },
|
|
455
|
+
});
|
|
456
|
+
notifyRailStateSubscribers();
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
subscribeRailState(
|
|
460
|
+
listener: UiListener<EditorRailTab | null>,
|
|
461
|
+
): UiUnsubscribe {
|
|
462
|
+
railStateSubscribers.add(listener);
|
|
463
|
+
emitUxResponse(ctx.handle, {
|
|
464
|
+
apiFn: subscribeRailStateMetadata.name,
|
|
465
|
+
intent: subscribeRailStateMetadata.uxIntent.expectedDelta ?? "",
|
|
466
|
+
mockOrLive: "live",
|
|
467
|
+
uiVisible: true,
|
|
468
|
+
expectedDelta: subscribeRailStateMetadata.uxIntent.expectedDelta,
|
|
469
|
+
actualDelta: { kind: "surface-refresh", payload: { subscribed: "ui.chrome.railState" } },
|
|
470
|
+
});
|
|
471
|
+
return () => {
|
|
472
|
+
railStateSubscribers.delete(listener);
|
|
473
|
+
};
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @endStateApi v3 — `ui.debug` family (layer 10).
|
|
3
|
+
*
|
|
4
|
+
* Slice 5 wiring:
|
|
5
|
+
* - attach — live-with-adapter. Delegates to the bound controller's
|
|
6
|
+
* `attachDebug` hook, which on the bind-side wires
|
|
7
|
+
* `runtime.debug.bus` + `runtime.debug.getSnapshot` to the
|
|
8
|
+
* mounted debug surface. Returns a DebugAttachment whose
|
|
9
|
+
* `detach` invokes the controller-provided cleanup and
|
|
10
|
+
* clears the UI API's attachment record.
|
|
11
|
+
* - detach — live-with-adapter. Ends the current attachment (if any)
|
|
12
|
+
* by invoking its controller-provided cleanup. Idempotent
|
|
13
|
+
* — calling detach without an attachment is a no-op.
|
|
14
|
+
*
|
|
15
|
+
* Phase Q (runtime-embedded debug UX at `src/ui-tailwind/debug/**`) is the
|
|
16
|
+
* presentation consumer of this surface. Per refactor plan 10 risk register
|
|
17
|
+
* #3 and §9 finishing-development-branch discipline, Slice 5 ships the
|
|
18
|
+
* substrate; the Phase Q React components + `debugMode` public prop +
|
|
19
|
+
* `test/ui/debug-mode-visibility-invariant.test.ts` land in a follow-up.
|
|
20
|
+
* `src/ui-tailwind/debug/README.md` documents the reserved scope.
|
|
21
|
+
*
|
|
22
|
+
* Contract U6 — debug attachment is opt-in and scoped. Default state:
|
|
23
|
+
* no debug UX. The `debugMode` prop gate (to-be-added) defaults to
|
|
24
|
+
* `"off"`. CLAUDE.md Protected Invariants flag this has regressed
|
|
25
|
+
* multiple times.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import type { ApiV3FnMetadata } from "../_layer-metadata.ts";
|
|
29
|
+
import type { DebugSession, DebugAttachment } from "./_types.ts";
|
|
30
|
+
import type { UiApiContext } from "./_context.ts";
|
|
31
|
+
|
|
32
|
+
export const attachMetadata: ApiV3FnMetadata = {
|
|
33
|
+
name: "ui.debug.attach",
|
|
34
|
+
status: "live-with-adapter",
|
|
35
|
+
sourceLayer: "presentation",
|
|
36
|
+
liveEvidence: {
|
|
37
|
+
runnerTest: "test/api/v3/ui/debug-attach.test.ts",
|
|
38
|
+
commit: "refactor-10-slice-5",
|
|
39
|
+
},
|
|
40
|
+
uxIntent: { uiVisible: false },
|
|
41
|
+
agentMetadata: { readOrMutate: "compound", boundedScope: "session", auditCategory: "ui-debug-attach" },
|
|
42
|
+
stateClass: "C-local",
|
|
43
|
+
persistsTo: "none",
|
|
44
|
+
rwdReference:
|
|
45
|
+
"§UI API § ui.debug.attach. Adapter delegates to UiController.attachDebug which on the bind-side wires runtime.debug.bus + getSnapshot to the mounted debug surface. Throws when no controller is bound or the hook is absent.",
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const detachMetadata: ApiV3FnMetadata = {
|
|
49
|
+
name: "ui.debug.detach",
|
|
50
|
+
status: "live-with-adapter",
|
|
51
|
+
sourceLayer: "presentation",
|
|
52
|
+
liveEvidence: {
|
|
53
|
+
runnerTest: "test/api/v3/ui/debug-attach.test.ts",
|
|
54
|
+
commit: "refactor-10-slice-5",
|
|
55
|
+
},
|
|
56
|
+
uxIntent: { uiVisible: false },
|
|
57
|
+
agentMetadata: { readOrMutate: "compound", boundedScope: "session", auditCategory: "ui-debug-detach" },
|
|
58
|
+
stateClass: "C-local",
|
|
59
|
+
persistsTo: "none",
|
|
60
|
+
rwdReference:
|
|
61
|
+
"§UI API § ui.debug.detach. Idempotent — invokes the controller-provided cleanup from the active attachment (if any) and clears the record.",
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
interface DebugAttachmentState {
|
|
65
|
+
session: DebugSession;
|
|
66
|
+
cleanup: () => void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function createDebugFamily(ctx: UiApiContext) {
|
|
70
|
+
// Per-instance attachment state. One UI API holds at most one active
|
|
71
|
+
// debug attachment; re-attaching tears down the prior attachment first.
|
|
72
|
+
let current: DebugAttachmentState | null = null;
|
|
73
|
+
|
|
74
|
+
function detachInternal(): void {
|
|
75
|
+
if (current) {
|
|
76
|
+
try {
|
|
77
|
+
current.cleanup();
|
|
78
|
+
} finally {
|
|
79
|
+
current = null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
attach(session: DebugSession): DebugAttachment {
|
|
86
|
+
const controller = ctx.binding?.controller;
|
|
87
|
+
if (!controller) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
"ui.debug.attach: no controller bound — call ui.session.bind(controller) first",
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
if (!controller.attachDebug) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`ui.debug.attach: controller of kind "${controller.kind}" did not provide an attachDebug hook`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
// Tear down any prior attachment so re-attaching does not leak
|
|
98
|
+
// subscriptions or telemetry-bus listeners.
|
|
99
|
+
detachInternal();
|
|
100
|
+
const cleanup = controller.attachDebug(session);
|
|
101
|
+
current = { session, cleanup };
|
|
102
|
+
// NOTE: `DebugAttachment.detach` is aliased to `detachInternal`,
|
|
103
|
+
// which detaches **whatever is currently attached** — not
|
|
104
|
+
// specifically the session this attachment was created with. At
|
|
105
|
+
// most one attachment is live per UI API instance, so this is
|
|
106
|
+
// semantically correct under the "single attachment at a time"
|
|
107
|
+
// model. Callers that hold onto an older `attachment` ref and
|
|
108
|
+
// call its `.detach()` after a re-attach will tear down the
|
|
109
|
+
// NEWER attachment. Typical hosts create + detach in lockstep
|
|
110
|
+
// (attach on mount, detach on unmount), so this is fine; the
|
|
111
|
+
// debug service's RPC driver re-attaches on every session, which
|
|
112
|
+
// matches this model.
|
|
113
|
+
return {
|
|
114
|
+
session,
|
|
115
|
+
detach: detachInternal,
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
detach(): void {
|
|
119
|
+
// Idempotent — no-op when nothing is attached. See U6; the default
|
|
120
|
+
// state is "no debug UX", so repeated detach calls must stay safe.
|
|
121
|
+
detachInternal();
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @endStateApi v3 — UI API barrel (layer 10).
|
|
3
|
+
*
|
|
4
|
+
* Public exports for `src/api/v3/ui/**`. Consumers import from this barrel
|
|
5
|
+
* rather than individual family files.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { createUiApi } from "./_create.ts";
|
|
9
|
+
export type {
|
|
10
|
+
ApiV3Ui,
|
|
11
|
+
ApiV3UiSession,
|
|
12
|
+
ApiV3UiSurface,
|
|
13
|
+
ApiV3UiViewport,
|
|
14
|
+
ApiV3UiOverlays,
|
|
15
|
+
ApiV3UiChrome,
|
|
16
|
+
ApiV3UiDebug,
|
|
17
|
+
ApiV3UiScope,
|
|
18
|
+
UiController,
|
|
19
|
+
UiControllerKind,
|
|
20
|
+
UiControllerFactory,
|
|
21
|
+
UiBinding,
|
|
22
|
+
UiListener,
|
|
23
|
+
UiUnsubscribe,
|
|
24
|
+
ViewportState,
|
|
25
|
+
ScrollTarget,
|
|
26
|
+
ScrollTargetBehavior,
|
|
27
|
+
SelectionRangeInput,
|
|
28
|
+
OverlayAnchorQuery,
|
|
29
|
+
OverlayKind,
|
|
30
|
+
OverlayVisibility,
|
|
31
|
+
WorkflowMarkupMode,
|
|
32
|
+
ChromePosture,
|
|
33
|
+
ChromeHostPosture,
|
|
34
|
+
ChromeSurface,
|
|
35
|
+
ChromeSurfaceKind,
|
|
36
|
+
ChromeDocumentMode,
|
|
37
|
+
ChromeMarkupDisplay,
|
|
38
|
+
ChromeReviewMode,
|
|
39
|
+
ChromeDebugMode,
|
|
40
|
+
DebugSession,
|
|
41
|
+
DebugAttachment,
|
|
42
|
+
GeometryRect,
|
|
43
|
+
} from "./_types.ts";
|
|
44
|
+
|
|
45
|
+
// Chrome composition types (U5.b) — relocated to layer 10 in Slice 8.
|
|
46
|
+
export { CHROME_RESPONSIVE_THRESHOLDS, resolveChromeComposition } from "./chrome-composition.ts";
|
|
47
|
+
export type {
|
|
48
|
+
ChromeComposition,
|
|
49
|
+
ChromeCompositionInput,
|
|
50
|
+
ChromeDensity,
|
|
51
|
+
ChromeResponsiveTier,
|
|
52
|
+
DiagnosticsSeverity,
|
|
53
|
+
DiagnosticsSignal,
|
|
54
|
+
EditorChromeMode,
|
|
55
|
+
EditorRailTab,
|
|
56
|
+
LocalSurfaceKind,
|
|
57
|
+
RailComposition,
|
|
58
|
+
WorkspaceRowComposition,
|
|
59
|
+
} from "./chrome-composition.ts";
|
|
60
|
+
export {
|
|
61
|
+
resolveChromePreset,
|
|
62
|
+
resolveChromePresetOptions,
|
|
63
|
+
resolveChromeVisibilityForPreset,
|
|
64
|
+
} from "./chrome-preset-model.ts";
|