@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
@@ -67,17 +67,21 @@ export function parseObject(
67
67
 
68
68
  // hotfix/ole-digestibility-guard — classify the embedding before
69
69
  // constructing a canonical node. When the classifier returns
70
- // "store-only" (nested Word docs, PDF OLE, Excel/PowerPoint
71
- // embeddings, unknown ProgIDs), return undefined so the caller's
72
- // existing opaque-fragment fallback preserves both <w:object> XML
73
- // and its r:id verbatim. Binary preservation is unaffected —
74
- // collectPreservedPackageParts indexes embedding parts by path, not
75
- // by canonical-tree reference.
70
+ // `store-only` OR `offloadable` (nested Word docs, PDF OLE, Excel/
71
+ // PowerPoint embeddings, unknown ProgIDs), return undefined so
72
+ // the caller's existing opaque-fragment fallback preserves both
73
+ // `<w:object>` XML and its r:id verbatim. Binary preservation is
74
+ // unaffected — `collectPreservedPackageParts` indexes embedding
75
+ // parts by path, not by canonical-tree reference.
76
76
  //
77
- // TODO(refactor/01 Step 6-7): replace this skip-construction with
78
- // extraction + offload via hostAdapter.storeEmbeddedDocument?. See
79
- // docs/architecture/01-package-session.md §P8 + docs/plans/refactor/
80
- // 01-package-session.md Steps 6-7.
77
+ // Offload handling: the session layer runs `processEmbeddedOffload`
78
+ // (`src/session/import/embedded-offload.ts`) AFTER this parse step
79
+ // and AFTER the preservation index is built. For `offloadable`
80
+ // manifests with an opted-in host adapter, the session removes the
81
+ // corresponding part path from preservation (via the offload
82
+ // `ownedPartPaths` list fed into `buildImportPreservation`) and
83
+ // reconstitutes at export time. This parser stays agnostic.
84
+ // Shipped in refactor/01 Step 7 (Slice 5e-10).
81
85
  const kind = classifyEmbedding({
82
86
  progId,
83
87
  relationshipType: resolved.relationshipType,
@@ -1,4 +1,7 @@
1
- import type { CanonicalRunFormatting } from "../../model/canonical-document.ts";
1
+ import type {
2
+ CanonicalRunFormatting,
3
+ ThemeFontSlot,
4
+ } from "../../model/canonical-document.ts";
2
5
  import { findChildOptional, readIntVal, readOnOff, readStringAttr } from "./xml-attr-helpers.ts";
3
6
  import type { XmlElementNode } from "./xml-element.ts";
4
7
  import {
@@ -124,6 +127,33 @@ export function readRunProperties(
124
127
  if (cs) rPr.fontFamilyCs = cs;
125
128
  const primary = ascii ?? hAnsi ?? eastAsia ?? cs;
126
129
  if (primary) rPr.fontFamily = primary;
130
+
131
+ // ECMA-376 §17.3.2.26 theme-slot attrs. Unknown slot values are
132
+ // dropped (enumeration is closed); parsers that see an ECMA-376-
133
+ // valid slot we don't model land in the grab bag and round-trip
134
+ // through a different path.
135
+ const asciiTheme = toThemeFontSlot(
136
+ rFonts.attributes["w:asciiTheme"] ?? rFonts.attributes.asciiTheme,
137
+ );
138
+ const hAnsiTheme = toThemeFontSlot(
139
+ rFonts.attributes["w:hAnsiTheme"] ?? rFonts.attributes.hAnsiTheme,
140
+ );
141
+ const eastAsiaTheme = toThemeFontSlot(
142
+ rFonts.attributes["w:eastAsiaTheme"] ?? rFonts.attributes.eastAsiaTheme,
143
+ );
144
+ // ECMA-376: the complex-script theme attr is "cstheme" (all lower)
145
+ // — not "csTheme" — and Word emits that form. Both casings are
146
+ // handled defensively.
147
+ const csTheme = toThemeFontSlot(
148
+ rFonts.attributes["w:cstheme"] ??
149
+ rFonts.attributes.cstheme ??
150
+ rFonts.attributes["w:csTheme"] ??
151
+ rFonts.attributes.csTheme,
152
+ );
153
+ if (asciiTheme) rPr.asciiTheme = asciiTheme;
154
+ if (hAnsiTheme) rPr.hAnsiTheme = hAnsiTheme;
155
+ if (eastAsiaTheme) rPr.eastAsiaTheme = eastAsiaTheme;
156
+ if (csTheme) rPr.csTheme = csTheme;
127
157
  }
128
158
 
129
159
  const sz = readIntVal(findChildOptional(node, "sz"));
@@ -176,3 +206,19 @@ export function readRunProperties(
176
206
 
177
207
  return Object.keys(rPr).length > 0 ? rPr : undefined;
178
208
  }
209
+
210
+ const THEME_FONT_SLOTS = new Set<string>([
211
+ "majorAscii",
212
+ "minorAscii",
213
+ "majorHAnsi",
214
+ "minorHAnsi",
215
+ "majorEastAsia",
216
+ "minorEastAsia",
217
+ "majorBidi",
218
+ "minorBidi",
219
+ ]);
220
+
221
+ function toThemeFontSlot(value: string | undefined): ThemeFontSlot | undefined {
222
+ if (value === undefined) return undefined;
223
+ return THEME_FONT_SLOTS.has(value) ? (value as ThemeFontSlot) : undefined;
224
+ }
@@ -5,7 +5,7 @@
5
5
  * property container (`<w:pPr>`, `<w:rPr>`, `<w:tcPr>`, `<w:trPr>`,
6
6
  * `<w:tblPr>`, `<w:sectPr>`) via per-container "grab bags" keyed by
7
7
  * element name — see `PropertyMap.hxx:82` and
8
- * `libreoffice-analysis.md` §2 for the mechanism. On export, every grab
8
+ * `docs/reference/libreoffice-analysis.md` §2 for the mechanism. On export, every grab
9
9
  * bag re-emits verbatim inside its container so the round-trip pipeline
10
10
  * does not silently drop extension-namespace properties (`w15:collapsed`,
11
11
  * `w16cex:...`, etc.) or attributes Word adds after we parsed its schema.
@@ -95,7 +95,7 @@ export interface PropertyGrabBagDescriptor {
95
95
  * `TableCellNode.propertiesXml`. That path cannot participate in the
96
96
  * attribute-level grab-bag slice until it is retrofit to emit
97
97
  * `UnknownPropertyChild[]` through this descriptor. Tracked as a Lane 3
98
- * Tier-2 backlog entry — see `docs/plans/lane-3-layout-engine-ooxml-fidelity.md`.
98
+ * Tier-2 backlog entry — see `CLAUDE.md (lane status table)`.
99
99
  */
100
100
  modelledChildAttributes: ReadonlyMap<string, ReadonlySet<string>>;
101
101
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * OOXML unit constants used by the IO layer's parsers.
3
+ *
4
+ * Slice 3 of refactor/01 package-session. Migrated from
5
+ * src/runtime/units.ts (drift fix #5 — IO-layer constants must not
6
+ * live under src/runtime/**). The runtime module keeps a re-export
7
+ * alias for one-release back-compat.
8
+ */
9
+
10
+ /** OOXML gradient-stop and crop-offset unit (100 000 = 100%). */
11
+ export const GRADIENT_STOP_UNITS = 100_000;
@@ -5,6 +5,12 @@ import type {
5
5
  } from "./part-manifest.ts";
6
6
  import type {
7
7
  EditorAnchorProjection,
8
+ OverlayKind,
9
+ OverlayVisibilityEnforcement,
10
+ OverlayVisibilityPolicy,
11
+ WorkflowMarkupMode,
12
+ WorkflowMarkupModeEnforcement,
13
+ WorkflowMarkupModePolicy,
8
14
  WorkflowOverlay,
9
15
  WorkflowMetadataDefinition,
10
16
  WorkflowMetadataEntry,
@@ -92,6 +98,17 @@ export interface WorkflowPayloadEnvelope {
92
98
  workflowOverlay?: WorkflowOverlay;
93
99
  /** Present only when the payload was version 1.2+ and carried a non-empty <bw:editorState> block. */
94
100
  editorState?: EditorStatePayload;
101
+ /**
102
+ * W10 — class-A overlay-visibility policies, one per `OverlayKind` at
103
+ * most. Empty array when the payload carried no `<bw:overlayVisibility>`
104
+ * block (documents predating the schema read as "no policy").
105
+ */
106
+ visibilityPolicies?: readonly OverlayVisibilityPolicy[];
107
+ /**
108
+ * X5 — class-A markup-mode policy (single record). `null` when the
109
+ * payload carried no `<bw:markupModePolicy>` block.
110
+ */
111
+ markupModePolicy?: WorkflowMarkupModePolicy | null;
95
112
  validatorIssues?: readonly ValidatorIssue[];
96
113
  }
97
114
 
@@ -119,6 +136,19 @@ export function buildWorkflowPayloadParts(input: {
119
136
  workflowOverlay?: WorkflowOverlay;
120
137
  /** Optional schema 1.2 editor-state block. Omitted when empty. */
121
138
  editorState?: EditorStatePayload;
139
+ /**
140
+ * W10 — class-A overlay-visibility policies. When non-empty, emitted
141
+ * as a `<bw:overlayVisibility>` block inside the workflow payload and
142
+ * forces the customXml part to be written (even when no overlay /
143
+ * metadata / editorState content would otherwise require it).
144
+ */
145
+ visibilityPolicies?: readonly OverlayVisibilityPolicy[];
146
+ /**
147
+ * X5 — class-A markup-mode policy. When non-null, emitted as a
148
+ * `<bw:markupModePolicy>` block and forces customXml rewrite (same
149
+ * pattern as visibility policies).
150
+ */
151
+ markupModePolicy?: WorkflowMarkupModePolicy | null;
122
152
  documentId: string;
123
153
  createdAt: string;
124
154
  updatedAt: string;
@@ -139,13 +169,29 @@ export function buildWorkflowPayloadParts(input: {
139
169
  : undefined;
140
170
  const sourceHasRuntimeOwnedPayloadContent = Boolean(
141
171
  existingPayloadEnvelope?.workflowOverlay ||
142
- (existingPayloadEnvelope?.workflowMetadata.definitions.length ?? 0) > 0,
172
+ (existingPayloadEnvelope?.workflowMetadata.definitions.length ?? 0) > 0 ||
173
+ (existingPayloadEnvelope?.visibilityPolicies?.length ?? 0) > 0 ||
174
+ existingPayloadEnvelope?.markupModePolicy != null,
143
175
  );
176
+ // Schema 1.2: the editorState block carries the runtime's full workflow
177
+ // state (definitions + entries including `metadataPersistence: "internal"`
178
+ // that do NOT pass the document-backed filter). When the runtime has any
179
+ // such state, the customXml part MUST be emitted — otherwise a caller who
180
+ // only uses `setWorkflowMetadataEntries` / `addScope({ persistence: ...})`
181
+ // ships docs with silently-dropped entries.
182
+ const hasEditorStateContent = hasNonEmptyEditorState(input.editorState);
183
+ const visibilityPolicies = input.visibilityPolicies ?? [];
184
+ const hasVisibilityPolicies = visibilityPolicies.length > 0;
185
+ const markupModePolicy = input.markupModePolicy ?? null;
186
+ const hasMarkupModePolicy = markupModePolicy !== null;
144
187
  if (
145
188
  workflowMetadata.definitions.length === 0 &&
146
189
  workflowMetadata.entries.length === 0 &&
147
190
  !input.workflowOverlay &&
148
- !sourceHasRuntimeOwnedPayloadContent
191
+ !sourceHasRuntimeOwnedPayloadContent &&
192
+ !hasEditorStateContent &&
193
+ !hasVisibilityPolicies &&
194
+ !hasMarkupModePolicy
149
195
  ) {
150
196
  return null;
151
197
  }
@@ -159,6 +205,8 @@ export function buildWorkflowPayloadParts(input: {
159
205
  workflowMetadata,
160
206
  workflowOverlay: input.workflowOverlay,
161
207
  editorState: input.editorState,
208
+ visibilityPolicies,
209
+ markupModePolicy,
162
210
  preservedExtensionsXml: getPreservedExtensionsXml(input.sourcePackage, descriptor.payloadPartPath),
163
211
  });
164
212
  const itemPropsXml = buildItemPropsXml(descriptor.itemId);
@@ -272,17 +320,80 @@ export function parseWorkflowPayloadEnvelopeFromPackage(
272
320
 
273
321
  const validatorIssues = validateWorkflowPayloadEnvelope(xml);
274
322
  const editorState = parseEditorStateXml(xml);
323
+ // Schema 1.2: the `<bw:editorState>` `workflowMetadata` namespace carries
324
+ // the full runtime snapshot (including entries whose effective persistence
325
+ // is `"internal"`, which do NOT survive the `<bw:entry>` / document-backed
326
+ // filter). Fold its entries + definitions in here so the reload path sees
327
+ // the authoritative state. Existing `<bw:entry>`-extracted data wins on
328
+ // metadataId collision — it represents the external/document-backed view.
329
+ const merged = mergeEditorStateWorkflowMetadata(
330
+ { definitions, entries },
331
+ editorState,
332
+ );
333
+ const visibilityPolicies = parseOverlayVisibilityBlock(xml);
334
+ const markupModePolicy = parseMarkupModePolicyBlock(xml);
275
335
  return {
276
- workflowMetadata: {
277
- definitions,
278
- entries,
279
- },
336
+ workflowMetadata: merged,
280
337
  workflowOverlay: parseWorkflowOverlay(xml),
281
338
  ...(editorState !== undefined ? { editorState } : {}),
339
+ ...(visibilityPolicies.length > 0 ? { visibilityPolicies } : {}),
340
+ ...(markupModePolicy !== null ? { markupModePolicy } : {}),
282
341
  ...(validatorIssues.length > 0 ? { validatorIssues } : {}),
283
342
  };
284
343
  }
285
344
 
345
+ function mergeEditorStateWorkflowMetadata(
346
+ base: WorkflowMetadataSnapshot,
347
+ editorState: EditorStatePayload | undefined,
348
+ ): WorkflowMetadataSnapshot {
349
+ if (!editorState) {
350
+ return base;
351
+ }
352
+ const inlineEntry = editorState.entries.find(
353
+ (candidate) => candidate.namespace === "workflowMetadata" && candidate.inline !== undefined,
354
+ );
355
+ if (!inlineEntry || typeof inlineEntry.inline !== "object" || inlineEntry.inline === null) {
356
+ return base;
357
+ }
358
+ const inlinePayload = inlineEntry.inline as {
359
+ definitions?: WorkflowMetadataDefinition[];
360
+ entries?: WorkflowMetadataEntry[];
361
+ };
362
+ const inlineDefinitions = Array.isArray(inlinePayload.definitions) ? inlinePayload.definitions : [];
363
+ const inlineEntries = Array.isArray(inlinePayload.entries) ? inlinePayload.entries : [];
364
+
365
+ const baseDefinitionIds = new Set(base.definitions.map((definition) => definition.metadataId));
366
+ const baseEntryKeys = new Set(
367
+ base.entries.map((entry) => `${entry.entryId}${entry.metadataId}`),
368
+ );
369
+
370
+ const mergedDefinitions = [
371
+ ...base.definitions,
372
+ ...inlineDefinitions.filter(
373
+ (definition) =>
374
+ definition !== null &&
375
+ typeof definition === "object" &&
376
+ typeof definition.metadataId === "string" &&
377
+ !baseDefinitionIds.has(definition.metadataId),
378
+ ),
379
+ ];
380
+ const mergedEntries = [
381
+ ...base.entries,
382
+ ...inlineEntries.filter(
383
+ (entry) =>
384
+ entry !== null &&
385
+ typeof entry === "object" &&
386
+ typeof entry.entryId === "string" &&
387
+ typeof entry.metadataId === "string" &&
388
+ !baseEntryKeys.has(`${entry.entryId}${entry.metadataId}`),
389
+ ),
390
+ ];
391
+ return {
392
+ definitions: mergedDefinitions,
393
+ entries: mergedEntries,
394
+ };
395
+ }
396
+
286
397
  function needsSchemaV11(input: {
287
398
  workflowMetadata: WorkflowMetadataSnapshot;
288
399
  workflowOverlay?: WorkflowOverlay;
@@ -460,10 +571,16 @@ function buildPayloadXml(input: {
460
571
  workflowMetadata: WorkflowMetadataSnapshot;
461
572
  workflowOverlay?: WorkflowOverlay;
462
573
  editorState?: EditorStatePayload;
574
+ /** W10 — class-A overlay-visibility policies. Empty = block omitted. */
575
+ visibilityPolicies: readonly OverlayVisibilityPolicy[];
576
+ /** X5 — class-A markup-mode policy. `null` = block omitted. */
577
+ markupModePolicy: WorkflowMarkupModePolicy | null;
463
578
  preservedExtensionsXml: string;
464
579
  }): string {
465
580
  const hasEditorState = hasNonEmptyEditorState(input.editorState);
466
- const schemaVersion = hasEditorState
581
+ const hasVisibilityPolicies = input.visibilityPolicies.length > 0;
582
+ const hasMarkupModePolicy = input.markupModePolicy !== null;
583
+ const schemaVersion = hasEditorState || hasVisibilityPolicies || hasMarkupModePolicy
467
584
  ? "1.2"
468
585
  : needsSchemaV11(input)
469
586
  ? "1.1"
@@ -523,6 +640,12 @@ function buildPayloadXml(input: {
523
640
  .join("\n");
524
641
 
525
642
  const editorStateXml = hasEditorState ? buildEditorStateXml(input.editorState!) : "";
643
+ const overlayVisibilityXml = hasVisibilityPolicies
644
+ ? buildOverlayVisibilityXml(input.visibilityPolicies)
645
+ : "";
646
+ const markupModePolicyXml = input.markupModePolicy
647
+ ? buildMarkupModePolicyXml(input.markupModePolicy)
648
+ : "";
526
649
 
527
650
  return [
528
651
  `<?xml version="1.0" encoding="UTF-8"?>`,
@@ -535,6 +658,8 @@ function buildPayloadXml(input: {
535
658
  definitionEntriesXml ? indentLines(definitionEntriesXml, 4) : "",
536
659
  metadataEntriesXml ? indentLines(metadataEntriesXml, 4) : "",
537
660
  ` </bw:metadata>`,
661
+ overlayVisibilityXml,
662
+ markupModePolicyXml,
538
663
  editorStateXml,
539
664
  extensionsXml
540
665
  ? ` <bw:extensions>\n${indentLines(extensionsXml, 4)}\n </bw:extensions>`
@@ -543,6 +668,156 @@ function buildPayloadXml(input: {
543
668
  ].filter((line) => line.length > 0).join("\n");
544
669
  }
545
670
 
671
+ // ---------------------------------------------------------------------------
672
+ // W10 · <bw:overlayVisibility> block
673
+ //
674
+ // Fixed OverlayKind order for byte-identical round-trip. See
675
+ // `src/runtime/workflow/visibility-policy.ts` for the canonical order.
676
+ // ---------------------------------------------------------------------------
677
+
678
+ const OVERLAY_KIND_ORDER: readonly OverlayKind[] = [
679
+ "scope-rail",
680
+ "comments",
681
+ "tracked-changes",
682
+ "suggestions",
683
+ "debug-panel",
684
+ "presence",
685
+ ];
686
+
687
+ function buildOverlayVisibilityXml(
688
+ policies: readonly OverlayVisibilityPolicy[],
689
+ ): string {
690
+ if (policies.length === 0) return "";
691
+ const index = (kind: OverlayKind): number => OVERLAY_KIND_ORDER.indexOf(kind);
692
+ const ordered = [...policies].sort((a, b) => index(a.kind) - index(b.kind));
693
+ const body = ordered.map(formatOverlayVisibilityPolicy).join("\n");
694
+ return ` <bw:overlayVisibility>\n${body}\n </bw:overlayVisibility>`;
695
+ }
696
+
697
+ function formatOverlayVisibilityPolicy(policy: OverlayVisibilityPolicy): string {
698
+ const attrs = [
699
+ ` kind="${escapeXml(policy.kind)}"`,
700
+ ` enforcement="${escapeXml(policy.enforcement)}"`,
701
+ ` defaultOn="${policy.defaultOn ? "true" : "false"}"`,
702
+ ].join("");
703
+ if (!policy.authoredBy) {
704
+ return ` <bw:policy${attrs}/>`;
705
+ }
706
+ return [
707
+ ` <bw:policy${attrs}>`,
708
+ ` <bw:authoredBy actorId="${escapeXml(policy.authoredBy.actorId)}" at="${escapeXml(policy.authoredBy.at)}"/>`,
709
+ ` </bw:policy>`,
710
+ ].join("\n");
711
+ }
712
+
713
+ const VALID_OVERLAY_KINDS: ReadonlySet<OverlayKind> = new Set(OVERLAY_KIND_ORDER);
714
+ const VALID_ENFORCEMENT: ReadonlySet<OverlayVisibilityEnforcement> = new Set([
715
+ "authored-default",
716
+ "always",
717
+ "never",
718
+ ]);
719
+
720
+ // ---------------------------------------------------------------------------
721
+ // X5 · <bw:markupModePolicy> block
722
+ //
723
+ // Single-record policy; no ordering concerns. Byte-identical round-trip
724
+ // when unchanged: same attribute ordering (mode, enforcement, optional
725
+ // authoredBy child) every emit.
726
+ // ---------------------------------------------------------------------------
727
+
728
+ const VALID_MARKUP_MODES: ReadonlySet<WorkflowMarkupMode> = new Set([
729
+ "clean",
730
+ "simple",
731
+ "all",
732
+ ]);
733
+ const VALID_MARKUP_ENFORCEMENT: ReadonlySet<WorkflowMarkupModeEnforcement> = new Set([
734
+ "authored-default",
735
+ "always",
736
+ ]);
737
+
738
+ function buildMarkupModePolicyXml(policy: WorkflowMarkupModePolicy): string {
739
+ const attrs = [
740
+ ` mode="${escapeXml(policy.mode)}"`,
741
+ ` enforcement="${escapeXml(policy.enforcement)}"`,
742
+ ].join("");
743
+ if (!policy.authoredBy) {
744
+ return ` <bw:markupModePolicy${attrs}/>`;
745
+ }
746
+ return [
747
+ ` <bw:markupModePolicy${attrs}>`,
748
+ ` <bw:authoredBy actorId="${escapeXml(policy.authoredBy.actorId)}" at="${escapeXml(policy.authoredBy.at)}"/>`,
749
+ ` </bw:markupModePolicy>`,
750
+ ].join("\n");
751
+ }
752
+
753
+ function parseMarkupModePolicyBlock(xml: string): WorkflowMarkupModePolicy | null {
754
+ const match = xml.match(
755
+ /<bw:markupModePolicy\b([^>]*?)(?:\/>|>([\s\S]*?)<\/bw:markupModePolicy>)/u,
756
+ );
757
+ if (!match) return null;
758
+ const attrs = parseAttributes(match[1] ?? "");
759
+ const mode = attrs.mode;
760
+ const enforcement = attrs.enforcement;
761
+ if (!mode || !VALID_MARKUP_MODES.has(mode as WorkflowMarkupMode)) return null;
762
+ if (!enforcement || !VALID_MARKUP_ENFORCEMENT.has(enforcement as WorkflowMarkupModeEnforcement)) {
763
+ return null;
764
+ }
765
+ const childXml = match[2] ?? "";
766
+ const authoredMatch = childXml.match(/<bw:authoredBy\b([^>]*)\/>/u);
767
+ const authoredAttrs = authoredMatch
768
+ ? parseAttributes(authoredMatch[1] ?? "")
769
+ : undefined;
770
+ const authoredBy =
771
+ authoredAttrs?.actorId && authoredAttrs?.at
772
+ ? { actorId: authoredAttrs.actorId, at: authoredAttrs.at }
773
+ : undefined;
774
+ return {
775
+ mode: mode as WorkflowMarkupMode,
776
+ enforcement: enforcement as WorkflowMarkupModeEnforcement,
777
+ ...(authoredBy ? { authoredBy } : {}),
778
+ };
779
+ }
780
+
781
+ function parseOverlayVisibilityBlock(
782
+ xml: string,
783
+ ): readonly OverlayVisibilityPolicy[] {
784
+ const block = xml.match(
785
+ /<bw:overlayVisibility\b[^>]*>([\s\S]*?)<\/bw:overlayVisibility>/u,
786
+ );
787
+ if (!block) return [];
788
+ const body = block[1] ?? "";
789
+ const policies: OverlayVisibilityPolicy[] = [];
790
+ const pattern =
791
+ /<bw:policy\b([^>]*?)(?:\/>|>([\s\S]*?)<\/bw:policy>)/gu;
792
+ for (const match of body.matchAll(pattern)) {
793
+ const attrs = parseAttributes(match[1] ?? "");
794
+ const kind = attrs.kind;
795
+ const enforcement = attrs.enforcement;
796
+ const defaultOnRaw = attrs.defaultOn;
797
+ if (!kind || !VALID_OVERLAY_KINDS.has(kind as OverlayKind)) continue;
798
+ if (!enforcement || !VALID_ENFORCEMENT.has(enforcement as OverlayVisibilityEnforcement)) continue;
799
+ if (defaultOnRaw !== "true" && defaultOnRaw !== "false") continue;
800
+
801
+ const childXml = match[2] ?? "";
802
+ const authoredMatch = childXml.match(/<bw:authoredBy\b([^>]*)\/>/u);
803
+ const authoredAttrs = authoredMatch
804
+ ? parseAttributes(authoredMatch[1] ?? "")
805
+ : undefined;
806
+ const authoredBy =
807
+ authoredAttrs?.actorId && authoredAttrs?.at
808
+ ? { actorId: authoredAttrs.actorId, at: authoredAttrs.at }
809
+ : undefined;
810
+
811
+ policies.push({
812
+ kind: kind as OverlayKind,
813
+ enforcement: enforcement as OverlayVisibilityEnforcement,
814
+ defaultOn: defaultOnRaw === "true",
815
+ ...(authoredBy ? { authoredBy } : {}),
816
+ });
817
+ }
818
+ return policies;
819
+ }
820
+
546
821
  function buildItemPropsXml(itemId: string): string {
547
822
  return [
548
823
  `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>`,
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Pure-data anchor shapes shared by the review-record types in
3
+ * `src/model/review/**` and the mapping mechanism in
4
+ * `src/core/selection/mapping.ts`. Anchors are model-level concerns — they
5
+ * describe where on a `CanonicalDocument` a revision or comment is
6
+ * attached — while the mapping / remap engine that evolves anchors across
7
+ * transactions remains in core.
8
+ *
9
+ * Moved here from `src/core/selection/mapping.ts` so the model layer can
10
+ * define `RevisionRecord` + `CommentThread` without importing up into core.
11
+ * The old mapping module re-exports every symbol in this file for
12
+ * back-compat; existing call-sites continue to work unchanged.
13
+ */
14
+
15
+ export type Position = number;
16
+ export type Assoc = -1 | 1;
17
+
18
+ export interface DocRange {
19
+ from: Position;
20
+ to: Position;
21
+ }
22
+
23
+ export interface BoundaryAssoc {
24
+ start: Assoc;
25
+ end: Assoc;
26
+ }
27
+
28
+ export interface RangeAnchor {
29
+ kind: "range";
30
+ range: DocRange;
31
+ assoc: BoundaryAssoc;
32
+ }
33
+
34
+ export interface NodeAnchor {
35
+ kind: "node";
36
+ at: Position;
37
+ assoc: Assoc;
38
+ }
39
+
40
+ export interface DetachedAnchor {
41
+ kind: "detached";
42
+ lastKnownRange: DocRange;
43
+ reason: "deleted" | "invalidatedByStructureChange" | "importAmbiguity";
44
+ }
45
+
46
+ /**
47
+ * Internal representation of an anchor projection — uses `DocRange` so
48
+ * mapping helpers compose ranges independently of assoc semantics. The
49
+ * public-facing shape lives at `src/api/public-types.ts` (same simple name
50
+ * `EditorAnchorProjection`) and is flat (`{ kind: "range", from, to, assoc }`).
51
+ * Conversion between the two goes through `src/core/selection/anchor-conversion.ts`.
52
+ */
53
+ export type InternalEditorAnchorProjection = RangeAnchor | NodeAnchor | DetachedAnchor;
54
+
55
+ /**
56
+ * @deprecated X3 Phase 5 — prefer `InternalEditorAnchorProjection` to avoid
57
+ * name-collision with the public `EditorAnchorProjection` from
58
+ * `src/api/public-types.ts`. Internal call-sites have migrated (via
59
+ * `import type { EditorAnchorProjection as InternalEditorAnchorProjection }`
60
+ * aliasing in `document-runtime.ts`). This alias stays for one release cycle
61
+ * so third-party internal consumers don't break. Remove in v2.1+.
62
+ */
63
+ export type EditorAnchorProjection = InternalEditorAnchorProjection;
64
+
65
+ export const DEFAULT_BOUNDARY_ASSOC: BoundaryAssoc = {
66
+ start: 1,
67
+ end: -1,
68
+ };
69
+
70
+ export function normalizeRange(range: DocRange): DocRange {
71
+ return range.from <= range.to
72
+ ? { from: range.from, to: range.to }
73
+ : { from: range.to, to: range.from };
74
+ }
75
+
76
+ export function createDetachedAnchor(
77
+ lastKnownRange: DocRange,
78
+ reason: DetachedAnchor["reason"],
79
+ ): DetachedAnchor {
80
+ return {
81
+ kind: "detached",
82
+ lastKnownRange: normalizeRange(lastKnownRange),
83
+ reason,
84
+ };
85
+ }