@beyondwork/docx-react-component 1.0.67 → 1.0.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (384) hide show
  1. package/README.md +75 -932
  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 -4797
  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
@@ -10,6 +10,7 @@ import {
10
10
  expectString,
11
11
  expectStringAllowEmpty,
12
12
  expectUuid,
13
+ isUuid,
13
14
  stableStringify,
14
15
  } from "./cds-1.0.0.ts";
15
16
  import type {
@@ -57,25 +58,132 @@ const ID_PATTERNS = {
57
58
  type StableIdDomain = keyof typeof ID_PATTERNS;
58
59
 
59
60
  export interface CanonicalDocument {
60
- schemaVersion: typeof CDS_SCHEMA_VERSION;
61
- docId: UUID;
62
- createdAt: ISO8601DateTime;
63
- updatedAt: ISO8601DateTime;
64
- metadata: DocumentMetadata;
65
- styles: StylesCatalog;
66
- numbering: NumberingCatalog;
67
- media: MediaCatalog;
68
- content: DocumentRootNode;
69
- review: ReviewStore;
70
- preservation: PreservationStore;
71
- diagnostics: DiagnosticStore;
72
- subParts?: SubPartsCatalog;
61
+ readonly schemaVersion: typeof CDS_SCHEMA_VERSION;
62
+ readonly docId: UUID;
63
+ readonly createdAt: ISO8601DateTime;
64
+ readonly updatedAt: ISO8601DateTime;
65
+ readonly metadata: DocumentMetadata;
66
+ readonly styles: StylesCatalog;
67
+ readonly numbering: NumberingCatalog;
68
+ readonly media: MediaCatalog;
69
+ readonly content: DocumentRootNode;
70
+ readonly review: ReviewStore;
71
+ readonly preservation: PreservationStore;
72
+ readonly diagnostics: DiagnosticStore;
73
+ readonly subParts?: SubPartsCatalog;
73
74
  /** Package-backed field registry for supported field families. */
74
- fieldRegistry?: FieldRegistry;
75
+ readonly fieldRegistry?: FieldRegistry;
75
76
  /** Parsed fontTable.xml. Undefined when the package has no fontTable part. */
76
- fontTable?: CanonicalFontTable;
77
+ readonly fontTable?: CanonicalFontTable;
77
78
  }
78
79
 
80
+ /**
81
+ * A detachable slice of canonical document content. The inverse of
82
+ * `DocumentRootNode` in the narrow sense — a fragment is block-level
83
+ * children that can be spliced into a target range on the main story or
84
+ * on any secondary story (header, footer, footnote, endnote) without
85
+ * carrying the enclosing envelope.
86
+ *
87
+ * Owned by Layer 02 because it is a shape of canonical content, not a
88
+ * layout or runtime concern. Consumed by the Layer 08 scope compiler's
89
+ * structured-replacement path (`paragraph.compileReplacement` with
90
+ * `proposedContent.kind === "structured"`) and by any future command
91
+ * that performs block-level splicing. The fragment does not carry its
92
+ * own catalogs / stores / registries — those are resolved against the
93
+ * destination document at splice time.
94
+ *
95
+ * Structural invariants:
96
+ * - Every `BlockNode` inside `blocks` is structurally valid in
97
+ * isolation (passes `validateDocumentNode` when walked).
98
+ * - The fragment carries no anchors to the source document; splicing
99
+ * is by target range supplied at apply time, not by fragment
100
+ * identity.
101
+ * - Styles / numbering / media referenced by inline children must
102
+ * resolve in the destination document; the fragment itself does not
103
+ * own catalog entries. A splice that introduces unresolved
104
+ * references is a caller error, not a fragment shape violation.
105
+ */
106
+ export interface CanonicalDocumentFragment {
107
+ /** Block-level children of the fragment, in document order. */
108
+ readonly blocks: ReadonlyArray<BlockNode>;
109
+ }
110
+
111
+ /**
112
+ * Draft-only mutable view of `CanonicalDocumentFragment`. Same
113
+ * semantics as {@link Mutable}. Used at fragment-construction sites;
114
+ * callers widen back to `CanonicalDocumentFragment` at the function
115
+ * boundary.
116
+ */
117
+ export type MutableCanonicalDocumentFragment = Mutable<CanonicalDocumentFragment>;
118
+
119
+ /**
120
+ * Strip every `readonly` modifier from an interface / type. The sanctioned
121
+ * "local draft → freeze" escape hatch for constructing a new canonical value:
122
+ * declare the draft as `Mutable<CanonicalType>`, populate it by in-place
123
+ * assignment, and return it widened back to the readonly type. Widening is
124
+ * identity at runtime; TypeScript preserves the immutability contract at the
125
+ * function boundary.
126
+ *
127
+ * Contract D2 (architecture §02) forbids consumers from mutating a
128
+ * `CanonicalDocument` or any nested canonical value received from elsewhere.
129
+ * This helper is the ONE acceptable cast for locally-owned drafts:
130
+ *
131
+ * function refreshStyles(doc: CanonicalDocument, fresh: StylesCatalog): CanonicalDocument {
132
+ * const draft: MutableCanonicalDocument = { ...doc };
133
+ * draft.styles = fresh;
134
+ * draft.updatedAt = new Date().toISOString();
135
+ * return draft; // widens back to CanonicalDocument
136
+ * }
137
+ *
138
+ * Never cast a value received as a parameter. Only cast values you just
139
+ * created via object literal, spread, `structuredClone`, or `createCanonicalDocument`.
140
+ *
141
+ * **Shallow only.** `Mutable<T>` strips `readonly` at the top level of `T`;
142
+ * nested `ReadonlyArray<…>`, `Readonly<…>`, and inner `readonly` modifiers
143
+ * declared inside referenced types are preserved. For example,
144
+ * `Mutable<CanonicalDocument>` does NOT make `draft.content.children`
145
+ * mutable — `content` is `DocumentRootNode`, whose `children` field is
146
+ * declared as it is in the source type. If you need to mutate nested
147
+ * structure, build the nested value first (also as a `Mutable<…>` of the
148
+ * nested type) and assign it onto the draft as a complete replacement.
149
+ * Deep-readonly propagation across nested catalogs/stores/nodes is a
150
+ * deferred D1 follow-up (see `docs/plans/refactor/02-canonical-document-deferred.md`).
151
+ */
152
+ export type Mutable<T> = { -readonly [K in keyof T]: T[K] };
153
+
154
+ /**
155
+ * Draft-only mutable view of `CanonicalDocument`. Equivalent to
156
+ * `Mutable<CanonicalDocument>`; kept as a named alias for discoverability at
157
+ * the most common call site. See {@link Mutable} for the general pattern and
158
+ * usage constraints.
159
+ */
160
+ export type MutableCanonicalDocument = Mutable<CanonicalDocument>;
161
+
162
+ /**
163
+ * Named `Mutable*` aliases for the top-level catalogs, stores, and
164
+ * registries referenced from `CanonicalDocument`. Same semantics as
165
+ * `Mutable<T>` — present as named exports so a consumer can write
166
+ * `const catalog: MutableStylesCatalog = { … }` without reaching for the
167
+ * generic. For deeply-nested shapes (node types, formatting records, table
168
+ * cells, chart models) use `Mutable<T>` directly.
169
+ *
170
+ * These aliases are the foundation layer for the deferred D1 deep-readonly
171
+ * propagation (see `docs/plans/refactor/02-canonical-document-deferred.md`
172
+ * § D1). They are type-only additions with zero runtime cost and zero
173
+ * consumer impact; they only unlock the migration path for later slices.
174
+ */
175
+ export type MutableDocumentMetadata = Mutable<DocumentMetadata>;
176
+ export type MutableStylesCatalog = Mutable<StylesCatalog>;
177
+ export type MutableNumberingCatalog = Mutable<NumberingCatalog>;
178
+ export type MutableMediaCatalog = Mutable<MediaCatalog>;
179
+ export type MutableDocumentRootNode = Mutable<DocumentRootNode>;
180
+ export type MutableReviewStore = Mutable<ReviewStore>;
181
+ export type MutablePreservationStore = Mutable<PreservationStore>;
182
+ export type MutableDiagnosticStore = Mutable<DiagnosticStore>;
183
+ export type MutableSubPartsCatalog = Mutable<SubPartsCatalog>;
184
+ export type MutableFieldRegistry = Mutable<FieldRegistry>;
185
+ export type MutableCanonicalFontTable = Mutable<CanonicalFontTable>;
186
+
79
187
 
80
188
  export interface DocumentMetadata {
81
189
  title?: string;
@@ -382,6 +490,34 @@ export interface ThemeFontScheme {
382
490
  minorFont?: string;
383
491
  }
384
492
 
493
+ /**
494
+ * ECMA-376 §17.18.94 `<w:rFonts>` theme-slot values. The set is a small
495
+ * closed enumeration; every attribute value outside this list is dropped
496
+ * at parse (preserving the "canonical reflects modeled state only"
497
+ * invariant — unknown values would be data the layer cannot round-trip).
498
+ *
499
+ * Slot semantics: `majorAscii` = major-font Latin glyph typeface,
500
+ * `minorEastAsia` = minor-font East-Asian typeface, etc. Word uses
501
+ * `majorFont` for headings and `minorFont` for body text; the script
502
+ * suffix picks the typeface within that major/minor family.
503
+ *
504
+ * Canonical `ThemeFontScheme` today captures only a single typeface
505
+ * per major/minor — all script variants of the same major/minor family
506
+ * resolve to the same concrete font. Extending the scheme with
507
+ * per-script (latin / ea / cs) typefaces is L02 territory and is
508
+ * tracked as a follow-up; the run-side field preserves the full
509
+ * attribute for byte-identical export regardless.
510
+ */
511
+ export type ThemeFontSlot =
512
+ | "majorAscii"
513
+ | "minorAscii"
514
+ | "majorHAnsi"
515
+ | "minorHAnsi"
516
+ | "majorEastAsia"
517
+ | "minorEastAsia"
518
+ | "majorBidi"
519
+ | "minorBidi";
520
+
385
521
  export interface ThemeDefinition {
386
522
  name?: string;
387
523
  colorScheme?: ThemeColorScheme;
@@ -664,6 +800,24 @@ export interface CanonicalRunFormatting {
664
800
  fontFamilyHAnsi?: string;
665
801
  fontFamilyEastAsia?: string;
666
802
  fontFamilyCs?: string;
803
+ /**
804
+ * ECMA-376 §17.3.2.26 theme-slot bindings on `<w:rFonts>`
805
+ * (`w:asciiTheme`, `w:hAnsiTheme`, `w:eastAsiaTheme`, `w:cstheme`).
806
+ * When present, the run asks for the typeface named by the
807
+ * corresponding slot on the document's `theme.fontScheme` — Word
808
+ * then resolves the concrete font through the theme. Preserved for
809
+ * round-trip; resolution lives in
810
+ * `src/runtime/formatting/font-resolution.ts`.
811
+ *
812
+ * Concrete names (`fontFamilyAscii`, etc.) take precedence over
813
+ * theme slots per ECMA-376 §17.3.2.26; when both are present the
814
+ * concrete name wins. Keep the theme slot on the canonical so the
815
+ * export path can re-emit it byte-identically.
816
+ */
817
+ asciiTheme?: ThemeFontSlot;
818
+ hAnsiTheme?: ThemeFontSlot;
819
+ eastAsiaTheme?: ThemeFontSlot;
820
+ csTheme?: ThemeFontSlot;
667
821
  fontSizeHalfPoints?: number;
668
822
  fontSizeCsHalfPoints?: number;
669
823
  /**
@@ -1930,6 +2084,151 @@ export function createCanonicalDocument(
1930
2084
  return document;
1931
2085
  }
1932
2086
 
2087
+ /**
2088
+ * Layer 02 default-envelope factory. Produces a fully-valid
2089
+ * {@link CanonicalDocument} with a single empty paragraph and empty
2090
+ * catalogs/stores. Used by:
2091
+ * - the session layer's default-new-document path (when a host requests
2092
+ * an editor without supplying source bytes)
2093
+ * - the persisted-snapshot repair path (as the "base" onto which a
2094
+ * malformed envelope is merged by {@link repairCanonicalDocumentEnvelope})
2095
+ *
2096
+ * The function is pure: no runtime, io, or session imports. Every field
2097
+ * populated here is a structural constant, so the resulting document
2098
+ * passes {@link assertCanonicalDocument} by construction.
2099
+ */
2100
+ export function createEmptyCanonicalDocument(params: {
2101
+ docId: UUID;
2102
+ createdAt: ISO8601DateTime;
2103
+ updatedAt?: ISO8601DateTime;
2104
+ }): CanonicalDocument {
2105
+ return createCanonicalDocument({
2106
+ docId: params.docId,
2107
+ createdAt: params.createdAt,
2108
+ updatedAt: params.updatedAt ?? params.createdAt,
2109
+ metadata: { customProperties: {} },
2110
+ styles: { paragraphs: {}, characters: {}, tables: {} },
2111
+ numbering: { abstractDefinitions: {}, instances: {} },
2112
+ media: { items: {} },
2113
+ content: {
2114
+ type: "doc",
2115
+ children: [{ type: "paragraph", children: [] }],
2116
+ },
2117
+ review: { comments: {}, revisions: {} },
2118
+ preservation: { opaqueFragments: {}, packageParts: {} },
2119
+ diagnostics: { warnings: [], errors: [] },
2120
+ });
2121
+ }
2122
+
2123
+ /**
2124
+ * Layer 02 repair-envelope factory. Coerces an unknown value that claims
2125
+ * to be a persisted {@link CanonicalDocument} into a structurally-valid
2126
+ * envelope by merging the value's recognizable fields onto a freshly
2127
+ * constructed empty envelope (see {@link createEmptyCanonicalDocument}).
2128
+ *
2129
+ * The repair preserves optional fields — `subParts`, `fieldRegistry`,
2130
+ * `fontTable` — unmodified when present as objects. The session layer's
2131
+ * previous copy of this function silently dropped `fieldRegistry` and
2132
+ * `fontTable`, which caused a round-trip of (load → persist → reload)
2133
+ * to lose canonical data. Having the function live on the model layer
2134
+ * ensures every repair path recognises every optional field the model
2135
+ * actually owns.
2136
+ *
2137
+ * Inputs that are not plain objects are treated as completely missing,
2138
+ * producing a document with default content. Inputs whose `docId` is
2139
+ * not a valid UUID are given {@link params.fallbackDocId} instead.
2140
+ *
2141
+ * This function is pure and synchronous. It does not throw; the caller
2142
+ * decides whether to pass the output through {@link assertCanonicalDocument}
2143
+ * for strict validation (the session-layer repair path does; the rehydration
2144
+ * path does too, after merging in the live session's canonicalDocument
2145
+ * snapshot). The output is always structurally valid — the final shape is
2146
+ * `createEmptyCanonicalDocument(fallback) ∪ filtered(input)`.
2147
+ */
2148
+ export function repairCanonicalDocumentEnvelope(
2149
+ value: unknown,
2150
+ params: {
2151
+ fallbackDocId: UUID;
2152
+ fallbackCreatedAt: ISO8601DateTime;
2153
+ fallbackUpdatedAt?: ISO8601DateTime;
2154
+ },
2155
+ ): CanonicalDocument {
2156
+ const base = createEmptyCanonicalDocument({
2157
+ docId: params.fallbackDocId,
2158
+ createdAt: params.fallbackCreatedAt,
2159
+ updatedAt: params.fallbackUpdatedAt ?? params.fallbackCreatedAt,
2160
+ });
2161
+ const record = isPlainRecord(value) ? value : {};
2162
+ const metadata = isPlainRecord(record.metadata) ? record.metadata : {};
2163
+ const customProperties = isPlainRecord(metadata.customProperties)
2164
+ ? (Object.fromEntries(
2165
+ Object.entries(metadata.customProperties).filter(
2166
+ ([, entry]) => typeof entry === "string",
2167
+ ),
2168
+ ) as Record<string, string>)
2169
+ : {};
2170
+
2171
+ const envelope: Mutable<CanonicalDocument> = {
2172
+ ...base,
2173
+ schemaVersion: CDS_SCHEMA_VERSION,
2174
+ docId: isUuid(record.docId) ? record.docId : params.fallbackDocId,
2175
+ createdAt:
2176
+ typeof record.createdAt === "string" && record.createdAt.length > 0
2177
+ ? record.createdAt
2178
+ : base.createdAt,
2179
+ updatedAt:
2180
+ typeof record.updatedAt === "string" && record.updatedAt.length > 0
2181
+ ? record.updatedAt
2182
+ : base.updatedAt,
2183
+ metadata: {
2184
+ ...base.metadata,
2185
+ ...metadata,
2186
+ customProperties,
2187
+ } as CanonicalDocument["metadata"],
2188
+ styles:
2189
+ record.styles === undefined
2190
+ ? base.styles
2191
+ : (record.styles as CanonicalDocument["styles"]),
2192
+ numbering:
2193
+ record.numbering === undefined
2194
+ ? base.numbering
2195
+ : (record.numbering as CanonicalDocument["numbering"]),
2196
+ media:
2197
+ record.media === undefined
2198
+ ? base.media
2199
+ : (record.media as CanonicalDocument["media"]),
2200
+ content: isPlainRecord(record.content)
2201
+ ? (record.content as unknown as CanonicalDocument["content"])
2202
+ : base.content,
2203
+ review:
2204
+ record.review === undefined
2205
+ ? base.review
2206
+ : (record.review as CanonicalDocument["review"]),
2207
+ preservation:
2208
+ record.preservation === undefined
2209
+ ? base.preservation
2210
+ : (record.preservation as CanonicalDocument["preservation"]),
2211
+ diagnostics:
2212
+ record.diagnostics === undefined
2213
+ ? base.diagnostics
2214
+ : (record.diagnostics as CanonicalDocument["diagnostics"]),
2215
+ };
2216
+ if (isPlainRecord(record.subParts)) {
2217
+ envelope.subParts = record.subParts as unknown as CanonicalDocument["subParts"];
2218
+ }
2219
+ if (isPlainRecord(record.fieldRegistry)) {
2220
+ envelope.fieldRegistry = record.fieldRegistry as unknown as CanonicalDocument["fieldRegistry"];
2221
+ }
2222
+ if (isPlainRecord(record.fontTable)) {
2223
+ envelope.fontTable = record.fontTable as unknown as CanonicalDocument["fontTable"];
2224
+ }
2225
+ return envelope;
2226
+ }
2227
+
2228
+ function isPlainRecord(value: unknown): value is Record<string, unknown> {
2229
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2230
+ }
2231
+
1933
2232
  export function serializeCanonicalDocument(document: CanonicalDocument): string {
1934
2233
  assertCanonicalDocument(document);
1935
2234
  return stableStringify(document);
@@ -3621,3 +3920,40 @@ function expectDomainString(
3621
3920
 
3622
3921
  return stableId;
3623
3922
  }
3923
+
3924
+ /**
3925
+ * Content-addressable canonical document id — a pseudo-UUIDv4
3926
+ * derived deterministically from an opaque `documentId` string
3927
+ * via four 32-bit FNV-1a hashes. Stable across load/export
3928
+ * (same `documentId` → same `docId`), zero I/O, no wall clock.
3929
+ *
3930
+ * Lives here (model) rather than `src/core/state/editor-state.ts`
3931
+ * because `CanonicalDocument.docId` is a model concern — the
3932
+ * editor-state layer consumes it, doesn't define it. The legacy
3933
+ * export at `src/core/state/editor-state.ts::createCanonicalDocumentId`
3934
+ * re-exports this function for back-compat.
3935
+ */
3936
+ export function createCanonicalDocumentId(documentId: string): string {
3937
+ const hashWord = (seed: string): string => {
3938
+ let hash = 0x811c9dc5;
3939
+ for (const char of seed) {
3940
+ hash ^= char.charCodeAt(0);
3941
+ hash = Math.imul(hash, 0x01000193);
3942
+ }
3943
+ return (hash >>> 0).toString(16).padStart(8, "0");
3944
+ };
3945
+
3946
+ const raw =
3947
+ hashWord(`${documentId}:0`) +
3948
+ hashWord(`${documentId}:1`) +
3949
+ hashWord(`${documentId}:2`) +
3950
+ hashWord(`${documentId}:3`);
3951
+
3952
+ return [
3953
+ raw.slice(0, 8),
3954
+ raw.slice(8, 12),
3955
+ `4${raw.slice(13, 16)}`,
3956
+ `8${raw.slice(17, 20)}`,
3957
+ raw.slice(20, 32),
3958
+ ].join("-");
3959
+ }
@@ -13,7 +13,7 @@
13
13
  * references; consumers of Stage 1 models should treat color resolution as
14
14
  * "not yet available."
15
15
  *
16
- * See docs/plans/lane-5-charts.md §3 Task 1.1 for the full specification.
16
+ * See CLAUDE.md (lane status table) §3 Task 1.1 for the full specification.
17
17
  */
18
18
 
19
19
  // ---------------------------------------------------------------------------
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Layer 02 · shared layout types (barrel).
3
+ *
4
+ * Neutral home for *data-only* layout types that multiple layers
5
+ * consume but nobody else should own — `RuntimePageGraph`,
6
+ * `RuntimeLayoutFacet`, `RuntimeLineBox`, `RuntimeNoteAllocation`,
7
+ * `RuntimePageAnchor`, etc. Proposed by Layer 04 at `cross-layer-
8
+ * coord-04.md §2` and accepted by Layer 02 at `cross-layer-coord-02.md §9`.
9
+ *
10
+ * # Acceptance rule (what can land in `src/model/layout/**`)
11
+ *
12
+ * This directory is inside `src/model/**` and therefore subject to the
13
+ * same architecture §D2 (quasi-immutability) and §D3 (no upward
14
+ * imports) contracts as the rest of Layer 02. The import-purity guard
15
+ * at `scripts/ci-check-model-import-purity.mjs` already scans this
16
+ * subdirectory recursively — no guard change needed when types land.
17
+ *
18
+ * Accepts:
19
+ * ✓ Plain data interfaces / type aliases for layout-graph + geometry
20
+ * shapes (e.g. `RuntimePageGraph`, `RuntimeLineBox`,
21
+ * `RuntimeNoteAllocation`, `RuntimePageAnchor`).
22
+ * ✓ Readonly / discriminated-union shapes.
23
+ * ✓ `Mutable<T>` draft aliases (same construction-pattern discipline
24
+ * as the rest of `src/model/`).
25
+ * ✓ Type-level utility aliases and branded-id types that describe a
26
+ * layout shape.
27
+ *
28
+ * Rejects (these belong elsewhere — typically `src/runtime/layout/`):
29
+ * ✗ Classes with methods, or interfaces that describe a stateful
30
+ * object.
31
+ * ✗ Factory functions or builders (`createPageGraph()`, etc.).
32
+ * ✗ Visitors / walkers / transformers that operate on a layout tree.
33
+ * ✗ Imports from any forbidden prefix (see guard).
34
+ * ✗ Non-trivial top-level statements (module side-effects).
35
+ *
36
+ * # Who edits here
37
+ *
38
+ * Layer 04 owns the content. Layer 02 owns the boundary (purity guard
39
+ * + acceptance rule). A type relocation from `src/runtime/layout/**`
40
+ * into this tree lands as an L04 commit; L02 reviews and, if the
41
+ * types pass the acceptance rule, the guard scan stays green on the
42
+ * next CI run. No L02 commit is needed per relocation.
43
+ *
44
+ * # Pass 1 — 2026-04-22
45
+ *
46
+ * 6 pure data types relocated from `src/runtime/layout/page-graph.ts`
47
+ * per `docs/plans/cross-layer-coord-04.md §1.12` (L04 purity audit).
48
+ *
49
+ * # Pass 2 — 2026-04-22
50
+ *
51
+ * `PageLayoutSnapshot`, `EditorStoryTarget`, and `DocumentPageSnapshot`
52
+ * relocated out of `src/api/public-types.ts`. They are pure data shapes
53
+ * derived from `CanonicalDocument` inputs; living in the model tree lets
54
+ * `RuntimePageGraph` (next step) drop its transitive api dependency.
55
+ * `src/api/public-types.ts` continues to re-export them as a stability
56
+ * guarantee for external consumers.
57
+ */
58
+
59
+ export type {
60
+ RuntimePageRegions,
61
+ RuntimePageRegion,
62
+ RuntimeBlockFragment,
63
+ RuntimeLineBox,
64
+ RuntimeNoteAllocation,
65
+ RuntimePageAnchor,
66
+ } from "./page-graph-types.ts";
67
+
68
+ export type {
69
+ PageLayoutSnapshot,
70
+ EditorStoryTarget,
71
+ DocumentPageSnapshot,
72
+ } from "./page-layout-snapshot.ts";
73
+
74
+ export type {
75
+ ResolvedPageStories,
76
+ ResolvedDocumentSection,
77
+ } from "./resolved-layout-types.ts";
78
+
79
+ export type {
80
+ RuntimePageGraph,
81
+ RuntimePageNode,
82
+ BuildPageGraphInput,
83
+ } from "./runtime-page-graph-types.ts";
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Layer 02 · shared layout data types — Pass 1 of the L04/L02
3
+ * `src/model/layout/` seam (see `docs/plans/cross-layer-coord-04.md
4
+ * §1.12` and `cross-layer-coord-02.md §9`).
5
+ *
6
+ * These six interfaces are the pure-data subset of the layout graph.
7
+ * They describe how the paginated layout engine exposes region /
8
+ * fragment / line-box / footnote / anchor state — no methods, no
9
+ * imports from higher layers, no runtime-scoped types. They live
10
+ * here so external consumers (Layer 01 `CacheEnvelope`, future debug
11
+ * runners, stateless services) can import the shapes without pulling
12
+ * in `src/runtime/layout/`.
13
+ *
14
+ * Runtime-scoped companions (`RuntimePageGraph`, `RuntimePageNode`,
15
+ * `BuildPageGraphInput`) remain in `src/runtime/layout/page-graph.ts`
16
+ * because they reference `ResolvedPageStories` / `ResolvedDocumentSection`
17
+ * which carry runtime identity today. Pass 2 will relocate the parent
18
+ * graph types once those references are narrowed (see L04's coord-04
19
+ * §1.12 "Pass 2 (full closure)").
20
+ *
21
+ * Acceptance rule: see `src/model/layout/index.ts`. Every type here is
22
+ * plain data (no classes / methods / factories / higher-layer imports).
23
+ */
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Region types
27
+ // ---------------------------------------------------------------------------
28
+
29
+ export interface RuntimePageRegions {
30
+ body: RuntimePageRegion;
31
+ header?: RuntimePageRegion;
32
+ footer?: RuntimePageRegion;
33
+ /**
34
+ * For multi-column bodies, `body.columns` is populated. For single-column
35
+ * pages, the top-level body is used and `columns` is undefined.
36
+ */
37
+ columns?: RuntimePageRegion[];
38
+ /**
39
+ * P4 — footnote regions reserved at the bottom of the page (above
40
+ * the footer band) when `noteAllocations` reserved space for one or
41
+ * more footnote bodies. Empty until the page-graph builder
42
+ * populates them, which lands alongside the P8 per-page region
43
+ * rendering work. The shape exists now so consumers
44
+ * (`getStoryRegionsOnPage` / `getLineBoxesForRegion("footnote-area")`)
45
+ * have a stable seam to read from when the builder catches up —
46
+ * pre-P8 reads return `[]` because no entries are populated.
47
+ */
48
+ footnotes?: RuntimePageRegion[];
49
+ }
50
+
51
+ export interface RuntimePageRegion {
52
+ kind: "body" | "header" | "footer" | "column" | "footnote-area";
53
+ /** Twips offset from page top (header) or similar region-specific origin. */
54
+ originTwips: number;
55
+ /** Width in twips. */
56
+ widthTwips: number;
57
+ /** Height in twips. */
58
+ heightTwips: number;
59
+ /** IDs of block fragments rendered in this region, in order. */
60
+ fragmentIds: string[];
61
+ }
62
+
63
+ // ---------------------------------------------------------------------------
64
+ // Fragment types
65
+ // ---------------------------------------------------------------------------
66
+
67
+ export interface RuntimeBlockFragment {
68
+ fragmentId: string;
69
+ /** Canonical block id the fragment slices. */
70
+ blockId: string;
71
+ /** Page this fragment lives on. */
72
+ pageId: string;
73
+ /** Zero-based order within the page region. */
74
+ orderInRegion: number;
75
+ /** Region id the fragment sits in (matches RuntimePageRegion.kind). */
76
+ regionKind: RuntimePageRegion["kind"];
77
+ /** Inclusive from / exclusive to offsets within the main story. */
78
+ from: number;
79
+ to: number;
80
+ /** Height consumed on this page (twips). */
81
+ heightTwips: number;
82
+ /**
83
+ * Fragment classification.
84
+ * - `"whole"` (default): the fragment represents the entire block; no slicing.
85
+ * - `"paragraph-slice"`: one of several fragments produced by intra-paragraph
86
+ * line-box splitting. `paragraphLineRange` identifies which lines this
87
+ * slice renders.
88
+ * - `"table-slice"`: one of several fragments produced by row-boundary table
89
+ * splitting (emitted by the table-fidelity workstream).
90
+ * `tableRowRange` identifies which canonical rows this slice renders.
91
+ * Consumers that predate multi-fragment blocks may treat an absent `kind`
92
+ * as `"whole"`.
93
+ */
94
+ kind?: "whole" | "paragraph-slice" | "table-slice";
95
+ /**
96
+ * For `kind === "paragraph-slice"`, the inclusive-exclusive line-box index
97
+ * range rendered by this slice plus the total line count for the source
98
+ * paragraph. `from`/`to` still span the full paragraph offset range on
99
+ * every slice — only the visible lines differ.
100
+ */
101
+ paragraphLineRange?: {
102
+ from: number;
103
+ to: number;
104
+ totalLines: number;
105
+ };
106
+ /**
107
+ * For `kind === "table-slice"`, the inclusive-exclusive row-index range
108
+ * rendered by this slice. Repeated header rows (when the table has
109
+ * `isHeader` rows and `continuation` pages) are implied by the owning
110
+ * table's canonical row list — consumers prepend header rows for slices
111
+ * whose `from > 0`.
112
+ */
113
+ tableRowRange?: {
114
+ from: number;
115
+ to: number;
116
+ totalRows: number;
117
+ };
118
+ /**
119
+ * Slice 5 — opaque style-chain ref derived from the block's `styleId`.
120
+ * Used by `analyzeStylesChange` to bound invalidation to the first page
121
+ * that carries a fragment referencing a dirty style.
122
+ */
123
+ resolvedStyleChainRef?: string;
124
+ /**
125
+ * Slice 5 — numbering instance id copied from the block's `numbering`
126
+ * field. Used by `analyzeNumberingChange` to bound invalidation to the
127
+ * first page that carries a fragment from the affected list instance.
128
+ */
129
+ numberingInstanceId?: string;
130
+ /**
131
+ * Refactor/04 Slice 4 — zero-based column index on the page, set only
132
+ * when the containing section paginated under a multi-column layout.
133
+ * Absent on single-column pages; absent on footnote-area + header/footer
134
+ * fragments. `buildRegions` uses this to route fragment ids into
135
+ * `RuntimePageRegions.columns[i].fragmentIds`.
136
+ */
137
+ columnIndex?: number;
138
+ }
139
+
140
+ // ---------------------------------------------------------------------------
141
+ // Line box + note + anchor types
142
+ // ---------------------------------------------------------------------------
143
+
144
+ export interface RuntimeLineBox {
145
+ /** Fragment this line belongs to. */
146
+ fragmentId: string;
147
+ /** Zero-based line index inside the fragment. */
148
+ lineIndex: number;
149
+ /** Baseline twips from the region's origin. */
150
+ baselineTwips: number;
151
+ /** Line height twips. */
152
+ heightTwips: number;
153
+ /** Approximate inline width consumed on this line. */
154
+ widthTwips: number;
155
+ }
156
+
157
+ export interface RuntimeNoteAllocation {
158
+ noteKind: "footnote" | "endnote";
159
+ noteId: string;
160
+ /** Twips reserved at the bottom of the page for this note's content. */
161
+ reservedHeightTwips: number;
162
+ /**
163
+ * P8 — fragment ID of this note's body. The corresponding
164
+ * `RuntimeBlockFragment` lives in `RuntimePageGraph.fragments` with
165
+ * `regionKind: "footnote-area"`. Undefined when the engine hasn't
166
+ * yet emitted a fragment for the allocation (back-compat for pre-P8
167
+ * callers).
168
+ */
169
+ fragmentId?: string;
170
+ }
171
+
172
+ export interface RuntimePageAnchor {
173
+ /** Story target the anchor is for. Main story is the common case. */
174
+ storyKey: string;
175
+ /** Offset the anchor represents. */
176
+ offset: number;
177
+ /** Page id resolved for this offset. */
178
+ pageId: string;
179
+ /** Fragment id resolved for this offset, if available. */
180
+ fragmentId?: string;
181
+ }