@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
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
|
|
3
3
|
import { preserveEditorSelectionMouseDown } from "../../ui/headless/preserve-editor-selection";
|
|
4
4
|
import type { ActiveImageContext } from "../../ui/headless/selection-tool-types";
|
|
5
|
-
import { EMU_PER_INCH } from "../../
|
|
5
|
+
import { EMU_PER_INCH } from "../../api/public-types.ts";
|
|
6
6
|
|
|
7
7
|
export interface TwImageContextToolbarProps {
|
|
8
8
|
activeImage: ActiveImageContext;
|
|
@@ -15,6 +15,16 @@ export interface TwImageContextToolbarProps {
|
|
|
15
15
|
mediaId: string,
|
|
16
16
|
offsets: { horizontalOffsetEmu?: number; verticalOffsetEmu?: number },
|
|
17
17
|
) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Phase D.3 — progressive-disclosure compact mode. When `true`,
|
|
20
|
+
* the toolbar reduces to the image badge + a single "More…" button
|
|
21
|
+
* that opens the shared context menu via `onOpenMore`. Size
|
|
22
|
+
* presets + nudge controls move into the registry's image actions
|
|
23
|
+
* (see `editor-action-registry.ts` — image-size-small/medium/large
|
|
24
|
+
* + image-replace + image-alt-text). See DESIGN-EDITOR.md §6.4.
|
|
25
|
+
*/
|
|
26
|
+
compact?: boolean;
|
|
27
|
+
onOpenMore?: (coords: { clientX: number; clientY: number }) => void;
|
|
18
28
|
}
|
|
19
29
|
|
|
20
30
|
const IMAGE_SIZE_PRESETS = [
|
|
@@ -28,6 +38,37 @@ const NUDGE_EMU = 228600;
|
|
|
28
38
|
export function TwImageContextToolbar(props: TwImageContextToolbarProps) {
|
|
29
39
|
const { activeImage } = props;
|
|
30
40
|
|
|
41
|
+
if (props.compact) {
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
data-testid="image-context-toolbar"
|
|
45
|
+
data-variant="compact"
|
|
46
|
+
className="inline-flex items-center gap-[6px] rounded-[var(--radius-lg)] border border-[var(--color-border-subtle)] bg-[var(--color-bg-canvas)] px-2 py-1 shadow-[var(--shadow-float)]"
|
|
47
|
+
>
|
|
48
|
+
<span className="text-[9px] font-semibold uppercase tracking-[0.12em] text-tertiary">
|
|
49
|
+
Image
|
|
50
|
+
</span>
|
|
51
|
+
<span className="rounded-full bg-surface px-1.5 py-0.5 text-[9px] font-medium uppercase tracking-[0.1em] text-secondary">
|
|
52
|
+
{activeImage.display}
|
|
53
|
+
</span>
|
|
54
|
+
<button
|
|
55
|
+
type="button"
|
|
56
|
+
data-testid="image-context-toolbar-more"
|
|
57
|
+
aria-label="Image actions menu"
|
|
58
|
+
className="inline-flex h-6 items-center gap-1 rounded-[var(--radius-sm)] px-2 text-xs font-medium text-secondary hover:bg-hover focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)]"
|
|
59
|
+
disabled={props.disabled}
|
|
60
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
61
|
+
onClick={(ev) => {
|
|
62
|
+
const rect = (ev.currentTarget as HTMLButtonElement).getBoundingClientRect();
|
|
63
|
+
props.onOpenMore?.({ clientX: rect.left, clientY: rect.bottom });
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
More…
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
31
72
|
return (
|
|
32
73
|
<div
|
|
33
74
|
data-testid="image-context-toolbar"
|
|
@@ -19,8 +19,8 @@ import type {
|
|
|
19
19
|
RenderFrameRect,
|
|
20
20
|
SelectionSnapshot,
|
|
21
21
|
TableStructureContextSnapshot,
|
|
22
|
-
WordReviewEditorLayoutFacet,
|
|
23
22
|
} from "../../api/public-types";
|
|
23
|
+
import type { GeometryFacet } from "../../api/public-types";
|
|
24
24
|
import type {
|
|
25
25
|
ActiveImageContext,
|
|
26
26
|
ActiveObjectContext,
|
|
@@ -28,7 +28,11 @@ import type {
|
|
|
28
28
|
} from "../../ui/headless/selection-tool-types";
|
|
29
29
|
|
|
30
30
|
export interface ResolveSelectionAnchorInput {
|
|
31
|
-
|
|
31
|
+
// refactor/05 cross-lane-coord §8.4 migration: read through
|
|
32
|
+
// runtime.geometry (not runtime.layout). Geometry facet carries
|
|
33
|
+
// getRenderFrame as a required method; the prior optional form on
|
|
34
|
+
// WordReviewEditorLayoutFacet is being deleted.
|
|
35
|
+
geometryFacet: GeometryFacet;
|
|
32
36
|
selection: SelectionSnapshot;
|
|
33
37
|
tool: ActiveSelectionToolModel | null;
|
|
34
38
|
}
|
|
@@ -52,12 +56,9 @@ export interface ResolveSelectionAnchorInput {
|
|
|
52
56
|
export function resolveSelectionAnchor(
|
|
53
57
|
input: ResolveSelectionAnchorInput,
|
|
54
58
|
): RenderFrameRect | null {
|
|
55
|
-
const {
|
|
59
|
+
const { geometryFacet, selection, tool } = input;
|
|
56
60
|
// No kernel, no anchor — caller falls back to DOM rects in that case.
|
|
57
|
-
const frame =
|
|
58
|
-
typeof facet.getRenderFrame === "function"
|
|
59
|
-
? facet.getRenderFrame() ?? null
|
|
60
|
-
: null;
|
|
61
|
+
const frame = geometryFacet.getRenderFrame() ?? null;
|
|
61
62
|
if (!frame) return null;
|
|
62
63
|
|
|
63
64
|
if (!tool) {
|
|
@@ -6,18 +6,52 @@ export interface TwSelectionToolBlockedProps {
|
|
|
6
6
|
model: BlockedExplainerSelectionToolModel;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Blocked-action explanation card. Per designsystem.md §6.8 the spec
|
|
11
|
+
* calls for:
|
|
12
|
+
* - Plain operational copy ("Editing is blocked in this scope.")
|
|
13
|
+
* - Always expose the alternative action as a button when available
|
|
14
|
+
* - Warning-tone severity ring; not error-tone unless destructive
|
|
15
|
+
* - role="status" + aria-live="polite"
|
|
16
|
+
*
|
|
17
|
+
* Chrome Closure Pass · Task 5 — split the previous single-line
|
|
18
|
+
* disabledReason render into an eyebrow ("Unavailable here") + a
|
|
19
|
+
* full-text body, and add a warning-tone severity ring + leading
|
|
20
|
+
* severity dot per §3.8 severity ladder. The model carries no
|
|
21
|
+
* "alternative action" callback today (out-of-scope for this slice
|
|
22
|
+
* — runtime enrichment is a follow-up issue against L06); when one
|
|
23
|
+
* lands, the action button slot below already has its placement
|
|
24
|
+
* guarded by the conditional render so adding a callback is a one-
|
|
25
|
+
* prop change.
|
|
26
|
+
*/
|
|
9
27
|
export function TwSelectionToolBlocked(props: TwSelectionToolBlockedProps) {
|
|
28
|
+
const { disabledReason } = props.model;
|
|
10
29
|
return (
|
|
11
30
|
<div
|
|
12
31
|
data-testid="blocked-selection-tool"
|
|
13
|
-
className="max-w-[min(20rem,calc(100vw-1.5rem))] rounded-lg border border-
|
|
32
|
+
className="max-w-[min(20rem,calc(100vw-1.5rem))] rounded-lg border border-[var(--color-semantic-warning)] bg-canvas px-2.5 py-2 shadow-md ring-1 ring-[var(--color-semantic-warning-soft)]"
|
|
14
33
|
role="status"
|
|
15
34
|
aria-live="polite"
|
|
16
35
|
>
|
|
17
|
-
<div className="
|
|
18
|
-
|
|
36
|
+
<div className="flex items-center gap-2">
|
|
37
|
+
<span
|
|
38
|
+
data-testid="blocked-selection-tool__severity"
|
|
39
|
+
className="inline-block h-1.5 w-1.5 rounded-full bg-[var(--color-semantic-warning)]"
|
|
40
|
+
aria-hidden="true"
|
|
41
|
+
/>
|
|
42
|
+
<span
|
|
43
|
+
data-testid="blocked-selection-tool__eyebrow"
|
|
44
|
+
className="text-[9px] font-semibold uppercase tracking-[0.12em] text-[var(--color-semantic-warning)]"
|
|
45
|
+
>
|
|
46
|
+
Unavailable here
|
|
47
|
+
</span>
|
|
48
|
+
</div>
|
|
49
|
+
<div
|
|
50
|
+
data-testid="blocked-selection-tool__reason"
|
|
51
|
+
className="mt-1 text-[13px] text-primary"
|
|
52
|
+
>
|
|
53
|
+
{disabledReason}
|
|
19
54
|
</div>
|
|
20
|
-
<div className="mt-0.5 text-[13px] text-primary">{props.model.disabledReason}</div>
|
|
21
55
|
</div>
|
|
22
56
|
);
|
|
23
57
|
}
|
|
@@ -8,22 +8,120 @@ export interface TwSelectionToolCommentProps {
|
|
|
8
8
|
onAddComment?: () => void;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Format an ISO-8601 timestamp as a relative-time hint suitable for the
|
|
13
|
+
* comment card header. Matches §6.6 anatomy ("avatar/initials + name +
|
|
14
|
+
* relative time, text.xs"). Falls through to absolute date when the
|
|
15
|
+
* delta is more than a week old. Pure helper — no Intl polyfills, no
|
|
16
|
+
* locale assumptions beyond the canonical ISO format the runtime emits.
|
|
17
|
+
*/
|
|
18
|
+
function formatRelative(iso: string | undefined, now = Date.now()): string {
|
|
19
|
+
if (!iso) return "";
|
|
20
|
+
const t = Date.parse(iso);
|
|
21
|
+
if (Number.isNaN(t)) return "";
|
|
22
|
+
const deltaSec = Math.max(0, Math.floor((now - t) / 1000));
|
|
23
|
+
if (deltaSec < 60) return "just now";
|
|
24
|
+
const min = Math.floor(deltaSec / 60);
|
|
25
|
+
if (min < 60) return `${min}m ago`;
|
|
26
|
+
const hr = Math.floor(min / 60);
|
|
27
|
+
if (hr < 24) return `${hr}h ago`;
|
|
28
|
+
const day = Math.floor(hr / 24);
|
|
29
|
+
if (day < 7) return `${day}d ago`;
|
|
30
|
+
// Older than a week — show the date portion of the ISO string.
|
|
31
|
+
return iso.slice(0, 10);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Initials helper for the author chip — first letter of each
|
|
36
|
+
* whitespace-separated word, max 2 chars. Falls back to "?" for empty
|
|
37
|
+
* input so the visual rhythm of the chip stays stable.
|
|
38
|
+
*/
|
|
39
|
+
function initialsOf(name: string | undefined): string {
|
|
40
|
+
if (!name) return "?";
|
|
41
|
+
const parts = name.trim().split(/\s+/).filter(Boolean);
|
|
42
|
+
if (parts.length === 0) return "?";
|
|
43
|
+
const head = parts[0]![0]!;
|
|
44
|
+
const tail = parts.length > 1 ? parts[parts.length - 1]![0]! : "";
|
|
45
|
+
return (head + tail).toUpperCase().slice(0, 2);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Comment quick card. Anchored when the selection intersects a comment
|
|
50
|
+
* anchor. Per designsystem.md §6.6 the spec calls for:
|
|
51
|
+
* - Author row: avatar/initials + name + relative time, text.xs
|
|
52
|
+
* - One-or-two-line excerpt at text.sm
|
|
53
|
+
* - One primary action (Open thread / Reply / Comment)
|
|
54
|
+
* - Inline marker pin in margin remains visible (handled elsewhere)
|
|
55
|
+
*
|
|
56
|
+
* Chrome Closure Pass · Task 5 — surface the author + timestamp +
|
|
57
|
+
* thread-status fields the model already exposes; previously the card
|
|
58
|
+
* showed only `previewText` and a single "Comment" button, leaving the
|
|
59
|
+
* §6.6 anatomy unrealized despite the data being available.
|
|
60
|
+
*/
|
|
11
61
|
export function TwSelectionToolComment(props: TwSelectionToolCommentProps) {
|
|
62
|
+
const { thread, previewText, canAddComment } = props.model;
|
|
63
|
+
const author = thread?.createdBy;
|
|
64
|
+
const created = thread?.createdAt;
|
|
65
|
+
const status = thread?.status; // open | resolved | detached
|
|
66
|
+
const excerpt = thread?.excerpt ?? previewText ?? "Comment thread";
|
|
67
|
+
const isResolved = status === "resolved";
|
|
68
|
+
const isDetached = status === "detached";
|
|
69
|
+
|
|
70
|
+
const statusToneClass = isResolved
|
|
71
|
+
? "bg-[var(--color-semantic-success-soft)] text-[var(--color-semantic-success)]"
|
|
72
|
+
: isDetached
|
|
73
|
+
? "bg-[var(--color-semantic-warning-soft)] text-[var(--color-semantic-warning)]"
|
|
74
|
+
: "bg-[var(--color-accent-soft)] text-[var(--color-accent-primary)]";
|
|
75
|
+
|
|
12
76
|
return (
|
|
13
77
|
<div
|
|
14
78
|
data-testid="comment-thread-selection-tool"
|
|
15
|
-
className="max-w-[min(20rem,calc(100vw-1.5rem))] rounded-lg border border-border bg-canvas px-2.5 py-
|
|
79
|
+
className="max-w-[min(20rem,calc(100vw-1.5rem))] rounded-lg border border-border bg-canvas px-2.5 py-2 shadow-md"
|
|
16
80
|
>
|
|
17
|
-
<div className="
|
|
18
|
-
|
|
81
|
+
<div className="flex items-center gap-2">
|
|
82
|
+
{author ? (
|
|
83
|
+
<span
|
|
84
|
+
data-testid="comment-thread-selection-tool__avatar"
|
|
85
|
+
className="inline-flex h-5 w-5 items-center justify-center rounded-full bg-[var(--color-bg-muted)] text-[9px] font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]"
|
|
86
|
+
aria-hidden="true"
|
|
87
|
+
>
|
|
88
|
+
{initialsOf(author)}
|
|
89
|
+
</span>
|
|
90
|
+
) : null}
|
|
91
|
+
<span
|
|
92
|
+
data-testid="comment-thread-selection-tool__author"
|
|
93
|
+
className="truncate text-[11px] font-semibold text-[var(--color-text-primary)]"
|
|
94
|
+
>
|
|
95
|
+
{author ?? "Comment"}
|
|
96
|
+
</span>
|
|
97
|
+
{created ? (
|
|
98
|
+
<span
|
|
99
|
+
data-testid="comment-thread-selection-tool__timestamp"
|
|
100
|
+
className="text-[10px] text-[var(--color-text-tertiary)]"
|
|
101
|
+
title={created}
|
|
102
|
+
>
|
|
103
|
+
{formatRelative(created)}
|
|
104
|
+
</span>
|
|
105
|
+
) : null}
|
|
106
|
+
{status && status !== "open" ? (
|
|
107
|
+
<span
|
|
108
|
+
data-testid="comment-thread-selection-tool__status"
|
|
109
|
+
className={`ml-auto inline-flex items-center rounded-[var(--radius-pill)] px-1.5 py-px text-[9px] font-semibold uppercase tracking-[0.08em] ${statusToneClass}`}
|
|
110
|
+
>
|
|
111
|
+
{status}
|
|
112
|
+
</span>
|
|
113
|
+
) : null}
|
|
19
114
|
</div>
|
|
20
|
-
<div
|
|
21
|
-
|
|
115
|
+
<div
|
|
116
|
+
data-testid="comment-thread-selection-tool__excerpt"
|
|
117
|
+
className="mt-1 line-clamp-2 text-[13px] text-primary"
|
|
118
|
+
>
|
|
119
|
+
{excerpt}
|
|
22
120
|
</div>
|
|
23
121
|
<button
|
|
24
122
|
type="button"
|
|
25
123
|
aria-label="Add comment from thread"
|
|
26
|
-
disabled={!
|
|
124
|
+
disabled={!canAddComment || !props.onAddComment}
|
|
27
125
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
28
126
|
onClick={props.onAddComment}
|
|
29
127
|
className="mt-1.5 inline-flex h-7 items-center rounded-md border border-border px-2 text-[11px] font-medium text-secondary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40"
|
|
@@ -7,6 +7,10 @@ import type {
|
|
|
7
7
|
RuntimeContextAnalyticsSnapshot,
|
|
8
8
|
} from "../../api/public-types";
|
|
9
9
|
import type { ActiveSelectionToolModel } from "../../ui/headless/selection-tool-types";
|
|
10
|
+
import {
|
|
11
|
+
useLocalSurfaceRequest,
|
|
12
|
+
type LocalSurfaceKind,
|
|
13
|
+
} from "./local-surface-arbiter";
|
|
10
14
|
import { TwContextAnalyticsSummary } from "./tw-context-analytics-summary";
|
|
11
15
|
import { TwDetachHandle } from "./tw-detach-handle";
|
|
12
16
|
import { TwSelectionToolBlocked } from "./tw-selection-tool-blocked";
|
|
@@ -109,10 +113,42 @@ export function TwSelectionToolHost(props: TwSelectionToolHostProps) {
|
|
|
109
113
|
const density = useDwellDensity(props.tool);
|
|
110
114
|
const { onChromePinChange } = props;
|
|
111
115
|
|
|
116
|
+
// Phase F.2 — gate the selection tool on the shared local-surface
|
|
117
|
+
// arbiter. When a higher-priority peer (scope card, suggestion
|
|
118
|
+
// card, blocked card, workflow card) has the slot, the host yields
|
|
119
|
+
// — no hard-thrash between two floats. The dwell hook above
|
|
120
|
+
// remains the density promotion authority; the arbiter is the
|
|
121
|
+
// visibility authority. A pinned scope card does NOT evict us
|
|
122
|
+
// because pinned surfaces coexist with the single active slot
|
|
123
|
+
// (see `local-surface-arbiter.ts` §pinned slot).
|
|
124
|
+
const arbiterKind = props.tool ? arbiterKindForToolKind(props.tool.kind) : null;
|
|
125
|
+
const isArbiterActive = useLocalSurfaceRequest(
|
|
126
|
+
arbiterKind && props.tool
|
|
127
|
+
? { kind: arbiterKind, id: `selection-${props.tool.kind}` }
|
|
128
|
+
: null,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// All hooks MUST be called unconditionally per the Rules of Hooks —
|
|
132
|
+
// keep `useCallback` above the early returns that depend on runtime
|
|
133
|
+
// state (`props.tool`, arbiter verdict).
|
|
134
|
+
const handlePinChange = useCallback(
|
|
135
|
+
(surface: ChromePinSurface, next: PinState | null) => {
|
|
136
|
+
onChromePinChange?.(surface, next);
|
|
137
|
+
},
|
|
138
|
+
[onChromePinChange],
|
|
139
|
+
);
|
|
140
|
+
|
|
112
141
|
if (!props.tool) {
|
|
113
142
|
return null;
|
|
114
143
|
}
|
|
115
144
|
|
|
145
|
+
if (!isArbiterActive) {
|
|
146
|
+
// A higher-priority local surface currently owns the slot.
|
|
147
|
+
// Rendering returns null so the host contributes no DOM and the
|
|
148
|
+
// rival surface is the only float visible this frame.
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
116
152
|
// R2.3: pin state now rides ViewState.chromePins.selectionTier so
|
|
117
153
|
// pinned tools survive snapshot rebuilds. When the host doesn't pass
|
|
118
154
|
// chromePins/onChromePinChange, the detach handle is suppressed (same
|
|
@@ -123,13 +159,6 @@ export function TwSelectionToolHost(props: TwSelectionToolHostProps) {
|
|
|
123
159
|
const supportsDetach =
|
|
124
160
|
supportsDetachForKind(props.tool.kind) && Boolean(props.onChromePinChange);
|
|
125
161
|
|
|
126
|
-
const handlePinChange = useCallback(
|
|
127
|
-
(surface: ChromePinSurface, next: PinState | null) => {
|
|
128
|
-
onChromePinChange?.(surface, next);
|
|
129
|
-
},
|
|
130
|
-
[onChromePinChange],
|
|
131
|
-
);
|
|
132
|
-
|
|
133
162
|
const overlayTestId = getOverlayTestId(props.tool.kind, Boolean(props.placement));
|
|
134
163
|
const toolContent = renderTool(props, props.tool, density);
|
|
135
164
|
const content = toolContent ? (
|
|
@@ -302,6 +331,36 @@ function supportsDetachForKind(kind: ActiveSelectionToolModel["kind"]): boolean
|
|
|
302
331
|
return kind !== "blocked-explainer";
|
|
303
332
|
}
|
|
304
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Map a selection-tool kind into its local-surface arbiter bucket.
|
|
336
|
+
* Priority ordering lives in `local-surface-arbiter.ts §LOCAL_SURFACE_PRIORITY`.
|
|
337
|
+
* - formatting-inline → selection-format
|
|
338
|
+
* - suggestion-review → suggestion-card
|
|
339
|
+
* - workflow-task → workflow-card
|
|
340
|
+
* - blocked-explainer → blocked-card
|
|
341
|
+
* - comment-thread → comment-preview
|
|
342
|
+
* - structure-context → selection-format (table/image/object
|
|
343
|
+
* selection chrome is the same priority tier as text formatting;
|
|
344
|
+
* the audit does not promote it above selection-format).
|
|
345
|
+
*/
|
|
346
|
+
function arbiterKindForToolKind(
|
|
347
|
+
kind: ActiveSelectionToolModel["kind"],
|
|
348
|
+
): LocalSurfaceKind {
|
|
349
|
+
switch (kind) {
|
|
350
|
+
case "formatting-inline":
|
|
351
|
+
case "structure-context":
|
|
352
|
+
return "selection-format";
|
|
353
|
+
case "suggestion-review":
|
|
354
|
+
return "suggestion-card";
|
|
355
|
+
case "workflow-task":
|
|
356
|
+
return "workflow-card";
|
|
357
|
+
case "blocked-explainer":
|
|
358
|
+
return "blocked-card";
|
|
359
|
+
case "comment-thread":
|
|
360
|
+
return "comment-preview";
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
305
364
|
function getTopMenuLabel(kind: ActiveSelectionToolModel["kind"]): string {
|
|
306
365
|
switch (kind) {
|
|
307
366
|
case "formatting-inline":
|
|
@@ -6,21 +6,67 @@ export interface TwSelectionToolWorkflowProps {
|
|
|
6
6
|
model: WorkflowTaskSelectionToolModel;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Workflow local card. Per designsystem.md §6.7 the spec calls for:
|
|
11
|
+
* - Title (scope name, text.sm semibold)
|
|
12
|
+
* - State chip (active / blocked / scheduled, tone per §3.7)
|
|
13
|
+
* - Allowed-or-blocked-action row (text.sm)
|
|
14
|
+
* - Optional assignee chip
|
|
15
|
+
*
|
|
16
|
+
* Chrome Closure Pass · Task 5 — derive a state chip from the model's
|
|
17
|
+
* `disabledReason`: present → "Blocked" with warning tone; absent →
|
|
18
|
+
* "Active" with accent tone. Title typography lifts to semibold per
|
|
19
|
+
* §6.7. Assignee chip omitted (not on the model — runtime enrichment
|
|
20
|
+
* is a follow-up issue against L06).
|
|
21
|
+
*/
|
|
9
22
|
export function TwSelectionToolWorkflow(props: TwSelectionToolWorkflowProps) {
|
|
23
|
+
const { workflowTitle, workflowDetail, disabledReason } = props.model;
|
|
24
|
+
const isBlocked = Boolean(disabledReason);
|
|
25
|
+
const stateLabel = isBlocked ? "Blocked" : "Active";
|
|
26
|
+
const stateToneClass = isBlocked
|
|
27
|
+
? "bg-[var(--color-semantic-warning-soft)] text-[var(--color-semantic-warning)]"
|
|
28
|
+
: "bg-[var(--color-accent-soft)] text-[var(--color-accent-primary)]";
|
|
29
|
+
|
|
10
30
|
return (
|
|
11
31
|
<div
|
|
12
32
|
data-testid="workflow-task-selection-tool"
|
|
13
|
-
className="max-w-[min(20rem,calc(100vw-1.5rem))] rounded-lg border border-border bg-canvas px-2.5 py-
|
|
33
|
+
className="max-w-[min(20rem,calc(100vw-1.5rem))] rounded-lg border border-border bg-canvas px-2.5 py-2 shadow-md"
|
|
14
34
|
>
|
|
15
|
-
<div className="
|
|
16
|
-
|
|
35
|
+
<div className="flex items-center gap-2">
|
|
36
|
+
<span
|
|
37
|
+
data-testid="workflow-task-selection-tool__eyebrow"
|
|
38
|
+
className="text-[9px] font-semibold uppercase tracking-[0.12em] text-tertiary"
|
|
39
|
+
>
|
|
40
|
+
Workflow
|
|
41
|
+
</span>
|
|
42
|
+
<span
|
|
43
|
+
data-testid="workflow-task-selection-tool__state"
|
|
44
|
+
className={`ml-auto inline-flex items-center rounded-[var(--radius-pill)] px-1.5 py-px text-[9px] font-semibold uppercase tracking-[0.08em] ${stateToneClass}`}
|
|
45
|
+
>
|
|
46
|
+
{stateLabel}
|
|
47
|
+
</span>
|
|
48
|
+
</div>
|
|
49
|
+
<div
|
|
50
|
+
data-testid="workflow-task-selection-tool__title"
|
|
51
|
+
className="mt-1 text-[13px] font-semibold text-primary"
|
|
52
|
+
>
|
|
53
|
+
{workflowTitle ?? "Scoped task"}
|
|
17
54
|
</div>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
55
|
+
{workflowDetail ? (
|
|
56
|
+
<div
|
|
57
|
+
data-testid="workflow-task-selection-tool__detail"
|
|
58
|
+
className="mt-0.5 text-[11px] text-secondary"
|
|
59
|
+
>
|
|
60
|
+
{workflowDetail}
|
|
61
|
+
</div>
|
|
21
62
|
) : null}
|
|
22
|
-
{
|
|
23
|
-
<div
|
|
63
|
+
{disabledReason ? (
|
|
64
|
+
<div
|
|
65
|
+
data-testid="workflow-task-selection-tool__reason"
|
|
66
|
+
className="mt-1 text-[11px] text-[var(--color-semantic-warning)]"
|
|
67
|
+
>
|
|
68
|
+
{disabledReason}
|
|
69
|
+
</div>
|
|
24
70
|
) : null}
|
|
25
71
|
</div>
|
|
26
72
|
);
|
|
@@ -12,6 +12,12 @@ export interface TwShortcutHintProps {
|
|
|
12
12
|
|
|
13
13
|
const MAC_SYMBOL: Record<string, string> = {
|
|
14
14
|
Cmd: "⌘",
|
|
15
|
+
// "Mod" is the registry's cross-platform modifier — Cmd on Mac,
|
|
16
|
+
// Ctrl elsewhere. Without this entry rows like `["Mod", "X"]`
|
|
17
|
+
// rendered as the literal "ModX" on Mac + "Mod+X" on Win instead
|
|
18
|
+
// of the expected "⌘X" / "Ctrl+X". Chrome Closure Pass Task 4
|
|
19
|
+
// fix-up.
|
|
20
|
+
Mod: "⌘",
|
|
15
21
|
Shift: "⇧",
|
|
16
22
|
Alt: "⌥",
|
|
17
23
|
Ctrl: "⌃",
|
|
@@ -46,7 +52,7 @@ export function TwShortcutHint(props: TwShortcutHintProps): React.JSX.Element {
|
|
|
46
52
|
const mapped = keys.map((k) =>
|
|
47
53
|
platform === "mac"
|
|
48
54
|
? (MAC_SYMBOL[k] ?? k)
|
|
49
|
-
: (k === "Cmd" ? "Ctrl" : k)
|
|
55
|
+
: (k === "Cmd" || k === "Mod" ? "Ctrl" : k),
|
|
50
56
|
);
|
|
51
57
|
|
|
52
58
|
const containerClass = [
|
|
@@ -14,6 +14,16 @@ export interface TwSuggestionCardProps {
|
|
|
14
14
|
onReject?: () => void;
|
|
15
15
|
onEditSuggestion?: () => void;
|
|
16
16
|
onAddComment?: () => void;
|
|
17
|
+
/**
|
|
18
|
+
* Phase D.4 — rail-detail handoff. When wired, the card exposes a
|
|
19
|
+
* "See detail →" link that opens the `Changes` rail tab focused on
|
|
20
|
+
* the current suggestion (audit §2.8 "long suggestion rationale to
|
|
21
|
+
* rail" + DESIGN-EDITOR.md §6.5 "clicking the card body opens the
|
|
22
|
+
* rail thread"). Lets the floating card stay compact — summary +
|
|
23
|
+
* accept/reject + next — while long rationale lives in the rail
|
|
24
|
+
* where it belongs.
|
|
25
|
+
*/
|
|
26
|
+
onOpenDetailInRail?: () => void;
|
|
17
27
|
}
|
|
18
28
|
|
|
19
29
|
const focusRingClass =
|
|
@@ -96,6 +106,29 @@ export function TwSuggestionCard(props: TwSuggestionCardProps) {
|
|
|
96
106
|
</Tooltip.Content>
|
|
97
107
|
</Tooltip.Portal>
|
|
98
108
|
</Tooltip.Root>
|
|
109
|
+
{/*
|
|
110
|
+
* Rail-detail handoff (Phase D.4). Only renders when the host
|
|
111
|
+
* wires `onOpenDetailInRail` so integrators without a rail
|
|
112
|
+
* don't get a dead link. Secondary-tone text link, not a
|
|
113
|
+
* button — per designsystem.md §6.5 the card stays compact.
|
|
114
|
+
*/}
|
|
115
|
+
{props.onOpenDetailInRail ? (
|
|
116
|
+
<button
|
|
117
|
+
type="button"
|
|
118
|
+
data-testid="suggestion-card-rail-handoff"
|
|
119
|
+
aria-label="See suggestion detail in rail"
|
|
120
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
121
|
+
onClick={props.onOpenDetailInRail}
|
|
122
|
+
// D.5.N6 — align focus ring with the design-system
|
|
123
|
+
// --shadow-focus token (designsystem.md §4.7) rather than
|
|
124
|
+
// the older Tailwind ring utility used elsewhere in this
|
|
125
|
+
// file. The ring token is mint-glow consistent across
|
|
126
|
+
// light/dark; Phase B/D compact toolbars already use it.
|
|
127
|
+
className="ml-auto inline-flex h-7 items-center text-[11px] font-medium text-[var(--color-text-tertiary)] underline decoration-dotted underline-offset-2 hover:text-[var(--color-accent-primary)] focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)] rounded-[var(--radius-sm)]"
|
|
128
|
+
>
|
|
129
|
+
See detail →
|
|
130
|
+
</button>
|
|
131
|
+
) : null}
|
|
99
132
|
</div>
|
|
100
133
|
</div>
|
|
101
134
|
);
|
|
@@ -28,6 +28,35 @@ export interface TwTableContextToolbarProps {
|
|
|
28
28
|
onDistributeColumnsEvenly?: () => void;
|
|
29
29
|
onSetTableAlignment?: (alignment: "left" | "center" | "right") => void;
|
|
30
30
|
onSetCellVerticalAlign?: (align: "top" | "center" | "bottom") => void;
|
|
31
|
+
/**
|
|
32
|
+
* Phase D.2 — progressive-disclosure compact mode.
|
|
33
|
+
*
|
|
34
|
+
* When `true`, the toolbar reduces to:
|
|
35
|
+
* - the tier badge + size + selection label (diagnostic context)
|
|
36
|
+
* - a single "More…" button that fires `onOpenMore`
|
|
37
|
+
*
|
|
38
|
+
* All action controls (Add row / Merge / Split / Fill / Style /
|
|
39
|
+
* Delete / Align / V-align / Distribute / Borders) are suppressed;
|
|
40
|
+
* the full set lives in the shared editor-action-registry and is
|
|
41
|
+
* accessed via right-click OR the "More…" button (which opens the
|
|
42
|
+
* same context menu). Per DESIGN-EDITOR.md §6.4 ("right-click
|
|
43
|
+
* cannot be richer than the floating surface; cannot duplicate
|
|
44
|
+
* command trees") the compact variant pins that rule — there IS no
|
|
45
|
+
* richer surface to diverge from.
|
|
46
|
+
*
|
|
47
|
+
* Default `false` preserves the rich in-tree behavior so no
|
|
48
|
+
* existing integrator breaks. Phase D.2 ships the opt-in; Phase E
|
|
49
|
+
* flips the default to `true` once the end-to-end
|
|
50
|
+
* `onContextMenuRequested` wiring is proven.
|
|
51
|
+
*/
|
|
52
|
+
compact?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Fires when the user clicks the "More…" button in compact mode.
|
|
55
|
+
* Receives the button's clientX / clientY so the integrator can
|
|
56
|
+
* open `TwContextMenu` at the button anchor via
|
|
57
|
+
* `chromeControllerRef.current?.openWithKinds({ kinds: ["table-cell"], clientX, clientY })`.
|
|
58
|
+
*/
|
|
59
|
+
onOpenMore?: (coords: { clientX: number; clientY: number }) => void;
|
|
31
60
|
}
|
|
32
61
|
|
|
33
62
|
/**
|
|
@@ -117,6 +146,44 @@ export function TwTableContextToolbar(props: TwTableContextToolbarProps) {
|
|
|
117
146
|
: null;
|
|
118
147
|
const selectionLabel = tableContext ? formatSelectionLabel(tableContext, tier) : null;
|
|
119
148
|
|
|
149
|
+
// Phase D.2 — compact variant: minimal inline affordance + one
|
|
150
|
+
// "More…" button that opens the shared context menu. The full
|
|
151
|
+
// action set lives in editor-action-registry so right-click and
|
|
152
|
+
// "More…" always show identical options (DESIGN-EDITOR.md §6.4).
|
|
153
|
+
if (props.compact) {
|
|
154
|
+
return (
|
|
155
|
+
<div
|
|
156
|
+
data-testid="table-context-toolbar"
|
|
157
|
+
data-tier={tier}
|
|
158
|
+
data-variant="compact"
|
|
159
|
+
className="inline-flex items-center gap-[6px] rounded-[var(--radius-lg)] border border-[var(--color-border-subtle)] bg-[var(--color-bg-canvas)] px-2 py-1 shadow-[var(--shadow-float)]"
|
|
160
|
+
>
|
|
161
|
+
<span className="text-[9px] font-semibold uppercase tracking-[0.12em] text-[var(--color-text-tertiary)]">
|
|
162
|
+
{tierLabel(tier)}
|
|
163
|
+
</span>
|
|
164
|
+
{tableSizeLabel ? <ToolbarBadge>{tableSizeLabel}</ToolbarBadge> : null}
|
|
165
|
+
{selectionLabel ? <ToolbarBadge>{selectionLabel}</ToolbarBadge> : null}
|
|
166
|
+
<button
|
|
167
|
+
type="button"
|
|
168
|
+
data-testid="table-context-toolbar-more"
|
|
169
|
+
aria-label="Table actions menu"
|
|
170
|
+
className="inline-flex h-6 items-center gap-1 rounded-[var(--radius-sm)] px-2 text-xs font-medium text-secondary hover:bg-hover focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)]"
|
|
171
|
+
disabled={props.disabled}
|
|
172
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
173
|
+
onClick={(ev) => {
|
|
174
|
+
const rect = (ev.currentTarget as HTMLButtonElement).getBoundingClientRect();
|
|
175
|
+
props.onOpenMore?.({
|
|
176
|
+
clientX: rect.left,
|
|
177
|
+
clientY: rect.bottom,
|
|
178
|
+
});
|
|
179
|
+
}}
|
|
180
|
+
>
|
|
181
|
+
More…
|
|
182
|
+
</button>
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
120
187
|
// Tier-specific width caps. Progressive: T2 ≤ 18rem, T3 ≤ 22rem, T4
|
|
121
188
|
// ≤ 24rem, T5 ≤ 28rem. Down from the old flat 30rem always.
|
|
122
189
|
const widthCap = tierWidthCap(tier);
|
|
@@ -147,7 +214,17 @@ export function TwTableContextToolbar(props: TwTableContextToolbarProps) {
|
|
|
147
214
|
capability={tableContext?.operations.setTableAlignment}
|
|
148
215
|
disabled={props.disabled}
|
|
149
216
|
onClick={() => props.onSetTableAlignment?.(align)}
|
|
150
|
-
|
|
217
|
+
// Chrome Closure Pass · Task 3 — pre-fix bug:
|
|
218
|
+
// `active` was hardcoded to `align === "left"`, lighting
|
|
219
|
+
// up the Left button regardless of the actual table
|
|
220
|
+
// alignment. `TableStructureContextSnapshot` does not
|
|
221
|
+
// currently surface table-level alignment (the field
|
|
222
|
+
// lives on `RuntimeTableRenderPlanSnapshot`); until the
|
|
223
|
+
// runtime exposes it on the structure context, treat
|
|
224
|
+
// alignment buttons as plain action buttons (no toggle).
|
|
225
|
+
// Fix scope: presentation-only correction; runtime
|
|
226
|
+
// enrichment is a follow-up issue against L07.
|
|
227
|
+
active={false}
|
|
151
228
|
>
|
|
152
229
|
{align[0]!.toUpperCase()}
|
|
153
230
|
</ToolbarButton>
|