@usetheo/ui 0.13.2 → 0.15.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 (1025) hide show
  1. package/README.md +4 -466
  2. package/dist/components/composites/command-palette/command-palette.d.ts +1 -1
  3. package/dist/components/composites/metric-card/metric-card.d.ts +1 -1
  4. package/dist/components/primitives/alert/alert.d.ts +1 -1
  5. package/dist/components/primitives/badge/badge.d.ts +1 -1
  6. package/dist/components/primitives/button/button.d.ts +1 -1
  7. package/dist/components/primitives/danger-zone/danger-zone.d.ts +1 -1
  8. package/dist/components/primitives/dialog/dialog.d.ts +3 -3
  9. package/dist/components/primitives/dropdown-menu/dropdown-menu.d.ts +1 -1
  10. package/dist/components/primitives/empty-state/empty-state.d.ts +1 -1
  11. package/dist/components/primitives/plan-badge/plan-badge.d.ts +1 -1
  12. package/dist/components/primitives/sheet/sheet.d.ts +3 -3
  13. package/dist/components/primitives/stat-tile/stat-tile.d.ts +1 -1
  14. package/dist/components/primitives/toast/toast.d.ts +1 -1
  15. package/dist/components/primitives/update-banner/update-banner.d.ts +1 -1
  16. package/dist/index.d.ts +0 -89
  17. package/dist/index.js +4657 -1545
  18. package/dist/lib/cn.d.ts +1 -1
  19. package/dist/{preset-v3-legacy.js → tailwind-preset.js} +1 -16
  20. package/package.json +29 -788
  21. package/CHANGELOG.md +0 -1797
  22. package/DESIGN.md +0 -456
  23. package/NOTICE +0 -38
  24. package/dist/chunk-23YEYNDS.js +0 -89
  25. package/dist/chunk-23YEYNDS.js.map +0 -1
  26. package/dist/chunk-25KBUQEQ.js +0 -95
  27. package/dist/chunk-25KBUQEQ.js.map +0 -1
  28. package/dist/chunk-2NTEJRPA.js +0 -212
  29. package/dist/chunk-2NTEJRPA.js.map +0 -1
  30. package/dist/chunk-2NZYMQKT.js +0 -56
  31. package/dist/chunk-2NZYMQKT.js.map +0 -1
  32. package/dist/chunk-2OVFVPSZ.js +0 -26
  33. package/dist/chunk-2OVFVPSZ.js.map +0 -1
  34. package/dist/chunk-2UP7SECE.js +0 -78
  35. package/dist/chunk-2UP7SECE.js.map +0 -1
  36. package/dist/chunk-33ETHPT7.js +0 -112
  37. package/dist/chunk-33ETHPT7.js.map +0 -1
  38. package/dist/chunk-33IIDFSM.js +0 -43
  39. package/dist/chunk-33IIDFSM.js.map +0 -1
  40. package/dist/chunk-345HYADQ.js +0 -88
  41. package/dist/chunk-345HYADQ.js.map +0 -1
  42. package/dist/chunk-357XIC2N.js +0 -98
  43. package/dist/chunk-357XIC2N.js.map +0 -1
  44. package/dist/chunk-3YOPTHZH.js +0 -31
  45. package/dist/chunk-3YOPTHZH.js.map +0 -1
  46. package/dist/chunk-44ZNZZUS.js +0 -111
  47. package/dist/chunk-44ZNZZUS.js.map +0 -1
  48. package/dist/chunk-45FWKR23.js +0 -62
  49. package/dist/chunk-45FWKR23.js.map +0 -1
  50. package/dist/chunk-47IPOYLQ.js +0 -116
  51. package/dist/chunk-47IPOYLQ.js.map +0 -1
  52. package/dist/chunk-4EH6F54D.js +0 -69
  53. package/dist/chunk-4EH6F54D.js.map +0 -1
  54. package/dist/chunk-4EKF4EIE.js +0 -140
  55. package/dist/chunk-4EKF4EIE.js.map +0 -1
  56. package/dist/chunk-4XYFJIRC.js +0 -65
  57. package/dist/chunk-4XYFJIRC.js.map +0 -1
  58. package/dist/chunk-52J7SDYH.js +0 -48
  59. package/dist/chunk-52J7SDYH.js.map +0 -1
  60. package/dist/chunk-5YX76GH6.js +0 -70
  61. package/dist/chunk-5YX76GH6.js.map +0 -1
  62. package/dist/chunk-624AATQZ.js +0 -77
  63. package/dist/chunk-624AATQZ.js.map +0 -1
  64. package/dist/chunk-65YSFZAN.js +0 -192
  65. package/dist/chunk-65YSFZAN.js.map +0 -1
  66. package/dist/chunk-6IODJQWC.js +0 -47
  67. package/dist/chunk-6IODJQWC.js.map +0 -1
  68. package/dist/chunk-6ORS6XOE.js +0 -57
  69. package/dist/chunk-6ORS6XOE.js.map +0 -1
  70. package/dist/chunk-6V2LQEPT.js +0 -165
  71. package/dist/chunk-6V2LQEPT.js.map +0 -1
  72. package/dist/chunk-75IDWFYX.js +0 -134
  73. package/dist/chunk-75IDWFYX.js.map +0 -1
  74. package/dist/chunk-7BQXMG2A.js +0 -93
  75. package/dist/chunk-7BQXMG2A.js.map +0 -1
  76. package/dist/chunk-7RFWVNQA.js +0 -47
  77. package/dist/chunk-7RFWVNQA.js.map +0 -1
  78. package/dist/chunk-ABBXLAJW.js +0 -74
  79. package/dist/chunk-ABBXLAJW.js.map +0 -1
  80. package/dist/chunk-ACZNFEOZ.js +0 -180
  81. package/dist/chunk-ACZNFEOZ.js.map +0 -1
  82. package/dist/chunk-AJ2LNQUQ.js +0 -108
  83. package/dist/chunk-AJ2LNQUQ.js.map +0 -1
  84. package/dist/chunk-AX6P3SDS.js +0 -89
  85. package/dist/chunk-AX6P3SDS.js.map +0 -1
  86. package/dist/chunk-AXUAQM45.js +0 -154
  87. package/dist/chunk-AXUAQM45.js.map +0 -1
  88. package/dist/chunk-B2FL7KBJ.js +0 -65
  89. package/dist/chunk-B2FL7KBJ.js.map +0 -1
  90. package/dist/chunk-B42EOFRD.js +0 -106
  91. package/dist/chunk-B42EOFRD.js.map +0 -1
  92. package/dist/chunk-BE232OKN.js +0 -42
  93. package/dist/chunk-BE232OKN.js.map +0 -1
  94. package/dist/chunk-BSF4Y4UT.js +0 -77
  95. package/dist/chunk-BSF4Y4UT.js.map +0 -1
  96. package/dist/chunk-BYUWQ6OP.js +0 -45
  97. package/dist/chunk-BYUWQ6OP.js.map +0 -1
  98. package/dist/chunk-C5ULP2P5.js +0 -140
  99. package/dist/chunk-C5ULP2P5.js.map +0 -1
  100. package/dist/chunk-CCG7PXLX.js +0 -24
  101. package/dist/chunk-CCG7PXLX.js.map +0 -1
  102. package/dist/chunk-CFVSXYVT.js +0 -59
  103. package/dist/chunk-CFVSXYVT.js.map +0 -1
  104. package/dist/chunk-CGWIOIEO.js +0 -27
  105. package/dist/chunk-CGWIOIEO.js.map +0 -1
  106. package/dist/chunk-CVFSNA4K.js +0 -716
  107. package/dist/chunk-CVFSNA4K.js.map +0 -1
  108. package/dist/chunk-CX54TUTT.js +0 -144
  109. package/dist/chunk-CX54TUTT.js.map +0 -1
  110. package/dist/chunk-DJK6H3FD.js +0 -200
  111. package/dist/chunk-DJK6H3FD.js.map +0 -1
  112. package/dist/chunk-E26M7ATD.js +0 -61
  113. package/dist/chunk-E26M7ATD.js.map +0 -1
  114. package/dist/chunk-E63IRXZZ.js +0 -78
  115. package/dist/chunk-E63IRXZZ.js.map +0 -1
  116. package/dist/chunk-EOTSD2GL.js +0 -83
  117. package/dist/chunk-EOTSD2GL.js.map +0 -1
  118. package/dist/chunk-ESJUISWY.js +0 -45
  119. package/dist/chunk-ESJUISWY.js.map +0 -1
  120. package/dist/chunk-ET7A3TQZ.js +0 -158
  121. package/dist/chunk-ET7A3TQZ.js.map +0 -1
  122. package/dist/chunk-ETTL6XGU.js +0 -139
  123. package/dist/chunk-ETTL6XGU.js.map +0 -1
  124. package/dist/chunk-EWDN56AS.js +0 -24
  125. package/dist/chunk-EWDN56AS.js.map +0 -1
  126. package/dist/chunk-FELH4AAQ.js +0 -90
  127. package/dist/chunk-FELH4AAQ.js.map +0 -1
  128. package/dist/chunk-FJZKA2LV.js +0 -165
  129. package/dist/chunk-FJZKA2LV.js.map +0 -1
  130. package/dist/chunk-FM72LBCJ.js +0 -102
  131. package/dist/chunk-FM72LBCJ.js.map +0 -1
  132. package/dist/chunk-FNQASFTK.js +0 -53
  133. package/dist/chunk-FNQASFTK.js.map +0 -1
  134. package/dist/chunk-FOE3XXBJ.js +0 -965
  135. package/dist/chunk-FOE3XXBJ.js.map +0 -1
  136. package/dist/chunk-GC52HWIL.js +0 -41
  137. package/dist/chunk-GC52HWIL.js.map +0 -1
  138. package/dist/chunk-GCKFA7X7.js +0 -83
  139. package/dist/chunk-GCKFA7X7.js.map +0 -1
  140. package/dist/chunk-GDMCDW66.js +0 -19
  141. package/dist/chunk-GDMCDW66.js.map +0 -1
  142. package/dist/chunk-GLRUM43F.js +0 -121
  143. package/dist/chunk-GLRUM43F.js.map +0 -1
  144. package/dist/chunk-H3L7WZDZ.js +0 -74
  145. package/dist/chunk-H3L7WZDZ.js.map +0 -1
  146. package/dist/chunk-H55WXDME.js +0 -45
  147. package/dist/chunk-H55WXDME.js.map +0 -1
  148. package/dist/chunk-HB45JHMM.js +0 -43
  149. package/dist/chunk-HB45JHMM.js.map +0 -1
  150. package/dist/chunk-HETHTYEZ.js +0 -80
  151. package/dist/chunk-HETHTYEZ.js.map +0 -1
  152. package/dist/chunk-HHSKNB32.js +0 -55
  153. package/dist/chunk-HHSKNB32.js.map +0 -1
  154. package/dist/chunk-HILOUYES.js +0 -151
  155. package/dist/chunk-HILOUYES.js.map +0 -1
  156. package/dist/chunk-HKVOCYTN.js +0 -113
  157. package/dist/chunk-HKVOCYTN.js.map +0 -1
  158. package/dist/chunk-HNRFBJ25.js +0 -129
  159. package/dist/chunk-HNRFBJ25.js.map +0 -1
  160. package/dist/chunk-HTJVHFNW.js +0 -178
  161. package/dist/chunk-HTJVHFNW.js.map +0 -1
  162. package/dist/chunk-HZ7Z22VW.js +0 -130
  163. package/dist/chunk-HZ7Z22VW.js.map +0 -1
  164. package/dist/chunk-II5Q5RIO.js +0 -236
  165. package/dist/chunk-II5Q5RIO.js.map +0 -1
  166. package/dist/chunk-ITEIRMSH.js +0 -121
  167. package/dist/chunk-ITEIRMSH.js.map +0 -1
  168. package/dist/chunk-JIZKW3WC.js +0 -74
  169. package/dist/chunk-JIZKW3WC.js.map +0 -1
  170. package/dist/chunk-JP3SHERK.js +0 -154
  171. package/dist/chunk-JP3SHERK.js.map +0 -1
  172. package/dist/chunk-JR4H3FJ2.js +0 -74
  173. package/dist/chunk-JR4H3FJ2.js.map +0 -1
  174. package/dist/chunk-JRW53TVG.js +0 -53
  175. package/dist/chunk-JRW53TVG.js.map +0 -1
  176. package/dist/chunk-JS5T2CRO.js +0 -86
  177. package/dist/chunk-JS5T2CRO.js.map +0 -1
  178. package/dist/chunk-JXOCE27Z.js +0 -82
  179. package/dist/chunk-JXOCE27Z.js.map +0 -1
  180. package/dist/chunk-JYW5YNF7.js +0 -188
  181. package/dist/chunk-JYW5YNF7.js.map +0 -1
  182. package/dist/chunk-KHBXI6AV.js +0 -149
  183. package/dist/chunk-KHBXI6AV.js.map +0 -1
  184. package/dist/chunk-KXZH7BTX.js +0 -92
  185. package/dist/chunk-KXZH7BTX.js.map +0 -1
  186. package/dist/chunk-LB4PMLCX.js +0 -57
  187. package/dist/chunk-LB4PMLCX.js.map +0 -1
  188. package/dist/chunk-LMGE2QEO.js +0 -117
  189. package/dist/chunk-LMGE2QEO.js.map +0 -1
  190. package/dist/chunk-M74ZYBOK.js +0 -93
  191. package/dist/chunk-M74ZYBOK.js.map +0 -1
  192. package/dist/chunk-ML7WLNIK.js +0 -11
  193. package/dist/chunk-ML7WLNIK.js.map +0 -1
  194. package/dist/chunk-MLEPCMTF.js +0 -147
  195. package/dist/chunk-MLEPCMTF.js.map +0 -1
  196. package/dist/chunk-NSZEZTDO.js +0 -98
  197. package/dist/chunk-NSZEZTDO.js.map +0 -1
  198. package/dist/chunk-OMR6ZGME.js +0 -63
  199. package/dist/chunk-OMR6ZGME.js.map +0 -1
  200. package/dist/chunk-ORVYP73T.js +0 -223
  201. package/dist/chunk-ORVYP73T.js.map +0 -1
  202. package/dist/chunk-OZFUUO2Q.js +0 -50
  203. package/dist/chunk-OZFUUO2Q.js.map +0 -1
  204. package/dist/chunk-PJWYIOY4.js +0 -3
  205. package/dist/chunk-PJWYIOY4.js.map +0 -1
  206. package/dist/chunk-PXT47DRZ.js +0 -59
  207. package/dist/chunk-PXT47DRZ.js.map +0 -1
  208. package/dist/chunk-QBSJM4XI.js +0 -83
  209. package/dist/chunk-QBSJM4XI.js.map +0 -1
  210. package/dist/chunk-QBUTRD2M.js +0 -58
  211. package/dist/chunk-QBUTRD2M.js.map +0 -1
  212. package/dist/chunk-QCSMJTA6.js +0 -68
  213. package/dist/chunk-QCSMJTA6.js.map +0 -1
  214. package/dist/chunk-QNYOIVQ4.js +0 -36
  215. package/dist/chunk-QNYOIVQ4.js.map +0 -1
  216. package/dist/chunk-QPURZJJM.js +0 -88
  217. package/dist/chunk-QPURZJJM.js.map +0 -1
  218. package/dist/chunk-QTG266XU.js +0 -69
  219. package/dist/chunk-QTG266XU.js.map +0 -1
  220. package/dist/chunk-QV4BQRNH.js +0 -29
  221. package/dist/chunk-QV4BQRNH.js.map +0 -1
  222. package/dist/chunk-RJ6RAMKH.js +0 -89
  223. package/dist/chunk-RJ6RAMKH.js.map +0 -1
  224. package/dist/chunk-RJBCTMVW.js +0 -59
  225. package/dist/chunk-RJBCTMVW.js.map +0 -1
  226. package/dist/chunk-RMJYXHBX.js +0 -113
  227. package/dist/chunk-RMJYXHBX.js.map +0 -1
  228. package/dist/chunk-RQT5DWGG.js +0 -34
  229. package/dist/chunk-RQT5DWGG.js.map +0 -1
  230. package/dist/chunk-SPGNNN4R.js +0 -115
  231. package/dist/chunk-SPGNNN4R.js.map +0 -1
  232. package/dist/chunk-SQ66DCXY.js +0 -35
  233. package/dist/chunk-SQ66DCXY.js.map +0 -1
  234. package/dist/chunk-SXKGWHAM.js +0 -179
  235. package/dist/chunk-SXKGWHAM.js.map +0 -1
  236. package/dist/chunk-T2OKGV6M.js +0 -13
  237. package/dist/chunk-T2OKGV6M.js.map +0 -1
  238. package/dist/chunk-T4Z7HBZR.js +0 -110
  239. package/dist/chunk-T4Z7HBZR.js.map +0 -1
  240. package/dist/chunk-TW5I37AE.js +0 -142
  241. package/dist/chunk-TW5I37AE.js.map +0 -1
  242. package/dist/chunk-TY6NTWN5.js +0 -29
  243. package/dist/chunk-TY6NTWN5.js.map +0 -1
  244. package/dist/chunk-UGKI466V.js +0 -12
  245. package/dist/chunk-UGKI466V.js.map +0 -1
  246. package/dist/chunk-UJAWV6LM.js +0 -136
  247. package/dist/chunk-UJAWV6LM.js.map +0 -1
  248. package/dist/chunk-V2WCZBVE.js +0 -122
  249. package/dist/chunk-V2WCZBVE.js.map +0 -1
  250. package/dist/chunk-V4LRBYOD.js +0 -55
  251. package/dist/chunk-V4LRBYOD.js.map +0 -1
  252. package/dist/chunk-V6H2RUVP.js +0 -77
  253. package/dist/chunk-V6H2RUVP.js.map +0 -1
  254. package/dist/chunk-VFRFUU7A.js +0 -35
  255. package/dist/chunk-VFRFUU7A.js.map +0 -1
  256. package/dist/chunk-VHK2OUCW.js +0 -38
  257. package/dist/chunk-VHK2OUCW.js.map +0 -1
  258. package/dist/chunk-VLNFUEOR.js +0 -86
  259. package/dist/chunk-VLNFUEOR.js.map +0 -1
  260. package/dist/chunk-VM4RMQQN.js +0 -11
  261. package/dist/chunk-VM4RMQQN.js.map +0 -1
  262. package/dist/chunk-VU7XKD4G.js +0 -66
  263. package/dist/chunk-VU7XKD4G.js.map +0 -1
  264. package/dist/chunk-VZX4HLBM.js +0 -84
  265. package/dist/chunk-VZX4HLBM.js.map +0 -1
  266. package/dist/chunk-W24RR5OD.js +0 -102
  267. package/dist/chunk-W24RR5OD.js.map +0 -1
  268. package/dist/chunk-W47V2F3Q.js +0 -115
  269. package/dist/chunk-W47V2F3Q.js.map +0 -1
  270. package/dist/chunk-W743ORLA.js +0 -91
  271. package/dist/chunk-W743ORLA.js.map +0 -1
  272. package/dist/chunk-W7I3ZX66.js +0 -76
  273. package/dist/chunk-W7I3ZX66.js.map +0 -1
  274. package/dist/chunk-WD42UBGR.js +0 -64
  275. package/dist/chunk-WD42UBGR.js.map +0 -1
  276. package/dist/chunk-WQVEJJV7.js +0 -108
  277. package/dist/chunk-WQVEJJV7.js.map +0 -1
  278. package/dist/chunk-WX4Q4DTX.js +0 -121
  279. package/dist/chunk-WX4Q4DTX.js.map +0 -1
  280. package/dist/chunk-XC7SYZYR.js +0 -86
  281. package/dist/chunk-XC7SYZYR.js.map +0 -1
  282. package/dist/chunk-XSENM65D.js +0 -128
  283. package/dist/chunk-XSENM65D.js.map +0 -1
  284. package/dist/chunk-Y7JTBQUQ.js +0 -152
  285. package/dist/chunk-Y7JTBQUQ.js.map +0 -1
  286. package/dist/chunk-YD2QEVHO.js +0 -68
  287. package/dist/chunk-YD2QEVHO.js.map +0 -1
  288. package/dist/chunk-YDKRUSB5.js +0 -35
  289. package/dist/chunk-YDKRUSB5.js.map +0 -1
  290. package/dist/chunk-YNDHYMPI.js +0 -46
  291. package/dist/chunk-YNDHYMPI.js.map +0 -1
  292. package/dist/chunk-YO3WEMOH.js +0 -130
  293. package/dist/chunk-YO3WEMOH.js.map +0 -1
  294. package/dist/chunk-YVXMWUWD.js +0 -118
  295. package/dist/chunk-YVXMWUWD.js.map +0 -1
  296. package/dist/chunk-Z6HZMGO2.js +0 -106
  297. package/dist/chunk-Z6HZMGO2.js.map +0 -1
  298. package/dist/chunk-ZAP4RIUL.js +0 -81
  299. package/dist/chunk-ZAP4RIUL.js.map +0 -1
  300. package/dist/chunk-ZEDDWVBB.js +0 -112
  301. package/dist/chunk-ZEDDWVBB.js.map +0 -1
  302. package/dist/chunk-ZEFOXF2I.js +0 -87
  303. package/dist/chunk-ZEFOXF2I.js.map +0 -1
  304. package/dist/chunk-ZPURFK4C.js +0 -203
  305. package/dist/chunk-ZPURFK4C.js.map +0 -1
  306. package/dist/chunk-ZXPDS6DH.js +0 -3
  307. package/dist/chunk-ZXPDS6DH.js.map +0 -1
  308. package/dist/chunk-ZZZIOTLC.js +0 -89
  309. package/dist/chunk-ZZZIOTLC.js.map +0 -1
  310. package/dist/components/composites/agent-composer/agent-composer.d.ts +0 -41
  311. package/dist/components/composites/agent-composer/index.d.ts +0 -1
  312. package/dist/components/composites/agent-editor/agent-editor.d.ts +0 -31
  313. package/dist/components/composites/agent-editor/index.d.ts +0 -1
  314. package/dist/components/composites/agent-stream/agent-stream.d.ts +0 -72
  315. package/dist/components/composites/agent-stream/index.d.ts +0 -2
  316. package/dist/components/composites/agent-stream/to-agent-stream-items.d.ts +0 -32
  317. package/dist/components/composites/agent-timeline/agent-timeline.d.ts +0 -22
  318. package/dist/components/composites/agent-timeline/index.d.ts +0 -1
  319. package/dist/components/composites/agent-tool-renderer/agent-tool-renderer.d.ts +0 -60
  320. package/dist/components/composites/agent-tool-renderer/index.d.ts +0 -1
  321. package/dist/components/composites/agent-tool-renderer/tool-call-part.d.ts +0 -5
  322. package/dist/components/composites/approval-card/approval-card.d.ts +0 -35
  323. package/dist/components/composites/approval-card/index.d.ts +0 -1
  324. package/dist/components/composites/chat-composer/chat-composer.d.ts +0 -68
  325. package/dist/components/composites/chat-composer/index.d.ts +0 -1
  326. package/dist/components/composites/chat-message/chat-message-actions.d.ts +0 -22
  327. package/dist/components/composites/chat-message/chat-message-branch.d.ts +0 -17
  328. package/dist/components/composites/chat-message/chat-message-response.d.ts +0 -15
  329. package/dist/components/composites/chat-message/chat-message-toolbar.d.ts +0 -7
  330. package/dist/components/composites/chat-message/chat-message.d.ts +0 -94
  331. package/dist/components/composites/chat-message/index.d.ts +0 -23
  332. package/dist/components/composites/chat-message/parts/data-part.d.ts +0 -9
  333. package/dist/components/composites/chat-message/parts/file-part.d.ts +0 -5
  334. package/dist/components/composites/chat-message/parts/reasoning-part.d.ts +0 -7
  335. package/dist/components/composites/chat-message/parts/source-part.d.ts +0 -9
  336. package/dist/components/composites/chat-message/parts/text-part.d.ts +0 -11
  337. package/dist/components/composites/choice-prompt/choice-prompt.d.ts +0 -65
  338. package/dist/components/composites/choice-prompt/index.d.ts +0 -2
  339. package/dist/components/composites/confirm-prompt/confirm-prompt.d.ts +0 -31
  340. package/dist/components/composites/confirm-prompt/index.d.ts +0 -1
  341. package/dist/components/composites/cron-jobs-list/cron-jobs-list.d.ts +0 -15
  342. package/dist/components/composites/cron-jobs-list/index.d.ts +0 -1
  343. package/dist/components/composites/mcp-server-list/index.d.ts +0 -1
  344. package/dist/components/composites/mcp-server-list/mcp-server-list.d.ts +0 -15
  345. package/dist/components/composites/multi-select-prompt/index.d.ts +0 -1
  346. package/dist/components/composites/multi-select-prompt/multi-select-prompt.d.ts +0 -61
  347. package/dist/components/composites/permission-modal/index.d.ts +0 -1
  348. package/dist/components/composites/permission-modal/permission-modal.d.ts +0 -48
  349. package/dist/components/composites/preview-panel/index.d.ts +0 -1
  350. package/dist/components/composites/preview-panel/preview-panel.d.ts +0 -24
  351. package/dist/components/composites/rule-editor/index.d.ts +0 -1
  352. package/dist/components/composites/rule-editor/rule-editor.d.ts +0 -18
  353. package/dist/components/composites/skill-editor/index.d.ts +0 -1
  354. package/dist/components/composites/skill-editor/skill-editor.d.ts +0 -19
  355. package/dist/components/composites/skills-list/index.d.ts +0 -1
  356. package/dist/components/composites/skills-list/skills-list.d.ts +0 -15
  357. package/dist/components/composites/slide-deck/context.d.ts +0 -23
  358. package/dist/components/composites/slide-deck/controls.d.ts +0 -8
  359. package/dist/components/composites/slide-deck/fragments.d.ts +0 -20
  360. package/dist/components/composites/slide-deck/index.d.ts +0 -21
  361. package/dist/components/composites/slide-deck/notes.d.ts +0 -14
  362. package/dist/components/composites/slide-deck/presenter-view.d.ts +0 -15
  363. package/dist/components/composites/slide-deck/print-styles.d.ts +0 -21
  364. package/dist/components/composites/slide-deck/progress-bar.d.ts +0 -8
  365. package/dist/components/composites/slide-deck/schema.d.ts +0 -31
  366. package/dist/components/composites/slide-deck/slide-deck.d.ts +0 -78
  367. package/dist/components/composites/slide-deck/slide-number.d.ts +0 -11
  368. package/dist/components/composites/slide-deck/split-deck.d.ts +0 -2
  369. package/dist/components/composites/slide-deck/thumbnails.d.ts +0 -19
  370. package/dist/components/composites/slide-deck/use-deck-hash-routing.d.ts +0 -23
  371. package/dist/components/composites/slide-deck/use-deck-keyboard.d.ts +0 -25
  372. package/dist/components/composites/slide-deck/use-deck-state.d.ts +0 -57
  373. package/dist/components/composites/slide-deck/use-deck-swipe.d.ts +0 -20
  374. package/dist/components/composites/slide-deck/use-fullscreen.d.ts +0 -18
  375. package/dist/components/composites/stability-bundle-viewer/index.d.ts +0 -1
  376. package/dist/components/composites/stability-bundle-viewer/stability-bundle-viewer.d.ts +0 -28
  377. package/dist/components/composites/text-prompt/index.d.ts +0 -1
  378. package/dist/components/composites/text-prompt/text-prompt.d.ts +0 -48
  379. package/dist/components/composites/usage-meter/index.d.ts +0 -1
  380. package/dist/components/composites/usage-meter/usage-meter.d.ts +0 -56
  381. package/dist/components/primitives/agent-error-card/agent-error-card.d.ts +0 -47
  382. package/dist/components/primitives/agent-error-card/index.d.ts +0 -1
  383. package/dist/components/primitives/agent-event/agent-event.d.ts +0 -25
  384. package/dist/components/primitives/agent-event/index.d.ts +0 -1
  385. package/dist/components/primitives/agent-handoff/agent-handoff.d.ts +0 -24
  386. package/dist/components/primitives/agent-handoff/index.d.ts +0 -1
  387. package/dist/components/primitives/agent-profile/agent-profile.d.ts +0 -28
  388. package/dist/components/primitives/agent-profile/index.d.ts +0 -1
  389. package/dist/components/primitives/agent-starting-state/agent-starting-state.d.ts +0 -15
  390. package/dist/components/primitives/agent-starting-state/index.d.ts +0 -1
  391. package/dist/components/primitives/agent-streaming/agent-streaming.d.ts +0 -16
  392. package/dist/components/primitives/agent-streaming/index.d.ts +0 -1
  393. package/dist/components/primitives/artifact-preview/artifact-preview.d.ts +0 -25
  394. package/dist/components/primitives/artifact-preview/index.d.ts +0 -1
  395. package/dist/components/primitives/attachment-chip/attachment-chip.d.ts +0 -14
  396. package/dist/components/primitives/attachment-chip/index.d.ts +0 -1
  397. package/dist/components/primitives/audit-log-entry/audit-log-entry.d.ts +0 -29
  398. package/dist/components/primitives/audit-log-entry/index.d.ts +0 -1
  399. package/dist/components/primitives/auto-compact-notice/auto-compact-notice.d.ts +0 -24
  400. package/dist/components/primitives/auto-compact-notice/index.d.ts +0 -1
  401. package/dist/components/primitives/branch-indicator/branch-indicator.d.ts +0 -13
  402. package/dist/components/primitives/branch-indicator/index.d.ts +0 -1
  403. package/dist/components/primitives/browser-controls/browser-controls.d.ts +0 -19
  404. package/dist/components/primitives/browser-controls/index.d.ts +0 -1
  405. package/dist/components/primitives/build-log-stream/build-log-stream.d.ts +0 -51
  406. package/dist/components/primitives/build-log-stream/index.d.ts +0 -1
  407. package/dist/components/primitives/capability-indicator/capability-indicator.d.ts +0 -57
  408. package/dist/components/primitives/capability-indicator/index.d.ts +0 -1
  409. package/dist/components/primitives/channel-card/channel-card.d.ts +0 -28
  410. package/dist/components/primitives/channel-card/index.d.ts +0 -1
  411. package/dist/components/primitives/chat-thread/chat-thread.d.ts +0 -12
  412. package/dist/components/primitives/chat-thread/index.d.ts +0 -1
  413. package/dist/components/primitives/context-card/context-card.d.ts +0 -18
  414. package/dist/components/primitives/context-card/index.d.ts +0 -1
  415. package/dist/components/primitives/context-window-bar/context-window-bar.d.ts +0 -27
  416. package/dist/components/primitives/context-window-bar/index.d.ts +0 -1
  417. package/dist/components/primitives/cost-meter/cost-meter.d.ts +0 -23
  418. package/dist/components/primitives/cost-meter/index.d.ts +0 -1
  419. package/dist/components/primitives/created-files-card/created-files-card.d.ts +0 -30
  420. package/dist/components/primitives/created-files-card/index.d.ts +0 -1
  421. package/dist/components/primitives/cron-job-card/cron-job-card.d.ts +0 -30
  422. package/dist/components/primitives/cron-job-card/index.d.ts +0 -1
  423. package/dist/components/primitives/diff-viewer/diff-viewer.d.ts +0 -45
  424. package/dist/components/primitives/diff-viewer/index.d.ts +0 -1
  425. package/dist/components/primitives/export-chat-dialog/export-chat-dialog.d.ts +0 -18
  426. package/dist/components/primitives/export-chat-dialog/index.d.ts +0 -1
  427. package/dist/components/primitives/folder-context-card/folder-context-card.d.ts +0 -40
  428. package/dist/components/primitives/folder-context-card/index.d.ts +0 -1
  429. package/dist/components/primitives/folder-selector/folder-selector.d.ts +0 -18
  430. package/dist/components/primitives/folder-selector/index.d.ts +0 -1
  431. package/dist/components/primitives/gateway-status-indicator/gateway-status-indicator.d.ts +0 -19
  432. package/dist/components/primitives/gateway-status-indicator/index.d.ts +0 -1
  433. package/dist/components/primitives/hook-config/hook-config.d.ts +0 -27
  434. package/dist/components/primitives/hook-config/index.d.ts +0 -1
  435. package/dist/components/primitives/hook-event-log/hook-event-log.d.ts +0 -27
  436. package/dist/components/primitives/hook-event-log/index.d.ts +0 -1
  437. package/dist/components/primitives/intent-selector/index.d.ts +0 -1
  438. package/dist/components/primitives/intent-selector/intent-selector.d.ts +0 -22
  439. package/dist/components/primitives/lane-board/index.d.ts +0 -1
  440. package/dist/components/primitives/lane-board/lane-board.d.ts +0 -26
  441. package/dist/components/primitives/mcp-server-card/index.d.ts +0 -1
  442. package/dist/components/primitives/mcp-server-card/mcp-server-card.d.ts +0 -32
  443. package/dist/components/primitives/memory-editor/index.d.ts +0 -1
  444. package/dist/components/primitives/memory-editor/memory-editor.d.ts +0 -29
  445. package/dist/components/primitives/mention-menu/index.d.ts +0 -1
  446. package/dist/components/primitives/mention-menu/mention-menu.d.ts +0 -44
  447. package/dist/components/primitives/model-card/index.d.ts +0 -1
  448. package/dist/components/primitives/model-card/model-card.d.ts +0 -72
  449. package/dist/components/primitives/model-selector/index.d.ts +0 -1
  450. package/dist/components/primitives/model-selector/model-selector.d.ts +0 -21
  451. package/dist/components/primitives/permission-matrix/index.d.ts +0 -1
  452. package/dist/components/primitives/permission-matrix/permission-matrix.d.ts +0 -43
  453. package/dist/components/primitives/progress-checklist/index.d.ts +0 -1
  454. package/dist/components/primitives/progress-checklist/progress-checklist.d.ts +0 -18
  455. package/dist/components/primitives/project-switcher/index.d.ts +0 -1
  456. package/dist/components/primitives/project-switcher/project-switcher.d.ts +0 -32
  457. package/dist/components/primitives/quick-action-chips/index.d.ts +0 -1
  458. package/dist/components/primitives/quick-action-chips/quick-action-chips.d.ts +0 -22
  459. package/dist/components/primitives/recent-folders-list/index.d.ts +0 -1
  460. package/dist/components/primitives/recent-folders-list/recent-folders-list.d.ts +0 -21
  461. package/dist/components/primitives/rule-card/index.d.ts +0 -1
  462. package/dist/components/primitives/rule-card/rule-card.d.ts +0 -15
  463. package/dist/components/primitives/run-stats/index.d.ts +0 -1
  464. package/dist/components/primitives/run-stats/run-stats.d.ts +0 -16
  465. package/dist/components/primitives/run-status-pill/index.d.ts +0 -1
  466. package/dist/components/primitives/run-status-pill/run-status-pill.d.ts +0 -13
  467. package/dist/components/primitives/running-tasks-panel/index.d.ts +0 -1
  468. package/dist/components/primitives/running-tasks-panel/running-tasks-panel.d.ts +0 -20
  469. package/dist/components/primitives/session-list-item/index.d.ts +0 -1
  470. package/dist/components/primitives/session-list-item/session-list-item.d.ts +0 -35
  471. package/dist/components/primitives/session-timeline/index.d.ts +0 -1
  472. package/dist/components/primitives/session-timeline/session-timeline.d.ts +0 -32
  473. package/dist/components/primitives/skill-card/index.d.ts +0 -1
  474. package/dist/components/primitives/skill-card/skill-card.d.ts +0 -28
  475. package/dist/components/primitives/slide/alerts.d.ts +0 -31
  476. package/dist/components/primitives/slide/frontmatter.d.ts +0 -23
  477. package/dist/components/primitives/slide/index.d.ts +0 -16
  478. package/dist/components/primitives/slide/json-schema.d.ts +0 -9
  479. package/dist/components/primitives/slide/marpit-bg.d.ts +0 -36
  480. package/dist/components/primitives/slide/parse.d.ts +0 -59
  481. package/dist/components/primitives/slide/plugin.d.ts +0 -79
  482. package/dist/components/primitives/slide/plugins/emoji/index.d.ts +0 -8
  483. package/dist/components/primitives/slide/plugins/emoji/map.d.ts +0 -15
  484. package/dist/components/primitives/slide/plugins/math/index.d.ts +0 -6
  485. package/dist/components/primitives/slide/plugins/mermaid/index.d.ts +0 -48
  486. package/dist/components/primitives/slide/plugins/shiki/index.d.ts +0 -11
  487. package/dist/components/primitives/slide/sanitize.d.ts +0 -33
  488. package/dist/components/primitives/slide/schema.d.ts +0 -109
  489. package/dist/components/primitives/slide/slide.d.ts +0 -53
  490. package/dist/components/primitives/slide/themes/index.d.ts +0 -9
  491. package/dist/components/primitives/slide/use-slide-fit.d.ts +0 -21
  492. package/dist/components/primitives/slide/validate.d.ts +0 -33
  493. package/dist/components/primitives/steps-rail/index.d.ts +0 -1
  494. package/dist/components/primitives/steps-rail/steps-rail.d.ts +0 -24
  495. package/dist/components/primitives/sub-agent-dispatch/index.d.ts +0 -1
  496. package/dist/components/primitives/sub-agent-dispatch/sub-agent-dispatch.d.ts +0 -30
  497. package/dist/components/primitives/system-prompt-editor/index.d.ts +0 -1
  498. package/dist/components/primitives/system-prompt-editor/system-prompt-editor.d.ts +0 -25
  499. package/dist/components/primitives/task-plan/index.d.ts +0 -1
  500. package/dist/components/primitives/task-plan/task-plan.d.ts +0 -36
  501. package/dist/components/primitives/terminal-panel/index.d.ts +0 -1
  502. package/dist/components/primitives/terminal-panel/terminal-panel.d.ts +0 -32
  503. package/dist/components/primitives/thinking-level-selector/index.d.ts +0 -1
  504. package/dist/components/primitives/thinking-level-selector/thinking-level-selector.d.ts +0 -24
  505. package/dist/components/primitives/token-usage-chart/index.d.ts +0 -2
  506. package/dist/components/primitives/token-usage-chart/token-usage-chart.d.ts +0 -44
  507. package/dist/components/primitives/token-usage-chart/usage-metrics.d.ts +0 -23
  508. package/dist/components/primitives/tool-call/index.d.ts +0 -1
  509. package/dist/components/primitives/tool-call/tool-call.d.ts +0 -30
  510. package/dist/components/primitives/tool-call-card/index.d.ts +0 -1
  511. package/dist/components/primitives/tool-call-card/tool-call-card.d.ts +0 -30
  512. package/dist/components/primitives/tool-result/index.d.ts +0 -1
  513. package/dist/components/primitives/tool-result/tool-result.d.ts +0 -19
  514. package/dist/components/primitives/tools-list/index.d.ts +0 -1
  515. package/dist/components/primitives/tools-list/tools-list.d.ts +0 -32
  516. package/dist/components/primitives/whiteboard/index.d.ts +0 -2
  517. package/dist/components/primitives/whiteboard/render/freedraw.d.ts +0 -2
  518. package/dist/components/primitives/whiteboard/render/line.d.ts +0 -4
  519. package/dist/components/primitives/whiteboard/render/rough-paths.d.ts +0 -16
  520. package/dist/components/primitives/whiteboard/render/scene.d.ts +0 -3
  521. package/dist/components/primitives/whiteboard/render/shape.d.ts +0 -5
  522. package/dist/components/primitives/whiteboard/render/style.d.ts +0 -24
  523. package/dist/components/primitives/whiteboard/render/text.d.ts +0 -2
  524. package/dist/components/primitives/whiteboard/schema.d.ts +0 -596
  525. package/dist/components/primitives/whiteboard/seed.d.ts +0 -22
  526. package/dist/components/primitives/whiteboard/validate.d.ts +0 -26
  527. package/dist/components/primitives/whiteboard/viewport/use-pointer-pan.d.ts +0 -11
  528. package/dist/components/primitives/whiteboard/viewport/use-viewport.d.ts +0 -33
  529. package/dist/components/primitives/whiteboard/whiteboard.d.ts +0 -21
  530. package/dist/components.css +0 -2
  531. package/dist/composites/account-menu/index.js +0 -6
  532. package/dist/composites/account-menu/index.js.map +0 -1
  533. package/dist/composites/agent-composer/index.js +0 -8
  534. package/dist/composites/agent-composer/index.js.map +0 -1
  535. package/dist/composites/agent-editor/index.js +0 -11
  536. package/dist/composites/agent-editor/index.js.map +0 -1
  537. package/dist/composites/agent-stream/index.js +0 -24
  538. package/dist/composites/agent-stream/index.js.map +0 -1
  539. package/dist/composites/agent-timeline/index.js +0 -5
  540. package/dist/composites/agent-timeline/index.js.map +0 -1
  541. package/dist/composites/agent-tool-renderer/index.js +0 -17
  542. package/dist/composites/agent-tool-renderer/index.js.map +0 -1
  543. package/dist/composites/approval-card/index.js +0 -5
  544. package/dist/composites/approval-card/index.js.map +0 -1
  545. package/dist/composites/chat-composer/index.js +0 -6
  546. package/dist/composites/chat-composer/index.js.map +0 -1
  547. package/dist/composites/chat-message/index.js +0 -20
  548. package/dist/composites/chat-message/index.js.map +0 -1
  549. package/dist/composites/choice-prompt/index.js +0 -11
  550. package/dist/composites/choice-prompt/index.js.map +0 -1
  551. package/dist/composites/code-block/index.js +0 -5
  552. package/dist/composites/code-block/index.js.map +0 -1
  553. package/dist/composites/command-palette/index.js +0 -6
  554. package/dist/composites/command-palette/index.js.map +0 -1
  555. package/dist/composites/confirm-dialog/index.js +0 -8
  556. package/dist/composites/confirm-dialog/index.js.map +0 -1
  557. package/dist/composites/confirm-prompt/index.js +0 -7
  558. package/dist/composites/confirm-prompt/index.js.map +0 -1
  559. package/dist/composites/cron-jobs-list/index.js +0 -5
  560. package/dist/composites/cron-jobs-list/index.js.map +0 -1
  561. package/dist/composites/data-table/index.js +0 -11
  562. package/dist/composites/data-table/index.js.map +0 -1
  563. package/dist/composites/deployment-row/index.js +0 -5
  564. package/dist/composites/deployment-row/index.js.map +0 -1
  565. package/dist/composites/domain-config/index.js +0 -8
  566. package/dist/composites/domain-config/index.js.map +0 -1
  567. package/dist/composites/env-var-editor/index.js +0 -9
  568. package/dist/composites/env-var-editor/index.js.map +0 -1
  569. package/dist/composites/mcp-server-list/index.js +0 -6
  570. package/dist/composites/mcp-server-list/index.js.map +0 -1
  571. package/dist/composites/metric-card/index.js +0 -5
  572. package/dist/composites/metric-card/index.js.map +0 -1
  573. package/dist/composites/multi-select-prompt/index.js +0 -11
  574. package/dist/composites/multi-select-prompt/index.js.map +0 -1
  575. package/dist/composites/page-shell/index.js +0 -8
  576. package/dist/composites/page-shell/index.js.map +0 -1
  577. package/dist/composites/permission-modal/index.js +0 -7
  578. package/dist/composites/permission-modal/index.js.map +0 -1
  579. package/dist/composites/preview-env-card/index.js +0 -6
  580. package/dist/composites/preview-env-card/index.js.map +0 -1
  581. package/dist/composites/preview-panel/index.js +0 -5
  582. package/dist/composites/preview-panel/index.js.map +0 -1
  583. package/dist/composites/project-card/index.js +0 -6
  584. package/dist/composites/project-card/index.js.map +0 -1
  585. package/dist/composites/rollback-ui/index.js +0 -7
  586. package/dist/composites/rollback-ui/index.js.map +0 -1
  587. package/dist/composites/rule-editor/index.js +0 -12
  588. package/dist/composites/rule-editor/index.js.map +0 -1
  589. package/dist/composites/skill-editor/index.js +0 -12
  590. package/dist/composites/skill-editor/index.js.map +0 -1
  591. package/dist/composites/skills-list/index.js +0 -6
  592. package/dist/composites/skills-list/index.js.map +0 -1
  593. package/dist/composites/stability-bundle-viewer/index.js +0 -5
  594. package/dist/composites/stability-bundle-viewer/index.js.map +0 -1
  595. package/dist/composites/status-indicator/index.js +0 -4
  596. package/dist/composites/status-indicator/index.js.map +0 -1
  597. package/dist/composites/task-header/index.js +0 -5
  598. package/dist/composites/task-header/index.js.map +0 -1
  599. package/dist/composites/text-prompt/index.js +0 -10
  600. package/dist/composites/text-prompt/index.js.map +0 -1
  601. package/dist/composites/usage-meter/index.js +0 -5
  602. package/dist/composites/usage-meter/index.js.map +0 -1
  603. package/dist/fonts/LICENSE-GEIST.txt +0 -92
  604. package/dist/fonts/geist-400.woff2 +0 -0
  605. package/dist/fonts/geist-500.woff2 +0 -0
  606. package/dist/fonts/geist-600.woff2 +0 -0
  607. package/dist/fonts/geist-mono-400.woff2 +0 -0
  608. package/dist/fonts/geist-mono-500.woff2 +0 -0
  609. package/dist/fonts/geist-mono-600.woff2 +0 -0
  610. package/dist/fonts-cdn.css +0 -28
  611. package/dist/fonts.css +0 -75
  612. package/dist/index.js.map +0 -1
  613. package/dist/lib/markdown/code-block.d.ts +0 -14
  614. package/dist/lib/markdown/inline-code.d.ts +0 -10
  615. package/dist/lib/markdown/math.d.ts +0 -12
  616. package/dist/lib/markdown/mermaid.d.ts +0 -6
  617. package/dist/lib/markdown/parser.d.ts +0 -47
  618. package/dist/lib/markdown/streaming-preprocess.d.ts +0 -39
  619. package/dist/lib/prompt.d.ts +0 -40
  620. package/dist/lib/sdk-tools-adapters/index.d.ts +0 -1
  621. package/dist/lib/sdk-tools-adapters/index.js +0 -3
  622. package/dist/lib/sdk-tools-adapters/index.js.map +0 -1
  623. package/dist/lib/sdk-tools-adapters/sdk-tools-adapters.d.ts +0 -60
  624. package/dist/preset-v3-legacy.d.ts +0 -32
  625. package/dist/preset-v3-legacy.js.map +0 -1
  626. package/dist/preset.css +0 -27
  627. package/dist/primitives/action-bar/index.js +0 -4
  628. package/dist/primitives/action-bar/index.js.map +0 -1
  629. package/dist/primitives/agent-error-card/index.js +0 -5
  630. package/dist/primitives/agent-error-card/index.js.map +0 -1
  631. package/dist/primitives/agent-event/index.js +0 -5
  632. package/dist/primitives/agent-event/index.js.map +0 -1
  633. package/dist/primitives/agent-handoff/index.js +0 -4
  634. package/dist/primitives/agent-handoff/index.js.map +0 -1
  635. package/dist/primitives/agent-profile/index.js +0 -4
  636. package/dist/primitives/agent-profile/index.js.map +0 -1
  637. package/dist/primitives/agent-starting-state/index.js +0 -5
  638. package/dist/primitives/agent-starting-state/index.js.map +0 -1
  639. package/dist/primitives/agent-streaming/index.js +0 -5
  640. package/dist/primitives/agent-streaming/index.js.map +0 -1
  641. package/dist/primitives/alert/index.js +0 -4
  642. package/dist/primitives/alert/index.js.map +0 -1
  643. package/dist/primitives/artifact-preview/index.js +0 -4
  644. package/dist/primitives/artifact-preview/index.js.map +0 -1
  645. package/dist/primitives/attachment-chip/index.js +0 -4
  646. package/dist/primitives/attachment-chip/index.js.map +0 -1
  647. package/dist/primitives/audit-log-entry/index.js +0 -4
  648. package/dist/primitives/audit-log-entry/index.js.map +0 -1
  649. package/dist/primitives/auto-compact-notice/index.js +0 -5
  650. package/dist/primitives/auto-compact-notice/index.js.map +0 -1
  651. package/dist/primitives/avatar/index.js +0 -4
  652. package/dist/primitives/avatar/index.js.map +0 -1
  653. package/dist/primitives/badge/index.js +0 -4
  654. package/dist/primitives/badge/index.js.map +0 -1
  655. package/dist/primitives/branch-indicator/index.js +0 -4
  656. package/dist/primitives/branch-indicator/index.js.map +0 -1
  657. package/dist/primitives/browser-controls/index.js +0 -4
  658. package/dist/primitives/browser-controls/index.js.map +0 -1
  659. package/dist/primitives/build-log-stream/index.js +0 -7
  660. package/dist/primitives/build-log-stream/index.js.map +0 -1
  661. package/dist/primitives/button/index.js +0 -4
  662. package/dist/primitives/button/index.js.map +0 -1
  663. package/dist/primitives/capability-indicator/index.js +0 -4
  664. package/dist/primitives/capability-indicator/index.js.map +0 -1
  665. package/dist/primitives/card/index.js +0 -5
  666. package/dist/primitives/card/index.js.map +0 -1
  667. package/dist/primitives/channel-card/index.js +0 -4
  668. package/dist/primitives/channel-card/index.js.map +0 -1
  669. package/dist/primitives/chat-thread/index.js +0 -5
  670. package/dist/primitives/chat-thread/index.js.map +0 -1
  671. package/dist/primitives/checkbox/index.js +0 -4
  672. package/dist/primitives/checkbox/index.js.map +0 -1
  673. package/dist/primitives/context-card/index.js +0 -4
  674. package/dist/primitives/context-card/index.js.map +0 -1
  675. package/dist/primitives/context-window-bar/index.js +0 -4
  676. package/dist/primitives/context-window-bar/index.js.map +0 -1
  677. package/dist/primitives/copy-button/index.js +0 -5
  678. package/dist/primitives/copy-button/index.js.map +0 -1
  679. package/dist/primitives/cost-meter/index.js +0 -4
  680. package/dist/primitives/cost-meter/index.js.map +0 -1
  681. package/dist/primitives/created-files-card/index.js +0 -4
  682. package/dist/primitives/created-files-card/index.js.map +0 -1
  683. package/dist/primitives/cron-job-card/index.js +0 -4
  684. package/dist/primitives/cron-job-card/index.js.map +0 -1
  685. package/dist/primitives/danger-zone/index.js +0 -4
  686. package/dist/primitives/danger-zone/index.js.map +0 -1
  687. package/dist/primitives/dialog/index.js +0 -4
  688. package/dist/primitives/dialog/index.js.map +0 -1
  689. package/dist/primitives/diff-viewer/index.js +0 -4
  690. package/dist/primitives/diff-viewer/index.js.map +0 -1
  691. package/dist/primitives/dropdown-menu/index.js +0 -4
  692. package/dist/primitives/dropdown-menu/index.js.map +0 -1
  693. package/dist/primitives/empty-state/index.js +0 -4
  694. package/dist/primitives/empty-state/index.js.map +0 -1
  695. package/dist/primitives/export-chat-dialog/index.js +0 -5
  696. package/dist/primitives/export-chat-dialog/index.js.map +0 -1
  697. package/dist/primitives/folder-context-card/index.js +0 -4
  698. package/dist/primitives/folder-context-card/index.js.map +0 -1
  699. package/dist/primitives/folder-selector/index.js +0 -4
  700. package/dist/primitives/folder-selector/index.js.map +0 -1
  701. package/dist/primitives/form-field/index.js +0 -5
  702. package/dist/primitives/form-field/index.js.map +0 -1
  703. package/dist/primitives/gateway-status-indicator/index.js +0 -4
  704. package/dist/primitives/gateway-status-indicator/index.js.map +0 -1
  705. package/dist/primitives/hook-config/index.js +0 -5
  706. package/dist/primitives/hook-config/index.js.map +0 -1
  707. package/dist/primitives/hook-event-log/index.js +0 -4
  708. package/dist/primitives/hook-event-log/index.js.map +0 -1
  709. package/dist/primitives/input/index.js +0 -4
  710. package/dist/primitives/input/index.js.map +0 -1
  711. package/dist/primitives/intent-selector/index.js +0 -4
  712. package/dist/primitives/intent-selector/index.js.map +0 -1
  713. package/dist/primitives/label/index.js +0 -4
  714. package/dist/primitives/label/index.js.map +0 -1
  715. package/dist/primitives/lane-board/index.js +0 -4
  716. package/dist/primitives/lane-board/index.js.map +0 -1
  717. package/dist/primitives/login-split/index.js +0 -4
  718. package/dist/primitives/login-split/index.js.map +0 -1
  719. package/dist/primitives/mcp-server-card/index.js +0 -4
  720. package/dist/primitives/mcp-server-card/index.js.map +0 -1
  721. package/dist/primitives/memory-editor/index.js +0 -4
  722. package/dist/primitives/memory-editor/index.js.map +0 -1
  723. package/dist/primitives/mention-menu/index.js +0 -5
  724. package/dist/primitives/mention-menu/index.js.map +0 -1
  725. package/dist/primitives/metrics-panel/index.js +0 -4
  726. package/dist/primitives/metrics-panel/index.js.map +0 -1
  727. package/dist/primitives/model-card/index.js +0 -4
  728. package/dist/primitives/model-card/index.js.map +0 -1
  729. package/dist/primitives/model-selector/index.js +0 -4
  730. package/dist/primitives/model-selector/index.js.map +0 -1
  731. package/dist/primitives/pagination/index.js +0 -4
  732. package/dist/primitives/pagination/index.js.map +0 -1
  733. package/dist/primitives/permission-matrix/index.js +0 -5
  734. package/dist/primitives/permission-matrix/index.js.map +0 -1
  735. package/dist/primitives/pin-input/index.js +0 -5
  736. package/dist/primitives/pin-input/index.js.map +0 -1
  737. package/dist/primitives/plan-badge/index.js +0 -4
  738. package/dist/primitives/plan-badge/index.js.map +0 -1
  739. package/dist/primitives/progress/index.js +0 -4
  740. package/dist/primitives/progress/index.js.map +0 -1
  741. package/dist/primitives/progress-checklist/index.js +0 -4
  742. package/dist/primitives/progress-checklist/index.js.map +0 -1
  743. package/dist/primitives/project-switcher/index.js +0 -4
  744. package/dist/primitives/project-switcher/index.js.map +0 -1
  745. package/dist/primitives/quick-action-chips/index.js +0 -4
  746. package/dist/primitives/quick-action-chips/index.js.map +0 -1
  747. package/dist/primitives/radio-group/index.js +0 -4
  748. package/dist/primitives/radio-group/index.js.map +0 -1
  749. package/dist/primitives/recent-folders-list/index.js +0 -4
  750. package/dist/primitives/recent-folders-list/index.js.map +0 -1
  751. package/dist/primitives/rule-card/index.js +0 -4
  752. package/dist/primitives/rule-card/index.js.map +0 -1
  753. package/dist/primitives/run-stats/index.js +0 -4
  754. package/dist/primitives/run-stats/index.js.map +0 -1
  755. package/dist/primitives/run-status-pill/index.js +0 -4
  756. package/dist/primitives/run-status-pill/index.js.map +0 -1
  757. package/dist/primitives/running-tasks-panel/index.js +0 -4
  758. package/dist/primitives/running-tasks-panel/index.js.map +0 -1
  759. package/dist/primitives/scroll-area/index.js +0 -5
  760. package/dist/primitives/scroll-area/index.js.map +0 -1
  761. package/dist/primitives/select/index.js +0 -4
  762. package/dist/primitives/select/index.js.map +0 -1
  763. package/dist/primitives/session-list-item/index.js +0 -4
  764. package/dist/primitives/session-list-item/index.js.map +0 -1
  765. package/dist/primitives/session-timeline/index.js +0 -4
  766. package/dist/primitives/session-timeline/index.js.map +0 -1
  767. package/dist/primitives/sheet/index.js +0 -4
  768. package/dist/primitives/sheet/index.js.map +0 -1
  769. package/dist/primitives/sidebar/index.js +0 -4
  770. package/dist/primitives/sidebar/index.js.map +0 -1
  771. package/dist/primitives/skeleton/index.js +0 -5
  772. package/dist/primitives/skeleton/index.js.map +0 -1
  773. package/dist/primitives/skill-card/index.js +0 -4
  774. package/dist/primitives/skill-card/index.js.map +0 -1
  775. package/dist/primitives/social-auth-row/index.js +0 -4
  776. package/dist/primitives/social-auth-row/index.js.map +0 -1
  777. package/dist/primitives/stat-tile/index.js +0 -4
  778. package/dist/primitives/stat-tile/index.js.map +0 -1
  779. package/dist/primitives/status-dot/index.js +0 -6
  780. package/dist/primitives/status-dot/index.js.map +0 -1
  781. package/dist/primitives/steps-rail/index.js +0 -4
  782. package/dist/primitives/steps-rail/index.js.map +0 -1
  783. package/dist/primitives/sub-agent-dispatch/index.js +0 -4
  784. package/dist/primitives/sub-agent-dispatch/index.js.map +0 -1
  785. package/dist/primitives/switch/index.js +0 -4
  786. package/dist/primitives/switch/index.js.map +0 -1
  787. package/dist/primitives/system-prompt-editor/index.js +0 -5
  788. package/dist/primitives/system-prompt-editor/index.js.map +0 -1
  789. package/dist/primitives/table/index.js +0 -5
  790. package/dist/primitives/table/index.js.map +0 -1
  791. package/dist/primitives/tabs/index.js +0 -4
  792. package/dist/primitives/tabs/index.js.map +0 -1
  793. package/dist/primitives/task-plan/index.js +0 -4
  794. package/dist/primitives/task-plan/index.js.map +0 -1
  795. package/dist/primitives/terminal-panel/index.js +0 -5
  796. package/dist/primitives/terminal-panel/index.js.map +0 -1
  797. package/dist/primitives/textarea/index.js +0 -4
  798. package/dist/primitives/textarea/index.js.map +0 -1
  799. package/dist/primitives/thinking-level-selector/index.js +0 -5
  800. package/dist/primitives/thinking-level-selector/index.js.map +0 -1
  801. package/dist/primitives/timestamp/index.js +0 -6
  802. package/dist/primitives/timestamp/index.js.map +0 -1
  803. package/dist/primitives/toast/index.js +0 -5
  804. package/dist/primitives/toast/index.js.map +0 -1
  805. package/dist/primitives/token-usage-chart/index.js +0 -4
  806. package/dist/primitives/token-usage-chart/index.js.map +0 -1
  807. package/dist/primitives/tool-call/index.js +0 -5
  808. package/dist/primitives/tool-call/index.js.map +0 -1
  809. package/dist/primitives/tool-call-card/index.js +0 -5
  810. package/dist/primitives/tool-call-card/index.js.map +0 -1
  811. package/dist/primitives/tool-result/index.js +0 -4
  812. package/dist/primitives/tool-result/index.js.map +0 -1
  813. package/dist/primitives/tools-list/index.js +0 -4
  814. package/dist/primitives/tools-list/index.js.map +0 -1
  815. package/dist/primitives/tooltip/index.js +0 -4
  816. package/dist/primitives/tooltip/index.js.map +0 -1
  817. package/dist/primitives/topnav/index.js +0 -4
  818. package/dist/primitives/topnav/index.js.map +0 -1
  819. package/dist/primitives/update-banner/index.js +0 -4
  820. package/dist/primitives/update-banner/index.js.map +0 -1
  821. package/dist/screens/theo-code-shell.d.ts +0 -9
  822. package/dist/screens/theo-code-shell.data.d.ts +0 -147
  823. package/dist/slide/index.js +0 -4
  824. package/dist/slide/index.js.map +0 -1
  825. package/dist/slide/plugins/emoji/index.js +0 -157
  826. package/dist/slide/plugins/emoji/index.js.map +0 -1
  827. package/dist/slide/plugins/math/index.js +0 -145
  828. package/dist/slide/plugins/math/index.js.map +0 -1
  829. package/dist/slide/plugins/mermaid/index.js +0 -218
  830. package/dist/slide/plugins/mermaid/index.js.map +0 -1
  831. package/dist/slide/plugins/shiki/index.js +0 -87
  832. package/dist/slide/plugins/shiki/index.js.map +0 -1
  833. package/dist/slide/themes/default.css +0 -256
  834. package/dist/slide/themes/layouts.css +0 -143
  835. package/dist/slide/themes/violet-forge.css +0 -256
  836. package/dist/slide-deck/index.css +0 -52
  837. package/dist/slide-deck/index.css.map +0 -1
  838. package/dist/slide-deck/index.js +0 -1122
  839. package/dist/slide-deck/index.js.map +0 -1
  840. package/dist/styles-v3-legacy.css +0 -88
  841. package/dist/styles.css +0 -137
  842. package/dist/themes/anthropic-style.d.ts +0 -11
  843. package/dist/themes/aurora-terminal.d.ts +0 -9
  844. package/dist/themes/classic-paper.d.ts +0 -17
  845. package/dist/themes/color-value-pattern.d.ts +0 -22
  846. package/dist/themes/color.d.ts +0 -62
  847. package/dist/themes/define.d.ts +0 -65
  848. package/dist/themes/density.d.ts +0 -17
  849. package/dist/themes/dracula.d.ts +0 -14
  850. package/dist/themes/github-dark.d.ts +0 -11
  851. package/dist/themes/index.d.ts +0 -26
  852. package/dist/themes/linear-glass.d.ts +0 -11
  853. package/dist/themes/one-dark.d.ts +0 -11
  854. package/dist/themes/openai-style.d.ts +0 -10
  855. package/dist/themes/schema.d.ts +0 -112
  856. package/dist/themes/theme-provider.d.ts +0 -82
  857. package/dist/themes/theme-script.d.ts +0 -48
  858. package/dist/themes/theme-switcher.d.ts +0 -17
  859. package/dist/themes/types.d.ts +0 -88
  860. package/dist/themes/vercel-mono.d.ts +0 -11
  861. package/dist/themes/violet-forge.d.ts +0 -12
  862. package/dist/theo-ui-provider.d.ts +0 -50
  863. package/dist/tokens-v4.css +0 -223
  864. package/dist/tokens.css +0 -315
  865. package/dist/types/agent.d.ts +0 -19
  866. package/dist/types/chat.d.ts +0 -173
  867. package/dist/types/mode.d.ts +0 -16
  868. package/dist/types/permission.d.ts +0 -8
  869. package/dist/types/rule.d.ts +0 -28
  870. package/dist/vite-plugin.d.ts +0 -25
  871. package/dist/vite-plugin.js +0 -76
  872. package/dist/vite-plugin.js.map +0 -1
  873. package/dist/whiteboard/index.js +0 -741
  874. package/dist/whiteboard/index.js.map +0 -1
  875. package/llms.txt +0 -273
  876. package/registry/index.json +0 -910
  877. package/registry/r/account-menu.json +0 -24
  878. package/registry/r/action-bar.json +0 -22
  879. package/registry/r/agent-composer.json +0 -22
  880. package/registry/r/agent-editor.json +0 -27
  881. package/registry/r/agent-error-card.json +0 -22
  882. package/registry/r/agent-event.json +0 -24
  883. package/registry/r/agent-handoff.json +0 -22
  884. package/registry/r/agent-profile.json +0 -23
  885. package/registry/r/agent-starting-state.json +0 -22
  886. package/registry/r/agent-stream.json +0 -27
  887. package/registry/r/agent-streaming.json +0 -22
  888. package/registry/r/agent-timeline.json +0 -22
  889. package/registry/r/agent-tool-renderer.json +0 -40
  890. package/registry/r/agent-types.json +0 -15
  891. package/registry/r/alert.json +0 -22
  892. package/registry/r/approval-card.json +0 -25
  893. package/registry/r/artifact-preview.json +0 -22
  894. package/registry/r/attachment-chip.json +0 -24
  895. package/registry/r/audit-log-entry.json +0 -23
  896. package/registry/r/auto-compact-notice.json +0 -22
  897. package/registry/r/avatar.json +0 -23
  898. package/registry/r/badge.json +0 -22
  899. package/registry/r/browser-controls.json +0 -22
  900. package/registry/r/build-log-stream.json +0 -20
  901. package/registry/r/button.json +0 -23
  902. package/registry/r/capability-indicator.json +0 -23
  903. package/registry/r/card.json +0 -22
  904. package/registry/r/chat-composer.json +0 -23
  905. package/registry/r/chat-message.json +0 -124
  906. package/registry/r/chat-thread.json +0 -20
  907. package/registry/r/chat-types.json +0 -15
  908. package/registry/r/checkbox.json +0 -24
  909. package/registry/r/choice-prompt.json +0 -25
  910. package/registry/r/cn.json +0 -19
  911. package/registry/r/code-block.json +0 -21
  912. package/registry/r/command-palette.json +0 -25
  913. package/registry/r/confirm-dialog.json +0 -25
  914. package/registry/r/confirm-prompt.json +0 -21
  915. package/registry/r/context-card.json +0 -23
  916. package/registry/r/context-window-bar.json +0 -20
  917. package/registry/r/copy-button.json +0 -22
  918. package/registry/r/cost-meter.json +0 -22
  919. package/registry/r/created-files-card.json +0 -23
  920. package/registry/r/cron-job-card.json +0 -22
  921. package/registry/r/cron-jobs-list.json +0 -23
  922. package/registry/r/danger-zone.json +0 -20
  923. package/registry/r/data-table.json +0 -27
  924. package/registry/r/deployment-row.json +0 -23
  925. package/registry/r/dialog.json +0 -23
  926. package/registry/r/diff-viewer.json +0 -20
  927. package/registry/r/domain-config.json +0 -25
  928. package/registry/r/dropdown-menu.json +0 -23
  929. package/registry/r/empty-state.json +0 -20
  930. package/registry/r/env-var-editor.json +0 -26
  931. package/registry/r/env.json +0 -15
  932. package/registry/r/folder-context-card.json +0 -23
  933. package/registry/r/folder-selector.json +0 -22
  934. package/registry/r/form-field.json +0 -23
  935. package/registry/r/hook-config.json +0 -22
  936. package/registry/r/hook-event-log.json +0 -22
  937. package/registry/r/input.json +0 -22
  938. package/registry/r/intent-selector.json +0 -24
  939. package/registry/r/label.json +0 -22
  940. package/registry/r/lane-board.json +0 -20
  941. package/registry/r/live-region-context.json +0 -16
  942. package/registry/r/login-split.json +0 -20
  943. package/registry/r/mcp-server-card.json +0 -22
  944. package/registry/r/mcp-server-list.json +0 -23
  945. package/registry/r/memory-editor.json +0 -23
  946. package/registry/r/mention-menu.json +0 -23
  947. package/registry/r/metric-card.json +0 -23
  948. package/registry/r/metrics-panel.json +0 -22
  949. package/registry/r/mode-types.json +0 -15
  950. package/registry/r/model-card.json +0 -23
  951. package/registry/r/model-selector.json +0 -23
  952. package/registry/r/multi-select-prompt.json +0 -25
  953. package/registry/r/page-shell.json +0 -25
  954. package/registry/r/pagination.json +0 -22
  955. package/registry/r/permission-matrix.json +0 -22
  956. package/registry/r/permission-modal.json +0 -24
  957. package/registry/r/permission-types.json +0 -15
  958. package/registry/r/pin-input.json +0 -20
  959. package/registry/r/plan-badge.json +0 -20
  960. package/registry/r/preview-env-card.json +0 -25
  961. package/registry/r/preview-panel.json +0 -21
  962. package/registry/r/progress-checklist.json +0 -23
  963. package/registry/r/progress.json +0 -20
  964. package/registry/r/project-card.json +0 -25
  965. package/registry/r/project-switcher.json +0 -22
  966. package/registry/r/prompt.json +0 -15
  967. package/registry/r/quick-action-chips.json +0 -21
  968. package/registry/r/radio-group.json +0 -23
  969. package/registry/r/recent-folders-list.json +0 -22
  970. package/registry/r/rollback-ui.json +0 -24
  971. package/registry/r/rule-card.json +0 -23
  972. package/registry/r/rule-editor.json +0 -28
  973. package/registry/r/rule-types.json +0 -18
  974. package/registry/r/run-stats.json +0 -22
  975. package/registry/r/running-tasks-panel.json +0 -22
  976. package/registry/r/safe-href.json +0 -16
  977. package/registry/r/scroll-area.json +0 -22
  978. package/registry/r/select.json +0 -24
  979. package/registry/r/session-list-item.json +0 -20
  980. package/registry/r/session-timeline.json +0 -22
  981. package/registry/r/sheet.json +0 -24
  982. package/registry/r/sidebar.json +0 -19
  983. package/registry/r/skeleton.json +0 -19
  984. package/registry/r/skill-card.json +0 -24
  985. package/registry/r/skill-editor.json +0 -28
  986. package/registry/r/skills-list.json +0 -23
  987. package/registry/r/slide-deck.json +0 -130
  988. package/registry/r/slide-plugin-emoji.json +0 -28
  989. package/registry/r/slide-plugin-math.json +0 -24
  990. package/registry/r/slide-plugin-mermaid.json +0 -23
  991. package/registry/r/slide-plugin-shiki.json +0 -23
  992. package/registry/r/slide.json +0 -123
  993. package/registry/r/social-auth-row.json +0 -21
  994. package/registry/r/stat-tile.json +0 -22
  995. package/registry/r/status-dot.json +0 -21
  996. package/registry/r/status-indicator.json +0 -20
  997. package/registry/r/steps-rail.json +0 -20
  998. package/registry/r/sub-agent-dispatch.json +0 -22
  999. package/registry/r/switch.json +0 -23
  1000. package/registry/r/system-prompt-editor.json +0 -22
  1001. package/registry/r/table.json +0 -22
  1002. package/registry/r/tabs.json +0 -22
  1003. package/registry/r/tailwind-preset.json +0 -19
  1004. package/registry/r/task-header.json +0 -24
  1005. package/registry/r/task-plan.json +0 -22
  1006. package/registry/r/task-types.json +0 -15
  1007. package/registry/r/terminal-panel.json +0 -22
  1008. package/registry/r/text-prompt.json +0 -24
  1009. package/registry/r/textarea.json +0 -22
  1010. package/registry/r/theme-provider.json +0 -60
  1011. package/registry/r/theme-script.json +0 -18
  1012. package/registry/r/theo-ui-provider.json +0 -20
  1013. package/registry/r/timestamp.json +0 -21
  1014. package/registry/r/toast.json +0 -30
  1015. package/registry/r/token-usage-chart.json +0 -20
  1016. package/registry/r/tokens.json +0 -21
  1017. package/registry/r/tool-call-card.json +0 -23
  1018. package/registry/r/tool-call.json +0 -22
  1019. package/registry/r/tool-result.json +0 -20
  1020. package/registry/r/tools-list.json +0 -23
  1021. package/registry/r/tooltip.json +0 -22
  1022. package/registry/r/topnav.json +0 -22
  1023. package/registry/r/types.json +0 -15
  1024. package/registry/r/usage-meter.json +0 -21
  1025. package/registry/r/whiteboard.json +0 -101
@@ -1,20 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
- "name": "token-usage-chart",
4
- "type": "registry:ui",
5
- "title": "TokenUsageChart",
6
- "description": "Stacked-bar chart of input vs output tokens over time.",
7
- "dependencies": [],
8
- "registryDependencies": [
9
- "https://usetheodev.github.io/theo-ui/r/cn.json",
10
- "https://usetheodev.github.io/theo-ui/r/tailwind-preset.json"
11
- ],
12
- "files": [
13
- {
14
- "path": "components/primitives/token-usage-chart/token-usage-chart.tsx",
15
- "type": "registry:ui",
16
- "target": "components/ui/token-usage-chart.tsx",
17
- "content": "import { forwardRef, useMemo } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport { toUsageMetrics } from \"./usage-metrics\";\n\nexport interface TokenUsagePoint {\n /** ISO date or friendly label for the x-axis tooltip. */\n label: string;\n /** Input tokens for this period (e.g. day). */\n input: number;\n /** Output tokens for this period. */\n output: number;\n}\n\ninterface TokenUsageChartProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n points: TokenUsagePoint[];\n /** Title above the chart. */\n title?: ReactNode;\n /** Chart height in px. Default 160. */\n height?: number;\n /** Show legend below the chart. Default true. */\n showLegend?: boolean;\n /**\n * Maximum number of bars to render. When `points.length` exceeds this, the\n * series is binned (summed in equal-width windows) so the chart stays\n * legible and SVG node count stays bounded. Default 60.\n */\n maxBars?: number;\n /**\n * Fix the y-axis maximum (the token value mapped to 100% height) so multiple\n * charts share a scale. When omitted, the scale auto-fits the data. Bars whose\n * value exceeds `maxScale` are clamped to 100% (the tooltip + a11y table still\n * show the true number).\n */\n maxScale?: number;\n /**\n * Render input and output as two adjacent (grouped) bars per period instead of\n * one stacked bar. Default false (stacked).\n */\n splitSeries?: boolean;\n}\n\nfunction binPoints(points: TokenUsagePoint[], maxBars: number): TokenUsagePoint[] {\n if (points.length <= maxBars) return points;\n const binSize = Math.ceil(points.length / maxBars);\n const out: TokenUsagePoint[] = [];\n for (let i = 0; i < points.length; i += binSize) {\n const slice = points.slice(i, i + binSize);\n if (slice.length === 0) continue;\n const first = slice[0]?.label ?? \"\";\n const last = slice[slice.length - 1]?.label ?? first;\n out.push({\n label: slice.length === 1 ? first : `${first}…${last}`,\n input: slice.reduce((a, p) => a + p.input, 0),\n output: slice.reduce((a, p) => a + p.output, 0),\n });\n }\n return out;\n}\n\nconst formatTokens = (n: number) => {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`;\n return `${n}`;\n};\n\n/**\n * TokenUsageChart — stacked-bar chart of input vs output tokens over time.\n *\n * Pure SVG, no chart lib. Width is fluid; bars adjust to viewBox. Hover any\n * bar to see the breakdown in the tooltip slot below.\n */\nconst TokenUsageChart = forwardRef<HTMLDivElement, TokenUsageChartProps>(\n (\n {\n className,\n points,\n title = \"Token usage\",\n height = 160,\n showLegend = true,\n maxBars = 60,\n maxScale,\n splitSeries = false,\n ...props\n },\n ref,\n ) => {\n const series = useMemo(() => binPoints(points, maxBars), [points, maxBars]);\n const autoMax = Math.max(1, ...series.map((p) => p.input + p.output));\n const max = maxScale && maxScale > 0 ? maxScale : autoMax;\n const pct = (value: number): number => Math.min(100, (value / max) * 100);\n const barCount = series.length;\n const barWidth = 100 / Math.max(1, barCount);\n const gap = Math.min(2, barWidth * 0.2);\n const innerWidth = barWidth - gap;\n\n const total = toUsageMetrics(series).total;\n\n return (\n <section\n data-slot=\"token-usage-chart\"\n ref={ref}\n className={cn(\"rounded-xl border bg-card p-4\", className)}\n aria-label=\"Token usage over time\"\n {...props}\n >\n <header className=\"flex items-baseline justify-between gap-3\">\n {title ? (\n <h3 className=\"font-display text-title-md tracking-tight\">{title}</h3>\n ) : (\n <span />\n )}\n <span className=\"font-mono text-label text-muted-foreground tabular-nums\">\n total · {formatTokens(total)}\n </span>\n </header>\n <div className=\"mt-3 grid grid-cols-[auto_1fr] gap-2\">\n {/* Y-axis ticks */}\n <div\n className=\"grid font-mono text-label text-muted-foreground\"\n style={{ height, gridTemplateRows: \"1fr 1fr 1fr\" }}\n aria-hidden=\"true\"\n >\n <span className=\"tabular-nums\">{formatTokens(max)}</span>\n <span className=\"tabular-nums\">{formatTokens(max / 2)}</span>\n <span className=\"self-end tabular-nums\">0</span>\n </div>\n {/* Chart */}\n <svg\n viewBox=\"0 0 100 100\"\n preserveAspectRatio=\"none\"\n className=\"w-full\"\n style={{ height }}\n role=\"img\"\n aria-label={`Token usage across ${barCount} periods`}\n >\n {/* gridlines */}\n {[25, 50, 75].map((y) => (\n <line\n key={y}\n x1={0}\n y1={y}\n x2={100}\n y2={y}\n stroke=\"hsl(var(--border) / 0.4)\"\n strokeWidth={0.2}\n />\n ))}\n {series.map((p, idx) => {\n const x = idx * barWidth + gap / 2;\n const titleText = `${p.label} — input ${formatTokens(p.input)} · output ${formatTokens(p.output)}`;\n if (splitSeries) {\n // Grouped: input + output as two adjacent half-width bars, each\n // scaled independently (and clamped) to the scale.\n const half = innerWidth / 2;\n const inputH = pct(p.input);\n const outputH = pct(p.output);\n return (\n <g key={p.label}>\n <title>{titleText}</title>\n <rect\n x={x}\n y={100 - inputH}\n width={half}\n height={inputH}\n fill=\"hsl(var(--accent))\"\n opacity={0.85}\n />\n <rect\n x={x + half}\n y={100 - outputH}\n width={half}\n height={outputH}\n fill=\"hsl(var(--primary))\"\n opacity={0.85}\n />\n </g>\n );\n }\n // Stacked: output on top of input. The total is clamped to the\n // scale, then split PROPORTIONALLY so the two segments tile to the\n // clamped total without overlap (a bar over `maxScale` keeps its\n // input/output ratio instead of both segments hitting 100%).\n const periodTotal = p.input + p.output;\n const totalH = pct(periodTotal);\n const outputH = periodTotal > 0 ? (totalH * p.output) / periodTotal : 0;\n const inputH = totalH - outputH;\n return (\n <g key={p.label}>\n <title>{titleText}</title>\n {/* output (top, primary tone) */}\n <rect\n x={x}\n y={100 - totalH}\n width={innerWidth}\n height={outputH}\n fill=\"hsl(var(--primary))\"\n opacity={0.85}\n />\n {/* input (bottom, accent tone) */}\n <rect\n x={x}\n y={100 - inputH}\n width={innerWidth}\n height={inputH}\n fill=\"hsl(var(--accent))\"\n opacity={0.85}\n />\n </g>\n );\n })}\n </svg>\n </div>\n {/* x-axis labels — aligned with the chart column of the parent grid */}\n <div\n className=\"mt-1 grid grid-cols-[auto_1fr] gap-2 font-mono text-label text-muted-foreground\"\n aria-hidden=\"true\"\n >\n <span aria-hidden=\"true\" />\n <div className=\"grid\" style={{ gridTemplateColumns: `repeat(${barCount}, 1fr)` }}>\n {series.map((p) => (\n <span key={p.label} className=\"truncate text-center\">\n {p.label}\n </span>\n ))}\n </div>\n </div>\n {/* Screen-reader fallback: the SVG is decorative-ish for AT users.\n * `<title>` per bar is unreliably exposed across NVDA/VoiceOver/JAWS,\n * so we ship an `sr-only` table with the same data. Sighted users\n * never see this; AT users can navigate the values cell-by-cell.\n * Reference: WCAG 1.1.1 (Non-text content). */}\n <table className=\"sr-only\">\n <caption>Token usage by period — input vs output</caption>\n <thead>\n <tr>\n <th scope=\"col\">Period</th>\n <th scope=\"col\">Input tokens</th>\n <th scope=\"col\">Output tokens</th>\n </tr>\n </thead>\n <tbody>\n {series.map((p) => (\n <tr key={`a11y-${p.label}`}>\n <td>{p.label}</td>\n <td>{p.input}</td>\n <td>{p.output}</td>\n </tr>\n ))}\n </tbody>\n </table>\n {showLegend ? (\n <footer className=\"mt-3 flex items-center gap-4 font-mono text-label text-muted-foreground\">\n <span className=\"inline-flex items-center gap-1.5\">\n <span className=\"size-2 rounded-sm bg-accent\" aria-hidden=\"true\" />\n Input\n </span>\n <span className=\"inline-flex items-center gap-1.5\">\n <span className=\"size-2 rounded-sm bg-primary\" aria-hidden=\"true\" />\n Output\n </span>\n </footer>\n ) : null}\n </section>\n );\n },\n);\nTokenUsageChart.displayName = \"TokenUsageChart\";\n\nexport { TokenUsageChart };\n"
18
- }
19
- ]
20
- }
@@ -1,21 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
- "name": "tokens",
4
- "type": "registry:lib",
5
- "title": "Violet Forge tokens",
6
- "description": "Design system CSS variables (light + dark) for the Theo UI 'Violet Forge' theme. Mirrors src/styles/tokens.css verbatim — the embedded files content is the single source of truth.",
7
- "files": [
8
- {
9
- "path": "styles/tokens.css",
10
- "type": "registry:lib",
11
- "target": "styles/tokens.css",
12
- "content": "/* Theo UI — Design System \"Violet Forge\" tokens.\n *\n * Tokens normativos. Não edite valores aqui sem revisão do design system.\n * Referência: docs/design-system.md\n *\n * Estratégia:\n * - CSS custom properties para todas as cores, scales e durations.\n * - Default é light. Dark é ativado via [data-theme=\"dark\"] ou .dark.\n * - HSL split (h s l) em vez de hex permite alpha via color-mix() ou hsl(var(--x) / .5).\n *\n * Roxo Theo: #A855F7 → oklch(0.628 0.225 296) (electric violet — unified 2026-06-09)\n * Burnt sienna: #C96442 → hsl(15 54% 53%)\n */\n\n/* `:root` and `[data-theme=\"dark\"]` declarations are intentionally NOT wrapped\n * in `@layer base` so they apply at the document level regardless of whether\n * the consumer's CSS pipeline runs Tailwind's preflight. The texture utilities\n * below remain in `@layer utilities` because that layer affects ordering only,\n * not the cascade root.\n */\n:root {\n /* Base palette ------------------------------------------------------- *\n * Neutrals are 100% desaturated (Vercel-style). Color comes from the\n * primary (violet) + accent (burnt sienna) tokens below — never from\n * surface tints.\n */\n --background: oklch(1 0 0); /* #FFFFFF */\n --foreground: oklch(0.146 0 0); /* #0A0A0A */\n\n --card: oklch(1 0 0); /* #FFFFFF */\n --card-foreground: oklch(0.146 0 0);\n\n --popover: oklch(1 0 0);\n --popover-foreground: oklch(0.146 0 0);\n\n /* Primary — Theo violet (equity) ------------------------------------- *\n * Post-T3.1 (ADR-0006): -deep and -glow are derived algorithmically via\n * OKLCH relative-color syntax. Themes that need a per-token override may\n * still declare these explicitly — the cascade resolves the override.\n * `max()`/`min()` clamps protect dark/light themes from L overflow (EC-7).\n */\n --primary: oklch(0.628 0.225 296); /* #A855F7 — Theo violet electric (unified 2026-06-09) */\n --primary-deep: oklch(from var(--primary) max(0.05, calc(l - 0.16)) c h);\n --primary-glow: oklch(from var(--primary) min(0.95, calc(l + 0.18)) c h);\n --primary-foreground: oklch(1 0 0);\n\n /* Secondary — neutral muted ----------------------------------------- */\n --secondary: oklch(0.97 0 0); /* #F5F5F5 */\n --secondary-foreground: oklch(0.146 0 0);\n\n /* Accent — burnt sienna --------------------------------------------- *\n * T3.1: accent-deep derived. Override path identical to primary.\n */\n --accent: oklch(0.621 0.132 39); /* #C96442 */\n --accent-deep: oklch(from var(--accent) max(0.05, calc(l - 0.13)) c h);\n --accent-foreground: oklch(1 0 0);\n\n /* Muted ------------------------------------------------------------- */\n --muted: oklch(0.97 0 0);\n --muted-foreground: oklch(0.555 0 0); /* #737373 */\n\n /* Surfaces, borders, inputs ----------------------------------------- */\n --border: oklch(0.931 0 0); /* #E8E8E8 — Vercel-style hairline */\n --input: oklch(0.931 0 0);\n --ring: oklch(0.628 0.225 296); /* matches primary */\n\n /* Semantics --------------------------------------------------------- */\n --success: oklch(0.611 0.161 149.7); /* #16A34A */\n --success-foreground: oklch(1 0 0);\n --warning: oklch(0.67 0.154 60.6); /* #D97706 */\n --warning-foreground: oklch(1 0 0);\n --destructive: oklch(0.579 0.214 27.2); /* #DC2626 */\n --destructive-foreground: oklch(1 0 0);\n --info: oklch(0.626 0.186 259.6); /* #3B82F6 */\n --info-foreground: oklch(1 0 0);\n\n /* Status (D4 — operational state group) ----------------------------- *\n * Separate from semantic group (success/destructive/warning/info, which\n * are action-result colors). Status describes component liveness:\n * online (alive), offline (dead), degraded (alive-but-slow), info (flag).\n * Defaults mirror semantic counterparts; themes override for distinction.\n */\n --status-online: oklch(0.611 0.161 149.7);\n --status-online-foreground: oklch(1 0 0);\n --status-offline: oklch(0.579 0.214 27.2);\n --status-offline-foreground: oklch(1 0 0);\n --status-degraded: oklch(0.67 0.154 60.6);\n --status-degraded-foreground: oklch(1 0 0);\n --status-info: oklch(0.626 0.186 259.6);\n --status-info-foreground: oklch(1 0 0);\n\n /* Radii ------------------------------------------------------------- */\n --radius-none: 0px;\n --radius-sm: 4px;\n --radius-md: 6px;\n --radius-lg: 10px;\n --radius-xl: 14px;\n --radius-2xl: 20px;\n --radius-full: 9999px;\n /* shadcn compat */\n --radius: 14px;\n\n /* Spacing scale (4px base) ------------------------------------------ */\n --space-1: 4px;\n --space-2: 8px;\n --space-3: 12px;\n --space-4: 16px;\n --space-5: 20px;\n --space-6: 24px;\n --space-8: 32px;\n --space-10: 40px;\n --space-12: 48px;\n --space-16: 64px;\n --space-20: 80px;\n --space-24: 96px;\n --space-32: 128px;\n\n /* Elevation — theme-aware (uses --foreground for shadow ink,\n * --primary for the signature glow, so swapping themes recolors them).\n * Post-T2.5: tokens are OKLCH; we compose alpha via color-mix(in oklch, ...)\n * which is perceptually correct AND works with any color-space variable.\n */\n --shadow-sm: 0 1px 2px 0 color-mix(in oklch, var(--foreground) 6%, transparent);\n --shadow-md: 0 2px 8px -2px color-mix(in oklch, var(--foreground) 8%, transparent), 0 1px 3px\n color-mix(in oklch, var(--foreground) 6%, transparent);\n --shadow-lg: 0 12px 32px -8px color-mix(in oklch, var(--foreground) 12%, transparent), 0 4px 12px\n color-mix(in oklch, var(--foreground) 8%, transparent);\n --shadow-glow: 0 0 24px color-mix(in oklch, var(--primary) 25%, transparent);\n --shadow-glow-strong: 0 0 32px color-mix(in oklch, var(--primary) 40%, transparent);\n\n /* Motion ------------------------------------------------------------ */\n --ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);\n --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);\n --ease-snap: cubic-bezier(0.85, 0, 0.15, 1);\n --duration-fast: 120ms;\n --duration-base: 200ms;\n --duration-slow: 360ms;\n --stagger: 60ms;\n\n /* Typography (Geist family — Vercel-inspired Violet Forge) ---------- */\n --font-display: \"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-body: \"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"Geist Mono\", ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n\n/* Dark mode — dominante --------------------------------------------- *\n * Vercel-aligned grayscale: pure neutrals (0% saturation) for every\n * surface and text layer. Color only in primary (violet), accent (burnt\n * sienna), and semantic tokens (success/warning/destructive/info).\n *\n * Activated exclusively via the `.dark` class on `<html>` (set by\n * `ThemeProvider`, `ThemeScript`, or consumers manually). The previous\n * `[data-theme=\"dark\"]` companion selector was dead — `data-theme` holds\n * the theme NAME, never the literal `\"dark\"`.\n */\n.dark {\n --background: oklch(0.146 0 0); /* #0A0A0A */\n --foreground: oklch(0.97 0 0); /* #F5F5F5 */\n\n --card: oklch(0.182 0 0); /* #121212 */\n --card-foreground: oklch(0.97 0 0);\n\n --popover: oklch(0.204 0 0); /* #171717 */\n --popover-foreground: oklch(0.97 0 0);\n\n --primary: oklch(0.628 0.225 296);\n --primary-deep: oklch(from var(--primary) max(0.05, calc(l - 0.16)) c h);\n --primary-glow: oklch(from var(--primary) min(0.95, calc(l + 0.18)) c h);\n --primary-foreground: oklch(1 0 0);\n\n --secondary: oklch(0.227 0 0); /* #1C1C1C */\n --secondary-foreground: oklch(0.97 0 0);\n\n --accent: oklch(0.621 0.132 39);\n --accent-deep: oklch(from var(--accent) max(0.05, calc(l - 0.13)) c h);\n --accent-foreground: oklch(1 0 0);\n\n --muted: oklch(0.227 0 0);\n --muted-foreground: oklch(0.683 0 0); /* #999 — Vercel gray-500 */\n\n --border: oklch(0.28 0 0); /* #292929 */\n --input: oklch(0.227 0 0);\n --ring: oklch(0.628 0.225 296);\n\n --success: oklch(0.814 0.192 155.7); /* #22E58C */\n --success-foreground: oklch(0.146 0 0);\n --warning: oklch(0.77 0.165 70.6); /* #F59E0B */\n --warning-foreground: oklch(0.146 0 0);\n --destructive: oklch(0.677 0.213 15.6); /* #FF4F6D */\n --destructive-foreground: oklch(0.146 0 0);\n --info: oklch(0.732 0.142 254.4); /* #5FB3FF */\n --info-foreground: oklch(0.146 0 0);\n\n /* Status (D4 — dark mode mirror of light defaults) ------------------ */\n --status-online: oklch(0.814 0.192 155.7);\n --status-online-foreground: oklch(0.146 0 0);\n --status-offline: oklch(0.677 0.213 15.6);\n --status-offline-foreground: oklch(0.146 0 0);\n --status-degraded: oklch(0.77 0.165 70.6);\n --status-degraded-foreground: oklch(0.146 0 0);\n --status-info: oklch(0.732 0.142 254.4);\n --status-info-foreground: oklch(0.146 0 0);\n\n /* In dark mode, shadows are heavier ink (against the dark surface) and\n * the glow brightens. Both still derive from theme tokens.\n * Post-T2.5: OKLCH-aware composition via color-mix() — black ink via\n * the literal oklch(0 0 0) (no theme indirection needed).\n */\n --shadow-sm: 0 1px 2px 0 oklch(0 0 0 / 0.4);\n --shadow-md: 0 2px 8px -2px oklch(0 0 0 / 0.5), 0 1px 3px oklch(0 0 0 / 0.4);\n --shadow-lg: 0 12px 32px -8px oklch(0 0 0 / 0.6), 0 4px 12px oklch(0 0 0 / 0.4);\n --shadow-glow: 0 0 24px color-mix(in oklch, var(--primary-glow) 40%, transparent);\n --shadow-glow-strong: 0 0 36px color-mix(in oklch, var(--primary-glow) 60%, transparent);\n}\n\n/* Forced colors (Windows High Contrast Mode) — D7 / ADR-0008 ----------- *\n * Maps Theo's semantic tokens to system colors so the UI remains usable\n * (and required by WCAG 1.4.1) when the OS forces a high-contrast palette.\n * Decorative textures opt out via `forced-color-adjust: none` (the dotted\n * patterns and hero glow would invert into solid blocks otherwise).\n *\n * Reference: MDN forced-colors media query, WCAG 2.2 SC 1.4.1.\n */\n@media (forced-colors: active) {\n :root,\n .dark {\n --background: Canvas;\n --foreground: CanvasText;\n --card: Canvas;\n --card-foreground: CanvasText;\n --popover: Canvas;\n --popover-foreground: CanvasText;\n --primary: Highlight;\n --primary-foreground: HighlightText;\n --secondary: ButtonFace;\n --secondary-foreground: ButtonText;\n --accent: Highlight;\n --accent-foreground: HighlightText;\n --muted: ButtonFace;\n --muted-foreground: GrayText;\n --border: ButtonBorder;\n --input: ButtonBorder;\n --ring: Highlight;\n }\n\n /* Texture utilities are decorative — keep them out of the system-color map. */\n .bg-dotted-violet,\n .bg-dotted-violet-strong,\n .bg-hero-glow,\n .bg-paper-grain {\n forced-color-adjust: none;\n }\n}\n\n/* Reduced motion — neutralizes durations and transitions for users who\n * request prefers-reduced-motion: reduce. Components that use animation as\n * semantic state (e.g. a spinner on a running step) can opt back in with\n * Tailwind's `motion-safe:` prefix.\n *\n * Reference: WCAG 2.3.3 Animation from Interactions (Level AAA).\n */\n@media (prefers-reduced-motion: reduce) {\n :root {\n --duration-fast: 0ms;\n --duration-base: 0ms;\n --duration-slow: 0ms;\n --stagger: 0ms;\n }\n *,\n *::before,\n *::after {\n animation-duration: 0.001ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.001ms !important;\n scroll-behavior: auto !important;\n }\n}\n\n/* Texture utilities (signature) ---------------------------------------- *\n * Post-T2.5: tokens are OKLCH; we compose alpha via color-mix(in oklch, …),\n * which is perceptually correct and works with any color-space variable.\n */\n@layer utilities {\n .bg-dotted-violet {\n background-image: radial-gradient(\n color-mix(in oklch, var(--primary) 8%, transparent) 1px,\n transparent 1px\n );\n background-size: 20px 20px;\n }\n\n .bg-dotted-violet-strong {\n background-image: radial-gradient(\n color-mix(in oklch, var(--primary) 16%, transparent) 1px,\n transparent 1px\n );\n background-size: 20px 20px;\n }\n\n .bg-hero-glow {\n background-image: radial-gradient(\n ellipse 60% 50% at 70% 0%,\n color-mix(in oklch, var(--primary) 18%, transparent) 0%,\n transparent 60%\n );\n }\n\n .bg-paper-grain {\n background-image: url(\"data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 0.06 0 0 0 0 0.04 0 0 0 0 0.08 0 0 0 0.18 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E\");\n }\n\n .text-balance {\n text-wrap: balance;\n }\n}\n"
13
- },
14
- {
15
- "path": "styles/fonts.css",
16
- "type": "registry:lib",
17
- "target": "styles/fonts.css",
18
- "content": "/* Theo UI — font loading (self-hosted by default).\n *\n * Default theme (Violet Forge): Geist Sans + Geist Mono.\n * Vercel-inspired typographic vocabulary — tight letter-spacing on display,\n * 3 strict weights (400 body / 500 UI / 600 display), OpenType \"liga\"\n * enabled globally. Geist is Vercel's open-source typeface, optimized for\n * product UIs and code surfaces. Licensed under SIL Open Font License\n * (LICENSE-GEIST.txt sits next to the woff2 files in `dist/fonts/`).\n *\n * ── Why self-hosted by default (HIGH-002 / D6) ────────────────────────────\n * The previous default `@import url(\"https://fonts.googleapis.com/...\")`\n * triggered a third-party network request on every page load — friction for\n * CSP-strict deployments, GDPR jurisdictions, and offline-first products.\n * The fonts ship as woff2 next to this CSS; consumers who installed\n * `@theokit/ui/styles.css` get Geist with zero external dependencies.\n *\n * Asset budget: 6 woff2 files (~290 KB total). Three weights per family\n * cover the entire Violet Forge typescale (display-2xl through code-sm).\n *\n * ── Opt-in CDN ────────────────────────────────────────────────────────────\n * Consumers who prefer Google Fonts CDN (e.g. don't want to host static\n * assets) can use the parallel entrypoint instead:\n *\n * @import \"@theokit/ui/fonts-cdn.css\";\n *\n * That file keeps the legacy @import to fonts.googleapis.com.\n */\n\n@font-face {\n font-family: \"Geist\";\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(\"./fonts/geist-400.woff2\") format(\"woff2\");\n}\n\n@font-face {\n font-family: \"Geist\";\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(\"./fonts/geist-500.woff2\") format(\"woff2\");\n}\n\n@font-face {\n font-family: \"Geist\";\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(\"./fonts/geist-600.woff2\") format(\"woff2\");\n}\n\n@font-face {\n font-family: \"Geist Mono\";\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(\"./fonts/geist-mono-400.woff2\") format(\"woff2\");\n}\n\n@font-face {\n font-family: \"Geist Mono\";\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(\"./fonts/geist-mono-500.woff2\") format(\"woff2\");\n}\n\n@font-face {\n font-family: \"Geist Mono\";\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(\"./fonts/geist-mono-600.woff2\") format(\"woff2\");\n}\n"
19
- }
20
- ]
21
- }
@@ -1,23 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
- "name": "tool-call-card",
4
- "type": "registry:ui",
5
- "title": "ToolCallCard",
6
- "description": "Single agent tool invocation rendered inside the stream.",
7
- "dependencies": [
8
- "lucide-react"
9
- ],
10
- "registryDependencies": [
11
- "https://usetheodev.github.io/theo-ui/r/cn.json",
12
- "https://usetheodev.github.io/theo-ui/r/tailwind-preset.json",
13
- "https://usetheodev.github.io/theo-ui/r/types.json"
14
- ],
15
- "files": [
16
- {
17
- "path": "components/primitives/tool-call-card/tool-call-card.tsx",
18
- "type": "registry:ui",
19
- "target": "components/ui/tool-call-card.tsx",
20
- "content": "\"use client\";\n\nimport { Check, ChevronRight, Loader2, X } from \"lucide-react\";\nimport { useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport type { IconComponent } from \"@/lib/types\";\n\n/**\n * ToolCallCard — single agent tool invocation rendered inside the stream.\n *\n * Visual: row with tool icon + tool name + target/command (mono) + status +\n * optional chevron. Expandable: when `output` is provided the row becomes a\n * `<details>` whose body renders the stdout/stderr/result block.\n *\n * Distinct from `AgentEvent` in the existing AgentTimeline by being a\n * stand-alone card that lives inside an AgentStream alongside chat messages.\n */\n\nexport type ToolCallStatus = \"running\" | \"success\" | \"failed\" | \"queued\" | \"skipped\";\n\nconst STATUS_ICON: Record<ToolCallStatus, ReactNode> = {\n running: <Loader2 className=\"size-3.5 animate-spin text-primary\" aria-hidden=\"true\" />,\n success: <Check className=\"size-3.5 text-success\" aria-hidden=\"true\" />,\n failed: <X className=\"size-3.5 text-destructive\" aria-hidden=\"true\" />,\n queued: <span className=\"size-2 rounded-full bg-warning\" aria-hidden=\"true\" />,\n skipped: <span className=\"size-2 rounded-full bg-muted-foreground\" aria-hidden=\"true\" />,\n};\n\nconst STATUS_LABEL: Record<ToolCallStatus, string> = {\n running: \"Running\",\n success: \"Completed\",\n failed: \"Failed\",\n queued: \"Queued\",\n skipped: \"Skipped\",\n};\n\ninterface ToolCallCardProps extends HTMLAttributes<HTMLElement> {\n /** Tool name (matches Theo Code / Claude Code tool registry: Bash, Edit, Read, …). */\n tool: ReactNode;\n /** Optional icon for the tool. */\n icon?: IconComponent;\n /** Target / command shown in monospace next to the tool name. */\n target?: ReactNode;\n status: ToolCallStatus;\n /** Optional stdout/stderr/result body. When present, the card becomes expandable. */\n output?: ReactNode;\n /** Default expanded state. Default: false (collapsed). */\n defaultExpanded?: boolean;\n /** Timestamp shown on the right. */\n timestamp?: ReactNode;\n}\n\nexport function ToolCallCard({\n className,\n tool,\n icon: Icon,\n target,\n status,\n output,\n defaultExpanded = false,\n timestamp,\n ...props\n}: ToolCallCardProps) {\n const [open, setOpen] = useState(defaultExpanded);\n const expandable = !!output;\n\n return (\n <article\n data-slot=\"tool-call-card\"\n className={cn(\n \"overflow-hidden rounded-lg border border-border/40 bg-card/40 text-card-foreground\",\n className,\n )}\n {...props}\n >\n {/* T5.4: <header role=\"button\"> previously failed axe's\n * aria-prohibited-attr + semantic-landmark guidance. Replaced by\n * <div> for the layout container and a separate <button> for the\n * expand affordance (when expandable). Status icon span now carries\n * role=\"img\" to make aria-label valid. */}\n <div className={cn(\"flex items-center gap-2 px-3 py-2\")}>\n {expandable ? (\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n aria-expanded={open}\n aria-label={open ? `Collapse ${tool} details` : `Expand ${tool} details`}\n className=\"-m-1 inline-flex size-6 shrink-0 items-center justify-center rounded-md p-1 text-muted-foreground hover:bg-muted/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <ChevronRight\n className={cn(\"size-3.5 transition-transform duration-base\", open && \"rotate-90\")}\n aria-hidden=\"true\"\n />\n </button>\n ) : null}\n {Icon ? (\n <Icon className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n ) : null}\n <span className=\"shrink-0 font-medium font-mono text-code-sm text-foreground\">{tool}</span>\n {target ? (\n <span className=\"truncate font-mono text-code-sm text-muted-foreground\">{target}</span>\n ) : null}\n <span\n role=\"img\"\n aria-label={STATUS_LABEL[status]}\n className=\"ml-auto inline-flex shrink-0 items-center gap-1.5\"\n >\n {STATUS_ICON[status]}\n </span>\n {timestamp ? (\n <span className=\"shrink-0 font-mono text-label text-muted-foreground tabular-nums\">\n {timestamp}\n </span>\n ) : null}\n </div>\n {expandable && open ? (\n <div className=\"border-border/40 border-t bg-muted/20 px-3 py-2 font-mono text-code-sm\">\n {output}\n </div>\n ) : null}\n </article>\n );\n}\n"
21
- }
22
- ]
23
- }
@@ -1,22 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
- "name": "tool-call",
4
- "type": "registry:ui",
5
- "title": "ToolCall",
6
- "description": "Collapsible row representing an agent tool invocation.",
7
- "dependencies": [
8
- "lucide-react"
9
- ],
10
- "registryDependencies": [
11
- "https://usetheodev.github.io/theo-ui/r/cn.json",
12
- "https://usetheodev.github.io/theo-ui/r/tailwind-preset.json"
13
- ],
14
- "files": [
15
- {
16
- "path": "components/primitives/tool-call/tool-call.tsx",
17
- "type": "registry:ui",
18
- "target": "components/ui/tool-call.tsx",
19
- "content": "\"use client\";\n\nimport { ChevronRight, Wrench } from \"lucide-react\";\nimport { forwardRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\n\ninterface ToolCallProps extends HTMLAttributes<HTMLDivElement> {\n /** Tool name e.g. \"bash\", \"read_file\", \"edit_file\". */\n name?: string;\n /**\n * Summary label e.g. \"Ran 2 commands\", \"Read 18 files\".\n */\n summary: ReactNode;\n /**\n * Collapsible payload (e.g. command, stdout, file list).\n */\n detail?: ReactNode;\n defaultOpen?: boolean;\n /**\n * If true, hides the wrench icon (useful when grouping by name elsewhere).\n */\n hideIcon?: boolean;\n}\n\n/**\n * ToolCall — collapsible row representing an agent tool invocation.\n *\n * Visual: subtle muted container, wrench icon, summary in body text,\n * chevron rotates on expand. Pairs with `<ToolResult>` for the rendered output.\n *\n * Typical usage inside a ChatMessage assistant body:\n *\n * <ToolCall summary=\"Read 18 files\" detail={<ToolResult>{output}</ToolResult>} />\n */\nconst ToolCall = forwardRef<HTMLDivElement, ToolCallProps>(\n ({ className, name, summary, detail, defaultOpen, hideIcon, ...props }, ref) => {\n const [open, setOpen] = useState(defaultOpen ?? false);\n const expandable = detail !== undefined;\n return (\n <div\n data-slot=\"tool-call\"\n ref={ref}\n className={cn(\"rounded-md border border-border/40 bg-muted/30\", className)}\n {...props}\n >\n <button\n type=\"button\"\n onClick={() => expandable && setOpen((v) => !v)}\n aria-expanded={expandable ? open : undefined}\n disabled={!expandable}\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-2 text-left\",\n \"font-sans text-body-sm text-foreground\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n expandable && \"cursor-pointer hover:bg-muted/60\",\n )}\n >\n {!hideIcon ? (\n <Wrench className=\"size-3.5 shrink-0 text-primary\" aria-hidden=\"true\" />\n ) : null}\n {name ? (\n <span className=\"font-mono text-code-sm text-muted-foreground\">{name}</span>\n ) : null}\n <span className=\"flex-1 truncate\">{summary}</span>\n {expandable ? (\n <ChevronRight\n className={cn(\n \"size-3.5 text-muted-foreground transition-transform\",\n open && \"rotate-90\",\n )}\n aria-hidden=\"true\"\n />\n ) : null}\n </button>\n {expandable && open ? (\n <div className=\"border-border/40 border-t bg-card px-3 py-2\">{detail}</div>\n ) : null}\n </div>\n );\n },\n);\nToolCall.displayName = \"ToolCall\";\n\nexport { ToolCall };\n"
20
- }
21
- ]
22
- }
@@ -1,20 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
- "name": "tool-result",
4
- "type": "registry:ui",
5
- "title": "ToolResult",
6
- "description": "Formatted output of a tool invocation.",
7
- "dependencies": [],
8
- "registryDependencies": [
9
- "https://usetheodev.github.io/theo-ui/r/cn.json",
10
- "https://usetheodev.github.io/theo-ui/r/tailwind-preset.json"
11
- ],
12
- "files": [
13
- {
14
- "path": "components/primitives/tool-result/tool-result.tsx",
15
- "type": "registry:ui",
16
- "target": "components/ui/tool-result.tsx",
17
- "content": "import { forwardRef } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\n\ntype Variant = \"text\" | \"code\" | \"json\";\n\ninterface ToolResultProps extends HTMLAttributes<HTMLDivElement> {\n variant?: Variant;\n /**\n * Pre-formatted content. For `code`/`json`, the component uses mono font\n * and preserves whitespace. For `text`, normal body font.\n */\n children: ReactNode;\n}\n\n/**\n * ToolResult — formatted output of a tool invocation.\n *\n * Three quick variants: plain text, code (monospace), json (monospace, tinted).\n * Always rendered as a `<div>` for predictable prop typing; code/json variants\n * wrap children in `<pre>` internally.\n */\nconst ToolResult = forwardRef<HTMLDivElement, ToolResultProps>(\n ({ className, variant = \"text\", children, ...props }, ref) => {\n if (variant === \"text\") {\n return (\n <div\n data-slot=\"tool-result\"\n ref={ref}\n className={cn(\"text-body-sm text-muted-foreground\", className)}\n {...props}\n >\n {children}\n </div>\n );\n }\n return (\n <div data-slot=\"tool-result\" ref={ref} className={className} {...props}>\n <pre\n className={cn(\n \"overflow-x-auto whitespace-pre-wrap font-mono text-code-sm\",\n variant === \"json\" ? \"text-primary-glow\" : \"text-foreground\",\n )}\n >\n {children}\n </pre>\n </div>\n );\n },\n);\nToolResult.displayName = \"ToolResult\";\n\nexport { ToolResult };\n"
18
- }
19
- ]
20
- }
@@ -1,23 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
- "name": "tools-list",
4
- "type": "registry:ui",
5
- "title": "ToolsList",
6
- "description": "Surface every tool the agent could call, with its enablement",
7
- "dependencies": [
8
- "lucide-react"
9
- ],
10
- "registryDependencies": [
11
- "https://usetheodev.github.io/theo-ui/r/cn.json",
12
- "https://usetheodev.github.io/theo-ui/r/tailwind-preset.json",
13
- "https://usetheodev.github.io/theo-ui/r/types.json"
14
- ],
15
- "files": [
16
- {
17
- "path": "components/primitives/tools-list/tools-list.tsx",
18
- "type": "registry:ui",
19
- "target": "components/ui/tools-list.tsx",
20
- "content": "import { Eye, Lock, Settings2 } from \"lucide-react\";\nimport { forwardRef } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport type { IconComponent } from \"@/lib/types\";\n\nexport type ToolEnablement = \"enabled\" | \"ask\" | \"denied\";\n\nexport interface ToolEntry {\n id: string;\n name: string;\n description?: ReactNode;\n icon?: IconComponent;\n enablement?: ToolEnablement;\n /**\n * Source of the tool: built-in, plugin/skill, or MCP server name.\n */\n source?: string;\n /** Optional badge text (e.g. \"destructive\", \"experimental\"). */\n badge?: ReactNode;\n}\n\ninterface ToolsListProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\" | \"onChange\"> {\n tools: ToolEntry[];\n /** Title above the list. */\n title?: ReactNode;\n /**\n * Fires when the consumer toggles a tool's enablement state.\n * Cycle: enabled → ask → denied → enabled.\n */\n onEnablementChange?: (id: string, next: ToolEnablement) => void;\n}\n\nconst ENABLEMENT_LABEL: Record<ToolEnablement, string> = {\n enabled: \"Allowed\",\n ask: \"Ask before use\",\n denied: \"Denied\",\n};\n\nconst ENABLEMENT_CLASS: Record<ToolEnablement, string> = {\n enabled: \"bg-success/15 text-success border-success/40\",\n ask: \"bg-warning/15 text-warning border-warning/40\",\n denied: \"bg-destructive/15 text-destructive border-destructive/40\",\n};\n\nconst cycle = (cur: ToolEnablement): ToolEnablement =>\n cur === \"enabled\" ? \"ask\" : cur === \"ask\" ? \"denied\" : \"enabled\";\n\n/**\n * ToolsList — surface every tool the agent could call, with its enablement\n * state. Click the chip to cycle: Allowed → Ask → Denied.\n */\nconst ToolsList = forwardRef<HTMLDivElement, ToolsListProps>(\n ({ className, tools, title = \"Tools\", onEnablementChange, ...props }, ref) => (\n <section\n data-slot=\"tools-list\"\n ref={ref}\n className={cn(\"rounded-xl border bg-card\", className)}\n {...props}\n >\n {title ? (\n <header className=\"flex items-center justify-between border-border/40 border-b px-4 py-3\">\n <h3 className=\"font-display text-title-md tracking-tight\">{title}</h3>\n <span className=\"font-mono text-label text-muted-foreground\">\n {tools.length} {tools.length === 1 ? \"tool\" : \"tools\"}\n </span>\n </header>\n ) : null}\n <ul className=\"divide-y divide-border/30\">\n {tools.map((tool) => {\n const Icon = tool.icon ?? Settings2;\n const state = tool.enablement ?? \"enabled\";\n return (\n <li\n key={tool.id}\n className=\"grid grid-cols-[auto_1fr_auto] items-start gap-3 px-4 py-3\"\n >\n <span className=\"mt-0.5 grid size-8 place-items-center rounded-md bg-muted text-muted-foreground\">\n <Icon className=\"size-4\" aria-hidden=\"true\" />\n </span>\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium font-mono text-body-sm text-foreground\">\n {tool.name}\n </span>\n {tool.source ? (\n <span className=\"font-mono text-label text-muted-foreground uppercase tracking-wider\">\n {tool.source}\n </span>\n ) : null}\n {tool.badge ? (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-accent/15 px-2 py-0.5 font-mono text-accent text-label uppercase\">\n {tool.badge}\n </span>\n ) : null}\n </div>\n {tool.description ? (\n <p className=\"mt-0.5 text-body-sm text-muted-foreground\">{tool.description}</p>\n ) : null}\n </div>\n <button\n type=\"button\"\n onClick={() => onEnablementChange?.(tool.id, cycle(state))}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1\",\n \"font-mono text-label uppercase tracking-wider transition-colors\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n ENABLEMENT_CLASS[state],\n !onEnablementChange && \"pointer-events-none\",\n )}\n aria-label={`Cycle enablement for ${tool.name}`}\n >\n {state === \"enabled\" ? (\n <Eye className=\"size-3\" aria-hidden=\"true\" />\n ) : state === \"ask\" ? (\n <Settings2 className=\"size-3\" aria-hidden=\"true\" />\n ) : (\n <Lock className=\"size-3\" aria-hidden=\"true\" />\n )}\n {ENABLEMENT_LABEL[state]}\n </button>\n </li>\n );\n })}\n </ul>\n </section>\n ),\n);\nToolsList.displayName = \"ToolsList\";\n\nexport { ToolsList };\n"
21
- }
22
- ]
23
- }
@@ -1,22 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
- "name": "tooltip",
4
- "type": "registry:ui",
5
- "title": "Tooltip",
6
- "description": "Built on Radix Tooltip — accessible hover / focus tooltip with delay and side / align controls.",
7
- "dependencies": [
8
- "@radix-ui/react-tooltip"
9
- ],
10
- "registryDependencies": [
11
- "https://usetheodev.github.io/theo-ui/r/cn.json",
12
- "https://usetheodev.github.io/theo-ui/r/tailwind-preset.json"
13
- ],
14
- "files": [
15
- {
16
- "path": "components/primitives/tooltip/tooltip.tsx",
17
- "type": "registry:ui",
18
- "target": "components/ui/tooltip.tsx",
19
- "content": "import * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\nimport { forwardRef } from \"react\";\nimport type { ComponentPropsWithoutRef, ElementRef, ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * Tooltip — built on Radix Tooltip.\n *\n * Visual: dark surface in light mode (and inverse in dark) with rounded-md,\n * shadow-md, text-body-sm. 8px delay-show default.\n *\n * Wrap your app in <Tooltip.Provider> once (or use the default delayDuration here).\n */\n\nconst Provider = TooltipPrimitive.Provider;\nconst Root = TooltipPrimitive.Root;\nconst Trigger = TooltipPrimitive.Trigger;\n\nconst Content = forwardRef<\n ElementRef<typeof TooltipPrimitive.Content>,\n ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset = 6, ...props }, ref) => (\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n data-slot=\"tooltip-content\"\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 overflow-hidden rounded-md border border-border/40 bg-foreground px-2.5 py-1.5\",\n \"text-background text-body-sm shadow-md\",\n \"data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-[state=delayed-open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:animate-out\",\n \"data-[side=top]:slide-in-from-bottom-1 data-[side=bottom]:slide-in-from-top-1\",\n \"data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1\",\n className,\n )}\n {...props}\n />\n </TooltipPrimitive.Portal>\n));\nContent.displayName = \"Tooltip.Content\";\n\ninterface TooltipProps extends ComponentPropsWithoutRef<typeof TooltipPrimitive.Root> {\n label: ReactNode;\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n align?: \"start\" | \"center\" | \"end\";\n children: ReactNode;\n}\n\n/**\n * Shorthand: <Tooltip label=\"…\"><Button>…</Button></Tooltip>\n * Wraps Provider + Root + Trigger asChild + Content for the common case.\n * For advanced usage (controlled state, custom content), use Tooltip.Root etc. directly.\n */\nconst Tooltip = ({\n label,\n side = \"top\",\n align = \"center\",\n delayDuration = 200,\n children,\n ...rootProps\n}: TooltipProps) =>\n (\n <Provider delayDuration={delayDuration}>\n <Root {...rootProps}>\n <Trigger asChild>{children}</Trigger>\n <Content side={side} align={align}>\n {label}\n </Content>\n </Root>\n </Provider>\n ) as ReturnType<typeof Provider>;\n\nconst TooltipWithStatics = Tooltip as typeof Tooltip & {\n Provider: typeof Provider;\n Root: typeof Root;\n Trigger: typeof Trigger;\n Content: typeof Content;\n};\nTooltipWithStatics.Provider = Provider;\nTooltipWithStatics.Root = Root;\nTooltipWithStatics.Trigger = Trigger;\nTooltipWithStatics.Content = Content;\n\nexport { TooltipWithStatics as Tooltip };\n"
20
- }
21
- ]
22
- }
@@ -1,22 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
- "name": "topnav",
4
- "type": "registry:ui",
5
- "title": "TopNav",
6
- "description": "Horizontal app bar (64px) with breadcrumbs, mode switcher (radiogroup), and action slots.",
7
- "dependencies": [
8
- "lucide-react"
9
- ],
10
- "registryDependencies": [
11
- "https://usetheodev.github.io/theo-ui/r/cn.json",
12
- "https://usetheodev.github.io/theo-ui/r/tailwind-preset.json"
13
- ],
14
- "files": [
15
- {
16
- "path": "components/primitives/topnav/topnav.tsx",
17
- "type": "registry:ui",
18
- "target": "components/ui/topnav.tsx",
19
- "content": "import { ChevronRight } from \"lucide-react\";\nimport { Fragment, forwardRef } from \"react\";\nimport type { HTMLAttributes, KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * TopNav — horizontal app bar (64px).\n *\n * Composition:\n * <TopNav>\n * <TopNav.Left>\n * <TopNav.Breadcrumbs items={[{label: \"acme\"}, {label: \"api\"}]} />\n * </TopNav.Left>\n * <TopNav.Center>…segmented switcher…</TopNav.Center>\n * <TopNav.Right>…actions…</TopNav.Right>\n * </TopNav>\n *\n * Variant — hairline bottom border. No glass/blur (anti-glass guideline).\n */\n\nconst Root = forwardRef<HTMLElement, HTMLAttributes<HTMLElement>>(\n ({ className, ...props }, ref) => (\n <header\n data-slot=\"top-nav\"\n ref={ref}\n className={cn(\n \"flex h-16 items-center justify-between gap-4 border-border/40 border-b bg-card px-6\",\n className,\n )}\n {...props}\n />\n ),\n);\nRoot.displayName = \"TopNav\";\n\nconst Left = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n data-slot=\"top-nav-left\"\n ref={ref}\n className={cn(\"flex flex-1 items-center gap-3\", className)}\n {...props}\n />\n ),\n);\nLeft.displayName = \"TopNav.Left\";\n\nconst Center = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n data-slot=\"top-nav-center\"\n ref={ref}\n className={cn(\"hidden flex-1 justify-center md:flex\", className)}\n {...props}\n />\n ),\n);\nCenter.displayName = \"TopNav.Center\";\n\nconst Right = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n data-slot=\"top-nav-right\"\n ref={ref}\n className={cn(\"flex flex-1 items-center justify-end gap-2\", className)}\n {...props}\n />\n ),\n);\nRight.displayName = \"TopNav.Right\";\n\ninterface BreadcrumbItem {\n label: ReactNode;\n href?: string;\n}\n\ninterface BreadcrumbsProps extends HTMLAttributes<HTMLElement> {\n items: BreadcrumbItem[];\n}\n\nconst Breadcrumbs = forwardRef<HTMLElement, BreadcrumbsProps>(\n ({ className, items, ...props }, ref) => (\n <nav\n data-slot=\"top-nav-breadcrumbs\"\n ref={ref}\n aria-label=\"Breadcrumb\"\n className={cn(\"flex items-center gap-1.5 text-body-sm\", className)}\n {...props}\n >\n {items.map((item, idx) => {\n const isLast = idx === items.length - 1;\n const key = typeof item.label === \"string\" ? item.label : idx;\n return (\n <Fragment key={key}>\n {item.href && !isLast ? (\n <a\n href={item.href}\n className=\"font-sans text-muted-foreground transition-colors hover:text-foreground\"\n >\n {item.label}\n </a>\n ) : (\n <span\n className={cn(\n \"font-sans\",\n isLast ? \"font-medium text-foreground\" : \"text-muted-foreground\",\n )}\n aria-current={isLast ? \"page\" : undefined}\n >\n {item.label}\n </span>\n )}\n {!isLast ? (\n <ChevronRight className=\"size-3.5 text-muted-foreground\" aria-hidden=\"true\" />\n ) : null}\n </Fragment>\n );\n })}\n </nav>\n ),\n);\nBreadcrumbs.displayName = \"TopNav.Breadcrumbs\";\n\ninterface ModeSwitcherOption {\n value: string;\n label: ReactNode;\n}\n\ninterface ModeSwitcherProps extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n value: string;\n options: ModeSwitcherOption[];\n onChange?: (value: string) => void;\n /**\n * Accessible label for the radiogroup. Defaults to \"Mode\".\n */\n ariaLabel?: string;\n}\n\n/**\n * TopNav.ModeSwitcher — segmented control (Chat / Code / Infra).\n *\n * ARIA semantics: `role=\"radiogroup\"` + `role=\"radio\"` per option, with roving\n * tabindex and full keyboard navigation (Arrow keys + Home/End). Per WAI-ARIA\n * radiogroup pattern, exactly one option has `tabIndex=0` (the active one) and\n * the rest have `tabIndex=-1`, so Tab moves in and Tab moves out.\n *\n * Stateless: pass `value` + `onChange`.\n */\nconst ModeSwitcher = forwardRef<HTMLDivElement, ModeSwitcherProps>(\n ({ className, value, options, onChange, ariaLabel = \"Mode\", ...props }, ref) => {\n const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n if (!onChange || options.length === 0) return;\n const idx = options.findIndex((o) => o.value === value);\n const current = idx >= 0 ? idx : 0;\n let nextIdx: number | null = null;\n if (e.key === \"ArrowRight\" || e.key === \"ArrowDown\") {\n nextIdx = (current + 1) % options.length;\n } else if (e.key === \"ArrowLeft\" || e.key === \"ArrowUp\") {\n nextIdx = (current - 1 + options.length) % options.length;\n } else if (e.key === \"Home\") {\n nextIdx = 0;\n } else if (e.key === \"End\") {\n nextIdx = options.length - 1;\n }\n if (nextIdx === null) return;\n e.preventDefault();\n const target = options[nextIdx];\n if (target) onChange(target.value);\n };\n\n return (\n <div\n data-slot=\"top-nav-mode-switcher\"\n ref={ref}\n role=\"radiogroup\"\n aria-label={ariaLabel}\n onKeyDown={handleKeyDown}\n className={cn(\n \"inline-flex items-center rounded-lg border border-border/60 bg-muted p-1\",\n className,\n )}\n {...props}\n >\n {options.map((opt) => {\n const isActive = opt.value === value;\n return (\n <button\n key={opt.value}\n type=\"button\"\n // biome-ignore lint/a11y/useSemanticElements: WAI-ARIA radiogroup pattern requires role=\"radio\" on buttons for segmented controls\n role=\"radio\"\n aria-checked={isActive}\n tabIndex={isActive ? 0 : -1}\n onClick={() => onChange?.(opt.value)}\n className={cn(\n \"rounded-md px-3 py-1.5 font-medium font-sans text-body-sm\",\n \"transition-all duration-base ease-out-soft\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n isActive\n ? \"bg-card text-foreground shadow-sm\"\n : \"text-muted-foreground hover:text-foreground\",\n )}\n >\n {opt.label}\n </button>\n );\n })}\n </div>\n );\n },\n);\nModeSwitcher.displayName = \"TopNav.ModeSwitcher\";\n\nconst TopNav = /*#__PURE__*/ Object.assign(Root, {\n Left,\n Center,\n Right,\n Breadcrumbs,\n ModeSwitcher,\n});\n\nexport { TopNav };\n"
20
- }
21
- ]
22
- }
@@ -1,15 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
- "name": "types",
4
- "type": "registry:lib",
5
- "title": "Theo UI shared types",
6
- "description": "Shared TypeScript helper types (IconComponent, etc.) used across Theo UI.",
7
- "files": [
8
- {
9
- "path": "lib/types.ts",
10
- "type": "registry:lib",
11
- "target": "lib/types.ts",
12
- "content": "import type { ComponentType, SVGProps } from \"react\";\n\n/**\n * IconComponent — shape compatible with both lucide-react icons and custom React SVG components.\n *\n * Centralizing this here avoids TS2322 noise from `exactOptionalPropertyTypes` when mapping\n * icons through Record<...> structures.\n */\nexport type IconComponent = ComponentType<SVGProps<SVGSVGElement> & { className?: string }>;\n"
13
- }
14
- ]
15
- }
@@ -1,21 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
- "name": "usage-meter",
4
- "type": "registry:ui",
5
- "title": "UsageMeter",
6
- "description": "Multi-metric stacked usage card for PaaS dashboards. Renders N metrics (data transfer, requests, build minutes, seats, …) each with label + value/max + Progress bar. Supports custom per-metric formatter, over-quota warning, and a compact bars-only mode. PaaS-shape sibling of CostMeter.",
7
- "dependencies": [],
8
- "registryDependencies": [
9
- "https://usetheodev.github.io/theo-ui/r/cn.json",
10
- "https://usetheodev.github.io/theo-ui/r/progress.json",
11
- "https://usetheodev.github.io/theo-ui/r/tailwind-preset.json"
12
- ],
13
- "files": [
14
- {
15
- "path": "components/composites/usage-meter/usage-meter.tsx",
16
- "type": "registry:ui",
17
- "target": "components/ui/usage-meter.tsx",
18
- "content": "import { forwardRef } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport { Progress } from \"@/components/ui/progress\";\n\n/**\n * UsageMeter — multi-metric stacked usage card.\n *\n * PaaS-shape sibling of `<CostMeter>` (which is mono-USD). Displays N\n * metrics with arbitrary units (GB, requests, build-minutes, seats) per\n * the reference TheoCloud dashboard mockup. Each row = label + value/max\n * + `<Progress>` fill bar.\n *\n * Composition:\n *\n * <UsageMeter\n * title=\"Last 30 days\"\n * action={<Badge variant=\"outline\">Upgrade</Badge>}\n * metrics={[\n * { label: \"Fast Data Transfer\", value: 0, max: 100, unit: \"GB\" },\n * { label: \"Edge Requests\", value: 0, max: 1_000_000, unit: \"req\",\n * formatter: (v, m) => `${v} / ${m >= 1e6 ? `${m / 1e6}M` : m}` },\n * ]}\n * />\n *\n * Over-quota detection: when `value > max`, the value text gets the\n * `text-warning` class and the underlying `<Progress>` uses\n * `intent=\"warning\"` (bar fills 100% via Progress's own clamping).\n *\n * `compact` mode renders only the bars (no label/value text) — useful in\n * narrow nav-bar slots.\n *\n * Imports the sibling `<Progress>` primitive via relative path (per RFC\n * dashboard-paas-primitives D3) — primitives must not depend on the\n * `@theokit/ui` barrel.\n */\n\nexport interface UsageMetric {\n /** Display label (e.g. \"Fast Data Transfer\"). */\n label: ReactNode;\n /** Current consumption. */\n value: number;\n /** Maximum allowed in the current period. */\n max: number;\n /** Unit string (e.g. \"GB\", \"req\", \"min\"). Rendered after value/max. */\n unit?: string;\n /** Optional custom formatter — overrides default `${value} / ${max} ${unit}`. */\n formatter?: (value: number, max: number, unit?: string) => string;\n}\n\nexport interface UsageMeterProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Card title (e.g. \"Last 30 days\", \"This billing period\"). */\n title?: ReactNode;\n /** Optional right-aligned action slot (e.g. an Upgrade Badge or Button). */\n action?: ReactNode;\n /** Array of metrics to display. Order preserved. */\n metrics: UsageMetric[];\n /** When true, show only the bars without label/value text (sparkline mode). */\n compact?: boolean;\n}\n\nfunction defaultFormat(value: number, max: number, unit?: string): string {\n return `${value} / ${max}${unit ? ` ${unit}` : \"\"}`;\n}\n\nfunction metricAriaLabel(metric: UsageMetric, formatted: string): string {\n // Label may be a ReactNode; coerce to string for aria-label.\n const label = typeof metric.label === \"string\" ? metric.label : \"metric\";\n return `${label}: ${formatted}`;\n}\n\nconst UsageMeter = forwardRef<HTMLDivElement, UsageMeterProps>(\n ({ className, title, action, metrics, compact = false, ...props }, ref) => {\n const hasHeader = Boolean(title) || Boolean(action);\n return (\n <div\n data-slot=\"usage-meter\"\n ref={ref}\n className={cn(\n \"grid gap-3 rounded-xl border border-border bg-card p-4\",\n compact && \"gap-2\",\n className,\n )}\n data-theo-usage-meter=\"\"\n {...props}\n >\n {hasHeader ? (\n <header className=\"flex items-baseline justify-between gap-3\">\n {title ? (\n <span\n className={cn(\n \"font-mono text-label-caps text-muted-foreground uppercase tracking-wider\",\n )}\n >\n {title}\n </span>\n ) : (\n <span />\n )}\n {action ? <div className=\"shrink-0\">{action}</div> : null}\n </header>\n ) : null}\n\n {metrics.map((metric, idx) => {\n const overQuota = metric.value > metric.max;\n const fmt = metric.formatter ?? defaultFormat;\n const formatted = fmt(metric.value, metric.max, metric.unit);\n const intent = overQuota ? \"warning\" : \"default\";\n // Each metric needs a stable key. Without a unique id field we\n // synthesize one from label + idx — duplicates resolve via the\n // index suffix.\n const key = `${typeof metric.label === \"string\" ? metric.label : \"metric\"}-${idx}`;\n\n if (compact) {\n return (\n <Progress\n key={key}\n value={metric.value}\n max={metric.max}\n intent={intent}\n aria-label={metricAriaLabel(metric, formatted)}\n />\n );\n }\n\n return (\n <div key={key} className=\"grid gap-1.5\">\n <div className=\"flex items-baseline justify-between gap-3 text-body-sm\">\n <span className=\"truncate text-muted-foreground\">{metric.label}</span>\n <span\n className={cn(\n \"shrink-0 font-mono tabular-nums\",\n overQuota ? \"text-warning\" : \"text-foreground\",\n )}\n >\n {formatted}\n </span>\n </div>\n <Progress\n value={metric.value}\n max={metric.max}\n intent={intent}\n aria-label={metricAriaLabel(metric, formatted)}\n />\n </div>\n );\n })}\n </div>\n );\n },\n);\nUsageMeter.displayName = \"UsageMeter\";\n\nexport { UsageMeter };\n"
19
- }
20
- ]
21
- }
@@ -1,101 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
- "name": "whiteboard",
4
- "type": "registry:ui",
5
- "title": "Whiteboard",
6
- "description": "View-only primitive that renders a declarative JSON scene as SVG with a hand-drawn aesthetic. Pan + zoom built-in; no editor surface. Subpath-isolated bundle in @theokit/ui.",
7
- "dependencies": [
8
- "perfect-freehand",
9
- "roughjs",
10
- "zod"
11
- ],
12
- "registryDependencies": [
13
- "https://usetheodev.github.io/theo-ui/r/tailwind-preset.json"
14
- ],
15
- "files": [
16
- {
17
- "path": "components/primitives/whiteboard/whiteboard.tsx",
18
- "type": "registry:ui",
19
- "target": "components/ui/whiteboard/whiteboard.tsx",
20
- "content": "\"use client\";\n\nimport { useEffect, useMemo, useRef } from \"react\";\n/**\n * `<Whiteboard>` — view-only primitive that turns a JSON scene into a\n * hand-drawn SVG. Lives in the isolated subpath `@theokit/ui/whiteboard`.\n *\n * See RFC 0001 (`docs/rfcs/0001-whiteboard.md`) and the plan in\n * `.claude/knowledge-base/plans/whiteboard-view-primitive-plan.md`.\n */\nimport rough from \"roughjs\";\nimport type { RoughGenerator } from \"roughjs/bin/generator\";\nimport { renderScene } from \"@/components/ui/whiteboard/render/scene\";\nimport type { WhiteboardScene, WhiteboardSceneInput } from \"@/components/ui/whiteboard/schema\";\nimport { type ValidationError, validateScene } from \"@/components/ui/whiteboard/validate\";\nimport { usePointerPan } from \"@/components/ui/whiteboard/viewport/use-pointer-pan\";\nimport { useViewport } from \"@/components/ui/whiteboard/viewport/use-viewport\";\n\n/** Input type accepted by the component (defaults like `headEnd` are optional). */\nexport type WhiteboardData = WhiteboardSceneInput;\nexport type { ValidationError } from \"@/components/ui/whiteboard/validate\";\n\nexport interface WhiteboardProps {\n /** The scene to render. Validated with Zod on every change. */\n data: WhiteboardData | unknown;\n className?: string;\n /** Starting zoom level (clamped 0.1–8). Default 1. */\n initialZoom?: number;\n /** World coordinate centered in the viewport on first render. */\n initialCenter?: [number, number];\n /** When true, computes a bounding box of all elements and zooms to fit. */\n fitOnLoad?: boolean;\n /** Called once per validation failure with the list of errors (EC-6: in useEffect, not render). */\n onValidationError?: (errors: ValidationError[]) => void;\n /** Accessible label for the SVG (defaults to \"Whiteboard diagram\"). */\n \"aria-label\"?: string;\n}\n\nfunction computeBounds(scene: WhiteboardScene): {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n} | null {\n if (scene.elements.length === 0) return null;\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n for (const el of scene.elements) {\n const x1 = el.x;\n const y1 = el.y;\n let x2 = el.x;\n let y2 = el.y;\n if (el.type === \"rect\" || el.type === \"ellipse\" || el.type === \"diamond\") {\n x2 = el.x + el.w;\n y2 = el.y + el.h;\n } else if (el.type === \"line\" || el.type === \"arrow\") {\n x2 = el.to[0];\n y2 = el.to[1];\n } else if (el.type === \"freedraw\") {\n for (const [px, py] of el.points) {\n const tx = el.x + px;\n const ty = el.y + py;\n if (tx < minX) minX = tx;\n if (ty < minY) minY = ty;\n if (tx > maxX) maxX = tx;\n if (ty > maxY) maxY = ty;\n }\n continue;\n }\n if (x1 < minX) minX = x1;\n if (y1 < minY) minY = y1;\n if (x2 < minX) minX = x2;\n if (y2 < minY) minY = y2;\n if (x1 > maxX) maxX = x1;\n if (y1 > maxY) maxY = y1;\n if (x2 > maxX) maxX = x2;\n if (y2 > maxY) maxY = y2;\n }\n if (!Number.isFinite(minX)) return null;\n return { minX, minY, maxX, maxY };\n}\n\nexport function Whiteboard({\n data,\n className,\n initialZoom,\n initialCenter,\n fitOnLoad,\n onValidationError,\n \"aria-label\": ariaLabel,\n}: WhiteboardProps) {\n const validation = useMemo(() => validateScene(data), [data]);\n const scene = validation.ok ? validation.scene : null;\n\n // EC-6: side effects only inside useEffect — never call user callbacks in render.\n useEffect(() => {\n if (!validation.ok && onValidationError) {\n onValidationError(validation.errors);\n }\n }, [validation, onValidationError]);\n\n // Stable rough.js generator across renders.\n const generator: RoughGenerator | null = useMemo(() => {\n if (typeof globalThis === \"undefined\") return null;\n try {\n return rough.generator();\n } catch {\n return null;\n }\n }, []);\n\n // Fallback dimensions for invalid scenes (still render a valid svg).\n const sceneWidth = scene?.width ?? 400;\n const sceneHeight = scene?.height ?? 300;\n const sceneBackground = scene?.background;\n\n const viewport = useViewport({\n width: sceneWidth,\n height: sceneHeight,\n initialZoom,\n initialCenter,\n });\n\n const svgRef = useRef<SVGSVGElement | null>(null);\n\n const fitTo = viewport.fitTo;\n // EC-14: fitOnLoad must run in useEffect (after mount), not during render.\n useEffect(() => {\n if (!fitOnLoad || !scene) return;\n const bounds = computeBounds(scene);\n if (!bounds) return;\n fitTo(bounds, { width: sceneWidth, height: sceneHeight });\n }, [fitOnLoad, scene, sceneWidth, sceneHeight, fitTo]);\n\n const handlers = usePointerPan(svgRef, viewport, {\n width: sceneWidth,\n height: sceneHeight,\n });\n\n const label = ariaLabel ?? \"Whiteboard diagram\";\n\n return (\n <svg\n data-slot=\"whiteboard\"\n ref={svgRef}\n viewBox={viewport.viewBox({ width: sceneWidth, height: sceneHeight })}\n width={sceneWidth}\n height={sceneHeight}\n className={className}\n role=\"img\"\n aria-label={label}\n xmlns=\"http://www.w3.org/2000/svg\"\n data-whiteboard-state={scene ? \"ok\" : \"invalid\"}\n style={{ touchAction: \"none\", userSelect: \"none\" }}\n {...handlers}\n >\n <title>{label}</title>\n {sceneBackground ? (\n <rect\n x={0}\n y={0}\n width={sceneWidth}\n height={sceneHeight}\n fill={sceneBackground}\n data-layer=\"background\"\n />\n ) : null}\n {scene && generator ? renderScene(scene, generator) : null}\n </svg>\n );\n}\n"
21
- },
22
- {
23
- "path": "components/primitives/whiteboard/schema.ts",
24
- "type": "registry:ui",
25
- "target": "components/ui/whiteboard/schema.ts",
26
- "content": "/**\n * Zod schema for the Whiteboard JSON v1 (`WhiteboardScene`).\n *\n * Design goals (see RFC 0001 §4 and ADR D4 in the plan):\n * - LLM-friendly: minimal fields per element.\n * - Discriminated union by `type` for exhaustive typing in TS.\n * - Defensive against LLM mistakes:\n * - EC-3: `.finite()` everywhere — rejects NaN / Infinity.\n * - EC-4: `.max()` on dimensions to cap absurd values from the model.\n */\nimport { z } from \"zod\";\n\nconst finiteNumber = z.number().finite();\nconst finitePositive = finiteNumber.positive();\n\nconst strokeStyle = z.enum([\"solid\", \"dashed\", \"dotted\"]);\nconst fillStyle = z.enum([\"hachure\", \"solid\", \"cross-hatch\", \"zigzag\"]);\nconst align = z.enum([\"left\", \"center\", \"right\"]);\nconst fontFamily = z.enum([\"sans\", \"serif\", \"mono\", \"hand\"]);\nconst roundness = z.enum([\"sharp\", \"round\"]);\n\nconst baseElement = z.object({\n id: z.string().max(120).optional(),\n x: finiteNumber,\n y: finiteNumber,\n stroke: z.string().max(64).optional(),\n strokeWidth: finitePositive.max(50).optional(),\n strokeStyle: strokeStyle.optional(),\n fill: z.string().max(64).optional(),\n fillStyle: fillStyle.optional(),\n opacity: finiteNumber.min(0).max(1).optional(),\n roughness: finiteNumber.min(0).max(3).optional(),\n seed: z.number().int().finite().optional(),\n});\n\nconst dim = finitePositive.max(20000);\nconst labelText = z.string().max(500);\n\nconst rectElement = baseElement.extend({\n type: z.literal(\"rect\"),\n w: dim,\n h: dim,\n label: labelText.optional(),\n roundness: roundness.optional(),\n});\n\nconst ellipseElement = baseElement.extend({\n type: z.literal(\"ellipse\"),\n w: dim,\n h: dim,\n label: labelText.optional(),\n});\n\nconst diamondElement = baseElement.extend({\n type: z.literal(\"diamond\"),\n w: dim,\n h: dim,\n label: labelText.optional(),\n});\n\nconst point2 = z.tuple([finiteNumber, finiteNumber]);\n\nconst lineElement = baseElement.extend({\n type: z.literal(\"line\"),\n to: point2,\n});\n\nconst arrowElement = baseElement.extend({\n type: z.literal(\"arrow\"),\n to: point2,\n label: labelText.optional(),\n headStart: z.boolean().optional(),\n headEnd: z.boolean().default(true),\n});\n\nconst textElement = baseElement.extend({\n type: z.literal(\"text\"),\n text: z.string().max(5000),\n fontSize: finitePositive.max(500).optional(),\n align: align.optional(),\n fontFamily: fontFamily.optional(),\n});\n\nconst freedrawPoint = z.tuple([finiteNumber, finiteNumber, finiteNumber.optional()]);\n\nconst freedrawElement = baseElement.extend({\n type: z.literal(\"freedraw\"),\n points: z.array(freedrawPoint).min(2).max(5000),\n});\n\nexport const whiteboardElement = z.discriminatedUnion(\"type\", [\n rectElement,\n ellipseElement,\n diamondElement,\n lineElement,\n arrowElement,\n textElement,\n freedrawElement,\n]);\n\nexport const whiteboardScene = z.object({\n version: z.literal(1),\n width: dim, // EC-4: clamped 1..20000\n height: dim, // EC-4: clamped 1..20000\n background: z.string().max(64).optional(),\n elements: z.array(whiteboardElement).max(5000),\n});\n\n// Internal \"parsed\" types (defaults applied) — used inside the renderer.\nexport type WhiteboardElement = z.output<typeof whiteboardElement>;\nexport type WhiteboardScene = z.output<typeof whiteboardScene>;\nexport type RectElement = z.output<typeof rectElement>;\nexport type EllipseElement = z.output<typeof ellipseElement>;\nexport type DiamondElement = z.output<typeof diamondElement>;\nexport type LineElement = z.output<typeof lineElement>;\nexport type ArrowElement = z.output<typeof arrowElement>;\nexport type TextElement = z.output<typeof textElement>;\nexport type FreedrawElement = z.output<typeof freedrawElement>;\n\n// Public \"input\" types (defaults optional) — used by consumers passing data.\nexport type WhiteboardElementInput = z.input<typeof whiteboardElement>;\nexport type WhiteboardSceneInput = z.input<typeof whiteboardScene>;\n"
27
- },
28
- {
29
- "path": "components/primitives/whiteboard/validate.ts",
30
- "type": "registry:ui",
31
- "target": "components/ui/whiteboard/validate.ts",
32
- "content": "/**\n * Public validator for `WhiteboardScene` JSON. Wraps Zod and converts errors\n * into a structured shape that an LLM can consume to self-correct.\n *\n * Returns `{ ok: true, scene }` on success or `{ ok: false, errors }` with\n * each error carrying `{ path, message, code, got? }`. See RFC 0001 §4.\n */\nimport { type WhiteboardScene, whiteboardScene } from \"@/components/ui/whiteboard/schema\";\n\nexport interface ValidationError {\n /** Dot-joined Zod path, e.g. \"elements.2.type\" or \"width\". */\n path: string;\n /** Human-readable explanation (passes through Zod's message). */\n message: string;\n /** Zod issue code: `invalid_type`, `too_small`, `invalid_literal`, ... */\n code: string;\n /** Actual value received at `path`. Populated for type mismatches, discriminator mismatches, and any issue where Zod's `received` is absent but the value can be recovered by walking the original input along `path`. */\n got?: unknown;\n}\n\nexport type ValidationResult =\n | { ok: true; scene: WhiteboardScene }\n | { ok: false; errors: ValidationError[] };\n\ninterface ZodLikeIssue {\n path: ReadonlyArray<string | number>;\n message: string;\n code: string;\n received?: unknown;\n}\n\n/** Walk `input` following the issue path and return the offending value. */\nfunction valueAtPath(input: unknown, path: ReadonlyArray<string | number>): unknown {\n let cursor: unknown = input;\n for (const segment of path) {\n if (cursor === null || typeof cursor !== \"object\") return undefined;\n cursor = (cursor as Record<string | number, unknown>)[segment];\n }\n return cursor;\n}\n\nfunction formatIssue(issue: ZodLikeIssue, input: unknown): ValidationError {\n const error: ValidationError = {\n path: issue.path.join(\".\"),\n message: issue.message,\n code: issue.code,\n };\n // Populate `got` from the issue when Zod provides it (invalid_type), or by\n // walking the original input along the path (invalid_union / invalid_value).\n // LLM auto-correction benefits from seeing what was actually emitted.\n if (\"received\" in issue && issue.received !== undefined) {\n error.got = issue.received;\n } else if (issue.path.length > 0) {\n const value = valueAtPath(input, issue.path);\n if (value !== undefined) error.got = value;\n }\n return error;\n}\n\nexport function validateScene(input: unknown): ValidationResult {\n const result = whiteboardScene.safeParse(input);\n if (result.success) {\n return { ok: true, scene: result.data };\n }\n const errors = result.error.issues.map((issue) =>\n formatIssue(issue as unknown as ZodLikeIssue, input),\n );\n return { ok: false, errors };\n}\n"
33
- },
34
- {
35
- "path": "components/primitives/whiteboard/seed.ts",
36
- "type": "registry:ui",
37
- "target": "components/ui/whiteboard/seed.ts",
38
- "content": "/**\n * Deterministic seed derivation for rough.js — see ADR D9.\n *\n * rough.js uses pseudo-randomness to produce the hand-drawn look. Without a\n * stable seed every render shifts the strokes slightly, breaking snapshot\n * tests, causing SSR hydration mismatches, and visible jitter when the parent\n * re-renders. FNV-1a 32-bit gives us a fast, dependency-free hash.\n */\n\nconst FNV_OFFSET_BASIS_32 = 0x811c9dc5;\nconst FNV_PRIME_32 = 0x01000193;\n\n/** FNV-1a 32-bit hash. Returns a signed 32-bit integer. */\nexport function fnv1a32(input: string): number {\n let hash = FNV_OFFSET_BASIS_32;\n for (let i = 0; i < input.length; i++) {\n hash ^= input.charCodeAt(i);\n // Multiply by the FNV prime, but keep within 32-bit unsigned range.\n hash = Math.imul(hash, FNV_PRIME_32);\n }\n // Cast to signed 32-bit via `| 0`.\n return hash | 0;\n}\n\ninterface SeedableShape {\n type: string;\n x: number;\n y: number;\n w?: number;\n h?: number;\n label?: string;\n seed?: number;\n}\n\n/** Return the element's explicit `seed` or derive a stable one from its props. */\nexport function deriveSeed(el: SeedableShape): number {\n if (typeof el.seed === \"number\" && Number.isFinite(el.seed)) {\n return el.seed | 0;\n }\n // Compose a key from the dimensions that visually define the shape. Other\n // fields (colors, opacity) don't change the underlying rough.js geometry.\n const key = `${el.type}|${el.x}|${el.y}|${el.w ?? \"\"}|${el.h ?? \"\"}|${el.label ?? \"\"}`;\n return fnv1a32(key);\n}\n"
39
- },
40
- {
41
- "path": "components/primitives/whiteboard/index.ts",
42
- "type": "registry:ui",
43
- "target": "components/ui/whiteboard/index.ts",
44
- "content": "export { Whiteboard, type WhiteboardData, type WhiteboardProps } from \"@/components/ui/whiteboard/whiteboard\";\nexport { validateScene, type ValidationError, type ValidationResult } from \"@/components/ui/whiteboard/validate\";\n"
45
- },
46
- {
47
- "path": "components/primitives/whiteboard/render/scene.tsx",
48
- "type": "registry:ui",
49
- "target": "components/ui/whiteboard/render/scene.tsx",
50
- "content": "import type { RoughGenerator } from \"roughjs/bin/generator\";\nimport type { WhiteboardElement, WhiteboardScene } from \"@/components/ui/whiteboard/schema\";\nimport { deriveSeed } from \"@/components/ui/whiteboard/seed\";\nimport { renderFreedraw } from \"@/components/ui/whiteboard/render/freedraw\";\nimport { renderArrow, renderLine } from \"@/components/ui/whiteboard/render/line\";\nimport { renderDiamond, renderEllipse, renderRect } from \"@/components/ui/whiteboard/render/shape\";\nimport { renderText } from \"@/components/ui/whiteboard/render/text\";\n\nfunction elementWithSeed<T extends WhiteboardElement>(el: T): T {\n if (typeof el.seed === \"number\") return el;\n return { ...el, seed: deriveSeed(el) } as T;\n}\n\nfunction renderElement(el: WhiteboardElement, gen: RoughGenerator): React.ReactNode {\n switch (el.type) {\n case \"rect\":\n return renderRect(gen, elementWithSeed(el));\n case \"ellipse\":\n return renderEllipse(gen, elementWithSeed(el));\n case \"diamond\":\n return renderDiamond(gen, elementWithSeed(el));\n case \"line\":\n return renderLine(gen, elementWithSeed(el));\n case \"arrow\":\n return renderArrow(gen, elementWithSeed(el));\n case \"text\":\n return renderText(elementWithSeed(el));\n case \"freedraw\":\n return renderFreedraw(elementWithSeed(el));\n }\n}\n\nexport function renderScene(scene: WhiteboardScene, gen: RoughGenerator): React.ReactNode {\n return (\n <>\n {scene.elements.map((el, i) => (\n <g\n key={el.id ?? `__idx-${i}`}\n data-element-id={el.id ?? String(i)}\n data-element-type={el.type}\n >\n {renderElement(el, gen)}\n </g>\n ))}\n </>\n );\n}\n"
51
- },
52
- {
53
- "path": "components/primitives/whiteboard/render/shape.tsx",
54
- "type": "registry:ui",
55
- "target": "components/ui/whiteboard/render/shape.tsx",
56
- "content": "import type { RoughGenerator } from \"roughjs/bin/generator\";\nimport type { DiamondElement, EllipseElement, RectElement } from \"@/components/ui/whiteboard/schema\";\nimport { toRoughPaths } from \"@/components/ui/whiteboard/render/rough-paths\";\nimport { buildOptions, strokeDashArray } from \"@/components/ui/whiteboard/render/style\";\n\ninterface LabelProps {\n cx: number;\n cy: number;\n label: string;\n stroke?: string;\n}\n\nfunction Label({ cx, cy, label, stroke }: LabelProps) {\n return (\n <text\n data-slot=\"label\"\n x={cx}\n y={cy}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n fontSize={16}\n fontFamily=\"ui-sans-serif, system-ui, sans-serif\"\n fill={stroke ?? \"currentColor\"}\n style={{ pointerEvents: \"none\" }}\n >\n {label}\n </text>\n );\n}\n\nfunction pathsToReactNodes(\n paths: ReturnType<typeof toRoughPaths>,\n keyPrefix: string,\n outlineStroke: string,\n dashArray?: string,\n): React.ReactNode[] {\n return paths.map((p, i) => {\n // Apply stroke-dasharray only to paths whose stroke matches the outline\n // color — this avoids dashing hachure/cross-hatch fill lines (which are\n // stroked in the fill color, not the outline color) when a shape mixes\n // fillStyle with strokeStyle.\n const isOutline = p.stroke === outlineStroke;\n return (\n <path\n // biome-ignore lint/suspicious/noArrayIndexKey: rough.js path order is stable for a given (geometry, seed) tuple — index is the most precise key.\n key={`${keyPrefix}-${i}`}\n d={p.d}\n stroke={p.stroke}\n strokeWidth={p.strokeWidth}\n fill={p.fill ?? \"none\"}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeDasharray={isOutline ? dashArray : undefined}\n />\n );\n });\n}\n\nexport function renderRect(gen: RoughGenerator, el: RectElement): React.ReactNode {\n const opts = buildOptions(el, el.seed ?? 0);\n const drawable = gen.rectangle(el.x, el.y, el.w, el.h, opts);\n const paths = toRoughPaths(gen, drawable);\n const dash = strokeDashArray(el.strokeStyle, el.strokeWidth ?? 1.5);\n return (\n <g opacity={el.opacity ?? 1}>\n {pathsToReactNodes(paths, \"rect\", opts.stroke as string, dash)}\n {el.label ? (\n <Label cx={el.x + el.w / 2} cy={el.y + el.h / 2} label={el.label} stroke={el.stroke} />\n ) : null}\n </g>\n );\n}\n\nexport function renderEllipse(gen: RoughGenerator, el: EllipseElement): React.ReactNode {\n const opts = buildOptions(el, el.seed ?? 0);\n // rough.js ellipse takes center + width + height.\n const drawable = gen.ellipse(el.x + el.w / 2, el.y + el.h / 2, el.w, el.h, opts);\n const paths = toRoughPaths(gen, drawable);\n const dash = strokeDashArray(el.strokeStyle, el.strokeWidth ?? 1.5);\n return (\n <g opacity={el.opacity ?? 1}>\n {pathsToReactNodes(paths, \"ellipse\", opts.stroke as string, dash)}\n {el.label ? (\n <Label cx={el.x + el.w / 2} cy={el.y + el.h / 2} label={el.label} stroke={el.stroke} />\n ) : null}\n </g>\n );\n}\n\nexport function renderDiamond(gen: RoughGenerator, el: DiamondElement): React.ReactNode {\n const opts = buildOptions(el, el.seed ?? 0);\n const cx = el.x + el.w / 2;\n const cy = el.y + el.h / 2;\n const points: [number, number][] = [\n [cx, el.y], // top\n [el.x + el.w, cy], // right\n [cx, el.y + el.h], // bottom\n [el.x, cy], // left\n ];\n const drawable = gen.polygon(points, opts);\n const paths = toRoughPaths(gen, drawable);\n const dash = strokeDashArray(el.strokeStyle, el.strokeWidth ?? 1.5);\n return (\n <g opacity={el.opacity ?? 1}>\n {pathsToReactNodes(paths, \"diamond\", opts.stroke as string, dash)}\n {el.label ? <Label cx={cx} cy={cy} label={el.label} stroke={el.stroke} /> : null}\n </g>\n );\n}\n"
57
- },
58
- {
59
- "path": "components/primitives/whiteboard/render/line.tsx",
60
- "type": "registry:ui",
61
- "target": "components/ui/whiteboard/render/line.tsx",
62
- "content": "import type { RoughGenerator } from \"roughjs/bin/generator\";\nimport type { ArrowElement, LineElement } from \"@/components/ui/whiteboard/schema\";\nimport { toRoughPaths } from \"@/components/ui/whiteboard/render/rough-paths\";\nimport { buildOptions, strokeDashArray } from \"@/components/ui/whiteboard/render/style\";\n\nconst HEAD_BASE_PX = 12;\nconst HEAD_ANGLE_RAD = Math.PI / 7; // ~25°\n\ninterface HeadGeom {\n apexX: number;\n apexY: number;\n leftX: number;\n leftY: number;\n rightX: number;\n rightY: number;\n}\n\nfunction arrowHeadGeometry(\n fromX: number,\n fromY: number,\n toX: number,\n toY: number,\n strokeWidth: number,\n): HeadGeom | null {\n const dx = toX - fromX;\n const dy = toY - fromY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n if (dist === 0) return null;\n const angle = Math.atan2(dy, dx);\n // EC-7: clamp head length so it never exceeds 40% of the segment.\n const headLen = Math.min(HEAD_BASE_PX + strokeWidth * 2, dist * 0.4);\n const leftX = toX - headLen * Math.cos(angle - HEAD_ANGLE_RAD);\n const leftY = toY - headLen * Math.sin(angle - HEAD_ANGLE_RAD);\n const rightX = toX - headLen * Math.cos(angle + HEAD_ANGLE_RAD);\n const rightY = toY - headLen * Math.sin(angle + HEAD_ANGLE_RAD);\n return { apexX: toX, apexY: toY, leftX, leftY, rightX, rightY };\n}\n\nfunction lineBody(\n gen: RoughGenerator,\n el: LineElement | ArrowElement,\n seed: number,\n): React.ReactNode[] {\n const opts = buildOptions(el, seed);\n const drawable = gen.line(el.x, el.y, el.to[0], el.to[1], opts);\n const dash = strokeDashArray(el.strokeStyle, el.strokeWidth ?? 1.5);\n return toRoughPaths(gen, drawable).map((p, i) => (\n <path\n // biome-ignore lint/suspicious/noArrayIndexKey: stable rough.js path order\n key={`body-${i}`}\n d={p.d}\n stroke={p.stroke}\n strokeWidth={p.strokeWidth}\n fill=\"none\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeDasharray={dash}\n />\n ));\n}\n\nfunction arrowHead(\n gen: RoughGenerator,\n fromX: number,\n fromY: number,\n toX: number,\n toY: number,\n seed: number,\n el: ArrowElement,\n side: \"start\" | \"end\",\n): React.ReactNode[] {\n const geom = arrowHeadGeometry(fromX, fromY, toX, toY, el.strokeWidth ?? 1.5);\n if (!geom) return [];\n const opts = buildOptions(el, seed);\n // Two short rough-drawn segments forming the V.\n const leftDrawable = gen.line(geom.apexX, geom.apexY, geom.leftX, geom.leftY, opts);\n const rightDrawable = gen.line(geom.apexX, geom.apexY, geom.rightX, geom.rightY, opts);\n const leftPaths = toRoughPaths(gen, leftDrawable);\n const rightPaths = toRoughPaths(gen, rightDrawable);\n const nodes: React.ReactNode[] = [];\n for (const [i, p] of leftPaths.entries()) {\n nodes.push(\n <path\n key={`${side}-l-${i}`}\n d={p.d}\n stroke={p.stroke}\n strokeWidth={p.strokeWidth}\n fill=\"none\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n data-line-part=\"arrowhead\"\n />,\n );\n }\n for (const [i, p] of rightPaths.entries()) {\n nodes.push(\n <path\n key={`${side}-r-${i}`}\n d={p.d}\n stroke={p.stroke}\n strokeWidth={p.strokeWidth}\n fill=\"none\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n data-line-part=\"arrowhead\"\n />,\n );\n }\n return nodes;\n}\n\nexport function renderLine(gen: RoughGenerator, el: LineElement): React.ReactNode {\n return <g opacity={el.opacity ?? 1}>{lineBody(gen, el, el.seed ?? 0)}</g>;\n}\n\nexport function renderArrow(gen: RoughGenerator, el: ArrowElement): React.ReactNode {\n const seed = el.seed ?? 0;\n const nodes: React.ReactNode[] = [...lineBody(gen, el, seed)];\n if (el.headEnd !== false) {\n nodes.push(...arrowHead(gen, el.x, el.y, el.to[0], el.to[1], seed + 1, el, \"end\"));\n }\n if (el.headStart) {\n nodes.push(...arrowHead(gen, el.to[0], el.to[1], el.x, el.y, seed + 2, el, \"start\"));\n }\n let label: React.ReactNode = null;\n if (el.label) {\n const midX = (el.x + el.to[0]) / 2;\n const midY = (el.y + el.to[1]) / 2;\n // Offset perpendicular to the line so label doesn't sit on the stroke.\n const dx = el.to[0] - el.x;\n const dy = el.to[1] - el.y;\n const dist = Math.sqrt(dx * dx + dy * dy) || 1;\n const nx = -dy / dist;\n const ny = dx / dist;\n const offset = 12;\n label = (\n <text\n x={midX + nx * offset}\n y={midY + ny * offset}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n fontSize={14}\n fontFamily=\"ui-sans-serif, system-ui, sans-serif\"\n fill={el.stroke ?? \"currentColor\"}\n style={{ pointerEvents: \"none\" }}\n >\n {el.label}\n </text>\n );\n }\n return (\n <g opacity={el.opacity ?? 1}>\n {nodes}\n {label}\n </g>\n );\n}\n"
63
- },
64
- {
65
- "path": "components/primitives/whiteboard/render/freedraw.tsx",
66
- "type": "registry:ui",
67
- "target": "components/ui/whiteboard/render/freedraw.tsx",
68
- "content": "import { getStroke } from \"perfect-freehand\";\nimport type { FreedrawElement } from \"@/components/ui/whiteboard/schema\";\n\nconst DEFAULT_STROKE_OPTIONS = {\n size: 8,\n thinning: 0.5,\n smoothing: 0.5,\n streamline: 0.5,\n};\n\nfunction fmt(n: number | undefined): string {\n return typeof n === \"number\" && Number.isFinite(n) ? n.toFixed(2) : \"0\";\n}\n\nfunction svgPathFromStroke(points: number[][]): string {\n if (points.length === 0) return \"\";\n const first = points[0] ?? [];\n let d = `M ${fmt(first[0])} ${fmt(first[1])}`;\n for (let i = 1; i < points.length; i++) {\n const p = points[i] ?? [];\n d += ` L ${fmt(p[0])} ${fmt(p[1])}`;\n }\n d += \" Z\";\n return d;\n}\n\nexport function renderFreedraw(el: FreedrawElement): React.ReactNode {\n const size = (el.strokeWidth ?? 1.5) * 5;\n const inputPoints = el.points.map(([x, y, pressure]) => {\n const tx = el.x + x;\n const ty = el.y + y;\n return pressure === undefined ? [tx, ty] : [tx, ty, pressure];\n });\n const stroke = getStroke(inputPoints, {\n ...DEFAULT_STROKE_OPTIONS,\n size,\n });\n const d = svgPathFromStroke(stroke as number[][]);\n return (\n <g opacity={el.opacity ?? 1}>\n <path d={d} fill={el.stroke ?? \"currentColor\"} stroke=\"none\" />\n </g>\n );\n}\n"
69
- },
70
- {
71
- "path": "components/primitives/whiteboard/render/text.tsx",
72
- "type": "registry:ui",
73
- "target": "components/ui/whiteboard/render/text.tsx",
74
- "content": "import type { TextElement } from \"@/components/ui/whiteboard/schema\";\n\nconst FONT_STACKS: Record<NonNullable<TextElement[\"fontFamily\"]>, string> = {\n sans: \"ui-sans-serif, system-ui, sans-serif\",\n serif: \"ui-serif, Georgia, serif\",\n mono: \"ui-monospace, SFMono-Regular, Menlo, monospace\",\n hand: '\"Virgil\", \"Caveat\", \"Comic Sans MS\", cursive',\n};\n\nfunction textAnchor(align: TextElement[\"align\"]): \"start\" | \"middle\" | \"end\" {\n if (align === \"center\") return \"middle\";\n if (align === \"right\") return \"end\";\n return \"start\";\n}\n\nexport function renderText(el: TextElement): React.ReactNode {\n const fontSize = el.fontSize ?? 18;\n const fontFamily = FONT_STACKS[el.fontFamily ?? \"hand\"];\n const anchor = textAnchor(el.align);\n const lines = el.text.split(\"\\n\");\n // dy: first tspan at 0, subsequent at fontSize * 1.2 line-height.\n return (\n <g opacity={el.opacity ?? 1}>\n <text\n x={el.x}\n y={el.y}\n textAnchor={anchor}\n dominantBaseline=\"hanging\"\n fontSize={fontSize}\n fontFamily={fontFamily}\n fill={el.stroke ?? \"currentColor\"}\n >\n {lines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: text is split deterministically on '\\n' so the line index is the most stable key.\n <tspan key={i} x={el.x} dy={i === 0 ? 0 : fontSize * 1.2}>\n {line}\n </tspan>\n ))}\n </text>\n </g>\n );\n}\n"
75
- },
76
- {
77
- "path": "components/primitives/whiteboard/render/rough-paths.ts",
78
- "type": "registry:ui",
79
- "target": "components/ui/whiteboard/render/rough-paths.ts",
80
- "content": "/**\n * Thin adapter over `roughjs` — converts a `Drawable` into the array of\n * `<path>` descriptors we render in SVG. The actual `rough.generator()`\n * instance is created once per scene and reused across elements.\n */\nimport type { Drawable, Options } from \"roughjs/bin/core\";\nimport type { RoughGenerator } from \"roughjs/bin/generator\";\n\nexport interface RoughPath {\n d: string;\n stroke: string;\n strokeWidth: number;\n fill?: string;\n}\n\nexport type RoughOptions = Options;\n\n/** Convert a rough.js Drawable into renderable SVG path descriptors. */\nexport function toRoughPaths(generator: RoughGenerator, drawable: Drawable): RoughPath[] {\n const paths = generator.toPaths(drawable);\n return paths.map((p) => ({\n d: p.d,\n stroke: p.stroke,\n strokeWidth: p.strokeWidth,\n fill: p.fill,\n }));\n}\n"
81
- },
82
- {
83
- "path": "components/primitives/whiteboard/render/style.ts",
84
- "type": "registry:ui",
85
- "target": "components/ui/whiteboard/render/style.ts",
86
- "content": "/**\n * Maps a `WhiteboardElement` style props onto rough.js `Options`.\n */\nimport type { Options } from \"roughjs/bin/core\";\n\ninterface StyleSource {\n stroke?: string;\n strokeWidth?: number;\n strokeStyle?: \"solid\" | \"dashed\" | \"dotted\";\n fill?: string;\n fillStyle?: \"hachure\" | \"solid\" | \"cross-hatch\" | \"zigzag\";\n opacity?: number;\n roughness?: number;\n}\n\nconst DEFAULT_STROKE = \"currentColor\";\n\nexport function buildOptions(src: StyleSource, seed: number): Options {\n const o: Options = {\n seed,\n stroke: src.stroke ?? DEFAULT_STROKE,\n strokeWidth: src.strokeWidth ?? 1.5,\n roughness: src.roughness ?? 1.2,\n };\n if (src.strokeStyle === \"dashed\") o.strokeLineDash = [10, 6];\n else if (src.strokeStyle === \"dotted\") o.strokeLineDash = [2, 4];\n if (src.fill) o.fill = src.fill;\n if (src.fillStyle) o.fillStyle = src.fillStyle;\n return o;\n}\n\n/**\n * SVG `stroke-dasharray` value for a logical strokeStyle. rough.js's\n * `RoughGenerator.toPaths()` does NOT propagate `strokeLineDash` into the\n * returned `PathInfo` — only the canvas / direct-SVG backends apply it. We\n * must set the attribute ourselves on the rendered `<path>`. Returns\n * `undefined` for `solid` and any unknown value.\n */\nexport function strokeDashArray(\n strokeStyle: StyleSource[\"strokeStyle\"],\n strokeWidth: number,\n): string | undefined {\n if (strokeStyle === \"dashed\") {\n const dash = Math.max(8, strokeWidth * 6);\n const gap = Math.max(5, strokeWidth * 4);\n return `${dash} ${gap}`;\n }\n if (strokeStyle === \"dotted\") {\n // 1×strokeWidth dot, ~2×strokeWidth gap. Round end caps make these look\n // like dots instead of squares.\n return `${strokeWidth} ${strokeWidth * 2.5}`;\n }\n return undefined;\n}\n\nexport { DEFAULT_STROKE };\n"
87
- },
88
- {
89
- "path": "components/primitives/whiteboard/viewport/use-pointer-pan.ts",
90
- "type": "registry:ui",
91
- "target": "components/ui/whiteboard/viewport/use-pointer-pan.ts",
92
- "content": "import { useCallback, useEffect, useRef } from \"react\";\nimport type { ViewportControls } from \"@/components/ui/whiteboard/viewport/use-viewport\";\n\ninterface DragState {\n pointerId: number;\n lastX: number;\n lastY: number;\n}\n\n/** Attaches pan + zoom listeners to an SVG element. */\nexport function usePointerPan(\n ref: React.RefObject<SVGSVGElement | null>,\n viewport: ViewportControls,\n size: { width: number; height: number },\n): {\n onPointerDown: (e: React.PointerEvent<SVGSVGElement>) => void;\n onPointerMove: (e: React.PointerEvent<SVGSVGElement>) => void;\n onPointerUp: (e: React.PointerEvent<SVGSVGElement>) => void;\n onPointerCancel: (e: React.PointerEvent<SVGSVGElement>) => void;\n} {\n const dragRef = useRef<DragState | null>(null);\n const spaceHeldRef = useRef(false);\n\n // EC-2: wheel events on React's JSX onWheel are passive — `preventDefault`\n // does nothing, which causes the page to scroll while the user tries to\n // zoom. We must attach the wheel handler imperatively with `{passive:false}`.\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const handler = (e: WheelEvent) => {\n e.preventDefault();\n const rect = el.getBoundingClientRect();\n const localX = e.clientX - rect.left;\n const localY = e.clientY - rect.top;\n // Map screen-px → viewBox coords by ratio of rect to size.\n const scaleX = size.width / rect.width;\n const scaleY = size.height / rect.height;\n viewport.zoomAt(localX * scaleX, localY * scaleY, -e.deltaY * 0.001, size);\n };\n el.addEventListener(\"wheel\", handler, { passive: false });\n return () => el.removeEventListener(\"wheel\", handler);\n }, [ref, viewport, size]);\n\n // Track Space key for pan-with-any-button (Excalidraw \"hand\" mode).\n useEffect(() => {\n const down = (e: KeyboardEvent) => {\n if (e.code === \"Space\") spaceHeldRef.current = true;\n };\n const up = (e: KeyboardEvent) => {\n if (e.code === \"Space\") spaceHeldRef.current = false;\n };\n window.addEventListener(\"keydown\", down);\n window.addEventListener(\"keyup\", up);\n return () => {\n window.removeEventListener(\"keydown\", down);\n window.removeEventListener(\"keyup\", up);\n };\n }, []);\n\n const onPointerDown = useCallback((e: React.PointerEvent<SVGSVGElement>) => {\n // EC-10: only start drag when the down event is on our SVG, not on a\n // child rendered via portal/etc. event.currentTarget guarantees this.\n if (e.target instanceof Element && !e.currentTarget.contains(e.target)) return;\n // Left button (0) or middle (1) or Space-held pan.\n const isMouseButton = e.button === 0 || e.button === 1;\n if (!isMouseButton && e.pointerType === \"mouse\") return;\n if (e.pointerType === \"mouse\" && e.button !== 1 && !spaceHeldRef.current && e.button !== 0) {\n return;\n }\n dragRef.current = { pointerId: e.pointerId, lastX: e.clientX, lastY: e.clientY };\n e.currentTarget.setPointerCapture(e.pointerId);\n }, []);\n\n const onPointerMove = useCallback(\n (e: React.PointerEvent<SVGSVGElement>) => {\n const drag = dragRef.current;\n if (!drag || drag.pointerId !== e.pointerId) return;\n const dx = e.clientX - drag.lastX;\n const dy = e.clientY - drag.lastY;\n drag.lastX = e.clientX;\n drag.lastY = e.clientY;\n const el = ref.current;\n if (!el) return;\n const rect = el.getBoundingClientRect();\n const scaleX = size.width / rect.width;\n const scaleY = size.height / rect.height;\n viewport.pan(dx * scaleX, dy * scaleY);\n },\n [ref, viewport, size],\n );\n\n const releaseDrag = useCallback((e: React.PointerEvent<SVGSVGElement>) => {\n const drag = dragRef.current;\n if (!drag || drag.pointerId !== e.pointerId) return;\n try {\n e.currentTarget.releasePointerCapture(e.pointerId);\n } catch {\n // Already released — ignore.\n }\n dragRef.current = null;\n }, []);\n\n return {\n onPointerDown,\n onPointerMove,\n onPointerUp: releaseDrag,\n onPointerCancel: releaseDrag,\n };\n}\n"
93
- },
94
- {
95
- "path": "components/primitives/whiteboard/viewport/use-viewport.ts",
96
- "type": "registry:ui",
97
- "target": "components/ui/whiteboard/viewport/use-viewport.ts",
98
- "content": "/**\n * Viewport state and helpers — pan via (x, y) and zoom via a scalar.\n *\n * Coordinate system:\n * - World space: where elements are defined (the JSON `x`, `y` etc.).\n * - Screen space: the rendered SVG bounding box on the page.\n * - viewBox: `${x} ${y} ${width/zoom} ${height/zoom}`. Increasing zoom\n * SHRINKS the viewBox dimensions, magnifying the rendered content.\n *\n * `zoomAt(screenX, screenY, delta, viewportSize)` keeps the world point under\n * the cursor stable while zooming (see test for invariant).\n */\nimport { useCallback, useMemo, useState } from \"react\";\n\nexport const MIN_ZOOM = 0.1;\nexport const MAX_ZOOM = 8;\n\nexport interface ViewportState {\n x: number;\n y: number;\n zoom: number;\n}\n\nexport interface ViewportSize {\n width: number;\n height: number;\n}\n\nexport interface ViewportBounds {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n}\n\nexport interface UseViewportOptions {\n width: number;\n height: number;\n initialZoom?: number;\n initialCenter?: [number, number];\n}\n\nexport interface ViewportControls {\n state: ViewportState;\n pan: (dx: number, dy: number) => void;\n setZoom: (zoom: number) => void;\n zoomAt: (screenX: number, screenY: number, delta: number, size: ViewportSize) => void;\n reset: () => void;\n fitTo: (bounds: ViewportBounds, size: ViewportSize) => void;\n viewBox: (size: ViewportSize) => string;\n}\n\nfunction clampZoom(z: number): number {\n if (!Number.isFinite(z)) return 1;\n return Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, z));\n}\n\nexport function useViewport(opts: UseViewportOptions): ViewportControls {\n const initial: ViewportState = useMemo(() => {\n const zoom = clampZoom(opts.initialZoom ?? 1);\n if (opts.initialCenter) {\n // Position viewBox so initialCenter sits at the visual center.\n const [cx, cy] = opts.initialCenter;\n return {\n x: cx - opts.width / (2 * zoom),\n y: cy - opts.height / (2 * zoom),\n zoom,\n };\n }\n return { x: 0, y: 0, zoom };\n }, [opts.initialCenter, opts.initialZoom, opts.width, opts.height]);\n\n const [state, setState] = useState<ViewportState>(initial);\n\n const pan = useCallback((dx: number, dy: number) => {\n setState((prev) => ({ ...prev, x: prev.x - dx / prev.zoom, y: prev.y - dy / prev.zoom }));\n }, []);\n\n const setZoom = useCallback((zoom: number) => {\n setState((prev) => ({ ...prev, zoom: clampZoom(zoom) }));\n }, []);\n\n const zoomAt = useCallback(\n (screenX: number, screenY: number, delta: number, _size: ViewportSize) => {\n setState((prev) => {\n const oldZoom = prev.zoom;\n const newZoom = clampZoom(prev.zoom * Math.exp(delta));\n if (newZoom === oldZoom) return prev;\n // World coordinate currently under cursor.\n const worldX = prev.x + screenX / oldZoom;\n const worldY = prev.y + screenY / oldZoom;\n // After zoom, we want the same screen point to map to the same world point.\n // viewBox.x + (screenX / newZoom) === worldX → x = worldX - screenX/newZoom.\n return {\n x: worldX - screenX / newZoom,\n y: worldY - screenY / newZoom,\n zoom: newZoom,\n };\n });\n },\n [],\n );\n\n const reset = useCallback(() => setState(initial), [initial]);\n\n const fitTo = useCallback((bounds: ViewportBounds, size: ViewportSize) => {\n const bboxWidth = Math.max(1, bounds.maxX - bounds.minX);\n const bboxHeight = Math.max(1, bounds.maxY - bounds.minY);\n const zoomX = size.width / bboxWidth;\n const zoomY = size.height / bboxHeight;\n const zoom = clampZoom(Math.min(zoomX, zoomY));\n const cx = (bounds.minX + bounds.maxX) / 2;\n const cy = (bounds.minY + bounds.maxY) / 2;\n setState({\n x: cx - size.width / (2 * zoom),\n y: cy - size.height / (2 * zoom),\n zoom,\n });\n }, []);\n\n const viewBox = useCallback(\n (size: ViewportSize) =>\n `${state.x} ${state.y} ${size.width / state.zoom} ${size.height / state.zoom}`,\n [state],\n );\n\n return { state, pan, setZoom, zoomAt, reset, fitTo, viewBox };\n}\n"
99
- }
100
- ]
101
- }