@webjsdev/ui 0.3.16 → 0.4.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 (358) hide show
  1. package/README.md +71 -10
  2. package/THIRD_PARTY_NOTICES.md +349 -0
  3. package/package.json +4 -2
  4. package/packages/registry/blocks/_shared/placeholder.svg +1 -0
  5. package/packages/registry/blocks/charts/chart-area-axes.ts +72 -0
  6. package/packages/registry/blocks/charts/chart-area-default.ts +64 -0
  7. package/packages/registry/blocks/charts/chart-area-gradient.ts +82 -0
  8. package/packages/registry/blocks/charts/chart-area-icons.ts +77 -0
  9. package/packages/registry/blocks/charts/chart-area-interactive.ts +204 -0
  10. package/packages/registry/blocks/charts/chart-area-legend.ts +72 -0
  11. package/packages/registry/blocks/charts/chart-area-linear.ts +64 -0
  12. package/packages/registry/blocks/charts/chart-area-stacked-expand.ts +77 -0
  13. package/packages/registry/blocks/charts/chart-area-stacked.ts +71 -0
  14. package/packages/registry/blocks/charts/chart-area-step.ts +68 -0
  15. package/packages/registry/blocks/charts/chart-bar-active.ts +93 -0
  16. package/packages/registry/blocks/charts/chart-bar-default.ts +60 -0
  17. package/packages/registry/blocks/charts/chart-bar-horizontal.ts +62 -0
  18. package/packages/registry/blocks/charts/chart-bar-interactive.ts +185 -0
  19. package/packages/registry/blocks/charts/chart-bar-label-custom.ts +78 -0
  20. package/packages/registry/blocks/charts/chart-bar-label.ts +66 -0
  21. package/packages/registry/blocks/charts/chart-bar-mixed.ts +87 -0
  22. package/packages/registry/blocks/charts/chart-bar-multiple.ts +67 -0
  23. package/packages/registry/blocks/charts/chart-bar-negative.ts +61 -0
  24. package/packages/registry/blocks/charts/chart-bar-stacked.ts +68 -0
  25. package/packages/registry/blocks/charts/chart-line-default.ts +61 -0
  26. package/packages/registry/blocks/charts/chart-line-dots-colors.ts +86 -0
  27. package/packages/registry/blocks/charts/chart-line-dots-custom.ts +75 -0
  28. package/packages/registry/blocks/charts/chart-line-dots.ts +65 -0
  29. package/packages/registry/blocks/charts/chart-line-interactive.ts +185 -0
  30. package/packages/registry/blocks/charts/chart-line-label-custom.ts +94 -0
  31. package/packages/registry/blocks/charts/chart-line-label.ts +73 -0
  32. package/packages/registry/blocks/charts/chart-line-linear.ts +61 -0
  33. package/packages/registry/blocks/charts/chart-line-multiple.ts +71 -0
  34. package/packages/registry/blocks/charts/chart-line-step.ts +61 -0
  35. package/packages/registry/blocks/charts/chart-pie-donut-active.ts +86 -0
  36. package/packages/registry/blocks/charts/chart-pie-donut-text.ts +89 -0
  37. package/packages/registry/blocks/charts/chart-pie-donut.ts +77 -0
  38. package/packages/registry/blocks/charts/chart-pie-interactive.ts +120 -0
  39. package/packages/registry/blocks/charts/chart-pie-label-custom.ts +82 -0
  40. package/packages/registry/blocks/charts/chart-pie-label-list.ts +86 -0
  41. package/packages/registry/blocks/charts/chart-pie-label.ts +77 -0
  42. package/packages/registry/blocks/charts/chart-pie-legend.ts +79 -0
  43. package/packages/registry/blocks/charts/chart-pie-separator-none.ts +77 -0
  44. package/packages/registry/blocks/charts/chart-pie-simple.ts +77 -0
  45. package/packages/registry/blocks/charts/chart-pie-stacked.ts +99 -0
  46. package/packages/registry/blocks/charts/chart-radar-default.ts +61 -0
  47. package/packages/registry/blocks/charts/chart-radar-dots.ts +61 -0
  48. package/packages/registry/blocks/charts/chart-radar-grid-circle-fill.ts +61 -0
  49. package/packages/registry/blocks/charts/chart-radar-grid-circle-no-lines.ts +61 -0
  50. package/packages/registry/blocks/charts/chart-radar-grid-circle.ts +61 -0
  51. package/packages/registry/blocks/charts/chart-radar-grid-custom.ts +61 -0
  52. package/packages/registry/blocks/charts/chart-radar-grid-fill.ts +61 -0
  53. package/packages/registry/blocks/charts/chart-radar-grid-none.ts +60 -0
  54. package/packages/registry/blocks/charts/chart-radar-icons.ts +76 -0
  55. package/packages/registry/blocks/charts/chart-radar-label-custom.ts +77 -0
  56. package/packages/registry/blocks/charts/chart-radar-legend.ts +70 -0
  57. package/packages/registry/blocks/charts/chart-radar-lines-only.ts +68 -0
  58. package/packages/registry/blocks/charts/chart-radar-multiple.ts +68 -0
  59. package/packages/registry/blocks/charts/chart-radar-radius.ts +68 -0
  60. package/packages/registry/blocks/charts/chart-radial-grid.ts +80 -0
  61. package/packages/registry/blocks/charts/chart-radial-label.ts +85 -0
  62. package/packages/registry/blocks/charts/chart-radial-shape.ts +81 -0
  63. package/packages/registry/blocks/charts/chart-radial-simple.ts +79 -0
  64. package/packages/registry/blocks/charts/chart-radial-stacked.ts +69 -0
  65. package/packages/registry/blocks/charts/chart-radial-text.ts +82 -0
  66. package/packages/registry/blocks/charts/chart-tooltip-advanced.ts +84 -0
  67. package/packages/registry/blocks/charts/chart-tooltip-default.ts +62 -0
  68. package/packages/registry/blocks/charts/chart-tooltip-formatter.ts +75 -0
  69. package/packages/registry/blocks/charts/chart-tooltip-icons.ts +68 -0
  70. package/packages/registry/blocks/charts/chart-tooltip-indicator-line.ts +62 -0
  71. package/packages/registry/blocks/charts/chart-tooltip-indicator-none.ts +62 -0
  72. package/packages/registry/blocks/charts/chart-tooltip-label-custom.ts +65 -0
  73. package/packages/registry/blocks/charts/chart-tooltip-label-formatter.ts +66 -0
  74. package/packages/registry/blocks/charts/chart-tooltip-label-none.ts +62 -0
  75. package/packages/registry/blocks/dashboard-01/components/app-sidebar.ts +108 -0
  76. package/packages/registry/blocks/dashboard-01/components/chart-area-interactive.ts +276 -0
  77. package/packages/registry/blocks/dashboard-01/components/dashboard-data.ts +89 -0
  78. package/packages/registry/blocks/dashboard-01/components/data-table.ts +637 -0
  79. package/packages/registry/blocks/dashboard-01/components/nav-documents.ts +65 -0
  80. package/packages/registry/blocks/dashboard-01/components/nav-main.ts +61 -0
  81. package/packages/registry/blocks/dashboard-01/components/nav-secondary.ts +40 -0
  82. package/packages/registry/blocks/dashboard-01/components/nav-user.ts +66 -0
  83. package/packages/registry/blocks/dashboard-01/components/section-cards.ts +59 -0
  84. package/packages/registry/blocks/dashboard-01/components/site-header.ts +52 -0
  85. package/packages/registry/blocks/dashboard-01/page.ts +39 -0
  86. package/packages/registry/blocks/login-01/components/login-form.ts +70 -0
  87. package/packages/registry/blocks/login-01/page.ts +19 -0
  88. package/packages/registry/blocks/login-02/components/login-form.ts +68 -0
  89. package/packages/registry/blocks/login-02/page.ts +36 -0
  90. package/packages/registry/blocks/login-03/components/login-form.ts +89 -0
  91. package/packages/registry/blocks/login-03/page.ts +28 -0
  92. package/packages/registry/blocks/login-04/components/login-form.ts +91 -0
  93. package/packages/registry/blocks/login-04/page.ts +20 -0
  94. package/packages/registry/blocks/login-05/components/login-form.ts +75 -0
  95. package/packages/registry/blocks/login-05/page.ts +19 -0
  96. package/packages/registry/blocks/preview/cards/activate-agent-dialog.ts +77 -0
  97. package/packages/registry/blocks/preview/cards/analytics-card.ts +64 -0
  98. package/packages/registry/blocks/preview/cards/anomaly-alert.ts +35 -0
  99. package/packages/registry/blocks/preview/cards/bar-chart-card.ts +91 -0
  100. package/packages/registry/blocks/preview/cards/book-appointment.ts +51 -0
  101. package/packages/registry/blocks/preview/cards/codespaces-card.ts +172 -0
  102. package/packages/registry/blocks/preview/cards/contributions-activity.ts +63 -0
  103. package/packages/registry/blocks/preview/cards/contributors.ts +51 -0
  104. package/packages/registry/blocks/preview/cards/environment-variables.ts +47 -0
  105. package/packages/registry/blocks/preview/cards/feedback-form.ts +61 -0
  106. package/packages/registry/blocks/preview/cards/file-upload.ts +51 -0
  107. package/packages/registry/blocks/preview/cards/github-profile.ts +66 -0
  108. package/packages/registry/blocks/preview/cards/icon-preview-grid.ts +47 -0
  109. package/packages/registry/blocks/preview/cards/invite-team.ts +78 -0
  110. package/packages/registry/blocks/preview/cards/invoice.ts +91 -0
  111. package/packages/registry/blocks/preview/cards/live-waveform.ts +330 -0
  112. package/packages/registry/blocks/preview/cards/no-team-members.ts +61 -0
  113. package/packages/registry/blocks/preview/cards/not-found.ts +46 -0
  114. package/packages/registry/blocks/preview/cards/observability-card.ts +44 -0
  115. package/packages/registry/blocks/preview/cards/pie-chart-card.ts +84 -0
  116. package/packages/registry/blocks/preview/cards/report-bug.ts +71 -0
  117. package/packages/registry/blocks/preview/cards/shipping-address.ts +79 -0
  118. package/packages/registry/blocks/preview/cards/shortcuts.ts +57 -0
  119. package/packages/registry/blocks/preview/cards/skeleton-loading.ts +37 -0
  120. package/packages/registry/blocks/preview/cards/sleep-report.ts +85 -0
  121. package/packages/registry/blocks/preview/cards/style-overview.ts +64 -0
  122. package/packages/registry/blocks/preview/cards/typography-specimen.ts +107 -0
  123. package/packages/registry/blocks/preview/cards/ui-elements.ts +164 -0
  124. package/packages/registry/blocks/preview/cards/usage-card.ts +63 -0
  125. package/packages/registry/blocks/preview/cards/visitors.ts +71 -0
  126. package/packages/registry/blocks/preview/cards/weekly-fitness-summary.ts +54 -0
  127. package/packages/registry/blocks/preview/page.ts +103 -0
  128. package/packages/registry/blocks/preview-02/cards/account-access.ts +70 -0
  129. package/packages/registry/blocks/preview-02/cards/card-overview.ts +75 -0
  130. package/packages/registry/blocks/preview-02/cards/claimable-balance.ts +54 -0
  131. package/packages/registry/blocks/preview-02/cards/contribution-history.ts +80 -0
  132. package/packages/registry/blocks/preview-02/cards/cover-art.ts +41 -0
  133. package/packages/registry/blocks/preview-02/cards/dividend-income.ts +76 -0
  134. package/packages/registry/blocks/preview-02/cards/empty-connect-bank.ts +38 -0
  135. package/packages/registry/blocks/preview-02/cards/empty-distribute-track.ts +38 -0
  136. package/packages/registry/blocks/preview-02/cards/empty-explore-catalog.ts +39 -0
  137. package/packages/registry/blocks/preview-02/cards/faq.ts +79 -0
  138. package/packages/registry/blocks/preview-02/cards/front-door.ts +40 -0
  139. package/packages/registry/blocks/preview-02/cards/index-investing.ts +31 -0
  140. package/packages/registry/blocks/preview-02/cards/kitchen-island.ts +90 -0
  141. package/packages/registry/blocks/preview-02/cards/loading-card.ts +34 -0
  142. package/packages/registry/blocks/preview-02/cards/new-milestone.ts +53 -0
  143. package/packages/registry/blocks/preview-02/cards/notification-settings.ts +126 -0
  144. package/packages/registry/blocks/preview-02/cards/payments.ts +88 -0
  145. package/packages/registry/blocks/preview-02/cards/payout-threshold.ts +92 -0
  146. package/packages/registry/blocks/preview-02/cards/power-usage.ts +84 -0
  147. package/packages/registry/blocks/preview-02/cards/preferences.ts +89 -0
  148. package/packages/registry/blocks/preview-02/cards/qr-connect.ts +54 -0
  149. package/packages/registry/blocks/preview-02/cards/receiving-method.ts +89 -0
  150. package/packages/registry/blocks/preview-02/cards/recent-transactions.ts +101 -0
  151. package/packages/registry/blocks/preview-02/cards/release-catalog.ts +73 -0
  152. package/packages/registry/blocks/preview-02/cards/roller-shades.ts +110 -0
  153. package/packages/registry/blocks/preview-02/cards/savings-progress.ts +68 -0
  154. package/packages/registry/blocks/preview-02/cards/savings-targets.ts +132 -0
  155. package/packages/registry/blocks/preview-02/cards/sidebar-nav.ts +88 -0
  156. package/packages/registry/blocks/preview-02/cards/social-links.ts +57 -0
  157. package/packages/registry/blocks/preview-02/cards/stock-performance.ts +106 -0
  158. package/packages/registry/blocks/preview-02/cards/syncing-state.ts +37 -0
  159. package/packages/registry/blocks/preview-02/cards/transfer-funds.ts +111 -0
  160. package/packages/registry/blocks/preview-02/cards/upcoming-payments.ts +69 -0
  161. package/packages/registry/blocks/preview-02/page.ts +111 -0
  162. package/packages/registry/blocks/preview-03/cards/files-chat.ts +75 -0
  163. package/packages/registry/blocks/preview-03/cards/group-chat.ts +114 -0
  164. package/packages/registry/blocks/preview-03/cards/reasoning-chat.ts +88 -0
  165. package/packages/registry/blocks/preview-03/cards/simple-chat.ts +53 -0
  166. package/packages/registry/blocks/preview-03/components/chat-card.ts +126 -0
  167. package/packages/registry/blocks/preview-03/components/chat-player.ts +174 -0
  168. package/packages/registry/blocks/preview-03/components/part-file.ts +77 -0
  169. package/packages/registry/blocks/preview-03/components/part-reasoning.ts +25 -0
  170. package/packages/registry/blocks/preview-03/components/part-source.ts +48 -0
  171. package/packages/registry/blocks/preview-03/components/part-text.ts +69 -0
  172. package/packages/registry/blocks/preview-03/components/part-tool.ts +71 -0
  173. package/packages/registry/blocks/preview-03/components/prompt-input.ts +86 -0
  174. package/packages/registry/blocks/preview-03/page.ts +39 -0
  175. package/packages/registry/blocks/sidebar-01/components/app-sidebar.ts +113 -0
  176. package/packages/registry/blocks/sidebar-01/components/search-form.ts +33 -0
  177. package/packages/registry/blocks/sidebar-01/components/version-switcher.ts +50 -0
  178. package/packages/registry/blocks/sidebar-01/page.ts +58 -0
  179. package/packages/registry/blocks/sidebar-02/components/app-sidebar.ts +131 -0
  180. package/packages/registry/blocks/sidebar-02/components/search-form.ts +33 -0
  181. package/packages/registry/blocks/sidebar-02/components/version-switcher.ts +50 -0
  182. package/packages/registry/blocks/sidebar-02/page.ts +53 -0
  183. package/packages/registry/blocks/sidebar-03/components/app-sidebar.ts +134 -0
  184. package/packages/registry/blocks/sidebar-03/page.ts +60 -0
  185. package/packages/registry/blocks/sidebar-04/components/app-sidebar.ts +136 -0
  186. package/packages/registry/blocks/sidebar-04/page.ts +58 -0
  187. package/packages/registry/blocks/sidebar-05/components/app-sidebar.ts +147 -0
  188. package/packages/registry/blocks/sidebar-05/components/search-form.ts +33 -0
  189. package/packages/registry/blocks/sidebar-05/page.ts +58 -0
  190. package/packages/registry/blocks/sidebar-06/components/app-sidebar.ts +105 -0
  191. package/packages/registry/blocks/sidebar-06/components/nav-main.ts +56 -0
  192. package/packages/registry/blocks/sidebar-06/components/sidebar-opt-in-form.ts +44 -0
  193. package/packages/registry/blocks/sidebar-06/page.ts +57 -0
  194. package/packages/registry/blocks/sidebar-07/components/app-sidebar.ts +108 -0
  195. package/packages/registry/blocks/sidebar-07/components/nav-main.ts +61 -0
  196. package/packages/registry/blocks/sidebar-07/components/nav-projects.ts +65 -0
  197. package/packages/registry/blocks/sidebar-07/components/nav-user.ts +70 -0
  198. package/packages/registry/blocks/sidebar-07/components/team-switcher.ts +66 -0
  199. package/packages/registry/blocks/sidebar-07/page.ts +59 -0
  200. package/packages/registry/blocks/sidebar-08/components/app-sidebar.ts +122 -0
  201. package/packages/registry/blocks/sidebar-08/components/nav-main.ts +68 -0
  202. package/packages/registry/blocks/sidebar-08/components/nav-projects.ts +65 -0
  203. package/packages/registry/blocks/sidebar-08/components/nav-secondary.ts +40 -0
  204. package/packages/registry/blocks/sidebar-08/components/nav-user.ts +70 -0
  205. package/packages/registry/blocks/sidebar-08/page.ts +59 -0
  206. package/packages/registry/blocks/sidebar-09/components/app-sidebar.ts +213 -0
  207. package/packages/registry/blocks/sidebar-09/components/nav-user.ts +70 -0
  208. package/packages/registry/blocks/sidebar-09/page.ts +52 -0
  209. package/packages/registry/blocks/sidebar-10/components/app-sidebar.ts +125 -0
  210. package/packages/registry/blocks/sidebar-10/components/nav-actions.ts +84 -0
  211. package/packages/registry/blocks/sidebar-10/components/nav-favorites.ts +66 -0
  212. package/packages/registry/blocks/sidebar-10/components/nav-main.ts +37 -0
  213. package/packages/registry/blocks/sidebar-10/components/nav-secondary.ts +43 -0
  214. package/packages/registry/blocks/sidebar-10/components/nav-workspaces.ts +78 -0
  215. package/packages/registry/blocks/sidebar-10/components/team-switcher.ts +63 -0
  216. package/packages/registry/blocks/sidebar-10/page.ts +46 -0
  217. package/packages/registry/blocks/sidebar-11/components/app-sidebar.ts +114 -0
  218. package/packages/registry/blocks/sidebar-11/page.ts +63 -0
  219. package/packages/registry/blocks/sidebar-12/components/app-sidebar.ts +63 -0
  220. package/packages/registry/blocks/sidebar-12/components/calendars.ts +53 -0
  221. package/packages/registry/blocks/sidebar-12/components/date-picker.ts +26 -0
  222. package/packages/registry/blocks/sidebar-12/components/nav-user.ts +70 -0
  223. package/packages/registry/blocks/sidebar-12/page.ts +43 -0
  224. package/packages/registry/blocks/sidebar-13/components/settings-dialog.ts +114 -0
  225. package/packages/registry/blocks/sidebar-13/page.ts +21 -0
  226. package/packages/registry/blocks/sidebar-14/components/app-sidebar.ts +120 -0
  227. package/packages/registry/blocks/sidebar-14/page.ts +55 -0
  228. package/packages/registry/blocks/sidebar-15/components/calendars.ts +53 -0
  229. package/packages/registry/blocks/sidebar-15/components/date-picker.ts +26 -0
  230. package/packages/registry/blocks/sidebar-15/components/nav-favorites.ts +66 -0
  231. package/packages/registry/blocks/sidebar-15/components/nav-main.ts +35 -0
  232. package/packages/registry/blocks/sidebar-15/components/nav-secondary.ts +43 -0
  233. package/packages/registry/blocks/sidebar-15/components/nav-user.ts +70 -0
  234. package/packages/registry/blocks/sidebar-15/components/nav-workspaces.ts +75 -0
  235. package/packages/registry/blocks/sidebar-15/components/sidebar-left.ts +126 -0
  236. package/packages/registry/blocks/sidebar-15/components/sidebar-right.ts +62 -0
  237. package/packages/registry/blocks/sidebar-15/components/team-switcher.ts +63 -0
  238. package/packages/registry/blocks/sidebar-15/page.ts +47 -0
  239. package/packages/registry/blocks/sidebar-16/components/app-sidebar.ts +124 -0
  240. package/packages/registry/blocks/sidebar-16/components/nav-main.ts +67 -0
  241. package/packages/registry/blocks/sidebar-16/components/nav-projects.ts +65 -0
  242. package/packages/registry/blocks/sidebar-16/components/nav-secondary.ts +40 -0
  243. package/packages/registry/blocks/sidebar-16/components/nav-user.ts +70 -0
  244. package/packages/registry/blocks/sidebar-16/components/search-form.ts +27 -0
  245. package/packages/registry/blocks/sidebar-16/components/site-header.ts +42 -0
  246. package/packages/registry/blocks/sidebar-16/page.ts +38 -0
  247. package/packages/registry/blocks/signup-01/components/signup-form.ts +78 -0
  248. package/packages/registry/blocks/signup-01/page.ts +19 -0
  249. package/packages/registry/blocks/signup-02/components/signup-form.ts +78 -0
  250. package/packages/registry/blocks/signup-02/page.ts +36 -0
  251. package/packages/registry/blocks/signup-03/components/signup-form.ts +79 -0
  252. package/packages/registry/blocks/signup-03/page.ts +28 -0
  253. package/packages/registry/blocks/signup-04/components/signup-form.ts +100 -0
  254. package/packages/registry/blocks/signup-04/page.ts +20 -0
  255. package/packages/registry/blocks/signup-05/components/signup-form.ts +75 -0
  256. package/packages/registry/blocks/signup-05/page.ts +20 -0
  257. package/packages/registry/components/accordion.ts +20 -7
  258. package/packages/registry/components/alert-dialog.ts +58 -13
  259. package/packages/registry/components/alert.ts +11 -6
  260. package/packages/registry/components/attachment.ts +119 -0
  261. package/packages/registry/components/avatar.ts +17 -8
  262. package/packages/registry/components/badge.ts +16 -9
  263. package/packages/registry/components/breadcrumb.ts +6 -6
  264. package/packages/registry/components/bubble.ts +95 -0
  265. package/packages/registry/components/button-group.ts +73 -0
  266. package/packages/registry/components/button.ts +35 -25
  267. package/packages/registry/components/calendar.ts +566 -0
  268. package/packages/registry/components/card.ts +8 -8
  269. package/packages/registry/components/carousel.ts +352 -0
  270. package/packages/registry/components/chart-element.ts +547 -0
  271. package/packages/registry/components/chart-geometry.ts +457 -0
  272. package/packages/registry/components/chart.ts +932 -0
  273. package/packages/registry/components/checkbox.ts +22 -84
  274. package/packages/registry/components/code-block.ts +191 -0
  275. package/packages/registry/components/collapsible.ts +5 -2
  276. package/packages/registry/components/combobox.ts +802 -0
  277. package/packages/registry/components/command.ts +618 -0
  278. package/packages/registry/components/context-menu.ts +302 -0
  279. package/packages/registry/components/dialog.ts +156 -45
  280. package/packages/registry/components/direction.ts +43 -0
  281. package/packages/registry/components/drawer.ts +226 -0
  282. package/packages/registry/components/dropdown-menu.ts +262 -83
  283. package/packages/registry/components/empty.ts +88 -0
  284. package/packages/registry/components/field.ts +165 -0
  285. package/packages/registry/components/floating.ts +124 -0
  286. package/packages/registry/components/hover-card.ts +19 -8
  287. package/packages/registry/components/input-group.ts +118 -0
  288. package/packages/registry/components/input-otp.ts +365 -0
  289. package/packages/registry/components/input.ts +3 -3
  290. package/packages/registry/components/item.ts +134 -0
  291. package/packages/registry/components/kbd.ts +2 -2
  292. package/packages/registry/components/label.ts +1 -1
  293. package/packages/registry/components/marker.ts +63 -0
  294. package/packages/registry/components/menubar.ts +388 -0
  295. package/packages/registry/components/message-scroller.ts +470 -0
  296. package/packages/registry/components/message.ts +69 -0
  297. package/packages/registry/components/native-select.ts +5 -5
  298. package/packages/registry/components/navigation-menu.ts +564 -0
  299. package/packages/registry/components/pagination.ts +5 -5
  300. package/packages/registry/components/popover.ts +30 -84
  301. package/packages/registry/components/progress.ts +7 -3
  302. package/packages/registry/components/questionnaire.ts +840 -0
  303. package/packages/registry/components/radio-group.ts +18 -73
  304. package/packages/registry/components/resizable.ts +330 -0
  305. package/packages/registry/components/scroll-area.ts +46 -0
  306. package/packages/registry/components/select.ts +650 -0
  307. package/packages/registry/components/separator.ts +1 -1
  308. package/packages/registry/components/sheet.ts +128 -0
  309. package/packages/registry/components/sidebar.ts +457 -0
  310. package/packages/registry/components/skeleton.ts +1 -1
  311. package/packages/registry/components/slider.ts +333 -0
  312. package/packages/registry/components/sonner.ts +74 -7
  313. package/packages/registry/components/spinner.ts +66 -0
  314. package/packages/registry/components/switch.ts +117 -9
  315. package/packages/registry/components/table.ts +11 -9
  316. package/packages/registry/components/tabs.ts +76 -25
  317. package/packages/registry/components/textarea.ts +1 -1
  318. package/packages/registry/components/toggle-group.ts +30 -11
  319. package/packages/registry/components/toggle.ts +20 -13
  320. package/packages/registry/components/tooltip.ts +68 -7
  321. package/packages/registry/components/trace.ts +75 -0
  322. package/packages/registry/lib/icons-hugeicons.ts +955 -0
  323. package/packages/registry/lib/icons-phosphor.ts +955 -0
  324. package/packages/registry/lib/icons-remixicon.ts +955 -0
  325. package/packages/registry/lib/icons-tabler.ts +955 -0
  326. package/packages/registry/lib/icons.ts +768 -0
  327. package/packages/registry/lib/utils.ts +49 -4
  328. package/packages/registry/registry.json +4092 -60
  329. package/packages/registry/styles/shadcn-tailwind.css +654 -0
  330. package/packages/registry/styles/style-luma.css +1987 -0
  331. package/packages/registry/styles/style-lyra.css +1965 -0
  332. package/packages/registry/styles/style-maia.css +1987 -0
  333. package/packages/registry/styles/style-mira.css +1992 -0
  334. package/packages/registry/styles/style-new-york.css +1597 -0
  335. package/packages/registry/styles/style-nova.css +1990 -0
  336. package/packages/registry/styles/style-rhea.css +1987 -0
  337. package/packages/registry/styles/style-sera.css +1977 -0
  338. package/packages/registry/styles/style-vega.css +1990 -0
  339. package/packages/registry/styles/supplement.json +54 -0
  340. package/packages/registry/styles/tw-animate.css +6 -0
  341. package/packages/registry/themes/base-colors.js +8 -2
  342. package/packages/registry/themes/fonts.js +120 -0
  343. package/packages/registry/themes/index.css +2 -0
  344. package/packages/registry/themes/items.js +61 -0
  345. package/packages/registry/themes/preset.js +302 -0
  346. package/packages/registry/themes/themes.js +1193 -0
  347. package/src/commands/add.js +236 -12
  348. package/src/commands/diff.js +34 -20
  349. package/src/commands/info.js +7 -0
  350. package/src/commands/init.js +125 -8
  351. package/src/commands/list.js +43 -11
  352. package/src/commands/view.js +33 -5
  353. package/src/registry/extract.js +192 -2
  354. package/src/registry/icons.js +31 -0
  355. package/src/registry/local.js +15 -1
  356. package/src/registry/schema.js +13 -0
  357. package/src/registry/style-bake.js +506 -0
  358. package/src/utils/theme.js +110 -21
@@ -1,12 +1,14 @@
1
1
  import { Command } from 'commander';
2
+ import { bakeForStyle } from '../registry/style-bake.js';
2
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
- import { dirname, join, basename, relative as relPath } from 'node:path';
4
+ import { dirname, join, basename, posix, relative as relPath } from 'node:path';
4
5
  import prompts from 'prompts';
5
6
  import { execSync } from 'node:child_process';
6
7
  import { getConfig } from '../utils/get-config.js';
7
8
  import { logger } from '../utils/logger.js';
8
9
  import { resolveTree, collectNpmDeps } from '../registry/resolver.js';
9
- import { DEFAULT_REGISTRY_URL } from '../registry/fetcher.js';
10
+ import { withIconLibrary } from '../registry/icons.js';
11
+ import { DEFAULT_REGISTRY_URL, getRegistryIndex, getRegistryItem } from '../registry/fetcher.js';
10
12
  import { isCustomElementSource } from '../registry/local.js';
11
13
  import { stripExample } from '../registry/example.js';
12
14
  import { ensureTheme } from '../utils/theme.js';
@@ -36,7 +38,8 @@ export const add = new Command()
36
38
  process.exit(1);
37
39
  }
38
40
 
39
- const tree = await resolveTree(components, opts.registry);
41
+ // The icon library is the one components.json names, not always Lucide.
42
+ const tree = await withIconLibrary(await resolveTree(components, opts.registry), config.iconLibrary, opts.registry);
40
43
  logger.info(`Installing ${logger.bold(components.join(', '))}…`);
41
44
 
42
45
  // Self-heal missing theme tokens (#983): the helpers render against CSS
@@ -53,10 +56,42 @@ export const add = new Command()
53
56
  }
54
57
  }
55
58
 
59
+ const written = [];
60
+ // Block files another block already wrote (two families sharing a
61
+ // target, sidebar-07 then dashboard-01), collected for one summary.
62
+ /** @type {{ file: string, item: string, by: string[] }[]} */
63
+ const conflicts = [];
64
+ /** @type {{ blocks?: any[] }} */
65
+ const blockCache = {};
56
66
  for (const item of tree) {
57
67
  for (const file of item.files || []) {
58
- await writeRegistryFile(cwd, config, item, file, opts);
68
+ const out = await writeRegistryFile(cwd, config, item, file, { ...opts, conflicts, blockCache });
69
+ if (out) written.push(out);
59
70
  }
71
+ // A block or a component that needs tokens an existing app may lack
72
+ // (sidebar's --sidebar-*, chart's --chart-*) carries them as cssVars,
73
+ // and the missing ones are added to the app's stylesheet (D18).
74
+ if (item.cssVars && tw.css) {
75
+ const added = mergeCssVars(cwd, tw.css, item.cssVars);
76
+ if (added > 0) logger.success(`Added ${added} theme token${added === 1 ? '' : 's'} for ${item.name} to ${tw.css}`);
77
+ }
78
+ }
79
+ if (written.length) {
80
+ logger.info('Files written:');
81
+ for (const f of written) logger.info(` ${f}`);
82
+ }
83
+ if (conflicts.length) {
84
+ const names = [...new Set(conflicts.map((c) => c.item))];
85
+ const others = [...new Set(conflicts.flatMap((c) => c.by))];
86
+ logger.warn(
87
+ `${conflicts.length} file${conflicts.length === 1 ? '' : 's'} of ${names.join(', ')} ` +
88
+ `${conflicts.length === 1 ? 'was' : 'were'} kept from ${others.join(', ')}, so the app mixes the blocks:`,
89
+ );
90
+ for (const c of conflicts) logger.warn(` ${c.file} (from ${c.by.join(', ')})`);
91
+ logger.warn(
92
+ `Run ${logger.cyan(`npx @webjsdev/ui add ${names.join(' ')} --overwrite`)} to replace them with ${names.join(', ')}'s version ` +
93
+ '(it also replaces every kit component and helper the install writes, so commit your edits first).',
94
+ );
60
95
  }
61
96
 
62
97
  if (opts.deps !== false) {
@@ -82,7 +117,31 @@ async function writeRegistryFile(cwd, config, item, file, opts) {
82
117
  // replace edited source on every `add`. Only an explicit `--overwrite` may.
83
118
  if (existsSync(target) && !opts.overwrite && helperTarget(config, item, file)) {
84
119
  logger.info(`${relative(cwd, target)} already exists: keeping it.`);
85
- return;
120
+ return null;
121
+ }
122
+
123
+ // A block's files are app code the user owns (a page, a module), so `--yes`
124
+ // does not license replacing one: only `--overwrite` does (D18). Keeping it
125
+ // silently is wrong when ANOTHER block wrote it (two block families share
126
+ // targets such as components/app-sidebar.ts), since the app then mixes the
127
+ // two, so the existing file is compared against what this block and every
128
+ // other block would write there, and a match with another block is a
129
+ // warning that names it.
130
+ if (existsSync(target) && !opts.overwrite && isBlock(item, file)) {
131
+ const rel = relative(cwd, target);
132
+ const local = readFileSync(target, 'utf8');
133
+ if (local === transformForProject(file.content || '', target, config, item, { file, cwd })) {
134
+ logger.info(`${rel} is already ${item.name}'s version: keeping it.`);
135
+ return null;
136
+ }
137
+ const by = await otherBlockWriters(cwd, config, item, target, local, opts);
138
+ if (by.length) {
139
+ logger.warn(`${rel} was written by ${by.join(', ')}, not ${item.name}: keeping it (pass --overwrite to replace it).`);
140
+ opts.conflicts?.push({ file: rel, item: item.name, by });
141
+ } else {
142
+ logger.info(`${rel} already exists and differs from ${item.name}'s version: keeping it (pass --overwrite to replace it).`);
143
+ }
144
+ return null;
86
145
  }
87
146
 
88
147
  if (existsSync(target) && !opts.overwrite && !opts.yes) {
@@ -94,20 +153,67 @@ async function writeRegistryFile(cwd, config, item, file, opts) {
94
153
  });
95
154
  if (!r.overwrite) {
96
155
  logger.info(`Skipped ${basename(target)}`);
97
- return;
156
+ return null;
98
157
  }
99
158
  }
100
159
 
101
- const content = transformForProject(file.content || '', target, config, item);
160
+ const content = transformForProject(file.content || '', target, config, item, { file, cwd });
102
161
  writeFileSync(target, content, 'utf8');
103
162
  logger.success(`Wrote ${relative(cwd, target)}`);
163
+ return relative(cwd, target);
164
+ }
165
+
166
+ /**
167
+ * The OTHER blocks whose file at `target` is exactly `local`, by name: what
168
+ * `add` would have written for each of them there (the same transform, so a
169
+ * baked, import-rewritten file still matches). The registry's blocks are
170
+ * loaded once per `add` run, and only when a block file already exists.
171
+ *
172
+ * @param {string} cwd
173
+ * @param {any} config
174
+ * @param {{ name: string }} item the block being installed
175
+ * @param {string} target absolute path of the existing file
176
+ * @param {string} local its content
177
+ * @param {{ registry?: string, blockCache?: { blocks?: any[] } }} opts
178
+ * @returns {Promise<string[]>}
179
+ */
180
+ async function otherBlockWriters(cwd, config, item, target, local, opts) {
181
+ const cache = opts.blockCache ?? {};
182
+ if (!cache.blocks) {
183
+ cache.blocks = [];
184
+ try {
185
+ const index = await getRegistryIndex(opts.registry);
186
+ for (const entry of index.filter((i) => i.type === 'registry:block')) {
187
+ cache.blocks.push(await getRegistryItem(entry.name, opts.registry));
188
+ }
189
+ } catch {
190
+ // An index that cannot be read only costs the warning its block name.
191
+ }
192
+ }
193
+ const by = [];
194
+ for (const other of cache.blocks) {
195
+ if (other.name === item.name) continue;
196
+ const match = (other.files || []).some((f) =>
197
+ resolveTarget(cwd, config, other, f) === target &&
198
+ transformForProject(f.content || '', target, config, other, { file: f, cwd }) === local);
199
+ if (match) by.push(other.name);
200
+ }
201
+ return by;
202
+ }
203
+
204
+ function isBlock(item, file) {
205
+ return item?.type === 'registry:block' || file?.type === 'registry:block';
104
206
  }
105
207
 
106
208
  /**
107
209
  * The single transform that turns a registry file's raw content into what
108
210
  * lands in the user's project. `add` writes this; `diff` compares against it,
109
- * so the two cannot disagree (#983). Two steps:
211
+ * so the two cannot disagree (#983). Three steps:
110
212
  *
213
+ * 0. Bake the style (#1666): a registry file in base form carries semantic
214
+ * `cn-*` names, which are replaced by the Tailwind classes of the style
215
+ * `components.json` names (`new-york` when it names none), so the file an
216
+ * app owns is plain Tailwind. A file with no `cn-*` name is untouched.
111
217
  * 1. Retarget the registry-relative `../lib/utils.ts` / `../lib/dom.ts` imports
112
218
  * to the project's resolved helper paths (see {@link rewriteUtilsImport}).
113
219
  * 2. For a Tier-1 helper component (a `registry:ui` file that is NOT a custom
@@ -118,12 +224,15 @@ async function writeRegistryFile(cwd, config, item, file, opts) {
118
224
  *
119
225
  * @param {string} content raw registry file content
120
226
  * @param {string} target absolute path where the file will be written
121
- * @param {{ resolvedPaths: { utils: string } }} config parsed components.json
227
+ * @param {{ resolvedPaths: { utils: string }, style?: string }} config parsed components.json
122
228
  * @param {{ name: string, type?: string }} [item] the registry item
123
229
  * @returns {string}
124
230
  */
125
- export function transformForProject(content, target, config, item) {
126
- let out = rewriteUtilsImport(content, target, config);
231
+ export function transformForProject(content, target, config, item, where = {}) {
232
+ let out = bakeForStyle(content, config?.style);
233
+ out = where.file?.path && where.cwd
234
+ ? rewriteRegistryImports(out, where.file.path, target, config, where.cwd, item)
235
+ : rewriteUtilsImport(out, target, config);
127
236
  if (item && item.type === 'registry:ui' && !isCustomElementSource(content)) {
128
237
  out = stripExample(out, item.name);
129
238
  }
@@ -160,6 +269,100 @@ export function rewriteUtilsImport(content, target, config) {
160
269
  return out;
161
270
  }
162
271
 
272
+ /**
273
+ * Rewrite every registry-relative import in a file to the path that resolves
274
+ * from where the file lands in the app (D18). Each relative specifier is
275
+ * resolved against the file's own registry path, then mapped to where THAT
276
+ * registry file lands:
277
+ *
278
+ * - another file of the same item (a block's page importing its own
279
+ * component) lands at its own target;
280
+ * - `lib/utils.ts` and `lib/dom.ts` land at the utils alias and beside it,
281
+ * as {@link rewriteUtilsImport} has always done (and `lib/icons.ts` beside
282
+ * them too);
283
+ * - a component (`components/<name>.ts`) lands in the ui alias directory.
284
+ *
285
+ * Anything else is left as written. This is the general form of the two
286
+ * fixed literals {@link rewriteUtilsImport} rewrites, which a block, whose
287
+ * files sit at other depths and import components and each other, needs.
288
+ *
289
+ * @param {string} content
290
+ * @param {string} fromPath the file's path inside the registry
291
+ * @param {string} target absolute path where the file is written
292
+ * @param {object} config parsed components.json
293
+ * @param {string} cwd the project root
294
+ * @param {{ files?: { path: string }[] }} [item] the item the file belongs to
295
+ */
296
+ export function rewriteRegistryImports(content, fromPath, target, config, cwd, item) {
297
+ const utilsAbs = config?.resolvedPaths?.utils;
298
+ const uiDir = config?.resolvedPaths?.ui ?? join(cwd, (config?.aliases?.ui || 'components/ui').replace(/^@\//, ''));
299
+ const own = new Map((item?.files || []).map((f) => [posix.normalize(f.path), f]));
300
+ const destOf = (regPath) => {
301
+ if (own.has(regPath) && regPath !== posix.normalize(fromPath)) return resolveTarget(cwd, config, item, own.get(regPath));
302
+ if (utilsAbs && regPath === 'lib/utils.ts') return utilsAbs;
303
+ if (utilsAbs && (regPath === 'lib/dom.ts' || regPath === 'lib/icons.ts')) return join(dirname(utilsAbs), posix.basename(regPath));
304
+ if (regPath.startsWith('components/') && !regPath.slice('components/'.length).includes('/')) return join(uiDir, posix.basename(regPath));
305
+ return null;
306
+ };
307
+ return content.replace(/((?:from|import)\s*\(?\s*)(['"])(\.{1,2}\/[^'"]+)\2/g, (whole, lead, q, spec) => {
308
+ const regPath = posix.normalize(posix.join(posix.dirname(fromPath), spec));
309
+ const dest = destOf(regPath);
310
+ if (!dest) return whole;
311
+ let rel = relPath(dirname(target), dest).split(/[\\/]/).join('/');
312
+ if (!rel.startsWith('.')) rel = './' + rel;
313
+ return `${lead}${q}${rel}${q}`;
314
+ });
315
+ }
316
+
317
+ /**
318
+ * Add the CSS variables an item carries to the app's stylesheet, leaving
319
+ * every one the app already defines as it is. `light` goes in the `:root`
320
+ * block, `dark` in `.dark`, and `theme` in `@theme inline`, which is where
321
+ * the kit's theme puts each. A missing block is appended. Returns how many
322
+ * variables were added.
323
+ *
324
+ * @param {string} cwd
325
+ * @param {string} cssPath the app's stylesheet, relative to cwd
326
+ * @param {{ theme?: Record<string, string>, light?: Record<string, string>, dark?: Record<string, string> }} cssVars
327
+ */
328
+ export function mergeCssVars(cwd, cssPath, cssVars) {
329
+ const file = join(cwd, cssPath);
330
+ let css = existsSync(file) ? readFileSync(file, 'utf8') : '';
331
+ let added = 0;
332
+ const groups = [
333
+ ['@theme inline', cssVars.theme],
334
+ [':root', cssVars.light],
335
+ ['.dark', cssVars.dark],
336
+ ];
337
+ for (const [selector, vars] of groups) {
338
+ const entries = Object.entries(vars || {});
339
+ if (!entries.length) continue;
340
+ // A token counts as present only inside ITS block: a light value in
341
+ // :root says nothing about the dark one .dark still needs.
342
+ const open = css.indexOf(`${selector} {`);
343
+ const body = open >= 0 ? css.slice(open, css.indexOf('}', open)) : '';
344
+ const missing = entries.filter(([name]) => !new RegExp(`--${escapeRe(name)}\\s*:`).test(body));
345
+ if (!missing.length) continue;
346
+ const lines = missing.map(([name, value]) => ` --${name}: ${value};`).join('\n');
347
+ if (open >= 0) {
348
+ const close = css.indexOf('}', open);
349
+ css = `${css.slice(0, close).replace(/\s*$/, '')}\n${lines}\n${css.slice(close)}`;
350
+ } else {
351
+ css = `${css.replace(/\s*$/, '')}\n\n${selector} {\n${lines}\n}\n`;
352
+ }
353
+ added += missing.length;
354
+ }
355
+ if (added) {
356
+ ensureDir(dirname(file));
357
+ writeFileSync(file, css, 'utf8');
358
+ }
359
+ return added;
360
+ }
361
+
362
+ function escapeRe(s) {
363
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
364
+ }
365
+
163
366
  /**
164
367
  * Replace a single registry-relative import specifier with the relative path
165
368
  * from `target`'s directory to `destAbs`. No-op when `spec` is absent.
@@ -178,7 +381,19 @@ function replaceImportSpecifier(content, spec, destAbs, target) {
178
381
  .replaceAll(`"${spec}"`, `"${rel}"`);
179
382
  }
180
383
 
181
- function resolveTarget(cwd, config, item, file) {
384
+ /**
385
+ * The absolute path a registry file is written to: the helper alias for the
386
+ * shared helpers, else the file's own `target`, else a folder picked by the
387
+ * file's type. `diff` reads the same answer, so it compares the file `add`
388
+ * would have written.
389
+ *
390
+ * @param {string} cwd
391
+ * @param {any} config parsed components.json
392
+ * @param {any} item the registry item
393
+ * @param {{ path: string, type?: string, target?: string }} file
394
+ * @returns {string}
395
+ */
396
+ export function resolveTarget(cwd, config, item, file) {
182
397
  // The two shared helpers are pinned to `lib/utils.ts` / `lib/dom.ts` by the
183
398
  // registry manifest, but where they actually BELONG is wherever the project's
184
399
  // `utils` alias points, because that is the path `rewriteUtilsImport` writes
@@ -205,6 +420,11 @@ function resolveTarget(cwd, config, item, file) {
205
420
  return join(cwd, (aliases.lib || 'lib').replace(/^@\//, ''), fileName);
206
421
  case 'registry:hook':
207
422
  return join(cwd, 'hooks', fileName);
423
+ case 'registry:block':
424
+ // Every shipped block file names its target (a registry test enforces
425
+ // it). A custom registry's block that does not keeps its path, so two
426
+ // files called page.ts cannot land on one another.
427
+ return join(cwd, file.path);
208
428
  default:
209
429
  return join(cwd, fileName);
210
430
  }
@@ -236,6 +456,10 @@ function helperTarget(config, item, file) {
236
456
  const name = basename(file.path || '');
237
457
  if (item.name === 'lib-utils' && name === 'utils.ts') return utilsAbs;
238
458
  if (item.name === 'lib-dom' && name === 'dom.ts') return join(dirname(utilsAbs), 'dom.ts');
459
+ // Every icon library (lib-icons, lib-icons-tabler, ...) is the SAME file in
460
+ // an app, the utils file's sibling `icons.ts`, which is where a block's
461
+ // `../lib/icons.ts` import is rewritten to (#1666).
462
+ if (/^lib-icons(-|$)/.test(item.name) && /^icons(-[a-z]+)?\.ts$/.test(name)) return join(dirname(utilsAbs), 'icons.ts');
239
463
  return null;
240
464
  }
241
465
 
@@ -1,15 +1,14 @@
1
1
  import { Command } from 'commander';
2
2
  import { existsSync, readFileSync } from 'node:fs';
3
- import { join, basename } from 'node:path';
4
3
  import { getConfig } from '../utils/get-config.js';
5
4
  import { fetchRegistryItem, fetchRegistryIndex, DEFAULT_REGISTRY_URL } from '../registry/fetcher.js';
6
- import { transformForProject } from './add.js';
5
+ import { transformForProject, resolveTarget } from './add.js';
7
6
  import { logger } from '../utils/logger.js';
8
7
 
9
8
  export const diff = new Command()
10
9
  .name('diff')
11
- .description('Show differences between local components and the registry')
12
- .argument('[name]', 'component to diff (omit to show all out-of-date)')
10
+ .description('Show differences between local components or blocks and the registry')
11
+ .argument('[name]', 'component or block to diff (omit to show all out-of-date)')
13
12
  .option('-c, --cwd <cwd>', 'the working directory', process.cwd())
14
13
  .option('--registry <url>', 'registry base URL', DEFAULT_REGISTRY_URL)
15
14
  .action(async (name, opts) => {
@@ -22,35 +21,50 @@ export const diff = new Command()
22
21
  // diff compares local files against the LIVE upstream, so it stays on the
23
22
  // network path (NOT local-first): local-first would compare the package
24
23
  // against itself. Resolve the names to compare, then fetch each item's full
25
- // content (the index is metadata-only).
24
+ // content (the index is metadata-only). Installed blocks are compared too
25
+ // (D18).
26
26
  let names;
27
27
  if (name) {
28
28
  names = [name];
29
29
  } else {
30
30
  const index = await fetchRegistryIndex(opts.registry);
31
- names = index.filter((i) => i.type === 'registry:ui').map((i) => i.name);
31
+ names = index.filter((i) => i.type === 'registry:ui' || i.type === 'registry:block').map((i) => i.name);
32
32
  }
33
33
 
34
- const uiDir = config.resolvedPaths.ui;
35
- let changed = 0;
36
-
34
+ // Grouped by the file on disk, not by item: several blocks can write the
35
+ // same target (every login block writes app/login/page.ts), so a file is
36
+ // up to date when it matches ANY item that writes it, and is reported
37
+ // once otherwise.
38
+ /** @type {Map<string, { items: string[], expected: string[] }>} */
39
+ const byTarget = new Map();
37
40
  for (const n of names) {
38
41
  const item = await fetchRegistryItem(n, opts.registry);
39
42
  for (const file of item.files || []) {
40
- const target = join(uiDir, basename(file.path));
43
+ // The path `add` writes, so the two cannot disagree (#983).
44
+ const target = resolveTarget(opts.cwd, config, item, file);
41
45
  if (!existsSync(target)) continue;
42
- const local = readFileSync(target, 'utf8');
43
- // Compare against what `add` WOULD write (import-rewrite + example
44
- // strip), not the raw registry content: otherwise a pristine install
45
- // reports every import-rewritten component as differing (#983).
46
- const expected = transformForProject(file.content || '', target, config, item);
47
- if (local !== expected) {
48
- logger.info(`${logger.bold(item.name)}: ${basename(file.path)} differs from registry`);
49
- changed++;
50
- }
46
+ // Compare against what `add` WOULD write (bake, import rewrite and
47
+ // example strip), not the raw registry content: otherwise a pristine
48
+ // install reports every import-rewritten file as differing (#983).
49
+ const expected = transformForProject(file.content || '', target, config, item, { file, cwd: opts.cwd });
50
+ const entry = byTarget.get(target) ?? { items: [], expected: [] };
51
+ entry.items.push(item.name);
52
+ entry.expected.push(expected);
53
+ byTarget.set(target, entry);
51
54
  }
52
55
  }
53
56
 
54
- if (changed === 0) logger.success('All local components match the registry.');
57
+ let changed = 0;
58
+ for (const [target, { items, expected }] of byTarget) {
59
+ const local = readFileSync(target, 'utf8');
60
+ if (expected.includes(local)) continue;
61
+ const rel = target.startsWith(opts.cwd) ? target.slice(opts.cwd.length + 1) : target;
62
+ logger.info(items.length === 1
63
+ ? `${logger.bold(items[0])}: ${rel} differs from registry`
64
+ : `${rel} differs from every item that writes it (${items.join(', ')})`);
65
+ changed++;
66
+ }
67
+
68
+ if (changed === 0) logger.success('All local components and blocks match the registry.');
55
69
  else logger.info(`\n${changed} file${changed === 1 ? '' : 's'} differ. Re-add with ${logger.cyan('npx @webjsdev/ui add <name> -o')} to overwrite.`);
56
70
  });
@@ -3,6 +3,8 @@ import { existsSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { getConfig } from '../utils/get-config.js';
5
5
  import { DEFAULT_REGISTRY_URL } from '../registry/fetcher.js';
6
+ import { loadRegistryIndex } from '../registry/local.js';
7
+ import { itemKind } from '../registry/extract.js';
6
8
  import { logger } from '../utils/logger.js';
7
9
 
8
10
  export const info = new Command()
@@ -15,6 +17,11 @@ export const info = new Command()
15
17
 
16
18
  logger.info(`${logger.bold('cwd')} ${cwd}`);
17
19
  logger.info(`${logger.bold('Registry')} ${DEFAULT_REGISTRY_URL}`);
20
+ // What the installed kit ships (the packaged registry `add` reads), with
21
+ // the blocks and charts counted beside the components (D18).
22
+ const kinds = loadRegistryIndex().map(itemKind);
23
+ const count = (k) => kinds.filter((x) => x === k).length;
24
+ logger.info(`${logger.bold('Kit')} ${count('ui')} components, ${count('block')} blocks, ${count('chart')} charts`);
18
25
  logger.info(`${logger.bold('Config')} ${config ? 'components.json ✔' : 'components.json ✖ (run `npx @webjsdev/ui init`)'}`);
19
26
  if (config) {
20
27
  logger.info(`${logger.bold('Base color')} ${config.tailwind.baseColor}`);
@@ -5,7 +5,16 @@ import prompts from 'prompts';
5
5
  import { writeConfig, CONFIG_FILE } from '../utils/get-config.js';
6
6
  import { logger } from '../utils/logger.js';
7
7
  import { getRegistryItem, DEFAULT_REGISTRY_URL } from '../registry/fetcher.js';
8
- import { ensureTheme } from '../utils/theme.js';
8
+ import { ensureTheme, presetThemeBlock } from '../utils/theme.js';
9
+ import { STYLE_NAMES } from '../registry/style-bake.js';
10
+ import {
11
+ ICON_LIBRARIES,
12
+ decodePreset,
13
+ getIconLibrary,
14
+ isPresetCode,
15
+ presetFontUrls,
16
+ resolvePresetConfig,
17
+ } from '../../packages/registry/themes/preset.js';
9
18
 
10
19
  const BASE_COLORS = ['neutral', 'stone', 'zinc', 'mauve', 'olive', 'mist', 'taupe'];
11
20
 
@@ -50,11 +59,46 @@ export const init = new Command()
50
59
  .option('-y, --yes', 'skip confirmation prompts', false)
51
60
  .option('--base-color <color>', `base color (${BASE_COLORS.join('|')})`)
52
61
  .option('--css <path>', 'path to the project Tailwind CSS file')
53
- .option('-o, --overwrite', 'replace an existing alias map and existing helper files', false)
62
+ .option('-o, --overwrite', 'replace an existing alias map, existing helper files and the theme block', false)
63
+ .option('--preset <code>', 'apply a design-system preset code from webjs.dev/ui/create (style, colors, font, radius, icons)')
64
+ .option('--style <name>', `component style (${STYLE_NAMES.join('|')})`)
65
+ .option('--icons <library>', `icon library (${ICON_LIBRARIES.map((l) => l.name).join('|')})`)
54
66
  .option('--registry <url>', 'registry base URL', DEFAULT_REGISTRY_URL)
55
67
  .action(async (opts) => {
56
68
  const cwd = opts.cwd;
57
69
 
70
+ // A preset is the configurator's whole design system in one code (#1666).
71
+ // An explicit flag beside it wins over the field it names, so a code can
72
+ // be reused with one choice changed.
73
+ /** @type {import('../../packages/registry/themes/preset.js').PresetConfig | null} */
74
+ let preset = null;
75
+ if (opts.preset) {
76
+ const decoded = isPresetCode(opts.preset) ? decodePreset(opts.preset) : null;
77
+ if (!decoded) {
78
+ logger.error(`"${opts.preset}" is not a preset code. Copy one from ${logger.cyan('https://webjs.dev/ui/create')}.`);
79
+ process.exit(1);
80
+ }
81
+ const resolved = resolvePresetConfig({
82
+ ...decoded,
83
+ ...(opts.style ? { style: opts.style } : null),
84
+ ...(opts.icons ? { iconLibrary: opts.icons } : null),
85
+ ...(opts.baseColor ? { baseColor: opts.baseColor } : null),
86
+ });
87
+ if (resolved.error) {
88
+ logger.error(`Preset ${opts.preset}: ${resolved.error}.`);
89
+ process.exit(1);
90
+ }
91
+ preset = resolved.config;
92
+ }
93
+ if (opts.style && !STYLE_NAMES.includes(opts.style)) {
94
+ logger.error(`Unknown style "${opts.style}". Use one of: ${STYLE_NAMES.join(', ')}.`);
95
+ process.exit(1);
96
+ }
97
+ if (opts.icons && !getIconLibrary(opts.icons)) {
98
+ logger.error(`Unknown icon library "${opts.icons}". Use one of: ${ICON_LIBRARIES.map((l) => l.name).join(', ')}.`);
99
+ process.exit(1);
100
+ }
101
+
58
102
  // Re-running `init` on a project that already has one must not RELOCATE it.
59
103
  // The alias map decides where cn() lives and what every already-added
60
104
  // component imports, and `tailwind.css` decides which stylesheet the app
@@ -97,7 +141,7 @@ export const init = new Command()
97
141
 
98
142
  /** @type {{ baseColor: string, css: string }} */
99
143
  let answers = {
100
- baseColor: opts.baseColor || initialBaseColor,
144
+ baseColor: (preset && preset.baseColor) || opts.baseColor || initialBaseColor,
101
145
  css: opts.css || initialCss,
102
146
  };
103
147
 
@@ -105,7 +149,7 @@ export const init = new Command()
105
149
  const r = await prompts(
106
150
  [
107
151
  {
108
- type: opts.baseColor ? null : 'select',
152
+ type: opts.baseColor || preset ? null : 'select',
109
153
  name: 'baseColor',
110
154
  message: 'Base color?',
111
155
  choices: BASE_COLORS.map((c) => ({ title: c, value: c })),
@@ -127,9 +171,12 @@ export const init = new Command()
127
171
  // unrecognised key through here would write a file that `getConfig` (and
128
172
  // therefore `add` / `diff` / `info`) throws on. Preserving a project's
129
173
  // settings means its VALUES, not arbitrary extra fields.
174
+ // A style or icon library the command names replaces the stored one,
175
+ // since naming it is the request; otherwise a re-run keeps what is there.
176
+ const iconLibrary = (preset && preset.iconLibrary) || opts.icons || (keep && existing.iconLibrary) || 'lucide';
130
177
  const config = {
131
178
  $schema: 'https://ui.webjs.dev/schema.json',
132
- style: (keep && existing.style) || 'default',
179
+ style: (preset && preset.style) || opts.style || (keep && existing.style) || 'default',
133
180
  tailwind: {
134
181
  css: answers.css,
135
182
  baseColor: answers.baseColor,
@@ -138,7 +185,7 @@ export const init = new Command()
138
185
  : true,
139
186
  },
140
187
  aliases,
141
- iconLibrary: (keep && existing.iconLibrary) || 'lucide',
188
+ iconLibrary,
142
189
  };
143
190
 
144
191
  writeConfig(cwd, config);
@@ -147,17 +194,49 @@ export const init = new Command()
147
194
  // Pull lib/utils + the chosen theme from the registry and write them in.
148
195
  await writeLibUtils(cwd, aliases.utils, opts.registry, opts.overwrite);
149
196
 
197
+ // The icon library is written only when the command names one, since it
198
+ // is a large file an app that draws no kit icons does not need.
199
+ if (preset || opts.icons) await writeIcons(cwd, aliases.utils, iconLibrary, opts.registry, opts.overwrite);
200
+
201
+ // A preset's tokens are its own palette over the kit's theme file.
202
+ let block;
203
+ if (preset) {
204
+ try {
205
+ const neutral = await getRegistryItem('theme-neutral', opts.registry);
206
+ block = presetThemeBlock(neutral.files[0].content || '', preset);
207
+ } catch (e) {
208
+ logger.error(`Could not build the theme for preset ${opts.preset}: ${e.message}`);
209
+ process.exit(1);
210
+ }
211
+ }
212
+
150
213
  // The theme tokens are what the class helpers render against. A silent
151
214
  // failure here (the old behaviour) left an unstyled install with a clean
152
215
  // exit code, the exact trap for an autonomous agent, so hard-fail (#983).
153
- const theme = await ensureTheme(cwd, answers.baseColor, answers.css, opts.registry);
216
+ // A preset, or --overwrite, replaces an existing block in place (#1666).
217
+ const theme = await ensureTheme(cwd, answers.baseColor, answers.css, opts.registry, {
218
+ replace: Boolean(preset || opts.overwrite),
219
+ block,
220
+ });
154
221
  if (theme.status === 'failed') {
155
222
  logger.error(`Could not write theme tokens into ${answers.css}: ${theme.error}`);
156
223
  logger.info('The class helpers render against these tokens, so this must succeed.');
157
224
  process.exit(1);
158
225
  }
159
226
  if (theme.status === 'written') logger.success(`Wrote theme into ${answers.css}`);
160
- else logger.info(`Theme already present in ${answers.css}: skipping.`);
227
+ else if (theme.status === 'replaced') logger.success(`Replaced the theme in ${answers.css}`);
228
+ else if (theme.status === 'legacy') {
229
+ logger.warn(`The theme block in ${answers.css} predates the end marker, so it was left as it is.`);
230
+ logger.info(`To replace it, delete the block (from ${logger.cyan('/* @webjsdev/ui theme */')} to the end of its rules) and re-run init.`);
231
+ } else logger.info(`Theme already present in ${answers.css}: skipping.`);
232
+
233
+ if (preset) {
234
+ const urls = presetFontUrls(preset);
235
+ if (urls.length) {
236
+ logger.info('The preset names its fonts in the theme. Load the faces, either self-hosted under public/ or from:');
237
+ for (const url of urls) logger.info(` ${url}`);
238
+ }
239
+ }
161
240
 
162
241
  logger.break();
163
242
  logger.success('Done.');
@@ -224,6 +303,44 @@ async function writeLibUtils(cwd, utilsAlias, registryUrl, overwrite) {
224
303
  }
225
304
  }
226
305
 
306
+ /**
307
+ * Write the chosen icon library as the utils file's sibling `icons.ts`, the
308
+ * path a block's icon import is rewritten to (#1666). Naming a library is a
309
+ * request to switch, so a file that is still one of the five libraries as
310
+ * shipped is replaced. A file the app has edited is kept unless `--overwrite`
311
+ * says otherwise, the same rule as the other helpers.
312
+ */
313
+ async function writeIcons(cwd, utilsAlias, library, registryUrl, overwrite) {
314
+ const lib = getIconLibrary(library);
315
+ if (!lib) return;
316
+ const target = join(dirname(join(cwd, utilsAlias.replace(/^@\//, '') + '.ts')), 'icons.ts');
317
+ const rel = relative(cwd, target);
318
+ try {
319
+ const item = await getRegistryItem(lib.item, registryUrl);
320
+ const content = item.files && item.files[0] ? item.files[0].content || '' : '';
321
+ if (!content) throw new Error(`${lib.item} has no content`);
322
+ if (existsSync(target) && !overwrite) {
323
+ const current = readFileSync(target, 'utf8');
324
+ if (current === content) {
325
+ logger.info(`${rel} is already ${lib.title}: keeping it.`);
326
+ return;
327
+ }
328
+ const pristine = await Promise.all(
329
+ ICON_LIBRARIES.map((l) => getRegistryItem(l.item, registryUrl).then((it) => it.files[0].content, () => null)),
330
+ );
331
+ if (!pristine.includes(current)) {
332
+ logger.warn(`${rel} has been edited, so it was kept. Re-run with --overwrite to replace it with ${lib.title}.`);
333
+ return;
334
+ }
335
+ }
336
+ ensureDir(dirname(target));
337
+ writeFileSync(target, content, 'utf8');
338
+ logger.success(`Wrote ${rel} (${lib.title})`);
339
+ } catch (e) {
340
+ logger.warn(`Could not fetch ${lib.item} from the registry (${e.message}).`);
341
+ }
342
+ }
343
+
227
344
  function relative(cwd, p) {
228
345
  return p.startsWith(cwd) ? p.slice(cwd.length + 1) : p;
229
346
  }