@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.
Files changed (384) hide show
  1. package/README.md +75 -931
  2. package/package.json +26 -27
  3. package/src/api/anchor-conversion.ts +43 -0
  4. package/src/api/editor-state-types.ts +2 -1
  5. package/src/api/public-types.ts +504 -101
  6. package/src/api/session-state.ts +4 -0
  7. package/src/api/v3/README.md +91 -0
  8. package/src/api/v3/_create.ts +146 -0
  9. package/src/api/v3/_layer-metadata.ts +362 -0
  10. package/src/api/v3/_mocks.ts +84 -0
  11. package/src/api/v3/_runtime-handle.ts +162 -0
  12. package/src/api/v3/_ux-response.ts +73 -0
  13. package/src/api/v3/ai/_metadata-audit.ts +225 -0
  14. package/src/api/v3/ai/attach.ts +235 -0
  15. package/src/api/v3/ai/bundle.ts +132 -0
  16. package/src/api/v3/ai/explain.ts +144 -0
  17. package/src/api/v3/ai/export.ts +54 -0
  18. package/src/api/v3/ai/inspect.ts +118 -0
  19. package/src/api/v3/ai/policy.ts +77 -0
  20. package/src/api/v3/ai/replacement.ts +341 -0
  21. package/src/api/v3/ai/resolve.ts +133 -0
  22. package/src/api/v3/index.ts +79 -0
  23. package/src/api/v3/runtime/chart.ts +310 -0
  24. package/src/api/v3/runtime/clipboard.ts +81 -0
  25. package/src/api/v3/runtime/collab.ts +331 -0
  26. package/src/api/v3/runtime/content.ts +236 -0
  27. package/src/api/v3/runtime/document.ts +282 -0
  28. package/src/api/v3/runtime/formatting.ts +186 -0
  29. package/src/api/v3/runtime/geometry.ts +349 -0
  30. package/src/api/v3/runtime/layout.ts +108 -0
  31. package/src/api/v3/runtime/review.ts +129 -0
  32. package/src/api/v3/runtime/search.ts +74 -0
  33. package/src/api/v3/runtime/table.ts +63 -0
  34. package/src/api/v3/runtime/workflow.ts +434 -0
  35. package/src/api/v3/ui/_context.ts +86 -0
  36. package/src/api/v3/ui/_create.ts +65 -0
  37. package/src/api/v3/ui/_types.ts +520 -0
  38. package/src/api/v3/ui/chrome-composition.ts +342 -0
  39. package/src/{ui-tailwind/chrome → api/v3/ui}/chrome-preset-model.ts +11 -1
  40. package/src/api/v3/ui/chrome.ts +476 -0
  41. package/src/api/v3/ui/debug.ts +124 -0
  42. package/src/api/v3/ui/index.ts +64 -0
  43. package/src/api/v3/ui/overlays-visibility.ts +170 -0
  44. package/src/api/v3/ui/overlays.ts +427 -0
  45. package/src/api/v3/ui/scope.ts +71 -0
  46. package/src/api/v3/ui/session.ts +100 -0
  47. package/src/api/v3/ui/surface.ts +170 -0
  48. package/src/api/v3/ui/viewport.ts +303 -0
  49. package/src/core/commands/index.ts +28 -6
  50. package/src/core/commands/list-commands.ts +3 -2
  51. package/src/core/commands/section-layout-commands.ts +9 -8
  52. package/src/core/schema/text-schema.ts +16 -0
  53. package/src/core/selection/mapping.ts +33 -72
  54. package/src/core/state/editor-state.ts +96 -189
  55. package/src/index.ts +23 -4
  56. package/src/io/chart-preview-resolver.ts +1 -1
  57. package/src/io/docx-session.ts +36 -4795
  58. package/src/io/export/build-app-properties-xml.ts +1 -1
  59. package/src/io/export/serialize-comments.ts +1 -1
  60. package/src/io/export/serialize-headers-footers.ts +6 -1
  61. package/src/io/export/serialize-main-document.ts +45 -0
  62. package/src/io/export/serialize-run-formatting.ts +17 -2
  63. package/src/io/export/twip.ts +1 -1
  64. package/src/io/normalize/normalize-text.ts +27 -20
  65. package/src/io/ooxml/chart/parse-series.ts +1 -1
  66. package/src/io/ooxml/chart/resolve-color.ts +2 -2
  67. package/src/io/ooxml/chart/types.ts +1 -1
  68. package/src/io/ooxml/classify-embedding.ts +83 -33
  69. package/src/io/ooxml/parse-fill.ts +1 -1
  70. package/src/io/ooxml/parse-main-document.ts +71 -1
  71. package/src/io/ooxml/parse-object.ts +14 -10
  72. package/src/io/ooxml/parse-run-formatting.ts +47 -1
  73. package/src/io/ooxml/property-grab-bag.ts +2 -2
  74. package/src/io/ooxml/units.ts +11 -0
  75. package/src/io/ooxml/workflow-payload.ts +282 -7
  76. package/src/model/anchor.ts +85 -0
  77. package/src/model/canonical-document.ts +351 -15
  78. package/src/model/chart-types.ts +1 -1
  79. package/src/model/layout/index.ts +83 -0
  80. package/src/model/layout/page-graph-types.ts +181 -0
  81. package/src/model/layout/page-layout-snapshot.ts +105 -0
  82. package/src/model/layout/resolved-layout-types.ts +47 -0
  83. package/src/model/layout/runtime-page-graph-types.ts +102 -0
  84. package/src/model/paragraph-scope-ids.ts +72 -0
  85. package/src/model/review/comment-types.ts +112 -0
  86. package/src/model/review/index.ts +2 -0
  87. package/src/model/review/revision-types.ts +215 -0
  88. package/src/model/snapshot.ts +32 -0
  89. package/src/review/store/comment-store.ts +21 -47
  90. package/src/review/store/revision-types.ts +40 -198
  91. package/src/runtime/collab/base-doc-fingerprint.ts +6 -1
  92. package/src/runtime/collab/runtime-collab-sync.ts +13 -3
  93. package/src/runtime/collab-session.ts +1 -1
  94. package/src/runtime/debug/build-debug-inspector-snapshot.ts +686 -0
  95. package/src/runtime/debug/event-ring-buffer.ts +64 -0
  96. package/src/runtime/debug/probability-sampler.ts +18 -0
  97. package/src/runtime/debug/runtime-debug-facet.ts +67 -0
  98. package/src/runtime/debug/stage-tokens.ts +31 -0
  99. package/src/runtime/debug/telemetry-bus.ts +271 -0
  100. package/src/runtime/debug/types.ts +275 -0
  101. package/src/runtime/debug/wrap-ref-for-telemetry.ts +118 -0
  102. package/src/runtime/document-layout.ts +8 -6
  103. package/src/runtime/document-runtime.ts +843 -1141
  104. package/src/runtime/document-search.ts +1 -1
  105. package/src/runtime/edit-ops/index.ts +1 -1
  106. package/src/runtime/external-send-runtime.ts +1 -1
  107. package/src/runtime/formatting/document-lookup.ts +235 -0
  108. package/src/runtime/formatting/field/registry.ts +41 -0
  109. package/src/runtime/{field-resolver.ts → formatting/field/resolver.ts} +27 -2
  110. package/src/runtime/formatting/font-resolution.ts +83 -0
  111. package/src/runtime/formatting/formatting-context.ts +903 -0
  112. package/src/runtime/formatting/formatting-types.ts +157 -0
  113. package/src/runtime/{hyperlink-color-resolver.ts → formatting/hyperlink-color.ts} +2 -2
  114. package/src/runtime/formatting/index.ts +125 -0
  115. package/src/runtime/{resolved-numbering-geometry.ts → formatting/numbering/geometry.ts} +1 -1
  116. package/src/runtime/{numbering-prefix.ts → formatting/numbering/prefix.ts} +170 -3
  117. package/src/runtime/formatting/paragraph-style-resolver.ts +92 -0
  118. package/src/runtime/formatting/projector.ts +75 -0
  119. package/src/runtime/formatting/resolve-effective.ts +407 -0
  120. package/src/runtime/formatting/revision-display.ts +105 -0
  121. package/src/runtime/{paragraph-style-resolver.ts → formatting/style-cascade.ts} +84 -141
  122. package/src/runtime/{table-style-resolver.ts → formatting/table-style-resolver.ts} +1 -1
  123. package/src/runtime/formatting/telemetry-bridge.ts +106 -0
  124. package/src/runtime/{theme-color-resolver.ts → formatting/theme-color.ts} +2 -30
  125. package/src/runtime/geometry/caret-geometry.ts +164 -0
  126. package/src/runtime/geometry/geometry-facet.ts +364 -0
  127. package/src/runtime/geometry/geometry-types.ts +256 -0
  128. package/src/runtime/geometry/hit-test.ts +125 -0
  129. package/src/runtime/geometry/index.ts +71 -0
  130. package/src/runtime/geometry/inert-geometry-facet.ts +43 -0
  131. package/src/runtime/geometry/invalidation.ts +35 -0
  132. package/src/runtime/geometry/object-handles.ts +77 -0
  133. package/src/runtime/geometry/overlay-rects.ts +85 -0
  134. package/src/runtime/geometry/project-anchors.ts +100 -0
  135. package/src/runtime/geometry/project-fragments.ts +216 -0
  136. package/src/runtime/geometry/projector.ts +129 -0
  137. package/src/runtime/geometry/replacement-envelope.ts +130 -0
  138. package/src/runtime/geometry/viewport.ts +218 -0
  139. package/src/runtime/layout/compat-input-ledger.ts +211 -0
  140. package/src/runtime/layout/index.ts +6 -1
  141. package/src/runtime/layout/inert-layout-facet.ts +12 -7
  142. package/src/runtime/layout/layout-engine-instance.ts +189 -11
  143. package/src/runtime/layout/layout-engine-version.ts +450 -1
  144. package/src/runtime/layout/layout-facet-types.ts +60 -0
  145. package/src/runtime/layout/layout-measurement-provider.ts +13 -0
  146. package/src/runtime/layout/measurement-backend-canvas.ts +14 -2
  147. package/src/runtime/layout/measurement-backend-empirical.ts +23 -4
  148. package/src/runtime/layout/page-graph.ts +62 -209
  149. package/src/runtime/layout/page-story-resolver.ts +7 -12
  150. package/src/runtime/layout/paginated-layout-engine.ts +186 -11
  151. package/src/runtime/layout/project-block-fragments.ts +11 -0
  152. package/src/runtime/layout/projector.ts +90 -0
  153. package/src/runtime/layout/public-facet.ts +187 -442
  154. package/src/runtime/layout/resolved-formatting-state.ts +158 -26
  155. package/src/runtime/layout/table-render-plan.ts +1 -1
  156. package/src/runtime/prerender/cache-envelope.ts +6 -1
  157. package/src/runtime/prerender/prerender-document.ts +18 -23
  158. package/src/runtime/render/decoration-resolver.ts +1 -1
  159. package/src/runtime/render/render-frame-types.ts +20 -0
  160. package/src/runtime/render/render-kernel.ts +94 -25
  161. package/src/runtime/scopes/_formatting-seam.ts +262 -0
  162. package/src/runtime/scopes/_scope-dependencies.ts +49 -0
  163. package/src/runtime/scopes/action-validation.ts +356 -0
  164. package/src/runtime/scopes/attach-explanation.ts +102 -0
  165. package/src/runtime/scopes/audit-bundle.ts +71 -0
  166. package/src/runtime/scopes/compile-scope-bundle.ts +163 -0
  167. package/src/runtime/scopes/compile-scope.ts +262 -0
  168. package/src/runtime/scopes/compiler-service.ts +431 -0
  169. package/src/runtime/scopes/create-issue.ts +107 -0
  170. package/src/runtime/scopes/enumerate-scopes.ts +543 -0
  171. package/src/runtime/scopes/evidence.ts +233 -0
  172. package/src/runtime/scopes/index.ts +150 -0
  173. package/src/runtime/scopes/position-map.ts +214 -0
  174. package/src/runtime/scopes/preservation-boundary.ts +91 -0
  175. package/src/runtime/scopes/projector.ts +49 -0
  176. package/src/runtime/scopes/replaceability.ts +87 -0
  177. package/src/runtime/scopes/replacement/apply.ts +228 -0
  178. package/src/runtime/scopes/replacement/compile.ts +59 -0
  179. package/src/runtime/scopes/replacement/propose.ts +42 -0
  180. package/src/runtime/scopes/resolve-reference.ts +347 -0
  181. package/src/runtime/scopes/review-bundle.ts +141 -0
  182. package/src/runtime/scopes/scope-kinds/_paragraph-text.ts +57 -0
  183. package/src/runtime/scopes/scope-kinds/_table-text.ts +42 -0
  184. package/src/runtime/scopes/scope-kinds/comment-thread.ts +59 -0
  185. package/src/runtime/scopes/scope-kinds/field.ts +65 -0
  186. package/src/runtime/scopes/scope-kinds/heading.ts +84 -0
  187. package/src/runtime/scopes/scope-kinds/list-item.ts +77 -0
  188. package/src/runtime/scopes/scope-kinds/paragraph.ts +182 -0
  189. package/src/runtime/scopes/scope-kinds/revision.ts +62 -0
  190. package/src/runtime/scopes/scope-kinds/table-cell.ts +57 -0
  191. package/src/runtime/scopes/scope-kinds/table-row.ts +61 -0
  192. package/src/runtime/scopes/scope-kinds/table.ts +55 -0
  193. package/src/runtime/scopes/scope-range.ts +208 -0
  194. package/src/runtime/scopes/semantic-scope-types.ts +454 -0
  195. package/src/runtime/scopes/workflow-overlap.ts +92 -0
  196. package/src/runtime/selection/index.ts +1 -1
  197. package/src/runtime/structure-ops/fragment-insert.ts +1 -1
  198. package/src/runtime/structure-ops/index.ts +1 -1
  199. package/src/runtime/surface-projection.ts +232 -262
  200. package/src/runtime/units.ts +4 -2
  201. package/src/runtime/workflow/coordinator.ts +1348 -0
  202. package/src/runtime/workflow/derived-scope-resolver.ts +125 -0
  203. package/src/runtime/workflow/index.ts +25 -0
  204. package/src/runtime/workflow/markup-mode-policy.ts +98 -0
  205. package/src/runtime/{workflow-markup.ts → workflow/markup.ts} +6 -6
  206. package/src/runtime/workflow/metadata-persistence.ts +306 -0
  207. package/src/runtime/workflow/metadata-writer.ts +123 -0
  208. package/src/runtime/workflow/overlay-store.ts +690 -0
  209. package/src/runtime/workflow/projector.ts +127 -0
  210. package/src/runtime/{query-scopes.ts → workflow/query-scopes.ts} +3 -3
  211. package/src/runtime/{workflow-rail-segments.ts → workflow/rail/compose.ts} +60 -165
  212. package/src/runtime/workflow/rail/types.ts +198 -0
  213. package/src/runtime/workflow/scope-rail-composer.ts +39 -0
  214. package/src/runtime/{scope-resolver.ts → workflow/scope-resolver.ts} +3 -3
  215. package/src/runtime/workflow/scope-writer.ts +188 -0
  216. package/src/runtime/{tamper-gate.ts → workflow/tamper-gate.ts} +1 -1
  217. package/src/runtime/workflow/visibility-policy.ts +129 -0
  218. package/src/session/_sync-legacy.ts +66 -0
  219. package/src/session/export/embedded-reconstitute.ts +104 -0
  220. package/src/session/export/export-diagnostics.ts +85 -0
  221. package/src/session/export/export-validation.ts +110 -0
  222. package/src/session/export/index.ts +34 -0
  223. package/src/session/export/preservation-reattach.ts +30 -0
  224. package/src/session/export/serialize-dispatch.ts +165 -0
  225. package/src/session/export/stateful-export-pipeline.ts +432 -0
  226. package/src/session/export/stateful-export.ts +684 -0
  227. package/src/session/import/canonical-assembly.ts +227 -0
  228. package/src/session/import/diagnostics-session.ts +54 -0
  229. package/src/session/import/embedded-discovery.ts +225 -0
  230. package/src/session/import/embedded-offload.ts +337 -0
  231. package/src/session/import/import-diagnostics.ts +69 -0
  232. package/src/session/import/loader-types.ts +313 -0
  233. package/src/session/import/loader.ts +1834 -0
  234. package/src/session/import/normalize.ts +195 -0
  235. package/src/session/import/package-parts.ts +217 -0
  236. package/src/session/import/package-read.ts +195 -0
  237. package/src/session/import/parse-orchestration.ts +105 -0
  238. package/src/session/import/part-constants.ts +70 -0
  239. package/src/session/import/part-discovery.ts +94 -0
  240. package/src/session/import/preservation-index.ts +46 -0
  241. package/src/{runtime/read-only-diagnostics-runtime.ts → session/import/read-only-diagnostics.ts} +24 -3
  242. package/src/session/import/review-import.ts +508 -0
  243. package/src/session/import/styles-consolidation.ts +281 -0
  244. package/src/session/import/workflow-scope-import.ts +256 -0
  245. package/src/session/index.ts +37 -0
  246. package/src/session/session-state.ts +69 -0
  247. package/src/session/session.ts +532 -0
  248. package/src/session/shared/protection.ts +228 -0
  249. package/src/session/shared/session-utils.ts +82 -0
  250. package/src/session/types.ts +499 -0
  251. package/src/shell/chart-snapshots.ts +96 -0
  252. package/src/shell/media-previews.ts +85 -0
  253. package/src/shell/overlay-anchor-bridge.ts +53 -0
  254. package/src/shell/paste-adapter.ts +23 -0
  255. package/src/shell/ref-commands.ts +1697 -0
  256. package/src/shell/ref-utilities.ts +48 -0
  257. package/src/shell/search.ts +51 -0
  258. package/src/{ui/editor-runtime-boundary.ts → shell/session-bootstrap.ts} +243 -67
  259. package/src/shell/ui-subscriber-channels.ts +81 -0
  260. package/src/shell/use-collab-sync.ts +116 -0
  261. package/src/ui/WordReviewEditor.tsx +496 -2051
  262. package/src/ui/editor-shell-view.tsx +30 -1
  263. package/src/ui/editor-surface-controller.tsx +49 -1
  264. package/src/ui/headless/revision-decoration-model.ts +83 -0
  265. package/src/{ui-tailwind/chrome → ui/headless}/role-action-sets.ts +1 -1
  266. package/src/ui/headless/scoped-chrome-policy.ts +2 -2
  267. package/src/ui/headless/selection-tool-context.ts +1 -1
  268. package/src/ui/headless/selection-tool-resolver.ts +1 -1
  269. package/src/ui/runtime-shortcut-dispatch.ts +46 -1
  270. package/src/ui/ui-controller-factory.ts +221 -0
  271. package/src/ui-tailwind/chart/ChartSurface.tsx +2 -2
  272. package/src/ui-tailwind/chart/layout/legend-layout.ts +1 -1
  273. package/src/ui-tailwind/chart/layout/plot-area.ts +2 -2
  274. package/src/ui-tailwind/chart/layout/title-layout.ts +1 -1
  275. package/src/ui-tailwind/chart/render/area.tsx +3 -3
  276. package/src/ui-tailwind/chart/render/bar-column.tsx +3 -3
  277. package/src/ui-tailwind/chart/render/bubble.tsx +3 -3
  278. package/src/ui-tailwind/chart/render/combo.tsx +2 -2
  279. package/src/ui-tailwind/chart/render/data-labels.tsx +2 -2
  280. package/src/ui-tailwind/chart/render/font-metrics.ts +2 -2
  281. package/src/ui-tailwind/chart/render/line.tsx +3 -3
  282. package/src/ui-tailwind/chart/render/pie.tsx +6 -6
  283. package/src/ui-tailwind/chart/render/scatter.tsx +3 -3
  284. package/src/ui-tailwind/chart/render/svg-primitives.ts +3 -3
  285. package/src/ui-tailwind/chart/render/unsupported.tsx +2 -2
  286. package/src/ui-tailwind/chrome/build-context-menu-entries.ts +88 -0
  287. package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +1 -1
  288. package/src/ui-tailwind/chrome/collab-send-to-supplier-button.tsx +1 -1
  289. package/src/ui-tailwind/chrome/collab-tamper-banner.tsx +1 -1
  290. package/src/ui-tailwind/chrome/collab-top-nav-container.tsx +1 -1
  291. package/src/ui-tailwind/chrome/editor-action-registry.ts +553 -0
  292. package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +182 -0
  293. package/src/ui-tailwind/chrome/local-surface-arbiter.ts +534 -0
  294. package/src/ui-tailwind/chrome/resolve-target-kind.ts +226 -0
  295. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +38 -4
  296. package/src/ui-tailwind/chrome/tw-context-band.tsx +125 -0
  297. package/src/ui-tailwind/chrome/tw-context-menu-portal.tsx +248 -0
  298. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +42 -1
  299. package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +8 -7
  300. package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +38 -4
  301. package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +104 -6
  302. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +66 -7
  303. package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +54 -8
  304. package/src/ui-tailwind/chrome/tw-shortcut-hint.tsx +7 -1
  305. package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +33 -0
  306. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +78 -1
  307. package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +16 -8
  308. package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +276 -0
  309. package/src/ui-tailwind/chrome/use-context-menu-controller.ts +201 -0
  310. package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +1 -1
  311. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +22 -4
  312. package/src/ui-tailwind/chrome-overlay/tw-comment-balloon-layer.tsx +1 -1
  313. package/src/ui-tailwind/chrome-overlay/tw-locked-block-layer.tsx +1 -1
  314. package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +11 -5
  315. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +197 -3
  316. package/src/ui-tailwind/chrome-overlay/tw-revision-margin-bar-layer.tsx +1 -1
  317. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +35 -6
  318. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +24 -16
  319. package/src/ui-tailwind/chrome-overlay/tw-table-continuation-header.tsx +1 -1
  320. package/src/ui-tailwind/debug/README.md +57 -0
  321. package/src/ui-tailwind/debug/index.ts +3 -0
  322. package/src/ui-tailwind/debug/tw-debug-overlay.tsx +186 -0
  323. package/src/ui-tailwind/debug/tw-debug-presentation.tsx +80 -0
  324. package/src/ui-tailwind/debug/tw-debug-top-bar.tsx +83 -0
  325. package/src/ui-tailwind/editor-surface/chart-node-view.tsx +2 -2
  326. package/src/ui-tailwind/editor-surface/float-wrap-resolver.ts +1 -1
  327. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +135 -10
  328. package/src/ui-tailwind/editor-surface/pm-decorations.ts +40 -13
  329. package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +1 -1
  330. package/src/ui-tailwind/editor-surface/pm-schema.ts +1 -1
  331. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +3 -3
  332. package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +1 -1
  333. package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +2 -2
  334. package/src/ui-tailwind/editor-surface/scroll-anchor.ts +91 -9
  335. package/src/ui-tailwind/editor-surface/shape-renderer.ts +1 -1
  336. package/src/ui-tailwind/editor-surface/surface-layer.ts +1 -1
  337. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +1 -1
  338. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +23 -6
  339. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +132 -22
  340. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +1 -1
  341. package/src/ui-tailwind/index.ts +0 -5
  342. package/src/ui-tailwind/overlay-anchor-bridge-context.tsx +33 -0
  343. package/src/ui-tailwind/page-stack/floating-image-overlay-model.ts +66 -29
  344. package/src/ui-tailwind/page-stack/tw-floating-image-layer.tsx +25 -2
  345. package/src/ui-tailwind/review/comment-markdown-renderer.tsx +15 -0
  346. package/src/ui-tailwind/review/tw-review-rail.tsx +92 -4
  347. package/src/ui-tailwind/review/tw-workflow-tab.tsx +1 -1
  348. package/src/ui-tailwind/review-workspace/page-chrome.ts +210 -0
  349. package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +101 -0
  350. package/src/ui-tailwind/review-workspace/paragraph-layout.ts +115 -0
  351. package/src/ui-tailwind/review-workspace/selection-toolbar-placement.ts +97 -0
  352. package/src/ui-tailwind/review-workspace/tw-review-workspace-navigator.tsx +130 -0
  353. package/src/ui-tailwind/review-workspace/tw-review-workspace-page-toolbar.tsx +240 -0
  354. package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +59 -0
  355. package/src/ui-tailwind/review-workspace/types.ts +408 -0
  356. package/src/ui-tailwind/review-workspace/use-chrome-policy.ts +104 -0
  357. package/src/ui-tailwind/review-workspace/use-derived-view-state.ts +151 -0
  358. package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +70 -0
  359. package/src/ui-tailwind/review-workspace/use-grabbed-segment-offsets.ts +40 -0
  360. package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +55 -0
  361. package/src/ui-tailwind/review-workspace/use-page-markers.ts +130 -0
  362. package/src/ui-tailwind/review-workspace/use-pm-surface-capture.ts +60 -0
  363. package/src/ui-tailwind/review-workspace/use-review-rail-state.ts +63 -0
  364. package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +170 -0
  365. package/src/ui-tailwind/review-workspace/use-scroll-root-capture.ts +28 -0
  366. package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +113 -0
  367. package/src/ui-tailwind/review-workspace/use-shell-selection-anchor-bridge.ts +120 -0
  368. package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +55 -0
  369. package/src/ui-tailwind/review-workspace/use-viewport-dimensions.ts +43 -0
  370. package/src/ui-tailwind/review-workspace/use-workspace-arbiter.ts +25 -0
  371. package/src/ui-tailwind/review-workspace/use-workspace-composition.ts +86 -0
  372. package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +150 -0
  373. package/src/ui-tailwind/theme/editor-theme.css +25 -0
  374. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +2 -2
  375. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +61 -98
  376. package/src/ui-tailwind/tw-review-workspace.tsx +521 -1802
  377. package/src/ui-tailwind/ui-api-context.tsx +43 -0
  378. package/src/ui-tailwind/ui-shell-channels-context.tsx +49 -0
  379. package/src/validation/compatibility-engine.ts +6 -6
  380. package/src/runtime/styles-cascade.ts +0 -33
  381. package/src/ui-tailwind/chrome/tw-mode-dock.tsx +0 -85
  382. /package/src/runtime/{page-number-format.ts → formatting/field/page-number-format.ts} +0 -0
  383. /package/src/runtime/{ai-action-policy.ts → workflow/ai-action-policy.ts} +0 -0
  384. /package/src/runtime/{scope-tag-registry.ts → workflow/scope-tag-registry.ts} +0 -0
@@ -27,6 +27,7 @@ import type {
27
27
  TableStructureContextSnapshot,
28
28
  WordReviewEditorLayoutFacet,
29
29
  } from "../../api/public-types";
30
+ import type { GeometryFacet } from "../../api/public-types";
30
31
  import type { OverlayCoordinateSpace } from "../chrome-overlay/chrome-overlay-projector";
31
32
  import { projectRectToOverlay } from "../chrome-overlay/chrome-overlay-projector";
32
33
  import { DEFAULT_PX_PER_TWIP } from "../../runtime/render/render-frame-types";
@@ -39,7 +40,19 @@ const MIN_COLUMN_TWIPS = 720;
39
40
  const MIN_ROW_TWIPS = 120;
40
41
 
41
42
  export interface TwTableGripLayerProps {
43
+ /**
44
+ * Layout facet — still used for layout-semantic reads
45
+ * (`getFirstPageIndexForBlock`, `getTableRenderPlan`). These stay
46
+ * on the layout facet per architecture-04 (twip-valued,
47
+ * page-graph-derived data, not pixel geometry).
48
+ */
42
49
  facet: WordReviewEditorLayoutFacet;
50
+ /**
51
+ * Geometry facet — used for render-frame + zoom access. Migrated
52
+ * from `facet.getRenderFrame` / `facet.getRenderZoom` in the
53
+ * refactor/05 cross-lane-coord §8.4 pass.
54
+ */
55
+ geometryFacet: GeometryFacet;
43
56
  tableContext: TableStructureContextSnapshot | null;
44
57
  space?: OverlayCoordinateSpace;
45
58
  disabled?: boolean;
@@ -53,6 +66,7 @@ export interface TwTableGripLayerProps {
53
66
 
54
67
  export function TwTableGripLayer({
55
68
  facet,
69
+ geometryFacet,
56
70
  tableContext,
57
71
  space,
58
72
  disabled,
@@ -61,10 +75,7 @@ export function TwTableGripLayer({
61
75
  }: TwTableGripLayerProps) {
62
76
  if (!tableContext) return null;
63
77
 
64
- const frame =
65
- typeof facet.getRenderFrame === "function"
66
- ? (facet.getRenderFrame() ?? null)
67
- : null;
78
+ const frame = geometryFacet.getRenderFrame() ?? null;
68
79
  if (!frame) return null;
69
80
 
70
81
  const blockId = `table-${tableContext.tableBlockIndex}`;
@@ -72,10 +83,7 @@ export function TwTableGripLayer({
72
83
  const plan = facet.getTableRenderPlan(blockId, pageIndex);
73
84
  if (!plan) return null;
74
85
 
75
- const pxPerTwip =
76
- typeof facet.getRenderZoom === "function"
77
- ? (facet.getRenderZoom()?.pxPerTwip ?? DEFAULT_PX_PER_TWIP)
78
- : DEFAULT_PX_PER_TWIP;
86
+ const pxPerTwip = geometryFacet.getRenderZoom()?.pxPerTwip ?? DEFAULT_PX_PER_TWIP;
79
87
 
80
88
  return (
81
89
  <>
@@ -0,0 +1,276 @@
1
+ /**
2
+ * TwWorkspaceChromeHost — composition-level mount that wires together:
3
+ * - the right-click context menu (portal-based, viewport-clamped)
4
+ * - the global command palette (Ctrl/Cmd+K)
5
+ * - the shared editor-action registry
6
+ *
7
+ * Per DESIGN-EDITOR.md §6.4 the three access routes (right-click,
8
+ * inline "More…" buttons shipped in Phase D, and command palette)
9
+ * all read from the same registry so they cannot diverge. This
10
+ * component is the single mount point that routes all three.
11
+ *
12
+ * Progressive-disclosure discipline (designsystem.md §2.1 principle 4):
13
+ * - Context menu opens only on user right-click and closes on
14
+ * Escape, outside-click, or row selection.
15
+ * - Menu is suppressed when no wired callback would produce at
16
+ * least one entry — no empty menus.
17
+ * - Command palette opens only on Ctrl/Cmd+K and fuzzy-matches
18
+ * across the same registry.
19
+ * - All surfaces dismiss themselves (run → dismiss) so focus
20
+ * returns to the editor immediately after an action fires.
21
+ *
22
+ * Host-side contract (TwReviewWorkspaceProps will thread this in):
23
+ * - `editorActionHost`: callback bag wired from ref methods
24
+ * (WordReviewEditorRef) that the shared registry dispatches to.
25
+ * Actions without a wired callback are hidden from every access
26
+ * route.
27
+ * - `mode`: current ChromeComposition.mode — drives mode-filtered
28
+ * actions (e.g. scope-only actions hide in edit mode).
29
+ * - `onContextMenuRequest` is the handler hosts pass into
30
+ * `tw-prosemirror-surface`'s command-bridge callback.
31
+ *
32
+ * Perf invariants honored:
33
+ * - No DOM reads on the edit path (invariant 7).
34
+ * - No PM-transaction subscriptions.
35
+ * - Portal mounts only while open (invariant 4).
36
+ */
37
+
38
+ import React, { useImperativeHandle, useMemo, useRef } from "react";
39
+
40
+ import type {
41
+ EditorChromeMode,
42
+ EditorRailTab,
43
+ } from "../../api/v3/ui/chrome-composition";
44
+ import type {
45
+ EditorActionHostCallbacks,
46
+ TargetKind,
47
+ } from "./editor-action-registry";
48
+ import { buildPaletteGroupsFromRegistry } from "./editor-actions-to-palette";
49
+ import { TwCommandPaletteMount } from "./tw-command-palette-mount";
50
+ import { TwContextMenuPortal } from "./tw-context-menu-portal";
51
+ import {
52
+ useContextMenuController,
53
+ type ContextMenuRequest,
54
+ } from "./use-context-menu-controller";
55
+
56
+ /**
57
+ * Build a stable host-callbacks proxy that always reads from the
58
+ * supplied ref. Pattern mirrors `callbacksRef` in
59
+ * `src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx:370–408`:
60
+ * the owning component sync-writes `hostRef.current = freshProps` at
61
+ * the top of every render, and consumers read through this proxy so
62
+ * they see the latest callbacks without the proxy identity ever
63
+ * changing. That stability lets the palette + registry + controller
64
+ * memo their outputs on (mode, proxy) — (mode) in practice, since the
65
+ * proxy is static per component lifetime.
66
+ */
67
+ function createStableHostProxy(
68
+ hostRef: { current: EditorActionHostCallbacks },
69
+ ): EditorActionHostCallbacks {
70
+ // Proxy construction is ~free and happens exactly once per component
71
+ // lifetime (see the empty-dep useMemo call site below). The `get`
72
+ // trap forwards to whichever callback the host currently has.
73
+ return new Proxy({} as EditorActionHostCallbacks, {
74
+ get: (_target, prop) => {
75
+ // Only read declared keys — filter `Symbol.toPrimitive` etc. so
76
+ // `typeof host[key] === "function"` checks elsewhere behave
77
+ // predictably.
78
+ if (typeof prop !== "string") return undefined;
79
+ return (hostRef.current as Record<string, unknown>)[prop];
80
+ },
81
+ has: (_target, prop) => {
82
+ if (typeof prop !== "string") return false;
83
+ return prop in (hostRef.current as Record<string, unknown>);
84
+ },
85
+ // `ownKeys` + `getOwnPropertyDescriptor` left default — iteration
86
+ // over the proxy isn't part of the registry contract.
87
+ });
88
+ }
89
+
90
+ export interface TwWorkspaceChromeHostProps {
91
+ /** Current composition mode; filters mode-scoped actions. */
92
+ mode: EditorChromeMode;
93
+ /**
94
+ * Callback bag for shared editor actions. Actions whose callback is
95
+ * absent are hidden from every surface (progressive disclosure).
96
+ */
97
+ editorActionHost: EditorActionHostCallbacks;
98
+ /**
99
+ * Optional scope root for target-kind resolution. Traversal stops
100
+ * here so right-click on a scope card doesn't walk into the shell.
101
+ */
102
+ surfaceRoot?: Element | null;
103
+ /**
104
+ * Suppress the global Ctrl+K listener (e.g. while a higher-priority
105
+ * modal captures keyboard focus).
106
+ */
107
+ paletteDisabled?: boolean;
108
+ /**
109
+ * When defined, the host routes keyboard Shift+F10 + "More…" button
110
+ * invocations here. Typically just the workspace's `contextMenuRef`
111
+ * — wiring below.
112
+ */
113
+ controllerRef?: React.Ref<TwWorkspaceChromeHostController>;
114
+ /**
115
+ * Element to return focus to when the menu/palette closes. Usually
116
+ * the PM editor surface so typing resumes where it left off.
117
+ */
118
+ returnFocusTo?: HTMLElement | null;
119
+ /**
120
+ * Host-supplied callback invoked when another chrome surface asks the
121
+ * rail to pivot to a specific tab — Phase E uses this for the signal-
122
+ * only diagnostics button (opens `"health"`) and the alert-banner's
123
+ * "Show detail" handoff. Hosts typically implement it by opening the
124
+ * rail drawer and forwarding `tab` to the rail's active-tab change
125
+ * handler. Omitting the prop turns `controller.openRailTab` into a
126
+ * no-op so hosts that haven't opted in see no behavior change.
127
+ */
128
+ onOpenRailTab?: (tab: EditorRailTab, itemId?: string) => void;
129
+ /**
130
+ * Optional live table-structure context (Chrome Closure Pass · Task 3).
131
+ * When supplied and the right-click target lands inside a table cell,
132
+ * the menu controller adds tier-specific kinds (`"table-row"` /
133
+ * `"table-column"` / `"table-whole"`) so the menu entries match the
134
+ * §6.9 tier ladder. Pass null when no table is active or the snapshot
135
+ * is unavailable.
136
+ */
137
+ tableContext?: import("../../api/public-types").TableStructureContextSnapshot | null;
138
+ }
139
+
140
+ /**
141
+ * Imperative surface exposed via `controllerRef` so the workspace can:
142
+ * - forward contextmenu events from the pm-command-bridge into the
143
+ * menu controller
144
+ * - open the menu at the current caret (Shift+F10 from the shortcut
145
+ * dispatcher)
146
+ * - open the menu with pre-resolved target kinds ("More…" buttons
147
+ * on demoted floating toolbars — Phase D)
148
+ */
149
+ export interface TwWorkspaceChromeHostController {
150
+ handleContextMenuRequest: (request: ContextMenuRequest) => void;
151
+ openWithKinds: (args: {
152
+ clientX: number;
153
+ clientY: number;
154
+ kinds: readonly TargetKind[];
155
+ }) => void;
156
+ dismissContextMenu: () => void;
157
+ /**
158
+ * Ask the host to pivot the management rail to `tab`. The host
159
+ * implementation (typically `TwReviewWorkspace`) opens the rail drawer
160
+ * and calls `onActiveRailTabChange(tab)`. No-op when the host didn't
161
+ * supply `onOpenRailTab`.
162
+ */
163
+ openRailTab: (tab: EditorRailTab, itemId?: string) => void;
164
+ }
165
+
166
+ export function TwWorkspaceChromeHost(
167
+ props: TwWorkspaceChromeHostProps,
168
+ ): React.JSX.Element {
169
+ const {
170
+ mode,
171
+ editorActionHost,
172
+ surfaceRoot,
173
+ paletteDisabled,
174
+ controllerRef,
175
+ returnFocusTo,
176
+ onOpenRailTab,
177
+ } = props;
178
+
179
+ // B1 fix — mirror the `callbacksRef` pattern in tw-prosemirror-surface
180
+ // so the host-callbacks proxy passed into the controller and the
181
+ // palette adapter stays referentially stable across renders, even
182
+ // when the integrator rebuilds `editorActionHost` inline on every
183
+ // parent render. Perf invariant 4: palette memo deps collapse to
184
+ // `[mode]` so wholesale-snapshot re-renders don't rebuild groups.
185
+ const hostRef = useRef<EditorActionHostCallbacks>(editorActionHost);
186
+ hostRef.current = editorActionHost;
187
+ const stableHost = useMemo(() => createStableHostProxy(hostRef), []);
188
+
189
+ const controller = useContextMenuController({
190
+ mode,
191
+ host: stableHost,
192
+ root: surfaceRoot ?? null,
193
+ ...(props.tableContext !== undefined ? { tableContext: props.tableContext } : {}),
194
+ });
195
+
196
+ // N2 fix — imperative handle identity must be stable across the
197
+ // component lifetime so downstream effects that read controllerRef
198
+ // don't self-trigger on every menu state change. Ref-wrap each
199
+ // method, then bind to empty deps.
200
+ const requestRef = useRef(controller.request);
201
+ requestRef.current = controller.request;
202
+ const requestWithKindsRef = useRef(controller.requestWithKinds);
203
+ requestWithKindsRef.current = controller.requestWithKinds;
204
+ const dismissRef = useRef(controller.dismiss);
205
+ dismissRef.current = controller.dismiss;
206
+ // Phase E.0 — ref-sync the rail-tab callback so the imperative handle
207
+ // identity stays stable while still dispatching to the freshest host
208
+ // closure on every call. `undefined` is kept in the ref when the host
209
+ // omits the prop so `openRailTab` becomes a safe no-op.
210
+ const openRailTabRef = useRef<
211
+ ((tab: EditorRailTab, itemId?: string) => void) | undefined
212
+ >(onOpenRailTab);
213
+ openRailTabRef.current = onOpenRailTab;
214
+
215
+ useImperativeHandle(
216
+ controllerRef,
217
+ () => ({
218
+ handleContextMenuRequest: (req) => requestRef.current(req),
219
+ openWithKinds: (args) => requestWithKindsRef.current(args),
220
+ dismissContextMenu: () => dismissRef.current(),
221
+ openRailTab: (tab, itemId) => {
222
+ openRailTabRef.current?.(tab, itemId);
223
+ },
224
+ }),
225
+ [],
226
+ );
227
+
228
+ // Palette groups rebuild only on mode change — host-callback identity
229
+ // is pinned via `stableHost`. `dismiss` passed into the registry is
230
+ // a no-op here: TwCommandPalette closes itself via `onOpenChange`.
231
+ const paletteGroups = useMemo(
232
+ () =>
233
+ buildPaletteGroupsFromRegistry({
234
+ mode,
235
+ host: stableHost,
236
+ dismiss: noopDismiss,
237
+ }),
238
+ [mode, stableHost],
239
+ );
240
+
241
+ // When the palette would be empty (no wired callbacks), we still
242
+ // mount the adapter so the Ctrl+K listener is consistent, but pass
243
+ // an empty-state message so users see "nothing here" rather than a
244
+ // naked input.
245
+ const emptyMessage =
246
+ paletteGroups.length === 0
247
+ ? "No commands are available in this posture."
248
+ : undefined;
249
+
250
+ return (
251
+ <>
252
+ <TwContextMenuPortal
253
+ open={controller.state.open}
254
+ clientX={controller.state.clientX}
255
+ clientY={controller.state.clientY}
256
+ entries={controller.state.entries}
257
+ onDismiss={controller.dismiss}
258
+ {...(returnFocusTo !== undefined ? { returnFocusTo } : {})}
259
+ />
260
+ <TwCommandPaletteMount
261
+ groups={paletteGroups}
262
+ {...(paletteDisabled !== undefined ? { disabled: paletteDisabled } : {})}
263
+ {...(emptyMessage !== undefined ? { emptyMessage } : {})}
264
+ />
265
+ </>
266
+ );
267
+ }
268
+
269
+ // Module-scope constant so the dismiss reference fed into the palette
270
+ // adapter is stable across the component's lifetime — contributes to
271
+ // the useMemo([mode, stableHost]) cache stability. The palette's
272
+ // TwCommandPaletteMount owns its open state, so command invocations
273
+ // dismiss via the palette's own onOpenChange; this is just a hook.
274
+ function noopDismiss(): void {
275
+ // intentionally empty
276
+ }
@@ -0,0 +1,201 @@
1
+ /**
2
+ * useContextMenuController — React hook that owns workspace-level
3
+ * context-menu state. Consumers (TwReviewWorkspace, Phase C.γ.3) wire
4
+ * the returned `request(event)` into their `onContextMenuRequested`
5
+ * callback, then render `<TwContextMenuPortal {...state} />`.
6
+ *
7
+ * Progressive-disclosure discipline (designsystem.md §2.1 principle 4):
8
+ * - No work happens until the user right-clicks. Target resolution,
9
+ * entry building, and portal mount all run ONLY inside `request()`.
10
+ * - The edit path is untouched (invariant 7).
11
+ *
12
+ * State shape is intentionally minimal — the hook doesn't own
13
+ * composition, host callbacks, or target-kind classification; those
14
+ * are passed in per request so the same hook works for right-click,
15
+ * "More…" buttons (Phase D), and keyboard Shift+F10 (Phase C.γ.5).
16
+ */
17
+
18
+ import { useCallback, useState } from "react";
19
+
20
+ import type { TableStructureContextSnapshot } from "../../api/public-types";
21
+ import type { EditorChromeMode } from "../../api/v3/ui/chrome-composition";
22
+ import {
23
+ buildContextMenuEntries,
24
+ type BuildContextMenuInput,
25
+ } from "./build-context-menu-entries";
26
+ import type {
27
+ EditorActionHostCallbacks,
28
+ TargetKind,
29
+ } from "./editor-action-registry";
30
+ import { resolveTargetKind } from "./resolve-target-kind";
31
+ import type { ContextMenuEntry } from "./tw-context-menu";
32
+ import { resolveTableTier } from "./tw-table-context-toolbar";
33
+
34
+ /**
35
+ * Project a `TableTier` to additional `TargetKind`s the menu should
36
+ * include. Mirrors the §6.9 tier ladder: a row-selected context (T3)
37
+ * surfaces both `"table-row"` and `"table-cell"` actions; a whole-
38
+ * table context (T5) surfaces every tier. Used by the menu controller
39
+ * when the host supplies a live table structure context — Chrome
40
+ * Closure Pass · Task 3.
41
+ */
42
+ function tableContextToKinds(
43
+ ctx: TableStructureContextSnapshot | null,
44
+ ): readonly TargetKind[] {
45
+ if (!ctx) return [];
46
+ const tier = resolveTableTier(ctx);
47
+ switch (tier) {
48
+ case "caret-in-cell":
49
+ return ["table-cell"];
50
+ case "multi-cell":
51
+ return ["table-cell"];
52
+ case "row-selected":
53
+ return ["table-cell", "table-row"];
54
+ case "column-selected":
55
+ return ["table-cell", "table-column"];
56
+ case "whole-table":
57
+ return ["table-cell", "table-row", "table-column", "table-whole"];
58
+ }
59
+ }
60
+
61
+ export interface ContextMenuRequest {
62
+ readonly clientX: number;
63
+ readonly clientY: number;
64
+ readonly target: EventTarget | null;
65
+ }
66
+
67
+ export interface ContextMenuState {
68
+ readonly open: boolean;
69
+ readonly clientX: number;
70
+ readonly clientY: number;
71
+ readonly entries: ContextMenuEntry[];
72
+ }
73
+
74
+ export interface UseContextMenuControllerOptions {
75
+ readonly mode: EditorChromeMode;
76
+ readonly host: EditorActionHostCallbacks;
77
+ /**
78
+ * Optional root passed through to `resolveTargetKind` so traversal
79
+ * stops at the editor surface instead of walking into the shell
80
+ * header or rail.
81
+ */
82
+ readonly root?: Element | null;
83
+ /**
84
+ * Advanced: override the builder (for "More…" buttons that know
85
+ * their target kind a priori and don't need DOM resolution).
86
+ */
87
+ readonly resolveKinds?: (target: EventTarget | null) => readonly TargetKind[];
88
+ /**
89
+ * Optional live table structure context — when present and the
90
+ * right-click target resolves to `"table-cell"`, the controller
91
+ * adds tier-specific kinds (`"table-row"` / `"table-column"` /
92
+ * `"table-whole"`) so the menu entries match the §6.9 tier ladder
93
+ * (Chrome Closure Pass · Task 3). Pass null when no table is
94
+ * active or you do not have access to the snapshot.
95
+ */
96
+ readonly tableContext?: TableStructureContextSnapshot | null;
97
+ }
98
+
99
+ export interface ContextMenuController {
100
+ readonly state: ContextMenuState;
101
+ /** Call from the `onContextMenuRequested` bridge callback. */
102
+ request: (request: ContextMenuRequest) => void;
103
+ /** Open with pre-resolved kinds (Phase D "More…" buttons). */
104
+ requestWithKinds: (args: {
105
+ clientX: number;
106
+ clientY: number;
107
+ kinds: readonly TargetKind[];
108
+ }) => void;
109
+ dismiss: () => void;
110
+ }
111
+
112
+ const EMPTY_STATE: ContextMenuState = {
113
+ open: false,
114
+ clientX: 0,
115
+ clientY: 0,
116
+ entries: [],
117
+ };
118
+
119
+ export function useContextMenuController(
120
+ options: UseContextMenuControllerOptions,
121
+ ): ContextMenuController {
122
+ const { mode, host, root, resolveKinds, tableContext } = options;
123
+ const [state, setState] = useState<ContextMenuState>(EMPTY_STATE);
124
+
125
+ const dismiss = useCallback(() => {
126
+ setState(EMPTY_STATE);
127
+ }, []);
128
+
129
+ const buildEntries = useCallback(
130
+ (kinds: readonly TargetKind[]): ContextMenuEntry[] => {
131
+ const input: BuildContextMenuInput = {
132
+ targetKinds: kinds,
133
+ mode,
134
+ host,
135
+ dismiss,
136
+ };
137
+ return buildContextMenuEntries(input);
138
+ },
139
+ [mode, host, dismiss],
140
+ );
141
+
142
+ const request = useCallback(
143
+ (req: ContextMenuRequest) => {
144
+ const baseKinds = resolveKinds
145
+ ? resolveKinds(req.target)
146
+ : resolveTargetKind(req.target, { root: root ?? undefined });
147
+ // Chrome Closure Pass · Task 3 — augment with tier-specific
148
+ // kinds when a live table context is supplied and the target
149
+ // is in a table cell. Cell-level ops carry every table-* kind
150
+ // (so they are still visible) and tier-only ops (delete row /
151
+ // delete column) gate on the matching kind.
152
+ const tierKinds =
153
+ baseKinds.includes("table-cell") && tableContext
154
+ ? tableContextToKinds(tableContext)
155
+ : [];
156
+ const merged = [...baseKinds];
157
+ for (const k of tierKinds) {
158
+ if (!merged.includes(k)) merged.push(k);
159
+ }
160
+ const entries = buildEntries(merged);
161
+ // Progressive disclosure: if no actions resolve (every host callback
162
+ // absent + no scope-anchor + etc), don't open an empty menu. Let
163
+ // the browser fall through instead.
164
+ if (entries.length === 0) {
165
+ return;
166
+ }
167
+ setState({
168
+ open: true,
169
+ clientX: req.clientX,
170
+ clientY: req.clientY,
171
+ entries,
172
+ });
173
+ },
174
+ [buildEntries, resolveKinds, root, tableContext],
175
+ );
176
+
177
+ const requestWithKinds = useCallback(
178
+ (args: {
179
+ clientX: number;
180
+ clientY: number;
181
+ kinds: readonly TargetKind[];
182
+ }) => {
183
+ const entries = buildEntries(args.kinds);
184
+ if (entries.length === 0) return;
185
+ setState({
186
+ open: true,
187
+ clientX: args.clientX,
188
+ clientY: args.clientY,
189
+ entries,
190
+ });
191
+ },
192
+ [buildEntries],
193
+ );
194
+
195
+ // D.5.Nit1 — the return value is consumed directly by the caller
196
+ // (the component that called this hook). It isn't handed to any
197
+ // downstream memoizer, so `useMemo` here just pre-allocates an
198
+ // object that React's reconciler would allocate anyway. Plain
199
+ // object literal is clearer + one less hook slot to track.
200
+ return { state, request, requestWithKinds, dismiss };
201
+ }
@@ -8,7 +8,7 @@
8
8
  * rect math can be unit-tested and reused.
9
9
  */
10
10
 
11
- import type { RenderFrameRect } from "../../runtime/render/index.ts";
11
+ import type { RenderFrameRect } from "../../api/public-types.ts";
12
12
  import { recordPerfSample } from "../editor-surface/perf-probe.ts";
13
13
 
14
14
  export interface OverlayCoordinateSpace {
@@ -14,7 +14,7 @@
14
14
 
15
15
  import * as React from "react";
16
16
  import type { OverlayCoordinateSpace } from "./chrome-overlay-projector";
17
- import type { ScopeRailSegment } from "../../runtime/layout";
17
+ import type { ScopeRailSegment } from "../../api/public-types.ts";
18
18
  import type {
19
19
  EditorRole,
20
20
  EditorStoryTarget,
@@ -30,8 +30,21 @@ import { TwTableGripLayer } from "../chrome/tw-table-grip-layer";
30
30
  import { TwObjectSelectionOverlay } from "./tw-object-selection-overlay";
31
31
 
32
32
  export interface TwChromeOverlayProps {
33
- /** Layout facet the overlay layers read from. */
33
+ /** Layout facet the overlay layers read from (layout-semantic data). */
34
34
  facet: WordReviewEditorLayoutFacet;
35
+ /**
36
+ * Geometry facet for render-frame + zoom access. Migrated from
37
+ * `facet.getRenderFrame` / `.getRenderZoom` in refactor/05
38
+ * cross-lane-coord §8.4.
39
+ */
40
+ geometryFacet: import("../../runtime/geometry/index.ts").GeometryFacet;
41
+ /**
42
+ * Workflow facet — Layer-06 canonical source of scope rail segments
43
+ * + scope card models (`runtime.workflow`). Required for scope-rail
44
+ * / scope-card rendering; pass `null` when no runtime is attached
45
+ * (e.g., during initial mount).
46
+ */
47
+ workflowFacet: import("../../runtime/workflow/rail/types.ts").WorkflowFacet | null;
35
48
  /** Optional coordinate space override. Defaults to the overlay origin. */
36
49
  space?: OverlayCoordinateSpace;
37
50
  /** Active scope id (for emphasis + rail tab sync). */
@@ -182,7 +195,9 @@ export interface TwChromeOverlayProps {
182
195
  * pointer events on their interactive elements (buttons, handles).
183
196
  */
184
197
  export const TwChromeOverlay: React.FC<TwChromeOverlayProps> = ({
198
+ workflowFacet,
185
199
  facet,
200
+ geometryFacet,
186
201
  space,
187
202
  activeScopeId,
188
203
  onScopeStripeClick,
@@ -231,7 +246,8 @@ export const TwChromeOverlay: React.FC<TwChromeOverlayProps> = ({
231
246
  />
232
247
  ) : null}
233
248
  <TwScopeRailLayer
234
- facet={facet}
249
+ geometryFacet={geometryFacet}
250
+ workflowFacet={workflowFacet}
235
251
  space={space}
236
252
  activeScopeId={activeScopeId}
237
253
  onStripeClick={onScopeStripeClick}
@@ -239,6 +255,7 @@ export const TwChromeOverlay: React.FC<TwChromeOverlayProps> = ({
239
255
  />
240
256
  <TwScopeCardLayer
241
257
  facet={facet}
258
+ workflowFacet={workflowFacet}
242
259
  activeScopeId={activeScopeId ?? null}
243
260
  onClose={onScopeCardClose ?? noop}
244
261
  onModeChange={onScopeCardModeChange ?? noopModeChange}
@@ -252,6 +269,7 @@ export const TwChromeOverlay: React.FC<TwChromeOverlayProps> = ({
252
269
  />
253
270
  <TwTableGripLayer
254
271
  facet={facet}
272
+ geometryFacet={geometryFacet}
255
273
  tableContext={tableContext ?? null}
256
274
  space={space}
257
275
  onSetColumnWidth={onSetColumnWidth}
@@ -261,7 +279,7 @@ export const TwChromeOverlay: React.FC<TwChromeOverlayProps> = ({
261
279
  grabbedObjectId={grabbedObjectId ?? null}
262
280
  grabbedObjectFromOffset={grabbedObjectFromOffset ?? null}
263
281
  grabbedObjectToOffset={grabbedObjectToOffset ?? null}
264
- facet={facet}
282
+ geometryFacet={geometryFacet}
265
283
  space={space}
266
284
  onDeselect={onDeselectObject}
267
285
  />
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import type { RenderBlockDecoration } from "../../runtime/render/render-frame-types";
2
+ import type { RenderBlockDecoration } from "../../api/public-types.ts";
3
3
  import { TwCommentPreview } from "../chrome/tw-comment-preview";
4
4
 
5
5
  const WIDE_BREAKPOINT_PX = 1024;
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { Lock } from "lucide-react";
3
- import type { RenderBlockDecoration } from "../../runtime/render/render-frame-types";
3
+ import type { RenderBlockDecoration } from "../../api/public-types.ts";
4
4
 
5
5
  const OUTLINE_INSET_PX = 2;
6
6
  const BADGE_SIZE_PX = 20;
@@ -20,7 +20,7 @@
20
20
 
21
21
  import * as React from "react";
22
22
  import { useEffect, useRef } from "react";
23
- import type { WordReviewEditorLayoutFacet } from "../../api/public-types";
23
+ import type { GeometryFacet } from "../../api/public-types";
24
24
  import type { OverlayCoordinateSpace } from "./chrome-overlay-projector";
25
25
  import { projectRectToOverlay } from "./chrome-overlay-projector";
26
26
 
@@ -45,8 +45,14 @@ export interface TwObjectSelectionOverlayProps {
45
45
  grabbedObjectFromOffset: number | null;
46
46
  /** Document offset (`to`) of the grabbed object's inline segment. Null when no object grabbed. */
47
47
  grabbedObjectToOffset: number | null;
48
- /** Layout facet for render-frame + anchor-index access. */
49
- facet: WordReviewEditorLayoutFacet;
48
+ /**
49
+ * Geometry facet for render-frame + anchor-index access. Migrated
50
+ * from `facet: WordReviewEditorLayoutFacet` in the refactor/05
51
+ * cross-lane-coord §8.4 pass — layout-facet's `getRenderFrame` /
52
+ * `getRenderZoom` are being deleted; consumers read through
53
+ * `runtime.geometry` now.
54
+ */
55
+ geometryFacet: GeometryFacet;
50
56
  /** Optional overlay coordinate-space override. */
51
57
  space?: OverlayCoordinateSpace;
52
58
  /** Called when the user clicks outside the selection box. */
@@ -57,7 +63,7 @@ export function TwObjectSelectionOverlay({
57
63
  grabbedObjectId,
58
64
  grabbedObjectFromOffset,
59
65
  grabbedObjectToOffset,
60
- facet,
66
+ geometryFacet,
61
67
  space,
62
68
  onDeselect,
63
69
  }: TwObjectSelectionOverlayProps) {
@@ -77,7 +83,7 @@ export function TwObjectSelectionOverlay({
77
83
 
78
84
  if (!grabbedObjectId || grabbedObjectFromOffset == null) return null;
79
85
 
80
- const frame = typeof facet.getRenderFrame === "function" ? facet.getRenderFrame() : null;
86
+ const frame = geometryFacet.getRenderFrame();
81
87
  if (!frame) return null;
82
88
 
83
89
  const rawRect = grabbedObjectToOffset != null