@dowel-ui/react 0.4.0 → 0.7.0

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 (335) hide show
  1. package/README.md +9 -5
  2. package/dist/blocks/admin-dashboard/meta.js +31 -0
  3. package/dist/blocks/admin-dashboard/meta.js.map +1 -0
  4. package/dist/blocks/agent-console/meta.js +27 -0
  5. package/dist/blocks/agent-console/meta.js.map +1 -0
  6. package/dist/blocks/ai-dashboard/meta.js +25 -0
  7. package/dist/blocks/ai-dashboard/meta.js.map +1 -0
  8. package/dist/blocks/ai-workspace/meta.js +33 -0
  9. package/dist/blocks/ai-workspace/meta.js.map +1 -0
  10. package/dist/blocks/analytics/meta.js +24 -0
  11. package/dist/blocks/analytics/meta.js.map +1 -0
  12. package/dist/blocks/billing/meta.js +25 -0
  13. package/dist/blocks/billing/meta.js.map +1 -0
  14. package/dist/blocks/command-center/meta.js +28 -0
  15. package/dist/blocks/command-center/meta.js.map +1 -0
  16. package/dist/blocks/crm/meta.js +32 -0
  17. package/dist/blocks/crm/meta.js.map +1 -0
  18. package/dist/blocks/onboarding/meta.js +23 -0
  19. package/dist/blocks/onboarding/meta.js.map +1 -0
  20. package/dist/components/accordion/accordion.js +1 -1
  21. package/dist/components/accordion/accordion.js.map +1 -1
  22. package/dist/components/activity-feed/activity-feed.js +1 -1
  23. package/dist/components/activity-feed/activity-feed.js.map +1 -1
  24. package/dist/components/ai-agent-plan/ai-agent-plan.js +1 -1
  25. package/dist/components/ai-agent-plan/ai-agent-plan.js.map +1 -1
  26. package/dist/components/ai-extraction-review/ai-extraction-review.d.ts +52 -0
  27. package/dist/components/ai-extraction-review/ai-extraction-review.d.ts.map +1 -0
  28. package/dist/components/ai-extraction-review/ai-extraction-review.js +249 -0
  29. package/dist/components/ai-extraction-review/ai-extraction-review.js.map +1 -0
  30. package/dist/components/ai-extraction-review/extraction-model.d.ts +94 -0
  31. package/dist/components/ai-extraction-review/extraction-model.d.ts.map +1 -0
  32. package/dist/components/ai-extraction-review/extraction-model.js +91 -0
  33. package/dist/components/ai-extraction-review/extraction-model.js.map +1 -0
  34. package/dist/components/ai-extraction-review/index.d.ts +3 -0
  35. package/dist/components/ai-extraction-review/index.js +3 -0
  36. package/dist/components/ai-extraction-review/meta.js +17 -0
  37. package/dist/components/ai-extraction-review/meta.js.map +1 -0
  38. package/dist/components/ai-prompt-input/ai-prompt-input.js +1 -1
  39. package/dist/components/ai-prompt-input/ai-prompt-input.js.map +1 -1
  40. package/dist/components/ai-reasoning/ai-reasoning.js +1 -1
  41. package/dist/components/ai-reasoning/ai-reasoning.js.map +1 -1
  42. package/dist/components/ai-response/ai-response.js +2 -2
  43. package/dist/components/ai-response/ai-response.js.map +1 -1
  44. package/dist/components/ai-suggested-value/ai-suggested-value.d.ts +72 -0
  45. package/dist/components/ai-suggested-value/ai-suggested-value.d.ts.map +1 -0
  46. package/dist/components/ai-suggested-value/ai-suggested-value.js +126 -0
  47. package/dist/components/ai-suggested-value/ai-suggested-value.js.map +1 -0
  48. package/dist/components/ai-suggested-value/index.d.ts +2 -0
  49. package/dist/components/ai-suggested-value/index.js +2 -0
  50. package/dist/components/ai-suggested-value/meta.js +17 -0
  51. package/dist/components/ai-suggested-value/meta.js.map +1 -0
  52. package/dist/components/ai-tool/ai-tool.js +2 -2
  53. package/dist/components/ai-tool/ai-tool.js.map +1 -1
  54. package/dist/components/breadcrumb/breadcrumb.d.ts +52 -0
  55. package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -0
  56. package/dist/components/breadcrumb/breadcrumb.js +141 -0
  57. package/dist/components/breadcrumb/breadcrumb.js.map +1 -0
  58. package/dist/components/breadcrumb/index.d.ts +2 -0
  59. package/dist/components/breadcrumb/index.js +2 -0
  60. package/dist/components/breadcrumb/meta.js +17 -0
  61. package/dist/components/breadcrumb/meta.js.map +1 -0
  62. package/dist/components/calendar/calendar.js +2 -2
  63. package/dist/components/calendar/calendar.js.map +1 -1
  64. package/dist/components/code-block/code-block.js +1 -1
  65. package/dist/components/code-block/code-block.js.map +1 -1
  66. package/dist/components/collapsible/collapsible.d.ts +30 -0
  67. package/dist/components/collapsible/collapsible.d.ts.map +1 -0
  68. package/dist/components/collapsible/collapsible.js +38 -0
  69. package/dist/components/collapsible/collapsible.js.map +1 -0
  70. package/dist/components/collapsible/index.d.ts +2 -0
  71. package/dist/components/collapsible/index.js +2 -0
  72. package/dist/components/collapsible/meta.js +17 -0
  73. package/dist/components/collapsible/meta.js.map +1 -0
  74. package/dist/components/combobox/combobox.js +2 -2
  75. package/dist/components/combobox/combobox.js.map +1 -1
  76. package/dist/components/command/command.js +1 -1
  77. package/dist/components/command/command.js.map +1 -1
  78. package/dist/components/confirm-typed/confirm-typed.d.ts +47 -0
  79. package/dist/components/confirm-typed/confirm-typed.d.ts.map +1 -0
  80. package/dist/components/confirm-typed/confirm-typed.js +106 -0
  81. package/dist/components/confirm-typed/confirm-typed.js.map +1 -0
  82. package/dist/components/confirm-typed/index.d.ts +2 -0
  83. package/dist/components/confirm-typed/index.js +2 -0
  84. package/dist/components/confirm-typed/meta.js +17 -0
  85. package/dist/components/confirm-typed/meta.js.map +1 -0
  86. package/dist/components/cron-editor/cron-editor.d.ts +36 -0
  87. package/dist/components/cron-editor/cron-editor.d.ts.map +1 -0
  88. package/dist/components/cron-editor/cron-editor.js +493 -0
  89. package/dist/components/cron-editor/cron-editor.js.map +1 -0
  90. package/dist/components/cron-editor/cron-expression.d.ts +82 -0
  91. package/dist/components/cron-editor/cron-expression.d.ts.map +1 -0
  92. package/dist/components/cron-editor/cron-expression.js +352 -0
  93. package/dist/components/cron-editor/cron-expression.js.map +1 -0
  94. package/dist/components/cron-editor/index.d.ts +3 -0
  95. package/dist/components/cron-editor/index.js +3 -0
  96. package/dist/components/cron-editor/meta.js +17 -0
  97. package/dist/components/cron-editor/meta.js.map +1 -0
  98. package/dist/components/data-table/data-table.d.ts.map +1 -1
  99. package/dist/components/data-table/data-table.js +4 -1
  100. package/dist/components/data-table/data-table.js.map +1 -1
  101. package/dist/components/dialog/dialog.js +2 -2
  102. package/dist/components/dialog/dialog.js.map +1 -1
  103. package/dist/components/diff-viewer/diff-viewer.js +4 -4
  104. package/dist/components/diff-viewer/diff-viewer.js.map +1 -1
  105. package/dist/components/direction/direction.d.ts +35 -0
  106. package/dist/components/direction/direction.d.ts.map +1 -0
  107. package/dist/components/direction/direction.js +14 -0
  108. package/dist/components/direction/direction.js.map +1 -0
  109. package/dist/components/direction/index.d.ts +2 -0
  110. package/dist/components/direction/index.js +2 -0
  111. package/dist/components/direction/meta.js +17 -0
  112. package/dist/components/direction/meta.js.map +1 -0
  113. package/dist/components/dns-record/dns-record.d.ts +61 -0
  114. package/dist/components/dns-record/dns-record.d.ts.map +1 -0
  115. package/dist/components/dns-record/dns-record.js +227 -0
  116. package/dist/components/dns-record/dns-record.js.map +1 -0
  117. package/dist/components/dns-record/index.d.ts +2 -0
  118. package/dist/components/dns-record/index.js +2 -0
  119. package/dist/components/dns-record/meta.js +17 -0
  120. package/dist/components/dns-record/meta.js.map +1 -0
  121. package/dist/components/drawer/drawer.js +1 -1
  122. package/dist/components/drawer/drawer.js.map +1 -1
  123. package/dist/components/dropdown-menu/dropdown-menu.d.ts.map +1 -1
  124. package/dist/components/dropdown-menu/dropdown-menu.js +9 -8
  125. package/dist/components/dropdown-menu/dropdown-menu.js.map +1 -1
  126. package/dist/components/input/input.js +1 -1
  127. package/dist/components/input/input.js.map +1 -1
  128. package/dist/components/log-viewer/log-viewer.js +2 -2
  129. package/dist/components/log-viewer/log-viewer.js.map +1 -1
  130. package/dist/components/meter/meter.js +1 -1
  131. package/dist/components/meter/meter.js.map +1 -1
  132. package/dist/components/pagination/pagination.d.ts.map +1 -1
  133. package/dist/components/pagination/pagination.js +3 -2
  134. package/dist/components/pagination/pagination.js.map +1 -1
  135. package/dist/components/permission-matrix/index.d.ts +3 -0
  136. package/dist/components/permission-matrix/index.js +3 -0
  137. package/dist/components/permission-matrix/meta.js +17 -0
  138. package/dist/components/permission-matrix/meta.js.map +1 -0
  139. package/dist/components/permission-matrix/permission-matrix.d.ts +46 -0
  140. package/dist/components/permission-matrix/permission-matrix.d.ts.map +1 -0
  141. package/dist/components/permission-matrix/permission-matrix.js +199 -0
  142. package/dist/components/permission-matrix/permission-matrix.js.map +1 -0
  143. package/dist/components/permission-matrix/permission-model.d.ts +53 -0
  144. package/dist/components/permission-matrix/permission-model.d.ts.map +1 -0
  145. package/dist/components/permission-matrix/permission-model.js +64 -0
  146. package/dist/components/permission-matrix/permission-model.js.map +1 -0
  147. package/dist/components/record-diff/record-diff.js +4 -4
  148. package/dist/components/record-diff/record-diff.js.map +1 -1
  149. package/dist/components/secret-field/index.d.ts +2 -0
  150. package/dist/components/secret-field/index.js +2 -0
  151. package/dist/components/secret-field/meta.js +17 -0
  152. package/dist/components/secret-field/meta.js.map +1 -0
  153. package/dist/components/secret-field/secret-field.d.ts +58 -0
  154. package/dist/components/secret-field/secret-field.d.ts.map +1 -0
  155. package/dist/components/secret-field/secret-field.js +168 -0
  156. package/dist/components/secret-field/secret-field.js.map +1 -0
  157. package/dist/components/select/select.js +2 -2
  158. package/dist/components/select/select.js.map +1 -1
  159. package/dist/components/session-expiry/index.d.ts +2 -0
  160. package/dist/components/session-expiry/index.js +2 -0
  161. package/dist/components/session-expiry/meta.js +17 -0
  162. package/dist/components/session-expiry/meta.js.map +1 -0
  163. package/dist/components/session-expiry/session-expiry.d.ts +64 -0
  164. package/dist/components/session-expiry/session-expiry.d.ts.map +1 -0
  165. package/dist/components/session-expiry/session-expiry.js +185 -0
  166. package/dist/components/session-expiry/session-expiry.js.map +1 -0
  167. package/dist/components/sheet/sheet.d.ts.map +1 -1
  168. package/dist/components/sheet/sheet.js +2 -2
  169. package/dist/components/sheet/sheet.js.map +1 -1
  170. package/dist/components/shortcut-recorder/index.d.ts +3 -0
  171. package/dist/components/shortcut-recorder/index.js +3 -0
  172. package/dist/components/shortcut-recorder/meta.js +17 -0
  173. package/dist/components/shortcut-recorder/meta.js.map +1 -0
  174. package/dist/components/shortcut-recorder/shortcut-model.d.ts +67 -0
  175. package/dist/components/shortcut-recorder/shortcut-model.d.ts.map +1 -0
  176. package/dist/components/shortcut-recorder/shortcut-model.js +168 -0
  177. package/dist/components/shortcut-recorder/shortcut-model.js.map +1 -0
  178. package/dist/components/shortcut-recorder/shortcut-recorder.d.ts +47 -0
  179. package/dist/components/shortcut-recorder/shortcut-recorder.d.ts.map +1 -0
  180. package/dist/components/shortcut-recorder/shortcut-recorder.js +212 -0
  181. package/dist/components/shortcut-recorder/shortcut-recorder.js.map +1 -0
  182. package/dist/components/sidebar/index.d.ts +2 -0
  183. package/dist/components/sidebar/index.js +2 -0
  184. package/dist/components/sidebar/meta.js +17 -0
  185. package/dist/components/sidebar/meta.js.map +1 -0
  186. package/dist/components/sidebar/sidebar.d.ts +69 -0
  187. package/dist/components/sidebar/sidebar.d.ts.map +1 -0
  188. package/dist/components/sidebar/sidebar.js +256 -0
  189. package/dist/components/sidebar/sidebar.js.map +1 -0
  190. package/dist/components/sync-status/index.d.ts +2 -0
  191. package/dist/components/sync-status/index.js +2 -0
  192. package/dist/components/sync-status/meta.js +17 -0
  193. package/dist/components/sync-status/meta.js.map +1 -0
  194. package/dist/components/sync-status/sync-status.d.ts +65 -0
  195. package/dist/components/sync-status/sync-status.d.ts.map +1 -0
  196. package/dist/components/sync-status/sync-status.js +103 -0
  197. package/dist/components/sync-status/sync-status.js.map +1 -0
  198. package/dist/components/table/table.js +2 -2
  199. package/dist/components/table/table.js.map +1 -1
  200. package/dist/components/tags-input/tags-input.js +1 -1
  201. package/dist/components/tags-input/tags-input.js.map +1 -1
  202. package/dist/components/textarea/index.d.ts +2 -0
  203. package/dist/components/textarea/index.js +2 -0
  204. package/dist/components/textarea/meta.js +17 -0
  205. package/dist/components/textarea/meta.js.map +1 -0
  206. package/dist/components/textarea/textarea.d.ts +43 -0
  207. package/dist/components/textarea/textarea.d.ts.map +1 -0
  208. package/dist/components/textarea/textarea.js +119 -0
  209. package/dist/components/textarea/textarea.js.map +1 -0
  210. package/dist/components/time-range-picker/time-range-picker.js +3 -3
  211. package/dist/components/time-range-picker/time-range-picker.js.map +1 -1
  212. package/dist/components/toast/toast.d.ts.map +1 -1
  213. package/dist/components/toast/toast.js +2 -2
  214. package/dist/components/toast/toast.js.map +1 -1
  215. package/dist/index.d.ts +36 -2
  216. package/dist/index.js +22 -3
  217. package/dist/lib/styles.d.ts +16 -1
  218. package/dist/lib/styles.d.ts.map +1 -1
  219. package/dist/lib/styles.js +16 -1
  220. package/dist/lib/styles.js.map +1 -1
  221. package/dist/registry/blocks.d.ts.map +1 -1
  222. package/dist/registry/blocks.js +27 -9
  223. package/dist/registry/blocks.js.map +1 -1
  224. package/dist/registry/components.d.ts.map +1 -1
  225. package/dist/registry/components.js +84 -54
  226. package/dist/registry/components.js.map +1 -1
  227. package/dist/registry/schema.d.ts +8 -0
  228. package/dist/registry/schema.d.ts.map +1 -1
  229. package/dist/registry/schema.js.map +1 -1
  230. package/package.json +5 -4
  231. package/src/components/accordion/accordion.tsx +1 -1
  232. package/src/components/activity-feed/activity-feed.tsx +1 -1
  233. package/src/components/ai-agent-plan/ai-agent-plan.tsx +0 -0
  234. package/src/components/ai-extraction-review/ai-extraction-review.tsx +492 -0
  235. package/src/components/ai-extraction-review/extraction-model.ts +159 -0
  236. package/src/components/ai-extraction-review/index.ts +13 -0
  237. package/src/components/ai-extraction-review/meta.ts +24 -0
  238. package/src/components/ai-prompt-input/ai-prompt-input.tsx +1 -1
  239. package/src/components/ai-reasoning/ai-reasoning.tsx +1 -1
  240. package/src/components/ai-response/ai-response.tsx +2 -2
  241. package/src/components/ai-suggested-value/ai-suggested-value.tsx +295 -0
  242. package/src/components/ai-suggested-value/index.ts +6 -0
  243. package/src/components/ai-suggested-value/meta.ts +24 -0
  244. package/src/components/ai-tool/ai-tool.tsx +2 -2
  245. package/src/components/breadcrumb/breadcrumb.tsx +157 -0
  246. package/src/components/breadcrumb/index.ts +10 -0
  247. package/src/components/breadcrumb/meta.ts +19 -0
  248. package/src/components/calendar/calendar.tsx +2 -2
  249. package/src/components/code-block/code-block.tsx +1 -1
  250. package/src/components/collapsible/collapsible.tsx +63 -0
  251. package/src/components/collapsible/index.ts +6 -0
  252. package/src/components/collapsible/meta.ts +16 -0
  253. package/src/components/combobox/combobox.tsx +2 -2
  254. package/src/components/command/command.tsx +1 -1
  255. package/src/components/confirm-typed/confirm-typed.tsx +191 -0
  256. package/src/components/confirm-typed/index.ts +1 -0
  257. package/src/components/confirm-typed/meta.ts +22 -0
  258. package/src/components/cron-editor/cron-editor.tsx +676 -0
  259. package/src/components/cron-editor/cron-expression.ts +552 -0
  260. package/src/components/cron-editor/index.ts +23 -0
  261. package/src/components/cron-editor/meta.ts +23 -0
  262. package/src/components/data-table/data-table.tsx +14 -3
  263. package/src/components/dialog/dialog.tsx +2 -2
  264. package/src/components/diff-viewer/diff-viewer.tsx +4 -4
  265. package/src/components/direction/direction.tsx +38 -0
  266. package/src/components/direction/index.ts +1 -0
  267. package/src/components/direction/meta.ts +19 -0
  268. package/src/components/dns-record/dns-record.tsx +315 -0
  269. package/src/components/dns-record/index.ts +7 -0
  270. package/src/components/dns-record/meta.ts +21 -0
  271. package/src/components/drawer/drawer.tsx +1 -1
  272. package/src/components/dropdown-menu/dropdown-menu.tsx +14 -8
  273. package/src/components/input/input.tsx +1 -1
  274. package/src/components/log-viewer/log-viewer.tsx +2 -2
  275. package/src/components/meter/meter.tsx +1 -1
  276. package/src/components/pagination/pagination.tsx +13 -2
  277. package/src/components/permission-matrix/index.ts +14 -0
  278. package/src/components/permission-matrix/meta.ts +23 -0
  279. package/src/components/permission-matrix/permission-matrix.tsx +368 -0
  280. package/src/components/permission-matrix/permission-model.ts +120 -0
  281. package/src/components/record-diff/record-diff.tsx +4 -4
  282. package/src/components/secret-field/index.ts +6 -0
  283. package/src/components/secret-field/meta.ts +21 -0
  284. package/src/components/secret-field/secret-field.tsx +284 -0
  285. package/src/components/select/select.tsx +2 -2
  286. package/src/components/session-expiry/index.ts +9 -0
  287. package/src/components/session-expiry/meta.ts +23 -0
  288. package/src/components/session-expiry/session-expiry.tsx +321 -0
  289. package/src/components/sheet/sheet.tsx +8 -4
  290. package/src/components/shortcut-recorder/index.ts +19 -0
  291. package/src/components/shortcut-recorder/meta.ts +22 -0
  292. package/src/components/shortcut-recorder/shortcut-model.ts +204 -0
  293. package/src/components/shortcut-recorder/shortcut-recorder.tsx +336 -0
  294. package/src/components/sidebar/index.ts +19 -0
  295. package/src/components/sidebar/meta.ts +25 -0
  296. package/src/components/sidebar/sidebar.tsx +374 -0
  297. package/src/components/sync-status/index.ts +9 -0
  298. package/src/components/sync-status/meta.ts +21 -0
  299. package/src/components/sync-status/sync-status.tsx +217 -0
  300. package/src/components/table/table.tsx +3 -3
  301. package/src/components/tags-input/tags-input.tsx +1 -1
  302. package/src/components/textarea/index.ts +1 -0
  303. package/src/components/textarea/meta.ts +21 -0
  304. package/src/components/textarea/textarea.tsx +210 -0
  305. package/src/components/time-range-picker/time-range-picker.tsx +3 -3
  306. package/src/components/toast/toast.tsx +14 -6
  307. package/src/index.ts +15 -0
  308. package/src/lib/styles.ts +16 -0
  309. package/src/registry/blocks.ts +18 -0
  310. package/src/registry/components.ts +30 -0
  311. package/src/registry/schema.ts +8 -0
  312. package/src/blocks/admin-users/admin-users.tsx +0 -251
  313. package/src/blocks/admin-users/index.ts +0 -1
  314. package/src/blocks/admin-users/meta.ts +0 -27
  315. package/src/blocks/ai-chat/ai-chat.tsx +0 -280
  316. package/src/blocks/ai-chat/index.ts +0 -7
  317. package/src/blocks/ai-chat/meta.ts +0 -32
  318. package/src/blocks/dashboard/dashboard.tsx +0 -192
  319. package/src/blocks/dashboard/index.ts +0 -6
  320. package/src/blocks/dashboard/meta.ts +0 -18
  321. package/src/blocks/forgot-password/forgot-password.tsx +0 -158
  322. package/src/blocks/forgot-password/index.ts +0 -1
  323. package/src/blocks/forgot-password/meta.ts +0 -18
  324. package/src/blocks/login/index.ts +0 -1
  325. package/src/blocks/login/login.tsx +0 -178
  326. package/src/blocks/login/meta.ts +0 -18
  327. package/src/blocks/pricing/index.ts +0 -1
  328. package/src/blocks/pricing/meta.ts +0 -18
  329. package/src/blocks/pricing/pricing.tsx +0 -176
  330. package/src/blocks/settings/index.ts +0 -6
  331. package/src/blocks/settings/meta.ts +0 -29
  332. package/src/blocks/settings/settings.tsx +0 -317
  333. package/src/blocks/signup/index.ts +0 -1
  334. package/src/blocks/signup/meta.ts +0 -18
  335. package/src/blocks/signup/signup.tsx +0 -240
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.js","names":["Dialog","DialogPrimitive"],"sources":["../../../src/components/dialog/dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A modal window that interrupts the user to gather a response.\n *\n * Focus is trapped while open, restored to the trigger on close, and the rest\n * of the page is hidden from assistive technology — all handled by the\n * underlying primitive rather than reimplemented here.\n */\nexport const Dialog = DialogPrimitive.Root;\nexport const DialogTrigger = DialogPrimitive.Trigger;\nexport const DialogPortal = DialogPrimitive.Portal;\nexport const DialogClose = DialogPrimitive.Close;\n\nexport type DialogOverlayProps = ComponentPropsWithRef<typeof DialogPrimitive.Overlay>;\n\nexport function DialogOverlay({ className, ...props }: DialogOverlayProps) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"fixed inset-0 z-[var(--z-overlay)] bg-overlay backdrop-blur-[2px]\",\n \"data-[state=closed]:animate-overlay-out data-[state=open]:animate-overlay-in\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface DialogContentProps extends ComponentPropsWithRef<\n typeof DialogPrimitive.Content\n> {\n /** Renders the built-in close button. Turn off to supply your own. */\n showCloseButton?: boolean;\n}\n\nexport function DialogContent({\n className,\n children,\n showCloseButton = true,\n ...props\n}: DialogContentProps) {\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"fixed top-1/2 left-1/2 z-[var(--z-modal)] w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2\",\n \"grid gap-4 rounded-xl border border-border bg-card p-6 text-card-foreground shadow-xl\",\n \"data-[state=closed]:animate-dialog-out data-[state=open]:animate-dialog-in\",\n className,\n )}\n {...props}\n >\n {children}\n {showCloseButton ? (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n aria-label=\"Close\"\n className={cn(\n \"absolute top-4 right-4 grid size-7 place-items-center rounded-md text-muted-foreground\",\n \"transition-colors duration-[var(--duration-fast)] hover:bg-accent hover:text-foreground\",\n \"[&_svg]:size-4\",\n focusRing,\n )}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"m6 6 12 12M18 6 6 18\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </DialogPrimitive.Close>\n ) : null}\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nexport function DialogHeader({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"flex flex-col gap-1.5 pr-8 text-left\", className)}\n {...props}\n />\n );\n}\n\nexport function DialogFooter({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogTitleProps = ComponentPropsWithRef<typeof DialogPrimitive.Title>;\n\nexport function DialogTitle({ className, ...props }: DialogTitleProps) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg leading-tight font-semibold tracking-tight\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogDescriptionProps = ComponentPropsWithRef<typeof DialogPrimitive.Description>;\n\nexport function DialogDescription({ className, ...props }: DialogDescriptionProps) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,MAAaA,WAASC,OAAgB;AACtC,MAAa,gBAAgBA,OAAgB;AAC7C,MAAa,eAAeA,OAAgB;AAC5C,MAAa,cAAcA,OAAgB;AAI3C,SAAgB,cAAc,EAAE,WAAW,GAAG,SAA6B;CACzE,OACE,oBAACA,OAAgB,SAAjB;EACE,aAAU;EACV,WAAW,GACT,qEACA,gFACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AASA,SAAgB,cAAc,EAC5B,WACA,UACA,kBAAkB,MAClB,GAAG,SACkB;CACrB,OACE,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,eAAD,CAAgB,CAAA,GAChB,qBAACA,OAAgB,SAAjB;EACE,aAAU;EACV,WAAW,GACT,4GACA,yFACA,8EACA,SACF;EACA,GAAI;EARN,UAAA,CAUG,UACA,kBACC,oBAACA,OAAgB,OAAjB;GACE,aAAU;GACV,cAAW;GACX,WAAW,GACT,0FACA,2FACA,kBACA,SACF;GAEA,UAAA,oBAAC,OAAD;IAAK,SAAQ;IAAY,MAAK;IAAO,eAAY;IAC/C,UAAA,oBAAC,QAAD;KACE,GAAE;KACF,QAAO;KACP,aAAY;KACZ,eAAc;IACf,CAAA;GACE,CAAA;EACgB,CAAA,IACrB,IACmB;CACb,CAAA,CAAA,EAAA,CAAA;AAElB;AAEA,SAAgB,aAAa,EAAE,WAAW,GAAG,SAAuC;CAClF,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,wCAAwC,SAAS;EAC/D,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,aAAa,EAAE,WAAW,GAAG,SAAuC;CAClF,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,0DAA0D,SAAS;EACjF,GAAI;CACL,CAAA;AAEL;AAIA,SAAgB,YAAY,EAAE,WAAW,GAAG,SAA2B;CACrE,OACE,oBAACA,OAAgB,OAAjB;EACE,aAAU;EACV,WAAW,GAAG,sDAAsD,SAAS;EAC7E,GAAI;CACL,CAAA;AAEL;AAIA,SAAgB,kBAAkB,EAAE,WAAW,GAAG,SAAiC;CACjF,OACE,oBAACA,OAAgB,aAAjB;EACE,aAAU;EACV,WAAW,GAAG,iCAAiC,SAAS;EACxD,GAAI;CACL,CAAA;AAEL"}
1
+ {"version":3,"file":"dialog.js","names":["Dialog","DialogPrimitive"],"sources":["../../../src/components/dialog/dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A modal window that interrupts the user to gather a response.\n *\n * Focus is trapped while open, restored to the trigger on close, and the rest\n * of the page is hidden from assistive technology — all handled by the\n * underlying primitive rather than reimplemented here.\n */\nexport const Dialog = DialogPrimitive.Root;\nexport const DialogTrigger = DialogPrimitive.Trigger;\nexport const DialogPortal = DialogPrimitive.Portal;\nexport const DialogClose = DialogPrimitive.Close;\n\nexport type DialogOverlayProps = ComponentPropsWithRef<typeof DialogPrimitive.Overlay>;\n\nexport function DialogOverlay({ className, ...props }: DialogOverlayProps) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"fixed inset-0 z-[var(--z-overlay)] bg-overlay backdrop-blur-[2px]\",\n \"data-[state=closed]:animate-overlay-out data-[state=open]:animate-overlay-in\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface DialogContentProps extends ComponentPropsWithRef<\n typeof DialogPrimitive.Content\n> {\n /** Renders the built-in close button. Turn off to supply your own. */\n showCloseButton?: boolean;\n}\n\nexport function DialogContent({\n className,\n children,\n showCloseButton = true,\n ...props\n}: DialogContentProps) {\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"fixed top-1/2 left-1/2 z-[var(--z-modal)] w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2\",\n \"grid gap-4 rounded-xl border border-border bg-card p-6 text-card-foreground shadow-xl\",\n \"data-[state=closed]:animate-dialog-out data-[state=open]:animate-dialog-in\",\n className,\n )}\n {...props}\n >\n {children}\n {showCloseButton ? (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n aria-label=\"Close\"\n className={cn(\n \"absolute end-4 top-4 grid size-7 place-items-center rounded-md text-muted-foreground\",\n \"transition-colors duration-[var(--duration-fast)] hover:bg-accent hover:text-foreground\",\n \"[&_svg]:size-4\",\n focusRing,\n )}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"m6 6 12 12M18 6 6 18\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </DialogPrimitive.Close>\n ) : null}\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nexport function DialogHeader({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"flex flex-col gap-1.5 pe-8 text-start\", className)}\n {...props}\n />\n );\n}\n\nexport function DialogFooter({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogTitleProps = ComponentPropsWithRef<typeof DialogPrimitive.Title>;\n\nexport function DialogTitle({ className, ...props }: DialogTitleProps) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg leading-tight font-semibold tracking-tight\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogDescriptionProps = ComponentPropsWithRef<typeof DialogPrimitive.Description>;\n\nexport function DialogDescription({ className, ...props }: DialogDescriptionProps) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,MAAaA,WAASC,OAAgB;AACtC,MAAa,gBAAgBA,OAAgB;AAC7C,MAAa,eAAeA,OAAgB;AAC5C,MAAa,cAAcA,OAAgB;AAI3C,SAAgB,cAAc,EAAE,WAAW,GAAG,SAA6B;CACzE,OACE,oBAACA,OAAgB,SAAjB;EACE,aAAU;EACV,WAAW,GACT,qEACA,gFACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AASA,SAAgB,cAAc,EAC5B,WACA,UACA,kBAAkB,MAClB,GAAG,SACkB;CACrB,OACE,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,eAAD,CAAgB,CAAA,GAChB,qBAACA,OAAgB,SAAjB;EACE,aAAU;EACV,WAAW,GACT,4GACA,yFACA,8EACA,SACF;EACA,GAAI;EARN,UAAA,CAUG,UACA,kBACC,oBAACA,OAAgB,OAAjB;GACE,aAAU;GACV,cAAW;GACX,WAAW,GACT,wFACA,2FACA,kBACA,SACF;GAEA,UAAA,oBAAC,OAAD;IAAK,SAAQ;IAAY,MAAK;IAAO,eAAY;IAC/C,UAAA,oBAAC,QAAD;KACE,GAAE;KACF,QAAO;KACP,aAAY;KACZ,eAAc;IACf,CAAA;GACE,CAAA;EACgB,CAAA,IACrB,IACmB;CACb,CAAA,CAAA,EAAA,CAAA;AAElB;AAEA,SAAgB,aAAa,EAAE,WAAW,GAAG,SAAuC;CAClF,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,yCAAyC,SAAS;EAChE,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,aAAa,EAAE,WAAW,GAAG,SAAuC;CAClF,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,0DAA0D,SAAS;EACjF,GAAI;CACL,CAAA;AAEL;AAIA,SAAgB,YAAY,EAAE,WAAW,GAAG,SAA2B;CACrE,OACE,oBAACA,OAAgB,OAAjB;EACE,aAAU;EACV,WAAW,GAAG,sDAAsD,SAAS;EAC7E,GAAI;CACL,CAAA;AAEL;AAIA,SAAgB,kBAAkB,EAAE,WAAW,GAAG,SAAiC;CACjF,OACE,oBAACA,OAAgB,aAAjB;EACE,aAAU;EACV,WAAW,GAAG,iCAAiC,SAAS;EACxD,GAAI;CACL,CAAA;AAEL"}
@@ -135,12 +135,12 @@ function UnifiedRow({ row }) {
135
135
  /* @__PURE__ */ jsx(LineNumber, { value: row.before }),
136
136
  /* @__PURE__ */ jsx(LineNumber, { value: row.after }),
137
137
  /* @__PURE__ */ jsx("td", {
138
- className: "w-4 pr-1 text-center text-muted-foreground select-none",
138
+ className: "w-4 pe-1 text-center text-muted-foreground select-none",
139
139
  "aria-hidden": "true",
140
140
  children: row.kind === "added" ? "+" : row.kind === "removed" ? "−" : ""
141
141
  }),
142
142
  /* @__PURE__ */ jsxs("td", {
143
- className: "w-full py-0.5 pr-3 break-all whitespace-pre-wrap",
143
+ className: "w-full py-0.5 pe-3 break-all whitespace-pre-wrap",
144
144
  children: [/* @__PURE__ */ jsxs("span", {
145
145
  className: "sr-only",
146
146
  children: [KIND_LABEL[row.kind], ": "]
@@ -158,7 +158,7 @@ function SplitCell({ row, side }) {
158
158
  className: "w-1/2 bg-muted/20"
159
159
  })] });
160
160
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(LineNumber, { value: side === "before" ? row.before : row.after }), /* @__PURE__ */ jsxs("td", {
161
- className: cn("w-1/2 py-0.5 pr-3 break-all whitespace-pre-wrap", ROW_STYLES[row.kind]),
161
+ className: cn("w-1/2 py-0.5 pe-3 break-all whitespace-pre-wrap", ROW_STYLES[row.kind]),
162
162
  children: [/* @__PURE__ */ jsxs("span", {
163
163
  className: "sr-only",
164
164
  children: [KIND_LABEL[row.kind], ": "]
@@ -175,7 +175,7 @@ function RowContent({ row }) {
175
175
  function LineNumber({ value }) {
176
176
  return /* @__PURE__ */ jsx("td", {
177
177
  "aria-hidden": "true",
178
- className: "w-10 border-r border-border bg-muted/40 px-2 py-0.5 text-right text-muted-foreground tabular-nums select-none",
178
+ className: "w-10 border-e border-border bg-muted/40 px-2 py-0.5 text-end text-muted-foreground tabular-nums select-none",
179
179
  children: value ?? ""
180
180
  });
181
181
  }
@@ -1 +1 @@
1
- {"version":3,"file":"diff-viewer.js","names":[],"sources":["../../../src/components/diff-viewer/diff-viewer.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo, type ComponentPropsWithRef, type ReactNode } from \"react\";\n\nimport { disabledStyles, focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\nimport { countChanges, toSplitRows, type DiffHunk, type DiffRow } from \"./diff-model\";\n\n/**\n * A diff, and a decision about it.\n *\n * The rendering half exists because every packaged viewer brings its own\n * styling system — emotion, or HTML strings plus a stylesheet — which design\n * tokens cannot reach and which is awkward under RSC. The deciding half exists\n * because Dowel is for AI products, and an agent proposing a change to a file is\n * the case that needs it: what every coding agent ships, and no component\n * library does.\n *\n * Decisions are controlled. The component never mutates the diff or applies\n * anything; it reports which hunks were accepted and rejected and leaves the\n * consequences to the application, the same way ai-action-ledger does.\n *\n * Not a grid. A code listing is a table of text, and role=\"grid\" would promise\n * cell-by-cell arrow navigation that neither exists here nor makes sense for\n * reading code. Line kind is carried in text, because a plus sign and a green\n * background are not information.\n */\n\nexport type HunkDecision = \"accepted\" | \"rejected\";\n\nconst KIND_LABEL: Record<DiffRow[\"kind\"], string> = {\n added: \"Added\",\n removed: \"Removed\",\n context: \"Unchanged\",\n};\n\nexport interface DiffViewerProps extends Omit<ComponentPropsWithRef<\"div\">, \"children\"> {\n hunks: DiffHunk[];\n /** Names the diff — usually the path of the file being changed. */\n label: string;\n view?: \"unified\" | \"split\";\n /** Per-hunk decisions, keyed by hunk id. Controlled. */\n decisions?: Record<string, HunkDecision>;\n onDecision?: (hunkId: string, decision: HunkDecision) => void;\n children?: ReactNode;\n}\n\nexport function DiffViewer({\n className,\n hunks,\n label,\n view = \"unified\",\n decisions,\n onDecision,\n children,\n ...props\n}: DiffViewerProps) {\n const counts = useMemo(() => countChanges(hunks), [hunks]);\n\n return (\n <div\n data-slot=\"diff-viewer\"\n data-view={view}\n className={cn(\"flex flex-col gap-2\", className)}\n {...props}\n >\n <div className=\"flex flex-wrap items-baseline justify-between gap-2\">\n <span className=\"font-mono text-sm font-medium\">{label}</span>\n {/* Said in words as well as coloured, and before the diff rather than\n after it: a reader deserves to know the size of the change before\n they start reading it. */}\n <span data-slot=\"diff-viewer-summary\" className=\"text-xs text-muted-foreground\">\n <span className=\"text-success\">+{counts.added}</span>{\" \"}\n <span className=\"text-destructive\">−{counts.removed}</span>\n <span className=\"sr-only\">\n {` — ${String(counts.added)} lines added, ${String(counts.removed)} removed`}\n </span>\n </span>\n </div>\n\n {children}\n\n {hunks.length === 0 ? (\n <p className=\"rounded-lg border border-border bg-muted/30 p-3 text-sm text-muted-foreground\">\n No changes.\n </p>\n ) : (\n <div className=\"overflow-hidden rounded-lg border border-border\">\n {hunks.map((hunk) => (\n <DiffHunkView\n key={hunk.id}\n hunk={hunk}\n label={label}\n view={view}\n decision={decisions?.[hunk.id]}\n onDecision={onDecision}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction DiffHunkView({\n hunk,\n label,\n view,\n decision,\n onDecision,\n}: {\n hunk: DiffHunk;\n label: string;\n view: \"unified\" | \"split\";\n decision?: HunkDecision;\n onDecision?: (hunkId: string, decision: HunkDecision) => void;\n}) {\n const splitRows = useMemo(\n () => (view === \"split\" ? toSplitRows(hunk.rows) : []),\n [view, hunk.rows],\n );\n\n return (\n <section\n data-slot=\"diff-hunk\"\n data-decision={decision}\n aria-label={`${label}, hunk ${hunk.id}`}\n className={cn(\n \"border-b border-border last:border-b-0\",\n decision === \"rejected\" && \"opacity-55\",\n )}\n >\n {hunk.skippedBefore > 0 ? (\n <p\n data-slot=\"diff-hunk-skipped\"\n className=\"border-b border-border bg-muted/40 px-3 py-1 font-mono text-2xs text-muted-foreground\"\n >\n {hunk.skippedBefore} unchanged {hunk.skippedBefore === 1 ? \"line\" : \"lines\"} hidden\n </p>\n ) : null}\n\n {onDecision ? (\n <div className=\"flex flex-wrap items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5\">\n <span className=\"flex-1 text-xs text-muted-foreground\">\n {decision === \"accepted\"\n ? \"Accepted\"\n : decision === \"rejected\"\n ? \"Rejected\"\n : \"Not decided\"}\n </span>\n <HunkButton\n pressed={decision === \"accepted\"}\n onClick={() => {\n onDecision(hunk.id, \"accepted\");\n }}\n >\n Accept\n </HunkButton>\n <HunkButton\n pressed={decision === \"rejected\"}\n onClick={() => {\n onDecision(hunk.id, \"rejected\");\n }}\n >\n Reject\n </HunkButton>\n </div>\n ) : null}\n\n <div className=\"overflow-x-auto\">\n <table data-slot=\"diff-table\" className=\"w-full border-collapse font-mono text-xs\">\n <tbody>\n {view === \"split\"\n ? splitRows.map((pair, index) => (\n <tr key={index} data-slot=\"diff-row\">\n <SplitCell row={pair.left} side=\"before\" />\n <SplitCell row={pair.right} side=\"after\" />\n </tr>\n ))\n : hunk.rows.map((row, index) => <UnifiedRow key={index} row={row} />)}\n </tbody>\n </table>\n </div>\n </section>\n );\n}\n\nconst ROW_STYLES: Record<DiffRow[\"kind\"], string> = {\n added: \"bg-success/10\",\n removed: \"bg-destructive/10\",\n context: \"\",\n};\n\nfunction UnifiedRow({ row }: { row: DiffRow }) {\n return (\n <tr data-slot=\"diff-row\" data-kind={row.kind} className={ROW_STYLES[row.kind]}>\n <LineNumber value={row.before} />\n <LineNumber value={row.after} />\n <td className=\"w-4 pr-1 text-center text-muted-foreground select-none\" aria-hidden=\"true\">\n {row.kind === \"added\" ? \"+\" : row.kind === \"removed\" ? \"−\" : \"\"}\n </td>\n <td className=\"w-full py-0.5 pr-3 break-all whitespace-pre-wrap\">\n {/* The kind, for anyone who cannot see the sign or the tint. Reading a\n diff aloud without it is reading the same file twice. */}\n <span className=\"sr-only\">{KIND_LABEL[row.kind]}: </span>\n <RowContent row={row} />\n </td>\n </tr>\n );\n}\n\nfunction SplitCell({ row, side }: { row: DiffRow | null; side: \"before\" | \"after\" }) {\n if (!row) {\n // An empty half of a pair, not a blank line of code. Hidden from assistive\n // technology so a reader is not read padding.\n return (\n <>\n <td aria-hidden=\"true\" className=\"w-10 bg-muted/40\" />\n <td aria-hidden=\"true\" className=\"w-1/2 bg-muted/20\" />\n </>\n );\n }\n\n return (\n <>\n <LineNumber value={side === \"before\" ? row.before : row.after} />\n <td\n className={cn(\"w-1/2 py-0.5 pr-3 break-all whitespace-pre-wrap\", ROW_STYLES[row.kind])}\n >\n <span className=\"sr-only\">{KIND_LABEL[row.kind]}: </span>\n <RowContent row={row} />\n </td>\n </>\n );\n}\n\nfunction RowContent({ row }: { row: DiffRow }) {\n if (!row.segments) return <>{row.content}</>;\n\n return (\n <>\n {row.segments.map((segment, index) =>\n segment.changed ? (\n // A mark element, so the changed words survive as structure rather\n // than existing only as a slightly stronger background.\n <mark\n key={index}\n className={cn(\n \"rounded-[2px] text-inherit\",\n row.kind === \"added\" ? \"bg-success/30\" : \"bg-destructive/30\",\n )}\n >\n {segment.text}\n </mark>\n ) : (\n <span key={index}>{segment.text}</span>\n ),\n )}\n </>\n );\n}\n\nfunction LineNumber({ value }: { value?: number }) {\n return (\n <td\n // Decorative: the line number is orientation for a sighted reader, and\n // announcing two numbers before every line makes the diff unlistenable.\n aria-hidden=\"true\"\n className=\"w-10 border-r border-border bg-muted/40 px-2 py-0.5 text-right text-muted-foreground tabular-nums select-none\"\n >\n {value ?? \"\"}\n </td>\n );\n}\n\nfunction HunkButton({\n className,\n pressed,\n ...props\n}: ComponentPropsWithRef<\"button\"> & { pressed: boolean }) {\n return (\n <button\n type=\"button\"\n aria-pressed={pressed}\n className={cn(\n \"rounded-md border px-2 py-0.5 text-xs font-medium transition-colors\",\n pressed\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n focusRing,\n disabledStyles,\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Switches between unified and side-by-side. */\nexport function DiffViewerToolbar({\n className,\n view,\n onViewChange,\n ...props\n}: Omit<ComponentPropsWithRef<\"div\">, \"onChange\"> & {\n view: \"unified\" | \"split\";\n onViewChange: (view: \"unified\" | \"split\") => void;\n}) {\n return (\n <div\n data-slot=\"diff-viewer-toolbar\"\n role=\"group\"\n aria-label=\"Diff layout\"\n className={cn(\"flex items-center gap-1\", className)}\n {...props}\n >\n {([\"unified\", \"split\"] as const).map((option) => (\n <button\n key={option}\n type=\"button\"\n aria-pressed={view === option}\n onClick={() => {\n onViewChange(option);\n }}\n className={cn(\n \"rounded-md border px-2 py-0.5 text-xs font-medium capitalize transition-colors\",\n view === option\n ? \"border-transparent bg-secondary text-secondary-foreground\"\n : \"border-input bg-background text-muted-foreground hover:text-foreground\",\n focusRing,\n )}\n >\n {option}\n </button>\n ))}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"diff-viewer.js","names":[],"sources":["../../../src/components/diff-viewer/diff-viewer.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo, type ComponentPropsWithRef, type ReactNode } from \"react\";\n\nimport { disabledStyles, focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\nimport { countChanges, toSplitRows, type DiffHunk, type DiffRow } from \"./diff-model\";\n\n/**\n * A diff, and a decision about it.\n *\n * The rendering half exists because every packaged viewer brings its own\n * styling system — emotion, or HTML strings plus a stylesheet — which design\n * tokens cannot reach and which is awkward under RSC. The deciding half exists\n * because Dowel is for AI products, and an agent proposing a change to a file is\n * the case that needs it: what every coding agent ships, and no component\n * library does.\n *\n * Decisions are controlled. The component never mutates the diff or applies\n * anything; it reports which hunks were accepted and rejected and leaves the\n * consequences to the application, the same way ai-action-ledger does.\n *\n * Not a grid. A code listing is a table of text, and role=\"grid\" would promise\n * cell-by-cell arrow navigation that neither exists here nor makes sense for\n * reading code. Line kind is carried in text, because a plus sign and a green\n * background are not information.\n */\n\nexport type HunkDecision = \"accepted\" | \"rejected\";\n\nconst KIND_LABEL: Record<DiffRow[\"kind\"], string> = {\n added: \"Added\",\n removed: \"Removed\",\n context: \"Unchanged\",\n};\n\nexport interface DiffViewerProps extends Omit<ComponentPropsWithRef<\"div\">, \"children\"> {\n hunks: DiffHunk[];\n /** Names the diff — usually the path of the file being changed. */\n label: string;\n view?: \"unified\" | \"split\";\n /** Per-hunk decisions, keyed by hunk id. Controlled. */\n decisions?: Record<string, HunkDecision>;\n onDecision?: (hunkId: string, decision: HunkDecision) => void;\n children?: ReactNode;\n}\n\nexport function DiffViewer({\n className,\n hunks,\n label,\n view = \"unified\",\n decisions,\n onDecision,\n children,\n ...props\n}: DiffViewerProps) {\n const counts = useMemo(() => countChanges(hunks), [hunks]);\n\n return (\n <div\n data-slot=\"diff-viewer\"\n data-view={view}\n className={cn(\"flex flex-col gap-2\", className)}\n {...props}\n >\n <div className=\"flex flex-wrap items-baseline justify-between gap-2\">\n <span className=\"font-mono text-sm font-medium\">{label}</span>\n {/* Said in words as well as coloured, and before the diff rather than\n after it: a reader deserves to know the size of the change before\n they start reading it. */}\n <span data-slot=\"diff-viewer-summary\" className=\"text-xs text-muted-foreground\">\n <span className=\"text-success\">+{counts.added}</span>{\" \"}\n <span className=\"text-destructive\">−{counts.removed}</span>\n <span className=\"sr-only\">\n {` — ${String(counts.added)} lines added, ${String(counts.removed)} removed`}\n </span>\n </span>\n </div>\n\n {children}\n\n {hunks.length === 0 ? (\n <p className=\"rounded-lg border border-border bg-muted/30 p-3 text-sm text-muted-foreground\">\n No changes.\n </p>\n ) : (\n <div className=\"overflow-hidden rounded-lg border border-border\">\n {hunks.map((hunk) => (\n <DiffHunkView\n key={hunk.id}\n hunk={hunk}\n label={label}\n view={view}\n decision={decisions?.[hunk.id]}\n onDecision={onDecision}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction DiffHunkView({\n hunk,\n label,\n view,\n decision,\n onDecision,\n}: {\n hunk: DiffHunk;\n label: string;\n view: \"unified\" | \"split\";\n decision?: HunkDecision;\n onDecision?: (hunkId: string, decision: HunkDecision) => void;\n}) {\n const splitRows = useMemo(\n () => (view === \"split\" ? toSplitRows(hunk.rows) : []),\n [view, hunk.rows],\n );\n\n return (\n <section\n data-slot=\"diff-hunk\"\n data-decision={decision}\n aria-label={`${label}, hunk ${hunk.id}`}\n className={cn(\n \"border-b border-border last:border-b-0\",\n decision === \"rejected\" && \"opacity-55\",\n )}\n >\n {hunk.skippedBefore > 0 ? (\n <p\n data-slot=\"diff-hunk-skipped\"\n className=\"border-b border-border bg-muted/40 px-3 py-1 font-mono text-2xs text-muted-foreground\"\n >\n {hunk.skippedBefore} unchanged {hunk.skippedBefore === 1 ? \"line\" : \"lines\"} hidden\n </p>\n ) : null}\n\n {onDecision ? (\n <div className=\"flex flex-wrap items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5\">\n <span className=\"flex-1 text-xs text-muted-foreground\">\n {decision === \"accepted\"\n ? \"Accepted\"\n : decision === \"rejected\"\n ? \"Rejected\"\n : \"Not decided\"}\n </span>\n <HunkButton\n pressed={decision === \"accepted\"}\n onClick={() => {\n onDecision(hunk.id, \"accepted\");\n }}\n >\n Accept\n </HunkButton>\n <HunkButton\n pressed={decision === \"rejected\"}\n onClick={() => {\n onDecision(hunk.id, \"rejected\");\n }}\n >\n Reject\n </HunkButton>\n </div>\n ) : null}\n\n <div className=\"overflow-x-auto\">\n <table data-slot=\"diff-table\" className=\"w-full border-collapse font-mono text-xs\">\n <tbody>\n {view === \"split\"\n ? splitRows.map((pair, index) => (\n <tr key={index} data-slot=\"diff-row\">\n <SplitCell row={pair.left} side=\"before\" />\n <SplitCell row={pair.right} side=\"after\" />\n </tr>\n ))\n : hunk.rows.map((row, index) => <UnifiedRow key={index} row={row} />)}\n </tbody>\n </table>\n </div>\n </section>\n );\n}\n\nconst ROW_STYLES: Record<DiffRow[\"kind\"], string> = {\n added: \"bg-success/10\",\n removed: \"bg-destructive/10\",\n context: \"\",\n};\n\nfunction UnifiedRow({ row }: { row: DiffRow }) {\n return (\n <tr data-slot=\"diff-row\" data-kind={row.kind} className={ROW_STYLES[row.kind]}>\n <LineNumber value={row.before} />\n <LineNumber value={row.after} />\n <td className=\"w-4 pe-1 text-center text-muted-foreground select-none\" aria-hidden=\"true\">\n {row.kind === \"added\" ? \"+\" : row.kind === \"removed\" ? \"−\" : \"\"}\n </td>\n <td className=\"w-full py-0.5 pe-3 break-all whitespace-pre-wrap\">\n {/* The kind, for anyone who cannot see the sign or the tint. Reading a\n diff aloud without it is reading the same file twice. */}\n <span className=\"sr-only\">{KIND_LABEL[row.kind]}: </span>\n <RowContent row={row} />\n </td>\n </tr>\n );\n}\n\nfunction SplitCell({ row, side }: { row: DiffRow | null; side: \"before\" | \"after\" }) {\n if (!row) {\n // An empty half of a pair, not a blank line of code. Hidden from assistive\n // technology so a reader is not read padding.\n return (\n <>\n <td aria-hidden=\"true\" className=\"w-10 bg-muted/40\" />\n <td aria-hidden=\"true\" className=\"w-1/2 bg-muted/20\" />\n </>\n );\n }\n\n return (\n <>\n <LineNumber value={side === \"before\" ? row.before : row.after} />\n <td\n className={cn(\"w-1/2 py-0.5 pe-3 break-all whitespace-pre-wrap\", ROW_STYLES[row.kind])}\n >\n <span className=\"sr-only\">{KIND_LABEL[row.kind]}: </span>\n <RowContent row={row} />\n </td>\n </>\n );\n}\n\nfunction RowContent({ row }: { row: DiffRow }) {\n if (!row.segments) return <>{row.content}</>;\n\n return (\n <>\n {row.segments.map((segment, index) =>\n segment.changed ? (\n // A mark element, so the changed words survive as structure rather\n // than existing only as a slightly stronger background.\n <mark\n key={index}\n className={cn(\n \"rounded-[2px] text-inherit\",\n row.kind === \"added\" ? \"bg-success/30\" : \"bg-destructive/30\",\n )}\n >\n {segment.text}\n </mark>\n ) : (\n <span key={index}>{segment.text}</span>\n ),\n )}\n </>\n );\n}\n\nfunction LineNumber({ value }: { value?: number }) {\n return (\n <td\n // Decorative: the line number is orientation for a sighted reader, and\n // announcing two numbers before every line makes the diff unlistenable.\n aria-hidden=\"true\"\n className=\"w-10 border-e border-border bg-muted/40 px-2 py-0.5 text-end text-muted-foreground tabular-nums select-none\"\n >\n {value ?? \"\"}\n </td>\n );\n}\n\nfunction HunkButton({\n className,\n pressed,\n ...props\n}: ComponentPropsWithRef<\"button\"> & { pressed: boolean }) {\n return (\n <button\n type=\"button\"\n aria-pressed={pressed}\n className={cn(\n \"rounded-md border px-2 py-0.5 text-xs font-medium transition-colors\",\n pressed\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n focusRing,\n disabledStyles,\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Switches between unified and side-by-side. */\nexport function DiffViewerToolbar({\n className,\n view,\n onViewChange,\n ...props\n}: Omit<ComponentPropsWithRef<\"div\">, \"onChange\"> & {\n view: \"unified\" | \"split\";\n onViewChange: (view: \"unified\" | \"split\") => void;\n}) {\n return (\n <div\n data-slot=\"diff-viewer-toolbar\"\n role=\"group\"\n aria-label=\"Diff layout\"\n className={cn(\"flex items-center gap-1\", className)}\n {...props}\n >\n {([\"unified\", \"split\"] as const).map((option) => (\n <button\n key={option}\n type=\"button\"\n aria-pressed={view === option}\n onClick={() => {\n onViewChange(option);\n }}\n className={cn(\n \"rounded-md border px-2 py-0.5 text-xs font-medium capitalize transition-colors\",\n view === option\n ? \"border-transparent bg-secondary text-secondary-foreground\"\n : \"border-input bg-background text-muted-foreground hover:text-foreground\",\n focusRing,\n )}\n >\n {option}\n </button>\n ))}\n </div>\n );\n}\n"],"mappings":";;;;;;;AA+BA,MAAM,aAA8C;CAClD,OAAO;CACP,SAAS;CACT,SAAS;AACX;AAaA,SAAgB,WAAW,EACzB,WACA,OACA,OACA,OAAO,WACP,WACA,YACA,UACA,GAAG,SACe;CAClB,MAAM,SAAS,cAAc,aAAa,KAAK,GAAG,CAAC,KAAK,CAAC;CAEzD,OACE,qBAAC,OAAD;EACE,aAAU;EACV,aAAW;EACX,WAAW,GAAG,uBAAuB,SAAS;EAC9C,GAAI;EAJN,UAAA;GAME,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,oBAAC,QAAD;KAAM,WAAU;KAAiC,UAAA;IAAY,CAAA,GAI7D,qBAAC,QAAD;KAAM,aAAU;KAAsB,WAAU;KAAhD,UAAA;MACE,qBAAC,QAAD;OAAM,WAAU;OAAhB,UAAA,CAA+B,KAAE,OAAO,KAAY;;MAAE;MACtD,qBAAC,QAAD;OAAM,WAAU;OAAhB,UAAA,CAAmC,KAAE,OAAO,OAAc;;MAC1D,oBAAC,QAAD;OAAM,WAAU;OACb,UAAA,MAAM,OAAO,OAAO,KAAK,EAAE,gBAAgB,OAAO,OAAO,OAAO,EAAE;MAC/D,CAAA;KACF;IACH,CAAA,CAAA;;GAEJ;GAEA,MAAM,WAAW,IAChB,oBAAC,KAAD;IAAG,WAAU;IAAgF,UAAA;GAE1F,CAAA,IAEH,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,MAAM,KAAK,SACV,oBAAC,cAAD;KAEQ;KACC;KACD;KACN,UAAU,YAAY,KAAK;KACf;IACb,GANM,KAAK,EAMX,CACF;GACE,CAAA;EAEJ;;AAET;AAEA,SAAS,aAAa,EACpB,MACA,OACA,MACA,UACA,cAOC;CACD,MAAM,YAAY,cACT,SAAS,UAAU,YAAY,KAAK,IAAI,IAAI,CAAC,GACpD,CAAC,MAAM,KAAK,IAAI,CAClB;CAEA,OACE,qBAAC,WAAD;EACE,aAAU;EACV,iBAAe;EACf,cAAY,GAAG,MAAM,SAAS,KAAK;EACnC,WAAW,GACT,0CACA,aAAa,cAAc,YAC7B;EAPF,UAAA;GASG,KAAK,gBAAgB,IACpB,qBAAC,KAAD;IACE,aAAU;IACV,WAAU;IAFZ,UAAA;KAIG,KAAK;KAAc;KAAY,KAAK,kBAAkB,IAAI,SAAS;KAAQ;IAC3E;GACD,CAAA,IAAA;GAEH,aACC,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,oBAAC,QAAD;MAAM,WAAU;MACb,UAAA,aAAa,aACV,aACA,aAAa,aACX,aACA;KACF,CAAA;KACN,oBAAC,YAAD;MACE,SAAS,aAAa;MACtB,eAAe;OACb,WAAW,KAAK,IAAI,UAAU;MAChC;MACD,UAAA;KAEW,CAAA;KACZ,oBAAC,YAAD;MACE,SAAS,aAAa;MACtB,eAAe;OACb,WAAW,KAAK,IAAI,UAAU;MAChC;MACD,UAAA;KAEW,CAAA;IACT;GACH,CAAA,IAAA;GAEJ,oBAAC,OAAD;IAAK,WAAU;IACb,UAAA,oBAAC,SAAD;KAAO,aAAU;KAAa,WAAU;KACtC,UAAA,oBAAC,SAAD,EAAA,UACG,SAAS,UACN,UAAU,KAAK,MAAM,UACnB,qBAAC,MAAD;MAAgB,aAAU;MAA1B,UAAA,CACE,oBAAC,WAAD;OAAW,KAAK,KAAK;OAAM,MAAK;MAAU,CAAA,GAC1C,oBAAC,WAAD;OAAW,KAAK,KAAK;OAAO,MAAK;MAAS,CAAA,CACxC;KAHK,GAAA,KAGL,CACL,IACD,KAAK,KAAK,KAAK,KAAK,UAAU,oBAAC,YAAD,EAA6B,IAAM,GAAlB,KAAkB,CAAC,EACjE,CAAA;IACF,CAAA;GACJ,CAAA;EACE;;AAEb;AAEA,MAAM,aAA8C;CAClD,OAAO;CACP,SAAS;CACT,SAAS;AACX;AAEA,SAAS,WAAW,EAAE,OAAyB;CAC7C,OACE,qBAAC,MAAD;EAAI,aAAU;EAAW,aAAW,IAAI;EAAM,WAAW,WAAW,IAAI;EAAxE,UAAA;GACE,oBAAC,YAAD,EAAY,OAAO,IAAI,OAAS,CAAA;GAChC,oBAAC,YAAD,EAAY,OAAO,IAAI,MAAQ,CAAA;GAC/B,oBAAC,MAAD;IAAI,WAAU;IAAyD,eAAY;IAChF,UAAA,IAAI,SAAS,UAAU,MAAM,IAAI,SAAS,YAAY,MAAM;GAC3D,CAAA;GACJ,qBAAC,MAAD;IAAI,WAAU;IAAd,UAAA,CAGE,qBAAC,QAAD;KAAM,WAAU;KAAhB,UAAA,CAA2B,WAAW,IAAI,OAAM,IAAQ;IACxD,CAAA,GAAA,oBAAC,YAAD,EAAiB,IAAM,CAAA,CACrB;;EACF;;AAER;AAEA,SAAS,UAAU,EAAE,KAAK,QAA2D;CACnF,IAAI,CAAC,KAGH,OACE,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,MAAD;EAAI,eAAY;EAAO,WAAU;CAAoB,CAAA,GACrD,oBAAC,MAAD;EAAI,eAAY;EAAO,WAAU;CAAqB,CAAA,CACtD,EAAA,CAAA;CAIN,OACE,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,YAAD,EAAY,OAAO,SAAS,WAAW,IAAI,SAAS,IAAI,MAAQ,CAAA,GAChE,qBAAC,MAAD;EACE,WAAW,GAAG,mDAAmD,WAAW,IAAI,KAAK;EADvF,UAAA,CAGE,qBAAC,QAAD;GAAM,WAAU;GAAhB,UAAA,CAA2B,WAAW,IAAI,OAAM,IAAQ;EACxD,CAAA,GAAA,oBAAC,YAAD,EAAiB,IAAM,CAAA,CACrB;CACJ,CAAA,CAAA,EAAA,CAAA;AAEN;AAEA,SAAS,WAAW,EAAE,OAAyB;CAC7C,IAAI,CAAC,IAAI,UAAU,OAAO,oBAAA,UAAA,EAAA,UAAG,IAAI,QAAU,CAAA;CAE3C,OACE,oBAAA,UAAA,EAAA,UACG,IAAI,SAAS,KAAK,SAAS,UAC1B,QAAQ,UAGN,oBAAC,QAAD;EAEE,WAAW,GACT,8BACA,IAAI,SAAS,UAAU,kBAAkB,mBAC3C;EAEC,UAAA,QAAQ;CACL,GAPC,KAOD,IAEN,oBAAC,QAAD,EAAA,UAAmB,QAAQ,KAAW,GAA3B,KAA2B,CAE1C,EACA,CAAA;AAEN;AAEA,SAAS,WAAW,EAAE,SAA6B;CACjD,OACE,oBAAC,MAAD;EAGE,eAAY;EACZ,WAAU;EAET,UAAA,SAAS;CACR,CAAA;AAER;AAEA,SAAS,WAAW,EAClB,WACA,SACA,GAAG,SACsD;CACzD,OACE,oBAAC,UAAD;EACE,MAAK;EACL,gBAAc;EACd,WAAW,GACT,uEACA,UACI,sDACA,2EACJ,WACA,gBACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;;AAGA,SAAgB,kBAAkB,EAChC,WACA,MACA,cACA,GAAG,SAIF;CACD,OACE,oBAAC,OAAD;EACE,aAAU;EACV,MAAK;EACL,cAAW;EACX,WAAW,GAAG,2BAA2B,SAAS;EAClD,GAAI;EAEF,UAAA,CAAC,WAAW,OAAO,CAAC,CAAW,KAAK,WACpC,oBAAC,UAAD;GAEE,MAAK;GACL,gBAAc,SAAS;GACvB,eAAe;IACb,aAAa,MAAM;GACrB;GACA,WAAW,GACT,kFACA,SAAS,SACL,8DACA,0EACJ,SACF;GAEC,UAAA;EACK,GAfD,MAeC,CACT;CACE,CAAA;AAET"}
@@ -0,0 +1,35 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/components/direction/direction.d.ts
3
+ /**
4
+ * Tells the component set which way the writing runs.
5
+ *
6
+ * Every direction-dependent style in this library is written logically —
7
+ * `ps-`, `me-`, `text-end` — so the CSS follows `dir` on its own. The
8
+ * primitives underneath do not: several of them read direction from React
9
+ * context rather than from the document, and with no provider they assume
10
+ * left-to-right and set `dir="ltr"` on their own elements. The result is a page
11
+ * that mirrors correctly except for its menus, selects and sliders, which is
12
+ * worse than one that does not mirror at all, because it looks deliberate.
13
+ *
14
+ * So an RTL application needs both: `dir` on the document for the CSS, and this
15
+ * for the primitives.
16
+ *
17
+ * ```tsx
18
+ * <html dir="rtl" lang="ar">
19
+ * <body>
20
+ * <DirectionProvider dir="rtl">{children}</DirectionProvider>
21
+ * </body>
22
+ * </html>
23
+ * ```
24
+ *
25
+ * A left-to-right application needs neither. Nothing here is required to render
26
+ * English correctly, which is why it is easy to ship without it and never know.
27
+ */
28
+ interface DirectionProviderProps {
29
+ dir: "ltr" | "rtl";
30
+ children?: ReactNode;
31
+ }
32
+ declare function DirectionProvider({ dir, children }: DirectionProviderProps): import("react").JSX.Element;
33
+ //#endregion
34
+ export { DirectionProvider, DirectionProviderProps };
35
+ //# sourceMappingURL=direction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"direction.d.ts","names":[],"sources":["../../../src/components/direction/direction.tsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;UA8BiB;EACf;EACA,WAAW;;iBAGG,oBAAoB,KAAK,YAAY,yCAAsB,IAAA"}
@@ -0,0 +1,14 @@
1
+ "use client";
2
+ import { Direction } from "radix-ui";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region src/components/direction/direction.tsx
5
+ function DirectionProvider({ dir, children }) {
6
+ return /* @__PURE__ */ jsx(Direction.Provider, {
7
+ dir,
8
+ children
9
+ });
10
+ }
11
+ //#endregion
12
+ export { DirectionProvider };
13
+
14
+ //# sourceMappingURL=direction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"direction.js","names":["DirectionPrimitive"],"sources":["../../../src/components/direction/direction.tsx"],"sourcesContent":["\"use client\";\n\nimport { Direction as DirectionPrimitive } from \"radix-ui\";\nimport type { ReactNode } from \"react\";\n\n/**\n * Tells the component set which way the writing runs.\n *\n * Every direction-dependent style in this library is written logically —\n * `ps-`, `me-`, `text-end` — so the CSS follows `dir` on its own. The\n * primitives underneath do not: several of them read direction from React\n * context rather than from the document, and with no provider they assume\n * left-to-right and set `dir=\"ltr\"` on their own elements. The result is a page\n * that mirrors correctly except for its menus, selects and sliders, which is\n * worse than one that does not mirror at all, because it looks deliberate.\n *\n * So an RTL application needs both: `dir` on the document for the CSS, and this\n * for the primitives.\n *\n * ```tsx\n * <html dir=\"rtl\" lang=\"ar\">\n * <body>\n * <DirectionProvider dir=\"rtl\">{children}</DirectionProvider>\n * </body>\n * </html>\n * ```\n *\n * A left-to-right application needs neither. Nothing here is required to render\n * English correctly, which is why it is easy to ship without it and never know.\n */\nexport interface DirectionProviderProps {\n dir: \"ltr\" | \"rtl\";\n children?: ReactNode;\n}\n\nexport function DirectionProvider({ dir, children }: DirectionProviderProps) {\n return <DirectionPrimitive.Provider dir={dir}>{children}</DirectionPrimitive.Provider>;\n}\n"],"mappings":";;;;AAmCA,SAAgB,kBAAkB,EAAE,KAAK,YAAoC;CAC3E,OAAO,oBAACA,UAAmB,UAApB;EAAkC;EAAM;CAAsC,CAAA;AACvF"}
@@ -0,0 +1,2 @@
1
+ import { DirectionProvider, DirectionProviderProps } from "./direction.js";
2
+ export { DirectionProvider, type DirectionProviderProps };
@@ -0,0 +1,2 @@
1
+ import { DirectionProvider } from "./direction.js";
2
+ export { DirectionProvider };
@@ -0,0 +1,17 @@
1
+ import { defineMeta } from "../../registry/schema.js";
2
+ //#region src/components/direction/meta.ts
3
+ const meta = defineMeta({
4
+ name: "direction",
5
+ title: "Direction Provider",
6
+ description: "Tells the component primitives which way the writing runs, so menus and sliders mirror with the rest of the page.",
7
+ category: "foundation",
8
+ status: "stable",
9
+ dependencies: ["radix-ui"],
10
+ registryDependencies: [],
11
+ files: ["direction.tsx"],
12
+ a11y: "Needed only for right-to-left languages, and needed in addition to `dir` on the document rather than instead of it. The styling here is written with logical properties and follows `dir` by itself; the primitives underneath read direction from React context and assume left-to-right without a provider, which mirrors a page correctly except for its menus, selects and sliders — worse than not mirroring at all, because it looks deliberate."
13
+ });
14
+ //#endregion
15
+ export { meta };
16
+
17
+ //# sourceMappingURL=meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta.js","names":[],"sources":["../../../src/components/direction/meta.ts"],"sourcesContent":["import { defineMeta } from \"@/registry/schema\";\n\nexport const meta = defineMeta({\n name: \"direction\",\n title: \"Direction Provider\",\n description:\n \"Tells the component primitives which way the writing runs, so menus and sliders mirror with the rest of the page.\",\n category: \"foundation\",\n status: \"stable\",\n dependencies: [\"radix-ui\"],\n registryDependencies: [],\n files: [\"direction.tsx\"],\n a11y:\n \"Needed only for right-to-left languages, and needed in addition to `dir` on the document \" +\n \"rather than instead of it. The styling here is written with logical properties and follows \" +\n \"`dir` by itself; the primitives underneath read direction from React context and assume \" +\n \"left-to-right without a provider, which mirrors a page correctly except for its menus, \" +\n \"selects and sliders — worse than not mirroring at all, because it looks deliberate.\",\n});\n"],"mappings":";;AAEA,MAAa,OAAO,WAAW;CAC7B,MAAM;CACN,OAAO;CACP,aACE;CACF,UAAU;CACV,QAAQ;CACR,cAAc,CAAC,UAAU;CACzB,sBAAsB,CAAC;CACvB,OAAO,CAAC,eAAe;CACvB,MACE;AAKJ,CAAC"}
@@ -0,0 +1,61 @@
1
+ import { ComponentPropsWithRef, ReactNode } from "react";
2
+ //#region src/components/dns-record/dns-record.d.ts
3
+ /**
4
+ * "Add this record at your DNS provider."
5
+ *
6
+ * Vercel, Resend, Cloudflare, Postmark, Google Workspace and every product
7
+ * that verifies a domain or routes its mail draws this card by hand, and
8
+ * they all learn the same three things the hard way.
9
+ *
10
+ * The parts are copied separately. A provider's form has a Name field, a
11
+ * Type field and a Value field, so one button that copies the whole line
12
+ * copies something nobody can paste anywhere.
13
+ *
14
+ * The Name field is a trap. Some providers want the host relative to the
15
+ * zone, `_dmarc`; some want the full name, `_dmarc.acme.com`; and some take
16
+ * the relative form and append the zone themselves, so a full name pasted
17
+ * in becomes `_dmarc.acme.com.acme.com` and the check fails for a reason
18
+ * nobody can see. So the host is shown both ways, with the sentence that
19
+ * explains which to use.
20
+ *
21
+ * A failed check says what was found. "Not verified" sends people back to
22
+ * their provider to stare at a record that is correct and has not
23
+ * propagated yet; "found v=spf1 -all, expected v=spf1 include:…" sends them
24
+ * to the typo. And nothing found at all is said as nothing found, with how
25
+ * long that can take, because it is not the same as wrong.
26
+ */
27
+ type DnsRecordStatus = "pending" | "checking" | "verified" | "failed";
28
+ interface DnsHostForms {
29
+ /** Relative to the zone: `_dmarc`, or `@` for the zone itself. */
30
+ relative: string;
31
+ /** Fully qualified: `_dmarc.acme.com`. */
32
+ full: string;
33
+ }
34
+ /**
35
+ * The host both ways a provider might want it. Accepts either form and a
36
+ * trailing dot, so the record can be declared however the backend has it.
37
+ */
38
+ declare function dnsHostForms(name: string, zone: string): DnsHostForms;
39
+ interface DnsRecordProps extends Omit<ComponentPropsWithRef<"section">, "children"> {
40
+ type: string;
41
+ /** The host, relative or fully qualified. */
42
+ name: string;
43
+ value: string;
44
+ /** The zone the record belongs to. Enables the host to be shown both ways. */
45
+ zone?: string;
46
+ ttl?: number | string;
47
+ priority?: number;
48
+ /** What the record is for, in the reader's language: "Proves you own acme.com". */
49
+ purpose?: string;
50
+ status?: DnsRecordStatus;
51
+ /** What the check found instead, when it failed. The useful part. */
52
+ found?: string[];
53
+ checkedAt?: Date;
54
+ locale?: string;
55
+ onCheck?: () => void;
56
+ children?: ReactNode;
57
+ }
58
+ declare function DnsRecord({ className, type, name, value, zone, ttl, priority, purpose, status, found, checkedAt, locale, onCheck, children, ...props }: DnsRecordProps): import("react").JSX.Element;
59
+ //#endregion
60
+ export { DnsHostForms, DnsRecord, DnsRecordProps, DnsRecordStatus, dnsHostForms };
61
+ //# sourceMappingURL=dns-record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dns-record.d.ts","names":[],"sources":["../../../src/components/dns-record/dns-record.tsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;KAyCY;UAEK;;EAEf;;EAEA;;;;;;iBAOc,aAAa,cAAc,eAAe;UAYzC,uBAAuB,KAAK;EAC3C;;EAEA;EACA;;EAEA;EACA;EACA;;EAEA;EACA,SAAS;;EAET;EACA,YAAY;EACZ;EACA;EACA,WAAW;;iBAKG,YACd,WACA,MACA,MACA,OACA,MACA,KACA,UACA,SACA,QACA,OACA,WACA,QACA,SACA,aACG,SACF,iCAAc,IAAA"}
@@ -0,0 +1,227 @@
1
+ "use client";
2
+ import { focusRing } from "../../lib/styles.js";
3
+ import { cn } from "../../lib/utils.js";
4
+ import { Spinner } from "../spinner/spinner.js";
5
+ import { Button } from "../button/button.js";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { useEffect, useId, useRef, useState } from "react";
8
+ //#region src/components/dns-record/dns-record.tsx
9
+ /**
10
+ * The host both ways a provider might want it. Accepts either form and a
11
+ * trailing dot, so the record can be declared however the backend has it.
12
+ */
13
+ function dnsHostForms(name, zone) {
14
+ const cleanZone = zone.replace(/\.$/, "");
15
+ const cleanName = name.replace(/\.$/, "");
16
+ if (cleanName === "@" || cleanName === "" || cleanName === cleanZone) return {
17
+ relative: "@",
18
+ full: cleanZone
19
+ };
20
+ if (cleanName.endsWith(`.${cleanZone}`)) return {
21
+ relative: cleanName.slice(0, -cleanZone.length - 1),
22
+ full: cleanName
23
+ };
24
+ return {
25
+ relative: cleanName,
26
+ full: `${cleanName}.${cleanZone}`
27
+ };
28
+ }
29
+ function DnsRecord({ className, type, name, value, zone, ttl, priority, purpose, status = "pending", found, checkedAt, locale, onCheck, children, ...props }) {
30
+ const id = useId();
31
+ const forms = zone ? dnsHostForms(name, zone) : null;
32
+ const host = forms ? forms.relative : name;
33
+ const [copy, setCopy] = useState(null);
34
+ const timer = useRef(null);
35
+ useEffect(() => () => {
36
+ if (timer.current) clearTimeout(timer.current);
37
+ }, []);
38
+ const copyPart = (part, text) => {
39
+ const settle = (state) => {
40
+ setCopy({
41
+ part,
42
+ state
43
+ });
44
+ if (timer.current) clearTimeout(timer.current);
45
+ timer.current = setTimeout(() => {
46
+ setCopy(null);
47
+ }, 2e3);
48
+ };
49
+ Promise.resolve().then(() => navigator.clipboard.writeText(text)).then(() => {
50
+ settle("copied");
51
+ }, () => {
52
+ settle("error");
53
+ });
54
+ };
55
+ const statusText = status === "verified" ? "Verified" : status === "checking" ? "Checking…" : status === "failed" ? found && found.length > 0 ? "Not verified. Something else was found." : "Not verified. Nothing found yet — DNS changes can take up to 48 hours to appear." : "Not checked yet";
56
+ const checked = checkedAt !== void 0 ? new Intl.DateTimeFormat(locale, {
57
+ dateStyle: "medium",
58
+ timeStyle: "short"
59
+ }).format(checkedAt) : null;
60
+ const parts = [
61
+ {
62
+ key: "type",
63
+ label: "Type",
64
+ text: type
65
+ },
66
+ {
67
+ key: "name",
68
+ label: "Name",
69
+ text: host,
70
+ note: forms ? forms.relative === "@" ? /* @__PURE__ */ jsxs(Fragment, { children: [
71
+ "Some providers want ",
72
+ /* @__PURE__ */ jsx("code", {
73
+ className: "font-mono",
74
+ children: forms.full
75
+ }),
76
+ " or leave it blank instead of ",
77
+ /* @__PURE__ */ jsx("code", {
78
+ className: "font-mono",
79
+ children: "@"
80
+ }),
81
+ "."
82
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
83
+ "Some providers want the full name, ",
84
+ /* @__PURE__ */ jsx("code", {
85
+ className: "font-mono",
86
+ children: forms.full
87
+ }),
88
+ ". If the field already ends in ",
89
+ /* @__PURE__ */ jsx("code", {
90
+ className: "font-mono",
91
+ children: zone
92
+ }),
93
+ ", enter just the part shown."
94
+ ] }) : void 0
95
+ },
96
+ {
97
+ key: "value",
98
+ label: "Value",
99
+ text: value,
100
+ note: type.toUpperCase() === "TXT" ? "Enter without quotes; your provider adds them if it needs to." : void 0
101
+ },
102
+ ...priority !== void 0 ? [{
103
+ key: "priority",
104
+ label: "Priority",
105
+ text: String(priority)
106
+ }] : [],
107
+ ...ttl !== void 0 ? [{
108
+ key: "ttl",
109
+ label: "TTL",
110
+ text: String(ttl)
111
+ }] : []
112
+ ];
113
+ return /* @__PURE__ */ jsxs("section", {
114
+ "data-slot": "dns-record",
115
+ "data-status": status,
116
+ "aria-labelledby": `${id}-heading`,
117
+ "aria-busy": status === "checking" || void 0,
118
+ className: cn("flex flex-col gap-3 rounded-lg border border-border bg-card p-4", className),
119
+ ...props,
120
+ children: [
121
+ /* @__PURE__ */ jsxs("div", {
122
+ className: "flex flex-wrap items-baseline justify-between gap-2",
123
+ children: [/* @__PURE__ */ jsxs("h3", {
124
+ id: `${id}-heading`,
125
+ className: "text-sm font-medium",
126
+ children: [
127
+ type,
128
+ " record",
129
+ purpose ? /* @__PURE__ */ jsxs("span", {
130
+ className: "font-normal text-muted-foreground",
131
+ children: [" · ", purpose]
132
+ }) : null
133
+ ]
134
+ }), /* @__PURE__ */ jsxs("p", {
135
+ "data-slot": "dns-record-status",
136
+ role: "status",
137
+ "aria-live": "polite",
138
+ className: cn("flex items-center gap-1.5 text-xs", status === "verified" && "text-success", status === "failed" && "text-destructive", (status === "pending" || status === "checking") && "text-muted-foreground"),
139
+ children: [
140
+ status === "checking" ? /* @__PURE__ */ jsx(Spinner, { size: "sm" }) : null,
141
+ statusText,
142
+ checked && status !== "checking" ? /* @__PURE__ */ jsxs("span", {
143
+ className: "text-muted-foreground",
144
+ children: [" · checked ", checked]
145
+ }) : null
146
+ ]
147
+ })]
148
+ }),
149
+ children,
150
+ /* @__PURE__ */ jsx("dl", {
151
+ className: "m-0 grid gap-x-4 gap-y-2 sm:grid-cols-[auto_1fr]",
152
+ children: parts.map((part) => /* @__PURE__ */ jsxs("div", {
153
+ className: "contents",
154
+ children: [/* @__PURE__ */ jsx("dt", {
155
+ className: "text-xs font-medium text-muted-foreground sm:pt-1.5",
156
+ children: part.label
157
+ }), /* @__PURE__ */ jsxs("dd", {
158
+ className: "m-0 flex flex-col gap-1",
159
+ children: [
160
+ /* @__PURE__ */ jsxs("div", {
161
+ className: "flex items-start gap-2",
162
+ children: [/* @__PURE__ */ jsx("code", {
163
+ "data-slot": "dns-record-part",
164
+ "data-part": part.key,
165
+ className: "min-w-0 flex-1 rounded-md bg-muted px-2 py-1 font-mono text-xs break-all",
166
+ children: part.text
167
+ }), /* @__PURE__ */ jsx("button", {
168
+ type: "button",
169
+ "aria-label": `Copy ${part.label.toLowerCase()}`,
170
+ "data-state": copy?.part === part.key ? copy.state : "idle",
171
+ className: cn("shrink-0 rounded-md border border-input bg-background px-2 py-1 text-xs font-medium", "hover:bg-accent hover:text-accent-foreground", focusRing),
172
+ onClick: () => {
173
+ copyPart(part.key, part.text);
174
+ },
175
+ children: copy?.part === part.key && copy.state === "copied" ? "Copied" : "Copy"
176
+ })]
177
+ }),
178
+ copy?.part === part.key && copy.state === "error" ? /* @__PURE__ */ jsx("p", {
179
+ "data-slot": "dns-record-copy-error",
180
+ className: "text-xs text-destructive",
181
+ children: "Could not copy. Select it and copy by hand."
182
+ }) : null,
183
+ part.note ? /* @__PURE__ */ jsx("p", {
184
+ className: "text-xs text-muted-foreground",
185
+ children: part.note
186
+ }) : null
187
+ ]
188
+ })]
189
+ }, part.key))
190
+ }),
191
+ status === "failed" && found && found.length > 0 ? /* @__PURE__ */ jsxs("div", {
192
+ "data-slot": "dns-record-found",
193
+ className: "rounded-md border border-destructive/40 bg-destructive/5 p-3 text-xs",
194
+ children: [/* @__PURE__ */ jsx("p", {
195
+ className: "font-medium text-destructive",
196
+ children: "Found instead:"
197
+ }), /* @__PURE__ */ jsx("ul", {
198
+ className: "m-0 mt-1 list-none p-0",
199
+ children: found.map((entry) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("code", {
200
+ className: "font-mono break-all",
201
+ children: entry
202
+ }) }, entry))
203
+ })]
204
+ }) : null,
205
+ /* @__PURE__ */ jsx("span", {
206
+ role: "status",
207
+ "aria-live": "polite",
208
+ className: "sr-only",
209
+ children: copy?.state === "copied" ? `Copied ${copy.part}` : copy?.state === "error" ? `Could not copy ${copy.part}. Select it and copy by hand.` : ""
210
+ }),
211
+ onCheck ? /* @__PURE__ */ jsx("div", {
212
+ className: "flex items-center gap-2",
213
+ children: /* @__PURE__ */ jsx(Button, {
214
+ variant: "outline",
215
+ size: "sm",
216
+ loading: status === "checking",
217
+ onClick: onCheck,
218
+ children: status === "verified" ? "Check again" : "Check now"
219
+ })
220
+ }) : null
221
+ ]
222
+ });
223
+ }
224
+ //#endregion
225
+ export { DnsRecord, dnsHostForms };
226
+
227
+ //# sourceMappingURL=dns-record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dns-record.js","names":[],"sources":["../../../src/components/dns-record/dns-record.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useEffect,\n useId,\n useRef,\n useState,\n type ComponentPropsWithRef,\n type ReactNode,\n} from \"react\";\n\nimport { Button } from \"@/components/button\";\nimport { Spinner } from \"@/components/spinner\";\nimport { focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * \"Add this record at your DNS provider.\"\n *\n * Vercel, Resend, Cloudflare, Postmark, Google Workspace and every product\n * that verifies a domain or routes its mail draws this card by hand, and\n * they all learn the same three things the hard way.\n *\n * The parts are copied separately. A provider's form has a Name field, a\n * Type field and a Value field, so one button that copies the whole line\n * copies something nobody can paste anywhere.\n *\n * The Name field is a trap. Some providers want the host relative to the\n * zone, `_dmarc`; some want the full name, `_dmarc.acme.com`; and some take\n * the relative form and append the zone themselves, so a full name pasted\n * in becomes `_dmarc.acme.com.acme.com` and the check fails for a reason\n * nobody can see. So the host is shown both ways, with the sentence that\n * explains which to use.\n *\n * A failed check says what was found. \"Not verified\" sends people back to\n * their provider to stare at a record that is correct and has not\n * propagated yet; \"found v=spf1 -all, expected v=spf1 include:…\" sends them\n * to the typo. And nothing found at all is said as nothing found, with how\n * long that can take, because it is not the same as wrong.\n */\n\nexport type DnsRecordStatus = \"pending\" | \"checking\" | \"verified\" | \"failed\";\n\nexport interface DnsHostForms {\n /** Relative to the zone: `_dmarc`, or `@` for the zone itself. */\n relative: string;\n /** Fully qualified: `_dmarc.acme.com`. */\n full: string;\n}\n\n/**\n * The host both ways a provider might want it. Accepts either form and a\n * trailing dot, so the record can be declared however the backend has it.\n */\nexport function dnsHostForms(name: string, zone: string): DnsHostForms {\n const cleanZone = zone.replace(/\\.$/, \"\");\n const cleanName = name.replace(/\\.$/, \"\");\n if (cleanName === \"@\" || cleanName === \"\" || cleanName === cleanZone) {\n return { relative: \"@\", full: cleanZone };\n }\n if (cleanName.endsWith(`.${cleanZone}`)) {\n return { relative: cleanName.slice(0, -cleanZone.length - 1), full: cleanName };\n }\n return { relative: cleanName, full: `${cleanName}.${cleanZone}` };\n}\n\nexport interface DnsRecordProps extends Omit<ComponentPropsWithRef<\"section\">, \"children\"> {\n type: string;\n /** The host, relative or fully qualified. */\n name: string;\n value: string;\n /** The zone the record belongs to. Enables the host to be shown both ways. */\n zone?: string;\n ttl?: number | string;\n priority?: number;\n /** What the record is for, in the reader's language: \"Proves you own acme.com\". */\n purpose?: string;\n status?: DnsRecordStatus;\n /** What the check found instead, when it failed. The useful part. */\n found?: string[];\n checkedAt?: Date;\n locale?: string;\n onCheck?: () => void;\n children?: ReactNode;\n}\n\ntype CopyState = \"idle\" | \"copied\" | \"error\";\n\nexport function DnsRecord({\n className,\n type,\n name,\n value,\n zone,\n ttl,\n priority,\n purpose,\n status = \"pending\",\n found,\n checkedAt,\n locale,\n onCheck,\n children,\n ...props\n}: DnsRecordProps) {\n const id = useId();\n const forms = zone ? dnsHostForms(name, zone) : null;\n const host = forms ? forms.relative : name;\n\n // Which part was copied, and whether it worked, so the button that was\n // pressed is the one that says so.\n const [copy, setCopy] = useState<{ part: string; state: CopyState } | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n useEffect(\n () => () => {\n if (timer.current) clearTimeout(timer.current);\n },\n [],\n );\n\n const copyPart = (part: string, text: string) => {\n const settle = (state: CopyState) => {\n setCopy({ part, state });\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => {\n setCopy(null);\n }, 2000);\n };\n Promise.resolve()\n .then(() => navigator.clipboard.writeText(text))\n .then(\n () => {\n settle(\"copied\");\n },\n () => {\n settle(\"error\");\n },\n );\n };\n\n const statusText =\n status === \"verified\"\n ? \"Verified\"\n : status === \"checking\"\n ? \"Checking…\"\n : status === \"failed\"\n ? found && found.length > 0\n ? \"Not verified. Something else was found.\"\n : \"Not verified. Nothing found yet — DNS changes can take up to 48 hours to appear.\"\n : \"Not checked yet\";\n\n const checked =\n checkedAt !== undefined\n ? new Intl.DateTimeFormat(locale, { dateStyle: \"medium\", timeStyle: \"short\" }).format(\n checkedAt,\n )\n : null;\n\n const parts: { key: string; label: string; text: string; note?: ReactNode }[] = [\n { key: \"type\", label: \"Type\", text: type },\n {\n key: \"name\",\n label: \"Name\",\n text: host,\n note: forms ? (\n forms.relative === \"@\" ? (\n <>\n Some providers want <code className=\"font-mono\">{forms.full}</code> or leave it\n blank instead of <code className=\"font-mono\">@</code>.\n </>\n ) : (\n <>\n Some providers want the full name, <code className=\"font-mono\">{forms.full}</code>.\n If the field already ends in <code className=\"font-mono\">{zone}</code>, enter just\n the part shown.\n </>\n )\n ) : undefined,\n },\n {\n key: \"value\",\n label: \"Value\",\n text: value,\n note:\n type.toUpperCase() === \"TXT\"\n ? \"Enter without quotes; your provider adds them if it needs to.\"\n : undefined,\n },\n ...(priority !== undefined\n ? [{ key: \"priority\", label: \"Priority\", text: String(priority) }]\n : []),\n ...(ttl !== undefined ? [{ key: \"ttl\", label: \"TTL\", text: String(ttl) }] : []),\n ];\n\n return (\n <section\n data-slot=\"dns-record\"\n data-status={status}\n aria-labelledby={`${id}-heading`}\n aria-busy={status === \"checking\" || undefined}\n className={cn(\n \"flex flex-col gap-3 rounded-lg border border-border bg-card p-4\",\n className,\n )}\n {...props}\n >\n <div className=\"flex flex-wrap items-baseline justify-between gap-2\">\n <h3 id={`${id}-heading`} className=\"text-sm font-medium\">\n {type} record\n {purpose ? (\n <span className=\"font-normal text-muted-foreground\"> · {purpose}</span>\n ) : null}\n </h3>\n {/* Status as a word, and announced when it changes: a check was asked\n for, and the answer should arrive where the asker is. */}\n <p\n data-slot=\"dns-record-status\"\n role=\"status\"\n aria-live=\"polite\"\n className={cn(\n \"flex items-center gap-1.5 text-xs\",\n status === \"verified\" && \"text-success\",\n status === \"failed\" && \"text-destructive\",\n (status === \"pending\" || status === \"checking\") && \"text-muted-foreground\",\n )}\n >\n {status === \"checking\" ? <Spinner size=\"sm\" /> : null}\n {statusText}\n {checked && status !== \"checking\" ? (\n <span className=\"text-muted-foreground\"> · checked {checked}</span>\n ) : null}\n </p>\n </div>\n\n {children}\n\n <dl className=\"m-0 grid gap-x-4 gap-y-2 sm:grid-cols-[auto_1fr]\">\n {parts.map((part) => (\n <div key={part.key} className=\"contents\">\n <dt className=\"text-xs font-medium text-muted-foreground sm:pt-1.5\">\n {part.label}\n </dt>\n <dd className=\"m-0 flex flex-col gap-1\">\n <div className=\"flex items-start gap-2\">\n <code\n data-slot=\"dns-record-part\"\n data-part={part.key}\n className=\"min-w-0 flex-1 rounded-md bg-muted px-2 py-1 font-mono text-xs break-all\"\n >\n {part.text}\n </code>\n <button\n type=\"button\"\n aria-label={`Copy ${part.label.toLowerCase()}`}\n data-state={copy?.part === part.key ? copy.state : \"idle\"}\n className={cn(\n \"shrink-0 rounded-md border border-input bg-background px-2 py-1 text-xs font-medium\",\n \"hover:bg-accent hover:text-accent-foreground\",\n focusRing,\n )}\n onClick={() => {\n copyPart(part.key, part.text);\n }}\n >\n {copy?.part === part.key && copy.state === \"copied\" ? \"Copied\" : \"Copy\"}\n </button>\n </div>\n {copy?.part === part.key && copy.state === \"error\" ? (\n <p data-slot=\"dns-record-copy-error\" className=\"text-xs text-destructive\">\n Could not copy. Select it and copy by hand.\n </p>\n ) : null}\n {part.note ? <p className=\"text-xs text-muted-foreground\">{part.note}</p> : null}\n </dd>\n </div>\n ))}\n </dl>\n\n {status === \"failed\" && found && found.length > 0 ? (\n <div\n data-slot=\"dns-record-found\"\n className=\"rounded-md border border-destructive/40 bg-destructive/5 p-3 text-xs\"\n >\n <p className=\"font-medium text-destructive\">Found instead:</p>\n <ul className=\"m-0 mt-1 list-none p-0\">\n {found.map((entry) => (\n <li key={entry}>\n <code className=\"font-mono break-all\">{entry}</code>\n </li>\n ))}\n </ul>\n </div>\n ) : null}\n\n {/* A separate region from the verification status, so \"Copied name\"\n never replaces or interrupts a check result that is being read.\n Present from the start, so the first copy is heard. */}\n <span role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {copy?.state === \"copied\"\n ? `Copied ${copy.part}`\n : copy?.state === \"error\"\n ? `Could not copy ${copy.part}. Select it and copy by hand.`\n : \"\"}\n </span>\n\n {onCheck ? (\n <div className=\"flex items-center gap-2\">\n <Button variant=\"outline\" size=\"sm\" loading={status === \"checking\"} onClick={onCheck}>\n {status === \"verified\" ? \"Check again\" : \"Check now\"}\n </Button>\n </div>\n ) : null}\n </section>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { DnsHostForms, DnsRecord, DnsRecordProps, DnsRecordStatus, dnsHostForms } from "./dns-record.js";
2
+ export { type DnsHostForms, DnsRecord, type DnsRecordProps, type DnsRecordStatus, dnsHostForms };
@@ -0,0 +1,2 @@
1
+ import { DnsRecord, dnsHostForms } from "./dns-record.js";
2
+ export { DnsRecord, dnsHostForms };
@@ -0,0 +1,17 @@
1
+ import { defineMeta } from "../../registry/schema.js";
2
+ //#region src/components/dns-record/meta.ts
3
+ const meta = defineMeta({
4
+ name: "dns-record",
5
+ title: "DNS Record",
6
+ description: "Add this record at your provider: parts copied separately, checked with what was found.",
7
+ category: "data",
8
+ status: "stable",
9
+ dependencies: [],
10
+ registryDependencies: ["button", "spinner"],
11
+ files: ["dns-record.tsx"],
12
+ a11y: "The card is a region named by the record's type and purpose. Each part is a definition with its own copy button, named by the part it copies, so five Copy buttons are five different buttons. Verification status is a word in a polite live region, so the answer to a check arrives where it was asked for, and the card is aria-busy while checking. Copy results are announced in a separate region, present from the start, so a copy never replaces a check result mid-sentence; failure is announced with what to do instead. What the check found is a list, not a colour."
13
+ });
14
+ //#endregion
15
+ export { meta };
16
+
17
+ //# sourceMappingURL=meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta.js","names":[],"sources":["../../../src/components/dns-record/meta.ts"],"sourcesContent":["import { defineMeta } from \"@/registry/schema\";\n\nexport const meta = defineMeta({\n name: \"dns-record\",\n title: \"DNS Record\",\n description:\n \"Add this record at your provider: parts copied separately, checked with what was found.\",\n category: \"data\",\n status: \"stable\",\n dependencies: [],\n registryDependencies: [\"button\", \"spinner\"],\n files: [\"dns-record.tsx\"],\n a11y:\n \"The card is a region named by the record's type and purpose. Each part is a definition \" +\n \"with its own copy button, named by the part it copies, so five Copy buttons are five \" +\n \"different buttons. Verification status is a word in a polite live region, so the answer to \" +\n \"a check arrives where it was asked for, and the card is aria-busy while checking. Copy \" +\n \"results are announced in a separate region, present from the start, so a copy never \" +\n \"replaces a check result mid-sentence; failure is announced with what to do instead. What \" +\n \"the check found is a list, not a colour.\",\n});\n"],"mappings":";;AAEA,MAAa,OAAO,WAAW;CAC7B,MAAM;CACN,OAAO;CACP,aACE;CACF,UAAU;CACV,QAAQ;CACR,cAAc,CAAC;CACf,sBAAsB,CAAC,UAAU,SAAS;CAC1C,OAAO,CAAC,gBAAgB;CACxB,MACE;AAOJ,CAAC"}
@@ -107,7 +107,7 @@ function DrawerContent({ className, children, showHandle = true, ...props }) {
107
107
  function DrawerHeader({ className, ...props }) {
108
108
  return /* @__PURE__ */ jsx("div", {
109
109
  "data-slot": "drawer-header",
110
- className: cn("flex shrink-0 flex-col gap-1.5 px-6 pt-4 pb-2 text-left", className),
110
+ className: cn("flex shrink-0 flex-col gap-1.5 px-6 pt-4 pb-2 text-start", className),
111
111
  ...props
112
112
  });
113
113
  }