@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,102 @@
1
+ /**
2
+ * Slice 6 — `ai.attachExplanation` live-with-adapter primitive.
3
+ *
4
+ * Attaches an agent-authored explanation to a semantic scope by routing
5
+ * through Layer-06's `attachScopeMetadata` writer — the same pipeline
6
+ * `runtime.workflow.attachMetadata` graduated against. The explanation
7
+ * persists as a `WorkflowMetadataEntry` with a stable `metadataId`
8
+ * prefix (`ai.explanation`) and the caller-supplied text +
9
+ * explanationId in `value`.
10
+ *
11
+ * Determinism (S3): given identical (scopeId, explanationText,
12
+ * explanationId) inputs, the emitted entry is identical. The
13
+ * explanationId + createdAtUtc are caller-supplied so consumers that
14
+ * want deterministic output (tests, replay) can pin them.
15
+ *
16
+ * Scope coverage (coord-06 §13c shipped 2026-04-23): both overlay
17
+ * (workflow) scopes AND derived paragraph / heading / list-item
18
+ * scopes are valid targets. Derived scopeIds (e.g. `para:<paraId>`,
19
+ * `heading:<paraId>`, `li:<paraId>`) resolve against the canonical
20
+ * document via `resolveDerivedScopeAnchorFromCanonical`; the stored
21
+ * entry's anchor is the resolver's range output, and the result
22
+ * carries `resolvedFrom: "derived"`. Non-paragraph-like scopeIds
23
+ * (fields, comment threads, revisions, table-kind) still return
24
+ * `attached: false`.
25
+ */
26
+
27
+ import type {
28
+ AttachScopeMetadataResult,
29
+ MetadataWriterRuntime,
30
+ } from "../workflow/metadata-writer.ts";
31
+ import { attachScopeMetadata } from "../workflow/metadata-writer.ts";
32
+
33
+ /** Stable metadataId prefix for AI-attached explanations. */
34
+ export const AI_EXPLANATION_METADATA_ID = "ai.explanation" as const;
35
+
36
+ export interface AttachExplanationInput {
37
+ readonly scopeId: string;
38
+ readonly explanation: string;
39
+ /** Optional stable id — defaults to `explanation-<scopeId>` when omitted. */
40
+ readonly explanationId?: string;
41
+ /** Optional ISO UTC stamp — defaults to `new Date(0).toISOString()` for determinism. */
42
+ readonly createdAtUtc?: string;
43
+ }
44
+
45
+ export type AttachExplanationResult =
46
+ | {
47
+ readonly attached: true;
48
+ readonly explanationId: string;
49
+ readonly entryId: string;
50
+ /**
51
+ * Coord-06 §13c — `"overlay"` when the scope was on the workflow
52
+ * overlay, `"derived"` when it was a paragraph / heading /
53
+ * list-item scopeId resolved from the canonical document.
54
+ */
55
+ readonly resolvedFrom: "overlay" | "derived";
56
+ }
57
+ | {
58
+ readonly attached: false;
59
+ readonly explanationId: string;
60
+ /**
61
+ * `scope-not-resolvable:<scopeId>` — the scopeId is neither on
62
+ * the workflow overlay nor a paragraph-like derived id the
63
+ * canonical walk can resolve. Pre-§13c this was always
64
+ * `scope-not-in-overlay`; the broader prefix reflects the
65
+ * post-§13c two-stage resolution.
66
+ */
67
+ readonly reason: string;
68
+ };
69
+
70
+ export function attachExplanation(
71
+ runtime: MetadataWriterRuntime,
72
+ input: AttachExplanationInput,
73
+ ): AttachExplanationResult {
74
+ const explanationId = input.explanationId ?? `explanation-${input.scopeId}`;
75
+ const createdAtUtc = input.createdAtUtc ?? new Date(0).toISOString();
76
+ const writerResult: AttachScopeMetadataResult = attachScopeMetadata(runtime, {
77
+ scopeId: input.scopeId,
78
+ metadataId: AI_EXPLANATION_METADATA_ID,
79
+ value: {
80
+ explanationId,
81
+ text: input.explanation,
82
+ createdAtUtc,
83
+ },
84
+ // `inherit` persistence is the right default: explanations that
85
+ // need to export through to docx do so via the standard
86
+ // workflow-metadata export path; callers wanting `external`
87
+ // (side-channel only) pass through `attachScopeMetadata` directly.
88
+ });
89
+ if (writerResult.status === "attached") {
90
+ return {
91
+ attached: true,
92
+ explanationId,
93
+ entryId: writerResult.entryId,
94
+ resolvedFrom: writerResult.resolvedFrom,
95
+ };
96
+ }
97
+ return {
98
+ attached: false,
99
+ explanationId,
100
+ reason: `scope-not-resolvable:${writerResult.scopeId}`,
101
+ };
102
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Slice 5 — `ScopeActionAudit` producer + telemetry emission.
3
+ *
4
+ * Every mutating replacement-scope action emits exactly one audit on the
5
+ * `scope` channel (S5 of layer-08 architecture). This module is the
6
+ * single producer — callers must not compose audit bundles locally and
7
+ * emit directly; they compose here so the shape + emission site stay
8
+ * honest.
9
+ */
10
+
11
+ import type { TelemetryBus } from "../debug/telemetry-bus.ts";
12
+ import type {
13
+ ReplacementScope,
14
+ RuntimeOperationPlan,
15
+ ScopeActionAudit,
16
+ SemanticScope,
17
+ ValidationResult,
18
+ } from "./semantic-scope-types.ts";
19
+
20
+ export interface EmitScopeActionAuditInputs {
21
+ readonly actionId: string;
22
+ readonly actorId: string;
23
+ readonly origin: "ui" | "agent" | "host";
24
+ readonly documentHashBefore: string;
25
+ readonly documentHashAfter?: string;
26
+ readonly targetScopeSnapshot: SemanticScope;
27
+ readonly proposed: ReplacementScope;
28
+ readonly plan: RuntimeOperationPlan;
29
+ readonly validation: ValidationResult;
30
+ readonly emittedAtUtc: string;
31
+ readonly bus?: TelemetryBus;
32
+ }
33
+
34
+ export function buildScopeActionAudit(
35
+ inputs: EmitScopeActionAuditInputs,
36
+ ): ScopeActionAudit {
37
+ return {
38
+ actionId: inputs.actionId,
39
+ actorId: inputs.actorId,
40
+ origin: inputs.origin,
41
+ documentHashBefore: inputs.documentHashBefore,
42
+ ...(inputs.documentHashAfter
43
+ ? { documentHashAfter: inputs.documentHashAfter }
44
+ : {}),
45
+ targetScopeSnapshot: inputs.targetScopeSnapshot,
46
+ proposed: inputs.proposed,
47
+ compiledOperations: Object.freeze(
48
+ inputs.plan.steps.map((step) => ({
49
+ kind: step.kind,
50
+ summary: step.summary,
51
+ })),
52
+ ),
53
+ validation: inputs.validation,
54
+ emittedAtUtc: inputs.emittedAtUtc,
55
+ };
56
+ }
57
+
58
+ /**
59
+ * Build + emit one audit event on the `scope` telemetry channel. Returns
60
+ * the bundle so callers can surface it in their apply-result response.
61
+ */
62
+ export function emitScopeActionAudit(
63
+ inputs: EmitScopeActionAuditInputs,
64
+ ): ScopeActionAudit {
65
+ const audit = buildScopeActionAudit(inputs);
66
+ inputs.bus?.emitLazy("scope", () => ({
67
+ type: "scope.action_audit",
68
+ payload: audit,
69
+ }));
70
+ return audit;
71
+ }
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Slice 3 — scope-bundle compiler.
3
+ *
4
+ * Wraps a compiled `SemanticScope` with its neighborhood (prev / next /
5
+ * parent / siblings) + evidence (review overlap, workflow overlap,
6
+ * formatting summary) into a single `ScopeBundle` sized for an agent turn
7
+ * budget (S7). The bundle is the primary unit an AI-API consumer reads.
8
+ *
9
+ * Cost: `neighborhoodFor` is O(n) per call. When composing many bundles
10
+ * over the same scope enumeration, pass `scopes` through explicitly so the
11
+ * enumeration runs once. A scope-id → index cache is a Slice 4+ follow-up.
12
+ */
13
+
14
+ import type { CanonicalDocument, StylesCatalog } from "../../model/canonical-document.ts";
15
+ import type { CanonicalDocumentEnvelope } from "../../core/state/editor-state.ts";
16
+ import type {
17
+ WorkflowMetadataEntry,
18
+ WorkflowOverlay,
19
+ } from "./_scope-dependencies.ts";
20
+
21
+ import { buildParagraphIndexMap, compileScope } from "./compile-scope.ts";
22
+ import type { EnumeratedScope } from "./enumerate-scopes.ts";
23
+ import { enumerateScopes } from "./enumerate-scopes.ts";
24
+ import { composeEvidence } from "./evidence.ts";
25
+ import type {
26
+ ScopeBundle,
27
+ ScopeBundleNeighborhood,
28
+ SemanticScope,
29
+ } from "./semantic-scope-types.ts";
30
+
31
+ export interface ScopeBundleInputs {
32
+ readonly document: Pick<CanonicalDocument, "content" | "docId" | "styles" | "review"> | CanonicalDocumentEnvelope;
33
+ readonly overlay?: WorkflowOverlay | null;
34
+ readonly nowUtc: string;
35
+ /** Pre-enumerated scopes; computed when absent. */
36
+ readonly scopes?: readonly EnumeratedScope[];
37
+ /**
38
+ * Workflow-metadata entries to project into the bundle's evidence as
39
+ * `aiExplanations` + `aiIssues`. When absent, those fields are
40
+ * omitted. Closes the adversarial-close read-side join: explanations
41
+ * + issues written via `ai.attachExplanation` / `ai.createIssue`
42
+ * become readable through `getScopeBundle` without the caller having
43
+ * to walk the metadata snapshot.
44
+ */
45
+ readonly workflowMetadataEntries?: readonly WorkflowMetadataEntry[];
46
+ }
47
+
48
+ /**
49
+ * S7 cap — the bundle is sized for an agent turn, so we clamp the
50
+ * `siblingScopeIds` list to avoid O(n) bloat on top-level paragraphs in a
51
+ * 500-scope document. Prev/next always carry the immediate neighbors; the
52
+ * sibling list is truncated lexicographically after the self-position.
53
+ */
54
+ const SIBLING_CAP = 20;
55
+
56
+ function neighborhoodFor(
57
+ handle: SemanticScope["handle"],
58
+ scopes: readonly EnumeratedScope[],
59
+ ): ScopeBundleNeighborhood {
60
+ const siblings: string[] = [];
61
+ let previousScopeId: string | undefined;
62
+ let nextScopeId: string | undefined;
63
+
64
+ const parentScopeId = handle.parentScopeId;
65
+ const parentKey = parentScopeId ?? null;
66
+
67
+ for (let i = 0; i < scopes.length; i += 1) {
68
+ const entry = scopes[i]!;
69
+ if (entry.handle.scopeId === handle.scopeId) {
70
+ for (let p = i - 1; p >= 0; p -= 1) {
71
+ const cand = scopes[p]!;
72
+ if ((cand.handle.parentScopeId ?? null) === parentKey) {
73
+ previousScopeId = cand.handle.scopeId;
74
+ break;
75
+ }
76
+ }
77
+ for (let n = i + 1; n < scopes.length; n += 1) {
78
+ const cand = scopes[n]!;
79
+ if ((cand.handle.parentScopeId ?? null) === parentKey) {
80
+ nextScopeId = cand.handle.scopeId;
81
+ break;
82
+ }
83
+ }
84
+ continue;
85
+ }
86
+ if (
87
+ (entry.handle.parentScopeId ?? null) === parentKey &&
88
+ entry.handle.scopeId !== handle.scopeId
89
+ ) {
90
+ if (siblings.length < SIBLING_CAP) {
91
+ siblings.push(entry.handle.scopeId);
92
+ }
93
+ }
94
+ }
95
+
96
+ return {
97
+ ...(previousScopeId ? { previousScopeId } : {}),
98
+ ...(nextScopeId ? { nextScopeId } : {}),
99
+ ...(parentScopeId ? { parentScopeId } : {}),
100
+ ...(siblings.length > 0 ? { siblingScopeIds: siblings } : {}),
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Compile a scope bundle for one `SemanticScope`. Callers typically
106
+ * produce `scope` via `compileScope(entry, catalog)` and thread the same
107
+ * `document` + `overlay` through here.
108
+ */
109
+ export function compileScopeBundle(
110
+ scope: SemanticScope,
111
+ inputs: ScopeBundleInputs,
112
+ ): ScopeBundle {
113
+ const scopes =
114
+ inputs.scopes ??
115
+ enumerateScopes(inputs.document, { overlay: inputs.overlay ?? null });
116
+ const neighborhood = neighborhoodFor(scope.handle, scopes);
117
+ const evidence = composeEvidence({
118
+ scope,
119
+ document: inputs.document,
120
+ overlay: inputs.overlay,
121
+ scopes,
122
+ ...(inputs.workflowMetadataEntries
123
+ ? { workflowMetadataEntries: inputs.workflowMetadataEntries }
124
+ : {}),
125
+ });
126
+ return {
127
+ scope,
128
+ neighborhood,
129
+ evidence,
130
+ generatedAtUtc: inputs.nowUtc,
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Convenience wrapper — resolves a scope by id, compiles it, and returns
136
+ * its bundle. Returns null when the scope id does not enumerate.
137
+ *
138
+ * The `document` on `inputs` is the primary Layer 03 cascade input — when
139
+ * present, the compiler delegates formatting resolution to Layer 03 (see
140
+ * `compileScope` + `_formatting-seam.ts`). A bare `catalog` remains
141
+ * accepted for back-compat but is ignored by the seam.
142
+ */
143
+ export function compileScopeBundleById(
144
+ scopeId: string,
145
+ inputs: ScopeBundleInputs & { readonly catalog?: StylesCatalog },
146
+ ): ScopeBundle | null {
147
+ const scopes =
148
+ inputs.scopes ??
149
+ enumerateScopes(inputs.document, { overlay: inputs.overlay ?? null });
150
+ const entry = scopes.find((e) => e.handle.scopeId === scopeId);
151
+ if (!entry) return null;
152
+ const fullDoc =
153
+ "styles" in inputs.document && "content" in inputs.document
154
+ ? (inputs.document as CanonicalDocument)
155
+ : undefined;
156
+ const compiled = compileScope(entry, {
157
+ ...(fullDoc ? { document: fullDoc } : {}),
158
+ ...(inputs.overlay !== undefined ? { overlay: inputs.overlay } : {}),
159
+ paragraphIndexByBlockIndex: buildParagraphIndexMap(inputs.document),
160
+ });
161
+ if (!compiled) return null;
162
+ return compileScopeBundle(compiled, { ...inputs, scopes });
163
+ }
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Scope compile dispatcher.
3
+ *
4
+ * Takes an `EnumeratedScope` and dispatches to the matching kind compiler.
5
+ * Callers may thread a `CompileScopeOptions` bundle carrying:
6
+ *
7
+ * - `document` — full canonical document; enables Layer 03
8
+ * `resolveEffectiveFormatting` delegation for
9
+ * paragraph-like kinds (paragraphStyleId / outlineLevel
10
+ * / numbering.label honour the style cascade).
11
+ * - `overlay` — workflow overlay; the dispatcher computes overlay-
12
+ * scope overlap and threads `SemanticScopeWorkflow`
13
+ * into every paragraph-like and table-like compile.
14
+ * Identity-posture kinds (field, comment-thread,
15
+ * revision) override this in their kind file because
16
+ * the kind's identity implies its own mode.
17
+ * - `paragraphIndexByBlockIndex` — map from the all-children
18
+ * `blockIndex` used by the scope walker to the
19
+ * paragraph-type index Layer 03's synthesis uses
20
+ * (`paragraph-${N}`). Built once per document by
21
+ * `buildParagraphIndexMap`.
22
+ * - `positionMap` — precomputed `ScopePositionMap`. Reused across
23
+ * all compiles in a single pass so we only walk the
24
+ * document once for offset arithmetic.
25
+ *
26
+ * Kinds not yet implemented (note, image, scope, composite — 4 of 13)
27
+ * return `null`; the caller decides how to handle. `enumerate-scopes`
28
+ * already skips emitting these, so the null branch is a belt-and-braces
29
+ * guard for forward compatibility.
30
+ *
31
+ * Back-compat: the legacy `(entry, catalog)` signature still compiles —
32
+ * a bare `StylesCatalog` is accepted and ignored (it carried no cascade
33
+ * information the seam consumes directly). Callers are encouraged to
34
+ * migrate to `(entry, options)` to get Layer 03 cascade-accurate output.
35
+ */
36
+
37
+ import type {
38
+ CanonicalDocument,
39
+ StylesCatalog,
40
+ } from "../../model/canonical-document.ts";
41
+ import type { WorkflowOverlay } from "./_scope-dependencies.ts";
42
+ import type {
43
+ CanonicalDocumentEnvelope,
44
+ } from "../../core/state/editor-state.ts";
45
+
46
+ import type { EnumeratedScope } from "./enumerate-scopes.ts";
47
+ import {
48
+ buildScopePositionMap,
49
+ type ScopePositionMap,
50
+ } from "./position-map.ts";
51
+ import { resolveScopeRange } from "./scope-range.ts";
52
+ import type {
53
+ SemanticScope,
54
+ SemanticScopeWorkflow,
55
+ } from "./semantic-scope-types.ts";
56
+ import { resolveWorkflowOverlap } from "./workflow-overlap.ts";
57
+
58
+ import { compileCommentThreadScope } from "./scope-kinds/comment-thread.ts";
59
+ import { compileFieldScope } from "./scope-kinds/field.ts";
60
+ import { compileHeadingScope } from "./scope-kinds/heading.ts";
61
+ import { compileListItemScope } from "./scope-kinds/list-item.ts";
62
+ import { compileParagraphScope } from "./scope-kinds/paragraph.ts";
63
+ import { compileRevisionScope } from "./scope-kinds/revision.ts";
64
+ import { compileTableScope } from "./scope-kinds/table.ts";
65
+ import { compileTableCellScope } from "./scope-kinds/table-cell.ts";
66
+ import { compileTableRowScope } from "./scope-kinds/table-row.ts";
67
+
68
+ export interface CompileScopeOptions {
69
+ readonly document?: CanonicalDocument;
70
+ readonly overlay?: WorkflowOverlay | null;
71
+ readonly paragraphIndexByBlockIndex?: ReadonlyMap<number, number>;
72
+ /**
73
+ * `blockIndex → sectionIndex` precomputed via
74
+ * `buildSectionIndexByBlockIndex`. When present, paragraph-like kinds
75
+ * project `layout.sectionIndex` (coord-08 §12). When absent, the
76
+ * dispatcher computes it lazily from `options.document`. Callers that
77
+ * compile many scopes against the same document should hoist.
78
+ */
79
+ readonly sectionIndexByBlockIndex?: ReadonlyMap<number, number>;
80
+ readonly positionMap?: ScopePositionMap;
81
+ }
82
+
83
+ function isCompileScopeOptions(
84
+ v: unknown,
85
+ ): v is CompileScopeOptions {
86
+ if (v === null || v === undefined) return true;
87
+ if (typeof v !== "object") return false;
88
+ return (
89
+ "document" in v ||
90
+ "overlay" in v ||
91
+ "paragraphIndexByBlockIndex" in v ||
92
+ "sectionIndexByBlockIndex" in v ||
93
+ "positionMap" in v
94
+ );
95
+ }
96
+
97
+ /**
98
+ * Walk the document once to build a `blockIndex → sectionIndex` map.
99
+ * Section index is derived by counting `section_break` blocks that
100
+ * appear BEFORE the current block — every `section_break` advances the
101
+ * counter for the next block, matching OOXML's "blocks belong to the
102
+ * section whose properties live at the *next* section_break (or
103
+ * `finalSectionProperties` for the trailing section)" convention.
104
+ *
105
+ * Blocks before the first `section_break` report `sectionIndex = 0`.
106
+ * Callers use this for `SemanticScopeLayout.sectionIndex` projection
107
+ * (coord-08 §12). L04's `page-graph` carries its own per-page section
108
+ * indexing; this helper stays on L08's side because the per-block
109
+ * answer is a pure canonical-tree walk that doesn't require the
110
+ * layout facet.
111
+ */
112
+ export function buildSectionIndexByBlockIndex(
113
+ document:
114
+ | Pick<CanonicalDocument, "content">
115
+ | CanonicalDocumentEnvelope,
116
+ ): Map<number, number> {
117
+ const envelope = document as CanonicalDocumentEnvelope;
118
+ const root =
119
+ "content" in envelope
120
+ ? envelope.content
121
+ : (document as unknown as { children: readonly { type: string }[] });
122
+ const out = new Map<number, number>();
123
+ let sectionIndex = 0;
124
+ for (let i = 0; i < root.children.length; i += 1) {
125
+ out.set(i, sectionIndex);
126
+ const child = root.children[i];
127
+ if (child && child.type === "section_break") {
128
+ sectionIndex += 1;
129
+ }
130
+ }
131
+ return out;
132
+ }
133
+
134
+ /**
135
+ * Walk the document once to build a `blockIndex → paragraphIndex` map.
136
+ * Paragraph-type indexing matches Layer 03's `paragraph-${N}` blockId
137
+ * synthesis (see `src/runtime/formatting/document-lookup.ts`). Callers
138
+ * that compile many scopes over the same document should build this
139
+ * once and reuse.
140
+ */
141
+ export function buildParagraphIndexMap(
142
+ document:
143
+ | Pick<CanonicalDocument, "content">
144
+ | CanonicalDocumentEnvelope,
145
+ ): Map<number, number> {
146
+ const envelope = document as CanonicalDocumentEnvelope;
147
+ const root =
148
+ "content" in envelope
149
+ ? envelope.content
150
+ : (document as unknown as { children: readonly { type: string }[] });
151
+ const out = new Map<number, number>();
152
+ let paragraphIndex = 0;
153
+ for (let i = 0; i < root.children.length; i += 1) {
154
+ const child = root.children[i];
155
+ if (child && child.type === "paragraph") {
156
+ out.set(i, paragraphIndex);
157
+ paragraphIndex += 1;
158
+ }
159
+ }
160
+ return out;
161
+ }
162
+
163
+ function workflowFor(
164
+ entry: EnumeratedScope,
165
+ overlay: WorkflowOverlay | null | undefined,
166
+ positionMap: ScopePositionMap | undefined,
167
+ ): SemanticScopeWorkflow | undefined {
168
+ if (!overlay || !positionMap) return undefined;
169
+ // Review-store kinds carry identity-derived posture in their kind
170
+ // file (comment-thread = "comment", revision = "suggest"), so we
171
+ // skip the overlap path for them. All structural kinds — including
172
+ // marker-backed paragraphs that hosts classify as "clause" — get
173
+ // the proper overlap merge, which means outer `view` / `comment`
174
+ // scopes correctly override inner `edit` scopes.
175
+ const isIdentityKind =
176
+ entry.kind === "comment-thread" || entry.kind === "revision";
177
+ if (isIdentityKind) return undefined;
178
+ const range = resolveScopeRange(entry, entry.handle, positionMap);
179
+ return resolveWorkflowOverlap({
180
+ overlay,
181
+ positionMap,
182
+ range,
183
+ // Intentionally do NOT pass selfScopeIds — a marker-backed scope's
184
+ // own overlay entry carries the authoritative mode for its own
185
+ // range, and outer overlap scopes merge on top via the most-
186
+ // restrictive rule. Filtering self would drop the scope's own
187
+ // mode signal and misrepresent its posture.
188
+ });
189
+ }
190
+
191
+ export function compileScope(
192
+ entry: EnumeratedScope,
193
+ optionsOrCatalog?: CompileScopeOptions | StylesCatalog,
194
+ ): SemanticScope | null {
195
+ const options: CompileScopeOptions = isCompileScopeOptions(optionsOrCatalog)
196
+ ? (optionsOrCatalog ?? {})
197
+ : {};
198
+ const blockIndex = "blockIndex" in entry ? entry.blockIndex : -1;
199
+ const paragraphIndex =
200
+ options.paragraphIndexByBlockIndex?.get(blockIndex);
201
+ // Lazy section-index map: build once per dispatcher call when the
202
+ // caller didn't hoist + we have a document to walk. Derived scopes
203
+ // without a blockIndex (e.g. comment-thread/revision identity kinds)
204
+ // skip the projection via the `blockIndex >= 0` check below.
205
+ let sectionIndexMap = options.sectionIndexByBlockIndex;
206
+ if (!sectionIndexMap && options.document && blockIndex >= 0) {
207
+ sectionIndexMap = buildSectionIndexByBlockIndex(options.document);
208
+ }
209
+ const sectionIndex =
210
+ blockIndex >= 0 ? sectionIndexMap?.get(blockIndex) : undefined;
211
+ const paragraphLikeOpts: {
212
+ document?: CanonicalDocument;
213
+ paragraphIndex?: number;
214
+ sectionIndex?: number;
215
+ } = {};
216
+ if (options.document) paragraphLikeOpts.document = options.document;
217
+ if (typeof paragraphIndex === "number") {
218
+ paragraphLikeOpts.paragraphIndex = paragraphIndex;
219
+ }
220
+ if (typeof sectionIndex === "number") {
221
+ paragraphLikeOpts.sectionIndex = sectionIndex;
222
+ }
223
+ // Compute positionMap + workflow only when overlay is threaded. This
224
+ // keeps hot-path unit tests (without overlay) allocation-free.
225
+ let positionMap = options.positionMap;
226
+ if (!positionMap && options.overlay && options.document) {
227
+ positionMap = buildScopePositionMap(options.document);
228
+ }
229
+ const workflow = workflowFor(entry, options.overlay, positionMap);
230
+
231
+ switch (entry.kind) {
232
+ case "paragraph":
233
+ return compileParagraphScope(entry, {
234
+ ...paragraphLikeOpts,
235
+ ...(workflow ? { workflow } : {}),
236
+ });
237
+ case "heading":
238
+ return compileHeadingScope(entry, {
239
+ ...paragraphLikeOpts,
240
+ ...(workflow ? { workflow } : {}),
241
+ });
242
+ case "list-item":
243
+ return compileListItemScope(entry, {
244
+ ...paragraphLikeOpts,
245
+ ...(workflow ? { workflow } : {}),
246
+ });
247
+ case "table":
248
+ return compileTableScope(entry, workflow ? { workflow } : {});
249
+ case "table-row":
250
+ return compileTableRowScope(entry, workflow ? { workflow } : {});
251
+ case "table-cell":
252
+ return compileTableCellScope(entry, workflow ? { workflow } : {});
253
+ case "field":
254
+ return compileFieldScope(entry);
255
+ case "comment-thread":
256
+ return compileCommentThreadScope(entry);
257
+ case "revision":
258
+ return compileRevisionScope(entry);
259
+ default:
260
+ return null;
261
+ }
262
+ }