@beyondwork/docx-react-component 1.0.28 → 1.0.29

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 (354) hide show
  1. package/dist/canonical-document-BLEbzL2J.d.cts +844 -0
  2. package/dist/canonical-document-BLEbzL2J.d.ts +844 -0
  3. package/dist/chunk-2FJS5GZM.js +763 -0
  4. package/dist/chunk-2FJS5GZM.js.map +1 -0
  5. package/{src/core/commands/section-layout-commands.ts → dist/chunk-2OQBZS3F.js} +106 -340
  6. package/dist/chunk-2OQBZS3F.js.map +1 -0
  7. package/dist/chunk-2S7W4KFO.js +127 -0
  8. package/dist/chunk-2S7W4KFO.js.map +1 -0
  9. package/dist/chunk-2TG72QSW.js +3874 -0
  10. package/dist/chunk-2TG72QSW.js.map +1 -0
  11. package/{src/core/commands/table-structure-commands.ts → dist/chunk-36QNIZBO.js} +126 -315
  12. package/dist/chunk-36QNIZBO.js.map +1 -0
  13. package/dist/chunk-4AQOYAW4.js +3069 -0
  14. package/dist/chunk-4AQOYAW4.js.map +1 -0
  15. package/dist/chunk-4D5EWJ3P.js +77 -0
  16. package/dist/chunk-4D5EWJ3P.js.map +1 -0
  17. package/dist/chunk-5FN54NDH.js +2257 -0
  18. package/dist/chunk-5FN54NDH.js.map +1 -0
  19. package/dist/chunk-BOYGQYRQ.js +7306 -0
  20. package/dist/chunk-BOYGQYRQ.js.map +1 -0
  21. package/dist/chunk-CN3XMECL.js +212 -0
  22. package/dist/chunk-CN3XMECL.js.map +1 -0
  23. package/dist/chunk-EBI3BX6U.js +164 -0
  24. package/dist/chunk-EBI3BX6U.js.map +1 -0
  25. package/dist/chunk-EILUG3VB.js +1275 -0
  26. package/dist/chunk-EILUG3VB.js.map +1 -0
  27. package/dist/chunk-FUDY333O.js +70 -0
  28. package/dist/chunk-FUDY333O.js.map +1 -0
  29. package/dist/chunk-GBVOWFIK.js +1237 -0
  30. package/dist/chunk-GBVOWFIK.js.map +1 -0
  31. package/dist/chunk-H4TQ3H3Y.js +262 -0
  32. package/dist/chunk-H4TQ3H3Y.js.map +1 -0
  33. package/{src/core/commands/style-commands.ts → dist/chunk-JGB3IXZO.js} +40 -113
  34. package/dist/chunk-JGB3IXZO.js.map +1 -0
  35. package/dist/chunk-KD2QRQPY.js +4342 -0
  36. package/dist/chunk-KD2QRQPY.js.map +1 -0
  37. package/dist/chunk-KLMXQVYK.js +369 -0
  38. package/dist/chunk-KLMXQVYK.js.map +1 -0
  39. package/dist/chunk-KZUG5KFQ.js +214 -0
  40. package/dist/chunk-KZUG5KFQ.js.map +1 -0
  41. package/{src/core/state/text-transaction.ts → dist/chunk-QDAQ4CJU.js} +79 -236
  42. package/dist/chunk-QDAQ4CJU.js.map +1 -0
  43. package/{src/legal/bookmarks.ts → dist/chunk-RMH72RZI.js} +44 -130
  44. package/dist/chunk-RMH72RZI.js.map +1 -0
  45. package/dist/chunk-SWKWQZXM.js +117 -0
  46. package/dist/chunk-SWKWQZXM.js.map +1 -0
  47. package/{src/core/commands/formatting-commands.ts → dist/chunk-TJBP2K4T.js} +196 -536
  48. package/dist/chunk-TJBP2K4T.js.map +1 -0
  49. package/dist/chunk-TLCEAQDQ.js +542 -0
  50. package/dist/chunk-TLCEAQDQ.js.map +1 -0
  51. package/{src/core/commands/text-commands.ts → dist/chunk-UZXBISGO.js} +86 -142
  52. package/dist/chunk-UZXBISGO.js.map +1 -0
  53. package/dist/chunk-WGBAKP3Q.js +3220 -0
  54. package/dist/chunk-WGBAKP3Q.js.map +1 -0
  55. package/dist/compare/index.cjs +5475 -0
  56. package/dist/compare/index.cjs.map +1 -0
  57. package/dist/compare/index.d.cts +114 -0
  58. package/dist/compare/index.d.ts +114 -0
  59. package/dist/compare/index.js +731 -0
  60. package/dist/compare/index.js.map +1 -0
  61. package/dist/core/commands/formatting-commands.cjs +828 -0
  62. package/dist/core/commands/formatting-commands.cjs.map +1 -0
  63. package/dist/core/commands/formatting-commands.d.cts +63 -0
  64. package/dist/core/commands/formatting-commands.d.ts +63 -0
  65. package/dist/core/commands/formatting-commands.js +37 -0
  66. package/dist/core/commands/formatting-commands.js.map +1 -0
  67. package/dist/core/commands/image-commands.cjs +2023 -0
  68. package/dist/core/commands/image-commands.cjs.map +1 -0
  69. package/dist/core/commands/image-commands.d.cts +58 -0
  70. package/dist/core/commands/image-commands.d.ts +58 -0
  71. package/dist/core/commands/image-commands.js +18 -0
  72. package/dist/core/commands/image-commands.js.map +1 -0
  73. package/dist/core/commands/section-layout-commands.cjs +477 -0
  74. package/dist/core/commands/section-layout-commands.cjs.map +1 -0
  75. package/dist/core/commands/section-layout-commands.d.cts +62 -0
  76. package/dist/core/commands/section-layout-commands.d.ts +62 -0
  77. package/dist/core/commands/section-layout-commands.js +21 -0
  78. package/dist/core/commands/section-layout-commands.js.map +1 -0
  79. package/dist/core/commands/style-commands.cjs +214 -0
  80. package/dist/core/commands/style-commands.cjs.map +1 -0
  81. package/dist/core/commands/style-commands.d.cts +13 -0
  82. package/dist/core/commands/style-commands.d.ts +13 -0
  83. package/dist/core/commands/style-commands.js +9 -0
  84. package/dist/core/commands/style-commands.js.map +1 -0
  85. package/dist/core/commands/table-structure-commands.cjs +1883 -0
  86. package/dist/core/commands/table-structure-commands.cjs.map +1 -0
  87. package/dist/core/commands/table-structure-commands.d.cts +59 -0
  88. package/dist/core/commands/table-structure-commands.d.ts +59 -0
  89. package/dist/core/commands/table-structure-commands.js +12 -0
  90. package/dist/core/commands/table-structure-commands.js.map +1 -0
  91. package/dist/core/commands/text-commands.cjs +2391 -0
  92. package/dist/core/commands/text-commands.cjs.map +1 -0
  93. package/dist/core/commands/text-commands.d.cts +24 -0
  94. package/dist/core/commands/text-commands.d.ts +24 -0
  95. package/dist/core/commands/text-commands.js +28 -0
  96. package/dist/core/commands/text-commands.js.map +1 -0
  97. package/dist/core/selection/mapping.cjs +200 -0
  98. package/dist/core/selection/mapping.cjs.map +1 -0
  99. package/dist/core/selection/mapping.d.cts +2 -0
  100. package/dist/core/selection/mapping.d.ts +2 -0
  101. package/dist/core/selection/mapping.js +31 -0
  102. package/dist/core/selection/mapping.js.map +1 -0
  103. package/dist/core/state/editor-state.cjs +2278 -0
  104. package/dist/core/state/editor-state.cjs.map +1 -0
  105. package/dist/core/state/editor-state.d.cts +2 -0
  106. package/dist/core/state/editor-state.d.ts +2 -0
  107. package/dist/core/state/editor-state.js +26 -0
  108. package/dist/core/state/editor-state.js.map +1 -0
  109. package/dist/index.cjs +38553 -0
  110. package/dist/index.cjs.map +1 -0
  111. package/dist/index.d.cts +15 -0
  112. package/dist/index.d.ts +15 -0
  113. package/dist/index.js +7856 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/io/docx-session.cjs +16236 -0
  116. package/dist/io/docx-session.cjs.map +1 -0
  117. package/dist/io/docx-session.d.cts +21 -0
  118. package/dist/io/docx-session.d.ts +21 -0
  119. package/dist/io/docx-session.js +18 -0
  120. package/dist/io/docx-session.js.map +1 -0
  121. package/dist/legal/index.cjs +3900 -0
  122. package/dist/legal/index.cjs.map +1 -0
  123. package/dist/legal/index.d.cts +86 -0
  124. package/dist/legal/index.d.ts +86 -0
  125. package/dist/legal/index.js +616 -0
  126. package/dist/legal/index.js.map +1 -0
  127. package/dist/public-types-7ZL_94cz.d.ts +1573 -0
  128. package/dist/public-types-CeMaDueh.d.cts +1573 -0
  129. package/dist/public-types.cjs +19 -0
  130. package/dist/public-types.cjs.map +1 -0
  131. package/dist/public-types.d.cts +2 -0
  132. package/dist/public-types.d.ts +2 -0
  133. package/dist/public-types.js +1 -0
  134. package/dist/public-types.js.map +1 -0
  135. package/dist/runtime/document-runtime.cjs +11140 -0
  136. package/dist/runtime/document-runtime.cjs.map +1 -0
  137. package/dist/runtime/document-runtime.d.cts +231 -0
  138. package/dist/runtime/document-runtime.d.ts +231 -0
  139. package/dist/runtime/document-runtime.js +21 -0
  140. package/dist/runtime/document-runtime.js.map +1 -0
  141. package/dist/structural-helpers-CilgOVhh.d.cts +10 -0
  142. package/dist/structural-helpers-q0Gd-eBN.d.ts +10 -0
  143. package/dist/ui-tailwind/editor-surface/search-plugin.cjs +313 -0
  144. package/dist/ui-tailwind/editor-surface/search-plugin.cjs.map +1 -0
  145. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +67 -0
  146. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +67 -0
  147. package/dist/ui-tailwind/editor-surface/search-plugin.js +23 -0
  148. package/dist/ui-tailwind/editor-surface/search-plugin.js.map +1 -0
  149. package/dist/ui-tailwind/index.cjs +4833 -0
  150. package/dist/ui-tailwind/index.cjs.map +1 -0
  151. package/dist/ui-tailwind/index.d.cts +617 -0
  152. package/dist/ui-tailwind/index.d.ts +617 -0
  153. package/dist/ui-tailwind/index.js +575 -0
  154. package/dist/ui-tailwind/index.js.map +1 -0
  155. package/package.json +61 -41
  156. package/src/README.md +0 -85
  157. package/src/api/README.md +0 -26
  158. package/src/api/public-types.ts +0 -1421
  159. package/src/api/session-state.ts +0 -60
  160. package/src/compare/diff-engine.ts +0 -623
  161. package/src/compare/export-redlines.ts +0 -280
  162. package/src/compare/index.ts +0 -25
  163. package/src/compare/snapshot.ts +0 -97
  164. package/src/component-inventory.md +0 -99
  165. package/src/core/README.md +0 -10
  166. package/src/core/commands/README.md +0 -3
  167. package/src/core/commands/image-commands.ts +0 -373
  168. package/src/core/commands/index.ts +0 -1757
  169. package/src/core/commands/list-commands.ts +0 -565
  170. package/src/core/commands/paragraph-layout-commands.ts +0 -339
  171. package/src/core/commands/review-commands.ts +0 -108
  172. package/src/core/commands/structural-helpers.ts +0 -309
  173. package/src/core/schema/README.md +0 -3
  174. package/src/core/schema/text-schema.ts +0 -516
  175. package/src/core/search/search-text.ts +0 -357
  176. package/src/core/selection/README.md +0 -3
  177. package/src/core/selection/mapping.ts +0 -289
  178. package/src/core/selection/review-anchors.ts +0 -183
  179. package/src/core/state/README.md +0 -3
  180. package/src/core/state/editor-state.ts +0 -892
  181. package/src/formats/xlsx/io/parse-shared-strings.ts +0 -41
  182. package/src/formats/xlsx/io/parse-sheet.ts +0 -459
  183. package/src/formats/xlsx/io/parse-styles.ts +0 -59
  184. package/src/formats/xlsx/io/parse-workbook.ts +0 -75
  185. package/src/formats/xlsx/io/serialize-shared-strings.ts +0 -72
  186. package/src/formats/xlsx/io/serialize-sheet.ts +0 -333
  187. package/src/formats/xlsx/io/serialize-styles.ts +0 -98
  188. package/src/formats/xlsx/io/serialize-workbook.ts +0 -429
  189. package/src/formats/xlsx/io/xlsx-session.ts +0 -314
  190. package/src/formats/xlsx/model/cell.ts +0 -189
  191. package/src/formats/xlsx/model/sheet.ts +0 -326
  192. package/src/formats/xlsx/model/styles.ts +0 -118
  193. package/src/formats/xlsx/model/workbook.ts +0 -453
  194. package/src/formats/xlsx/runtime/cell-commands.ts +0 -567
  195. package/src/formats/xlsx/runtime/sheet-commands.ts +0 -206
  196. package/src/formats/xlsx/runtime/workbook-runtime.ts +0 -177
  197. package/src/formats/xlsx/runtime/workbook-transaction.ts +0 -822
  198. package/src/index.ts +0 -101
  199. package/src/io/README.md +0 -10
  200. package/src/io/docx-session.ts +0 -2882
  201. package/src/io/export/README.md +0 -3
  202. package/src/io/export/export-session.ts +0 -220
  203. package/src/io/export/minimal-docx.ts +0 -115
  204. package/src/io/export/reattach-preserved-parts.ts +0 -54
  205. package/src/io/export/serialize-comments.ts +0 -947
  206. package/src/io/export/serialize-footnotes.ts +0 -399
  207. package/src/io/export/serialize-headers-footers.ts +0 -372
  208. package/src/io/export/serialize-main-document.ts +0 -1376
  209. package/src/io/export/serialize-numbering.ts +0 -118
  210. package/src/io/export/serialize-revisions.ts +0 -389
  211. package/src/io/export/serialize-runtime-revisions.ts +0 -269
  212. package/src/io/export/serialize-tables.ts +0 -174
  213. package/src/io/export/split-review-boundaries.ts +0 -356
  214. package/src/io/normalize/README.md +0 -3
  215. package/src/io/normalize/normalize-text.ts +0 -639
  216. package/src/io/ooxml/README.md +0 -3
  217. package/src/io/ooxml/highlight-colors.ts +0 -39
  218. package/src/io/ooxml/numbering-sentinels.ts +0 -44
  219. package/src/io/ooxml/parse-comments.ts +0 -846
  220. package/src/io/ooxml/parse-complex-content.ts +0 -287
  221. package/src/io/ooxml/parse-fields.ts +0 -834
  222. package/src/io/ooxml/parse-footnotes.ts +0 -896
  223. package/src/io/ooxml/parse-headers-footers.ts +0 -1169
  224. package/src/io/ooxml/parse-inline-media.ts +0 -461
  225. package/src/io/ooxml/parse-main-document.ts +0 -2877
  226. package/src/io/ooxml/parse-numbering.ts +0 -432
  227. package/src/io/ooxml/parse-revisions.ts +0 -931
  228. package/src/io/ooxml/parse-settings.ts +0 -184
  229. package/src/io/ooxml/parse-shapes.ts +0 -296
  230. package/src/io/ooxml/parse-styles.ts +0 -463
  231. package/src/io/ooxml/parse-tables.ts +0 -618
  232. package/src/io/ooxml/parse-theme.ts +0 -346
  233. package/src/io/ooxml/part-manifest.ts +0 -136
  234. package/src/io/ooxml/revision-boundaries.ts +0 -351
  235. package/src/io/opc/README.md +0 -3
  236. package/src/io/opc/corrupt-package.ts +0 -166
  237. package/src/io/opc/docx-package.ts +0 -74
  238. package/src/io/opc/package-reader.ts +0 -325
  239. package/src/io/opc/package-writer.ts +0 -273
  240. package/src/io/source-package-provenance.ts +0 -241
  241. package/src/legal/cross-references.ts +0 -414
  242. package/src/legal/defined-terms.ts +0 -203
  243. package/src/legal/index.ts +0 -32
  244. package/src/legal/signature-blocks.ts +0 -259
  245. package/src/model/README.md +0 -3
  246. package/src/model/canonical-document.ts +0 -2632
  247. package/src/model/cds-1.0.0.ts +0 -212
  248. package/src/model/snapshot.ts +0 -649
  249. package/src/preservation/README.md +0 -3
  250. package/src/preservation/markup-compatibility.ts +0 -48
  251. package/src/preservation/opaque-fragment-store.ts +0 -89
  252. package/src/preservation/opaque-region.ts +0 -233
  253. package/src/preservation/package-preservation.ts +0 -113
  254. package/src/preservation/preserved-part-manifest.ts +0 -56
  255. package/src/preservation/relationship-retention.ts +0 -57
  256. package/src/preservation/store.ts +0 -185
  257. package/src/review/README.md +0 -16
  258. package/src/review/store/README.md +0 -3
  259. package/src/review/store/comment-anchors.ts +0 -70
  260. package/src/review/store/comment-remapping.ts +0 -154
  261. package/src/review/store/comment-store.ts +0 -331
  262. package/src/review/store/comment-thread.ts +0 -109
  263. package/src/review/store/revision-actions.ts +0 -394
  264. package/src/review/store/revision-store.ts +0 -312
  265. package/src/review/store/revision-types.ts +0 -171
  266. package/src/review/store/runtime-comment-store.ts +0 -43
  267. package/src/runtime/README.md +0 -3
  268. package/src/runtime/ai-action-policy.ts +0 -764
  269. package/src/runtime/document-layout.ts +0 -332
  270. package/src/runtime/document-navigation.ts +0 -603
  271. package/src/runtime/document-runtime.ts +0 -3159
  272. package/src/runtime/document-search.ts +0 -145
  273. package/src/runtime/numbering-prefix.ts +0 -216
  274. package/src/runtime/page-layout-estimation.ts +0 -212
  275. package/src/runtime/read-only-diagnostics-runtime.ts +0 -241
  276. package/src/runtime/review-runtime.ts +0 -44
  277. package/src/runtime/revision-runtime.ts +0 -107
  278. package/src/runtime/session-capabilities.ts +0 -192
  279. package/src/runtime/story-context.ts +0 -164
  280. package/src/runtime/story-targeting.ts +0 -162
  281. package/src/runtime/surface-projection.ts +0 -1357
  282. package/src/runtime/table-commands.ts +0 -173
  283. package/src/runtime/table-schema.ts +0 -309
  284. package/src/runtime/view-state.ts +0 -477
  285. package/src/runtime/virtualized-rendering.ts +0 -258
  286. package/src/runtime/workflow-markup.ts +0 -353
  287. package/src/ui/README.md +0 -30
  288. package/src/ui/WordReviewEditor.tsx +0 -4086
  289. package/src/ui/browser-export.ts +0 -52
  290. package/src/ui/comments/README.md +0 -3
  291. package/src/ui/compatibility/README.md +0 -3
  292. package/src/ui/editor-command-bag.ts +0 -120
  293. package/src/ui/editor-runtime-boundary.ts +0 -1457
  294. package/src/ui/editor-shell-view.tsx +0 -142
  295. package/src/ui/editor-surface/README.md +0 -3
  296. package/src/ui/editor-surface-controller.tsx +0 -61
  297. package/src/ui/headless/comment-decoration-model.ts +0 -124
  298. package/src/ui/headless/preserve-editor-selection.ts +0 -5
  299. package/src/ui/headless/revision-decoration-model.ts +0 -128
  300. package/src/ui/headless/selection-helpers.ts +0 -54
  301. package/src/ui/headless/selection-toolbar-model.ts +0 -34
  302. package/src/ui/headless/use-editor-keyboard.ts +0 -103
  303. package/src/ui/review/README.md +0 -3
  304. package/src/ui/runtime-snapshot-selectors.ts +0 -197
  305. package/src/ui/shared/revision-filters.ts +0 -31
  306. package/src/ui/status/README.md +0 -3
  307. package/src/ui/theme/README.md +0 -3
  308. package/src/ui/toolbar/README.md +0 -3
  309. package/src/ui/workflow-surface-blocked-rails.ts +0 -94
  310. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +0 -64
  311. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +0 -129
  312. package/src/ui-tailwind/chrome/tw-layout-panel.tsx +0 -114
  313. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +0 -34
  314. package/src/ui-tailwind/chrome/tw-page-ruler.tsx +0 -386
  315. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +0 -186
  316. package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +0 -139
  317. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +0 -128
  318. package/src/ui-tailwind/chrome/tw-unsaved-modal.tsx +0 -58
  319. package/src/ui-tailwind/chrome/use-before-unload.ts +0 -20
  320. package/src/ui-tailwind/editor-surface/perf-probe.ts +0 -179
  321. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +0 -184
  322. package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +0 -31
  323. package/src/ui-tailwind/editor-surface/pm-decorations.ts +0 -427
  324. package/src/ui-tailwind/editor-surface/pm-position-map.ts +0 -123
  325. package/src/ui-tailwind/editor-surface/pm-schema.ts +0 -876
  326. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +0 -504
  327. package/src/ui-tailwind/editor-surface/search-plugin.ts +0 -168
  328. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +0 -61
  329. package/src/ui-tailwind/editor-surface/tw-caret.tsx +0 -12
  330. package/src/ui-tailwind/editor-surface/tw-editor-surface.tsx +0 -150
  331. package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +0 -129
  332. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +0 -58
  333. package/src/ui-tailwind/editor-surface/tw-paragraph-block.tsx +0 -151
  334. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +0 -944
  335. package/src/ui-tailwind/editor-surface/tw-segment-view.tsx +0 -111
  336. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +0 -436
  337. package/src/ui-tailwind/index.ts +0 -62
  338. package/src/ui-tailwind/page-chrome-model.ts +0 -27
  339. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +0 -406
  340. package/src/ui-tailwind/review/tw-health-panel.tsx +0 -149
  341. package/src/ui-tailwind/review/tw-review-rail.tsx +0 -120
  342. package/src/ui-tailwind/review/tw-revision-sidebar.tsx +0 -164
  343. package/src/ui-tailwind/status/tw-status-bar.tsx +0 -61
  344. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +0 -52
  345. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +0 -1064
  346. package/src/ui-tailwind/tw-review-workspace.tsx +0 -1417
  347. package/src/validation/README.md +0 -3
  348. package/src/validation/compatibility-engine.ts +0 -634
  349. package/src/validation/compatibility-report.ts +0 -161
  350. package/src/validation/diagnostics.ts +0 -204
  351. package/src/validation/docx-comment-proof.ts +0 -707
  352. package/src/validation/import-diagnostics.ts +0 -128
  353. package/src/validation/low-priority-word-surfaces.ts +0 -373
  354. /package/{src → dist}/ui-tailwind/theme/editor-theme.css +0 -0
@@ -0,0 +1,3874 @@
1
+ import {
2
+ DEFAULT_PAGE_ESTIMATE_PX_PER_TWIP,
3
+ canCreateDocxCommentAnchor,
4
+ estimateBlockHeight,
5
+ estimateParagraphLineCount,
6
+ estimateParagraphLineHeight,
7
+ findPageForOffset,
8
+ getUsableColumnWidth,
9
+ incrementInvalidationCounter,
10
+ recordPerfSample
11
+ } from "./chunk-GBVOWFIK.js";
12
+ import {
13
+ createDetachedAnchor,
14
+ createNodeAnchor,
15
+ createRangeAnchor
16
+ } from "./chunk-EBI3BX6U.js";
17
+
18
+ // src/runtime/session-capabilities.ts
19
+ function deriveCapabilities(snapshot, reviewMode, workflowScope) {
20
+ const hasFatalError = Boolean(snapshot.fatalError);
21
+ const isReady = snapshot.isReady;
22
+ const isReadOnly = snapshot.readOnly;
23
+ const exportBlocked = snapshot.compatibility.blockExport;
24
+ const activeStory = snapshot.activeStory ?? { kind: "main" };
25
+ const documentMode = snapshot.documentMode ?? "editing";
26
+ const phase = !isReady ? "loading" : hasFatalError ? "diagnostics" : "ready";
27
+ const mode = phase === "diagnostics" ? "read-only-diagnostics" : reviewMode;
28
+ const canEdit = isReady && !isReadOnly && !hasFatalError && documentMode !== "viewing";
29
+ const canUndo = snapshot.commandState.canUndo && canEdit;
30
+ const canRedo = snapshot.commandState.canRedo && canEdit;
31
+ const canAddComment = canEdit && activeStory.kind === "main" && !snapshot.selection.isCollapsed && Boolean(snapshot.surface) && canCreateDocxCommentAnchor(snapshot.surface, toRuntimeAnchor(snapshot.selection.activeRange));
32
+ const canExport = isReady && !exportBlocked && !hasFatalError;
33
+ const actionableRevisions = snapshot.trackedChanges.revisions.filter(
34
+ (r) => r.status === "active" && r.actionability === "actionable"
35
+ );
36
+ const canAcceptChange = canEdit && actionableRevisions.some((r) => r.canAccept);
37
+ const canRejectChange = canEdit && actionableRevisions.some((r) => r.canReject);
38
+ const canAcceptAll = canEdit && actionableRevisions.length > 0;
39
+ const canRejectAll = canEdit && actionableRevisions.length > 0;
40
+ const preserveOnlyCount = snapshot.compatibility.featureEntries.filter(
41
+ (e) => e.featureClass === "preserve-only"
42
+ ).length;
43
+ const unsupportedFatalCount = snapshot.compatibility.featureEntries.filter(
44
+ (e) => e.featureClass === "unsupported-fatal"
45
+ ).length;
46
+ const workflowOverlayPresent = workflowScope?.overlayPresent ?? false;
47
+ const workflowBlocked = (workflowScope?.blockedReasons?.length ?? 0) > 0;
48
+ const trackChangesSupported = snapshot.trackedChanges.totalCount > 0;
49
+ const hasTrustConcerns = exportBlocked || preserveOnlyCount > 0 || unsupportedFatalCount > 0 || snapshot.warnings.length > 0 || hasFatalError;
50
+ const reviewRailVisible = workflowOverlayPresent ? false : mode === "review" || mode === "read-only-diagnostics" || mode === "editing" && hasTrustConcerns;
51
+ const healthIssueCount = preserveOnlyCount + unsupportedFatalCount + snapshot.warnings.length;
52
+ const protection = snapshot.protectionSnapshot;
53
+ const hasDocumentProtection = protection?.hasDocumentProtection ?? false;
54
+ const protectedRangeCount = protection?.ranges?.length ?? 0;
55
+ return {
56
+ phase,
57
+ mode,
58
+ documentMode,
59
+ canUndo,
60
+ canRedo,
61
+ canEdit,
62
+ canAddComment,
63
+ canExport,
64
+ canAcceptChange,
65
+ canRejectChange,
66
+ canAcceptAll,
67
+ canRejectAll,
68
+ reviewRailVisible,
69
+ trackChangesSupported,
70
+ exportBlocked,
71
+ preserveOnlyCount,
72
+ unsupportedFatalCount,
73
+ hasDocumentProtection,
74
+ protectedRangeCount,
75
+ healthIssueCount,
76
+ workflowOverlayPresent,
77
+ workflowBlocked,
78
+ isDirty: snapshot.isDirty,
79
+ isReady,
80
+ hasFatalError
81
+ };
82
+ }
83
+ function toRuntimeAnchor(anchor) {
84
+ switch (anchor.kind) {
85
+ case "range":
86
+ return createRangeAnchor(anchor.from, anchor.to, anchor.assoc);
87
+ case "node":
88
+ return createNodeAnchor(anchor.at, anchor.assoc);
89
+ case "detached":
90
+ return createDetachedAnchor(anchor.lastKnownRange, anchor.reason);
91
+ }
92
+ }
93
+
94
+ // src/ui-tailwind/chrome/tw-alert-banner.tsx
95
+ import { AlertTriangle, XCircle } from "lucide-react";
96
+ import { jsx, jsxs } from "react/jsx-runtime";
97
+ function TwAlertBanner(props) {
98
+ const { snapshot, preserveOnlyCount, workflowBlockedReasons = [] } = props;
99
+ if (snapshot.fatalError) {
100
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-danger-soft text-danger text-xs", children: [
101
+ /* @__PURE__ */ jsx(XCircle, { className: "h-3.5 w-3.5 shrink-0" }),
102
+ /* @__PURE__ */ jsx("span", { children: snapshot.fatalError.message })
103
+ ] });
104
+ }
105
+ if (snapshot.compatibility.blockExport) {
106
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-danger-soft text-danger text-xs", children: [
107
+ /* @__PURE__ */ jsx(XCircle, { className: "h-3.5 w-3.5 shrink-0" }),
108
+ /* @__PURE__ */ jsxs("span", { children: [
109
+ "Export blocked \u2014",
110
+ " ",
111
+ snapshot.compatibility.blockExportReasons[0] ?? "unsupported content"
112
+ ] })
113
+ ] });
114
+ }
115
+ if (preserveOnlyCount > 0) {
116
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-warning-soft text-comment text-xs", children: [
117
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3.5 w-3.5 shrink-0" }),
118
+ /* @__PURE__ */ jsxs("span", { children: [
119
+ preserveOnlyCount,
120
+ " preserve-only feature",
121
+ preserveOnlyCount !== 1 ? "s" : "",
122
+ " detected"
123
+ ] })
124
+ ] });
125
+ }
126
+ if (workflowBlockedReasons.length > 0) {
127
+ const firstReason = workflowBlockedReasons[0];
128
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-amber-50 text-amber-700 text-xs", children: [
129
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3.5 w-3.5 shrink-0" }),
130
+ /* @__PURE__ */ jsxs("span", { children: [
131
+ firstReason.message,
132
+ workflowBlockedReasons.length > 1 ? ` (+${workflowBlockedReasons.length - 1} more)` : ""
133
+ ] })
134
+ ] });
135
+ }
136
+ return null;
137
+ }
138
+
139
+ // src/ui-tailwind/chrome/tw-selection-toolbar.tsx
140
+ import { forwardRef } from "react";
141
+ import * as Tooltip from "@radix-ui/react-tooltip";
142
+ import { Baseline, Bold, Highlighter, Italic, MessageSquare, Underline } from "lucide-react";
143
+
144
+ // src/ui/headless/preserve-editor-selection.ts
145
+ function preserveEditorSelectionMouseDown(event) {
146
+ event.preventDefault();
147
+ }
148
+
149
+ // src/ui-tailwind/chrome/tw-selection-toolbar.tsx
150
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
151
+ var focusRingClass = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas";
152
+ var TwSelectionToolbar = forwardRef(function TwSelectionToolbar2(props, ref) {
153
+ const { model } = props;
154
+ const addCommentDisabled = !model.canAddComment;
155
+ const formattingDisabled = !model.canToggleFormatting;
156
+ const contextLabel = summarizeSelectionContext(model);
157
+ const tooltipLabel = addCommentDisabled ? props.disabledReason ?? "Select text within one paragraph to add a DOCX comment" : "Add comment";
158
+ return /* @__PURE__ */ jsxs2(
159
+ "div",
160
+ {
161
+ ref,
162
+ "data-testid": "selection-toolbar",
163
+ className: "inline-flex max-w-[min(24rem,calc(100vw-2rem))] items-center gap-1.5 rounded-xl border border-border/80 bg-canvas px-1.5 py-1.5 shadow-lg ring-1 ring-border/80",
164
+ role: "toolbar",
165
+ "aria-label": "Selection actions",
166
+ onFocusCapture: props.onFocusCapture,
167
+ onBlurCapture: props.onBlurCapture,
168
+ children: [
169
+ /* @__PURE__ */ jsx2(
170
+ ToolbarActionButton,
171
+ {
172
+ icon: /* @__PURE__ */ jsx2(Bold, { className: "h-3.5 w-3.5" }),
173
+ label: "Bold selection",
174
+ pressed: model.boldActive,
175
+ disabled: formattingDisabled,
176
+ onClick: props.onToggleBold
177
+ }
178
+ ),
179
+ /* @__PURE__ */ jsx2(
180
+ ToolbarActionButton,
181
+ {
182
+ icon: /* @__PURE__ */ jsx2(Italic, { className: "h-3.5 w-3.5" }),
183
+ label: "Italic selection",
184
+ pressed: model.italicActive,
185
+ disabled: formattingDisabled,
186
+ onClick: props.onToggleItalic
187
+ }
188
+ ),
189
+ /* @__PURE__ */ jsx2(
190
+ ToolbarActionButton,
191
+ {
192
+ icon: /* @__PURE__ */ jsx2(Underline, { className: "h-3.5 w-3.5" }),
193
+ label: "Underline selection",
194
+ pressed: model.underlineActive,
195
+ disabled: formattingDisabled,
196
+ onClick: props.onToggleUnderline
197
+ }
198
+ ),
199
+ /* @__PURE__ */ jsx2(
200
+ ToolbarActionButton,
201
+ {
202
+ icon: /* @__PURE__ */ jsx2(Baseline, { className: "h-3.5 w-3.5" }),
203
+ label: "Text color blue",
204
+ pressed: false,
205
+ disabled: formattingDisabled,
206
+ onClick: () => props.onSetTextColor?.("#1660a8")
207
+ }
208
+ ),
209
+ /* @__PURE__ */ jsx2(
210
+ ToolbarActionButton,
211
+ {
212
+ icon: /* @__PURE__ */ jsx2(Highlighter, { className: "h-3.5 w-3.5" }),
213
+ label: "Highlight yellow",
214
+ pressed: false,
215
+ disabled: formattingDisabled,
216
+ onClick: () => props.onSetHighlightColor?.("#ffff00")
217
+ }
218
+ ),
219
+ /* @__PURE__ */ jsx2("div", { className: "mx-0.5 h-4 w-px bg-border" }),
220
+ /* @__PURE__ */ jsxs2(Tooltip.Root, { children: [
221
+ /* @__PURE__ */ jsx2(Tooltip.Trigger, { asChild: true, children: /* @__PURE__ */ jsx2(
222
+ "button",
223
+ {
224
+ type: "button",
225
+ "aria-label": "Add comment from selection",
226
+ disabled: addCommentDisabled,
227
+ onMouseDown: preserveEditorSelectionMouseDown,
228
+ onClick: props.onAddComment,
229
+ className: `inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors text-accent hover:bg-accent-soft disabled:cursor-not-allowed disabled:opacity-30 ${focusRingClass}`,
230
+ children: /* @__PURE__ */ jsx2(MessageSquare, { className: "h-3.5 w-3.5" })
231
+ }
232
+ ) }),
233
+ /* @__PURE__ */ jsx2(Tooltip.Portal, { children: /* @__PURE__ */ jsx2(
234
+ Tooltip.Content,
235
+ {
236
+ className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50",
237
+ sideOffset: 6,
238
+ children: tooltipLabel
239
+ }
240
+ ) })
241
+ ] }),
242
+ model.previewText ? /* @__PURE__ */ jsxs2(Fragment, { children: [
243
+ /* @__PURE__ */ jsx2("div", { className: "mx-0.5 h-4 w-px bg-border" }),
244
+ /* @__PURE__ */ jsx2("span", { className: "max-w-[8rem] truncate text-[11px] text-secondary", children: model.previewText })
245
+ ] }) : null,
246
+ contextLabel ? /* @__PURE__ */ jsxs2(Fragment, { children: [
247
+ !model.previewText ? /* @__PURE__ */ jsx2("div", { className: "mx-0.5 h-4 w-px bg-border" }) : null,
248
+ /* @__PURE__ */ jsx2(
249
+ "span",
250
+ {
251
+ className: `min-w-0 max-w-[11rem] truncate rounded-full px-2 py-0.5 text-[10px] font-medium tracking-[0.08em] ${model.badges.some((badge) => badge.tone === "accent") ? "bg-accent-soft text-accent" : "bg-surface text-tertiary"}`,
252
+ children: contextLabel
253
+ }
254
+ )
255
+ ] }) : null
256
+ ]
257
+ }
258
+ );
259
+ });
260
+ function summarizeSelectionContext(model) {
261
+ if (model.badges.length === 0) {
262
+ return null;
263
+ }
264
+ const accentBadges = model.badges.filter((badge) => badge.tone === "accent");
265
+ const source = accentBadges.length > 0 ? accentBadges : model.badges;
266
+ const labels = source.slice(0, 2).map((badge) => badge.label.trim()).filter(Boolean);
267
+ if (labels.length === 0) {
268
+ return null;
269
+ }
270
+ const summary = labels.join(" \xB7 ");
271
+ return summary.length > 30 ? `${summary.slice(0, 27)}...` : summary;
272
+ }
273
+ function ToolbarActionButton(props) {
274
+ return /* @__PURE__ */ jsxs2(Tooltip.Root, { children: [
275
+ /* @__PURE__ */ jsx2(Tooltip.Trigger, { asChild: true, children: /* @__PURE__ */ jsx2(
276
+ "button",
277
+ {
278
+ type: "button",
279
+ "aria-label": props.label,
280
+ "aria-pressed": props.pressed,
281
+ disabled: props.disabled,
282
+ onMouseDown: preserveEditorSelectionMouseDown,
283
+ onClick: props.onClick,
284
+ className: `inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors disabled:cursor-not-allowed disabled:opacity-30 ${props.pressed ? "bg-accent-soft text-accent" : "text-secondary hover:bg-surface"} ${focusRingClass}`,
285
+ children: props.icon
286
+ }
287
+ ) }),
288
+ /* @__PURE__ */ jsx2(Tooltip.Portal, { children: /* @__PURE__ */ jsx2(
289
+ Tooltip.Content,
290
+ {
291
+ className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50",
292
+ sideOffset: 6,
293
+ children: props.label
294
+ }
295
+ ) })
296
+ ] });
297
+ }
298
+
299
+ // src/ui-tailwind/review/tw-comment-sidebar.tsx
300
+ import { useCallback, useEffect, useRef, useState } from "react";
301
+ import { Check, CornerDownRight, RotateCcw } from "lucide-react";
302
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
303
+ var focusRingClass2 = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas";
304
+ function TwCommentSidebar(props) {
305
+ const { comments, activeCommentId, currentUserId } = props;
306
+ return /* @__PURE__ */ jsxs3("div", { className: "outline-none", children: [
307
+ /* @__PURE__ */ jsxs3("div", { className: "mb-2 flex items-center gap-2 text-[10px] text-tertiary", children: [
308
+ /* @__PURE__ */ jsxs3("span", { children: [
309
+ comments.openCommentIds.length,
310
+ " open"
311
+ ] }),
312
+ /* @__PURE__ */ jsx3("span", { className: "text-border", children: "\xB7" }),
313
+ /* @__PURE__ */ jsxs3("span", { children: [
314
+ comments.resolvedCommentIds.length,
315
+ " resolved"
316
+ ] }),
317
+ comments.detachedCommentIds.length > 0 && /* @__PURE__ */ jsxs3(Fragment2, { children: [
318
+ /* @__PURE__ */ jsx3("span", { className: "text-border", children: "\xB7" }),
319
+ /* @__PURE__ */ jsxs3("span", { children: [
320
+ comments.detachedCommentIds.length,
321
+ " detached"
322
+ ] })
323
+ ] })
324
+ ] }),
325
+ comments.threads.length > 0 ? /* @__PURE__ */ jsx3("div", { className: "space-y-1.5", children: comments.threads.map((thread) => /* @__PURE__ */ jsx3(
326
+ CommentThreadCard,
327
+ {
328
+ thread,
329
+ isActive: activeCommentId === thread.commentId,
330
+ currentUserId,
331
+ onOpenComment: props.onOpenComment,
332
+ onResolveComment: props.onResolveComment,
333
+ onReopenComment: props.onReopenComment,
334
+ onAddReply: props.onAddReply,
335
+ onEditBody: props.onEditBody
336
+ },
337
+ thread.commentId
338
+ )) }) : /* @__PURE__ */ jsx3("div", { className: "rounded-lg border border-dashed border-border bg-surface/60 px-2.5 py-3 text-[10px] leading-4 text-tertiary", children: "No comment threads yet. Select text and add one from the toolbar." })
339
+ ] });
340
+ }
341
+ function CommentThreadCard(props) {
342
+ const { thread, isActive } = props;
343
+ const leadEntry = thread.entries[0];
344
+ const isDraftThread = thread.status === "open" && thread.entryCount === 1 && isEmptyCommentBody(leadEntry?.body);
345
+ const isOwnComment = props.currentUserId != null && leadEntry?.authorId === props.currentUserId;
346
+ const canEdit = isOwnComment && thread.status === "open" && props.onEditBody != null;
347
+ const hasNoBody = isEmptyCommentBody(leadEntry?.body);
348
+ const showExcerpt = Boolean(thread.excerpt) && !isDraftThread && thread.excerpt !== "Empty thread";
349
+ const scrollRef = useCallback(
350
+ (node) => {
351
+ if (node && isActive && typeof node.scrollIntoView === "function") {
352
+ node.scrollIntoView({ behavior: "smooth", block: "nearest" });
353
+ }
354
+ },
355
+ [isActive]
356
+ );
357
+ return /* @__PURE__ */ jsxs3(
358
+ "div",
359
+ {
360
+ ref: scrollRef,
361
+ "data-comment-thread-id": thread.commentId,
362
+ "data-comment-thread-status": thread.status,
363
+ role: "button",
364
+ tabIndex: 0,
365
+ className: [
366
+ "cursor-pointer rounded-lg border px-2 py-1.5 transition-colors",
367
+ focusRingClass2,
368
+ isActive ? "border-accent/25 bg-accent-soft/35" : "border-border bg-canvas hover:border-border-strong hover:bg-surface/70",
369
+ thread.status === "detached" ? "opacity-70" : ""
370
+ ].join(" "),
371
+ onClick: () => props.onOpenComment?.(thread),
372
+ onKeyDown: (event) => {
373
+ if (event.key === "Enter" || event.key === " ") {
374
+ event.preventDefault();
375
+ props.onOpenComment?.(thread);
376
+ }
377
+ },
378
+ children: [
379
+ /* @__PURE__ */ jsxs3("div", { className: "mb-1 flex items-center gap-1.5", children: [
380
+ /* @__PURE__ */ jsx3("span", { className: "inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-accent/10 text-[8px] font-semibold text-accent", children: thread.createdBy.charAt(0).toUpperCase() }),
381
+ /* @__PURE__ */ jsx3("span", { className: "truncate text-[10px] font-medium text-primary", children: thread.createdBy }),
382
+ /* @__PURE__ */ jsx3("span", { "data-comment-thread-created-at": "true", className: "text-[9px] text-tertiary", children: formatCommentDate(thread.createdAt) }),
383
+ /* @__PURE__ */ jsx3("span", { className: "flex-1" }),
384
+ isDraftThread ? /* @__PURE__ */ jsx3(StatusBadge, { label: "draft", tone: "draft" }) : null,
385
+ thread.status === "resolved" ? /* @__PURE__ */ jsx3(StatusBadge, { label: "resolved", tone: "resolved" }) : null,
386
+ thread.status === "detached" ? /* @__PURE__ */ jsx3(StatusBadge, { label: "detached", tone: "detached" }) : null
387
+ ] }),
388
+ showExcerpt ? /* @__PURE__ */ jsx3("p", { className: "mb-1 rounded-md border-l-2 border-comment/25 bg-comment-soft/30 px-2 py-1 text-[9px] leading-4 text-comment/80 italic whitespace-pre-wrap break-words line-clamp-2", children: thread.excerpt }) : null,
389
+ canEdit && (isActive || hasNoBody) ? /* @__PURE__ */ jsx3(
390
+ InlineEditableBody,
391
+ {
392
+ body: leadEntry?.body ?? "",
393
+ autoFocus: isActive && hasNoBody,
394
+ onSave: (newBody) => props.onEditBody?.(thread.commentId, newBody),
395
+ label: isDraftThread ? "New comment" : void 0
396
+ }
397
+ ) : leadEntry?.body ? /* @__PURE__ */ jsx3(
398
+ "p",
399
+ {
400
+ className: "text-[10px] leading-[1.1rem] text-secondary whitespace-pre-wrap break-words line-clamp-4",
401
+ "data-comment-thread-body": "true",
402
+ children: leadEntry.body
403
+ }
404
+ ) : canEdit ? /* @__PURE__ */ jsx3(
405
+ "p",
406
+ {
407
+ className: "cursor-text text-[10px] italic text-tertiary",
408
+ onClick: (e) => {
409
+ e.stopPropagation();
410
+ props.onOpenComment?.(thread);
411
+ },
412
+ children: "New comment"
413
+ }
414
+ ) : null,
415
+ thread.entries.slice(1).map((entry) => /* @__PURE__ */ jsxs3("div", { className: "mt-1.5 ml-4 border-l border-border/50 pl-2.5", children: [
416
+ /* @__PURE__ */ jsxs3("div", { className: "mb-0.5 flex items-center gap-1", children: [
417
+ /* @__PURE__ */ jsx3("span", { className: "text-[9px] font-medium text-secondary", children: entry.authorId }),
418
+ /* @__PURE__ */ jsx3("span", { className: "text-[9px] text-tertiary", children: formatCommentDate(entry.createdAt) })
419
+ ] }),
420
+ /* @__PURE__ */ jsx3(
421
+ "p",
422
+ {
423
+ className: "text-[10px] leading-4 text-secondary whitespace-pre-wrap break-words line-clamp-3",
424
+ "data-comment-reply-body": "true",
425
+ children: entry.body
426
+ }
427
+ )
428
+ ] }, entry.entryId)),
429
+ thread.entryCount > thread.entries.length ? /* @__PURE__ */ jsxs3("p", { className: "mt-1 text-[9px] text-tertiary", children: [
430
+ "+",
431
+ thread.entryCount - thread.entries.length,
432
+ " more"
433
+ ] }) : null,
434
+ /* @__PURE__ */ jsxs3("div", { className: "mt-1 flex items-center gap-0.5", children: [
435
+ thread.status === "open" && /* @__PURE__ */ jsxs3(Fragment2, { children: [
436
+ /* @__PURE__ */ jsxs3(
437
+ "button",
438
+ {
439
+ type: "button",
440
+ className: "inline-flex items-center gap-0.5 rounded px-1 py-0.5 text-[9px] font-medium text-insert hover:bg-insert-soft transition-colors",
441
+ onClick: (e) => {
442
+ e.stopPropagation();
443
+ props.onResolveComment?.(thread.commentId);
444
+ },
445
+ children: [
446
+ /* @__PURE__ */ jsx3(Check, { className: "h-2 w-2" }),
447
+ " Resolve"
448
+ ]
449
+ }
450
+ ),
451
+ props.onAddReply && /* @__PURE__ */ jsx3(ReplyInput, { commentId: thread.commentId, onAddReply: props.onAddReply })
452
+ ] }),
453
+ thread.status === "resolved" && /* @__PURE__ */ jsxs3(
454
+ "button",
455
+ {
456
+ type: "button",
457
+ className: "inline-flex items-center gap-0.5 rounded px-1 py-0.5 text-[9px] font-medium text-secondary hover:bg-surface transition-colors",
458
+ "data-comment-thread-action": "reopen",
459
+ onClick: (e) => {
460
+ e.stopPropagation();
461
+ props.onReopenComment?.(thread.commentId);
462
+ },
463
+ children: [
464
+ /* @__PURE__ */ jsx3(RotateCcw, { className: "h-2 w-2" }),
465
+ " Reopen"
466
+ ]
467
+ }
468
+ ),
469
+ thread.status === "detached" && /* @__PURE__ */ jsx3("span", { className: "text-[9px] text-comment", children: "Detached" })
470
+ ] })
471
+ ]
472
+ }
473
+ );
474
+ }
475
+ function InlineEditableBody(props) {
476
+ const [isEditing, setIsEditing] = useState(props.autoFocus || props.body === "");
477
+ const [draft, setDraft] = useState(props.body);
478
+ const textareaRef = useRef(null);
479
+ useEffect(() => {
480
+ if (isEditing && textareaRef.current) {
481
+ textareaRef.current.focus();
482
+ textareaRef.current.setSelectionRange(draft.length, draft.length);
483
+ }
484
+ }, [isEditing]);
485
+ if (!isEditing) {
486
+ return /* @__PURE__ */ jsx3(
487
+ "p",
488
+ {
489
+ className: `cursor-text rounded px-1 text-[10px] leading-[1.15rem] -mx-1 transition-colors hover:bg-surface ${props.body ? "text-secondary" : "text-tertiary italic"}`,
490
+ onClick: (e) => {
491
+ e.stopPropagation();
492
+ setDraft(props.body);
493
+ setIsEditing(true);
494
+ },
495
+ title: "Click to edit",
496
+ children: props.body || "Click to add comment\u2026"
497
+ }
498
+ );
499
+ }
500
+ return /* @__PURE__ */ jsxs3("div", { className: "space-y-1", children: [
501
+ props.label ? /* @__PURE__ */ jsx3("span", { className: "block text-[10px] font-medium uppercase tracking-[0.08em] text-tertiary", children: props.label }) : null,
502
+ /* @__PURE__ */ jsx3(
503
+ "textarea",
504
+ {
505
+ ref: textareaRef,
506
+ className: "w-full resize-none rounded-md border border-border bg-surface px-2 py-1.5 text-[10px] leading-4 text-primary placeholder:text-tertiary focus:outline-none focus:ring-1 focus:ring-accent",
507
+ rows: 2,
508
+ value: draft,
509
+ placeholder: "Type your comment...",
510
+ onClick: (e) => e.stopPropagation(),
511
+ onChange: (e) => setDraft(e.target.value),
512
+ onBlur: () => {
513
+ if (draft.trim() && draft.trim() !== props.body) {
514
+ props.onSave(draft.trim());
515
+ }
516
+ setIsEditing(false);
517
+ },
518
+ onKeyDown: (e) => {
519
+ if (e.key === "Enter" && !e.shiftKey) {
520
+ e.preventDefault();
521
+ if (draft.trim() && draft.trim() !== props.body) {
522
+ props.onSave(draft.trim());
523
+ }
524
+ setIsEditing(false);
525
+ }
526
+ if (e.key === "Escape") {
527
+ setDraft(props.body);
528
+ setIsEditing(false);
529
+ }
530
+ e.stopPropagation();
531
+ }
532
+ }
533
+ )
534
+ ] });
535
+ }
536
+ function ReplyInput(props) {
537
+ const [body, setBody] = useState("");
538
+ const [isOpen, setIsOpen] = useState(false);
539
+ const inputRef = useRef(null);
540
+ useEffect(() => {
541
+ if (isOpen && inputRef.current) {
542
+ inputRef.current.focus();
543
+ }
544
+ }, [isOpen]);
545
+ if (!isOpen) {
546
+ return /* @__PURE__ */ jsxs3(
547
+ "button",
548
+ {
549
+ type: "button",
550
+ className: "inline-flex items-center gap-0.5 rounded px-1.5 py-0.5 text-[10px] font-medium text-tertiary hover:text-secondary hover:bg-surface transition-colors",
551
+ onClick: (e) => {
552
+ e.stopPropagation();
553
+ setIsOpen(true);
554
+ },
555
+ children: [
556
+ /* @__PURE__ */ jsx3(CornerDownRight, { className: "h-2.5 w-2.5" }),
557
+ " Reply"
558
+ ]
559
+ }
560
+ );
561
+ }
562
+ return /* @__PURE__ */ jsxs3("div", { className: "w-full mt-1.5", onClick: (e) => e.stopPropagation(), children: [
563
+ /* @__PURE__ */ jsx3(
564
+ "textarea",
565
+ {
566
+ ref: inputRef,
567
+ className: "w-full rounded border border-border bg-surface px-2 py-1 text-[11px] text-primary placeholder:text-tertiary resize-none focus:outline-none focus:ring-1 focus:ring-accent",
568
+ rows: 2,
569
+ placeholder: "Reply...",
570
+ value: body,
571
+ onChange: (e) => setBody(e.target.value),
572
+ onKeyDown: (e) => {
573
+ if (e.key === "Enter" && !e.shiftKey && body.trim()) {
574
+ e.preventDefault();
575
+ props.onAddReply(props.commentId, body.trim());
576
+ setBody("");
577
+ setIsOpen(false);
578
+ }
579
+ if (e.key === "Escape") {
580
+ setBody("");
581
+ setIsOpen(false);
582
+ }
583
+ e.stopPropagation();
584
+ }
585
+ }
586
+ ),
587
+ /* @__PURE__ */ jsxs3("div", { className: "flex gap-1 mt-0.5", children: [
588
+ /* @__PURE__ */ jsx3(
589
+ "button",
590
+ {
591
+ type: "button",
592
+ disabled: !body.trim(),
593
+ className: "rounded px-1.5 py-0.5 text-[10px] font-medium text-accent hover:bg-accent-soft transition-colors disabled:opacity-40",
594
+ onClick: () => {
595
+ if (body.trim()) {
596
+ props.onAddReply(props.commentId, body.trim());
597
+ setBody("");
598
+ setIsOpen(false);
599
+ }
600
+ },
601
+ children: "Send"
602
+ }
603
+ ),
604
+ /* @__PURE__ */ jsx3(
605
+ "button",
606
+ {
607
+ type: "button",
608
+ className: "rounded px-1.5 py-0.5 text-[10px] text-tertiary hover:bg-surface transition-colors",
609
+ onClick: () => {
610
+ setBody("");
611
+ setIsOpen(false);
612
+ },
613
+ children: "Cancel"
614
+ }
615
+ )
616
+ ] })
617
+ ] });
618
+ }
619
+ function formatCommentDate(raw) {
620
+ try {
621
+ const date = new Date(raw);
622
+ if (Number.isNaN(date.getTime())) return raw;
623
+ const now = /* @__PURE__ */ new Date();
624
+ const diffMs = now.getTime() - date.getTime();
625
+ const diffMin = Math.floor(diffMs / 6e4);
626
+ if (diffMin < 1) return "just now";
627
+ if (diffMin < 60) return `${diffMin}m ago`;
628
+ const diffHours = Math.floor(diffMin / 60);
629
+ if (diffHours < 24) return `${diffHours}h ago`;
630
+ const diffDays = Math.floor(diffHours / 24);
631
+ if (diffDays < 7) return `${diffDays}d ago`;
632
+ return new Intl.DateTimeFormat("en-US", {
633
+ month: "short",
634
+ day: "numeric",
635
+ year: date.getFullYear() !== now.getFullYear() ? "numeric" : void 0
636
+ }).format(date);
637
+ } catch {
638
+ return raw;
639
+ }
640
+ }
641
+ function StatusBadge(props) {
642
+ const styles = {
643
+ resolved: "text-insert bg-insert-soft",
644
+ detached: "text-comment bg-warning-soft",
645
+ draft: "text-secondary bg-subtle"
646
+ };
647
+ return /* @__PURE__ */ jsx3(
648
+ "span",
649
+ {
650
+ className: `shrink-0 rounded px-1 py-px text-[8px] font-medium uppercase tracking-[0.08em] ${styles[props.tone] ?? "text-secondary bg-subtle"}`,
651
+ "data-comment-thread-badge": props.tone,
652
+ children: props.label
653
+ }
654
+ );
655
+ }
656
+ function isEmptyCommentBody(body) {
657
+ return !body || body.trim() === "";
658
+ }
659
+
660
+ // src/ui-tailwind/review/tw-revision-sidebar.tsx
661
+ import { Check as Check2, X } from "lucide-react";
662
+
663
+ // src/ui/shared/revision-filters.ts
664
+ function selectVisibleRevisions(revisions, markupDisplay) {
665
+ switch (markupDisplay) {
666
+ case "clean":
667
+ case "simple":
668
+ return revisions.filter(
669
+ (revision) => revision.status === "active" && revision.actionability === "actionable"
670
+ );
671
+ case "all":
672
+ return [...revisions];
673
+ }
674
+ }
675
+
676
+ // src/ui-tailwind/review/tw-revision-sidebar.tsx
677
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
678
+ var focusRingClass3 = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas";
679
+ function TwRevisionSidebar(props) {
680
+ const { trackedChanges, markupDisplay, activeRevisionId } = props;
681
+ const visibleRevisions = selectVisibleRevisions(trackedChanges.revisions, markupDisplay);
682
+ const actionablePendingCount = trackedChanges.revisions.filter(
683
+ (r) => r.status === "active" && r.actionability === "actionable"
684
+ ).length;
685
+ return /* @__PURE__ */ jsxs4("div", { className: "outline-none", children: [
686
+ /* @__PURE__ */ jsxs4("p", { className: "mb-2 text-[10px] text-tertiary", children: [
687
+ trackedChanges.pendingChangeIds.length,
688
+ " active \xB7 ",
689
+ trackedChanges.acceptedChangeIds.length,
690
+ " accepted \xB7 ",
691
+ trackedChanges.preserveOnlyChangeIds.length,
692
+ " preserve-only"
693
+ ] }),
694
+ /* @__PURE__ */ jsxs4("div", { className: "mb-2 flex gap-1", children: [
695
+ /* @__PURE__ */ jsxs4(
696
+ "button",
697
+ {
698
+ type: "button",
699
+ disabled: actionablePendingCount === 0,
700
+ className: "inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-semibold text-accent hover:bg-accent-soft transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
701
+ onClick: props.onAcceptAllChanges,
702
+ children: [
703
+ "Accept all (",
704
+ actionablePendingCount,
705
+ ")"
706
+ ]
707
+ }
708
+ ),
709
+ /* @__PURE__ */ jsx4(
710
+ "button",
711
+ {
712
+ type: "button",
713
+ disabled: actionablePendingCount === 0,
714
+ className: "inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] text-secondary hover:bg-surface transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
715
+ onClick: props.onRejectAllChanges,
716
+ children: "Reject all"
717
+ }
718
+ )
719
+ ] }),
720
+ visibleRevisions.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "space-y-1", children: visibleRevisions.map((rev) => {
721
+ const isActive = activeRevisionId === rev.revisionId;
722
+ return /* @__PURE__ */ jsxs4(
723
+ "div",
724
+ {
725
+ role: "button",
726
+ tabIndex: 0,
727
+ className: `flex rounded-lg transition-colors cursor-pointer ${focusRingClass3} ${isActive ? "bg-accent-soft" : "hover:bg-surface"}`,
728
+ onClick: () => props.onOpenRevision?.(rev),
729
+ onKeyDown: (event) => {
730
+ if (event.key === "Enter" || event.key === " ") {
731
+ event.preventDefault();
732
+ props.onOpenRevision?.(rev);
733
+ }
734
+ },
735
+ children: [
736
+ /* @__PURE__ */ jsx4("div", { className: `w-0.5 shrink-0 rounded-l-lg ${rev.kind === "insertion" ? "bg-insert" : rev.kind === "deletion" ? "bg-danger" : "bg-tertiary"}` }),
737
+ /* @__PURE__ */ jsxs4("div", { className: "p-2 flex-1 min-w-0", children: [
738
+ /* @__PURE__ */ jsxs4("div", { className: "mb-0.5 flex items-start justify-between gap-2", children: [
739
+ /* @__PURE__ */ jsx4("span", { className: "text-[11px] font-medium text-primary", children: rev.anchorLabel }),
740
+ /* @__PURE__ */ jsx4(RevisionBadge, { status: rev.status, actionability: rev.actionability })
741
+ ] }),
742
+ /* @__PURE__ */ jsxs4("p", { className: "mb-1 text-[10px] text-tertiary", children: [
743
+ rev.authorId,
744
+ " \xB7 ",
745
+ rev.createdAt
746
+ ] }),
747
+ rev.excerpt ? /* @__PURE__ */ jsx4("p", { className: `text-[11px] ${rev.kind === "insertion" ? "text-insert" : rev.kind === "deletion" ? "text-danger line-through" : "text-secondary"}`, children: rev.excerpt }) : /* @__PURE__ */ jsx4("p", { className: "text-[11px] text-secondary", children: rev.label }),
748
+ rev.detail ? /* @__PURE__ */ jsx4("p", { className: "mt-1 text-[10px] text-secondary", children: rev.detail }) : null,
749
+ /* @__PURE__ */ jsx4("div", { className: "mt-1.5 flex gap-1", children: rev.actionability === "actionable" ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
750
+ /* @__PURE__ */ jsxs4(
751
+ "button",
752
+ {
753
+ type: "button",
754
+ disabled: !rev.canAccept || rev.status === "accepted",
755
+ className: "inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[10px] text-insert hover:bg-insert-soft transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
756
+ onClick: (e) => {
757
+ e.stopPropagation();
758
+ props.onAcceptRevision?.(rev.revisionId);
759
+ },
760
+ children: [
761
+ /* @__PURE__ */ jsx4(Check2, { className: "h-3 w-3" }),
762
+ " Accept"
763
+ ]
764
+ }
765
+ ),
766
+ /* @__PURE__ */ jsxs4(
767
+ "button",
768
+ {
769
+ type: "button",
770
+ disabled: !rev.canReject || rev.status === "rejected",
771
+ className: "inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[10px] text-danger hover:bg-delete-soft transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
772
+ onClick: (e) => {
773
+ e.stopPropagation();
774
+ props.onRejectRevision?.(rev.revisionId);
775
+ },
776
+ children: [
777
+ /* @__PURE__ */ jsx4(X, { className: "h-3 w-3" }),
778
+ " Reject"
779
+ ]
780
+ }
781
+ )
782
+ ] }) : /* @__PURE__ */ jsx4("span", { className: "px-1.5 py-0.5 text-[10px] text-tertiary", children: "Preserve-only" }) })
783
+ ] })
784
+ ]
785
+ },
786
+ rev.revisionId
787
+ );
788
+ }) }) : /* @__PURE__ */ jsx4("p", { className: "text-xs text-tertiary py-4", children: trackedChanges.totalCount > 0 ? "Switch to Full markup to see all tracked changes." : "Tracked change cards will appear here when present." })
789
+ ] });
790
+ }
791
+ function RevisionBadge(props) {
792
+ if (props.actionability === "preserve-only") {
793
+ return /* @__PURE__ */ jsx4("span", { className: "inline-flex items-center rounded px-1.5 py-0.5 text-[10px] font-medium text-comment bg-warning-soft", children: "preserve-only" });
794
+ }
795
+ const styles = {
796
+ active: "text-secondary bg-subtle",
797
+ accepted: "text-insert bg-insert-soft",
798
+ rejected: "text-danger bg-delete-soft",
799
+ detached: "text-comment bg-warning-soft"
800
+ };
801
+ return /* @__PURE__ */ jsx4("span", { className: `inline-flex items-center rounded px-1.5 py-0.5 text-[10px] font-medium ${styles[props.status] ?? "text-secondary bg-subtle"}`, children: props.status });
802
+ }
803
+
804
+ // src/ui-tailwind/review/tw-review-rail.tsx
805
+ import * as Tabs from "@radix-ui/react-tabs";
806
+ import * as ScrollArea from "@radix-ui/react-scroll-area";
807
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
808
+ var focusRingClass4 = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas";
809
+ function TwReviewRail(props) {
810
+ const warningCount = props.compatibility.featureEntries.filter(
811
+ (e) => e.featureClass !== "supported-roundtrip"
812
+ ).length + props.warnings.length;
813
+ return /* @__PURE__ */ jsx5(
814
+ "aside",
815
+ {
816
+ "aria-label": "Review rail",
817
+ className: "flex w-[304px] shrink-0 flex-col border-l border-border bg-canvas",
818
+ children: /* @__PURE__ */ jsxs5(
819
+ Tabs.Root,
820
+ {
821
+ value: props.activeTab,
822
+ onValueChange: (v) => props.onActiveTabChange(v),
823
+ className: "flex flex-1 flex-col min-h-0",
824
+ children: [
825
+ /* @__PURE__ */ jsxs5(Tabs.List, { className: "flex shrink-0 border-b border-border px-2", children: [
826
+ /* @__PURE__ */ jsxs5(
827
+ Tabs.Trigger,
828
+ {
829
+ value: "comments",
830
+ className: `flex-1 py-2 text-xs text-tertiary font-medium transition-colors data-[state=active]:text-primary data-[state=active]:font-semibold data-[state=active]:shadow-[inset_0_-2px_0_var(--color-accent)] outline-none ${focusRingClass4}`,
831
+ children: [
832
+ "Comments",
833
+ " ",
834
+ /* @__PURE__ */ jsx5("span", { className: "ml-1 inline-flex min-w-[14px] items-center justify-center rounded-full bg-subtle px-1.5 py-px text-[10px] font-medium text-tertiary", children: props.comments.totalCount })
835
+ ]
836
+ }
837
+ ),
838
+ /* @__PURE__ */ jsxs5(
839
+ Tabs.Trigger,
840
+ {
841
+ value: "changes",
842
+ className: `flex-1 py-2 text-xs text-tertiary font-medium transition-colors data-[state=active]:text-primary data-[state=active]:font-semibold data-[state=active]:shadow-[inset_0_-2px_0_var(--color-accent)] outline-none ${focusRingClass4}`,
843
+ children: [
844
+ "Changes",
845
+ " ",
846
+ /* @__PURE__ */ jsx5("span", { className: "ml-1 inline-flex min-w-[14px] items-center justify-center rounded-full bg-subtle px-1.5 py-px text-[10px] font-medium text-tertiary", children: props.trackedChanges.totalCount })
847
+ ]
848
+ }
849
+ )
850
+ ] }),
851
+ /* @__PURE__ */ jsxs5(ScrollArea.Root, { className: "flex-1 min-h-0", children: [
852
+ /* @__PURE__ */ jsxs5(ScrollArea.Viewport, { className: "h-full w-full", children: [
853
+ /* @__PURE__ */ jsx5(Tabs.Content, { value: "comments", className: "p-2.5 outline-none", children: /* @__PURE__ */ jsx5(
854
+ TwCommentSidebar,
855
+ {
856
+ currentUserId: props.currentUserId,
857
+ comments: props.comments,
858
+ activeCommentId: props.activeCommentId,
859
+ onOpenComment: props.onOpenComment,
860
+ onResolveComment: props.onResolveComment,
861
+ onReopenComment: props.onReopenComment,
862
+ onAddReply: props.onAddReply,
863
+ onEditBody: props.onEditBody
864
+ }
865
+ ) }),
866
+ /* @__PURE__ */ jsx5(Tabs.Content, { value: "changes", className: "p-2.5 outline-none", children: /* @__PURE__ */ jsx5(
867
+ TwRevisionSidebar,
868
+ {
869
+ trackedChanges: props.trackedChanges,
870
+ markupDisplay: props.markupDisplay,
871
+ activeRevisionId: props.activeRevisionId,
872
+ onOpenRevision: props.onOpenRevision,
873
+ onAcceptRevision: props.onAcceptRevision,
874
+ onRejectRevision: props.onRejectRevision,
875
+ onAcceptAllChanges: props.onAcceptAllChanges,
876
+ onRejectAllChanges: props.onRejectAllChanges
877
+ }
878
+ ) })
879
+ ] }),
880
+ /* @__PURE__ */ jsx5(
881
+ ScrollArea.Scrollbar,
882
+ {
883
+ orientation: "vertical",
884
+ className: "flex w-1.5 touch-none select-none p-0.5",
885
+ children: /* @__PURE__ */ jsx5(ScrollArea.Thumb, { className: "relative flex-1 rounded-full bg-black/[0.12]" })
886
+ }
887
+ )
888
+ ] })
889
+ ]
890
+ }
891
+ )
892
+ }
893
+ );
894
+ }
895
+
896
+ // src/ui-tailwind/status/tw-status-bar.tsx
897
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
898
+ function TwStatusBar(props) {
899
+ const saveState = props.isExportBlocked ? "Read-only" : props.isDirty ? "Unsaved" : "Ready";
900
+ const exportState = props.isExportBlocked ? "Blocked" : props.preserveOnlyCount > 0 ? "Warnings" : "Ready";
901
+ return /* @__PURE__ */ jsxs6(
902
+ "footer",
903
+ {
904
+ "data-testid": "status-bar",
905
+ className: "flex h-7 shrink-0 items-center gap-4 border-t border-border px-3 text-xs text-tertiary",
906
+ children: [
907
+ /* @__PURE__ */ jsxs6("span", { className: "flex items-center gap-1.5", children: [
908
+ /* @__PURE__ */ jsx6(
909
+ "span",
910
+ {
911
+ className: `inline-block h-1.5 w-1.5 rounded-full ${props.isExportBlocked ? "bg-danger" : props.isDirty ? "bg-comment" : "bg-insert"}`
912
+ }
913
+ ),
914
+ saveState
915
+ ] }),
916
+ /* @__PURE__ */ jsxs6("span", { className: "flex items-center gap-1.5", children: [
917
+ /* @__PURE__ */ jsx6(
918
+ "span",
919
+ {
920
+ className: `inline-block h-1.5 w-1.5 rounded-full ${props.isExportBlocked ? "bg-danger" : props.preserveOnlyCount > 0 ? "bg-comment" : "bg-insert"}`
921
+ }
922
+ ),
923
+ "Export ",
924
+ exportState.toLowerCase()
925
+ ] }),
926
+ /* @__PURE__ */ jsxs6("span", { children: [
927
+ props.commentCount,
928
+ " comment",
929
+ props.commentCount !== 1 ? "s" : "",
930
+ " \xB7",
931
+ " ",
932
+ props.changeCount,
933
+ " change",
934
+ props.changeCount !== 1 ? "s" : ""
935
+ ] }),
936
+ /* @__PURE__ */ jsx6("span", { className: "flex-1" }),
937
+ /* @__PURE__ */ jsx6("span", { children: props.sessionId })
938
+ ]
939
+ }
940
+ );
941
+ }
942
+
943
+ // src/ui-tailwind/review/tw-health-panel.tsx
944
+ import { AlertTriangle as AlertTriangle2, Info, Shield, ShieldAlert, ShieldCheck } from "lucide-react";
945
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
946
+ function TwHealthPanel(props) {
947
+ const { compatibility, warnings, blockedReasons = [] } = props;
948
+ const supportedCount = compatibility.featureEntries.filter(
949
+ (e) => e.featureClass === "supported-roundtrip"
950
+ ).length;
951
+ const preserveOnlyCount = compatibility.featureEntries.filter(
952
+ (e) => e.featureClass === "preserve-only"
953
+ ).length;
954
+ const blockedCount = compatibility.featureEntries.filter(
955
+ (e) => e.featureClass === "unsupported-fatal"
956
+ ).length;
957
+ return /* @__PURE__ */ jsxs7("div", { className: "outline-none", children: [
958
+ /* @__PURE__ */ jsxs7("p", { className: "text-xs text-tertiary mb-3", children: [
959
+ supportedCount,
960
+ " supported \xB7 ",
961
+ preserveOnlyCount,
962
+ " preserve-only \xB7 ",
963
+ blockedCount,
964
+ " blocked",
965
+ warnings.length > 0 ? ` \xB7 ${warnings.length} warning${warnings.length !== 1 ? "s" : ""}` : ""
966
+ ] }),
967
+ /* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
968
+ compatibility.featureEntries.map((entry) => /* @__PURE__ */ jsxs7("div", { className: "flex rounded-lg transition-colors hover:bg-surface", children: [
969
+ entry.featureClass !== "supported-roundtrip" ? /* @__PURE__ */ jsx7("div", { className: `w-0.5 shrink-0 rounded-l-lg ${entry.featureClass === "unsupported-fatal" ? "bg-danger" : "bg-comment"}` }) : null,
970
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-start gap-2 p-2.5 flex-1", children: [
971
+ /* @__PURE__ */ jsx7(HealthIcon, { featureClass: entry.featureClass }),
972
+ /* @__PURE__ */ jsxs7("div", { className: "flex-1 min-w-0", children: [
973
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-start justify-between gap-2", children: [
974
+ /* @__PURE__ */ jsx7("span", { className: "text-sm font-medium text-primary", children: entry.message }),
975
+ /* @__PURE__ */ jsx7(FeatureClassBadge, { featureClass: entry.featureClass })
976
+ ] }),
977
+ /* @__PURE__ */ jsx7("p", { className: "text-xs text-tertiary mt-0.5", children: entry.featureKey })
978
+ ] })
979
+ ] })
980
+ ] }, entry.featureEntryId)),
981
+ warnings.map((warning) => /* @__PURE__ */ jsxs7("div", { className: "flex rounded-lg transition-colors hover:bg-surface", children: [
982
+ /* @__PURE__ */ jsx7("div", { className: `w-0.5 shrink-0 rounded-l-lg ${warning.severity === "warning" ? "bg-comment" : "bg-accent"}` }),
983
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-start gap-2 p-2.5 flex-1", children: [
984
+ warning.severity === "warning" ? /* @__PURE__ */ jsx7(AlertTriangle2, { className: "h-4 w-4 text-comment shrink-0 mt-0.5" }) : /* @__PURE__ */ jsx7(Info, { className: "h-4 w-4 text-accent shrink-0 mt-0.5" }),
985
+ /* @__PURE__ */ jsxs7("div", { className: "flex-1 min-w-0", children: [
986
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-start justify-between gap-2", children: [
987
+ /* @__PURE__ */ jsx7("span", { className: "text-sm font-medium text-primary", children: warning.message }),
988
+ /* @__PURE__ */ jsx7("span", { className: `inline-flex items-center rounded px-1.5 py-0.5 text-[10px] font-medium ${warning.severity === "warning" ? "text-comment bg-warning-soft" : "text-accent bg-accent-soft"}`, children: warning.code.replace(/_/g, " ") })
989
+ ] }),
990
+ /* @__PURE__ */ jsx7("p", { className: "text-xs text-tertiary mt-0.5", children: warning.source })
991
+ ] })
992
+ ] })
993
+ ] }, warning.warningId)),
994
+ blockedReasons.length > 0 ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
995
+ /* @__PURE__ */ jsx7("div", { className: "border-t border-border mt-2 pt-2", children: /* @__PURE__ */ jsx7("p", { className: "text-xs font-medium text-tertiary mb-1", children: "Workflow blocked reasons" }) }),
996
+ blockedReasons.map((reason, index) => /* @__PURE__ */ jsxs7("div", { className: "flex rounded-lg transition-colors hover:bg-surface", children: [
997
+ /* @__PURE__ */ jsx7("div", { className: "w-0.5 shrink-0 rounded-l-lg bg-amber-400" }),
998
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-start gap-2 p-2.5 flex-1", children: [
999
+ /* @__PURE__ */ jsx7(ShieldAlert, { className: "h-4 w-4 text-amber-500 shrink-0 mt-0.5" }),
1000
+ /* @__PURE__ */ jsxs7("div", { className: "flex-1 min-w-0", children: [
1001
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-start justify-between gap-2", children: [
1002
+ /* @__PURE__ */ jsx7("span", { className: "text-sm font-medium text-primary", children: reason.message }),
1003
+ /* @__PURE__ */ jsx7("span", { className: "inline-flex items-center rounded px-1.5 py-0.5 text-[10px] font-medium text-amber-700 bg-amber-100", children: reason.code.replace(/_/g, " ") })
1004
+ ] }),
1005
+ reason.scopeId ? /* @__PURE__ */ jsxs7("p", { className: "text-xs text-tertiary mt-0.5", children: [
1006
+ "scope: ",
1007
+ reason.scopeId
1008
+ ] }) : null
1009
+ ] })
1010
+ ] })
1011
+ ] }, `blocked-${index}`))
1012
+ ] }) : null,
1013
+ compatibility.featureEntries.length === 0 && warnings.length === 0 && blockedReasons.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "text-xs text-tertiary py-4", children: "No compatibility entries or warnings to display." }) : null
1014
+ ] })
1015
+ ] });
1016
+ }
1017
+ function HealthIcon(props) {
1018
+ switch (props.featureClass) {
1019
+ case "supported-roundtrip":
1020
+ return /* @__PURE__ */ jsx7(ShieldCheck, { className: "h-4 w-4 text-insert shrink-0 mt-0.5" });
1021
+ case "preserve-only":
1022
+ return /* @__PURE__ */ jsx7(Shield, { className: "h-4 w-4 text-comment shrink-0 mt-0.5" });
1023
+ case "unsupported-fatal":
1024
+ return /* @__PURE__ */ jsx7(ShieldAlert, { className: "h-4 w-4 text-danger shrink-0 mt-0.5" });
1025
+ }
1026
+ }
1027
+ function FeatureClassBadge(props) {
1028
+ const styles = {
1029
+ "supported-roundtrip": "text-insert bg-insert-soft",
1030
+ "preserve-only": "text-comment bg-warning-soft",
1031
+ "unsupported-fatal": "text-danger bg-delete-soft"
1032
+ };
1033
+ const labels = {
1034
+ "supported-roundtrip": "supported",
1035
+ "preserve-only": "preserve-only",
1036
+ "unsupported-fatal": "blocked"
1037
+ };
1038
+ return /* @__PURE__ */ jsx7("span", { className: `inline-flex items-center rounded px-1.5 py-0.5 text-[10px] font-medium ${styles[props.featureClass]}`, children: labels[props.featureClass] });
1039
+ }
1040
+
1041
+ // src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx
1042
+ import * as Tooltip2 from "@radix-ui/react-tooltip";
1043
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1044
+ var focusRingClass5 = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas";
1045
+ function TwToolbarIconButton(props) {
1046
+ return /* @__PURE__ */ jsxs8(Tooltip2.Root, { children: [
1047
+ /* @__PURE__ */ jsx8(Tooltip2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx8(
1048
+ "button",
1049
+ {
1050
+ type: "button",
1051
+ "aria-label": props.label,
1052
+ disabled: props.disabled,
1053
+ onMouseDown: preserveEditorSelectionMouseDown,
1054
+ onClick: props.onClick,
1055
+ className: [
1056
+ "inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors outline-none",
1057
+ "disabled:opacity-30 disabled:cursor-not-allowed",
1058
+ props.emphasis ? "text-accent hover:bg-accent-soft" : props.active ? "bg-accent-soft text-accent" : "text-secondary hover:bg-surface hover:text-primary",
1059
+ focusRingClass5
1060
+ ].join(" "),
1061
+ children: /* @__PURE__ */ jsx8(props.icon, { className: "h-4 w-4" })
1062
+ }
1063
+ ) }),
1064
+ /* @__PURE__ */ jsx8(Tooltip2.Portal, { children: /* @__PURE__ */ jsx8(
1065
+ Tooltip2.Content,
1066
+ {
1067
+ className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50",
1068
+ sideOffset: 6,
1069
+ children: props.label
1070
+ }
1071
+ ) })
1072
+ ] });
1073
+ }
1074
+
1075
+ // src/ui-tailwind/toolbar/tw-toolbar.tsx
1076
+ import React3 from "react";
1077
+ import * as Popover from "@radix-ui/react-popover";
1078
+ import * as Select from "@radix-ui/react-select";
1079
+ import * as Toggle from "@radix-ui/react-toggle";
1080
+ import * as ToggleGroup from "@radix-ui/react-toggle-group";
1081
+ import * as Tooltip3 from "@radix-ui/react-tooltip";
1082
+ import {
1083
+ AlignCenter,
1084
+ AlignJustify,
1085
+ AlignLeft,
1086
+ AlignRight,
1087
+ Baseline as Baseline2,
1088
+ Bold as Bold2,
1089
+ ChevronDown,
1090
+ Download,
1091
+ Eye,
1092
+ EyeOff,
1093
+ FileText,
1094
+ Highlighter as Highlighter2,
1095
+ ImagePlus,
1096
+ Indent,
1097
+ Italic as Italic2,
1098
+ MessageSquare as MessageSquare2,
1099
+ Minus,
1100
+ Monitor,
1101
+ MoreHorizontal,
1102
+ Outdent,
1103
+ Plus,
1104
+ Redo2,
1105
+ Rows3,
1106
+ Strikethrough,
1107
+ Subscript,
1108
+ Superscript,
1109
+ ShieldAlert as ShieldAlert2,
1110
+ ShieldCheck as ShieldCheck2,
1111
+ Underline as Underline2,
1112
+ Undo2
1113
+ } from "lucide-react";
1114
+ import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1115
+ function getSupportedZoomPresets() {
1116
+ return [75, 100, 125, 150];
1117
+ }
1118
+ var focusRingClass6 = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas";
1119
+ var FONT_FAMILIES = ["Arial", "Times New Roman", "Calibri", "Cambria", "Georgia", "Verdana"];
1120
+ var FONT_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 36];
1121
+ var TEXT_COLORS = ["#000000", "#434343", "#1660a8", "#1a7f37", "#cf222e", "#7a4f00"];
1122
+ var HIGHLIGHT_COLORS = [
1123
+ { value: "#ffff00", label: "Yellow" },
1124
+ { value: "#00ff00", label: "Green" },
1125
+ { value: "#00ffff", label: "Cyan" },
1126
+ { value: "#ff69b4", label: "Pink" },
1127
+ { value: null, label: "None" }
1128
+ ];
1129
+ function TwToolbar(props) {
1130
+ const caps = props.capabilities;
1131
+ const workspaceMode = props.workspaceMode;
1132
+ const isPageMode = workspaceMode === "page";
1133
+ const paragraphStyles = props.styleCatalog?.paragraphs ?? [];
1134
+ const zoomLevel = props.zoomLevel ?? 100;
1135
+ const canEdit = props.interactionPolicy?.canFormatText ?? (caps ? caps.canEdit : false);
1136
+ const canInsertStructural = props.interactionPolicy?.canInsertStructural ?? canEdit;
1137
+ const canAddComment = props.interactionPolicy?.canAddComment ?? (caps ? caps.canAddComment : false);
1138
+ const zoomLabel = typeof zoomLevel === "number" ? `${zoomLevel}%` : zoomLevel === "pageWidth" ? "Fit width" : "Fit page";
1139
+ return /* @__PURE__ */ jsxs9("header", { className: "flex h-10 shrink-0 items-center gap-1 border-b border-border px-2", children: [
1140
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-0.5", children: [
1141
+ /* @__PURE__ */ jsx9(
1142
+ TwToolbarIconButton,
1143
+ {
1144
+ icon: Undo2,
1145
+ label: "Undo",
1146
+ disabled: caps ? !caps.canUndo : true,
1147
+ onClick: props.onUndo
1148
+ }
1149
+ ),
1150
+ /* @__PURE__ */ jsx9(
1151
+ TwToolbarIconButton,
1152
+ {
1153
+ icon: Redo2,
1154
+ label: "Redo",
1155
+ disabled: caps ? !caps.canRedo : true,
1156
+ onClick: props.onRedo
1157
+ }
1158
+ ),
1159
+ /* @__PURE__ */ jsx9("div", { className: "mx-1 h-4 w-px bg-border" }),
1160
+ /* @__PURE__ */ jsx9(
1161
+ ToolbarParagraphStyleSelect,
1162
+ {
1163
+ disabled: !canEdit || paragraphStyles.length === 0 || !props.onSetParagraphStyle,
1164
+ styles: paragraphStyles,
1165
+ value: props.formattingState?.paragraphStyleId,
1166
+ onValueChange: props.onSetParagraphStyle
1167
+ }
1168
+ ),
1169
+ /* @__PURE__ */ jsx9(
1170
+ ToolbarFontFamilySelect,
1171
+ {
1172
+ disabled: !canEdit || !props.onSetFontFamily,
1173
+ value: props.formattingState?.fontFamily,
1174
+ onValueChange: props.onSetFontFamily
1175
+ }
1176
+ ),
1177
+ /* @__PURE__ */ jsx9(
1178
+ ToolbarFontSizeSelect,
1179
+ {
1180
+ disabled: !canEdit || !props.onSetFontSize,
1181
+ value: props.formattingState?.fontSize,
1182
+ onValueChange: props.onSetFontSize
1183
+ }
1184
+ ),
1185
+ /* @__PURE__ */ jsx9("div", { className: "mx-1 h-4 w-px bg-border" }),
1186
+ /* @__PURE__ */ jsx9(
1187
+ TwToolbarIconButton,
1188
+ {
1189
+ icon: Bold2,
1190
+ label: "Bold",
1191
+ active: props.formattingState?.bold ?? false,
1192
+ disabled: !canEdit,
1193
+ onClick: props.onToggleBold
1194
+ }
1195
+ ),
1196
+ /* @__PURE__ */ jsx9(
1197
+ TwToolbarIconButton,
1198
+ {
1199
+ icon: Italic2,
1200
+ label: "Italic",
1201
+ active: props.formattingState?.italic ?? false,
1202
+ disabled: !canEdit,
1203
+ onClick: props.onToggleItalic
1204
+ }
1205
+ ),
1206
+ /* @__PURE__ */ jsx9(
1207
+ TwToolbarIconButton,
1208
+ {
1209
+ icon: Underline2,
1210
+ label: "Underline",
1211
+ active: props.formattingState?.underline ?? false,
1212
+ disabled: !canEdit,
1213
+ onClick: props.onToggleUnderline
1214
+ }
1215
+ ),
1216
+ /* @__PURE__ */ jsx9(
1217
+ ToolbarFormattingOverflow,
1218
+ {
1219
+ disabled: !canEdit,
1220
+ formattingState: props.formattingState,
1221
+ onToggleStrikethrough: props.onToggleStrikethrough,
1222
+ onToggleSuperscript: props.onToggleSuperscript,
1223
+ onToggleSubscript: props.onToggleSubscript
1224
+ }
1225
+ ),
1226
+ /* @__PURE__ */ jsx9(
1227
+ ToolbarColorPopover,
1228
+ {
1229
+ ariaLabel: "Text color",
1230
+ colors: TEXT_COLORS.map((value) => ({ value, label: value })),
1231
+ disabled: !canEdit || !props.onSetTextColor,
1232
+ icon: /* @__PURE__ */ jsx9(Baseline2, { className: "h-3.5 w-3.5" }),
1233
+ onSelect: (value) => {
1234
+ if (value) {
1235
+ props.onSetTextColor?.(value);
1236
+ }
1237
+ },
1238
+ title: "Text color"
1239
+ }
1240
+ ),
1241
+ /* @__PURE__ */ jsx9(
1242
+ ToolbarColorPopover,
1243
+ {
1244
+ ariaLabel: "Highlight color",
1245
+ colors: HIGHLIGHT_COLORS.map((entry) => ({ value: entry.value, label: entry.label })),
1246
+ disabled: !canEdit || !props.onSetHighlightColor,
1247
+ icon: /* @__PURE__ */ jsx9(Highlighter2, { className: "h-3.5 w-3.5" }),
1248
+ onSelect: (value) => props.onSetHighlightColor?.(value),
1249
+ title: "Highlight color"
1250
+ }
1251
+ ),
1252
+ /* @__PURE__ */ jsx9(
1253
+ ToolbarAlignmentPopover,
1254
+ {
1255
+ activeAlignment: props.formattingState?.alignment,
1256
+ disabled: !canEdit || !props.onSetAlignment,
1257
+ onSelect: (alignment) => props.onSetAlignment?.(alignment)
1258
+ }
1259
+ ),
1260
+ /* @__PURE__ */ jsx9("div", { className: "mx-1 h-4 w-px bg-border" }),
1261
+ /* @__PURE__ */ jsx9(
1262
+ TwToolbarIconButton,
1263
+ {
1264
+ icon: Outdent,
1265
+ label: "Outdent",
1266
+ disabled: !canEdit,
1267
+ onClick: props.onOutdent
1268
+ }
1269
+ ),
1270
+ /* @__PURE__ */ jsx9(
1271
+ TwToolbarIconButton,
1272
+ {
1273
+ icon: Indent,
1274
+ label: "Indent",
1275
+ disabled: !canEdit,
1276
+ onClick: props.onIndent
1277
+ }
1278
+ ),
1279
+ /* @__PURE__ */ jsx9(
1280
+ ToolbarInsertMenu,
1281
+ {
1282
+ disabled: !canInsertStructural,
1283
+ onInsertImage: props.onInsertImage,
1284
+ onInsertPageBreak: props.onInsertPageBreak,
1285
+ onInsertSectionBreak: props.onInsertSectionBreak,
1286
+ onInsertTable: props.onInsertTable
1287
+ }
1288
+ ),
1289
+ props.activeStory && props.activeStory.kind !== "main" ? /* @__PURE__ */ jsxs9(Fragment5, { children: [
1290
+ /* @__PURE__ */ jsx9("div", { className: "mx-1 h-4 w-px bg-border" }),
1291
+ /* @__PURE__ */ jsxs9(
1292
+ "button",
1293
+ {
1294
+ type: "button",
1295
+ onClick: props.onCloseStory,
1296
+ onMouseDown: preserveEditorSelectionMouseDown,
1297
+ className: `inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-xs font-medium text-accent hover:bg-accent-soft transition-colors outline-none ${focusRingClass6}`,
1298
+ "aria-label": `Editing ${storyLabel(props.activeStory)} \u2014 click to return to main body`,
1299
+ children: [
1300
+ /* @__PURE__ */ jsx9("span", { className: "text-secondary", children: "\u2190" }),
1301
+ storyLabel(props.activeStory)
1302
+ ]
1303
+ }
1304
+ )
1305
+ ] }) : null
1306
+ ] }),
1307
+ /* @__PURE__ */ jsx9("div", { className: "flex-1 text-center min-w-0", children: /* @__PURE__ */ jsx9("span", { className: "text-sm font-medium truncate block", children: props.sourceLabel ?? "Untitled" }) }),
1308
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-0.5", children: [
1309
+ /* @__PURE__ */ jsx9(
1310
+ TwToolbarIconButton,
1311
+ {
1312
+ icon: MessageSquare2,
1313
+ label: "Add comment",
1314
+ disabled: !canAddComment,
1315
+ emphasis: true,
1316
+ onClick: props.onAddComment
1317
+ }
1318
+ ),
1319
+ /* @__PURE__ */ jsxs9(Tooltip3.Root, { children: [
1320
+ /* @__PURE__ */ jsx9(Tooltip3.Trigger, { asChild: true, children: /* @__PURE__ */ jsx9(
1321
+ Toggle.Root,
1322
+ {
1323
+ pressed: props.showTrackedChanges,
1324
+ onPressedChange: props.onShowTrackedChangesChange,
1325
+ disabled: caps ? !caps.trackChangesSupported : false,
1326
+ onMouseDown: preserveEditorSelectionMouseDown,
1327
+ className: `inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface data-[state=on]:bg-accent-soft data-[state=on]:text-accent outline-none disabled:opacity-40 ${focusRingClass6}`,
1328
+ children: props.showTrackedChanges ? /* @__PURE__ */ jsx9(Eye, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx9(EyeOff, { className: "h-4 w-4" })
1329
+ }
1330
+ ) }),
1331
+ /* @__PURE__ */ jsx9(Tooltip3.Portal, { children: /* @__PURE__ */ jsx9(
1332
+ Tooltip3.Content,
1333
+ {
1334
+ className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50",
1335
+ sideOffset: 6,
1336
+ children: props.showTrackedChanges ? "Hide tracked changes" : "Show tracked changes"
1337
+ }
1338
+ ) })
1339
+ ] }),
1340
+ /* @__PURE__ */ jsx9("div", { className: "mx-1 h-4 w-px bg-border" }),
1341
+ /* @__PURE__ */ jsxs9(
1342
+ ToggleGroup.Root,
1343
+ {
1344
+ type: "single",
1345
+ value: workspaceMode,
1346
+ onValueChange: (v) => {
1347
+ if (v) props.onWorkspaceModeChange(v);
1348
+ },
1349
+ className: "flex items-center gap-0.5",
1350
+ children: [
1351
+ /* @__PURE__ */ jsxs9(Tooltip3.Root, { children: [
1352
+ /* @__PURE__ */ jsx9(Tooltip3.Trigger, { asChild: true, children: /* @__PURE__ */ jsx9(
1353
+ ToggleGroup.Item,
1354
+ {
1355
+ value: "canvas",
1356
+ "aria-label": "Canvas workspace",
1357
+ onMouseDown: preserveEditorSelectionMouseDown,
1358
+ className: `inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface data-[state=on]:bg-accent-soft data-[state=on]:text-accent outline-none ${focusRingClass6}`,
1359
+ children: /* @__PURE__ */ jsx9(Monitor, { className: "h-3.5 w-3.5" })
1360
+ }
1361
+ ) }),
1362
+ /* @__PURE__ */ jsx9(Tooltip3.Portal, { children: /* @__PURE__ */ jsx9(Tooltip3.Content, { className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50", sideOffset: 6, children: "Canvas \u2014 clean flowing text" }) })
1363
+ ] }),
1364
+ /* @__PURE__ */ jsxs9(Tooltip3.Root, { children: [
1365
+ /* @__PURE__ */ jsx9(Tooltip3.Trigger, { asChild: true, children: /* @__PURE__ */ jsx9(
1366
+ ToggleGroup.Item,
1367
+ {
1368
+ value: "page",
1369
+ "aria-label": "Page workspace",
1370
+ onMouseDown: preserveEditorSelectionMouseDown,
1371
+ className: `inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface data-[state=on]:bg-accent-soft data-[state=on]:text-accent outline-none ${focusRingClass6}`,
1372
+ children: /* @__PURE__ */ jsx9(FileText, { className: "h-3.5 w-3.5" })
1373
+ }
1374
+ ) }),
1375
+ /* @__PURE__ */ jsx9(Tooltip3.Portal, { children: /* @__PURE__ */ jsx9(Tooltip3.Content, { className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50", sideOffset: 6, children: "Page \u2014 layout-sensitive view" }) })
1376
+ ] })
1377
+ ]
1378
+ }
1379
+ ),
1380
+ isPageMode && props.onZoomChange ? /* @__PURE__ */ jsxs9(Fragment5, { children: [
1381
+ /* @__PURE__ */ jsx9("div", { className: "mx-1 h-4 w-px bg-border" }),
1382
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-0.5", children: [
1383
+ /* @__PURE__ */ jsxs9(Tooltip3.Root, { children: [
1384
+ /* @__PURE__ */ jsx9(Tooltip3.Trigger, { asChild: true, children: /* @__PURE__ */ jsx9(
1385
+ "button",
1386
+ {
1387
+ type: "button",
1388
+ "aria-label": "Zoom out",
1389
+ className: `inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none ${focusRingClass6}`,
1390
+ disabled: typeof zoomLevel === "number" && zoomLevel <= 50,
1391
+ onMouseDown: preserveEditorSelectionMouseDown,
1392
+ onClick: () => {
1393
+ const current = typeof zoomLevel === "number" ? zoomLevel : 100;
1394
+ props.onZoomChange(Math.max(50, current - 10));
1395
+ },
1396
+ children: /* @__PURE__ */ jsx9(Minus, { className: "h-3 w-3" })
1397
+ }
1398
+ ) }),
1399
+ /* @__PURE__ */ jsx9(Tooltip3.Portal, { children: /* @__PURE__ */ jsx9(Tooltip3.Content, { className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50", sideOffset: 6, children: "Zoom out" }) })
1400
+ ] }),
1401
+ /* @__PURE__ */ jsxs9(Popover.Root, { children: [
1402
+ /* @__PURE__ */ jsxs9(Tooltip3.Root, { children: [
1403
+ /* @__PURE__ */ jsx9(Tooltip3.Trigger, { asChild: true, children: /* @__PURE__ */ jsx9(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx9(
1404
+ "button",
1405
+ {
1406
+ type: "button",
1407
+ "aria-label": `Zoom: ${zoomLabel}`,
1408
+ onMouseDown: preserveEditorSelectionMouseDown,
1409
+ className: `inline-flex h-7 items-center justify-center rounded-md px-1.5 text-[11px] font-medium text-secondary transition-colors hover:bg-surface outline-none ${focusRingClass6}`,
1410
+ children: zoomLabel
1411
+ }
1412
+ ) }) }),
1413
+ /* @__PURE__ */ jsx9(Tooltip3.Portal, { children: /* @__PURE__ */ jsx9(Tooltip3.Content, { className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50", sideOffset: 6, children: "Zoom level" }) })
1414
+ ] }),
1415
+ /* @__PURE__ */ jsx9(Popover.Portal, { children: /* @__PURE__ */ jsx9(
1416
+ Popover.Content,
1417
+ {
1418
+ className: "w-[140px] rounded-lg bg-canvas shadow-lg ring-1 ring-border p-1 z-50",
1419
+ sideOffset: 8,
1420
+ align: "center",
1421
+ children: getSupportedZoomPresets().map((preset) => {
1422
+ const label = `${preset}%`;
1423
+ return /* @__PURE__ */ jsx9(Popover.Close, { asChild: true, children: /* @__PURE__ */ jsx9(
1424
+ "button",
1425
+ {
1426
+ type: "button",
1427
+ onMouseDown: preserveEditorSelectionMouseDown,
1428
+ className: `w-full rounded-md px-3 py-1.5 text-left text-xs transition-colors hover:bg-surface ${zoomLevel === preset ? "font-semibold text-accent" : "text-primary"}`,
1429
+ onClick: () => props.onZoomChange(preset),
1430
+ children: label
1431
+ }
1432
+ ) }, preset);
1433
+ })
1434
+ }
1435
+ ) })
1436
+ ] }),
1437
+ /* @__PURE__ */ jsxs9(Tooltip3.Root, { children: [
1438
+ /* @__PURE__ */ jsx9(Tooltip3.Trigger, { asChild: true, children: /* @__PURE__ */ jsx9(
1439
+ "button",
1440
+ {
1441
+ type: "button",
1442
+ "aria-label": "Zoom in",
1443
+ className: `inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none ${focusRingClass6}`,
1444
+ disabled: typeof zoomLevel === "number" && zoomLevel >= 200,
1445
+ onMouseDown: preserveEditorSelectionMouseDown,
1446
+ onClick: () => {
1447
+ const current = typeof zoomLevel === "number" ? zoomLevel : 100;
1448
+ props.onZoomChange(Math.min(200, current + 10));
1449
+ },
1450
+ children: /* @__PURE__ */ jsx9(Plus, { className: "h-3 w-3" })
1451
+ }
1452
+ ) }),
1453
+ /* @__PURE__ */ jsx9(Tooltip3.Portal, { children: /* @__PURE__ */ jsx9(Tooltip3.Content, { className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50", sideOffset: 6, children: "Zoom in" }) })
1454
+ ] })
1455
+ ] })
1456
+ ] }) : null,
1457
+ props.compatibility && props.warnings ? /* @__PURE__ */ jsxs9(Popover.Root, { children: [
1458
+ /* @__PURE__ */ jsxs9(Tooltip3.Root, { children: [
1459
+ /* @__PURE__ */ jsx9(Tooltip3.Trigger, { asChild: true, children: /* @__PURE__ */ jsx9(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs9(
1460
+ "button",
1461
+ {
1462
+ type: "button",
1463
+ onMouseDown: preserveEditorSelectionMouseDown,
1464
+ className: `relative inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors hover:bg-surface hover:text-primary outline-none ${focusRingClass6} ${(caps?.healthIssueCount ?? 0) > 0 ? "text-secondary" : "text-secondary"}`,
1465
+ children: [
1466
+ (caps?.healthIssueCount ?? 0) > 0 ? /* @__PURE__ */ jsx9(ShieldAlert2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx9(ShieldCheck2, { className: "h-4 w-4" }),
1467
+ (caps?.healthIssueCount ?? 0) > 0 ? /* @__PURE__ */ jsx9("span", { className: "absolute -top-0.5 -right-0.5 flex h-3 min-w-[12px] items-center justify-center rounded-full bg-tertiary text-[8px] font-medium text-white", children: caps?.healthIssueCount }) : null
1468
+ ]
1469
+ }
1470
+ ) }) }),
1471
+ /* @__PURE__ */ jsx9(Tooltip3.Portal, { children: /* @__PURE__ */ jsx9(Tooltip3.Content, { className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50", sideOffset: 6, children: (caps?.healthIssueCount ?? 0) > 0 ? `Document health \u2014 ${caps?.healthIssueCount} issue${(caps?.healthIssueCount ?? 0) !== 1 ? "s" : ""}` : "Document health \u2014 no issues" }) })
1472
+ ] }),
1473
+ /* @__PURE__ */ jsx9(Popover.Portal, { children: /* @__PURE__ */ jsx9(
1474
+ Popover.Content,
1475
+ {
1476
+ className: "w-[360px] max-h-[480px] overflow-y-auto rounded-lg bg-canvas shadow-lg ring-1 ring-border p-3 z-50",
1477
+ sideOffset: 8,
1478
+ align: "end",
1479
+ children: /* @__PURE__ */ jsx9(
1480
+ TwHealthPanel,
1481
+ {
1482
+ blockedReasons: props.blockedReasons,
1483
+ compatibility: props.compatibility,
1484
+ warnings: props.warnings
1485
+ }
1486
+ )
1487
+ }
1488
+ ) })
1489
+ ] }) : null,
1490
+ /* @__PURE__ */ jsx9("div", { className: "mx-1 h-4 w-px bg-border" }),
1491
+ /* @__PURE__ */ jsxs9(Tooltip3.Root, { children: [
1492
+ /* @__PURE__ */ jsx9(Tooltip3.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs9(
1493
+ "button",
1494
+ {
1495
+ type: "button",
1496
+ disabled: caps ? !caps.canExport : true,
1497
+ onMouseDown: preserveEditorSelectionMouseDown,
1498
+ className: [
1499
+ "inline-flex h-7 items-center gap-1.5 rounded-md px-2.5 text-xs font-semibold transition-colors outline-none",
1500
+ focusRingClass6,
1501
+ caps?.exportBlocked ? "cursor-not-allowed text-danger opacity-50" : "text-accent hover:bg-accent-soft"
1502
+ ].join(" "),
1503
+ onClick: props.onExport,
1504
+ children: [
1505
+ /* @__PURE__ */ jsx9(Download, { className: "h-3.5 w-3.5" }),
1506
+ "Export .docx"
1507
+ ]
1508
+ }
1509
+ ) }),
1510
+ /* @__PURE__ */ jsx9(Tooltip3.Portal, { children: /* @__PURE__ */ jsx9(Tooltip3.Content, { className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50", sideOffset: 6, children: caps?.exportBlocked ? "Export blocked by unsupported content" : "Export document" }) })
1511
+ ] })
1512
+ ] })
1513
+ ] });
1514
+ }
1515
+ function ToolbarParagraphStyleSelect(props) {
1516
+ const resolvedValue = props.value && props.styles.some((style) => style.styleId === props.value) ? props.value : "";
1517
+ return /* @__PURE__ */ jsxs9(
1518
+ Select.Root,
1519
+ {
1520
+ disabled: props.disabled,
1521
+ onValueChange: (value) => props.onValueChange?.(value),
1522
+ value: resolvedValue,
1523
+ children: [
1524
+ /* @__PURE__ */ jsxs9(
1525
+ Select.Trigger,
1526
+ {
1527
+ "aria-label": "Paragraph style",
1528
+ "aria-disabled": props.disabled || void 0,
1529
+ "data-disabled": props.disabled ? "" : void 0,
1530
+ onMouseDown: preserveEditorSelectionMouseDown,
1531
+ className: `inline-flex h-7 min-w-[8.5rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2.5 text-xs font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass6}`,
1532
+ children: [
1533
+ /* @__PURE__ */ jsx9(Select.Value, { placeholder: "Style" }),
1534
+ /* @__PURE__ */ jsx9(Select.Icon, { children: /* @__PURE__ */ jsx9(ChevronDown, { className: "h-3.5 w-3.5 text-tertiary" }) })
1535
+ ]
1536
+ }
1537
+ ),
1538
+ /* @__PURE__ */ jsx9(Select.Portal, { children: /* @__PURE__ */ jsx9(
1539
+ Select.Content,
1540
+ {
1541
+ align: "start",
1542
+ className: "z-50 overflow-hidden rounded-lg bg-canvas shadow-lg ring-1 ring-border",
1543
+ position: "popper",
1544
+ sideOffset: 8,
1545
+ children: /* @__PURE__ */ jsx9(Select.Viewport, { className: "p-1", children: props.styles.map((style) => /* @__PURE__ */ jsx9(
1546
+ Select.Item,
1547
+ {
1548
+ className: `flex cursor-pointer items-center rounded-md px-2.5 py-1.5 text-xs text-primary outline-none data-[highlighted]:bg-surface data-[state=checked]:bg-accent-soft data-[state=checked]:text-accent ${focusRingClass6}`,
1549
+ value: style.styleId,
1550
+ children: /* @__PURE__ */ jsx9(Select.ItemText, { children: style.displayName })
1551
+ },
1552
+ style.styleId
1553
+ )) })
1554
+ }
1555
+ ) })
1556
+ ]
1557
+ }
1558
+ );
1559
+ }
1560
+ function ToolbarFontFamilySelect(props) {
1561
+ const resolvedValue = props.value && FONT_FAMILIES.includes(props.value) ? props.value : "";
1562
+ return /* @__PURE__ */ jsxs9(
1563
+ Select.Root,
1564
+ {
1565
+ disabled: props.disabled,
1566
+ onValueChange: (value) => props.onValueChange?.(value),
1567
+ value: resolvedValue,
1568
+ children: [
1569
+ /* @__PURE__ */ jsxs9(
1570
+ Select.Trigger,
1571
+ {
1572
+ "aria-label": "Font family",
1573
+ "aria-disabled": props.disabled || void 0,
1574
+ "data-disabled": props.disabled ? "" : void 0,
1575
+ onMouseDown: preserveEditorSelectionMouseDown,
1576
+ className: `inline-flex h-7 min-w-[7rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2 text-xs font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass6}`,
1577
+ children: [
1578
+ /* @__PURE__ */ jsx9(Select.Value, { placeholder: "Font" }),
1579
+ /* @__PURE__ */ jsx9(Select.Icon, { children: /* @__PURE__ */ jsx9(ChevronDown, { className: "h-3.5 w-3.5 text-tertiary" }) })
1580
+ ]
1581
+ }
1582
+ ),
1583
+ /* @__PURE__ */ jsx9(Select.Portal, { children: /* @__PURE__ */ jsx9(
1584
+ Select.Content,
1585
+ {
1586
+ align: "start",
1587
+ className: "z-50 overflow-hidden rounded-lg bg-canvas shadow-lg ring-1 ring-border",
1588
+ position: "popper",
1589
+ sideOffset: 8,
1590
+ children: /* @__PURE__ */ jsx9(Select.Viewport, { className: "p-1", children: FONT_FAMILIES.map((font) => /* @__PURE__ */ jsx9(
1591
+ Select.Item,
1592
+ {
1593
+ className: `flex cursor-pointer items-center rounded-md px-2.5 py-1.5 text-xs text-primary outline-none data-[highlighted]:bg-surface data-[state=checked]:bg-accent-soft data-[state=checked]:text-accent ${focusRingClass6}`,
1594
+ value: font,
1595
+ children: /* @__PURE__ */ jsx9(Select.ItemText, { children: font })
1596
+ },
1597
+ font
1598
+ )) })
1599
+ }
1600
+ ) })
1601
+ ]
1602
+ }
1603
+ );
1604
+ }
1605
+ function ToolbarFontSizeSelect(props) {
1606
+ const resolvedValue = typeof props.value === "number" && FONT_SIZES.includes(props.value) ? String(props.value) : "";
1607
+ return /* @__PURE__ */ jsxs9(
1608
+ Select.Root,
1609
+ {
1610
+ disabled: props.disabled,
1611
+ onValueChange: (value) => props.onValueChange?.(Number(value)),
1612
+ value: resolvedValue,
1613
+ children: [
1614
+ /* @__PURE__ */ jsxs9(
1615
+ Select.Trigger,
1616
+ {
1617
+ "aria-label": "Font size",
1618
+ "aria-disabled": props.disabled || void 0,
1619
+ "data-disabled": props.disabled ? "" : void 0,
1620
+ onMouseDown: preserveEditorSelectionMouseDown,
1621
+ className: `inline-flex h-7 min-w-[4rem] items-center justify-between gap-2 rounded-md border border-border bg-canvas px-2 text-xs font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass6}`,
1622
+ children: [
1623
+ /* @__PURE__ */ jsx9(Select.Value, { placeholder: "Size" }),
1624
+ /* @__PURE__ */ jsx9(Select.Icon, { children: /* @__PURE__ */ jsx9(ChevronDown, { className: "h-3.5 w-3.5 text-tertiary" }) })
1625
+ ]
1626
+ }
1627
+ ),
1628
+ /* @__PURE__ */ jsx9(Select.Portal, { children: /* @__PURE__ */ jsx9(
1629
+ Select.Content,
1630
+ {
1631
+ align: "start",
1632
+ className: "z-50 overflow-hidden rounded-lg bg-canvas shadow-lg ring-1 ring-border",
1633
+ position: "popper",
1634
+ sideOffset: 8,
1635
+ children: /* @__PURE__ */ jsx9(Select.Viewport, { className: "p-1", children: FONT_SIZES.map((size) => /* @__PURE__ */ jsx9(
1636
+ Select.Item,
1637
+ {
1638
+ className: `flex cursor-pointer items-center rounded-md px-2.5 py-1.5 text-xs text-primary outline-none data-[highlighted]:bg-surface data-[state=checked]:bg-accent-soft data-[state=checked]:text-accent ${focusRingClass6}`,
1639
+ value: String(size),
1640
+ children: /* @__PURE__ */ jsx9(Select.ItemText, { children: size })
1641
+ },
1642
+ size
1643
+ )) })
1644
+ }
1645
+ ) })
1646
+ ]
1647
+ }
1648
+ );
1649
+ }
1650
+ function ToolbarFormattingOverflow(props) {
1651
+ const [open, setOpen] = React3.useState(false);
1652
+ return /* @__PURE__ */ jsxs9("div", { className: "relative", children: [
1653
+ /* @__PURE__ */ jsxs9(Tooltip3.Root, { children: [
1654
+ /* @__PURE__ */ jsx9(Tooltip3.Trigger, { asChild: true, children: /* @__PURE__ */ jsx9(
1655
+ "button",
1656
+ {
1657
+ type: "button",
1658
+ "aria-label": "More text formatting",
1659
+ "aria-expanded": open,
1660
+ disabled: props.disabled,
1661
+ onMouseDown: preserveEditorSelectionMouseDown,
1662
+ onClick: () => setOpen((value) => !value),
1663
+ className: `inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass6}`,
1664
+ children: /* @__PURE__ */ jsx9(MoreHorizontal, { className: "h-3.5 w-3.5" })
1665
+ }
1666
+ ) }),
1667
+ /* @__PURE__ */ jsx9(Tooltip3.Portal, { children: /* @__PURE__ */ jsx9(Tooltip3.Content, { className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50", sideOffset: 6, children: "More text formatting" }) })
1668
+ ] }),
1669
+ open ? /* @__PURE__ */ jsxs9("div", { className: "absolute left-0 top-9 z-50 w-[220px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border", children: [
1670
+ /* @__PURE__ */ jsx9("div", { className: "mb-1 px-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary", children: "Text styling" }),
1671
+ /* @__PURE__ */ jsxs9("div", { className: "grid grid-cols-3 gap-1", children: [
1672
+ /* @__PURE__ */ jsx9(
1673
+ ToolbarPopoverActionButton,
1674
+ {
1675
+ active: props.formattingState?.strikethrough ?? false,
1676
+ ariaLabel: "Strikethrough",
1677
+ disabled: props.disabled,
1678
+ icon: /* @__PURE__ */ jsx9(Strikethrough, { className: "h-3.5 w-3.5" }),
1679
+ onClick: () => {
1680
+ props.onToggleStrikethrough?.();
1681
+ setOpen(false);
1682
+ }
1683
+ }
1684
+ ),
1685
+ /* @__PURE__ */ jsx9(
1686
+ ToolbarPopoverActionButton,
1687
+ {
1688
+ active: props.formattingState?.superscript ?? false,
1689
+ ariaLabel: "Superscript",
1690
+ disabled: props.disabled,
1691
+ icon: /* @__PURE__ */ jsx9(Superscript, { className: "h-3.5 w-3.5" }),
1692
+ onClick: () => {
1693
+ props.onToggleSuperscript?.();
1694
+ setOpen(false);
1695
+ }
1696
+ }
1697
+ ),
1698
+ /* @__PURE__ */ jsx9(
1699
+ ToolbarPopoverActionButton,
1700
+ {
1701
+ active: props.formattingState?.subscript ?? false,
1702
+ ariaLabel: "Subscript",
1703
+ disabled: props.disabled,
1704
+ icon: /* @__PURE__ */ jsx9(Subscript, { className: "h-3.5 w-3.5" }),
1705
+ onClick: () => {
1706
+ props.onToggleSubscript?.();
1707
+ setOpen(false);
1708
+ }
1709
+ }
1710
+ )
1711
+ ] })
1712
+ ] }) : null
1713
+ ] });
1714
+ }
1715
+ function ToolbarColorPopover(props) {
1716
+ const [open, setOpen] = React3.useState(false);
1717
+ return /* @__PURE__ */ jsxs9("div", { className: "relative", children: [
1718
+ /* @__PURE__ */ jsxs9(Tooltip3.Root, { children: [
1719
+ /* @__PURE__ */ jsx9(Tooltip3.Trigger, { asChild: true, children: /* @__PURE__ */ jsx9(
1720
+ "button",
1721
+ {
1722
+ type: "button",
1723
+ "aria-label": props.ariaLabel,
1724
+ "aria-expanded": open,
1725
+ disabled: props.disabled,
1726
+ onMouseDown: preserveEditorSelectionMouseDown,
1727
+ onClick: () => setOpen((value) => !value),
1728
+ className: `inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass6}`,
1729
+ children: props.icon
1730
+ }
1731
+ ) }),
1732
+ /* @__PURE__ */ jsx9(Tooltip3.Portal, { children: /* @__PURE__ */ jsx9(Tooltip3.Content, { className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50", sideOffset: 6, children: props.title }) })
1733
+ ] }),
1734
+ open ? /* @__PURE__ */ jsxs9("div", { className: "absolute left-0 top-9 z-50 w-[180px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border", children: [
1735
+ /* @__PURE__ */ jsx9("div", { className: "mb-1 px-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary", children: props.title }),
1736
+ /* @__PURE__ */ jsx9("div", { className: "grid grid-cols-3 gap-1", children: props.colors.map((color) => /* @__PURE__ */ jsx9(
1737
+ "button",
1738
+ {
1739
+ type: "button",
1740
+ "aria-label": `${props.title} ${color.label}`,
1741
+ disabled: props.disabled,
1742
+ onMouseDown: preserveEditorSelectionMouseDown,
1743
+ onClick: () => {
1744
+ props.onSelect(color.value);
1745
+ setOpen(false);
1746
+ },
1747
+ className: `inline-flex h-8 items-center justify-center rounded-md border border-border text-[10px] font-medium text-primary transition-transform hover:scale-[1.04] disabled:cursor-not-allowed disabled:opacity-40 ${color.value ? "" : "bg-surface"} ${focusRingClass6}`,
1748
+ style: color.value ? { backgroundColor: color.value } : void 0,
1749
+ children: color.value ? /* @__PURE__ */ jsx9("span", { className: "sr-only", children: color.label }) : "None"
1750
+ },
1751
+ `${props.ariaLabel}-${color.label}`
1752
+ )) })
1753
+ ] }) : null
1754
+ ] });
1755
+ }
1756
+ function ToolbarAlignmentPopover(props) {
1757
+ const [open, setOpen] = React3.useState(false);
1758
+ const alignments = [
1759
+ { value: "left", label: "Align left", icon: /* @__PURE__ */ jsx9(AlignLeft, { className: "h-3.5 w-3.5" }) },
1760
+ { value: "center", label: "Align center", icon: /* @__PURE__ */ jsx9(AlignCenter, { className: "h-3.5 w-3.5" }) },
1761
+ { value: "right", label: "Align right", icon: /* @__PURE__ */ jsx9(AlignRight, { className: "h-3.5 w-3.5" }) },
1762
+ { value: "justify", label: "Align justify", icon: /* @__PURE__ */ jsx9(AlignJustify, { className: "h-3.5 w-3.5" }) }
1763
+ ];
1764
+ return /* @__PURE__ */ jsxs9("div", { className: "relative", children: [
1765
+ /* @__PURE__ */ jsxs9(Tooltip3.Root, { children: [
1766
+ /* @__PURE__ */ jsx9(Tooltip3.Trigger, { asChild: true, children: /* @__PURE__ */ jsx9(
1767
+ "button",
1768
+ {
1769
+ type: "button",
1770
+ "aria-label": "Paragraph alignment",
1771
+ "aria-expanded": open,
1772
+ disabled: props.disabled,
1773
+ onMouseDown: preserveEditorSelectionMouseDown,
1774
+ onClick: () => setOpen((value) => !value),
1775
+ className: `inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass6}`,
1776
+ children: (alignments.find((entry) => entry.value === props.activeAlignment) ?? alignments[0])?.icon
1777
+ }
1778
+ ) }),
1779
+ /* @__PURE__ */ jsx9(Tooltip3.Portal, { children: /* @__PURE__ */ jsx9(Tooltip3.Content, { className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50", sideOffset: 6, children: "Paragraph alignment" }) })
1780
+ ] }),
1781
+ open ? /* @__PURE__ */ jsxs9("div", { className: "absolute left-0 top-9 z-50 w-[220px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border", children: [
1782
+ /* @__PURE__ */ jsx9("div", { className: "mb-1 px-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary", children: "Paragraph alignment" }),
1783
+ /* @__PURE__ */ jsx9("div", { className: "grid grid-cols-2 gap-1", children: alignments.map((entry) => /* @__PURE__ */ jsx9(
1784
+ ToolbarPopoverActionButton,
1785
+ {
1786
+ active: props.activeAlignment === entry.value,
1787
+ ariaLabel: entry.label,
1788
+ disabled: props.disabled,
1789
+ icon: entry.icon,
1790
+ onClick: () => {
1791
+ props.onSelect(entry.value);
1792
+ setOpen(false);
1793
+ }
1794
+ },
1795
+ entry.value
1796
+ )) })
1797
+ ] }) : null
1798
+ ] });
1799
+ }
1800
+ function ToolbarInsertMenu(props) {
1801
+ const [open, setOpen] = React3.useState(false);
1802
+ async function handleImageChange(event) {
1803
+ const file = event.target.files?.[0];
1804
+ if (!file || props.disabled || !props.onInsertImage) {
1805
+ event.target.value = "";
1806
+ return;
1807
+ }
1808
+ const data = new Uint8Array(await file.arrayBuffer());
1809
+ props.onInsertImage({
1810
+ data,
1811
+ mimeType: file.type || "image/png",
1812
+ altText: file.name
1813
+ });
1814
+ setOpen(false);
1815
+ event.target.value = "";
1816
+ }
1817
+ return /* @__PURE__ */ jsxs9("div", { className: "relative", children: [
1818
+ /* @__PURE__ */ jsxs9(Tooltip3.Root, { children: [
1819
+ /* @__PURE__ */ jsx9(Tooltip3.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs9(
1820
+ "button",
1821
+ {
1822
+ type: "button",
1823
+ "aria-label": "Insert",
1824
+ "aria-expanded": open,
1825
+ disabled: props.disabled,
1826
+ onMouseDown: preserveEditorSelectionMouseDown,
1827
+ onClick: () => setOpen((value) => !value),
1828
+ className: `inline-flex h-7 items-center gap-1 rounded-md border border-border bg-canvas px-2 text-xs font-medium text-primary transition-colors hover:bg-surface outline-none disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass6}`,
1829
+ children: [
1830
+ "Insert",
1831
+ /* @__PURE__ */ jsx9(ChevronDown, { className: "h-3.5 w-3.5 text-tertiary" })
1832
+ ]
1833
+ }
1834
+ ) }),
1835
+ /* @__PURE__ */ jsx9(Tooltip3.Portal, { children: /* @__PURE__ */ jsx9(Tooltip3.Content, { className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50", sideOffset: 6, children: "Insert" }) })
1836
+ ] }),
1837
+ open ? /* @__PURE__ */ jsx9("div", { className: "absolute left-0 top-9 z-50 w-[220px] rounded-lg bg-canvas p-2 shadow-lg ring-1 ring-border", children: /* @__PURE__ */ jsxs9("div", { className: "space-y-1", children: [
1838
+ /* @__PURE__ */ jsx9(
1839
+ ToolbarMenuButton,
1840
+ {
1841
+ ariaLabel: "Insert page break",
1842
+ disabled: props.disabled || !props.onInsertPageBreak,
1843
+ icon: /* @__PURE__ */ jsx9(Minus, { className: "h-3.5 w-3.5" }),
1844
+ label: "Page break",
1845
+ onClick: () => {
1846
+ props.onInsertPageBreak?.();
1847
+ setOpen(false);
1848
+ }
1849
+ }
1850
+ ),
1851
+ /* @__PURE__ */ jsx9(
1852
+ ToolbarMenuButton,
1853
+ {
1854
+ ariaLabel: "Insert table",
1855
+ disabled: props.disabled || !props.onInsertTable,
1856
+ icon: /* @__PURE__ */ jsx9(Rows3, { className: "h-3.5 w-3.5" }),
1857
+ label: "Table",
1858
+ onClick: () => {
1859
+ props.onInsertTable?.();
1860
+ setOpen(false);
1861
+ }
1862
+ }
1863
+ ),
1864
+ /* @__PURE__ */ jsxs9(
1865
+ "label",
1866
+ {
1867
+ className: `flex h-8 cursor-pointer items-center gap-2 rounded-md px-2 text-xs font-medium text-primary transition-colors hover:bg-surface ${props.disabled || !props.onInsertImage ? "pointer-events-none opacity-40" : ""}`,
1868
+ children: [
1869
+ /* @__PURE__ */ jsx9(ImagePlus, { className: "h-3.5 w-3.5 text-secondary" }),
1870
+ /* @__PURE__ */ jsx9("span", { children: "Image" }),
1871
+ /* @__PURE__ */ jsx9(
1872
+ "input",
1873
+ {
1874
+ accept: "image/png,image/jpeg,image/gif",
1875
+ "aria-label": "Insert image",
1876
+ className: "sr-only",
1877
+ disabled: props.disabled || !props.onInsertImage,
1878
+ type: "file",
1879
+ onChange: (event) => {
1880
+ void handleImageChange(event);
1881
+ }
1882
+ }
1883
+ )
1884
+ ]
1885
+ }
1886
+ ),
1887
+ /* @__PURE__ */ jsx9(
1888
+ ToolbarMenuButton,
1889
+ {
1890
+ ariaLabel: "Insert next-page section break",
1891
+ disabled: props.disabled || !props.onInsertSectionBreak,
1892
+ icon: /* @__PURE__ */ jsx9(FileText, { className: "h-3.5 w-3.5" }),
1893
+ label: "Next-page section break",
1894
+ onClick: () => {
1895
+ props.onInsertSectionBreak?.("nextPage");
1896
+ setOpen(false);
1897
+ }
1898
+ }
1899
+ )
1900
+ ] }) }) : null
1901
+ ] });
1902
+ }
1903
+ function ToolbarPopoverActionButton(props) {
1904
+ return /* @__PURE__ */ jsx9(
1905
+ "button",
1906
+ {
1907
+ type: "button",
1908
+ "aria-label": props.ariaLabel,
1909
+ "aria-pressed": props.active,
1910
+ disabled: props.disabled,
1911
+ onMouseDown: preserveEditorSelectionMouseDown,
1912
+ onClick: props.onClick,
1913
+ className: `inline-flex h-8 items-center justify-center rounded-md border border-border transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${props.active ? "bg-accent-soft text-accent" : "bg-canvas text-secondary hover:bg-surface"} ${focusRingClass6}`,
1914
+ children: props.icon
1915
+ }
1916
+ );
1917
+ }
1918
+ function ToolbarMenuButton(props) {
1919
+ return /* @__PURE__ */ jsxs9(
1920
+ "button",
1921
+ {
1922
+ type: "button",
1923
+ "aria-label": props.ariaLabel,
1924
+ disabled: props.disabled,
1925
+ onMouseDown: preserveEditorSelectionMouseDown,
1926
+ onClick: props.onClick,
1927
+ className: `flex h-8 w-full items-center gap-2 rounded-md px-2 text-left text-xs font-medium text-primary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass6}`,
1928
+ children: [
1929
+ /* @__PURE__ */ jsx9("span", { className: "text-secondary", children: props.icon }),
1930
+ /* @__PURE__ */ jsx9("span", { children: props.label })
1931
+ ]
1932
+ }
1933
+ );
1934
+ }
1935
+ function storyLabel(target) {
1936
+ switch (target.kind) {
1937
+ case "header":
1938
+ return `Header (${target.variant})`;
1939
+ case "footer":
1940
+ return `Footer (${target.variant})`;
1941
+ case "footnote":
1942
+ return "Footnote";
1943
+ case "endnote":
1944
+ return "Endnote";
1945
+ default:
1946
+ return "Document";
1947
+ }
1948
+ }
1949
+
1950
+ // src/ui-tailwind/tw-review-workspace.tsx
1951
+ import {
1952
+ useCallback as useCallback2,
1953
+ useEffect as useEffect3,
1954
+ useMemo as useMemo2,
1955
+ useRef as useRef3,
1956
+ useState as useState3
1957
+ } from "react";
1958
+ import * as Tooltip5 from "@radix-ui/react-tooltip";
1959
+ import { ChevronLeft, ChevronRight, List as List2 } from "lucide-react";
1960
+
1961
+ // src/ui-tailwind/page-chrome-model.ts
1962
+ function computeLineMarkersIfEnabled(input) {
1963
+ if (!input.pageLayout?.lineNumbering) {
1964
+ return [];
1965
+ }
1966
+ return input.buildLineNumberMarkers(input.surfaceBlocks, input.pages);
1967
+ }
1968
+
1969
+ // src/ui-tailwind/chrome/tw-image-context-toolbar.tsx
1970
+ import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1971
+ var IMAGE_SIZE_PRESETS = [
1972
+ { label: "Small image", widthEmu: 1828800, heightEmu: 914400 },
1973
+ { label: "Medium image", widthEmu: 2743200, heightEmu: 1371600 },
1974
+ { label: "Large image", widthEmu: 3657600, heightEmu: 1828800 }
1975
+ ];
1976
+ var NUDGE_EMU = 228600;
1977
+ function TwImageContextToolbar(props) {
1978
+ const { activeImage } = props;
1979
+ return /* @__PURE__ */ jsxs10(
1980
+ "div",
1981
+ {
1982
+ "data-testid": "image-context-toolbar",
1983
+ className: "flex flex-wrap items-center gap-2 rounded-xl border border-border bg-canvas px-3 py-2 shadow-sm",
1984
+ children: [
1985
+ /* @__PURE__ */ jsx10("span", { className: "text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary", children: "Image" }),
1986
+ /* @__PURE__ */ jsx10("span", { className: "rounded-full bg-surface px-2 py-1 text-[10px] font-medium uppercase tracking-[0.1em] text-secondary", children: activeImage.display }),
1987
+ IMAGE_SIZE_PRESETS.map((preset) => /* @__PURE__ */ jsx10(
1988
+ ToolbarButton,
1989
+ {
1990
+ ariaLabel: preset.label,
1991
+ disabled: props.disabled || !props.onSetImageLayout,
1992
+ onClick: () => props.onSetImageLayout?.(activeImage.mediaId, {
1993
+ widthEmu: preset.widthEmu,
1994
+ heightEmu: preset.heightEmu
1995
+ }),
1996
+ children: preset.label.replace(" image", "")
1997
+ },
1998
+ preset.label
1999
+ )),
2000
+ activeImage.display === "floating" ? /* @__PURE__ */ jsxs10(Fragment6, { children: [
2001
+ /* @__PURE__ */ jsx10(
2002
+ ToolbarButton,
2003
+ {
2004
+ ariaLabel: "Nudge image left",
2005
+ disabled: props.disabled || !props.onSetImageFrame,
2006
+ onClick: () => props.onSetImageFrame?.(activeImage.mediaId, {
2007
+ horizontalOffsetEmu: (activeImage.horizontalOffsetEmu ?? 0) - NUDGE_EMU
2008
+ }),
2009
+ children: "Left"
2010
+ }
2011
+ ),
2012
+ /* @__PURE__ */ jsx10(
2013
+ ToolbarButton,
2014
+ {
2015
+ ariaLabel: "Nudge image right",
2016
+ disabled: props.disabled || !props.onSetImageFrame,
2017
+ onClick: () => props.onSetImageFrame?.(activeImage.mediaId, {
2018
+ horizontalOffsetEmu: (activeImage.horizontalOffsetEmu ?? 0) + NUDGE_EMU
2019
+ }),
2020
+ children: "Right"
2021
+ }
2022
+ ),
2023
+ /* @__PURE__ */ jsx10(
2024
+ ToolbarButton,
2025
+ {
2026
+ ariaLabel: "Nudge image up",
2027
+ disabled: props.disabled || !props.onSetImageFrame,
2028
+ onClick: () => props.onSetImageFrame?.(activeImage.mediaId, {
2029
+ verticalOffsetEmu: (activeImage.verticalOffsetEmu ?? 0) - NUDGE_EMU
2030
+ }),
2031
+ children: "Up"
2032
+ }
2033
+ ),
2034
+ /* @__PURE__ */ jsx10(
2035
+ ToolbarButton,
2036
+ {
2037
+ ariaLabel: "Nudge image down",
2038
+ disabled: props.disabled || !props.onSetImageFrame,
2039
+ onClick: () => props.onSetImageFrame?.(activeImage.mediaId, {
2040
+ verticalOffsetEmu: (activeImage.verticalOffsetEmu ?? 0) + NUDGE_EMU
2041
+ }),
2042
+ children: "Down"
2043
+ }
2044
+ )
2045
+ ] }) : null
2046
+ ]
2047
+ }
2048
+ );
2049
+ }
2050
+ function ToolbarButton(props) {
2051
+ return /* @__PURE__ */ jsx10(
2052
+ "button",
2053
+ {
2054
+ type: "button",
2055
+ "aria-label": props.ariaLabel,
2056
+ disabled: props.disabled,
2057
+ onMouseDown: preserveEditorSelectionMouseDown,
2058
+ onClick: props.onClick,
2059
+ className: "inline-flex h-8 items-center rounded-md px-2 text-xs font-medium text-primary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40",
2060
+ children: props.children
2061
+ }
2062
+ );
2063
+ }
2064
+
2065
+ // src/ui-tailwind/chrome/tw-layout-panel.tsx
2066
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
2067
+ function TwLayoutPanel(props) {
2068
+ const nextOrientation = props.pageLayout.orientation === "portrait" ? "landscape" : "portrait";
2069
+ const titlePageEnabled = props.pageLayout.differentFirstPage;
2070
+ return /* @__PURE__ */ jsxs11("div", { className: "mt-3 flex flex-wrap items-center gap-2 rounded-xl border border-border bg-canvas px-3 py-2 shadow-sm", children: [
2071
+ /* @__PURE__ */ jsx11("span", { className: "text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary", children: "Section" }),
2072
+ /* @__PURE__ */ jsx11(
2073
+ ToolbarButton2,
2074
+ {
2075
+ ariaLabel: "Insert next-page section break",
2076
+ disabled: props.readOnly || !props.onInsertSectionBreak,
2077
+ onClick: () => props.onInsertSectionBreak?.("nextPage"),
2078
+ children: "Next-page break"
2079
+ }
2080
+ ),
2081
+ /* @__PURE__ */ jsx11(
2082
+ ToolbarButton2,
2083
+ {
2084
+ ariaLabel: `Switch section to ${nextOrientation}`,
2085
+ disabled: props.readOnly || !props.onUpdateSectionLayout,
2086
+ onClick: () => props.onUpdateSectionLayout?.(props.pageLayout.sectionIndex, {
2087
+ pageSize: {
2088
+ orientation: nextOrientation,
2089
+ width: props.pageLayout.pageHeight,
2090
+ height: props.pageLayout.pageWidth
2091
+ }
2092
+ }),
2093
+ children: nextOrientation === "landscape" ? "Landscape" : "Portrait"
2094
+ }
2095
+ ),
2096
+ /* @__PURE__ */ jsx11(
2097
+ ToolbarButton2,
2098
+ {
2099
+ ariaLabel: "Delete current section break",
2100
+ disabled: props.readOnly || props.pageLayout.sectionIndex === 0 || !props.onDeleteSectionBreak,
2101
+ onClick: () => props.onDeleteSectionBreak?.(props.pageLayout.sectionIndex),
2102
+ children: "Delete break"
2103
+ }
2104
+ ),
2105
+ /* @__PURE__ */ jsx11(
2106
+ ToolbarButton2,
2107
+ {
2108
+ ariaLabel: "Restart page numbering at 1",
2109
+ disabled: props.readOnly || !props.onSetSectionPageNumbering,
2110
+ onClick: () => props.onSetSectionPageNumbering?.(props.pageLayout.sectionIndex, {
2111
+ ...props.pageLayout.pageNumbering ?? {},
2112
+ start: 1
2113
+ }),
2114
+ children: "Restart numbering"
2115
+ }
2116
+ ),
2117
+ /* @__PURE__ */ jsx11(
2118
+ ToolbarButton2,
2119
+ {
2120
+ ariaLabel: "Use roman page numbering",
2121
+ disabled: props.readOnly || !props.onSetSectionPageNumbering,
2122
+ onClick: () => props.onSetSectionPageNumbering?.(props.pageLayout.sectionIndex, {
2123
+ ...props.pageLayout.pageNumbering ?? {},
2124
+ format: "roman"
2125
+ }),
2126
+ children: "Roman numerals"
2127
+ }
2128
+ ),
2129
+ /* @__PURE__ */ jsx11(
2130
+ ToolbarButton2,
2131
+ {
2132
+ ariaLabel: "Toggle different first page",
2133
+ disabled: props.readOnly || !props.onUpdateSectionLayout,
2134
+ onClick: () => props.onUpdateSectionLayout?.(props.pageLayout.sectionIndex, {
2135
+ titlePage: !titlePageEnabled
2136
+ }),
2137
+ children: titlePageEnabled ? "Same first page" : "Different first page"
2138
+ }
2139
+ )
2140
+ ] });
2141
+ }
2142
+ function ToolbarButton2(props) {
2143
+ return /* @__PURE__ */ jsx11(
2144
+ "button",
2145
+ {
2146
+ type: "button",
2147
+ "aria-label": props.ariaLabel,
2148
+ disabled: props.disabled,
2149
+ onMouseDown: preserveEditorSelectionMouseDown,
2150
+ onClick: props.onClick,
2151
+ className: "inline-flex h-8 items-center rounded-md px-2 text-xs font-medium text-primary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40",
2152
+ children: props.children
2153
+ }
2154
+ );
2155
+ }
2156
+
2157
+ // src/ui-tailwind/chrome/tw-object-context-toolbar.tsx
2158
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
2159
+ function TwObjectContextToolbar(props) {
2160
+ const label = props.activeObject.kind === "textbox" ? "Text box" : "Shape";
2161
+ return /* @__PURE__ */ jsxs12(
2162
+ "div",
2163
+ {
2164
+ "data-testid": "object-context-toolbar",
2165
+ className: "flex flex-wrap items-center gap-2 rounded-xl border border-border bg-canvas px-3 py-2 shadow-sm",
2166
+ children: [
2167
+ /* @__PURE__ */ jsx12("span", { className: "text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary", children: "Object" }),
2168
+ /* @__PURE__ */ jsx12("span", { className: "rounded-full bg-surface px-2 py-1 text-[10px] font-medium uppercase tracking-[0.1em] text-secondary", children: label }),
2169
+ /* @__PURE__ */ jsx12("span", { className: "rounded-full bg-surface px-2 py-1 text-[10px] font-medium uppercase tracking-[0.1em] text-secondary", children: props.activeObject.display }),
2170
+ /* @__PURE__ */ jsx12("span", { className: "text-xs text-secondary", children: "Object selection is active." })
2171
+ ]
2172
+ }
2173
+ );
2174
+ }
2175
+
2176
+ // src/ui-tailwind/chrome/tw-page-ruler.tsx
2177
+ import { useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
2178
+ import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
2179
+ var MIN_HANDLE_TWIPS = 0;
2180
+ var HANDLE_OVERLAP_THRESHOLD_PERCENT = 1.4;
2181
+ var HANDLE_OFFSET_PERCENT = 0.9;
2182
+ var MARKER_HALF_PX = 8;
2183
+ function TwPageRuler(props) {
2184
+ const trackRef = useRef2(null);
2185
+ const dragCleanupRef = useRef2(null);
2186
+ const [, setDragState] = useState2(null);
2187
+ const [previewLayout, setPreviewLayout] = useState2(null);
2188
+ const effectiveLayout = previewLayout ?? props.paragraphLayout;
2189
+ const activeRegion = props.viewState.activePageRegion?.region ?? "body";
2190
+ const isBodyParagraphContext = activeRegion === "body" && Boolean(props.paragraphLayout);
2191
+ const availableHeader = props.pageLayout.headerVariants[0];
2192
+ const availableFooter = props.pageLayout.footerVariants[0];
2193
+ const usablePageWidth = Math.max(
2194
+ 1440,
2195
+ props.pageLayout.pageWidth - props.pageLayout.marginLeft - props.pageLayout.marginRight
2196
+ );
2197
+ useEffect2(() => {
2198
+ return () => {
2199
+ dragCleanupRef.current?.();
2200
+ };
2201
+ }, []);
2202
+ function beginDrag(kind, clientX) {
2203
+ if (!isBodyParagraphContext || !props.paragraphLayout || props.readOnly) {
2204
+ return;
2205
+ }
2206
+ dragCleanupRef.current?.();
2207
+ const activeDrag = {
2208
+ kind,
2209
+ startClientX: clientX,
2210
+ startLeftIndent: props.paragraphLayout.leftIndent,
2211
+ startFirstLineOffset: props.paragraphLayout.firstLineOffset
2212
+ };
2213
+ setDragState(activeDrag);
2214
+ const handleMouseMove = (event) => {
2215
+ const track = trackRef.current;
2216
+ if (!track) {
2217
+ return;
2218
+ }
2219
+ const rect = track.getBoundingClientRect();
2220
+ const deltaPx = event.clientX - activeDrag.startClientX;
2221
+ const deltaTwips = pxToTwips(deltaPx, rect.width, usablePageWidth);
2222
+ if (activeDrag.kind === "left-indent") {
2223
+ setPreviewLayout({
2224
+ ...props.paragraphLayout,
2225
+ leftIndent: clampTwips(activeDrag.startLeftIndent + deltaTwips),
2226
+ firstLineOffset: activeDrag.startFirstLineOffset
2227
+ });
2228
+ return;
2229
+ }
2230
+ setPreviewLayout({
2231
+ ...props.paragraphLayout,
2232
+ leftIndent: activeDrag.startLeftIndent,
2233
+ firstLineOffset: activeDrag.startFirstLineOffset + deltaTwips
2234
+ });
2235
+ };
2236
+ const handleMouseUp = (event) => {
2237
+ const track = trackRef.current;
2238
+ if (!track || !props.onSetIndentation) {
2239
+ cleanupDrag();
2240
+ setDragState(null);
2241
+ setPreviewLayout(null);
2242
+ return;
2243
+ }
2244
+ const rect = track.getBoundingClientRect();
2245
+ const deltaPx = event.clientX - activeDrag.startClientX;
2246
+ const deltaTwips = pxToTwips(deltaPx, rect.width, usablePageWidth);
2247
+ const leftIndent = activeDrag.kind === "left-indent" ? clampTwips(activeDrag.startLeftIndent + deltaTwips) : activeDrag.startLeftIndent;
2248
+ const firstLineOffset = activeDrag.kind === "first-line" ? activeDrag.startFirstLineOffset + deltaTwips : activeDrag.startFirstLineOffset;
2249
+ cleanupDrag();
2250
+ props.onSetIndentation(
2251
+ composeIndentation(leftIndent, effectiveLayout?.rightIndent ?? props.paragraphLayout?.rightIndent ?? 0, firstLineOffset)
2252
+ );
2253
+ setDragState(null);
2254
+ setPreviewLayout(null);
2255
+ };
2256
+ const cleanupDrag = () => {
2257
+ window.removeEventListener("mousemove", handleMouseMove);
2258
+ window.removeEventListener("mouseup", handleMouseUp);
2259
+ if (dragCleanupRef.current === cleanupDrag) {
2260
+ dragCleanupRef.current = null;
2261
+ }
2262
+ };
2263
+ dragCleanupRef.current = cleanupDrag;
2264
+ window.addEventListener("mousemove", handleMouseMove);
2265
+ window.addEventListener("mouseup", handleMouseUp);
2266
+ }
2267
+ const markerLayout = useMemo(() => {
2268
+ if (!effectiveLayout || !isBodyParagraphContext) {
2269
+ return null;
2270
+ }
2271
+ return {
2272
+ leftIndent: twipsToPercent(effectiveLayout.leftIndent, usablePageWidth),
2273
+ firstLine: twipsToPercent(
2274
+ Math.max(MIN_HANDLE_TWIPS, effectiveLayout.leftIndent + effectiveLayout.firstLineOffset),
2275
+ usablePageWidth
2276
+ ),
2277
+ tabStops: effectiveLayout.tabStops.map((tabStop, index) => ({
2278
+ id: `${tabStop.pos}-${index}`,
2279
+ left: twipsToPercent(tabStop.pos, usablePageWidth)
2280
+ }))
2281
+ };
2282
+ }, [effectiveLayout, isBodyParagraphContext, usablePageWidth]);
2283
+ const handlesOverlap = markerLayout ? Math.abs(markerLayout.leftIndent - markerLayout.firstLine) < HANDLE_OVERLAP_THRESHOLD_PERCENT : false;
2284
+ const leftIndentHandleLeft = markerLayout ? offsetHandlePercent(markerLayout.leftIndent, handlesOverlap ? -HANDLE_OFFSET_PERCENT : 0) : 0;
2285
+ const firstLineHandleLeft = markerLayout ? offsetHandlePercent(markerLayout.firstLine, handlesOverlap ? HANDLE_OFFSET_PERCENT : 0) : 0;
2286
+ return /* @__PURE__ */ jsxs13(
2287
+ "div",
2288
+ {
2289
+ "aria-label": "Page ruler",
2290
+ className: "mb-4 rounded-2xl border border-border bg-surface/80 px-4 py-3 shadow-sm",
2291
+ children: [
2292
+ /* @__PURE__ */ jsxs13("div", { className: "mb-3 flex flex-wrap items-center gap-2", children: [
2293
+ /* @__PURE__ */ jsx13(
2294
+ "button",
2295
+ {
2296
+ type: "button",
2297
+ "aria-label": "Return to document body",
2298
+ title: "Return to document body",
2299
+ onClick: props.onReturnToBody,
2300
+ className: regionButtonClass(activeRegion === "body"),
2301
+ children: "Body"
2302
+ }
2303
+ ),
2304
+ availableHeader ? /* @__PURE__ */ jsx13(
2305
+ "button",
2306
+ {
2307
+ type: "button",
2308
+ "aria-label": "Open header story",
2309
+ title: "Open header story",
2310
+ onClick: props.onOpenHeader,
2311
+ className: regionButtonClass(activeRegion === "header"),
2312
+ children: "Header"
2313
+ }
2314
+ ) : null,
2315
+ availableFooter ? /* @__PURE__ */ jsx13(
2316
+ "button",
2317
+ {
2318
+ type: "button",
2319
+ "aria-label": "Open footer story",
2320
+ title: "Open footer story",
2321
+ onClick: props.onOpenFooter,
2322
+ className: regionButtonClass(activeRegion === "footer"),
2323
+ children: "Footer"
2324
+ }
2325
+ ) : null,
2326
+ props.viewState.activeListContext ? /* @__PURE__ */ jsxs13(Fragment7, { children: [
2327
+ /* @__PURE__ */ jsx13("div", { className: "h-4 w-px bg-border" }),
2328
+ /* @__PURE__ */ jsx13(
2329
+ "button",
2330
+ {
2331
+ type: "button",
2332
+ "aria-label": "Continue numbering",
2333
+ title: "Continue numbering from previous list",
2334
+ disabled: props.readOnly,
2335
+ onClick: props.onContinueNumbering,
2336
+ className: controlButtonClass,
2337
+ children: "Continue"
2338
+ }
2339
+ ),
2340
+ /* @__PURE__ */ jsx13(
2341
+ "button",
2342
+ {
2343
+ type: "button",
2344
+ "aria-label": "Restart numbering",
2345
+ title: "Restart numbering at 1",
2346
+ disabled: props.readOnly,
2347
+ onClick: props.onRestartNumbering,
2348
+ className: controlButtonClass,
2349
+ children: "Restart"
2350
+ }
2351
+ )
2352
+ ] }) : null
2353
+ ] }),
2354
+ /* @__PURE__ */ jsx13(
2355
+ "div",
2356
+ {
2357
+ className: "mb-2 flex items-center justify-between",
2358
+ "aria-label": `Section ${props.pageLayout.sectionIndex + 1}, ${props.pageLayout.orientation}`,
2359
+ title: `Section ${props.pageLayout.sectionIndex + 1} \xB7 ${props.pageLayout.orientation}`,
2360
+ children: /* @__PURE__ */ jsx13("span", { className: "sr-only", children: "Page ruler" })
2361
+ }
2362
+ ),
2363
+ /* @__PURE__ */ jsxs13(
2364
+ "div",
2365
+ {
2366
+ ref: trackRef,
2367
+ "aria-label": "Page ruler track",
2368
+ className: "relative h-14 overflow-hidden rounded-xl border border-border bg-canvas",
2369
+ onClick: (event) => {
2370
+ if (props.readOnly || !isBodyParagraphContext || !props.paragraphLayout || !props.onSetTabStops) {
2371
+ return;
2372
+ }
2373
+ if (event.target.dataset.handle === "true") {
2374
+ return;
2375
+ }
2376
+ const rect = event.currentTarget.getBoundingClientRect();
2377
+ const nextPos = clampTwips(pxToTwips(event.clientX - rect.left, rect.width, usablePageWidth));
2378
+ props.onSetTabStops(
2379
+ [...props.paragraphLayout.tabStops, { pos: nextPos, val: "left" }].sort((left, right) => left.pos - right.pos)
2380
+ );
2381
+ },
2382
+ children: [
2383
+ /* @__PURE__ */ jsx13("div", { className: "absolute inset-x-4 top-2 h-px bg-border" }),
2384
+ /* @__PURE__ */ jsx13("div", { className: "absolute inset-x-4 top-7 h-px bg-border/70" }),
2385
+ Array.from({ length: 8 }, (_, index) => /* @__PURE__ */ jsx13(
2386
+ "div",
2387
+ {
2388
+ className: "absolute top-1 h-3 w-px bg-border/80",
2389
+ style: { left: `${12 + index * 12}%` }
2390
+ },
2391
+ `tick-${index}`
2392
+ )),
2393
+ markerLayout ? /* @__PURE__ */ jsxs13(Fragment7, { children: [
2394
+ /* @__PURE__ */ jsx13(
2395
+ "button",
2396
+ {
2397
+ type: "button",
2398
+ "data-handle": "true",
2399
+ "aria-label": "Left indent handle",
2400
+ title: `Left indent: ${effectiveLayout?.leftIndent ?? 0} twips`,
2401
+ disabled: props.readOnly,
2402
+ className: `absolute top-8 h-4 w-4 -translate-x-1/2 rounded-[5px] border border-accent/40 bg-accent-soft shadow-sm transition-opacity ${handlesOverlap ? "opacity-80 z-10" : ""}`,
2403
+ style: { left: markerLeftStyle(leftIndentHandleLeft) },
2404
+ onMouseDown: (event) => {
2405
+ event.preventDefault();
2406
+ beginDrag("left-indent", event.clientX);
2407
+ }
2408
+ }
2409
+ ),
2410
+ /* @__PURE__ */ jsx13(
2411
+ "button",
2412
+ {
2413
+ type: "button",
2414
+ "data-handle": "true",
2415
+ "aria-label": "First line indent handle",
2416
+ title: `First line offset: ${effectiveLayout?.firstLineOffset ?? 0} twips`,
2417
+ disabled: props.readOnly,
2418
+ className: `absolute top-1 h-4 w-4 -translate-x-1/2 rotate-45 rounded-[4px] border border-primary/30 bg-surface-raised shadow-sm transition-opacity ${handlesOverlap ? "opacity-80 z-20" : ""}`,
2419
+ style: { left: markerLeftStyle(firstLineHandleLeft) },
2420
+ onMouseDown: (event) => {
2421
+ event.preventDefault();
2422
+ beginDrag("first-line", event.clientX);
2423
+ }
2424
+ }
2425
+ ),
2426
+ markerLayout.tabStops.map((tabStop) => /* @__PURE__ */ jsx13(
2427
+ "div",
2428
+ {
2429
+ "data-handle": "true",
2430
+ "aria-label": `Tab stop at ${tabStop.left.toFixed(0)}%`,
2431
+ title: `Tab stop`,
2432
+ className: "absolute top-5 h-4 w-4 -translate-x-1/2 rounded-sm border border-border bg-surface-raised shadow-sm",
2433
+ style: { left: markerLeftStyle(tabStop.left) }
2434
+ },
2435
+ tabStop.id
2436
+ ))
2437
+ ] }) : null
2438
+ ]
2439
+ }
2440
+ )
2441
+ ]
2442
+ }
2443
+ );
2444
+ }
2445
+ function composeIndentation(leftIndent, rightIndent, firstLineOffset) {
2446
+ const indentation = {};
2447
+ if (leftIndent > 0) {
2448
+ indentation.left = leftIndent;
2449
+ }
2450
+ if (rightIndent > 0) {
2451
+ indentation.right = rightIndent;
2452
+ }
2453
+ if (firstLineOffset > 0) {
2454
+ indentation.firstLine = Math.round(firstLineOffset);
2455
+ } else if (firstLineOffset < 0) {
2456
+ indentation.hanging = Math.round(Math.abs(firstLineOffset));
2457
+ }
2458
+ return indentation;
2459
+ }
2460
+ function regionButtonClass(active) {
2461
+ return `inline-flex items-center rounded-full border px-3 py-1 text-xs transition-colors ${active ? "border-accent/30 bg-accent-soft text-accent" : "border-border bg-canvas text-secondary hover:bg-surface"}`;
2462
+ }
2463
+ var controlButtonClass = "inline-flex items-center rounded-full border border-border bg-canvas px-3 py-1 text-xs text-secondary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-50";
2464
+ function twipsToPercent(value, usablePageWidth) {
2465
+ return Math.max(0, Math.min(100, value / usablePageWidth * 100));
2466
+ }
2467
+ function pxToTwips(px, width, usablePageWidth) {
2468
+ if (width <= 0) {
2469
+ return 0;
2470
+ }
2471
+ return Math.round(px / width * usablePageWidth);
2472
+ }
2473
+ function clampTwips(value) {
2474
+ return Math.max(MIN_HANDLE_TWIPS, Math.round(value));
2475
+ }
2476
+ function offsetHandlePercent(value, offset) {
2477
+ return Math.max(0, Math.min(100, value + offset));
2478
+ }
2479
+ function markerLeftStyle(value) {
2480
+ const clamped = Math.max(0, Math.min(100, value));
2481
+ return `clamp(${MARKER_HALF_PX}px, ${clamped}%, calc(100% - ${MARKER_HALF_PX}px))`;
2482
+ }
2483
+
2484
+ // src/ui-tailwind/chrome/tw-suggestion-card.tsx
2485
+ import * as Tooltip4 from "@radix-ui/react-tooltip";
2486
+ import { Check as Check3, MessageSquare as MessageSquare3, Pencil, X as X2 } from "lucide-react";
2487
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2488
+ var focusRingClass7 = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-canvas";
2489
+ function TwSuggestionCard(props) {
2490
+ const contextLabel = summarizeSuggestionContext(props.model);
2491
+ const commentDisabled = !props.model.canAddComment;
2492
+ const tooltipLabel = commentDisabled ? props.model.disabledReason ?? "Commenting is unavailable for this selection" : "Comment on suggestion";
2493
+ return /* @__PURE__ */ jsxs14(
2494
+ "div",
2495
+ {
2496
+ "data-testid": "suggestion-card",
2497
+ className: "inline-flex max-w-[min(28rem,calc(100vw-2rem))] flex-col gap-2 rounded-2xl border border-border/80 bg-canvas px-3 py-2 shadow-xl ring-1 ring-border/80",
2498
+ onFocusCapture: props.onFocusCapture,
2499
+ onBlurCapture: props.onBlurCapture,
2500
+ role: "group",
2501
+ "aria-label": "Suggestion actions",
2502
+ children: [
2503
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-start justify-between gap-3", children: [
2504
+ /* @__PURE__ */ jsxs14("div", { className: "min-w-0", children: [
2505
+ /* @__PURE__ */ jsx14("div", { className: "text-[11px] font-semibold uppercase tracking-[0.12em] text-warning", children: props.model.kindLabel }),
2506
+ /* @__PURE__ */ jsx14("div", { className: "mt-1 max-w-[16rem] truncate text-sm text-primary", children: props.model.previewText })
2507
+ ] }),
2508
+ contextLabel ? /* @__PURE__ */ jsx14("div", { className: "shrink-0 rounded-full bg-accent-soft px-2 py-0.5 text-[10px] font-medium uppercase tracking-[0.08em] text-accent", children: contextLabel }) : null
2509
+ ] }),
2510
+ /* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-1.5", children: [
2511
+ /* @__PURE__ */ jsx14(
2512
+ SuggestionActionButton,
2513
+ {
2514
+ icon: /* @__PURE__ */ jsx14(Check3, { className: "h-3.5 w-3.5" }),
2515
+ label: "Accept suggestion",
2516
+ disabled: !props.model.canAccept,
2517
+ tone: "accept",
2518
+ onClick: props.onAccept
2519
+ }
2520
+ ),
2521
+ /* @__PURE__ */ jsx14(
2522
+ SuggestionActionButton,
2523
+ {
2524
+ icon: /* @__PURE__ */ jsx14(X2, { className: "h-3.5 w-3.5" }),
2525
+ label: "Reject suggestion",
2526
+ disabled: !props.model.canReject,
2527
+ tone: "reject",
2528
+ onClick: props.onReject
2529
+ }
2530
+ ),
2531
+ /* @__PURE__ */ jsx14(
2532
+ SuggestionActionButton,
2533
+ {
2534
+ icon: /* @__PURE__ */ jsx14(Pencil, { className: "h-3.5 w-3.5" }),
2535
+ label: "Edit suggestion",
2536
+ disabled: !props.model.canEditSuggestion,
2537
+ tone: "neutral",
2538
+ onClick: props.onEditSuggestion
2539
+ }
2540
+ ),
2541
+ /* @__PURE__ */ jsxs14(Tooltip4.Root, { children: [
2542
+ /* @__PURE__ */ jsx14(Tooltip4.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs14(
2543
+ "button",
2544
+ {
2545
+ type: "button",
2546
+ "aria-label": "Comment on suggestion",
2547
+ disabled: commentDisabled,
2548
+ onMouseDown: preserveEditorSelectionMouseDown,
2549
+ onClick: props.onAddComment,
2550
+ className: `inline-flex h-8 items-center gap-1 rounded-lg border border-border px-2.5 text-xs font-medium text-secondary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40 ${focusRingClass7}`,
2551
+ children: [
2552
+ /* @__PURE__ */ jsx14(MessageSquare3, { className: "h-3.5 w-3.5" }),
2553
+ "Comment"
2554
+ ]
2555
+ }
2556
+ ) }),
2557
+ /* @__PURE__ */ jsx14(Tooltip4.Portal, { children: /* @__PURE__ */ jsx14(
2558
+ Tooltip4.Content,
2559
+ {
2560
+ className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50",
2561
+ sideOffset: 6,
2562
+ children: tooltipLabel
2563
+ }
2564
+ ) })
2565
+ ] })
2566
+ ] })
2567
+ ]
2568
+ }
2569
+ );
2570
+ }
2571
+ function summarizeSuggestionContext(model) {
2572
+ const labels = model.badges.map((badge) => badge.label.trim()).filter(Boolean);
2573
+ if (labels.length === 0) {
2574
+ return null;
2575
+ }
2576
+ const summary = labels.slice(0, 2).join(" \xB7 ");
2577
+ return summary.length > 36 ? `${summary.slice(0, 33)}...` : summary;
2578
+ }
2579
+ function SuggestionActionButton(props) {
2580
+ const toneClass = props.tone === "accept" ? "border-emerald-500/30 bg-emerald-500/10 text-emerald-700 hover:bg-emerald-500/15 dark:text-emerald-300" : props.tone === "reject" ? "border-rose-500/30 bg-rose-500/10 text-rose-700 hover:bg-rose-500/15 dark:text-rose-300" : "border-border text-secondary hover:bg-surface";
2581
+ return /* @__PURE__ */ jsxs14(
2582
+ "button",
2583
+ {
2584
+ type: "button",
2585
+ "aria-label": props.label,
2586
+ disabled: props.disabled,
2587
+ onMouseDown: preserveEditorSelectionMouseDown,
2588
+ onClick: props.onClick,
2589
+ className: `inline-flex h-8 items-center gap-1 rounded-lg border px-2.5 text-xs font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${toneClass} ${focusRingClass7}`,
2590
+ children: [
2591
+ props.icon,
2592
+ props.label.replace(" suggestion", "").replace(" on suggestion", "")
2593
+ ]
2594
+ }
2595
+ );
2596
+ }
2597
+
2598
+ // src/ui-tailwind/chrome/tw-table-context-toolbar.tsx
2599
+ import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
2600
+ var CELL_COLORS = [
2601
+ "#ffffff",
2602
+ "#f0f0ee",
2603
+ "#dbeafe",
2604
+ "#fef3c7",
2605
+ "#dcfce7",
2606
+ "#fce7f3"
2607
+ ];
2608
+ function TwTableContextToolbar(props) {
2609
+ return /* @__PURE__ */ jsxs15(
2610
+ "div",
2611
+ {
2612
+ "data-testid": "table-context-toolbar",
2613
+ className: "flex flex-wrap items-center gap-2 rounded-xl border border-border bg-canvas px-3 py-2 shadow-sm",
2614
+ children: [
2615
+ /* @__PURE__ */ jsx15("span", { className: "text-[10px] font-semibold uppercase tracking-[0.12em] text-tertiary", children: "Table" }),
2616
+ /* @__PURE__ */ jsxs15(
2617
+ "select",
2618
+ {
2619
+ "aria-label": "Table style",
2620
+ className: "h-8 rounded-md border border-border bg-canvas px-2 text-xs text-primary disabled:opacity-40",
2621
+ disabled: props.disabled || props.tableStyles.length === 0 || !props.onSetTableStyle,
2622
+ onMouseDown: preserveEditorSelectionMouseDown,
2623
+ onChange: (event) => props.onSetTableStyle?.(event.target.value),
2624
+ defaultValue: "",
2625
+ children: [
2626
+ /* @__PURE__ */ jsx15("option", { value: "", disabled: true, children: "Table style" }),
2627
+ props.tableStyles.map((style) => /* @__PURE__ */ jsx15("option", { value: style.styleId, children: style.displayName }, style.styleId))
2628
+ ]
2629
+ }
2630
+ ),
2631
+ /* @__PURE__ */ jsx15(ToolbarButton3, { ariaLabel: "Add row above", disabled: props.disabled, onClick: props.onAddRowBefore, children: "Row above" }),
2632
+ /* @__PURE__ */ jsx15(ToolbarButton3, { ariaLabel: "Add row below", disabled: props.disabled, onClick: props.onAddRowAfter, children: "Row below" }),
2633
+ /* @__PURE__ */ jsx15(ToolbarButton3, { ariaLabel: "Delete row", disabled: props.disabled, onClick: props.onDeleteRow, children: "Delete row" }),
2634
+ /* @__PURE__ */ jsx15(ToolbarButton3, { ariaLabel: "Add column left", disabled: props.disabled, onClick: props.onAddColumnBefore, children: "Column left" }),
2635
+ /* @__PURE__ */ jsx15(ToolbarButton3, { ariaLabel: "Add column right", disabled: props.disabled, onClick: props.onAddColumnAfter, children: "Column right" }),
2636
+ /* @__PURE__ */ jsx15(ToolbarButton3, { ariaLabel: "Delete column", disabled: props.disabled, onClick: props.onDeleteColumn, children: "Delete column" }),
2637
+ /* @__PURE__ */ jsx15(ToolbarButton3, { ariaLabel: "Merge cells", disabled: props.disabled, onClick: props.onMergeCells, children: "Merge" }),
2638
+ /* @__PURE__ */ jsx15(ToolbarButton3, { ariaLabel: "Split cell", disabled: props.disabled, onClick: props.onSplitCell, children: "Split" }),
2639
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-1", children: [
2640
+ /* @__PURE__ */ jsx15("span", { className: "text-[11px] text-secondary", children: "Fill" }),
2641
+ CELL_COLORS.map((color) => /* @__PURE__ */ jsx15(
2642
+ "button",
2643
+ {
2644
+ type: "button",
2645
+ "aria-label": `Set cell fill ${color}`,
2646
+ disabled: props.disabled || !props.onSetCellBackground,
2647
+ onMouseDown: preserveEditorSelectionMouseDown,
2648
+ onClick: () => props.onSetCellBackground?.(color),
2649
+ className: "h-6 w-6 rounded border border-border disabled:opacity-40",
2650
+ style: { backgroundColor: color }
2651
+ },
2652
+ color
2653
+ ))
2654
+ ] }),
2655
+ /* @__PURE__ */ jsx15(ToolbarButton3, { ariaLabel: "Delete table", danger: true, disabled: props.disabled, onClick: props.onDeleteTable, children: "Delete table" })
2656
+ ]
2657
+ }
2658
+ );
2659
+ }
2660
+ function ToolbarButton3(props) {
2661
+ return /* @__PURE__ */ jsx15(
2662
+ "button",
2663
+ {
2664
+ type: "button",
2665
+ "aria-label": props.ariaLabel,
2666
+ disabled: props.disabled || !props.onClick,
2667
+ onMouseDown: preserveEditorSelectionMouseDown,
2668
+ onClick: props.onClick,
2669
+ className: `inline-flex h-8 items-center rounded-md px-2 text-xs font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${props.danger ? "text-danger hover:bg-danger/10" : "text-primary hover:bg-surface"}`,
2670
+ children: props.children
2671
+ }
2672
+ );
2673
+ }
2674
+
2675
+ // src/ui-tailwind/tw-review-workspace.tsx
2676
+ import { Fragment as Fragment8, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
2677
+ function TwReviewWorkspace(inputProps) {
2678
+ const props = {
2679
+ ...inputProps,
2680
+ ...inputProps.commands
2681
+ };
2682
+ const { snapshot, viewState } = props;
2683
+ const selectionToolbarRootRef = useRef3(null);
2684
+ const caps = props.capabilities;
2685
+ const isPageWorkspace = props.workspaceMode === "page";
2686
+ const markupDisplay = props.markupDisplay;
2687
+ const [navOpen, setNavOpen] = useState3(false);
2688
+ const [layoutToolsOpen, setLayoutToolsOpen] = useState3(false);
2689
+ const zoomLevel = props.zoomLevel ?? 100;
2690
+ const zoomScale = typeof zoomLevel === "number" ? zoomLevel / 100 : 1;
2691
+ const preserveOnlyCount = caps?.preserveOnlyCount ?? snapshot.compatibility.featureEntries.filter(
2692
+ (entry) => entry.featureClass === "preserve-only"
2693
+ ).length;
2694
+ const blockedReasons = props.interactionGuardSnapshot?.blockedReasons ?? props.workflowScopeSnapshot?.blockedReasons ?? [];
2695
+ const chromeVisibility = {
2696
+ toolbar: true,
2697
+ alerts: true,
2698
+ selectionOverlay: true,
2699
+ contextToolbars: true,
2700
+ pageChrome: true,
2701
+ statusBar: true,
2702
+ reviewRail: true,
2703
+ ...props.chromeVisibility
2704
+ };
2705
+ const showReviewRail = chromeVisibility.reviewRail && (caps?.reviewRailVisible ?? true);
2706
+ const headings = props.documentNavigation?.headings ?? [];
2707
+ const headerVariant = snapshot.pageLayout?.headerVariants[0]?.variant ?? "default";
2708
+ const footerVariant = snapshot.pageLayout?.footerVariants[0]?.variant ?? "default";
2709
+ const selectionPosition = viewState.selection.activeRange.kind === "node" ? viewState.selection.activeRange.at : viewState.selection.head;
2710
+ const activeParagraphLayout = useMemo2(
2711
+ () => resolveActiveParagraphLayout(snapshot.surface, selectionPosition),
2712
+ [selectionPosition, snapshot.surface]
2713
+ );
2714
+ const isTableContext = Boolean(
2715
+ props.formattingState?.breadcrumb.some((item) => item.kind === "table" || item.kind === "table_cell" || item.kind === "table_row")
2716
+ );
2717
+ const contextualSurface = props.activeImageContext ? "image" : props.activeObjectContext ? "object" : isTableContext ? "table" : null;
2718
+ const pageChromeModel = useMemo2(
2719
+ () => buildPageChromeModel(
2720
+ snapshot.surface,
2721
+ snapshot.pageLayout,
2722
+ props.documentNavigation,
2723
+ snapshot.activeStory
2724
+ ),
2725
+ [props.documentNavigation, snapshot.activeStory, snapshot.pageLayout, snapshot.surface]
2726
+ );
2727
+ const selectionToolbarPlacement = resolveSelectionToolbarPlacement(
2728
+ props.selectionToolbarAnchor,
2729
+ selectionToolbarRootRef.current,
2730
+ zoomScale
2731
+ );
2732
+ const activePage = props.documentNavigation?.pages[props.documentNavigation.activePageIndex] ?? null;
2733
+ const pageShellMetrics = useMemo2(
2734
+ () => buildPageShellMetrics(snapshot.pageLayout),
2735
+ [snapshot.pageLayout]
2736
+ );
2737
+ const hidePageBorderForActiveEditing = isPageWorkspace && snapshot.activeStory.kind === "main" && shouldHidePageBorderForSelection(viewState.selection);
2738
+ const effectiveSelectionMode = props.interactionGuardSnapshot?.effectiveMode ?? "edit";
2739
+ const allowLocalChromeMutations = Boolean(caps?.canEdit) && effectiveSelectionMode === "edit";
2740
+ const pageChromeReadOnly = snapshot.readOnly || snapshot.activeStory.kind !== "main" || effectiveSelectionMode !== "edit";
2741
+ const toolbarInteractionPolicy = caps ? {
2742
+ mode: effectiveSelectionMode,
2743
+ canFormatText: caps.canEdit && effectiveSelectionMode === "edit",
2744
+ canInsertStructural: caps.canEdit && effectiveSelectionMode === "edit",
2745
+ canAddComment: caps.canAddComment && effectiveSelectionMode !== "view" && effectiveSelectionMode !== "blocked"
2746
+ } : void 0;
2747
+ useEffect3(() => {
2748
+ recordPerfSample("workspace.chrome");
2749
+ incrementInvalidationCounter("workspace.chrome.recomputes");
2750
+ }, [activeParagraphLayout, pageChromeModel, pageShellMetrics]);
2751
+ useEffect3(() => {
2752
+ if (isPageWorkspace && snapshot.activeStory.kind !== "main") {
2753
+ setLayoutToolsOpen(true);
2754
+ }
2755
+ }, [isPageWorkspace, snapshot.activeStory.kind]);
2756
+ const dismissSelectionToolbar = useCallback2(() => {
2757
+ props.onDismissSelectionToolbar?.();
2758
+ }, [props.onDismissSelectionToolbar]);
2759
+ const runWithSelectionToolbarDismiss = useCallback2(
2760
+ (action) => () => {
2761
+ dismissSelectionToolbar();
2762
+ action?.();
2763
+ },
2764
+ [dismissSelectionToolbar]
2765
+ );
2766
+ return /* @__PURE__ */ jsx16(Tooltip5.Provider, { delayDuration: 400, children: /* @__PURE__ */ jsxs16("div", { className: "flex h-full flex-col bg-canvas text-primary", children: [
2767
+ chromeVisibility.toolbar ? /* @__PURE__ */ jsx16(
2768
+ TwToolbar,
2769
+ {
2770
+ sourceLabel: snapshot.sourceLabel,
2771
+ capabilities: caps,
2772
+ compatibility: snapshot.compatibility,
2773
+ warnings: snapshot.warnings,
2774
+ interactionPolicy: toolbarInteractionPolicy,
2775
+ workspaceMode: props.workspaceMode,
2776
+ zoomLevel: props.zoomLevel,
2777
+ formattingState: props.formattingState,
2778
+ styleCatalog: props.styleCatalog,
2779
+ showTrackedChanges: props.showTrackedChanges,
2780
+ onUndo: runWithSelectionToolbarDismiss(props.onUndo),
2781
+ onRedo: runWithSelectionToolbarDismiss(props.onRedo),
2782
+ onSetParagraphStyle: props.onSetParagraphStyle ? (styleId) => {
2783
+ dismissSelectionToolbar();
2784
+ props.onSetParagraphStyle?.(styleId);
2785
+ } : void 0,
2786
+ onToggleBold: runWithSelectionToolbarDismiss(props.onToggleBold),
2787
+ onToggleItalic: runWithSelectionToolbarDismiss(props.onToggleItalic),
2788
+ onToggleUnderline: runWithSelectionToolbarDismiss(props.onToggleUnderline),
2789
+ onToggleStrikethrough: runWithSelectionToolbarDismiss(props.onToggleStrikethrough),
2790
+ onToggleSuperscript: runWithSelectionToolbarDismiss(props.onToggleSuperscript),
2791
+ onToggleSubscript: runWithSelectionToolbarDismiss(props.onToggleSubscript),
2792
+ onSetFontFamily: props.onSetFontFamily ? (fontFamily) => {
2793
+ dismissSelectionToolbar();
2794
+ props.onSetFontFamily?.(fontFamily);
2795
+ } : void 0,
2796
+ onSetFontSize: props.onSetFontSize ? (fontSize) => {
2797
+ dismissSelectionToolbar();
2798
+ props.onSetFontSize?.(fontSize);
2799
+ } : void 0,
2800
+ onSetTextColor: props.onSetTextColor ? (color) => {
2801
+ dismissSelectionToolbar();
2802
+ props.onSetTextColor?.(color);
2803
+ } : void 0,
2804
+ onSetHighlightColor: props.onSetHighlightColor ? (color) => {
2805
+ dismissSelectionToolbar();
2806
+ props.onSetHighlightColor?.(color);
2807
+ } : void 0,
2808
+ onSetAlignment: props.onSetAlignment ? (alignment) => {
2809
+ dismissSelectionToolbar();
2810
+ props.onSetAlignment?.(alignment);
2811
+ } : void 0,
2812
+ onOutdent: runWithSelectionToolbarDismiss(props.onOutdent),
2813
+ onIndent: runWithSelectionToolbarDismiss(props.onIndent),
2814
+ onAddComment: runWithSelectionToolbarDismiss(props.onAddComment),
2815
+ onInsertPageBreak: runWithSelectionToolbarDismiss(props.onInsertPageBreak),
2816
+ onInsertTable: runWithSelectionToolbarDismiss(props.onInsertTable),
2817
+ onInsertSectionBreak: props.onInsertSectionBreak ? (type) => {
2818
+ dismissSelectionToolbar();
2819
+ props.onInsertSectionBreak?.(type);
2820
+ } : void 0,
2821
+ onInsertImage: props.onInsertImage ? (options) => {
2822
+ dismissSelectionToolbar();
2823
+ props.onInsertImage?.(options);
2824
+ } : void 0,
2825
+ onExport: runWithSelectionToolbarDismiss(props.onExport),
2826
+ activeStory: snapshot.activeStory,
2827
+ onCloseStory: props.onCloseStory ? runWithSelectionToolbarDismiss(props.onCloseStory) : void 0,
2828
+ onWorkspaceModeChange: (value) => {
2829
+ dismissSelectionToolbar();
2830
+ props.onWorkspaceModeChange(value);
2831
+ },
2832
+ onZoomChange: props.onZoomChange ? (level) => {
2833
+ dismissSelectionToolbar();
2834
+ props.onZoomChange?.(level);
2835
+ } : void 0,
2836
+ onShowTrackedChangesChange: (show) => {
2837
+ dismissSelectionToolbar();
2838
+ props.onShowTrackedChangesChange(show);
2839
+ },
2840
+ blockedReasons
2841
+ }
2842
+ ) : null,
2843
+ chromeVisibility.alerts ? /* @__PURE__ */ jsx16(
2844
+ TwAlertBanner,
2845
+ {
2846
+ snapshot,
2847
+ preserveOnlyCount,
2848
+ workflowBlockedReasons: blockedReasons
2849
+ }
2850
+ ) : null,
2851
+ /* @__PURE__ */ jsxs16("div", { className: "flex flex-1 min-h-0", children: [
2852
+ isPageWorkspace && chromeVisibility.pageChrome ? /* @__PURE__ */ jsx16(
2853
+ "aside",
2854
+ {
2855
+ "aria-label": "Document navigator",
2856
+ className: `shrink-0 border-r border-border bg-surface transition-[width] duration-200 ${navOpen ? "w-48" : "w-0"} overflow-hidden`,
2857
+ children: navOpen ? /* @__PURE__ */ jsxs16("div", { className: "flex h-full flex-col", children: [
2858
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center justify-between px-3 py-2 border-b border-border", children: [
2859
+ /* @__PURE__ */ jsx16("span", { className: "text-xs font-medium text-secondary uppercase tracking-wider", children: "Navigator" }),
2860
+ /* @__PURE__ */ jsxs16(Tooltip5.Root, { children: [
2861
+ /* @__PURE__ */ jsx16(Tooltip5.Trigger, { asChild: true, children: /* @__PURE__ */ jsx16(
2862
+ "button",
2863
+ {
2864
+ type: "button",
2865
+ "aria-label": "Collapse navigator",
2866
+ onMouseDown: preserveEditorSelectionMouseDown,
2867
+ onClick: () => {
2868
+ dismissSelectionToolbar();
2869
+ setNavOpen(false);
2870
+ },
2871
+ className: "inline-flex h-6 w-6 items-center justify-center rounded-md text-secondary hover:bg-surface-hover transition-colors",
2872
+ children: /* @__PURE__ */ jsx16(ChevronLeft, { className: "h-3.5 w-3.5" })
2873
+ }
2874
+ ) }),
2875
+ /* @__PURE__ */ jsx16(Tooltip5.Portal, { children: /* @__PURE__ */ jsx16(Tooltip5.Content, { className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50", sideOffset: 6, children: "Collapse navigator" }) })
2876
+ ] })
2877
+ ] }),
2878
+ /* @__PURE__ */ jsx16("nav", { className: "flex-1 overflow-y-auto px-2 py-2", "aria-label": "Document headings", children: headings.length > 0 ? /* @__PURE__ */ jsx16("ul", { className: "space-y-0.5", children: headings.map((entry) => /* @__PURE__ */ jsx16("li", { children: /* @__PURE__ */ jsx16(
2879
+ "button",
2880
+ {
2881
+ type: "button",
2882
+ className: "block w-full truncate rounded-md px-2 py-1 text-left text-xs text-primary hover:bg-surface-hover",
2883
+ style: { paddingLeft: `${8 + (entry.level - 1) * 12}px` },
2884
+ onMouseDown: preserveEditorSelectionMouseDown,
2885
+ onClick: () => {
2886
+ dismissSelectionToolbar();
2887
+ props.onNavigateHeading?.(entry.headingId);
2888
+ setNavOpen(false);
2889
+ },
2890
+ children: entry.text
2891
+ }
2892
+ ) }, entry.headingId)) }) : /* @__PURE__ */ jsx16("p", { className: "px-2 py-4 text-xs text-tertiary", children: "No headings found." }) })
2893
+ ] }) : null
2894
+ }
2895
+ ) : null,
2896
+ isPageWorkspace && chromeVisibility.pageChrome && !navOpen ? /* @__PURE__ */ jsx16("div", { className: "shrink-0 flex items-start pt-2 pl-1", children: /* @__PURE__ */ jsxs16(Tooltip5.Root, { children: [
2897
+ /* @__PURE__ */ jsx16(Tooltip5.Trigger, { asChild: true, children: /* @__PURE__ */ jsx16(
2898
+ "button",
2899
+ {
2900
+ type: "button",
2901
+ "aria-label": "Open document navigator",
2902
+ onMouseDown: preserveEditorSelectionMouseDown,
2903
+ onClick: () => {
2904
+ dismissSelectionToolbar();
2905
+ setNavOpen(true);
2906
+ },
2907
+ className: "inline-flex h-7 w-7 items-center justify-center rounded-md text-secondary hover:bg-surface-hover transition-colors",
2908
+ children: /* @__PURE__ */ jsx16(List2, { className: "h-3.5 w-3.5" })
2909
+ }
2910
+ ) }),
2911
+ /* @__PURE__ */ jsx16(Tooltip5.Portal, { children: /* @__PURE__ */ jsx16(Tooltip5.Content, { className: "rounded-md bg-primary px-2 py-1 text-xs text-white shadow-md z-50", sideOffset: 6, children: "Open document navigator" }) })
2912
+ ] }) }) : null,
2913
+ /* @__PURE__ */ jsxs16("div", { className: "flex flex-1 flex-col min-w-0", children: [
2914
+ /* @__PURE__ */ jsx16(
2915
+ "div",
2916
+ {
2917
+ className: `flex-1 overflow-y-auto ${isPageWorkspace ? "bg-surface" : "bg-canvas"}`,
2918
+ "data-wre-scroll-root": "true",
2919
+ children: /* @__PURE__ */ jsxs16(
2920
+ "div",
2921
+ {
2922
+ ref: selectionToolbarRootRef,
2923
+ className: `mx-auto min-h-full ${isPageWorkspace ? "wre-page-chrome wre-page-surface relative max-w-[840px] my-8 overflow-hidden" : "wre-canvas-surface relative bg-canvas"}`,
2924
+ style: isPageWorkspace && zoomScale !== 1 ? { transform: `scale(${zoomScale})`, transformOrigin: "top center" } : void 0,
2925
+ children: [
2926
+ isPageWorkspace && chromeVisibility.pageChrome && snapshot.pageLayout ? /* @__PURE__ */ jsx16("div", { className: "border-b border-border/70 bg-surface/65 px-5 py-3", "data-testid": "page-context-summary", children: /* @__PURE__ */ jsxs16("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
2927
+ /* @__PURE__ */ jsxs16("div", { className: "flex flex-wrap items-center gap-2 text-xs text-secondary", children: [
2928
+ /* @__PURE__ */ jsx16("span", { className: "rounded-full bg-canvas px-2 py-1 font-medium text-primary", children: activePage ? `Page ${activePage.pageIndex + 1} of ${props.documentNavigation?.pageCount ?? 1}` : "Page workspace" }),
2929
+ /* @__PURE__ */ jsx16("span", { children: `Section ${snapshot.pageLayout.sectionIndex + 1}` }),
2930
+ /* @__PURE__ */ jsx16("span", { className: "uppercase tracking-[0.12em] text-tertiary", children: snapshot.pageLayout.orientation })
2931
+ ] }),
2932
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2", children: [
2933
+ snapshot.activeStory.kind !== "main" ? /* @__PURE__ */ jsx16(
2934
+ "button",
2935
+ {
2936
+ type: "button",
2937
+ "aria-label": "Return to document body",
2938
+ onMouseDown: preserveEditorSelectionMouseDown,
2939
+ onClick: runWithSelectionToolbarDismiss(props.onCloseStory),
2940
+ className: "inline-flex items-center gap-1 rounded-md border border-border bg-canvas px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-surface",
2941
+ children: "Body"
2942
+ }
2943
+ ) : null,
2944
+ snapshot.activeStory.kind === "main" && snapshot.pageLayout.sectionIndex > 0 ? /* @__PURE__ */ jsxs16(Fragment8, { children: [
2945
+ /* @__PURE__ */ jsx16(
2946
+ "button",
2947
+ {
2948
+ type: "button",
2949
+ "aria-label": "Link header to previous",
2950
+ disabled: !props.onSetHeaderFooterLink || !allowLocalChromeMutations,
2951
+ onMouseDown: preserveEditorSelectionMouseDown,
2952
+ onClick: () => {
2953
+ dismissSelectionToolbar();
2954
+ props.onSetHeaderFooterLink?.(snapshot.pageLayout.sectionIndex, {
2955
+ kind: "header",
2956
+ variant: headerVariant,
2957
+ linkToPrevious: true
2958
+ });
2959
+ },
2960
+ className: "inline-flex items-center gap-1 rounded-md border border-border bg-canvas px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40",
2961
+ children: "Link header"
2962
+ }
2963
+ ),
2964
+ /* @__PURE__ */ jsx16(
2965
+ "button",
2966
+ {
2967
+ type: "button",
2968
+ "aria-label": "Link footer to previous",
2969
+ disabled: !props.onSetHeaderFooterLink || !allowLocalChromeMutations,
2970
+ onMouseDown: preserveEditorSelectionMouseDown,
2971
+ onClick: () => {
2972
+ dismissSelectionToolbar();
2973
+ props.onSetHeaderFooterLink?.(snapshot.pageLayout.sectionIndex, {
2974
+ kind: "footer",
2975
+ variant: footerVariant,
2976
+ linkToPrevious: true
2977
+ });
2978
+ },
2979
+ className: "inline-flex items-center gap-1 rounded-md border border-border bg-canvas px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-surface disabled:cursor-not-allowed disabled:opacity-40",
2980
+ children: "Link footer"
2981
+ }
2982
+ )
2983
+ ] }) : null,
2984
+ /* @__PURE__ */ jsxs16(
2985
+ "button",
2986
+ {
2987
+ type: "button",
2988
+ "aria-label": "Toggle layout tools",
2989
+ "aria-expanded": layoutToolsOpen,
2990
+ onMouseDown: preserveEditorSelectionMouseDown,
2991
+ onClick: () => {
2992
+ dismissSelectionToolbar();
2993
+ setLayoutToolsOpen((open) => !open);
2994
+ },
2995
+ className: "inline-flex items-center gap-1 rounded-md border border-border bg-canvas px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-surface",
2996
+ children: [
2997
+ /* @__PURE__ */ jsx16(ChevronRight, { className: `h-3.5 w-3.5 transition-transform ${layoutToolsOpen ? "rotate-90" : ""}` }),
2998
+ "Layout tools"
2999
+ ]
3000
+ }
3001
+ )
3002
+ ] })
3003
+ ] }) }) : null,
3004
+ isPageWorkspace && chromeVisibility.pageChrome && snapshot.pageLayout && layoutToolsOpen ? /* @__PURE__ */ jsxs16("div", { className: "px-5 pt-3", children: [
3005
+ /* @__PURE__ */ jsx16(
3006
+ TwPageRuler,
3007
+ {
3008
+ pageLayout: snapshot.pageLayout,
3009
+ viewState,
3010
+ paragraphLayout: activeParagraphLayout,
3011
+ readOnly: pageChromeReadOnly,
3012
+ onReturnToBody: props.onCloseStory ? runWithSelectionToolbarDismiss(props.onCloseStory) : () => void 0,
3013
+ onOpenHeader: props.onOpenHeaderStory ? runWithSelectionToolbarDismiss(props.onOpenHeaderStory) : void 0,
3014
+ onOpenFooter: props.onOpenFooterStory ? runWithSelectionToolbarDismiss(props.onOpenFooterStory) : void 0,
3015
+ onSetIndentation: props.onSetParagraphIndentation ? (indentation) => {
3016
+ dismissSelectionToolbar();
3017
+ props.onSetParagraphIndentation?.(indentation);
3018
+ } : void 0,
3019
+ onSetTabStops: props.onSetParagraphTabStops ? (tabStops) => {
3020
+ dismissSelectionToolbar();
3021
+ props.onSetParagraphTabStops?.(tabStops);
3022
+ } : void 0,
3023
+ onRestartNumbering: props.onRestartNumbering ? runWithSelectionToolbarDismiss(props.onRestartNumbering) : void 0,
3024
+ onContinueNumbering: props.onContinueNumbering ? runWithSelectionToolbarDismiss(props.onContinueNumbering) : void 0
3025
+ }
3026
+ ),
3027
+ /* @__PURE__ */ jsx16(
3028
+ TwLayoutPanel,
3029
+ {
3030
+ pageLayout: snapshot.pageLayout,
3031
+ readOnly: pageChromeReadOnly,
3032
+ onInsertSectionBreak: props.onInsertSectionBreak ? (type) => {
3033
+ dismissSelectionToolbar();
3034
+ props.onInsertSectionBreak?.(type);
3035
+ } : void 0,
3036
+ onDeleteSectionBreak: props.onDeleteSectionBreak ? (sectionIndex) => {
3037
+ dismissSelectionToolbar();
3038
+ props.onDeleteSectionBreak?.(sectionIndex);
3039
+ } : void 0,
3040
+ onUpdateSectionLayout: props.onUpdateSectionLayout ? (sectionIndex, patch) => {
3041
+ dismissSelectionToolbar();
3042
+ props.onUpdateSectionLayout?.(sectionIndex, patch);
3043
+ } : void 0,
3044
+ onSetSectionPageNumbering: props.onSetSectionPageNumbering ? (sectionIndex, patch) => {
3045
+ dismissSelectionToolbar();
3046
+ props.onSetSectionPageNumbering?.(sectionIndex, patch);
3047
+ } : void 0
3048
+ }
3049
+ )
3050
+ ] }) : null,
3051
+ chromeVisibility.contextToolbars && contextualSurface ? /* @__PURE__ */ jsxs16("div", { className: "px-5 pt-3 space-y-3", children: [
3052
+ contextualSurface === "table" ? /* @__PURE__ */ jsx16(
3053
+ TwTableContextToolbar,
3054
+ {
3055
+ disabled: !allowLocalChromeMutations,
3056
+ tableStyles: props.styleCatalog?.tables ?? [],
3057
+ onSetTableStyle: props.onSetTableStyle ? (styleId) => {
3058
+ dismissSelectionToolbar();
3059
+ props.onSetTableStyle?.(styleId);
3060
+ } : void 0,
3061
+ onAddRowBefore: runWithSelectionToolbarDismiss(props.onAddRowBefore),
3062
+ onAddRowAfter: runWithSelectionToolbarDismiss(props.onAddRowAfter),
3063
+ onAddColumnBefore: runWithSelectionToolbarDismiss(props.onAddColumnBefore),
3064
+ onAddColumnAfter: runWithSelectionToolbarDismiss(props.onAddColumnAfter),
3065
+ onDeleteRow: runWithSelectionToolbarDismiss(props.onDeleteRow),
3066
+ onDeleteColumn: runWithSelectionToolbarDismiss(props.onDeleteColumn),
3067
+ onDeleteTable: runWithSelectionToolbarDismiss(props.onDeleteTable),
3068
+ onMergeCells: runWithSelectionToolbarDismiss(props.onMergeCells),
3069
+ onSplitCell: runWithSelectionToolbarDismiss(props.onSplitCell),
3070
+ onSetCellBackground: props.onSetCellBackground ? (color) => {
3071
+ dismissSelectionToolbar();
3072
+ props.onSetCellBackground?.(color);
3073
+ } : void 0
3074
+ }
3075
+ ) : null,
3076
+ contextualSurface === "image" && props.activeImageContext ? /* @__PURE__ */ jsx16(
3077
+ TwImageContextToolbar,
3078
+ {
3079
+ activeImage: props.activeImageContext,
3080
+ disabled: !allowLocalChromeMutations,
3081
+ onSetImageLayout: props.onSetImageLayout ? (mediaId, dimensions) => {
3082
+ dismissSelectionToolbar();
3083
+ props.onSetImageLayout?.(mediaId, dimensions);
3084
+ } : void 0,
3085
+ onSetImageFrame: props.onSetImageFrame ? (mediaId, offsets) => {
3086
+ dismissSelectionToolbar();
3087
+ props.onSetImageFrame?.(mediaId, offsets);
3088
+ } : void 0
3089
+ }
3090
+ ) : null,
3091
+ contextualSurface === "object" && props.activeObjectContext ? /* @__PURE__ */ jsx16(TwObjectContextToolbar, { activeObject: props.activeObjectContext }) : null
3092
+ ] }) : null,
3093
+ chromeVisibility.selectionOverlay && props.suggestionCard && selectionToolbarPlacement ? /* @__PURE__ */ jsx16("div", { className: "pointer-events-none absolute inset-0 z-20", "data-testid": "suggestion-card-overlay", children: /* @__PURE__ */ jsx16(
3094
+ "div",
3095
+ {
3096
+ className: "pointer-events-auto absolute",
3097
+ "data-placement": selectionToolbarPlacement.placement,
3098
+ style: selectionToolbarPlacement.style,
3099
+ children: /* @__PURE__ */ jsx16(
3100
+ TwSuggestionCard,
3101
+ {
3102
+ model: props.suggestionCard,
3103
+ onFocusCapture: props.onSelectionToolbarFocusCapture,
3104
+ onBlurCapture: props.onSelectionToolbarBlurCapture,
3105
+ onAccept: props.onAcceptSuggestion,
3106
+ onReject: props.onRejectSuggestion,
3107
+ onEditSuggestion: props.onEditSuggestion,
3108
+ onAddComment: props.onAddCommentFromSuggestion ?? props.onAddComment
3109
+ }
3110
+ )
3111
+ }
3112
+ ) }) : null,
3113
+ chromeVisibility.selectionOverlay && props.suggestionCard && !selectionToolbarPlacement ? /* @__PURE__ */ jsx16(
3114
+ "div",
3115
+ {
3116
+ className: "pointer-events-none absolute inset-x-0 top-0 z-20 flex justify-center px-4 pt-3",
3117
+ "data-testid": "suggestion-card-fallback",
3118
+ children: /* @__PURE__ */ jsx16("div", { className: "pointer-events-auto", "data-placement": "fallback", children: /* @__PURE__ */ jsx16(
3119
+ TwSuggestionCard,
3120
+ {
3121
+ model: props.suggestionCard,
3122
+ onFocusCapture: props.onSelectionToolbarFocusCapture,
3123
+ onBlurCapture: props.onSelectionToolbarBlurCapture,
3124
+ onAccept: props.onAcceptSuggestion,
3125
+ onReject: props.onRejectSuggestion,
3126
+ onEditSuggestion: props.onEditSuggestion,
3127
+ onAddComment: props.onAddCommentFromSuggestion ?? props.onAddComment
3128
+ }
3129
+ ) })
3130
+ }
3131
+ ) : null,
3132
+ chromeVisibility.selectionOverlay && props.selectionToolbar && !props.suggestionCard && selectionToolbarPlacement ? /* @__PURE__ */ jsx16("div", { className: "pointer-events-none absolute inset-0 z-20", "data-testid": "selection-toolbar-overlay", children: /* @__PURE__ */ jsx16(
3133
+ "div",
3134
+ {
3135
+ className: "pointer-events-auto absolute",
3136
+ "data-placement": selectionToolbarPlacement.placement,
3137
+ style: selectionToolbarPlacement.style,
3138
+ children: /* @__PURE__ */ jsx16(
3139
+ TwSelectionToolbar,
3140
+ {
3141
+ ref: props.selectionToolbarRef,
3142
+ model: props.selectionToolbar,
3143
+ disabledReason: props.selectionToolbar.disabledReason,
3144
+ onFocusCapture: props.onSelectionToolbarFocusCapture,
3145
+ onBlurCapture: props.onSelectionToolbarBlurCapture,
3146
+ onToggleBold: props.onToggleBold,
3147
+ onToggleItalic: props.onToggleItalic,
3148
+ onToggleUnderline: props.onToggleUnderline,
3149
+ onSetTextColor: props.onSetSelectionTextColor,
3150
+ onSetHighlightColor: props.onSetSelectionHighlightColor,
3151
+ onAddComment: props.onAddCommentFromSelection ?? props.onAddComment
3152
+ }
3153
+ )
3154
+ }
3155
+ ) }) : null,
3156
+ chromeVisibility.selectionOverlay && props.selectionToolbar && !props.suggestionCard && !selectionToolbarPlacement ? /* @__PURE__ */ jsx16(
3157
+ "div",
3158
+ {
3159
+ className: "pointer-events-none absolute inset-x-0 top-0 z-20 flex justify-center px-4 pt-3",
3160
+ "data-testid": "selection-toolbar-fallback",
3161
+ children: /* @__PURE__ */ jsx16("div", { className: "pointer-events-auto", "data-placement": "fallback", children: /* @__PURE__ */ jsx16(
3162
+ TwSelectionToolbar,
3163
+ {
3164
+ ref: props.selectionToolbarRef,
3165
+ model: props.selectionToolbar,
3166
+ disabledReason: props.selectionToolbar.disabledReason,
3167
+ onFocusCapture: props.onSelectionToolbarFocusCapture,
3168
+ onBlurCapture: props.onSelectionToolbarBlurCapture,
3169
+ onToggleBold: props.onToggleBold,
3170
+ onToggleItalic: props.onToggleItalic,
3171
+ onToggleUnderline: props.onToggleUnderline,
3172
+ onSetTextColor: props.onSetSelectionTextColor,
3173
+ onSetHighlightColor: props.onSetSelectionHighlightColor,
3174
+ onAddComment: props.onAddCommentFromSelection ?? props.onAddComment
3175
+ }
3176
+ ) })
3177
+ }
3178
+ ) : null,
3179
+ /* @__PURE__ */ jsxs16(
3180
+ "div",
3181
+ {
3182
+ className: isPageWorkspace ? "relative" : void 0,
3183
+ "data-line-numbering": pageChromeModel.lineNumberingEnabled ? "enabled" : "disabled",
3184
+ children: [
3185
+ isPageWorkspace && chromeVisibility.pageChrome && pageChromeModel.lineNumberingEnabled ? /* @__PURE__ */ jsx16(
3186
+ "div",
3187
+ {
3188
+ "aria-hidden": "true",
3189
+ className: "pointer-events-none absolute inset-y-0 left-0 z-10",
3190
+ "data-testid": "page-line-number-gutter",
3191
+ style: { width: `${pageChromeModel.gutterWidthPx}px` },
3192
+ children: pageChromeModel.lineMarkers.map((marker) => /* @__PURE__ */ jsx16(
3193
+ "span",
3194
+ {
3195
+ className: "absolute right-2 font-[family-name:var(--font-legal-sans)] text-[10px] font-medium tabular-nums tracking-[0.12em] text-tertiary/80",
3196
+ style: { top: `${marker.topPx}px` },
3197
+ children: marker.label
3198
+ },
3199
+ marker.id
3200
+ ))
3201
+ }
3202
+ ) : null,
3203
+ /* @__PURE__ */ jsx16(
3204
+ "div",
3205
+ {
3206
+ className: isPageWorkspace && chromeVisibility.pageChrome && pageChromeModel.lineNumberingEnabled ? "pl-12" : void 0,
3207
+ style: isPageWorkspace ? pageShellMetrics.contentInsetStyle : void 0,
3208
+ children: /* @__PURE__ */ jsxs16(
3209
+ "div",
3210
+ {
3211
+ className: isPageWorkspace ? "relative" : void 0,
3212
+ "data-document-grid": pageChromeModel.documentGridType,
3213
+ "data-page-border-display": pageChromeModel.pageBorderDisplay,
3214
+ style: isPageWorkspace ? {
3215
+ ...pageChromeModel.documentGridStyle,
3216
+ ...pageShellMetrics.pageFrameStyle
3217
+ } : pageChromeModel.documentGridStyle,
3218
+ children: [
3219
+ isPageWorkspace && chromeVisibility.pageChrome ? /* @__PURE__ */ jsxs16(
3220
+ "div",
3221
+ {
3222
+ "data-testid": "page-header-band",
3223
+ className: "relative z-10 flex items-center justify-between border-b border-dashed border-border/60 px-4 text-[11px] text-secondary",
3224
+ style: pageShellMetrics.headerBandStyle,
3225
+ children: [
3226
+ /* @__PURE__ */ jsx16("span", { className: "uppercase tracking-[0.12em] text-tertiary", children: "Header" }),
3227
+ snapshot.pageLayout?.headerVariants[0] ? /* @__PURE__ */ jsx16(
3228
+ "button",
3229
+ {
3230
+ type: "button",
3231
+ "aria-label": "Open header story",
3232
+ onClick: props.onOpenHeaderStory,
3233
+ className: "rounded-md px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-surface",
3234
+ children: "Edit header"
3235
+ }
3236
+ ) : null
3237
+ ]
3238
+ }
3239
+ ) : null,
3240
+ isPageWorkspace && chromeVisibility.pageChrome && pageChromeModel.showPageBorder && !hidePageBorderForActiveEditing ? /* @__PURE__ */ jsx16(
3241
+ "div",
3242
+ {
3243
+ "aria-hidden": "true",
3244
+ className: "pointer-events-none absolute inset-0 z-0 rounded-[2px]",
3245
+ "data-testid": "page-border-overlay",
3246
+ style: pageChromeModel.pageBorderStyle
3247
+ }
3248
+ ) : null,
3249
+ /* @__PURE__ */ jsx16("div", { className: isPageWorkspace ? "relative z-10" : void 0, children: props.document }),
3250
+ isPageWorkspace && chromeVisibility.pageChrome ? /* @__PURE__ */ jsxs16(
3251
+ "div",
3252
+ {
3253
+ "data-testid": "page-footer-band",
3254
+ className: "relative z-10 flex items-center justify-between border-t border-dashed border-border/60 px-4 text-[11px] text-secondary",
3255
+ style: pageShellMetrics.footerBandStyle,
3256
+ children: [
3257
+ /* @__PURE__ */ jsx16("span", { className: "uppercase tracking-[0.12em] text-tertiary", children: "Footer" }),
3258
+ snapshot.pageLayout?.footerVariants[0] ? /* @__PURE__ */ jsx16(
3259
+ "button",
3260
+ {
3261
+ type: "button",
3262
+ "aria-label": "Open footer story",
3263
+ onClick: props.onOpenFooterStory,
3264
+ className: "rounded-md px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-surface",
3265
+ children: "Edit footer"
3266
+ }
3267
+ ) : null
3268
+ ]
3269
+ }
3270
+ ) : null
3271
+ ]
3272
+ }
3273
+ )
3274
+ }
3275
+ )
3276
+ ]
3277
+ }
3278
+ )
3279
+ ]
3280
+ }
3281
+ )
3282
+ }
3283
+ ),
3284
+ chromeVisibility.statusBar ? /* @__PURE__ */ jsx16(
3285
+ TwStatusBar,
3286
+ {
3287
+ isDirty: snapshot.isDirty,
3288
+ isExportBlocked: snapshot.compatibility.blockExport,
3289
+ preserveOnlyCount,
3290
+ commentCount: snapshot.comments.totalCount,
3291
+ changeCount: snapshot.trackedChanges.totalCount,
3292
+ sessionId: snapshot.sessionId
3293
+ }
3294
+ ) : null
3295
+ ] }),
3296
+ showReviewRail ? /* @__PURE__ */ jsx16(
3297
+ TwReviewRail,
3298
+ {
3299
+ activeTab: props.activeRailTab,
3300
+ currentUserId: props.currentUserId,
3301
+ comments: snapshot.comments,
3302
+ trackedChanges: snapshot.trackedChanges,
3303
+ compatibility: snapshot.compatibility,
3304
+ warnings: snapshot.warnings,
3305
+ markupDisplay,
3306
+ activeCommentId: props.activeCommentId,
3307
+ activeRevisionId: props.activeRevisionId,
3308
+ onActiveTabChange: props.onActiveRailTabChange,
3309
+ onOpenComment: props.onOpenComment,
3310
+ onResolveComment: props.onResolveComment,
3311
+ onReopenComment: props.onReopenComment,
3312
+ onAddReply: props.onAddReply,
3313
+ onEditBody: props.onEditBody,
3314
+ onOpenRevision: props.onOpenRevision,
3315
+ onAcceptRevision: props.onAcceptRevision,
3316
+ onRejectRevision: props.onRejectRevision,
3317
+ onAcceptAllChanges: props.onAcceptAllChanges,
3318
+ onRejectAllChanges: props.onRejectAllChanges
3319
+ }
3320
+ ) : null
3321
+ ] })
3322
+ ] }) });
3323
+ }
3324
+ function shouldHidePageBorderForSelection(selection) {
3325
+ if (selection.isCollapsed) {
3326
+ return false;
3327
+ }
3328
+ return selection.activeRange.kind === "range";
3329
+ }
3330
+ function resolveActiveParagraphLayout(surface, position) {
3331
+ const paragraph = surface ? findActiveParagraph(surface.blocks, position) : null;
3332
+ if (!paragraph) {
3333
+ return null;
3334
+ }
3335
+ return {
3336
+ leftIndent: paragraph.indentation?.left ?? 0,
3337
+ rightIndent: paragraph.indentation?.right ?? 0,
3338
+ firstLineOffset: paragraph.indentation?.firstLine ?? (paragraph.indentation?.hanging ? -paragraph.indentation.hanging : 0),
3339
+ tabStops: paragraph.tabStops ? [...paragraph.tabStops] : []
3340
+ };
3341
+ }
3342
+ function findActiveParagraph(blocks, position) {
3343
+ for (const block of blocks) {
3344
+ if (block.kind === "paragraph" && position >= block.from && position <= block.to) {
3345
+ return block;
3346
+ }
3347
+ if (block.kind === "table") {
3348
+ for (const row of block.rows) {
3349
+ for (const cell of row.cells) {
3350
+ const paragraph = findActiveParagraph(cell.content, position);
3351
+ if (paragraph) {
3352
+ return paragraph;
3353
+ }
3354
+ }
3355
+ }
3356
+ }
3357
+ if (block.kind === "sdt_block") {
3358
+ const paragraph = findActiveParagraph(block.children, position);
3359
+ if (paragraph) {
3360
+ return paragraph;
3361
+ }
3362
+ }
3363
+ }
3364
+ return null;
3365
+ }
3366
+ var EMPTY_PAGE_CHROME_MODEL = {
3367
+ lineNumberingEnabled: false,
3368
+ gutterWidthPx: 0,
3369
+ lineMarkers: [],
3370
+ showPageBorder: false,
3371
+ pageBorderDisplay: "none",
3372
+ pageBorderStyle: void 0,
3373
+ documentGridType: "none",
3374
+ documentGridStyle: void 0
3375
+ };
3376
+ var DOCUMENT_CONTENT_TOP_PADDING_PX = 40;
3377
+ function buildPageChromeModel(surface, pageLayout, navigation, activeStory) {
3378
+ if (!surface || !pageLayout || !navigation || activeStory.kind !== "main") {
3379
+ return EMPTY_PAGE_CHROME_MODEL;
3380
+ }
3381
+ const lineMarkers = computeLineMarkersIfEnabled({
3382
+ pageLayout,
3383
+ surfaceBlocks: surface.blocks,
3384
+ pages: navigation.pages,
3385
+ buildLineNumberMarkers
3386
+ });
3387
+ const lineNumberingEnabled = Boolean(pageLayout.lineNumbering) && lineMarkers.length > 0;
3388
+ const distance = pageLayout.lineNumbering?.distance ?? 0;
3389
+ const gutterWidthPx = lineNumberingEnabled ? Math.max(40, Math.min(88, 24 + Math.round(distance * DEFAULT_PAGE_ESTIMATE_PX_PER_TWIP))) : 0;
3390
+ const showPageBorder = shouldRenderPageBorder(pageLayout, navigation.pages, navigation.activePageIndex);
3391
+ return {
3392
+ lineNumberingEnabled,
3393
+ gutterWidthPx,
3394
+ lineMarkers,
3395
+ showPageBorder,
3396
+ pageBorderDisplay: pageLayout.pageBorders?.display ?? "none",
3397
+ pageBorderStyle: showPageBorder ? buildPageBorderStyle(pageLayout) : void 0,
3398
+ documentGridType: pageLayout.documentGrid?.type ?? "none",
3399
+ documentGridStyle: buildDocumentGridStyle(pageLayout.documentGrid)
3400
+ };
3401
+ }
3402
+ function buildPageShellMetrics(pageLayout) {
3403
+ if (!pageLayout) {
3404
+ return {
3405
+ contentInsetStyle: {},
3406
+ pageFrameStyle: {},
3407
+ headerBandStyle: {},
3408
+ footerBandStyle: {}
3409
+ };
3410
+ }
3411
+ const horizontalInsetPx = Math.max(
3412
+ 24,
3413
+ Math.min(120, Math.round(pageLayout.marginLeft * DEFAULT_PAGE_ESTIMATE_PX_PER_TWIP))
3414
+ );
3415
+ const verticalInsetPx = Math.max(
3416
+ 24,
3417
+ Math.min(140, Math.round(pageLayout.marginTop * DEFAULT_PAGE_ESTIMATE_PX_PER_TWIP))
3418
+ );
3419
+ const headerBandHeightPx = Math.max(
3420
+ 40,
3421
+ Math.min(96, Math.round(pageLayout.headerMargin * DEFAULT_PAGE_ESTIMATE_PX_PER_TWIP + 16))
3422
+ );
3423
+ const footerBandHeightPx = Math.max(
3424
+ 40,
3425
+ Math.min(96, Math.round(pageLayout.footerMargin * DEFAULT_PAGE_ESTIMATE_PX_PER_TWIP + 16))
3426
+ );
3427
+ return {
3428
+ contentInsetStyle: {
3429
+ paddingLeft: `${horizontalInsetPx}px`,
3430
+ paddingRight: `${horizontalInsetPx}px`,
3431
+ paddingTop: `${Math.max(20, verticalInsetPx - 12)}px`,
3432
+ paddingBottom: `${Math.max(20, Math.round(pageLayout.marginBottom * DEFAULT_PAGE_ESTIMATE_PX_PER_TWIP) - 12)}px`
3433
+ },
3434
+ pageFrameStyle: {
3435
+ backgroundColor: "var(--color-page-bg)"
3436
+ },
3437
+ headerBandStyle: {
3438
+ minHeight: `${headerBandHeightPx}px`
3439
+ },
3440
+ footerBandStyle: {
3441
+ minHeight: `${footerBandHeightPx}px`
3442
+ }
3443
+ };
3444
+ }
3445
+ function buildLineNumberMarkers(blocks, pages) {
3446
+ const markers = [];
3447
+ if (pages.length === 0) {
3448
+ return markers;
3449
+ }
3450
+ let currentTopTwips = 0;
3451
+ let lineNumber = 1;
3452
+ let lastPageIndex = -1;
3453
+ let lastSectionIndex = -1;
3454
+ for (const block of blocks) {
3455
+ const pageIndex = findPageForOffset(pages, block.from);
3456
+ const page = pages[pageIndex];
3457
+ if (!page) {
3458
+ continue;
3459
+ }
3460
+ const lineNumbering = page.layout.lineNumbering;
3461
+ const restartMode = lineNumbering?.restart ?? "newPage";
3462
+ const restartStart = lineNumbering?.start ?? 1;
3463
+ const countBy = Math.max(1, lineNumbering?.countBy ?? 1);
3464
+ const columnWidth = getUsableColumnWidth(page.layout);
3465
+ if (pageIndex !== lastPageIndex) {
3466
+ if (restartMode === "newPage" || lastPageIndex === -1) {
3467
+ lineNumber = restartStart;
3468
+ }
3469
+ lastPageIndex = pageIndex;
3470
+ }
3471
+ if (page.sectionIndex !== lastSectionIndex) {
3472
+ if (restartMode === "newSection" || lastSectionIndex === -1) {
3473
+ lineNumber = restartStart;
3474
+ }
3475
+ lastSectionIndex = page.sectionIndex;
3476
+ }
3477
+ if (block.kind === "paragraph" && lineNumbering) {
3478
+ const lineCount = estimateParagraphLineCount(block, columnWidth);
3479
+ const lineHeight = estimateParagraphLineHeight(block);
3480
+ const suppress = block.suppressLineNumbers === true;
3481
+ for (let lineIndex = 0; lineIndex < lineCount; lineIndex += 1) {
3482
+ if (!suppress && (lineNumber - restartStart) % countBy === 0) {
3483
+ markers.push({
3484
+ id: `${block.blockId}-${lineIndex}`,
3485
+ label: String(lineNumber),
3486
+ topPx: DOCUMENT_CONTENT_TOP_PADDING_PX + (currentTopTwips + lineIndex * lineHeight) * DEFAULT_PAGE_ESTIMATE_PX_PER_TWIP
3487
+ });
3488
+ }
3489
+ if (!suppress) {
3490
+ lineNumber += 1;
3491
+ }
3492
+ }
3493
+ }
3494
+ currentTopTwips += estimateBlockHeight(block, columnWidth);
3495
+ }
3496
+ return markers;
3497
+ }
3498
+ function shouldRenderPageBorder(pageLayout, pages, activePageIndex) {
3499
+ const display = pageLayout?.pageBorders?.display ?? "allPages";
3500
+ const activePage = pages[activePageIndex];
3501
+ if (!pageLayout?.pageBorders || !activePage) {
3502
+ return false;
3503
+ }
3504
+ switch (display) {
3505
+ case "firstPage":
3506
+ return activePage.pageInSection === 0;
3507
+ case "notFirstPage":
3508
+ return activePage.pageInSection > 0;
3509
+ default:
3510
+ return true;
3511
+ }
3512
+ }
3513
+ function buildPageBorderStyle(pageLayout) {
3514
+ const pageBorders = pageLayout.pageBorders;
3515
+ if (!pageBorders) {
3516
+ return void 0;
3517
+ }
3518
+ const leftInset = createInsetValue(
3519
+ pageBorders.left?.space,
3520
+ pageBorders.offsetFrom === "text" ? pageLayout.marginLeft / Math.max(1, pageLayout.pageWidth) * 100 : 1.25
3521
+ );
3522
+ const rightInset = createInsetValue(
3523
+ pageBorders.right?.space,
3524
+ pageBorders.offsetFrom === "text" ? pageLayout.marginRight / Math.max(1, pageLayout.pageWidth) * 100 : 1.25
3525
+ );
3526
+ const topInset = createInsetValue(
3527
+ pageBorders.top?.space,
3528
+ pageBorders.offsetFrom === "text" ? pageLayout.marginTop / Math.max(1, pageLayout.pageHeight) * 100 : 1.5
3529
+ );
3530
+ const bottomInset = createInsetValue(
3531
+ pageBorders.bottom?.space,
3532
+ pageBorders.offsetFrom === "text" ? pageLayout.marginBottom / Math.max(1, pageLayout.pageHeight) * 100 : 1.5
3533
+ );
3534
+ return {
3535
+ top: topInset,
3536
+ right: rightInset,
3537
+ bottom: bottomInset,
3538
+ left: leftInset,
3539
+ borderTop: toBorderCss(pageBorders.top),
3540
+ borderRight: toBorderCss(pageBorders.right),
3541
+ borderBottom: toBorderCss(pageBorders.bottom),
3542
+ borderLeft: toBorderCss(pageBorders.left),
3543
+ boxSizing: "border-box",
3544
+ mixBlendMode: pageBorders.zOrder === "back" ? "multiply" : void 0
3545
+ };
3546
+ }
3547
+ function buildDocumentGridStyle(documentGrid) {
3548
+ if (!documentGrid || !documentGrid.type || documentGrid.type === "default") {
3549
+ return void 0;
3550
+ }
3551
+ const linePitchPx = Math.max(
3552
+ 18,
3553
+ Math.round((documentGrid.linePitch ?? 360) * DEFAULT_PAGE_ESTIMATE_PX_PER_TWIP)
3554
+ );
3555
+ const charSpacePx = Math.max(
3556
+ 12,
3557
+ Math.round((documentGrid.charSpace ?? 204) * DEFAULT_PAGE_ESTIMATE_PX_PER_TWIP)
3558
+ );
3559
+ const gridColor = "rgba(15, 23, 42, 0.06)";
3560
+ const backgrounds = [];
3561
+ if (documentGrid.type === "lines" || documentGrid.type === "linesAndChars" || documentGrid.type === "snapToChars") {
3562
+ backgrounds.push(
3563
+ `repeating-linear-gradient(to bottom, ${gridColor} 0, ${gridColor} 1px, transparent 1px, transparent ${linePitchPx}px)`
3564
+ );
3565
+ }
3566
+ if (documentGrid.type === "linesAndChars" || documentGrid.type === "snapToChars") {
3567
+ backgrounds.push(
3568
+ `repeating-linear-gradient(to right, rgba(15, 23, 42, 0.04) 0, rgba(15, 23, 42, 0.04) 1px, transparent 1px, transparent ${charSpacePx}px)`
3569
+ );
3570
+ }
3571
+ if (backgrounds.length === 0) {
3572
+ return void 0;
3573
+ }
3574
+ return {
3575
+ backgroundImage: backgrounds.join(", "),
3576
+ backgroundOrigin: "content-box"
3577
+ };
3578
+ }
3579
+ function createInsetValue(spaceTwips, percent) {
3580
+ const spacingPx = Math.max(0, Math.round((spaceTwips ?? 0) * DEFAULT_PAGE_ESTIMATE_PX_PER_TWIP));
3581
+ return `calc(${percent.toFixed(2)}% + ${spacingPx}px)`;
3582
+ }
3583
+ function resolveSelectionToolbarPlacement(anchor, root, zoomScale) {
3584
+ if (!anchor || !root) {
3585
+ return null;
3586
+ }
3587
+ const rootRect = root.getBoundingClientRect();
3588
+ if (rootRect.width <= 0 || rootRect.height <= 0 || zoomScale <= 0) {
3589
+ return null;
3590
+ }
3591
+ const centerX = (anchor.left + anchor.right) / 2;
3592
+ const centerY = (anchor.top + anchor.bottom) / 2;
3593
+ const localLeftEdge = (anchor.left - rootRect.left) / zoomScale;
3594
+ const localRightEdge = (anchor.right - rootRect.left) / zoomScale;
3595
+ const localLeft = (centerX - rootRect.left) / zoomScale;
3596
+ const localCenterY = (centerY - rootRect.top) / zoomScale;
3597
+ const localTop = (anchor.top - rootRect.top) / zoomScale;
3598
+ const localBottom = (anchor.bottom - rootRect.top) / zoomScale;
3599
+ const edgePadding = 16 / zoomScale;
3600
+ const containerWidth = rootRect.width / zoomScale;
3601
+ const containerHeight = rootRect.height / zoomScale;
3602
+ const gapPx = 12 / zoomScale;
3603
+ const estimatedToolbarWidth = Math.min(260 / zoomScale, Math.max(168 / zoomScale, containerWidth * 0.32));
3604
+ const estimatedToolbarHeight = 44 / zoomScale;
3605
+ const clampedCenterLeft = Math.max(
3606
+ edgePadding,
3607
+ Math.min(localLeft, Math.max(edgePadding, containerWidth - edgePadding))
3608
+ );
3609
+ const clampedCenterY = Math.max(
3610
+ edgePadding + estimatedToolbarHeight / 2,
3611
+ Math.min(localCenterY, Math.max(edgePadding + estimatedToolbarHeight / 2, containerHeight - edgePadding - estimatedToolbarHeight / 2))
3612
+ );
3613
+ const rightClearance = containerWidth - localRightEdge - gapPx - edgePadding;
3614
+ const leftClearance = localLeftEdge - gapPx - edgePadding;
3615
+ if (rightClearance >= estimatedToolbarWidth) {
3616
+ return {
3617
+ placement: "right",
3618
+ style: {
3619
+ left: `${localRightEdge}px`,
3620
+ top: `${clampedCenterY}px`,
3621
+ maxWidth: `${Math.max(220, containerWidth - edgePadding * 2)}px`,
3622
+ transform: `translate(${gapPx}px, -50%)`
3623
+ }
3624
+ };
3625
+ }
3626
+ if (leftClearance >= estimatedToolbarWidth) {
3627
+ return {
3628
+ placement: "left",
3629
+ style: {
3630
+ left: `${localLeftEdge}px`,
3631
+ top: `${clampedCenterY}px`,
3632
+ maxWidth: `${Math.max(220, containerWidth - edgePadding * 2)}px`,
3633
+ transform: `translate(calc(-100% - ${gapPx}px), -50%)`
3634
+ }
3635
+ };
3636
+ }
3637
+ const placement = localTop < estimatedToolbarHeight + gapPx + edgePadding ? "below" : "above";
3638
+ return {
3639
+ placement,
3640
+ style: {
3641
+ left: `${clampedCenterLeft}px`,
3642
+ top: `${placement === "above" ? localTop : localBottom}px`,
3643
+ maxWidth: `${Math.max(220, containerWidth - edgePadding * 2)}px`,
3644
+ transform: placement === "above" ? `translate(-50%, calc(-100% - ${gapPx}px))` : `translate(-50%, ${gapPx}px)`
3645
+ }
3646
+ };
3647
+ }
3648
+ function toBorderCss(border) {
3649
+ if (!border || border.value === "none" || border.value === "nil") {
3650
+ return void 0;
3651
+ }
3652
+ const width = border.size ? `${Math.max(1, Math.round(border.size / 8))}px` : "1px";
3653
+ const style = border.value === "double" ? "double" : border.value === "dotted" ? "dotted" : border.value === "dashed" || border.value === "dashSmallGap" ? "dashed" : "solid";
3654
+ const color = border.color && border.color !== "auto" ? `#${border.color}` : "rgba(31, 31, 31, 0.28)";
3655
+ return `${width} ${style} ${color}`;
3656
+ }
3657
+
3658
+ // src/ui/headless/comment-decoration-model.ts
3659
+ function createCommentDecorationModel(snapshot) {
3660
+ if (!snapshot) {
3661
+ return void 0;
3662
+ }
3663
+ return {
3664
+ activeCommentId: snapshot.activeCommentId,
3665
+ threads: snapshot.threads.filter((thread) => thread.anchor.kind !== "detached").map((thread) => {
3666
+ const anchor = thread.anchor;
3667
+ const from = anchor.kind === "range" ? anchor.from : anchor.kind === "node" ? anchor.at : 0;
3668
+ const to = anchor.kind === "range" ? anchor.to : anchor.kind === "node" ? anchor.at : 0;
3669
+ return {
3670
+ commentId: thread.commentId,
3671
+ from,
3672
+ to,
3673
+ status: thread.status,
3674
+ isActive: thread.isActive
3675
+ };
3676
+ })
3677
+ };
3678
+ }
3679
+ function getCommentRangeState(model, from, to) {
3680
+ if (!model) {
3681
+ return {
3682
+ hasComments: false,
3683
+ hasOpen: false,
3684
+ hasResolved: false,
3685
+ hasActive: false,
3686
+ count: 0,
3687
+ overlapping: []
3688
+ };
3689
+ }
3690
+ const overlapping = model.threads.filter(
3691
+ (thread) => rangesOverlap(thread.from, thread.to, from, to)
3692
+ );
3693
+ return {
3694
+ hasComments: overlapping.length > 0,
3695
+ hasOpen: overlapping.some((thread) => thread.status === "open"),
3696
+ hasResolved: overlapping.some((thread) => thread.status === "resolved"),
3697
+ hasActive: overlapping.some((thread) => thread.isActive),
3698
+ count: overlapping.length,
3699
+ overlapping
3700
+ };
3701
+ }
3702
+ function getCommentHighlightClass(model, from, to, markupDisplay = "all") {
3703
+ const state = getCommentRangeState(model, from, to);
3704
+ if (!state.hasComments) {
3705
+ return "";
3706
+ }
3707
+ switch (markupDisplay) {
3708
+ case "clean":
3709
+ return state.hasActive ? "bg-comment-soft" : "";
3710
+ case "simple":
3711
+ if (state.hasActive) {
3712
+ return "underline decoration-comment decoration-2 underline-offset-4";
3713
+ }
3714
+ if (state.hasOpen) {
3715
+ return "underline decoration-comment/60 decoration-1 underline-offset-4";
3716
+ }
3717
+ return "underline decoration-comment/40 decoration-1 underline-offset-4";
3718
+ case "all":
3719
+ if (state.hasActive) {
3720
+ return "bg-comment-strong";
3721
+ }
3722
+ if (state.hasOpen) {
3723
+ return "bg-comment-soft";
3724
+ }
3725
+ return "bg-comment-soft opacity-60";
3726
+ }
3727
+ }
3728
+ function rangesOverlap(leftFrom, leftTo, rightFrom, rightTo) {
3729
+ const leftEnd = Math.max(leftFrom, leftTo);
3730
+ const rightEnd = Math.max(rightFrom, rightTo);
3731
+ return leftFrom < rightEnd && rightFrom < leftEnd;
3732
+ }
3733
+
3734
+ // src/ui/headless/revision-decoration-model.ts
3735
+ function createRevisionDecorationModel(snapshot, activeRevisionId) {
3736
+ if (!snapshot) {
3737
+ return void 0;
3738
+ }
3739
+ return {
3740
+ revisions: snapshot.revisions.filter((rev) => rev.anchor.kind !== "detached" && rev.status === "active").map((rev) => {
3741
+ const anchor = rev.anchor;
3742
+ const from = anchor.kind === "range" ? anchor.from : anchor.kind === "node" ? anchor.at : 0;
3743
+ const to = anchor.kind === "range" ? anchor.to : anchor.kind === "node" ? anchor.at : 0;
3744
+ return {
3745
+ revisionId: rev.revisionId,
3746
+ from,
3747
+ to,
3748
+ kind: rev.kind,
3749
+ status: rev.status,
3750
+ actionability: rev.actionability,
3751
+ isActive: rev.revisionId === activeRevisionId
3752
+ };
3753
+ })
3754
+ };
3755
+ }
3756
+ function getRevisionRangeState(model, from, to) {
3757
+ if (!model) {
3758
+ return {
3759
+ hasChanges: false,
3760
+ hasInsertions: false,
3761
+ hasDeletions: false,
3762
+ hasActive: false,
3763
+ count: 0,
3764
+ overlapping: []
3765
+ };
3766
+ }
3767
+ const overlapping = model.revisions.filter(
3768
+ (rev) => rangesOverlap(rev.from, rev.to, from, to)
3769
+ );
3770
+ return {
3771
+ hasChanges: overlapping.length > 0,
3772
+ hasInsertions: overlapping.some((rev) => rev.kind === "insertion"),
3773
+ hasDeletions: overlapping.some((rev) => rev.kind === "deletion"),
3774
+ hasActive: overlapping.some((rev) => rev.isActive),
3775
+ count: overlapping.length,
3776
+ overlapping
3777
+ };
3778
+ }
3779
+ function getRevisionHighlightClass(model, from, to, markupDisplay = "all") {
3780
+ const state = getRevisionRangeState(model, from, to);
3781
+ if (!state.hasChanges) {
3782
+ return "";
3783
+ }
3784
+ const activeRing = state.hasActive ? " ring-1 ring-accent/30" : "";
3785
+ switch (markupDisplay) {
3786
+ case "clean":
3787
+ return "";
3788
+ case "simple":
3789
+ if (state.hasInsertions) {
3790
+ return `underline decoration-insert/60 decoration-1 underline-offset-2 text-primary${activeRing}`;
3791
+ }
3792
+ if (state.hasDeletions) {
3793
+ return `text-secondary line-through decoration-danger/70 decoration-1${activeRing}`;
3794
+ }
3795
+ return activeRing;
3796
+ case "all":
3797
+ if (state.hasInsertions) {
3798
+ return `text-primary bg-insert-soft/80 ring-1 ring-insert/20${activeRing}`;
3799
+ }
3800
+ if (state.hasDeletions) {
3801
+ return `text-danger line-through decoration-danger/80 decoration-1 bg-delete-soft/70${activeRing}`;
3802
+ }
3803
+ return activeRing;
3804
+ }
3805
+ }
3806
+ function shouldHideInCleanMode(model, from, to) {
3807
+ const state = getRevisionRangeState(model, from, to);
3808
+ return state.hasDeletions;
3809
+ }
3810
+
3811
+ // src/ui/headless/selection-helpers.ts
3812
+ function createSelectionSnapshot(anchor, head = anchor) {
3813
+ const from = Math.min(anchor, head);
3814
+ const to = Math.max(anchor, head);
3815
+ return {
3816
+ anchor,
3817
+ head,
3818
+ isCollapsed: anchor === head,
3819
+ activeRange: {
3820
+ kind: "range",
3821
+ from,
3822
+ to,
3823
+ assoc: {
3824
+ start: -1,
3825
+ end: 1
3826
+ }
3827
+ }
3828
+ };
3829
+ }
3830
+ function createNodeSelectionSnapshot(at, assoc = 1) {
3831
+ return {
3832
+ anchor: at,
3833
+ head: at,
3834
+ isCollapsed: true,
3835
+ activeRange: {
3836
+ kind: "node",
3837
+ at,
3838
+ assoc
3839
+ }
3840
+ };
3841
+ }
3842
+ function selectionTouchesRange(selection, from, to) {
3843
+ if (selection.isCollapsed) {
3844
+ return false;
3845
+ }
3846
+ const selectionFrom = Math.min(selection.anchor, selection.head);
3847
+ const selectionTo = Math.max(selection.anchor, selection.head);
3848
+ return selectionFrom < to && from < selectionTo;
3849
+ }
3850
+
3851
+ export {
3852
+ deriveCapabilities,
3853
+ TwAlertBanner,
3854
+ TwSelectionToolbar,
3855
+ TwCommentSidebar,
3856
+ TwRevisionSidebar,
3857
+ TwReviewRail,
3858
+ TwStatusBar,
3859
+ TwHealthPanel,
3860
+ TwToolbarIconButton,
3861
+ TwToolbar,
3862
+ TwReviewWorkspace,
3863
+ createCommentDecorationModel,
3864
+ getCommentRangeState,
3865
+ getCommentHighlightClass,
3866
+ createRevisionDecorationModel,
3867
+ getRevisionRangeState,
3868
+ getRevisionHighlightClass,
3869
+ shouldHideInCleanMode,
3870
+ createSelectionSnapshot,
3871
+ createNodeSelectionSnapshot,
3872
+ selectionTouchesRange
3873
+ };
3874
+ //# sourceMappingURL=chunk-2TG72QSW.js.map