@beyondwork/docx-react-component 1.0.29 → 1.0.31

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 (383) hide show
  1. package/package.json +65 -96
  2. package/src/README.md +85 -0
  3. package/src/api/README.md +26 -0
  4. package/src/api/public-types.ts +1952 -0
  5. package/src/api/session-state.ts +62 -0
  6. package/src/compare/diff-engine.ts +623 -0
  7. package/src/compare/export-redlines.ts +280 -0
  8. package/src/compare/index.ts +25 -0
  9. package/src/compare/snapshot.ts +97 -0
  10. package/src/component-inventory.md +99 -0
  11. package/src/core/README.md +10 -0
  12. package/src/core/commands/README.md +3 -0
  13. package/{dist/chunk-TJBP2K4T.js → src/core/commands/formatting-commands.ts} +536 -196
  14. package/src/core/commands/image-commands.ts +373 -0
  15. package/src/core/commands/index.ts +1879 -0
  16. package/src/core/commands/list-commands.ts +565 -0
  17. package/src/core/commands/paragraph-layout-commands.ts +339 -0
  18. package/src/core/commands/review-commands.ts +108 -0
  19. package/{dist/core/commands/section-layout-commands.cjs → src/core/commands/section-layout-commands.ts} +340 -137
  20. package/src/core/commands/structural-helpers.ts +309 -0
  21. package/{dist/core/commands/style-commands.cjs → src/core/commands/style-commands.ts} +113 -65
  22. package/src/core/commands/table-structure-commands.ts +854 -0
  23. package/{dist/chunk-UZXBISGO.js → src/core/commands/text-commands.ts} +142 -86
  24. package/src/core/schema/README.md +3 -0
  25. package/src/core/schema/text-schema.ts +516 -0
  26. package/src/core/search/search-text.ts +357 -0
  27. package/src/core/selection/README.md +3 -0
  28. package/src/core/selection/mapping.ts +289 -0
  29. package/src/core/selection/review-anchors.ts +183 -0
  30. package/src/core/state/README.md +3 -0
  31. package/src/core/state/editor-state.ts +892 -0
  32. package/src/core/state/text-transaction.ts +869 -0
  33. package/src/formats/xlsx/io/parse-shared-strings.ts +41 -0
  34. package/src/formats/xlsx/io/parse-sheet.ts +459 -0
  35. package/src/formats/xlsx/io/parse-styles.ts +59 -0
  36. package/src/formats/xlsx/io/parse-workbook.ts +75 -0
  37. package/src/formats/xlsx/io/serialize-shared-strings.ts +72 -0
  38. package/src/formats/xlsx/io/serialize-sheet.ts +333 -0
  39. package/src/formats/xlsx/io/serialize-styles.ts +98 -0
  40. package/src/formats/xlsx/io/serialize-workbook.ts +429 -0
  41. package/src/formats/xlsx/io/xlsx-session.ts +314 -0
  42. package/src/formats/xlsx/model/cell.ts +189 -0
  43. package/src/formats/xlsx/model/sheet.ts +326 -0
  44. package/src/formats/xlsx/model/styles.ts +118 -0
  45. package/src/formats/xlsx/model/workbook.ts +453 -0
  46. package/src/formats/xlsx/runtime/cell-commands.ts +567 -0
  47. package/src/formats/xlsx/runtime/sheet-commands.ts +206 -0
  48. package/src/formats/xlsx/runtime/workbook-runtime.ts +177 -0
  49. package/src/formats/xlsx/runtime/workbook-transaction.ts +822 -0
  50. package/src/index.ts +142 -0
  51. package/src/io/README.md +10 -0
  52. package/src/io/docx-session.ts +3175 -0
  53. package/src/io/export/README.md +3 -0
  54. package/src/io/export/export-session.ts +220 -0
  55. package/src/io/export/minimal-docx.ts +115 -0
  56. package/src/io/export/reattach-preserved-parts.ts +54 -0
  57. package/src/io/export/serialize-comments.ts +947 -0
  58. package/src/io/export/serialize-footnotes.ts +394 -0
  59. package/src/io/export/serialize-headers-footers.ts +368 -0
  60. package/src/io/export/serialize-main-document.ts +1342 -0
  61. package/src/io/export/serialize-numbering.ts +218 -0
  62. package/src/io/export/serialize-revisions.ts +389 -0
  63. package/src/io/export/serialize-runtime-revisions.ts +463 -0
  64. package/src/io/export/serialize-tables.ts +174 -0
  65. package/src/io/export/split-review-boundaries.ts +356 -0
  66. package/src/io/export/split-story-blocks-for-runtime-revisions.ts +252 -0
  67. package/src/io/export/table-properties-xml.ts +318 -0
  68. package/src/io/normalize/README.md +3 -0
  69. package/src/io/normalize/normalize-text.ts +670 -0
  70. package/src/io/ooxml/README.md +3 -0
  71. package/src/io/ooxml/highlight-colors.ts +39 -0
  72. package/src/io/ooxml/numbering-sentinels.ts +44 -0
  73. package/src/io/ooxml/parse-comments.ts +852 -0
  74. package/src/io/ooxml/parse-complex-content.ts +287 -0
  75. package/src/io/ooxml/parse-fields.ts +834 -0
  76. package/src/io/ooxml/parse-footnotes.ts +952 -0
  77. package/src/io/ooxml/parse-headers-footers.ts +1212 -0
  78. package/src/io/ooxml/parse-inline-media.ts +461 -0
  79. package/src/io/ooxml/parse-main-document.ts +2947 -0
  80. package/src/io/ooxml/parse-numbering.ts +747 -0
  81. package/src/io/ooxml/parse-revisions.ts +1045 -0
  82. package/src/io/ooxml/parse-settings.ts +184 -0
  83. package/src/io/ooxml/parse-shapes.ts +296 -0
  84. package/src/io/ooxml/parse-styles.ts +639 -0
  85. package/src/io/ooxml/parse-tables.ts +627 -0
  86. package/src/io/ooxml/parse-theme.ts +346 -0
  87. package/src/io/ooxml/part-manifest.ts +136 -0
  88. package/src/io/ooxml/revision-boundaries.ts +475 -0
  89. package/src/io/ooxml/workflow-payload.ts +544 -0
  90. package/src/io/opc/README.md +3 -0
  91. package/src/io/opc/corrupt-package.ts +166 -0
  92. package/src/io/opc/docx-package.ts +74 -0
  93. package/src/io/opc/package-reader.ts +325 -0
  94. package/src/io/opc/package-writer.ts +273 -0
  95. package/src/io/source-package-provenance.ts +241 -0
  96. package/{dist/chunk-RMH72RZI.js → src/legal/bookmarks.ts} +130 -44
  97. package/src/legal/cross-references.ts +414 -0
  98. package/src/legal/defined-terms.ts +203 -0
  99. package/src/legal/index.ts +32 -0
  100. package/src/legal/signature-blocks.ts +259 -0
  101. package/src/model/README.md +3 -0
  102. package/src/model/canonical-document.ts +2722 -0
  103. package/src/model/cds-1.0.0.ts +212 -0
  104. package/src/model/snapshot.ts +760 -0
  105. package/src/preservation/README.md +3 -0
  106. package/src/preservation/markup-compatibility.ts +48 -0
  107. package/src/preservation/opaque-fragment-store.ts +89 -0
  108. package/src/preservation/opaque-region.ts +233 -0
  109. package/src/preservation/package-preservation.ts +113 -0
  110. package/src/preservation/preserved-part-manifest.ts +56 -0
  111. package/src/preservation/relationship-retention.ts +57 -0
  112. package/src/preservation/store.ts +255 -0
  113. package/src/review/README.md +16 -0
  114. package/src/review/store/README.md +3 -0
  115. package/src/review/store/comment-anchors.ts +70 -0
  116. package/src/review/store/comment-remapping.ts +154 -0
  117. package/src/review/store/comment-store.ts +349 -0
  118. package/src/review/store/comment-thread.ts +109 -0
  119. package/src/review/store/revision-actions.ts +423 -0
  120. package/src/review/store/revision-store.ts +323 -0
  121. package/src/review/store/revision-types.ts +182 -0
  122. package/src/review/store/runtime-comment-store.ts +43 -0
  123. package/src/runtime/README.md +3 -0
  124. package/src/runtime/ai-action-policy.ts +764 -0
  125. package/src/runtime/context-analytics.ts +824 -0
  126. package/src/runtime/document-layout.ts +332 -0
  127. package/src/runtime/document-locations.ts +521 -0
  128. package/src/runtime/document-navigation.ts +616 -0
  129. package/src/runtime/document-outline.ts +440 -0
  130. package/src/runtime/document-runtime.ts +4055 -0
  131. package/src/runtime/document-search.ts +145 -0
  132. package/src/runtime/event-refresh-hints.ts +137 -0
  133. package/src/runtime/numbering-prefix.ts +244 -0
  134. package/src/runtime/page-layout-estimation.ts +305 -0
  135. package/src/runtime/read-only-diagnostics-runtime.ts +241 -0
  136. package/src/runtime/resolved-numbering-geometry.ts +293 -0
  137. package/src/runtime/review-runtime.ts +44 -0
  138. package/src/runtime/revision-runtime.ts +107 -0
  139. package/src/runtime/session-capabilities.ts +192 -0
  140. package/src/runtime/story-context.ts +164 -0
  141. package/src/runtime/story-targeting.ts +162 -0
  142. package/src/runtime/suggestions-snapshot.ts +137 -0
  143. package/src/runtime/surface-projection.ts +1553 -0
  144. package/src/runtime/table-commands.ts +173 -0
  145. package/src/runtime/table-schema.ts +309 -0
  146. package/src/runtime/table-style-resolver.ts +409 -0
  147. package/src/runtime/view-state.ts +493 -0
  148. package/src/runtime/virtualized-rendering.ts +258 -0
  149. package/src/runtime/workflow-markup.ts +393 -0
  150. package/src/ui/README.md +30 -0
  151. package/src/ui/WordReviewEditor.tsx +5268 -0
  152. package/src/ui/browser-export.ts +52 -0
  153. package/src/ui/comments/README.md +3 -0
  154. package/src/ui/compatibility/README.md +3 -0
  155. package/src/ui/editor-command-bag.ts +127 -0
  156. package/src/ui/editor-runtime-boundary.ts +1558 -0
  157. package/src/ui/editor-shell-view.tsx +144 -0
  158. package/src/ui/editor-surface/README.md +3 -0
  159. package/src/ui/editor-surface-controller.tsx +66 -0
  160. package/src/ui/headless/comment-decoration-model.ts +124 -0
  161. package/src/ui/headless/preserve-editor-selection.ts +5 -0
  162. package/src/ui/headless/revision-decoration-model.ts +128 -0
  163. package/src/ui/headless/selection-helpers.ts +54 -0
  164. package/src/ui/headless/selection-tool-context.ts +19 -0
  165. package/src/ui/headless/selection-tool-resolver.ts +752 -0
  166. package/src/ui/headless/selection-tool-types.ts +129 -0
  167. package/src/ui/headless/selection-toolbar-model.ts +11 -0
  168. package/src/ui/headless/use-editor-keyboard.ts +103 -0
  169. package/src/ui/review/README.md +3 -0
  170. package/src/ui/runtime-shortcut-dispatch.ts +365 -0
  171. package/src/ui/runtime-snapshot-selectors.ts +197 -0
  172. package/src/ui/shared/revision-filters.ts +31 -0
  173. package/src/ui/status/README.md +3 -0
  174. package/src/ui/theme/README.md +3 -0
  175. package/src/ui/toolbar/README.md +3 -0
  176. package/src/ui/workflow-surface-blocked-rails.ts +94 -0
  177. package/src/ui-tailwind/chrome/chrome-preset-model.ts +107 -0
  178. package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +15 -0
  179. package/src/ui-tailwind/chrome/responsive-chrome.ts +46 -0
  180. package/src/ui-tailwind/chrome/review-queue-bar.tsx +97 -0
  181. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +64 -0
  182. package/src/ui-tailwind/chrome/tw-context-analytics-summary.tsx +122 -0
  183. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +121 -0
  184. package/src/ui-tailwind/chrome/tw-layout-panel.tsx +114 -0
  185. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +30 -0
  186. package/src/ui-tailwind/chrome/tw-page-ruler.tsx +365 -0
  187. package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +23 -0
  188. package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +35 -0
  189. package/src/ui-tailwind/chrome/tw-selection-tool-formatting.tsx +37 -0
  190. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +303 -0
  191. package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +116 -0
  192. package/src/ui-tailwind/chrome/tw-selection-tool-suggestion.tsx +29 -0
  193. package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +27 -0
  194. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +186 -0
  195. package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +139 -0
  196. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +250 -0
  197. package/src/ui-tailwind/chrome/tw-unsaved-modal.tsx +58 -0
  198. package/src/ui-tailwind/chrome/use-before-unload.ts +20 -0
  199. package/src/ui-tailwind/editor-surface/perf-probe.ts +179 -0
  200. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +189 -0
  201. package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +31 -0
  202. package/src/ui-tailwind/editor-surface/pm-decorations.ts +411 -0
  203. package/src/ui-tailwind/editor-surface/pm-position-map.ts +123 -0
  204. package/src/ui-tailwind/editor-surface/pm-schema.ts +927 -0
  205. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +567 -0
  206. package/src/ui-tailwind/editor-surface/search-plugin.ts +168 -0
  207. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +63 -0
  208. package/src/ui-tailwind/editor-surface/tw-caret.tsx +12 -0
  209. package/src/ui-tailwind/editor-surface/tw-editor-surface.tsx +150 -0
  210. package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +129 -0
  211. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +58 -0
  212. package/src/ui-tailwind/editor-surface/tw-paragraph-block.tsx +151 -0
  213. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +1047 -0
  214. package/src/ui-tailwind/editor-surface/tw-segment-view.tsx +111 -0
  215. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +503 -0
  216. package/src/ui-tailwind/index.ts +62 -0
  217. package/src/ui-tailwind/page-chrome-model.ts +27 -0
  218. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +406 -0
  219. package/src/ui-tailwind/review/tw-health-panel.tsx +149 -0
  220. package/src/ui-tailwind/review/tw-review-rail.tsx +130 -0
  221. package/src/ui-tailwind/review/tw-revision-sidebar.tsx +164 -0
  222. package/src/ui-tailwind/status/tw-status-bar.tsx +65 -0
  223. package/{dist → src}/ui-tailwind/theme/editor-theme.css +58 -40
  224. package/src/ui-tailwind/toolbar/toolbar-layout.ts +47 -0
  225. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +52 -0
  226. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +1478 -0
  227. package/src/ui-tailwind/tw-review-workspace.tsx +1587 -0
  228. package/src/validation/README.md +3 -0
  229. package/src/validation/compatibility-engine.ts +878 -0
  230. package/src/validation/compatibility-report.ts +161 -0
  231. package/src/validation/diagnostics.ts +204 -0
  232. package/src/validation/docx-comment-proof.ts +720 -0
  233. package/src/validation/import-diagnostics.ts +128 -0
  234. package/src/validation/low-priority-word-surfaces.ts +373 -0
  235. package/dist/canonical-document-BLEbzL2J.d.cts +0 -844
  236. package/dist/canonical-document-BLEbzL2J.d.ts +0 -844
  237. package/dist/chunk-2FJS5GZM.js +0 -763
  238. package/dist/chunk-2FJS5GZM.js.map +0 -1
  239. package/dist/chunk-2OQBZS3F.js +0 -446
  240. package/dist/chunk-2OQBZS3F.js.map +0 -1
  241. package/dist/chunk-2S7W4KFO.js +0 -127
  242. package/dist/chunk-2S7W4KFO.js.map +0 -1
  243. package/dist/chunk-2TG72QSW.js +0 -3874
  244. package/dist/chunk-2TG72QSW.js.map +0 -1
  245. package/dist/chunk-36QNIZBO.js +0 -532
  246. package/dist/chunk-36QNIZBO.js.map +0 -1
  247. package/dist/chunk-4AQOYAW4.js +0 -3069
  248. package/dist/chunk-4AQOYAW4.js.map +0 -1
  249. package/dist/chunk-4D5EWJ3P.js +0 -77
  250. package/dist/chunk-4D5EWJ3P.js.map +0 -1
  251. package/dist/chunk-5FN54NDH.js +0 -2257
  252. package/dist/chunk-5FN54NDH.js.map +0 -1
  253. package/dist/chunk-BOYGQYRQ.js +0 -7306
  254. package/dist/chunk-BOYGQYRQ.js.map +0 -1
  255. package/dist/chunk-CN3XMECL.js +0 -212
  256. package/dist/chunk-CN3XMECL.js.map +0 -1
  257. package/dist/chunk-EBI3BX6U.js +0 -164
  258. package/dist/chunk-EBI3BX6U.js.map +0 -1
  259. package/dist/chunk-EILUG3VB.js +0 -1275
  260. package/dist/chunk-EILUG3VB.js.map +0 -1
  261. package/dist/chunk-FUDY333O.js +0 -70
  262. package/dist/chunk-FUDY333O.js.map +0 -1
  263. package/dist/chunk-GBVOWFIK.js +0 -1237
  264. package/dist/chunk-GBVOWFIK.js.map +0 -1
  265. package/dist/chunk-H4TQ3H3Y.js +0 -262
  266. package/dist/chunk-H4TQ3H3Y.js.map +0 -1
  267. package/dist/chunk-JGB3IXZO.js +0 -189
  268. package/dist/chunk-JGB3IXZO.js.map +0 -1
  269. package/dist/chunk-KD2QRQPY.js +0 -4342
  270. package/dist/chunk-KD2QRQPY.js.map +0 -1
  271. package/dist/chunk-KLMXQVYK.js +0 -369
  272. package/dist/chunk-KLMXQVYK.js.map +0 -1
  273. package/dist/chunk-KZUG5KFQ.js +0 -214
  274. package/dist/chunk-KZUG5KFQ.js.map +0 -1
  275. package/dist/chunk-QDAQ4CJU.js +0 -345
  276. package/dist/chunk-QDAQ4CJU.js.map +0 -1
  277. package/dist/chunk-RMH72RZI.js.map +0 -1
  278. package/dist/chunk-SWKWQZXM.js +0 -117
  279. package/dist/chunk-SWKWQZXM.js.map +0 -1
  280. package/dist/chunk-TJBP2K4T.js.map +0 -1
  281. package/dist/chunk-TLCEAQDQ.js +0 -542
  282. package/dist/chunk-TLCEAQDQ.js.map +0 -1
  283. package/dist/chunk-UZXBISGO.js.map +0 -1
  284. package/dist/chunk-WGBAKP3Q.js +0 -3220
  285. package/dist/chunk-WGBAKP3Q.js.map +0 -1
  286. package/dist/compare/index.cjs +0 -5475
  287. package/dist/compare/index.cjs.map +0 -1
  288. package/dist/compare/index.d.cts +0 -114
  289. package/dist/compare/index.d.ts +0 -114
  290. package/dist/compare/index.js +0 -731
  291. package/dist/compare/index.js.map +0 -1
  292. package/dist/core/commands/formatting-commands.cjs +0 -828
  293. package/dist/core/commands/formatting-commands.cjs.map +0 -1
  294. package/dist/core/commands/formatting-commands.d.cts +0 -63
  295. package/dist/core/commands/formatting-commands.d.ts +0 -63
  296. package/dist/core/commands/formatting-commands.js +0 -37
  297. package/dist/core/commands/formatting-commands.js.map +0 -1
  298. package/dist/core/commands/image-commands.cjs +0 -2023
  299. package/dist/core/commands/image-commands.cjs.map +0 -1
  300. package/dist/core/commands/image-commands.d.cts +0 -58
  301. package/dist/core/commands/image-commands.d.ts +0 -58
  302. package/dist/core/commands/image-commands.js +0 -18
  303. package/dist/core/commands/image-commands.js.map +0 -1
  304. package/dist/core/commands/section-layout-commands.cjs.map +0 -1
  305. package/dist/core/commands/section-layout-commands.d.cts +0 -62
  306. package/dist/core/commands/section-layout-commands.d.ts +0 -62
  307. package/dist/core/commands/section-layout-commands.js +0 -21
  308. package/dist/core/commands/section-layout-commands.js.map +0 -1
  309. package/dist/core/commands/style-commands.cjs.map +0 -1
  310. package/dist/core/commands/style-commands.d.cts +0 -13
  311. package/dist/core/commands/style-commands.d.ts +0 -13
  312. package/dist/core/commands/style-commands.js +0 -9
  313. package/dist/core/commands/style-commands.js.map +0 -1
  314. package/dist/core/commands/table-structure-commands.cjs +0 -1883
  315. package/dist/core/commands/table-structure-commands.cjs.map +0 -1
  316. package/dist/core/commands/table-structure-commands.d.cts +0 -59
  317. package/dist/core/commands/table-structure-commands.d.ts +0 -59
  318. package/dist/core/commands/table-structure-commands.js +0 -12
  319. package/dist/core/commands/table-structure-commands.js.map +0 -1
  320. package/dist/core/commands/text-commands.cjs +0 -2391
  321. package/dist/core/commands/text-commands.cjs.map +0 -1
  322. package/dist/core/commands/text-commands.d.cts +0 -24
  323. package/dist/core/commands/text-commands.d.ts +0 -24
  324. package/dist/core/commands/text-commands.js +0 -28
  325. package/dist/core/commands/text-commands.js.map +0 -1
  326. package/dist/core/selection/mapping.cjs +0 -200
  327. package/dist/core/selection/mapping.cjs.map +0 -1
  328. package/dist/core/selection/mapping.d.cts +0 -2
  329. package/dist/core/selection/mapping.d.ts +0 -2
  330. package/dist/core/selection/mapping.js +0 -31
  331. package/dist/core/selection/mapping.js.map +0 -1
  332. package/dist/core/state/editor-state.cjs +0 -2278
  333. package/dist/core/state/editor-state.cjs.map +0 -1
  334. package/dist/core/state/editor-state.d.cts +0 -2
  335. package/dist/core/state/editor-state.d.ts +0 -2
  336. package/dist/core/state/editor-state.js +0 -26
  337. package/dist/core/state/editor-state.js.map +0 -1
  338. package/dist/index.cjs +0 -38553
  339. package/dist/index.cjs.map +0 -1
  340. package/dist/index.d.cts +0 -15
  341. package/dist/index.d.ts +0 -15
  342. package/dist/index.js +0 -7856
  343. package/dist/index.js.map +0 -1
  344. package/dist/io/docx-session.cjs +0 -16236
  345. package/dist/io/docx-session.cjs.map +0 -1
  346. package/dist/io/docx-session.d.cts +0 -21
  347. package/dist/io/docx-session.d.ts +0 -21
  348. package/dist/io/docx-session.js +0 -18
  349. package/dist/io/docx-session.js.map +0 -1
  350. package/dist/legal/index.cjs +0 -3900
  351. package/dist/legal/index.cjs.map +0 -1
  352. package/dist/legal/index.d.cts +0 -86
  353. package/dist/legal/index.d.ts +0 -86
  354. package/dist/legal/index.js +0 -616
  355. package/dist/legal/index.js.map +0 -1
  356. package/dist/public-types-7ZL_94cz.d.ts +0 -1573
  357. package/dist/public-types-CeMaDueh.d.cts +0 -1573
  358. package/dist/public-types.cjs +0 -19
  359. package/dist/public-types.cjs.map +0 -1
  360. package/dist/public-types.d.cts +0 -2
  361. package/dist/public-types.d.ts +0 -2
  362. package/dist/public-types.js +0 -1
  363. package/dist/public-types.js.map +0 -1
  364. package/dist/runtime/document-runtime.cjs +0 -11140
  365. package/dist/runtime/document-runtime.cjs.map +0 -1
  366. package/dist/runtime/document-runtime.d.cts +0 -231
  367. package/dist/runtime/document-runtime.d.ts +0 -231
  368. package/dist/runtime/document-runtime.js +0 -21
  369. package/dist/runtime/document-runtime.js.map +0 -1
  370. package/dist/structural-helpers-CilgOVhh.d.cts +0 -10
  371. package/dist/structural-helpers-q0Gd-eBN.d.ts +0 -10
  372. package/dist/ui-tailwind/editor-surface/search-plugin.cjs +0 -313
  373. package/dist/ui-tailwind/editor-surface/search-plugin.cjs.map +0 -1
  374. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +0 -67
  375. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +0 -67
  376. package/dist/ui-tailwind/editor-surface/search-plugin.js +0 -23
  377. package/dist/ui-tailwind/editor-surface/search-plugin.js.map +0 -1
  378. package/dist/ui-tailwind/index.cjs +0 -4833
  379. package/dist/ui-tailwind/index.cjs.map +0 -1
  380. package/dist/ui-tailwind/index.d.cts +0 -617
  381. package/dist/ui-tailwind/index.d.ts +0 -617
  382. package/dist/ui-tailwind/index.js +0 -575
  383. package/dist/ui-tailwind/index.js.map +0 -1
@@ -0,0 +1,1047 @@
1
+ import React, {
2
+ forwardRef,
3
+ type FocusEventHandler,
4
+ useCallback,
5
+ useEffect,
6
+ useImperativeHandle,
7
+ useMemo,
8
+ useRef,
9
+ } from "react";
10
+ import { EditorView } from "prosemirror-view";
11
+
12
+ import type {
13
+ DocumentNavigationSnapshot,
14
+ EditorUser,
15
+ RuntimeRenderSnapshot,
16
+ SearchOptions,
17
+ SearchResultSnapshot,
18
+ SelectionSnapshot,
19
+ WorkflowBlockedCommandReason,
20
+ WorkflowCandidateRange,
21
+ WorkflowMetadataMarkup,
22
+ WorkflowScope,
23
+ } from "../../api/public-types";
24
+ import type { CanonicalDocumentEnvelope } from "../../core/state/editor-state.ts";
25
+ import { searchDocument } from "../../runtime/document-search.ts";
26
+ import {
27
+ getTableSelectionDescriptor,
28
+ type TableSelectionDescriptor,
29
+ } from "../../runtime/table-commands.ts";
30
+ import {
31
+ createCommentDecorationModel,
32
+ type MarkupDisplay,
33
+ } from "../../ui/headless/comment-decoration-model";
34
+ import { createRevisionDecorationModel } from "../../ui/headless/revision-decoration-model";
35
+ import {
36
+ createPMSelectionFromSnapshot,
37
+ createPMStateFromSnapshot,
38
+ } from "./pm-state-from-snapshot";
39
+ import type { ActiveSelectionToolModel } from "../../ui/headless/selection-tool-types";
40
+ import {
41
+ createCommandBridgePlugins,
42
+ type CommandBridgeCallbacks,
43
+ } from "./pm-command-bridge";
44
+ import { buildDecorations } from "./pm-decorations";
45
+ import { createContextualInteractionPlugin } from "./pm-contextual-ui";
46
+ import {
47
+ finishPerfProbe,
48
+ incrementInvalidationCounter,
49
+ recordPerfSample,
50
+ startPerfProbe,
51
+ } from "./perf-probe";
52
+ import type { PositionMap } from "./pm-position-map";
53
+ import {
54
+ clearSearch as clearSearchPlugin,
55
+ createSearchPlugin,
56
+ DEFAULT_SEARCH_HIGHLIGHT_COLOR,
57
+ performSearch,
58
+ searchPluginKey,
59
+ } from "./search-plugin";
60
+ import {
61
+ createSurfaceDecorationKey,
62
+ createSurfaceDocumentBuildKey,
63
+ } from "./surface-build-keys";
64
+ import { tableNodeViews } from "./tw-table-node-view";
65
+ import type { SelectionToolbarAnchor } from "../../ui/headless/selection-toolbar-model";
66
+ import type { MediaPreviewDescriptor } from "./pm-state-from-snapshot";
67
+
68
+ /**
69
+ * Same props interface as the legacy TwEditorSurface — drop-in replacement.
70
+ */
71
+ export interface TwProseMirrorSurfaceProps {
72
+ currentUser: EditorUser;
73
+ snapshot: RuntimeRenderSnapshot;
74
+ canonicalDocument: CanonicalDocumentEnvelope;
75
+ documentNavigation: DocumentNavigationSnapshot;
76
+ reviewMode: "editing" | "review";
77
+ markupDisplay: MarkupDisplay;
78
+ showUnsupportedObjectPreviews?: boolean;
79
+ activeRevisionId?: string;
80
+ activeSelectionToolKind?: ActiveSelectionToolModel["kind"] | null;
81
+ showTrackedChanges?: boolean;
82
+ /** When true, the surface renders inside the page workspace (vs canvas). */
83
+ isPageWorkspace?: boolean;
84
+ onFocus: FocusEventHandler<HTMLDivElement>;
85
+ onBlur: FocusEventHandler<HTMLDivElement>;
86
+ onSelectionChange?: (selection: SelectionSnapshot) => void;
87
+ onInsertText?: (text: string) => void;
88
+ onDeleteBackward?: () => void;
89
+ onDeleteForward?: () => void;
90
+ onInsertTab?: () => void;
91
+ onOutdentTab?: () => void;
92
+ onInsertHardBreak?: () => void;
93
+ onSplitParagraph?: () => void;
94
+ onUndo?: () => void;
95
+ onRedo?: () => void;
96
+ onBlockedInput?: (command: "paste" | "drop", message: string) => void;
97
+ onCommentActivated?: (commentId: string) => void;
98
+ onRevisionActivated?: (revisionId: string) => void;
99
+ onSelectionToolbarAnchorChange?: (anchor: SelectionToolbarAnchor | null) => void;
100
+ mediaPreviews?: Record<string, MediaPreviewDescriptor>;
101
+ workflowScopes?: readonly WorkflowScope[];
102
+ workflowCandidates?: readonly WorkflowCandidateRange[];
103
+ workflowBlockedReasons?: readonly WorkflowBlockedCommandReason[];
104
+ activeWorkflowWorkItemId?: string | null;
105
+ activeWorkflowScopeIds?: readonly string[];
106
+ workflowMetadata?: readonly WorkflowMetadataMarkup[];
107
+ }
108
+
109
+ export interface TwProseMirrorSurfaceRef {
110
+ search(query: string, options?: SearchOptions): SearchResultSnapshot[];
111
+ clearSearch(): void;
112
+ getTableSelection(): TableSelectionDescriptor | null;
113
+ }
114
+
115
+ export const TwProseMirrorSurface = forwardRef<
116
+ TwProseMirrorSurfaceRef,
117
+ TwProseMirrorSurfaceProps
118
+ >(function TwProseMirrorSurface(props, ref) {
119
+ const {
120
+ currentUser,
121
+ snapshot,
122
+ markupDisplay,
123
+ onFocus,
124
+ onBlur,
125
+ } = props;
126
+ const surface = snapshot.surface;
127
+ const mediaPreviewKey = useMemo(
128
+ () =>
129
+ Object.entries(props.mediaPreviews ?? {})
130
+ .sort(([leftId], [rightId]) => leftId.localeCompare(rightId))
131
+ .map(
132
+ ([mediaId, preview]) =>
133
+ `${mediaId}:${preview.widthEmu ?? ""}:${preview.heightEmu ?? ""}:${preview.src}`,
134
+ )
135
+ .join("|"),
136
+ [props.mediaPreviews],
137
+ );
138
+
139
+ const canEdit = Boolean(
140
+ surface && snapshot.isReady && !snapshot.readOnly && !snapshot.fatalError,
141
+ );
142
+
143
+ const mountRef = useRef<HTMLDivElement>(null);
144
+ const viewRef = useRef<EditorView | null>(null);
145
+ const positionMapRef = useRef<PositionMap | null>(null);
146
+ const callbacksRef = useRef<CommandBridgeCallbacks | null>(null);
147
+ const activeSearchRef = useRef<{ query: string; options: SearchOptions } | null>(null);
148
+ const pendingTypingProbeRef = useRef<string | null>(null);
149
+ const pendingSelectionProbeRef = useRef<string | null>(null);
150
+ const documentBuildKeyRef = useRef<string | null>(null);
151
+ const decorationBuildKeyRef = useRef<string | null>(null);
152
+ const suppressSelectionEchoRef = useRef(false);
153
+ const selectionToolbarFrameRef = useRef<number | null>(null);
154
+ const lastSelectionToolbarMeasurementRef = useRef<{
155
+ key: string | null;
156
+ anchor: SelectionToolbarAnchor | null;
157
+ }>({
158
+ key: null,
159
+ anchor: null,
160
+ });
161
+
162
+ // Keep callbacks ref up to date (avoids stale closures in PM plugins)
163
+ callbacksRef.current = {
164
+ onInsertText: (text) => {
165
+ pendingTypingProbeRef.current = startPerfProbe("typing");
166
+ props.onInsertText?.(text);
167
+ },
168
+ onDeleteBackward: () => props.onDeleteBackward?.(),
169
+ onDeleteForward: () => props.onDeleteForward?.(),
170
+ onSplitParagraph: () => props.onSplitParagraph?.(),
171
+ onInsertHardBreak: () => props.onInsertHardBreak?.(),
172
+ onInsertTab: () => props.onInsertTab?.(),
173
+ onOutdentTab: () => props.onOutdentTab?.(),
174
+ onUndo: () => props.onUndo?.(),
175
+ onRedo: () => props.onRedo?.(),
176
+ onBlockedInput: (command, message) => {
177
+ props.onBlockedInput?.(command, message);
178
+ },
179
+ onSelectionChange: (sel) => {
180
+ pendingSelectionProbeRef.current = startPerfProbe("selection");
181
+ props.onSelectionChange?.(
182
+ snapshot.activeStory.kind === "main"
183
+ ? sel
184
+ : { ...sel, storyTarget: snapshot.activeStory },
185
+ );
186
+ },
187
+ getPositionMap: () => positionMapRef.current,
188
+ isSelectionSyncSuppressed: () => suppressSelectionEchoRef.current,
189
+ };
190
+
191
+ // Comment/revision decoration models
192
+ const commentModel = useMemo(
193
+ () => createCommentDecorationModel(snapshot.comments),
194
+ [snapshot.comments],
195
+ );
196
+ const showTrackedChanges = props.showTrackedChanges !== false;
197
+ // Always create the revision model — needed for deletion hiding in clean mode
198
+ // even when the tracked changes display toggle is off.
199
+ const revisionModel = useMemo(
200
+ () => createRevisionDecorationModel(snapshot.trackedChanges, props.activeRevisionId),
201
+ [snapshot.trackedChanges, props.activeRevisionId],
202
+ );
203
+ const documentBuildKey = useMemo(
204
+ () =>
205
+ createSurfaceDocumentBuildKey({
206
+ surface,
207
+ activeStory: snapshot.activeStory,
208
+ mediaPreviewKey,
209
+ showUnsupportedObjectPreviews: props.showUnsupportedObjectPreviews,
210
+ }),
211
+ [mediaPreviewKey, props.showUnsupportedObjectPreviews, snapshot.activeStory, surface],
212
+ );
213
+ const decorationBuildKey = useMemo(
214
+ () =>
215
+ createSurfaceDecorationKey({
216
+ markupDisplay,
217
+ showTrackedChanges,
218
+ canEdit,
219
+ activeCommentId: snapshot.comments.activeCommentId,
220
+ activeRevisionId: props.activeRevisionId,
221
+ workflowScopeSignature: createWorkflowScopeSignature(props.workflowScopes),
222
+ workflowCandidateSignature: createWorkflowCandidateSignature(props.workflowCandidates),
223
+ workflowBlockedSignature: createWorkflowBlockedSignature(props.workflowBlockedReasons),
224
+ workflowMetadataSignature: createWorkflowMetadataSignature(props.workflowMetadata),
225
+ activeWorkflowWorkItemId: props.activeWorkflowWorkItemId ?? null,
226
+ activeWorkflowScopeIds: props.activeWorkflowScopeIds ?? [],
227
+ }),
228
+ [
229
+ canEdit,
230
+ markupDisplay,
231
+ props.activeRevisionId,
232
+ props.workflowCandidates,
233
+ props.workflowBlockedReasons,
234
+ props.workflowMetadata,
235
+ props.activeWorkflowWorkItemId,
236
+ props.activeWorkflowScopeIds,
237
+ props.workflowScopes,
238
+ showTrackedChanges,
239
+ snapshot.comments.activeCommentId,
240
+ ],
241
+ );
242
+
243
+ // Create PM plugins (stable across renders — callbacks accessed via ref)
244
+ const plugins = useMemo(() => {
245
+ return [
246
+ ...createCommandBridgePlugins({
247
+ onInsertText: (text) => callbacksRef.current?.onInsertText(text),
248
+ onDeleteBackward: () => callbacksRef.current?.onDeleteBackward(),
249
+ onDeleteForward: () => callbacksRef.current?.onDeleteForward(),
250
+ onSplitParagraph: () => callbacksRef.current?.onSplitParagraph(),
251
+ onInsertHardBreak: () => callbacksRef.current?.onInsertHardBreak(),
252
+ onInsertTab: () => callbacksRef.current?.onInsertTab(),
253
+ onOutdentTab: () => callbacksRef.current?.onOutdentTab?.(),
254
+ onUndo: () => callbacksRef.current?.onUndo(),
255
+ onRedo: () => callbacksRef.current?.onRedo(),
256
+ onBlockedInput: (command, message) => callbacksRef.current?.onBlockedInput?.(command, message),
257
+ onSelectionChange: (sel) => callbacksRef.current?.onSelectionChange(sel),
258
+ getPositionMap: () => callbacksRef.current?.getPositionMap() ?? null,
259
+ isSelectionSyncSuppressed: () =>
260
+ callbacksRef.current?.isSelectionSyncSuppressed?.() ?? false,
261
+ }),
262
+ createContextualInteractionPlugin({
263
+ onCommentActivated: (commentId) => props.onCommentActivated?.(commentId),
264
+ onRevisionActivated: (revisionId) => props.onRevisionActivated?.(revisionId),
265
+ }),
266
+ createSearchPlugin(),
267
+ ];
268
+ }, [props.onCommentActivated, props.onRevisionActivated]);
269
+
270
+ const applyDecorationProps = useCallback(
271
+ (view: EditorView, positionMap: PositionMap): void => {
272
+ const decorations = buildDecorations(
273
+ view.state.doc,
274
+ positionMap,
275
+ commentModel,
276
+ revisionModel,
277
+ markupDisplay,
278
+ showTrackedChanges,
279
+ props.workflowScopes,
280
+ snapshot.activeStory,
281
+ props.workflowCandidates,
282
+ props.workflowBlockedReasons,
283
+ props.activeWorkflowWorkItemId,
284
+ props.activeWorkflowScopeIds,
285
+ props.workflowMetadata,
286
+ );
287
+ view.setProps({
288
+ editable: () => canEdit,
289
+ decorations: () => decorations,
290
+ });
291
+ decorationBuildKeyRef.current = decorationBuildKey;
292
+ recordPerfSample("pm.decorations");
293
+ incrementInvalidationCounter("pm.laneB.decorationUpdates");
294
+ },
295
+ [
296
+ canEdit,
297
+ commentModel,
298
+ decorationBuildKey,
299
+ markupDisplay,
300
+ props.activeWorkflowScopeIds,
301
+ props.activeWorkflowWorkItemId,
302
+ props.workflowBlockedReasons,
303
+ props.workflowMetadata,
304
+ props.workflowCandidates,
305
+ props.workflowScopes,
306
+ revisionModel,
307
+ showTrackedChanges,
308
+ ],
309
+ );
310
+
311
+ // Create or update the PM document only when the structural key changes.
312
+ useEffect(() => {
313
+ if (!mountRef.current || !surface) return;
314
+
315
+ if (viewRef.current && documentBuildKeyRef.current === documentBuildKey) {
316
+ return;
317
+ }
318
+
319
+ const { state, positionMap } = createPMStateFromSnapshot(
320
+ surface,
321
+ snapshot.selection,
322
+ plugins,
323
+ props.mediaPreviews,
324
+ props.showUnsupportedObjectPreviews,
325
+ );
326
+ positionMapRef.current = positionMap;
327
+ const decorations = buildDecorations(
328
+ state.doc,
329
+ positionMap,
330
+ commentModel,
331
+ revisionModel,
332
+ markupDisplay,
333
+ showTrackedChanges,
334
+ props.workflowScopes,
335
+ snapshot.activeStory,
336
+ props.workflowCandidates,
337
+ props.workflowBlockedReasons,
338
+ props.activeWorkflowWorkItemId,
339
+ props.activeWorkflowScopeIds,
340
+ props.workflowMetadata,
341
+ );
342
+ recordPerfSample("pm.rebuild");
343
+ incrementInvalidationCounter("pm.laneA.rebuilds");
344
+
345
+ if (!viewRef.current) {
346
+ // First time surface is available — create the EditorView
347
+ const view = new EditorView(mountRef.current, {
348
+ state,
349
+ nodeViews: tableNodeViews,
350
+ editable: () => canEdit,
351
+ decorations: () => decorations,
352
+ dispatchTransaction(tr) {
353
+ const newState = view.state.apply(tr);
354
+ view.updateState(newState);
355
+ },
356
+ });
357
+ viewRef.current = view;
358
+ recordPerfSample("pm.mount");
359
+ } else {
360
+ suppressSelectionEchoRef.current = true;
361
+ viewRef.current.updateState(state);
362
+ queueMicrotask(() => {
363
+ suppressSelectionEchoRef.current = false;
364
+ });
365
+ }
366
+ documentBuildKeyRef.current = documentBuildKey;
367
+ applyDecorationProps(viewRef.current, positionMap);
368
+
369
+ if (activeSearchRef.current) {
370
+ applySearch(
371
+ activeSearchRef.current.query,
372
+ activeSearchRef.current.options,
373
+ );
374
+ }
375
+ if (pendingTypingProbeRef.current) {
376
+ finishPerfProbe(pendingTypingProbeRef.current);
377
+ pendingTypingProbeRef.current = null;
378
+ }
379
+ }, [
380
+ applyDecorationProps,
381
+ documentBuildKey,
382
+ surface,
383
+ snapshot.selection,
384
+ plugins,
385
+ props.mediaPreviews,
386
+ ]);
387
+
388
+ // Update decorations and editability without rebuilding the PM document.
389
+ useEffect(() => {
390
+ const view = viewRef.current;
391
+ const positionMap = positionMapRef.current;
392
+ if (!view || !surface || !positionMap) {
393
+ return;
394
+ }
395
+
396
+ if (decorationBuildKeyRef.current === decorationBuildKey) {
397
+ return;
398
+ }
399
+
400
+ applyDecorationProps(view, positionMap);
401
+ }, [applyDecorationProps, decorationBuildKey, surface]);
402
+
403
+ useEffect(() => {
404
+ if (!activeSearchRef.current || !surface) {
405
+ return;
406
+ }
407
+ applySearch(activeSearchRef.current.query, activeSearchRef.current.options);
408
+ }, [
409
+ markupDisplay,
410
+ props.canonicalDocument,
411
+ props.documentNavigation,
412
+ snapshot.activeStory,
413
+ snapshot.trackedChanges,
414
+ surface,
415
+ ]);
416
+
417
+ useEffect(() => {
418
+ const view = viewRef.current;
419
+ const positionMap = positionMapRef.current;
420
+ if (!view || !surface || !positionMap) {
421
+ return;
422
+ }
423
+
424
+ const nextSelection = createPMSelectionFromSnapshot(
425
+ view.state.doc,
426
+ positionMap,
427
+ snapshot.selection,
428
+ );
429
+ if (view.state.selection.eq(nextSelection)) {
430
+ return;
431
+ }
432
+
433
+ suppressSelectionEchoRef.current = true;
434
+ view.dispatch(view.state.tr.setSelection(nextSelection));
435
+ recordPerfSample("selection.sync");
436
+ queueMicrotask(() => {
437
+ suppressSelectionEchoRef.current = false;
438
+ });
439
+ }, [snapshot.selection, surface]);
440
+
441
+ useEffect(() => {
442
+ if (!pendingSelectionProbeRef.current) {
443
+ return;
444
+ }
445
+ finishPerfProbe(pendingSelectionProbeRef.current);
446
+ pendingSelectionProbeRef.current = null;
447
+ }, [snapshot.selection]);
448
+
449
+ // Cleanup on unmount
450
+ useEffect(() => {
451
+ return () => {
452
+ const win = mountRef.current?.ownerDocument.defaultView;
453
+ if (selectionToolbarFrameRef.current !== null && win) {
454
+ win.cancelAnimationFrame(selectionToolbarFrameRef.current);
455
+ selectionToolbarFrameRef.current = null;
456
+ }
457
+ viewRef.current?.destroy();
458
+ viewRef.current = null;
459
+ };
460
+ }, []);
461
+
462
+ useImperativeHandle(
463
+ ref,
464
+ () => ({
465
+ search: (query, options = {}) => {
466
+ const normalizedQuery = query.trim();
467
+ if (!normalizedQuery) {
468
+ activeSearchRef.current = null;
469
+ clearLiveSearch();
470
+ return [];
471
+ }
472
+
473
+ activeSearchRef.current = { query: normalizedQuery, options };
474
+ return applySearch(normalizedQuery, options);
475
+ },
476
+ clearSearch: () => {
477
+ activeSearchRef.current = null;
478
+ clearLiveSearch();
479
+ },
480
+ getTableSelection: () => {
481
+ const view = viewRef.current;
482
+ if (!view) {
483
+ return null;
484
+ }
485
+ return getTableSelectionDescriptor(view.state);
486
+ },
487
+ }),
488
+ [
489
+ markupDisplay,
490
+ props.canonicalDocument,
491
+ props.documentNavigation,
492
+ snapshot.activeStory,
493
+ snapshot.selection,
494
+ snapshot.surface,
495
+ snapshot.trackedChanges,
496
+ ],
497
+ );
498
+
499
+ function applySearch(query: string, options: SearchOptions): SearchResultSnapshot[] {
500
+ const view = viewRef.current;
501
+ const hiddenDeletionRanges =
502
+ markupDisplay === "clean"
503
+ ? snapshot.trackedChanges.revisions
504
+ .filter(
505
+ (
506
+ revision,
507
+ ): revision is typeof revision & {
508
+ anchor: Extract<typeof revision.anchor, { kind: "range" }>;
509
+ } =>
510
+ revision.kind === "deletion" &&
511
+ revision.status === "active" &&
512
+ revision.anchor.kind === "range",
513
+ )
514
+ .map((revision) => ({
515
+ from: revision.anchor.from,
516
+ to: revision.anchor.to,
517
+ }))
518
+ : [];
519
+ if (view) {
520
+ const rawResults = performSearch(view.state, query, options)
521
+ .filter((result) => {
522
+ if (hiddenDeletionRanges.length === 0) {
523
+ return true;
524
+ }
525
+ const positionMap = positionMapRef.current;
526
+ if (!positionMap) {
527
+ return true;
528
+ }
529
+ const runtimeFrom = positionMap.pmToRuntime(result.from);
530
+ const runtimeTo = positionMap.pmToRuntime(result.to);
531
+ return !hiddenDeletionRanges.some(
532
+ (range) => runtimeFrom < range.to && runtimeTo > range.from,
533
+ );
534
+ })
535
+ .slice(0, options.limit ?? Number.POSITIVE_INFINITY);
536
+ view.dispatch(
537
+ view.state.tr.setMeta(searchPluginKey, {
538
+ results: rawResults,
539
+ highlightColor: DEFAULT_SEARCH_HIGHLIGHT_COLOR,
540
+ }),
541
+ );
542
+ }
543
+
544
+ return filterHiddenDeletionSearchResults(
545
+ searchDocument(
546
+ props.canonicalDocument,
547
+ snapshot.selection,
548
+ snapshot.activeStory,
549
+ props.documentNavigation,
550
+ query,
551
+ options,
552
+ ),
553
+ hiddenDeletionRanges,
554
+ );
555
+ }
556
+
557
+ function filterHiddenDeletionSearchResults(
558
+ results: SearchResultSnapshot[],
559
+ hiddenRanges: Array<{ from: number; to: number }>,
560
+ ): SearchResultSnapshot[] {
561
+ if (hiddenRanges.length === 0) {
562
+ return results;
563
+ }
564
+ return results.filter((result) => {
565
+ const anchor = result.anchor;
566
+ if (anchor.kind !== "range") {
567
+ return true;
568
+ }
569
+ return !hiddenRanges.some(
570
+ (range) => anchor.from < range.to && anchor.to > range.from,
571
+ );
572
+ });
573
+ }
574
+
575
+ function clearLiveSearch(): void {
576
+ const view = viewRef.current;
577
+ if (!view) {
578
+ return;
579
+ }
580
+
581
+ clearSearchPlugin(view.state, (tr) => {
582
+ view.dispatch(tr);
583
+ });
584
+ }
585
+
586
+ const fontClass =
587
+ markupDisplay === "clean"
588
+ ? "font-[family-name:var(--font-legal-sans)]"
589
+ : "font-[family-name:var(--font-legal-serif)]";
590
+
591
+ // Story focus indicator — runtime-backed, not DOM-only
592
+ const storyKind = snapshot.activeStory.kind;
593
+ const storyFocusAttr = storyKind !== "main" ? storyKind : undefined;
594
+
595
+ // Table focus cue — add subtle ring when selection head is inside a table block
596
+ const tableFocusClass = (() => {
597
+ if (!surface || !snapshot.selection) return "";
598
+ const head = snapshot.selection.head;
599
+ const inTable = surface.blocks.some(
600
+ (b) => b.kind === "table" && head >= b.from && head <= b.to,
601
+ );
602
+ return inTable ? "prosemirror-table-focus" : "";
603
+ })();
604
+
605
+ const workspaceLabel = props.isPageWorkspace ? "Document page" : "Document canvas";
606
+
607
+ const selectionToolbarMeasurementKey = useMemo(
608
+ () =>
609
+ buildSelectionToolbarMeasurementKey(
610
+ snapshot.selection,
611
+ snapshot.activeStory,
612
+ props.activeSelectionToolKind,
613
+ ),
614
+ [props.activeSelectionToolKind, snapshot.activeStory, snapshot.selection],
615
+ );
616
+
617
+ const emitSelectionToolbarAnchor = useCallback((): void => {
618
+ const callback = props.onSelectionToolbarAnchorChange;
619
+ if (!callback) {
620
+ return;
621
+ }
622
+
623
+ const nextAnchor = measureSelectionToolbarAnchor();
624
+ const previous = lastSelectionToolbarMeasurementRef.current;
625
+ if (
626
+ previous.key === selectionToolbarMeasurementKey &&
627
+ selectionToolbarAnchorsEqual(previous.anchor, nextAnchor)
628
+ ) {
629
+ return;
630
+ }
631
+
632
+ lastSelectionToolbarMeasurementRef.current = {
633
+ key: selectionToolbarMeasurementKey,
634
+ anchor: nextAnchor,
635
+ };
636
+ callback(nextAnchor);
637
+ }, [
638
+ props.onSelectionToolbarAnchorChange,
639
+ selectionToolbarMeasurementKey,
640
+ snapshot.activeStory,
641
+ snapshot.selection,
642
+ ]);
643
+
644
+ const scheduleSelectionToolbarAnchorUpdate = useCallback((): void => {
645
+ const callback = props.onSelectionToolbarAnchorChange;
646
+ const mount = mountRef.current;
647
+ const win = mount?.ownerDocument.defaultView;
648
+ if (!callback || !win) {
649
+ emitSelectionToolbarAnchor();
650
+ return;
651
+ }
652
+
653
+ if (selectionToolbarFrameRef.current !== null) {
654
+ win.cancelAnimationFrame(selectionToolbarFrameRef.current);
655
+ selectionToolbarFrameRef.current = null;
656
+ }
657
+
658
+ selectionToolbarFrameRef.current = win.requestAnimationFrame(() => {
659
+ selectionToolbarFrameRef.current = null;
660
+ emitSelectionToolbarAnchor();
661
+ });
662
+ }, [emitSelectionToolbarAnchor, props.onSelectionToolbarAnchorChange]);
663
+
664
+ useEffect(() => {
665
+ scheduleSelectionToolbarAnchorUpdate();
666
+ }, [
667
+ props.activeSelectionToolKind,
668
+ scheduleSelectionToolbarAnchorUpdate,
669
+ snapshot.revisionToken,
670
+ snapshot.selection,
671
+ snapshot.surface,
672
+ props.isPageWorkspace,
673
+ ]);
674
+
675
+ useEffect(() => {
676
+ const mount = mountRef.current;
677
+ const callback = props.onSelectionToolbarAnchorChange;
678
+ if (!mount || !callback) {
679
+ return;
680
+ }
681
+
682
+ const updateAnchor = () => {
683
+ scheduleSelectionToolbarAnchorUpdate();
684
+ };
685
+ const scrollRoot = mount.closest<HTMLElement>("[data-wre-scroll-root='true']");
686
+ const win = mount.ownerDocument.defaultView;
687
+ const resizeObserver =
688
+ typeof ResizeObserver !== "undefined"
689
+ ? new ResizeObserver(() => {
690
+ updateAnchor();
691
+ })
692
+ : null;
693
+
694
+ updateAnchor();
695
+ scrollRoot?.addEventListener("scroll", updateAnchor, { passive: true });
696
+ win?.addEventListener("resize", updateAnchor);
697
+ resizeObserver?.observe(mount);
698
+ if (scrollRoot) {
699
+ resizeObserver?.observe(scrollRoot);
700
+ }
701
+
702
+ return () => {
703
+ scrollRoot?.removeEventListener("scroll", updateAnchor);
704
+ win?.removeEventListener("resize", updateAnchor);
705
+ resizeObserver?.disconnect();
706
+ };
707
+ }, [
708
+ props.activeSelectionToolKind,
709
+ props.onSelectionToolbarAnchorChange,
710
+ scheduleSelectionToolbarAnchorUpdate,
711
+ snapshot.revisionToken,
712
+ snapshot.selection,
713
+ ]);
714
+
715
+ useEffect(() => {
716
+ return () => {
717
+ lastSelectionToolbarMeasurementRef.current = {
718
+ key: null,
719
+ anchor: null,
720
+ };
721
+ props.onSelectionToolbarAnchorChange?.(null);
722
+ };
723
+ }, [props.onSelectionToolbarAnchorChange]);
724
+
725
+ return (
726
+ <section
727
+ aria-label={workspaceLabel}
728
+ className="min-w-0"
729
+ data-active-story={storyFocusAttr}
730
+ data-workspace={props.isPageWorkspace ? "page" : "canvas"}
731
+ >
732
+ {/* ProseMirror mount point — document content including headings is editable */}
733
+ {surface ? (
734
+ <div
735
+ ref={mountRef}
736
+ role="textbox"
737
+ tabIndex={0}
738
+ aria-multiline="true"
739
+ className={`px-12 py-10 ${fontClass} text-[15px] text-primary leading-relaxed prosemirror-surface outline-none ${tableFocusClass}`}
740
+ onFocus={(event) => {
741
+ onFocus(event);
742
+ if (event.target === event.currentTarget) {
743
+ viewRef.current?.focus();
744
+ }
745
+ }}
746
+ onBlur={onBlur as unknown as React.FocusEventHandler<HTMLDivElement>}
747
+ onKeyDown={(event) => {
748
+ if (event.target !== event.currentTarget) {
749
+ return;
750
+ }
751
+
752
+ switch (event.key) {
753
+ case "Backspace":
754
+ event.preventDefault();
755
+ props.onDeleteBackward?.();
756
+ return;
757
+ case "Delete":
758
+ event.preventDefault();
759
+ props.onDeleteForward?.();
760
+ return;
761
+ case "Enter":
762
+ event.preventDefault();
763
+ if (event.shiftKey) {
764
+ props.onInsertHardBreak?.();
765
+ } else {
766
+ props.onSplitParagraph?.();
767
+ }
768
+ return;
769
+ case "Tab":
770
+ event.preventDefault();
771
+ if (event.shiftKey) {
772
+ props.onOutdentTab?.();
773
+ } else {
774
+ props.onInsertTab?.();
775
+ }
776
+ return;
777
+ default:
778
+ return;
779
+ }
780
+ }}
781
+ aria-label="Document surface"
782
+ data-wre-document-surface="true"
783
+ data-story-focus={storyFocusAttr}
784
+ />
785
+ ) : (
786
+ <div className="px-12 pb-10">
787
+ <p className="text-sm text-secondary leading-relaxed">
788
+ Loading the review surface...
789
+ </p>
790
+ </div>
791
+ )}
792
+
793
+ {snapshot.fatalError ? (
794
+ <div className="px-12 pb-10">
795
+ <p className="text-sm text-danger">
796
+ Fatal runtime error: {snapshot.fatalError.message}
797
+ </p>
798
+ </div>
799
+ ) : null}
800
+ </section>
801
+ );
802
+
803
+ function measureSelectionToolbarAnchor(): SelectionToolbarAnchor | null {
804
+ const callback = props.onSelectionToolbarAnchorChange;
805
+ const view = viewRef.current;
806
+ const mount = mountRef.current;
807
+ const positionMap = positionMapRef.current;
808
+ const range = snapshot.selection.activeRange;
809
+
810
+ if (!callback || !view || !mount || !positionMap || range.kind === "detached") {
811
+ return null;
812
+ }
813
+
814
+ const rootRect = mount.getBoundingClientRect();
815
+ if (rootRect.width <= 0 || rootRect.height <= 0) {
816
+ return null;
817
+ }
818
+
819
+ try {
820
+ if (!snapshot.selection.isCollapsed && range.kind === "range") {
821
+ const pmFrom = positionMap.runtimeToPm(range.from);
822
+ const pmTo = positionMap.runtimeToPm(range.to);
823
+ return createSelectionToolbarAnchor(rootRect, view.coordsAtPos(pmFrom), view.coordsAtPos(pmTo));
824
+ }
825
+
826
+ if (
827
+ range.kind === "node" ||
828
+ (
829
+ snapshot.selection.isCollapsed &&
830
+ range.kind === "range" &&
831
+ (
832
+ props.activeSelectionToolKind === "comment-thread" ||
833
+ props.activeSelectionToolKind === "structure-context"
834
+ )
835
+ )
836
+ ) {
837
+ const runtimePosition = range.kind === "node" ? range.at : range.from;
838
+ const pmAt = positionMap.runtimeToPm(runtimePosition);
839
+ return createSelectionToolbarAnchor(
840
+ rootRect,
841
+ view.coordsAtPos(pmAt, -1),
842
+ view.coordsAtPos(pmAt, 1),
843
+ );
844
+ }
845
+
846
+ return null;
847
+ } catch {
848
+ return null;
849
+ }
850
+ }
851
+ });
852
+
853
+ function createWorkflowScopeSignature(scopes: readonly WorkflowScope[] | undefined): string {
854
+ if (!scopes || scopes.length === 0) {
855
+ return "";
856
+ }
857
+ return scopes.map((scope) =>
858
+ [
859
+ scope.scopeId,
860
+ scope.mode,
861
+ scope.workItemId ?? "",
862
+ serializeAnchorSignature(scope.anchor),
863
+ serializeStoryTargetSignature(scope.storyTarget),
864
+ ].join(":")
865
+ ).join("|");
866
+ }
867
+
868
+ function createWorkflowCandidateSignature(
869
+ candidates: readonly WorkflowCandidateRange[] | undefined,
870
+ ): string {
871
+ if (!candidates || candidates.length === 0) {
872
+ return "";
873
+ }
874
+ return candidates.map((candidate) =>
875
+ [
876
+ candidate.candidateId,
877
+ serializeAnchorSignature(candidate.anchor),
878
+ serializeStoryTargetSignature(candidate.storyTarget),
879
+ candidate.source ?? "",
880
+ ].join(":")
881
+ ).join("|");
882
+ }
883
+
884
+ function createWorkflowBlockedSignature(
885
+ blockedReasons: readonly WorkflowBlockedCommandReason[] | undefined,
886
+ ): string {
887
+ if (!blockedReasons || blockedReasons.length === 0) {
888
+ return "";
889
+ }
890
+ return blockedReasons.map((reason) =>
891
+ [
892
+ reason.code,
893
+ reason.scopeId ?? "",
894
+ reason.workItemId ?? "",
895
+ serializeAnchorSignature(reason.anchor),
896
+ serializeStoryTargetSignature(reason.storyTarget),
897
+ ].join(":")
898
+ ).join("|");
899
+ }
900
+
901
+ function createWorkflowMetadataSignature(
902
+ metadata: readonly WorkflowMetadataMarkup[] | undefined,
903
+ ): string {
904
+ if (!metadata || metadata.length === 0) {
905
+ return "";
906
+ }
907
+ return metadata.map((entry) =>
908
+ [
909
+ entry.entryId,
910
+ entry.metadataId,
911
+ entry.color ?? "",
912
+ entry.persistence,
913
+ serializeAnchorSignature(entry.anchor),
914
+ serializeStoryTargetSignature(entry.storyTarget),
915
+ JSON.stringify(entry.value ?? {}),
916
+ ].join(":")
917
+ ).join("|");
918
+ }
919
+
920
+ function serializeAnchorSignature(
921
+ anchor:
922
+ | WorkflowScope["anchor"]
923
+ | WorkflowCandidateRange["anchor"]
924
+ | WorkflowBlockedCommandReason["anchor"]
925
+ | WorkflowMetadataMarkup["anchor"]
926
+ | undefined,
927
+ ): string {
928
+ if (!anchor) {
929
+ return "";
930
+ }
931
+ switch (anchor.kind) {
932
+ case "range":
933
+ return `range:${anchor.from}:${anchor.to}:${anchor.assoc.start}:${anchor.assoc.end}`;
934
+ case "node":
935
+ return `node:${anchor.at}:${anchor.assoc}`;
936
+ case "detached":
937
+ return `detached:${anchor.lastKnownRange.from}:${anchor.lastKnownRange.to}:${anchor.reason}`;
938
+ }
939
+ }
940
+
941
+ function serializeStoryTargetSignature(
942
+ storyTarget:
943
+ | WorkflowScope["storyTarget"]
944
+ | WorkflowCandidateRange["storyTarget"]
945
+ | WorkflowBlockedCommandReason["storyTarget"]
946
+ | WorkflowMetadataMarkup["storyTarget"],
947
+ ): string {
948
+ if (!storyTarget) {
949
+ return "";
950
+ }
951
+ switch (storyTarget.kind) {
952
+ case "main":
953
+ return "main";
954
+ case "header":
955
+ case "footer":
956
+ return `${storyTarget.kind}:${storyTarget.relationshipId}:${storyTarget.variant}:${storyTarget.sectionIndex ?? ""}`;
957
+ case "footnote":
958
+ case "endnote":
959
+ return `${storyTarget.kind}:${storyTarget.noteId}`;
960
+ }
961
+ }
962
+
963
+ function buildSelectionToolbarMeasurementKey(
964
+ selection: SelectionSnapshot,
965
+ activeStory: RuntimeRenderSnapshot["activeStory"],
966
+ activeSelectionToolKind?: ActiveSelectionToolModel["kind"] | null,
967
+ ): string | null {
968
+ if (!activeSelectionToolKind || selection.activeRange.kind === "detached") {
969
+ return null;
970
+ }
971
+
972
+ return JSON.stringify({
973
+ story: activeStory,
974
+ tool: activeSelectionToolKind,
975
+ ...(selection.activeRange.kind === "node"
976
+ ? { nodeAt: selection.activeRange.at }
977
+ : {
978
+ from: selection.activeRange.from,
979
+ to: selection.activeRange.to,
980
+ collapsed: selection.isCollapsed,
981
+ }),
982
+ });
983
+ }
984
+
985
+ function createSelectionToolbarAnchor(
986
+ rootRect: DOMRect,
987
+ ...rects: Array<Pick<DOMRect, "left" | "right" | "top" | "bottom">>
988
+ ): SelectionToolbarAnchor | null {
989
+ const validRects = rects.filter((rect) =>
990
+ Number.isFinite(rect.left) &&
991
+ Number.isFinite(rect.right) &&
992
+ Number.isFinite(rect.top) &&
993
+ Number.isFinite(rect.bottom),
994
+ );
995
+ if (validRects.length === 0) {
996
+ return null;
997
+ }
998
+
999
+ let left = Math.max(rootRect.left, Math.min(...validRects.map((rect) => Math.min(rect.left, rect.right))));
1000
+ let right = Math.min(rootRect.right, Math.max(...validRects.map((rect) => Math.max(rect.left, rect.right))));
1001
+ let top = Math.max(rootRect.top, Math.min(...validRects.map((rect) => Math.min(rect.top, rect.bottom))));
1002
+ let bottom = Math.min(rootRect.bottom, Math.max(...validRects.map((rect) => Math.max(rect.top, rect.bottom))));
1003
+
1004
+ if (right <= left) {
1005
+ const centerX = Math.min(rootRect.right, Math.max(rootRect.left, (left + right) / 2 || left));
1006
+ left = Math.max(rootRect.left, centerX - 1);
1007
+ right = Math.min(rootRect.right, centerX + 1);
1008
+ }
1009
+ if (bottom <= top) {
1010
+ const centerY = Math.min(rootRect.bottom, Math.max(rootRect.top, (top + bottom) / 2 || top));
1011
+ top = Math.max(rootRect.top, centerY - 1);
1012
+ bottom = Math.min(rootRect.bottom, centerY + 1);
1013
+ }
1014
+
1015
+ if (
1016
+ !Number.isFinite(left) ||
1017
+ !Number.isFinite(right) ||
1018
+ !Number.isFinite(top) ||
1019
+ !Number.isFinite(bottom) ||
1020
+ right <= left ||
1021
+ bottom <= top ||
1022
+ bottom < rootRect.top ||
1023
+ top > rootRect.bottom
1024
+ ) {
1025
+ return null;
1026
+ }
1027
+
1028
+ return { left, right, top, bottom };
1029
+ }
1030
+
1031
+ function selectionToolbarAnchorsEqual(
1032
+ left: SelectionToolbarAnchor | null,
1033
+ right: SelectionToolbarAnchor | null,
1034
+ ): boolean {
1035
+ if (left === right) {
1036
+ return true;
1037
+ }
1038
+ if (!left || !right) {
1039
+ return false;
1040
+ }
1041
+ return (
1042
+ left.left === right.left &&
1043
+ left.right === right.right &&
1044
+ left.top === right.top &&
1045
+ left.bottom === right.bottom
1046
+ );
1047
+ }