@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
@@ -1,62 +1,44 @@
1
1
  /**
2
- * Resolve style chains for paragraph and character styles by walking the
3
- * `basedOn` reference, with cycle detection.
2
+ * Layer 03 four-tier style cascade core.
4
3
  *
5
- * Returned chains are leaf-first (most-specific style at index 0, root at the
6
- * end). Cycles break at the first re-encounter. Dangling `basedOn` references
7
- * stop the walk silently (the leaf is still returned).
4
+ * Owns the primitive merge helpers (`mergeRun`, `mergeParagraph`) and the
5
+ * drivers that walk the `basedOn` chains to produce effective formatting:
8
6
  *
9
- * Tasks 9 and 10 extend this module with `resolveEffective*Formatting`
10
- * helpers that cascade the chain into a flat formatting record.
7
+ * - `resolveEffectiveParagraphFormatting` docDefaults basedOn chain
8
+ * (root-to-leaf) direct
9
+ * - `resolveEffectiveRunFormatting` — docDefaults.run → paragraph-style
10
+ * chain rPr → character-style chain rPr → direct
11
+ * - `resolveNumberingMarkerRunFormatting` — docDefaults.run →
12
+ * paragraph-style chain → paragraph-mark rPr → level rPr
13
+ * - `resolveTableCellTextFormatting` — docDefaults → table-style chain
14
+ *
15
+ * The chain walkers (`resolveParagraphStyleChain`, `resolveCharacterStyleChain`)
16
+ * remain in `./paragraph-style-resolver.ts` and are imported from there —
17
+ * they are not cascade drivers, they are tree-walkers.
18
+ *
19
+ * Contract: all drivers are pure functions on (input, catalog). They do not
20
+ * consult the document content, the theme, the numbering catalog, or any
21
+ * layout state. Theme concretization + numbering + field + revision-display
22
+ * posture layer on top in `./resolve-effective.ts`.
11
23
  */
12
24
 
13
25
  import type {
14
- CanonicalFontTable,
15
26
  CanonicalParagraphFormatting,
16
27
  CanonicalRunFormatting,
17
- CharacterStyleDefinition,
18
- ParagraphStyleDefinition,
19
28
  StylesCatalog,
29
+ TableStyleConditionalRegion,
20
30
  TableStyleDefinition,
21
- } from "../model/canonical-document.ts";
31
+ } from "../../model/canonical-document.ts";
32
+ import {
33
+ resolveCharacterStyleChain,
34
+ resolveParagraphStyleChain,
35
+ } from "./paragraph-style-resolver.ts";
22
36
 
23
- export function resolveParagraphStyleChain(
24
- styleId: string,
25
- catalog: StylesCatalog | undefined,
26
- ): string[] {
27
- const chain: string[] = [];
28
- if (!catalog) return chain;
29
- const visited = new Set<string>();
30
- let current: string | undefined = styleId;
31
- while (current && !visited.has(current)) {
32
- visited.add(current);
33
- const def: ParagraphStyleDefinition | undefined = catalog.paragraphs[current];
34
- if (!def) break;
35
- chain.push(current);
36
- current = def.basedOn;
37
- }
38
- return chain;
39
- }
40
-
41
- export function resolveCharacterStyleChain(
42
- styleId: string,
43
- catalog: StylesCatalog | undefined,
44
- ): string[] {
45
- const chain: string[] = [];
46
- if (!catalog) return chain;
47
- const visited = new Set<string>();
48
- let current: string | undefined = styleId;
49
- while (current && !visited.has(current)) {
50
- visited.add(current);
51
- const def: CharacterStyleDefinition | undefined = catalog.characters[current];
52
- if (!def) break;
53
- chain.push(current);
54
- current = def.basedOn;
55
- }
56
- return chain;
57
- }
37
+ // ---------------------------------------------------------------------------
38
+ // Merge primitives
39
+ // ---------------------------------------------------------------------------
58
40
 
59
- function mergeRun(
41
+ export function mergeRun(
60
42
  base: CanonicalRunFormatting | undefined,
61
43
  over: CanonicalRunFormatting | undefined,
62
44
  ): CanonicalRunFormatting | undefined {
@@ -64,7 +46,7 @@ function mergeRun(
64
46
  return { ...(base ?? {}), ...(over ?? {}) };
65
47
  }
66
48
 
67
- function mergeParagraph(
49
+ export function mergeParagraph(
68
50
  base: CanonicalParagraphFormatting | undefined,
69
51
  over: CanonicalParagraphFormatting | undefined,
70
52
  ): CanonicalParagraphFormatting | undefined {
@@ -91,13 +73,32 @@ function mergeParagraph(
91
73
  return merged;
92
74
  }
93
75
 
76
+ // ---------------------------------------------------------------------------
77
+ // Driver inputs
78
+ // ---------------------------------------------------------------------------
79
+
94
80
  export interface ParagraphResolveInput {
95
81
  styleId: string | undefined;
96
82
  direct: CanonicalParagraphFormatting | undefined;
97
83
  }
98
84
 
85
+ export interface RunResolveInput {
86
+ paragraphStyleId: string | undefined;
87
+ characterStyleId: string | undefined;
88
+ direct: CanonicalRunFormatting | undefined;
89
+ }
90
+
91
+ export interface MarkerResolveInput {
92
+ paragraphStyleId: string | undefined;
93
+ levelRunProperties: CanonicalRunFormatting | undefined;
94
+ }
95
+
96
+ // ---------------------------------------------------------------------------
97
+ // Drivers
98
+ // ---------------------------------------------------------------------------
99
+
99
100
  /**
100
- * Resolve paragraph formatting cascade: docDefaults.paragraph → basedOn chain
101
+ * Paragraph formatting cascade: docDefaults.paragraph → basedOn chain
101
102
  * (root-to-leaf) → direct. Returns `{}` when nothing is present.
102
103
  */
103
104
  export function resolveEffectiveParagraphFormatting(
@@ -107,7 +108,7 @@ export function resolveEffectiveParagraphFormatting(
107
108
  let acc: CanonicalParagraphFormatting | undefined = catalog?.docDefaults?.paragraph;
108
109
  if (catalog && input.styleId) {
109
110
  const chain = resolveParagraphStyleChain(input.styleId, catalog);
110
- // Walk root-to-leaf (most-general first) so the most-specific style wins
111
+ // Walk root-to-leaf (most-general first) so the most-specific style wins.
111
112
  for (let i = chain.length - 1; i >= 0; i -= 1) {
112
113
  const styleId = chain[i]!;
113
114
  acc = mergeParagraph(acc, catalog.paragraphs[styleId]?.paragraphProperties);
@@ -117,14 +118,8 @@ export function resolveEffectiveParagraphFormatting(
117
118
  return acc ?? {};
118
119
  }
119
120
 
120
- export interface RunResolveInput {
121
- paragraphStyleId: string | undefined;
122
- characterStyleId: string | undefined;
123
- direct: CanonicalRunFormatting | undefined;
124
- }
125
-
126
121
  /**
127
- * Resolve run formatting cascade: docDefaults.run → paragraph-style-chain rPr
122
+ * Run formatting cascade: docDefaults.run → paragraph-style-chain rPr
128
123
  * (root-to-leaf) → character-style-chain (root-to-leaf) → direct. Returns
129
124
  * `{}` when nothing is present.
130
125
  */
@@ -151,102 +146,35 @@ export function resolveEffectiveRunFormatting(
151
146
  return acc ?? {};
152
147
  }
153
148
 
154
- export interface MarkerResolveInput {
155
- paragraphStyleId: string | undefined;
156
- levelRunProperties: CanonicalRunFormatting | undefined;
157
- }
158
-
159
149
  /**
160
- * Resolve the numbering marker's character formatting the rPr that styles
161
- * the number/bullet glyph itself (bold, color, font, size, etc.).
150
+ * Numbering marker rPr cascade (ECMA-376 §17.9).
162
151
  *
163
- * Cascade order (lowest to highest priority):
164
- * 1. `docDefaults.run` — Word's baseline run formatting.
165
- * 2. Paragraph style chain's `runProperties` — walked root-to-leaf; the
166
- * numbered paragraph's styleId contributes each ancestor's rPr.
167
- * 3. The leaf paragraph style's `paragraphProperties.paragraphMarkRunProperties`
168
- * Word stores the paragraph-mark rPr separately from the body rPr,
169
- * and the mark's formatting is what the numbering marker inherits from.
170
- * 4. `levelRunProperties` — `<w:lvl><w:rPr>` on the numbering level. Per
171
- * ECMA-376, this is the most specific formatting for the marker and
172
- * overrides the paragraph's run formatting entirely for the marker.
152
+ * Order (lowest highest priority):
153
+ * 1. `docDefaults.run`
154
+ * 2. Paragraph style chain `runProperties` — root-to-leaf
155
+ * 3. Leaf paragraph style's `paragraphProperties.paragraphMarkRunProperties`
156
+ * (Word stores the mark rPr separately from body rPr; the marker
157
+ * inherits from the mark).
158
+ * 4. `levelRunProperties` (`<w:lvl><w:rPr>`) final word.
173
159
  */
174
160
  export function resolveNumberingMarkerRunFormatting(
175
161
  input: MarkerResolveInput,
176
162
  catalog: StylesCatalog | undefined,
177
163
  ): CanonicalRunFormatting {
178
- // Start with docDefaults baseline
179
164
  let acc: CanonicalRunFormatting | undefined = catalog?.docDefaults?.run;
180
-
181
- // Walk the paragraph style chain's rPr (root-to-leaf, most-specific wins)
182
165
  if (catalog && input.paragraphStyleId) {
183
166
  const chain = resolveParagraphStyleChain(input.paragraphStyleId, catalog);
184
167
  for (let i = chain.length - 1; i >= 0; i -= 1) {
185
168
  const styleId = chain[i]!;
186
169
  acc = mergeRun(acc, catalog.paragraphs[styleId]?.runProperties);
187
170
  }
188
- // Leaf paragraph style's paragraph-mark rPr layers on top of the chain
189
171
  const leaf = catalog.paragraphs[input.paragraphStyleId];
190
172
  acc = mergeRun(acc, leaf?.paragraphProperties?.paragraphMarkRunProperties);
191
173
  }
192
-
193
- // Level rPr has the final say (per ECMA-376 17.9)
194
174
  acc = mergeRun(acc, input.levelRunProperties);
195
175
  return acc ?? {};
196
176
  }
197
177
 
198
- /**
199
- * Return the `w:next` style ID for `styleId` — the paragraph style Word
200
- * applies after pressing Enter at the end of a paragraph of this style.
201
- * Used by Lane 1's suggest-paragraph-split command.
202
- */
203
- export function getNextStyleId(
204
- styleId: string,
205
- catalog: StylesCatalog | undefined,
206
- ): string | undefined {
207
- return catalog?.paragraphs[styleId]?.nextStyle;
208
- }
209
-
210
- /**
211
- * Resolve the effective font family name for a run.
212
- *
213
- * Walks the ECMA-376 §17.3.2.26 precedence for `<w:rFonts>`: `ascii` →
214
- * `hAnsi` → `eastAsia` → `cs`. When none of those resolve on the direct
215
- * run formatting, layers the paragraph/character style cascade for the
216
- * same four fields, then falls back to the caller-supplied
217
- * `themeMinorFont` (ECMA-376 default for body text).
218
- *
219
- * `fontTable`, when supplied, is consulted only for presence — an entry
220
- * whose `w:name` does not appear in `fontTable.fonts` is still returned
221
- * as-is (Word behavior: substitute at render time, not at model resolve).
222
- * Lane 3a's measurement backend owns the substitution decision.
223
- *
224
- * Returns undefined only when no source in the cascade supplies a family
225
- * name (rare — production docs always hit docDefaults or the theme).
226
- */
227
- export function resolveRunFontFamily(
228
- input: RunResolveInput,
229
- catalog: StylesCatalog | undefined,
230
- themeMinorFont: string | undefined,
231
- fontTable?: CanonicalFontTable | undefined,
232
- ): string | undefined {
233
- // `fontTable` is currently unused inside this resolver; the ECMA-376
234
- // rFonts precedence does not require consulting the package's fontTable
235
- // to pick a name. It is reserved so Lane 3a's measurement backend can
236
- // layer substitution (e.g. pick a monospace fallback when the resolved
237
- // name has `pitch === "fixed"`) by threading the same table through.
238
- void fontTable;
239
- const resolved = resolveEffectiveRunFormatting(input, catalog);
240
- const name =
241
- resolved.fontFamilyAscii ??
242
- resolved.fontFamilyHAnsi ??
243
- resolved.fontFamilyEastAsia ??
244
- resolved.fontFamilyCs ??
245
- resolved.fontFamily ??
246
- themeMinorFont;
247
- return name;
248
- }
249
-
250
178
  function resolveTableStyleChain(
251
179
  styleId: string,
252
180
  catalog: StylesCatalog | undefined,
@@ -273,26 +201,41 @@ function resolveTableStyleChain(
273
201
  * 1. `catalog.docDefaults.paragraph` / `.run`
274
202
  * 2. Table style's `basedOn` chain, walked root-to-leaf, projecting each
275
203
  * style's `formatting.paragraphProperties` / `.runProperties`.
204
+ * 3. When `activeRegions` is supplied — the `conditionalFormatting[region]`
205
+ * pPr / rPr for each active region, applied in the caller's region
206
+ * order (typically `getActiveCellRegions()` output: row bands →
207
+ * column bands → first/last row → first/last column → corner cells).
208
+ * Higher regions in the list win over lower — consistent with
209
+ * ECMA-376 §17.7.6 region-precedence semantics and matches how
210
+ * `mergeStyleFormattingCell` already layers cell-level properties.
276
211
  *
277
- * Conditional-region pPr/rPr (`<w:tblStylePr w:type="...">`) is intentionally
278
- * NOT layered here callers that need region-aware cell-text cascade must
279
- * extend this once the conditional region parser captures pPr/rPr (Lane 3a
280
- * follow-up). Per-paragraph and per-run formatting from the cell's content
281
- * is layered by `resolveEffectiveParagraphFormatting` /
282
- * `resolveEffectiveRunFormatting`; this function only returns the floor.
212
+ * Callers that have no region information (e.g. a non-contextual lookup)
213
+ * omit `activeRegions` and receive the basedOn-chain floor only. Per-paragraph
214
+ * and per-run formatting from the cell's content is layered on top by the
215
+ * paragraph + run cascade drivers; this function returns the pre-direct
216
+ * table-style floor.
283
217
  */
284
218
  export function resolveTableCellTextFormatting(
285
219
  tableStyleId: string | undefined,
286
220
  catalog: StylesCatalog | undefined,
221
+ activeRegions?: readonly TableStyleConditionalRegion[],
287
222
  ): { paragraph: CanonicalParagraphFormatting; run: CanonicalRunFormatting } {
288
223
  let paragraph: CanonicalParagraphFormatting | undefined = catalog?.docDefaults?.paragraph;
289
224
  let run: CanonicalRunFormatting | undefined = catalog?.docDefaults?.run;
290
225
  if (catalog && tableStyleId) {
291
226
  const chain = resolveTableStyleChain(tableStyleId, catalog);
292
227
  for (let i = chain.length - 1; i >= 0; i -= 1) {
293
- const formatting = chain[i]!.formatting;
294
- paragraph = mergeParagraph(paragraph, formatting?.paragraphProperties);
295
- run = mergeRun(run, formatting?.runProperties);
228
+ const def = chain[i]!;
229
+ paragraph = mergeParagraph(paragraph, def.formatting?.paragraphProperties);
230
+ run = mergeRun(run, def.formatting?.runProperties);
231
+ if (activeRegions && activeRegions.length > 0 && def.conditionalFormatting) {
232
+ for (const region of activeRegions) {
233
+ const regionFormatting = def.conditionalFormatting[region];
234
+ if (!regionFormatting) continue;
235
+ paragraph = mergeParagraph(paragraph, regionFormatting.paragraphProperties);
236
+ run = mergeRun(run, regionFormatting.runProperties);
237
+ }
238
+ }
296
239
  }
297
240
  }
298
241
  return { paragraph: paragraph ?? {}, run: run ?? {} };
@@ -10,7 +10,7 @@ import type {
10
10
  TableStyleDefinition,
11
11
  TableStyleFormatting,
12
12
  TableWidth,
13
- } from "../model/canonical-document.ts";
13
+ } from "../../model/canonical-document.ts";
14
14
 
15
15
  export interface ResolvedTableCellStyle {
16
16
  width?: TableWidth;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Layer 03 — formatting-telemetry bridge.
3
+ *
4
+ * Consumers (surface-projection, document-runtime, field resolver) pass a
5
+ * `ResolveEffectiveFormattingOptions.emitFormattingTelemetry` callback
6
+ * that bridges entry events onto the runtime `TelemetryBus`. This helper
7
+ * builds that callback with:
8
+ *
9
+ * - Probability gating for `formatting.resolution_sampled` (default
10
+ * rate `0.01` — 1% of resolutions).
11
+ * - Unfiltered pass-through for `formatting.cache.invalidated` and
12
+ * `formatting.revision_display.mode_changed` (low-frequency signals).
13
+ * - Bus-level `isEnabled("formatting")` short-circuit so the callback
14
+ * returns immediately when the channel is off, preserving the
15
+ * "zero cost when off" contract.
16
+ *
17
+ * The bridge lives in the formatting layer so it can stay colocated with
18
+ * the telemetry event type. The telemetry bus `TelemetryBus` is imported
19
+ * by type only; there is no value dependency on layer 02's debug
20
+ * infrastructure at runtime.
21
+ *
22
+ * Performance: each call is a channel-enabled check (O(1)) + an optional
23
+ * Math.random for sampled events. `bus.emitLazy` is used for sampled
24
+ * events so the payload factory runs only on hit.
25
+ */
26
+
27
+ import type { TelemetryBus } from "../debug/telemetry-bus.ts";
28
+ import { shouldEmitSample } from "../debug/probability-sampler.ts";
29
+ import type { FormattingTelemetryEvent } from "./resolve-effective.ts";
30
+
31
+ export interface FormattingTelemetryBridgeOptions {
32
+ /** Probability gate for `formatting.resolution_sampled` events.
33
+ * Default `0.01` (1%). Set to 0 to disable sample emission entirely. */
34
+ readonly resolutionSampleRate?: number;
35
+ }
36
+
37
+ const DEFAULT_RESOLUTION_SAMPLE_RATE = 0.01;
38
+
39
+ /**
40
+ * Build an `emitFormattingTelemetry` callback suitable for passing to
41
+ * `resolveEffectiveFormatting({ ..., emitFormattingTelemetry })`. Wraps
42
+ * the runtime telemetry bus with probability-gated sampling for the
43
+ * high-frequency event.
44
+ */
45
+ export function buildFormattingTelemetryBridge(
46
+ bus: TelemetryBus,
47
+ options: FormattingTelemetryBridgeOptions = {},
48
+ ): (event: FormattingTelemetryEvent) => void {
49
+ const sampleRate = options.resolutionSampleRate ?? DEFAULT_RESOLUTION_SAMPLE_RATE;
50
+
51
+ return function emit(event: FormattingTelemetryEvent): void {
52
+ if (!bus.isEnabled("formatting")) return;
53
+
54
+ switch (event.type) {
55
+ case "formatting.resolution_sampled": {
56
+ if (!shouldEmitSample(sampleRate)) return;
57
+ bus.emit({
58
+ channel: "formatting",
59
+ type: event.type,
60
+ t: 0,
61
+ payload: event.payload,
62
+ });
63
+ return;
64
+ }
65
+ case "formatting.cache.invalidated": {
66
+ bus.emit({
67
+ channel: "formatting",
68
+ type: event.type,
69
+ t: 0,
70
+ payload: event.payload,
71
+ });
72
+ return;
73
+ }
74
+ case "formatting.numbering_format_unsupported": {
75
+ // Low-frequency fidelity-gap signal (dedup happens at the emit
76
+ // site — see surface-projection). No sampling; every hit matters.
77
+ bus.emit({
78
+ channel: "formatting",
79
+ type: event.type,
80
+ t: 0,
81
+ payload: event.payload,
82
+ });
83
+ return;
84
+ }
85
+ }
86
+ };
87
+ }
88
+
89
+ /**
90
+ * Narrow helper for emitting the revision-display mode-change event.
91
+ * Consumers (render-kernel, surface-projection) call this directly when
92
+ * the active markup mode toggles — it's a state-change event, not per
93
+ * resolution.
94
+ */
95
+ export function emitRevisionDisplayModeChanged(
96
+ bus: TelemetryBus,
97
+ mode: "clean" | "simple" | "all",
98
+ ): void {
99
+ if (!bus.isEnabled("formatting")) return;
100
+ bus.emit({
101
+ channel: "formatting",
102
+ type: "formatting.revision_display.mode_changed",
103
+ t: 0,
104
+ payload: { mode },
105
+ });
106
+ }
@@ -24,9 +24,8 @@
24
24
  * reference byte-for-byte.
25
25
  */
26
26
 
27
- import type { CanonicalRunFormatting, CanonicalTheme, ClrSchemeMappingSlot, ResolvedTheme } from "../model/canonical-document.ts";
28
- import { resolveThemeColor } from "../io/ooxml/parse-theme.ts";
29
- import { GRADIENT_STOP_UNITS } from "./units.ts";
27
+ import type { CanonicalRunFormatting, CanonicalTheme, ClrSchemeMappingSlot } from "../../model/canonical-document.ts";
28
+ import { GRADIENT_STOP_UNITS } from "../units.ts";
30
29
 
31
30
  /**
32
31
  * DrawingML color modifier (ECMA-376 §20.1.2.3.x).
@@ -98,33 +97,6 @@ export class ThemeColorResolver {
98
97
  }
99
98
  }
100
99
 
101
- /**
102
- * Collapse `<w:color>`-style theme-slot + tint + shade references into a
103
- * single resolved hex colour. §17-only byte-form path; does NOT apply
104
- * `w:clrSchemeMapping` remap.
105
- *
106
- * @deprecated Use `ThemeColorResolver.resolveWordThemeColor` instead —
107
- * it honours clrSchemeMapping. This free function is retained only for
108
- * its targeted unit tests (`test/io/theme-color-tint-shade.test.ts`) and
109
- * will be removed once those migrate to the class.
110
- */
111
- export function resolveThemeColorHex(
112
- rPr: Pick<
113
- CanonicalRunFormatting,
114
- "colorHex" | "colorThemeSlot" | "colorThemeTint" | "colorThemeShade"
115
- >,
116
- theme: ResolvedTheme | undefined,
117
- ): string | undefined {
118
- if (rPr.colorHex === "auto") return "auto";
119
- if (rPr.colorHex) return rPr.colorHex;
120
- if (!rPr.colorThemeSlot) return undefined;
121
-
122
- const baseHex = resolveThemeColor(theme, rPr.colorThemeSlot);
123
- if (!baseHex) return undefined;
124
-
125
- return applyThemeTintShade(baseHex, rPr.colorThemeTint, rPr.colorThemeShade);
126
- }
127
-
128
100
  /**
129
101
  * Post-process a cascade of run formatting to concretize any
130
102
  * theme-color-only reference into a paintable `colorHex`.
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Layer 05 — Caret + selection-rect geometry.
3
+ *
4
+ * Pure projections against a `RenderFrame`:
5
+ *
6
+ * - `resolveCaretGeometry(frame, offset, story?)` → `CaretGeometry | null`
7
+ * Returns the caret position + line height + baseline + writing
8
+ * direction at `offset`, projected to frame-local pixels.
9
+ * - `resolveSelectionRects(frame, { from, to, story? })`
10
+ * Returns one `GeometryRect` per line the selection covers when
11
+ * per-line data is available, or the kernel's union selection rect
12
+ * otherwise. Empty range collapses to a zero-width caret rect;
13
+ * off-document ranges return `[]`.
14
+ *
15
+ * Slice-4 substrate notes:
16
+ *
17
+ * - The primary source is `frame.anchorIndex` — the render kernel's
18
+ * anchor lookup is populated for every layout-produced offset even
19
+ * when per-line `RenderLine[]` arrays are empty (which they are for
20
+ * synthetic docs with no measurement provider). `byRuntimeOffset`
21
+ * returns a rect shaped like "the line at that offset", which gives
22
+ * the caret its correct top + height. When per-line anchors DO
23
+ * populate (Slice 5 with richer line projection), selection-rect
24
+ * enumeration upgrades to one-rect-per-line automatically.
25
+ * - **RTL + bidi** is reserved. `direction` is always `"ltr"` in this
26
+ * slice because the render kernel does not yet expose run-level
27
+ * writing-direction metadata. The `CaretGeometry.direction` field
28
+ * is stable so a future slice can fill in RTL without a consumer-
29
+ * visible API change.
30
+ * - **Empty blocks** snap to the block's left edge at the full line
31
+ * height — the Word-compatible behavior. `byRuntimeOffset` on an
32
+ * offset inside an empty paragraph returns the paragraph's rect;
33
+ * the caret lives at that rect's leftPx.
34
+ */
35
+
36
+ import type { EditorStoryTarget } from "../../api/public-types";
37
+ import type {
38
+ RenderFrame,
39
+ RenderFrameRect,
40
+ } from "../render/index.ts";
41
+ import type {
42
+ CaretGeometry,
43
+ GeometryRect,
44
+ } from "./geometry-types.ts";
45
+
46
+ /**
47
+ * Resolve the caret for `offset`, or `null` if the offset falls outside
48
+ * the frame's resolvable range. Slice 4 routes through
49
+ * `frame.anchorIndex.byRuntimeOffset`; Slice 5 tightens x-position
50
+ * within a line once per-run anchors land.
51
+ */
52
+ export function resolveCaretGeometry(
53
+ frame: RenderFrame | null,
54
+ offset: number,
55
+ story?: EditorStoryTarget,
56
+ ): CaretGeometry | null {
57
+ if (!frame) return null;
58
+ if (!Number.isFinite(offset)) return null;
59
+
60
+ const anchorRect = frame.anchorIndex.byRuntimeOffset(offset, story);
61
+ if (!anchorRect) return null;
62
+
63
+ const rect: GeometryRect = {
64
+ leftPx: anchorRect.leftPx,
65
+ topPx: anchorRect.topPx,
66
+ widthPx: 0,
67
+ heightPx: anchorRect.heightPx,
68
+ space: "frame",
69
+ };
70
+
71
+ // Slice 7b (2026-04-22): `direction` is hardcoded "ltr" and
72
+ // `baseline` uses a 0.8 * height heuristic. Both are substrate
73
+ // placeholders pending the render-kernel's per-run anchor work
74
+ // (refactor/05 Slice 7, Task 2). Tag the coarsest precision so
75
+ // consumers can gate bidi-sensitive work.
76
+ return {
77
+ rect,
78
+ baseline: resolveBaselinePx(anchorRect),
79
+ height: anchorRect.heightPx,
80
+ direction: "ltr", // reserved for a future bidi-aware slice.
81
+ precision: "heuristic",
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Resolve `GeometryRect[]` for a selection range. Slice 4 produces:
87
+ * - an empty list when either endpoint can't be resolved
88
+ * - a single zero-width caret rect when `from === to`
89
+ * - a single union rect otherwise (via `frame.anchorIndex.bySelection`)
90
+ *
91
+ * When the kernel's per-line anchor projection matures (Slice 5+), this
92
+ * function flips to emitting one rect per line. The single-rect fallback
93
+ * keeps chrome surfaces that currently call `getAnchorRects({ kind:
94
+ * "runtime-offset", … })[0]` in sync with the new selection API.
95
+ */
96
+ export function resolveSelectionRects(
97
+ frame: RenderFrame | null,
98
+ range: {
99
+ from: number;
100
+ to: number;
101
+ story?: EditorStoryTarget;
102
+ },
103
+ ): readonly GeometryRect[] {
104
+ if (!frame) return [];
105
+ const { from, to } = range;
106
+ if (!Number.isFinite(from) || !Number.isFinite(to)) return [];
107
+ const lo = Math.min(from, to);
108
+ const hi = Math.max(from, to);
109
+
110
+ if (lo === hi) {
111
+ const caret = resolveCaretGeometry(frame, lo, range.story);
112
+ if (!caret) return [];
113
+ // Caret rect for a collapsed range is an exact anchor-index read —
114
+ // the placeholder-grade metadata on `CaretGeometry` (direction,
115
+ // baseline) applies to the caret as a whole, not to the rect's
116
+ // position. Return the rect untagged so callers that iterate
117
+ // `getSelectionRects(...)` and expect pixel-exact carets see the
118
+ // expected `"exact"` default.
119
+ return [caret.rect];
120
+ }
121
+
122
+ const union = frame.anchorIndex.bySelection(lo, hi, range.story);
123
+ if (!union) return [];
124
+ // Slice 7b (2026-04-22): the substrate returns ONE union rect for any
125
+ // non-collapsed range. That rect is geometrically correct at the
126
+ // block level but loses per-line detail — tag it `within-tolerance`
127
+ // so callers can gate per-line chrome work. When the render kernel
128
+ // ships per-run anchors (refactor/05 Slice 7 Task 2), this site
129
+ // upgrades to multiple `"exact"` rects automatically.
130
+ return [toGeometryRect(union, "within-tolerance")];
131
+ }
132
+
133
+ // ---------------------------------------------------------------------------
134
+ // Internals
135
+ // ---------------------------------------------------------------------------
136
+
137
+ function toGeometryRect(
138
+ rect: RenderFrameRect,
139
+ precision?: GeometryRect["precision"],
140
+ ): GeometryRect {
141
+ const out: GeometryRect = {
142
+ leftPx: rect.leftPx,
143
+ topPx: rect.topPx,
144
+ widthPx: rect.widthPx,
145
+ heightPx: rect.heightPx,
146
+ space: "frame",
147
+ };
148
+ if (precision !== undefined) {
149
+ out.precision = precision;
150
+ }
151
+ return out;
152
+ }
153
+
154
+ /**
155
+ * Approximate the baseline offset within a line rect. The render frame
156
+ * does not yet carry per-anchor baseline metadata, so Slice 4 uses a
157
+ * fixed 0.8× ratio — the typical cap-height heuristic for Latin scripts.
158
+ * A later slice replaces this with the per-line `baselineTwips` once
159
+ * per-line anchors are populated.
160
+ */
161
+ function resolveBaselinePx(rect: RenderFrameRect): number {
162
+ if (rect.heightPx <= 0) return 0;
163
+ return rect.heightPx * 0.8;
164
+ }