@usetheo/ui 0.12.0 → 0.13.1

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 (1227) hide show
  1. package/CHANGELOG.md +634 -68
  2. package/DESIGN.md +18 -18
  3. package/NOTICE +2 -2
  4. package/README.md +69 -66
  5. package/dist/chunk-23YEYNDS.js +89 -0
  6. package/dist/chunk-23YEYNDS.js.map +1 -0
  7. package/dist/chunk-25KBUQEQ.js +95 -0
  8. package/dist/chunk-25KBUQEQ.js.map +1 -0
  9. package/dist/chunk-2A3E5Y72.js +165 -0
  10. package/dist/chunk-2A3E5Y72.js.map +1 -0
  11. package/dist/chunk-2EUKYGH5.js +98 -0
  12. package/dist/chunk-2EUKYGH5.js.map +1 -0
  13. package/dist/chunk-2NZYMQKT.js +56 -0
  14. package/dist/chunk-2NZYMQKT.js.map +1 -0
  15. package/dist/chunk-2OVFVPSZ.js +26 -0
  16. package/dist/chunk-2OVFVPSZ.js.map +1 -0
  17. package/dist/chunk-2UP7SECE.js +78 -0
  18. package/dist/chunk-2UP7SECE.js.map +1 -0
  19. package/dist/chunk-33ETHPT7.js +112 -0
  20. package/dist/chunk-33ETHPT7.js.map +1 -0
  21. package/dist/chunk-33IIDFSM.js +43 -0
  22. package/dist/chunk-33IIDFSM.js.map +1 -0
  23. package/dist/chunk-345HYADQ.js +88 -0
  24. package/dist/chunk-345HYADQ.js.map +1 -0
  25. package/dist/chunk-3ZXZGZOX.js +57 -0
  26. package/dist/chunk-3ZXZGZOX.js.map +1 -0
  27. package/dist/chunk-45FWKR23.js +62 -0
  28. package/dist/chunk-45FWKR23.js.map +1 -0
  29. package/dist/chunk-4DTLUBRW.js +122 -0
  30. package/dist/chunk-4DTLUBRW.js.map +1 -0
  31. package/dist/chunk-4EH6F54D.js +69 -0
  32. package/dist/chunk-4EH6F54D.js.map +1 -0
  33. package/dist/chunk-4EKF4EIE.js +140 -0
  34. package/dist/chunk-4EKF4EIE.js.map +1 -0
  35. package/dist/chunk-4SMYNLTA.js +83 -0
  36. package/dist/chunk-4SMYNLTA.js.map +1 -0
  37. package/dist/chunk-4UBFLXS3.js +35 -0
  38. package/dist/chunk-4UBFLXS3.js.map +1 -0
  39. package/dist/chunk-52J7SDYH.js +48 -0
  40. package/dist/chunk-52J7SDYH.js.map +1 -0
  41. package/dist/chunk-5XCTTXC3.js +180 -0
  42. package/dist/chunk-5XCTTXC3.js.map +1 -0
  43. package/dist/chunk-624AATQZ.js +77 -0
  44. package/dist/chunk-624AATQZ.js.map +1 -0
  45. package/dist/chunk-6IODJQWC.js +47 -0
  46. package/dist/chunk-6IODJQWC.js.map +1 -0
  47. package/dist/chunk-75IDWFYX.js +134 -0
  48. package/dist/chunk-75IDWFYX.js.map +1 -0
  49. package/dist/chunk-7BGCWJQR.js +113 -0
  50. package/dist/chunk-7BGCWJQR.js.map +1 -0
  51. package/dist/chunk-7BQXMG2A.js +93 -0
  52. package/dist/chunk-7BQXMG2A.js.map +1 -0
  53. package/dist/chunk-7RFWVNQA.js +47 -0
  54. package/dist/chunk-7RFWVNQA.js.map +1 -0
  55. package/dist/chunk-7T4NK6W6.js +192 -0
  56. package/dist/chunk-7T4NK6W6.js.map +1 -0
  57. package/dist/chunk-A3OU6ICP.js +91 -0
  58. package/dist/chunk-A3OU6ICP.js.map +1 -0
  59. package/dist/chunk-ABBXLAJW.js +74 -0
  60. package/dist/chunk-ABBXLAJW.js.map +1 -0
  61. package/dist/chunk-AENNHS3S.js +83 -0
  62. package/dist/chunk-AENNHS3S.js.map +1 -0
  63. package/dist/chunk-AXNJFBYX.js +136 -0
  64. package/dist/chunk-AXNJFBYX.js.map +1 -0
  65. package/dist/chunk-AXUAQM45.js +154 -0
  66. package/dist/chunk-AXUAQM45.js.map +1 -0
  67. package/dist/chunk-B2FL7KBJ.js +65 -0
  68. package/dist/chunk-B2FL7KBJ.js.map +1 -0
  69. package/dist/chunk-BE232OKN.js +42 -0
  70. package/dist/chunk-BE232OKN.js.map +1 -0
  71. package/dist/chunk-BP4RQSX7.js +200 -0
  72. package/dist/chunk-BP4RQSX7.js.map +1 -0
  73. package/dist/chunk-BPJ3RN3U.js +112 -0
  74. package/dist/chunk-BPJ3RN3U.js.map +1 -0
  75. package/dist/chunk-BSF4Y4UT.js +77 -0
  76. package/dist/chunk-BSF4Y4UT.js.map +1 -0
  77. package/dist/chunk-BWIDDAR7.js +86 -0
  78. package/dist/chunk-BWIDDAR7.js.map +1 -0
  79. package/dist/chunk-BYUWQ6OP.js +45 -0
  80. package/dist/chunk-BYUWQ6OP.js.map +1 -0
  81. package/dist/chunk-C5ULP2P5.js +140 -0
  82. package/dist/chunk-C5ULP2P5.js.map +1 -0
  83. package/dist/chunk-CCG7PXLX.js +24 -0
  84. package/dist/chunk-CCG7PXLX.js.map +1 -0
  85. package/dist/chunk-CED2LKHI.js +117 -0
  86. package/dist/chunk-CED2LKHI.js.map +1 -0
  87. package/dist/chunk-CGWIOIEO.js +27 -0
  88. package/dist/chunk-CGWIOIEO.js.map +1 -0
  89. package/dist/chunk-CSEGVTKO.js +110 -0
  90. package/dist/chunk-CSEGVTKO.js.map +1 -0
  91. package/dist/chunk-CVFSNA4K.js +716 -0
  92. package/dist/chunk-CVFSNA4K.js.map +1 -0
  93. package/dist/chunk-E26M7ATD.js +61 -0
  94. package/dist/chunk-E26M7ATD.js.map +1 -0
  95. package/dist/chunk-E63IRXZZ.js +78 -0
  96. package/dist/chunk-E63IRXZZ.js.map +1 -0
  97. package/dist/chunk-ESJUISWY.js +45 -0
  98. package/dist/chunk-ESJUISWY.js.map +1 -0
  99. package/dist/chunk-ET7A3TQZ.js +158 -0
  100. package/dist/chunk-ET7A3TQZ.js.map +1 -0
  101. package/dist/chunk-ETTL6XGU.js +139 -0
  102. package/dist/chunk-ETTL6XGU.js.map +1 -0
  103. package/dist/chunk-EWDN56AS.js +24 -0
  104. package/dist/chunk-EWDN56AS.js.map +1 -0
  105. package/dist/chunk-FJZKA2LV.js +165 -0
  106. package/dist/chunk-FJZKA2LV.js.map +1 -0
  107. package/dist/chunk-G2WCT6PJ.js +69 -0
  108. package/dist/chunk-G2WCT6PJ.js.map +1 -0
  109. package/dist/chunk-GC52HWIL.js +41 -0
  110. package/dist/chunk-GC52HWIL.js.map +1 -0
  111. package/dist/chunk-GCKFA7X7.js +83 -0
  112. package/dist/chunk-GCKFA7X7.js.map +1 -0
  113. package/dist/chunk-GDMCDW66.js +19 -0
  114. package/dist/chunk-GDMCDW66.js.map +1 -0
  115. package/dist/chunk-GIXHBTHH.js +59 -0
  116. package/dist/chunk-GIXHBTHH.js.map +1 -0
  117. package/dist/chunk-GLEOUWPN.js +108 -0
  118. package/dist/chunk-GLEOUWPN.js.map +1 -0
  119. package/dist/chunk-GLRUM43F.js +121 -0
  120. package/dist/chunk-GLRUM43F.js.map +1 -0
  121. package/dist/chunk-H3L7WZDZ.js +74 -0
  122. package/dist/chunk-H3L7WZDZ.js.map +1 -0
  123. package/dist/chunk-H55WXDME.js +45 -0
  124. package/dist/chunk-H55WXDME.js.map +1 -0
  125. package/dist/chunk-H5QPJNVD.js +98 -0
  126. package/dist/chunk-H5QPJNVD.js.map +1 -0
  127. package/dist/chunk-HB45JHMM.js +43 -0
  128. package/dist/chunk-HB45JHMM.js.map +1 -0
  129. package/dist/chunk-HETHTYEZ.js +80 -0
  130. package/dist/chunk-HETHTYEZ.js.map +1 -0
  131. package/dist/chunk-HHSKNB32.js +55 -0
  132. package/dist/chunk-HHSKNB32.js.map +1 -0
  133. package/dist/chunk-HILOUYES.js +151 -0
  134. package/dist/chunk-HILOUYES.js.map +1 -0
  135. package/dist/chunk-HTJVHFNW.js +178 -0
  136. package/dist/chunk-HTJVHFNW.js.map +1 -0
  137. package/dist/chunk-HZ7Z22VW.js +130 -0
  138. package/dist/chunk-HZ7Z22VW.js.map +1 -0
  139. package/dist/chunk-JFKBFQA5.js +129 -0
  140. package/dist/chunk-JFKBFQA5.js.map +1 -0
  141. package/dist/chunk-JIZKW3WC.js +74 -0
  142. package/dist/chunk-JIZKW3WC.js.map +1 -0
  143. package/dist/chunk-JQTCCKZA.js +154 -0
  144. package/dist/chunk-JQTCCKZA.js.map +1 -0
  145. package/dist/chunk-JR4H3FJ2.js +74 -0
  146. package/dist/chunk-JR4H3FJ2.js.map +1 -0
  147. package/dist/chunk-JRW53TVG.js +53 -0
  148. package/dist/chunk-JRW53TVG.js.map +1 -0
  149. package/dist/chunk-JXOCE27Z.js +82 -0
  150. package/dist/chunk-JXOCE27Z.js.map +1 -0
  151. package/dist/chunk-JYW5YNF7.js +188 -0
  152. package/dist/chunk-JYW5YNF7.js.map +1 -0
  153. package/dist/chunk-K72DJOXR.js +81 -0
  154. package/dist/chunk-K72DJOXR.js.map +1 -0
  155. package/dist/chunk-KDE3NYTI.js +144 -0
  156. package/dist/chunk-KDE3NYTI.js.map +1 -0
  157. package/dist/chunk-KFUFTZLS.js +111 -0
  158. package/dist/chunk-KFUFTZLS.js.map +1 -0
  159. package/dist/chunk-KHBXI6AV.js +149 -0
  160. package/dist/chunk-KHBXI6AV.js.map +1 -0
  161. package/dist/chunk-M74ZYBOK.js +93 -0
  162. package/dist/chunk-M74ZYBOK.js.map +1 -0
  163. package/dist/chunk-ML7WLNIK.js +11 -0
  164. package/dist/chunk-ML7WLNIK.js.map +1 -0
  165. package/dist/chunk-MXB2GVEQ.js +57 -0
  166. package/dist/chunk-MXB2GVEQ.js.map +1 -0
  167. package/dist/chunk-NEMXW2LB.js +102 -0
  168. package/dist/chunk-NEMXW2LB.js.map +1 -0
  169. package/dist/chunk-OJPPSJMF.js +65 -0
  170. package/dist/chunk-OJPPSJMF.js.map +1 -0
  171. package/dist/chunk-OMR6ZGME.js +63 -0
  172. package/dist/chunk-OMR6ZGME.js.map +1 -0
  173. package/dist/chunk-ORVYP73T.js +223 -0
  174. package/dist/chunk-ORVYP73T.js.map +1 -0
  175. package/dist/chunk-OZFUUO2Q.js +50 -0
  176. package/dist/chunk-OZFUUO2Q.js.map +1 -0
  177. package/dist/chunk-P44UAK45.js +115 -0
  178. package/dist/chunk-P44UAK45.js.map +1 -0
  179. package/dist/chunk-PJUP4BJD.js +116 -0
  180. package/dist/chunk-PJUP4BJD.js.map +1 -0
  181. package/dist/chunk-PJWYIOY4.js +3 -0
  182. package/dist/chunk-PJWYIOY4.js.map +1 -0
  183. package/dist/chunk-PVCW4O37.js +147 -0
  184. package/dist/chunk-PVCW4O37.js.map +1 -0
  185. package/dist/chunk-PXT47DRZ.js +59 -0
  186. package/dist/chunk-PXT47DRZ.js.map +1 -0
  187. package/dist/chunk-PZGUZHI7.js +142 -0
  188. package/dist/chunk-PZGUZHI7.js.map +1 -0
  189. package/dist/chunk-Q2BAL2PF.js +965 -0
  190. package/dist/chunk-Q2BAL2PF.js.map +1 -0
  191. package/dist/chunk-QBUTRD2M.js +58 -0
  192. package/dist/chunk-QBUTRD2M.js.map +1 -0
  193. package/dist/chunk-QCQTTOMD.js +236 -0
  194. package/dist/chunk-QCQTTOMD.js.map +1 -0
  195. package/dist/chunk-QNYOIVQ4.js +36 -0
  196. package/dist/chunk-QNYOIVQ4.js.map +1 -0
  197. package/dist/chunk-QPURZJJM.js +88 -0
  198. package/dist/chunk-QPURZJJM.js.map +1 -0
  199. package/dist/chunk-QRXIYPWP.js +121 -0
  200. package/dist/chunk-QRXIYPWP.js.map +1 -0
  201. package/dist/chunk-QV4BQRNH.js +29 -0
  202. package/dist/chunk-QV4BQRNH.js.map +1 -0
  203. package/dist/chunk-REFY5MLN.js +212 -0
  204. package/dist/chunk-REFY5MLN.js.map +1 -0
  205. package/dist/chunk-RJ6RAMKH.js +89 -0
  206. package/dist/chunk-RJ6RAMKH.js.map +1 -0
  207. package/dist/chunk-RJBCTMVW.js +59 -0
  208. package/dist/chunk-RJBCTMVW.js.map +1 -0
  209. package/dist/chunk-RLWULF5B.js +113 -0
  210. package/dist/chunk-RLWULF5B.js.map +1 -0
  211. package/dist/chunk-RPZMFGYI.js +31 -0
  212. package/dist/chunk-RPZMFGYI.js.map +1 -0
  213. package/dist/chunk-RQT5DWGG.js +34 -0
  214. package/dist/chunk-RQT5DWGG.js.map +1 -0
  215. package/dist/chunk-SBKVVVYY.js +89 -0
  216. package/dist/chunk-SBKVVVYY.js.map +1 -0
  217. package/dist/chunk-SOJW47EZ.js +70 -0
  218. package/dist/chunk-SOJW47EZ.js.map +1 -0
  219. package/dist/chunk-SQ66DCXY.js +35 -0
  220. package/dist/chunk-SQ66DCXY.js.map +1 -0
  221. package/dist/chunk-SXKGWHAM.js +179 -0
  222. package/dist/chunk-SXKGWHAM.js.map +1 -0
  223. package/dist/chunk-T2OKGV6M.js +13 -0
  224. package/dist/chunk-T2OKGV6M.js.map +1 -0
  225. package/dist/chunk-TJLULCZW.js +108 -0
  226. package/dist/chunk-TJLULCZW.js.map +1 -0
  227. package/dist/chunk-TY6NTWN5.js +29 -0
  228. package/dist/chunk-TY6NTWN5.js.map +1 -0
  229. package/dist/chunk-U3AEEFVB.js +152 -0
  230. package/dist/chunk-U3AEEFVB.js.map +1 -0
  231. package/dist/chunk-V3HFDVZ3.js +102 -0
  232. package/dist/chunk-V3HFDVZ3.js.map +1 -0
  233. package/dist/chunk-V6H2RUVP.js +77 -0
  234. package/dist/chunk-V6H2RUVP.js.map +1 -0
  235. package/dist/chunk-VB53UMAL.js +106 -0
  236. package/dist/chunk-VB53UMAL.js.map +1 -0
  237. package/dist/chunk-VBEMLZGX.js +86 -0
  238. package/dist/chunk-VBEMLZGX.js.map +1 -0
  239. package/dist/chunk-VBQFE5RV.js +92 -0
  240. package/dist/chunk-VBQFE5RV.js.map +1 -0
  241. package/dist/chunk-VHK2OUCW.js +38 -0
  242. package/dist/chunk-VHK2OUCW.js.map +1 -0
  243. package/dist/chunk-VHTAVROO.js +53 -0
  244. package/dist/chunk-VHTAVROO.js.map +1 -0
  245. package/dist/chunk-VZX4HLBM.js +84 -0
  246. package/dist/chunk-VZX4HLBM.js.map +1 -0
  247. package/dist/chunk-W47V2F3Q.js +115 -0
  248. package/dist/chunk-W47V2F3Q.js.map +1 -0
  249. package/dist/chunk-W7I3ZX66.js +76 -0
  250. package/dist/chunk-W7I3ZX66.js.map +1 -0
  251. package/dist/chunk-WD42UBGR.js +64 -0
  252. package/dist/chunk-WD42UBGR.js.map +1 -0
  253. package/dist/chunk-XC7SYZYR.js +86 -0
  254. package/dist/chunk-XC7SYZYR.js.map +1 -0
  255. package/dist/chunk-XJA4B3F5.js +68 -0
  256. package/dist/chunk-XJA4B3F5.js.map +1 -0
  257. package/dist/chunk-XSENM65D.js +128 -0
  258. package/dist/chunk-XSENM65D.js.map +1 -0
  259. package/dist/chunk-YD2QEVHO.js +68 -0
  260. package/dist/chunk-YD2QEVHO.js.map +1 -0
  261. package/dist/chunk-YDKRUSB5.js +35 -0
  262. package/dist/chunk-YDKRUSB5.js.map +1 -0
  263. package/dist/chunk-YES6SPDT.js +55 -0
  264. package/dist/chunk-YES6SPDT.js.map +1 -0
  265. package/dist/chunk-YNDHYMPI.js +46 -0
  266. package/dist/chunk-YNDHYMPI.js.map +1 -0
  267. package/dist/chunk-YO3WEMOH.js +130 -0
  268. package/dist/chunk-YO3WEMOH.js.map +1 -0
  269. package/dist/chunk-YUFFAWNB.js +90 -0
  270. package/dist/chunk-YUFFAWNB.js.map +1 -0
  271. package/dist/chunk-YVXMWUWD.js +118 -0
  272. package/dist/chunk-YVXMWUWD.js.map +1 -0
  273. package/dist/chunk-Z6HZMGO2.js +106 -0
  274. package/dist/chunk-Z6HZMGO2.js.map +1 -0
  275. package/dist/chunk-ZAMPCRFJ.js +121 -0
  276. package/dist/chunk-ZAMPCRFJ.js.map +1 -0
  277. package/dist/chunk-ZEFOXF2I.js +87 -0
  278. package/dist/chunk-ZEFOXF2I.js.map +1 -0
  279. package/dist/chunk-ZJXOHLQE.js +66 -0
  280. package/dist/chunk-ZJXOHLQE.js.map +1 -0
  281. package/dist/chunk-ZPURFK4C.js +203 -0
  282. package/dist/chunk-ZPURFK4C.js.map +1 -0
  283. package/dist/chunk-ZZZIOTLC.js +89 -0
  284. package/dist/chunk-ZZZIOTLC.js.map +1 -0
  285. package/dist/components/composites/account-menu/account-menu.d.ts +43 -0
  286. package/dist/components/composites/account-menu/index.d.ts +1 -0
  287. package/dist/components/composites/agent-composer/agent-composer.d.ts +41 -0
  288. package/dist/components/composites/agent-composer/index.d.ts +1 -0
  289. package/dist/components/composites/agent-editor/agent-editor.d.ts +31 -0
  290. package/dist/components/composites/agent-editor/index.d.ts +1 -0
  291. package/dist/components/composites/agent-stream/agent-stream.d.ts +72 -0
  292. package/dist/components/composites/agent-stream/index.d.ts +2 -0
  293. package/dist/components/composites/agent-stream/to-agent-stream-items.d.ts +32 -0
  294. package/dist/components/composites/agent-timeline/agent-timeline.d.ts +22 -0
  295. package/dist/components/composites/agent-timeline/index.d.ts +1 -0
  296. package/dist/components/composites/agent-tool-renderer/agent-tool-renderer.d.ts +60 -0
  297. package/dist/components/composites/agent-tool-renderer/index.d.ts +1 -0
  298. package/dist/components/composites/agent-tool-renderer/tool-call-part.d.ts +5 -0
  299. package/dist/components/composites/approval-card/approval-card.d.ts +35 -0
  300. package/dist/components/composites/approval-card/index.d.ts +1 -0
  301. package/dist/components/composites/chat-composer/chat-composer.d.ts +68 -0
  302. package/dist/components/composites/chat-composer/index.d.ts +1 -0
  303. package/dist/components/composites/chat-message/chat-message-actions.d.ts +22 -0
  304. package/dist/components/composites/chat-message/chat-message-branch.d.ts +17 -0
  305. package/dist/components/composites/chat-message/chat-message-response.d.ts +15 -0
  306. package/dist/components/composites/chat-message/chat-message-toolbar.d.ts +7 -0
  307. package/dist/components/composites/chat-message/chat-message.d.ts +94 -0
  308. package/dist/components/composites/chat-message/index.d.ts +23 -0
  309. package/dist/components/composites/chat-message/parts/data-part.d.ts +9 -0
  310. package/dist/components/composites/chat-message/parts/file-part.d.ts +5 -0
  311. package/dist/components/composites/chat-message/parts/reasoning-part.d.ts +7 -0
  312. package/dist/components/composites/chat-message/parts/source-part.d.ts +9 -0
  313. package/dist/components/composites/chat-message/parts/text-part.d.ts +11 -0
  314. package/dist/components/composites/choice-prompt/choice-prompt.d.ts +65 -0
  315. package/dist/components/composites/choice-prompt/index.d.ts +2 -0
  316. package/dist/components/composites/code-block/code-block.d.ts +29 -0
  317. package/dist/components/composites/code-block/index.d.ts +1 -0
  318. package/dist/components/composites/command-palette/command-palette.d.ts +41 -0
  319. package/dist/components/composites/command-palette/index.d.ts +1 -0
  320. package/dist/components/composites/confirm-dialog/confirm-dialog.d.ts +41 -0
  321. package/dist/components/composites/confirm-dialog/index.d.ts +1 -0
  322. package/dist/components/composites/confirm-prompt/confirm-prompt.d.ts +31 -0
  323. package/dist/components/composites/confirm-prompt/index.d.ts +1 -0
  324. package/dist/components/composites/cron-jobs-list/cron-jobs-list.d.ts +15 -0
  325. package/dist/components/composites/cron-jobs-list/index.d.ts +1 -0
  326. package/dist/components/composites/data-table/data-table.d.ts +63 -0
  327. package/dist/components/composites/data-table/index.d.ts +1 -0
  328. package/dist/components/composites/deployment-row/deployment-row.d.ts +28 -0
  329. package/dist/components/composites/deployment-row/index.d.ts +1 -0
  330. package/dist/components/composites/domain-config/domain-config.d.ts +34 -0
  331. package/dist/components/composites/domain-config/index.d.ts +1 -0
  332. package/dist/components/composites/env-var-editor/env-var-editor.d.ts +35 -0
  333. package/dist/components/composites/env-var-editor/index.d.ts +1 -0
  334. package/dist/components/composites/mcp-server-list/index.d.ts +1 -0
  335. package/dist/components/composites/mcp-server-list/mcp-server-list.d.ts +15 -0
  336. package/dist/components/composites/metric-card/index.d.ts +2 -0
  337. package/dist/components/composites/metric-card/metric-card.d.ts +46 -0
  338. package/dist/components/composites/multi-select-prompt/index.d.ts +1 -0
  339. package/dist/components/composites/multi-select-prompt/multi-select-prompt.d.ts +61 -0
  340. package/dist/components/composites/page-shell/index.d.ts +1 -0
  341. package/dist/components/composites/page-shell/page-shell.d.ts +68 -0
  342. package/dist/components/composites/permission-modal/index.d.ts +1 -0
  343. package/dist/components/composites/permission-modal/permission-modal.d.ts +48 -0
  344. package/dist/components/composites/preview-env-card/index.d.ts +1 -0
  345. package/dist/components/composites/preview-env-card/preview-env-card.d.ts +36 -0
  346. package/dist/components/composites/preview-panel/index.d.ts +1 -0
  347. package/dist/components/composites/preview-panel/preview-panel.d.ts +24 -0
  348. package/dist/components/composites/project-card/index.d.ts +1 -0
  349. package/dist/components/composites/project-card/project-card.d.ts +32 -0
  350. package/dist/components/composites/rollback-ui/index.d.ts +1 -0
  351. package/dist/components/composites/rollback-ui/rollback-ui.d.ts +32 -0
  352. package/dist/components/composites/rule-editor/index.d.ts +1 -0
  353. package/dist/components/composites/rule-editor/rule-editor.d.ts +18 -0
  354. package/dist/components/composites/skill-editor/index.d.ts +1 -0
  355. package/dist/components/composites/skill-editor/skill-editor.d.ts +19 -0
  356. package/dist/components/composites/skills-list/index.d.ts +1 -0
  357. package/dist/components/composites/skills-list/skills-list.d.ts +15 -0
  358. package/dist/components/composites/slide-deck/context.d.ts +23 -0
  359. package/dist/components/composites/slide-deck/controls.d.ts +8 -0
  360. package/dist/components/composites/slide-deck/fragments.d.ts +20 -0
  361. package/dist/components/composites/slide-deck/index.d.ts +21 -0
  362. package/dist/components/composites/slide-deck/notes.d.ts +14 -0
  363. package/dist/components/composites/slide-deck/presenter-view.d.ts +15 -0
  364. package/dist/components/composites/slide-deck/print-styles.d.ts +21 -0
  365. package/dist/components/composites/slide-deck/progress-bar.d.ts +8 -0
  366. package/dist/components/composites/slide-deck/schema.d.ts +31 -0
  367. package/dist/components/composites/slide-deck/slide-deck.d.ts +78 -0
  368. package/dist/components/composites/slide-deck/slide-number.d.ts +11 -0
  369. package/dist/components/composites/slide-deck/split-deck.d.ts +2 -0
  370. package/dist/components/composites/slide-deck/thumbnails.d.ts +19 -0
  371. package/dist/components/composites/slide-deck/use-deck-hash-routing.d.ts +23 -0
  372. package/dist/components/composites/slide-deck/use-deck-keyboard.d.ts +25 -0
  373. package/dist/components/composites/slide-deck/use-deck-state.d.ts +57 -0
  374. package/dist/components/composites/slide-deck/use-deck-swipe.d.ts +20 -0
  375. package/dist/components/composites/slide-deck/use-fullscreen.d.ts +18 -0
  376. package/dist/components/composites/stability-bundle-viewer/index.d.ts +1 -0
  377. package/dist/components/composites/stability-bundle-viewer/stability-bundle-viewer.d.ts +28 -0
  378. package/dist/components/composites/status-indicator/index.d.ts +2 -0
  379. package/dist/components/composites/status-indicator/status-indicator.d.ts +31 -0
  380. package/dist/components/composites/task-header/index.d.ts +1 -0
  381. package/dist/components/composites/task-header/task-header.d.ts +20 -0
  382. package/dist/components/composites/text-prompt/index.d.ts +1 -0
  383. package/dist/components/composites/text-prompt/text-prompt.d.ts +48 -0
  384. package/dist/components/composites/usage-meter/index.d.ts +1 -0
  385. package/dist/components/composites/usage-meter/usage-meter.d.ts +56 -0
  386. package/dist/components/primitives/action-bar/action-bar.d.ts +34 -0
  387. package/dist/components/primitives/action-bar/index.d.ts +1 -0
  388. package/dist/components/primitives/agent-error-card/agent-error-card.d.ts +47 -0
  389. package/dist/components/primitives/agent-error-card/index.d.ts +1 -0
  390. package/dist/components/primitives/agent-event/agent-event.d.ts +25 -0
  391. package/dist/components/primitives/agent-event/index.d.ts +1 -0
  392. package/dist/components/primitives/agent-handoff/agent-handoff.d.ts +24 -0
  393. package/dist/components/primitives/agent-handoff/index.d.ts +1 -0
  394. package/dist/components/primitives/agent-profile/agent-profile.d.ts +28 -0
  395. package/dist/components/primitives/agent-profile/index.d.ts +1 -0
  396. package/dist/components/primitives/agent-starting-state/agent-starting-state.d.ts +15 -0
  397. package/dist/components/primitives/agent-starting-state/index.d.ts +1 -0
  398. package/dist/components/primitives/agent-streaming/agent-streaming.d.ts +16 -0
  399. package/dist/components/primitives/agent-streaming/index.d.ts +1 -0
  400. package/dist/components/primitives/alert/alert.d.ts +31 -0
  401. package/dist/components/primitives/alert/index.d.ts +1 -0
  402. package/dist/components/primitives/artifact-preview/artifact-preview.d.ts +25 -0
  403. package/dist/components/primitives/artifact-preview/index.d.ts +1 -0
  404. package/dist/components/primitives/attachment-chip/attachment-chip.d.ts +14 -0
  405. package/dist/components/primitives/attachment-chip/index.d.ts +1 -0
  406. package/dist/components/primitives/audit-log-entry/audit-log-entry.d.ts +29 -0
  407. package/dist/components/primitives/audit-log-entry/index.d.ts +1 -0
  408. package/dist/components/primitives/auto-compact-notice/auto-compact-notice.d.ts +24 -0
  409. package/dist/components/primitives/auto-compact-notice/index.d.ts +1 -0
  410. package/dist/components/primitives/avatar/avatar.d.ts +26 -0
  411. package/dist/components/primitives/avatar/index.d.ts +1 -0
  412. package/dist/components/primitives/badge/badge.d.ts +33 -0
  413. package/dist/components/primitives/badge/index.d.ts +1 -0
  414. package/dist/components/primitives/branch-indicator/branch-indicator.d.ts +13 -0
  415. package/dist/components/primitives/branch-indicator/index.d.ts +1 -0
  416. package/dist/components/primitives/browser-controls/browser-controls.d.ts +19 -0
  417. package/dist/components/primitives/browser-controls/index.d.ts +1 -0
  418. package/dist/components/primitives/build-log-stream/build-log-stream.d.ts +51 -0
  419. package/dist/components/primitives/build-log-stream/index.d.ts +1 -0
  420. package/dist/components/primitives/button/button.d.ts +26 -0
  421. package/dist/components/primitives/button/index.d.ts +1 -0
  422. package/dist/components/primitives/capability-indicator/capability-indicator.d.ts +57 -0
  423. package/dist/components/primitives/capability-indicator/index.d.ts +1 -0
  424. package/dist/components/primitives/card/card.d.ts +40 -0
  425. package/dist/components/primitives/card/index.d.ts +1 -0
  426. package/dist/components/primitives/channel-card/channel-card.d.ts +28 -0
  427. package/dist/components/primitives/channel-card/index.d.ts +1 -0
  428. package/dist/components/primitives/chat-thread/chat-thread.d.ts +12 -0
  429. package/dist/components/primitives/chat-thread/index.d.ts +1 -0
  430. package/dist/components/primitives/checkbox/checkbox.d.ts +20 -0
  431. package/dist/components/primitives/checkbox/index.d.ts +1 -0
  432. package/dist/components/primitives/context-card/context-card.d.ts +18 -0
  433. package/dist/components/primitives/context-card/index.d.ts +1 -0
  434. package/dist/components/primitives/context-window-bar/context-window-bar.d.ts +27 -0
  435. package/dist/components/primitives/context-window-bar/index.d.ts +1 -0
  436. package/dist/components/primitives/copy-button/copy-button.d.ts +17 -0
  437. package/dist/components/primitives/copy-button/index.d.ts +1 -0
  438. package/dist/components/primitives/cost-meter/cost-meter.d.ts +23 -0
  439. package/dist/components/primitives/cost-meter/index.d.ts +1 -0
  440. package/dist/components/primitives/created-files-card/created-files-card.d.ts +30 -0
  441. package/dist/components/primitives/created-files-card/index.d.ts +1 -0
  442. package/dist/components/primitives/cron-job-card/cron-job-card.d.ts +30 -0
  443. package/dist/components/primitives/cron-job-card/index.d.ts +1 -0
  444. package/dist/components/primitives/danger-zone/danger-zone.d.ts +40 -0
  445. package/dist/components/primitives/danger-zone/index.d.ts +1 -0
  446. package/dist/components/primitives/dialog/dialog.d.ts +26 -0
  447. package/dist/components/primitives/dialog/index.d.ts +1 -0
  448. package/dist/components/primitives/diff-viewer/diff-viewer.d.ts +45 -0
  449. package/dist/components/primitives/diff-viewer/index.d.ts +1 -0
  450. package/dist/components/primitives/dropdown-menu/dropdown-menu.d.ts +68 -0
  451. package/dist/components/primitives/dropdown-menu/index.d.ts +1 -0
  452. package/dist/components/primitives/empty-state/empty-state.d.ts +22 -0
  453. package/dist/components/primitives/empty-state/index.d.ts +1 -0
  454. package/dist/components/primitives/export-chat-dialog/export-chat-dialog.d.ts +18 -0
  455. package/dist/components/primitives/export-chat-dialog/index.d.ts +1 -0
  456. package/dist/components/primitives/folder-context-card/folder-context-card.d.ts +40 -0
  457. package/dist/components/primitives/folder-context-card/index.d.ts +1 -0
  458. package/dist/components/primitives/folder-selector/folder-selector.d.ts +18 -0
  459. package/dist/components/primitives/folder-selector/index.d.ts +1 -0
  460. package/dist/components/primitives/form-field/form-field.d.ts +44 -0
  461. package/dist/components/primitives/form-field/index.d.ts +1 -0
  462. package/dist/components/primitives/gateway-status-indicator/gateway-status-indicator.d.ts +19 -0
  463. package/dist/components/primitives/gateway-status-indicator/index.d.ts +1 -0
  464. package/dist/components/primitives/hook-config/hook-config.d.ts +27 -0
  465. package/dist/components/primitives/hook-config/index.d.ts +1 -0
  466. package/dist/components/primitives/hook-event-log/hook-event-log.d.ts +27 -0
  467. package/dist/components/primitives/hook-event-log/index.d.ts +1 -0
  468. package/dist/components/primitives/input/index.d.ts +1 -0
  469. package/dist/components/primitives/input/input.d.ts +24 -0
  470. package/dist/components/primitives/intent-selector/index.d.ts +1 -0
  471. package/dist/components/primitives/intent-selector/intent-selector.d.ts +22 -0
  472. package/dist/components/primitives/label/index.d.ts +1 -0
  473. package/dist/components/primitives/label/label.d.ts +15 -0
  474. package/dist/components/primitives/lane-board/index.d.ts +1 -0
  475. package/dist/components/primitives/lane-board/lane-board.d.ts +26 -0
  476. package/dist/components/primitives/login-split/index.d.ts +1 -0
  477. package/dist/components/primitives/login-split/login-split.d.ts +24 -0
  478. package/dist/components/primitives/mcp-server-card/index.d.ts +1 -0
  479. package/dist/components/primitives/mcp-server-card/mcp-server-card.d.ts +32 -0
  480. package/dist/components/primitives/memory-editor/index.d.ts +1 -0
  481. package/dist/components/primitives/memory-editor/memory-editor.d.ts +29 -0
  482. package/dist/components/primitives/mention-menu/index.d.ts +1 -0
  483. package/dist/components/primitives/mention-menu/mention-menu.d.ts +44 -0
  484. package/dist/components/primitives/metrics-panel/index.d.ts +1 -0
  485. package/dist/components/primitives/metrics-panel/metrics-panel.d.ts +62 -0
  486. package/dist/components/primitives/model-card/index.d.ts +1 -0
  487. package/dist/components/primitives/model-card/model-card.d.ts +72 -0
  488. package/dist/components/primitives/model-selector/index.d.ts +1 -0
  489. package/dist/components/primitives/model-selector/model-selector.d.ts +21 -0
  490. package/dist/components/primitives/pagination/index.d.ts +1 -0
  491. package/dist/components/primitives/pagination/pagination.d.ts +44 -0
  492. package/dist/components/primitives/permission-matrix/index.d.ts +1 -0
  493. package/dist/components/primitives/permission-matrix/permission-matrix.d.ts +43 -0
  494. package/dist/components/primitives/pin-input/index.d.ts +1 -0
  495. package/dist/components/primitives/pin-input/pin-input.d.ts +41 -0
  496. package/dist/components/primitives/plan-badge/index.d.ts +1 -0
  497. package/dist/components/primitives/plan-badge/plan-badge.d.ts +35 -0
  498. package/dist/components/primitives/progress/index.d.ts +1 -0
  499. package/dist/components/primitives/progress/progress.d.ts +44 -0
  500. package/dist/components/primitives/progress-checklist/index.d.ts +1 -0
  501. package/dist/components/primitives/progress-checklist/progress-checklist.d.ts +18 -0
  502. package/dist/components/primitives/project-switcher/index.d.ts +1 -0
  503. package/dist/components/primitives/project-switcher/project-switcher.d.ts +32 -0
  504. package/dist/components/primitives/quick-action-chips/index.d.ts +1 -0
  505. package/dist/components/primitives/quick-action-chips/quick-action-chips.d.ts +22 -0
  506. package/dist/components/primitives/radio-group/index.d.ts +1 -0
  507. package/dist/components/primitives/radio-group/radio-group.d.ts +5 -0
  508. package/dist/components/primitives/recent-folders-list/index.d.ts +1 -0
  509. package/dist/components/primitives/recent-folders-list/recent-folders-list.d.ts +21 -0
  510. package/dist/components/primitives/rule-card/index.d.ts +1 -0
  511. package/dist/components/primitives/rule-card/rule-card.d.ts +15 -0
  512. package/dist/components/primitives/run-stats/index.d.ts +1 -0
  513. package/dist/components/primitives/run-stats/run-stats.d.ts +16 -0
  514. package/dist/components/primitives/run-status-pill/index.d.ts +1 -0
  515. package/dist/components/primitives/run-status-pill/run-status-pill.d.ts +13 -0
  516. package/dist/components/primitives/running-tasks-panel/index.d.ts +1 -0
  517. package/dist/components/primitives/running-tasks-panel/running-tasks-panel.d.ts +20 -0
  518. package/dist/components/primitives/scroll-area/index.d.ts +2 -0
  519. package/dist/components/primitives/scroll-area/scroll-area.d.ts +40 -0
  520. package/dist/components/primitives/scroll-area/use-stick-to-bottom.d.ts +20 -0
  521. package/dist/components/primitives/select/index.d.ts +1 -0
  522. package/dist/components/primitives/select/select.d.ts +50 -0
  523. package/dist/components/primitives/session-list-item/index.d.ts +1 -0
  524. package/dist/components/primitives/session-list-item/session-list-item.d.ts +35 -0
  525. package/dist/components/primitives/session-timeline/index.d.ts +1 -0
  526. package/dist/components/primitives/session-timeline/session-timeline.d.ts +32 -0
  527. package/dist/components/primitives/sheet/index.d.ts +1 -0
  528. package/dist/components/primitives/sheet/sheet.d.ts +57 -0
  529. package/dist/components/primitives/sidebar/index.d.ts +1 -0
  530. package/dist/components/primitives/sidebar/sidebar.d.ts +18 -0
  531. package/dist/components/primitives/skeleton/index.d.ts +1 -0
  532. package/dist/components/primitives/skeleton/skeleton.d.ts +21 -0
  533. package/dist/components/primitives/skill-card/index.d.ts +1 -0
  534. package/dist/components/primitives/skill-card/skill-card.d.ts +28 -0
  535. package/dist/components/primitives/slide/alerts.d.ts +31 -0
  536. package/dist/components/primitives/slide/frontmatter.d.ts +23 -0
  537. package/dist/components/primitives/slide/index.d.ts +16 -0
  538. package/dist/components/primitives/slide/json-schema.d.ts +9 -0
  539. package/dist/components/primitives/slide/marpit-bg.d.ts +36 -0
  540. package/dist/components/primitives/slide/parse.d.ts +59 -0
  541. package/dist/components/primitives/slide/plugin.d.ts +79 -0
  542. package/dist/components/primitives/slide/plugins/emoji/index.d.ts +8 -0
  543. package/dist/components/primitives/slide/plugins/emoji/map.d.ts +15 -0
  544. package/dist/components/primitives/slide/plugins/math/index.d.ts +6 -0
  545. package/dist/components/primitives/slide/plugins/mermaid/index.d.ts +48 -0
  546. package/dist/components/primitives/slide/plugins/shiki/index.d.ts +11 -0
  547. package/dist/components/primitives/slide/sanitize.d.ts +33 -0
  548. package/dist/components/primitives/slide/schema.d.ts +109 -0
  549. package/dist/components/primitives/slide/slide.d.ts +53 -0
  550. package/dist/components/primitives/slide/themes/index.d.ts +9 -0
  551. package/dist/components/primitives/slide/use-slide-fit.d.ts +21 -0
  552. package/dist/components/primitives/slide/validate.d.ts +33 -0
  553. package/dist/components/primitives/social-auth-row/index.d.ts +1 -0
  554. package/dist/components/primitives/social-auth-row/social-auth-row.d.ts +24 -0
  555. package/dist/components/primitives/stat-tile/index.d.ts +1 -0
  556. package/dist/components/primitives/stat-tile/stat-tile.d.ts +28 -0
  557. package/dist/components/primitives/status-dot/index.d.ts +1 -0
  558. package/dist/components/primitives/status-dot/status-dot.d.ts +31 -0
  559. package/dist/components/primitives/steps-rail/index.d.ts +1 -0
  560. package/dist/components/primitives/steps-rail/steps-rail.d.ts +24 -0
  561. package/dist/components/primitives/sub-agent-dispatch/index.d.ts +1 -0
  562. package/dist/components/primitives/sub-agent-dispatch/sub-agent-dispatch.d.ts +30 -0
  563. package/dist/components/primitives/switch/index.d.ts +1 -0
  564. package/dist/components/primitives/switch/switch.d.ts +20 -0
  565. package/dist/components/primitives/system-prompt-editor/index.d.ts +1 -0
  566. package/dist/components/primitives/system-prompt-editor/system-prompt-editor.d.ts +25 -0
  567. package/dist/components/primitives/table/index.d.ts +1 -0
  568. package/dist/components/primitives/table/table.d.ts +61 -0
  569. package/dist/components/primitives/tabs/index.d.ts +1 -0
  570. package/dist/components/primitives/tabs/tabs.d.ts +7 -0
  571. package/dist/components/primitives/task-plan/index.d.ts +1 -0
  572. package/dist/components/primitives/task-plan/task-plan.d.ts +36 -0
  573. package/dist/components/primitives/terminal-panel/index.d.ts +1 -0
  574. package/dist/components/primitives/terminal-panel/terminal-panel.d.ts +32 -0
  575. package/dist/components/primitives/textarea/index.d.ts +1 -0
  576. package/dist/components/primitives/textarea/textarea.d.ts +20 -0
  577. package/dist/components/primitives/thinking-level-selector/index.d.ts +1 -0
  578. package/dist/components/primitives/thinking-level-selector/thinking-level-selector.d.ts +24 -0
  579. package/dist/components/primitives/timestamp/index.d.ts +1 -0
  580. package/dist/components/primitives/timestamp/timestamp.d.ts +34 -0
  581. package/dist/components/primitives/toast/index.d.ts +2 -0
  582. package/dist/components/primitives/toast/toast.d.ts +34 -0
  583. package/dist/components/primitives/toast/toaster.d.ts +38 -0
  584. package/dist/components/primitives/token-usage-chart/index.d.ts +2 -0
  585. package/dist/components/primitives/token-usage-chart/token-usage-chart.d.ts +44 -0
  586. package/dist/components/primitives/token-usage-chart/usage-metrics.d.ts +23 -0
  587. package/dist/components/primitives/tool-call/index.d.ts +1 -0
  588. package/dist/components/primitives/tool-call/tool-call.d.ts +30 -0
  589. package/dist/components/primitives/tool-call-card/index.d.ts +1 -0
  590. package/dist/components/primitives/tool-call-card/tool-call-card.d.ts +30 -0
  591. package/dist/components/primitives/tool-result/index.d.ts +1 -0
  592. package/dist/components/primitives/tool-result/tool-result.d.ts +19 -0
  593. package/dist/components/primitives/tools-list/index.d.ts +1 -0
  594. package/dist/components/primitives/tools-list/tools-list.d.ts +32 -0
  595. package/dist/components/primitives/tooltip/index.d.ts +1 -0
  596. package/dist/components/primitives/tooltip/tooltip.d.ts +33 -0
  597. package/dist/components/primitives/topnav/index.d.ts +1 -0
  598. package/dist/components/primitives/topnav/topnav.d.ts +29 -0
  599. package/dist/components/primitives/update-banner/index.d.ts +1 -0
  600. package/dist/components/primitives/update-banner/update-banner.d.ts +16 -0
  601. package/dist/components/primitives/whiteboard/index.d.ts +2 -0
  602. package/dist/components/primitives/whiteboard/render/freedraw.d.ts +2 -0
  603. package/dist/components/primitives/whiteboard/render/line.d.ts +4 -0
  604. package/dist/components/primitives/whiteboard/render/rough-paths.d.ts +16 -0
  605. package/dist/components/primitives/whiteboard/render/scene.d.ts +3 -0
  606. package/dist/components/primitives/whiteboard/render/shape.d.ts +5 -0
  607. package/dist/components/primitives/whiteboard/render/style.d.ts +24 -0
  608. package/dist/components/primitives/whiteboard/render/text.d.ts +2 -0
  609. package/dist/components/primitives/whiteboard/schema.d.ts +596 -0
  610. package/dist/components/primitives/whiteboard/seed.d.ts +22 -0
  611. package/dist/components/primitives/whiteboard/validate.d.ts +26 -0
  612. package/dist/components/primitives/whiteboard/viewport/use-pointer-pan.d.ts +11 -0
  613. package/dist/components/primitives/whiteboard/viewport/use-viewport.d.ts +33 -0
  614. package/dist/components/primitives/whiteboard/whiteboard.d.ts +21 -0
  615. package/dist/components.css +1 -1
  616. package/dist/composites/account-menu/index.js +4 -4
  617. package/dist/composites/agent-composer/index.js +6 -5
  618. package/dist/composites/agent-editor/index.js +8 -7
  619. package/dist/composites/agent-stream/index.js +21 -9
  620. package/dist/composites/agent-timeline/index.js +3 -3
  621. package/dist/composites/agent-tool-renderer/index.js +17 -0
  622. package/dist/composites/agent-tool-renderer/index.js.map +1 -0
  623. package/dist/composites/approval-card/index.js +3 -3
  624. package/dist/composites/chat-composer/index.js +3 -3
  625. package/dist/composites/chat-message/index.js +18 -4
  626. package/dist/composites/choice-prompt/index.js +11 -0
  627. package/dist/composites/choice-prompt/index.js.map +1 -0
  628. package/dist/composites/code-block/index.js +3 -3
  629. package/dist/composites/command-palette/index.js +4 -3
  630. package/dist/composites/confirm-dialog/index.js +6 -5
  631. package/dist/composites/confirm-prompt/index.js +7 -0
  632. package/dist/composites/confirm-prompt/index.js.map +1 -0
  633. package/dist/composites/cron-jobs-list/index.js +3 -3
  634. package/dist/composites/data-table/index.js +8 -7
  635. package/dist/composites/deployment-row/index.js +3 -3
  636. package/dist/composites/domain-config/index.js +6 -5
  637. package/dist/composites/env-var-editor/index.js +7 -5
  638. package/dist/composites/mcp-server-list/index.js +4 -3
  639. package/dist/composites/metric-card/index.js +5 -0
  640. package/dist/composites/metric-card/index.js.map +1 -0
  641. package/dist/composites/multi-select-prompt/index.js +11 -0
  642. package/dist/composites/multi-select-prompt/index.js.map +1 -0
  643. package/dist/composites/page-shell/index.js +6 -5
  644. package/dist/composites/permission-modal/index.js +5 -4
  645. package/dist/composites/preview-env-card/index.js +4 -4
  646. package/dist/composites/preview-panel/index.js +3 -3
  647. package/dist/composites/project-card/index.js +4 -4
  648. package/dist/composites/rollback-ui/index.js +5 -4
  649. package/dist/composites/rule-editor/index.js +9 -8
  650. package/dist/composites/skill-editor/index.js +9 -8
  651. package/dist/composites/skills-list/index.js +4 -3
  652. package/dist/composites/stability-bundle-viewer/index.js +5 -0
  653. package/dist/composites/stability-bundle-viewer/index.js.map +1 -0
  654. package/dist/composites/status-indicator/index.js +4 -0
  655. package/dist/composites/status-indicator/index.js.map +1 -0
  656. package/dist/composites/task-header/index.js +3 -3
  657. package/dist/composites/text-prompt/index.js +10 -0
  658. package/dist/composites/text-prompt/index.js.map +1 -0
  659. package/dist/composites/usage-meter/index.js +3 -3
  660. package/dist/fonts-cdn.css +4 -4
  661. package/dist/fonts.css +2 -2
  662. package/dist/index.d.ts +144 -4461
  663. package/dist/index.js +999 -731
  664. package/dist/index.js.map +1 -1
  665. package/dist/lib/cn.d.ts +6 -0
  666. package/dist/lib/env.d.ts +55 -0
  667. package/dist/lib/live-region-context.d.ts +2 -0
  668. package/dist/lib/markdown/code-block.d.ts +14 -0
  669. package/dist/lib/markdown/inline-code.d.ts +10 -0
  670. package/dist/lib/markdown/math.d.ts +12 -0
  671. package/dist/lib/markdown/mermaid.d.ts +6 -0
  672. package/dist/lib/markdown/parser.d.ts +47 -0
  673. package/dist/lib/markdown/streaming-preprocess.d.ts +39 -0
  674. package/dist/lib/prompt.d.ts +40 -0
  675. package/dist/lib/safe-href.d.ts +27 -0
  676. package/dist/lib/sdk-tools-adapters/index.d.ts +1 -0
  677. package/dist/lib/sdk-tools-adapters/index.js +3 -0
  678. package/dist/lib/sdk-tools-adapters/index.js.map +1 -0
  679. package/dist/lib/sdk-tools-adapters/sdk-tools-adapters.d.ts +60 -0
  680. package/dist/lib/types.d.ts +10 -0
  681. package/dist/preset-v3-legacy.d.ts +7 -10
  682. package/dist/preset-v3-legacy.js +23 -3
  683. package/dist/preset-v3-legacy.js.map +1 -1
  684. package/dist/preset.css +2 -2
  685. package/dist/primitives/action-bar/index.js +2 -2
  686. package/dist/primitives/agent-error-card/index.js +2 -2
  687. package/dist/primitives/agent-event/index.js +3 -2
  688. package/dist/primitives/agent-handoff/index.js +2 -2
  689. package/dist/primitives/agent-profile/index.js +2 -2
  690. package/dist/primitives/agent-starting-state/index.js +2 -2
  691. package/dist/primitives/agent-streaming/index.js +2 -2
  692. package/dist/primitives/alert/index.js +2 -2
  693. package/dist/primitives/artifact-preview/index.js +2 -2
  694. package/dist/primitives/attachment-chip/index.js +2 -2
  695. package/dist/primitives/audit-log-entry/index.js +2 -2
  696. package/dist/primitives/auto-compact-notice/index.js +2 -2
  697. package/dist/primitives/avatar/index.js +2 -2
  698. package/dist/primitives/badge/index.js +2 -2
  699. package/dist/primitives/branch-indicator/index.js +4 -0
  700. package/dist/primitives/branch-indicator/index.js.map +1 -0
  701. package/dist/primitives/browser-controls/index.js +2 -2
  702. package/dist/primitives/build-log-stream/index.js +4 -2
  703. package/dist/primitives/button/index.js +2 -2
  704. package/dist/primitives/capability-indicator/index.js +2 -2
  705. package/dist/primitives/card/index.js +3 -2
  706. package/dist/primitives/channel-card/index.js +4 -0
  707. package/dist/primitives/channel-card/index.js.map +1 -0
  708. package/dist/primitives/chat-thread/index.js +2 -2
  709. package/dist/primitives/checkbox/index.js +2 -2
  710. package/dist/primitives/context-card/index.js +2 -2
  711. package/dist/primitives/context-window-bar/index.js +2 -2
  712. package/dist/primitives/copy-button/index.js +3 -2
  713. package/dist/primitives/cost-meter/index.js +2 -2
  714. package/dist/primitives/created-files-card/index.js +2 -2
  715. package/dist/primitives/cron-job-card/index.js +2 -2
  716. package/dist/primitives/danger-zone/index.js +2 -2
  717. package/dist/primitives/dialog/index.js +2 -2
  718. package/dist/primitives/diff-viewer/index.js +2 -2
  719. package/dist/primitives/dropdown-menu/index.js +2 -2
  720. package/dist/primitives/empty-state/index.js +2 -2
  721. package/dist/primitives/export-chat-dialog/index.js +5 -0
  722. package/dist/primitives/export-chat-dialog/index.js.map +1 -0
  723. package/dist/primitives/folder-context-card/index.js +2 -2
  724. package/dist/primitives/folder-selector/index.js +2 -2
  725. package/dist/primitives/form-field/index.js +3 -2
  726. package/dist/primitives/gateway-status-indicator/index.js +4 -0
  727. package/dist/primitives/gateway-status-indicator/index.js.map +1 -0
  728. package/dist/primitives/hook-config/index.js +3 -2
  729. package/dist/primitives/hook-event-log/index.js +2 -2
  730. package/dist/primitives/input/index.js +2 -2
  731. package/dist/primitives/intent-selector/index.js +2 -2
  732. package/dist/primitives/label/index.js +2 -2
  733. package/dist/primitives/lane-board/index.js +2 -2
  734. package/dist/primitives/login-split/index.js +2 -2
  735. package/dist/primitives/mcp-server-card/index.js +2 -2
  736. package/dist/primitives/memory-editor/index.js +2 -2
  737. package/dist/primitives/mention-menu/index.js +3 -2
  738. package/dist/primitives/metrics-panel/index.js +2 -2
  739. package/dist/primitives/model-card/index.js +2 -2
  740. package/dist/primitives/model-selector/index.js +2 -2
  741. package/dist/primitives/pagination/index.js +2 -2
  742. package/dist/primitives/permission-matrix/index.js +3 -2
  743. package/dist/primitives/pin-input/index.js +3 -2
  744. package/dist/primitives/plan-badge/index.js +2 -2
  745. package/dist/primitives/progress/index.js +2 -2
  746. package/dist/primitives/progress-checklist/index.js +2 -2
  747. package/dist/primitives/project-switcher/index.js +2 -2
  748. package/dist/primitives/quick-action-chips/index.js +2 -2
  749. package/dist/primitives/radio-group/index.js +2 -2
  750. package/dist/primitives/recent-folders-list/index.js +2 -2
  751. package/dist/primitives/rule-card/index.js +2 -2
  752. package/dist/primitives/run-stats/index.js +2 -2
  753. package/dist/primitives/run-status-pill/index.js +4 -0
  754. package/dist/primitives/run-status-pill/index.js.map +1 -0
  755. package/dist/primitives/running-tasks-panel/index.js +2 -2
  756. package/dist/primitives/scroll-area/index.js +3 -2
  757. package/dist/primitives/select/index.js +2 -2
  758. package/dist/primitives/session-list-item/index.js +2 -2
  759. package/dist/primitives/session-timeline/index.js +2 -2
  760. package/dist/primitives/sheet/index.js +2 -2
  761. package/dist/primitives/sidebar/index.js +2 -2
  762. package/dist/primitives/skeleton/index.js +2 -2
  763. package/dist/primitives/skill-card/index.js +2 -2
  764. package/dist/primitives/social-auth-row/index.js +2 -2
  765. package/dist/primitives/stat-tile/index.js +2 -2
  766. package/dist/primitives/status-dot/index.js +4 -2
  767. package/dist/primitives/steps-rail/index.js +2 -2
  768. package/dist/primitives/sub-agent-dispatch/index.js +2 -2
  769. package/dist/primitives/switch/index.js +2 -2
  770. package/dist/primitives/system-prompt-editor/index.js +3 -2
  771. package/dist/primitives/table/index.js +3 -2
  772. package/dist/primitives/tabs/index.js +2 -2
  773. package/dist/primitives/task-plan/index.js +2 -2
  774. package/dist/primitives/terminal-panel/index.js +2 -2
  775. package/dist/primitives/textarea/index.js +2 -2
  776. package/dist/primitives/thinking-level-selector/index.js +5 -0
  777. package/dist/primitives/thinking-level-selector/index.js.map +1 -0
  778. package/dist/primitives/timestamp/index.js +4 -2
  779. package/dist/primitives/toast/index.js +3 -2
  780. package/dist/primitives/token-usage-chart/index.js +2 -2
  781. package/dist/primitives/tool-call/index.js +3 -2
  782. package/dist/primitives/tool-call-card/index.js +3 -2
  783. package/dist/primitives/tool-result/index.js +2 -2
  784. package/dist/primitives/tools-list/index.js +2 -2
  785. package/dist/primitives/tooltip/index.js +2 -2
  786. package/dist/primitives/topnav/index.js +2 -2
  787. package/dist/primitives/update-banner/index.js +4 -0
  788. package/dist/primitives/update-banner/index.js.map +1 -0
  789. package/dist/screens/theo-code-shell.d.ts +9 -0
  790. package/dist/screens/theo-code-shell.data.d.ts +147 -0
  791. package/dist/slide/index.js +2 -1
  792. package/dist/slide/plugins/mermaid/index.js +1 -1
  793. package/dist/slide/plugins/mermaid/index.js.map +1 -1
  794. package/dist/slide/plugins/shiki/index.js.map +1 -1
  795. package/dist/slide/themes/violet-forge.css +1 -1
  796. package/dist/slide-deck/index.js +13 -2
  797. package/dist/slide-deck/index.js.map +1 -1
  798. package/dist/styles/tailwind-preset.d.ts +29 -0
  799. package/dist/styles.css +3 -3
  800. package/dist/test/a11y.d.ts +13 -0
  801. package/dist/test/setup.d.ts +1 -0
  802. package/dist/themes/anthropic-style.d.ts +11 -0
  803. package/dist/themes/aurora-terminal.d.ts +9 -0
  804. package/dist/themes/classic-paper.d.ts +17 -0
  805. package/dist/themes/color-value-pattern.d.ts +22 -0
  806. package/dist/themes/color.d.ts +62 -0
  807. package/dist/themes/define.d.ts +65 -0
  808. package/dist/themes/density.d.ts +17 -0
  809. package/dist/themes/dracula.d.ts +14 -0
  810. package/dist/themes/github-dark.d.ts +11 -0
  811. package/dist/themes/index.d.ts +26 -0
  812. package/dist/themes/linear-glass.d.ts +11 -0
  813. package/dist/themes/one-dark.d.ts +11 -0
  814. package/dist/themes/openai-style.d.ts +10 -0
  815. package/dist/themes/schema.d.ts +112 -0
  816. package/dist/themes/theme-provider.d.ts +82 -0
  817. package/dist/themes/theme-script.d.ts +48 -0
  818. package/dist/themes/theme-switcher.d.ts +17 -0
  819. package/dist/themes/types.d.ts +88 -0
  820. package/dist/themes/vercel-mono.d.ts +11 -0
  821. package/dist/themes/violet-forge.d.ts +12 -0
  822. package/dist/theo-ui-provider.d.ts +50 -0
  823. package/dist/tokens-v4.css +77 -41
  824. package/dist/tokens.css +159 -74
  825. package/dist/types/agent.d.ts +19 -0
  826. package/dist/types/chat.d.ts +173 -0
  827. package/dist/types/mode.d.ts +16 -0
  828. package/dist/types/permission.d.ts +8 -0
  829. package/dist/types/rule.d.ts +28 -0
  830. package/dist/types/task.d.ts +9 -0
  831. package/dist/vite-plugin.d.ts +7 -11
  832. package/dist/vite-plugin.js +4 -4
  833. package/dist/vite-plugin.js.map +1 -1
  834. package/dist/whiteboard/index.js +3 -0
  835. package/dist/whiteboard/index.js.map +1 -1
  836. package/llms.txt +39 -39
  837. package/package.json +246 -174
  838. package/registry/index.json +58 -4
  839. package/registry/r/account-menu.json +1 -1
  840. package/registry/r/action-bar.json +1 -1
  841. package/registry/r/agent-composer.json +1 -1
  842. package/registry/r/agent-editor.json +1 -1
  843. package/registry/r/agent-error-card.json +1 -1
  844. package/registry/r/agent-event.json +1 -1
  845. package/registry/r/agent-handoff.json +1 -1
  846. package/registry/r/agent-profile.json +1 -1
  847. package/registry/r/agent-starting-state.json +1 -1
  848. package/registry/r/agent-stream.json +1 -1
  849. package/registry/r/agent-streaming.json +1 -1
  850. package/registry/r/agent-timeline.json +1 -1
  851. package/registry/r/agent-tool-renderer.json +40 -0
  852. package/registry/r/alert.json +1 -1
  853. package/registry/r/approval-card.json +1 -1
  854. package/registry/r/artifact-preview.json +1 -1
  855. package/registry/r/attachment-chip.json +1 -1
  856. package/registry/r/audit-log-entry.json +1 -1
  857. package/registry/r/auto-compact-notice.json +1 -1
  858. package/registry/r/avatar.json +1 -1
  859. package/registry/r/badge.json +1 -1
  860. package/registry/r/browser-controls.json +1 -1
  861. package/registry/r/build-log-stream.json +2 -1
  862. package/registry/r/button.json +1 -1
  863. package/registry/r/capability-indicator.json +1 -1
  864. package/registry/r/card.json +1 -1
  865. package/registry/r/chat-composer.json +1 -1
  866. package/registry/r/chat-message.json +11 -16
  867. package/registry/r/chat-thread.json +1 -1
  868. package/registry/r/chat-types.json +1 -1
  869. package/registry/r/checkbox.json +1 -1
  870. package/registry/r/choice-prompt.json +25 -0
  871. package/registry/r/cn.json +1 -1
  872. package/registry/r/code-block.json +1 -1
  873. package/registry/r/command-palette.json +1 -1
  874. package/registry/r/confirm-dialog.json +1 -1
  875. package/registry/r/confirm-prompt.json +21 -0
  876. package/registry/r/context-card.json +1 -1
  877. package/registry/r/context-window-bar.json +1 -1
  878. package/registry/r/copy-button.json +1 -1
  879. package/registry/r/cost-meter.json +1 -1
  880. package/registry/r/created-files-card.json +1 -1
  881. package/registry/r/cron-job-card.json +1 -1
  882. package/registry/r/cron-jobs-list.json +1 -1
  883. package/registry/r/danger-zone.json +1 -1
  884. package/registry/r/data-table.json +1 -1
  885. package/registry/r/deployment-row.json +1 -1
  886. package/registry/r/dialog.json +1 -1
  887. package/registry/r/diff-viewer.json +1 -1
  888. package/registry/r/domain-config.json +1 -1
  889. package/registry/r/dropdown-menu.json +2 -2
  890. package/registry/r/empty-state.json +1 -1
  891. package/registry/r/env-var-editor.json +2 -1
  892. package/registry/r/env.json +15 -0
  893. package/registry/r/folder-context-card.json +1 -1
  894. package/registry/r/folder-selector.json +1 -1
  895. package/registry/r/form-field.json +1 -1
  896. package/registry/r/hook-config.json +1 -1
  897. package/registry/r/hook-event-log.json +1 -1
  898. package/registry/r/input.json +1 -1
  899. package/registry/r/intent-selector.json +1 -1
  900. package/registry/r/label.json +1 -1
  901. package/registry/r/lane-board.json +1 -1
  902. package/registry/r/login-split.json +1 -1
  903. package/registry/r/mcp-server-card.json +1 -1
  904. package/registry/r/mcp-server-list.json +1 -1
  905. package/registry/r/memory-editor.json +1 -1
  906. package/registry/r/mention-menu.json +1 -1
  907. package/registry/r/metric-card.json +23 -0
  908. package/registry/r/metrics-panel.json +1 -1
  909. package/registry/r/model-card.json +1 -1
  910. package/registry/r/model-selector.json +1 -1
  911. package/registry/r/multi-select-prompt.json +25 -0
  912. package/registry/r/page-shell.json +1 -1
  913. package/registry/r/pagination.json +1 -1
  914. package/registry/r/permission-matrix.json +1 -1
  915. package/registry/r/permission-modal.json +1 -1
  916. package/registry/r/pin-input.json +1 -1
  917. package/registry/r/plan-badge.json +1 -1
  918. package/registry/r/preview-env-card.json +1 -1
  919. package/registry/r/preview-panel.json +1 -1
  920. package/registry/r/progress-checklist.json +1 -1
  921. package/registry/r/progress.json +1 -1
  922. package/registry/r/project-card.json +1 -1
  923. package/registry/r/project-switcher.json +1 -1
  924. package/registry/r/prompt.json +15 -0
  925. package/registry/r/quick-action-chips.json +1 -1
  926. package/registry/r/radio-group.json +1 -1
  927. package/registry/r/recent-folders-list.json +1 -1
  928. package/registry/r/rollback-ui.json +1 -1
  929. package/registry/r/rule-card.json +1 -1
  930. package/registry/r/rule-editor.json +1 -1
  931. package/registry/r/run-stats.json +1 -1
  932. package/registry/r/running-tasks-panel.json +1 -1
  933. package/registry/r/safe-href.json +1 -1
  934. package/registry/r/scroll-area.json +1 -1
  935. package/registry/r/select.json +1 -1
  936. package/registry/r/session-list-item.json +1 -1
  937. package/registry/r/session-timeline.json +1 -1
  938. package/registry/r/sheet.json +1 -1
  939. package/registry/r/sidebar.json +1 -1
  940. package/registry/r/skeleton.json +1 -1
  941. package/registry/r/skill-card.json +1 -1
  942. package/registry/r/skill-editor.json +1 -1
  943. package/registry/r/skills-list.json +1 -1
  944. package/registry/r/slide-deck.json +8 -8
  945. package/registry/r/slide-plugin-mermaid.json +1 -1
  946. package/registry/r/slide-plugin-shiki.json +1 -1
  947. package/registry/r/slide.json +6 -6
  948. package/registry/r/social-auth-row.json +1 -1
  949. package/registry/r/stat-tile.json +1 -1
  950. package/registry/r/status-dot.json +2 -1
  951. package/registry/r/status-indicator.json +20 -0
  952. package/registry/r/steps-rail.json +1 -1
  953. package/registry/r/sub-agent-dispatch.json +1 -1
  954. package/registry/r/switch.json +1 -1
  955. package/registry/r/system-prompt-editor.json +1 -1
  956. package/registry/r/table.json +1 -1
  957. package/registry/r/tabs.json +1 -1
  958. package/registry/r/tailwind-preset.json +1 -1
  959. package/registry/r/task-header.json +1 -1
  960. package/registry/r/task-plan.json +1 -1
  961. package/registry/r/terminal-panel.json +1 -1
  962. package/registry/r/text-prompt.json +24 -0
  963. package/registry/r/textarea.json +1 -1
  964. package/registry/r/theme-provider.json +7 -6
  965. package/registry/r/theo-ui-provider.json +1 -1
  966. package/registry/r/timestamp.json +2 -1
  967. package/registry/r/toast.json +2 -2
  968. package/registry/r/token-usage-chart.json +1 -1
  969. package/registry/r/tokens.json +2 -2
  970. package/registry/r/tool-call-card.json +1 -1
  971. package/registry/r/tool-call.json +1 -1
  972. package/registry/r/tool-result.json +1 -1
  973. package/registry/r/tools-list.json +1 -1
  974. package/registry/r/tooltip.json +1 -1
  975. package/registry/r/topnav.json +1 -1
  976. package/registry/r/usage-meter.json +1 -1
  977. package/registry/r/whiteboard.json +3 -3
  978. package/dist/chunk-2UJROWAG.js +0 -106
  979. package/dist/chunk-2UJROWAG.js.map +0 -1
  980. package/dist/chunk-2XPWOUEH.js +0 -68
  981. package/dist/chunk-2XPWOUEH.js.map +0 -1
  982. package/dist/chunk-3GHLNCM3.js +0 -42
  983. package/dist/chunk-3GHLNCM3.js.map +0 -1
  984. package/dist/chunk-3HOXC25T.js +0 -48
  985. package/dist/chunk-3HOXC25T.js.map +0 -1
  986. package/dist/chunk-3QGO5SB3.js +0 -46
  987. package/dist/chunk-3QGO5SB3.js.map +0 -1
  988. package/dist/chunk-47QJVWW2.js +0 -85
  989. package/dist/chunk-47QJVWW2.js.map +0 -1
  990. package/dist/chunk-4L63UW3I.js +0 -35
  991. package/dist/chunk-4L63UW3I.js.map +0 -1
  992. package/dist/chunk-4UUSJJFZ.js +0 -25
  993. package/dist/chunk-4UUSJJFZ.js.map +0 -1
  994. package/dist/chunk-4ZBZBRG5.js +0 -127
  995. package/dist/chunk-4ZBZBRG5.js.map +0 -1
  996. package/dist/chunk-57NXT3OX.js +0 -92
  997. package/dist/chunk-57NXT3OX.js.map +0 -1
  998. package/dist/chunk-5FF5EUZP.js +0 -44
  999. package/dist/chunk-5FF5EUZP.js.map +0 -1
  1000. package/dist/chunk-5UGQXB2P.js +0 -714
  1001. package/dist/chunk-5UGQXB2P.js.map +0 -1
  1002. package/dist/chunk-62FT22CI.js +0 -85
  1003. package/dist/chunk-62FT22CI.js.map +0 -1
  1004. package/dist/chunk-673R3GSK.js +0 -19
  1005. package/dist/chunk-673R3GSK.js.map +0 -1
  1006. package/dist/chunk-6VINZJBV.js +0 -128
  1007. package/dist/chunk-6VINZJBV.js.map +0 -1
  1008. package/dist/chunk-74NZ5U3E.js +0 -145
  1009. package/dist/chunk-74NZ5U3E.js.map +0 -1
  1010. package/dist/chunk-755NWSNW.js +0 -36
  1011. package/dist/chunk-755NWSNW.js.map +0 -1
  1012. package/dist/chunk-7GLBWWMW.js +0 -70
  1013. package/dist/chunk-7GLBWWMW.js.map +0 -1
  1014. package/dist/chunk-7RXYW5VM.js +0 -88
  1015. package/dist/chunk-7RXYW5VM.js.map +0 -1
  1016. package/dist/chunk-AC4MGCXI.js +0 -92
  1017. package/dist/chunk-AC4MGCXI.js.map +0 -1
  1018. package/dist/chunk-AEVSVDT6.js +0 -67
  1019. package/dist/chunk-AEVSVDT6.js.map +0 -1
  1020. package/dist/chunk-AODIMN2N.js +0 -68
  1021. package/dist/chunk-AODIMN2N.js.map +0 -1
  1022. package/dist/chunk-ATHOPBCA.js +0 -61
  1023. package/dist/chunk-ATHOPBCA.js.map +0 -1
  1024. package/dist/chunk-AXKBNRZW.js +0 -173
  1025. package/dist/chunk-AXKBNRZW.js.map +0 -1
  1026. package/dist/chunk-B75MEYNR.js +0 -106
  1027. package/dist/chunk-B75MEYNR.js.map +0 -1
  1028. package/dist/chunk-BGKA6DI6.js +0 -34
  1029. package/dist/chunk-BGKA6DI6.js.map +0 -1
  1030. package/dist/chunk-BNQAJGEN.js +0 -88
  1031. package/dist/chunk-BNQAJGEN.js.map +0 -1
  1032. package/dist/chunk-BP2SETUC.js +0 -101
  1033. package/dist/chunk-BP2SETUC.js.map +0 -1
  1034. package/dist/chunk-BPUQWMBD.js +0 -79
  1035. package/dist/chunk-BPUQWMBD.js.map +0 -1
  1036. package/dist/chunk-BVDASR3Y.js +0 -74
  1037. package/dist/chunk-BVDASR3Y.js.map +0 -1
  1038. package/dist/chunk-BX7A5GUV.js +0 -78
  1039. package/dist/chunk-BX7A5GUV.js.map +0 -1
  1040. package/dist/chunk-CDA6RYOX.js +0 -115
  1041. package/dist/chunk-CDA6RYOX.js.map +0 -1
  1042. package/dist/chunk-CG7O3A42.js +0 -80
  1043. package/dist/chunk-CG7O3A42.js.map +0 -1
  1044. package/dist/chunk-CIYGNPKT.js +0 -76
  1045. package/dist/chunk-CIYGNPKT.js.map +0 -1
  1046. package/dist/chunk-CKXY4FTV.js +0 -59
  1047. package/dist/chunk-CKXY4FTV.js.map +0 -1
  1048. package/dist/chunk-CVOKZITR.js +0 -82
  1049. package/dist/chunk-CVOKZITR.js.map +0 -1
  1050. package/dist/chunk-CWFMFKDI.js +0 -82
  1051. package/dist/chunk-CWFMFKDI.js.map +0 -1
  1052. package/dist/chunk-CWVKSV7S.js +0 -124
  1053. package/dist/chunk-CWVKSV7S.js.map +0 -1
  1054. package/dist/chunk-CYOLRWOX.js +0 -63
  1055. package/dist/chunk-CYOLRWOX.js.map +0 -1
  1056. package/dist/chunk-D23LRJT6.js +0 -116
  1057. package/dist/chunk-D23LRJT6.js.map +0 -1
  1058. package/dist/chunk-DFADMEJK.js +0 -127
  1059. package/dist/chunk-DFADMEJK.js.map +0 -1
  1060. package/dist/chunk-DKQAHZG2.js +0 -83
  1061. package/dist/chunk-DKQAHZG2.js.map +0 -1
  1062. package/dist/chunk-DW247T3Q.js +0 -199
  1063. package/dist/chunk-DW247T3Q.js.map +0 -1
  1064. package/dist/chunk-E5A7HN6H.js +0 -32
  1065. package/dist/chunk-E5A7HN6H.js.map +0 -1
  1066. package/dist/chunk-EI63GTN7.js +0 -57
  1067. package/dist/chunk-EI63GTN7.js.map +0 -1
  1068. package/dist/chunk-EP25QJ4N.js +0 -146
  1069. package/dist/chunk-EP25QJ4N.js.map +0 -1
  1070. package/dist/chunk-ET44426Q.js +0 -80
  1071. package/dist/chunk-ET44426Q.js.map +0 -1
  1072. package/dist/chunk-ETEIDY34.js +0 -67
  1073. package/dist/chunk-ETEIDY34.js.map +0 -1
  1074. package/dist/chunk-EU55O4P7.js +0 -76
  1075. package/dist/chunk-EU55O4P7.js.map +0 -1
  1076. package/dist/chunk-F436537E.js +0 -104
  1077. package/dist/chunk-F436537E.js.map +0 -1
  1078. package/dist/chunk-FLBTGNQI.js +0 -86
  1079. package/dist/chunk-FLBTGNQI.js.map +0 -1
  1080. package/dist/chunk-FUT45NFW.js +0 -46
  1081. package/dist/chunk-FUT45NFW.js.map +0 -1
  1082. package/dist/chunk-G3LWNTVZ.js +0 -51
  1083. package/dist/chunk-G3LWNTVZ.js.map +0 -1
  1084. package/dist/chunk-GBJB5WLT.js +0 -58
  1085. package/dist/chunk-GBJB5WLT.js.map +0 -1
  1086. package/dist/chunk-GIEPEFRX.js +0 -110
  1087. package/dist/chunk-GIEPEFRX.js.map +0 -1
  1088. package/dist/chunk-GSO7MISR.js +0 -58
  1089. package/dist/chunk-GSO7MISR.js.map +0 -1
  1090. package/dist/chunk-GUQFYUIC.js +0 -61
  1091. package/dist/chunk-GUQFYUIC.js.map +0 -1
  1092. package/dist/chunk-H3VJMFJQ.js +0 -35
  1093. package/dist/chunk-H3VJMFJQ.js.map +0 -1
  1094. package/dist/chunk-HG4WEERE.js +0 -26
  1095. package/dist/chunk-HG4WEERE.js.map +0 -1
  1096. package/dist/chunk-HGPBGLNP.js +0 -51
  1097. package/dist/chunk-HGPBGLNP.js.map +0 -1
  1098. package/dist/chunk-HQFTW7SF.js +0 -141
  1099. package/dist/chunk-HQFTW7SF.js.map +0 -1
  1100. package/dist/chunk-I7WYM63C.js +0 -170
  1101. package/dist/chunk-I7WYM63C.js.map +0 -1
  1102. package/dist/chunk-IPEYGWA7.js +0 -186
  1103. package/dist/chunk-IPEYGWA7.js.map +0 -1
  1104. package/dist/chunk-JQXLPVWP.js +0 -74
  1105. package/dist/chunk-JQXLPVWP.js.map +0 -1
  1106. package/dist/chunk-K5ARID4S.js +0 -26
  1107. package/dist/chunk-K5ARID4S.js.map +0 -1
  1108. package/dist/chunk-K6RTLPIJ.js +0 -41
  1109. package/dist/chunk-K6RTLPIJ.js.map +0 -1
  1110. package/dist/chunk-KQNKKV2C.js +0 -56
  1111. package/dist/chunk-KQNKKV2C.js.map +0 -1
  1112. package/dist/chunk-KRN4NE4U.js +0 -155
  1113. package/dist/chunk-KRN4NE4U.js.map +0 -1
  1114. package/dist/chunk-L2BI762I.js +0 -82
  1115. package/dist/chunk-L2BI762I.js.map +0 -1
  1116. package/dist/chunk-LEEH63B2.js +0 -56
  1117. package/dist/chunk-LEEH63B2.js.map +0 -1
  1118. package/dist/chunk-LHRWVM3G.js +0 -42
  1119. package/dist/chunk-LHRWVM3G.js.map +0 -1
  1120. package/dist/chunk-LIGWMGXM.js +0 -117
  1121. package/dist/chunk-LIGWMGXM.js.map +0 -1
  1122. package/dist/chunk-LKYSX3QF.js +0 -104
  1123. package/dist/chunk-LKYSX3QF.js.map +0 -1
  1124. package/dist/chunk-MCIFB6VS.js +0 -54
  1125. package/dist/chunk-MCIFB6VS.js.map +0 -1
  1126. package/dist/chunk-MI5CXMZU.js +0 -171
  1127. package/dist/chunk-MI5CXMZU.js.map +0 -1
  1128. package/dist/chunk-NQZYY4LR.js +0 -84
  1129. package/dist/chunk-NQZYY4LR.js.map +0 -1
  1130. package/dist/chunk-O23LKHUR.js +0 -66
  1131. package/dist/chunk-O23LKHUR.js.map +0 -1
  1132. package/dist/chunk-PASI2U2R.js +0 -23
  1133. package/dist/chunk-PASI2U2R.js.map +0 -1
  1134. package/dist/chunk-PPH5NTHV.js +0 -34
  1135. package/dist/chunk-PPH5NTHV.js.map +0 -1
  1136. package/dist/chunk-PR6OZF6D.js +0 -28
  1137. package/dist/chunk-PR6OZF6D.js.map +0 -1
  1138. package/dist/chunk-PWXOXPFT.js +0 -142
  1139. package/dist/chunk-PWXOXPFT.js.map +0 -1
  1140. package/dist/chunk-QB6BNHO3.js +0 -112
  1141. package/dist/chunk-QB6BNHO3.js.map +0 -1
  1142. package/dist/chunk-QJGGTIUN.js +0 -110
  1143. package/dist/chunk-QJGGTIUN.js.map +0 -1
  1144. package/dist/chunk-QSOIJ6J3.js +0 -91
  1145. package/dist/chunk-QSOIJ6J3.js.map +0 -1
  1146. package/dist/chunk-R2PAGRDP.js +0 -152
  1147. package/dist/chunk-R2PAGRDP.js.map +0 -1
  1148. package/dist/chunk-R63ZKLQM.js +0 -45
  1149. package/dist/chunk-R63ZKLQM.js.map +0 -1
  1150. package/dist/chunk-RTYYJPPE.js +0 -77
  1151. package/dist/chunk-RTYYJPPE.js.map +0 -1
  1152. package/dist/chunk-RVOBP7PO.js +0 -116
  1153. package/dist/chunk-RVOBP7PO.js.map +0 -1
  1154. package/dist/chunk-SF6R5VMQ.js +0 -97
  1155. package/dist/chunk-SF6R5VMQ.js.map +0 -1
  1156. package/dist/chunk-SP4CP5HY.js +0 -57
  1157. package/dist/chunk-SP4CP5HY.js.map +0 -1
  1158. package/dist/chunk-SWJ4EUOI.js +0 -30
  1159. package/dist/chunk-SWJ4EUOI.js.map +0 -1
  1160. package/dist/chunk-TK24HQJJ.js +0 -128
  1161. package/dist/chunk-TK24HQJJ.js.map +0 -1
  1162. package/dist/chunk-TNBJ36XJ.js +0 -156
  1163. package/dist/chunk-TNBJ36XJ.js.map +0 -1
  1164. package/dist/chunk-TO3UAT6O.js +0 -221
  1165. package/dist/chunk-TO3UAT6O.js.map +0 -1
  1166. package/dist/chunk-UAYOOTRR.js +0 -77
  1167. package/dist/chunk-UAYOOTRR.js.map +0 -1
  1168. package/dist/chunk-UDTAMHXW.js +0 -55
  1169. package/dist/chunk-UDTAMHXW.js.map +0 -1
  1170. package/dist/chunk-UOXU7NDY.js +0 -120
  1171. package/dist/chunk-UOXU7NDY.js.map +0 -1
  1172. package/dist/chunk-V7OOTVK3.js +0 -106
  1173. package/dist/chunk-V7OOTVK3.js.map +0 -1
  1174. package/dist/chunk-VI5M7KJ2.js +0 -1022
  1175. package/dist/chunk-VI5M7KJ2.js.map +0 -1
  1176. package/dist/chunk-VMMATOPE.js +0 -64
  1177. package/dist/chunk-VMMATOPE.js.map +0 -1
  1178. package/dist/chunk-W2PVSIW3.js +0 -89
  1179. package/dist/chunk-W2PVSIW3.js.map +0 -1
  1180. package/dist/chunk-W3DUDZDU.js +0 -88
  1181. package/dist/chunk-W3DUDZDU.js.map +0 -1
  1182. package/dist/chunk-WKEUU2FU.js +0 -114
  1183. package/dist/chunk-WKEUU2FU.js.map +0 -1
  1184. package/dist/chunk-WKLW7RC6.js +0 -28
  1185. package/dist/chunk-WKLW7RC6.js.map +0 -1
  1186. package/dist/chunk-WSJGZNUH.js +0 -111
  1187. package/dist/chunk-WSJGZNUH.js.map +0 -1
  1188. package/dist/chunk-WVPDQMC2.js +0 -144
  1189. package/dist/chunk-WVPDQMC2.js.map +0 -1
  1190. package/dist/chunk-WWNH5ENT.js +0 -43
  1191. package/dist/chunk-WWNH5ENT.js.map +0 -1
  1192. package/dist/chunk-X5L62PXY.js +0 -112
  1193. package/dist/chunk-X5L62PXY.js.map +0 -1
  1194. package/dist/chunk-XGCV5E6W.js +0 -133
  1195. package/dist/chunk-XGCV5E6W.js.map +0 -1
  1196. package/dist/chunk-XRKIEL5M.js +0 -72
  1197. package/dist/chunk-XRKIEL5M.js.map +0 -1
  1198. package/dist/chunk-XUJYEADU.js +0 -80
  1199. package/dist/chunk-XUJYEADU.js.map +0 -1
  1200. package/dist/chunk-XVYNSIQC.js +0 -116
  1201. package/dist/chunk-XVYNSIQC.js.map +0 -1
  1202. package/dist/chunk-XWTISHXO.js +0 -54
  1203. package/dist/chunk-XWTISHXO.js.map +0 -1
  1204. package/dist/chunk-YOGHS4UU.js +0 -202
  1205. package/dist/chunk-YOGHS4UU.js.map +0 -1
  1206. package/dist/chunk-YRSKXEOD.js +0 -135
  1207. package/dist/chunk-YRSKXEOD.js.map +0 -1
  1208. package/dist/chunk-ZALLCR7X.js +0 -108
  1209. package/dist/chunk-ZALLCR7X.js.map +0 -1
  1210. package/dist/chunk-ZDAOHMCW.js +0 -46
  1211. package/dist/chunk-ZDAOHMCW.js.map +0 -1
  1212. package/dist/chunk-ZESICCKK.js +0 -37
  1213. package/dist/chunk-ZESICCKK.js.map +0 -1
  1214. package/dist/chunk-ZIKFOD6N.js +0 -87
  1215. package/dist/chunk-ZIKFOD6N.js.map +0 -1
  1216. package/dist/chunk-ZJRWCQEN.js +0 -76
  1217. package/dist/chunk-ZJRWCQEN.js.map +0 -1
  1218. package/dist/chunk-ZSRJCIWF.js +0 -24
  1219. package/dist/chunk-ZSRJCIWF.js.map +0 -1
  1220. package/dist/plugin-Atb0VKtr.d.ts +0 -172
  1221. package/dist/slide/index.d.ts +0 -212
  1222. package/dist/slide/plugins/emoji/index.d.ts +0 -29
  1223. package/dist/slide/plugins/math/index.d.ts +0 -13
  1224. package/dist/slide/plugins/mermaid/index.d.ts +0 -55
  1225. package/dist/slide/plugins/shiki/index.d.ts +0 -18
  1226. package/dist/slide-deck/index.d.ts +0 -377
  1227. package/dist/whiteboard/index.d.ts +0 -258
@@ -10,6 +10,7 @@
10
10
  ],
11
11
  "registryDependencies": [
12
12
  "https://usetheodev.github.io/theo-ui/r/cn.json",
13
+ "https://usetheodev.github.io/theo-ui/r/env.json",
13
14
  "https://usetheodev.github.io/theo-ui/r/tokens.json"
14
15
  ],
15
16
  "files": [
@@ -17,31 +18,31 @@
17
18
  "path": "themes/types.ts",
18
19
  "type": "registry:lib",
19
20
  "target": "themes/types.ts",
20
- "content": "/**\n * Theo UI — Theme types.\n *\n * A Theme is a frozen bundle of CSS-var values that the runtime can swap by\n * setting `data-theme=\"<name>\"` on `<html>`. The structure mirrors what lives\n * in tokens.css so themes can be merged without ambiguity.\n */\n\nexport type ThemeMode = \"light\" | \"dark\";\n\nexport interface ColorScale {\n background: string;\n foreground: string;\n card: string;\n \"card-foreground\": string;\n popover: string;\n \"popover-foreground\": string;\n primary: string;\n \"primary-deep\": string;\n \"primary-glow\": string;\n \"primary-foreground\": string;\n secondary: string;\n \"secondary-foreground\": string;\n accent: string;\n \"accent-deep\": string;\n \"accent-foreground\": string;\n muted: string;\n \"muted-foreground\": string;\n border: string;\n input: string;\n ring: string;\n success: string;\n \"success-foreground\": string;\n warning: string;\n \"warning-foreground\": string;\n destructive: string;\n \"destructive-foreground\": string;\n info: string;\n \"info-foreground\": string;\n}\n\nexport interface ThemeFonts {\n /** Display headlines (h1-h3, hero text). */\n display: string;\n /** Body / UI text. */\n body: string;\n /** Code, mono, paths, timestamps. */\n mono: string;\n}\n\nexport interface Theme {\n /** Stable id, used in `data-theme`. */\n name: string;\n /** Human-readable label for theme switchers. */\n label: string;\n /** Optional short description shown in switchers. */\n description?: string;\n fonts: ThemeFonts;\n light: ColorScale;\n dark: ColorScale;\n /**\n * Optional URL(s) to fetch before applying. The provider injects a `<link>`\n * tag once per URL to load remote fonts. Already-injected URLs are deduped.\n */\n fontUrls?: string[];\n}\n"
21
+ "content": "/**\n * Theo UI — Theme types.\n *\n * A Theme is a frozen bundle of CSS-var values that the runtime can swap by\n * setting `data-theme=\"<name>\"` on `<html>`. The structure mirrors what lives\n * in tokens.css so themes can be merged without ambiguity.\n */\n\nexport type ThemeMode = \"light\" | \"dark\";\n\nexport interface ColorScale {\n background: string;\n foreground: string;\n card: string;\n \"card-foreground\": string;\n popover: string;\n \"popover-foreground\": string;\n primary: string;\n /**\n * Tonal scale variants of `primary`. Optional since T3.2 (ADR-0006).\n * When omitted, derived in CSS from `--primary` via `oklch(from ...)`.\n * Override per-theme by providing an explicit string.\n */\n \"primary-deep\"?: string;\n \"primary-glow\"?: string;\n \"primary-foreground\": string;\n secondary: string;\n \"secondary-foreground\": string;\n accent: string;\n /**\n * Tonal scale variant of `accent`. Optional since T3.2 — derived in CSS\n * when omitted. Override per-theme by providing an explicit string.\n */\n \"accent-deep\"?: string;\n \"accent-foreground\": string;\n muted: string;\n \"muted-foreground\": string;\n border: string;\n input: string;\n ring: string;\n success: string;\n \"success-foreground\": string;\n warning: string;\n \"warning-foreground\": string;\n destructive: string;\n \"destructive-foreground\": string;\n info: string;\n \"info-foreground\": string;\n /**\n * Status semantic group (D4 ADR — community-best-practices plan).\n *\n * Operational state colors (gateway connected/disconnected/slow/info-flag)\n * separated from action-result semantics (success/destructive/warning/info).\n * Defaults in built-in themes mirror their semantic counterparts; consumers\n * may override for visually-distinct status surfaces. `defineTheme(partial)`\n * auto-populates from semantic group when omitted.\n */\n \"status-online\": string;\n \"status-online-foreground\": string;\n \"status-offline\": string;\n \"status-offline-foreground\": string;\n \"status-degraded\": string;\n \"status-degraded-foreground\": string;\n \"status-info\": string;\n \"status-info-foreground\": string;\n}\n\nexport interface ThemeFonts {\n /** Display headlines (h1-h3, hero text). */\n display: string;\n /** Body / UI text. */\n body: string;\n /** Code, mono, paths, timestamps. */\n mono: string;\n}\n\nexport interface Theme {\n /** Stable id, used in `data-theme`. */\n name: string;\n /** Human-readable label for theme switchers. */\n label: string;\n /** Optional short description shown in switchers. */\n description?: string;\n fonts: ThemeFonts;\n light: ColorScale;\n dark: ColorScale;\n /**\n * Optional URL(s) to fetch before applying. The provider injects a `<link>`\n * tag once per URL to load remote fonts. Already-injected URLs are deduped.\n */\n fontUrls?: string[];\n}\n"
21
22
  },
22
23
  {
23
24
  "path": "themes/violet-forge.ts",
24
25
  "type": "registry:lib",
25
26
  "target": "themes/violet-forge.ts",
26
- "content": "import type { Theme } from \"@/themes/types\";\n\n/**\n * Violet Forge — the default Theo theme.\n *\n * Identity: Theo violet primary (#7C3AED), burnt sienna accent (#C96442),\n * Vercel-style neutral surfaces (pure white light / charcoal dark),\n * Geist Sans + Geist Mono throughout.\n *\n * Source of truth for `data-theme` overrides. Values mirror\n * src/styles/tokens.css for the default `:root`.\n */\nexport const violetForge: Theme = {\n name: \"violet-forge\",\n label: \"Violet Forge\",\n description: \"Theo default — violet primary, burnt sienna accent, Geist Sans + Geist Mono.\",\n fonts: {\n display: '\"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif',\n body: '\"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif',\n mono: '\"Geist Mono\", ui-monospace, SFMono-Regular, Menlo, monospace',\n },\n fontUrls: [\n \"https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@100..900&display=swap\",\n ],\n light: {\n background: \"0 0% 100%\",\n foreground: \"0 0% 4%\",\n card: \"0 0% 100%\",\n \"card-foreground\": \"0 0% 4%\",\n popover: \"0 0% 100%\",\n \"popover-foreground\": \"0 0% 4%\",\n primary: \"262 83% 58%\",\n \"primary-deep\": \"263 70% 42%\",\n \"primary-glow\": \"263 90% 76%\",\n \"primary-foreground\": \"0 0% 100%\",\n secondary: \"0 0% 96%\",\n \"secondary-foreground\": \"0 0% 4%\",\n accent: \"15 54% 53%\",\n \"accent-deep\": \"15 55% 40%\",\n \"accent-foreground\": \"0 0% 100%\",\n muted: \"0 0% 96%\",\n \"muted-foreground\": \"0 0% 45%\",\n border: \"0 0% 91%\",\n input: \"0 0% 91%\",\n ring: \"262 83% 58%\",\n success: \"142 71% 36%\",\n \"success-foreground\": \"0 0% 100%\",\n warning: \"33 92% 44%\",\n \"warning-foreground\": \"0 0% 100%\",\n destructive: \"0 72% 51%\",\n \"destructive-foreground\": \"0 0% 100%\",\n info: \"217 91% 60%\",\n \"info-foreground\": \"0 0% 100%\",\n },\n dark: {\n background: \"0 0% 4%\",\n foreground: \"0 0% 96%\",\n card: \"0 0% 7%\",\n \"card-foreground\": \"0 0% 96%\",\n popover: \"0 0% 9%\",\n \"popover-foreground\": \"0 0% 96%\",\n primary: \"262 83% 58%\",\n \"primary-deep\": \"263 70% 42%\",\n \"primary-glow\": \"263 90% 76%\",\n \"primary-foreground\": \"0 0% 100%\",\n secondary: \"0 0% 11%\",\n \"secondary-foreground\": \"0 0% 96%\",\n accent: \"15 54% 53%\",\n \"accent-deep\": \"15 55% 40%\",\n \"accent-foreground\": \"0 0% 100%\",\n muted: \"0 0% 11%\",\n \"muted-foreground\": \"0 0% 60%\",\n border: \"0 0% 16%\",\n input: \"0 0% 11%\",\n ring: \"262 83% 58%\",\n success: \"152 79% 52%\",\n \"success-foreground\": \"0 0% 4%\",\n warning: \"38 92% 50%\",\n \"warning-foreground\": \"0 0% 4%\",\n destructive: \"350 100% 65%\",\n \"destructive-foreground\": \"0 0% 4%\",\n info: \"213 100% 70%\",\n \"info-foreground\": \"0 0% 4%\",\n },\n};\n"
27
+ "content": "import type { Theme } from \"@/themes/types\";\n\n/**\n * Violet Forge — the default Theo theme.\n *\n * Identity: Theo violet electric primary (#A855F7, unified 2026-06-09),\n * burnt sienna accent (#C96442), Vercel-style neutral surfaces\n * (pure white light / charcoal dark), Geist Sans + Geist Mono throughout.\n *\n * Source of truth for `data-theme` overrides. Values mirror\n * src/styles/tokens.css for the default `:root`.\n */\nexport const violetForge: Theme = {\n name: \"violet-forge\",\n label: \"Violet Forge\",\n description: \"Theo default — violet primary, burnt sienna accent, Geist Sans + Geist Mono.\",\n fonts: {\n display: '\"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif',\n body: '\"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif',\n mono: '\"Geist Mono\", ui-monospace, SFMono-Regular, Menlo, monospace',\n },\n fontUrls: [\n \"https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@100..900&display=swap\",\n ],\n light: {\n background: \"oklch(1 0 0)\",\n foreground: \"oklch(0.146 0 0)\",\n card: \"oklch(1 0 0)\",\n \"card-foreground\": \"oklch(0.146 0 0)\",\n popover: \"oklch(1 0 0)\",\n \"popover-foreground\": \"oklch(0.146 0 0)\",\n primary: \"oklch(0.628 0.225 296)\",\n \"primary-foreground\": \"oklch(1 0 0)\",\n secondary: \"oklch(0.97 0 0)\",\n \"secondary-foreground\": \"oklch(0.146 0 0)\",\n accent: \"oklch(0.621 0.132 39)\",\n \"accent-foreground\": \"oklch(1 0 0)\",\n muted: \"oklch(0.97 0 0)\",\n \"muted-foreground\": \"oklch(0.555 0 0)\",\n border: \"oklch(0.931 0 0)\",\n input: \"oklch(0.931 0 0)\",\n ring: \"oklch(0.628 0.225 296)\",\n success: \"oklch(0.611 0.161 149.7)\",\n \"success-foreground\": \"oklch(1 0 0)\",\n warning: \"oklch(0.67 0.154 60.6)\",\n \"warning-foreground\": \"oklch(1 0 0)\",\n destructive: \"oklch(0.579 0.214 27.2)\",\n \"destructive-foreground\": \"oklch(1 0 0)\",\n info: \"oklch(0.626 0.186 259.6)\",\n \"info-foreground\": \"oklch(1 0 0)\",\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 dark: {\n background: \"oklch(0.146 0 0)\",\n foreground: \"oklch(0.97 0 0)\",\n card: \"oklch(0.182 0 0)\",\n \"card-foreground\": \"oklch(0.97 0 0)\",\n popover: \"oklch(0.204 0 0)\",\n \"popover-foreground\": \"oklch(0.97 0 0)\",\n primary: \"oklch(0.628 0.225 296)\",\n \"primary-foreground\": \"oklch(1 0 0)\",\n secondary: \"oklch(0.227 0 0)\",\n \"secondary-foreground\": \"oklch(0.97 0 0)\",\n accent: \"oklch(0.621 0.132 39)\",\n \"accent-foreground\": \"oklch(1 0 0)\",\n muted: \"oklch(0.227 0 0)\",\n \"muted-foreground\": \"oklch(0.683 0 0)\",\n border: \"oklch(0.28 0 0)\",\n input: \"oklch(0.227 0 0)\",\n ring: \"oklch(0.628 0.225 296)\",\n success: \"oklch(0.814 0.192 155.7)\",\n \"success-foreground\": \"oklch(0.146 0 0)\",\n warning: \"oklch(0.77 0.165 70.6)\",\n \"warning-foreground\": \"oklch(0.146 0 0)\",\n destructive: \"oklch(0.677 0.213 15.6)\",\n \"destructive-foreground\": \"oklch(0.146 0 0)\",\n info: \"oklch(0.732 0.142 254.4)\",\n \"info-foreground\": \"oklch(0.146 0 0)\",\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};\n"
27
28
  },
28
29
  {
29
30
  "path": "themes/classic-paper.ts",
30
31
  "type": "registry:lib",
31
32
  "target": "themes/classic-paper.ts",
32
- "content": "import type { Theme } from \"@/themes/types\";\n\n/**\n * Classic Paper — light-primary with deep-navy dark mirror; Inter + JetBrains Mono.\n *\n * Identity: warm paper background, deep navy foreground, indigo primary\n * (closer to traditional dashboard SaaS), Inter throughout. Maximizes\n * legibility and familiarity use when reading endurance > differentiation.\n *\n * Provides a full `dark` palette mirror so consumers toggling `.dark` still\n * get a coherent surface (it is not \"light-only\" by accident).\n */\nexport const classicPaper: Theme = {\n name: \"classic-paper\",\n label: \"Classic Paper\",\n description: \"Inter + paper background. Maximum legibility, conservative.\",\n fonts: {\n display: '\"Inter\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n body: '\"Inter\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n mono: '\"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, monospace',\n },\n fontUrls: [\n \"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap\",\n ],\n light: {\n background: \"36 30% 98%\",\n foreground: \"222 47% 11%\",\n card: \"0 0% 100%\",\n \"card-foreground\": \"222 47% 11%\",\n popover: \"0 0% 100%\",\n \"popover-foreground\": \"222 47% 11%\",\n primary: \"221 83% 53%\",\n \"primary-deep\": \"224 76% 38%\",\n \"primary-glow\": \"217 91% 70%\",\n \"primary-foreground\": \"0 0% 100%\",\n secondary: \"210 40% 96%\",\n \"secondary-foreground\": \"222 47% 11%\",\n accent: \"37 92% 40%\",\n \"accent-deep\": \"32 81% 30%\",\n \"accent-foreground\": \"0 0% 100%\",\n muted: \"210 40% 96%\",\n \"muted-foreground\": \"215 16% 47%\",\n border: \"214 32% 91%\",\n input: \"214 32% 91%\",\n ring: \"221 83% 53%\",\n success: \"142 71% 36%\",\n \"success-foreground\": \"0 0% 100%\",\n warning: \"33 92% 44%\",\n \"warning-foreground\": \"0 0% 100%\",\n destructive: \"0 72% 51%\",\n \"destructive-foreground\": \"0 0% 100%\",\n info: \"217 91% 60%\",\n \"info-foreground\": \"0 0% 100%\",\n },\n // Dark mirror — mainly the same hues with inverted lightness so the theme\n // still feels coherent if a consumer toggles `.dark`.\n dark: {\n background: \"222 47% 8%\",\n foreground: \"210 40% 98%\",\n card: \"222 47% 11%\",\n \"card-foreground\": \"210 40% 98%\",\n popover: \"222 47% 11%\",\n \"popover-foreground\": \"210 40% 98%\",\n primary: \"217 91% 60%\",\n \"primary-deep\": \"221 83% 45%\",\n \"primary-glow\": \"213 100% 80%\",\n \"primary-foreground\": \"222 47% 11%\",\n secondary: \"217 19% 18%\",\n \"secondary-foreground\": \"210 40% 98%\",\n accent: \"37 92% 60%\",\n \"accent-deep\": \"32 81% 45%\",\n \"accent-foreground\": \"222 47% 11%\",\n muted: \"217 19% 18%\",\n \"muted-foreground\": \"215 20% 65%\",\n border: \"217 19% 22%\",\n input: \"217 19% 18%\",\n ring: \"217 91% 60%\",\n success: \"152 79% 52%\",\n \"success-foreground\": \"222 47% 11%\",\n warning: \"38 92% 50%\",\n \"warning-foreground\": \"222 47% 11%\",\n destructive: \"350 100% 65%\",\n \"destructive-foreground\": \"222 47% 11%\",\n info: \"213 100% 70%\",\n \"info-foreground\": \"222 47% 11%\",\n },\n};\n"
33
+ "content": "import type { Theme } from \"@/themes/types\";\n\n/**\n * Classic Paper — visibly warm cream surface; Inter + JetBrains Mono.\n *\n * Identity: cream paper background with sepia warmth (calibrated against the\n * Vintage Paper / Anthropic Claude UI references), deep navy foreground,\n * indigo primary. Optimized for long agent/chat sessions where pure-white\n * surfaces cause vision fatigue (per IxDF 2026 + ACM 2025 light-mode studies).\n *\n * Token calibration (light mode):\n * - background L=0.95 chroma=0.025 hue=80 visibly cream paper\n * - card L=0.97 chroma=0.012 hue=80 — sub-paper layer (cards stand out)\n * - foreground unchanged (deep navy, AAA contrast >12:1 vs background)\n *\n * Dark mirror unchanged.\n */\nexport const classicPaper: Theme = {\n name: \"classic-paper\",\n label: \"Classic Paper\",\n description: \"Inter + paper background. Maximum legibility, conservative.\",\n fonts: {\n display: '\"Inter\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n body: '\"Inter\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n mono: '\"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, monospace',\n },\n fontUrls: [\n \"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap\",\n ],\n light: {\n background: \"oklch(0.95 0.025 80)\",\n foreground: \"oklch(0.206 0.039 265.5)\",\n card: \"oklch(0.97 0.012 80)\",\n \"card-foreground\": \"oklch(0.206 0.039 265.5)\",\n popover: \"oklch(0.98 0.008 80)\",\n \"popover-foreground\": \"oklch(0.206 0.039 265.5)\",\n primary: \"oklch(0.545 0.215 262.7)\",\n \"primary-foreground\": \"oklch(1 0 0)\",\n secondary: \"oklch(0.93 0.02 80)\",\n \"secondary-foreground\": \"oklch(0.206 0.039 265.5)\",\n accent: \"oklch(0.647 0.139 69)\",\n \"accent-foreground\": \"oklch(1 0 0)\",\n muted: \"oklch(0.93 0.02 80)\",\n \"muted-foreground\": \"oklch(0.45 0.03 80)\",\n border: \"oklch(0.88 0.025 80)\",\n input: \"oklch(0.92 0.02 80)\",\n ring: \"oklch(0.545 0.215 262.7)\",\n success: \"oklch(0.611 0.161 149.7)\",\n \"success-foreground\": \"oklch(1 0 0)\",\n warning: \"oklch(0.67 0.154 60.6)\",\n \"warning-foreground\": \"oklch(1 0 0)\",\n destructive: \"oklch(0.579 0.214 27.2)\",\n \"destructive-foreground\": \"oklch(1 0 0)\",\n info: \"oklch(0.626 0.186 259.6)\",\n \"info-foreground\": \"oklch(1 0 0)\",\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 // Dark mirror — mainly the same hues with inverted lightness so the theme\n // still feels coherent if a consumer toggles `.dark`.\n dark: {\n background: \"oklch(0.177 0.029 265.8)\",\n foreground: \"oklch(0.984 0.003 247.9)\",\n card: \"oklch(0.206 0.039 265.5)\",\n \"card-foreground\": \"oklch(0.984 0.003 247.9)\",\n popover: \"oklch(0.206 0.039 265.5)\",\n \"popover-foreground\": \"oklch(0.984 0.003 247.9)\",\n primary: \"oklch(0.626 0.186 259.6)\",\n \"primary-foreground\": \"oklch(0.206 0.039 265.5)\",\n secondary: \"oklch(0.291 0.022 259.9)\",\n \"secondary-foreground\": \"oklch(0.984 0.003 247.9)\",\n accent: \"oklch(0.803 0.15 74.7)\",\n \"accent-foreground\": \"oklch(0.206 0.039 265.5)\",\n muted: \"oklch(0.291 0.022 259.9)\",\n \"muted-foreground\": \"oklch(0.71 0.035 256.8)\",\n border: \"oklch(0.329 0.026 259.9)\",\n input: \"oklch(0.291 0.022 259.9)\",\n ring: \"oklch(0.626 0.186 259.6)\",\n success: \"oklch(0.814 0.192 155.7)\",\n \"success-foreground\": \"oklch(0.206 0.039 265.5)\",\n warning: \"oklch(0.77 0.165 70.6)\",\n \"warning-foreground\": \"oklch(0.206 0.039 265.5)\",\n destructive: \"oklch(0.677 0.213 15.6)\",\n \"destructive-foreground\": \"oklch(0.206 0.039 265.5)\",\n info: \"oklch(0.732 0.142 254.4)\",\n \"info-foreground\": \"oklch(0.206 0.039 265.5)\",\n \"status-online\": \"oklch(0.814 0.192 155.7)\",\n \"status-online-foreground\": \"oklch(0.206 0.039 265.5)\",\n \"status-offline\": \"oklch(0.677 0.213 15.6)\",\n \"status-offline-foreground\": \"oklch(0.206 0.039 265.5)\",\n \"status-degraded\": \"oklch(0.77 0.165 70.6)\",\n \"status-degraded-foreground\": \"oklch(0.206 0.039 265.5)\",\n \"status-info\": \"oklch(0.732 0.142 254.4)\",\n \"status-info-foreground\": \"oklch(0.206 0.039 265.5)\",\n },\n};\n"
33
34
  },
34
35
  {
35
36
  "path": "themes/aurora-terminal.ts",
36
37
  "type": "registry:lib",
37
38
  "target": "themes/aurora-terminal.ts",
38
- "content": "import type { Theme } from \"@/themes/types\";\n\n/**\n * Aurora Terminal — dark-first, cyan-aurora primary, Geist Mono everywhere.\n *\n * Identity: deep oceanic background, cyan-aurora primary, aurora-pink accent.\n * Headers use Geist with heavier tracking; body uses Geist Mono for full\n * \"developer console\" feel. Suits CLI/devtools showcase.\n */\nexport const auroraTerminal: Theme = {\n name: \"aurora-terminal\",\n label: \"Aurora Terminal\",\n description: \"Dark sci-fi developer console — cyan-aurora + Geist Mono body.\",\n fonts: {\n display: '\"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif',\n body: '\"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif',\n mono: '\"Geist Mono\", ui-monospace, SFMono-Regular, Menlo, monospace',\n },\n fontUrls: [\n \"https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@100..900&display=swap\",\n ],\n light: {\n background: \"220 30% 96%\",\n foreground: \"222 47% 11%\",\n card: \"0 0% 100%\",\n \"card-foreground\": \"222 47% 11%\",\n popover: \"0 0% 100%\",\n \"popover-foreground\": \"222 47% 11%\",\n primary: \"178 78% 41%\",\n \"primary-deep\": \"180 100% 25%\",\n \"primary-glow\": \"180 89% 70%\",\n \"primary-foreground\": \"222 47% 11%\",\n secondary: \"210 40% 96%\",\n \"secondary-foreground\": \"222 47% 11%\",\n accent: \"340 82% 60%\",\n \"accent-deep\": \"340 80% 50%\",\n \"accent-foreground\": \"0 0% 100%\",\n muted: \"214 32% 91%\",\n \"muted-foreground\": \"215 16% 47%\",\n border: \"214 32% 91%\",\n input: \"214 32% 91%\",\n ring: \"178 78% 41%\",\n success: \"152 79% 42%\",\n \"success-foreground\": \"0 0% 100%\",\n warning: \"33 92% 44%\",\n \"warning-foreground\": \"0 0% 100%\",\n destructive: \"0 72% 51%\",\n \"destructive-foreground\": \"0 0% 100%\",\n info: \"217 91% 60%\",\n \"info-foreground\": \"0 0% 100%\",\n },\n dark: {\n background: \"224 36% 7%\",\n foreground: \"220 30% 96%\",\n card: \"224 35% 10%\",\n \"card-foreground\": \"220 30% 96%\",\n popover: \"224 35% 10%\",\n \"popover-foreground\": \"220 30% 96%\",\n primary: \"178 71% 60%\",\n \"primary-deep\": \"180 100% 35%\",\n \"primary-glow\": \"180 89% 80%\",\n \"primary-foreground\": \"224 36% 7%\",\n secondary: \"222 30% 14%\",\n \"secondary-foreground\": \"220 30% 96%\",\n accent: \"340 90% 65%\",\n \"accent-deep\": \"340 80% 50%\",\n \"accent-foreground\": \"224 36% 7%\",\n muted: \"222 30% 14%\",\n \"muted-foreground\": \"215 20% 65%\",\n border: \"222 28% 18%\",\n input: \"222 30% 14%\",\n ring: \"178 71% 60%\",\n success: \"152 79% 52%\",\n \"success-foreground\": \"224 36% 7%\",\n warning: \"38 92% 50%\",\n \"warning-foreground\": \"224 36% 7%\",\n destructive: \"350 100% 65%\",\n \"destructive-foreground\": \"224 36% 7%\",\n info: \"213 100% 70%\",\n \"info-foreground\": \"224 36% 7%\",\n },\n};\n"
39
+ "content": "import type { Theme } from \"@/themes/types\";\n\n/**\n * Aurora Terminal — dark-first, cyan-aurora primary, Geist Mono everywhere.\n *\n * Identity: deep oceanic background, cyan-aurora primary, aurora-pink accent.\n * Headers use Geist with heavier tracking; body uses Geist Mono for full\n * \"developer console\" feel. Suits CLI/devtools showcase.\n */\nexport const auroraTerminal: Theme = {\n name: \"aurora-terminal\",\n label: \"Aurora Terminal\",\n description: \"Dark sci-fi developer console — cyan-aurora + Geist Mono body.\",\n fonts: {\n display: '\"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif',\n body: '\"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif',\n mono: '\"Geist Mono\", ui-monospace, SFMono-Regular, Menlo, monospace',\n },\n fontUrls: [\n \"https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@100..900&display=swap\",\n ],\n light: {\n background: \"oklch(0.966 0.006 264.5)\",\n foreground: \"oklch(0.206 0.039 265.5)\",\n card: \"oklch(1 0 0)\",\n \"card-foreground\": \"oklch(0.206 0.039 265.5)\",\n popover: \"oklch(1 0 0)\",\n \"popover-foreground\": \"oklch(0.206 0.039 265.5)\",\n primary: \"oklch(0.714 0.12 191)\",\n \"primary-foreground\": \"oklch(0.206 0.039 265.5)\",\n secondary: \"oklch(0.968 0.007 247.9)\",\n \"secondary-foreground\": \"oklch(0.206 0.039 265.5)\",\n accent: \"oklch(0.646 0.206 4.9)\",\n \"accent-foreground\": \"oklch(1 0 0)\",\n muted: \"oklch(0.926 0.013 255)\",\n \"muted-foreground\": \"oklch(0.556 0.04 256.8)\",\n border: \"oklch(0.926 0.013 255)\",\n input: \"oklch(0.926 0.013 255)\",\n ring: \"oklch(0.714 0.12 191)\",\n success: \"oklch(0.711 0.171 155.2)\",\n \"success-foreground\": \"oklch(1 0 0)\",\n warning: \"oklch(0.67 0.154 60.6)\",\n \"warning-foreground\": \"oklch(1 0 0)\",\n destructive: \"oklch(0.579 0.214 27.2)\",\n \"destructive-foreground\": \"oklch(1 0 0)\",\n info: \"oklch(0.626 0.186 259.6)\",\n \"info-foreground\": \"oklch(1 0 0)\",\n \"status-online\": \"oklch(0.711 0.171 155.2)\",\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 dark: {\n background: \"oklch(0.169 0.021 268.7)\",\n foreground: \"oklch(0.966 0.006 264.5)\",\n card: \"oklch(0.199 0.027 268.5)\",\n \"card-foreground\": \"oklch(0.966 0.006 264.5)\",\n popover: \"oklch(0.199 0.027 268.5)\",\n \"popover-foreground\": \"oklch(0.966 0.006 264.5)\",\n primary: \"oklch(0.834 0.122 191.9)\",\n \"primary-foreground\": \"oklch(0.169 0.021 268.7)\",\n secondary: \"oklch(0.242 0.03 266.3)\",\n \"secondary-foreground\": \"oklch(0.966 0.006 264.5)\",\n accent: \"oklch(0.68 0.2 3.6)\",\n \"accent-foreground\": \"oklch(0.169 0.021 268.7)\",\n muted: \"oklch(0.242 0.03 266.3)\",\n \"muted-foreground\": \"oklch(0.71 0.035 256.8)\",\n border: \"oklch(0.281 0.035 266.3)\",\n input: \"oklch(0.242 0.03 266.3)\",\n ring: \"oklch(0.834 0.122 191.9)\",\n success: \"oklch(0.814 0.192 155.7)\",\n \"success-foreground\": \"oklch(0.169 0.021 268.7)\",\n warning: \"oklch(0.77 0.165 70.6)\",\n \"warning-foreground\": \"oklch(0.169 0.021 268.7)\",\n destructive: \"oklch(0.677 0.213 15.6)\",\n \"destructive-foreground\": \"oklch(0.169 0.021 268.7)\",\n info: \"oklch(0.732 0.142 254.4)\",\n \"info-foreground\": \"oklch(0.169 0.021 268.7)\",\n \"status-online\": \"oklch(0.814 0.192 155.7)\",\n \"status-online-foreground\": \"oklch(0.169 0.021 268.7)\",\n \"status-offline\": \"oklch(0.677 0.213 15.6)\",\n \"status-offline-foreground\": \"oklch(0.169 0.021 268.7)\",\n \"status-degraded\": \"oklch(0.77 0.165 70.6)\",\n \"status-degraded-foreground\": \"oklch(0.169 0.021 268.7)\",\n \"status-info\": \"oklch(0.732 0.142 254.4)\",\n \"status-info-foreground\": \"oklch(0.169 0.021 268.7)\",\n },\n};\n"
39
40
  },
40
41
  {
41
42
  "path": "themes/theme-provider.tsx",
42
43
  "type": "registry:lib",
43
44
  "target": "themes/theme-provider.tsx",
44
- "content": "import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { JSX, ReactNode } from \"react\";\nimport { safeHref } from \"@/lib/safe-href\";\nimport { type Density, DensityContext, injectDensityCss } from \"./density\";\nimport type { ColorScale, Theme, ThemeMode } from \"@/themes/types\";\n\ninterface ThemeContextValue {\n /** Active theme (full descriptor). */\n theme: Theme;\n /** Active mode: light or dark. */\n mode: ThemeMode;\n /** All available themes. */\n themes: Theme[];\n /** Swap the active theme by name. */\n setTheme: (name: string) => void;\n /** Set light/dark explicitly. */\n setMode: (mode: ThemeMode) => void;\n /** Toggle light <> dark. */\n toggleMode: () => void;\n /** Register an additional theme at runtime. */\n registerTheme: (theme: Theme) => void;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | undefined>(undefined);\n\nconst STYLE_ELEMENT_ID = \"theo-ui-theme-vars\";\n\n// T3.2 (SEC-001): allowlist validators for theme values. injectThemeCss\n// interpolates theme name + color values + font families into a <style>\n// textContent. Without validation, a theme object from an untrusted source\n// (e.g., a feature-flag service, a CMS) could inject arbitrary CSS via\n// closing the declaration with `}` or smuggling `url(...)` for exfiltration.\n// We reject rather than escape: themes are code, not user input. Invalid\n// values cause a dev-time throw (caller sees the problem); production\n// silently substitutes a safe fallback so a misconfigured theme can't\n// crash the app.\n\n// Color values. Multiple accepted shapes:\n// 1. Hex: `#fff`, `#0a0a0a`, `#0a0a0aff`.\n// 2. Fully-parenthesized CSS color functions: `oklch(...)`, `rgb(...)`,\n// `hsl(...)`, etc. Inner content restricted to digits/dots/spaces/\n// percent/slash/comma/dash/plus — no semicolons, no braces, no `url(`.\n// 3. HSL-component split (shadcn-ui convention used by the built-in\n// themes): `\"0 0% 100%\"`, `\"262 83% 58%\"` — space-separated numeric\n// components consumed via `hsl(var(--token))` in stylesheets.\n// 4. `var(--token)` references, optionally with a fallback value that\n// contains no parens/braces/semicolons.\n// 5. CSS keywords: `transparent`, `currentColor`, `inherit`, `initial`,\n// `unset`.\nconst COLOR_VALUE_PATTERN =\n /^(#[0-9a-fA-F]{3,8}|(?:oklch|oklab|rgb|rgba|hsl|hsla|lab|lch|color)\\(\\s*[\\d.\\s%,/+\\-]+\\s*\\)|-?\\d+(?:\\.\\d+)?%?(?:\\s+-?\\d+(?:\\.\\d+)?%?){1,3}|var\\(--[a-zA-Z0-9-]+(?:\\s*,\\s*[^();{}]+)?\\)|transparent|currentColor|inherit|initial|unset)$/;\n\n// Font family: word chars, spaces, commas, hyphens, dots, quotes. Excludes\n// parens (blocks `url(...)`) and semicolons (blocks declaration breakouts).\nconst FONT_FAMILY_PATTERN = /^[\\w\\s,\"'\\-.]+$/;\n\n// Theme name: kebab-case identifier. Excludes anything that could break out\n// of an attribute selector or inject additional rules.\nconst THEME_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;\n\nconst IS_DEV = typeof process === \"undefined\" || process.env.NODE_ENV !== \"production\";\n\nfunction rejectOrFallback(scope: string, value: string, fallback: string): string {\n if (IS_DEV) {\n throw new Error(\n `[@usetheo/ui] invalid ${scope} value: ${JSON.stringify(value)}. Theme values must match the allowlist (see src/themes/theme-provider.tsx). Refusing to inject potentially unsafe CSS.`,\n );\n }\n return fallback;\n}\n\nfunction validatedColor(token: string, value: string): string {\n if (COLOR_VALUE_PATTERN.test(value)) return value;\n return rejectOrFallback(`color \"${token}\"`, value, \"transparent\");\n}\n\nfunction validatedFontFamily(slot: string, value: string): string {\n if (FONT_FAMILY_PATTERN.test(value)) return value;\n return rejectOrFallback(`fontFamily \"${slot}\"`, value, \"inherit\");\n}\n\nfunction validatedThemeName(value: string): string {\n if (THEME_NAME_PATTERN.test(value)) return value;\n return rejectOrFallback(\"theme.name\", value, \"invalid-theme\");\n}\n\nfunction colorScaleToCss(name: string, mode: ThemeMode, colors: ColorScale): string {\n const safeName = validatedThemeName(name);\n const selector =\n mode === \"light\"\n ? `[data-theme=\"${safeName}\"]`\n : `[data-theme=\"${safeName}\"].dark, [data-theme=\"${safeName}\"][data-mode=\"dark\"]`;\n const decls = Object.entries(colors)\n .map(([token, value]) => ` --${token}: ${validatedColor(token, value)};`)\n .join(\"\\n\");\n return `${selector} {\\n${decls}\\n}`;\n}\n\nfunction fontsToCss(name: string, fonts: Theme[\"fonts\"]): string {\n const safeName = validatedThemeName(name);\n const display = validatedFontFamily(\"display\", fonts.display);\n const body = validatedFontFamily(\"body\", fonts.body);\n const mono = validatedFontFamily(\"mono\", fonts.mono);\n return `[data-theme=\"${safeName}\"] {\\n --font-display: ${display};\\n --font-body: ${body};\\n --font-mono: ${mono};\\n}`;\n}\n\nfunction injectThemeCss(themes: Theme[]): void {\n if (typeof document === \"undefined\") return;\n let style = document.getElementById(STYLE_ELEMENT_ID) as HTMLStyleElement | null;\n if (!style) {\n style = document.createElement(\"style\");\n style.id = STYLE_ELEMENT_ID;\n document.head.appendChild(style);\n }\n const blocks: string[] = [];\n for (const theme of themes) {\n blocks.push(fontsToCss(theme.name, theme.fonts));\n blocks.push(colorScaleToCss(theme.name, \"light\", theme.light));\n blocks.push(colorScaleToCss(theme.name, \"dark\", theme.dark));\n }\n style.textContent = blocks.join(\"\\n\\n\");\n}\n\n/**\n * loadThemeFonts — idempotently inject `<link rel=\"stylesheet\">` for each\n * font URL declared by the theme.\n *\n * T4.2: the previous implementation kept a module-level `Set` to track\n * already-injected URLs. That singleton broke test isolation (state\n * leaked across renders) and silently skipped injection in micro-frontend\n * setups with multiple `<ThemeProvider>` mounts. Replaced with a DOM\n * check: we query `document.head` for an existing link with the same\n * `href` before appending a new one. The DOM is the single source of\n * truth; no shared state across instances.\n */\nfunction loadThemeFonts(theme: Theme): void {\n if (typeof document === \"undefined\") return;\n if (!theme.fontUrls) return;\n for (const url of theme.fontUrls) {\n // Re-audit NEW-001 (SSRF, LOW): defang dangerous protocols on\n // consumer-provided font URLs. Built-in themes use\n // fonts.googleapis.com/gstatic.com — safe. registerTheme accepts\n // arbitrary objects at runtime; a malicious theme could try to inject\n // javascript:/data:text/html via fontUrls. safeHref returns undefined\n // for dangerous protocols, which we skip silently.\n const safe = safeHref(url);\n if (!safe) continue;\n if (document.head.querySelector(`link[rel=\"stylesheet\"][href=\"${CSS.escape(safe)}\"]`)) {\n continue;\n }\n const link = document.createElement(\"link\");\n link.rel = \"stylesheet\";\n link.href = safe;\n document.head.appendChild(link);\n }\n}\n\ninterface ThemeProviderProps {\n children: ReactNode;\n /**\n * Theme to start with. Must match the `name` of an entry in `themes`.\n * Defaults to `\"violet-forge\"` for backward compat — if you don't pass\n * `violet-forge` in `themes`, set this prop explicitly.\n */\n defaultTheme?: string;\n /** Mode to start with. Defaults to `\"dark\"` (library is dark-first). */\n defaultMode?: ThemeMode;\n /**\n * Available themes. **Required**: ThemeProvider does not auto-include any\n * built-in theme since v0.1.0-next.0 — pass `builtinThemes` for all three\n * Violet Forge defaults, or your own array for a slimmer bundle.\n *\n * Migration: consumers previously calling `<ThemeProvider>` without this\n * prop now must pass `themes={builtinThemes}` (or use `<TheoUIProvider>`\n * which defaults to `builtinThemes` for you).\n */\n themes: Theme[];\n /**\n * Persist selection in localStorage under this key. Pass `null` to disable.\n * Default: \"theo-ui:theme\".\n */\n storageKey?: string | null;\n /**\n * Initial density. Drives `data-density` on `<html>` and the `--theo-control-h`\n * / `--theo-control-px` CSS vars consumed by form-control `md` variants.\n * Defaults to `\"comfortable\"` (36px controls — FAANG-tier modern density).\n * Plan: faang-density-tightening (D3).\n */\n defaultDensity?: Density;\n}\n\n/**\n * Storage failure diagnostic — dev-only one-line warn so engineers see\n * something when localStorage throws (Safari private mode, blocked\n * third-party cookies, sandboxed iframes). In production we stay silent;\n * runtime behavior is fail-safe (state still lives in memory).\n *\n * Per HIGH-006: silent catches diverge from the \"fail loud\" principle\n * declared in the global CLAUDE.md. We accept silence in prod because the\n * fallback is correct, but we surface a single warn per call site in dev.\n */\nfunction warnStorageFailure(scope: string, err: unknown): void {\n if (typeof process === \"undefined\" || process.env.NODE_ENV === \"production\") return;\n // biome-ignore lint/suspicious/noConsole: dev-only diagnostic for storage failures (HIGH-006)\n console.warn(`[@usetheo/ui] theme storage failure (${scope}):`, err);\n}\n\n/**\n * ThemeProvider — central registry + runtime switcher for Theo themes.\n *\n * Behavior:\n * 1. On mount, injects a `<style id=\"theo-ui-theme-vars\">` element with\n * one CSS block per theme (`[data-theme=\"<name>\"] { --token: ... }`).\n * 2. Sets `data-theme` and `data-mode` on `<html>` so any element nested\n * below inherits the right tokens (the Tailwind config consumes them).\n * 3. Lazy-loads theme font URLs by injecting `<link rel=\"stylesheet\">`.\n * 4. Optionally persists choice in localStorage.\n */\nfunction ThemeProvider({\n children,\n defaultTheme = \"violet-forge\",\n defaultMode = \"dark\",\n themes: themesProp,\n storageKey = \"theo-ui:theme\",\n defaultDensity = \"comfortable\",\n}: ThemeProviderProps): JSX.Element {\n // Themes prop is required since v0.1.0-next.0 — see migration note in\n // the JSDoc on ThemeProviderProps. Pass `builtinThemes` for the legacy\n // default behavior (violet-forge + classic-paper + aurora-terminal), or\n // an array of your own. Empty array is rejected: ThemeProvider has no\n // valid state without at least one registered theme.\n if (!themesProp || themesProp.length === 0) {\n throw new Error(\n \"<ThemeProvider> requires the `themes` prop with at least one Theme. \" +\n \"Pass `themes={builtinThemes}` for the Violet Forge defaults (importable \" +\n \"via the package barrel), or use <TheoUIProvider> which sets this for you.\",\n );\n }\n\n // T3.2 (SEC-001): eager validation. Calling validatedColor/FontFamily/\n // ThemeName here ensures CSS-injection attempts throw at construction\n // time rather than inside the deferred useEffect that injects the\n // <style>. Production-mode fallbacks keep the app rendering even if a\n // theme has bad values.\n //\n // Re-audit NEW-3: wrapped in useMemo so the validation cost (O(themes *\n // tokens), ~60 ops per built-in theme) only runs when themesProp's\n // reference changes — not on every parent re-render. Consumers passing\n // inline array literals (`themes={[violetForge, classicPaper]}`) would\n // otherwise pay this on every parent update.\n const mergedThemes = useMemo<Theme[]>(() => {\n for (const t of themesProp) {\n validatedThemeName(t.name);\n validatedFontFamily(\"display\", t.fonts.display);\n validatedFontFamily(\"body\", t.fonts.body);\n validatedFontFamily(\"mono\", t.fonts.mono);\n for (const [token, value] of Object.entries(t.light)) {\n validatedColor(token, value);\n }\n for (const [token, value] of Object.entries(t.dark)) {\n validatedColor(token, value);\n }\n }\n // Dedup by theme name; last writer wins (allows registerTheme override).\n const map = new Map<string, Theme>();\n for (const t of themesProp) map.set(t.name, t);\n return Array.from(map.values());\n }, [themesProp]);\n\n const [themes, setThemes] = useState<Theme[]>(mergedThemes);\n\n // Re-sync state when the `themes` prop changes between renders. Avoids the\n // common pitfall where the user passes a different array later and the\n // initial-state-only seed silently ignores the change.\n useEffect(() => {\n setThemes(mergedThemes);\n }, [mergedThemes]);\n\n // SSR-safe initialization (0.6.3-next.0 hydration-mismatch fix).\n //\n // Previously: `useState(() => localStorage.getItem(…))`. The initializer\n // ran on BOTH server (no `window`, returned default) AND client at\n // hydration time (with `window`, returned the stored value). The two\n // results disagreed → React threw a hydration error on every page load\n // for any user who had previously changed themes, and re-rendered the\n // entire tree from scratch — defeating SSR.\n //\n // Fix: initialize with the SSR default ALWAYS. Promote to the stored\n // value in a post-mount `useEffect` below. The 1-frame visual flicker\n // is mitigated by the optional `<ThemeScript>` component, which sets\n // `data-theme` / `data-mode` / `data-density` on `<html>` before React\n // hydrates — see `theme-script.tsx`.\n //\n // The `hydratedRef` flag below guards the persist effect so that\n // first-mount writes (with the SSR default values) don't clobber the\n // user's stored preference in the brief window between mount and the\n // post-mount hydration effect.\n const [themeName, setThemeName] = useState<string>(defaultTheme);\n const [mode, setModeState] = useState<ThemeMode>(defaultMode);\n const [density, setDensityState] = useState<Density>(defaultDensity);\n\n // First-run guard for the persist effect below. On initial mount the\n // state is the SSR-safe default; we MUST NOT clobber the user's stored\n // preference with that default before the post-mount hydration effect\n // can promote it. After the first persist-effect call returns early,\n // every subsequent change (post-hydration setState OR user-driven)\n // persists normally.\n const skipFirstPersistRef = useRef(true);\n\n // Post-mount hydration: read localStorage and promote stored values to\n // state. Runs ONCE on mount. If `storageKey` is null or no value is\n // stored, this is a no-op — state stays at the SSR defaults.\n useEffect(() => {\n if (typeof window === \"undefined\" || !storageKey) return;\n try {\n const storedName = window.localStorage.getItem(`${storageKey}:name`);\n const storedMode = window.localStorage.getItem(`${storageKey}:mode`);\n const storedDensity = window.localStorage.getItem(`${storageKey}:density`);\n if (storedName) setThemeName(storedName);\n if (storedMode === \"dark\" || storedMode === \"light\") setModeState(storedMode);\n if (\n storedDensity === \"compact\" ||\n storedDensity === \"comfortable\" ||\n storedDensity === \"spacious\"\n ) {\n setDensityState(storedDensity);\n }\n } catch (err) {\n warnStorageFailure(\"read theme + mode + density\", err);\n }\n }, [storageKey]);\n\n // Inject CSS vars whenever the themes list changes.\n useEffect(() => {\n injectThemeCss(themes);\n }, [themes]);\n\n // Apply data-theme + data-mode + data-density to <html>, load fonts,\n // inject density CSS vars.\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const active = themes.find((t) => t.name === themeName) ?? themes[0];\n if (!active) return;\n document.documentElement.setAttribute(\"data-theme\", active.name);\n document.documentElement.setAttribute(\"data-mode\", mode);\n document.documentElement.setAttribute(\"data-density\", density);\n document.documentElement.classList.toggle(\"dark\", mode === \"dark\");\n loadThemeFonts(active);\n injectDensityCss();\n }, [themeName, mode, density, themes]);\n\n // Persist on change.\n //\n // The first run is SKIPPED via `skipFirstPersistRef`: state at mount\n // is the SSR-safe default. If we wrote it to storage immediately,\n // we'd clobber the user's stored preference between mount and the\n // post-mount hydration effect that promotes the stored value. After\n // the first call, every subsequent run persists — whether the change\n // came from the hydration effect (no-op write back of the stored\n // value) or a user-driven `setTheme` / `toggleMode` / `setDensity`.\n useEffect(() => {\n if (skipFirstPersistRef.current) {\n skipFirstPersistRef.current = false;\n return;\n }\n if (typeof window === \"undefined\" || !storageKey) return;\n try {\n window.localStorage.setItem(`${storageKey}:name`, themeName);\n window.localStorage.setItem(`${storageKey}:mode`, mode);\n window.localStorage.setItem(`${storageKey}:density`, density);\n } catch (err) {\n // Storage may fail in private mode; behavior remains correct (state\n // lives in memory). Per HIGH-006 we surface a one-time dev warning so\n // the engineer sees something instead of complete silence.\n warnStorageFailure(\"persist theme + mode + density\", err);\n }\n }, [themeName, mode, density, storageKey]);\n\n const setTheme = useCallback((name: string) => setThemeName(name), []);\n const setMode = useCallback((next: ThemeMode) => setModeState(next), []);\n const toggleMode = useCallback(\n () => setModeState((cur) => (cur === \"light\" ? \"dark\" : \"light\")),\n [],\n );\n const setDensity = useCallback((next: Density) => setDensityState(next), []);\n const registerTheme = useCallback((theme: Theme) => {\n setThemes((cur) => {\n const idx = cur.findIndex((t) => t.name === theme.name);\n if (idx >= 0) {\n const next = cur.slice();\n next[idx] = theme;\n return next;\n }\n return [...cur, theme];\n });\n }, []);\n\n // themes[0] is guaranteed non-undefined by the constructor-time check\n // above (themesProp is non-empty); the non-null assert encodes that\n // invariant for TypeScript, which can't trace it through useState.\n // biome-ignore lint/style/noNonNullAssertion: T2.5 runtime invariant — themesProp non-empty validated at top of function\n const active = themes.find((t) => t.name === themeName) ?? themes[0]!;\n\n const value = useMemo<ThemeContextValue>(\n () => ({\n theme: active,\n mode,\n themes,\n setTheme,\n setMode,\n toggleMode,\n registerTheme,\n }),\n [active, mode, themes, setTheme, setMode, toggleMode, registerTheme],\n );\n\n const densityValue = useMemo(() => ({ density, setDensity }), [density, setDensity]);\n\n return (\n <ThemeContext.Provider value={value}>\n <DensityContext.Provider value={densityValue}>{children}</DensityContext.Provider>\n </ThemeContext.Provider>\n );\n}\n\n/**\n * useTheme — access theme state from any component inside <ThemeProvider>.\n * Throws if used outside the provider — fail-fast.\n */\nfunction useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) {\n throw new Error(\"useTheme must be used inside <ThemeProvider>.\");\n }\n return ctx;\n}\n\nexport { ThemeProvider, useTheme };\n"
45
+ "content": "import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { JSX, ReactNode } from \"react\";\nimport { isDev } from \"@/lib/env\";\nimport { safeHref } from \"@/lib/safe-href\";\nimport { COLOR_VALUE_PATTERN } from \"./color-value-pattern\";\nimport { type Density, DensityContext, injectDensityCss } from \"./density\";\nimport { formatThemeIssues, validateTheme } from \"./schema\";\nimport type { ColorScale, Theme, ThemeMode } from \"@/themes/types\";\n\ninterface ThemeContextValue {\n /** Active theme (full descriptor). */\n theme: Theme;\n /** Active mode: light or dark. */\n mode: ThemeMode;\n /** All available themes. */\n themes: Theme[];\n /** Swap the active theme by name. */\n setTheme: (name: string) => void;\n /** Set light/dark explicitly. */\n setMode: (mode: ThemeMode) => void;\n /** Toggle light <> dark. */\n toggleMode: () => void;\n /** Register an additional theme at runtime. */\n registerTheme: (theme: Theme) => void;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | undefined>(undefined);\n\nconst STYLE_ELEMENT_ID = \"theo-ui-theme-vars\";\n\n// T3.2 (SEC-001): allowlist validators for theme values. injectThemeCss\n// interpolates theme name + color values + font families into a <style>\n// textContent. Without validation, a theme object from an untrusted source\n// (e.g., a feature-flag service, a CMS) could inject arbitrary CSS via\n// closing the declaration with `}` or smuggling `url(...)` for exfiltration.\n// We reject rather than escape: themes are code, not user input. Invalid\n// values cause a dev-time throw (caller sees the problem); production\n// silently substitutes a safe fallback so a misconfigured theme can't\n// crash the app.\n\n// COLOR_VALUE_PATTERN was inlined here pre-T2.5. Now extracted to\n// `color-value-pattern.ts` so the regex is reusable by the Valibot schema (T2.7)\n// and so the OKLCH relative-color syntax (`oklch(from var(--x) calc(l - 0.16) c h)`,\n// required by T3.1 tonal derivations) is supported without expanding the inline\n// regex monolith. See EC-5 in the edge-case review.\n\n// Font family: word chars, spaces, commas, hyphens, dots, quotes. Excludes\n// parens (blocks `url(...)`) and semicolons (blocks declaration breakouts).\nconst FONT_FAMILY_PATTERN = /^[\\w\\s,\"'\\-.]+$/;\n\n// Theme name: kebab-case identifier. Excludes anything that could break out\n// of an attribute selector or inject additional rules.\nconst THEME_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;\n\nconst IS_DEV = isDev();\n\nfunction rejectOrFallback(scope: string, value: string, fallback: string): string {\n if (IS_DEV) {\n throw new Error(\n `[@theokit/ui] invalid ${scope} value: ${JSON.stringify(value)}. Theme values must match the allowlist (see src/themes/theme-provider.tsx). Refusing to inject potentially unsafe CSS.`,\n );\n }\n return fallback;\n}\n\nfunction validatedColor(token: string, value: string): string {\n if (COLOR_VALUE_PATTERN.test(value)) return value;\n return rejectOrFallback(`color \"${token}\"`, value, \"transparent\");\n}\n\nfunction validatedFontFamily(slot: string, value: string): string {\n if (FONT_FAMILY_PATTERN.test(value)) return value;\n return rejectOrFallback(`fontFamily \"${slot}\"`, value, \"inherit\");\n}\n\nfunction validatedThemeName(value: string): string {\n if (THEME_NAME_PATTERN.test(value)) return value;\n return rejectOrFallback(\"theme.name\", value, \"invalid-theme\");\n}\n\nfunction colorScaleToCss(name: string, mode: ThemeMode, colors: ColorScale): string {\n const safeName = validatedThemeName(name);\n const selector =\n mode === \"light\"\n ? `[data-theme=\"${safeName}\"]`\n : `[data-theme=\"${safeName}\"].dark, [data-theme=\"${safeName}\"][data-mode=\"dark\"]`;\n const decls = Object.entries(colors)\n .map(([token, value]) => ` --${token}: ${validatedColor(token, value)};`)\n .join(\"\\n\");\n return `${selector} {\\n${decls}\\n}`;\n}\n\nfunction fontsToCss(name: string, fonts: Theme[\"fonts\"]): string {\n const safeName = validatedThemeName(name);\n const display = validatedFontFamily(\"display\", fonts.display);\n const body = validatedFontFamily(\"body\", fonts.body);\n const mono = validatedFontFamily(\"mono\", fonts.mono);\n return `[data-theme=\"${safeName}\"] {\\n --font-display: ${display};\\n --font-body: ${body};\\n --font-mono: ${mono};\\n}`;\n}\n\nfunction injectThemeCss(themes: Theme[]): void {\n if (typeof document === \"undefined\") return;\n let style = document.getElementById(STYLE_ELEMENT_ID) as HTMLStyleElement | null;\n if (!style) {\n style = document.createElement(\"style\");\n style.id = STYLE_ELEMENT_ID;\n document.head.appendChild(style);\n }\n const blocks: string[] = [];\n for (const theme of themes) {\n blocks.push(fontsToCss(theme.name, theme.fonts));\n blocks.push(colorScaleToCss(theme.name, \"light\", theme.light));\n blocks.push(colorScaleToCss(theme.name, \"dark\", theme.dark));\n }\n style.textContent = blocks.join(\"\\n\\n\");\n}\n\n/**\n * loadThemeFonts — idempotently inject `<link rel=\"stylesheet\">` for each\n * font URL declared by the theme.\n *\n * T4.2: the previous implementation kept a module-level `Set` to track\n * already-injected URLs. That singleton broke test isolation (state\n * leaked across renders) and silently skipped injection in micro-frontend\n * setups with multiple `<ThemeProvider>` mounts. Replaced with a DOM\n * check: we query `document.head` for an existing link with the same\n * `href` before appending a new one. The DOM is the single source of\n * truth; no shared state across instances.\n */\nfunction loadThemeFonts(theme: Theme): void {\n if (typeof document === \"undefined\") return;\n if (!theme.fontUrls) return;\n for (const url of theme.fontUrls) {\n // Re-audit NEW-001 (SSRF, LOW): defang dangerous protocols on\n // consumer-provided font URLs. Built-in themes use\n // fonts.googleapis.com/gstatic.com — safe. registerTheme accepts\n // arbitrary objects at runtime; a malicious theme could try to inject\n // javascript:/data:text/html via fontUrls. safeHref returns undefined\n // for dangerous protocols, which we skip silently.\n const safe = safeHref(url);\n if (!safe) continue;\n if (document.head.querySelector(`link[rel=\"stylesheet\"][href=\"${CSS.escape(safe)}\"]`)) {\n continue;\n }\n const link = document.createElement(\"link\");\n link.rel = \"stylesheet\";\n link.href = safe;\n document.head.appendChild(link);\n }\n}\n\ninterface ThemeProviderProps {\n children: ReactNode;\n /**\n * Theme to start with. Must match the `name` of an entry in `themes`.\n * Defaults to `\"violet-forge\"` for backward compat — if you don't pass\n * `violet-forge` in `themes`, set this prop explicitly.\n */\n defaultTheme?: string;\n /** Mode to start with. Defaults to `\"dark\"` (library is dark-first). */\n defaultMode?: ThemeMode;\n /**\n * Available themes. **Required**: ThemeProvider does not auto-include any\n * built-in theme since v0.1.0-next.0 — pass `builtinThemes` for all three\n * Violet Forge defaults, or your own array for a slimmer bundle.\n *\n * Migration: consumers previously calling `<ThemeProvider>` without this\n * prop now must pass `themes={builtinThemes}` (or use `<TheoUIProvider>`\n * which defaults to `builtinThemes` for you).\n */\n themes: Theme[];\n /**\n * Persist selection in localStorage under this key. Pass `null` to disable.\n * Default: \"theo-ui:theme\".\n */\n storageKey?: string | null;\n /**\n * Initial density. Drives `data-density` on `<html>` and the `--theo-control-h`\n * / `--theo-control-px` CSS vars consumed by form-control `md` variants.\n * Defaults to `\"comfortable\"` (36px controls — FAANG-tier modern density).\n * Plan: faang-density-tightening (D3).\n */\n defaultDensity?: Density;\n /**\n * Respect the consumer's OS `prefers-color-scheme` preference on initial\n * mount (D6 / T5.1). When `true` (default) and no theme-mode is stored in\n * `localStorage[storageKey]`, the provider reads\n * `matchMedia('(prefers-color-scheme: dark)')` and subscribes to changes.\n * User-driven `setMode()` overrides the system signal — subsequent OS\n * changes are ignored after the user fixes a preference.\n *\n * Pass `false` to force `defaultMode` regardless of the system preference.\n * EC-12: matchMedia listener is cleaned up on unmount.\n */\n respectSystemMode?: boolean;\n}\n\n/**\n * Storage failure diagnostic — dev-only one-line warn so engineers see\n * something when localStorage throws (Safari private mode, blocked\n * third-party cookies, sandboxed iframes). In production we stay silent;\n * runtime behavior is fail-safe (state still lives in memory).\n *\n * Per HIGH-006: silent catches diverge from the \"fail loud\" principle\n * declared in the global CLAUDE.md. We accept silence in prod because the\n * fallback is correct, but we surface a single warn per call site in dev.\n */\nfunction warnStorageFailure(scope: string, err: unknown): void {\n if (!isDev()) return;\n // biome-ignore lint/suspicious/noConsole: dev-only diagnostic for storage failures (HIGH-006)\n console.warn(`[@theokit/ui] theme storage failure (${scope}):`, err);\n}\n\n// T2.7: production-mode diagnostic for invalid themes that fall through\n// dev-time throw. Single console.warn so engineer sees something instead of\n// total silence; theme is kept in the array but per-value regex fallback\n// substitutes `transparent` for unsafe values downstream.\nfunction warnInvalidTheme(message: string): void {\n // biome-ignore lint/suspicious/noConsole: dev/prod diagnostic for invalid theme (T2.7/D5)\n console.warn(message);\n}\n\n/**\n * ThemeProvider — central registry + runtime switcher for Theo themes.\n *\n * Behavior:\n * 1. On mount, injects a `<style id=\"theo-ui-theme-vars\">` element with\n * one CSS block per theme (`[data-theme=\"<name>\"] { --token: ... }`).\n * 2. Sets `data-theme` and `data-mode` on `<html>` so any element nested\n * below inherits the right tokens (the Tailwind config consumes them).\n * 3. Lazy-loads theme font URLs by injecting `<link rel=\"stylesheet\">`.\n * 4. Optionally persists choice in localStorage.\n */\nfunction ThemeProvider({\n children,\n defaultTheme = \"violet-forge\",\n defaultMode = \"dark\",\n themes: themesProp,\n storageKey = \"theo-ui:theme\",\n defaultDensity = \"comfortable\",\n respectSystemMode = true,\n}: ThemeProviderProps): JSX.Element {\n // Themes prop is required since v0.1.0-next.0 — see migration note in\n // the JSDoc on ThemeProviderProps. Pass `builtinThemes` for the legacy\n // default behavior (violet-forge + classic-paper + aurora-terminal), or\n // an array of your own. Empty array is rejected: ThemeProvider has no\n // valid state without at least one registered theme.\n if (!themesProp || themesProp.length === 0) {\n throw new Error(\n \"<ThemeProvider> requires the `themes` prop with at least one Theme. \" +\n \"Pass `themes={builtinThemes}` for the Violet Forge defaults (importable \" +\n \"via the package barrel), or use <TheoUIProvider> which sets this for you.\",\n );\n }\n\n // T3.2 (SEC-001): eager validation. Calling validatedColor/FontFamily/\n // ThemeName here ensures CSS-injection attempts throw at construction\n // time rather than inside the deferred useEffect that injects the\n // <style>. Production-mode fallbacks keep the app rendering even if a\n // theme has bad values.\n //\n // Re-audit NEW-3: wrapped in useMemo so the validation cost (O(themes *\n // tokens), ~60 ops per built-in theme) only runs when themesProp's\n // reference changes — not on every parent re-render. Consumers passing\n // inline array literals (`themes={[violetForge, classicPaper]}`) would\n // otherwise pay this on every parent update.\n const mergedThemes = useMemo<Theme[]>(() => {\n // T2.7 (D5): valibot shape validation runs FIRST — catches missing keys,\n // wrong types, malformed font URLs. Existing per-value regex validation\n // below stays as the second defense layer against CSS injection.\n for (const t of themesProp) {\n const result = validateTheme(t);\n if (!result.success) {\n const message = formatThemeIssues(\n (t as { name?: string }).name ?? \"(unnamed)\",\n result.issues ?? [],\n );\n if (IS_DEV) throw new Error(message);\n warnInvalidTheme(message);\n // In production we keep the theme array intact; the per-value regex\n // fallbacks below will substitute `transparent` for any value that\n // still fails the second layer, so the app continues rendering.\n }\n }\n for (const t of themesProp) {\n validatedThemeName(t.name);\n validatedFontFamily(\"display\", t.fonts.display);\n validatedFontFamily(\"body\", t.fonts.body);\n validatedFontFamily(\"mono\", t.fonts.mono);\n for (const [token, value] of Object.entries(t.light)) {\n validatedColor(token, value);\n }\n for (const [token, value] of Object.entries(t.dark)) {\n validatedColor(token, value);\n }\n }\n // Dedup by theme name; last writer wins (allows registerTheme override).\n const map = new Map<string, Theme>();\n for (const t of themesProp) map.set(t.name, t);\n return Array.from(map.values());\n }, [themesProp]);\n\n const [themes, setThemes] = useState<Theme[]>(mergedThemes);\n\n // Re-sync state when the `themes` prop changes between renders. Avoids the\n // common pitfall where the user passes a different array later and the\n // initial-state-only seed silently ignores the change.\n useEffect(() => {\n setThemes(mergedThemes);\n }, [mergedThemes]);\n\n // SSR-safe initialization (0.6.3-next.0 hydration-mismatch fix).\n //\n // Previously: `useState(() => localStorage.getItem(…))`. The initializer\n // ran on BOTH server (no `window`, returned default) AND client at\n // hydration time (with `window`, returned the stored value). The two\n // results disagreed → React threw a hydration error on every page load\n // for any user who had previously changed themes, and re-rendered the\n // entire tree from scratch — defeating SSR.\n //\n // Fix: initialize with the SSR default ALWAYS. Promote to the stored\n // value in a post-mount `useEffect` below. The 1-frame visual flicker\n // is mitigated by the optional `<ThemeScript>` component, which sets\n // `data-theme` / `data-mode` / `data-density` on `<html>` before React\n // hydrates — see `theme-script.tsx`.\n //\n // The `hydratedRef` flag below guards the persist effect so that\n // first-mount writes (with the SSR default values) don't clobber the\n // user's stored preference in the brief window between mount and the\n // post-mount hydration effect.\n const [themeName, setThemeName] = useState<string>(defaultTheme);\n const [mode, setModeState] = useState<ThemeMode>(defaultMode);\n const [density, setDensityState] = useState<Density>(defaultDensity);\n\n // T5.1 / D6: track whether the user has explicitly fixed the mode via\n // setMode / toggleMode. Once true, OS prefers-color-scheme changes do\n // NOT override the user's choice.\n const userOverrodeModeRef = useRef(false);\n\n // First-run guard for the persist effect below. On initial mount the\n // state is the SSR-safe default; we MUST NOT clobber the user's stored\n // preference with that default before the post-mount hydration effect\n // can promote it. After the first persist-effect call returns early,\n // every subsequent change (post-hydration setState OR user-driven)\n // persists normally.\n const skipFirstPersistRef = useRef(true);\n\n // Post-mount hydration: read localStorage and promote stored values to\n // state. Runs ONCE on mount. If `storageKey` is null or no value is\n // stored, this is a no-op — state stays at the SSR defaults.\n useEffect(() => {\n if (typeof window === \"undefined\" || !storageKey) return;\n try {\n const storedName = window.localStorage.getItem(`${storageKey}:name`);\n const storedMode = window.localStorage.getItem(`${storageKey}:mode`);\n const storedDensity = window.localStorage.getItem(`${storageKey}:density`);\n if (storedName) setThemeName(storedName);\n if (storedMode === \"dark\" || storedMode === \"light\") {\n setModeState(storedMode);\n // Stored value implies user fixed a preference at some point.\n userOverrodeModeRef.current = true;\n }\n if (\n storedDensity === \"compact\" ||\n storedDensity === \"comfortable\" ||\n storedDensity === \"spacious\"\n ) {\n setDensityState(storedDensity);\n }\n } catch (err) {\n warnStorageFailure(\"read theme + mode + density\", err);\n }\n }, [storageKey]);\n\n // T5.1 / D6 / EC-12: subscribe to OS prefers-color-scheme when\n // respectSystemMode is true. On mount, if user has NOT overridden the\n // mode (no stored preference), align with the OS signal. Re-aligns on\n // OS changes UNLESS user explicitly fixed a preference. Cleanup on\n // unmount via removeEventListener (EC-12) to avoid listener leaks in\n // micro-frontend scenarios.\n useEffect(() => {\n if (!respectSystemMode) return;\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return;\n const mql = window.matchMedia(\"(prefers-color-scheme: dark)\");\n // Align on first run if user hasn't overridden.\n if (!userOverrodeModeRef.current) {\n setModeState(mql.matches ? \"dark\" : \"light\");\n }\n const onChange = (event: MediaQueryListEvent): void => {\n if (!userOverrodeModeRef.current) {\n setModeState(event.matches ? \"dark\" : \"light\");\n }\n };\n mql.addEventListener(\"change\", onChange);\n return () => {\n mql.removeEventListener(\"change\", onChange);\n };\n }, [respectSystemMode]);\n\n // Inject CSS vars whenever the themes list changes.\n useEffect(() => {\n injectThemeCss(themes);\n }, [themes]);\n\n // Apply data-theme + data-mode + data-density to <html>, load fonts,\n // inject density CSS vars.\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const active = themes.find((t) => t.name === themeName) ?? themes[0];\n if (!active) return;\n document.documentElement.setAttribute(\"data-theme\", active.name);\n document.documentElement.setAttribute(\"data-mode\", mode);\n document.documentElement.setAttribute(\"data-density\", density);\n document.documentElement.classList.toggle(\"dark\", mode === \"dark\");\n loadThemeFonts(active);\n injectDensityCss();\n }, [themeName, mode, density, themes]);\n\n // Persist on change.\n //\n // The first run is SKIPPED via `skipFirstPersistRef`: state at mount\n // is the SSR-safe default. If we wrote it to storage immediately,\n // we'd clobber the user's stored preference between mount and the\n // post-mount hydration effect that promotes the stored value. After\n // the first call, every subsequent run persists — whether the change\n // came from the hydration effect (no-op write back of the stored\n // value) or a user-driven `setTheme` / `toggleMode` / `setDensity`.\n useEffect(() => {\n if (skipFirstPersistRef.current) {\n skipFirstPersistRef.current = false;\n return;\n }\n if (typeof window === \"undefined\" || !storageKey) return;\n try {\n window.localStorage.setItem(`${storageKey}:name`, themeName);\n window.localStorage.setItem(`${storageKey}:mode`, mode);\n window.localStorage.setItem(`${storageKey}:density`, density);\n } catch (err) {\n // Storage may fail in private mode; behavior remains correct (state\n // lives in memory). Per HIGH-006 we surface a one-time dev warning so\n // the engineer sees something instead of complete silence.\n warnStorageFailure(\"persist theme + mode + density\", err);\n }\n }, [themeName, mode, density, storageKey]);\n\n const setTheme = useCallback((name: string) => setThemeName(name), []);\n const setMode = useCallback((next: ThemeMode) => {\n // T5.1 / D6: fix the user preference; subsequent system changes no longer override.\n userOverrodeModeRef.current = true;\n setModeState(next);\n }, []);\n const toggleMode = useCallback(() => {\n userOverrodeModeRef.current = true;\n setModeState((cur) => (cur === \"light\" ? \"dark\" : \"light\"));\n }, []);\n const setDensity = useCallback((next: Density) => setDensityState(next), []);\n const registerTheme = useCallback((theme: Theme) => {\n setThemes((cur) => {\n const idx = cur.findIndex((t) => t.name === theme.name);\n if (idx >= 0) {\n const next = cur.slice();\n next[idx] = theme;\n return next;\n }\n return [...cur, theme];\n });\n }, []);\n\n // themes[0] is guaranteed non-undefined by the constructor-time check\n // above (themesProp is non-empty); the non-null assert encodes that\n // invariant for TypeScript, which can't trace it through useState.\n // biome-ignore lint/style/noNonNullAssertion: T2.5 runtime invariant — themesProp non-empty validated at top of function\n const active = themes.find((t) => t.name === themeName) ?? themes[0]!;\n\n const value = useMemo<ThemeContextValue>(\n () => ({\n theme: active,\n mode,\n themes,\n setTheme,\n setMode,\n toggleMode,\n registerTheme,\n }),\n [active, mode, themes, setTheme, setMode, toggleMode, registerTheme],\n );\n\n const densityValue = useMemo(() => ({ density, setDensity }), [density, setDensity]);\n\n return (\n <ThemeContext.Provider value={value}>\n <DensityContext.Provider value={densityValue}>{children}</DensityContext.Provider>\n </ThemeContext.Provider>\n );\n}\n\n/**\n * useTheme — access theme state from any component inside <ThemeProvider>.\n * Throws if used outside the provider — fail-fast.\n */\nfunction useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) {\n throw new Error(\"useTheme must be used inside <ThemeProvider>.\");\n }\n return ctx;\n}\n\nexport { ThemeProvider, useTheme };\n"
45
46
  },
46
47
  {
47
48
  "path": "themes/theme-switcher.tsx",
@@ -53,7 +54,7 @@
53
54
  "path": "themes/index.ts",
54
55
  "type": "registry:lib",
55
56
  "target": "themes/index.ts",
56
- "content": "export type { ColorScale, Theme, ThemeFonts, ThemeMode } from \"@/themes/types\";\nexport { ThemeProvider, useTheme } from \"@/themes/theme-provider\";\nexport { ThemeScript } from \"@/themes/theme-script\";\nexport { ThemeSwitcher } from \"@/themes/theme-switcher\";\nexport { violetForge } from \"@/themes/violet-forge\";\nexport { classicPaper } from \"@/themes/classic-paper\";\nexport { auroraTerminal } from \"@/themes/aurora-terminal\";\nexport { defineTheme, type DefineThemeInput } from \"./define\";\nexport { hex, rgb } from \"./color\";\nexport { useDensity, type Density, type DensityContextValue } from \"./density\";\n// Seven new themes — RFC 0007, plan: seven-themes (2026-05-22).\n// Trademark-safe slugs (D1.1) — `*-style` / `*-mono` / `*-glass` suffixes,\n// description includes \"Inspired by, not affiliated with [X]\".\nexport { vercelMono } from \"./vercel-mono\";\nexport { githubDark } from \"./github-dark\";\nexport { dracula } from \"./dracula\";\nexport { oneDark } from \"./one-dark\";\nexport { anthropicStyle } from \"./anthropic-style\";\nexport { openaiStyle } from \"./openai-style\";\nexport { linearGlass } from \"./linear-glass\";\n\nimport { anthropicStyle } from \"./anthropic-style\";\nimport { auroraTerminal } from \"@/themes/aurora-terminal\";\nimport { classicPaper } from \"@/themes/classic-paper\";\nimport { dracula } from \"./dracula\";\nimport { githubDark } from \"./github-dark\";\nimport { linearGlass } from \"./linear-glass\";\nimport { oneDark } from \"./one-dark\";\nimport { openaiStyle } from \"./openai-style\";\nimport { vercelMono } from \"./vercel-mono\";\nimport { violetForge } from \"@/themes/violet-forge\";\n\n/**\n * All themes bundled with Theo UI. Pass to `<ThemeProvider themes={builtinThemes}>`\n * if you want all 10 available out of the box.\n *\n * EC-4 note: passing the full 10-entry list triggers ~60 KB CSS injection in\n * `<style id=\"theo-ui-theme-vars\">`. For apps focused on 1-2 themes, prefer\n * `themes={[violetForge, dracula]}` to keep the payload at ~12 KB.\n */\nexport const builtinThemes = [\n violetForge,\n classicPaper,\n auroraTerminal,\n vercelMono,\n githubDark,\n dracula,\n oneDark,\n anthropicStyle,\n openaiStyle,\n linearGlass,\n];\n"
57
+ "content": "export type { ColorScale, Theme, ThemeFonts, ThemeMode } from \"@/themes/types\";\nexport { ThemeProvider, useTheme } from \"@/themes/theme-provider\";\nexport { ThemeScript } from \"@/themes/theme-script\";\nexport { ThemeSwitcher } from \"@/themes/theme-switcher\";\nexport { violetForge } from \"@/themes/violet-forge\";\nexport { classicPaper } from \"@/themes/classic-paper\";\nexport { auroraTerminal } from \"@/themes/aurora-terminal\";\nexport { defineTheme, type DefineThemeInput } from \"./define\";\nexport { hex, hexToHsl, rgb, rgbToHslLegacy } from \"./color\";\nexport { useDensity, type Density, type DensityContextValue } from \"./density\";\n// Seven new themes — RFC 0007, plan: seven-themes (2026-05-22).\n// Trademark-safe slugs (D1.1) — `*-style` / `*-mono` / `*-glass` suffixes,\n// description includes \"Inspired by, not affiliated with [X]\".\nexport { vercelMono } from \"./vercel-mono\";\nexport { githubDark } from \"./github-dark\";\nexport { dracula } from \"./dracula\";\nexport { oneDark } from \"./one-dark\";\nexport { anthropicStyle } from \"./anthropic-style\";\nexport { openaiStyle } from \"./openai-style\";\nexport { linearGlass } from \"./linear-glass\";\n\nimport { anthropicStyle } from \"./anthropic-style\";\nimport { auroraTerminal } from \"@/themes/aurora-terminal\";\nimport { classicPaper } from \"@/themes/classic-paper\";\nimport { dracula } from \"./dracula\";\nimport { githubDark } from \"./github-dark\";\nimport { linearGlass } from \"./linear-glass\";\nimport { oneDark } from \"./one-dark\";\nimport { openaiStyle } from \"./openai-style\";\nimport { vercelMono } from \"./vercel-mono\";\nimport { violetForge } from \"@/themes/violet-forge\";\n\n/**\n * All themes bundled with Theo UI. Pass to `<ThemeProvider themes={builtinThemes}>`\n * if you want all 10 available out of the box.\n *\n * EC-4 note: passing the full 10-entry list triggers ~60 KB CSS injection in\n * `<style id=\"theo-ui-theme-vars\">`. For apps focused on 1-2 themes, prefer\n * `themes={[violetForge, dracula]}` to keep the payload at ~12 KB.\n */\nexport const builtinThemes = [\n violetForge,\n classicPaper,\n auroraTerminal,\n vercelMono,\n githubDark,\n dracula,\n oneDark,\n anthropicStyle,\n openaiStyle,\n linearGlass,\n];\n"
57
58
  }
58
59
  ]
59
60
  }
@@ -14,7 +14,7 @@
14
14
  "path": "theo-ui-provider.tsx",
15
15
  "type": "registry:lib",
16
16
  "target": "theo-ui-provider.tsx",
17
- "content": "\"use client\";\n\nimport type { ComponentProps, JSX, ReactNode } from \"react\";\nimport { Toaster } from \"@/components/ui/toaster\";\nimport { builtinThemes } from \"@/themes/index\";\nimport { ThemeProvider } from \"@/themes/theme-provider\";\n\n/**\n * TheoUIProvider — primary entry point composing `<ThemeProvider>` and\n * `<Toaster>` in the correct order with sensible defaults.\n *\n * Recommended for consumer apps to avoid hand-wiring the provider stack.\n * Equivalent to:\n *\n * <ThemeProvider themes={builtinThemes} {...theme}>\n * <Toaster {...toaster}>{children}</Toaster>\n * </ThemeProvider>\n *\n * Notes:\n * - Since v0.1.0-next.0, `<ThemeProvider>` requires the `themes` prop\n * (T2.5 decoupling). `<TheoUIProvider>` defaults it to `builtinThemes`\n * to preserve \"works out of the box\" DX. Pass `theme.themes` to override.\n * - The `\"use client\"` directive is required for Next.js App Router (RSC)\n * because `<ThemeProvider>` uses React state and effects internally.\n * - `<Tooltip>` provider stays per-instance (Radix recommendation) — this\n * wrapper does NOT introduce a global tooltip context.\n * - `<ThemeProvider>` and `<Toaster>` remain independently exported for\n * consumers who want bespoke control.\n *\n * SSR:\n * - Place `<ThemeScript>` in `<head>` to avoid FOUC + hydration mismatch.\n * - Wrap `<html lang=\"en\" suppressHydrationWarning>`.\n *\n * @example\n * import { TheoUIProvider } from \"@usetheo/ui\";\n *\n * export default function App({ children }) {\n * return <TheoUIProvider>{children}</TheoUIProvider>;\n * }\n */\nexport interface TheoUIProviderProps {\n children: ReactNode;\n /** Pass-through props for the inner `<ThemeProvider>`. Optional. */\n theme?: Omit<ComponentProps<typeof ThemeProvider>, \"children\" | \"themes\"> & {\n /** Override the default theme set (defaults to `builtinThemes`). */\n themes?: ComponentProps<typeof ThemeProvider>[\"themes\"];\n };\n /** Pass-through props for the inner `<Toaster>`. */\n toaster?: Omit<ComponentProps<typeof Toaster>, \"children\">;\n}\n\nexport function TheoUIProvider({ children, theme, toaster }: TheoUIProviderProps): JSX.Element {\n const { themes, ...restTheme } = theme ?? {};\n return (\n <ThemeProvider themes={themes ?? builtinThemes} {...restTheme}>\n <Toaster {...toaster}>{children}</Toaster>\n </ThemeProvider>\n );\n}\n\nTheoUIProvider.displayName = \"TheoUIProvider\";\n"
17
+ "content": "\"use client\";\n\nimport type { ComponentProps, JSX, ReactNode } from \"react\";\nimport { Toaster } from \"@/components/ui/toaster\";\nimport { builtinThemes } from \"@/themes/index\";\nimport { ThemeProvider } from \"@/themes/theme-provider\";\n\n/**\n * TheoUIProvider — primary entry point composing `<ThemeProvider>` and\n * `<Toaster>` in the correct order with sensible defaults.\n *\n * Recommended for consumer apps to avoid hand-wiring the provider stack.\n * Equivalent to:\n *\n * <ThemeProvider themes={builtinThemes} {...theme}>\n * <Toaster {...toaster}>{children}</Toaster>\n * </ThemeProvider>\n *\n * Notes:\n * - Since v0.1.0-next.0, `<ThemeProvider>` requires the `themes` prop\n * (T2.5 decoupling). `<TheoUIProvider>` defaults it to `builtinThemes`\n * to preserve \"works out of the box\" DX. Pass `theme.themes` to override.\n * - The `\"use client\"` directive is required for Next.js App Router (RSC)\n * because `<ThemeProvider>` uses React state and effects internally.\n * - `<Tooltip>` provider stays per-instance (Radix recommendation) — this\n * wrapper does NOT introduce a global tooltip context.\n * - `<ThemeProvider>` and `<Toaster>` remain independently exported for\n * consumers who want bespoke control.\n *\n * SSR:\n * - Place `<ThemeScript>` in `<head>` to avoid FOUC + hydration mismatch.\n * - Wrap `<html lang=\"en\" suppressHydrationWarning>`.\n *\n * @example\n * import { TheoUIProvider } from \"@theokit/ui\";\n *\n * export default function App({ children }) {\n * return <TheoUIProvider>{children}</TheoUIProvider>;\n * }\n */\nexport interface TheoUIProviderProps {\n children: ReactNode;\n /** Pass-through props for the inner `<ThemeProvider>`. Optional. */\n theme?: Omit<ComponentProps<typeof ThemeProvider>, \"children\" | \"themes\"> & {\n /** Override the default theme set (defaults to `builtinThemes`). */\n themes?: ComponentProps<typeof ThemeProvider>[\"themes\"];\n };\n /** Pass-through props for the inner `<Toaster>`. */\n toaster?: Omit<ComponentProps<typeof Toaster>, \"children\">;\n}\n\nexport function TheoUIProvider({ children, theme, toaster }: TheoUIProviderProps): JSX.Element {\n const { themes, ...restTheme } = theme ?? {};\n return (\n <ThemeProvider themes={themes ?? builtinThemes} {...restTheme}>\n <Toaster {...toaster}>{children}</Toaster>\n </ThemeProvider>\n );\n}\n\nTheoUIProvider.displayName = \"TheoUIProvider\";\n"
18
18
  }
19
19
  ]
20
20
  }
@@ -7,6 +7,7 @@
7
7
  "dependencies": [],
8
8
  "registryDependencies": [
9
9
  "https://usetheodev.github.io/theo-ui/r/cn.json",
10
+ "https://usetheodev.github.io/theo-ui/r/env.json",
10
11
  "https://usetheodev.github.io/theo-ui/r/tailwind-preset.json"
11
12
  ],
12
13
  "files": [
@@ -14,7 +15,7 @@
14
15
  "path": "components/primitives/timestamp/timestamp.tsx",
15
16
  "type": "registry:ui",
16
17
  "target": "components/ui/timestamp.tsx",
17
- "content": "import { forwardRef, useEffect, useState } from \"react\";\nimport type { TimeHTMLAttributes } from \"react\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * Timestamp — accessible relative/absolute time primitive.\n *\n * Renders a semantic `<time datetime>` element. Format modes:\n * - `relative` (default) — \"just now\", \"5 minutes ago\", \"2 hours ago\",\n * \"Dec 5\" (>7d), \"Dec 5, 2024\" (different year)\n * - `absolute` — full localized date+time\n * - `both` — \"Dec 5, 2026 (2 hours ago)\"\n *\n * Uses zero-dep `Intl.RelativeTimeFormat`. The tooltip on hover is the\n * HTML `title` attribute (not the `<Tooltip>` component) — keeps this\n * file a true primitive without sibling-primitive imports.\n *\n * Auto-refreshes via `setInterval` (default 60_000ms); pass\n * `refreshInterval={0}` to disable. `aria-label` always carries the\n * absolute time so screen readers read \"May 23, 2026 14:32 — posted\n * 2 hours ago\".\n *\n * @param value Source date — ISO string, Date object, or **Unix ms**\n * (NOT seconds). Passing seconds renders ~1970.\n */\nexport interface TimestampProps extends Omit<TimeHTMLAttributes<HTMLTimeElement>, \"children\"> {\n value: string | Date | number;\n format?: \"relative\" | \"absolute\" | \"both\";\n /** Auto-refresh interval when format=relative. Default 60000. 0 disables. */\n refreshInterval?: number;\n /** Locale for absolute formatting + Intl.RelativeTimeFormat. Default browser locale. */\n locale?: string;\n /** When true, omit the `title` tooltip. */\n noTooltip?: boolean;\n}\n\nfunction toDate(value: string | Date | number): Date | null {\n const d = value instanceof Date ? value : new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n}\n\nconst SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;\nconst UNITS: Array<{ unit: Intl.RelativeTimeFormatUnit; ms: number }> = [\n { unit: \"year\", ms: 365 * 24 * 60 * 60 * 1000 },\n { unit: \"month\", ms: 30 * 24 * 60 * 60 * 1000 },\n { unit: \"day\", ms: 24 * 60 * 60 * 1000 },\n { unit: \"hour\", ms: 60 * 60 * 1000 },\n { unit: \"minute\", ms: 60 * 1000 },\n];\n\nfunction safeRelativeFormatter(locale: string | undefined): Intl.RelativeTimeFormat | null {\n try {\n return new Intl.RelativeTimeFormat(locale, { numeric: \"auto\" });\n } catch {\n // EC-8: invalid locale tag — fall back to default locale.\n if (process.env.NODE_ENV !== \"production\") {\n // biome-ignore lint/suspicious/noConsole: dev-only diagnostic.\n console.warn(`<Timestamp locale=\"${locale}\">: invalid locale tag, falling back to default.`);\n }\n try {\n return new Intl.RelativeTimeFormat(undefined, { numeric: \"auto\" });\n } catch {\n return null;\n }\n }\n}\n\nfunction safeAbsoluteFormat(date: Date, locale: string | undefined, withYear: boolean): string {\n try {\n return date.toLocaleDateString(locale, {\n month: \"short\",\n day: \"numeric\",\n ...(withYear ? { year: \"numeric\" } : {}),\n });\n } catch {\n return date.toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n ...(withYear ? { year: \"numeric\" } : {}),\n });\n }\n}\n\nfunction formatRelative(date: Date, now: Date, locale: string | undefined): string {\n const diffMs = date.getTime() - now.getTime();\n const absMs = Math.abs(diffMs);\n\n if (absMs < 60_000) return \"just now\";\n\n if (diffMs < 0 && absMs > SEVEN_DAYS_MS) {\n const sameYear = date.getFullYear() === now.getFullYear();\n return safeAbsoluteFormat(date, locale, !sameYear);\n }\n\n const rtf = safeRelativeFormatter(locale);\n if (rtf === null) {\n return safeAbsoluteFormat(date, locale, date.getFullYear() !== now.getFullYear());\n }\n\n for (const { unit, ms } of UNITS) {\n if (absMs >= ms) {\n return rtf.format(Math.round(diffMs / ms), unit);\n }\n }\n return \"just now\";\n}\n\nfunction formatAbsolute(date: Date, locale: string | undefined): string {\n try {\n return date.toLocaleString(locale, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n } catch {\n return date.toLocaleString(undefined, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n }\n}\n\nconst Timestamp = forwardRef<HTMLTimeElement, TimestampProps>(\n (\n {\n className,\n value,\n format = \"relative\",\n refreshInterval = 60_000,\n locale,\n noTooltip,\n ...props\n },\n ref,\n ) => {\n const date = toDate(value);\n const [now, setNow] = useState<Date>(() => new Date());\n\n useEffect(() => {\n if (format !== \"relative\" || refreshInterval === 0 || date === null) return;\n const id = setInterval(() => setNow(new Date()), refreshInterval);\n return () => clearInterval(id);\n }, [format, refreshInterval, date]);\n\n if (date === null) {\n return <time ref={ref} className={cn(className)} suppressHydrationWarning {...props} />;\n }\n\n const iso = date.toISOString();\n const absolute = formatAbsolute(date, locale);\n const relative = formatRelative(date, now, locale);\n const visibleText =\n format === \"absolute\" ? absolute : format === \"both\" ? `${absolute} (${relative})` : relative;\n\n return (\n <time\n ref={ref}\n dateTime={iso}\n title={noTooltip ? undefined : absolute}\n aria-label={absolute}\n suppressHydrationWarning\n className={cn(className)}\n {...props}\n >\n {visibleText}\n </time>\n );\n },\n);\nTimestamp.displayName = \"Timestamp\";\n\nexport { Timestamp };\n"
18
+ "content": "\"use client\";\n\nimport { forwardRef, useEffect, useState } from \"react\";\nimport type { TimeHTMLAttributes } from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport { isDev } from \"@/lib/env\";\n\n/**\n * Timestamp — accessible relative/absolute time primitive.\n *\n * Renders a semantic `<time datetime>` element. Format modes:\n * - `relative` (default) — \"just now\", \"5 minutes ago\", \"2 hours ago\",\n * \"Dec 5\" (>7d), \"Dec 5, 2024\" (different year)\n * - `absolute` — full localized date+time\n * - `both` — \"Dec 5, 2026 (2 hours ago)\"\n *\n * Uses zero-dep `Intl.RelativeTimeFormat`. The tooltip on hover is the\n * HTML `title` attribute (not the `<Tooltip>` component) — keeps this\n * file a true primitive without sibling-primitive imports.\n *\n * Auto-refreshes via `setInterval` (default 60_000ms); pass\n * `refreshInterval={0}` to disable. `aria-label` always carries the\n * absolute time so screen readers read \"May 23, 2026 14:32 — posted\n * 2 hours ago\".\n *\n * @param value Source date — ISO string, Date object, or **Unix ms**\n * (NOT seconds). Passing seconds renders ~1970.\n */\nexport interface TimestampProps extends Omit<TimeHTMLAttributes<HTMLTimeElement>, \"children\"> {\n value: string | Date | number;\n format?: \"relative\" | \"absolute\" | \"both\";\n /** Auto-refresh interval when format=relative. Default 60000. 0 disables. */\n refreshInterval?: number;\n /** Locale for absolute formatting + Intl.RelativeTimeFormat. Default browser locale. */\n locale?: string;\n /** When true, omit the `title` tooltip. */\n noTooltip?: boolean;\n}\n\nfunction toDate(value: string | Date | number): Date | null {\n const d = value instanceof Date ? value : new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n}\n\nconst SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;\nconst UNITS: Array<{ unit: Intl.RelativeTimeFormatUnit; ms: number }> = [\n { unit: \"year\", ms: 365 * 24 * 60 * 60 * 1000 },\n { unit: \"month\", ms: 30 * 24 * 60 * 60 * 1000 },\n { unit: \"day\", ms: 24 * 60 * 60 * 1000 },\n { unit: \"hour\", ms: 60 * 60 * 1000 },\n { unit: \"minute\", ms: 60 * 1000 },\n];\n\nfunction safeRelativeFormatter(locale: string | undefined): Intl.RelativeTimeFormat | null {\n try {\n return new Intl.RelativeTimeFormat(locale, { numeric: \"auto\" });\n } catch {\n // EC-8: invalid locale tag — fall back to default locale.\n if (isDev()) {\n // biome-ignore lint/suspicious/noConsole: dev-only diagnostic.\n console.warn(`<Timestamp locale=\"${locale}\">: invalid locale tag, falling back to default.`);\n }\n try {\n return new Intl.RelativeTimeFormat(undefined, { numeric: \"auto\" });\n } catch {\n return null;\n }\n }\n}\n\nfunction safeAbsoluteFormat(date: Date, locale: string | undefined, withYear: boolean): string {\n try {\n return date.toLocaleDateString(locale, {\n month: \"short\",\n day: \"numeric\",\n ...(withYear ? { year: \"numeric\" } : {}),\n });\n } catch {\n return date.toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n ...(withYear ? { year: \"numeric\" } : {}),\n });\n }\n}\n\nfunction formatRelative(date: Date, now: Date, locale: string | undefined): string {\n const diffMs = date.getTime() - now.getTime();\n const absMs = Math.abs(diffMs);\n\n if (absMs < 60_000) return \"just now\";\n\n if (diffMs < 0 && absMs > SEVEN_DAYS_MS) {\n const sameYear = date.getFullYear() === now.getFullYear();\n return safeAbsoluteFormat(date, locale, !sameYear);\n }\n\n const rtf = safeRelativeFormatter(locale);\n if (rtf === null) {\n return safeAbsoluteFormat(date, locale, date.getFullYear() !== now.getFullYear());\n }\n\n for (const { unit, ms } of UNITS) {\n if (absMs >= ms) {\n return rtf.format(Math.round(diffMs / ms), unit);\n }\n }\n return \"just now\";\n}\n\nfunction formatAbsolute(date: Date, locale: string | undefined): string {\n try {\n return date.toLocaleString(locale, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n } catch {\n return date.toLocaleString(undefined, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n }\n}\n\nconst Timestamp = forwardRef<HTMLTimeElement, TimestampProps>(\n (\n {\n className,\n value,\n format = \"relative\",\n refreshInterval = 60_000,\n locale,\n noTooltip,\n ...props\n },\n ref,\n ) => {\n const date = toDate(value);\n const [now, setNow] = useState<Date>(() => new Date());\n\n useEffect(() => {\n if (format !== \"relative\" || refreshInterval === 0 || date === null) return;\n const id = setInterval(() => setNow(new Date()), refreshInterval);\n return () => clearInterval(id);\n }, [format, refreshInterval, date]);\n\n if (date === null) {\n return (\n <time\n data-slot=\"timestamp\"\n ref={ref}\n className={cn(className)}\n suppressHydrationWarning\n {...props}\n />\n );\n }\n\n const iso = date.toISOString();\n const absolute = formatAbsolute(date, locale);\n const relative = formatRelative(date, now, locale);\n const visibleText =\n format === \"absolute\" ? absolute : format === \"both\" ? `${absolute} (${relative})` : relative;\n\n return (\n <time\n data-slot=\"timestamp\"\n ref={ref}\n dateTime={iso}\n title={noTooltip ? undefined : absolute}\n aria-label={absolute}\n suppressHydrationWarning\n className={cn(className)}\n {...props}\n >\n {visibleText}\n </time>\n );\n },\n);\nTimestamp.displayName = \"Timestamp\";\n\nexport { Timestamp };\n"
18
19
  }
19
20
  ]
20
21
  }
@@ -18,13 +18,13 @@
18
18
  "path": "components/primitives/toast/toast.tsx",
19
19
  "type": "registry:ui",
20
20
  "target": "components/ui/toast.tsx",
21
- "content": "import * as ToastPrimitive from \"@radix-ui/react-toast\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport { AlertCircle, CheckCircle2, Info, TriangleAlert, X } from \"lucide-react\";\nimport { forwardRef } from \"react\";\nimport type { ComponentPropsWithoutRef, ElementRef, ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * Toast — transient notification built on Radix Toast.\n *\n * Composition: app mounts <Toaster /> once. Components call `toast(...)` via\n * the `useToast()` hook (see toaster.tsx). The look is theme-aware: status\n * icon coloured by --primary/--success/--warning/--destructive; the body\n * card uses --popover surface.\n *\n * Variants:\n * - default (neutral)\n * - info (primary tint)\n * - success (deploy ok, action completed)\n * - warning (queued, permission requested)\n * - destructive (failed, fatal)\n */\n\nconst toastVariants = cva(\n [\n \"group pointer-events-auto relative flex w-full items-start gap-3 overflow-hidden rounded-lg border pr-10 shadow-md\",\n \"data-[state=open]:slide-in-from-top-full data-[state=open]:fade-in-0 data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=closed]:animate-out\",\n \"data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)]\",\n \"data-[swipe=end]:animate-out data-[swipe=cancel]:transition-[transform_var(--duration-base)] data-[swipe=move]:transition-none\",\n ],\n {\n variants: {\n variant: {\n default: \"border-border/40 bg-popover text-popover-foreground\",\n info: \"border-primary/40 bg-popover text-popover-foreground\",\n success: \"border-success/40 bg-popover text-popover-foreground\",\n warning: \"border-warning/40 bg-popover text-popover-foreground\",\n destructive: \"border-destructive/50 bg-popover text-popover-foreground\",\n },\n size: {\n sm: \"p-3 text-body-sm\",\n md: \"p-4 text-body-md\",\n lg: \"p-5 text-body-lg\",\n },\n },\n defaultVariants: { variant: \"default\", size: \"md\" },\n },\n);\n\nconst iconForVariant: Record<NonNullable<ToastVariant>, ReactNode> = {\n default: <Info className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />,\n info: <Info className=\"size-4 shrink-0 text-primary\" aria-hidden=\"true\" />,\n success: <CheckCircle2 className=\"size-4 shrink-0 text-success\" aria-hidden=\"true\" />,\n warning: <TriangleAlert className=\"size-4 shrink-0 text-warning\" aria-hidden=\"true\" />,\n destructive: <AlertCircle className=\"size-4 shrink-0 text-destructive\" aria-hidden=\"true\" />,\n};\n\ntype ToastVariant = NonNullable<VariantProps<typeof toastVariants>[\"variant\"]>;\n\ninterface ToastProps\n extends ComponentPropsWithoutRef<typeof ToastPrimitive.Root>,\n VariantProps<typeof toastVariants> {}\n\nconst ToastRoot = forwardRef<ElementRef<typeof ToastPrimitive.Root>, ToastProps>(\n ({ className, variant = \"default\", size, children, ...props }, ref) => (\n <ToastPrimitive.Root\n ref={ref}\n className={cn(toastVariants({ variant, size }), className)}\n {...props}\n >\n <span aria-hidden=\"true\">{iconForVariant[variant as ToastVariant]}</span>\n <div className=\"min-w-0 flex-1\">{children}</div>\n </ToastPrimitive.Root>\n ),\n);\nToastRoot.displayName = \"Toast\";\n\nconst ToastTitle = forwardRef<\n ElementRef<typeof ToastPrimitive.Title>,\n ComponentPropsWithoutRef<typeof ToastPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <ToastPrimitive.Title\n ref={ref}\n className={cn(\"font-medium text-body-sm text-foreground\", className)}\n {...props}\n />\n));\nToastTitle.displayName = \"Toast.Title\";\n\nconst ToastDescription = forwardRef<\n ElementRef<typeof ToastPrimitive.Description>,\n ComponentPropsWithoutRef<typeof ToastPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <ToastPrimitive.Description\n ref={ref}\n className={cn(\"mt-0.5 text-body-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nToastDescription.displayName = \"Toast.Description\";\n\nconst ToastClose = forwardRef<\n ElementRef<typeof ToastPrimitive.Close>,\n ComponentPropsWithoutRef<typeof ToastPrimitive.Close>\n>(({ className, ...props }, ref) => (\n <ToastPrimitive.Close\n ref={ref}\n className={cn(\n \"absolute top-2 right-2 rounded-md p-1 text-muted-foreground opacity-70 transition-opacity hover:opacity-100\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n className,\n )}\n toast-close=\"\"\n {...props}\n >\n <X className=\"size-3.5\" />\n <span className=\"sr-only\">Close</span>\n </ToastPrimitive.Close>\n));\nToastClose.displayName = \"Toast.Close\";\n\nconst ToastAction = forwardRef<\n ElementRef<typeof ToastPrimitive.Action>,\n ComponentPropsWithoutRef<typeof ToastPrimitive.Action>\n>(({ className, ...props }, ref) => (\n <ToastPrimitive.Action\n ref={ref}\n className={cn(\n \"mt-2 inline-flex h-7 items-center rounded-md border border-border/60 bg-card px-2 font-sans text-foreground text-label\",\n \"transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n className,\n )}\n {...props}\n />\n));\nToastAction.displayName = \"Toast.Action\";\n\nconst Toast = /*#__PURE__*/ Object.assign(ToastRoot, {\n Title: ToastTitle,\n Description: ToastDescription,\n Close: ToastClose,\n Action: ToastAction,\n Provider: ToastPrimitive.Provider,\n Viewport: ToastPrimitive.Viewport,\n});\n\nexport { Toast, toastVariants, type ToastVariant };\n"
21
+ "content": "import * as ToastPrimitive from \"@radix-ui/react-toast\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport { AlertCircle, CheckCircle2, Info, TriangleAlert, X } from \"lucide-react\";\nimport { forwardRef } from \"react\";\nimport type { ComponentPropsWithoutRef, ElementRef, ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * Toast — transient notification built on Radix Toast.\n *\n * Composition: app mounts <Toaster /> once. Components call `toast(...)` via\n * the `useToast()` hook (see toaster.tsx). The look is theme-aware: status\n * icon coloured by --primary/--success/--warning/--destructive; the body\n * card uses --popover surface.\n *\n * Variants:\n * - default (neutral)\n * - info (primary tint)\n * - success (deploy ok, action completed)\n * - warning (queued, permission requested)\n * - destructive (failed, fatal)\n */\n\nconst toastVariants = cva(\n [\n \"group pointer-events-auto relative flex w-full items-start gap-3 overflow-hidden rounded-lg border pr-10 shadow-md\",\n \"data-[state=open]:slide-in-from-top-full data-[state=open]:fade-in-0 data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=closed]:animate-out\",\n \"data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)]\",\n \"data-[swipe=end]:animate-out data-[swipe=cancel]:transition-[transform_var(--duration-base)] data-[swipe=move]:transition-none\",\n ],\n {\n variants: {\n variant: {\n default: \"border-border/40 bg-popover text-popover-foreground\",\n info: \"border-primary/40 bg-popover text-popover-foreground\",\n success: \"border-success/40 bg-popover text-popover-foreground\",\n warning: \"border-warning/40 bg-popover text-popover-foreground\",\n destructive: \"border-destructive/50 bg-popover text-popover-foreground\",\n },\n size: {\n sm: \"p-3 text-body-sm\",\n md: \"p-4 text-body-md\",\n lg: \"p-5 text-body-lg\",\n },\n },\n defaultVariants: { variant: \"default\", size: \"md\" },\n },\n);\n\nconst iconForVariant: Record<NonNullable<ToastVariant>, ReactNode> = {\n default: <Info className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />,\n info: <Info className=\"size-4 shrink-0 text-primary\" aria-hidden=\"true\" />,\n success: <CheckCircle2 className=\"size-4 shrink-0 text-success\" aria-hidden=\"true\" />,\n warning: <TriangleAlert className=\"size-4 shrink-0 text-warning\" aria-hidden=\"true\" />,\n destructive: <AlertCircle className=\"size-4 shrink-0 text-destructive\" aria-hidden=\"true\" />,\n};\n\ntype ToastVariant = NonNullable<VariantProps<typeof toastVariants>[\"variant\"]>;\n\ninterface ToastProps\n extends ComponentPropsWithoutRef<typeof ToastPrimitive.Root>,\n VariantProps<typeof toastVariants> {}\n\nconst ToastRoot = forwardRef<ElementRef<typeof ToastPrimitive.Root>, ToastProps>(\n ({ className, variant = \"default\", size, children, ...props }, ref) => (\n <ToastPrimitive.Root\n data-slot=\"toast\"\n data-variant={variant}\n data-size={size}\n ref={ref}\n className={cn(toastVariants({ variant, size }), className)}\n {...props}\n >\n <span aria-hidden=\"true\">{iconForVariant[variant as ToastVariant]}</span>\n <div className=\"min-w-0 flex-1\">{children}</div>\n </ToastPrimitive.Root>\n ),\n);\nToastRoot.displayName = \"Toast\";\n\nconst ToastTitle = forwardRef<\n ElementRef<typeof ToastPrimitive.Title>,\n ComponentPropsWithoutRef<typeof ToastPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <ToastPrimitive.Title\n data-slot=\"toast-title\"\n ref={ref}\n className={cn(\"font-medium text-body-sm text-foreground\", className)}\n {...props}\n />\n));\nToastTitle.displayName = \"Toast.Title\";\n\nconst ToastDescription = forwardRef<\n ElementRef<typeof ToastPrimitive.Description>,\n ComponentPropsWithoutRef<typeof ToastPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <ToastPrimitive.Description\n data-slot=\"toast-description\"\n ref={ref}\n className={cn(\"mt-0.5 text-body-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nToastDescription.displayName = \"Toast.Description\";\n\nconst ToastClose = forwardRef<\n ElementRef<typeof ToastPrimitive.Close>,\n ComponentPropsWithoutRef<typeof ToastPrimitive.Close>\n>(({ className, ...props }, ref) => (\n <ToastPrimitive.Close\n data-slot=\"toast-close\"\n ref={ref}\n className={cn(\n \"absolute top-2 right-2 rounded-md p-1 text-muted-foreground opacity-70 transition-opacity hover:opacity-100\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n className,\n )}\n toast-close=\"\"\n {...props}\n >\n <X className=\"size-3.5\" />\n <span className=\"sr-only\">Close</span>\n </ToastPrimitive.Close>\n));\nToastClose.displayName = \"Toast.Close\";\n\nconst ToastAction = forwardRef<\n ElementRef<typeof ToastPrimitive.Action>,\n ComponentPropsWithoutRef<typeof ToastPrimitive.Action>\n>(({ className, ...props }, ref) => (\n <ToastPrimitive.Action\n data-slot=\"toast-action\"\n ref={ref}\n className={cn(\n \"mt-2 inline-flex h-7 items-center rounded-md border border-border/60 bg-card px-2 font-sans text-foreground text-label\",\n \"transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n className,\n )}\n {...props}\n />\n));\nToastAction.displayName = \"Toast.Action\";\n\nconst Toast = /*#__PURE__*/ Object.assign(ToastRoot, {\n Title: ToastTitle,\n Description: ToastDescription,\n Close: ToastClose,\n Action: ToastAction,\n Provider: ToastPrimitive.Provider,\n Viewport: ToastPrimitive.Viewport,\n});\n\nexport { Toast, toastVariants, type ToastVariant };\n"
22
22
  },
23
23
  {
24
24
  "path": "components/primitives/toast/toaster.tsx",
25
25
  "type": "registry:ui",
26
26
  "target": "components/ui/toaster.tsx",
27
- "content": "import { createContext, useCallback, useContext, useMemo, useState } from \"react\";\nimport type { JSX, ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport { Toast, type ToastVariant } from \"@/components/ui/toast\";\n\ninterface ToastDescriptor {\n id: string;\n title?: ReactNode;\n description?: ReactNode;\n variant?: ToastVariant;\n /** Auto-dismiss in ms. Default 5000. Pass `null` for sticky. */\n duration?: number | null;\n action?: { label: ReactNode; onClick: () => void };\n}\n\ninterface ToastContextValue {\n toast: (descriptor: Omit<ToastDescriptor, \"id\">) => string;\n dismiss: (id: string) => void;\n}\n\nconst ToastContext = createContext<ToastContextValue | undefined>(undefined);\n\ninterface ToasterProps {\n children: ReactNode;\n /** Toast viewport position. Default bottom-right. */\n position?: \"top-right\" | \"top-left\" | \"bottom-right\" | \"bottom-left\";\n className?: string;\n}\n\nconst POSITION_CLASS: Record<NonNullable<ToasterProps[\"position\"]>, string> = {\n \"top-right\": \"top-4 right-4\",\n \"top-left\": \"top-4 left-4\",\n \"bottom-right\": \"bottom-4 right-4\",\n \"bottom-left\": \"bottom-4 left-4\",\n};\n\n/**\n * Toaster — mount once at the app root. Wraps children in a Radix Toast\n * Provider + Viewport and exposes the `useToast()` hook for descendants.\n */\nfunction Toaster({ children, position = \"bottom-right\", className }: ToasterProps): JSX.Element {\n const [toasts, setToasts] = useState<ToastDescriptor[]>([]);\n\n const dismiss = useCallback((id: string) => {\n setToasts((cur) => cur.filter((t) => t.id !== id));\n }, []);\n\n const toast = useCallback((descriptor: Omit<ToastDescriptor, \"id\">) => {\n const id = `t-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n setToasts((cur) => [...cur, { id, ...descriptor }]);\n return id;\n }, []);\n\n const value = useMemo(() => ({ toast, dismiss }), [toast, dismiss]);\n\n return (\n <ToastContext.Provider value={value}>\n <Toast.Provider swipeDirection=\"right\" duration={5000}>\n {children}\n {toasts.map((t) => (\n <Toast\n key={t.id}\n variant={t.variant ?? \"default\"}\n duration={t.duration === null ? Number.POSITIVE_INFINITY : (t.duration ?? 5000)}\n onOpenChange={(open) => {\n if (!open) dismiss(t.id);\n }}\n >\n {t.title ? <Toast.Title>{t.title}</Toast.Title> : null}\n {t.description ? <Toast.Description>{t.description}</Toast.Description> : null}\n {t.action ? (\n <Toast.Action altText={String(t.action.label)} onClick={t.action.onClick}>\n {t.action.label}\n </Toast.Action>\n ) : null}\n <Toast.Close />\n </Toast>\n ))}\n <Toast.Viewport\n className={cn(\n \"fixed z-50 flex max-h-screen w-full max-w-sm flex-col gap-2 outline-none\",\n POSITION_CLASS[position],\n className,\n )}\n />\n </Toast.Provider>\n </ToastContext.Provider>\n );\n}\n\n/**\n * useToast — fire toasts from anywhere inside <Toaster>.\n *\n * Example:\n * const { toast } = useToast();\n * toast({ title: \"Deployed\", variant: \"success\" });\n */\nfunction useToast(): ToastContextValue {\n const ctx = useContext(ToastContext);\n if (!ctx) throw new Error(\"useToast must be used inside <Toaster>.\");\n return ctx;\n}\n\nexport { Toaster, useToast };\n"
27
+ "content": "\"use client\";\n\nimport { createContext, useCallback, useContext, useMemo, useState } from \"react\";\nimport type { JSX, ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport { Toast, type ToastVariant } from \"@/components/ui/toast\";\n\ninterface ToastDescriptor {\n id: string;\n title?: ReactNode;\n description?: ReactNode;\n variant?: ToastVariant;\n /** Auto-dismiss in ms. Default 5000. Pass `null` for sticky. */\n duration?: number | null;\n action?: { label: ReactNode; onClick: () => void };\n}\n\ninterface ToastContextValue {\n toast: (descriptor: Omit<ToastDescriptor, \"id\">) => string;\n dismiss: (id: string) => void;\n}\n\nconst ToastContext = createContext<ToastContextValue | undefined>(undefined);\n\ninterface ToasterProps {\n children: ReactNode;\n /** Toast viewport position. Default bottom-right. */\n position?: \"top-right\" | \"top-left\" | \"bottom-right\" | \"bottom-left\";\n className?: string;\n}\n\nconst POSITION_CLASS: Record<NonNullable<ToasterProps[\"position\"]>, string> = {\n \"top-right\": \"top-4 right-4\",\n \"top-left\": \"top-4 left-4\",\n \"bottom-right\": \"bottom-4 right-4\",\n \"bottom-left\": \"bottom-4 left-4\",\n};\n\n/**\n * Toaster — mount once at the app root. Wraps children in a Radix Toast\n * Provider + Viewport and exposes the `useToast()` hook for descendants.\n */\nfunction Toaster({ children, position = \"bottom-right\", className }: ToasterProps): JSX.Element {\n const [toasts, setToasts] = useState<ToastDescriptor[]>([]);\n\n const dismiss = useCallback((id: string) => {\n setToasts((cur) => cur.filter((t) => t.id !== id));\n }, []);\n\n const toast = useCallback((descriptor: Omit<ToastDescriptor, \"id\">) => {\n const id = `t-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n setToasts((cur) => [...cur, { id, ...descriptor }]);\n return id;\n }, []);\n\n const value = useMemo(() => ({ toast, dismiss }), [toast, dismiss]);\n\n return (\n <ToastContext.Provider data-slot=\"toaster\" value={value}>\n <Toast.Provider swipeDirection=\"right\" duration={5000}>\n {children}\n {toasts.map((t) => (\n <Toast\n key={t.id}\n variant={t.variant ?? \"default\"}\n duration={t.duration === null ? Number.POSITIVE_INFINITY : (t.duration ?? 5000)}\n onOpenChange={(open) => {\n if (!open) dismiss(t.id);\n }}\n >\n {t.title ? <Toast.Title>{t.title}</Toast.Title> : null}\n {t.description ? <Toast.Description>{t.description}</Toast.Description> : null}\n {t.action ? (\n <Toast.Action altText={String(t.action.label)} onClick={t.action.onClick}>\n {t.action.label}\n </Toast.Action>\n ) : null}\n <Toast.Close />\n </Toast>\n ))}\n <Toast.Viewport\n className={cn(\n \"fixed z-50 flex max-h-screen w-full max-w-sm flex-col gap-2 outline-none\",\n POSITION_CLASS[position],\n className,\n )}\n />\n </Toast.Provider>\n </ToastContext.Provider>\n );\n}\n\n/**\n * useToast — fire toasts from anywhere inside <Toaster>.\n *\n * Example:\n * const { toast } = useToast();\n * toast({ title: \"Deployed\", variant: \"success\" });\n */\nfunction useToast(): ToastContextValue {\n const ctx = useContext(ToastContext);\n if (!ctx) throw new Error(\"useToast must be used inside <Toaster>.\");\n return ctx;\n}\n\nexport { Toaster, useToast };\n"
28
28
  }
29
29
  ]
30
30
  }
@@ -14,7 +14,7 @@
14
14
  "path": "components/primitives/token-usage-chart/token-usage-chart.tsx",
15
15
  "type": "registry:ui",
16
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\";\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\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 ...props\n },\n ref,\n ) => {\n const series = useMemo(() => binPoints(points, maxBars), [points, maxBars]);\n const max = Math.max(1, ...series.map((p) => p.input + p.output));\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 = series.reduce((acc, p) => acc + p.input + p.output, 0);\n\n return (\n <section\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 totalH = ((p.input + p.output) / max) * 100;\n const inputH = (p.input / max) * 100;\n const outputH = (p.output / max) * 100;\n const x = idx * barWidth + gap / 2;\n return (\n <g key={p.label}>\n <title>{`${p.label} — input ${formatTokens(p.input)} · output ${formatTokens(p.output)}`}</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"
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
18
  }
19
19
  ]
20
20
  }
@@ -9,13 +9,13 @@
9
9
  "path": "styles/tokens.css",
10
10
  "type": "registry:lib",
11
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: #7C3AED → hsl(262 83% 58%)\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: 0 0% 100%; /* #FFFFFF */\n --foreground: 0 0% 4%; /* #0A0A0A */\n\n --card: 0 0% 100%; /* #FFFFFF */\n --card-foreground: 0 0% 4%;\n\n --popover: 0 0% 100%;\n --popover-foreground: 0 0% 4%;\n\n /* Primary — Theo violet (equity) ------------------------------------- */\n --primary: 262 83% 58%; /* #7C3AED */\n --primary-deep: 263 70% 42%; /* #5B21B6 — pressed */\n --primary-glow: 263 90% 76%; /* #A78BFA — hover halo */\n --primary-foreground: 0 0% 100%;\n\n /* Secondary — neutral muted ----------------------------------------- */\n --secondary: 0 0% 96%; /* #F5F5F5 */\n --secondary-foreground: 0 0% 4%;\n\n /* Accent — burnt sienna --------------------------------------------- */\n --accent: 15 54% 53%; /* #C96442 */\n --accent-deep: 15 55% 40%; /* #9C4A2E */\n --accent-foreground: 0 0% 100%;\n\n /* Muted ------------------------------------------------------------- */\n --muted: 0 0% 96%;\n --muted-foreground: 0 0% 45%; /* #737373 */\n\n /* Surfaces, borders, inputs ----------------------------------------- */\n --border: 0 0% 91%; /* #E8E8E8 — Vercel-style hairline */\n --input: 0 0% 91%;\n --ring: 262 83% 58%; /* matches primary */\n\n /* Semantics --------------------------------------------------------- */\n --success: 142 71% 36%; /* #16A34A */\n --success-foreground: 0 0% 100%;\n --warning: 33 92% 44%; /* #D97706 */\n --warning-foreground: 0 0% 100%;\n --destructive: 0 72% 51%; /* #DC2626 */\n --destructive-foreground: 0 0% 100%;\n --info: 217 91% 60%; /* #3B82F6 */\n --info-foreground: 0 0% 100%;\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 */\n --shadow-sm: 0 1px 2px 0 hsl(var(--foreground) / 0.06);\n --shadow-md: 0 2px 8px -2px hsl(var(--foreground) / 0.08), 0 1px 3px hsl(var(--foreground) / 0.06);\n --shadow-lg: 0 12px 32px -8px hsl(var(--foreground) / 0.12), 0 4px 12px\n hsl(var(--foreground) / 0.08);\n --shadow-glow: 0 0 24px hsl(var(--primary) / 0.25);\n --shadow-glow-strong: 0 0 32px hsl(var(--primary) / 0.4);\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: 0 0% 4%; /* #0A0A0A */\n --foreground: 0 0% 96%; /* #F5F5F5 */\n\n --card: 0 0% 7%; /* #121212 */\n --card-foreground: 0 0% 96%;\n\n --popover: 0 0% 9%; /* #171717 */\n --popover-foreground: 0 0% 96%;\n\n --primary: 262 83% 58%;\n --primary-deep: 263 70% 42%;\n --primary-glow: 263 90% 76%;\n --primary-foreground: 0 0% 100%;\n\n --secondary: 0 0% 11%; /* #1C1C1C */\n --secondary-foreground: 0 0% 96%;\n\n --accent: 15 54% 53%;\n --accent-deep: 15 55% 40%;\n --accent-foreground: 0 0% 100%;\n\n --muted: 0 0% 11%;\n --muted-foreground: 0 0% 60%; /* #999 — Vercel gray-500 */\n\n --border: 0 0% 16%; /* #292929 */\n --input: 0 0% 11%;\n --ring: 262 83% 58%;\n\n --success: 152 79% 52%; /* #22E58C */\n --success-foreground: 0 0% 4%;\n --warning: 38 92% 50%; /* #F59E0B */\n --warning-foreground: 0 0% 4%;\n --destructive: 350 100% 65%; /* #FF4F6D */\n --destructive-foreground: 0 0% 4%;\n --info: 213 100% 70%; /* #5FB3FF */\n --info-foreground: 0 0% 4%;\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 */\n --shadow-sm: 0 1px 2px 0 hsl(0 0% 0% / 0.4);\n --shadow-md: 0 2px 8px -2px hsl(0 0% 0% / 0.5), 0 1px 3px hsl(0 0% 0% / 0.4);\n --shadow-lg: 0 12px 32px -8px hsl(0 0% 0% / 0.6), 0 4px 12px hsl(0 0% 0% / 0.4);\n --shadow-glow: 0 0 24px hsl(var(--primary-glow) / 0.4);\n --shadow-glow-strong: 0 0 36px hsl(var(--primary-glow) / 0.6);\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@layer utilities {\n .bg-dotted-violet {\n background-image: radial-gradient(hsl(var(--primary) / 0.08) 1px, transparent 1px);\n background-size: 20px 20px;\n }\n\n .bg-dotted-violet-strong {\n background-image: radial-gradient(hsl(var(--primary) / 0.16) 1px, transparent 1px);\n background-size: 20px 20px;\n }\n\n .bg-hero-glow {\n background-image: radial-gradient(\n ellipse 60% 50% at 70% 0%,\n hsl(var(--primary) / 0.18) 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"
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
13
  },
14
14
  {
15
15
  "path": "styles/fonts.css",
16
16
  "type": "registry:lib",
17
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 * `@usetheo/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 \"@usetheo/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"
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
19
  }
20
20
  ]
21
21
  }
@@ -17,7 +17,7 @@
17
17
  "path": "components/primitives/tool-call-card/tool-call-card.tsx",
18
18
  "type": "registry:ui",
19
19
  "target": "components/ui/tool-call-card.tsx",
20
- "content": "import { 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 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"
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
21
  }
22
22
  ]
23
23
  }