@nikala-ui/folio 0.13.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 (454) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +221 -0
  3. package/dist/cli/commands/build.d.ts +5 -0
  4. package/dist/cli/commands/build.d.ts.map +1 -0
  5. package/dist/cli/commands/build.js +28 -0
  6. package/dist/cli/commands/build.js.map +1 -0
  7. package/dist/cli/commands/dev.d.ts +7 -0
  8. package/dist/cli/commands/dev.d.ts.map +1 -0
  9. package/dist/cli/commands/dev.js +33 -0
  10. package/dist/cli/commands/dev.js.map +1 -0
  11. package/dist/cli/commands/init.d.ts +2 -0
  12. package/dist/cli/commands/init.d.ts.map +1 -0
  13. package/dist/cli/commands/init.js +359 -0
  14. package/dist/cli/commands/init.js.map +1 -0
  15. package/dist/cli/commands/preview.d.ts +7 -0
  16. package/dist/cli/commands/preview.d.ts.map +1 -0
  17. package/dist/cli/commands/preview.js +25 -0
  18. package/dist/cli/commands/preview.js.map +1 -0
  19. package/dist/cli/commands/serve.d.ts +7 -0
  20. package/dist/cli/commands/serve.d.ts.map +1 -0
  21. package/dist/cli/commands/serve.js +34 -0
  22. package/dist/cli/commands/serve.js.map +1 -0
  23. package/dist/cli/index.d.ts +3 -0
  24. package/dist/cli/index.d.ts.map +1 -0
  25. package/dist/cli/index.js +55 -0
  26. package/dist/cli/index.js.map +1 -0
  27. package/dist/client/app-types.d.ts +18 -0
  28. package/dist/client/app-types.d.ts.map +1 -0
  29. package/dist/client/app-types.js +2 -0
  30. package/dist/client/app-types.js.map +1 -0
  31. package/dist/client/app.d.ts +5 -0
  32. package/dist/client/app.d.ts.map +1 -0
  33. package/dist/client/app.jsx +51 -0
  34. package/dist/client/app.jsx.map +1 -0
  35. package/dist/client/base.css +90 -0
  36. package/dist/client/components/docs-layout-shell.d.ts +23 -0
  37. package/dist/client/components/docs-layout-shell.d.ts.map +1 -0
  38. package/dist/client/components/docs-layout-shell.jsx +15 -0
  39. package/dist/client/components/docs-layout-shell.jsx.map +1 -0
  40. package/dist/client/components/docs-page-content.d.ts +9 -0
  41. package/dist/client/components/docs-page-content.d.ts.map +1 -0
  42. package/dist/client/components/docs-page-content.jsx +10 -0
  43. package/dist/client/components/docs-page-content.jsx.map +1 -0
  44. package/dist/client/entry.d.ts +2 -0
  45. package/dist/client/entry.d.ts.map +1 -0
  46. package/dist/client/entry.jsx +32 -0
  47. package/dist/client/entry.jsx.map +1 -0
  48. package/dist/client/index.html +23 -0
  49. package/dist/client/navigation/page-navigation.d.ts +9 -0
  50. package/dist/client/navigation/page-navigation.d.ts.map +1 -0
  51. package/dist/client/navigation/page-navigation.js +51 -0
  52. package/dist/client/navigation/page-navigation.js.map +1 -0
  53. package/dist/client/page-actions.d.ts +10 -0
  54. package/dist/client/page-actions.d.ts.map +1 -0
  55. package/dist/client/page-actions.js +63 -0
  56. package/dist/client/page-actions.js.map +1 -0
  57. package/dist/client/public/favicon.ico +0 -0
  58. package/dist/client/routing/use-docs-router.d.ts +17 -0
  59. package/dist/client/routing/use-docs-router.d.ts.map +1 -0
  60. package/dist/client/routing/use-docs-router.js +87 -0
  61. package/dist/client/routing/use-docs-router.js.map +1 -0
  62. package/dist/client/ssr-entry.d.ts +3 -0
  63. package/dist/client/ssr-entry.d.ts.map +1 -0
  64. package/dist/client/ssr-entry.jsx +13 -0
  65. package/dist/client/ssr-entry.jsx.map +1 -0
  66. package/dist/client/style.css +186 -0
  67. package/dist/client/utils/sidebar.d.ts +2 -0
  68. package/dist/client/utils/sidebar.d.ts.map +1 -0
  69. package/dist/client/utils/sidebar.js +2 -0
  70. package/dist/client/utils/sidebar.js.map +1 -0
  71. package/dist/components/mdx-components.d.ts +65 -0
  72. package/dist/components/mdx-components.d.ts.map +1 -0
  73. package/dist/components/mdx-components.jsx +133 -0
  74. package/dist/components/mdx-components.jsx.map +1 -0
  75. package/dist/components/ui/api-table.d.ts +29 -0
  76. package/dist/components/ui/api-table.d.ts.map +1 -0
  77. package/dist/components/ui/api-table.jsx +66 -0
  78. package/dist/components/ui/api-table.jsx.map +1 -0
  79. package/dist/components/ui/badge.d.ts +17 -0
  80. package/dist/components/ui/badge.d.ts.map +1 -0
  81. package/dist/components/ui/badge.jsx +33 -0
  82. package/dist/components/ui/badge.jsx.map +1 -0
  83. package/dist/components/ui/breadcrumb.d.ts +51 -0
  84. package/dist/components/ui/breadcrumb.d.ts.map +1 -0
  85. package/dist/components/ui/breadcrumb.jsx +62 -0
  86. package/dist/components/ui/breadcrumb.jsx.map +1 -0
  87. package/dist/components/ui/button.d.ts +19 -0
  88. package/dist/components/ui/button.d.ts.map +1 -0
  89. package/dist/components/ui/button.jsx +54 -0
  90. package/dist/components/ui/button.jsx.map +1 -0
  91. package/dist/components/ui/callout.d.ts +21 -0
  92. package/dist/components/ui/callout.d.ts.map +1 -0
  93. package/dist/components/ui/callout.jsx +76 -0
  94. package/dist/components/ui/callout.jsx.map +1 -0
  95. package/dist/components/ui/card.d.ts +44 -0
  96. package/dist/components/ui/card.d.ts.map +1 -0
  97. package/dist/components/ui/card.jsx +45 -0
  98. package/dist/components/ui/card.jsx.map +1 -0
  99. package/dist/components/ui/code-block.d.ts +23 -0
  100. package/dist/components/ui/code-block.d.ts.map +1 -0
  101. package/dist/components/ui/code-block.jsx +263 -0
  102. package/dist/components/ui/code-block.jsx.map +1 -0
  103. package/dist/components/ui/collapsible.d.ts +27 -0
  104. package/dist/components/ui/collapsible.d.ts.map +1 -0
  105. package/dist/components/ui/collapsible.jsx +29 -0
  106. package/dist/components/ui/collapsible.jsx.map +1 -0
  107. package/dist/components/ui/command.d.ts +57 -0
  108. package/dist/components/ui/command.d.ts.map +1 -0
  109. package/dist/components/ui/command.jsx +224 -0
  110. package/dist/components/ui/command.jsx.map +1 -0
  111. package/dist/components/ui/component-viewer.d.ts +33 -0
  112. package/dist/components/ui/component-viewer.d.ts.map +1 -0
  113. package/dist/components/ui/component-viewer.jsx +210 -0
  114. package/dist/components/ui/component-viewer.jsx.map +1 -0
  115. package/dist/components/ui/container.d.ts +13 -0
  116. package/dist/components/ui/container.d.ts.map +1 -0
  117. package/dist/components/ui/container.jsx +29 -0
  118. package/dist/components/ui/container.jsx.map +1 -0
  119. package/dist/components/ui/dropdown-menu.d.ts +58 -0
  120. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  121. package/dist/components/ui/dropdown-menu.jsx +112 -0
  122. package/dist/components/ui/dropdown-menu.jsx.map +1 -0
  123. package/dist/components/ui/input-group.d.ts +16 -0
  124. package/dist/components/ui/input-group.d.ts.map +1 -0
  125. package/dist/components/ui/input-group.jsx +33 -0
  126. package/dist/components/ui/input-group.jsx.map +1 -0
  127. package/dist/components/ui/kbd.d.ts +15 -0
  128. package/dist/components/ui/kbd.d.ts.map +1 -0
  129. package/dist/components/ui/kbd.jsx +42 -0
  130. package/dist/components/ui/kbd.jsx.map +1 -0
  131. package/dist/components/ui/list.d.ts +36 -0
  132. package/dist/components/ui/list.d.ts.map +1 -0
  133. package/dist/components/ui/list.jsx +131 -0
  134. package/dist/components/ui/list.jsx.map +1 -0
  135. package/dist/components/ui/logo.d.ts +9 -0
  136. package/dist/components/ui/logo.d.ts.map +1 -0
  137. package/dist/components/ui/logo.jsx +31 -0
  138. package/dist/components/ui/logo.jsx.map +1 -0
  139. package/dist/components/ui/navbar.d.ts +57 -0
  140. package/dist/components/ui/navbar.d.ts.map +1 -0
  141. package/dist/components/ui/navbar.jsx +174 -0
  142. package/dist/components/ui/navbar.jsx.map +1 -0
  143. package/dist/components/ui/package-manager-tabs.d.ts +21 -0
  144. package/dist/components/ui/package-manager-tabs.d.ts.map +1 -0
  145. package/dist/components/ui/package-manager-tabs.jsx +167 -0
  146. package/dist/components/ui/package-manager-tabs.jsx.map +1 -0
  147. package/dist/components/ui/pager.d.ts +27 -0
  148. package/dist/components/ui/pager.d.ts.map +1 -0
  149. package/dist/components/ui/pager.jsx +50 -0
  150. package/dist/components/ui/pager.jsx.map +1 -0
  151. package/dist/components/ui/scroll-area.d.ts +9 -0
  152. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  153. package/dist/components/ui/scroll-area.jsx +160 -0
  154. package/dist/components/ui/scroll-area.jsx.map +1 -0
  155. package/dist/components/ui/section-heading.d.ts +23 -0
  156. package/dist/components/ui/section-heading.d.ts.map +1 -0
  157. package/dist/components/ui/section-heading.jsx +82 -0
  158. package/dist/components/ui/section-heading.jsx.map +1 -0
  159. package/dist/components/ui/separator.d.ts +13 -0
  160. package/dist/components/ui/separator.d.ts.map +1 -0
  161. package/dist/components/ui/separator.jsx +16 -0
  162. package/dist/components/ui/separator.jsx.map +1 -0
  163. package/dist/components/ui/sidebar.d.ts +103 -0
  164. package/dist/components/ui/sidebar.d.ts.map +1 -0
  165. package/dist/components/ui/sidebar.jsx +315 -0
  166. package/dist/components/ui/sidebar.jsx.map +1 -0
  167. package/dist/components/ui/skeleton.d.ts +9 -0
  168. package/dist/components/ui/skeleton.d.ts.map +1 -0
  169. package/dist/components/ui/skeleton.jsx +11 -0
  170. package/dist/components/ui/skeleton.jsx.map +1 -0
  171. package/dist/components/ui/spinner.d.ts +11 -0
  172. package/dist/components/ui/spinner.d.ts.map +1 -0
  173. package/dist/components/ui/spinner.jsx +21 -0
  174. package/dist/components/ui/spinner.jsx.map +1 -0
  175. package/dist/components/ui/table-of-contents.d.ts +20 -0
  176. package/dist/components/ui/table-of-contents.d.ts.map +1 -0
  177. package/dist/components/ui/table-of-contents.jsx +77 -0
  178. package/dist/components/ui/table-of-contents.jsx.map +1 -0
  179. package/dist/components/ui/table.d.ts +58 -0
  180. package/dist/components/ui/table.d.ts.map +1 -0
  181. package/dist/components/ui/table.jsx +61 -0
  182. package/dist/components/ui/table.jsx.map +1 -0
  183. package/dist/components/ui/tabs.d.ts +42 -0
  184. package/dist/components/ui/tabs.d.ts.map +1 -0
  185. package/dist/components/ui/tabs.jsx +93 -0
  186. package/dist/components/ui/tabs.jsx.map +1 -0
  187. package/dist/components/ui/theme-toggle.d.ts +19 -0
  188. package/dist/components/ui/theme-toggle.d.ts.map +1 -0
  189. package/dist/components/ui/theme-toggle.jsx +142 -0
  190. package/dist/components/ui/theme-toggle.jsx.map +1 -0
  191. package/dist/components/ui/tooltip.d.ts +10 -0
  192. package/dist/components/ui/tooltip.d.ts.map +1 -0
  193. package/dist/components/ui/tooltip.jsx +18 -0
  194. package/dist/components/ui/tooltip.jsx.map +1 -0
  195. package/dist/config.d.ts +6 -0
  196. package/dist/config.d.ts.map +1 -0
  197. package/dist/config.js +105 -0
  198. package/dist/config.js.map +1 -0
  199. package/dist/core/content-scanner.d.ts +10 -0
  200. package/dist/core/content-scanner.d.ts.map +1 -0
  201. package/dist/core/content-scanner.js +218 -0
  202. package/dist/core/content-scanner.js.map +1 -0
  203. package/dist/core/route-tree.d.ts +24 -0
  204. package/dist/core/route-tree.d.ts.map +1 -0
  205. package/dist/core/route-tree.js +221 -0
  206. package/dist/core/route-tree.js.map +1 -0
  207. package/dist/hooks/create-click-outside.d.ts +18 -0
  208. package/dist/hooks/create-click-outside.d.ts.map +1 -0
  209. package/dist/hooks/create-click-outside.js +55 -0
  210. package/dist/hooks/create-click-outside.js.map +1 -0
  211. package/dist/hooks/create-clipboard.d.ts +20 -0
  212. package/dist/hooks/create-clipboard.d.ts.map +1 -0
  213. package/dist/hooks/create-clipboard.js +48 -0
  214. package/dist/hooks/create-clipboard.js.map +1 -0
  215. package/dist/hooks/create-color-mode.d.ts +27 -0
  216. package/dist/hooks/create-color-mode.d.ts.map +1 -0
  217. package/dist/hooks/create-color-mode.js +86 -0
  218. package/dist/hooks/create-color-mode.js.map +1 -0
  219. package/dist/hooks/create-controllable-signal.d.ts +17 -0
  220. package/dist/hooks/create-controllable-signal.d.ts.map +1 -0
  221. package/dist/hooks/create-controllable-signal.js +35 -0
  222. package/dist/hooks/create-controllable-signal.js.map +1 -0
  223. package/dist/hooks/create-focus-trap.d.ts +15 -0
  224. package/dist/hooks/create-focus-trap.d.ts.map +1 -0
  225. package/dist/hooks/create-focus-trap.js +72 -0
  226. package/dist/hooks/create-focus-trap.js.map +1 -0
  227. package/dist/hooks/create-keybindings.d.ts +34 -0
  228. package/dist/hooks/create-keybindings.d.ts.map +1 -0
  229. package/dist/hooks/create-keybindings.js +113 -0
  230. package/dist/hooks/create-keybindings.js.map +1 -0
  231. package/dist/hooks/create-lock-scroll.d.ts +20 -0
  232. package/dist/hooks/create-lock-scroll.d.ts.map +1 -0
  233. package/dist/hooks/create-lock-scroll.js +77 -0
  234. package/dist/hooks/create-lock-scroll.js.map +1 -0
  235. package/dist/hooks/create-resize-observer.d.ts +27 -0
  236. package/dist/hooks/create-resize-observer.d.ts.map +1 -0
  237. package/dist/hooks/create-resize-observer.js +59 -0
  238. package/dist/hooks/create-resize-observer.js.map +1 -0
  239. package/dist/hooks/create-scroll-position.d.ts +29 -0
  240. package/dist/hooks/create-scroll-position.d.ts.map +1 -0
  241. package/dist/hooks/create-scroll-position.js +101 -0
  242. package/dist/hooks/create-scroll-position.js.map +1 -0
  243. package/dist/index.d.ts +13 -0
  244. package/dist/index.d.ts.map +1 -0
  245. package/dist/index.js +14 -0
  246. package/dist/index.js.map +1 -0
  247. package/dist/lib/cn.d.ts +6 -0
  248. package/dist/lib/cn.d.ts.map +1 -0
  249. package/dist/lib/cn.js +9 -0
  250. package/dist/lib/cn.js.map +1 -0
  251. package/dist/mdx/compiler.d.ts +13 -0
  252. package/dist/mdx/compiler.d.ts.map +1 -0
  253. package/dist/mdx/compiler.js +38 -0
  254. package/dist/mdx/compiler.js.map +1 -0
  255. package/dist/mdx/highlighter.d.ts +22 -0
  256. package/dist/mdx/highlighter.d.ts.map +1 -0
  257. package/dist/mdx/highlighter.js +190 -0
  258. package/dist/mdx/highlighter.js.map +1 -0
  259. package/dist/mdx/rehype-shiki.d.ts +3 -0
  260. package/dist/mdx/rehype-shiki.d.ts.map +1 -0
  261. package/dist/mdx/rehype-shiki.js +83 -0
  262. package/dist/mdx/rehype-shiki.js.map +1 -0
  263. package/dist/navigation/index.d.ts +3 -0
  264. package/dist/navigation/index.d.ts.map +1 -0
  265. package/dist/navigation/index.js +3 -0
  266. package/dist/navigation/index.js.map +1 -0
  267. package/dist/navigation/repository-links.d.ts +3 -0
  268. package/dist/navigation/repository-links.d.ts.map +1 -0
  269. package/dist/navigation/repository-links.js +35 -0
  270. package/dist/navigation/repository-links.js.map +1 -0
  271. package/dist/navigation/sidebar-state.d.ts +6 -0
  272. package/dist/navigation/sidebar-state.d.ts.map +1 -0
  273. package/dist/navigation/sidebar-state.js +20 -0
  274. package/dist/navigation/sidebar-state.js.map +1 -0
  275. package/dist/navigation/sidebar-tree.d.ts +7 -0
  276. package/dist/navigation/sidebar-tree.d.ts.map +1 -0
  277. package/dist/navigation/sidebar-tree.js +11 -0
  278. package/dist/navigation/sidebar-tree.js.map +1 -0
  279. package/dist/providers/theme-provider.d.ts +33 -0
  280. package/dist/providers/theme-provider.d.ts.map +1 -0
  281. package/dist/providers/theme-provider.jsx +158 -0
  282. package/dist/providers/theme-provider.jsx.map +1 -0
  283. package/dist/providers/theme-script.d.ts +16 -0
  284. package/dist/providers/theme-script.d.ts.map +1 -0
  285. package/dist/providers/theme-script.jsx +41 -0
  286. package/dist/providers/theme-script.jsx.map +1 -0
  287. package/dist/providers/theme-transitions.d.ts +12 -0
  288. package/dist/providers/theme-transitions.d.ts.map +1 -0
  289. package/dist/providers/theme-transitions.js +115 -0
  290. package/dist/providers/theme-transitions.js.map +1 -0
  291. package/dist/registry/api-table.json +21 -0
  292. package/dist/registry/badge.json +18 -0
  293. package/dist/registry/breadcrumb.json +17 -0
  294. package/dist/registry/button.json +21 -0
  295. package/dist/registry/callout.json +19 -0
  296. package/dist/registry/card.json +17 -0
  297. package/dist/registry/code-block.json +28 -0
  298. package/dist/registry/collapsible.json +18 -0
  299. package/dist/registry/command.json +27 -0
  300. package/dist/registry/component-viewer.json +25 -0
  301. package/dist/registry/container.json +18 -0
  302. package/dist/registry/create-click-outside.json +13 -0
  303. package/dist/registry/create-clipboard.json +13 -0
  304. package/dist/registry/create-color-mode.json +13 -0
  305. package/dist/registry/create-controllable-signal.json +13 -0
  306. package/dist/registry/create-focus-trap.json +13 -0
  307. package/dist/registry/create-keybindings.json +13 -0
  308. package/dist/registry/create-lock-scroll.json +13 -0
  309. package/dist/registry/create-resize-observer.json +13 -0
  310. package/dist/registry/create-scroll-position.json +13 -0
  311. package/dist/registry/dropdown-menu.json +22 -0
  312. package/dist/registry/input-group.json +21 -0
  313. package/dist/registry/kbd.json +18 -0
  314. package/dist/registry/list.json +20 -0
  315. package/dist/registry/logo.json +17 -0
  316. package/dist/registry/navbar.json +19 -0
  317. package/dist/registry/package-manager-tabs.json +24 -0
  318. package/dist/registry/pager.json +21 -0
  319. package/dist/registry/scroll-area.json +21 -0
  320. package/dist/registry/section-heading.json +21 -0
  321. package/dist/registry/separator.json +17 -0
  322. package/dist/registry/sidebar.json +24 -0
  323. package/dist/registry/skeleton.json +17 -0
  324. package/dist/registry/spinner.json +18 -0
  325. package/dist/registry/table-of-contents.json +23 -0
  326. package/dist/registry/table.json +17 -0
  327. package/dist/registry/tabs.json +20 -0
  328. package/dist/registry/theme-manager.json +39 -0
  329. package/dist/registry/tooltip.json +18 -0
  330. package/dist/search/provider.d.ts +14 -0
  331. package/dist/search/provider.d.ts.map +1 -0
  332. package/dist/search/provider.js +14 -0
  333. package/dist/search/provider.js.map +1 -0
  334. package/dist/server/index.d.ts +18 -0
  335. package/dist/server/index.d.ts.map +1 -0
  336. package/dist/server/index.js +565 -0
  337. package/dist/server/index.js.map +1 -0
  338. package/dist/server/plugin.d.ts +10 -0
  339. package/dist/server/plugin.d.ts.map +1 -0
  340. package/dist/server/plugin.js +522 -0
  341. package/dist/server/plugin.js.map +1 -0
  342. package/dist/theme-accent.d.ts +8 -0
  343. package/dist/theme-accent.d.ts.map +1 -0
  344. package/dist/theme-accent.js +18 -0
  345. package/dist/theme-accent.js.map +1 -0
  346. package/dist/theme-mode.d.ts +4 -0
  347. package/dist/theme-mode.d.ts.map +1 -0
  348. package/dist/theme-mode.js +5 -0
  349. package/dist/theme-mode.js.map +1 -0
  350. package/dist/themes/default/content/breadcrumbs.d.ts +4 -0
  351. package/dist/themes/default/content/breadcrumbs.d.ts.map +1 -0
  352. package/dist/themes/default/content/breadcrumbs.jsx +31 -0
  353. package/dist/themes/default/content/breadcrumbs.jsx.map +1 -0
  354. package/dist/themes/default/content/pager.d.ts +5 -0
  355. package/dist/themes/default/content/pager.d.ts.map +1 -0
  356. package/dist/themes/default/content/pager.jsx +9 -0
  357. package/dist/themes/default/content/pager.jsx.map +1 -0
  358. package/dist/themes/default/content/table-of-contents.d.ts +4 -0
  359. package/dist/themes/default/content/table-of-contents.d.ts.map +1 -0
  360. package/dist/themes/default/content/table-of-contents.jsx +19 -0
  361. package/dist/themes/default/content/table-of-contents.jsx.map +1 -0
  362. package/dist/themes/default/icons.d.ts +3 -0
  363. package/dist/themes/default/icons.d.ts.map +1 -0
  364. package/dist/themes/default/icons.js +15 -0
  365. package/dist/themes/default/icons.js.map +1 -0
  366. package/dist/themes/default/index.d.ts +12 -0
  367. package/dist/themes/default/index.d.ts.map +1 -0
  368. package/dist/themes/default/index.js +30 -0
  369. package/dist/themes/default/index.js.map +1 -0
  370. package/dist/themes/default/layout.d.ts +4 -0
  371. package/dist/themes/default/layout.d.ts.map +1 -0
  372. package/dist/themes/default/layout.jsx +194 -0
  373. package/dist/themes/default/layout.jsx.map +1 -0
  374. package/dist/themes/default/navbar.d.ts +4 -0
  375. package/dist/themes/default/navbar.d.ts.map +1 -0
  376. package/dist/themes/default/navbar.jsx +83 -0
  377. package/dist/themes/default/navbar.jsx.map +1 -0
  378. package/dist/themes/default/navigation/mobile-table-of-contents.d.ts +4 -0
  379. package/dist/themes/default/navigation/mobile-table-of-contents.d.ts.map +1 -0
  380. package/dist/themes/default/navigation/mobile-table-of-contents.jsx +38 -0
  381. package/dist/themes/default/navigation/mobile-table-of-contents.jsx.map +1 -0
  382. package/dist/themes/default/navigation/sidebar-tree.d.ts +8 -0
  383. package/dist/themes/default/navigation/sidebar-tree.d.ts.map +1 -0
  384. package/dist/themes/default/navigation/sidebar-tree.jsx +124 -0
  385. package/dist/themes/default/navigation/sidebar-tree.jsx.map +1 -0
  386. package/dist/themes/default/overlays/search-dialog.d.ts +4 -0
  387. package/dist/themes/default/overlays/search-dialog.d.ts.map +1 -0
  388. package/dist/themes/default/overlays/search-dialog.jsx +52 -0
  389. package/dist/themes/default/overlays/search-dialog.jsx.map +1 -0
  390. package/dist/themes/default/sidebar.d.ts +4 -0
  391. package/dist/themes/default/sidebar.d.ts.map +1 -0
  392. package/dist/themes/default/sidebar.jsx +92 -0
  393. package/dist/themes/default/sidebar.jsx.map +1 -0
  394. package/dist/themes/index.d.ts +4 -0
  395. package/dist/themes/index.d.ts.map +1 -0
  396. package/dist/themes/index.js +5 -0
  397. package/dist/themes/index.js.map +1 -0
  398. package/dist/themes/types.d.ts +91 -0
  399. package/dist/themes/types.d.ts.map +1 -0
  400. package/dist/themes/types.js +2 -0
  401. package/dist/themes/types.js.map +1 -0
  402. package/dist/types.d.ts +132 -0
  403. package/dist/types.d.ts.map +1 -0
  404. package/dist/types.js +3 -0
  405. package/dist/types.js.map +1 -0
  406. package/dist/vendor/docs-src/components/mdx-components.tsx +144 -0
  407. package/dist/vendor/docs-src/components/ui/api-table.tsx +117 -0
  408. package/dist/vendor/docs-src/components/ui/badge.tsx +56 -0
  409. package/dist/vendor/docs-src/components/ui/breadcrumb.tsx +136 -0
  410. package/dist/vendor/docs-src/components/ui/button.tsx +82 -0
  411. package/dist/vendor/docs-src/components/ui/callout.tsx +137 -0
  412. package/dist/vendor/docs-src/components/ui/card.tsx +113 -0
  413. package/dist/vendor/docs-src/components/ui/code-block.tsx +347 -0
  414. package/dist/vendor/docs-src/components/ui/collapsible.tsx +73 -0
  415. package/dist/vendor/docs-src/components/ui/command.tsx +382 -0
  416. package/dist/vendor/docs-src/components/ui/component-viewer.tsx +363 -0
  417. package/dist/vendor/docs-src/components/ui/container.tsx +45 -0
  418. package/dist/vendor/docs-src/components/ui/dropdown-menu.tsx +298 -0
  419. package/dist/vendor/docs-src/components/ui/input-group.tsx +80 -0
  420. package/dist/vendor/docs-src/components/ui/kbd.tsx +73 -0
  421. package/dist/vendor/docs-src/components/ui/list.tsx +222 -0
  422. package/dist/vendor/docs-src/components/ui/logo.tsx +68 -0
  423. package/dist/vendor/docs-src/components/ui/navbar.tsx +375 -0
  424. package/dist/vendor/docs-src/components/ui/package-manager-tabs.tsx +226 -0
  425. package/dist/vendor/docs-src/components/ui/pager.tsx +102 -0
  426. package/dist/vendor/docs-src/components/ui/scroll-area.tsx +228 -0
  427. package/dist/vendor/docs-src/components/ui/section-heading.tsx +115 -0
  428. package/dist/vendor/docs-src/components/ui/separator.tsx +37 -0
  429. package/dist/vendor/docs-src/components/ui/sidebar.tsx +712 -0
  430. package/dist/vendor/docs-src/components/ui/skeleton.tsx +24 -0
  431. package/dist/vendor/docs-src/components/ui/spinner.tsx +41 -0
  432. package/dist/vendor/docs-src/components/ui/table-of-contents.tsx +131 -0
  433. package/dist/vendor/docs-src/components/ui/table.tsx +160 -0
  434. package/dist/vendor/docs-src/components/ui/tabs.tsx +204 -0
  435. package/dist/vendor/docs-src/components/ui/theme-toggle.tsx +238 -0
  436. package/dist/vendor/docs-src/components/ui/tooltip.tsx +41 -0
  437. package/dist/vendor/docs-src/hooks/create-click-outside.ts +82 -0
  438. package/dist/vendor/docs-src/hooks/create-clipboard.ts +69 -0
  439. package/dist/vendor/docs-src/hooks/create-color-mode.ts +115 -0
  440. package/dist/vendor/docs-src/hooks/create-controllable-signal.ts +56 -0
  441. package/dist/vendor/docs-src/hooks/create-focus-trap.ts +94 -0
  442. package/dist/vendor/docs-src/hooks/create-keybindings.ts +148 -0
  443. package/dist/vendor/docs-src/hooks/create-lock-scroll.ts +97 -0
  444. package/dist/vendor/docs-src/hooks/create-resize-observer.ts +89 -0
  445. package/dist/vendor/docs-src/hooks/create-scroll-position.ts +129 -0
  446. package/dist/vendor/docs-src/lib/cn.ts +9 -0
  447. package/dist/vendor/docs-src/providers/theme-provider.tsx +203 -0
  448. package/dist/vendor/docs-src/providers/theme-script.tsx +54 -0
  449. package/dist/vendor/docs-src/providers/theme-transitions.ts +139 -0
  450. package/dist/version.d.ts +2 -0
  451. package/dist/version.d.ts.map +1 -0
  452. package/dist/version.js +2 -0
  453. package/dist/version.js.map +1 -0
  454. package/package.json +95 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-transitions.js","sourceRoot":"","sources":["../../src/providers/theme-transitions.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,SAAS,sBAAsB;IAC7B,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO;IAC5C,MAAM,OAAO,GAAG,+BAA+B,CAAC;IAChD,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC9C,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC;QACnB,KAAK,CAAC,WAAW,GAAG;;;;;;;;;;;;KAYnB,CAAC;QACF,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa;IACpB,IAAI,OAAO,SAAS,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IACnD,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,IAAI,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,OAAO,MAAM,KAAK,WAAW,IAAI,OAAQ,MAAc,CAAC,mBAAmB,KAAK,WAAW,CAAC;IAE5G,OAAO,OAAO,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAM,GAAgB,MAAM,EAC5B,KAA6B,EAC7B,mBAA+B;IAE/B,qFAAqF;IACrF,4FAA4F;IAC5F,IACE,MAAM,KAAK,MAAM;QACjB,OAAO,QAAQ,KAAK,WAAW;QAC/B,CAAE,QAAgB,CAAC,mBAAmB;QACtC,MAAM,CAAC,UAAU,CAAC,kCAAkC,CAAC,CAAC,OAAO;QAC7D,aAAa,EAAE,EACf,CAAC;QACD,mBAAmB,EAAE,CAAC;QACtB,OAAO;IACT,CAAC;IAED,sBAAsB,EAAE,CAAC;IAEzB,IAAI,CAAC;QACH,0EAA0E;QAC1E,IAAI,MAAM,KAAK,UAAU,IAAI,KAAK,EAAE,CAAC;YACnC,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;YACxB,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;YAExB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAC1B,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC,EAClC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,WAAW,GAAG,CAAC,CAAC,CACpC,CAAC;YAEF,MAAM,UAAU,GAAI,QAAgB,CAAC,mBAAmB,CAAC,GAAG,EAAE;gBAC5D,mBAAmB,EAAE,CAAC;YACxB,CAAC,CAAC,CAAC;YAEH,UAAU,CAAC,KAAK;gBACd,EAAE,IAAI,CAAC,GAAG,EAAE;gBACV,QAAQ,CAAC,eAAe,CAAC,OAAO,CAC9B;oBACE,QAAQ,EAAE;wBACR,iBAAiB,CAAC,MAAM,CAAC,KAAK;wBAC9B,UAAU,SAAS,SAAS,CAAC,MAAM,CAAC,KAAK;qBAC1C;iBACF,EACD;oBACE,QAAQ,EAAE,GAAG;oBACb,MAAM,EAAE,aAAa;oBACrB,aAAa,EAAE,6BAA6B;iBAC7C,CACF,CAAC;YACJ,CAAC,CAAC;gBACF,EAAE,KAAK,CAAC,GAAG,EAAE;gBACX,oBAAoB;YACtB,CAAC,CAAC,CAAC;YACL,OAAO;QACT,CAAC;QAED,sCAAsC;QACtC,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,MAAM,UAAU,GAAI,QAAgB,CAAC,mBAAmB,CAAC,GAAG,EAAE;gBAC5D,mBAAmB,EAAE,CAAC;YACxB,CAAC,CAAC,CAAC;YAEH,UAAU,CAAC,KAAK;gBACd,EAAE,IAAI,CAAC,GAAG,EAAE;gBACV,QAAQ,CAAC,eAAe,CAAC,OAAO,CAC9B;oBACE,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;iBAChB,EACD;oBACE,QAAQ,EAAE,GAAG;oBACb,MAAM,EAAE,aAAa;oBACrB,aAAa,EAAE,6BAA6B;iBAC7C,CACF,CAAC;YACJ,CAAC,CAAC;gBACF,EAAE,KAAK,CAAC,GAAG,EAAE;gBACX,oBAAoB;YACtB,CAAC,CAAC,CAAC;YACL,OAAO;QACT,CAAC;QAED,mBAAmB,EAAE,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,mBAAmB,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "api-table",
3
+ "title": "API Table",
4
+ "description": "A structured, clean API reference table component for documenting props, options, and events.",
5
+ "type": "registry:ui",
6
+ "dependencies": [
7
+ "clsx",
8
+ "tailwind-merge"
9
+ ],
10
+ "registryDependencies": [
11
+ "table",
12
+ "section-heading"
13
+ ],
14
+ "files": [
15
+ {
16
+ "path": "ui/api-table.tsx",
17
+ "content": "import {\n splitProps,\n For,\n Show,\n type JSX,\n type Component,\n} from \"solid-js\";\nimport { SectionHeading } from \"@/components/ui/section-heading\";\nimport {\n Table,\n TableHeader,\n TableBody,\n TableRow,\n TableHead,\n TableCell,\n} from \"@/components/ui/table\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface ApiTableItem {\n /** Property, attribute, or method name */\n prop: string;\n /** TypeScript type definition string */\n type: string;\n /** Default value if optional */\n default?: string;\n /** Detailed description of purpose and usage */\n description: string;\n /** Whether the property is strictly required */\n required?: boolean;\n}\n\nexport interface ApiTableProps extends JSX.HTMLAttributes<HTMLDivElement> {\n /** Title header for this API section (e.g. component or interface name) */\n title?: string;\n /** Subtitle or explanatory note */\n description?: string;\n /** Badge label next to the title (defaults to \"Props\") */\n badge?: string;\n /** List of API properties to render in the table */\n items: ApiTableItem[];\n class?: string;\n}\n\n/**\n * Clean, structured API reference table component composed from Table and Badge primitives.\n */\nexport const ApiTable: Component<ApiTableProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"title\",\n \"description\",\n \"badge\",\n \"items\",\n \"class\",\n ]);\n\n return (\n <div class={cn(\"my-6 space-y-3\", local.class)} {...rest}>\n {/* Header */}\n <Show when={local.title}>\n <SectionHeading\n variant=\"compact\"\n title={local.title!}\n badge={local.badge || \"Props\"}\n description={local.description}\n />\n </Show>\n\n {/* Responsive Table Container Composed from Nikala Table Primitives */}\n <div class=\"rounded-lg border border-border bg-card/50 shadow-2xs overflow-hidden\">\n <Table class=\"text-xs\">\n <TableHeader class=\"bg-muted/40 font-mono text-[11px] uppercase tracking-wider text-muted-foreground select-none\">\n <TableRow class=\"hover:bg-transparent\">\n <TableHead class=\"h-9 px-4 font-semibold\">Prop</TableHead>\n <TableHead class=\"h-9 px-4 font-semibold\">Type</TableHead>\n <TableHead class=\"h-9 px-4 font-semibold\">Default</TableHead>\n <TableHead class=\"h-9 px-4 font-semibold\">Description</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody class=\"font-mono text-xs\">\n <For each={local.items}>\n {(item) => (\n <TableRow class=\"transition-colors hover:bg-muted/20\">\n <TableCell class=\"px-4 py-3 font-semibold text-primary\">\n <span class=\"inline-flex items-center gap-1\">\n {item.prop}\n <Show when={item.required}>\n <span class=\"text-rose-500 font-bold\" title=\"Required\">*</span>\n </Show>\n </span>\n </TableCell>\n <TableCell class=\"px-4 py-3 text-muted-foreground\">\n <code class=\"rounded-md bg-muted px-1.5 py-0.5 font-mono text-[11px] text-foreground\">\n {item.type}\n </code>\n </TableCell>\n <TableCell class=\"px-4 py-3 text-muted-foreground\">\n <Show\n when={item.default}\n fallback={<span class=\"text-muted-foreground/40 font-mono\">-</span>}\n >\n <code class=\"rounded-md bg-muted/60 px-1 py-0.5 font-mono text-[11px]\">\n {item.default}\n </code>\n </Show>\n </TableCell>\n <TableCell class=\"px-4 py-3 font-sans font-normal leading-relaxed text-muted-foreground\">\n {item.description}\n </TableCell>\n </TableRow>\n )}\n </For>\n </TableBody>\n </Table>\n </div>\n </div>\n );\n};\n",
18
+ "type": "registry:ui"
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "badge",
3
+ "title": "Badge",
4
+ "description": "A small badge component for status indicators and tags.",
5
+ "type": "registry:ui",
6
+ "dependencies": [
7
+ "clsx",
8
+ "tailwind-merge",
9
+ "class-variance-authority"
10
+ ],
11
+ "files": [
12
+ {
13
+ "path": "ui/badge.tsx",
14
+ "content": "import { splitProps, type Component, type JSX } from \"solid-js\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * Class variance authority configuration for badge styling variants.\n */\nexport const badgeVariants = cva(\n \"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\",\n {\n variants: {\n variant: {\n default:\n \"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80\",\n secondary:\n \"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n destructive:\n \"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80\",\n outline:\n \"text-foreground border-border\",\n success:\n \"border-transparent bg-green-600 text-zinc-50 shadow-sm hover:bg-green-600/90 dark:bg-green-900 dark:text-zinc-50 dark:hover:bg-green-900/90\",\n warning:\n \"border-transparent bg-yellow-600 text-zinc-50 shadow-sm hover:bg-yellow-600/90 dark:bg-yellow-900 dark:text-zinc-50 dark:hover:bg-yellow-900/90\",\n info:\n \"border-transparent bg-blue-600 text-zinc-50 shadow-sm hover:bg-blue-600/90 dark:bg-blue-900 dark:text-zinc-50 dark:hover:bg-blue-900/90\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n);\n\n/**\n * Props interface for the Badge component extending standard HTML div attributes.\n */\nexport interface BadgeProps\n extends JSX.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof badgeVariants> {\n class?: string;\n}\n\n/**\n * Nikala UI Badge component built for SolidJS with Tailwind CSS v4 styling.\n */\nexport const Badge: Component<BadgeProps> = (props) => {\n // Use splitProps to preserve SolidJS reactivity\n const [local, rest] = splitProps(props, [\"variant\", \"class\", \"children\"]);\n\n return (\n <div class={cn(badgeVariants({ variant: local.variant }), local.class)} {...rest}>\n {local.children}\n </div>\n );\n};",
15
+ "type": "registry:ui"
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "breadcrumb",
3
+ "title": "Breadcrumb",
4
+ "description": "Displays the path to the current resource using a hierarchy of links.",
5
+ "type": "registry:ui",
6
+ "dependencies": [
7
+ "clsx",
8
+ "tailwind-merge"
9
+ ],
10
+ "files": [
11
+ {
12
+ "path": "ui/breadcrumb.tsx",
13
+ "content": "import { splitProps, type Component, type JSX } from \"solid-js\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface BreadcrumbProps extends JSX.HTMLAttributes<HTMLElement> {\n class?: string;\n}\n\n/**\n * Root navigation container for the Nikala UI Breadcrumb hierarchy.\n */\nexport const Breadcrumb: Component<BreadcrumbProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n return <nav aria-label=\"breadcrumb\" class={cn(\"relative w-full\", local.class)} {...rest} />;\n};\n\nexport interface BreadcrumbListProps extends JSX.HTMLAttributes<HTMLOListElement> {\n class?: string;\n}\n\n/**\n * Ordered list container housing all breadcrumb path items.\n */\nexport const BreadcrumbList: Component<BreadcrumbListProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n return (\n <ol\n class={cn(\n \"flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground break-words sm:gap-2.5\",\n local.class\n )}\n {...rest}\n />\n );\n};\n\nexport interface BreadcrumbItemProps extends JSX.HTMLAttributes<HTMLLIElement> {\n class?: string;\n}\n\n/**\n * Individual list item element wrapper in the breadcrumb hierarchy.\n */\nexport const BreadcrumbItem: Component<BreadcrumbItemProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n return <li class={cn(\"inline-flex items-center gap-1.5\", local.class)} {...rest} />;\n};\n\nexport interface BreadcrumbLinkProps extends JSX.AnchorHTMLAttributes<HTMLAnchorElement> {\n class?: string;\n}\n\n/**\n * Clickable breadcrumb trail link element.\n */\nexport const BreadcrumbLink: Component<BreadcrumbLinkProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n return (\n <a\n class={cn(\n \"transition-colors hover:text-foreground underline-offset-4 hover:underline\",\n local.class\n )}\n {...rest}\n />\n );\n};\n\nexport interface BreadcrumbPageProps extends JSX.HTMLAttributes<HTMLSpanElement> {\n class?: string;\n}\n\n/**\n * Non-clickable active page indicator in breadcrumb hierarchy.\n */\nexport const BreadcrumbPage: Component<BreadcrumbPageProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n return (\n <span\n role=\"link\"\n aria-disabled=\"true\"\n aria-current=\"page\"\n class={cn(\"font-normal text-foreground\", local.class)}\n {...rest}\n />\n );\n};\n\nexport interface BreadcrumbSeparatorProps extends JSX.HTMLAttributes<HTMLLIElement> {\n class?: string;\n}\n\n/**\n * Visual separator icon rendered between breadcrumb path links.\n */\nexport const BreadcrumbSeparator: Component<BreadcrumbSeparatorProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n return (\n <li\n role=\"presentation\"\n aria-hidden=\"true\"\n class={cn(\"[&>svg]:size-3.5 text-muted-foreground\", local.class)}\n {...rest}\n >\n {local.children || (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n )}\n </li>\n );\n};\n\nexport interface BreadcrumbEllipsisProps extends JSX.HTMLAttributes<HTMLSpanElement> {\n class?: string;\n}\n\n/**\n * Ellipsis element representing truncated intermediate breadcrumb items.\n */\nexport const BreadcrumbEllipsis: Component<BreadcrumbEllipsisProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n return (\n <span\n role=\"presentation\"\n aria-hidden=\"true\"\n class={cn(\"flex h-9 w-9 items-center justify-center text-muted-foreground\", local.class)}\n {...rest}\n >\n <svg class=\"h-4 w-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"1\" />\n <circle cx=\"19\" cy=\"12\" r=\"1\" />\n <circle cx=\"5\" cy=\"12\" r=\"1\" />\n </svg>\n </span>\n );\n};",
14
+ "type": "registry:ui"
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "button",
3
+ "title": "Button",
4
+ "description": "An interactive button component with variant and size options.",
5
+ "type": "registry:ui",
6
+ "dependencies": [
7
+ "clsx",
8
+ "tailwind-merge",
9
+ "class-variance-authority"
10
+ ],
11
+ "registryDependencies": [
12
+ "spinner"
13
+ ],
14
+ "files": [
15
+ {
16
+ "path": "ui/button.tsx",
17
+ "content": "import { Show, splitProps, type Component, type JSX } from \"solid-js\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"@/lib/cn\";\nimport { Spinner } from \"@/components/ui/spinner\";\n\n/**\n * Class variance authority configuration for button styling variants and sizes.\n */\nexport const buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 cursor-pointer\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground shadow hover:bg-primary/90 border border-primary/50\",\n destructive:\n \"bg-red-600 text-zinc-50 shadow-sm hover:bg-red-600/90 dark:bg-red-900 dark:text-zinc-50 dark:hover:bg-red-900/90\",\n outline:\n \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80 border border-border\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n success: \"*:bg-green-600 text-zinc-50 shadow-sm hover:bg-green-600/90 dark:bg-green-900 dark:text-zinc-50 dark:hover:bg-green-900/90\",\n warning: \"*:bg-yellow-600 text-zinc-50 shadow-sm hover:bg-yellow-600/90 dark:bg-yellow-900 dark:text-zinc-50 dark:hover:bg-yellow-900/90\",\n info: \"*:bg-blue-600 text-zinc-50 shadow-sm hover:bg-blue-600/90 dark:bg-blue-900 dark:text-zinc-50 dark:hover:bg-blue-900/90\",\n },\n size: {\n xs: \"h-6 rounded-md px-2 text-xs\",\n default: \"h-9 px-4 py-2\",\n sm: \"h-8 rounded-md px-3 text-xs\",\n lg: \"h-10 rounded-md px-8\",\n icon: \"h-9 w-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\n/**\n * Props interface for the Button component extending standard HTML button attributes.\n */\nexport interface ButtonProps\n extends JSX.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n /** Shows a loading spinner and prevents repeated clicks while active. */\n loading?: boolean;\n class?: string;\n}\n\n/**\n * Nikala UI Button component built for SolidJS with Tailwind CSS v4 styling.\n */\nexport const Button: Component<ButtonProps> = (props) => {\n // Use splitProps to preserve SolidJS reactivity for destructured props\n const [local, rest] = splitProps(props, [\n \"variant\",\n \"size\",\n \"class\",\n \"children\",\n \"loading\",\n \"disabled\",\n ]);\n\n return (\n <button\n class={cn(buttonVariants({ variant: local.variant, size: local.size }), local.class)}\n disabled={local.disabled || local.loading}\n aria-busy={local.loading ? \"true\" : undefined}\n {...rest}\n >\n <Show when={local.loading}>\n <Spinner size=\"sm\" class=\"text-current\" />\n </Show>\n {local.children}\n </button>\n );\n};\n",
18
+ "type": "registry:ui"
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "callout",
3
+ "title": "Callout",
4
+ "description": "A semantic callout and alert block with status variants, icons, and titles for documentation and notices.",
5
+ "type": "registry:ui",
6
+ "dependencies": [
7
+ "clsx",
8
+ "tailwind-merge",
9
+ "class-variance-authority",
10
+ "lucide-solid"
11
+ ],
12
+ "files": [
13
+ {
14
+ "path": "ui/callout.tsx",
15
+ "content": "import {\n splitProps,\n Show,\n type Component,\n type JSX,\n type ParentComponent,\n} from \"solid-js\";\nimport { Dynamic } from \"solid-js/web\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport {\n Info,\n Lightbulb,\n AlertTriangle,\n AlertCircle,\n CheckCircle2,\n Bookmark,\n} from \"lucide-solid\";\nimport { cn } from \"@/lib/cn\";\n\nexport const calloutVariants = cva(\n \"relative w-full rounded-lg border p-4 text-sm leading-relaxed transition-colors\",\n {\n variants: {\n variant: {\n note: \"border-border/80 bg-muted/50 text-foreground [&>svg]:text-foreground\",\n info: \"border-sky-500/30 bg-sky-500/10 text-sky-950 dark:text-sky-100 [&>svg]:text-sky-600 dark:[&>svg]:text-sky-400\",\n tip: \"border-emerald-500/30 bg-emerald-500/10 text-emerald-950 dark:text-emerald-100 [&>svg]:text-emerald-600 dark:[&>svg]:text-emerald-400\",\n warning: \"border-amber-500/30 bg-amber-500/10 text-amber-950 dark:text-amber-100 [&>svg]:text-amber-600 dark:[&>svg]:text-amber-400\",\n danger: \"border-destructive/30 bg-destructive/10 text-destructive dark:text-red-200 [&>svg]:text-destructive\",\n success: \"border-emerald-500/30 bg-emerald-500/10 text-emerald-950 dark:text-emerald-100 [&>svg]:text-emerald-600 dark:[&>svg]:text-emerald-400\",\n },\n },\n defaultVariants: {\n variant: \"note\",\n },\n }\n);\n\nconst defaultIcons = {\n note: Bookmark,\n info: Info,\n tip: Lightbulb,\n warning: AlertTriangle,\n danger: AlertCircle,\n success: CheckCircle2,\n};\n\nexport interface CalloutProps\n extends JSX.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof calloutVariants> {\n title?: string;\n type?: VariantProps<typeof calloutVariants>[\"variant\"];\n icon?: Component<{ class?: string }> | false;\n class?: string;\n}\n\nexport const Callout: ParentComponent<CalloutProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"variant\",\n \"type\",\n \"title\",\n \"icon\",\n \"class\",\n \"children\",\n ]);\n\n const variant = () => local.variant || local.type || \"note\";\n\n const IconComponent = () => {\n if (local.icon === false) return null;\n if (local.icon) return local.icon;\n return defaultIcons[variant()] || Info;\n };\n\n return (\n <div\n role=\"region\"\n aria-label={local.title || `${variant()} callout`}\n class={cn(\n calloutVariants({ variant: variant() }),\n \"flex gap-3.5 items-start\",\n local.class\n )}\n {...rest}\n >\n <Show when={IconComponent()}>\n {(Icon) => (\n <span class=\"inline-flex shrink-0 items-center justify-center mt-0.5 select-none\">\n <Dynamic component={Icon()} class=\"size-4.5 shrink-0\" />\n </span>\n )}\n </Show>\n <div class=\"flex-1 space-y-1 min-w-0\">\n <Show when={local.title}>\n <h5 class=\"font-semibold text-sm leading-none tracking-tight\">\n {local.title}\n </h5>\n </Show>\n <div class=\"text-sm opacity-90 leading-relaxed break-words\">\n {local.children}\n </div>\n </div>\n </div>\n );\n};\n\nexport interface CalloutTitleProps extends JSX.HTMLAttributes<HTMLHeadingElement> {\n class?: string;\n}\n\nexport const CalloutTitle: ParentComponent<CalloutTitleProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n return (\n <h5\n class={cn(\"font-semibold text-sm leading-none tracking-tight mb-1\", local.class)}\n {...rest}\n >\n {local.children}\n </h5>\n );\n};\n\nexport interface CalloutDescriptionProps extends JSX.HTMLAttributes<HTMLParagraphElement> {\n class?: string;\n}\n\nexport const CalloutDescription: ParentComponent<CalloutDescriptionProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n return (\n <div\n class={cn(\"text-sm opacity-90 leading-relaxed\", local.class)}\n {...rest}\n >\n {local.children}\n </div>\n );\n};\n",
16
+ "type": "registry:ui"
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "card",
3
+ "title": "Card",
4
+ "description": "A versatile container component with header, content, and footer sections.",
5
+ "type": "registry:ui",
6
+ "dependencies": [
7
+ "clsx",
8
+ "tailwind-merge"
9
+ ],
10
+ "files": [
11
+ {
12
+ "path": "ui/card.tsx",
13
+ "content": "import { splitProps, type Component, type JSX } from \"solid-js\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface CardProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\n/**\n * Root Card container component.\n */\nexport const Card: Component<CardProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <div\n class={cn(\n \"rounded-lg border border-border bg-card text-card-foreground shadow-sm\",\n local.class\n )}\n {...rest}\n />\n );\n};\n\nexport interface CardHeaderProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\n/**\n * Header wrapper for the Card component.\n */\nexport const CardHeader: Component<CardHeaderProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <div\n class={cn(\"flex flex-col space-y-1.5 p-6\", local.class)}\n {...rest}\n />\n );\n};\n\nexport interface CardTitleProps extends JSX.HTMLAttributes<HTMLHeadingElement> {\n class?: string;\n}\n\n/**\n * Main title heading for the Card header.\n */\nexport const CardTitle: Component<CardTitleProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <h3\n class={cn(\"font-semibold leading-none tracking-tight text-lg\", local.class)}\n {...rest}\n />\n );\n};\n\nexport interface CardDescriptionProps extends JSX.HTMLAttributes<HTMLParagraphElement> {\n class?: string;\n}\n\n/**\n * Subtitle description text for the Card header.\n */\nexport const CardDescription: Component<CardDescriptionProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <p\n class={cn(\"text-sm text-muted-foreground\", local.class)}\n {...rest}\n />\n );\n};\n\nexport interface CardContentProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\n/**\n * Main body content area for the Card.\n */\nexport const CardContent: Component<CardContentProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <div\n class={cn(\"p-6 pt-0\", local.class)}\n {...rest}\n />\n );\n};\n\nexport interface CardFooterProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\n/**\n * Footer action area for the Card.\n */\nexport const CardFooter: Component<CardFooterProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <div\n class={cn(\"flex items-center p-6 pt-0\", local.class)}\n {...rest}\n />\n );\n};",
14
+ "type": "registry:ui"
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "code-block",
3
+ "title": "Code Block",
4
+ "description": "A code block container with filename header, language indicator, and interactive copy to clipboard button.",
5
+ "type": "registry:ui",
6
+ "dependencies": [
7
+ "clsx",
8
+ "tailwind-merge",
9
+ "lucide-solid",
10
+ "shiki",
11
+ "@shikijs/langs",
12
+ "@shikijs/themes"
13
+ ],
14
+ "registryDependencies": [
15
+ "button",
16
+ "badge",
17
+ "tooltip",
18
+ "tabs",
19
+ "create-clipboard"
20
+ ],
21
+ "files": [
22
+ {
23
+ "path": "ui/code-block.tsx",
24
+ "content": "import {\n createSignal,\n createEffect,\n createMemo,\n splitProps,\n For,\n Show,\n type JSX,\n type ParentComponent,\n} from \"solid-js\";\nimport { Check, Copy } from \"lucide-solid\";\nimport { Button } from \"@/components/ui/button\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Tabs, TabsList, TabsTrigger } from \"@/components/ui/tabs\";\nimport { Tooltip, TooltipTrigger, TooltipContent } from \"@/components/ui/tooltip\";\nimport { createClipboard } from \"@/hooks/create-clipboard\";\nimport { cn } from \"@/lib/cn\";\n\nexport type CodeBlockPackageManager = \"bunx\" | \"npx\" | \"pnpm\" | \"yarn\";\n\nexport interface CodeBlockProps extends JSX.HTMLAttributes<HTMLDivElement> {\n /** The raw source code text to display and copy */\n code?: string;\n /** File name to display in the header bar (e.g. \"App.tsx\", \"main.rs\") */\n filename?: string;\n /** Programming language badge (e.g. \"tsx\", \"rust\", \"bash\") */\n language?: string;\n /** Alias for language */\n lang?: string;\n /** Whether the code snippet is an executable CLI command */\n isCli?: boolean;\n /** Shows the interactive package manager runner tabs (bunx, npx, pnpm, yarn) */\n showPmSwitcher?: boolean;\n /** List of package manager runners to show */\n managers?: CodeBlockPackageManager[];\n /** Whether the copy button is enabled. Defaults to true. */\n copyable?: boolean;\n class?: string;\n}\n\nlet shikiHighlighterPromise: Promise<any> | null = null;\n\nasync function getShikiHighlighter() {\n if (typeof window === \"undefined\") return null;\n if (!shikiHighlighterPromise) {\n try {\n const [{ createBundledHighlighter }, { createJavaScriptRegexEngine }] = await Promise.all([\n import(\"shiki/core\"),\n import(\"shiki/engine/javascript\"),\n ]);\n const createHighlighter = createBundledHighlighter({\n langs: {\n typescript: () => import(\"@shikijs/langs/typescript\"),\n javascript: () => import(\"@shikijs/langs/javascript\"),\n tsx: () => import(\"@shikijs/langs/tsx\"),\n jsx: () => import(\"@shikijs/langs/jsx\"),\n bash: () => import(\"@shikijs/langs/shellscript\"),\n json: () => import(\"@shikijs/langs/json\"),\n css: () => import(\"@shikijs/langs/css\"),\n html: () => import(\"@shikijs/langs/html\"),\n rust: () => import(\"@shikijs/langs/rust\"),\n },\n themes: {\n \"github-dark\": () => import(\"@shikijs/themes/github-dark\"),\n \"github-light\": () => import(\"@shikijs/themes/github-light\"),\n },\n engine: () => createJavaScriptRegexEngine(),\n });\n shikiHighlighterPromise = createHighlighter({\n themes: [\"github-dark\", \"github-light\"],\n langs: [\n \"typescript\",\n \"javascript\",\n \"tsx\",\n \"jsx\",\n \"bash\",\n \"json\",\n \"css\",\n \"html\",\n \"rust\",\n ],\n });\n } catch (e) {\n console.warn(\"Failed to load Shiki highlighter\", e);\n return null;\n }\n }\n return shikiHighlighterPromise;\n}\n\nfunction highlightCliCommand(code: string): string {\n const trimmed = code.trim();\n const parts = trimmed.split(/\\s+/);\n if (parts.length === 0) return code;\n\n const isRunner = [\"bunx\", \"npx\", \"pnpm\", \"yarn\", \"bun\", \"npm\", \"cargo\", \"deno\"].includes(parts[0]);\n if (!isRunner) return `<span class=\"text-foreground\">${escapeHtml(trimmed)}</span>`;\n\n return parts\n .map((part, index) => {\n if (index === 0) {\n return `<span class=\"text-amber-500 dark:text-amber-400 font-bold\">${escapeHtml(part)}</span>`;\n }\n if (part.startsWith(\"-\")) {\n return `<span class=\"text-purple-400 dark:text-purple-300\">${escapeHtml(part)}</span>`;\n }\n if (part.startsWith(\"@\")) {\n return `<span class=\"text-emerald-400 dark:text-emerald-300 font-medium\">${escapeHtml(part)}</span>`;\n }\n if (index === 1 && [\"add\", \"install\", \"init\", \"create\", \"run\"].includes(part)) {\n return `<span class=\"text-sky-500 dark:text-sky-400 font-medium\">${escapeHtml(part)}</span>`;\n }\n return `<span class=\"text-foreground/90\">${escapeHtml(part)}</span>`;\n })\n .join(\" \");\n}\n\nfunction transformCommandForPm(cmd: string, pm: CodeBlockPackageManager): string {\n const trimmed = cmd.trim();\n const runners = [\"bunx\", \"npx\", \"pnpm dlx\", \"pnpm\", \"yarn dlx\", \"yarn\", \"bun\", \"npm\"];\n\n let base = trimmed;\n for (const r of runners) {\n if (trimmed.startsWith(r)) {\n base = trimmed.slice(r.length).trim();\n break;\n }\n }\n\n switch (pm) {\n case \"bunx\":\n return `bunx ${base}`;\n case \"npx\":\n return `npx ${base}`;\n case \"pnpm\":\n return `pnpm dlx ${base}`;\n case \"yarn\":\n return `yarn dlx ${base}`;\n default:\n return `${pm} ${base}`;\n }\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n}\n\nexport const CodeBlock: ParentComponent<CodeBlockProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"id\",\n \"code\",\n \"filename\",\n \"language\",\n \"lang\",\n \"isCli\",\n \"showPmSwitcher\",\n \"managers\",\n \"copyable\",\n \"class\",\n \"children\",\n ]);\n\n const clipboard = createClipboard();\n const [activePm, setActivePm] = createSignal<CodeBlockPackageManager>(\"bunx\");\n const [highlightedHtml, setHighlightedHtml] = createSignal(\"\");\n\n const effectiveLang = () => (local.lang || local.language || \"tsx\").toLowerCase();\n const copyable = () => local.copyable ?? true;\n const defaultManagers: CodeBlockPackageManager[] = [\"bunx\", \"npx\", \"pnpm\", \"yarn\"];\n const managersList = () => local.managers || defaultManagers;\n\n const isBashCli = () => {\n const l = effectiveLang();\n return l === \"bash\" || l === \"sh\" || l === \"shell\" || Boolean(local.isCli);\n };\n\n const cleanCode = createMemo(() => {\n const raw = (local.code || \"\").trim();\n if (local.isCli || local.showPmSwitcher) {\n return transformCommandForPm(raw, activePm());\n }\n return raw;\n });\n\n createEffect(() => {\n const codeStr = cleanCode();\n const lang = effectiveLang();\n\n if (isBashCli()) {\n setHighlightedHtml(highlightCliCommand(codeStr));\n return;\n }\n\n if (!codeStr) {\n setHighlightedHtml(\"\");\n return;\n }\n\n // Attempt Shiki highlighting\n getShikiHighlighter().then((highlighter) => {\n if (highlighter) {\n try {\n const html = highlighter.codeToHtml(codeStr, {\n lang: lang === \"js\" ? \"javascript\" : lang === \"ts\" ? \"typescript\" : lang,\n themes: {\n light: \"github-light\",\n dark: \"github-dark\",\n },\n });\n setHighlightedHtml(html);\n return;\n } catch (err) {\n console.warn(`Shiki failed for language: ${lang}`, err);\n }\n }\n // Fallback\n setHighlightedHtml(`<pre class=\"text-foreground/90\"><code>${escapeHtml(codeStr)}</code></pre>`);\n });\n });\n\n const handleCopy = async () => {\n let textToCopy = cleanCode();\n if (!textToCopy && typeof window !== \"undefined\") {\n const target = document.querySelector(`[data-code-block-id=\"${local.id}\"]`);\n if (target) textToCopy = target.textContent || \"\";\n }\n\n if (!textToCopy) return;\n await clipboard.copy(textToCopy);\n };\n\n const shouldShowSwitcher = () => Boolean(local.showPmSwitcher || local.isCli);\n const hasHeader = () => Boolean(shouldShowSwitcher() || local.filename || effectiveLang());\n\n return (\n <div\n class={cn(\n \"group relative my-4 w-full overflow-hidden rounded-lg border border-border bg-muted/40 font-mono text-sm shadow-2xs\",\n local.class\n )}\n {...rest}\n >\n {/* Code Header Bar */}\n <Show when={hasHeader()}>\n <div class=\"flex h-9 items-center justify-between border-b border-border/70 bg-muted/70 px-3.5 text-xs text-muted-foreground select-none\">\n <div class=\"flex items-center gap-2\">\n <Show when={shouldShowSwitcher()}>\n <Tabs\n value={activePm()}\n onChange={(val) => setActivePm(val as CodeBlockPackageManager)}\n class=\"w-auto flex-none\"\n >\n <TabsList class=\"w-auto inline-flex h-auto bg-background/60 p-0.5 border border-border/50 gap-0.5 rounded-md font-mono\">\n <For each={managersList()}>\n {(pm) => (\n <TabsTrigger\n value={pm}\n class=\"rounded-xs px-2 py-0.5 text-[11px] font-mono transition-colors cursor-pointer data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=active]:font-semibold data-[state=active]:shadow-2xs\"\n >\n {pm}\n </TabsTrigger>\n )}\n </For>\n </TabsList>\n </Tabs>\n </Show>\n\n <Show when={local.filename}>\n <span class=\"font-medium text-foreground tracking-tight\">{local.filename}</span>\n </Show>\n\n <Show when={effectiveLang() && !shouldShowSwitcher() && !local.filename}>\n <Badge variant=\"outline\" class=\"uppercase text-[10px] px-1.5 py-0 h-4 font-mono font-semibold\">\n {effectiveLang()}\n </Badge>\n </Show>\n </div>\n\n <Show when={copyable() && (local.code || cleanCode())}>\n <Tooltip>\n <TooltipTrigger\n as={Button}\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleCopy}\n aria-label={clipboard.copied() ? \"Copied code\" : \"Copy code\"}\n class=\"size-6 p-0 rounded-xs text-muted-foreground hover:bg-background hover:text-foreground cursor-pointer\"\n >\n <Show when={clipboard.copied()} fallback={<Copy class=\"size-3.5\" />}>\n <Check class=\"size-3.5 text-emerald-500 dark:text-emerald-400\" />\n </Show>\n </TooltipTrigger>\n <TooltipContent class=\"text-[10px] py-1 px-2\">\n {clipboard.copied() ? \"Copied!\" : \"Copy code\"}\n </TooltipContent>\n </Tooltip>\n </Show>\n </div>\n </Show>\n\n {/* Floating Copy Button (if no header bar is present) */}\n <Show when={!hasHeader() && copyable() && (local.code || cleanCode())}>\n <div class=\"absolute right-2.5 top-2.5 z-10 opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100\">\n <Tooltip>\n <TooltipTrigger\n as={Button}\n variant=\"outline\"\n size=\"icon\"\n onClick={handleCopy}\n aria-label={clipboard.copied() ? \"Copied code\" : \"Copy code\"}\n class=\"size-7 rounded-md border-border/80 bg-background/90 text-muted-foreground backdrop-blur-xs hover:bg-muted hover:text-foreground cursor-pointer shadow-2xs\"\n >\n <Show when={clipboard.copied()} fallback={<Copy class=\"size-3.5\" />}>\n <Check class=\"size-3.5 text-emerald-500 dark:text-emerald-400\" />\n </Show>\n </TooltipTrigger>\n <TooltipContent class=\"text-[10px] py-1 px-2\">\n {clipboard.copied() ? \"Copied!\" : \"Copy code\"}\n </TooltipContent>\n </Tooltip>\n </div>\n </Show>\n\n {/* Code Container */}\n <div class=\"overflow-x-auto p-4 text-[13px] leading-relaxed [scrollbar-width:thin]\">\n <Show\n when={local.children}\n fallback={\n <div\n class=\"font-mono text-sm leading-relaxed overflow-x-auto [&>pre]:!bg-transparent [&>pre]:!p-0 [&>pre]:!m-0 [&>pre]:!border-none\"\n innerHTML={highlightedHtml() || `<pre class=\"text-foreground/90\"><code>${escapeHtml(cleanCode())}</code></pre>`}\n />\n }\n >\n <pre class=\"shiki nikala-code-block m-0 overflow-x-auto bg-transparent p-0 font-mono text-xs\">\n {local.children}\n </pre>\n </Show>\n </div>\n </div>\n );\n};\n",
25
+ "type": "registry:ui"
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "collapsible",
3
+ "title": "Collapsible",
4
+ "description": "An interactive component that expands and collapses content panels with smooth height animations, built on Kobalte primitives.",
5
+ "type": "registry:ui",
6
+ "dependencies": [
7
+ "clsx",
8
+ "tailwind-merge",
9
+ "@kobalte/core"
10
+ ],
11
+ "files": [
12
+ {
13
+ "path": "ui/collapsible.tsx",
14
+ "content": "import { splitProps, type JSX, type ValidComponent } from \"solid-js\";\nimport * as CollapsiblePrimitive from \"@kobalte/core/collapsible\";\nimport type { PolymorphicProps } from \"@kobalte/core/polymorphic\";\nimport { cn } from \"@/lib/cn\";\n\nexport type CollapsibleRootProps<T extends ValidComponent = \"div\"> =\n CollapsiblePrimitive.CollapsibleRootProps<T> & {\n class?: string;\n };\n\n/**\n * Root Collapsible component built on Kobalte primitives.\n */\nexport const Collapsible = <T extends ValidComponent = \"div\">(\n props: PolymorphicProps<T, CollapsibleRootProps<T>>\n) => {\n const [local, rest] = splitProps(props as CollapsibleRootProps, [\"class\"]);\n return (\n <CollapsiblePrimitive.Root\n class={cn(\"w-full\", local.class)}\n {...(rest as any)}\n />\n );\n};\n\nexport type CollapsibleTriggerProps<T extends ValidComponent = \"button\"> =\n CollapsiblePrimitive.CollapsibleTriggerProps<T> & {\n class?: string;\n children?: JSX.Element;\n };\n\n/**\n * Trigger element that toggles the Collapsible open/closed state.\n */\nexport const CollapsibleTrigger = <T extends ValidComponent = \"button\">(\n props: PolymorphicProps<T, CollapsibleTriggerProps<T>>\n) => {\n const [local, rest] = splitProps(props as CollapsibleTriggerProps, [\"class\", \"children\"]);\n return (\n <CollapsiblePrimitive.Trigger\n class={cn(\"flex w-full items-center justify-between cursor-pointer\", local.class)}\n {...rest}\n >\n {local.children}\n </CollapsiblePrimitive.Trigger>\n );\n};\n\nexport type CollapsibleContentProps<T extends ValidComponent = \"div\"> =\n CollapsiblePrimitive.CollapsibleContentProps<T> & {\n class?: string;\n children?: JSX.Element;\n };\n\n/**\n * Collapsible content panel revealed when opened.\n */\nexport const CollapsibleContent = <T extends ValidComponent = \"div\">(\n props: PolymorphicProps<T, CollapsibleContentProps<T>>\n) => {\n const [local, rest] = splitProps(props as CollapsibleContentProps, [\"class\", \"children\"]);\n return (\n <CollapsiblePrimitive.Content\n class={cn(\n \"overflow-hidden transition-all data-expanded:animate-collapsible-down data-closed:animate-collapsible-up\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </CollapsiblePrimitive.Content>\n );\n};\n",
15
+ "type": "registry:ui"
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "command",
3
+ "title": "Command / Command Palette",
4
+ "description": "Fast, accessible command palette and search modal built on Kobalte Dialog primitives with auto-filtering.",
5
+ "type": "registry:ui",
6
+ "dependencies": [
7
+ "clsx",
8
+ "tailwind-merge",
9
+ "class-variance-authority",
10
+ "lucide-solid",
11
+ "@kobalte/core"
12
+ ],
13
+ "registryDependencies": [
14
+ "kbd",
15
+ "input-group",
16
+ "list",
17
+ "scroll-area",
18
+ "create-keybindings"
19
+ ],
20
+ "files": [
21
+ {
22
+ "path": "ui/command.tsx",
23
+ "content": "import {\n createContext,\n useContext,\n createSignal,\n onCleanup,\n Show,\n splitProps,\n type Component,\n type JSX,\n type Accessor,\n} from \"solid-js\";\nimport { createKeybindings } from \"@/hooks/create-keybindings\";\nimport { Dialog } from \"@kobalte/core/dialog\";\nimport { Search, ArrowUp, ArrowDown, CornerDownLeft } from \"lucide-solid\";\nimport { cn } from \"@/lib/cn\";\nimport { Kbd, KbdGroup } from \"../ui/kbd\";\nimport { InputGroup, InputGroupInput, InputGroupAddon } from \"../ui/input-group\";\nimport { List, ListGroup, ListHeader, ListItem, type ListItemProps } from \"../ui/list\";\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\n\n/* --- Command Context State --- */\ninterface CommandContextValue {\n search: Accessor<string>;\n setSearch: (value: string) => void;\n activeIndex: Accessor<number>;\n setActiveIndex: (fn: (prev: number) => number) => void;\n registerItemIndex: (element: HTMLElement) => number;\n onItemSelect: (index: number, action?: () => void) => void;\n}\n\nconst CommandContext = createContext<CommandContextValue>();\n\nexport const useCommand = () => {\n const ctx = useContext(CommandContext);\n if (!ctx) {\n throw new Error(\"useCommand must be used within a <Command />\");\n }\n return ctx;\n};\n\n/* --- Root Command Container --- */\nexport interface CommandProps extends Omit<JSX.HTMLAttributes<HTMLDivElement>, \"children\"> {\n class?: string;\n children?: JSX.Element | ((ctx: CommandContextValue) => JSX.Element);\n}\n\nexport const Command: Component<CommandProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n const [search, setSearch] = createSignal(\"\");\n const [activeIndex, setActiveIndex] = createSignal(0);\n\n let containerRef: HTMLDivElement | undefined;\n let itemsList: HTMLElement[] = [];\n\n const registerItemIndex = (element: HTMLElement) => {\n if (!itemsList.includes(element)) {\n itemsList.push(element);\n }\n return itemsList.indexOf(element);\n };\n\n const onItemSelect = (index: number, action?: () => void) => {\n setActiveIndex(index);\n if (action) action();\n };\n\n const getVisibleItems = () => {\n if (!containerRef) return [];\n return Array.from(containerRef.querySelectorAll<HTMLElement>(\"[data-command-item]:not(.hidden)\"));\n };\n\n const updateActiveAttribute = (index: number) => {\n const visible = getVisibleItems();\n visible.forEach((el, idx) => {\n if (idx === index) {\n el.setAttribute(\"aria-selected\", \"true\");\n el.scrollIntoView({ block: \"nearest\" });\n } else {\n el.removeAttribute(\"aria-selected\");\n }\n });\n };\n\n const handleKeyDown = (e: KeyboardEvent) => {\n const visible = getVisibleItems();\n if (visible.length === 0) return;\n\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n const next = (activeIndex() + 1) % visible.length;\n setActiveIndex(next);\n updateActiveAttribute(next);\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n const prev = (activeIndex() - 1 + visible.length) % visible.length;\n setActiveIndex(prev);\n updateActiveAttribute(prev);\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n const current = visible[activeIndex()];\n if (current) {\n current.click();\n }\n }\n };\n\n const ctx: CommandContextValue = {\n search,\n setSearch: (val) => {\n setSearch(val);\n setActiveIndex(0);\n },\n activeIndex,\n setActiveIndex: (fn) => setActiveIndex(fn),\n registerItemIndex,\n onItemSelect,\n };\n\n return (\n <CommandContext.Provider value={ctx}>\n <div\n ref={containerRef}\n onKeyDown={handleKeyDown}\n class={cn(\n \"flex flex-col w-full h-full rounded-lg bg-popover text-popover-foreground overflow-hidden border border-border shadow-md outline-none\",\n local.class\n )}\n tabIndex={0}\n {...rest}\n >\n {typeof local.children === \"function\" ? (local.children as any)(ctx) : local.children}\n </div>\n </CommandContext.Provider>\n );\n};\n\n/* --- Command Dialog (Modal) --- */\nexport interface CommandDialogProps {\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n enableHotkey?: boolean;\n children?: JSX.Element | ((ctx: CommandContextValue) => JSX.Element);\n class?: string;\n}\n\nexport const CommandDialog: Component<CommandDialogProps> = (props) => {\n const [internalOpen, setInternalOpen] = createSignal(false);\n\n const isOpen = () => (props.open !== undefined ? props.open : internalOpen());\n const setOpen = (val: boolean) => {\n if (props.open === undefined) setInternalOpen(val);\n if (typeof props.onOpenChange === \"function\") props.onOpenChange(val);\n };\n\n /* Listen for global Ctrl+K / Cmd+K hotkeys */\n createKeybindings(\n [\n {\n key: [\"meta+k\", \"ctrl+k\"],\n handler: () => setOpen(!isOpen()),\n preventDefault: true,\n },\n ],\n {\n enabled: () => props.enableHotkey !== false,\n }\n );\n\n return (\n <Dialog open={isOpen()} onOpenChange={setOpen}>\n <Dialog.Portal>\n <Dialog.Overlay class=\"fixed inset-0 z-50 bg-black/60 backdrop-blur-xs data-expanded:animate-in data-closed:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0\" />\n <div class=\"fixed inset-0 z-50 flex items-start justify-center pt-16 sm:pt-24 px-4\">\n <Dialog.Content class=\"w-full max-w-xl rounded-lg border border-border bg-popover text-popover-foreground shadow-2xl outline-none data-expanded:animate-in data-closed:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-closed:zoom-out-95 data-expanded:zoom-in-95\">\n <Command class={props.class}>{props.children}</Command>\n </Dialog.Content>\n </div>\n </Dialog.Portal>\n </Dialog>\n );\n};\n\n/* --- Command Input --- */\nexport interface CommandInputProps\n extends JSX.InputHTMLAttributes<HTMLInputElement> {\n class?: string;\n}\n\nexport const CommandInput: Component<CommandInputProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"value\", \"onInput\"]);\n const { search, setSearch } = useCommand();\n\n return (\n <InputGroup class=\"border-0 border-b border-border rounded-none bg-transparent px-3 py-1 shadow-none focus-within:ring-0 focus-within:border-border\">\n <InputGroupAddon align=\"inline-start\">\n <Search class=\"w-4 h-4 text-muted-foreground\" />\n </InputGroupAddon>\n\n <InputGroupInput\n value={search()}\n onInput={(e) => {\n setSearch(e.currentTarget.value);\n if (typeof local.onInput === \"function\") {\n local.onInput(e);\n }\n }}\n placeholder=\"Type a command or search...\"\n class=\"h-11 text-base sm:text-sm font-medium\"\n {...rest}\n />\n </InputGroup>\n );\n};\n\n/* --- Command List Container --- */\nexport interface CommandListProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const CommandList: Component<CommandListProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <ScrollArea\n class={cn(\"max-h-82.5 p-1.5\", local.class)}\n {...rest}\n >\n <List>{local.children}</List>\n </ScrollArea>\n );\n};\n\n/* --- Command Empty Block --- */\nexport interface CommandEmptyProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const CommandEmpty: Component<CommandEmptyProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n const { search } = useCommand();\n\n return (\n <Show when={search().trim().length > 0}>\n <div\n class={cn(\n \"py-8 text-center text-sm text-muted-foreground font-medium select-none\",\n local.class\n )}\n {...rest}\n >\n {local.children || `No results found for \"${search()}\".`}\n </div>\n </Show>\n );\n};\n\n/* --- Command Group --- */\nexport interface CommandGroupProps {\n heading: string;\n children?: JSX.Element;\n class?: string;\n}\n\nexport const CommandGroup: Component<CommandGroupProps> = (props) => {\n return (\n <ListGroup class={props.class}>\n <ListHeader title={props.heading} />\n {props.children}\n </ListGroup>\n );\n};\n\n/* --- Command Item --- */\nexport interface CommandItemProps extends ListItemProps {\n keywords?: string[];\n description?: string;\n onSelect?: () => void;\n shouldFilter?: boolean;\n}\n\nexport const CommandItem: Component<CommandItemProps> = (props) => {\n let itemRef: HTMLDivElement | undefined;\n const [local, rest] = splitProps(props, [\n \"title\",\n \"subtitle\",\n \"description\",\n \"keywords\",\n \"onSelect\",\n \"onClick\",\n \"shouldFilter\",\n \"class\",\n ]);\n const { search } = useCommand();\n\n /* Auto filter item based on search query */\n const matchesSearch = () => {\n if (local.shouldFilter === false) return true;\n const query = search().toLowerCase().trim();\n if (!query) return true;\n\n const titleMatch = local.title?.toLowerCase().includes(query);\n const subtitleMatch = local.subtitle?.toLowerCase().includes(query);\n const descMatch = local.description?.toLowerCase().includes(query);\n const keywordMatch = local.keywords?.some((k) =>\n k.toLowerCase().includes(query)\n );\n\n return Boolean(titleMatch || subtitleMatch || descMatch || keywordMatch);\n };\n\n const handleClick = (e: MouseEvent) => {\n if (typeof local.onSelect === \"function\") {\n local.onSelect();\n }\n if (typeof local.onClick === \"function\") {\n local.onClick(e as any);\n }\n };\n\n return (\n <Show when={matchesSearch()}>\n <ListItem\n ref={itemRef}\n data-command-item=\"true\"\n title={local.title}\n subtitle={local.subtitle || local.description}\n onClick={handleClick}\n class={cn(\n \"aria-selected:bg-accent aria-selected:text-accent-foreground cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground\",\n local.class\n )}\n {...rest}\n />\n </Show>\n );\n};\n\n/* --- Command Footer --- */\nexport interface CommandFooterProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n children?: JSX.Element;\n}\n\nexport const CommandFooter: Component<CommandFooterProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <div\n class={cn(\n \"flex items-center justify-between border-t border-border bg-muted/30 px-3 py-2 text-xs text-muted-foreground select-none\",\n local.class\n )}\n {...rest}\n >\n <div class=\"flex items-center gap-3\">\n <span class=\"flex items-center gap-1\">\n <KbdGroup>\n <Kbd size=\"sm\"><ArrowUp class=\"w-2.5 h-2.5\" /></Kbd>\n <Kbd size=\"sm\"><ArrowDown class=\"w-2.5 h-2.5\" /></Kbd>\n </KbdGroup>\n <span>Navigate</span>\n </span>\n\n <span class=\"flex items-center gap-1\">\n <Kbd size=\"sm\"><CornerDownLeft class=\"w-2.5 h-2.5\" /></Kbd>\n <span>Select</span>\n </span>\n\n <span class=\"flex items-center gap-1\">\n <Kbd size=\"sm\">Esc</Kbd>\n <span>Close</span>\n </span>\n </div>\n\n {local.children}\n </div>\n );\n};",
24
+ "type": "registry:ui"
25
+ }
26
+ ]
27
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "component-viewer",
3
+ "title": "Component Viewer",
4
+ "description": "An interactive preview canvas and code viewer container with responsive viewports, canvas grid, and AI prompts.",
5
+ "type": "registry:ui",
6
+ "dependencies": [
7
+ "clsx",
8
+ "tailwind-merge",
9
+ "lucide-solid"
10
+ ],
11
+ "registryDependencies": [
12
+ "tabs",
13
+ "button",
14
+ "tooltip",
15
+ "code-block",
16
+ "create-clipboard"
17
+ ],
18
+ "files": [
19
+ {
20
+ "path": "ui/component-viewer.tsx",
21
+ "content": "import {\n createSignal,\n splitProps,\n Show,\n type JSX,\n type ParentComponent,\n} from \"solid-js\";\nimport { Portal } from \"solid-js/web\";\nimport {\n Monitor,\n Tablet,\n Smartphone,\n Sparkles,\n RotateCcw,\n Terminal,\n Check,\n Maximize2,\n Minimize2,\n Grid,\n} from \"lucide-solid\";\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from \"@/components/ui/tabs\";\nimport { Button } from \"@/components/ui/button\";\nimport { Tooltip, TooltipTrigger, TooltipContent } from \"@/components/ui/tooltip\";\nimport { CodeBlock } from \"@/components/ui/code-block\";\nimport { createClipboard } from \"@/hooks/create-clipboard\";\nimport { cn } from \"@/lib/cn\";\n\nexport type ComponentViewerViewport = \"desktop\" | \"tablet\" | \"mobile\";\n\nexport interface ComponentViewerProps extends JSX.HTMLAttributes<HTMLDivElement> {\n /** Name of the component or UI primitive (e.g. \"button\", \"dialog\") */\n name?: string;\n /** Title label displayed in the viewer header */\n title?: string;\n /** Raw TSX/JSX source code to display in the Code tab and copy */\n code: string;\n /** Programming language for syntax highlighting (defaults to \"tsx\") */\n lang?: string;\n /** Alignment of the preview canvas: \"center\" (default), \"start\", or \"end\" */\n align?: \"center\" | \"start\" | \"end\";\n /** Optional CLI command to install this component (e.g. \"bunx @nikala-ui/cli add button\") */\n command?: string;\n /** Initial responsive viewport (default: \"desktop\") */\n defaultViewport?: ComponentViewerViewport;\n /** Whether to allow overflowing elements in the canvas (e.g. popovers, dropdowns) */\n allowOverflow?: boolean;\n /** Enables responsive screen size toggle controls (Desktop, Tablet, Mobile) */\n responsive?: boolean;\n /** Enables canvas grid pattern toggle */\n showGridToggle?: boolean;\n /** Enables a reset button to re-mount the preview canvas */\n reRenderable?: boolean;\n class?: string;\n}\n\n/**\n * Advanced component inspection and preview container with responsive viewport emulation,\n * background grid patterns, live re-mounting, AI context generation, and syntax-highlighted code.\n */\nexport const ComponentViewer: ParentComponent<ComponentViewerProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"name\",\n \"title\",\n \"code\",\n \"lang\",\n \"align\",\n \"command\",\n \"defaultViewport\",\n \"allowOverflow\",\n \"responsive\",\n \"showGridToggle\",\n \"reRenderable\",\n \"class\",\n \"children\",\n ]);\n\n const [viewport, setViewport] = createSignal<ComponentViewerViewport>(\n local.defaultViewport || \"desktop\"\n );\n const [showGrid, setShowGrid] = createSignal(true);\n const [isFullscreen, setIsFullscreen] = createSignal(false);\n const aiClipboard = createClipboard();\n const cmdClipboard = createClipboard();\n const [mounted, setMounted] = createSignal(true);\n\n const cliCommand = () => local.command;\n\n const handleCopyAi = async () => {\n const cmd = cliCommand();\n const prompt = `// ${local.title || local.name || \"Component\"}\n${cmd ? `// Installation: ${cmd}\\n` : \"\"}// Usage Code:\n${local.code}`;\n await aiClipboard.copy(prompt);\n };\n\n const handleCopyCmd = async () => {\n const cmd = cliCommand();\n if (!cmd) return;\n await cmdClipboard.copy(cmd);\n };\n\n const handleReset = () => {\n setMounted(false);\n setTimeout(() => setMounted(true), 20);\n };\n\n const alignmentClass = () => {\n if (local.align === \"start\") return \"items-start justify-start\";\n if (local.align === \"end\") return \"items-end justify-end\";\n return \"items-center justify-center\";\n };\n\n const viewportWidthClass = () => {\n switch (viewport()) {\n case \"mobile\":\n return \"max-w-[375px]\";\n case \"tablet\":\n return \"max-w-[768px]\";\n default:\n return \"max-w-full\";\n }\n };\n\n const renderViewerContent = (isModal = false) => (\n <div\n class={cn(\n \"group relative flex flex-col rounded-lg border border-border bg-card/60 shadow-2xs transition-all w-full\",\n isModal && \"h-full flex-1 bg-background shadow-2xl\",\n !isModal && \"my-6\",\n local.class\n )}\n {...rest}\n >\n <Tabs defaultValue=\"preview\" class=\"relative w-full flex flex-col h-full flex-1 gap-0\">\n {/* Top Header & Toolbar */}\n <div class=\"flex flex-wrap items-center justify-between border-b border-border/70 px-3 py-2 gap-2 bg-muted/30 shrink-0\">\n <div class=\"flex items-center gap-3\">\n <TabsList class=\"h-8 rounded-md bg-muted/60 p-0.5 gap-1\">\n <TabsTrigger value=\"preview\" class=\"h-7 px-3 text-xs cursor-pointer data-[state=active]:bg-background data-[state=active]:shadow-2xs\">\n Preview\n </TabsTrigger>\n <TabsTrigger value=\"code\" class=\"h-7 px-3 text-xs cursor-pointer data-[state=active]:bg-background data-[state=active]:shadow-2xs\">\n Code\n </TabsTrigger>\n </TabsList>\n\n <Show when={local.title}>\n <span class=\"text-xs font-semibold text-foreground tracking-tight hidden sm:inline-block\">\n {local.title}\n </span>\n </Show>\n </div>\n\n {/* Right Action Tools */}\n <div class=\"flex flex-wrap items-center gap-1.5 ml-auto\">\n {/* Responsive Viewport Switcher */}\n <Show when={local.responsive ?? true}>\n <div class=\"flex items-center rounded-md border border-border/60 bg-muted/40 p-0.5 gap-0.5 mr-1\">\n <Tooltip>\n <TooltipTrigger\n as={Button}\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setViewport(\"desktop\")}\n aria-label=\"Desktop viewport\"\n class={cn(\n \"size-6.5 p-0 rounded-sm cursor-pointer text-muted-foreground\",\n viewport() === \"desktop\" && \"bg-background text-foreground shadow-2xs font-semibold\"\n )}\n >\n <Monitor class=\"size-3.5\" />\n </TooltipTrigger>\n <TooltipContent class=\"text-[10px] py-1 px-2\">Desktop (100%)</TooltipContent>\n </Tooltip>\n\n <Tooltip>\n <TooltipTrigger\n as={Button}\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setViewport(\"tablet\")}\n aria-label=\"Tablet viewport\"\n class={cn(\n \"size-6.5 p-0 rounded-sm cursor-pointer text-muted-foreground\",\n viewport() === \"tablet\" && \"bg-background text-foreground shadow-2xs font-semibold\"\n )}\n >\n <Tablet class=\"size-3.5\" />\n </TooltipTrigger>\n <TooltipContent class=\"text-[10px] py-1 px-2\">Tablet (768px)</TooltipContent>\n </Tooltip>\n\n <Tooltip>\n <TooltipTrigger\n as={Button}\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setViewport(\"mobile\")}\n aria-label=\"Mobile viewport\"\n class={cn(\n \"size-6.5 p-0 rounded-sm cursor-pointer text-muted-foreground\",\n viewport() === \"mobile\" && \"bg-background text-foreground shadow-2xs font-semibold\"\n )}\n >\n <Smartphone class=\"size-3.5\" />\n </TooltipTrigger>\n <TooltipContent class=\"text-[10px] py-1 px-2\">Mobile (375px)</TooltipContent>\n </Tooltip>\n </div>\n </Show>\n\n {/* Grid Pattern Toggle */}\n <Show when={local.showGridToggle ?? true}>\n <Tooltip>\n <TooltipTrigger\n as={Button}\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setShowGrid(!showGrid())}\n aria-label=\"Toggle background grid\"\n class={cn(\n \"size-7 rounded-md border border-border/50 text-muted-foreground hover:text-foreground cursor-pointer\",\n showGrid() && \"text-foreground bg-muted/60\"\n )}\n >\n <Grid class=\"size-3.5\" />\n </TooltipTrigger>\n <TooltipContent class=\"text-[10px] py-1 px-2\">Toggle Canvas Grid</TooltipContent>\n </Tooltip>\n </Show>\n\n {/* Re-render Reset Canvas */}\n <Show when={local.reRenderable ?? true}>\n <Tooltip>\n <TooltipTrigger\n as={Button}\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleReset}\n aria-label=\"Reset preview canvas\"\n class=\"size-7 rounded-md border border-border/50 text-muted-foreground hover:text-foreground cursor-pointer\"\n >\n <RotateCcw class=\"size-3.5\" />\n </TooltipTrigger>\n <TooltipContent class=\"text-[10px] py-1 px-2\">Reset canvas</TooltipContent>\n </Tooltip>\n </Show>\n\n {/* Copy for AI */}\n <Tooltip>\n <TooltipTrigger\n as={Button}\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleCopyAi}\n aria-label=\"Copy context formatted for AI assistants\"\n class=\"h-7 px-2 text-xs text-muted-foreground hover:text-foreground border border-border/50 rounded-md cursor-pointer flex items-center gap-1.5\"\n >\n <Show when={aiClipboard.copied()} fallback={<Sparkles class=\"size-3\" />}>\n <Check class=\"size-3 text-emerald-500\" />\n </Show>\n <span class=\"hidden sm:inline\">{aiClipboard.copied() ? \"Copied!\" : \"Prompt\"}</span>\n </TooltipTrigger>\n <TooltipContent class=\"text-[10px] py-1 px-2\">Copy code formatted for AI</TooltipContent>\n </Tooltip>\n\n {/* Copy CLI Installation Command (only when command prop is provided) */}\n <Show when={cliCommand()}>\n <Tooltip>\n <TooltipTrigger\n as={Button}\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleCopyCmd}\n aria-label=\"Copy CLI installation command\"\n class=\"h-7 px-2 text-xs font-mono text-muted-foreground hover:text-foreground border border-border/50 rounded-md cursor-pointer flex items-center gap-1.5\"\n >\n <Show when={cmdClipboard.copied()} fallback={<Terminal class=\"size-3\" />}>\n <Check class=\"size-3 text-emerald-500\" />\n </Show>\n <span class=\"hidden md:inline\">{cmdClipboard.copied() ? \"Copied!\" : cliCommand()}</span>\n </TooltipTrigger>\n <TooltipContent class=\"text-[10px] py-1 px-2\">Copy CLI command</TooltipContent>\n </Tooltip>\n </Show>\n\n {/* Fullscreen Expand Button */}\n <Tooltip>\n <TooltipTrigger\n as={Button}\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setIsFullscreen(!isFullscreen())}\n aria-label={isFullscreen() ? \"Exit fullscreen\" : \"Expand to fullscreen\"}\n class=\"size-7 rounded-md border border-border/50 text-muted-foreground hover:text-foreground cursor-pointer\"\n >\n <Show when={isFullscreen()} fallback={<Maximize2 class=\"size-3.5\" />}>\n <Minimize2 class=\"size-3.5 text-primary\" />\n </Show>\n </TooltipTrigger>\n <TooltipContent class=\"text-[10px] py-1 px-2\">\n {isFullscreen() ? \"Exit Fullscreen\" : \"Fullscreen Preview\"}\n </TooltipContent>\n </Tooltip>\n </div>\n </div>\n\n {/* Live Preview Canvas Content */}\n <TabsContent\n value=\"preview\"\n class={cn(\n \"relative flex-1 p-4 sm:p-6 md:p-10 flex flex-col items-center justify-center transition-all\",\n isModal ? \"min-h-[450px] flex-1\" : \"min-h-[280px]\",\n showGrid() &&\n \"bg-[radial-gradient(#e5e7eb_1px,transparent_1px)] dark:bg-[radial-gradient(#27272a_1px,transparent_1px)] [background-size:16px_16px]\",\n local.allowOverflow ? \"overflow-visible\" : \"overflow-x-auto\"\n )}\n >\n <div\n class={cn(\n \"w-full transition-all duration-300 flex\",\n viewportWidthClass(),\n alignmentClass(),\n viewport() !== \"desktop\" &&\n \"rounded-lg border border-dashed border-border/80 p-4 bg-background/80 shadow-xs\"\n )}\n >\n <Show when={mounted()}>\n {local.children}\n </Show>\n </div>\n </TabsContent>\n\n {/* Source Code View Content */}\n <TabsContent value=\"code\" class={cn(\"p-0 m-0\", isModal && \"flex-1 overflow-auto\")}>\n <CodeBlock\n code={local.code}\n lang={local.lang || \"tsx\"}\n filename={local.name ? `${local.name}.tsx` : undefined}\n class=\"my-0 border-0 rounded-t-none\"\n />\n </TabsContent>\n </Tabs>\n </div>\n );\n\n return (\n <>\n <Show when={!isFullscreen()}>\n {renderViewerContent(false)}\n </Show>\n\n {/* Fullscreen Portal Modal */}\n <Show when={isFullscreen()}>\n <Portal mount={typeof document !== \"undefined\" ? document.body : undefined}>\n <div class=\"fixed inset-0 z-[9999] flex flex-col bg-background/95 backdrop-blur-md p-4 sm:p-8 animate-in fade-in duration-200\">\n {renderViewerContent(true)}\n </div>\n </Portal>\n </Show>\n </>\n );\n};\n",
22
+ "type": "registry:ui"
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "container",
3
+ "title": "Container",
4
+ "description": "A responsive layout container constraining maximum width with semantic padding tokens.",
5
+ "type": "registry:ui",
6
+ "dependencies": [
7
+ "clsx",
8
+ "tailwind-merge",
9
+ "class-variance-authority"
10
+ ],
11
+ "files": [
12
+ {
13
+ "path": "ui/container.tsx",
14
+ "content": "import { splitProps, type JSX, type ValidComponent } from \"solid-js\";\nimport { Dynamic } from \"solid-js/web\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"@/lib/cn\";\n\nexport const containerVariants = cva(\"w-full mx-auto px-4 sm:px-6 lg:px-8\", {\n variants: {\n size: {\n sm: \"max-w-screen-sm\",\n md: \"max-w-screen-md\",\n lg: \"max-w-screen-lg\",\n xl: \"max-w-screen-xl\",\n \"2xl\": \"max-w-screen-2xl\",\n full: \"max-w-full\",\n },\n },\n defaultVariants: {\n size: \"2xl\",\n },\n});\n\nexport interface ContainerProps<T extends ValidComponent = \"div\">\n extends JSX.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof containerVariants> {\n /** Underlying HTML element or Solid component tag (defaults to \"div\") */\n as?: T;\n class?: string;\n}\n\n/**\n * Responsive container primitive constraining max-width with semantic padding tokens.\n */\nexport const Container = <T extends ValidComponent = \"div\">(props: ContainerProps<T>) => {\n const [local, rest] = splitProps(props as ContainerProps, [\"as\", \"size\", \"class\", \"children\"]);\n\n return (\n <Dynamic\n component={local.as || \"div\"}\n class={cn(containerVariants({ size: local.size }), local.class)}\n {...rest}\n >\n {local.children}\n </Dynamic>\n );\n};\n",
15
+ "type": "registry:ui"
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-click-outside",
3
+ "title": "createClickOutside",
4
+ "description": "SolidJS reactive primitive for detecting click and pointer interactions outside target elements",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-click-outside.ts",
9
+ "content": "import { onMount, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateClickOutsideOptions {\n /** Target HTML element or accessor returning the container element */\n target:\n | HTMLElement\n | Accessor<HTMLElement | undefined>\n | (HTMLElement | Accessor<HTMLElement | undefined>)[];\n /** Callback fired when a click/pointer event occurs outside the target element(s) */\n onInteractOutside: (event: MouseEvent | PointerEvent | TouchEvent) => void;\n /** Whether the listener is active. Defaults to true. */\n enabled?: boolean | Accessor<boolean>;\n /** Optional element or accessor to ignore when checking outside clicks (e.g. trigger button) */\n ignore?:\n | HTMLElement\n | Accessor<HTMLElement | undefined>\n | (HTMLElement | Accessor<HTMLElement | undefined>)[];\n}\n\n/**\n * SolidJS reactive primitive for detecting user interactions outside specified element(s).\n *\n * @param options Configuration options including target element(s), callback, and optional ignore elements.\n */\nexport function createClickOutside(options: CreateClickOutsideOptions): void {\n const isEnabled = () => {\n if (typeof options.enabled === \"function\") {\n return options.enabled();\n }\n return options.enabled ?? true;\n };\n\n const getElement = (\n el: HTMLElement | Accessor<HTMLElement | undefined> | undefined\n ): HTMLElement | undefined => {\n if (!el) return undefined;\n if (typeof el === \"function\") {\n return (el as Accessor<HTMLElement | undefined>)();\n }\n return el;\n };\n\n const getElements = (\n targets:\n | HTMLElement\n | Accessor<HTMLElement | undefined>\n | (HTMLElement | Accessor<HTMLElement | undefined>)[]\n | undefined\n ): HTMLElement[] => {\n if (!targets) return [];\n const list = Array.isArray(targets) ? targets : [targets];\n return list.map(getElement).filter((e): e is HTMLElement => e !== undefined);\n };\n\n const handlePointerDown = (event: MouseEvent | PointerEvent | TouchEvent) => {\n if (!isEnabled()) return;\n\n const targetNode = event.target as Node | null;\n if (!targetNode) return;\n\n const mainElements = getElements(options.target);\n if (mainElements.length === 0) return;\n\n const isInsideTarget = mainElements.some((el) => el.contains(targetNode));\n if (isInsideTarget) return;\n\n const ignoreElements = getElements(options.ignore);\n const isInsideIgnore = ignoreElements.some((el) => el.contains(targetNode));\n if (isInsideIgnore) return;\n\n options.onInteractOutside(event);\n };\n\n onMount(() => {\n if (typeof window === \"undefined\") return;\n\n window.addEventListener(\"pointerdown\", handlePointerDown, true);\n onCleanup(() => {\n window.removeEventListener(\"pointerdown\", handlePointerDown, true);\n });\n });\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-clipboard",
3
+ "title": "createClipboard",
4
+ "description": "SolidJS reactive primitive for copying text to clipboard with automatic status reset",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-clipboard.ts",
9
+ "content": "import { createSignal, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateClipboardOptions {\n /** Time in milliseconds to maintain the copied state before resetting. Defaults to 2000ms. */\n timeout?: number;\n}\n\nexport interface CreateClipboardReturn {\n /** Signal accessor indicating if content was recently copied */\n copied: Accessor<boolean>;\n /** Function to copy text to clipboard */\n copy: (text: string) => Promise<boolean>;\n /** Error state if clipboard write fails */\n error: Accessor<Error | undefined>;\n}\n\n/**\n * SolidJS reactive primitive for copying text to clipboard with automatic reset state.\n *\n * @param options Configuration options including copied status reset timeout duration.\n */\nexport function createClipboard(options: CreateClipboardOptions = {}): CreateClipboardReturn {\n const timeoutDuration = options.timeout ?? 2000;\n const [copied, setCopied] = createSignal(false);\n const [error, setError] = createSignal<Error | undefined>(undefined);\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const clearTimer = () => {\n if (timer) {\n clearTimeout(timer);\n timer = undefined;\n }\n };\n\n const copy = async (text: string): Promise<boolean> => {\n clearTimer();\n setError(undefined);\n\n try {\n if (typeof window !== \"undefined\" && navigator.clipboard && navigator.clipboard.writeText) {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n\n timer = setTimeout(() => {\n setCopied(false);\n }, timeoutDuration);\n\n return true;\n }\n\n throw new Error(\"Clipboard API not supported\");\n } catch (err) {\n const copyError = err instanceof Error ? err : new Error(String(err));\n setError(copyError);\n setCopied(false);\n return false;\n }\n };\n\n onCleanup(() => {\n clearTimer();\n });\n\n return {\n copied,\n copy,\n error,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-color-mode",
3
+ "title": "createColorMode",
4
+ "description": "SolidJS reactive primitive for managing dark/light themes and system preferences",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-color-mode.ts",
9
+ "content": "import { createEffect, createSignal, onCleanup, onMount, type Accessor } from \"solid-js\";\n\nexport type ColorMode = \"light\" | \"dark\" | \"system\";\n\nexport interface CreateColorModeOptions {\n /** Initial color mode fallback. Defaults to 'system'. */\n initialValue?: ColorMode;\n /** LocalStorage key for persisting color mode state. Defaults to 'nikala-color-mode'. */\n storageKey?: string;\n /** HTML attribute to apply dark mode class on document.documentElement. Defaults to 'class'. */\n attribute?: string;\n}\n\nexport interface CreateColorModeReturn {\n /** Accessor for current active color mode ('light', 'dark', 'system') */\n mode: Accessor<ColorMode>;\n /** Function to update current color mode */\n setMode: (mode: ColorMode) => void;\n /** Function to toggle color mode between 'light' and 'dark' */\n toggleColorMode: () => void;\n /** Accessor indicating if current resolved mode is dark */\n isDark: Accessor<boolean>;\n}\n\n/**\n * SolidJS reactive primitive for managing dark/light color mode themes and system preferences.\n *\n * @param options Configuration options including storage key and initial mode.\n */\nexport function createColorMode(\n options: CreateColorModeOptions = {}\n): CreateColorModeReturn {\n const initialMode = options.initialValue ?? \"system\";\n const storageKey = options.storageKey ?? \"nikala-color-mode\";\n const attribute = options.attribute ?? \"class\";\n\n const [mode, setModeSignal] = createSignal<ColorMode>(initialMode);\n const [systemDark, setSystemDark] = createSignal(false);\n\n const isDark = () => {\n const currentMode = mode();\n if (currentMode === \"system\") return systemDark();\n return currentMode === \"dark\";\n };\n\n const applyTheme = (dark: boolean) => {\n if (typeof document === \"undefined\") return;\n const root = document.documentElement;\n if (attribute === \"class\") {\n if (dark) root.classList.add(\"dark\");\n else root.classList.remove(\"dark\");\n } else {\n root.setAttribute(attribute, dark ? \"dark\" : \"light\");\n }\n };\n\n const setMode = (newMode: ColorMode) => {\n setModeSignal(newMode);\n if (typeof window !== \"undefined\") {\n try {\n localStorage.setItem(storageKey, newMode);\n } catch (e) {\n console.warn(`[nikala-ui/hooks] Error setting color mode storage:`, e);\n }\n }\n };\n\n const toggleColorMode = () => {\n const next = isDark() ? \"light\" : \"dark\";\n setMode(next);\n };\n\n createEffect(() => {\n applyTheme(isDark());\n });\n\n onMount(() => {\n if (typeof window === \"undefined\") return;\n\n // Check system preference\n const mediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");\n setSystemDark(mediaQuery.matches);\n\n const handleMediaChange = (e: MediaQueryListEvent) => {\n setSystemDark(e.matches);\n };\n\n if (mediaQuery.addEventListener) {\n mediaQuery.addEventListener(\"change\", handleMediaChange);\n }\n\n // Check localStorage\n try {\n const stored = localStorage.getItem(storageKey) as ColorMode | null;\n if (stored && [\"light\", \"dark\", \"system\"].includes(stored)) {\n setModeSignal(stored);\n }\n } catch (e) {\n console.warn(`[nikala-ui/hooks] Error reading color mode storage:`, e);\n }\n\n onCleanup(() => {\n if (mediaQuery.removeEventListener) {\n mediaQuery.removeEventListener(\"change\", handleMediaChange);\n }\n });\n });\n\n return {\n mode,\n setMode,\n toggleColorMode,\n isDark,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-controllable-signal",
3
+ "title": "createControllableSignal",
4
+ "description": "SolidJS reactive primitive supporting both controlled and uncontrolled state management",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-controllable-signal.ts",
9
+ "content": "import { createSignal, type Accessor } from \"solid-js\";\n\nexport interface CreateControllableSignalOptions<T> {\n /** Controlled value accessor or raw value */\n value?: Accessor<T | undefined> | T;\n /** Uncontrolled default initial value */\n defaultValue?: T;\n /** Callback fired whenever the value changes */\n onChange?: (value: T) => void;\n}\n\n/**\n * SolidJS reactive primitive for managing state supporting both controlled and uncontrolled modes.\n *\n * @param options Configuration options for value, defaultValue, and onChange callback.\n * @returns A tuple containing [valueAccessor, setValueFunction].\n */\nexport function createControllableSignal<T>(\n options: CreateControllableSignalOptions<T>\n): [Accessor<T | undefined>, (nextValue: T | ((prev: T | undefined) => T)) => void] {\n const [internalValue, setInternalValue] = createSignal<T | undefined>(\n options.defaultValue\n );\n\n const getValue = (): T | undefined => {\n if (typeof options.value === \"function\") {\n return (options.value as Accessor<T | undefined>)();\n }\n return options.value;\n };\n\n const isControlled = () => getValue() !== undefined;\n\n const value = () => {\n const controlledVal = getValue();\n return controlledVal !== undefined ? controlledVal : internalValue();\n };\n\n const setValue = (nextValue: T | ((prev: T | undefined) => T)) => {\n const current = value();\n const resolvedNext =\n typeof nextValue === \"function\"\n ? (nextValue as (prev: T | undefined) => T)(current)\n : nextValue;\n\n if (!isControlled()) {\n setInternalValue(resolvedNext as any);\n }\n\n if (typeof options.onChange === \"function\") {\n options.onChange(resolvedNext);\n }\n };\n\n return [value, setValue];\n}",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-focus-trap",
3
+ "title": "createFocusTrap",
4
+ "description": "SolidJS reactive primitive for trapping keyboard focus inside target container element for accessibility",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-focus-trap.ts",
9
+ "content": "import { createEffect, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateFocusTrapOptions {\n /** Whether focus trap is actively enabled. Defaults to true. */\n enabled?: boolean | Accessor<boolean>;\n /** Whether to return focus to previously focused element on cleanup. Defaults to true. */\n returnFocusOnDeactivate?: boolean;\n}\n\nconst FOCUSABLE_SELECTOR =\n 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex]:not([tabindex=\"-1\"]), [contenteditable]';\n\n/**\n * SolidJS reactive primitive for trapping keyboard focus inside target container element.\n *\n * @param target Target element or accessor returning HTML element.\n * @param options Configuration options for focus trap behavior.\n */\nexport function createFocusTrap(\n target: HTMLElement | Accessor<HTMLElement | undefined>,\n options: CreateFocusTrapOptions = {}\n): void {\n const getTarget = (): HTMLElement | undefined => {\n if (typeof target === \"function\") {\n return (target as Accessor<HTMLElement | undefined>)();\n }\n return target;\n };\n\n const isEnabled = (): boolean => {\n if (typeof options.enabled === \"function\") {\n return options.enabled();\n }\n return options.enabled ?? true;\n };\n\n createEffect(() => {\n if (typeof window === \"undefined\" || !isEnabled()) return;\n\n const container = getTarget();\n if (!container) return;\n\n const previousActiveElement = document.activeElement as HTMLElement | null;\n\n const getFocusableElements = (): HTMLElement[] => {\n return Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).filter(\n (el) => el.offsetWidth > 0 || el.offsetHeight > 0 || el.getClientRects().length > 0\n );\n };\n\n const focusable = getFocusableElements();\n if (focusable.length > 0) {\n focusable[0].focus();\n } else {\n container.focus();\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Tab\") return;\n\n const elements = getFocusableElements();\n if (elements.length === 0) {\n event.preventDefault();\n return;\n }\n\n const firstElement = elements[0];\n const lastElement = elements[elements.length - 1];\n const activeElement = document.activeElement;\n\n if (event.shiftKey) {\n if (activeElement === firstElement || !container.contains(activeElement)) {\n event.preventDefault();\n lastElement.focus();\n }\n } else {\n if (activeElement === lastElement || !container.contains(activeElement)) {\n event.preventDefault();\n firstElement.focus();\n }\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n\n onCleanup(() => {\n document.removeEventListener(\"keydown\", handleKeyDown);\n\n if (options.returnFocusOnDeactivate !== false && previousActiveElement) {\n previousActiveElement.focus?.();\n }\n });\n });\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-keybindings",
3
+ "title": "createKeybindings",
4
+ "description": "SolidJS reactive primitives for listening to keyboard shortcuts, key combinations, and Escape key presses",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-keybindings.ts",
9
+ "content": "import { onMount, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface KeybindingDefinition {\n /** Key combination string, e.g. \"meta+k\", \"ctrl+k\", \"Escape\", \"Shift+Enter\" */\n key: string | string[];\n /** Callback handler invoked when the key combination is triggered */\n handler: (event: KeyboardEvent) => void;\n /** Whether to prevent default browser action. Defaults to false. */\n preventDefault?: boolean;\n /** Whether to stop event propagation. Defaults to false. */\n stopPropagation?: boolean;\n}\n\nexport interface CreateKeybindingsOptions {\n /** Event target to attach listener to. Defaults to window. */\n target?: HTMLElement | Window | Accessor<HTMLElement | Window | undefined>;\n /** Event type: \"keydown\" (default) or \"keyup\" */\n eventType?: \"keydown\" | \"keyup\";\n /** Whether keybinding listeners are active. Defaults to true. */\n enabled?: boolean | Accessor<boolean>;\n}\n\n/**\n * Normalizes key string into standardized combination signature (e.g. \"ctrl+meta+k\").\n */\nfunction normalizeKeyCombination(keyStr: string): string {\n const parts = keyStr.toLowerCase().split(\"+\").map((p) => p.trim());\n const modifiers = new Set<string>();\n let mainKey = \"\";\n\n for (const part of parts) {\n if (part === \"meta\" || part === \"cmd\" || part === \"command\" || part === \"⌘\") {\n modifiers.add(\"meta\");\n } else if (part === \"ctrl\" || part === \"control\") {\n modifiers.add(\"ctrl\");\n } else if (part === \"alt\" || part === \"option\" || part === \"⌥\") {\n modifiers.add(\"alt\");\n } else if (part === \"shift\" || part === \"⇧\") {\n modifiers.add(\"shift\");\n } else {\n mainKey = part;\n }\n }\n\n const sortedMods = Array.from(modifiers).sort();\n return [...sortedMods, mainKey].join(\"+\");\n}\n\n/**\n * Checks if a KeyboardEvent matches a normalized key combination.\n */\nfunction isEventMatch(event: KeyboardEvent, targetCombination: string): boolean {\n const modifiers = new Set<string>();\n if (event.metaKey) modifiers.add(\"meta\");\n if (event.ctrlKey) modifiers.add(\"ctrl\");\n if (event.altKey) modifiers.add(\"alt\");\n if (event.shiftKey) modifiers.add(\"shift\");\n\n const mainKey = event.key.toLowerCase();\n const eventCombination = [...Array.from(modifiers).sort(), mainKey].join(\"+\");\n\n return eventCombination === targetCombination;\n}\n\n/**\n * SolidJS reactive primitive for listening to single or multiple keyboard shortcuts.\n *\n * @param bindings Array of keybinding definitions or single definition object.\n * @param options Configuration options including target, event type, and enabled state.\n */\nexport function createKeybindings(\n bindings: KeybindingDefinition | KeybindingDefinition[],\n options: CreateKeybindingsOptions = {}\n): void {\n const bindingList = Array.isArray(bindings) ? bindings : [bindings];\n\n const isEnabled = () => {\n if (typeof options.enabled === \"function\") {\n return options.enabled();\n }\n return options.enabled ?? true;\n };\n\n const getTargetElement = (): HTMLElement | Window | undefined => {\n if (!options.target) {\n return typeof window !== \"undefined\" ? window : undefined;\n }\n if (typeof options.target === \"function\") {\n return (options.target as Accessor<HTMLElement | Window | undefined>)();\n }\n return options.target;\n };\n\n const handleKeyboardEvent = (event: KeyboardEvent) => {\n if (!isEnabled()) return;\n\n for (const binding of bindingList) {\n const keys = Array.isArray(binding.key) ? binding.key : [binding.key];\n\n for (const keyCombo of keys) {\n const normalizedCombo = normalizeKeyCombination(keyCombo);\n\n if (isEventMatch(event, normalizedCombo)) {\n if (binding.preventDefault) {\n event.preventDefault();\n }\n if (binding.stopPropagation) {\n event.stopPropagation();\n }\n binding.handler(event);\n break;\n }\n }\n }\n };\n\n onMount(() => {\n const target = getTargetElement();\n if (!target) return;\n\n const eventType = options.eventType || \"keydown\";\n target.addEventListener(eventType, handleKeyboardEvent as EventListener);\n\n onCleanup(() => {\n target.removeEventListener(eventType, handleKeyboardEvent as EventListener);\n });\n });\n}\n\n/**\n * SolidJS primitive specialized for handling Escape key presses.\n *\n * @param handler Callback invoked when Escape key is pressed.\n * @param options Keybindings options.\n */\nexport function createEscapeKey(\n handler: (event: KeyboardEvent) => void,\n options: CreateKeybindingsOptions = {}\n): void {\n createKeybindings(\n {\n key: \"Escape\",\n handler,\n preventDefault: true,\n },\n options\n );\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-lock-scroll",
3
+ "title": "createLockScroll",
4
+ "description": "SolidJS reactive primitive for locking body or container scrolling when overlays are active",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-lock-scroll.ts",
9
+ "content": "import { createEffect, createSignal, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateLockScrollOptions {\n /** Target element to lock scroll for. Defaults to document.body. */\n target?: HTMLElement | Accessor<HTMLElement | undefined>;\n /** Initial lock state. Defaults to true. */\n enabled?: boolean | Accessor<boolean>;\n}\n\nexport interface CreateLockScrollReturn {\n /** Accessor indicating whether scroll is currently locked */\n isLocked: Accessor<boolean>;\n /** Setter function to programmatically lock or unlock scroll */\n setLocked: (locked: boolean) => void;\n}\n\n/**\n * SolidJS reactive primitive for locking target element (or document.body) scroll.\n *\n * @param options Configuration options including target element and initial enabled state.\n */\nexport function createLockScroll(options: CreateLockScrollOptions = {}): CreateLockScrollReturn {\n const [internalLocked, setInternalLocked] = createSignal(false);\n\n const getTarget = (): HTMLElement | undefined => {\n if (typeof window === \"undefined\") return undefined;\n\n if (!options.target) {\n return document.body;\n }\n if (typeof options.target === \"function\") {\n return (options.target as Accessor<HTMLElement | undefined>)();\n }\n return options.target;\n };\n\n const isEnabled = (): boolean => {\n if (typeof options.enabled === \"function\") {\n return options.enabled();\n }\n return options.enabled ?? true;\n };\n\n let originalOverflow: string | undefined;\n\n const applyLock = (element: HTMLElement) => {\n if (originalOverflow === undefined) {\n originalOverflow = element.style.overflow;\n }\n element.style.overflow = \"hidden\";\n setInternalLocked(true);\n };\n\n const removeLock = (element: HTMLElement) => {\n if (originalOverflow !== undefined) {\n element.style.overflow = originalOverflow;\n originalOverflow = undefined;\n } else {\n element.style.overflow = \"\";\n }\n setInternalLocked(false);\n };\n\n createEffect(() => {\n const el = getTarget();\n if (!el) return;\n\n if (isEnabled()) {\n applyLock(el);\n } else {\n removeLock(el);\n }\n });\n\n onCleanup(() => {\n const el = getTarget();\n if (el) {\n removeLock(el);\n }\n });\n\n const setLocked = (locked: boolean) => {\n const el = getTarget();\n if (!el) return;\n\n if (locked) {\n applyLock(el);\n } else {\n removeLock(el);\n }\n };\n\n return {\n isLocked: internalLocked,\n setLocked,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-resize-observer",
3
+ "title": "createResizeObserver",
4
+ "description": "SolidJS reactive primitives for tracking element width and height dimensions dynamically",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-resize-observer.ts",
9
+ "content": "import { createEffect, createSignal, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateResizeObserverOptions extends ResizeObserverOptions {\n /** Whether the observer is active. Defaults to true. */\n enabled?: boolean | Accessor<boolean>;\n}\n\n/**\n * SolidJS reactive primitive for observing element size changes via ResizeObserver.\n *\n * @param target Target element or accessor returning HTML element.\n * @param callback Observer callback invoked on element resize events.\n * @param options ResizeObserver options (box, enabled).\n */\nexport function createResizeObserver(\n target: HTMLElement | Accessor<HTMLElement | undefined>,\n callback: ResizeObserverCallback,\n options: CreateResizeObserverOptions = {}\n): void {\n const getTarget = (): HTMLElement | undefined => {\n if (typeof target === \"function\") {\n return (target as Accessor<HTMLElement | undefined>)();\n }\n return target;\n };\n\n const isEnabled = (): boolean => {\n if (typeof options.enabled === \"function\") {\n return options.enabled();\n }\n return options.enabled ?? true;\n };\n\n createEffect(() => {\n if (typeof window === \"undefined\" || !window.ResizeObserver) {\n return;\n }\n\n if (!isEnabled()) return;\n\n const el = getTarget();\n if (!el) return;\n\n const observer = new ResizeObserver(callback);\n observer.observe(el, { box: options.box });\n\n onCleanup(() => {\n observer.disconnect();\n });\n });\n}\n\nexport interface CreateElementSizeReturn {\n /** Accessor for element width in pixels */\n width: Accessor<number>;\n /** Accessor for element height in pixels */\n height: Accessor<number>;\n}\n\n/**\n * SolidJS reactive primitive returning width and height accessors for a target HTML element.\n *\n * @param target Target element or accessor returning HTML element.\n * @param options ResizeObserver options.\n */\nexport function createElementSize(\n target: HTMLElement | Accessor<HTMLElement | undefined>,\n options: CreateResizeObserverOptions = {}\n): CreateElementSizeReturn {\n const [width, setWidth] = createSignal(0);\n const [height, setHeight] = createSignal(0);\n\n createResizeObserver(\n target,\n (entries) => {\n const entry = entries[0];\n if (entry) {\n setWidth(entry.contentRect.width);\n setHeight(entry.contentRect.height);\n }\n },\n options\n );\n\n return {\n width,\n height,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-scroll-position",
3
+ "title": "createScrollPosition",
4
+ "description": "SolidJS reactive primitive for tracking scroll position, scroll direction, and container bounds",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-scroll-position.ts",
9
+ "content": "import { createEffect, createSignal, onCleanup, type Accessor } from \"solid-js\";\n\nexport type ScrollDirection = \"up\" | \"down\" | \"left\" | \"right\" | \"none\";\n\nexport interface CreateScrollPositionOptions {\n /** Target element to observe scroll events on. Defaults to window. */\n target?: HTMLElement | Window | Accessor<HTMLElement | Window | undefined>;\n}\n\nexport interface CreateScrollPositionReturn {\n /** Accessor for horizontal scroll position (scrollLeft / pageXOffset) */\n x: Accessor<number>;\n /** Accessor for vertical scroll position (scrollTop / pageYOffset) */\n y: Accessor<number>;\n /** Accessor indicating if element is actively scrolling */\n isScrolling: Accessor<boolean>;\n /** Accessor for current scroll direction ('up', 'down', 'left', 'right', 'none') */\n direction: Accessor<ScrollDirection>;\n /** Accessor indicating if scroll is at top (y <= 0) */\n isAtTop: Accessor<boolean>;\n /** Accessor indicating if scroll is at bottom of container */\n isAtBottom: Accessor<boolean>;\n /** Function to programmatically scroll target element */\n scrollTo: (options: ScrollToOptions) => void;\n}\n\n/**\n * SolidJS reactive primitive for tracking target element or window scroll position and scroll metrics.\n *\n * @param options Configuration options including target element.\n */\nexport function createScrollPosition(\n options: CreateScrollPositionOptions = {}\n): CreateScrollPositionReturn {\n const [x, setX] = createSignal(0);\n const [y, setY] = createSignal(0);\n const [isScrolling, setIsScrolling] = createSignal(false);\n const [direction, setDirection] = createSignal<ScrollDirection>(\"none\");\n const [isAtTop, setIsAtTop] = createSignal(true);\n const [isAtBottom, setIsAtBottom] = createSignal(false);\n\n let scrollTimeout: ReturnType<typeof setTimeout> | undefined;\n let lastX = 0;\n let lastY = 0;\n\n const getTarget = (): HTMLElement | Window | undefined => {\n if (typeof window === \"undefined\") return undefined;\n if (!options.target) return window;\n if (typeof options.target === \"function\") {\n return (options.target as Accessor<HTMLElement | Window | undefined>)();\n }\n return options.target;\n };\n\n const updateScroll = () => {\n const target = getTarget();\n if (!target) return;\n\n let currentX = 0;\n let currentY = 0;\n let maxScrollY = 0;\n\n if (target === window) {\n currentX = window.scrollX || window.pageXOffset;\n currentY = window.scrollY || window.pageYOffset;\n maxScrollY = document.documentElement.scrollHeight - window.innerHeight;\n } else {\n const el = target as HTMLElement;\n currentX = el.scrollLeft;\n currentY = el.scrollTop;\n maxScrollY = el.scrollHeight - el.clientHeight;\n }\n\n // Determine direction\n const deltaX = currentX - lastX;\n const deltaY = currentY - lastY;\n\n if (Math.abs(deltaY) > Math.abs(deltaX)) {\n if (deltaY > 0) setDirection(\"down\");\n else if (deltaY < 0) setDirection(\"up\");\n } else if (Math.abs(deltaX) > 0) {\n if (deltaX > 0) setDirection(\"right\");\n else if (deltaX < 0) setDirection(\"left\");\n }\n\n lastX = currentX;\n lastY = currentY;\n\n setX(currentX);\n setY(currentY);\n setIsAtTop(currentY <= 0);\n setIsAtBottom(maxScrollY > 0 && currentY >= maxScrollY - 1);\n setIsScrolling(true);\n\n if (scrollTimeout) clearTimeout(scrollTimeout);\n scrollTimeout = setTimeout(() => {\n setIsScrolling(false);\n }, 150);\n };\n\n createEffect(() => {\n const target = getTarget();\n if (!target) return;\n\n updateScroll();\n\n target.addEventListener(\"scroll\", updateScroll, { passive: true });\n onCleanup(() => {\n target.removeEventListener(\"scroll\", updateScroll);\n if (scrollTimeout) clearTimeout(scrollTimeout);\n });\n });\n\n const scrollTo = (scrollOptions: ScrollToOptions) => {\n const target = getTarget();\n if (!target) return;\n target.scrollTo(scrollOptions);\n };\n\n return {\n x,\n y,\n isScrolling,\n direction,\n isAtTop,\n isAtBottom,\n scrollTo,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }