@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
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Phase Q — Debug overlay.
3
+ *
4
+ * Full-screen tabbed panel shown when
5
+ * `WordReviewEditorProps.debugMode === "full"`. Renders the top bar
6
+ * (reused from `tw-debug-top-bar.tsx`) + three content panes:
7
+ *
8
+ * - **Inspector** — pretty-prints the bound `DebugInspectorSnapshot`
9
+ * (canonical / layout / geometry / scope / review / diagnostics
10
+ * JSON, same shape the Railway debug service renders).
11
+ * - **Events** — tail of the last N events on the `api`, `render`,
12
+ * `command`, `commit`, `collab`, `layout` channels the runtime
13
+ * projector ships (`DebugBus`). Populated on arrival via the
14
+ * `events` prop.
15
+ * - **REPL** — stub in this slice. Operators type into the textarea
16
+ * and the host-provided `onReplEval` receives the source. Full
17
+ * in-scope `api` / `runtime` / `debug` handles land in the Phase Q
18
+ * follow-up alongside localStorage history.
19
+ *
20
+ * Slice 7 ships the visible scaffolding + the visibility invariant;
21
+ * the REPL evaluator + the deeper event-tail filters are follow-up
22
+ * work tracked under the refactor/11 plan.
23
+ */
24
+
25
+ import React, { useState } from "react";
26
+
27
+ import { TwDebugTopBar, type TwDebugTopBarProps } from "./tw-debug-top-bar.tsx";
28
+
29
+ type DebugOverlayTab = "inspector" | "events" | "repl";
30
+
31
+ export interface TwDebugOverlayProps {
32
+ sessionId: string | null;
33
+ snapshot: TwDebugTopBarProps["snapshot"];
34
+ /**
35
+ * Raw `DebugInspectorSnapshot` the host wired through
36
+ * `ui.debug.attach()`. Rendered as pretty-printed JSON in the
37
+ * Inspector pane. Large snapshots are clipped to the first 16 KB
38
+ * to keep the DOM responsive.
39
+ */
40
+ inspectorJson?: unknown;
41
+ /**
42
+ * Ring-buffered events flowing through the runtime's `DebugBus`.
43
+ * Each entry is rendered as `channel · kind · t` with a collapsible
44
+ * details row for the payload.
45
+ */
46
+ events?: ReadonlyArray<{
47
+ readonly channel: string;
48
+ readonly kind: string;
49
+ readonly t: number;
50
+ readonly payload?: unknown;
51
+ }>;
52
+ /** Invoked when the operator submits text from the REPL pane. */
53
+ onReplEval?: (source: string) => void;
54
+ /** Collapses the overlay back to `"top-bar"` mode. */
55
+ onCollapse?: () => void;
56
+ }
57
+
58
+ const MAX_INSPECTOR_BYTES = 16 * 1024;
59
+
60
+ function formatInspector(json: unknown): string {
61
+ if (json === undefined || json === null) return "(no snapshot bound)";
62
+ try {
63
+ const s = JSON.stringify(json, null, 2);
64
+ if (s.length > MAX_INSPECTOR_BYTES) {
65
+ return `${s.slice(0, MAX_INSPECTOR_BYTES)}\n… [clipped ${s.length - MAX_INSPECTOR_BYTES} bytes]`;
66
+ }
67
+ return s;
68
+ } catch (err) {
69
+ return `(snapshot serialization failed: ${(err as Error).message})`;
70
+ }
71
+ }
72
+
73
+ export function TwDebugOverlay(props: TwDebugOverlayProps): React.ReactElement {
74
+ const [tab, setTab] = useState<DebugOverlayTab>("inspector");
75
+ const [replInput, setReplInput] = useState("");
76
+
77
+ const tabBtn = (id: DebugOverlayTab, label: string) => (
78
+ <button
79
+ key={id}
80
+ type="button"
81
+ onClick={() => setTab(id)}
82
+ data-state={tab === id ? "active" : "inactive"}
83
+ data-testid={`tw-debug-overlay__tab-${id}`}
84
+ className={`inline-flex h-6 items-center rounded-[var(--radius-sm)] px-2 text-[11px] font-medium transition-colors ${
85
+ tab === id
86
+ ? "bg-[var(--color-accent-primary)]/12 text-[var(--color-accent-primary)]"
87
+ : "text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-hover)] hover:text-[var(--color-text-primary)]"
88
+ }`}
89
+ >
90
+ {label}
91
+ </button>
92
+ );
93
+
94
+ return (
95
+ <div
96
+ className="fixed bottom-0 left-0 right-0 z-40 flex max-h-[48vh] flex-col border-t border-[var(--color-border-subtle)] bg-[var(--color-bg-canvas)] shadow-[var(--shadow-float)]"
97
+ data-testid="tw-debug-overlay"
98
+ data-debug-mode="full"
99
+ role="region"
100
+ aria-label="Editor debug overlay"
101
+ >
102
+ <TwDebugTopBar
103
+ sessionId={props.sessionId}
104
+ snapshot={props.snapshot}
105
+ {...(props.onCollapse ? { onExpand: props.onCollapse } : {})}
106
+ />
107
+ <div
108
+ className="flex shrink-0 items-center gap-1 border-b border-[var(--color-border-subtle)] bg-[var(--color-bg-chrome)]/80 px-3 py-1"
109
+ role="tablist"
110
+ aria-label="Debug panes"
111
+ >
112
+ {tabBtn("inspector", "Inspector")}
113
+ {tabBtn("events", "Events")}
114
+ {tabBtn("repl", "REPL")}
115
+ </div>
116
+ <div
117
+ className="flex-1 overflow-auto px-3 py-2 text-[11px]"
118
+ data-testid={`tw-debug-overlay__pane-${tab}`}
119
+ >
120
+ {tab === "inspector" ? (
121
+ <pre className="whitespace-pre-wrap font-mono text-[11px] leading-[1.45] text-[var(--color-text-primary)]">
122
+ {formatInspector(props.inspectorJson)}
123
+ </pre>
124
+ ) : null}
125
+ {tab === "events" ? (
126
+ <ul className="flex flex-col gap-0.5 font-mono text-[10px] text-[var(--color-text-secondary)]">
127
+ {(props.events ?? []).length === 0 ? (
128
+ <li className="italic text-[var(--color-text-tertiary)]">
129
+ (no events — wire `ui.debug.attach()` with a DebugBus listener)
130
+ </li>
131
+ ) : (
132
+ props.events!.slice(-200).map((e, i) => (
133
+ <li
134
+ key={`${e.t}-${i}`}
135
+ className="border-b border-[var(--color-border-subtle)]/60 py-0.5"
136
+ >
137
+ <span className="text-[var(--color-accent-primary)]">{e.channel}</span>
138
+ {" · "}
139
+ <span className="text-[var(--color-text-primary)]">{e.kind}</span>
140
+ {" · "}
141
+ <span className="text-[var(--color-text-tertiary)]">
142
+ t={Math.round(e.t)}
143
+ </span>
144
+ </li>
145
+ ))
146
+ )}
147
+ </ul>
148
+ ) : null}
149
+ {tab === "repl" ? (
150
+ <form
151
+ onSubmit={(ev) => {
152
+ ev.preventDefault();
153
+ if (replInput.trim().length === 0) return;
154
+ props.onReplEval?.(replInput);
155
+ setReplInput("");
156
+ }}
157
+ className="flex flex-col gap-2"
158
+ >
159
+ <textarea
160
+ value={replInput}
161
+ onChange={(ev) => setReplInput(ev.target.value)}
162
+ placeholder="// Phase Q REPL — follow-up slice will provide in-scope `api` / `runtime` / `debug` handles + localStorage history."
163
+ className="min-h-[120px] w-full resize-y rounded-[var(--radius-sm)] border border-[var(--color-border-default)] bg-[var(--color-bg-muted)] px-2 py-1 font-mono text-[11px] text-[var(--color-text-primary)] outline-none focus-visible:border-[var(--color-accent-primary)]"
164
+ data-testid="tw-debug-overlay__repl-input"
165
+ />
166
+ <div className="flex items-center justify-between">
167
+ <span className="text-[10px] text-[var(--color-text-tertiary)]">
168
+ {props.onReplEval
169
+ ? "Submit (Enter or click Run) invokes the host's onReplEval."
170
+ : "Host has not wired onReplEval — input is inert."}
171
+ </span>
172
+ <button
173
+ type="submit"
174
+ disabled={!props.onReplEval || replInput.trim().length === 0}
175
+ className="inline-flex h-6 items-center rounded-[var(--radius-sm)] bg-[var(--color-accent-primary)] px-3 text-[11px] font-semibold text-[var(--color-text-on-accent)] transition-colors hover:bg-[var(--color-accent-primary-hover)] disabled:opacity-40"
176
+ data-testid="tw-debug-overlay__repl-submit"
177
+ >
178
+ Run
179
+ </button>
180
+ </div>
181
+ </form>
182
+ ) : null}
183
+ </div>
184
+ </div>
185
+ );
186
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Phase Q — DebugMode router.
3
+ *
4
+ * Single mount point `WordReviewEditor.tsx` consumes. Picks between
5
+ * the minimal top-bar, the full overlay, or nothing depending on the
6
+ * public `debugMode` prop. Keeping the router in one file means the
7
+ * invariant test + CI guard can target a single surface.
8
+ *
9
+ * Data flow (consumers wire through `ui.debug.attach()` per refactor/10
10
+ * Slice 5):
11
+ *
12
+ * host → `ui.debug.attach({ id, channels })` →
13
+ * controller.attachDebug(session) →
14
+ * { sessionId, snapshot, events } → this component
15
+ *
16
+ * Slice 7 ships the visibility scaffold; the full REPL + event-tail
17
+ * wiring to `runtime.debug.bus` + `DebugInspectorSnapshot` lives in a
18
+ * follow-up (see `src/ui-tailwind/debug/README.md`).
19
+ */
20
+
21
+ import React from "react";
22
+
23
+ import type { DebugMode } from "../../api/public-types.ts";
24
+ import { TwDebugTopBar, type TwDebugTopBarProps } from "./tw-debug-top-bar.tsx";
25
+ import { TwDebugOverlay, type TwDebugOverlayProps } from "./tw-debug-overlay.tsx";
26
+
27
+ export interface TwDebugPresentationProps {
28
+ mode: DebugMode;
29
+ /**
30
+ * Debug session id — mirrors what the host passed into
31
+ * `ui.debug.attach({ id })`. `null` when no attachment is active.
32
+ */
33
+ sessionId?: string | null;
34
+ snapshot?: TwDebugTopBarProps["snapshot"];
35
+ inspectorJson?: TwDebugOverlayProps["inspectorJson"];
36
+ events?: TwDebugOverlayProps["events"];
37
+ onReplEval?: TwDebugOverlayProps["onReplEval"];
38
+ /**
39
+ * Optional override for the mode toggle — host supplies when it
40
+ * owns the `debugMode` state (e.g. harness with a keyboard shortcut).
41
+ */
42
+ onModeChange?: (next: DebugMode) => void;
43
+ }
44
+
45
+ export function TwDebugPresentation(
46
+ props: TwDebugPresentationProps,
47
+ ): React.ReactElement | null {
48
+ if (props.mode === "off") return null;
49
+
50
+ const sessionId = props.sessionId ?? null;
51
+ const snapshot = props.snapshot ?? null;
52
+
53
+ if (props.mode === "top-bar") {
54
+ return (
55
+ <TwDebugTopBar
56
+ sessionId={sessionId}
57
+ snapshot={snapshot}
58
+ {...(props.onModeChange
59
+ ? { onExpand: () => props.onModeChange!("full") }
60
+ : {})}
61
+ />
62
+ );
63
+ }
64
+
65
+ // mode === "full"
66
+ return (
67
+ <TwDebugOverlay
68
+ sessionId={sessionId}
69
+ snapshot={snapshot}
70
+ {...(props.inspectorJson !== undefined
71
+ ? { inspectorJson: props.inspectorJson }
72
+ : {})}
73
+ {...(props.events ? { events: props.events } : {})}
74
+ {...(props.onReplEval ? { onReplEval: props.onReplEval } : {})}
75
+ {...(props.onModeChange
76
+ ? { onCollapse: () => props.onModeChange!("top-bar") }
77
+ : {})}
78
+ />
79
+ );
80
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Phase Q — Debug top bar.
3
+ *
4
+ * Minimal always-on status strip shown when
5
+ * `WordReviewEditorProps.debugMode === "top-bar"` (or as the header of
6
+ * the "full" overlay). Renders the bound debug session id + a small
7
+ * set of counters derived from the `DebugInspectorSnapshot` the host
8
+ * supplies through `ui.debug.attach()` wiring.
9
+ *
10
+ * Slice 7 ships the minimal surface; the REPL + event-tail panes live
11
+ * in `tw-debug-overlay.tsx` (full mode only). Future telemetry (scope
12
+ * count, invalidation counter, channel activity) plugs in through
13
+ * `snapshot` without changing the public prop.
14
+ */
15
+
16
+ import React from "react";
17
+
18
+ export interface TwDebugTopBarProps {
19
+ /**
20
+ * The debug-session id the host passed into `ui.debug.attach()`.
21
+ * Shown verbatim so operators can correlate with Railway debug-
22
+ * service logs. `null` when no attachment is active.
23
+ */
24
+ sessionId: string | null;
25
+ /**
26
+ * Lightweight counters sourced from `DebugInspectorSnapshot`.
27
+ * `null` means no snapshot has flowed yet.
28
+ */
29
+ snapshot: {
30
+ readonly scopeCount?: number;
31
+ readonly blockCount?: number;
32
+ readonly revisionToken?: string;
33
+ } | null;
34
+ /**
35
+ * Optional click handler — the harness can use this to toggle
36
+ * between `"top-bar"` and `"full"` modes.
37
+ */
38
+ onExpand?: () => void;
39
+ }
40
+
41
+ export function TwDebugTopBar(props: TwDebugTopBarProps): React.ReactElement {
42
+ const pill = (label: string, value: string | number | undefined | null) =>
43
+ value === undefined || value === null ? null : (
44
+ <span
45
+ key={label}
46
+ className="inline-flex items-center gap-1 rounded-[var(--radius-sm)] bg-[var(--color-bg-muted)] px-2 py-0.5 text-[10px] font-medium text-[var(--color-text-secondary)]"
47
+ data-testid={`tw-debug-top-bar__${label.toLowerCase().replace(/\s+/g, "-")}`}
48
+ >
49
+ <span className="text-[var(--color-text-tertiary)]">{label}</span>
50
+ <span className="font-mono text-[var(--color-text-primary)]">{value}</span>
51
+ </span>
52
+ );
53
+
54
+ return (
55
+ <div
56
+ className="flex h-7 shrink-0 items-center gap-2 border-b border-[var(--color-border-subtle)] bg-[var(--color-bg-chrome)]/95 px-3 text-[11px] backdrop-blur-sm"
57
+ data-testid="tw-debug-top-bar"
58
+ data-debug-mode="top-bar"
59
+ role="status"
60
+ aria-label="Editor debug status bar"
61
+ >
62
+ <span className="font-semibold uppercase tracking-[0.14em] text-[var(--color-text-tertiary)]">
63
+ Debug
64
+ </span>
65
+ {pill("session", props.sessionId ?? "(detached)")}
66
+ {pill("scopes", props.snapshot?.scopeCount)}
67
+ {pill("blocks", props.snapshot?.blockCount)}
68
+ {props.snapshot?.revisionToken
69
+ ? pill("rev", props.snapshot.revisionToken.slice(0, 8))
70
+ : null}
71
+ {props.onExpand ? (
72
+ <button
73
+ type="button"
74
+ onClick={props.onExpand}
75
+ className="ml-auto inline-flex h-5 items-center rounded-[var(--radius-sm)] px-1.5 text-[10px] font-medium text-[var(--color-text-secondary)] transition-colors hover:bg-[var(--color-bg-hover)] hover:text-[var(--color-text-primary)]"
76
+ data-testid="tw-debug-top-bar__expand"
77
+ >
78
+ Expand ▾
79
+ </button>
80
+ ) : null}
81
+ </div>
82
+ );
83
+ }
@@ -17,7 +17,7 @@ import { createRoot, type Root } from "react-dom/client";
17
17
  import type { Node as PMNode } from "prosemirror-model";
18
18
  import type { NodeViewConstructor } from "prosemirror-view";
19
19
  import { ChartSurface } from "../chart/ChartSurface.tsx";
20
- import { chartModelStore } from "../../runtime/chart/chart-model-store.ts";
20
+ import { getChartModel } from "../../api/v3/runtime/chart.ts";
21
21
 
22
22
  const DEFAULT_WIDTH = 576;
23
23
  const DEFAULT_HEIGHT = 336;
@@ -55,7 +55,7 @@ class ChartNodeViewInstance {
55
55
  return;
56
56
  }
57
57
 
58
- const entry = chartModelStore.get(parsedChartId);
58
+ const entry = getChartModel(parsedChartId);
59
59
  if (!entry) {
60
60
  this._unmountRoot();
61
61
  return;
@@ -18,7 +18,7 @@
18
18
  */
19
19
 
20
20
  import type { SurfaceDrawingAnchor } from "../../api/public-types";
21
- import { EMU_PER_PX } from "../../runtime/units";
21
+ import { EMU_PER_PX } from "../../api/public-types";
22
22
 
23
23
  export interface FloatWrapStyle {
24
24
  /** CSS `float` value when the anchor wraps inline text. */
@@ -12,8 +12,6 @@ import {
12
12
  extractPlainTextSegments,
13
13
  type PastePlainSegment,
14
14
  } from "./paste-plain-text";
15
- import { parseCanonicalFragmentFromWordML } from "../../io/paste/word-clipboard";
16
- import { parseCanonicalFragmentFromHtml } from "../../io/paste/html-clipboard";
17
15
  import type { PositionMap } from "./pm-position-map";
18
16
 
19
17
  /**
@@ -243,6 +241,83 @@ export interface CommandBridgeCallbacks extends SelectionSyncCallbacks {
243
241
  * lands. When absent, the legacy "block all docChanged" behavior applies.
244
242
  */
245
243
  gate?: Plugin;
244
+ /**
245
+ * Phase C.β — right-click composition hook.
246
+ *
247
+ * When wired, fires on `contextmenu` DOM events with the event
248
+ * coordinates + target; the bridge suppresses the browser-native menu
249
+ * so the host can mount `TwContextMenu` at the returned coordinates.
250
+ *
251
+ * When NOT wired, the bridge leaves the native menu alone so
252
+ * integrators that haven't opted in retain browser-default behavior
253
+ * (back-compat).
254
+ *
255
+ * Shape deliberately minimal — the host resolves target kind from the
256
+ * target DOM node, queries the `editor-action-registry`, and renders
257
+ * the resulting `TwContextMenu`. The bridge does not import the
258
+ * registry directly — this preserves the three-actor split (bridge
259
+ * is surface-layer; registry + composition are chrome-layer).
260
+ */
261
+ onContextMenuRequested?: (event: ContextMenuRequestEvent) => void;
262
+ }
263
+
264
+ export interface ContextMenuRequestEvent {
265
+ readonly clientX: number;
266
+ readonly clientY: number;
267
+ /**
268
+ * The native `event.target`. Typically an Element (PM-rendered span,
269
+ * table cell, image, etc.) but can be a Text node or null. Callers
270
+ * must walk to the nearest Element and apply their own target-kind
271
+ * resolver — see the Phase C.γ `resolveTargetKind(el)` helper once
272
+ * it ships, or use `target instanceof Element ? target.closest(…) : null`
273
+ * in the interim.
274
+ */
275
+ readonly target: EventTarget | null;
276
+ }
277
+
278
+ /**
279
+ * Pure helper used by the bridge's `handleDOMEvents.contextmenu` hook.
280
+ * Returns `true` when the native menu should be suppressed (i.e. the
281
+ * host wired a callback); `false` when the bridge lets browser default
282
+ * take over.
283
+ *
284
+ * Exception handling: if the host's callback throws, the error is
285
+ * logged and the helper returns `false` so PM/browser fall back to
286
+ * native behavior. `event.preventDefault()` is called BEFORE the
287
+ * handler fires (the contract is "host owns the menu now"), but if
288
+ * the handler throws we still propagate `false` so PM does not
289
+ * double-suppress — the user at minimum gets no menu rather than a
290
+ * silent no-op cascade.
291
+ *
292
+ * Exported so unit tests can exercise the contextmenu dispatch path
293
+ * without standing up a full EditorView (the prosemirror-tables
294
+ * columnResizing plugin requires a tables-aware schema that's
295
+ * overkill for this contract).
296
+ */
297
+ export function applyContextMenuEvent(
298
+ callbacks: CommandBridgeCallbacks,
299
+ event: MouseEvent,
300
+ ): boolean {
301
+ const handler = callbacks.onContextMenuRequested;
302
+ if (!handler) return false;
303
+ try {
304
+ event.preventDefault();
305
+ handler({
306
+ clientX: event.clientX,
307
+ clientY: event.clientY,
308
+ target: event.target,
309
+ });
310
+ return true;
311
+ } catch (err) {
312
+ // Log so integrators can debug; do not re-throw (throwing from a
313
+ // PM handleDOMEvents returns a truthy value that tells PM the
314
+ // event was handled, which would then cause PM to swallow it).
315
+ // Surface `false` so the user still gets native menu as fallback
316
+ // rather than a silent empty overlay.
317
+ // eslint-disable-next-line no-console
318
+ console.error("[pm-command-bridge] onContextMenuRequested threw:", err);
319
+ return false;
320
+ }
246
321
  }
247
322
 
248
323
  const bridgeKey = new PluginKey("command-bridge");
@@ -281,9 +356,45 @@ export function createSelectionSyncPlugin(
281
356
  });
282
357
  }
283
358
 
359
+ /**
360
+ * Result shape from the paste-parser adapters — mirrors the native
361
+ * `parseCanonicalFragmentFromWordML` / `parseCanonicalFragmentFromHtml`
362
+ * return so callers don't have to re-map.
363
+ */
364
+ export type PasteFragmentParseResult =
365
+ | {
366
+ readonly ok: true;
367
+ readonly fragment: import("../../api/public-types.ts").CanonicalDocumentFragment;
368
+ }
369
+ | { readonly ok: false; readonly reason?: string };
370
+
371
+ /**
372
+ * Host-supplied adapter that turns raw WordML / HTML clipboard payloads
373
+ * into a `CanonicalDocumentFragment`. Refactor/11 Slice 5 retires the
374
+ * PM bridge's direct `io/paste/**` imports: the bridge emits the raw
375
+ * payload + mime type, the host parses, and fires the resulting
376
+ * `onPasteFragment` callback. Hosts that don't supply the adapter fall
377
+ * through to Tier A plain-text paste, as if `onPasteFragment` was not
378
+ * wired.
379
+ */
380
+ export interface PasteFragmentParserAdapter {
381
+ parseWordML(wordml: string): PasteFragmentParseResult;
382
+ parseHtml(html: string): PasteFragmentParseResult;
383
+ }
384
+
284
385
  export interface CommandBridgePluginOptions {
285
386
  /** P6: when true, omit prosemirror-tables columnResizing() — chrome overlay grips take over. */
286
387
  useChromeColumnResizing?: boolean;
388
+ /**
389
+ * Slice 5 retirement seam — host-supplied parser adapter for rich
390
+ * paste (WordML + HTML). When absent, the bridge does NOT parse
391
+ * rich clipboard payloads; they fall through to plain-text paste or
392
+ * `onBlockedInput`. `WordReviewEditor.tsx` supplies the adapter
393
+ * wrapping `io/paste/word-clipboard` + `io/paste/html-clipboard`;
394
+ * headless consumers can plug their own (or omit it to accept
395
+ * plain-text-only paste).
396
+ */
397
+ pasteFragmentParser?: PasteFragmentParserAdapter;
287
398
  }
288
399
 
289
400
  export function createCommandBridgePlugins(
@@ -363,6 +474,13 @@ export function createCommandBridgePlugins(
363
474
  dragSourceRange = null;
364
475
  return false;
365
476
  },
477
+ // Phase C.β — right-click composition hook. Returns true to
478
+ // block the native menu only when the host has wired a callback,
479
+ // so integrators that haven't opted in still get browser-native
480
+ // behavior (back-compat).
481
+ contextmenu(_view, event) {
482
+ return applyContextMenuEvent(callbacks, event as MouseEvent);
483
+ },
366
484
  },
367
485
  handleTextInput(_view, _from, _to, text) {
368
486
  if (isComposing) {
@@ -374,7 +492,7 @@ export function createCommandBridgePlugins(
374
492
 
375
493
  // I2 paste handler — Tier B (WordML) preferred, Tier A (plain) fallback.
376
494
  //
377
- // Preference order per `docs/plans/lane-1-i2-tier-b-rich-paste.md`:
495
+ // Preference order per `CLAUDE.md (lane status table)`:
378
496
  // 1. Office-clipboard WordprocessingML payload if the host wired
379
497
  // `onPasteFragment` AND the clipboard carries the MIME. Parsed via
380
498
  // `parseCanonicalFragmentFromWordML`.
@@ -417,10 +535,15 @@ export function createCommandBridgePlugins(
417
535
  }
418
536
 
419
537
  // Tier B: WordprocessingML (highest fidelity when available)
420
- if (callbacks.onPasteFragment) {
538
+ // Slice 5 — the bridge no longer imports `io/paste/**` directly;
539
+ // callers inject `pasteFragmentParser` so rich-paste parsing
540
+ // lives in the shell (or the host). Without the adapter, rich
541
+ // paste falls through to plain text (same as before when
542
+ // `onPasteFragment` was not wired).
543
+ if (callbacks.onPasteFragment && options?.pasteFragmentParser) {
421
544
  const wordml = readWordMLPayload(clipboard);
422
545
  if (wordml) {
423
- const parsed = parseCanonicalFragmentFromWordML(wordml);
546
+ const parsed = options.pasteFragmentParser.parseWordML(wordml);
424
547
  if (parsed.ok && parsed.fragment.blocks.length > 0) {
425
548
  callbacks.onPasteFragment({ fragment: parsed.fragment, source: "wordml" });
426
549
  return true;
@@ -431,10 +554,10 @@ export function createCommandBridgePlugins(
431
554
  }
432
555
 
433
556
  // Tier B: HTML (Google Docs, Word web, Outlook-lite)
434
- if (callbacks.onPasteFragment) {
557
+ if (callbacks.onPasteFragment && options?.pasteFragmentParser) {
435
558
  const htmlPayload = clipboard.getData("text/html");
436
559
  if (htmlPayload && htmlPayload.trim().length > 0) {
437
- const parsed = parseCanonicalFragmentFromHtml(htmlPayload);
560
+ const parsed = options.pasteFragmentParser.parseHtml(htmlPayload);
438
561
  if (parsed.ok && parsed.fragment.blocks.length > 0) {
439
562
  callbacks.onPasteFragment({ fragment: parsed.fragment, source: "html" });
440
563
  return true;
@@ -479,11 +602,13 @@ export function createCommandBridgePlugins(
479
602
  return true;
480
603
  }
481
604
 
482
- // Tier B drop: WordprocessingML fragment
483
- if (callbacks.onDropFragment) {
605
+ // Tier B drop: WordprocessingML fragment — uses the host-
606
+ // supplied parser adapter (Slice 5 retirement); absent adapter
607
+ // falls through to plain text.
608
+ if (callbacks.onDropFragment && options?.pasteFragmentParser) {
484
609
  const wordml = readWordMLPayload(dt);
485
610
  if (wordml) {
486
- const parsed = parseCanonicalFragmentFromWordML(wordml);
611
+ const parsed = options.pasteFragmentParser.parseWordML(wordml);
487
612
  if (parsed.ok && parsed.fragment.blocks.length > 0) {
488
613
  const ctrlModifier = Boolean(dragEvent.ctrlKey || dragEvent.metaKey);
489
614
  const effect = ctrlModifier ? "copy" : "move";
@@ -2,8 +2,14 @@ import { Decoration, DecorationSet } from "prosemirror-view";
2
2
 
3
3
  import type { CommentDecorationModel } from "../../ui/headless/comment-decoration-model";
4
4
  import { getCommentHighlightClass, type MarkupDisplay } from "../../ui/headless/comment-decoration-model";
5
- import type { RevisionDecorationModel } from "../../ui/headless/revision-decoration-model";
6
- import { getRevisionHighlightClass } from "../../ui/headless/revision-decoration-model";
5
+ import type {
6
+ RevisionDecorationModel,
7
+ RevisionDisplayFlags,
8
+ } from "../../ui/headless/revision-decoration-model";
9
+ import {
10
+ buildClassFromRevisionDisplay,
11
+ getRevisionHighlightClass,
12
+ } from "../../ui/headless/revision-decoration-model";
7
13
  import type {
8
14
  EditorAnchorProjection,
9
15
  EditorStoryTarget,
@@ -13,7 +19,7 @@ import type {
13
19
  WorkflowMetadataMarkup,
14
20
  WorkflowScope,
15
21
  } from "../../api/public-types";
16
- import { MAIN_STORY_TARGET, storyTargetsEqual } from "../../core/selection/mapping.ts";
22
+ import { MAIN_STORY_TARGET, storyTargetsEqual } from "../../api/public-types.ts";
17
23
  import type { PositionMap } from "./pm-position-map";
18
24
  import type { Node as PMNode } from "prosemirror-model";
19
25
 
@@ -313,6 +319,14 @@ export function buildDecorations(
313
319
  activeWorkflowWorkItemIdOrScopeIds?: string | null | readonly string[],
314
320
  activeWorkflowScopeIdsOrMetadata?: readonly string[] | readonly WorkflowMetadataMarkup[],
315
321
  workflowMetadata?: readonly WorkflowMetadataMarkup[],
322
+ // §4.3 — Track B integration. When the caller threads an offset →
323
+ // `SurfaceInlineSegment.revisionDisplay` map (pre-computed from
324
+ // `snapshot.surfaceInlineSegments`), the revision-decoration loop
325
+ // reads the L03-authoritative display flags instead of re-deriving
326
+ // from the review-store side channel via `getRevisionHighlightClass`.
327
+ // Consumers without a live surface-segment stream (headless / legacy
328
+ // callers) omit this argument and keep the pre-Track-B behavior.
329
+ revisionDisplayByOffset?: ReadonlyMap<number, RevisionDisplayFlags>,
316
330
  ): DecorationSet {
317
331
  const isStoryTarget = (value: unknown): value is EditorStoryTarget =>
318
332
  Boolean(value) &&
@@ -408,10 +422,17 @@ export function buildDecorations(
408
422
  // Visual styling (underlines, colors) only applies when showTrackedChanges is on.
409
423
  if (revisionModel) {
410
424
  for (const rev of revisionModel.revisions) {
411
- // Always hide deletions in clean mode (final-text semantics).
412
- // This is the critical behavior: "hide tracked changes" must show
413
- // the document as if accepted, not show deleted text as kept text.
414
- if (markupDisplay === "clean" && rev.kind === "deletion") {
425
+ // §4.4 L03's `revisionDisplay.hidden` is the authoritative hide
426
+ // signal when the caller threads the offset map. When absent, fall
427
+ // back to the pre-L03 `markupDisplay === "clean" && deletion` rule
428
+ // so legacy / headless callers keep their final-text semantics.
429
+ const revDisplayFlags = revisionDisplayByOffset?.get(rev.from);
430
+ const hideAsDeletion =
431
+ rev.kind === "deletion" &&
432
+ (revDisplayFlags
433
+ ? revDisplayFlags.hidden === true
434
+ : markupDisplay === "clean");
435
+ if (hideAsDeletion) {
415
436
  const cleanPmFrom = positionMap.runtimeToPm(rev.from);
416
437
  const cleanPmTo = positionMap.runtimeToPm(rev.to);
417
438
  if (cleanPmFrom < cleanPmTo) {
@@ -470,12 +491,18 @@ export function buildDecorations(
470
491
  // Skip normal markup styling when tracked changes display is off
471
492
  if (!showTrackedChanges) continue;
472
493
 
473
- const cls = getRevisionHighlightClass(
474
- revisionModel,
475
- rev.from,
476
- rev.to,
477
- markupDisplay,
478
- );
494
+ // §4.3 prefer L03's authoritative `revisionDisplay` flags when
495
+ // the caller threads the offset map; otherwise fall through to the
496
+ // review-store side-channel class helper.
497
+ const displayFlags = revisionDisplayByOffset?.get(rev.from);
498
+ const cls = displayFlags
499
+ ? buildClassFromRevisionDisplay(displayFlags)
500
+ : getRevisionHighlightClass(
501
+ revisionModel,
502
+ rev.from,
503
+ rev.to,
504
+ markupDisplay,
505
+ );
479
506
  if (!cls) continue;
480
507
 
481
508
  decorations.push(