@beyondwork/docx-react-component 1.0.66 → 1.0.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -931
- package/package.json +26 -27
- package/src/api/anchor-conversion.ts +43 -0
- package/src/api/editor-state-types.ts +2 -1
- package/src/api/public-types.ts +504 -101
- package/src/api/session-state.ts +4 -0
- package/src/api/v3/README.md +91 -0
- package/src/api/v3/_create.ts +146 -0
- package/src/api/v3/_layer-metadata.ts +362 -0
- package/src/api/v3/_mocks.ts +84 -0
- package/src/api/v3/_runtime-handle.ts +162 -0
- package/src/api/v3/_ux-response.ts +73 -0
- package/src/api/v3/ai/_metadata-audit.ts +225 -0
- package/src/api/v3/ai/attach.ts +235 -0
- package/src/api/v3/ai/bundle.ts +132 -0
- package/src/api/v3/ai/explain.ts +144 -0
- package/src/api/v3/ai/export.ts +54 -0
- package/src/api/v3/ai/inspect.ts +118 -0
- package/src/api/v3/ai/policy.ts +77 -0
- package/src/api/v3/ai/replacement.ts +341 -0
- package/src/api/v3/ai/resolve.ts +133 -0
- package/src/api/v3/index.ts +79 -0
- package/src/api/v3/runtime/chart.ts +310 -0
- package/src/api/v3/runtime/clipboard.ts +81 -0
- package/src/api/v3/runtime/collab.ts +331 -0
- package/src/api/v3/runtime/content.ts +236 -0
- package/src/api/v3/runtime/document.ts +282 -0
- package/src/api/v3/runtime/formatting.ts +186 -0
- package/src/api/v3/runtime/geometry.ts +349 -0
- package/src/api/v3/runtime/layout.ts +108 -0
- package/src/api/v3/runtime/review.ts +129 -0
- package/src/api/v3/runtime/search.ts +74 -0
- package/src/api/v3/runtime/table.ts +63 -0
- package/src/api/v3/runtime/workflow.ts +434 -0
- package/src/api/v3/ui/_context.ts +86 -0
- package/src/api/v3/ui/_create.ts +65 -0
- package/src/api/v3/ui/_types.ts +520 -0
- package/src/api/v3/ui/chrome-composition.ts +342 -0
- package/src/{ui-tailwind/chrome → api/v3/ui}/chrome-preset-model.ts +11 -1
- package/src/api/v3/ui/chrome.ts +476 -0
- package/src/api/v3/ui/debug.ts +124 -0
- package/src/api/v3/ui/index.ts +64 -0
- package/src/api/v3/ui/overlays-visibility.ts +170 -0
- package/src/api/v3/ui/overlays.ts +427 -0
- package/src/api/v3/ui/scope.ts +71 -0
- package/src/api/v3/ui/session.ts +100 -0
- package/src/api/v3/ui/surface.ts +170 -0
- package/src/api/v3/ui/viewport.ts +303 -0
- package/src/core/commands/index.ts +28 -6
- package/src/core/commands/list-commands.ts +3 -2
- package/src/core/commands/section-layout-commands.ts +9 -8
- package/src/core/schema/text-schema.ts +16 -0
- package/src/core/selection/mapping.ts +33 -72
- package/src/core/state/editor-state.ts +96 -189
- package/src/index.ts +23 -4
- package/src/io/chart-preview-resolver.ts +1 -1
- package/src/io/docx-session.ts +36 -4795
- package/src/io/export/build-app-properties-xml.ts +1 -1
- package/src/io/export/serialize-comments.ts +1 -1
- package/src/io/export/serialize-headers-footers.ts +6 -1
- package/src/io/export/serialize-main-document.ts +45 -0
- package/src/io/export/serialize-run-formatting.ts +17 -2
- package/src/io/export/twip.ts +1 -1
- package/src/io/normalize/normalize-text.ts +27 -20
- package/src/io/ooxml/chart/parse-series.ts +1 -1
- package/src/io/ooxml/chart/resolve-color.ts +2 -2
- package/src/io/ooxml/chart/types.ts +1 -1
- package/src/io/ooxml/classify-embedding.ts +83 -33
- package/src/io/ooxml/parse-fill.ts +1 -1
- package/src/io/ooxml/parse-main-document.ts +71 -1
- package/src/io/ooxml/parse-object.ts +14 -10
- package/src/io/ooxml/parse-run-formatting.ts +47 -1
- package/src/io/ooxml/property-grab-bag.ts +2 -2
- package/src/io/ooxml/units.ts +11 -0
- package/src/io/ooxml/workflow-payload.ts +282 -7
- package/src/model/anchor.ts +85 -0
- package/src/model/canonical-document.ts +351 -15
- package/src/model/chart-types.ts +1 -1
- package/src/model/layout/index.ts +83 -0
- package/src/model/layout/page-graph-types.ts +181 -0
- package/src/model/layout/page-layout-snapshot.ts +105 -0
- package/src/model/layout/resolved-layout-types.ts +47 -0
- package/src/model/layout/runtime-page-graph-types.ts +102 -0
- package/src/model/paragraph-scope-ids.ts +72 -0
- package/src/model/review/comment-types.ts +112 -0
- package/src/model/review/index.ts +2 -0
- package/src/model/review/revision-types.ts +215 -0
- package/src/model/snapshot.ts +32 -0
- package/src/review/store/comment-store.ts +21 -47
- package/src/review/store/revision-types.ts +40 -198
- package/src/runtime/collab/base-doc-fingerprint.ts +6 -1
- package/src/runtime/collab/runtime-collab-sync.ts +13 -3
- package/src/runtime/collab-session.ts +1 -1
- package/src/runtime/debug/build-debug-inspector-snapshot.ts +686 -0
- package/src/runtime/debug/event-ring-buffer.ts +64 -0
- package/src/runtime/debug/probability-sampler.ts +18 -0
- package/src/runtime/debug/runtime-debug-facet.ts +67 -0
- package/src/runtime/debug/stage-tokens.ts +31 -0
- package/src/runtime/debug/telemetry-bus.ts +271 -0
- package/src/runtime/debug/types.ts +275 -0
- package/src/runtime/debug/wrap-ref-for-telemetry.ts +118 -0
- package/src/runtime/document-layout.ts +8 -6
- package/src/runtime/document-runtime.ts +843 -1141
- package/src/runtime/document-search.ts +1 -1
- package/src/runtime/edit-ops/index.ts +1 -1
- package/src/runtime/external-send-runtime.ts +1 -1
- package/src/runtime/formatting/document-lookup.ts +235 -0
- package/src/runtime/formatting/field/registry.ts +41 -0
- package/src/runtime/{field-resolver.ts → formatting/field/resolver.ts} +27 -2
- package/src/runtime/formatting/font-resolution.ts +83 -0
- package/src/runtime/formatting/formatting-context.ts +903 -0
- package/src/runtime/formatting/formatting-types.ts +157 -0
- package/src/runtime/{hyperlink-color-resolver.ts → formatting/hyperlink-color.ts} +2 -2
- package/src/runtime/formatting/index.ts +125 -0
- package/src/runtime/{resolved-numbering-geometry.ts → formatting/numbering/geometry.ts} +1 -1
- package/src/runtime/{numbering-prefix.ts → formatting/numbering/prefix.ts} +170 -3
- package/src/runtime/formatting/paragraph-style-resolver.ts +92 -0
- package/src/runtime/formatting/projector.ts +75 -0
- package/src/runtime/formatting/resolve-effective.ts +407 -0
- package/src/runtime/formatting/revision-display.ts +105 -0
- package/src/runtime/{paragraph-style-resolver.ts → formatting/style-cascade.ts} +84 -141
- package/src/runtime/{table-style-resolver.ts → formatting/table-style-resolver.ts} +1 -1
- package/src/runtime/formatting/telemetry-bridge.ts +106 -0
- package/src/runtime/{theme-color-resolver.ts → formatting/theme-color.ts} +2 -30
- package/src/runtime/geometry/caret-geometry.ts +164 -0
- package/src/runtime/geometry/geometry-facet.ts +364 -0
- package/src/runtime/geometry/geometry-types.ts +256 -0
- package/src/runtime/geometry/hit-test.ts +125 -0
- package/src/runtime/geometry/index.ts +71 -0
- package/src/runtime/geometry/inert-geometry-facet.ts +43 -0
- package/src/runtime/geometry/invalidation.ts +35 -0
- package/src/runtime/geometry/object-handles.ts +77 -0
- package/src/runtime/geometry/overlay-rects.ts +85 -0
- package/src/runtime/geometry/project-anchors.ts +100 -0
- package/src/runtime/geometry/project-fragments.ts +216 -0
- package/src/runtime/geometry/projector.ts +129 -0
- package/src/runtime/geometry/replacement-envelope.ts +130 -0
- package/src/runtime/geometry/viewport.ts +218 -0
- package/src/runtime/layout/compat-input-ledger.ts +211 -0
- package/src/runtime/layout/index.ts +6 -1
- package/src/runtime/layout/inert-layout-facet.ts +12 -7
- package/src/runtime/layout/layout-engine-instance.ts +189 -11
- package/src/runtime/layout/layout-engine-version.ts +450 -1
- package/src/runtime/layout/layout-facet-types.ts +60 -0
- package/src/runtime/layout/layout-measurement-provider.ts +13 -0
- package/src/runtime/layout/measurement-backend-canvas.ts +14 -2
- package/src/runtime/layout/measurement-backend-empirical.ts +23 -4
- package/src/runtime/layout/page-graph.ts +62 -209
- package/src/runtime/layout/page-story-resolver.ts +7 -12
- package/src/runtime/layout/paginated-layout-engine.ts +186 -11
- package/src/runtime/layout/project-block-fragments.ts +11 -0
- package/src/runtime/layout/projector.ts +90 -0
- package/src/runtime/layout/public-facet.ts +187 -442
- package/src/runtime/layout/resolved-formatting-state.ts +158 -26
- package/src/runtime/layout/table-render-plan.ts +1 -1
- package/src/runtime/prerender/cache-envelope.ts +6 -1
- package/src/runtime/prerender/prerender-document.ts +18 -23
- package/src/runtime/render/decoration-resolver.ts +1 -1
- package/src/runtime/render/render-frame-types.ts +20 -0
- package/src/runtime/render/render-kernel.ts +94 -25
- package/src/runtime/scopes/_formatting-seam.ts +262 -0
- package/src/runtime/scopes/_scope-dependencies.ts +49 -0
- package/src/runtime/scopes/action-validation.ts +356 -0
- package/src/runtime/scopes/attach-explanation.ts +102 -0
- package/src/runtime/scopes/audit-bundle.ts +71 -0
- package/src/runtime/scopes/compile-scope-bundle.ts +163 -0
- package/src/runtime/scopes/compile-scope.ts +262 -0
- package/src/runtime/scopes/compiler-service.ts +431 -0
- package/src/runtime/scopes/create-issue.ts +107 -0
- package/src/runtime/scopes/enumerate-scopes.ts +543 -0
- package/src/runtime/scopes/evidence.ts +233 -0
- package/src/runtime/scopes/index.ts +150 -0
- package/src/runtime/scopes/position-map.ts +214 -0
- package/src/runtime/scopes/preservation-boundary.ts +91 -0
- package/src/runtime/scopes/projector.ts +49 -0
- package/src/runtime/scopes/replaceability.ts +87 -0
- package/src/runtime/scopes/replacement/apply.ts +228 -0
- package/src/runtime/scopes/replacement/compile.ts +59 -0
- package/src/runtime/scopes/replacement/propose.ts +42 -0
- package/src/runtime/scopes/resolve-reference.ts +347 -0
- package/src/runtime/scopes/review-bundle.ts +141 -0
- package/src/runtime/scopes/scope-kinds/_paragraph-text.ts +57 -0
- package/src/runtime/scopes/scope-kinds/_table-text.ts +42 -0
- package/src/runtime/scopes/scope-kinds/comment-thread.ts +59 -0
- package/src/runtime/scopes/scope-kinds/field.ts +65 -0
- package/src/runtime/scopes/scope-kinds/heading.ts +84 -0
- package/src/runtime/scopes/scope-kinds/list-item.ts +77 -0
- package/src/runtime/scopes/scope-kinds/paragraph.ts +182 -0
- package/src/runtime/scopes/scope-kinds/revision.ts +62 -0
- package/src/runtime/scopes/scope-kinds/table-cell.ts +57 -0
- package/src/runtime/scopes/scope-kinds/table-row.ts +61 -0
- package/src/runtime/scopes/scope-kinds/table.ts +55 -0
- package/src/runtime/scopes/scope-range.ts +208 -0
- package/src/runtime/scopes/semantic-scope-types.ts +454 -0
- package/src/runtime/scopes/workflow-overlap.ts +92 -0
- package/src/runtime/selection/index.ts +1 -1
- package/src/runtime/structure-ops/fragment-insert.ts +1 -1
- package/src/runtime/structure-ops/index.ts +1 -1
- package/src/runtime/surface-projection.ts +232 -262
- package/src/runtime/units.ts +4 -2
- package/src/runtime/workflow/coordinator.ts +1348 -0
- package/src/runtime/workflow/derived-scope-resolver.ts +125 -0
- package/src/runtime/workflow/index.ts +25 -0
- package/src/runtime/workflow/markup-mode-policy.ts +98 -0
- package/src/runtime/{workflow-markup.ts → workflow/markup.ts} +6 -6
- package/src/runtime/workflow/metadata-persistence.ts +306 -0
- package/src/runtime/workflow/metadata-writer.ts +123 -0
- package/src/runtime/workflow/overlay-store.ts +690 -0
- package/src/runtime/workflow/projector.ts +127 -0
- package/src/runtime/{query-scopes.ts → workflow/query-scopes.ts} +3 -3
- package/src/runtime/{workflow-rail-segments.ts → workflow/rail/compose.ts} +60 -165
- package/src/runtime/workflow/rail/types.ts +198 -0
- package/src/runtime/workflow/scope-rail-composer.ts +39 -0
- package/src/runtime/{scope-resolver.ts → workflow/scope-resolver.ts} +3 -3
- package/src/runtime/workflow/scope-writer.ts +188 -0
- package/src/runtime/{tamper-gate.ts → workflow/tamper-gate.ts} +1 -1
- package/src/runtime/workflow/visibility-policy.ts +129 -0
- package/src/session/_sync-legacy.ts +66 -0
- package/src/session/export/embedded-reconstitute.ts +104 -0
- package/src/session/export/export-diagnostics.ts +85 -0
- package/src/session/export/export-validation.ts +110 -0
- package/src/session/export/index.ts +34 -0
- package/src/session/export/preservation-reattach.ts +30 -0
- package/src/session/export/serialize-dispatch.ts +165 -0
- package/src/session/export/stateful-export-pipeline.ts +432 -0
- package/src/session/export/stateful-export.ts +684 -0
- package/src/session/import/canonical-assembly.ts +227 -0
- package/src/session/import/diagnostics-session.ts +54 -0
- package/src/session/import/embedded-discovery.ts +225 -0
- package/src/session/import/embedded-offload.ts +337 -0
- package/src/session/import/import-diagnostics.ts +69 -0
- package/src/session/import/loader-types.ts +313 -0
- package/src/session/import/loader.ts +1834 -0
- package/src/session/import/normalize.ts +195 -0
- package/src/session/import/package-parts.ts +217 -0
- package/src/session/import/package-read.ts +195 -0
- package/src/session/import/parse-orchestration.ts +105 -0
- package/src/session/import/part-constants.ts +70 -0
- package/src/session/import/part-discovery.ts +94 -0
- package/src/session/import/preservation-index.ts +46 -0
- package/src/{runtime/read-only-diagnostics-runtime.ts → session/import/read-only-diagnostics.ts} +24 -3
- package/src/session/import/review-import.ts +508 -0
- package/src/session/import/styles-consolidation.ts +281 -0
- package/src/session/import/workflow-scope-import.ts +256 -0
- package/src/session/index.ts +37 -0
- package/src/session/session-state.ts +69 -0
- package/src/session/session.ts +532 -0
- package/src/session/shared/protection.ts +228 -0
- package/src/session/shared/session-utils.ts +82 -0
- package/src/session/types.ts +499 -0
- package/src/shell/chart-snapshots.ts +96 -0
- package/src/shell/media-previews.ts +85 -0
- package/src/shell/overlay-anchor-bridge.ts +53 -0
- package/src/shell/paste-adapter.ts +23 -0
- package/src/shell/ref-commands.ts +1697 -0
- package/src/shell/ref-utilities.ts +48 -0
- package/src/shell/search.ts +51 -0
- package/src/{ui/editor-runtime-boundary.ts → shell/session-bootstrap.ts} +243 -67
- package/src/shell/ui-subscriber-channels.ts +81 -0
- package/src/shell/use-collab-sync.ts +116 -0
- package/src/ui/WordReviewEditor.tsx +496 -2051
- package/src/ui/editor-shell-view.tsx +30 -1
- package/src/ui/editor-surface-controller.tsx +49 -1
- package/src/ui/headless/revision-decoration-model.ts +83 -0
- package/src/{ui-tailwind/chrome → ui/headless}/role-action-sets.ts +1 -1
- package/src/ui/headless/scoped-chrome-policy.ts +2 -2
- package/src/ui/headless/selection-tool-context.ts +1 -1
- package/src/ui/headless/selection-tool-resolver.ts +1 -1
- package/src/ui/runtime-shortcut-dispatch.ts +46 -1
- package/src/ui/ui-controller-factory.ts +221 -0
- package/src/ui-tailwind/chart/ChartSurface.tsx +2 -2
- package/src/ui-tailwind/chart/layout/legend-layout.ts +1 -1
- package/src/ui-tailwind/chart/layout/plot-area.ts +2 -2
- package/src/ui-tailwind/chart/layout/title-layout.ts +1 -1
- package/src/ui-tailwind/chart/render/area.tsx +3 -3
- package/src/ui-tailwind/chart/render/bar-column.tsx +3 -3
- package/src/ui-tailwind/chart/render/bubble.tsx +3 -3
- package/src/ui-tailwind/chart/render/combo.tsx +2 -2
- package/src/ui-tailwind/chart/render/data-labels.tsx +2 -2
- package/src/ui-tailwind/chart/render/font-metrics.ts +2 -2
- package/src/ui-tailwind/chart/render/line.tsx +3 -3
- package/src/ui-tailwind/chart/render/pie.tsx +6 -6
- package/src/ui-tailwind/chart/render/scatter.tsx +3 -3
- package/src/ui-tailwind/chart/render/svg-primitives.ts +3 -3
- package/src/ui-tailwind/chart/render/unsupported.tsx +2 -2
- package/src/ui-tailwind/chrome/build-context-menu-entries.ts +88 -0
- package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +1 -1
- package/src/ui-tailwind/chrome/collab-send-to-supplier-button.tsx +1 -1
- package/src/ui-tailwind/chrome/collab-tamper-banner.tsx +1 -1
- package/src/ui-tailwind/chrome/collab-top-nav-container.tsx +1 -1
- package/src/ui-tailwind/chrome/editor-action-registry.ts +553 -0
- package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +182 -0
- package/src/ui-tailwind/chrome/local-surface-arbiter.ts +534 -0
- package/src/ui-tailwind/chrome/resolve-target-kind.ts +226 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +38 -4
- package/src/ui-tailwind/chrome/tw-context-band.tsx +125 -0
- package/src/ui-tailwind/chrome/tw-context-menu-portal.tsx +248 -0
- package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +42 -1
- package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +8 -7
- package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +38 -4
- package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +104 -6
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +66 -7
- package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +54 -8
- package/src/ui-tailwind/chrome/tw-shortcut-hint.tsx +7 -1
- package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +33 -0
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +78 -1
- package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +16 -8
- package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +276 -0
- package/src/ui-tailwind/chrome/use-context-menu-controller.ts +201 -0
- package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +22 -4
- package/src/ui-tailwind/chrome-overlay/tw-comment-balloon-layer.tsx +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-locked-block-layer.tsx +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +11 -5
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +197 -3
- package/src/ui-tailwind/chrome-overlay/tw-revision-margin-bar-layer.tsx +1 -1
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +35 -6
- package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +24 -16
- package/src/ui-tailwind/chrome-overlay/tw-table-continuation-header.tsx +1 -1
- package/src/ui-tailwind/debug/README.md +57 -0
- package/src/ui-tailwind/debug/index.ts +3 -0
- package/src/ui-tailwind/debug/tw-debug-overlay.tsx +186 -0
- package/src/ui-tailwind/debug/tw-debug-presentation.tsx +80 -0
- package/src/ui-tailwind/debug/tw-debug-top-bar.tsx +83 -0
- package/src/ui-tailwind/editor-surface/chart-node-view.tsx +2 -2
- package/src/ui-tailwind/editor-surface/float-wrap-resolver.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +135 -10
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +40 -13
- package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-schema.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +3 -3
- package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +1 -1
- package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +2 -2
- package/src/ui-tailwind/editor-surface/scroll-anchor.ts +91 -9
- package/src/ui-tailwind/editor-surface/shape-renderer.ts +1 -1
- package/src/ui-tailwind/editor-surface/surface-layer.ts +1 -1
- package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +1 -1
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +23 -6
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +132 -22
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +1 -1
- package/src/ui-tailwind/index.ts +0 -5
- package/src/ui-tailwind/overlay-anchor-bridge-context.tsx +33 -0
- package/src/ui-tailwind/page-stack/floating-image-overlay-model.ts +66 -29
- package/src/ui-tailwind/page-stack/tw-floating-image-layer.tsx +25 -2
- package/src/ui-tailwind/review/comment-markdown-renderer.tsx +15 -0
- package/src/ui-tailwind/review/tw-review-rail.tsx +92 -4
- package/src/ui-tailwind/review/tw-workflow-tab.tsx +1 -1
- package/src/ui-tailwind/review-workspace/page-chrome.ts +210 -0
- package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +101 -0
- package/src/ui-tailwind/review-workspace/paragraph-layout.ts +115 -0
- package/src/ui-tailwind/review-workspace/selection-toolbar-placement.ts +97 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-navigator.tsx +130 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-page-toolbar.tsx +240 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +59 -0
- package/src/ui-tailwind/review-workspace/types.ts +408 -0
- package/src/ui-tailwind/review-workspace/use-chrome-policy.ts +104 -0
- package/src/ui-tailwind/review-workspace/use-derived-view-state.ts +151 -0
- package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +70 -0
- package/src/ui-tailwind/review-workspace/use-grabbed-segment-offsets.ts +40 -0
- package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +55 -0
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +130 -0
- package/src/ui-tailwind/review-workspace/use-pm-surface-capture.ts +60 -0
- package/src/ui-tailwind/review-workspace/use-review-rail-state.ts +63 -0
- package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +170 -0
- package/src/ui-tailwind/review-workspace/use-scroll-root-capture.ts +28 -0
- package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +113 -0
- package/src/ui-tailwind/review-workspace/use-shell-selection-anchor-bridge.ts +120 -0
- package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +55 -0
- package/src/ui-tailwind/review-workspace/use-viewport-dimensions.ts +43 -0
- package/src/ui-tailwind/review-workspace/use-workspace-arbiter.ts +25 -0
- package/src/ui-tailwind/review-workspace/use-workspace-composition.ts +86 -0
- package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +150 -0
- package/src/ui-tailwind/theme/editor-theme.css +25 -0
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +2 -2
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +61 -98
- package/src/ui-tailwind/tw-review-workspace.tsx +521 -1802
- package/src/ui-tailwind/ui-api-context.tsx +43 -0
- package/src/ui-tailwind/ui-shell-channels-context.tsx +49 -0
- package/src/validation/compatibility-engine.ts +6 -6
- package/src/runtime/styles-cascade.ts +0 -33
- package/src/ui-tailwind/chrome/tw-mode-dock.tsx +0 -85
- /package/src/runtime/{page-number-format.ts → formatting/field/page-number-format.ts} +0 -0
- /package/src/runtime/{ai-action-policy.ts → workflow/ai-action-policy.ts} +0 -0
- /package/src/runtime/{scope-tag-registry.ts → workflow/scope-tag-registry.ts} +0 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase Q — Debug overlay.
|
|
3
|
+
*
|
|
4
|
+
* Full-screen tabbed panel shown when
|
|
5
|
+
* `WordReviewEditorProps.debugMode === "full"`. Renders the top bar
|
|
6
|
+
* (reused from `tw-debug-top-bar.tsx`) + three content panes:
|
|
7
|
+
*
|
|
8
|
+
* - **Inspector** — pretty-prints the bound `DebugInspectorSnapshot`
|
|
9
|
+
* (canonical / layout / geometry / scope / review / diagnostics
|
|
10
|
+
* JSON, same shape the Railway debug service renders).
|
|
11
|
+
* - **Events** — tail of the last N events on the `api`, `render`,
|
|
12
|
+
* `command`, `commit`, `collab`, `layout` channels the runtime
|
|
13
|
+
* projector ships (`DebugBus`). Populated on arrival via the
|
|
14
|
+
* `events` prop.
|
|
15
|
+
* - **REPL** — stub in this slice. Operators type into the textarea
|
|
16
|
+
* and the host-provided `onReplEval` receives the source. Full
|
|
17
|
+
* in-scope `api` / `runtime` / `debug` handles land in the Phase Q
|
|
18
|
+
* follow-up alongside localStorage history.
|
|
19
|
+
*
|
|
20
|
+
* Slice 7 ships the visible scaffolding + the visibility invariant;
|
|
21
|
+
* the REPL evaluator + the deeper event-tail filters are follow-up
|
|
22
|
+
* work tracked under the refactor/11 plan.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import React, { useState } from "react";
|
|
26
|
+
|
|
27
|
+
import { TwDebugTopBar, type TwDebugTopBarProps } from "./tw-debug-top-bar.tsx";
|
|
28
|
+
|
|
29
|
+
type DebugOverlayTab = "inspector" | "events" | "repl";
|
|
30
|
+
|
|
31
|
+
export interface TwDebugOverlayProps {
|
|
32
|
+
sessionId: string | null;
|
|
33
|
+
snapshot: TwDebugTopBarProps["snapshot"];
|
|
34
|
+
/**
|
|
35
|
+
* Raw `DebugInspectorSnapshot` the host wired through
|
|
36
|
+
* `ui.debug.attach()`. Rendered as pretty-printed JSON in the
|
|
37
|
+
* Inspector pane. Large snapshots are clipped to the first 16 KB
|
|
38
|
+
* to keep the DOM responsive.
|
|
39
|
+
*/
|
|
40
|
+
inspectorJson?: unknown;
|
|
41
|
+
/**
|
|
42
|
+
* Ring-buffered events flowing through the runtime's `DebugBus`.
|
|
43
|
+
* Each entry is rendered as `channel · kind · t` with a collapsible
|
|
44
|
+
* details row for the payload.
|
|
45
|
+
*/
|
|
46
|
+
events?: ReadonlyArray<{
|
|
47
|
+
readonly channel: string;
|
|
48
|
+
readonly kind: string;
|
|
49
|
+
readonly t: number;
|
|
50
|
+
readonly payload?: unknown;
|
|
51
|
+
}>;
|
|
52
|
+
/** Invoked when the operator submits text from the REPL pane. */
|
|
53
|
+
onReplEval?: (source: string) => void;
|
|
54
|
+
/** Collapses the overlay back to `"top-bar"` mode. */
|
|
55
|
+
onCollapse?: () => void;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const MAX_INSPECTOR_BYTES = 16 * 1024;
|
|
59
|
+
|
|
60
|
+
function formatInspector(json: unknown): string {
|
|
61
|
+
if (json === undefined || json === null) return "(no snapshot bound)";
|
|
62
|
+
try {
|
|
63
|
+
const s = JSON.stringify(json, null, 2);
|
|
64
|
+
if (s.length > MAX_INSPECTOR_BYTES) {
|
|
65
|
+
return `${s.slice(0, MAX_INSPECTOR_BYTES)}\n… [clipped ${s.length - MAX_INSPECTOR_BYTES} bytes]`;
|
|
66
|
+
}
|
|
67
|
+
return s;
|
|
68
|
+
} catch (err) {
|
|
69
|
+
return `(snapshot serialization failed: ${(err as Error).message})`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function TwDebugOverlay(props: TwDebugOverlayProps): React.ReactElement {
|
|
74
|
+
const [tab, setTab] = useState<DebugOverlayTab>("inspector");
|
|
75
|
+
const [replInput, setReplInput] = useState("");
|
|
76
|
+
|
|
77
|
+
const tabBtn = (id: DebugOverlayTab, label: string) => (
|
|
78
|
+
<button
|
|
79
|
+
key={id}
|
|
80
|
+
type="button"
|
|
81
|
+
onClick={() => setTab(id)}
|
|
82
|
+
data-state={tab === id ? "active" : "inactive"}
|
|
83
|
+
data-testid={`tw-debug-overlay__tab-${id}`}
|
|
84
|
+
className={`inline-flex h-6 items-center rounded-[var(--radius-sm)] px-2 text-[11px] font-medium transition-colors ${
|
|
85
|
+
tab === id
|
|
86
|
+
? "bg-[var(--color-accent-primary)]/12 text-[var(--color-accent-primary)]"
|
|
87
|
+
: "text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-hover)] hover:text-[var(--color-text-primary)]"
|
|
88
|
+
}`}
|
|
89
|
+
>
|
|
90
|
+
{label}
|
|
91
|
+
</button>
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div
|
|
96
|
+
className="fixed bottom-0 left-0 right-0 z-40 flex max-h-[48vh] flex-col border-t border-[var(--color-border-subtle)] bg-[var(--color-bg-canvas)] shadow-[var(--shadow-float)]"
|
|
97
|
+
data-testid="tw-debug-overlay"
|
|
98
|
+
data-debug-mode="full"
|
|
99
|
+
role="region"
|
|
100
|
+
aria-label="Editor debug overlay"
|
|
101
|
+
>
|
|
102
|
+
<TwDebugTopBar
|
|
103
|
+
sessionId={props.sessionId}
|
|
104
|
+
snapshot={props.snapshot}
|
|
105
|
+
{...(props.onCollapse ? { onExpand: props.onCollapse } : {})}
|
|
106
|
+
/>
|
|
107
|
+
<div
|
|
108
|
+
className="flex shrink-0 items-center gap-1 border-b border-[var(--color-border-subtle)] bg-[var(--color-bg-chrome)]/80 px-3 py-1"
|
|
109
|
+
role="tablist"
|
|
110
|
+
aria-label="Debug panes"
|
|
111
|
+
>
|
|
112
|
+
{tabBtn("inspector", "Inspector")}
|
|
113
|
+
{tabBtn("events", "Events")}
|
|
114
|
+
{tabBtn("repl", "REPL")}
|
|
115
|
+
</div>
|
|
116
|
+
<div
|
|
117
|
+
className="flex-1 overflow-auto px-3 py-2 text-[11px]"
|
|
118
|
+
data-testid={`tw-debug-overlay__pane-${tab}`}
|
|
119
|
+
>
|
|
120
|
+
{tab === "inspector" ? (
|
|
121
|
+
<pre className="whitespace-pre-wrap font-mono text-[11px] leading-[1.45] text-[var(--color-text-primary)]">
|
|
122
|
+
{formatInspector(props.inspectorJson)}
|
|
123
|
+
</pre>
|
|
124
|
+
) : null}
|
|
125
|
+
{tab === "events" ? (
|
|
126
|
+
<ul className="flex flex-col gap-0.5 font-mono text-[10px] text-[var(--color-text-secondary)]">
|
|
127
|
+
{(props.events ?? []).length === 0 ? (
|
|
128
|
+
<li className="italic text-[var(--color-text-tertiary)]">
|
|
129
|
+
(no events — wire `ui.debug.attach()` with a DebugBus listener)
|
|
130
|
+
</li>
|
|
131
|
+
) : (
|
|
132
|
+
props.events!.slice(-200).map((e, i) => (
|
|
133
|
+
<li
|
|
134
|
+
key={`${e.t}-${i}`}
|
|
135
|
+
className="border-b border-[var(--color-border-subtle)]/60 py-0.5"
|
|
136
|
+
>
|
|
137
|
+
<span className="text-[var(--color-accent-primary)]">{e.channel}</span>
|
|
138
|
+
{" · "}
|
|
139
|
+
<span className="text-[var(--color-text-primary)]">{e.kind}</span>
|
|
140
|
+
{" · "}
|
|
141
|
+
<span className="text-[var(--color-text-tertiary)]">
|
|
142
|
+
t={Math.round(e.t)}
|
|
143
|
+
</span>
|
|
144
|
+
</li>
|
|
145
|
+
))
|
|
146
|
+
)}
|
|
147
|
+
</ul>
|
|
148
|
+
) : null}
|
|
149
|
+
{tab === "repl" ? (
|
|
150
|
+
<form
|
|
151
|
+
onSubmit={(ev) => {
|
|
152
|
+
ev.preventDefault();
|
|
153
|
+
if (replInput.trim().length === 0) return;
|
|
154
|
+
props.onReplEval?.(replInput);
|
|
155
|
+
setReplInput("");
|
|
156
|
+
}}
|
|
157
|
+
className="flex flex-col gap-2"
|
|
158
|
+
>
|
|
159
|
+
<textarea
|
|
160
|
+
value={replInput}
|
|
161
|
+
onChange={(ev) => setReplInput(ev.target.value)}
|
|
162
|
+
placeholder="// Phase Q REPL — follow-up slice will provide in-scope `api` / `runtime` / `debug` handles + localStorage history."
|
|
163
|
+
className="min-h-[120px] w-full resize-y rounded-[var(--radius-sm)] border border-[var(--color-border-default)] bg-[var(--color-bg-muted)] px-2 py-1 font-mono text-[11px] text-[var(--color-text-primary)] outline-none focus-visible:border-[var(--color-accent-primary)]"
|
|
164
|
+
data-testid="tw-debug-overlay__repl-input"
|
|
165
|
+
/>
|
|
166
|
+
<div className="flex items-center justify-between">
|
|
167
|
+
<span className="text-[10px] text-[var(--color-text-tertiary)]">
|
|
168
|
+
{props.onReplEval
|
|
169
|
+
? "Submit (Enter or click Run) invokes the host's onReplEval."
|
|
170
|
+
: "Host has not wired onReplEval — input is inert."}
|
|
171
|
+
</span>
|
|
172
|
+
<button
|
|
173
|
+
type="submit"
|
|
174
|
+
disabled={!props.onReplEval || replInput.trim().length === 0}
|
|
175
|
+
className="inline-flex h-6 items-center rounded-[var(--radius-sm)] bg-[var(--color-accent-primary)] px-3 text-[11px] font-semibold text-[var(--color-text-on-accent)] transition-colors hover:bg-[var(--color-accent-primary-hover)] disabled:opacity-40"
|
|
176
|
+
data-testid="tw-debug-overlay__repl-submit"
|
|
177
|
+
>
|
|
178
|
+
Run
|
|
179
|
+
</button>
|
|
180
|
+
</div>
|
|
181
|
+
</form>
|
|
182
|
+
) : null}
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase Q — DebugMode router.
|
|
3
|
+
*
|
|
4
|
+
* Single mount point `WordReviewEditor.tsx` consumes. Picks between
|
|
5
|
+
* the minimal top-bar, the full overlay, or nothing depending on the
|
|
6
|
+
* public `debugMode` prop. Keeping the router in one file means the
|
|
7
|
+
* invariant test + CI guard can target a single surface.
|
|
8
|
+
*
|
|
9
|
+
* Data flow (consumers wire through `ui.debug.attach()` per refactor/10
|
|
10
|
+
* Slice 5):
|
|
11
|
+
*
|
|
12
|
+
* host → `ui.debug.attach({ id, channels })` →
|
|
13
|
+
* controller.attachDebug(session) →
|
|
14
|
+
* { sessionId, snapshot, events } → this component
|
|
15
|
+
*
|
|
16
|
+
* Slice 7 ships the visibility scaffold; the full REPL + event-tail
|
|
17
|
+
* wiring to `runtime.debug.bus` + `DebugInspectorSnapshot` lives in a
|
|
18
|
+
* follow-up (see `src/ui-tailwind/debug/README.md`).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import React from "react";
|
|
22
|
+
|
|
23
|
+
import type { DebugMode } from "../../api/public-types.ts";
|
|
24
|
+
import { TwDebugTopBar, type TwDebugTopBarProps } from "./tw-debug-top-bar.tsx";
|
|
25
|
+
import { TwDebugOverlay, type TwDebugOverlayProps } from "./tw-debug-overlay.tsx";
|
|
26
|
+
|
|
27
|
+
export interface TwDebugPresentationProps {
|
|
28
|
+
mode: DebugMode;
|
|
29
|
+
/**
|
|
30
|
+
* Debug session id — mirrors what the host passed into
|
|
31
|
+
* `ui.debug.attach({ id })`. `null` when no attachment is active.
|
|
32
|
+
*/
|
|
33
|
+
sessionId?: string | null;
|
|
34
|
+
snapshot?: TwDebugTopBarProps["snapshot"];
|
|
35
|
+
inspectorJson?: TwDebugOverlayProps["inspectorJson"];
|
|
36
|
+
events?: TwDebugOverlayProps["events"];
|
|
37
|
+
onReplEval?: TwDebugOverlayProps["onReplEval"];
|
|
38
|
+
/**
|
|
39
|
+
* Optional override for the mode toggle — host supplies when it
|
|
40
|
+
* owns the `debugMode` state (e.g. harness with a keyboard shortcut).
|
|
41
|
+
*/
|
|
42
|
+
onModeChange?: (next: DebugMode) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function TwDebugPresentation(
|
|
46
|
+
props: TwDebugPresentationProps,
|
|
47
|
+
): React.ReactElement | null {
|
|
48
|
+
if (props.mode === "off") return null;
|
|
49
|
+
|
|
50
|
+
const sessionId = props.sessionId ?? null;
|
|
51
|
+
const snapshot = props.snapshot ?? null;
|
|
52
|
+
|
|
53
|
+
if (props.mode === "top-bar") {
|
|
54
|
+
return (
|
|
55
|
+
<TwDebugTopBar
|
|
56
|
+
sessionId={sessionId}
|
|
57
|
+
snapshot={snapshot}
|
|
58
|
+
{...(props.onModeChange
|
|
59
|
+
? { onExpand: () => props.onModeChange!("full") }
|
|
60
|
+
: {})}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// mode === "full"
|
|
66
|
+
return (
|
|
67
|
+
<TwDebugOverlay
|
|
68
|
+
sessionId={sessionId}
|
|
69
|
+
snapshot={snapshot}
|
|
70
|
+
{...(props.inspectorJson !== undefined
|
|
71
|
+
? { inspectorJson: props.inspectorJson }
|
|
72
|
+
: {})}
|
|
73
|
+
{...(props.events ? { events: props.events } : {})}
|
|
74
|
+
{...(props.onReplEval ? { onReplEval: props.onReplEval } : {})}
|
|
75
|
+
{...(props.onModeChange
|
|
76
|
+
? { onCollapse: () => props.onModeChange!("top-bar") }
|
|
77
|
+
: {})}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase Q — Debug top bar.
|
|
3
|
+
*
|
|
4
|
+
* Minimal always-on status strip shown when
|
|
5
|
+
* `WordReviewEditorProps.debugMode === "top-bar"` (or as the header of
|
|
6
|
+
* the "full" overlay). Renders the bound debug session id + a small
|
|
7
|
+
* set of counters derived from the `DebugInspectorSnapshot` the host
|
|
8
|
+
* supplies through `ui.debug.attach()` wiring.
|
|
9
|
+
*
|
|
10
|
+
* Slice 7 ships the minimal surface; the REPL + event-tail panes live
|
|
11
|
+
* in `tw-debug-overlay.tsx` (full mode only). Future telemetry (scope
|
|
12
|
+
* count, invalidation counter, channel activity) plugs in through
|
|
13
|
+
* `snapshot` without changing the public prop.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import React from "react";
|
|
17
|
+
|
|
18
|
+
export interface TwDebugTopBarProps {
|
|
19
|
+
/**
|
|
20
|
+
* The debug-session id the host passed into `ui.debug.attach()`.
|
|
21
|
+
* Shown verbatim so operators can correlate with Railway debug-
|
|
22
|
+
* service logs. `null` when no attachment is active.
|
|
23
|
+
*/
|
|
24
|
+
sessionId: string | null;
|
|
25
|
+
/**
|
|
26
|
+
* Lightweight counters sourced from `DebugInspectorSnapshot`.
|
|
27
|
+
* `null` means no snapshot has flowed yet.
|
|
28
|
+
*/
|
|
29
|
+
snapshot: {
|
|
30
|
+
readonly scopeCount?: number;
|
|
31
|
+
readonly blockCount?: number;
|
|
32
|
+
readonly revisionToken?: string;
|
|
33
|
+
} | null;
|
|
34
|
+
/**
|
|
35
|
+
* Optional click handler — the harness can use this to toggle
|
|
36
|
+
* between `"top-bar"` and `"full"` modes.
|
|
37
|
+
*/
|
|
38
|
+
onExpand?: () => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function TwDebugTopBar(props: TwDebugTopBarProps): React.ReactElement {
|
|
42
|
+
const pill = (label: string, value: string | number | undefined | null) =>
|
|
43
|
+
value === undefined || value === null ? null : (
|
|
44
|
+
<span
|
|
45
|
+
key={label}
|
|
46
|
+
className="inline-flex items-center gap-1 rounded-[var(--radius-sm)] bg-[var(--color-bg-muted)] px-2 py-0.5 text-[10px] font-medium text-[var(--color-text-secondary)]"
|
|
47
|
+
data-testid={`tw-debug-top-bar__${label.toLowerCase().replace(/\s+/g, "-")}`}
|
|
48
|
+
>
|
|
49
|
+
<span className="text-[var(--color-text-tertiary)]">{label}</span>
|
|
50
|
+
<span className="font-mono text-[var(--color-text-primary)]">{value}</span>
|
|
51
|
+
</span>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
className="flex h-7 shrink-0 items-center gap-2 border-b border-[var(--color-border-subtle)] bg-[var(--color-bg-chrome)]/95 px-3 text-[11px] backdrop-blur-sm"
|
|
57
|
+
data-testid="tw-debug-top-bar"
|
|
58
|
+
data-debug-mode="top-bar"
|
|
59
|
+
role="status"
|
|
60
|
+
aria-label="Editor debug status bar"
|
|
61
|
+
>
|
|
62
|
+
<span className="font-semibold uppercase tracking-[0.14em] text-[var(--color-text-tertiary)]">
|
|
63
|
+
Debug
|
|
64
|
+
</span>
|
|
65
|
+
{pill("session", props.sessionId ?? "(detached)")}
|
|
66
|
+
{pill("scopes", props.snapshot?.scopeCount)}
|
|
67
|
+
{pill("blocks", props.snapshot?.blockCount)}
|
|
68
|
+
{props.snapshot?.revisionToken
|
|
69
|
+
? pill("rev", props.snapshot.revisionToken.slice(0, 8))
|
|
70
|
+
: null}
|
|
71
|
+
{props.onExpand ? (
|
|
72
|
+
<button
|
|
73
|
+
type="button"
|
|
74
|
+
onClick={props.onExpand}
|
|
75
|
+
className="ml-auto inline-flex h-5 items-center rounded-[var(--radius-sm)] px-1.5 text-[10px] font-medium text-[var(--color-text-secondary)] transition-colors hover:bg-[var(--color-bg-hover)] hover:text-[var(--color-text-primary)]"
|
|
76
|
+
data-testid="tw-debug-top-bar__expand"
|
|
77
|
+
>
|
|
78
|
+
Expand ▾
|
|
79
|
+
</button>
|
|
80
|
+
) : null}
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
@@ -17,7 +17,7 @@ import { createRoot, type Root } from "react-dom/client";
|
|
|
17
17
|
import type { Node as PMNode } from "prosemirror-model";
|
|
18
18
|
import type { NodeViewConstructor } from "prosemirror-view";
|
|
19
19
|
import { ChartSurface } from "../chart/ChartSurface.tsx";
|
|
20
|
-
import {
|
|
20
|
+
import { getChartModel } from "../../api/v3/runtime/chart.ts";
|
|
21
21
|
|
|
22
22
|
const DEFAULT_WIDTH = 576;
|
|
23
23
|
const DEFAULT_HEIGHT = 336;
|
|
@@ -55,7 +55,7 @@ class ChartNodeViewInstance {
|
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const entry =
|
|
58
|
+
const entry = getChartModel(parsedChartId);
|
|
59
59
|
if (!entry) {
|
|
60
60
|
this._unmountRoot();
|
|
61
61
|
return;
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import type { SurfaceDrawingAnchor } from "../../api/public-types";
|
|
21
|
-
import { EMU_PER_PX } from "../../
|
|
21
|
+
import { EMU_PER_PX } from "../../api/public-types";
|
|
22
22
|
|
|
23
23
|
export interface FloatWrapStyle {
|
|
24
24
|
/** CSS `float` value when the anchor wraps inline text. */
|
|
@@ -12,8 +12,6 @@ import {
|
|
|
12
12
|
extractPlainTextSegments,
|
|
13
13
|
type PastePlainSegment,
|
|
14
14
|
} from "./paste-plain-text";
|
|
15
|
-
import { parseCanonicalFragmentFromWordML } from "../../io/paste/word-clipboard";
|
|
16
|
-
import { parseCanonicalFragmentFromHtml } from "../../io/paste/html-clipboard";
|
|
17
15
|
import type { PositionMap } from "./pm-position-map";
|
|
18
16
|
|
|
19
17
|
/**
|
|
@@ -243,6 +241,83 @@ export interface CommandBridgeCallbacks extends SelectionSyncCallbacks {
|
|
|
243
241
|
* lands. When absent, the legacy "block all docChanged" behavior applies.
|
|
244
242
|
*/
|
|
245
243
|
gate?: Plugin;
|
|
244
|
+
/**
|
|
245
|
+
* Phase C.β — right-click composition hook.
|
|
246
|
+
*
|
|
247
|
+
* When wired, fires on `contextmenu` DOM events with the event
|
|
248
|
+
* coordinates + target; the bridge suppresses the browser-native menu
|
|
249
|
+
* so the host can mount `TwContextMenu` at the returned coordinates.
|
|
250
|
+
*
|
|
251
|
+
* When NOT wired, the bridge leaves the native menu alone so
|
|
252
|
+
* integrators that haven't opted in retain browser-default behavior
|
|
253
|
+
* (back-compat).
|
|
254
|
+
*
|
|
255
|
+
* Shape deliberately minimal — the host resolves target kind from the
|
|
256
|
+
* target DOM node, queries the `editor-action-registry`, and renders
|
|
257
|
+
* the resulting `TwContextMenu`. The bridge does not import the
|
|
258
|
+
* registry directly — this preserves the three-actor split (bridge
|
|
259
|
+
* is surface-layer; registry + composition are chrome-layer).
|
|
260
|
+
*/
|
|
261
|
+
onContextMenuRequested?: (event: ContextMenuRequestEvent) => void;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface ContextMenuRequestEvent {
|
|
265
|
+
readonly clientX: number;
|
|
266
|
+
readonly clientY: number;
|
|
267
|
+
/**
|
|
268
|
+
* The native `event.target`. Typically an Element (PM-rendered span,
|
|
269
|
+
* table cell, image, etc.) but can be a Text node or null. Callers
|
|
270
|
+
* must walk to the nearest Element and apply their own target-kind
|
|
271
|
+
* resolver — see the Phase C.γ `resolveTargetKind(el)` helper once
|
|
272
|
+
* it ships, or use `target instanceof Element ? target.closest(…) : null`
|
|
273
|
+
* in the interim.
|
|
274
|
+
*/
|
|
275
|
+
readonly target: EventTarget | null;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Pure helper used by the bridge's `handleDOMEvents.contextmenu` hook.
|
|
280
|
+
* Returns `true` when the native menu should be suppressed (i.e. the
|
|
281
|
+
* host wired a callback); `false` when the bridge lets browser default
|
|
282
|
+
* take over.
|
|
283
|
+
*
|
|
284
|
+
* Exception handling: if the host's callback throws, the error is
|
|
285
|
+
* logged and the helper returns `false` so PM/browser fall back to
|
|
286
|
+
* native behavior. `event.preventDefault()` is called BEFORE the
|
|
287
|
+
* handler fires (the contract is "host owns the menu now"), but if
|
|
288
|
+
* the handler throws we still propagate `false` so PM does not
|
|
289
|
+
* double-suppress — the user at minimum gets no menu rather than a
|
|
290
|
+
* silent no-op cascade.
|
|
291
|
+
*
|
|
292
|
+
* Exported so unit tests can exercise the contextmenu dispatch path
|
|
293
|
+
* without standing up a full EditorView (the prosemirror-tables
|
|
294
|
+
* columnResizing plugin requires a tables-aware schema that's
|
|
295
|
+
* overkill for this contract).
|
|
296
|
+
*/
|
|
297
|
+
export function applyContextMenuEvent(
|
|
298
|
+
callbacks: CommandBridgeCallbacks,
|
|
299
|
+
event: MouseEvent,
|
|
300
|
+
): boolean {
|
|
301
|
+
const handler = callbacks.onContextMenuRequested;
|
|
302
|
+
if (!handler) return false;
|
|
303
|
+
try {
|
|
304
|
+
event.preventDefault();
|
|
305
|
+
handler({
|
|
306
|
+
clientX: event.clientX,
|
|
307
|
+
clientY: event.clientY,
|
|
308
|
+
target: event.target,
|
|
309
|
+
});
|
|
310
|
+
return true;
|
|
311
|
+
} catch (err) {
|
|
312
|
+
// Log so integrators can debug; do not re-throw (throwing from a
|
|
313
|
+
// PM handleDOMEvents returns a truthy value that tells PM the
|
|
314
|
+
// event was handled, which would then cause PM to swallow it).
|
|
315
|
+
// Surface `false` so the user still gets native menu as fallback
|
|
316
|
+
// rather than a silent empty overlay.
|
|
317
|
+
// eslint-disable-next-line no-console
|
|
318
|
+
console.error("[pm-command-bridge] onContextMenuRequested threw:", err);
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
246
321
|
}
|
|
247
322
|
|
|
248
323
|
const bridgeKey = new PluginKey("command-bridge");
|
|
@@ -281,9 +356,45 @@ export function createSelectionSyncPlugin(
|
|
|
281
356
|
});
|
|
282
357
|
}
|
|
283
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Result shape from the paste-parser adapters — mirrors the native
|
|
361
|
+
* `parseCanonicalFragmentFromWordML` / `parseCanonicalFragmentFromHtml`
|
|
362
|
+
* return so callers don't have to re-map.
|
|
363
|
+
*/
|
|
364
|
+
export type PasteFragmentParseResult =
|
|
365
|
+
| {
|
|
366
|
+
readonly ok: true;
|
|
367
|
+
readonly fragment: import("../../api/public-types.ts").CanonicalDocumentFragment;
|
|
368
|
+
}
|
|
369
|
+
| { readonly ok: false; readonly reason?: string };
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Host-supplied adapter that turns raw WordML / HTML clipboard payloads
|
|
373
|
+
* into a `CanonicalDocumentFragment`. Refactor/11 Slice 5 retires the
|
|
374
|
+
* PM bridge's direct `io/paste/**` imports: the bridge emits the raw
|
|
375
|
+
* payload + mime type, the host parses, and fires the resulting
|
|
376
|
+
* `onPasteFragment` callback. Hosts that don't supply the adapter fall
|
|
377
|
+
* through to Tier A plain-text paste, as if `onPasteFragment` was not
|
|
378
|
+
* wired.
|
|
379
|
+
*/
|
|
380
|
+
export interface PasteFragmentParserAdapter {
|
|
381
|
+
parseWordML(wordml: string): PasteFragmentParseResult;
|
|
382
|
+
parseHtml(html: string): PasteFragmentParseResult;
|
|
383
|
+
}
|
|
384
|
+
|
|
284
385
|
export interface CommandBridgePluginOptions {
|
|
285
386
|
/** P6: when true, omit prosemirror-tables columnResizing() — chrome overlay grips take over. */
|
|
286
387
|
useChromeColumnResizing?: boolean;
|
|
388
|
+
/**
|
|
389
|
+
* Slice 5 retirement seam — host-supplied parser adapter for rich
|
|
390
|
+
* paste (WordML + HTML). When absent, the bridge does NOT parse
|
|
391
|
+
* rich clipboard payloads; they fall through to plain-text paste or
|
|
392
|
+
* `onBlockedInput`. `WordReviewEditor.tsx` supplies the adapter
|
|
393
|
+
* wrapping `io/paste/word-clipboard` + `io/paste/html-clipboard`;
|
|
394
|
+
* headless consumers can plug their own (or omit it to accept
|
|
395
|
+
* plain-text-only paste).
|
|
396
|
+
*/
|
|
397
|
+
pasteFragmentParser?: PasteFragmentParserAdapter;
|
|
287
398
|
}
|
|
288
399
|
|
|
289
400
|
export function createCommandBridgePlugins(
|
|
@@ -363,6 +474,13 @@ export function createCommandBridgePlugins(
|
|
|
363
474
|
dragSourceRange = null;
|
|
364
475
|
return false;
|
|
365
476
|
},
|
|
477
|
+
// Phase C.β — right-click composition hook. Returns true to
|
|
478
|
+
// block the native menu only when the host has wired a callback,
|
|
479
|
+
// so integrators that haven't opted in still get browser-native
|
|
480
|
+
// behavior (back-compat).
|
|
481
|
+
contextmenu(_view, event) {
|
|
482
|
+
return applyContextMenuEvent(callbacks, event as MouseEvent);
|
|
483
|
+
},
|
|
366
484
|
},
|
|
367
485
|
handleTextInput(_view, _from, _to, text) {
|
|
368
486
|
if (isComposing) {
|
|
@@ -374,7 +492,7 @@ export function createCommandBridgePlugins(
|
|
|
374
492
|
|
|
375
493
|
// I2 paste handler — Tier B (WordML) preferred, Tier A (plain) fallback.
|
|
376
494
|
//
|
|
377
|
-
// Preference order per `
|
|
495
|
+
// Preference order per `CLAUDE.md (lane status table)`:
|
|
378
496
|
// 1. Office-clipboard WordprocessingML payload if the host wired
|
|
379
497
|
// `onPasteFragment` AND the clipboard carries the MIME. Parsed via
|
|
380
498
|
// `parseCanonicalFragmentFromWordML`.
|
|
@@ -417,10 +535,15 @@ export function createCommandBridgePlugins(
|
|
|
417
535
|
}
|
|
418
536
|
|
|
419
537
|
// Tier B: WordprocessingML (highest fidelity when available)
|
|
420
|
-
|
|
538
|
+
// Slice 5 — the bridge no longer imports `io/paste/**` directly;
|
|
539
|
+
// callers inject `pasteFragmentParser` so rich-paste parsing
|
|
540
|
+
// lives in the shell (or the host). Without the adapter, rich
|
|
541
|
+
// paste falls through to plain text (same as before when
|
|
542
|
+
// `onPasteFragment` was not wired).
|
|
543
|
+
if (callbacks.onPasteFragment && options?.pasteFragmentParser) {
|
|
421
544
|
const wordml = readWordMLPayload(clipboard);
|
|
422
545
|
if (wordml) {
|
|
423
|
-
const parsed =
|
|
546
|
+
const parsed = options.pasteFragmentParser.parseWordML(wordml);
|
|
424
547
|
if (parsed.ok && parsed.fragment.blocks.length > 0) {
|
|
425
548
|
callbacks.onPasteFragment({ fragment: parsed.fragment, source: "wordml" });
|
|
426
549
|
return true;
|
|
@@ -431,10 +554,10 @@ export function createCommandBridgePlugins(
|
|
|
431
554
|
}
|
|
432
555
|
|
|
433
556
|
// Tier B: HTML (Google Docs, Word web, Outlook-lite)
|
|
434
|
-
if (callbacks.onPasteFragment) {
|
|
557
|
+
if (callbacks.onPasteFragment && options?.pasteFragmentParser) {
|
|
435
558
|
const htmlPayload = clipboard.getData("text/html");
|
|
436
559
|
if (htmlPayload && htmlPayload.trim().length > 0) {
|
|
437
|
-
const parsed =
|
|
560
|
+
const parsed = options.pasteFragmentParser.parseHtml(htmlPayload);
|
|
438
561
|
if (parsed.ok && parsed.fragment.blocks.length > 0) {
|
|
439
562
|
callbacks.onPasteFragment({ fragment: parsed.fragment, source: "html" });
|
|
440
563
|
return true;
|
|
@@ -479,11 +602,13 @@ export function createCommandBridgePlugins(
|
|
|
479
602
|
return true;
|
|
480
603
|
}
|
|
481
604
|
|
|
482
|
-
// Tier B drop: WordprocessingML fragment
|
|
483
|
-
|
|
605
|
+
// Tier B drop: WordprocessingML fragment — uses the host-
|
|
606
|
+
// supplied parser adapter (Slice 5 retirement); absent adapter
|
|
607
|
+
// falls through to plain text.
|
|
608
|
+
if (callbacks.onDropFragment && options?.pasteFragmentParser) {
|
|
484
609
|
const wordml = readWordMLPayload(dt);
|
|
485
610
|
if (wordml) {
|
|
486
|
-
const parsed =
|
|
611
|
+
const parsed = options.pasteFragmentParser.parseWordML(wordml);
|
|
487
612
|
if (parsed.ok && parsed.fragment.blocks.length > 0) {
|
|
488
613
|
const ctrlModifier = Boolean(dragEvent.ctrlKey || dragEvent.metaKey);
|
|
489
614
|
const effect = ctrlModifier ? "copy" : "move";
|
|
@@ -2,8 +2,14 @@ import { Decoration, DecorationSet } from "prosemirror-view";
|
|
|
2
2
|
|
|
3
3
|
import type { CommentDecorationModel } from "../../ui/headless/comment-decoration-model";
|
|
4
4
|
import { getCommentHighlightClass, type MarkupDisplay } from "../../ui/headless/comment-decoration-model";
|
|
5
|
-
import type {
|
|
6
|
-
|
|
5
|
+
import type {
|
|
6
|
+
RevisionDecorationModel,
|
|
7
|
+
RevisionDisplayFlags,
|
|
8
|
+
} from "../../ui/headless/revision-decoration-model";
|
|
9
|
+
import {
|
|
10
|
+
buildClassFromRevisionDisplay,
|
|
11
|
+
getRevisionHighlightClass,
|
|
12
|
+
} from "../../ui/headless/revision-decoration-model";
|
|
7
13
|
import type {
|
|
8
14
|
EditorAnchorProjection,
|
|
9
15
|
EditorStoryTarget,
|
|
@@ -13,7 +19,7 @@ import type {
|
|
|
13
19
|
WorkflowMetadataMarkup,
|
|
14
20
|
WorkflowScope,
|
|
15
21
|
} from "../../api/public-types";
|
|
16
|
-
import { MAIN_STORY_TARGET, storyTargetsEqual } from "../../
|
|
22
|
+
import { MAIN_STORY_TARGET, storyTargetsEqual } from "../../api/public-types.ts";
|
|
17
23
|
import type { PositionMap } from "./pm-position-map";
|
|
18
24
|
import type { Node as PMNode } from "prosemirror-model";
|
|
19
25
|
|
|
@@ -313,6 +319,14 @@ export function buildDecorations(
|
|
|
313
319
|
activeWorkflowWorkItemIdOrScopeIds?: string | null | readonly string[],
|
|
314
320
|
activeWorkflowScopeIdsOrMetadata?: readonly string[] | readonly WorkflowMetadataMarkup[],
|
|
315
321
|
workflowMetadata?: readonly WorkflowMetadataMarkup[],
|
|
322
|
+
// §4.3 — Track B integration. When the caller threads an offset →
|
|
323
|
+
// `SurfaceInlineSegment.revisionDisplay` map (pre-computed from
|
|
324
|
+
// `snapshot.surfaceInlineSegments`), the revision-decoration loop
|
|
325
|
+
// reads the L03-authoritative display flags instead of re-deriving
|
|
326
|
+
// from the review-store side channel via `getRevisionHighlightClass`.
|
|
327
|
+
// Consumers without a live surface-segment stream (headless / legacy
|
|
328
|
+
// callers) omit this argument and keep the pre-Track-B behavior.
|
|
329
|
+
revisionDisplayByOffset?: ReadonlyMap<number, RevisionDisplayFlags>,
|
|
316
330
|
): DecorationSet {
|
|
317
331
|
const isStoryTarget = (value: unknown): value is EditorStoryTarget =>
|
|
318
332
|
Boolean(value) &&
|
|
@@ -408,10 +422,17 @@ export function buildDecorations(
|
|
|
408
422
|
// Visual styling (underlines, colors) only applies when showTrackedChanges is on.
|
|
409
423
|
if (revisionModel) {
|
|
410
424
|
for (const rev of revisionModel.revisions) {
|
|
411
|
-
//
|
|
412
|
-
//
|
|
413
|
-
//
|
|
414
|
-
|
|
425
|
+
// §4.4 — L03's `revisionDisplay.hidden` is the authoritative hide
|
|
426
|
+
// signal when the caller threads the offset map. When absent, fall
|
|
427
|
+
// back to the pre-L03 `markupDisplay === "clean" && deletion` rule
|
|
428
|
+
// so legacy / headless callers keep their final-text semantics.
|
|
429
|
+
const revDisplayFlags = revisionDisplayByOffset?.get(rev.from);
|
|
430
|
+
const hideAsDeletion =
|
|
431
|
+
rev.kind === "deletion" &&
|
|
432
|
+
(revDisplayFlags
|
|
433
|
+
? revDisplayFlags.hidden === true
|
|
434
|
+
: markupDisplay === "clean");
|
|
435
|
+
if (hideAsDeletion) {
|
|
415
436
|
const cleanPmFrom = positionMap.runtimeToPm(rev.from);
|
|
416
437
|
const cleanPmTo = positionMap.runtimeToPm(rev.to);
|
|
417
438
|
if (cleanPmFrom < cleanPmTo) {
|
|
@@ -470,12 +491,18 @@ export function buildDecorations(
|
|
|
470
491
|
// Skip normal markup styling when tracked changes display is off
|
|
471
492
|
if (!showTrackedChanges) continue;
|
|
472
493
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
494
|
+
// §4.3 — prefer L03's authoritative `revisionDisplay` flags when
|
|
495
|
+
// the caller threads the offset map; otherwise fall through to the
|
|
496
|
+
// review-store side-channel class helper.
|
|
497
|
+
const displayFlags = revisionDisplayByOffset?.get(rev.from);
|
|
498
|
+
const cls = displayFlags
|
|
499
|
+
? buildClassFromRevisionDisplay(displayFlags)
|
|
500
|
+
: getRevisionHighlightClass(
|
|
501
|
+
revisionModel,
|
|
502
|
+
rev.from,
|
|
503
|
+
rev.to,
|
|
504
|
+
markupDisplay,
|
|
505
|
+
);
|
|
479
506
|
if (!cls) continue;
|
|
480
507
|
|
|
481
508
|
decorations.push(
|