@vadimcomanescu/nadicode-design-system 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1116) hide show
  1. package/.agents/skills/seed/SKILL.md +433 -0
  2. package/.agents/skills/seed/contract.md +92 -0
  3. package/.agents/skills/seed/intent-map.md +320 -0
  4. package/.agents/skills/seed/recipes/agency-home.md +311 -0
  5. package/.agents/skills/seed/recipes/agents-chat.md +281 -0
  6. package/.agents/skills/seed/recipes/analytics.md +253 -0
  7. package/.agents/skills/seed/recipes/auth.md +254 -0
  8. package/.agents/skills/seed/recipes/blog-content.md +307 -0
  9. package/.agents/skills/seed/recipes/checkout.md +311 -0
  10. package/.agents/skills/seed/recipes/company-about.md +276 -0
  11. package/.agents/skills/seed/recipes/company-contact.md +234 -0
  12. package/.agents/skills/seed/recipes/crud-form.md +233 -0
  13. package/.agents/skills/seed/recipes/crud-list-detail.md +230 -0
  14. package/.agents/skills/seed/recipes/dashboard.md +354 -0
  15. package/.agents/skills/seed/recipes/digital-workers.md +314 -0
  16. package/.agents/skills/seed/recipes/error-pages.md +199 -0
  17. package/.agents/skills/seed/recipes/marketing-landing.md +293 -0
  18. package/.agents/skills/seed/recipes/marketing-shell.md +165 -0
  19. package/.agents/skills/seed/recipes/navigation-shell.md +786 -0
  20. package/.agents/skills/seed/recipes/onboarding.md +258 -0
  21. package/.agents/skills/seed/recipes/pricing.md +271 -0
  22. package/.agents/skills/seed/recipes/service-detail.md +302 -0
  23. package/.agents/skills/seed/recipes/settings.md +252 -0
  24. package/.agents/skills/seed/references/animation.md +292 -0
  25. package/.agents/skills/seed/references/blocks.md +122 -0
  26. package/.agents/skills/seed/references/brand-override.md +140 -0
  27. package/.agents/skills/seed/references/components.md +270 -0
  28. package/.agents/skills/seed/references/composition.md +258 -0
  29. package/.agents/skills/seed/references/dashboard-patterns.md +101 -0
  30. package/.agents/skills/seed/references/glass-and-effects.md +201 -0
  31. package/.agents/skills/seed/references/icons.md +169 -0
  32. package/.agents/skills/seed/references/nextjs.md +109 -0
  33. package/.agents/skills/seed/references/opinions.md +271 -0
  34. package/.agents/skills/seed/references/patterns.md +373 -0
  35. package/.agents/skills/seed/references/responsive.md +209 -0
  36. package/.agents/skills/seed/references/state-machines.md +252 -0
  37. package/.agents/skills/seed/references/tokens.md +279 -0
  38. package/LICENSE +21 -0
  39. package/README.md +243 -0
  40. package/css/tokens.css +981 -0
  41. package/dist/chunk-226EBOTK.js +43 -0
  42. package/dist/chunk-2HDB6MDK.js +142 -0
  43. package/dist/chunk-2IZC7HSV.js +42 -0
  44. package/dist/chunk-2MOEOEOI.js +106 -0
  45. package/dist/chunk-2NT57K4U.js +43 -0
  46. package/dist/chunk-2SDZMTB2.js +47 -0
  47. package/dist/chunk-2WEUTBDI.js +79 -0
  48. package/dist/chunk-2ZJVU7NV.js +36 -0
  49. package/dist/chunk-3CLXYQKB.js +31 -0
  50. package/dist/chunk-3NYTIDKZ.js +1 -0
  51. package/dist/chunk-3R2VLZUR.js +56 -0
  52. package/dist/chunk-3UJ3HJZ3.js +24 -0
  53. package/dist/chunk-3ZDYYZDD.js +1 -0
  54. package/dist/chunk-4FEAWXJD.js +64 -0
  55. package/dist/chunk-4GSFNJAJ.js +19 -0
  56. package/dist/chunk-4HRVRW2X.js +669 -0
  57. package/dist/chunk-4K3PATUT.js +370 -0
  58. package/dist/chunk-4NXZIMYZ.js +32 -0
  59. package/dist/chunk-4O6L5YWT.js +17 -0
  60. package/dist/chunk-4OBE2FS2.js +44 -0
  61. package/dist/chunk-4S326Z3D.js +143 -0
  62. package/dist/chunk-55HD4L6G.js +67 -0
  63. package/dist/chunk-55U27XY4.js +28 -0
  64. package/dist/chunk-5DKCZWC6.js +186 -0
  65. package/dist/chunk-5ESF6N36.js +79 -0
  66. package/dist/chunk-5I3FWRC5.js +148 -0
  67. package/dist/chunk-5LCXASRW.js +41 -0
  68. package/dist/chunk-5UZDOF6V.js +35 -0
  69. package/dist/chunk-5Y4BOIYO.js +121 -0
  70. package/dist/chunk-666N6OTL.js +45 -0
  71. package/dist/chunk-6BAV4TZ5.js +39 -0
  72. package/dist/chunk-6DYFX5IR.js +44 -0
  73. package/dist/chunk-6FOHUNXR.js +14 -0
  74. package/dist/chunk-6G3RRWJT.js +43 -0
  75. package/dist/chunk-72X6SXOX.js +36 -0
  76. package/dist/chunk-73T6GXPA.js +37 -0
  77. package/dist/chunk-74RKVIBV.js +83 -0
  78. package/dist/chunk-756Q7AC5.js +27 -0
  79. package/dist/chunk-7KIDDF3I.js +67 -0
  80. package/dist/chunk-7WDT2PRI.js +43 -0
  81. package/dist/chunk-A7NUWD76.js +40 -0
  82. package/dist/chunk-ACRVOD7W.js +32 -0
  83. package/dist/chunk-AH6YSYYT.js +88 -0
  84. package/dist/chunk-ALMGJVMT.js +43 -0
  85. package/dist/chunk-ALUL3PF7.js +161 -0
  86. package/dist/chunk-ANBJ2OLC.js +104 -0
  87. package/dist/chunk-AP3XXYAY.js +51 -0
  88. package/dist/chunk-AP5Y5M3R.js +43 -0
  89. package/dist/chunk-ASKFAYYR.js +39 -0
  90. package/dist/chunk-AUSYEAIJ.js +93 -0
  91. package/dist/chunk-AWIJHDNK.js +65 -0
  92. package/dist/chunk-AZUJNRQU.js +52 -0
  93. package/dist/chunk-B3BYBSF2.js +38 -0
  94. package/dist/chunk-B5QL76GA.js +40 -0
  95. package/dist/chunk-BFLN54VR.js +31 -0
  96. package/dist/chunk-BJYCQ2NV.js +35 -0
  97. package/dist/chunk-BPCCTJSK.js +37 -0
  98. package/dist/chunk-BRP6D56U.js +47 -0
  99. package/dist/chunk-BRXNGF5W.js +96 -0
  100. package/dist/chunk-BYASYDOH.js +22 -0
  101. package/dist/chunk-BZYMCJHW.js +104 -0
  102. package/dist/chunk-C4SNHMYC.js +21 -0
  103. package/dist/chunk-CGUCH322.js +53 -0
  104. package/dist/chunk-CI7GPGCJ.js +43 -0
  105. package/dist/chunk-CRY67BIF.js +57 -0
  106. package/dist/chunk-CRZ2JE24.js +35 -0
  107. package/dist/chunk-CUZJIDU7.js +19 -0
  108. package/dist/chunk-CXACRCZ4.js +38 -0
  109. package/dist/chunk-D4NC7WX5.js +107 -0
  110. package/dist/chunk-DARC2ACH.js +240 -0
  111. package/dist/chunk-DF47R6LN.js +347 -0
  112. package/dist/chunk-DFPXK2JO.js +71 -0
  113. package/dist/chunk-DJTF3XFB.js +86 -0
  114. package/dist/chunk-DQPK2XRL.js +66 -0
  115. package/dist/chunk-DT6DGTVW.js +41 -0
  116. package/dist/chunk-E7RBK6ML.js +44 -0
  117. package/dist/chunk-EB5PYS7Q.js +57 -0
  118. package/dist/chunk-ELKIUARM.js +32 -0
  119. package/dist/chunk-EV2V5RWT.js +432 -0
  120. package/dist/chunk-EYWOKTOY.js +58 -0
  121. package/dist/chunk-F3T2U7YL.js +79 -0
  122. package/dist/chunk-FLF5AMNO.js +28 -0
  123. package/dist/chunk-FMH55OKV.js +42 -0
  124. package/dist/chunk-FOFGPWFS.js +7 -0
  125. package/dist/chunk-FTVLDDOQ.js +69 -0
  126. package/dist/chunk-G24VV5NO.js +62 -0
  127. package/dist/chunk-G5EO22OR.js +1 -0
  128. package/dist/chunk-G5EO5FWC.js +29 -0
  129. package/dist/chunk-G5YLGJXR.js +62 -0
  130. package/dist/chunk-GAKU7DFY.js +36 -0
  131. package/dist/chunk-GCUTJI7M.js +41 -0
  132. package/dist/chunk-GHIBG7OM.js +21 -0
  133. package/dist/chunk-GJUR6HT3.js +81 -0
  134. package/dist/chunk-GLU236NN.js +48 -0
  135. package/dist/chunk-GMMPLZLC.js +142 -0
  136. package/dist/chunk-GO35FTNJ.js +1 -0
  137. package/dist/chunk-H466RJCI.js +611 -0
  138. package/dist/chunk-HJBXUXTD.js +62 -0
  139. package/dist/chunk-HJC6U46F.js +83 -0
  140. package/dist/chunk-HNY45VUQ.js +35 -0
  141. package/dist/chunk-HOWTYZL5.js +134 -0
  142. package/dist/chunk-HRIEX66J.js +86 -0
  143. package/dist/chunk-HS7QNBCO.js +49 -0
  144. package/dist/chunk-I23DDSU7.js +40 -0
  145. package/dist/chunk-I3TH7PIB.js +69 -0
  146. package/dist/chunk-IFIDWZ5M.js +38 -0
  147. package/dist/chunk-IJJI4RQU.js +32 -0
  148. package/dist/chunk-ILIHQ2KZ.js +73 -0
  149. package/dist/chunk-IPXL7WX7.js +90 -0
  150. package/dist/chunk-IQEBXXB5.js +35 -0
  151. package/dist/chunk-J3QL5UQ7.js +163 -0
  152. package/dist/chunk-J5DRK4RF.js +54 -0
  153. package/dist/chunk-JBK2LA6U.js +46 -0
  154. package/dist/chunk-JC5W5UPI.js +125 -0
  155. package/dist/chunk-JHQ5NMLZ.js +43 -0
  156. package/dist/chunk-JHQTKMKG.js +35 -0
  157. package/dist/chunk-JJTQOH7J.js +76 -0
  158. package/dist/chunk-JL4MVVFH.js +36 -0
  159. package/dist/chunk-JMCSAGZ3.js +101 -0
  160. package/dist/chunk-JRJGSNI4.js +32 -0
  161. package/dist/chunk-JWQXBRE7.js +30 -0
  162. package/dist/chunk-JZ4G2SJH.js +52 -0
  163. package/dist/chunk-K6GIX57H.js +15 -0
  164. package/dist/chunk-KRBLVZII.js +20 -0
  165. package/dist/chunk-KSHJQ2VT.js +37 -0
  166. package/dist/chunk-KYGKJ553.js +52 -0
  167. package/dist/chunk-KYZT3RNW.js +37 -0
  168. package/dist/chunk-LCDDAE7J.js +28 -0
  169. package/dist/chunk-LDW4LHUM.js +47 -0
  170. package/dist/chunk-LIBXYD5Q.js +19 -0
  171. package/dist/chunk-LNVZNQCZ.js +124 -0
  172. package/dist/chunk-LQLFA2EL.js +65 -0
  173. package/dist/chunk-LSPO6OBI.js +94 -0
  174. package/dist/chunk-LV4LBWCS.js +29 -0
  175. package/dist/chunk-M7YSMMAC.js +294 -0
  176. package/dist/chunk-MAUTIHQG.js +54 -0
  177. package/dist/chunk-MDAYDDTC.js +23 -0
  178. package/dist/chunk-MJ4CB6ZL.js +23 -0
  179. package/dist/chunk-MLUSJTS2.js +107 -0
  180. package/dist/chunk-N53OMWW2.js +45 -0
  181. package/dist/chunk-NAAU5IWU.js +54 -0
  182. package/dist/chunk-NCULQXJE.js +36 -0
  183. package/dist/chunk-NURPUVUV.js +97 -0
  184. package/dist/chunk-O74AEVHW.js +37 -0
  185. package/dist/chunk-OCQB4IU7.js +71 -0
  186. package/dist/chunk-OHCQPI3W.js +31 -0
  187. package/dist/chunk-OHX2LFAH.js +112 -0
  188. package/dist/chunk-OITJWGFV.js +48 -0
  189. package/dist/chunk-ONGJ7AC2.js +34 -0
  190. package/dist/chunk-P6IRHPFM.js +32 -0
  191. package/dist/chunk-PD2YEH3H.js +84 -0
  192. package/dist/chunk-PJNHVPHF.js +266 -0
  193. package/dist/chunk-PKVTEDKO.js +32 -0
  194. package/dist/chunk-PRUXIDBD.js +69 -0
  195. package/dist/chunk-PSVQ7ZNX.js +44 -0
  196. package/dist/chunk-PX2WVDDB.js +69 -0
  197. package/dist/chunk-PXDHNGTG.js +142 -0
  198. package/dist/chunk-Q7LVUGFL.js +58 -0
  199. package/dist/chunk-QPXTBZWN.js +49 -0
  200. package/dist/chunk-QSU23VYZ.js +49 -0
  201. package/dist/chunk-QUKDUSHD.js +39 -0
  202. package/dist/chunk-QYZT24TS.js +18 -0
  203. package/dist/chunk-R3AO6AZM.js +49 -0
  204. package/dist/chunk-R5XP45PD.js +36 -0
  205. package/dist/chunk-R674XI7C.js +31 -0
  206. package/dist/chunk-R7N7YLFT.js +27 -0
  207. package/dist/chunk-RASEB2XI.js +16 -0
  208. package/dist/chunk-RBPLOM3A.js +52 -0
  209. package/dist/chunk-RF6ECFS5.js +36 -0
  210. package/dist/chunk-RHEF3WH6.js +83 -0
  211. package/dist/chunk-RWNJ54CI.js +22 -0
  212. package/dist/chunk-RX5EUODB.js +174 -0
  213. package/dist/chunk-RYOTIXZO.js +57 -0
  214. package/dist/chunk-S2WSXZ7Y.js +90 -0
  215. package/dist/chunk-S4GKGKON.js +38 -0
  216. package/dist/chunk-S4JAHKOP.js +26 -0
  217. package/dist/chunk-S733NAYS.js +34 -0
  218. package/dist/chunk-SEMWC3IT.js +32 -0
  219. package/dist/chunk-SGI25ZJ6.js +54 -0
  220. package/dist/chunk-SH72GWB7.js +28 -0
  221. package/dist/chunk-SR5U52V3.js +43 -0
  222. package/dist/chunk-SV3KZ6CB.js +136 -0
  223. package/dist/chunk-SW6QPJM4.js +79 -0
  224. package/dist/chunk-SXHYB7JB.js +36 -0
  225. package/dist/chunk-T5LXQNIP.js +177 -0
  226. package/dist/chunk-T6BRD7TS.js +44 -0
  227. package/dist/chunk-T7H53CK2.js +83 -0
  228. package/dist/chunk-TBKJ34BB.js +43 -0
  229. package/dist/chunk-TSVN5A2F.js +65 -0
  230. package/dist/chunk-TV4RSQH4.js +33 -0
  231. package/dist/chunk-TXGANOAX.js +37 -0
  232. package/dist/chunk-U7V6TREO.js +55 -0
  233. package/dist/chunk-UBIGDGOP.js +52 -0
  234. package/dist/chunk-UEG2LI7Z.js +43 -0
  235. package/dist/chunk-UHXGBV5N.js +35 -0
  236. package/dist/chunk-UIUMTURU.js +31 -0
  237. package/dist/chunk-UJDEGCCZ.js +49 -0
  238. package/dist/chunk-ULLTRLBD.js +268 -0
  239. package/dist/chunk-UN2SJ42K.js +118 -0
  240. package/dist/chunk-UWHKZX4Y.js +99 -0
  241. package/dist/chunk-UYT33NG6.js +23 -0
  242. package/dist/chunk-V6WNRZT3.js +43 -0
  243. package/dist/chunk-VCCI3NRD.js +392 -0
  244. package/dist/chunk-VDYPDMYR.js +37 -0
  245. package/dist/chunk-VJIL7W55.js +8 -0
  246. package/dist/chunk-VM462WZC.js +37 -0
  247. package/dist/chunk-VTAOHSPW.js +63 -0
  248. package/dist/chunk-W3B3UOEG.js +36 -0
  249. package/dist/chunk-W4YX7N46.js +60 -0
  250. package/dist/chunk-W73JAOHW.js +143 -0
  251. package/dist/chunk-WA45EC4Y.js +36 -0
  252. package/dist/chunk-WAP7DBSW.js +47 -0
  253. package/dist/chunk-WDGIQDQG.js +35 -0
  254. package/dist/chunk-WFJQ43SM.js +43 -0
  255. package/dist/chunk-WGPK3FQ7.js +44 -0
  256. package/dist/chunk-WH62BE24.js +165 -0
  257. package/dist/chunk-WI37ZYIF.js +370 -0
  258. package/dist/chunk-WI547C47.js +43 -0
  259. package/dist/chunk-WKRIKCJC.js +32 -0
  260. package/dist/chunk-WN5ONOHC.js +77 -0
  261. package/dist/chunk-WSU2PCA2.js +248 -0
  262. package/dist/chunk-WUO7OONN.js +33 -0
  263. package/dist/chunk-WXVNTJIB.js +99 -0
  264. package/dist/chunk-XBBR4PLJ.js +39 -0
  265. package/dist/chunk-XCZIF75R.js +114 -0
  266. package/dist/chunk-XQ2UDMPO.js +35 -0
  267. package/dist/chunk-XZ3A33GP.js +29 -0
  268. package/dist/chunk-Y56WBNCY.js +37 -0
  269. package/dist/chunk-YQIABLDO.js +18 -0
  270. package/dist/chunk-Z74D6D2W.js +46 -0
  271. package/dist/chunk-ZHTQF2KI.js +93 -0
  272. package/dist/chunk-ZJ6PY5ST.js +66 -0
  273. package/dist/chunk-ZLSWCV55.js +19 -0
  274. package/dist/chunk-ZU2GYVAP.js +32 -0
  275. package/dist/chunk-ZU55E4A7.js +50 -0
  276. package/dist/chunk-ZUHIWALK.js +48 -0
  277. package/dist/components/animate-ui/primitives/animate/scroll-progress.d.ts +27 -0
  278. package/dist/components/animate-ui/primitives/animate/scroll-progress.js +91 -0
  279. package/dist/components/animate-ui/primitives/animate/slot.d.ts +17 -0
  280. package/dist/components/animate-ui/primitives/animate/slot.js +6 -0
  281. package/dist/components/animate-ui/primitives/effects/fade.d.ts +18 -0
  282. package/dist/components/animate-ui/primitives/effects/fade.js +72 -0
  283. package/dist/components/animate-ui/primitives/effects/highlight.d.ts +91 -0
  284. package/dist/components/animate-ui/primitives/effects/highlight.js +433 -0
  285. package/dist/components/animate-ui/primitives/effects/shine.d.ts +17 -0
  286. package/dist/components/animate-ui/primitives/effects/shine.js +5 -0
  287. package/dist/components/animate-ui/primitives/effects/slide.d.ts +19 -0
  288. package/dist/components/animate-ui/primitives/effects/slide.js +74 -0
  289. package/dist/components/animate-ui/primitives/effects/zoom.d.ts +18 -0
  290. package/dist/components/animate-ui/primitives/effects/zoom.js +72 -0
  291. package/dist/components/blocks/AccountLockedBlock.d.ts +11 -0
  292. package/dist/components/blocks/AccountLockedBlock.js +61 -0
  293. package/dist/components/blocks/ActivityFeedBlock.d.ts +14 -0
  294. package/dist/components/blocks/ActivityFeedBlock.js +43 -0
  295. package/dist/components/blocks/AgentConversationBlock.d.ts +25 -0
  296. package/dist/components/blocks/AgentConversationBlock.js +149 -0
  297. package/dist/components/blocks/AgentProfileGridBlock.d.ts +19 -0
  298. package/dist/components/blocks/AgentProfileGridBlock.js +90 -0
  299. package/dist/components/blocks/AgentRunOverviewBlock.d.ts +32 -0
  300. package/dist/components/blocks/AgentRunOverviewBlock.js +154 -0
  301. package/dist/components/blocks/AgentWorkbenchBlock.d.ts +56 -0
  302. package/dist/components/blocks/AgentWorkbenchBlock.js +374 -0
  303. package/dist/components/blocks/AudioVisualizerBlock.d.ts +6 -0
  304. package/dist/components/blocks/AudioVisualizerBlock.js +51 -0
  305. package/dist/components/blocks/AuthLayout.d.ts +17 -0
  306. package/dist/components/blocks/AuthLayout.js +82 -0
  307. package/dist/components/blocks/AuthSuccessBlock.d.ts +12 -0
  308. package/dist/components/blocks/AuthSuccessBlock.js +63 -0
  309. package/dist/components/blocks/BannerBlock.d.ts +9 -0
  310. package/dist/components/blocks/BannerBlock.js +178 -0
  311. package/dist/components/blocks/BarChartBlock.d.ts +15 -0
  312. package/dist/components/blocks/BarChartBlock.js +52 -0
  313. package/dist/components/blocks/CallToActionBlock.d.ts +13 -0
  314. package/dist/components/blocks/CallToActionBlock.js +67 -0
  315. package/dist/components/blocks/ChangelogBlock.d.ts +16 -0
  316. package/dist/components/blocks/ChangelogBlock.js +218 -0
  317. package/dist/components/blocks/ChartBlock.d.ts +16 -0
  318. package/dist/components/blocks/ChartBlock.js +169 -0
  319. package/dist/components/blocks/ChartCollectionBlock.d.ts +5 -0
  320. package/dist/components/blocks/ChartCollectionBlock.js +177 -0
  321. package/dist/components/blocks/ChatLayout.d.ts +14 -0
  322. package/dist/components/blocks/ChatLayout.js +238 -0
  323. package/dist/components/blocks/CodeBlock.d.ts +8 -0
  324. package/dist/components/blocks/CodeBlock.js +65 -0
  325. package/dist/components/blocks/ComparisonBlock.d.ts +13 -0
  326. package/dist/components/blocks/ComparisonBlock.js +99 -0
  327. package/dist/components/blocks/ContactBlock.d.ts +17 -0
  328. package/dist/components/blocks/ContactBlock.js +126 -0
  329. package/dist/components/blocks/CreateBlock.d.ts +16 -0
  330. package/dist/components/blocks/CreateBlock.js +222 -0
  331. package/dist/components/blocks/DataGridBlock.d.ts +16 -0
  332. package/dist/components/blocks/DataGridBlock.js +236 -0
  333. package/dist/components/blocks/DirectoryBlock.d.ts +17 -0
  334. package/dist/components/blocks/DirectoryBlock.js +204 -0
  335. package/dist/components/blocks/FAQBlock.d.ts +12 -0
  336. package/dist/components/blocks/FAQBlock.js +160 -0
  337. package/dist/components/blocks/FeatureBlock.d.ts +27 -0
  338. package/dist/components/blocks/FeatureBlock.js +265 -0
  339. package/dist/components/blocks/FeatureGridBlock.d.ts +14 -0
  340. package/dist/components/blocks/FeatureGridBlock.js +62 -0
  341. package/dist/components/blocks/FooterBlock.d.ts +19 -0
  342. package/dist/components/blocks/FooterBlock.js +74 -0
  343. package/dist/components/blocks/GalleryBlock.d.ts +13 -0
  344. package/dist/components/blocks/GalleryBlock.js +187 -0
  345. package/dist/components/blocks/HeaderBlock.d.ts +14 -0
  346. package/dist/components/blocks/HeaderBlock.js +123 -0
  347. package/dist/components/blocks/HeatmapChartBlock.d.ts +15 -0
  348. package/dist/components/blocks/HeatmapChartBlock.js +9 -0
  349. package/dist/components/blocks/HeroBlock.d.ts +21 -0
  350. package/dist/components/blocks/HeroBlock.js +318 -0
  351. package/dist/components/blocks/HeroSectionBlock.d.ts +19 -0
  352. package/dist/components/blocks/HeroSectionBlock.js +267 -0
  353. package/dist/components/blocks/IntegrationsBlock.d.ts +15 -0
  354. package/dist/components/blocks/IntegrationsBlock.js +211 -0
  355. package/dist/components/blocks/InteractiveAreaChartBlock.d.ts +21 -0
  356. package/dist/components/blocks/InteractiveAreaChartBlock.js +407 -0
  357. package/dist/components/blocks/KanbanDemoBlock.d.ts +4 -0
  358. package/dist/components/blocks/KanbanDemoBlock.js +154 -0
  359. package/dist/components/blocks/LoginBlock.d.ts +41 -0
  360. package/dist/components/blocks/LoginBlock.js +13 -0
  361. package/dist/components/blocks/LogoCloud.d.ts +12 -0
  362. package/dist/components/blocks/LogoCloud.js +47 -0
  363. package/dist/components/blocks/NavUser.d.ts +18 -0
  364. package/dist/components/blocks/NavUser.js +211 -0
  365. package/dist/components/blocks/NewsletterBlock.d.ts +8 -0
  366. package/dist/components/blocks/NewsletterBlock.js +90 -0
  367. package/dist/components/blocks/NotFoundBlock.d.ts +9 -0
  368. package/dist/components/blocks/NotFoundBlock.js +157 -0
  369. package/dist/components/blocks/OnboardingBlock.d.ts +14 -0
  370. package/dist/components/blocks/OnboardingBlock.js +181 -0
  371. package/dist/components/blocks/PasswordRecoveryBlock.d.ts +12 -0
  372. package/dist/components/blocks/PasswordRecoveryBlock.js +146 -0
  373. package/dist/components/blocks/PricingBlock.d.ts +19 -0
  374. package/dist/components/blocks/PricingBlock.js +125 -0
  375. package/dist/components/blocks/ProcessFlowBlock.d.ts +15 -0
  376. package/dist/components/blocks/ProcessFlowBlock.js +82 -0
  377. package/dist/components/blocks/ResetPasswordBlock.d.ts +9 -0
  378. package/dist/components/blocks/ResetPasswordBlock.js +132 -0
  379. package/dist/components/blocks/SettingsLayout.d.ts +16 -0
  380. package/dist/components/blocks/SettingsLayout.js +205 -0
  381. package/dist/components/blocks/SignUpBlock.d.ts +11 -0
  382. package/dist/components/blocks/SignUpBlock.js +173 -0
  383. package/dist/components/blocks/SocialProofBlock.d.ts +23 -0
  384. package/dist/components/blocks/SocialProofBlock.js +122 -0
  385. package/dist/components/blocks/SolutionShowcaseBlock.d.ts +17 -0
  386. package/dist/components/blocks/SolutionShowcaseBlock.js +117 -0
  387. package/dist/components/blocks/StatsBlock.d.ts +21 -0
  388. package/dist/components/blocks/StatsBlock.js +180 -0
  389. package/dist/components/blocks/StatsMarketingBlock.d.ts +14 -0
  390. package/dist/components/blocks/StatsMarketingBlock.js +51 -0
  391. package/dist/components/blocks/TeamBlock.d.ts +16 -0
  392. package/dist/components/blocks/TeamBlock.js +57 -0
  393. package/dist/components/blocks/TestimonialsBlock.d.ts +15 -0
  394. package/dist/components/blocks/TestimonialsBlock.js +138 -0
  395. package/dist/components/blocks/TwoFactorChallengeBlock.d.ts +12 -0
  396. package/dist/components/blocks/TwoFactorChallengeBlock.js +207 -0
  397. package/dist/components/blocks/TwoFactorSetupBlock.d.ts +12 -0
  398. package/dist/components/blocks/TwoFactorSetupBlock.js +296 -0
  399. package/dist/components/blocks/UsageDonutBlock.d.ts +16 -0
  400. package/dist/components/blocks/UsageDonutBlock.js +84 -0
  401. package/dist/components/blocks/VoiceAgentCard.d.ts +17 -0
  402. package/dist/components/blocks/VoiceAgentCard.js +79 -0
  403. package/dist/components/blocks/WizardBlock.d.ts +15 -0
  404. package/dist/components/blocks/WizardBlock.js +207 -0
  405. package/dist/components/blocks/user/InviteUserModal.d.ts +20 -0
  406. package/dist/components/blocks/user/InviteUserModal.js +248 -0
  407. package/dist/components/layout/Grid.d.ts +13 -0
  408. package/dist/components/layout/Grid.js +42 -0
  409. package/dist/components/layout/GridSystem.d.ts +51 -0
  410. package/dist/components/layout/GridSystem.js +115 -0
  411. package/dist/components/logos/Gemini.d.ts +2 -0
  412. package/dist/components/logos/Gemini.js +2 -0
  413. package/dist/components/logos/GooglePaLM.d.ts +2 -0
  414. package/dist/components/logos/GooglePaLM.js +2 -0
  415. package/dist/components/logos/MagicUI.d.ts +2 -0
  416. package/dist/components/logos/MagicUI.js +2 -0
  417. package/dist/components/logos/MediaWiki.d.ts +2 -0
  418. package/dist/components/logos/MediaWiki.js +2 -0
  419. package/dist/components/logos/Replit.d.ts +2 -0
  420. package/dist/components/logos/Replit.js +2 -0
  421. package/dist/components/logos/VSCodium.d.ts +2 -0
  422. package/dist/components/logos/VSCodium.js +2 -0
  423. package/dist/components/logos/index.d.ts +6 -0
  424. package/dist/components/logos/index.js +7 -0
  425. package/dist/components/ui/Accordion.d.ts +13 -0
  426. package/dist/components/ui/Accordion.js +115 -0
  427. package/dist/components/ui/AgentAvatar.d.ts +15 -0
  428. package/dist/components/ui/AgentAvatar.js +73 -0
  429. package/dist/components/ui/AgentMessageBubble.d.ts +21 -0
  430. package/dist/components/ui/AgentMessageBubble.js +115 -0
  431. package/dist/components/ui/AgentMetricsCard.d.ts +19 -0
  432. package/dist/components/ui/AgentMetricsCard.js +7 -0
  433. package/dist/components/ui/AgentStatus.d.ts +19 -0
  434. package/dist/components/ui/AgentStatus.js +77 -0
  435. package/dist/components/ui/AgentTeamPanel.d.ts +30 -0
  436. package/dist/components/ui/AgentTeamPanel.js +6 -0
  437. package/dist/components/ui/AgentTerminal.d.ts +14 -0
  438. package/dist/components/ui/AgentTerminal.js +3 -0
  439. package/dist/components/ui/AgentTimeline.d.ts +17 -0
  440. package/dist/components/ui/AgentTimeline.js +90 -0
  441. package/dist/components/ui/Alert.d.ts +18 -0
  442. package/dist/components/ui/Alert.js +3 -0
  443. package/dist/components/ui/AlertDialog.d.ts +26 -0
  444. package/dist/components/ui/AlertDialog.js +137 -0
  445. package/dist/components/ui/AmbientGrid.d.ts +9 -0
  446. package/dist/components/ui/AmbientGrid.js +26 -0
  447. package/dist/components/ui/AnimatedBackground.d.ts +3 -0
  448. package/dist/components/ui/AnimatedBackground.js +3 -0
  449. package/dist/components/ui/AnimatedDialog.d.ts +30 -0
  450. package/dist/components/ui/AnimatedDialog.js +119 -0
  451. package/dist/components/ui/AnimatedSheet.d.ts +26 -0
  452. package/dist/components/ui/AnimatedSheet.js +264 -0
  453. package/dist/components/ui/AnimatedTabs.d.ts +16 -0
  454. package/dist/components/ui/AnimatedTabs.js +175 -0
  455. package/dist/components/ui/AnnouncementBanner.d.ts +10 -0
  456. package/dist/components/ui/AnnouncementBanner.js +48 -0
  457. package/dist/components/ui/ApprovalCard.d.ts +16 -0
  458. package/dist/components/ui/ApprovalCard.js +117 -0
  459. package/dist/components/ui/ArtifactCard.d.ts +18 -0
  460. package/dist/components/ui/ArtifactCard.js +4 -0
  461. package/dist/components/ui/AspectRatio.d.ts +3 -0
  462. package/dist/components/ui/AspectRatio.js +6 -0
  463. package/dist/components/ui/AudioWaveform.d.ts +18 -0
  464. package/dist/components/ui/AudioWaveform.js +77 -0
  465. package/dist/components/ui/AuroraEffect.d.ts +9 -0
  466. package/dist/components/ui/AuroraEffect.js +77 -0
  467. package/dist/components/ui/Avatar.d.ts +12 -0
  468. package/dist/components/ui/Avatar.js +3 -0
  469. package/dist/components/ui/Avatar3D.d.ts +18 -0
  470. package/dist/components/ui/Avatar3D.js +6 -0
  471. package/dist/components/ui/AvatarUpload.d.ts +6 -0
  472. package/dist/components/ui/AvatarUpload.js +124 -0
  473. package/dist/components/ui/Badge.d.ts +11 -0
  474. package/dist/components/ui/Badge.js +3 -0
  475. package/dist/components/ui/BentoGrid.d.ts +10 -0
  476. package/dist/components/ui/BentoGrid.js +32 -0
  477. package/dist/components/ui/BrandIcons.d.ts +5 -0
  478. package/dist/components/ui/BrandIcons.js +2 -0
  479. package/dist/components/ui/Breadcrumb.d.ts +23 -0
  480. package/dist/components/ui/Breadcrumb.js +217 -0
  481. package/dist/components/ui/Button.d.ts +14 -0
  482. package/dist/components/ui/Button.js +6 -0
  483. package/dist/components/ui/ButtonGroup.d.ts +5 -0
  484. package/dist/components/ui/ButtonGroup.js +19 -0
  485. package/dist/components/ui/Calendar.d.ts +5 -0
  486. package/dist/components/ui/Calendar.js +119 -0
  487. package/dist/components/ui/Card.d.ts +26 -0
  488. package/dist/components/ui/Card.js +6 -0
  489. package/dist/components/ui/Carousel.d.ts +31 -0
  490. package/dist/components/ui/Carousel.js +331 -0
  491. package/dist/components/ui/Chart.d.ts +43 -0
  492. package/dist/components/ui/Chart.js +3 -0
  493. package/dist/components/ui/ChartCard.d.ts +17 -0
  494. package/dist/components/ui/ChartCard.js +42 -0
  495. package/dist/components/ui/CheckStatus.d.ts +11 -0
  496. package/dist/components/ui/CheckStatus.js +58 -0
  497. package/dist/components/ui/Checkbox.d.ts +6 -0
  498. package/dist/components/ui/Checkbox.js +115 -0
  499. package/dist/components/ui/CheckoutForm.d.ts +7 -0
  500. package/dist/components/ui/CheckoutForm.js +186 -0
  501. package/dist/components/ui/CheckoutFormDemo.d.ts +7 -0
  502. package/dist/components/ui/CheckoutFormDemo.js +294 -0
  503. package/dist/components/ui/CodeDiffViewer.d.ts +20 -0
  504. package/dist/components/ui/CodeDiffViewer.js +4 -0
  505. package/dist/components/ui/Collapsible.d.ts +5 -0
  506. package/dist/components/ui/Collapsible.js +2 -0
  507. package/dist/components/ui/Combobox.d.ts +11 -0
  508. package/dist/components/ui/Combobox.js +195 -0
  509. package/dist/components/ui/Command.d.ts +28 -0
  510. package/dist/components/ui/Command.js +121 -0
  511. package/dist/components/ui/ConfettiBurst.d.ts +11 -0
  512. package/dist/components/ui/ConfettiBurst.js +74 -0
  513. package/dist/components/ui/ContextMenu.d.ts +40 -0
  514. package/dist/components/ui/ContextMenu.js +296 -0
  515. package/dist/components/ui/ContextMeter.d.ts +18 -0
  516. package/dist/components/ui/ContextMeter.js +3 -0
  517. package/dist/components/ui/ConversationThread.d.ts +21 -0
  518. package/dist/components/ui/ConversationThread.js +81 -0
  519. package/dist/components/ui/DataFreshness.d.ts +10 -0
  520. package/dist/components/ui/DataFreshness.js +74 -0
  521. package/dist/components/ui/DataTable.d.ts +8 -0
  522. package/dist/components/ui/DataTable.js +123 -0
  523. package/dist/components/ui/DatePicker.d.ts +1 -0
  524. package/dist/components/ui/DatePicker.js +154 -0
  525. package/dist/components/ui/DateRangePicker.d.ts +2 -0
  526. package/dist/components/ui/DateRangePicker.js +167 -0
  527. package/dist/components/ui/Dialog.d.ts +27 -0
  528. package/dist/components/ui/Dialog.js +120 -0
  529. package/dist/components/ui/DiffStat.d.ts +7 -0
  530. package/dist/components/ui/DiffStat.js +33 -0
  531. package/dist/components/ui/Drawer.d.ts +27 -0
  532. package/dist/components/ui/Drawer.js +117 -0
  533. package/dist/components/ui/DropdownMenu.d.ts +40 -0
  534. package/dist/components/ui/DropdownMenu.js +115 -0
  535. package/dist/components/ui/Empty.d.ts +14 -0
  536. package/dist/components/ui/Empty.js +3 -0
  537. package/dist/components/ui/Field.d.ts +44 -0
  538. package/dist/components/ui/Field.js +3 -0
  539. package/dist/components/ui/FileUpload.d.ts +33 -0
  540. package/dist/components/ui/FileUpload.js +252 -0
  541. package/dist/components/ui/FloatingDock.d.ts +14 -0
  542. package/dist/components/ui/FloatingDock.js +103 -0
  543. package/dist/components/ui/Form.d.ts +34 -0
  544. package/dist/components/ui/Form.js +4 -0
  545. package/dist/components/ui/FormWizard.d.ts +31 -0
  546. package/dist/components/ui/FormWizard.js +9 -0
  547. package/dist/components/ui/HandoffIndicator.d.ts +14 -0
  548. package/dist/components/ui/HandoffIndicator.js +46 -0
  549. package/dist/components/ui/Heading.d.ts +293 -0
  550. package/dist/components/ui/Heading.js +3 -0
  551. package/dist/components/ui/HoverCard.d.ts +8 -0
  552. package/dist/components/ui/HoverCard.js +24 -0
  553. package/dist/components/ui/InfiniteSlider.d.ts +11 -0
  554. package/dist/components/ui/InfiniteSlider.js +3 -0
  555. package/dist/components/ui/Input.d.ts +15 -0
  556. package/dist/components/ui/Input.js +4 -0
  557. package/dist/components/ui/InputGroup.d.ts +8 -0
  558. package/dist/components/ui/InputGroup.js +37 -0
  559. package/dist/components/ui/InputOTP.d.ts +17 -0
  560. package/dist/components/ui/InputOTP.js +115 -0
  561. package/dist/components/ui/Item.d.ts +42 -0
  562. package/dist/components/ui/Item.js +177 -0
  563. package/dist/components/ui/KanbanBoard.d.ts +8 -0
  564. package/dist/components/ui/KanbanBoard.js +11 -0
  565. package/dist/components/ui/Kbd.d.ts +4 -0
  566. package/dist/components/ui/Kbd.js +18 -0
  567. package/dist/components/ui/Label.d.ts +6 -0
  568. package/dist/components/ui/Label.js +3 -0
  569. package/dist/components/ui/LanguageSwitcher.d.ts +9 -0
  570. package/dist/components/ui/LanguageSwitcher.js +116 -0
  571. package/dist/components/ui/Logo.d.ts +3 -0
  572. package/dist/components/ui/Logo.js +2 -0
  573. package/dist/components/ui/MagneticElement.d.ts +10 -0
  574. package/dist/components/ui/MagneticElement.js +44 -0
  575. package/dist/components/ui/MemoryInspector.d.ts +16 -0
  576. package/dist/components/ui/MemoryInspector.js +95 -0
  577. package/dist/components/ui/Menubar.d.ts +47 -0
  578. package/dist/components/ui/Menubar.js +338 -0
  579. package/dist/components/ui/MeteorShower.d.ts +9 -0
  580. package/dist/components/ui/MeteorShower.js +3 -0
  581. package/dist/components/ui/MetricCard.d.ts +22 -0
  582. package/dist/components/ui/MetricCard.js +16 -0
  583. package/dist/components/ui/MouseEffect.d.ts +20 -0
  584. package/dist/components/ui/MouseEffect.js +6 -0
  585. package/dist/components/ui/MovingBorder.d.ts +11 -0
  586. package/dist/components/ui/MovingBorder.js +41 -0
  587. package/dist/components/ui/NativeSelect.d.ts +5 -0
  588. package/dist/components/ui/NativeSelect.js +19 -0
  589. package/dist/components/ui/NavigationMenu.d.ts +24 -0
  590. package/dist/components/ui/NavigationMenu.js +242 -0
  591. package/dist/components/ui/NotificationCenter.d.ts +25 -0
  592. package/dist/components/ui/NotificationCenter.js +217 -0
  593. package/dist/components/ui/PageTransition.d.ts +9 -0
  594. package/dist/components/ui/PageTransition.js +56 -0
  595. package/dist/components/ui/Pagination.d.ts +17 -0
  596. package/dist/components/ui/Pagination.js +230 -0
  597. package/dist/components/ui/PasswordInput.d.ts +6 -0
  598. package/dist/components/ui/PasswordInput.js +8 -0
  599. package/dist/components/ui/Popover.d.ts +8 -0
  600. package/dist/components/ui/Popover.js +3 -0
  601. package/dist/components/ui/Progress.d.ts +6 -0
  602. package/dist/components/ui/Progress.js +3 -0
  603. package/dist/components/ui/ProgressRing.d.ts +17 -0
  604. package/dist/components/ui/ProgressRing.js +131 -0
  605. package/dist/components/ui/ProgressiveBlur.d.ts +7 -0
  606. package/dist/components/ui/ProgressiveBlur.js +3 -0
  607. package/dist/components/ui/PromoCard.d.ts +9 -0
  608. package/dist/components/ui/PromoCard.js +45 -0
  609. package/dist/components/ui/RadioGroup.d.ts +9 -0
  610. package/dist/components/ui/RadioGroup.js +151 -0
  611. package/dist/components/ui/Resizable.d.ts +8 -0
  612. package/dist/components/ui/Resizable.js +149 -0
  613. package/dist/components/ui/RoleBadge.d.ts +11 -0
  614. package/dist/components/ui/RoleBadge.js +3 -0
  615. package/dist/components/ui/ScrollArea.d.ts +9 -0
  616. package/dist/components/ui/ScrollArea.js +3 -0
  617. package/dist/components/ui/ScrollFadeIn.d.ts +12 -0
  618. package/dist/components/ui/ScrollFadeIn.js +6 -0
  619. package/dist/components/ui/SearchCommand.d.ts +33 -0
  620. package/dist/components/ui/SearchCommand.js +263 -0
  621. package/dist/components/ui/Select.d.ts +27 -0
  622. package/dist/components/ui/Select.js +115 -0
  623. package/dist/components/ui/Separator.d.ts +7 -0
  624. package/dist/components/ui/Separator.js +3 -0
  625. package/dist/components/ui/SettingsModal.d.ts +19 -0
  626. package/dist/components/ui/SettingsModal.js +219 -0
  627. package/dist/components/ui/ShaderBackground.d.ts +16 -0
  628. package/dist/components/ui/ShaderBackground.js +74 -0
  629. package/dist/components/ui/Sheet.d.ts +33 -0
  630. package/dist/components/ui/Sheet.js +116 -0
  631. package/dist/components/ui/Sidebar.d.ts +122 -0
  632. package/dist/components/ui/Sidebar.js +128 -0
  633. package/dist/components/ui/Skeleton.d.ts +5 -0
  634. package/dist/components/ui/Skeleton.js +3 -0
  635. package/dist/components/ui/SkipNav.d.ts +2 -0
  636. package/dist/components/ui/SkipNav.js +20 -0
  637. package/dist/components/ui/Slider.d.ts +22 -0
  638. package/dist/components/ui/Slider.js +6 -0
  639. package/dist/components/ui/Sonner.d.ts +4 -0
  640. package/dist/components/ui/Sonner.js +26 -0
  641. package/dist/components/ui/SourceCitation.d.ts +19 -0
  642. package/dist/components/ui/SourceCitation.js +120 -0
  643. package/dist/components/ui/Spinner.d.ts +6 -0
  644. package/dist/components/ui/Spinner.js +3 -0
  645. package/dist/components/ui/SpringHover.d.ts +12 -0
  646. package/dist/components/ui/SpringHover.js +35 -0
  647. package/dist/components/ui/StaggerChildren.d.ts +11 -0
  648. package/dist/components/ui/StaggerChildren.js +6 -0
  649. package/dist/components/ui/StaggeredEntrance.d.ts +8 -0
  650. package/dist/components/ui/StaggeredEntrance.js +24 -0
  651. package/dist/components/ui/StatusDot.d.ts +11 -0
  652. package/dist/components/ui/StatusDot.js +3 -0
  653. package/dist/components/ui/StyleToggle.d.ts +7 -0
  654. package/dist/components/ui/StyleToggle.js +65 -0
  655. package/dist/components/ui/SuccessCheck.d.ts +10 -0
  656. package/dist/components/ui/SuccessCheck.js +55 -0
  657. package/dist/components/ui/Switch.d.ts +6 -0
  658. package/dist/components/ui/Switch.js +3 -0
  659. package/dist/components/ui/Table.d.ts +29 -0
  660. package/dist/components/ui/Table.js +3 -0
  661. package/dist/components/ui/Tabs.d.ts +13 -0
  662. package/dist/components/ui/Tabs.js +3 -0
  663. package/dist/components/ui/TagInput.d.ts +10 -0
  664. package/dist/components/ui/TagInput.js +216 -0
  665. package/dist/components/ui/Textarea.d.ts +13 -0
  666. package/dist/components/ui/Textarea.js +4 -0
  667. package/dist/components/ui/ThemeToggle.d.ts +7 -0
  668. package/dist/components/ui/ThemeToggle.js +150 -0
  669. package/dist/components/ui/ThinkingIndicator.d.ts +12 -0
  670. package/dist/components/ui/ThinkingIndicator.js +8 -0
  671. package/dist/components/ui/TiltCard.d.ts +10 -0
  672. package/dist/components/ui/TiltCard.js +51 -0
  673. package/dist/components/ui/Timeline.d.ts +14 -0
  674. package/dist/components/ui/Timeline.js +3 -0
  675. package/dist/components/ui/Toast.d.ts +28 -0
  676. package/dist/components/ui/Toast.js +115 -0
  677. package/dist/components/ui/Toaster.d.ts +1 -0
  678. package/dist/components/ui/Toaster.js +136 -0
  679. package/dist/components/ui/Toggle.d.ts +11 -0
  680. package/dist/components/ui/Toggle.js +3 -0
  681. package/dist/components/ui/ToggleGroup.d.ts +11 -0
  682. package/dist/components/ui/ToggleGroup.js +57 -0
  683. package/dist/components/ui/ToolCallCard.d.ts +16 -0
  684. package/dist/components/ui/ToolCallCard.js +9 -0
  685. package/dist/components/ui/Tooltip.d.ts +9 -0
  686. package/dist/components/ui/Tooltip.js +3 -0
  687. package/dist/components/ui/TreeView.d.ts +15 -0
  688. package/dist/components/ui/TreeView.js +299 -0
  689. package/dist/components/ui/TrendIndicator.d.ts +11 -0
  690. package/dist/components/ui/TrendIndicator.js +7 -0
  691. package/dist/components/ui/Typography.d.ts +11 -0
  692. package/dist/components/ui/Typography.js +3 -0
  693. package/dist/components/ui/VisuallyHidden.d.ts +2 -0
  694. package/dist/components/ui/VisuallyHidden.js +8 -0
  695. package/dist/components/ui/WorkflowGraph.d.ts +25 -0
  696. package/dist/components/ui/WorkflowGraph.js +3 -0
  697. package/dist/components/ui/charts/AreaChart.d.ts +14 -0
  698. package/dist/components/ui/charts/AreaChart.js +4 -0
  699. package/dist/components/ui/charts/BarChart.d.ts +16 -0
  700. package/dist/components/ui/charts/BarChart.js +4 -0
  701. package/dist/components/ui/charts/HeatmapChart.d.ts +16 -0
  702. package/dist/components/ui/charts/HeatmapChart.js +4 -0
  703. package/dist/components/ui/charts/LineChart.d.ts +13 -0
  704. package/dist/components/ui/charts/LineChart.js +4 -0
  705. package/dist/components/ui/charts/PieChart.d.ts +15 -0
  706. package/dist/components/ui/charts/PieChart.js +4 -0
  707. package/dist/components/ui/charts/RadarChart.d.ts +12 -0
  708. package/dist/components/ui/charts/RadarChart.js +4 -0
  709. package/dist/components/ui/charts/RadialBarChart.d.ts +14 -0
  710. package/dist/components/ui/charts/RadialBarChart.js +4 -0
  711. package/dist/components/ui/charts/Sparkline.d.ts +13 -0
  712. package/dist/components/ui/charts/Sparkline.js +6 -0
  713. package/dist/components/ui/charts/index.d.ts +7 -0
  714. package/dist/components/ui/charts/index.js +9 -0
  715. package/dist/components/ui/icons/activity.d.ts +7 -0
  716. package/dist/components/ui/icons/activity.js +4 -0
  717. package/dist/components/ui/icons/alert-circle.d.ts +7 -0
  718. package/dist/components/ui/icons/alert-circle.js +4 -0
  719. package/dist/components/ui/icons/alert-triangle.d.ts +7 -0
  720. package/dist/components/ui/icons/alert-triangle.js +4 -0
  721. package/dist/components/ui/icons/arrow-down-right.d.ts +7 -0
  722. package/dist/components/ui/icons/arrow-down-right.js +4 -0
  723. package/dist/components/ui/icons/arrow-down.d.ts +7 -0
  724. package/dist/components/ui/icons/arrow-down.js +4 -0
  725. package/dist/components/ui/icons/arrow-left.d.ts +7 -0
  726. package/dist/components/ui/icons/arrow-left.js +4 -0
  727. package/dist/components/ui/icons/arrow-right.d.ts +7 -0
  728. package/dist/components/ui/icons/arrow-right.js +4 -0
  729. package/dist/components/ui/icons/arrow-up-right.d.ts +7 -0
  730. package/dist/components/ui/icons/arrow-up-right.js +4 -0
  731. package/dist/components/ui/icons/arrow-up.d.ts +7 -0
  732. package/dist/components/ui/icons/arrow-up.js +4 -0
  733. package/dist/components/ui/icons/bell.d.ts +7 -0
  734. package/dist/components/ui/icons/bell.js +4 -0
  735. package/dist/components/ui/icons/bold.d.ts +7 -0
  736. package/dist/components/ui/icons/bold.js +4 -0
  737. package/dist/components/ui/icons/bot.d.ts +7 -0
  738. package/dist/components/ui/icons/bot.js +4 -0
  739. package/dist/components/ui/icons/calendar.d.ts +7 -0
  740. package/dist/components/ui/icons/calendar.js +4 -0
  741. package/dist/components/ui/icons/chart-bar.d.ts +7 -0
  742. package/dist/components/ui/icons/chart-bar.js +4 -0
  743. package/dist/components/ui/icons/chart-line.d.ts +7 -0
  744. package/dist/components/ui/icons/chart-line.js +4 -0
  745. package/dist/components/ui/icons/chart-pie.d.ts +7 -0
  746. package/dist/components/ui/icons/chart-pie.js +4 -0
  747. package/dist/components/ui/icons/check-circle.d.ts +7 -0
  748. package/dist/components/ui/icons/check-circle.js +4 -0
  749. package/dist/components/ui/icons/check.d.ts +7 -0
  750. package/dist/components/ui/icons/check.js +4 -0
  751. package/dist/components/ui/icons/chevron-down.d.ts +7 -0
  752. package/dist/components/ui/icons/chevron-down.js +4 -0
  753. package/dist/components/ui/icons/chevron-left.d.ts +7 -0
  754. package/dist/components/ui/icons/chevron-left.js +4 -0
  755. package/dist/components/ui/icons/chevron-right.d.ts +7 -0
  756. package/dist/components/ui/icons/chevron-right.js +4 -0
  757. package/dist/components/ui/icons/chevron-up.d.ts +7 -0
  758. package/dist/components/ui/icons/chevron-up.js +4 -0
  759. package/dist/components/ui/icons/chevrons-up-down.d.ts +7 -0
  760. package/dist/components/ui/icons/chevrons-up-down.js +4 -0
  761. package/dist/components/ui/icons/circle.d.ts +7 -0
  762. package/dist/components/ui/icons/circle.js +4 -0
  763. package/dist/components/ui/icons/clipboard.d.ts +7 -0
  764. package/dist/components/ui/icons/clipboard.js +4 -0
  765. package/dist/components/ui/icons/clock.d.ts +7 -0
  766. package/dist/components/ui/icons/clock.js +4 -0
  767. package/dist/components/ui/icons/cloud.d.ts +7 -0
  768. package/dist/components/ui/icons/cloud.js +4 -0
  769. package/dist/components/ui/icons/code-2.d.ts +7 -0
  770. package/dist/components/ui/icons/code-2.js +4 -0
  771. package/dist/components/ui/icons/copy.d.ts +7 -0
  772. package/dist/components/ui/icons/copy.js +4 -0
  773. package/dist/components/ui/icons/corner-down-left.d.ts +7 -0
  774. package/dist/components/ui/icons/corner-down-left.js +4 -0
  775. package/dist/components/ui/icons/credit-card.d.ts +7 -0
  776. package/dist/components/ui/icons/credit-card.js +4 -0
  777. package/dist/components/ui/icons/database.d.ts +7 -0
  778. package/dist/components/ui/icons/database.js +4 -0
  779. package/dist/components/ui/icons/dollar-sign.d.ts +7 -0
  780. package/dist/components/ui/icons/dollar-sign.js +4 -0
  781. package/dist/components/ui/icons/dot.d.ts +7 -0
  782. package/dist/components/ui/icons/dot.js +4 -0
  783. package/dist/components/ui/icons/download.d.ts +7 -0
  784. package/dist/components/ui/icons/download.js +4 -0
  785. package/dist/components/ui/icons/ellipsis.d.ts +7 -0
  786. package/dist/components/ui/icons/ellipsis.js +4 -0
  787. package/dist/components/ui/icons/external-link.d.ts +7 -0
  788. package/dist/components/ui/icons/external-link.js +4 -0
  789. package/dist/components/ui/icons/eye-off.d.ts +7 -0
  790. package/dist/components/ui/icons/eye-off.js +4 -0
  791. package/dist/components/ui/icons/eye.d.ts +7 -0
  792. package/dist/components/ui/icons/eye.js +4 -0
  793. package/dist/components/ui/icons/file.d.ts +7 -0
  794. package/dist/components/ui/icons/file.js +4 -0
  795. package/dist/components/ui/icons/filter.d.ts +7 -0
  796. package/dist/components/ui/icons/filter.js +4 -0
  797. package/dist/components/ui/icons/folder.d.ts +7 -0
  798. package/dist/components/ui/icons/folder.js +4 -0
  799. package/dist/components/ui/icons/github.d.ts +7 -0
  800. package/dist/components/ui/icons/github.js +4 -0
  801. package/dist/components/ui/icons/globe.d.ts +7 -0
  802. package/dist/components/ui/icons/globe.js +4 -0
  803. package/dist/components/ui/icons/grip-vertical.d.ts +7 -0
  804. package/dist/components/ui/icons/grip-vertical.js +4 -0
  805. package/dist/components/ui/icons/home.d.ts +7 -0
  806. package/dist/components/ui/icons/home.js +4 -0
  807. package/dist/components/ui/icons/image.d.ts +7 -0
  808. package/dist/components/ui/icons/image.js +4 -0
  809. package/dist/components/ui/icons/index.d.ts +111 -0
  810. package/dist/components/ui/icons/index.js +113 -0
  811. package/dist/components/ui/icons/info.d.ts +7 -0
  812. package/dist/components/ui/icons/info.js +4 -0
  813. package/dist/components/ui/icons/italic.d.ts +7 -0
  814. package/dist/components/ui/icons/italic.js +4 -0
  815. package/dist/components/ui/icons/key.d.ts +7 -0
  816. package/dist/components/ui/icons/key.js +4 -0
  817. package/dist/components/ui/icons/laptop.d.ts +7 -0
  818. package/dist/components/ui/icons/laptop.js +4 -0
  819. package/dist/components/ui/icons/layers.d.ts +7 -0
  820. package/dist/components/ui/icons/layers.js +4 -0
  821. package/dist/components/ui/icons/layout-dashboard.d.ts +7 -0
  822. package/dist/components/ui/icons/layout-dashboard.js +4 -0
  823. package/dist/components/ui/icons/lib/create-animated-icon.d.ts +7 -0
  824. package/dist/components/ui/icons/lib/create-animated-icon.js +4 -0
  825. package/dist/components/ui/icons/lib/types.d.ts +8 -0
  826. package/dist/components/ui/icons/lib/types.js +1 -0
  827. package/dist/components/ui/icons/lib/use-animated-icon.d.ts +26 -0
  828. package/dist/components/ui/icons/lib/use-animated-icon.js +2 -0
  829. package/dist/components/ui/icons/link.d.ts +7 -0
  830. package/dist/components/ui/icons/link.js +4 -0
  831. package/dist/components/ui/icons/linkedin.d.ts +7 -0
  832. package/dist/components/ui/icons/linkedin.js +4 -0
  833. package/dist/components/ui/icons/list.d.ts +7 -0
  834. package/dist/components/ui/icons/list.js +4 -0
  835. package/dist/components/ui/icons/loader-circle.d.ts +7 -0
  836. package/dist/components/ui/icons/loader-circle.js +4 -0
  837. package/dist/components/ui/icons/loader.d.ts +7 -0
  838. package/dist/components/ui/icons/loader.js +4 -0
  839. package/dist/components/ui/icons/lock.d.ts +7 -0
  840. package/dist/components/ui/icons/lock.js +4 -0
  841. package/dist/components/ui/icons/log-in.d.ts +7 -0
  842. package/dist/components/ui/icons/log-in.js +4 -0
  843. package/dist/components/ui/icons/log-out.d.ts +7 -0
  844. package/dist/components/ui/icons/log-out.js +4 -0
  845. package/dist/components/ui/icons/lucide-wrapped.d.ts +289 -0
  846. package/dist/components/ui/icons/lucide-wrapped.js +5 -0
  847. package/dist/components/ui/icons/mail.d.ts +7 -0
  848. package/dist/components/ui/icons/mail.js +4 -0
  849. package/dist/components/ui/icons/menu.d.ts +7 -0
  850. package/dist/components/ui/icons/menu.js +4 -0
  851. package/dist/components/ui/icons/mic-off.d.ts +7 -0
  852. package/dist/components/ui/icons/mic-off.js +4 -0
  853. package/dist/components/ui/icons/mic.d.ts +7 -0
  854. package/dist/components/ui/icons/mic.js +4 -0
  855. package/dist/components/ui/icons/minus.d.ts +7 -0
  856. package/dist/components/ui/icons/minus.js +4 -0
  857. package/dist/components/ui/icons/monitor.d.ts +7 -0
  858. package/dist/components/ui/icons/monitor.js +4 -0
  859. package/dist/components/ui/icons/moon.d.ts +7 -0
  860. package/dist/components/ui/icons/moon.js +4 -0
  861. package/dist/components/ui/icons/package-2.d.ts +7 -0
  862. package/dist/components/ui/icons/package-2.js +4 -0
  863. package/dist/components/ui/icons/package.d.ts +7 -0
  864. package/dist/components/ui/icons/package.js +4 -0
  865. package/dist/components/ui/icons/panel-left.d.ts +7 -0
  866. package/dist/components/ui/icons/panel-left.js +4 -0
  867. package/dist/components/ui/icons/paperclip.d.ts +7 -0
  868. package/dist/components/ui/icons/paperclip.js +4 -0
  869. package/dist/components/ui/icons/pause.d.ts +7 -0
  870. package/dist/components/ui/icons/pause.js +4 -0
  871. package/dist/components/ui/icons/pencil.d.ts +7 -0
  872. package/dist/components/ui/icons/pencil.js +4 -0
  873. package/dist/components/ui/icons/play.d.ts +7 -0
  874. package/dist/components/ui/icons/play.js +4 -0
  875. package/dist/components/ui/icons/plus.d.ts +7 -0
  876. package/dist/components/ui/icons/plus.js +4 -0
  877. package/dist/components/ui/icons/receipt.d.ts +7 -0
  878. package/dist/components/ui/icons/receipt.js +4 -0
  879. package/dist/components/ui/icons/refresh-cw.d.ts +7 -0
  880. package/dist/components/ui/icons/refresh-cw.js +4 -0
  881. package/dist/components/ui/icons/reply.d.ts +7 -0
  882. package/dist/components/ui/icons/reply.js +4 -0
  883. package/dist/components/ui/icons/rocket.d.ts +7 -0
  884. package/dist/components/ui/icons/rocket.js +4 -0
  885. package/dist/components/ui/icons/save.d.ts +7 -0
  886. package/dist/components/ui/icons/save.js +4 -0
  887. package/dist/components/ui/icons/search.d.ts +7 -0
  888. package/dist/components/ui/icons/search.js +4 -0
  889. package/dist/components/ui/icons/send.d.ts +7 -0
  890. package/dist/components/ui/icons/send.js +4 -0
  891. package/dist/components/ui/icons/settings-2.d.ts +7 -0
  892. package/dist/components/ui/icons/settings-2.js +4 -0
  893. package/dist/components/ui/icons/settings.d.ts +7 -0
  894. package/dist/components/ui/icons/settings.js +4 -0
  895. package/dist/components/ui/icons/share.d.ts +7 -0
  896. package/dist/components/ui/icons/share.js +4 -0
  897. package/dist/components/ui/icons/shield-check.d.ts +7 -0
  898. package/dist/components/ui/icons/shield-check.js +4 -0
  899. package/dist/components/ui/icons/shield.d.ts +7 -0
  900. package/dist/components/ui/icons/shield.js +4 -0
  901. package/dist/components/ui/icons/smartphone.d.ts +7 -0
  902. package/dist/components/ui/icons/smartphone.js +4 -0
  903. package/dist/components/ui/icons/snowflake.d.ts +7 -0
  904. package/dist/components/ui/icons/snowflake.js +4 -0
  905. package/dist/components/ui/icons/sort.d.ts +7 -0
  906. package/dist/components/ui/icons/sort.js +4 -0
  907. package/dist/components/ui/icons/sparkles.d.ts +7 -0
  908. package/dist/components/ui/icons/sparkles.js +4 -0
  909. package/dist/components/ui/icons/star.d.ts +7 -0
  910. package/dist/components/ui/icons/star.js +4 -0
  911. package/dist/components/ui/icons/stop.d.ts +7 -0
  912. package/dist/components/ui/icons/stop.js +4 -0
  913. package/dist/components/ui/icons/sun.d.ts +7 -0
  914. package/dist/components/ui/icons/sun.js +4 -0
  915. package/dist/components/ui/icons/table.d.ts +7 -0
  916. package/dist/components/ui/icons/table.js +4 -0
  917. package/dist/components/ui/icons/terminal.d.ts +7 -0
  918. package/dist/components/ui/icons/terminal.js +4 -0
  919. package/dist/components/ui/icons/thumbs-down.d.ts +7 -0
  920. package/dist/components/ui/icons/thumbs-down.js +4 -0
  921. package/dist/components/ui/icons/thumbs-up.d.ts +7 -0
  922. package/dist/components/ui/icons/thumbs-up.js +4 -0
  923. package/dist/components/ui/icons/trash-2.d.ts +7 -0
  924. package/dist/components/ui/icons/trash-2.js +4 -0
  925. package/dist/components/ui/icons/twitter.d.ts +7 -0
  926. package/dist/components/ui/icons/twitter.js +4 -0
  927. package/dist/components/ui/icons/underline.d.ts +7 -0
  928. package/dist/components/ui/icons/underline.js +4 -0
  929. package/dist/components/ui/icons/upload.d.ts +7 -0
  930. package/dist/components/ui/icons/upload.js +4 -0
  931. package/dist/components/ui/icons/user.d.ts +7 -0
  932. package/dist/components/ui/icons/user.js +4 -0
  933. package/dist/components/ui/icons/users.d.ts +7 -0
  934. package/dist/components/ui/icons/users.js +4 -0
  935. package/dist/components/ui/icons/wrench.d.ts +7 -0
  936. package/dist/components/ui/icons/wrench.js +4 -0
  937. package/dist/components/ui/icons/x-circle.d.ts +7 -0
  938. package/dist/components/ui/icons/x-circle.js +4 -0
  939. package/dist/components/ui/icons/x.d.ts +7 -0
  940. package/dist/components/ui/icons/x.js +4 -0
  941. package/dist/components/ui/icons/zap.d.ts +7 -0
  942. package/dist/components/ui/icons/zap.js +4 -0
  943. package/dist/components/ui/kanban.a11y.d.ts +10 -0
  944. package/dist/components/ui/kanban.a11y.js +1 -0
  945. package/dist/components/ui/kanban.context.d.ts +18 -0
  946. package/dist/components/ui/kanban.context.js +2 -0
  947. package/dist/components/ui/kanban.types.d.ts +56 -0
  948. package/dist/components/ui/kanban.types.js +1 -0
  949. package/dist/components/ui/kanban.utils.d.ts +14 -0
  950. package/dist/components/ui/kanban.utils.js +1 -0
  951. package/dist/components/ui/stepper/index.d.ts +2 -0
  952. package/dist/components/ui/stepper/index.js +7 -0
  953. package/dist/components/ui/stepper/step.d.ts +10 -0
  954. package/dist/components/ui/stepper/step.js +6 -0
  955. package/dist/components/ui/stepper/stepper.d.ts +19 -0
  956. package/dist/components/ui/stepper/stepper.js +3 -0
  957. package/dist/components/ui/text-effects/AnimatedGradientText.d.ts +9 -0
  958. package/dist/components/ui/text-effects/AnimatedGradientText.js +3 -0
  959. package/dist/components/ui/text-effects/CountingNumber.d.ts +15 -0
  960. package/dist/components/ui/text-effects/CountingNumber.js +3 -0
  961. package/dist/components/ui/text-effects/FlipWords.d.ts +10 -0
  962. package/dist/components/ui/text-effects/FlipWords.js +6 -0
  963. package/dist/components/ui/text-effects/HighlightText.d.ts +9 -0
  964. package/dist/components/ui/text-effects/HighlightText.js +7 -0
  965. package/dist/components/ui/text-effects/MorphingText.d.ts +10 -0
  966. package/dist/components/ui/text-effects/MorphingText.js +6 -0
  967. package/dist/components/ui/text-effects/ShimmeringText.d.ts +9 -0
  968. package/dist/components/ui/text-effects/ShimmeringText.js +2 -0
  969. package/dist/components/ui/text-effects/SlidingNumber.d.ts +16 -0
  970. package/dist/components/ui/text-effects/SlidingNumber.js +6 -0
  971. package/dist/components/ui/text-effects/StreamingText.d.ts +26 -0
  972. package/dist/components/ui/text-effects/StreamingText.js +3 -0
  973. package/dist/components/ui/text-effects/TextReveal.d.ts +11 -0
  974. package/dist/components/ui/text-effects/TextReveal.js +3 -0
  975. package/dist/components/ui/text-effects/index.d.ts +15 -0
  976. package/dist/components/ui/text-effects/index.js +15 -0
  977. package/dist/hooks/use-dismissed.d.ts +1 -0
  978. package/dist/hooks/use-dismissed.js +2 -0
  979. package/dist/hooks/use-is-in-view.d.ts +12 -0
  980. package/dist/hooks/use-is-in-view.js +2 -0
  981. package/dist/hooks/use-mobile.d.ts +1 -0
  982. package/dist/hooks/use-mobile.js +2 -0
  983. package/dist/hooks/use-motion-value-state.d.ts +3 -0
  984. package/dist/hooks/use-motion-value-state.js +2 -0
  985. package/dist/hooks/use-reduced-motion.d.ts +1 -0
  986. package/dist/hooks/use-reduced-motion.js +2 -0
  987. package/dist/hooks/use-safe-timeout.d.ts +5 -0
  988. package/dist/hooks/use-safe-timeout.js +1 -0
  989. package/dist/hooks/use-shader-preset.d.ts +12 -0
  990. package/dist/hooks/use-shader-preset.js +4 -0
  991. package/dist/hooks/use-toast.d.ts +44 -0
  992. package/dist/hooks/use-toast.js +2 -0
  993. package/dist/hooks/use-token.d.ts +9 -0
  994. package/dist/hooks/use-token.js +28 -0
  995. package/dist/hooks/useHotkey.d.ts +3 -0
  996. package/dist/hooks/useHotkey.js +37 -0
  997. package/dist/index.js +139 -0
  998. package/dist/lib/ThemeProvider.d.ts +21 -0
  999. package/dist/lib/ThemeProvider.js +2 -0
  1000. package/dist/lib/animation.tokens.d.ts +249 -0
  1001. package/dist/lib/animation.tokens.js +1 -0
  1002. package/dist/lib/get-strict-context.d.ts +9 -0
  1003. package/dist/lib/get-strict-context.js +2 -0
  1004. package/dist/lib/merge-messages.d.ts +7 -0
  1005. package/dist/lib/merge-messages.js +19 -0
  1006. package/dist/lib/motion.d.ts +281 -0
  1007. package/dist/lib/motion.js +4 -0
  1008. package/dist/lib/patterns.d.ts +11 -0
  1009. package/dist/lib/patterns.js +174 -0
  1010. package/dist/lib/react-utils.d.ts +12 -0
  1011. package/dist/lib/react-utils.js +1 -0
  1012. package/dist/lib/shader-presets.d.ts +15 -0
  1013. package/dist/lib/shader-presets.js +1 -0
  1014. package/dist/lib/tokens.config.d.ts +796 -0
  1015. package/dist/lib/tokens.config.test.d.ts +1 -0
  1016. package/dist/lib/utils.d.ts +4 -0
  1017. package/dist/lib/utils.js +1 -0
  1018. package/dist/lib-index.d.ts +29 -0
  1019. package/dist/messages/en.d.ts +3 -0
  1020. package/dist/messages/en.js +1 -0
  1021. package/dist/messages/it.d.ts +3 -0
  1022. package/dist/messages/it.js +1 -0
  1023. package/dist/site.config.d.ts +25 -0
  1024. package/dist/test/PublicSeedTestProvider.d.ts +16 -0
  1025. package/dist/test/PublicSeedTestProvider.js +25 -0
  1026. package/dist/tokens.d.ts +188 -0
  1027. package/eslint-rules/nadicode/__tests__/rules.test.js +2143 -0
  1028. package/eslint-rules/nadicode/config.js +154 -0
  1029. package/eslint-rules/nadicode/flat-config.mjs +16 -0
  1030. package/eslint-rules/nadicode/index.js +145 -0
  1031. package/eslint-rules/nadicode/rules/__tests__/no-direct-motion-import.test.js +72 -0
  1032. package/eslint-rules/nadicode/rules/__tests__/no-diy-backdrop-blur.test.js +115 -0
  1033. package/eslint-rules/nadicode/rules/__tests__/no-forbidden-tokens.test.js +97 -0
  1034. package/eslint-rules/nadicode/rules/__tests__/no-handcoded-heading.test.js +107 -0
  1035. package/eslint-rules/nadicode/rules/__tests__/no-hardcoded-motion-transition.test.js +149 -0
  1036. package/eslint-rules/nadicode/rules/__tests__/require-aria-hidden-on-decorative-icon.test.js +73 -0
  1037. package/eslint-rules/nadicode/rules/__tests__/require-focus-ring-with-outline-none.test.js +65 -0
  1038. package/eslint-rules/nadicode/rules/__tests__/require-focus-visible-with-group-hover.test.js +60 -0
  1039. package/eslint-rules/nadicode/rules/no-admin-arbitrary-text-size.js +53 -0
  1040. package/eslint-rules/nadicode/rules/no-admin-manual-chart-bars.js +46 -0
  1041. package/eslint-rules/nadicode/rules/no-app-primitive-composition.js +58 -0
  1042. package/eslint-rules/nadicode/rules/no-arbitrary-chart-color.js +65 -0
  1043. package/eslint-rules/nadicode/rules/no-barrel-imports.js +44 -0
  1044. package/eslint-rules/nadicode/rules/no-deprecated-segment-config.js +56 -0
  1045. package/eslint-rules/nadicode/rules/no-derived-state-via-useeffect.js +139 -0
  1046. package/eslint-rules/nadicode/rules/no-direct-lucide-import.js +30 -0
  1047. package/eslint-rules/nadicode/rules/no-direct-motion-import.js +68 -0
  1048. package/eslint-rules/nadicode/rules/no-diy-backdrop-blur.js +82 -0
  1049. package/eslint-rules/nadicode/rules/no-external-ui-library.js +43 -0
  1050. package/eslint-rules/nadicode/rules/no-falsy-and-render.js +106 -0
  1051. package/eslint-rules/nadicode/rules/no-forbidden-bespoke-chat-primitive.js +54 -0
  1052. package/eslint-rules/nadicode/rules/no-forbidden-chat-class-usage.js +53 -0
  1053. package/eslint-rules/nadicode/rules/no-forbidden-tokens.js +153 -0
  1054. package/eslint-rules/nadicode/rules/no-forward-ref.js +44 -0
  1055. package/eslint-rules/nadicode/rules/no-framer-motion-import.js +36 -0
  1056. package/eslint-rules/nadicode/rules/no-handcoded-badge.js +132 -0
  1057. package/eslint-rules/nadicode/rules/no-handcoded-empty-state.js +82 -0
  1058. package/eslint-rules/nadicode/rules/no-handcoded-heading.js +273 -0
  1059. package/eslint-rules/nadicode/rules/no-hardcoded-inline-color.js +233 -0
  1060. package/eslint-rules/nadicode/rules/no-hardcoded-inline-duration.js +98 -0
  1061. package/eslint-rules/nadicode/rules/no-hardcoded-inline-spacing.js +84 -0
  1062. package/eslint-rules/nadicode/rules/no-hardcoded-inline-zindex.js +54 -0
  1063. package/eslint-rules/nadicode/rules/no-hardcoded-motion-transition.js +139 -0
  1064. package/eslint-rules/nadicode/rules/no-has-svg-selector.js +51 -0
  1065. package/eslint-rules/nadicode/rules/no-inline-default-nonprimitive-props.js +93 -0
  1066. package/eslint-rules/nadicode/rules/no-layout-property-animation.js +84 -0
  1067. package/eslint-rules/nadicode/rules/no-low-contrast-text-opacity.js +67 -0
  1068. package/eslint-rules/nadicode/rules/no-mutating-sort-on-state.js +136 -0
  1069. package/eslint-rules/nadicode/rules/no-nested-glass.js +64 -0
  1070. package/eslint-rules/nadicode/rules/no-object-in-effect-deps.js +48 -0
  1071. package/eslint-rules/nadicode/rules/no-off-grid-spacing.js +69 -0
  1072. package/eslint-rules/nadicode/rules/no-raw-recharts-import.js +36 -0
  1073. package/eslint-rules/nadicode/rules/no-raw-tailwind-palette.js +57 -0
  1074. package/eslint-rules/nadicode/rules/no-redirect-in-try-catch.js +129 -0
  1075. package/eslint-rules/nadicode/rules/no-regexp-in-render.js +140 -0
  1076. package/eslint-rules/nadicode/rules/no-runtime-api-in-use-cache.js +130 -0
  1077. package/eslint-rules/nadicode/rules/no-screen-height-classes.js +72 -0
  1078. package/eslint-rules/nadicode/rules/no-ssr-false-on-page.js +101 -0
  1079. package/eslint-rules/nadicode/rules/no-unregistered-glass.js +125 -0
  1080. package/eslint-rules/nadicode/rules/no-unstable-cache.js +36 -0
  1081. package/eslint-rules/nadicode/rules/require-action-schema.js +142 -0
  1082. package/eslint-rules/nadicode/rules/require-admin-sidebar-group-label.js +39 -0
  1083. package/eslint-rules/nadicode/rules/require-aria-hidden-on-decorative-icon.js +76 -0
  1084. package/eslint-rules/nadicode/rules/require-aria-label-on-nav.js +44 -0
  1085. package/eslint-rules/nadicode/rules/require-auth-in-server-action.js +203 -0
  1086. package/eslint-rules/nadicode/rules/require-chart-empty-handler.js +67 -0
  1087. package/eslint-rules/nadicode/rules/require-dashboard-loading-skeleton.js +78 -0
  1088. package/eslint-rules/nadicode/rules/require-effect-cleanup.js +116 -0
  1089. package/eslint-rules/nadicode/rules/require-focus-ring-with-outline-none.js +48 -0
  1090. package/eslint-rules/nadicode/rules/require-focus-visible-with-group-hover.js +57 -0
  1091. package/eslint-rules/nadicode/rules/require-form-action.js +32 -0
  1092. package/eslint-rules/nadicode/rules/require-glass-on-floating-ui.js +105 -0
  1093. package/eslint-rules/nadicode/rules/require-input-name.js +56 -0
  1094. package/eslint-rules/nadicode/rules/require-lazy-motion-provider.js +47 -0
  1095. package/eslint-rules/nadicode/rules/require-lazy-state-init.js +61 -0
  1096. package/eslint-rules/nadicode/rules/require-loading-fallback-on-dynamic.js +58 -0
  1097. package/eslint-rules/nadicode/rules/require-metadata-on-page.js +96 -0
  1098. package/eslint-rules/nadicode/rules/require-overflow-x-on-table-wrapper.js +59 -0
  1099. package/eslint-rules/nadicode/rules/require-passive-event-listener.js +92 -0
  1100. package/eslint-rules/nadicode/rules/require-reduced-motion-on-motion-import.js +112 -0
  1101. package/eslint-rules/nadicode/rules/require-scroll-containment-on-overlay.js +69 -0
  1102. package/eslint-rules/nadicode/rules/require-sidebar-trigger.js +52 -0
  1103. package/eslint-rules/nadicode/rules/require-sizes-on-fill-image.js +65 -0
  1104. package/eslint-rules/nadicode/rules/require-stagger-on-metric-grid.js +74 -0
  1105. package/eslint-rules/nadicode/rules/require-touch-target-on-interactive.js +146 -0
  1106. package/eslint-rules/nadicode/rules/require-usememo-for-render-filter-sort.js +172 -0
  1107. package/eslint-rules/nadicode/rules/require-usesyncexternalstore-for-subscriptions.js +117 -0
  1108. package/eslint-rules/nadicode/utils.js +302 -0
  1109. package/package.json +1673 -0
  1110. package/scripts/ds-check.mjs +969 -0
  1111. package/scripts/ds-update.mjs +50 -0
  1112. package/scripts/sync-seed-skill.mjs +64 -0
  1113. package/src/lib/tokens.config.js +561 -0
  1114. package/tailwind.config.js +300 -0
  1115. package/tailwind.js +42 -0
  1116. package/templates/consumer-eslint.config.mjs +63 -0
@@ -0,0 +1,2143 @@
1
+ import { describe } from "vitest";
2
+ import { RuleTester } from "eslint";
3
+
4
+ import noAdminArbitraryTextSize from "../rules/no-admin-arbitrary-text-size.js";
5
+ import noAdminManualChartBars from "../rules/no-admin-manual-chart-bars.js";
6
+ import noAppPrimitiveComposition from "../rules/no-app-primitive-composition.js";
7
+ import noBarrelImports from "../rules/no-barrel-imports.js";
8
+ import noDerivedStateViaUseeffect from "../rules/no-derived-state-via-useeffect.js";
9
+ import noDirectLucideImport from "../rules/no-direct-lucide-import.js";
10
+ import noExternalUiLibrary from "../rules/no-external-ui-library.js";
11
+ import noFalsyAndRender from "../rules/no-falsy-and-render.js";
12
+ import noForbiddenBespokeChatPrimitive from "../rules/no-forbidden-bespoke-chat-primitive.js";
13
+ import noForbiddenChatClassUsage from "../rules/no-forbidden-chat-class-usage.js";
14
+ import noForbiddenTokens from "../rules/no-forbidden-tokens.js";
15
+ import noForwardRef from "../rules/no-forward-ref.js";
16
+ import noHardcodedInlineColor from "../rules/no-hardcoded-inline-color.js";
17
+ import noHardcodedInlineDuration from "../rules/no-hardcoded-inline-duration.js";
18
+ import noHardcodedInlineSpacing from "../rules/no-hardcoded-inline-spacing.js";
19
+ import noHardcodedInlineZindex from "../rules/no-hardcoded-inline-zindex.js";
20
+ import noInlineDefaultNonprimitiveProps from "../rules/no-inline-default-nonprimitive-props.js";
21
+ import noMutatingSortOnState from "../rules/no-mutating-sort-on-state.js";
22
+ import noNestedGlass from "../rules/no-nested-glass.js";
23
+ import noUnregisteredGlass from "../rules/no-unregistered-glass.js";
24
+ import noObjectInEffectDeps from "../rules/no-object-in-effect-deps.js";
25
+ import noHasSvgSelector from "../rules/no-has-svg-selector.js";
26
+ import noOffGridSpacing from "../rules/no-off-grid-spacing.js";
27
+ import noRawTailwindPalette from "../rules/no-raw-tailwind-palette.js";
28
+ import noScreenHeightClasses from "../rules/no-screen-height-classes.js";
29
+ import noRegexpInRender from "../rules/no-regexp-in-render.js";
30
+ import requireActionSchema from "../rules/require-action-schema.js";
31
+ import requireAdminSidebarGroupLabel from "../rules/require-admin-sidebar-group-label.js";
32
+ import requireFormAction from "../rules/require-form-action.js";
33
+ import requireInputName from "../rules/require-input-name.js";
34
+ import requireLazyStateInit from "../rules/require-lazy-state-init.js";
35
+ import requireLoadingFallbackOnDynamic from "../rules/require-loading-fallback-on-dynamic.js";
36
+ import requireOverflowXOnTableWrapper from "../rules/require-overflow-x-on-table-wrapper.js";
37
+ import requireReducedMotionOnMotionImport from "../rules/require-reduced-motion-on-motion-import.js";
38
+ import requireScrollContainmentOnOverlay from "../rules/require-scroll-containment-on-overlay.js";
39
+ import requireTouchTargetOnInteractive from "../rules/require-touch-target-on-interactive.js";
40
+ import requireUsememoForRenderFilterSort from "../rules/require-usememo-for-render-filter-sort.js";
41
+ import requireUsesyncexternalstoreForSubscriptions from "../rules/require-usesyncexternalstore-for-subscriptions.js";
42
+ import noRedirectInTryCatch from "../rules/no-redirect-in-try-catch.js";
43
+ import noFramerMotionImport from "../rules/no-framer-motion-import.js";
44
+ import requireSizesOnFillImage from "../rules/require-sizes-on-fill-image.js";
45
+ import requireEffectCleanup from "../rules/require-effect-cleanup.js";
46
+ import requirePassiveEventListener from "../rules/require-passive-event-listener.js";
47
+ import noLayoutPropertyAnimation from "../rules/no-layout-property-animation.js";
48
+ import noUnstableCache from "../rules/no-unstable-cache.js";
49
+ import noDeprecatedSegmentConfig from "../rules/no-deprecated-segment-config.js";
50
+ import noRuntimeApiInUseCache from "../rules/no-runtime-api-in-use-cache.js";
51
+ import requireAuthInServerAction from "../rules/require-auth-in-server-action.js";
52
+ import noRawRechartsImport from "../rules/no-raw-recharts-import.js";
53
+ import requireChartEmptyHandler from "../rules/require-chart-empty-handler.js";
54
+ import noArbitraryChartColor from "../rules/no-arbitrary-chart-color.js";
55
+ import requireDashboardLoadingSkeleton from "../rules/require-dashboard-loading-skeleton.js";
56
+ import noLowContrastTextOpacity from "../rules/no-low-contrast-text-opacity.js";
57
+ import requireSidebarTrigger from "../rules/require-sidebar-trigger.js";
58
+ import requireStaggerOnMetricGrid from "../rules/require-stagger-on-metric-grid.js";
59
+
60
+ const tester = new RuleTester({
61
+ languageOptions: {
62
+ ecmaVersion: "latest",
63
+ sourceType: "module",
64
+ parserOptions: {
65
+ ecmaFeatures: { jsx: true },
66
+ },
67
+ },
68
+ });
69
+
70
+ // ── Design system rules ──────────────────────────────────────────────
71
+
72
+ describe("nadicode design system rules", () => {
73
+ tester.run("no-raw-tailwind-palette", noRawTailwindPalette, {
74
+ valid: [{ code: "const x = <div className='bg-surface text-text-primary' />;" }],
75
+ invalid: [
76
+ {
77
+ code: "const x = <div className='bg-zinc-950 text-blue-500' />;",
78
+ errors: [{ message: /semantic token classes/i }, { message: /semantic token classes/i }],
79
+ },
80
+ ],
81
+ });
82
+
83
+ tester.run("no-direct-lucide-import", noDirectLucideImport, {
84
+ valid: [
85
+ {
86
+ filename: "/repo/src/components/ui/icons/home.tsx",
87
+ code: "import { Home } from 'lucide-react'; export const x = Home;",
88
+ },
89
+ ],
90
+ invalid: [
91
+ {
92
+ filename: "/repo/src/components/ui/Button.tsx",
93
+ code: "import { Home } from 'lucide-react'; export const x = Home;",
94
+ errors: [{ message: /animated icons/i }],
95
+ },
96
+ ],
97
+ });
98
+
99
+ tester.run("no-forbidden-tokens", noForbiddenTokens, {
100
+ valid: [{ code: "const x = <div className='text-text-primary bg-overlay/80' />;" }],
101
+ invalid: [
102
+ {
103
+ code: "const x = <div className='text-foreground border-error bg-black/80' />;",
104
+ output: "const x = <div className='text-text-primary border-destructive bg-overlay/80' />;",
105
+ errors: [
106
+ { message: /text-text-primary or text-text-secondary/ },
107
+ { message: /border-destructive/ },
108
+ { message: /bg-overlay\/80/ },
109
+ ],
110
+ },
111
+ ],
112
+ });
113
+
114
+ tester.run("no-screen-height-classes", noScreenHeightClasses, {
115
+ valid: [
116
+ { code: "const x = <div className='min-h-dvh h-full' />;" },
117
+ { code: "const x = <div className={cn('max-h-dvh', className)} />;" },
118
+ { code: "const classes = cva('min-h-dvh bg-surface');" },
119
+ ],
120
+ invalid: [
121
+ {
122
+ code: "const x = <div className='h-screen w-full' />;",
123
+ errors: [{ message: /h-dvh/ }],
124
+ },
125
+ {
126
+ code: "const x = <div className={cn('min-h-screen bg-surface')} />;",
127
+ errors: [{ message: /min-h-dvh/ }],
128
+ },
129
+ {
130
+ code: "const classes = cva('max-h-screen overflow-y-auto');",
131
+ errors: [{ message: /max-h-dvh/ }],
132
+ },
133
+ ],
134
+ });
135
+
136
+ tester.run("no-external-ui-library", noExternalUiLibrary, {
137
+ valid: [{ code: "import { Button } from '@/components/ui/Button';" }],
138
+ invalid: [
139
+ {
140
+ code: "import { Button } from '@mui/material';",
141
+ errors: [{ message: /External UI library/i }],
142
+ },
143
+ ],
144
+ });
145
+
146
+ tester.run("no-hardcoded-inline-color", noHardcodedInlineColor, {
147
+ valid: [
148
+ { code: "const x = <div style={{ color: 'var(--color-text-primary)' }} />;" },
149
+ { code: "const x = <div style={{ color: 'inherit' }} />;" },
150
+ { code: "const x = <div style={{ color: 'transparent' }} />;" },
151
+ { code: "const x = <div style={{ color: 'currentColor' }} />;" },
152
+ ],
153
+ invalid: [
154
+ {
155
+ code: "const x = <div style={{ color: '#fff' }} />;",
156
+ errors: [{ message: /hardcoded inline colors/i }],
157
+ },
158
+ {
159
+ code: "const x = <div style={{ boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)' }} />;",
160
+ errors: [{ message: /hardcoded inline colors/i }],
161
+ },
162
+ {
163
+ code: "const x = <div style={{ textShadow: '1px 1px 2px #000' }} />;",
164
+ errors: [{ message: /hardcoded inline colors/i }],
165
+ },
166
+ {
167
+ code: "const x = <div style={{ backgroundColor: 'rgb(255, 0, 0)' }} />;",
168
+ errors: [{ message: /hardcoded inline colors/i }],
169
+ },
170
+ {
171
+ code: "const x = <div style={{ borderColor: 'hsl(210, 50%, 50%)' }} />;",
172
+ errors: [{ message: /hardcoded inline colors/i }],
173
+ },
174
+ ],
175
+ });
176
+
177
+ tester.run("no-hardcoded-inline-spacing", noHardcodedInlineSpacing, {
178
+ valid: [
179
+ { code: "const x = <div style={{ marginTop: tokenSpacing }} />;" },
180
+ { code: "const x = <div style={{ padding: 'var(--spacing-md)' }} />;" },
181
+ ],
182
+ invalid: [
183
+ {
184
+ code: "const x = <div style={{ padding: '16px' }} />;",
185
+ errors: [{ message: /hardcoded inline spacing/i }],
186
+ },
187
+ {
188
+ code: "const x = <div style={{ marginTop: '1.5rem' }} />;",
189
+ errors: [{ message: /hardcoded inline spacing/i }],
190
+ },
191
+ {
192
+ code: "const x = <div style={{ gap: 8 }} />;",
193
+ errors: [{ message: /hardcoded inline spacing/i }],
194
+ },
195
+ {
196
+ code: "const x = <div style={{ width: '200px' }} />;",
197
+ errors: [{ message: /hardcoded inline spacing/i }],
198
+ },
199
+ ],
200
+ });
201
+
202
+ tester.run("no-hardcoded-inline-duration", noHardcodedInlineDuration, {
203
+ valid: [
204
+ { code: "const x = <div style={{ transitionDuration: 'var(--motion-fast)' }} />;" },
205
+ { code: "const x = <div style={{ animation: 'float var(--duration-normal) ease-in-out infinite' }} />;" },
206
+ { code: "const x = <div style={{ transition: 'opacity var(--duration-fast) ease-out' }} />;" },
207
+ { code: "const x = <div style={{ animation: 'spin infinite linear' }} />;" },
208
+ ],
209
+ invalid: [
210
+ {
211
+ code: "const x = <div style={{ transitionDuration: '120ms' }} />;",
212
+ errors: [{ message: /hardcoded inline duration/i }],
213
+ },
214
+ {
215
+ code: "const x = <div style={{ animation: 'float 2s ease-in-out infinite' }} />;",
216
+ errors: [{ message: /hardcoded inline duration/i }],
217
+ },
218
+ {
219
+ code: "const x = <div style={{ transition: 'opacity 120ms ease-out, transform 200ms ease-in' }} />;",
220
+ errors: [{ message: /hardcoded inline duration/i }],
221
+ },
222
+ {
223
+ code: "const x = <div style={{ transition: 'opacity 120ms var(--ease-out)' }} />;",
224
+ errors: [{ message: /hardcoded inline duration/i }],
225
+ },
226
+ ],
227
+ });
228
+
229
+ tester.run("no-hardcoded-inline-zindex", noHardcodedInlineZindex, {
230
+ valid: [{ code: "const x = <div style={{ zIndex: layer }} />;" }],
231
+ invalid: [
232
+ {
233
+ code: "const x = <div style={{ zIndex: 40 }} />;",
234
+ errors: [{ message: /hardcoded inline z-index/i }],
235
+ },
236
+ ],
237
+ });
238
+
239
+ tester.run("no-admin-arbitrary-text-size", noAdminArbitraryTextSize, {
240
+ valid: [
241
+ {
242
+ filename: "/repo/src/components/admin/dashboard/Widget.tsx",
243
+ code: "const x = <div className=\"text-sm\" />;",
244
+ },
245
+ ],
246
+ invalid: [
247
+ {
248
+ filename: "/repo/src/components/admin/dashboard/Widget.tsx",
249
+ code: "const x = <div className=\"text-[13px]\" />;",
250
+ errors: [{ message: /not allowed in admin UI/i }],
251
+ },
252
+ ],
253
+ });
254
+
255
+ tester.run("no-forbidden-chat-class-usage", noForbiddenChatClassUsage, {
256
+ valid: [
257
+ {
258
+ filename: "/repo/src/components/admin/chat/Thread.tsx",
259
+ code: "const x = <div className=\"glass-panel\" />;",
260
+ },
261
+ ],
262
+ invalid: [
263
+ {
264
+ filename: "/repo/src/components/admin/chat/Thread.tsx",
265
+ code: "const x = <div className=\"chat-message\" />;",
266
+ errors: [{ message: /not allowed in admin UI/i }],
267
+ },
268
+ ],
269
+ });
270
+
271
+ tester.run("no-forbidden-bespoke-chat-primitive", noForbiddenBespokeChatPrimitive, {
272
+ valid: [
273
+ {
274
+ filename: "/repo/src/components/admin/chat/Thread.tsx",
275
+ code: "import { ToolCallCard } from '@/components/ui/ToolCallCard';",
276
+ },
277
+ ],
278
+ invalid: [
279
+ {
280
+ filename: "/repo/src/components/admin/chat/Thread.tsx",
281
+ code: "import MessageBubble from './MessageBubble';",
282
+ errors: [{ message: /not allowed in admin chat/i }],
283
+ },
284
+ ],
285
+ });
286
+
287
+ tester.run("no-admin-manual-chart-bars", noAdminManualChartBars, {
288
+ valid: [
289
+ {
290
+ filename: "/repo/src/components/admin/dashboard/Stats.tsx",
291
+ code: "import { BarChart } from '@/components/ui/charts/BarChart'; const x = <BarChart />;",
292
+ },
293
+ ],
294
+ invalid: [
295
+ {
296
+ filename: "/repo/src/components/admin/dashboard/Stats.tsx",
297
+ code:
298
+ "const value = 20; const x = <div className=\"bg-[rgb(var(--chart-1))]\" style={{ width: `${value}%` }} />;",
299
+ errors: [{ message: /Manual proportional bars with inline width are not allowed/i }],
300
+ },
301
+ ],
302
+ });
303
+
304
+ tester.run("require-admin-sidebar-group-label", requireAdminSidebarGroupLabel, {
305
+ valid: [
306
+ {
307
+ filename: "/repo/src/components/admin/AdminShell.tsx",
308
+ code: "const x = <SidebarMenu><SidebarGroupLabel>Ops</SidebarGroupLabel></SidebarMenu>;",
309
+ },
310
+ ],
311
+ invalid: [
312
+ {
313
+ filename: "/repo/src/components/admin/AdminShell.tsx",
314
+ code: "const x = <SidebarMenu />;",
315
+ errors: [{ message: /must include SidebarGroupLabel/i }],
316
+ },
317
+ ],
318
+ });
319
+
320
+ tester.run("require-action-schema", requireActionSchema, {
321
+ valid: [
322
+ {
323
+ filename: "/repo/src/actions/create-user.ts",
324
+ code: `
325
+ 'use server';
326
+ import { z } from 'zod';
327
+ const Schema = z.object({ id: z.string() });
328
+ export async function createUser(input) {
329
+ const parsed = Schema.parse(input);
330
+ return parsed;
331
+ }
332
+ `,
333
+ },
334
+ {
335
+ filename: "/repo/src/actions/create-user.ts",
336
+ code: `
337
+ 'use server';
338
+ import { z } from 'zod';
339
+ const Schema = z.object({ id: z.string() });
340
+ export async function createUser(input) {
341
+ const result = Schema.safeParse(input);
342
+ return result;
343
+ }
344
+ `,
345
+ },
346
+ ],
347
+ invalid: [
348
+ {
349
+ filename: "/repo/src/actions/create-user.ts",
350
+ code: `
351
+ 'use server';
352
+ export async function createUser(input) {
353
+ return input;
354
+ }
355
+ `,
356
+ errors: [{ message: /must validate input with Zod/i }],
357
+ },
358
+ {
359
+ filename: "/repo/src/actions/delete-user.ts",
360
+ code: `
361
+ 'use server';
362
+ export const deleteUser = async (id) => {
363
+ return { deleted: id };
364
+ };
365
+ `,
366
+ errors: [{ message: /must validate input with Zod/i }],
367
+ },
368
+ ],
369
+ });
370
+
371
+ tester.run("no-app-primitive-composition", noAppPrimitiveComposition, {
372
+ valid: [
373
+ {
374
+ filename: "/repo/src/app/dashboard/page.tsx",
375
+ code: `
376
+ import { Button } from '@/components/ui/Button';
377
+ import { Card } from '@/components/ui/Card';
378
+ import { Badge } from '@/components/ui/Badge';
379
+ const x = <Card><Button><Badge>OK</Badge></Button></Card>;
380
+ `,
381
+ },
382
+ {
383
+ filename: "/repo/src/components/blocks/HeroBlock.tsx",
384
+ code: `
385
+ import { Button } from '@/components/ui/Button';
386
+ import { Card } from '@/components/ui/Card';
387
+ import { Badge } from '@/components/ui/Badge';
388
+ import { Avatar } from '@/components/ui/Avatar';
389
+ import { Dialog } from '@/components/ui/Dialog';
390
+ const x = <div />;
391
+ `,
392
+ },
393
+ {
394
+ filename: "/repo/src/app/dashboard/page.tsx",
395
+ code: `
396
+ import { Button } from '@/components/ui/Button';
397
+ import { Card } from '@/components/ui/Card';
398
+ import { Badge } from '@/components/ui/Badge';
399
+ import { HomeIcon } from '@/components/ui/icons/home';
400
+ import { UserIcon } from '@/components/ui/icons/user';
401
+ const x = <div />;
402
+ `,
403
+ },
404
+ {
405
+ filename: "/repo/src/app/api/auth/route.ts",
406
+ code: `
407
+ import { Button } from '@/components/ui/Button';
408
+ import { Card } from '@/components/ui/Card';
409
+ import { Badge } from '@/components/ui/Badge';
410
+ import { Avatar } from '@/components/ui/Avatar';
411
+ const x = <div />;
412
+ `,
413
+ },
414
+ {
415
+ filename: "/repo/src/app/dashboard/page.test.tsx",
416
+ code: `
417
+ import { Button } from '@/components/ui/Button';
418
+ import { Card } from '@/components/ui/Card';
419
+ import { Badge } from '@/components/ui/Badge';
420
+ import { Avatar } from '@/components/ui/Avatar';
421
+ const x = <div />;
422
+ `,
423
+ },
424
+ ],
425
+ invalid: [
426
+ {
427
+ filename: "/repo/src/app/dashboard/page.tsx",
428
+ code: `
429
+ import { Button } from '@/components/ui/Button';
430
+ import { Card } from '@/components/ui/Card';
431
+ import { Badge } from '@/components/ui/Badge';
432
+ import { Avatar } from '@/components/ui/Avatar';
433
+ const x = <div />;
434
+ `,
435
+ errors: [
436
+ { message: /4 UI primitives/ },
437
+ { message: /4 UI primitives/ },
438
+ { message: /4 UI primitives/ },
439
+ { message: /4 UI primitives/ },
440
+ ],
441
+ },
442
+ ],
443
+ });
444
+
445
+ tester.run("require-form-action", requireFormAction, {
446
+ valid: [
447
+ { code: "const x = <form action={handleAction}></form>;" },
448
+ { code: "const x = <form onSubmit={handleSubmit}></form>;" },
449
+ { code: 'const x = <form action={action} className="space-y-4"></form>;' },
450
+ ],
451
+ invalid: [
452
+ {
453
+ code: 'const x = <form className="space-y-4"></form>;',
454
+ errors: [{ message: /must have an action, formAction, or onSubmit handler/i }],
455
+ },
456
+ {
457
+ code: 'const x = <form action=""></form>;',
458
+ errors: [{ message: /must have an action, formAction, or onSubmit handler/i }],
459
+ },
460
+ ],
461
+ });
462
+
463
+ tester.run("require-input-name", requireInputName, {
464
+ valid: [
465
+ { code: 'const x = <form action={a}><input name="email" /></form>;' },
466
+ { code: 'const x = <form action={a}><input type="submit" /></form>;' },
467
+ { code: "const x = <form action={a}><input {...field} /></form>;" },
468
+ { code: "const x = <input />;" },
469
+ { code: "const x = <form action={a}><input readOnly /></form>;" },
470
+ ],
471
+ invalid: [
472
+ {
473
+ code: 'const x = <form action={a}><input type="email" /></form>;',
474
+ errors: [{ message: /<input> inside a form must have a 'name' attribute/i }],
475
+ },
476
+ {
477
+ code: 'const x = <form action={a}><Input type="text" /></form>;',
478
+ errors: [{ message: /<Input> inside a form must have a 'name' attribute/i }],
479
+ },
480
+ {
481
+ code: "const x = <form action={a}><textarea /></form>;",
482
+ errors: [{ message: /<textarea> inside a form must have a 'name' attribute/i }],
483
+ },
484
+ ],
485
+ });
486
+
487
+ tester.run("require-overflow-x-on-table-wrapper", requireOverflowXOnTableWrapper, {
488
+ valid: [
489
+ { code: "const x = <div className='overflow-x-auto rounded-md'><Table /></div>;" },
490
+ { code: "const x = <div className='overflow-x-scroll'><Table /></div>;" },
491
+ ],
492
+ invalid: [
493
+ {
494
+ code: "const x = <div className='rounded-md'><Table /></div>;",
495
+ errors: [{ message: /overflow-x-auto/ }],
496
+ },
497
+ {
498
+ code: "const x = <div><Table /></div>;",
499
+ errors: [{ message: /overflow-x-auto/ }],
500
+ },
501
+ ],
502
+ });
503
+
504
+ tester.run("require-scroll-containment-on-overlay", requireScrollContainmentOnOverlay, {
505
+ valid: [
506
+ { code: "const x = <DialogContent className='fixed z-50 max-h-[90dvh] overflow-y-auto glass-panel' />;" },
507
+ { code: "const x = <SheetContent className='fixed z-50 max-h-full overflow-y-auto' />;" },
508
+ { code: "const x = <PopoverContent className='fixed z-50 max-h-[24rem] overflow-y-auto' />;" },
509
+ { code: "const x = <SheetContent className='p-4' />;" },
510
+ ],
511
+ invalid: [
512
+ {
513
+ code: "const x = <DialogContent className='fixed z-50 p-4' />;",
514
+ errors: [{ message: /max-h-.*and overflow-y-auto/ }],
515
+ },
516
+ {
517
+ code: "const x = <PopoverContent className='fixed z-50 max-h-[24rem]' />;",
518
+ errors: [{ message: /overflow-y-auto/ }],
519
+ },
520
+ ],
521
+ });
522
+
523
+ tester.run("require-touch-target-on-interactive", requireTouchTargetOnInteractive, {
524
+ valid: [
525
+ { code: "const x = <button className='h-11 w-11'>Click</button>;" },
526
+ { code: "const x = <button className='h-8 min-h-11'>Click</button>;" },
527
+ { code: "const x = <button className='h-8 min-h-[44px]'>Click</button>;" },
528
+ { code: "const x = <button className='h-8 w-8 after:-inset-2'>Click</button>;" },
529
+ { code: "const x = <div className='h-8 w-8 after:-inset-2' role='button' tabIndex={0}>Click</div>;" },
530
+ { code: "const x = <div className='h-6'>Not interactive</div>;" },
531
+ ],
532
+ invalid: [
533
+ {
534
+ code: "const x = <button className='h-8 w-8'>Click</button>;",
535
+ errors: [{ message: /undersized touch target class/ }],
536
+ },
537
+ {
538
+ code: "const x = <a className='h-6 p-1' href='#'>Link</a>;",
539
+ errors: [{ message: /undersized touch target class/ }],
540
+ },
541
+ {
542
+ code: "const x = <div className={cn('size-8 rounded-md')} role='button' tabIndex={0}>Open</div>;",
543
+ errors: [{ message: /undersized touch target class/ }],
544
+ },
545
+ ],
546
+ });
547
+
548
+ tester.run("no-off-grid-spacing", noOffGridSpacing, {
549
+ valid: [
550
+ { code: "const x = <div className='p-2 gap-4 m-6' />;" },
551
+ { code: "const x = <div className='p-3 gap-1.5 px-2.5' />;" },
552
+ { code: "const x = <div className='p-[12px]' />;" },
553
+ { code: "const x = <div className='gap-[8px]' />;" },
554
+ { code: "const x = <div className='m-[20px]' />;" },
555
+ { code: "const x = <div className='p-1 gap-3 m-5' />;" },
556
+ { code: "const x = <div className='md:p-[16px]' />;" },
557
+ ],
558
+ invalid: [
559
+ {
560
+ code: "const x = <div className='p-[13px]' />;",
561
+ errors: [{ message: /off the 4px grid/i }],
562
+ },
563
+ {
564
+ code: "const x = <div className='gap-[7px]' />;",
565
+ errors: [{ message: /off the 4px grid/i }],
566
+ },
567
+ {
568
+ code: "const x = <div className='m-[5px]' />;",
569
+ errors: [{ message: /off the 4px grid/i }],
570
+ },
571
+ {
572
+ code: "const x = <div className='space-y-[11px]' />;",
573
+ errors: [{ message: /off the 4px grid/i }],
574
+ },
575
+ ],
576
+ });
577
+
578
+ tester.run("no-has-svg-selector", noHasSvgSelector, {
579
+ valid: [
580
+ { code: "const x = <div className='[&>svg]:size-4' />;" },
581
+ { code: "const x = <div className='[&>[data-slot=icon]]:text-destructive' />;" },
582
+ { code: "const x = <div className='[&:has([data-slot=icon])]:grid' />;" },
583
+ { code: "const x = cn('flex items-center gap-2');" },
584
+ ],
585
+ invalid: [
586
+ {
587
+ code: "const x = <div className='[&:has(svg)]:gap-2' />;",
588
+ errors: [{ message: /data-slot sub-component/i }],
589
+ },
590
+ {
591
+ code: "const x = <div className='[&:has(>svg)]:grid' />;",
592
+ errors: [{ message: /data-slot sub-component/i }],
593
+ },
594
+ {
595
+ code: "const x = cn('[&:has(svg)]:pl-8');",
596
+ errors: [{ message: /data-slot sub-component/i }],
597
+ },
598
+ {
599
+ code: "const x = cva('[&:has(>svg)]:gap-3 flex');",
600
+ errors: [{ message: /data-slot sub-component/i }],
601
+ },
602
+ ],
603
+ });
604
+ });
605
+
606
+ // ── Reduced motion enforcement ───────────────────────────────────────
607
+
608
+ describe("reduced motion enforcement", () => {
609
+ tester.run("require-reduced-motion-on-motion-import", requireReducedMotionOnMotionImport, {
610
+ valid: [
611
+ {
612
+ code: `
613
+ import { motion } from 'motion/react';
614
+ import { useReducedMotion } from 'motion/react';
615
+ const X = () => { useReducedMotion(); return <motion.div animate={{ opacity: 1 }} /> };
616
+ `,
617
+ },
618
+ {
619
+ code: `
620
+ import { motion } from 'motion/react';
621
+ import { useMotionConfig } from '../../lib/motion';
622
+ const X = () => { const c = useMotionConfig(); return <motion.div animate={{ opacity: 1 }} /> };
623
+ `,
624
+ },
625
+ {
626
+ code: `
627
+ import { motion } from 'motion/react';
628
+ import { MotionConfig } from 'motion/react';
629
+ const X = () => <MotionConfig reducedMotion="user"><motion.div animate={{ x: 0 }} /></MotionConfig>;
630
+ `,
631
+ },
632
+ {
633
+ code: `
634
+ import { motion } from 'motion/react';
635
+ const X = () => <motion.div layoutId="x" />;
636
+ `,
637
+ },
638
+ {
639
+ code: `
640
+ const X = () => <div initial="test" animate="test" />;
641
+ `,
642
+ },
643
+ {
644
+ filename: "/repo/src/components/ui/Foo.test.tsx",
645
+ code: `
646
+ import { motion } from 'motion/react';
647
+ const X = () => <motion.div animate={{ opacity: 1 }} />;
648
+ `,
649
+ },
650
+ {
651
+ filename: "/repo/src/components/ui/icons/check.tsx",
652
+ code: `
653
+ import { motion } from 'motion/react';
654
+ const X = () => <motion.path animate={{ pathLength: 1 }} />;
655
+ `,
656
+ },
657
+ {
658
+ code: `
659
+ import { motion } from 'motion/react';
660
+ const mq = 'prefers-reduced-motion';
661
+ const X = () => <motion.div animate={{ opacity: 1 }} />;
662
+ `,
663
+ },
664
+ ],
665
+ invalid: [
666
+ {
667
+ code: `
668
+ import { motion } from 'motion/react';
669
+ const X = () => <motion.div animate={{ opacity: 1 }} />;
670
+ `,
671
+ errors: [{ message: /prefers-reduced-motion/i }],
672
+ },
673
+ {
674
+ code: `
675
+ import { motion } from 'framer-motion';
676
+ const X = () => <motion.div initial={{ y: 20 }} animate={{ y: 0 }} />;
677
+ `,
678
+ errors: [{ message: /prefers-reduced-motion/i }],
679
+ },
680
+ {
681
+ code: `
682
+ import { motion } from 'motion/react';
683
+ const X = () => <motion.div whileHover={{ scale: 1.1 }} />;
684
+ `,
685
+ errors: [{ message: /prefers-reduced-motion/i }],
686
+ },
687
+ {
688
+ code: `
689
+ import { motion, MotionConfig } from 'motion/react';
690
+ const X = () => <MotionConfig transition={{ duration: 0.3 }}><motion.div animate={{ x: 0 }} /></MotionConfig>;
691
+ `,
692
+ errors: [{ message: /prefers-reduced-motion/i }],
693
+ },
694
+ ],
695
+ });
696
+ });
697
+
698
+ // ── React 19 regression guards ───────────────────────────────────────
699
+
700
+ describe("react 19 regression guards", () => {
701
+ tester.run("no-forward-ref", noForwardRef, {
702
+ valid: [
703
+ { code: "function Button({ ref, ...props }) { return <button ref={ref} {...props} /> }" },
704
+ { code: "import * as React from 'react'; function X({ ref }) { return <div ref={ref} /> }" },
705
+ {
706
+ filename: "/repo/src/components/ui/Dialog.test.tsx",
707
+ code: "import { forwardRef } from 'react'; const X = forwardRef((props, ref) => <div />)",
708
+ },
709
+ ],
710
+ invalid: [
711
+ {
712
+ code: "import { forwardRef } from 'react'; const X = forwardRef((props, ref) => <div />)",
713
+ errors: [{ message: /ref-as-prop/i }],
714
+ },
715
+ {
716
+ code: "import * as React from 'react'; const X = React.forwardRef((props, ref) => <div />)",
717
+ errors: [{ message: /ref-as-prop/i }],
718
+ },
719
+ ],
720
+ });
721
+
722
+ tester.run("no-nested-glass", noNestedGlass, {
723
+ valid: [
724
+ { code: "const x = <div className='glass-panel p-4'><span>ok</span></div>;" },
725
+ { code: "const x = <><div className='glass-panel' /><div className='glass-floating' /></>;" },
726
+ { code: "const x = <div className='backdrop-blur-xl'><span>ok</span></div>;" },
727
+ { code: "const x = <div className='glass-panel'><div className='bg-surface p-4'>ok</div></div>;" },
728
+ ],
729
+ invalid: [
730
+ {
731
+ code: "const x = <div className='glass-panel'><div className='glass-panel'>nested</div></div>;",
732
+ errors: [{ message: /No glass-on-glass/ }],
733
+ },
734
+ {
735
+ code: "const x = <div className='glass-panel'><div className='glass-floating'>nested</div></div>;",
736
+ errors: [{ message: /No glass-on-glass/ }],
737
+ },
738
+ {
739
+ code: "const x = <div className='backdrop-blur-xl bg-surface/85'><div className='glass-panel'>nested</div></div>;",
740
+ errors: [{ message: /No glass-on-glass/ }],
741
+ },
742
+ {
743
+ code: "const x = <div className='glass-panel'><div className='backdrop-blur-sm'>nested</div></div>;",
744
+ errors: [{ message: /No glass-on-glass/ }],
745
+ },
746
+ {
747
+ code: "const x = <div className='glass-panel'><section><div className='glass-overlay'>deep</div></section></div>;",
748
+ errors: [{ message: /No glass-on-glass/ }],
749
+ },
750
+ {
751
+ code: "const x = <div className={cn('glass-panel')}><div className={cn('glass-floating')}>nested</div></div>;",
752
+ errors: [{ message: /No glass-on-glass/ }],
753
+ },
754
+ ],
755
+ });
756
+
757
+ tester.run("no-unregistered-glass", noUnregisteredGlass, {
758
+ valid: [
759
+ // Glass in allowed directory (blocks/)
760
+ {
761
+ filename: "/repo/src/components/blocks/HeroBlock.tsx",
762
+ code: "const x = <div className='glass-panel p-4'>hero</div>;",
763
+ },
764
+ // Glass in allowed directory (pages/)
765
+ {
766
+ filename: "/repo/src/components/pages/Landing.tsx",
767
+ code: "const x = <section className='glass-floating'>content</section>;",
768
+ },
769
+ // Glass in allowed directory (app/)
770
+ {
771
+ filename: "/repo/src/app/dashboard/page.tsx",
772
+ code: "const x = <div className='glass-overlay'>overlay</div>;",
773
+ },
774
+ // Glass in allowed file (Dialog.tsx)
775
+ {
776
+ filename: "/repo/src/components/ui/Dialog.tsx",
777
+ code: "const x = <div className='glass-floating'>dialog</div>;",
778
+ },
779
+ // Glass in allowed file (Card.tsx)
780
+ {
781
+ filename: "/repo/src/components/ui/Card.tsx",
782
+ code: "const x = <div className='glass-panel'>card</div>;",
783
+ },
784
+ // Glass in allowed file (ToolCallCard.tsx)
785
+ {
786
+ filename: "/repo/src/components/ui/ToolCallCard.tsx",
787
+ code: "const x = <div className='glass-panel'>tool</div>;",
788
+ },
789
+ // Glass in test file (always exempt)
790
+ {
791
+ filename: "/repo/src/components/ui/Sidebar.test.tsx",
792
+ code: "const x = <div className='glass-panel'>test</div>;",
793
+ },
794
+ // No glass classes (always valid)
795
+ {
796
+ filename: "/repo/src/components/ui/Sidebar.tsx",
797
+ code: "const x = <div className='bg-surface p-4'>sidebar</div>;",
798
+ },
799
+ // Glass in cva() call in allowed file
800
+ {
801
+ filename: "/repo/src/components/ui/Button.tsx",
802
+ code: "const v = cva('glass-panel rounded-lg');",
803
+ },
804
+ ],
805
+ invalid: [
806
+ // Glass in className on unregistered UI component
807
+ {
808
+ filename: "/repo/src/components/ui/Sidebar.tsx",
809
+ code: "const x = <div className='glass-panel'>sidebar</div>;",
810
+ errors: [{ message: /glass-panel.*not allowed/i }],
811
+ },
812
+ // Glass in cn() call on unregistered component
813
+ {
814
+ filename: "/repo/src/components/ui/Tabs.tsx",
815
+ code: "const x = <div className={cn('glass-floating', 'p-4')}>tabs</div>;",
816
+ errors: [{ message: /glass-floating.*not allowed/i }],
817
+ },
818
+ // Glass in cva() call on unregistered component
819
+ {
820
+ filename: "/repo/src/components/ui/Badge.tsx",
821
+ code: "const v = cva('glass-overlay rounded');",
822
+ errors: [{ message: /glass-overlay.*not allowed/i }],
823
+ },
824
+ // Glass in file outside all allowed paths
825
+ {
826
+ filename: "/repo/src/lib/helpers.tsx",
827
+ code: "const x = <div className='glass-panel'>helper</div>;",
828
+ errors: [{ message: /glass-panel.*not allowed/i }],
829
+ },
830
+ ],
831
+ });
832
+ });
833
+
834
+ // ── React best practices ─────────────────────────────────────────────
835
+
836
+ describe("react best practice rules", () => {
837
+ tester.run(
838
+ "no-derived-state-via-useeffect",
839
+ noDerivedStateViaUseeffect,
840
+ {
841
+ valid: [
842
+ {
843
+ code: `
844
+ function Component({ items }) {
845
+ const [count, setCount] = useState(0);
846
+ const total = useMemo(() => items.length, [items]);
847
+ return <div>{total}</div>;
848
+ }
849
+ `,
850
+ },
851
+ {
852
+ code: `
853
+ function Component({ id }) {
854
+ const [data, setData] = useState(null);
855
+ useEffect(() => {
856
+ fetch('/api/' + id).then(r => r.json()).then(setData);
857
+ }, [id]);
858
+ return <div>{data}</div>;
859
+ }
860
+ `,
861
+ },
862
+ {
863
+ code: `
864
+ function Component({ items }) {
865
+ const [a, setA] = useState(0);
866
+ const [b, setB] = useState(0);
867
+ useEffect(() => {
868
+ setA(items.length);
869
+ setB(items.length * 2);
870
+ }, [items]);
871
+ return <div>{a} {b}</div>;
872
+ }
873
+ `,
874
+ },
875
+ ],
876
+ invalid: [
877
+ {
878
+ code: `
879
+ function Component({ items }) {
880
+ const [total, setTotal] = useState(0);
881
+ useEffect(() => setTotal(items.length), [items]);
882
+ return <div>{total}</div>;
883
+ }
884
+ `,
885
+ errors: [{ message: /Avoid deriving state via useEffect/i }],
886
+ },
887
+ {
888
+ code: `
889
+ function Component({ items }) {
890
+ const [total, setTotal] = useState(0);
891
+ useEffect(() => {
892
+ const len = items.length;
893
+ setTotal(len);
894
+ }, [items]);
895
+ return <div>{total}</div>;
896
+ }
897
+ `,
898
+ errors: [{ message: /Avoid deriving state via useEffect/i }],
899
+ },
900
+ ],
901
+ },
902
+ );
903
+
904
+ tester.run(
905
+ "require-usesyncexternalstore-for-subscriptions",
906
+ requireUsesyncexternalstoreForSubscriptions,
907
+ {
908
+ valid: [
909
+ {
910
+ code: `
911
+ function Component() {
912
+ const value = useSyncExternalStore(store.subscribe, store.getSnapshot);
913
+ return <div>{value}</div>;
914
+ }
915
+ `,
916
+ },
917
+ {
918
+ code: `
919
+ function Component() {
920
+ useEffect(() => {
921
+ console.log('mounted');
922
+ return () => console.log('unmounted');
923
+ }, []);
924
+ }
925
+ `,
926
+ },
927
+ {
928
+ code: `
929
+ function Component() {
930
+ useEffect(() => {
931
+ window.addEventListener('resize', handler);
932
+ }, []);
933
+ }
934
+ `,
935
+ },
936
+ ],
937
+ invalid: [
938
+ {
939
+ code: `
940
+ function Component() {
941
+ useEffect(() => {
942
+ window.addEventListener('resize', handler);
943
+ return () => {
944
+ window.removeEventListener('resize', handler);
945
+ };
946
+ }, []);
947
+ }
948
+ `,
949
+ errors: [{ message: /useSyncExternalStore/i }],
950
+ },
951
+ {
952
+ code: `
953
+ function Component() {
954
+ useEffect(() => {
955
+ store.subscribe(handler);
956
+ return () => {
957
+ store.unsubscribe(handler);
958
+ };
959
+ }, []);
960
+ }
961
+ `,
962
+ errors: [{ message: /useSyncExternalStore/i }],
963
+ },
964
+ {
965
+ code: `
966
+ function Component() {
967
+ useEffect(() => {
968
+ emitter.on('event', handler);
969
+ return () => {
970
+ emitter.off('event', handler);
971
+ };
972
+ }, []);
973
+ }
974
+ `,
975
+ errors: [{ message: /useSyncExternalStore/i }],
976
+ },
977
+ ],
978
+ },
979
+ );
980
+
981
+ tester.run("no-object-in-effect-deps", noObjectInEffectDeps, {
982
+ valid: [
983
+ { code: `useEffect(() => {}, [id, name, count]);` },
984
+ { code: `useEffect(() => {}, [options]);` },
985
+ { code: `useEffect(() => {});` },
986
+ ],
987
+ invalid: [
988
+ {
989
+ code: `useEffect(() => {}, [{ key: 'value' }]);`,
990
+ errors: [{ message: /object or array literals/i }],
991
+ },
992
+ {
993
+ code: `useEffect(() => {}, [[1, 2, 3]]);`,
994
+ errors: [{ message: /object or array literals/i }],
995
+ },
996
+ {
997
+ code: `useEffect(() => {}, [id, { a: 1 }]);`,
998
+ errors: [{ message: /object or array literals/i }],
999
+ },
1000
+ ],
1001
+ });
1002
+
1003
+ tester.run(
1004
+ "require-usememo-for-render-filter-sort",
1005
+ requireUsememoForRenderFilterSort,
1006
+ {
1007
+ valid: [
1008
+ {
1009
+ code: `
1010
+ function MyComponent({ items }) {
1011
+ const filtered = useMemo(() => items.filter(x => x.active), [items]);
1012
+ return <div>{filtered}</div>;
1013
+ }
1014
+ `,
1015
+ },
1016
+ {
1017
+ code: `
1018
+ function MyComponent({ items }) {
1019
+ const handleClick = () => {
1020
+ const sorted = items.sort((a, b) => a - b);
1021
+ };
1022
+ return <div onClick={handleClick} />;
1023
+ }
1024
+ `,
1025
+ },
1026
+ {
1027
+ code: `
1028
+ const sorted = items.filter(x => x.active);
1029
+ function helper() {
1030
+ return data.sort((a, b) => a - b);
1031
+ }
1032
+ `,
1033
+ },
1034
+ {
1035
+ code: `
1036
+ function MyComponent({ items }) {
1037
+ return <div onClick={() => items.filter(x => x)} />;
1038
+ }
1039
+ `,
1040
+ },
1041
+ ],
1042
+ invalid: [
1043
+ {
1044
+ code: `
1045
+ function MyComponent({ items }) {
1046
+ const active = items.filter(x => x.active);
1047
+ return <div>{active}</div>;
1048
+ }
1049
+ `,
1050
+ errors: [{ message: /Wrap .filter\(\) in useMemo/i }],
1051
+ },
1052
+ {
1053
+ code: `
1054
+ function MyComponent({ items }) {
1055
+ const sorted = items.sort((a, b) => a - b);
1056
+ return <div>{sorted}</div>;
1057
+ }
1058
+ `,
1059
+ errors: [{ message: /Wrap .sort\(\) in useMemo/i }],
1060
+ },
1061
+ {
1062
+ code: `
1063
+ const MyComponent = ({ items }) => {
1064
+ const item = items.find(x => x.id === 1);
1065
+ return <div>{item}</div>;
1066
+ };
1067
+ `,
1068
+ errors: [{ message: /Wrap .find\(\) in useMemo/i }],
1069
+ },
1070
+ ],
1071
+ },
1072
+ );
1073
+
1074
+ tester.run(
1075
+ "require-loading-fallback-on-dynamic",
1076
+ requireLoadingFallbackOnDynamic,
1077
+ {
1078
+ valid: [
1079
+ {
1080
+ code: `
1081
+ import dynamic from 'next/dynamic';
1082
+ const Chart = dynamic(() => import('./Chart'), { loading: () => <p>Loading...</p> });
1083
+ `,
1084
+ },
1085
+ { code: `const Chart = dynamic(() => import('./Chart'));` },
1086
+ {
1087
+ code: `
1088
+ import something from 'other-lib';
1089
+ const x = something(() => import('./Chart'));
1090
+ `,
1091
+ },
1092
+ ],
1093
+ invalid: [
1094
+ {
1095
+ code: `
1096
+ import dynamic from 'next/dynamic';
1097
+ const Chart = dynamic(() => import('./Chart'));
1098
+ `,
1099
+ errors: [{ message: /must include a loading fallback/i }],
1100
+ },
1101
+ {
1102
+ code: `
1103
+ import dynamic from 'next/dynamic';
1104
+ const Chart = dynamic(() => import('./Chart'), { ssr: false });
1105
+ `,
1106
+ errors: [{ message: /must include a loading fallback/i }],
1107
+ },
1108
+ ],
1109
+ },
1110
+ );
1111
+
1112
+ tester.run(
1113
+ "no-inline-default-nonprimitive-props",
1114
+ noInlineDefaultNonprimitiveProps,
1115
+ {
1116
+ valid: [
1117
+ {
1118
+ code: `
1119
+ function MyComponent({ count = 0, name = 'default', active = true }) {
1120
+ return <div>{count} {name}</div>;
1121
+ }
1122
+ `,
1123
+ },
1124
+ {
1125
+ code: `
1126
+ const DEFAULT_ITEMS = [];
1127
+ function MyComponent({ items = DEFAULT_ITEMS }) {
1128
+ return <div>{items}</div>;
1129
+ }
1130
+ `,
1131
+ },
1132
+ {
1133
+ code: `
1134
+ function processData({ items = [], config = {} }) {
1135
+ return items;
1136
+ }
1137
+ `,
1138
+ },
1139
+ ],
1140
+ invalid: [
1141
+ {
1142
+ code: `
1143
+ function MyComponent({ items = [] }) {
1144
+ return <div>{items}</div>;
1145
+ }
1146
+ `,
1147
+ errors: [{ message: /inline non-primitive default props/i }],
1148
+ },
1149
+ {
1150
+ code: `
1151
+ function MyComponent({ config = {} }) {
1152
+ return <div />;
1153
+ }
1154
+ `,
1155
+ errors: [{ message: /inline non-primitive default props/i }],
1156
+ },
1157
+ {
1158
+ code: `
1159
+ const MyComponent = ({ cache = new Map() }) => {
1160
+ return <div />;
1161
+ };
1162
+ `,
1163
+ errors: [{ message: /inline non-primitive default props/i }],
1164
+ },
1165
+ ],
1166
+ },
1167
+ );
1168
+
1169
+ tester.run("no-barrel-imports", noBarrelImports, {
1170
+ valid: [
1171
+ { code: `import { Button } from './components/Button.tsx';` },
1172
+ { code: `import React from 'react';` },
1173
+ { code: `import { motion } from 'framer-motion';` },
1174
+ { code: `import { cn } from '@/lib/utils.ts';` },
1175
+ ],
1176
+ invalid: [
1177
+ {
1178
+ code: `import { Button } from './components/index.js';`,
1179
+ errors: [{ message: /barrel.*import/i }],
1180
+ },
1181
+ {
1182
+ code: `import { hooks } from './hooks/index';`,
1183
+ errors: [{ message: /barrel.*import/i }],
1184
+ },
1185
+ ],
1186
+ });
1187
+
1188
+ tester.run("no-mutating-sort-on-state", noMutatingSortOnState, {
1189
+ valid: [
1190
+ {
1191
+ code: `
1192
+ function MyComponent() {
1193
+ const [items, setItems] = useState([]);
1194
+ const sorted = [...items].sort((a, b) => a - b);
1195
+ return <div>{sorted}</div>;
1196
+ }
1197
+ `,
1198
+ },
1199
+ {
1200
+ code: `
1201
+ function MyComponent() {
1202
+ const [items, setItems] = useState([]);
1203
+ const sorted = items.toSorted((a, b) => a - b);
1204
+ return <div>{sorted}</div>;
1205
+ }
1206
+ `,
1207
+ },
1208
+ {
1209
+ code: `
1210
+ function MyComponent() {
1211
+ const local = [3, 1, 2];
1212
+ local.sort();
1213
+ return <div />;
1214
+ }
1215
+ `,
1216
+ },
1217
+ ],
1218
+ invalid: [
1219
+ {
1220
+ code: `
1221
+ function MyComponent() {
1222
+ const [items, setItems] = useState([]);
1223
+ const sorted = items.sort((a, b) => a - b);
1224
+ return <div>{sorted}</div>;
1225
+ }
1226
+ `,
1227
+ errors: [{ message: /Do not call .sort\(\) on state or props/i }],
1228
+ },
1229
+ {
1230
+ code: `
1231
+ function MyComponent() {
1232
+ const [items, setItems] = useState([]);
1233
+ const reversed = items.reverse();
1234
+ return <div>{reversed}</div>;
1235
+ }
1236
+ `,
1237
+ errors: [{ message: /Do not call .reverse\(\) on state or props/i }],
1238
+ },
1239
+ {
1240
+ code: `
1241
+ function MyComponent({ items }) {
1242
+ const sorted = items.sort();
1243
+ return <div>{sorted}</div>;
1244
+ }
1245
+ `,
1246
+ errors: [{ message: /Do not call .sort\(\) on state or props/i }],
1247
+ },
1248
+ ],
1249
+ });
1250
+
1251
+ tester.run("require-lazy-state-init", requireLazyStateInit, {
1252
+ valid: [
1253
+ { code: `const [count, setCount] = useState(0);` },
1254
+ { code: `const [data, setData] = useState(() => JSON.parse(stored));` },
1255
+ { code: `const [value, setValue] = useState(initialValue);` },
1256
+ { code: `const [value, setValue] = useState();` },
1257
+ ],
1258
+ invalid: [
1259
+ {
1260
+ code: `const [data, setData] = useState(JSON.parse(localStorage.getItem('data')));`,
1261
+ errors: [{ message: /lazy state initialization/i }],
1262
+ },
1263
+ {
1264
+ code: `const [cache, setCache] = useState(new Map());`,
1265
+ errors: [{ message: /lazy state initialization/i }],
1266
+ },
1267
+ {
1268
+ code: `const [now, setNow] = useState(new Date());`,
1269
+ errors: [{ message: /lazy state initialization/i }],
1270
+ },
1271
+ ],
1272
+ });
1273
+
1274
+ tester.run("no-falsy-and-render", noFalsyAndRender, {
1275
+ valid: [
1276
+ { code: `const x = <div>{count > 0 && <Child />}</div>;` },
1277
+ { code: `const x = <div>{!!items && <Child />}</div>;` },
1278
+ { code: `const x = <div>{Boolean(items) && <Child />}</div>;` },
1279
+ { code: `const x = <div>{isVisible && <Child />}</div>;` },
1280
+ { code: `const x = <div>{hasItems && <Child />}</div>;` },
1281
+ ],
1282
+ invalid: [
1283
+ {
1284
+ code: `const x = <div>{items && <Child />}</div>;`,
1285
+ errors: [{ message: /potentially non-boolean values/i }],
1286
+ },
1287
+ {
1288
+ code: `const x = <div>{items.length && <Child />}</div>;`,
1289
+ errors: [{ message: /potentially non-boolean values/i }],
1290
+ },
1291
+ {
1292
+ code: `const x = <div>{data.count && <Child />}</div>;`,
1293
+ errors: [{ message: /potentially non-boolean values/i }],
1294
+ },
1295
+ {
1296
+ code: `const x = <div>{getCount() && <Child />}</div>;`,
1297
+ errors: [{ message: /potentially non-boolean values/i }],
1298
+ },
1299
+ ],
1300
+ });
1301
+
1302
+ tester.run("no-regexp-in-render", noRegexpInRender, {
1303
+ valid: [
1304
+ {
1305
+ code: `
1306
+ const pattern = new RegExp('abc');
1307
+ function MyComponent() { return <div />; }
1308
+ `,
1309
+ },
1310
+ {
1311
+ code: `
1312
+ function MyComponent({ pattern }) {
1313
+ const regex = useMemo(() => new RegExp(pattern), [pattern]);
1314
+ return <div />;
1315
+ }
1316
+ `,
1317
+ },
1318
+ {
1319
+ code: `
1320
+ function MyComponent() {
1321
+ const check = useCallback(() => {
1322
+ const re = new RegExp('test');
1323
+ return re.test('abc');
1324
+ }, []);
1325
+ return <div />;
1326
+ }
1327
+ `,
1328
+ },
1329
+ {
1330
+ code: `
1331
+ function MyComponent() {
1332
+ function handleClick() {
1333
+ const re = new RegExp('test');
1334
+ }
1335
+ return <div onClick={handleClick} />;
1336
+ }
1337
+ `,
1338
+ },
1339
+ {
1340
+ code: `
1341
+ function helper() {
1342
+ const re = new RegExp('test');
1343
+ return re;
1344
+ }
1345
+ `,
1346
+ },
1347
+ ],
1348
+ invalid: [
1349
+ {
1350
+ code: `
1351
+ function MyComponent() {
1352
+ const pattern = new RegExp('abc');
1353
+ return <div />;
1354
+ }
1355
+ `,
1356
+ errors: [{ message: /Avoid creating RegExp inside component body/i }],
1357
+ },
1358
+ {
1359
+ code: `
1360
+ const MyComponent = () => {
1361
+ const re = new RegExp('test', 'i');
1362
+ return <div />;
1363
+ };
1364
+ `,
1365
+ errors: [{ message: /Avoid creating RegExp inside component body/i }],
1366
+ },
1367
+ ],
1368
+ });
1369
+ });
1370
+
1371
+ // ── Next.js navigation rules ─────────────────────────────────────────
1372
+
1373
+ describe("next.js rules", () => {
1374
+ tester.run("no-redirect-in-try-catch", noRedirectInTryCatch, {
1375
+ valid: [
1376
+ {
1377
+ code: `
1378
+ import { redirect } from 'next/navigation';
1379
+ async function action() {
1380
+ redirect('/home');
1381
+ }
1382
+ `,
1383
+ },
1384
+ {
1385
+ code: `
1386
+ import { redirect } from 'next/navigation';
1387
+ import { unstable_rethrow } from 'next/navigation';
1388
+ async function action() {
1389
+ try {
1390
+ redirect('/home');
1391
+ } catch (e) {
1392
+ unstable_rethrow(e);
1393
+ console.error(e);
1394
+ }
1395
+ }
1396
+ `,
1397
+ },
1398
+ {
1399
+ code: `
1400
+ async function action() {
1401
+ try {
1402
+ doSomething();
1403
+ } catch (e) {
1404
+ console.error(e);
1405
+ }
1406
+ }
1407
+ `,
1408
+ },
1409
+ ],
1410
+ invalid: [
1411
+ {
1412
+ code: `
1413
+ import { redirect } from 'next/navigation';
1414
+ async function action() {
1415
+ try {
1416
+ redirect('/home');
1417
+ } catch (e) {
1418
+ console.error(e);
1419
+ }
1420
+ }
1421
+ `,
1422
+ errors: [{ message: /redirect.*silently caught/i }],
1423
+ },
1424
+ {
1425
+ code: `
1426
+ import { notFound } from 'next/navigation';
1427
+ async function action() {
1428
+ try {
1429
+ notFound();
1430
+ } catch (e) {
1431
+ return null;
1432
+ }
1433
+ }
1434
+ `,
1435
+ errors: [{ message: /notFound.*silently caught/i }],
1436
+ },
1437
+ ],
1438
+ });
1439
+
1440
+ tester.run("no-framer-motion-import", noFramerMotionImport, {
1441
+ valid: [
1442
+ { code: `import { motion } from 'motion/react';` },
1443
+ { code: `import { AnimatePresence } from 'motion/react';` },
1444
+ ],
1445
+ invalid: [
1446
+ {
1447
+ code: `import { motion } from 'framer-motion';`,
1448
+ output: `import { motion } from 'motion/react';`,
1449
+ errors: [{ message: /motion\/react.*instead.*framer-motion/i }],
1450
+ },
1451
+ ],
1452
+ });
1453
+
1454
+ tester.run("require-sizes-on-fill-image", requireSizesOnFillImage, {
1455
+ valid: [
1456
+ {
1457
+ code: `
1458
+ import Image from 'next/image';
1459
+ const x = <Image fill sizes="(max-width: 768px) 100vw, 50vw" src="/hero.jpg" alt="" />;
1460
+ `,
1461
+ },
1462
+ {
1463
+ code: `
1464
+ import Image from 'next/image';
1465
+ const x = <Image src="/hero.jpg" width={400} height={300} alt="" />;
1466
+ `,
1467
+ },
1468
+ {
1469
+ code: `
1470
+ const x = <img src="/hero.jpg" />;
1471
+ `,
1472
+ },
1473
+ ],
1474
+ invalid: [
1475
+ {
1476
+ code: `
1477
+ import Image from 'next/image';
1478
+ const x = <Image fill src="/hero.jpg" alt="" />;
1479
+ `,
1480
+ errors: [{ message: /sizes.*prop/i }],
1481
+ },
1482
+ ],
1483
+ });
1484
+
1485
+ tester.run("no-unstable-cache", noUnstableCache, {
1486
+ valid: [
1487
+ { code: `import { cacheLife } from 'next/cache';` },
1488
+ { code: `import { revalidateTag } from 'next/cache';` },
1489
+ ],
1490
+ invalid: [
1491
+ {
1492
+ code: `import { unstable_cache } from 'next/cache';`,
1493
+ errors: [{ message: /unstable_cache is deprecated/i }],
1494
+ },
1495
+ ],
1496
+ });
1497
+
1498
+ tester.run("no-deprecated-segment-config", noDeprecatedSegmentConfig, {
1499
+ valid: [
1500
+ { code: `export const metadata = { title: 'Home' };` },
1501
+ {
1502
+ code: `export const revalidate = 60;`,
1503
+ options: [{ ignore: ["revalidate"] }],
1504
+ },
1505
+ ],
1506
+ invalid: [
1507
+ {
1508
+ code: `export const dynamic = 'force-dynamic';`,
1509
+ errors: [{ message: /dynamic.*deprecated/i }],
1510
+ },
1511
+ {
1512
+ code: `export const revalidate = 60;`,
1513
+ errors: [{ message: /revalidate.*deprecated/i }],
1514
+ },
1515
+ {
1516
+ code: `export const fetchCache = 'force-no-store';`,
1517
+ errors: [{ message: /fetchCache.*deprecated/i }],
1518
+ },
1519
+ ],
1520
+ });
1521
+
1522
+ tester.run("no-runtime-api-in-use-cache", noRuntimeApiInUseCache, {
1523
+ valid: [
1524
+ {
1525
+ code: `
1526
+ async function getData() {
1527
+ const c = cookies();
1528
+ return c.get('session');
1529
+ }
1530
+ `,
1531
+ },
1532
+ {
1533
+ code: `
1534
+ 'use cache';
1535
+ async function getData() {
1536
+ return { value: 42 };
1537
+ }
1538
+ `,
1539
+ },
1540
+ ],
1541
+ invalid: [
1542
+ {
1543
+ code: `
1544
+ 'use cache';
1545
+ async function getData() {
1546
+ const c = cookies();
1547
+ return c.get('session');
1548
+ }
1549
+ `,
1550
+ errors: [{ message: /cookies.*cannot be called.*use cache/i }],
1551
+ },
1552
+ {
1553
+ code: `
1554
+ async function getData() {
1555
+ 'use cache';
1556
+ const h = headers();
1557
+ return h.get('x-custom');
1558
+ }
1559
+ `,
1560
+ errors: [{ message: /headers.*cannot be called.*use cache/i }],
1561
+ },
1562
+ ],
1563
+ });
1564
+ });
1565
+
1566
+ // ── Effect hygiene rules ──────────────────────────────────────────────
1567
+
1568
+ describe("effect hygiene rules", () => {
1569
+ tester.run("require-effect-cleanup", requireEffectCleanup, {
1570
+ valid: [
1571
+ {
1572
+ code: `
1573
+ useEffect(() => {
1574
+ window.addEventListener('resize', handler);
1575
+ return () => window.removeEventListener('resize', handler);
1576
+ }, []);
1577
+ `,
1578
+ },
1579
+ {
1580
+ code: `
1581
+ useEffect(() => {
1582
+ console.log('mounted');
1583
+ }, []);
1584
+ `,
1585
+ },
1586
+ {
1587
+ code: `
1588
+ useEffect(() => {
1589
+ const id = setInterval(tick, 1000);
1590
+ return () => clearInterval(id);
1591
+ }, []);
1592
+ `,
1593
+ },
1594
+ ],
1595
+ invalid: [
1596
+ {
1597
+ code: `
1598
+ useEffect(() => {
1599
+ window.addEventListener('resize', handler);
1600
+ }, []);
1601
+ `,
1602
+ errors: [{ message: /cleanup function/i }],
1603
+ },
1604
+ {
1605
+ code: `
1606
+ useEffect(() => {
1607
+ const id = setInterval(tick, 1000);
1608
+ }, []);
1609
+ `,
1610
+ errors: [{ message: /cleanup function/i }],
1611
+ },
1612
+ {
1613
+ code: `
1614
+ useEffect(() => {
1615
+ const id = setTimeout(doWork, 500);
1616
+ }, []);
1617
+ `,
1618
+ errors: [{ message: /cleanup function/i }],
1619
+ },
1620
+ {
1621
+ code: `
1622
+ useEffect(() => {
1623
+ observer.observe(element);
1624
+ }, []);
1625
+ `,
1626
+ errors: [{ message: /cleanup function/i }],
1627
+ },
1628
+ ],
1629
+ });
1630
+
1631
+ tester.run("require-passive-event-listener", requirePassiveEventListener, {
1632
+ valid: [
1633
+ {
1634
+ code: `element.addEventListener('touchstart', handler, { passive: true });`,
1635
+ },
1636
+ {
1637
+ code: `element.addEventListener('click', handler);`,
1638
+ },
1639
+ {
1640
+ code: `element.addEventListener('wheel', handler, { capture: true, passive: true });`,
1641
+ },
1642
+ ],
1643
+ invalid: [
1644
+ {
1645
+ code: `element.addEventListener('touchstart', handler);`,
1646
+ output: `element.addEventListener('touchstart', handler, { passive: true });`,
1647
+ errors: [{ message: /passive.*true/i }],
1648
+ },
1649
+ {
1650
+ code: `element.addEventListener('wheel', handler, { capture: true });`,
1651
+ output: `element.addEventListener('wheel', handler, { capture: true, passive: true });`,
1652
+ errors: [{ message: /passive.*true/i }],
1653
+ },
1654
+ ],
1655
+ });
1656
+ });
1657
+
1658
+ // ── Animation rules ───────────────────────────────────────────────────
1659
+
1660
+ describe("animation rules", () => {
1661
+ tester.run("no-layout-property-animation", noLayoutPropertyAnimation, {
1662
+ valid: [
1663
+ {
1664
+ code: `const x = <motion.div animate={{ opacity: 1, x: 0, scale: 1 }} />;`,
1665
+ },
1666
+ {
1667
+ code: `const x = <motion.div whileHover={{ scale: 1.05 }} />;`,
1668
+ },
1669
+ {
1670
+ code: `const x = <div style={{ width: '100px' }} />;`,
1671
+ },
1672
+ ],
1673
+ invalid: [
1674
+ {
1675
+ code: `const x = <motion.div animate={{ width: '100px' }} />;`,
1676
+ errors: [{ message: /Avoid animating 'width'/i }],
1677
+ },
1678
+ {
1679
+ code: `const x = <motion.div whileHover={{ height: 200, opacity: 1 }} />;`,
1680
+ errors: [{ message: /Avoid animating 'height'/i }],
1681
+ },
1682
+ {
1683
+ code: `const x = <motion.div initial={{ marginTop: 0 }} />;`,
1684
+ errors: [{ message: /Avoid animating 'marginTop'/i }],
1685
+ },
1686
+ {
1687
+ code: `const x = <motion.div animate={{ left: 0, top: 0 }} />;`,
1688
+ errors: [
1689
+ { message: /Avoid animating 'left'/i },
1690
+ { message: /Avoid animating 'top'/i },
1691
+ ],
1692
+ },
1693
+ ],
1694
+ });
1695
+ });
1696
+
1697
+ // ── Server action security rules ──────────────────────────────────────
1698
+
1699
+ describe("server action security rules", () => {
1700
+ tester.run("require-auth-in-server-action", requireAuthInServerAction, {
1701
+ valid: [
1702
+ {
1703
+ code: `
1704
+ 'use server';
1705
+ export async function createPost(data) {
1706
+ const session = await auth();
1707
+ return { ok: true };
1708
+ }
1709
+ `,
1710
+ },
1711
+ {
1712
+ code: `
1713
+ 'use server';
1714
+ export async function deleteItem(id) {
1715
+ await requireAuth();
1716
+ return { deleted: true };
1717
+ }
1718
+ `,
1719
+ },
1720
+ {
1721
+ code: `
1722
+ export async function helper(data) {
1723
+ return data;
1724
+ }
1725
+ `,
1726
+ },
1727
+ {
1728
+ code: `
1729
+ 'use server';
1730
+ export async function deleteUser(id) {
1731
+ await requireAdmin();
1732
+ return { deleted: id };
1733
+ }
1734
+ `,
1735
+ },
1736
+ {
1737
+ code: `
1738
+ 'use server';
1739
+ export async function updateSettings(data) {
1740
+ checkRole('admin');
1741
+ return { ok: true };
1742
+ }
1743
+ `,
1744
+ },
1745
+ {
1746
+ code: `
1747
+ 'use server';
1748
+ export async function createPost(data) {
1749
+ await myCustomGuard();
1750
+ return { ok: true };
1751
+ }
1752
+ `,
1753
+ options: [{ authFunctions: ["myCustomGuard"] }],
1754
+ },
1755
+ {
1756
+ code: `
1757
+ 'use server';
1758
+ export async function getAuthSession() {
1759
+ return null;
1760
+ }
1761
+ `,
1762
+ filename: "/app/lib/auth.ts",
1763
+ },
1764
+ {
1765
+ code: `
1766
+ 'use server';
1767
+ export async function requireAdminRole() {
1768
+ return null;
1769
+ }
1770
+ `,
1771
+ filename: "/app/lib/admin.ts",
1772
+ },
1773
+ ],
1774
+ invalid: [
1775
+ {
1776
+ code: `
1777
+ 'use server';
1778
+ export async function createPost(data) {
1779
+ return { ok: true };
1780
+ }
1781
+ `,
1782
+ errors: [{ message: /should verify authentication/i }],
1783
+ },
1784
+ {
1785
+ code: `
1786
+ 'use server';
1787
+ export const deleteItem = async (id) => {
1788
+ return { deleted: id };
1789
+ };
1790
+ `,
1791
+ errors: [{ message: /should verify authentication/i }],
1792
+ },
1793
+ {
1794
+ code: `
1795
+ 'use server';
1796
+ export async function doWork(data) {
1797
+ return { ok: true };
1798
+ }
1799
+ `,
1800
+ options: [{ authFunctions: ["myCustomGuard"] }],
1801
+ errors: [{ message: /should verify authentication/i }],
1802
+ },
1803
+ {
1804
+ code: `
1805
+ 'use server';
1806
+ export async function createPost(data) {
1807
+ await requireAuth();
1808
+ return { ok: true };
1809
+ }
1810
+ `,
1811
+ options: [{ authFunctions: ["myCustomGuard"], replaceAuthFunctions: true }],
1812
+ errors: [{ message: /should verify authentication/i }],
1813
+ },
1814
+ ],
1815
+ });
1816
+ });
1817
+
1818
+ // ── Dashboard pattern rules ──────────────────────────────────────────
1819
+
1820
+ describe("dashboard pattern rules", () => {
1821
+ tester.run("no-raw-recharts-import", noRawRechartsImport, {
1822
+ valid: [
1823
+ {
1824
+ filename: "/repo/src/components/ui/charts/AreaChart.tsx",
1825
+ code: "import { AreaChart } from 'recharts'; export default AreaChart;",
1826
+ },
1827
+ {
1828
+ filename: "/repo/src/components/ui/Chart.tsx",
1829
+ code: "import { ResponsiveContainer } from 'recharts'; export default ResponsiveContainer;",
1830
+ },
1831
+ {
1832
+ filename: "/repo/src/app/dashboard/page.tsx",
1833
+ code: "import { AreaChart } from '@/components/ui/charts/AreaChart';",
1834
+ },
1835
+ ],
1836
+ invalid: [
1837
+ {
1838
+ filename: "/repo/src/app/dashboard/page.tsx",
1839
+ code: "import { AreaChart } from 'recharts'; const x = AreaChart;",
1840
+ errors: [{ message: /chart wrappers enforce consistent styling/i }],
1841
+ },
1842
+ {
1843
+ filename: "/repo/src/components/blocks/StatsBlock.tsx",
1844
+ code: "import { BarChart } from 'recharts'; const x = BarChart;",
1845
+ errors: [{ message: /chart wrappers enforce consistent styling/i }],
1846
+ },
1847
+ ],
1848
+ });
1849
+
1850
+ tester.run("require-chart-empty-handler", requireChartEmptyHandler, {
1851
+ valid: [
1852
+ {
1853
+ code: `
1854
+ import { AreaChart } from '@/components/ui/charts/AreaChart';
1855
+ function Dashboard({ data }) {
1856
+ return data.length > 0 ? <AreaChart data={data} /> : <Empty />;
1857
+ }
1858
+ `,
1859
+ },
1860
+ {
1861
+ code: `
1862
+ import { BarChart } from '@/components/ui/charts/BarChart';
1863
+ function Dashboard({ data }) {
1864
+ return data.length > 0 && <BarChart data={data} />;
1865
+ }
1866
+ `,
1867
+ },
1868
+ {
1869
+ code: `
1870
+ import { Button } from '@/components/ui/Button';
1871
+ function Dashboard() {
1872
+ return <Button>Click</Button>;
1873
+ }
1874
+ `,
1875
+ },
1876
+ ],
1877
+ invalid: [
1878
+ {
1879
+ code: `
1880
+ import { AreaChart } from '@/components/ui/charts/AreaChart';
1881
+ function Dashboard({ data }) {
1882
+ return <AreaChart data={data} />;
1883
+ }
1884
+ `,
1885
+ errors: [{ message: /AreaChart.*must handle empty data/i }],
1886
+ },
1887
+ {
1888
+ code: `
1889
+ import { PieChart } from '@/components/ui/charts/PieChart';
1890
+ function Dashboard({ data }) {
1891
+ return <div><PieChart data={data} /></div>;
1892
+ }
1893
+ `,
1894
+ errors: [{ message: /PieChart.*must handle empty data/i }],
1895
+ },
1896
+ ],
1897
+ });
1898
+
1899
+ tester.run("no-arbitrary-chart-color", noArbitraryChartColor, {
1900
+ valid: [
1901
+ {
1902
+ code: `
1903
+ import { AreaChart } from '@/components/ui/charts/AreaChart';
1904
+ const config = { color: 'rgb(var(--chart-1))' };
1905
+ `,
1906
+ },
1907
+ {
1908
+ code: `
1909
+ import { BarChart } from '@/components/ui/charts/BarChart';
1910
+ const config = { color: 'var(--color-chart-primary)' };
1911
+ `,
1912
+ },
1913
+ {
1914
+ code: `
1915
+ const style = { color: '#ff0000' };
1916
+ `,
1917
+ },
1918
+ ],
1919
+ invalid: [
1920
+ {
1921
+ code: `
1922
+ import { AreaChart } from '@/components/ui/charts/AreaChart';
1923
+ const config = { color: '#3b82f6' };
1924
+ `,
1925
+ errors: [{ message: /semantic tokens/i }],
1926
+ },
1927
+ {
1928
+ code: `
1929
+ import { BarChart } from '@/components/ui/charts/BarChart';
1930
+ const config = { color: 'rgb(59, 130, 246)' };
1931
+ `,
1932
+ errors: [{ message: /semantic tokens/i }],
1933
+ },
1934
+ {
1935
+ code: `
1936
+ import { LineChart } from '@/components/ui/charts/LineChart';
1937
+ const config = { color: 'red' };
1938
+ `,
1939
+ errors: [{ message: /semantic tokens/i }],
1940
+ },
1941
+ ],
1942
+ });
1943
+
1944
+ tester.run("require-dashboard-loading-skeleton", requireDashboardLoadingSkeleton, {
1945
+ valid: [
1946
+ {
1947
+ filename: "/repo/src/app/dashboard/page.tsx",
1948
+ code: `
1949
+ import { AreaChart } from '@/components/ui/charts/AreaChart';
1950
+ import { Skeleton } from '@/components/ui/Skeleton';
1951
+ function Dashboard() { return <AreaChart />; }
1952
+ `,
1953
+ },
1954
+ {
1955
+ filename: "/repo/src/app/dashboard/page.tsx",
1956
+ code: `
1957
+ import { Button } from '@/components/ui/Button';
1958
+ function Dashboard() { return <Button>Click</Button>; }
1959
+ `,
1960
+ },
1961
+ {
1962
+ filename: "/repo/src/app/settings/page.tsx",
1963
+ code: `
1964
+ import { AreaChart } from '@/components/ui/charts/AreaChart';
1965
+ function Settings() { return <AreaChart />; }
1966
+ `,
1967
+ },
1968
+ ],
1969
+ invalid: [
1970
+ {
1971
+ filename: "/repo/src/app/dashboard/page.tsx",
1972
+ code: `
1973
+ import { AreaChart } from '@/components/ui/charts/AreaChart';
1974
+ function Dashboard() { return <AreaChart />; }
1975
+ `,
1976
+ errors: [{ message: /must import 'Skeleton'/i }],
1977
+ },
1978
+ {
1979
+ filename: "/repo/src/app/(dashboard)/analytics/page.tsx",
1980
+ code: `
1981
+ import { MetricCard } from '@/components/ui/MetricCard';
1982
+ import { DataTable } from '@/components/ui/DataTable';
1983
+ function Analytics() { return <div><MetricCard /><DataTable /></div>; }
1984
+ `,
1985
+ errors: [{ message: /must import 'Skeleton'/i }],
1986
+ },
1987
+ ],
1988
+ });
1989
+
1990
+ tester.run("require-stagger-on-metric-grid", requireStaggerOnMetricGrid, {
1991
+ valid: [
1992
+ {
1993
+ code: `
1994
+ function Dashboard() {
1995
+ return (
1996
+ <StaggerChildren>
1997
+ <div className="grid grid-cols-3 gap-4">
1998
+ <MetricCard>A</MetricCard>
1999
+ <MetricCard>B</MetricCard>
2000
+ <MetricCard>C</MetricCard>
2001
+ </div>
2002
+ </StaggerChildren>
2003
+ );
2004
+ }
2005
+ `,
2006
+ },
2007
+ {
2008
+ code: `
2009
+ function Dashboard() {
2010
+ return (
2011
+ <div className="grid grid-cols-2 gap-4">
2012
+ <MetricCard>A</MetricCard>
2013
+ <MetricCard>B</MetricCard>
2014
+ </div>
2015
+ );
2016
+ }
2017
+ `,
2018
+ },
2019
+ {
2020
+ code: `
2021
+ function Dashboard() {
2022
+ return (
2023
+ <div className="grid grid-cols-3 gap-4">
2024
+ <div>A</div>
2025
+ <div>B</div>
2026
+ <div>C</div>
2027
+ </div>
2028
+ );
2029
+ }
2030
+ `,
2031
+ },
2032
+ {
2033
+ code: `
2034
+ function Dashboard() {
2035
+ return (
2036
+ <div className="grid grid-cols-3 gap-4">
2037
+ <Card>A</Card>
2038
+ <Card>B</Card>
2039
+ <Card>C</Card>
2040
+ </div>
2041
+ );
2042
+ }
2043
+ `,
2044
+ },
2045
+ ],
2046
+ invalid: [
2047
+ {
2048
+ code: `
2049
+ function Dashboard() {
2050
+ return (
2051
+ <div className="grid grid-cols-3 gap-4">
2052
+ <MetricCard>A</MetricCard>
2053
+ <MetricCard>B</MetricCard>
2054
+ <MetricCard>C</MetricCard>
2055
+ </div>
2056
+ );
2057
+ }
2058
+ `,
2059
+ errors: [{ message: /StaggerChildren.*entrance animation/i }],
2060
+ },
2061
+ {
2062
+ code: `
2063
+ function Dashboard() {
2064
+ return (
2065
+ <div className="grid grid-cols-4 gap-4">
2066
+ <MetricCard>A</MetricCard>
2067
+ <MetricCard>B</MetricCard>
2068
+ <MetricCard>C</MetricCard>
2069
+ <MetricCard>D</MetricCard>
2070
+ </div>
2071
+ );
2072
+ }
2073
+ `,
2074
+ errors: [{ message: /StaggerChildren.*entrance animation/i }],
2075
+ },
2076
+ ],
2077
+ });
2078
+
2079
+ tester.run("no-low-contrast-text-opacity", noLowContrastTextOpacity, {
2080
+ valid: [
2081
+ { code: "const x = <div className='text-text-primary opacity-70' />;" },
2082
+ { code: "const x = <div className='text-text-tertiary' />;" },
2083
+ { code: "const x = <div className='opacity-50' />;" },
2084
+ { code: "const x = <div className='text-text-primary' />;" },
2085
+ { code: "const x = <div className='text-text-secondary hover:opacity-100' />;" },
2086
+ ],
2087
+ invalid: [
2088
+ {
2089
+ code: "const x = <div className='text-text-tertiary opacity-50' />;",
2090
+ errors: [{ message: /Low-contrast text.*text-text-tertiary.*opacity-50.*WCAG/ }],
2091
+ },
2092
+ {
2093
+ code: "const x = <div className='text-text-secondary opacity-30' />;",
2094
+ errors: [{ message: /Low-contrast text.*text-text-secondary.*opacity-30.*WCAG/ }],
2095
+ },
2096
+ {
2097
+ code: "const x = <div className='text-disabled-foreground opacity-50' />;",
2098
+ errors: [{ message: /Low-contrast text.*text-disabled-foreground.*opacity-50.*WCAG/ }],
2099
+ },
2100
+ {
2101
+ code: "const x = cn('text-text-tertiary opacity-50');",
2102
+ errors: [{ message: /Low-contrast text/ }],
2103
+ },
2104
+ ],
2105
+ });
2106
+
2107
+ tester.run("require-sidebar-trigger", requireSidebarTrigger, {
2108
+ valid: [
2109
+ {
2110
+ code: "import { SidebarProvider, SidebarTrigger } from '@/components/ui/Sidebar'; const x = <SidebarProvider><SidebarTrigger /></SidebarProvider>;",
2111
+ },
2112
+ {
2113
+ code: "import { SidebarTrigger, SidebarProvider } from '@/components/ui/Sidebar'; const x = <SidebarProvider><SidebarTrigger /></SidebarProvider>;",
2114
+ },
2115
+ {
2116
+ code: "import { SidebarMenu } from '@/components/ui/Sidebar'; const x = <SidebarMenu />;",
2117
+ },
2118
+ {
2119
+ code: "import { useState } from 'react'; const x = useState();",
2120
+ },
2121
+ {
2122
+ // Test files are exempt
2123
+ filename: "/repo/src/components/ui/Sidebar.test.tsx",
2124
+ code: "import { SidebarProvider } from '@/components/ui/Sidebar'; const x = <SidebarProvider>test</SidebarProvider>;",
2125
+ },
2126
+ {
2127
+ // The Sidebar component source itself is exempt
2128
+ filename: "/repo/src/components/ui/Sidebar.tsx",
2129
+ code: "import { SidebarProvider } from '@/components/ui/Sidebar'; const x = <SidebarProvider>internal</SidebarProvider>;",
2130
+ },
2131
+ ],
2132
+ invalid: [
2133
+ {
2134
+ code: "import { SidebarProvider } from '@/components/ui/Sidebar'; const x = <SidebarProvider>content</SidebarProvider>;",
2135
+ errors: [{ message: /SidebarTrigger/ }],
2136
+ },
2137
+ {
2138
+ code: "import { SidebarProvider, SidebarContent } from '@/components/ui/Sidebar'; const x = <SidebarProvider><SidebarContent /></SidebarProvider>;",
2139
+ errors: [{ message: /SidebarTrigger/ }],
2140
+ },
2141
+ ],
2142
+ });
2143
+ });