@nikala-ui/docs 0.12.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 (463) hide show
  1. package/README.md +22 -0
  2. package/dist/cli/commands/build.d.ts +5 -0
  3. package/dist/cli/commands/build.d.ts.map +1 -0
  4. package/dist/cli/commands/build.js +27 -0
  5. package/dist/cli/commands/build.js.map +1 -0
  6. package/dist/cli/commands/dev.d.ts +7 -0
  7. package/dist/cli/commands/dev.d.ts.map +1 -0
  8. package/dist/cli/commands/dev.js +32 -0
  9. package/dist/cli/commands/dev.js.map +1 -0
  10. package/dist/cli/commands/init.d.ts +2 -0
  11. package/dist/cli/commands/init.d.ts.map +1 -0
  12. package/dist/cli/commands/init.js +308 -0
  13. package/dist/cli/commands/init.js.map +1 -0
  14. package/dist/cli/commands/preview.d.ts +7 -0
  15. package/dist/cli/commands/preview.d.ts.map +1 -0
  16. package/dist/cli/commands/preview.js +24 -0
  17. package/dist/cli/commands/preview.js.map +1 -0
  18. package/dist/cli/commands/serve.d.ts +7 -0
  19. package/dist/cli/commands/serve.d.ts.map +1 -0
  20. package/dist/cli/commands/serve.js +33 -0
  21. package/dist/cli/commands/serve.js.map +1 -0
  22. package/dist/cli/index.d.ts +3 -0
  23. package/dist/cli/index.d.ts.map +1 -0
  24. package/dist/cli/index.js +53 -0
  25. package/dist/cli/index.js.map +1 -0
  26. package/dist/client/App.d.ts +5 -0
  27. package/dist/client/App.d.ts.map +1 -0
  28. package/dist/client/App.jsx +31 -0
  29. package/dist/client/App.jsx.map +1 -0
  30. package/dist/client/app-types.d.ts +14 -0
  31. package/dist/client/app-types.d.ts.map +1 -0
  32. package/dist/client/app-types.js +2 -0
  33. package/dist/client/app-types.js.map +1 -0
  34. package/dist/client/components/DocsLayoutShell.d.ts +21 -0
  35. package/dist/client/components/DocsLayoutShell.d.ts.map +1 -0
  36. package/dist/client/components/DocsLayoutShell.jsx +14 -0
  37. package/dist/client/components/DocsLayoutShell.jsx.map +1 -0
  38. package/dist/client/components/DocsPageContent.d.ts +9 -0
  39. package/dist/client/components/DocsPageContent.d.ts.map +1 -0
  40. package/dist/client/components/DocsPageContent.jsx +10 -0
  41. package/dist/client/components/DocsPageContent.jsx.map +1 -0
  42. package/dist/client/entry.d.ts +2 -0
  43. package/dist/client/entry.d.ts.map +1 -0
  44. package/dist/client/entry.jsx +28 -0
  45. package/dist/client/entry.jsx.map +1 -0
  46. package/dist/client/index.html +21 -0
  47. package/dist/client/navigation/page-navigation.d.ts +9 -0
  48. package/dist/client/navigation/page-navigation.d.ts.map +1 -0
  49. package/dist/client/navigation/page-navigation.js +51 -0
  50. package/dist/client/navigation/page-navigation.js.map +1 -0
  51. package/dist/client/routing/use-docs-router.d.ts +17 -0
  52. package/dist/client/routing/use-docs-router.d.ts.map +1 -0
  53. package/dist/client/routing/use-docs-router.js +71 -0
  54. package/dist/client/routing/use-docs-router.js.map +1 -0
  55. package/dist/client/ssr-entry.d.ts +3 -0
  56. package/dist/client/ssr-entry.d.ts.map +1 -0
  57. package/dist/client/ssr-entry.jsx +13 -0
  58. package/dist/client/ssr-entry.jsx.map +1 -0
  59. package/dist/client/style.css +183 -0
  60. package/dist/client/utils/sidebar.d.ts +2 -0
  61. package/dist/client/utils/sidebar.d.ts.map +1 -0
  62. package/dist/client/utils/sidebar.js +2 -0
  63. package/dist/client/utils/sidebar.js.map +1 -0
  64. package/dist/components/mdx-components.d.ts +34 -0
  65. package/dist/components/mdx-components.d.ts.map +1 -0
  66. package/dist/components/mdx-components.jsx +131 -0
  67. package/dist/components/mdx-components.jsx.map +1 -0
  68. package/dist/config.d.ts +6 -0
  69. package/dist/config.d.ts.map +1 -0
  70. package/dist/config.js +105 -0
  71. package/dist/config.js.map +1 -0
  72. package/dist/core/content-scanner.d.ts +10 -0
  73. package/dist/core/content-scanner.d.ts.map +1 -0
  74. package/dist/core/content-scanner.js +153 -0
  75. package/dist/core/content-scanner.js.map +1 -0
  76. package/dist/core/route-tree.d.ts +19 -0
  77. package/dist/core/route-tree.d.ts.map +1 -0
  78. package/dist/core/route-tree.js +176 -0
  79. package/dist/core/route-tree.js.map +1 -0
  80. package/dist/index.d.ts +13 -0
  81. package/dist/index.d.ts.map +1 -0
  82. package/dist/index.js +14 -0
  83. package/dist/index.js.map +1 -0
  84. package/dist/lib/cn.d.ts +6 -0
  85. package/dist/lib/cn.d.ts.map +1 -0
  86. package/dist/lib/cn.js +9 -0
  87. package/dist/lib/cn.js.map +1 -0
  88. package/dist/mdx/compiler.d.ts +13 -0
  89. package/dist/mdx/compiler.d.ts.map +1 -0
  90. package/dist/mdx/compiler.js +38 -0
  91. package/dist/mdx/compiler.js.map +1 -0
  92. package/dist/mdx/highlighter.d.ts +22 -0
  93. package/dist/mdx/highlighter.d.ts.map +1 -0
  94. package/dist/mdx/highlighter.js +139 -0
  95. package/dist/mdx/highlighter.js.map +1 -0
  96. package/dist/mdx/rehype-shiki.d.ts +3 -0
  97. package/dist/mdx/rehype-shiki.d.ts.map +1 -0
  98. package/dist/mdx/rehype-shiki.js +83 -0
  99. package/dist/mdx/rehype-shiki.js.map +1 -0
  100. package/dist/navigation/index.d.ts +2 -0
  101. package/dist/navigation/index.d.ts.map +1 -0
  102. package/dist/navigation/index.js +2 -0
  103. package/dist/navigation/index.js.map +1 -0
  104. package/dist/navigation/sidebar-state.d.ts +6 -0
  105. package/dist/navigation/sidebar-state.d.ts.map +1 -0
  106. package/dist/navigation/sidebar-state.js +20 -0
  107. package/dist/navigation/sidebar-state.js.map +1 -0
  108. package/dist/navigation/sidebar-tree.d.ts +7 -0
  109. package/dist/navigation/sidebar-tree.d.ts.map +1 -0
  110. package/dist/navigation/sidebar-tree.js +11 -0
  111. package/dist/navigation/sidebar-tree.js.map +1 -0
  112. package/dist/registry/accordion.json +18 -0
  113. package/dist/registry/alert.json +18 -0
  114. package/dist/registry/api-table.json +21 -0
  115. package/dist/registry/aspect-ratio.json +17 -0
  116. package/dist/registry/avatar.json +17 -0
  117. package/dist/registry/badge.json +18 -0
  118. package/dist/registry/banner.json +19 -0
  119. package/dist/registry/breadcrumb.json +17 -0
  120. package/dist/registry/bubble.json +18 -0
  121. package/dist/registry/button-group.json +20 -0
  122. package/dist/registry/button.json +21 -0
  123. package/dist/registry/callout.json +19 -0
  124. package/dist/registry/card.json +17 -0
  125. package/dist/registry/checkbox.json +20 -0
  126. package/dist/registry/code-block.json +26 -0
  127. package/dist/registry/code-group.json +20 -0
  128. package/dist/registry/collapsible.json +18 -0
  129. package/dist/registry/combobox.json +21 -0
  130. package/dist/registry/command.json +27 -0
  131. package/dist/registry/component-viewer.json +25 -0
  132. package/dist/registry/container.json +18 -0
  133. package/dist/registry/context-menu.json +24 -0
  134. package/dist/registry/create-active-element.json +13 -0
  135. package/dist/registry/create-app-updater.json +13 -0
  136. package/dist/registry/create-audio.json +7 -0
  137. package/dist/registry/create-battery.json +13 -0
  138. package/dist/registry/create-chat-scroll.json +13 -0
  139. package/dist/registry/create-click-outside.json +13 -0
  140. package/dist/registry/create-clipboard.json +13 -0
  141. package/dist/registry/create-color-mode.json +13 -0
  142. package/dist/registry/create-controllable-signal.json +13 -0
  143. package/dist/registry/create-debounce.json +13 -0
  144. package/dist/registry/create-disclosure.json +13 -0
  145. package/dist/registry/create-document-tabs.json +13 -0
  146. package/dist/registry/create-document-title.json +13 -0
  147. package/dist/registry/create-drop-zone.json +13 -0
  148. package/dist/registry/create-event-source.json +13 -0
  149. package/dist/registry/create-favicon.json +13 -0
  150. package/dist/registry/create-fetch.json +13 -0
  151. package/dist/registry/create-focus-trap.json +13 -0
  152. package/dist/registry/create-form.json +13 -0
  153. package/dist/registry/create-fullscreen.json +13 -0
  154. package/dist/registry/create-geolocation.json +13 -0
  155. package/dist/registry/create-global-shortcut.json +13 -0
  156. package/dist/registry/create-hover.json +13 -0
  157. package/dist/registry/create-idle.json +13 -0
  158. package/dist/registry/create-infinite-scroll.json +13 -0
  159. package/dist/registry/create-input-mask.json +13 -0
  160. package/dist/registry/create-intersection-observer.json +13 -0
  161. package/dist/registry/create-keybindings.json +13 -0
  162. package/dist/registry/create-lock-scroll.json +13 -0
  163. package/dist/registry/create-long-press.json +13 -0
  164. package/dist/registry/create-media-query.json +13 -0
  165. package/dist/registry/create-mouse-position.json +13 -0
  166. package/dist/registry/create-network-status.json +13 -0
  167. package/dist/registry/create-orientation.json +13 -0
  168. package/dist/registry/create-pagination.json +13 -0
  169. package/dist/registry/create-permission.json +13 -0
  170. package/dist/registry/create-previous.json +13 -0
  171. package/dist/registry/create-resize-observer.json +13 -0
  172. package/dist/registry/create-scroll-into-view.json +13 -0
  173. package/dist/registry/create-scroll-position.json +13 -0
  174. package/dist/registry/create-storage.json +13 -0
  175. package/dist/registry/create-tauri-window.json +13 -0
  176. package/dist/registry/create-timer.json +13 -0
  177. package/dist/registry/create-tiptap-editor.json +34 -0
  178. package/dist/registry/create-undo-redo.json +13 -0
  179. package/dist/registry/create-web-notification.json +13 -0
  180. package/dist/registry/create-websocket.json +13 -0
  181. package/dist/registry/create-window-size.json +13 -0
  182. package/dist/registry/dialog.json +21 -0
  183. package/dist/registry/dropdown-menu.json +22 -0
  184. package/dist/registry/dropzone.json +21 -0
  185. package/dist/registry/empty.json +17 -0
  186. package/dist/registry/field.json +20 -0
  187. package/dist/registry/file-tree.json +21 -0
  188. package/dist/registry/footer.json +18 -0
  189. package/dist/registry/forgot-password-01.json +28 -0
  190. package/dist/registry/form-message.json +17 -0
  191. package/dist/registry/form.json +17 -0
  192. package/dist/registry/hero-01.json +22 -0
  193. package/dist/registry/hover-card.json +18 -0
  194. package/dist/registry/icon-button.json +20 -0
  195. package/dist/registry/index.json +1489 -0
  196. package/dist/registry/input-group.json +21 -0
  197. package/dist/registry/input.json +17 -0
  198. package/dist/registry/kbd.json +18 -0
  199. package/dist/registry/label.json +18 -0
  200. package/dist/registry/list.json +20 -0
  201. package/dist/registry/login-01.json +28 -0
  202. package/dist/registry/logo.json +17 -0
  203. package/dist/registry/marker.json +17 -0
  204. package/dist/registry/marquee.json +17 -0
  205. package/dist/registry/message.json +17 -0
  206. package/dist/registry/navbar.json +19 -0
  207. package/dist/registry/navigation-menu.json +22 -0
  208. package/dist/registry/number-input.json +24 -0
  209. package/dist/registry/otp-verification-01.json +26 -0
  210. package/dist/registry/package-manager-tabs.json +24 -0
  211. package/dist/registry/pager.json +21 -0
  212. package/dist/registry/pagination.json +22 -0
  213. package/dist/registry/pin-input.json +17 -0
  214. package/dist/registry/popover.json +22 -0
  215. package/dist/registry/progress.json +18 -0
  216. package/dist/registry/radio-group.json +18 -0
  217. package/dist/registry/rating.json +19 -0
  218. package/dist/registry/register-01.json +31 -0
  219. package/dist/registry/resizable.json +21 -0
  220. package/dist/registry/review-card.json +23 -0
  221. package/dist/registry/rich-text-editor.json +95 -0
  222. package/dist/registry/scroll-area.json +21 -0
  223. package/dist/registry/section-heading.json +21 -0
  224. package/dist/registry/select.json +22 -0
  225. package/dist/registry/separator.json +17 -0
  226. package/dist/registry/sheet.json +22 -0
  227. package/dist/registry/sidebar.json +24 -0
  228. package/dist/registry/skeleton.json +17 -0
  229. package/dist/registry/slider.json +18 -0
  230. package/dist/registry/spinner.json +18 -0
  231. package/dist/registry/stat.json +19 -0
  232. package/dist/registry/status.json +18 -0
  233. package/dist/registry/steps.json +20 -0
  234. package/dist/registry/switch.json +20 -0
  235. package/dist/registry/table-of-contents.json +23 -0
  236. package/dist/registry/table.json +17 -0
  237. package/dist/registry/tabs.json +20 -0
  238. package/dist/registry/textarea.json +17 -0
  239. package/dist/registry/theme-manager.json +39 -0
  240. package/dist/registry/timeline.json +18 -0
  241. package/dist/registry/titlebar-tabs.json +24 -0
  242. package/dist/registry/titlebar.json +26 -0
  243. package/dist/registry/toast.json +20 -0
  244. package/dist/registry/toggle-group.json +21 -0
  245. package/dist/registry/toggle.json +18 -0
  246. package/dist/registry/tooltip.json +18 -0
  247. package/dist/registry/updater-modal.json +26 -0
  248. package/dist/server/index.d.ts +18 -0
  249. package/dist/server/index.d.ts.map +1 -0
  250. package/dist/server/index.js +526 -0
  251. package/dist/server/index.js.map +1 -0
  252. package/dist/server/plugin.d.ts +10 -0
  253. package/dist/server/plugin.d.ts.map +1 -0
  254. package/dist/server/plugin.js +271 -0
  255. package/dist/server/plugin.js.map +1 -0
  256. package/dist/themes/default/content/breadcrumbs.d.ts +4 -0
  257. package/dist/themes/default/content/breadcrumbs.d.ts.map +1 -0
  258. package/dist/themes/default/content/breadcrumbs.jsx +31 -0
  259. package/dist/themes/default/content/breadcrumbs.jsx.map +1 -0
  260. package/dist/themes/default/content/pager.d.ts +5 -0
  261. package/dist/themes/default/content/pager.d.ts.map +1 -0
  262. package/dist/themes/default/content/pager.jsx +9 -0
  263. package/dist/themes/default/content/pager.jsx.map +1 -0
  264. package/dist/themes/default/content/table-of-contents.d.ts +4 -0
  265. package/dist/themes/default/content/table-of-contents.d.ts.map +1 -0
  266. package/dist/themes/default/content/table-of-contents.jsx +19 -0
  267. package/dist/themes/default/content/table-of-contents.jsx.map +1 -0
  268. package/dist/themes/default/index.d.ts +11 -0
  269. package/dist/themes/default/index.d.ts.map +1 -0
  270. package/dist/themes/default/index.js +30 -0
  271. package/dist/themes/default/index.js.map +1 -0
  272. package/dist/themes/default/layout.d.ts +4 -0
  273. package/dist/themes/default/layout.d.ts.map +1 -0
  274. package/dist/themes/default/layout.jsx +75 -0
  275. package/dist/themes/default/layout.jsx.map +1 -0
  276. package/dist/themes/default/navbar.d.ts +4 -0
  277. package/dist/themes/default/navbar.d.ts.map +1 -0
  278. package/dist/themes/default/navbar.jsx +74 -0
  279. package/dist/themes/default/navbar.jsx.map +1 -0
  280. package/dist/themes/default/navigation/mobile-table-of-contents.d.ts +4 -0
  281. package/dist/themes/default/navigation/mobile-table-of-contents.d.ts.map +1 -0
  282. package/dist/themes/default/navigation/mobile-table-of-contents.jsx +22 -0
  283. package/dist/themes/default/navigation/mobile-table-of-contents.jsx.map +1 -0
  284. package/dist/themes/default/navigation/sidebar-tree.d.ts +8 -0
  285. package/dist/themes/default/navigation/sidebar-tree.d.ts.map +1 -0
  286. package/dist/themes/default/navigation/sidebar-tree.jsx +100 -0
  287. package/dist/themes/default/navigation/sidebar-tree.jsx.map +1 -0
  288. package/dist/themes/default/overlays/search-dialog.d.ts +4 -0
  289. package/dist/themes/default/overlays/search-dialog.d.ts.map +1 -0
  290. package/dist/themes/default/overlays/search-dialog.jsx +34 -0
  291. package/dist/themes/default/overlays/search-dialog.jsx.map +1 -0
  292. package/dist/themes/default/sidebar.d.ts +4 -0
  293. package/dist/themes/default/sidebar.d.ts.map +1 -0
  294. package/dist/themes/default/sidebar.jsx +60 -0
  295. package/dist/themes/default/sidebar.jsx.map +1 -0
  296. package/dist/themes/index.d.ts +4 -0
  297. package/dist/themes/index.d.ts.map +1 -0
  298. package/dist/themes/index.js +5 -0
  299. package/dist/themes/index.js.map +1 -0
  300. package/dist/themes/types.d.ts +83 -0
  301. package/dist/themes/types.d.ts.map +1 -0
  302. package/dist/themes/types.js +2 -0
  303. package/dist/themes/types.js.map +1 -0
  304. package/dist/types.d.ts +97 -0
  305. package/dist/types.d.ts.map +1 -0
  306. package/dist/types.js +3 -0
  307. package/dist/types.js.map +1 -0
  308. package/dist/vendor/core-src/index.css +11 -0
  309. package/dist/vendor/core-src/index.ts +48 -0
  310. package/dist/vendor/core-src/lib/cn.ts +9 -0
  311. package/dist/vendor/core-src/registry/blocks/forgot-password-01.tsx +141 -0
  312. package/dist/vendor/core-src/registry/blocks/hero-01.tsx +28 -0
  313. package/dist/vendor/core-src/registry/blocks/login-01.tsx +231 -0
  314. package/dist/vendor/core-src/registry/blocks/otp-verification-01.tsx +170 -0
  315. package/dist/vendor/core-src/registry/blocks/register-01.tsx +318 -0
  316. package/dist/vendor/core-src/registry/components/ui/accordion.tsx +128 -0
  317. package/dist/vendor/core-src/registry/components/ui/alert.tsx +155 -0
  318. package/dist/vendor/core-src/registry/components/ui/api-table.tsx +117 -0
  319. package/dist/vendor/core-src/registry/components/ui/aspect-ratio.tsx +31 -0
  320. package/dist/vendor/core-src/registry/components/ui/avatar.tsx +114 -0
  321. package/dist/vendor/core-src/registry/components/ui/badge.tsx +56 -0
  322. package/dist/vendor/core-src/registry/components/ui/banner.tsx +187 -0
  323. package/dist/vendor/core-src/registry/components/ui/breadcrumb.tsx +136 -0
  324. package/dist/vendor/core-src/registry/components/ui/bubble.tsx +142 -0
  325. package/dist/vendor/core-src/registry/components/ui/button-group.tsx +42 -0
  326. package/dist/vendor/core-src/registry/components/ui/button.tsx +82 -0
  327. package/dist/vendor/core-src/registry/components/ui/callout.tsx +137 -0
  328. package/dist/vendor/core-src/registry/components/ui/card.tsx +113 -0
  329. package/dist/vendor/core-src/registry/components/ui/checkbox.tsx +74 -0
  330. package/dist/vendor/core-src/registry/components/ui/code-block.tsx +326 -0
  331. package/dist/vendor/core-src/registry/components/ui/code-group.tsx +105 -0
  332. package/dist/vendor/core-src/registry/components/ui/collapsible.tsx +73 -0
  333. package/dist/vendor/core-src/registry/components/ui/combobox.tsx +299 -0
  334. package/dist/vendor/core-src/registry/components/ui/command.tsx +378 -0
  335. package/dist/vendor/core-src/registry/components/ui/component-viewer.tsx +363 -0
  336. package/dist/vendor/core-src/registry/components/ui/container.tsx +45 -0
  337. package/dist/vendor/core-src/registry/components/ui/context-menu.tsx +237 -0
  338. package/dist/vendor/core-src/registry/components/ui/dialog.tsx +200 -0
  339. package/dist/vendor/core-src/registry/components/ui/dropdown-menu.tsx +298 -0
  340. package/dist/vendor/core-src/registry/components/ui/dropzone.tsx +189 -0
  341. package/dist/vendor/core-src/registry/components/ui/empty.tsx +83 -0
  342. package/dist/vendor/core-src/registry/components/ui/field.tsx +71 -0
  343. package/dist/vendor/core-src/registry/components/ui/file-tree.tsx +181 -0
  344. package/dist/vendor/core-src/registry/components/ui/footer.tsx +247 -0
  345. package/dist/vendor/core-src/registry/components/ui/form-message.tsx +36 -0
  346. package/dist/vendor/core-src/registry/components/ui/form.tsx +21 -0
  347. package/dist/vendor/core-src/registry/components/ui/hover-card.tsx +105 -0
  348. package/dist/vendor/core-src/registry/components/ui/icon-button.tsx +34 -0
  349. package/dist/vendor/core-src/registry/components/ui/input-group.tsx +80 -0
  350. package/dist/vendor/core-src/registry/components/ui/input.tsx +28 -0
  351. package/dist/vendor/core-src/registry/components/ui/kbd.tsx +73 -0
  352. package/dist/vendor/core-src/registry/components/ui/label.tsx +30 -0
  353. package/dist/vendor/core-src/registry/components/ui/list.tsx +222 -0
  354. package/dist/vendor/core-src/registry/components/ui/logo.tsx +68 -0
  355. package/dist/vendor/core-src/registry/components/ui/marker.tsx +102 -0
  356. package/dist/vendor/core-src/registry/components/ui/marquee.tsx +118 -0
  357. package/dist/vendor/core-src/registry/components/ui/message.tsx +168 -0
  358. package/dist/vendor/core-src/registry/components/ui/navbar.tsx +375 -0
  359. package/dist/vendor/core-src/registry/components/ui/navigation-menu.tsx +358 -0
  360. package/dist/vendor/core-src/registry/components/ui/number-input.tsx +150 -0
  361. package/dist/vendor/core-src/registry/components/ui/package-manager-tabs.tsx +226 -0
  362. package/dist/vendor/core-src/registry/components/ui/pager.tsx +102 -0
  363. package/dist/vendor/core-src/registry/components/ui/pagination.tsx +258 -0
  364. package/dist/vendor/core-src/registry/components/ui/pin-input.tsx +198 -0
  365. package/dist/vendor/core-src/registry/components/ui/popover.tsx +90 -0
  366. package/dist/vendor/core-src/registry/components/ui/progress.tsx +66 -0
  367. package/dist/vendor/core-src/registry/components/ui/radio-group.tsx +95 -0
  368. package/dist/vendor/core-src/registry/components/ui/rating.tsx +185 -0
  369. package/dist/vendor/core-src/registry/components/ui/resizable.tsx +195 -0
  370. package/dist/vendor/core-src/registry/components/ui/review-card.tsx +195 -0
  371. package/dist/vendor/core-src/registry/components/ui/rich-text-editor/bubble-menu.tsx +236 -0
  372. package/dist/vendor/core-src/registry/components/ui/rich-text-editor/editor.tsx +308 -0
  373. package/dist/vendor/core-src/registry/components/ui/rich-text-editor/extensions.ts +116 -0
  374. package/dist/vendor/core-src/registry/components/ui/rich-text-editor/footer.tsx +36 -0
  375. package/dist/vendor/core-src/registry/components/ui/rich-text-editor/image-dialog.tsx +75 -0
  376. package/dist/vendor/core-src/registry/components/ui/rich-text-editor/index.ts +11 -0
  377. package/dist/vendor/core-src/registry/components/ui/rich-text-editor/link-dialog.tsx +48 -0
  378. package/dist/vendor/core-src/registry/components/ui/rich-text-editor/markdown.ts +170 -0
  379. package/dist/vendor/core-src/registry/components/ui/rich-text-editor/slash-command.tsx +301 -0
  380. package/dist/vendor/core-src/registry/components/ui/rich-text-editor/table-controls.tsx +68 -0
  381. package/dist/vendor/core-src/registry/components/ui/rich-text-editor/toolbar.tsx +465 -0
  382. package/dist/vendor/core-src/registry/components/ui/rich-text-editor/use-editor.ts +231 -0
  383. package/dist/vendor/core-src/registry/components/ui/scroll-area.tsx +228 -0
  384. package/dist/vendor/core-src/registry/components/ui/section-heading.tsx +108 -0
  385. package/dist/vendor/core-src/registry/components/ui/select.tsx +156 -0
  386. package/dist/vendor/core-src/registry/components/ui/separator.tsx +37 -0
  387. package/dist/vendor/core-src/registry/components/ui/sheet.tsx +220 -0
  388. package/dist/vendor/core-src/registry/components/ui/sidebar.tsx +692 -0
  389. package/dist/vendor/core-src/registry/components/ui/skeleton.tsx +24 -0
  390. package/dist/vendor/core-src/registry/components/ui/slider.tsx +153 -0
  391. package/dist/vendor/core-src/registry/components/ui/spinner.tsx +41 -0
  392. package/dist/vendor/core-src/registry/components/ui/stat.tsx +245 -0
  393. package/dist/vendor/core-src/registry/components/ui/status.tsx +121 -0
  394. package/dist/vendor/core-src/registry/components/ui/steps.tsx +198 -0
  395. package/dist/vendor/core-src/registry/components/ui/switch.tsx +68 -0
  396. package/dist/vendor/core-src/registry/components/ui/table-of-contents.tsx +131 -0
  397. package/dist/vendor/core-src/registry/components/ui/table.tsx +160 -0
  398. package/dist/vendor/core-src/registry/components/ui/tabs.tsx +204 -0
  399. package/dist/vendor/core-src/registry/components/ui/textarea.tsx +82 -0
  400. package/dist/vendor/core-src/registry/components/ui/theme-toggle.tsx +238 -0
  401. package/dist/vendor/core-src/registry/components/ui/timeline.tsx +350 -0
  402. package/dist/vendor/core-src/registry/components/ui/titlebar-tabs.tsx +773 -0
  403. package/dist/vendor/core-src/registry/components/ui/titlebar.tsx +468 -0
  404. package/dist/vendor/core-src/registry/components/ui/toast.tsx +124 -0
  405. package/dist/vendor/core-src/registry/components/ui/toggle-group.tsx +203 -0
  406. package/dist/vendor/core-src/registry/components/ui/toggle.tsx +101 -0
  407. package/dist/vendor/core-src/registry/components/ui/tooltip.tsx +41 -0
  408. package/dist/vendor/core-src/registry/components/ui/updater-modal.tsx +254 -0
  409. package/dist/vendor/core-src/registry/index.ts +50 -0
  410. package/dist/vendor/core-src/registry/metadata.ts +742 -0
  411. package/dist/vendor/core-src/registry/providers/theme-provider.tsx +216 -0
  412. package/dist/vendor/core-src/registry/providers/theme-script.tsx +78 -0
  413. package/dist/vendor/core-src/registry/providers/theme-transitions.ts +139 -0
  414. package/dist/vendor/hooks-src/create-active-element.ts +51 -0
  415. package/dist/vendor/hooks-src/create-app-updater.ts +272 -0
  416. package/dist/vendor/hooks-src/create-audio-video.ts +444 -0
  417. package/dist/vendor/hooks-src/create-battery.ts +78 -0
  418. package/dist/vendor/hooks-src/create-chat-scroll.ts +113 -0
  419. package/dist/vendor/hooks-src/create-click-outside.ts +82 -0
  420. package/dist/vendor/hooks-src/create-clipboard.ts +69 -0
  421. package/dist/vendor/hooks-src/create-color-mode.ts +115 -0
  422. package/dist/vendor/hooks-src/create-controllable-signal.ts +56 -0
  423. package/dist/vendor/hooks-src/create-debounce.ts +90 -0
  424. package/dist/vendor/hooks-src/create-disclosure.ts +82 -0
  425. package/dist/vendor/hooks-src/create-document-tabs.ts +267 -0
  426. package/dist/vendor/hooks-src/create-document-title.ts +33 -0
  427. package/dist/vendor/hooks-src/create-drop-zone.ts +317 -0
  428. package/dist/vendor/hooks-src/create-event-source.ts +126 -0
  429. package/dist/vendor/hooks-src/create-favicon.ts +52 -0
  430. package/dist/vendor/hooks-src/create-fetch.ts +93 -0
  431. package/dist/vendor/hooks-src/create-focus-trap.ts +94 -0
  432. package/dist/vendor/hooks-src/create-form.ts +200 -0
  433. package/dist/vendor/hooks-src/create-fullscreen.ts +105 -0
  434. package/dist/vendor/hooks-src/create-geolocation.ts +140 -0
  435. package/dist/vendor/hooks-src/create-global-shortcut.ts +140 -0
  436. package/dist/vendor/hooks-src/create-hover.ts +88 -0
  437. package/dist/vendor/hooks-src/create-idle.ts +109 -0
  438. package/dist/vendor/hooks-src/create-infinite-scroll.ts +90 -0
  439. package/dist/vendor/hooks-src/create-input-mask.ts +109 -0
  440. package/dist/vendor/hooks-src/create-intersection-observer.ts +82 -0
  441. package/dist/vendor/hooks-src/create-keybindings.ts +148 -0
  442. package/dist/vendor/hooks-src/create-lock-scroll.ts +97 -0
  443. package/dist/vendor/hooks-src/create-long-press.ts +85 -0
  444. package/dist/vendor/hooks-src/create-media-query.ts +94 -0
  445. package/dist/vendor/hooks-src/create-mouse-position.ts +87 -0
  446. package/dist/vendor/hooks-src/create-network-status.ts +100 -0
  447. package/dist/vendor/hooks-src/create-orientation.ts +129 -0
  448. package/dist/vendor/hooks-src/create-pagination.ts +208 -0
  449. package/dist/vendor/hooks-src/create-permission.ts +91 -0
  450. package/dist/vendor/hooks-src/create-previous.ts +21 -0
  451. package/dist/vendor/hooks-src/create-resize-observer.ts +89 -0
  452. package/dist/vendor/hooks-src/create-scroll-into-view.ts +57 -0
  453. package/dist/vendor/hooks-src/create-scroll-position.ts +129 -0
  454. package/dist/vendor/hooks-src/create-storage.ts +106 -0
  455. package/dist/vendor/hooks-src/create-tauri-window.ts +628 -0
  456. package/dist/vendor/hooks-src/create-timer.ts +150 -0
  457. package/dist/vendor/hooks-src/create-tiptap-editor.ts +326 -0
  458. package/dist/vendor/hooks-src/create-undo-redo.ts +100 -0
  459. package/dist/vendor/hooks-src/create-web-notification.ts +111 -0
  460. package/dist/vendor/hooks-src/create-websocket.ts +179 -0
  461. package/dist/vendor/hooks-src/create-window-size.ts +41 -0
  462. package/dist/vendor/hooks-src/index.ts +48 -0
  463. package/package.json +87 -0
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-form",
3
+ "title": "createForm",
4
+ "description": "SolidJS reactive primitive for form state management, field validation, errors, and submission",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-form.ts",
9
+ "content": "import { createSignal, createMemo, type Accessor } from \"solid-js\";\n\nexport type FormErrors<T> = Partial<Record<keyof T, string>>;\nexport type FormTouched<T> = Partial<Record<keyof T, boolean>>;\nexport type ValidateOn = \"change\" | \"blur\" | \"submit\";\n\nexport type FormInputEvent = Event & {\n currentTarget:\n | HTMLInputElement\n | HTMLTextAreaElement\n | HTMLSelectElement;\n};\n\nexport interface CreateFormOptions<T extends Record<string, any>> {\n /** Initial form field values object */\n initialValues: T;\n /** Custom validation function returning error messages object */\n validate?: (values: T) => FormErrors<T> | Promise<FormErrors<T>>;\n /** Event that triggers validation. Defaults to \"change\". */\n validateOn?: ValidateOn;\n /** Submit handler callback invoked when validation succeeds */\n onSubmit?: (values: T) => void | Promise<void>;\n}\n\nexport interface CreateFormReturn<T extends Record<string, any>> {\n /** Accessor for current form field values */\n values: Accessor<T>;\n /** Accessor for form field validation error messages */\n errors: Accessor<FormErrors<T>>;\n /** Accessor for form field touched states */\n touched: Accessor<FormTouched<T>>;\n /** Accessor indicating if form is currently submitting */\n isSubmitting: Accessor<boolean>;\n /** Accessor containing an exception thrown by onSubmit, if any */\n submitError: Accessor<unknown>;\n /** Accessor indicating if form has zero validation errors */\n isValid: Accessor<boolean>;\n /** Accessor indicating if form values differ from initial values */\n isDirty: Accessor<boolean>;\n /** Update specific form field value */\n setFieldValue: <K extends keyof T>(field: K, value: T[K]) => void;\n /** Set specific form field validation error */\n setFieldError: <K extends keyof T>(field: K, error: string | undefined) => void;\n /** Set specific form field touched state */\n setFieldTouched: <K extends keyof T>(field: K, isTouched?: boolean) => void;\n /** Input change event listener helper factory function */\n handleChange: <K extends keyof T>(field: K) => (e: FormInputEvent) => void;\n /** Input blur event listener helper factory function */\n handleBlur: <K extends keyof T>(field: K) => () => void;\n /** Form onSubmit event handler */\n handleSubmit: (e?: Event) => Promise<void>;\n /** Reset form values, errors, and touched states to initial values */\n resetForm: () => void;\n}\n\n/**\n * SolidJS reactive primitive for managing form field state, validation, errors, and submission.\n *\n * @param options Form configuration options including initialValues and validate function.\n */\nexport function createForm<T extends Record<string, any>>(\n options: CreateFormOptions<T>\n): CreateFormReturn<T> {\n const initialValues = { ...options.initialValues };\n\n const [values, setValues] = createSignal<T>({ ...initialValues });\n const [errors, setErrors] = createSignal<FormErrors<T>>({});\n const [touched, setTouched] = createSignal<FormTouched<T>>({});\n const [isSubmitting, setIsSubmitting] = createSignal(false);\n const [submitError, setSubmitError] = createSignal<unknown>();\n const validateOn = options.validateOn ?? \"change\";\n let validationSequence = 0;\n\n const isDirty = createMemo(() => {\n const current = values();\n return Object.keys(initialValues).some((key) => current[key] !== initialValues[key]);\n });\n\n const isValid = createMemo(() => {\n const errs = errors();\n return Object.keys(errs).length === 0;\n });\n\n const runValidation = async (currentValues: T): Promise<FormErrors<T>> => {\n const sequence = ++validationSequence;\n if (!options.validate) {\n setErrors(() => ({}));\n return {};\n }\n\n const result = await options.validate(currentValues);\n const newErrors = result || {};\n // Ignore stale async validations that finished after a newer request.\n if (sequence === validationSequence) {\n setErrors(() => newErrors);\n }\n return newErrors;\n };\n\n const setFieldValue = <K extends keyof T>(field: K, value: T[K]) => {\n const next = { ...values(), [field]: value };\n setValues(() => next);\n if (validateOn === \"change\") {\n void runValidation(next);\n }\n };\n\n const setFieldError = <K extends keyof T>(field: K, error: string | undefined) => {\n setErrors((prev) => {\n const next = { ...prev };\n if (error) next[field] = error;\n else delete next[field];\n return next;\n });\n };\n\n const setFieldTouched = <K extends keyof T>(field: K, isTouched = true) => {\n setTouched((prev) => ({ ...prev, [field]: isTouched }));\n };\n\n const handleChange = <K extends keyof T>(field: K) => {\n return (e: FormInputEvent) => {\n const target = e.currentTarget;\n let value: unknown = target.value;\n\n if (\n \"checked\" in target &&\n ((target as HTMLInputElement).type === \"checkbox\" ||\n (target as HTMLInputElement).type === \"radio\")\n ) {\n value = (target as HTMLInputElement).checked;\n } else if (\"selectedOptions\" in target && target.multiple) {\n value = Array.from(target.selectedOptions, (option) => option.value);\n }\n\n setFieldValue(field, value as T[K]);\n };\n };\n\n const handleBlur = <K extends keyof T>(field: K) => {\n return () => {\n setFieldTouched(field, true);\n if (validateOn === \"blur\") {\n void runValidation(values());\n }\n };\n };\n\n const handleSubmit = async (e?: Event) => {\n e?.preventDefault();\n setIsSubmitting(true);\n setSubmitError(undefined);\n\n // Mark all fields as touched on submit\n const allTouched = Object.keys(values()).reduce((acc, key) => {\n acc[key as keyof T] = true;\n return acc;\n }, {} as FormTouched<T>);\n setTouched(() => allTouched);\n\n try {\n const validationErrors = await runValidation(values());\n if (Object.keys(validationErrors).length === 0 && options.onSubmit) {\n try {\n await options.onSubmit(values());\n } catch (error) {\n setSubmitError(() => error);\n }\n }\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const resetForm = () => {\n setValues(() => ({ ...initialValues }));\n setErrors(() => ({}));\n setTouched(() => ({}));\n setIsSubmitting(false);\n setSubmitError(undefined);\n validationSequence += 1;\n };\n\n return {\n values,\n errors,\n touched,\n isSubmitting,\n submitError,\n isValid,\n isDirty,\n setFieldValue,\n setFieldError,\n setFieldTouched,\n handleChange,\n handleBlur,\n handleSubmit,\n resetForm,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-fullscreen",
3
+ "title": "createFullscreen",
4
+ "description": "SolidJS reactive primitive for requesting and monitoring element or document fullscreen status",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-fullscreen.ts",
9
+ "content": "import { createSignal, createEffect, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateFullscreenOptions {\n /** Target element accessor or reference. Defaults to document.documentElement. */\n target?: HTMLElement | Accessor<HTMLElement | undefined>;\n /** Callback fired when entering fullscreen mode. */\n onEnter?: () => void;\n /** Callback fired when exiting fullscreen mode. */\n onExit?: () => void;\n /** Callback fired when fullscreen request fails. */\n onError?: (err: Event) => void;\n}\n\nexport interface CreateFullscreenReturn {\n /** Signal indicating whether fullscreen mode is currently active. */\n isFullscreen: Accessor<boolean>;\n /** Request full screen mode for target element. */\n enter: () => Promise<void>;\n /** Exit full screen mode. */\n exit: () => Promise<void>;\n /** Toggle full screen mode. */\n toggle: () => Promise<void>;\n}\n\n/**\n * SolidJS reactive primitive for requesting and monitoring element fullscreen status.\n */\nexport function createFullscreen(\n options: CreateFullscreenOptions = {}\n): CreateFullscreenReturn {\n const [isFullscreen, setIsFullscreen] = createSignal(false);\n\n const getTarget = (): HTMLElement | undefined => {\n if (typeof document === \"undefined\") return undefined;\n if (typeof options.target === \"function\") {\n return options.target() ?? document.documentElement;\n }\n return options.target ?? document.documentElement;\n };\n\n const updateStatus = (): void => {\n if (typeof document === \"undefined\") return;\n const activeEl = document.fullscreenElement;\n const isTargetFullscreen = Boolean(activeEl && activeEl === getTarget());\n setIsFullscreen(isTargetFullscreen);\n };\n\n const enter = async (): Promise<void> => {\n if (typeof window === \"undefined\") return;\n const el = getTarget();\n if (el?.requestFullscreen) {\n await el.requestFullscreen();\n }\n };\n\n const exit = async (): Promise<void> => {\n if (typeof document === \"undefined\") return;\n if (document.fullscreenElement && document.exitFullscreen) {\n await document.exitFullscreen();\n }\n };\n\n const toggle = async (): Promise<void> => {\n if (isFullscreen()) {\n await exit();\n } else {\n await enter();\n }\n };\n\n createEffect(() => {\n if (typeof document === \"undefined\") return;\n\n const handleFullscreenChange = (): void => {\n const activeEl = document.fullscreenElement;\n const isTarget = Boolean(activeEl && activeEl === getTarget());\n setIsFullscreen(isTarget);\n\n if (isTarget) {\n options.onEnter?.();\n } else {\n options.onExit?.();\n }\n };\n\n const handleFullscreenError = (err: Event): void => {\n options.onError?.(err);\n };\n\n document.addEventListener(\"fullscreenchange\", handleFullscreenChange);\n document.addEventListener(\"fullscreenerror\", handleFullscreenError);\n\n onCleanup(() => {\n document.removeEventListener(\"fullscreenchange\", handleFullscreenChange);\n document.removeEventListener(\"fullscreenerror\", handleFullscreenError);\n });\n });\n\n return {\n isFullscreen,\n enter,\n exit,\n toggle,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-geolocation",
3
+ "title": "createGeolocation",
4
+ "description": "SolidJS reactive primitive for tracking browser Geolocation position, coordinates, speed, and GPS accuracy",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-geolocation.ts",
9
+ "content": "import { createSignal, createEffect, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateGeolocationOptions extends PositionOptions {\n /** Whether to start watching position immediately upon primitive initialization. Defaults to true. */\n immediate?: boolean;\n}\n\nexport interface GeolocationState {\n /** Current latitude coordinate in degrees. */\n latitude: number | null;\n /** Current longitude coordinate in degrees. */\n longitude: number | null;\n /** Current altitude above sea level in meters. */\n altitude: number | null;\n /** Position accuracy level in meters. */\n accuracy: number | null;\n /** Altitude accuracy in meters. */\n altitudeAccuracy: number | null;\n /** Current heading direction in degrees relative to true north. */\n heading: number | null;\n /** Current speed in meters per second. */\n speed: number | null;\n /** Timestamp when location was captured. */\n timestamp: number | null;\n}\n\nexport interface CreateGeolocationReturn {\n /** Signal accessor containing current geolocation coordinates and metrics. */\n coords: Accessor<GeolocationState>;\n /** Signal accessor indicating whether position retrieval is in progress. */\n loading: Accessor<boolean>;\n /** Signal accessor containing GeolocationPositionError if request failed. */\n error: Accessor<GeolocationPositionError | Error | null>;\n /** Signal accessor indicating whether Geolocation API is supported in browser environment. */\n isSupported: Accessor<boolean>;\n /** Imperative function to fetch current position once. */\n getCurrentPosition: () => void;\n}\n\nconst initialCoords: GeolocationState = {\n latitude: null,\n longitude: null,\n altitude: null,\n accuracy: null,\n altitudeAccuracy: null,\n heading: null,\n speed: null,\n timestamp: null,\n};\n\n/**\n * SolidJS reactive primitive for tracking user geographic location and GPS metrics.\n */\nexport function createGeolocation(\n options: CreateGeolocationOptions = {}\n): CreateGeolocationReturn {\n const [coords, setCoords] = createSignal<GeolocationState>(initialCoords);\n const [loading, setLoading] = createSignal(false);\n const [error, setError] = createSignal<GeolocationPositionError | Error | null>(null);\n\n const isSupported = (): boolean => {\n if (typeof window === \"undefined\" || typeof navigator === \"undefined\") return false;\n return \"geolocation\" in navigator;\n };\n\n const getOptions = (): PositionOptions => ({\n timeout: options.timeout ?? 10000,\n maximumAge: options.maximumAge ?? 5000,\n enableHighAccuracy: options.enableHighAccuracy ?? false,\n });\n\n let watchId: number | null = null;\n\n const updatePosition = (position: GeolocationPosition): void => {\n setCoords({\n latitude: position.coords.latitude,\n longitude: position.coords.longitude,\n altitude: position.coords.altitude,\n accuracy: position.coords.accuracy,\n altitudeAccuracy: position.coords.altitudeAccuracy,\n heading: position.coords.heading,\n speed: position.coords.speed,\n timestamp: position.timestamp,\n });\n setLoading(false);\n setError(null);\n };\n\n const handleError = (err: GeolocationPositionError): void => {\n setError(err);\n setLoading(false);\n };\n\n const getCurrentPosition = (): void => {\n if (!isSupported()) {\n setError(new Error(\"Geolocation API is not supported in this browser environment.\"));\n return;\n }\n\n setLoading(true);\n setError(null);\n\n navigator.geolocation.getCurrentPosition(\n (pos) => {\n updatePosition(pos);\n },\n (err) => {\n handleError(err);\n },\n getOptions()\n );\n };\n\n createEffect(() => {\n if (!isSupported()) return;\n\n if (options.immediate ?? true) {\n setLoading(true);\n watchId = navigator.geolocation.watchPosition(\n updatePosition,\n handleError,\n getOptions()\n );\n }\n\n onCleanup(() => {\n if (watchId !== null && typeof window !== \"undefined\" && \"geolocation\" in navigator) {\n navigator.geolocation.clearWatch(watchId);\n }\n });\n });\n\n return {\n coords,\n loading,\n error,\n isSupported,\n getCurrentPosition,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-global-shortcut",
3
+ "title": "createGlobalShortcut",
4
+ "description": "SolidJS reactive primitive for registering OS-level global keyboard shortcuts via Tauri v2 plugin",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-global-shortcut.ts",
9
+ "content": "import { onMount, onCleanup } from \"solid-js\";\nimport { isTauriEnvironment } from \"./create-tauri-window\";\n\nexport interface CreateGlobalShortcutOptions {\n /** Shortcut key combination, e.g. 'CommandOrControl+Shift+P' or 'Alt+Space'. */\n shortcut: string | string[];\n /** Handler invoked when global shortcut is triggered. */\n onTrigger: (shortcut: string) => void;\n /** Automatically register shortcut on mount. Defaults to true. */\n autoRegister?: boolean;\n}\n\nexport interface CreateGlobalShortcutReturn {\n /** Register the configured global shortcut with Tauri / Browser. */\n register: () => Promise<boolean>;\n /** Unregister the shortcut. */\n unregister: () => Promise<void>;\n /** Indicates whether the shortcut is currently registered. */\n isRegistered: () => boolean;\n}\n\n/**\n * SolidJS reactive primitive for registering native OS global hotkeys via Tauri v2 global shortcut plugin,\n * with automatic fallback to browser DOM keyboard listeners.\n */\nexport function createGlobalShortcut(\n options: CreateGlobalShortcutOptions\n): CreateGlobalShortcutReturn {\n let registered = false;\n const shortcuts = Array.isArray(options.shortcut) ? options.shortcut : [options.shortcut];\n\n const getTauriShortcutPlugin = async () => {\n if (typeof window === \"undefined\") return null;\n try {\n if ((window as any).__TAURI__?.globalShortcut) {\n return (window as any).__TAURI__.globalShortcut;\n }\n // Variable prevents Vite dev server from static module resolution\n const moduleName = \"@tauri-apps/plugin-global-shortcut\";\n // @ts-ignore - Optional runtime dependency in Tauri apps\n const plugin = await import(/* @vite-ignore */ moduleName).catch(() => null);\n return plugin;\n } catch {\n return null;\n }\n };\n\n const normalizeKey = (k: string) => {\n const key = k.toLowerCase().trim();\n if (key === \" \" || key === \"space\" || key === \"spacebar\") return \"space\";\n if (key === \"esc\" || key === \"escape\") return \"escape\";\n if (key === \"return\" || key === \"enter\") return \"enter\";\n return key;\n };\n\n const handleBrowserKeydown = (e: KeyboardEvent) => {\n const eventKey = normalizeKey(e.key);\n const eventCode = normalizeKey(e.code.replace(/^Key|^Digit/, \"\"));\n\n for (const keyCombo of shortcuts) {\n const parts = keyCombo.toLowerCase().split(\"+\");\n const hasCtrlOrCmd = parts.includes(\"commandorcontrol\") || parts.includes(\"ctrl\") || parts.includes(\"cmd\");\n const hasShift = parts.includes(\"shift\");\n const hasAlt = parts.includes(\"alt\");\n const mainKey = parts.find((p) => ![\"commandorcontrol\", \"ctrl\", \"cmd\", \"shift\", \"alt\"].includes(p));\n\n const ctrlMatch = hasCtrlOrCmd ? e.ctrlKey || e.metaKey : !e.ctrlKey && !e.metaKey;\n const shiftMatch = hasShift ? e.shiftKey : !e.shiftKey;\n const altMatch = hasAlt ? e.altKey : !e.altKey;\n const keyMatch = mainKey\n ? eventKey === normalizeKey(mainKey) || eventCode === normalizeKey(mainKey)\n : false;\n\n if (ctrlMatch && shiftMatch && altMatch && keyMatch) {\n e.preventDefault();\n options.onTrigger(keyCombo);\n break;\n }\n }\n };\n\n const register = async (): Promise<boolean> => {\n if (typeof window === \"undefined\") return false;\n\n if (isTauriEnvironment()) {\n const plugin = await getTauriShortcutPlugin();\n if (plugin?.register) {\n try {\n for (const s of shortcuts) {\n await plugin.register(s, () => options.onTrigger(s));\n }\n registered = true;\n return true;\n } catch {\n // Fallback to web listener\n }\n }\n }\n\n window.addEventListener(\"keydown\", handleBrowserKeydown);\n registered = true;\n return true;\n };\n\n const unregister = async (): Promise<void> => {\n if (typeof window === \"undefined\") return;\n\n if (isTauriEnvironment()) {\n const plugin = await getTauriShortcutPlugin();\n if (plugin?.unregister) {\n try {\n for (const s of shortcuts) {\n await plugin.unregister(s);\n }\n } catch {\n // Handled\n }\n }\n }\n\n window.removeEventListener(\"keydown\", handleBrowserKeydown);\n registered = false;\n };\n\n onMount(() => {\n if (options.autoRegister !== false) {\n register();\n }\n });\n\n onCleanup(() => {\n unregister();\n });\n\n return {\n register,\n unregister,\n isRegistered: () => registered,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-hover",
3
+ "title": "createHover",
4
+ "description": "SolidJS reactive primitive for tracking element hover state with entrance and exit delays",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-hover.ts",
9
+ "content": "import { createSignal, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateHoverOptions {\n /** Delay in milliseconds before setting hover state to true */\n delayEnter?: number;\n /** Delay in milliseconds before setting hover state to false */\n delayLeave?: number;\n /** Callback fired when hover state transitions to true */\n onHoverStart?: () => void;\n /** Callback fired when hover state transitions to false */\n onHoverEnd?: () => void;\n}\n\nexport interface CreateHoverReturn {\n /** Accessor indicating whether target element is hovered */\n isHovered: Accessor<boolean>;\n /** Event listeners props to spread onto target JSX element */\n props: {\n onPointerEnter: (e: PointerEvent) => void;\n onPointerLeave: (e: PointerEvent) => void;\n };\n}\n\n/**\n * SolidJS reactive primitive for tracking element hover state with optional entrance/exit delays.\n *\n * @param options Configuration options for hover delays and callbacks.\n */\nexport function createHover(options: CreateHoverOptions = {}): CreateHoverReturn {\n const [isHovered, setIsHovered] = createSignal(false);\n let enterTimer: ReturnType<typeof setTimeout> | undefined;\n let leaveTimer: ReturnType<typeof setTimeout> | undefined;\n\n const clearTimers = () => {\n if (enterTimer) {\n clearTimeout(enterTimer);\n enterTimer = undefined;\n }\n if (leaveTimer) {\n clearTimeout(leaveTimer);\n leaveTimer = undefined;\n }\n };\n\n const onPointerEnter = () => {\n clearTimers();\n const delay = options.delayEnter ?? 0;\n\n if (delay > 0) {\n enterTimer = setTimeout(() => {\n setIsHovered(true);\n options.onHoverStart?.();\n enterTimer = undefined;\n }, delay);\n } else {\n setIsHovered(true);\n options.onHoverStart?.();\n }\n };\n\n const onPointerLeave = () => {\n clearTimers();\n const delay = options.delayLeave ?? 0;\n\n if (delay > 0) {\n leaveTimer = setTimeout(() => {\n setIsHovered(false);\n options.onHoverEnd?.();\n leaveTimer = undefined;\n }, delay);\n } else {\n setIsHovered(false);\n options.onHoverEnd?.();\n }\n };\n\n onCleanup(() => {\n clearTimers();\n });\n\n return {\n isHovered,\n props: {\n onPointerEnter,\n onPointerLeave,\n },\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-idle",
3
+ "title": "createIdle",
4
+ "description": "SolidJS reactive primitive for detecting user inactivity with customizable timeout",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-idle.ts",
9
+ "content": "import { createSignal, onMount, onCleanup, type Accessor } from \"solid-js\";\nimport { isServer } from \"solid-js/web\";\n\nexport interface CreateIdleOptions {\n /** Timeout in milliseconds before user is considered idle (default: 60000 = 60s) */\n timeout?: number;\n /** Initial idle state (default: false) */\n initialState?: boolean;\n /** DOM events to listen for user activity */\n events?: string[];\n /** Callback fired when user becomes idle */\n onIdle?: () => void;\n /** Callback fired when user becomes active after being idle */\n onActive?: () => void;\n}\n\nexport interface CreateIdleReturn {\n /** Accessor returning true if user has been inactive for timeout period */\n isIdle: Accessor<boolean>;\n /** Accessor returning timestamp in ms of last detected user interaction */\n lastActive: Accessor<number>;\n /** Reset idle state and restart timer */\n reset: () => void;\n}\n\nconst DEFAULT_EVENTS = [\n \"mousemove\",\n \"mousedown\",\n \"keydown\",\n \"touchstart\",\n \"scroll\",\n \"wheel\",\n];\n\n/**\n * SolidJS reactive primitive for detecting user inactivity (idle state) with customizable timeout and event triggers.\n *\n * @param options Configuration options including timeout in ms and event handlers.\n */\nexport function createIdle(options: CreateIdleOptions = {}): CreateIdleReturn {\n const initialIdle = options.initialState ?? false;\n\n // SSR: return static defaults — no timers, no listeners\n if (isServer) {\n const [isIdle] = createSignal(initialIdle);\n const [lastActive] = createSignal(0);\n return { isIdle, lastActive, reset: () => {} };\n }\n\n const timeoutMs = options.timeout ?? 60000;\n const events = options.events ?? DEFAULT_EVENTS;\n\n const [isIdle, setIsIdle] = createSignal(initialIdle);\n const [lastActive, setLastActive] = createSignal(0);\n\n let timerId: any = null;\n\n const startTimer = () => {\n if (timerId) clearTimeout(timerId);\n timerId = setTimeout(() => {\n if (!isIdle()) {\n setIsIdle(true);\n if (options.onIdle) options.onIdle();\n }\n }, timeoutMs);\n };\n\n const handleUserActivity = () => {\n setLastActive(Date.now());\n\n if (isIdle()) {\n setIsIdle(false);\n if (options.onActive) options.onActive();\n }\n\n startTimer();\n };\n\n const reset = () => {\n setLastActive(Date.now());\n setIsIdle(false);\n startTimer();\n };\n\n onMount(() => {\n // Set initial lastActive to now on client mount\n setLastActive(Date.now());\n\n events.forEach((evt) => {\n window.addEventListener(evt, handleUserActivity, { passive: true });\n });\n\n startTimer();\n\n onCleanup(() => {\n if (timerId) clearTimeout(timerId);\n events.forEach((evt) => {\n window.removeEventListener(evt, handleUserActivity);\n });\n });\n });\n\n return {\n isIdle,\n lastActive,\n reset,\n };\n}\n\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-infinite-scroll",
3
+ "title": "createInfiniteScroll",
4
+ "description": "SolidJS reactive primitive for dynamic infinite scrolling, auto-fetching pages, and scroll pagination",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-infinite-scroll.ts",
9
+ "content": "import { createSignal, createEffect, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateInfiniteScrollOptions {\n /** Target element to observe or trigger scroll on. Defaults to document / scroll parent. */\n target?: HTMLElement | Accessor<HTMLElement | undefined>;\n /** Distance threshold from bottom in pixels to trigger fetch. Defaults to 100. */\n threshold?: number;\n /** Whether loading is currently enabled or auto-fetching is active. Defaults to true. */\n enabled?: boolean | Accessor<boolean>;\n /** Callback function when scrolled near bottom to fetch next items. */\n onLoadMore: () => Promise<void> | void;\n}\n\nexport interface CreateInfiniteScrollReturn {\n /** Sentinel ref function to bind to a DOM element at the bottom of the list. */\n setSentinelRef: (el: HTMLElement | null) => void;\n /** Signal indicating whether fetching is currently in progress. */\n isLoading: Accessor<boolean>;\n /** Signal indicating whether an error occurred during last fetch. */\n error: Accessor<Error | null>;\n /** Imperative function to manually trigger next page load. */\n loadMore: () => Promise<void>;\n}\n\n/**\n * SolidJS reactive primitive for dynamic infinite scrolling / auto-fetching.\n */\nexport function createInfiniteScroll(\n options: CreateInfiniteScrollOptions\n): CreateInfiniteScrollReturn {\n const [sentinelEl, setSentinelEl] = createSignal<HTMLElement | null>(null);\n const [isLoading, setIsLoading] = createSignal(false);\n const [error, setError] = createSignal<Error | null>(null);\n\n const isEnabled = (): boolean => {\n if (typeof options.enabled === \"function\") {\n return options.enabled();\n }\n return options.enabled ?? true;\n };\n\n const loadMore = async (): Promise<void> => {\n if (isLoading() || !isEnabled()) return;\n setIsLoading(true);\n setError(null);\n try {\n await options.onLoadMore();\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setIsLoading(false);\n }\n };\n\n createEffect(() => {\n if (typeof window === \"undefined\" || !window.IntersectionObserver) return;\n if (!isEnabled()) return;\n\n const el = sentinelEl();\n if (!el) return;\n\n const rootMargin = `${options.threshold ?? 100}px`;\n\n const observer = new IntersectionObserver(\n (entries) => {\n const entry = entries[0];\n if (entry?.isIntersecting && !isLoading()) {\n loadMore();\n }\n },\n {\n rootMargin,\n threshold: 0,\n }\n );\n\n observer.observe(el);\n\n onCleanup(() => {\n observer.disconnect();\n });\n });\n\n return {\n setSentinelRef: setSentinelEl,\n isLoading,\n error,\n loadMore,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-input-mask",
3
+ "title": "createInputMask",
4
+ "description": "SolidJS reactive primitive for input value masking (phone numbers, credit cards, dates)",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-input-mask.ts",
9
+ "content": "import { createSignal, type Accessor } from \"solid-js\";\n\nexport interface CreateInputMaskOptions {\n /** Mask pattern template (e.g. '+995 ### ##-##-##' or '#### #### #### ####') */\n mask: string;\n /** Initial default value */\n defaultValue?: string;\n}\n\nexport interface CreateInputMaskReturn {\n /** Accessor returning formatted masked input string */\n value: Accessor<string>;\n /** Accessor returning raw unmasked user digits string */\n unmaskedValue: Accessor<string>;\n /** Function to programmatically update input value */\n setValue: (val: string) => void;\n /** JSX props object to spread onto target HTMLInputElement */\n props: {\n value: () => string;\n onInput: (e: Event & { currentTarget: HTMLInputElement }) => void;\n };\n}\n\n/**\n * Format raw unmasked text against a mask pattern template.\n */\nexport function formatMask(rawInput: string, pattern: string): { masked: string; unmasked: string } {\n if (!rawInput) {\n return { masked: \"\", unmasked: \"\" };\n }\n\n // Find static prefix of pattern before first slot placeholder (#, 0, X)\n let staticPrefix = \"\";\n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i];\n if (char === \"#\" || char === \"0\" || char === \"X\") break;\n staticPrefix += char;\n }\n const staticPrefixDigits = staticPrefix.replace(/\\D/g, \"\");\n\n let digits = rawInput.replace(/\\D/g, \"\");\n\n // Strip static prefix digits if present at the start\n if (staticPrefixDigits && digits.startsWith(staticPrefixDigits)) {\n digits = digits.slice(staticPrefixDigits.length);\n }\n\n if (!digits) {\n return { masked: \"\", unmasked: \"\" };\n }\n\n let masked = \"\";\n let digitIndex = 0;\n\n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i];\n if (char === \"#\" || char === \"0\" || char === \"X\") {\n if (digitIndex < digits.length) {\n masked += digits[digitIndex++];\n } else {\n break;\n }\n } else {\n if (digitIndex < digits.length) {\n masked += char;\n } else {\n break;\n }\n }\n }\n\n return { masked, unmasked: digits };\n}\n\n/**\n * SolidJS reactive primitive for input value masking (phone numbers, credit cards, dates).\n *\n * @param options Configuration options including mask pattern.\n */\nexport function createInputMask(options: CreateInputMaskOptions): CreateInputMaskReturn {\n const initial = formatMask(options.defaultValue || \"\", options.mask);\n const [value, setFormattedValue] = createSignal(initial.masked);\n const [unmaskedValue, setRawValue] = createSignal(initial.unmasked);\n\n const setValue = (newVal: string) => {\n const formatted = formatMask(newVal, options.mask);\n setFormattedValue(formatted.masked);\n setRawValue(formatted.unmasked);\n };\n\n const onInput = (e: Event & { currentTarget: HTMLInputElement }) => {\n const inputVal = e.currentTarget.value;\n const formatted = formatMask(inputVal, options.mask);\n\n setFormattedValue(formatted.masked);\n setRawValue(formatted.unmasked);\n e.currentTarget.value = formatted.masked;\n };\n\n return {\n value,\n unmaskedValue,\n setValue,\n props: {\n value: () => value(),\n onInput,\n },\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-intersection-observer",
3
+ "title": "createIntersectionObserver",
4
+ "description": "SolidJS reactive primitives for observing element visibility and viewport intersection status",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-intersection-observer.ts",
9
+ "content": "import { createEffect, createSignal, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateIntersectionObserverOptions extends IntersectionObserverInit {\n /** Whether the observer is active. Defaults to true. */\n enabled?: boolean | Accessor<boolean>;\n}\n\n/**\n * SolidJS reactive primitive for observing element visibility and intersection with viewport or root element.\n *\n * @param target Target element or accessor returning HTML element.\n * @param callback Observer callback invoked on intersection state change.\n * @param options IntersectionObserver options (root, rootMargin, threshold, enabled).\n */\nexport function createIntersectionObserver(\n target: HTMLElement | Accessor<HTMLElement | undefined>,\n callback: IntersectionObserverCallback,\n options: CreateIntersectionObserverOptions = {}\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.IntersectionObserver) {\n return;\n }\n\n if (!isEnabled()) return;\n\n const el = getTarget();\n if (!el) return;\n\n const observer = new IntersectionObserver(callback, {\n root: options.root,\n rootMargin: options.rootMargin,\n threshold: options.threshold,\n });\n\n observer.observe(el);\n\n onCleanup(() => {\n observer.disconnect();\n });\n });\n}\n\n/**\n * SolidJS reactive primitive returning a boolean accessor indicating if element is currently visible in viewport.\n *\n * @param target Target element or accessor returning HTML element.\n * @param options IntersectionObserver options.\n */\nexport function createInView(\n target: HTMLElement | Accessor<HTMLElement | undefined>,\n options: CreateIntersectionObserverOptions = {}\n): Accessor<boolean> {\n const [isInView, setIsInView] = createSignal(false);\n\n createIntersectionObserver(\n target,\n (entries) => {\n const entry = entries[0];\n if (entry) {\n setIsInView(entry.isIntersecting);\n }\n },\n options\n );\n\n return isInView;\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-long-press",
3
+ "title": "createLongPress",
4
+ "description": "SolidJS reactive primitive for detecting long press / hold touch and pointer interactions",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-long-press.ts",
9
+ "content": "import { createSignal, onCleanup } from \"solid-js\";\n\nexport interface CreateLongPressOptions {\n /** Long press hold threshold duration in milliseconds. Defaults to 500. */\n threshold?: number;\n /** Callback fired when long press hold starts */\n onStart?: () => void;\n /** Callback fired when long press is successfully completed */\n onFinish?: () => void;\n /** Callback fired when long press is cancelled before threshold */\n onCancel?: () => void;\n}\n\nexport interface CreateLongPressReturn {\n /** Accessor indicating if long press is currently being held */\n isPressed: () => boolean;\n /** JSX Event Handlers object to spread or attach onto target element */\n props: {\n onPointerDown: (e: PointerEvent) => void;\n onPointerUp: (e: PointerEvent) => void;\n onPointerLeave: (e: PointerEvent) => void;\n onCancel: (e: Event) => void;\n };\n}\n\n/**\n * SolidJS reactive primitive for detecting long press / hold interactions on elements.\n *\n * @param handler Callback function invoked when long press threshold is reached.\n * @param options Options for threshold duration and state callbacks.\n */\nexport function createLongPress(\n handler: (event: Event) => void,\n options: CreateLongPressOptions = {}\n): CreateLongPressReturn {\n const threshold = options.threshold ?? 500;\n const [isPressed, setIsPressed] = createSignal(false);\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const cancel = (e?: Event) => {\n if (timer) {\n clearTimeout(timer);\n timer = undefined;\n }\n if (isPressed()) {\n setIsPressed(false);\n options.onCancel?.();\n }\n };\n\n const start = (e: Event) => {\n cancel();\n setIsPressed(true);\n options.onStart?.();\n\n timer = setTimeout(() => {\n handler(e);\n options.onFinish?.();\n timer = undefined;\n }, threshold);\n };\n\n const finish = (e: Event) => {\n if (timer) {\n clearTimeout(timer);\n timer = undefined;\n options.onCancel?.();\n }\n setIsPressed(false);\n };\n\n onCleanup(() => {\n cancel();\n });\n\n return {\n isPressed,\n props: {\n onPointerDown: start,\n onPointerUp: finish,\n onPointerLeave: cancel,\n onCancel: cancel,\n },\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-media-query",
3
+ "title": "createMediaQuery",
4
+ "description": "SolidJS reactive primitives for tracking CSS media queries and responsive Tailwind breakpoints",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-media-query.ts",
9
+ "content": "import { createEffect, createSignal, onCleanup, type Accessor } from \"solid-js\";\n\nexport const DEFAULT_BREAKPOINTS = {\n sm: \"(min-width: 640px)\",\n md: \"(min-width: 768px)\",\n lg: \"(min-width: 1024px)\",\n xl: \"(min-width: 1280px)\",\n \"2xl\": \"(min-width: 1536px)\",\n};\n\n/**\n * SolidJS reactive primitive for tracking CSS media query match state.\n *\n * @param query Media query string or accessor returning media query string.\n */\nexport function createMediaQuery(query: string | Accessor<string>): Accessor<boolean> {\n const getQuery = (): string => {\n return typeof query === \"function\" ? query() : query;\n };\n\n const [matches, setMatches] = createSignal<boolean>(false);\n\n createEffect(() => {\n if (typeof window === \"undefined\" || !window.matchMedia) {\n setMatches(false);\n return;\n }\n\n const currentQuery = getQuery();\n const mediaQueryList = window.matchMedia(currentQuery);\n\n setMatches(mediaQueryList.matches);\n\n const listener = (event: MediaQueryListEvent) => {\n setMatches(event.matches);\n };\n\n if (mediaQueryList.addEventListener) {\n mediaQueryList.addEventListener(\"change\", listener);\n onCleanup(() => mediaQueryList.removeEventListener(\"change\", listener));\n } else {\n mediaQueryList.addListener(listener);\n onCleanup(() => mediaQueryList.removeListener(listener));\n }\n });\n\n return matches;\n}\n\nexport interface CreateBreakpointReturn {\n /** Accessor returning active breakpoint key (e.g. 'sm', 'md', 'lg') */\n active: Accessor<string>;\n /** Accessor indicating if screen width matches mobile (<768px) */\n isMobile: Accessor<boolean>;\n /** Accessor indicating if screen width matches tablet (768px - 1024px) */\n isTablet: Accessor<boolean>;\n /** Accessor indicating if screen width matches desktop (>=1024px) */\n isDesktop: Accessor<boolean>;\n}\n\n/**\n * SolidJS reactive primitive for tracking responsive Tailwind CSS design breakpoints.\n *\n * @param customBreakpoints Custom breakpoint definitions mapping key names to media query strings.\n */\nexport function createBreakpoint(\n customBreakpoints: Record<string, string> = DEFAULT_BREAKPOINTS\n): CreateBreakpointReturn {\n const isSm = createMediaQuery(customBreakpoints.sm || DEFAULT_BREAKPOINTS.sm);\n const isMd = createMediaQuery(customBreakpoints.md || DEFAULT_BREAKPOINTS.md);\n const isLg = createMediaQuery(customBreakpoints.lg || DEFAULT_BREAKPOINTS.lg);\n const isXl = createMediaQuery(customBreakpoints.xl || DEFAULT_BREAKPOINTS.xl);\n const is2Xl = createMediaQuery(customBreakpoints[\"2xl\"] || DEFAULT_BREAKPOINTS[\"2xl\"]);\n\n const active = (): string => {\n if (is2Xl()) return \"2xl\";\n if (isXl()) return \"xl\";\n if (isLg()) return \"lg\";\n if (isMd()) return \"md\";\n if (isSm()) return \"sm\";\n return \"xs\";\n };\n\n const isMobile = (): boolean => !isMd();\n const isTablet = (): boolean => isMd() && !isLg();\n const isDesktop = (): boolean => isLg();\n\n return {\n active,\n isMobile,\n isTablet,\n isDesktop,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-mouse-position",
3
+ "title": "createMousePosition",
4
+ "description": "SolidJS reactive primitive for tracking global and element-relative mouse pointer coordinates",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-mouse-position.ts",
9
+ "content": "import { createEffect, createSignal, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateMousePositionOptions {\n /** Target element to calculate element-relative mouse coordinates for. Defaults to window. */\n target?: HTMLElement | Window | Accessor<HTMLElement | Window | undefined>;\n}\n\nexport interface CreateMousePositionReturn {\n /** Accessor for global page X mouse position */\n x: Accessor<number>;\n /** Accessor for global page Y mouse position */\n y: Accessor<number>;\n /** Accessor for element-relative X mouse coordinate */\n elementX: Accessor<number>;\n /** Accessor for element-relative Y mouse coordinate */\n elementY: Accessor<number>;\n /** Accessor indicating if mouse pointer is inside target element bounds */\n isInside: Accessor<boolean>;\n}\n\n/**\n * SolidJS reactive primitive for tracking global and element-relative mouse pointer coordinates.\n *\n * @param options Configuration options including target element.\n */\nexport function createMousePosition(\n options: CreateMousePositionOptions = {}\n): CreateMousePositionReturn {\n const [x, setX] = createSignal(0);\n const [y, setY] = createSignal(0);\n const [elementX, setElementX] = createSignal(0);\n const [elementY, setElementY] = createSignal(0);\n const [isInside, setIsInside] = createSignal(false);\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 handleMouseMove = (event: MouseEvent) => {\n const pageX = event.pageX;\n const pageY = event.pageY;\n\n setX(pageX);\n setY(pageY);\n\n const target = getTarget();\n if (target && target !== window) {\n const el = target as HTMLElement;\n const rect = el.getBoundingClientRect();\n const relX = event.clientX - rect.left;\n const relY = event.clientY - rect.top;\n\n setElementX(relX);\n setElementY(relY);\n\n const inside =\n relX >= 0 && relX <= rect.width && relY >= 0 && relY <= rect.height;\n setIsInside(inside);\n } else {\n setElementX(pageX);\n setElementY(pageY);\n setIsInside(true);\n }\n };\n\n createEffect(() => {\n if (typeof window === \"undefined\") return;\n\n window.addEventListener(\"mousemove\", handleMouseMove, { passive: true });\n onCleanup(() => {\n window.removeEventListener(\"mousemove\", handleMouseMove);\n });\n });\n\n return {\n x,\n y,\n elementX,\n elementY,\n isInside,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-network-status",
3
+ "title": "createNetworkStatus",
4
+ "description": "SolidJS reactive primitives for tracking browser network connectivity and connection quality metrics",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-network-status.ts",
9
+ "content": "import { createEffect, createSignal, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateNetworkStatusReturn {\n /** Accessor indicating if browser is currently connected to the network */\n isOnline: Accessor<boolean>;\n /** Date timestamp when network went offline, if applicable */\n offlineAt: Accessor<Date | undefined>;\n /** Date timestamp when network re-connected online, if applicable */\n onlineAt: Accessor<Date | undefined>;\n /** Network connection estimated downlink speed in Mbps */\n downlink: Accessor<number | undefined>;\n /** Network connection estimated round-trip time in ms */\n rtt: Accessor<number | undefined>;\n /** Network connection data saver mode enabled status */\n saveData: Accessor<boolean | undefined>;\n /** Network connection effective type ('slow-2g', '2g', '3g', '4g') */\n effectiveType: Accessor<\"slow-2g\" | \"2g\" | \"3g\" | \"4g\" | undefined>;\n}\n\n/**\n * SolidJS reactive primitive for tracking browser network connectivity and connection quality metrics.\n */\nexport function createNetworkStatus(): CreateNetworkStatusReturn {\n const getInitialOnline = () => (typeof navigator !== \"undefined\" ? navigator.onLine : true);\n\n const [isOnline, setIsOnline] = createSignal(getInitialOnline());\n const [offlineAt, setOfflineAt] = createSignal<Date | undefined>(undefined);\n const [onlineAt, setOnlineAt] = createSignal<Date | undefined>(undefined);\n\n const getNetworkConnection = () => {\n if (typeof navigator === \"undefined\") return undefined;\n return (navigator as any).connection || (navigator as any).mozConnection || (navigator as any).webkitConnection;\n };\n\n const conn = getNetworkConnection();\n\n const [downlink, setDownlink] = createSignal<number | undefined>(conn?.downlink);\n const [rtt, setRtt] = createSignal<number | undefined>(conn?.rtt);\n const [saveData, setSaveData] = createSignal<boolean | undefined>(conn?.saveData);\n const [effectiveType, setEffectiveType] = createSignal<\"slow-2g\" | \"2g\" | \"3g\" | \"4g\" | undefined>(conn?.effectiveType);\n\n const updateNetworkInfo = () => {\n const currentConn = getNetworkConnection();\n if (currentConn) {\n setDownlink(currentConn.downlink);\n setRtt(currentConn.rtt);\n setSaveData(currentConn.saveData);\n setEffectiveType(currentConn.effectiveType);\n }\n };\n\n const handleOnline = () => {\n setIsOnline(true);\n setOnlineAt(new Date());\n updateNetworkInfo();\n };\n\n const handleOffline = () => {\n setIsOnline(false);\n setOfflineAt(new Date());\n };\n\n createEffect(() => {\n if (typeof window === \"undefined\") return;\n\n window.addEventListener(\"online\", handleOnline);\n window.addEventListener(\"offline\", handleOffline);\n\n const currentConn = getNetworkConnection();\n if (currentConn && currentConn.addEventListener) {\n currentConn.addEventListener(\"change\", updateNetworkInfo);\n }\n\n onCleanup(() => {\n window.removeEventListener(\"online\", handleOnline);\n window.removeEventListener(\"offline\", handleOffline);\n if (currentConn && currentConn.removeEventListener) {\n currentConn.removeEventListener(\"change\", updateNetworkInfo);\n }\n });\n });\n\n return {\n isOnline,\n offlineAt,\n onlineAt,\n downlink,\n rtt,\n saveData,\n effectiveType,\n };\n}\n\n/**\n * SolidJS reactive primitive for checking if browser is connected online.\n */\nexport function createOnline(): Accessor<boolean> {\n const { isOnline } = createNetworkStatus();\n return isOnline;\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-orientation",
3
+ "title": "createOrientation",
4
+ "description": "SolidJS reactive primitive for observing mobile and desktop screen orientation changes and rotation angles",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-orientation.ts",
9
+ "content": "import { createSignal, createEffect, onCleanup, type Accessor } from \"solid-js\";\n\nexport type ScreenOrientationType =\n | \"portrait-primary\"\n | \"portrait-secondary\"\n | \"landscape-primary\"\n | \"landscape-secondary\"\n | \"portrait\"\n | \"landscape\"\n | \"unknown\";\n\nexport interface CreateOrientationOptions {\n /** Callback fired when screen orientation changes. */\n onChange?: (orientation: ScreenOrientationType, angle: number) => void;\n}\n\nexport interface CreateOrientationReturn {\n /** Signal indicating current orientation type. */\n type: Accessor<ScreenOrientationType>;\n /** Signal indicating current orientation angle in degrees (0, 90, 180, 270). */\n angle: Accessor<number>;\n /** Signal indicating if device screen is in portrait mode. */\n isPortrait: Accessor<boolean>;\n /** Signal indicating if device screen is in landscape mode. */\n isLandscape: Accessor<boolean>;\n /** Lock screen orientation if supported by device/browser. */\n lock: (orientation: string) => Promise<void>;\n /** Unlock screen orientation. */\n unlock: () => void;\n}\n\n/**\n * SolidJS reactive primitive for observing mobile/desktop screen orientation and angle.\n */\nexport function createOrientation(\n options: CreateOrientationOptions = {}\n): CreateOrientationReturn {\n const [type, setType] = createSignal<ScreenOrientationType>(\"unknown\");\n const [angle, setAngle] = createSignal<number>(0);\n\n const getOrientationState = (): { type: ScreenOrientationType; angle: number } => {\n if (typeof window === \"undefined\") {\n return { type: \"unknown\", angle: 0 };\n }\n\n if (window.screen?.orientation) {\n return {\n type: window.screen.orientation.type as ScreenOrientationType,\n angle: window.screen.orientation.angle || 0,\n };\n }\n\n /* Fallback for older browsers using window.orientation */\n const legacyAngle = (window as unknown as { orientation?: number }).orientation ?? 0;\n const isPortraitMode = Math.abs(legacyAngle) !== 90;\n return {\n type: isPortraitMode ? \"portrait\" : \"landscape\",\n angle: Number(legacyAngle),\n };\n };\n\n createEffect(() => {\n if (typeof window === \"undefined\") return;\n\n const updateState = (): void => {\n const state = getOrientationState();\n setType(state.type);\n setAngle(state.angle);\n options.onChange?.(state.type, state.angle);\n };\n\n updateState();\n\n if (window.screen?.orientation) {\n window.screen.orientation.addEventListener(\"change\", updateState);\n onCleanup(() => {\n window.screen.orientation.removeEventListener(\"change\", updateState);\n });\n } else {\n window.addEventListener(\"orientationchange\", updateState);\n window.addEventListener(\"resize\", updateState);\n onCleanup(() => {\n window.removeEventListener(\"orientationchange\", updateState);\n window.removeEventListener(\"resize\", updateState);\n });\n }\n });\n\n const isPortrait = (): boolean => {\n const currentType = type();\n return currentType.startsWith(\"portrait\");\n };\n\n const isLandscape = (): boolean => {\n const currentType = type();\n return currentType.startsWith(\"landscape\");\n };\n\n const lock = async (orientation: string): Promise<void> => {\n if (typeof window !== \"undefined\" && window.screen?.orientation) {\n const orientationApi = window.screen.orientation as unknown as {\n lock?: (orient: string) => Promise<void>;\n };\n if (typeof orientationApi.lock === \"function\") {\n await orientationApi.lock(orientation);\n }\n }\n };\n\n const unlock = (): void => {\n if (typeof window !== \"undefined\" && window.screen?.orientation) {\n const orientationApi = window.screen.orientation as unknown as {\n unlock?: () => void;\n };\n if (typeof orientationApi.unlock === \"function\") {\n orientationApi.unlock();\n }\n }\n };\n\n return {\n type,\n angle,\n isPortrait,\n isLandscape,\n lock,\n unlock,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-pagination",
3
+ "title": "createPagination",
4
+ "description": "SolidJS reactive primitive for computing pagination state, dynamic page ranges with ellipses, and navigation controls",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-pagination.ts",
9
+ "content": "import { createSignal, createMemo, type Accessor } from \"solid-js\";\n\nexport interface CreatePaginationOptions {\n /** Total number of items across all pages, or total count. */\n count?: number | Accessor<number>;\n /** Explicit total number of pages. If provided, overrides count / pageSize calculation. */\n totalPages?: number | Accessor<number>;\n /** Controlled active page number (1-indexed). */\n page?: number | Accessor<number>;\n /** Default active page number for uncontrolled state. Defaults to 1. */\n defaultPage?: number;\n /** Number of items per page. Defaults to 10. */\n pageSize?: number | Accessor<number>;\n /** Number of sibling page buttons visible on each side of the current active page. Defaults to 1. */\n siblingCount?: number | Accessor<number>;\n /** Number of boundary pages visible at the beginning and end. Defaults to 1. */\n boundaries?: number | Accessor<number>;\n /** Callback fired whenever the active page changes. */\n onChange?: (page: number) => void;\n}\n\nexport interface CreatePaginationReturn {\n /** Accessor returning the current active page number (1-indexed). */\n page: Accessor<number>;\n /** Accessor returning the calculated total number of pages. */\n totalPages: Accessor<number>;\n /** Accessor returning the active page size. */\n pageSize: Accessor<number>;\n /** Accessor returning an array of page numbers and \"ellipsis\" strings. */\n range: Accessor<(number | \"ellipsis\")[]>;\n /** Function to programmatically change to a specific page number. */\n setPage: (page: number) => void;\n /** Function to navigate to the next page. */\n next: () => void;\n /** Function to navigate to the previous page. */\n previous: () => void;\n /** Function to navigate to the first page (1). */\n first: () => void;\n /** Function to navigate to the last page (totalPages). */\n last: () => void;\n /** Accessor indicating whether a next page exists. */\n hasNext: Accessor<boolean>;\n /** Accessor indicating whether a previous page exists. */\n hasPrevious: Accessor<boolean>;\n /** 1-based start index of items on the current page (e.g. 1 for page 1 with pageSize 10). */\n startIndex: Accessor<number>;\n /** 1-based end index of items on the current page (e.g. 10 for page 1 with pageSize 10). */\n endIndex: Accessor<number>;\n}\n\n/**\n * SolidJS reactive primitive for computing pagination state, dynamic page range with ellipses, and navigation helpers.\n *\n * @param options Pagination configuration options.\n */\nexport function createPagination(options: CreatePaginationOptions = {}): CreatePaginationReturn {\n const getCount = () => {\n const raw = typeof options.count === \"function\" ? options.count() : options.count ?? 0;\n return Math.max(0, raw);\n };\n\n const getPageSize = () => {\n const raw = typeof options.pageSize === \"function\" ? options.pageSize() : options.pageSize ?? 10;\n return Math.max(1, raw);\n };\n\n const getExplicitTotalPages = () => {\n const raw = typeof options.totalPages === \"function\" ? options.totalPages() : options.totalPages;\n return raw !== undefined ? Math.max(1, raw) : undefined;\n };\n\n const getSiblingCount = () => {\n const raw = typeof options.siblingCount === \"function\" ? options.siblingCount() : options.siblingCount ?? 1;\n return Math.max(0, raw);\n };\n\n const getBoundaries = () => {\n const raw = typeof options.boundaries === \"function\" ? options.boundaries() : options.boundaries ?? 1;\n return Math.max(0, raw);\n };\n\n const [internalPage, setInternalPage] = createSignal<number>(Math.max(1, options.defaultPage ?? 1));\n\n const totalPages = createMemo<number>(() => {\n const explicit = getExplicitTotalPages();\n if (explicit !== undefined) {\n return Math.max(1, explicit);\n }\n const count = getCount();\n const size = getPageSize();\n return Math.max(1, Math.ceil(count / size));\n });\n\n const rawPage = () => {\n if (typeof options.page === \"function\") {\n return options.page();\n }\n if (typeof options.page === \"number\") {\n return options.page;\n }\n return internalPage();\n };\n\n const page = createMemo<number>(() => {\n const p = rawPage();\n const max = totalPages();\n if (p < 1) return 1;\n if (p > max) return max;\n return p;\n });\n\n const setPage = (nextPage: number) => {\n const max = totalPages();\n const clamped = Math.max(1, Math.min(nextPage, max));\n if (typeof options.page !== \"function\" && typeof options.page !== \"number\") {\n setInternalPage(clamped);\n }\n options.onChange?.(clamped);\n };\n\n const next = () => setPage(page() + 1);\n const previous = () => setPage(page() - 1);\n const first = () => setPage(1);\n const last = () => setPage(totalPages());\n\n const hasNext = createMemo(() => page() < totalPages());\n const hasPrevious = createMemo(() => page() > 1);\n\n const startIndex = createMemo(() => {\n const count = getCount();\n if (count === 0 && getExplicitTotalPages() === undefined) return 0;\n return (page() - 1) * getPageSize() + 1;\n });\n\n const endIndex = createMemo(() => {\n const count = getCount();\n const calculated = page() * getPageSize();\n if (count > 0) {\n return Math.min(calculated, count);\n }\n return calculated;\n });\n\n const range = createMemo<(number | \"ellipsis\")[]>(() => {\n const total = totalPages();\n const current = page();\n const siblings = getSiblingCount();\n const boundaries = getBoundaries();\n\n if (total <= 1) {\n return [1];\n }\n\n const pagesSet = new Set<number>();\n\n // 1. Boundary pages at the start\n for (let i = 1; i <= Math.min(boundaries, total); i++) {\n pagesSet.add(i);\n }\n\n // 2. Sibling pages around current page\n const leftSibling = Math.max(1, current - siblings);\n const rightSibling = Math.min(total, current + siblings);\n for (let i = leftSibling; i <= rightSibling; i++) {\n pagesSet.add(i);\n }\n\n // 3. Boundary pages at the end\n for (let i = Math.max(1, total - boundaries + 1); i <= total; i++) {\n pagesSet.add(i);\n }\n\n const sortedPages = Array.from(pagesSet).sort((a, b) => a - b);\n const result: (number | \"ellipsis\")[] = [];\n\n for (let i = 0; i < sortedPages.length; i++) {\n const currentPageNum = sortedPages[i];\n if (i > 0) {\n const prevPageNum = sortedPages[i - 1];\n const gap = currentPageNum - prevPageNum;\n if (gap === 2) {\n result.push(prevPageNum + 1);\n } else if (gap > 2) {\n result.push(\"ellipsis\");\n }\n }\n result.push(currentPageNum);\n }\n\n return result;\n });\n\n return {\n page,\n totalPages,\n pageSize: getPageSize,\n range,\n setPage,\n next,\n previous,\n first,\n last,\n hasNext,\n hasPrevious,\n startIndex,\n endIndex,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-permission",
3
+ "title": "createPermission",
4
+ "description": "SolidJS reactive primitive for querying and observing browser permission status changes",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-permission.ts",
9
+ "content": "import { createSignal, createEffect, onCleanup, type Accessor } from \"solid-js\";\n\nexport type PermissionNameType =\n | \"geolocation\"\n | \"notifications\"\n | \"persistent-storage\"\n | \"push\"\n | \"screen-wake-lock\"\n | \"clipboard-read\"\n | \"clipboard-write\"\n | \"camera\"\n | \"microphone\"\n | (string & {});\n\nexport type PermissionStatusState = \"granted\" | \"denied\" | \"prompt\" | \"unknown\";\n\nexport interface CreatePermissionOptions {\n /** Permission descriptor or name to query. */\n name: PermissionNameType;\n}\n\nexport interface CreatePermissionReturn {\n /** Signal accessor containing current permission status ('granted', 'denied', 'prompt', 'unknown'). */\n state: Accessor<PermissionStatusState>;\n /** Signal accessor indicating whether Permissions API is supported in browser environment. */\n isSupported: Accessor<boolean>;\n /** Imperative function to re-query permission status manually. */\n query: () => Promise<PermissionStatusState>;\n}\n\n/**\n * SolidJS reactive primitive for querying and observing browser permission status changes.\n */\nexport function createPermission(\n options: PermissionNameType | CreatePermissionOptions\n): CreatePermissionReturn {\n const [state, setState] = createSignal<PermissionStatusState>(\"unknown\");\n\n const permissionName = typeof options === \"string\" ? options : options.name;\n\n const isSupported = (): boolean =>\n typeof window !== \"undefined\" &&\n typeof navigator !== \"undefined\" &&\n \"permissions\" in navigator;\n\n let permissionStatus: PermissionStatus | null = null;\n\n const query = async (): Promise<PermissionStatusState> => {\n if (!isSupported()) {\n setState(\"unknown\");\n return \"unknown\";\n }\n\n try {\n const status = await navigator.permissions.query({\n name: permissionName as PermissionName,\n });\n\n permissionStatus = status;\n const currentState = status.state as PermissionStatusState;\n setState(currentState);\n\n status.onchange = () => {\n setState(status.state as PermissionStatusState);\n };\n\n return currentState;\n } catch {\n setState(\"unknown\");\n return \"unknown\";\n }\n };\n\n createEffect(() => {\n if (!isSupported()) return;\n\n query();\n\n onCleanup(() => {\n if (permissionStatus) {\n permissionStatus.onchange = null;\n }\n });\n });\n\n return {\n state,\n isSupported,\n query,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-previous",
3
+ "title": "createPrevious",
4
+ "description": "SolidJS reactive primitive for tracking previous value of a signal accessor",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-previous.ts",
9
+ "content": "import { createEffect, createSignal, type Accessor } from \"solid-js\";\n\n/**\n * SolidJS reactive primitive for tracking previous value of a signal accessor.\n *\n * @param source Target reactive signal accessor to observe.\n */\nexport function createPrevious<T>(source: Accessor<T>): Accessor<T | undefined> {\n const [previous, setPrevious] = createSignal<T | undefined>(undefined);\n let current: T = source();\n\n createEffect(() => {\n const next = source();\n if (current !== next) {\n setPrevious(() => current);\n current = next;\n }\n });\n\n return previous;\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-into-view",
3
+ "title": "createScrollIntoView",
4
+ "description": "SolidJS reactive primitive for scrolling a target element into view smooth or auto behavior",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-scroll-into-view.ts",
9
+ "content": "import { createEffect, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateScrollIntoViewOptions extends ScrollIntoViewOptions {\n /** Whether the element should scroll into view automatically. Defaults to true. */\n enabled?: boolean | Accessor<boolean>;\n /** Delay in milliseconds before executing scrollIntoView. Defaults to 0. */\n delay?: number;\n}\n\n/**\n * SolidJS reactive primitive for scrolling a target element into view smooth or auto behavior.\n */\nexport function createScrollIntoView(\n target: HTMLElement | Accessor<HTMLElement | null | undefined> | null | undefined,\n options: CreateScrollIntoViewOptions = {}\n): void {\n const getTarget = (): HTMLElement | null | undefined => {\n if (typeof target === \"function\") {\n return target();\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 (!isEnabled()) return;\n\n const el = getTarget();\n if (!el || typeof window === \"undefined\") return;\n\n const scrollOptions: ScrollIntoViewOptions = {\n behavior: options.behavior ?? \"smooth\",\n block: options.block ?? \"nearest\",\n inline: options.inline ?? \"nearest\",\n };\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n if (options.delay && options.delay > 0) {\n timer = setTimeout(() => {\n el.scrollIntoView(scrollOptions);\n }, options.delay);\n } else {\n el.scrollIntoView(scrollOptions);\n }\n\n onCleanup(() => {\n if (timer) clearTimeout(timer);\n });\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
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-storage",
3
+ "title": "createLocalStorage",
4
+ "description": "SolidJS reactive primitives for Web Storage state synchronization across components and browser tabs",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-storage.ts",
9
+ "content": "import { createSignal, onMount, onCleanup, type Accessor } from \"solid-js\";\n\nexport type StorageType = \"local\" | \"session\";\n\n/**\n * Helper to safely read item from Web Storage.\n */\nfunction readStorage<T>(key: string, storage: Storage | undefined, fallback: T): T {\n if (!storage) return fallback;\n try {\n const item = storage.getItem(key);\n return item !== null ? JSON.parse(item) : fallback;\n } catch (error) {\n console.warn(`[nikala-ui/hooks] Error reading storage key \"${key}\":`, error);\n return fallback;\n }\n}\n\n/**\n * SolidJS reactive primitive for Web Storage (localStorage / sessionStorage) synchronization.\n *\n * @param key Storage key name.\n * @param initialValue Default initial value if key doesn't exist.\n * @param type Storage type: \"local\" (default) or \"session\".\n */\nexport function createStorage<T>(\n key: string,\n initialValue: T | Accessor<T>,\n type: StorageType = \"local\"\n): [value: Accessor<T>, setValue: (val: T | ((prev: T) => T)) => void, remove: () => void] {\n const getStorage = (): Storage | undefined => {\n if (typeof window === \"undefined\") return undefined;\n return type === \"local\" ? window.localStorage : window.sessionStorage;\n };\n\n const getFallback = (): T => (typeof initialValue === \"function\" ? (initialValue as Accessor<T>)() : initialValue);\n\n const [value, setInternalValue] = createSignal<T>(getFallback());\n\n const setValue = (val: T | ((prev: T) => T)) => {\n const storage = getStorage();\n const currentValue = value();\n const nextValue = typeof val === \"function\" ? (val as (prev: T) => T)(currentValue) : val;\n\n setInternalValue(() => nextValue);\n\n if (storage) {\n try {\n storage.setItem(key, JSON.stringify(nextValue));\n } catch (error) {\n console.warn(`[nikala-ui/hooks] Error setting storage key \"${key}\":`, error);\n }\n }\n };\n\n const remove = () => {\n const storage = getStorage();\n setInternalValue(() => getFallback());\n if (storage) {\n storage.removeItem(key);\n }\n };\n\n onMount(() => {\n if (typeof window === \"undefined\") return;\n\n const storage = getStorage();\n if (storage) {\n const stored = readStorage<T>(key, storage, getFallback());\n setInternalValue(() => stored);\n }\n\n const handleStorageChange = (event: StorageEvent) => {\n if (event.key === key) {\n setInternalValue(() => (event.newValue !== null ? JSON.parse(event.newValue) : getFallback()));\n }\n };\n\n window.addEventListener(\"storage\", handleStorageChange);\n onCleanup(() => {\n window.removeEventListener(\"storage\", handleStorageChange);\n });\n });\n\n return [value, setValue, remove];\n}\n\n/**\n * SolidJS reactive primitive for localStorage synchronization across components and browser tabs.\n */\nexport function createLocalStorage<T>(\n key: string,\n initialValue: T | Accessor<T>\n): [value: Accessor<T>, setValue: (val: T | ((prev: T) => T)) => void, remove: () => void] {\n return createStorage<T>(key, initialValue, \"local\");\n}\n\n/**\n * SolidJS reactive primitive for sessionStorage synchronization.\n */\nexport function createSessionStorage<T>(\n key: string,\n initialValue: T | Accessor<T>\n): [value: Accessor<T>, setValue: (val: T | ((prev: T) => T)) => void, remove: () => void] {\n return createStorage<T>(key, initialValue, \"session\");\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-tauri-window",
3
+ "title": "createTauriWindow",
4
+ "description": "SolidJS reactive primitive for interacting with and controlling Tauri v2 application window states",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-tauri-window.ts",
9
+ "content": "import { createSignal, onMount, onCleanup, type Accessor } from \"solid-js\";\n\nexport type TauriPlatform = \"macos\" | \"windows\" | \"linux\" | \"web\";\n\nexport interface CreateTauriWindowOptions {\n /** Initial fallback platform if not detected automatically. */\n defaultPlatform?: TauriPlatform;\n /** Listen to window resize and focus events. Defaults to true. */\n listenEvents?: boolean;\n}\n\nexport interface CreateTauriWindowReturn {\n /** Signal indicating whether the application is running inside a Tauri runtime. */\n isTauri: Accessor<boolean>;\n /** Detected host platform ('macos' | 'windows' | 'linux' | 'web'). */\n platform: Accessor<TauriPlatform>;\n /** Signal indicating whether the window is currently maximized. */\n isMaximized: Accessor<boolean>;\n /** Signal indicating whether the window is currently minimized. */\n isMinimized: Accessor<boolean>;\n /** Signal indicating whether the window is currently in fullscreen mode. */\n isFullscreen: Accessor<boolean>;\n /** Signal indicating whether the window is currently focused. */\n isFocused: Accessor<boolean>;\n /** Minimize the application window. */\n minimize: () => Promise<void>;\n /** Maximize the application window. */\n maximize: () => Promise<void>;\n /** Unmaximize the application window. */\n unmaximize: () => Promise<void>;\n /** Toggle between maximized and normal window states. */\n toggleMaximize: () => Promise<void>;\n /** Close the application window or exit process if main window. */\n close: () => Promise<void>;\n /** Forcefully destroy/close the application window. */\n destroy: () => Promise<void>;\n /** Toggle fullscreen state. */\n setFullscreen: (fullscreen: boolean) => Promise<void>;\n /** Start dragging the window. Can be bound to mousedown event or custom titlebar headers. */\n startDragging: (e?: MouseEvent) => Promise<void>;\n}\n\n/**\n * Helper to detect current operating system in browser or desktop environments.\n */\nexport function detectPlatform(): TauriPlatform {\n if (typeof window === \"undefined\" || typeof navigator === \"undefined\") {\n return \"web\";\n }\n\n const userAgent = navigator.userAgent.toLowerCase();\n const platform =\n (navigator as any).userAgentData?.platform?.toLowerCase() ||\n navigator.platform?.toLowerCase() ||\n \"\";\n\n if (platform.includes(\"mac\") || userAgent.includes(\"macintosh\") || userAgent.includes(\"mac os x\")) {\n return \"macos\";\n }\n if (platform.includes(\"win\") || userAgent.includes(\"windows\")) {\n return \"windows\";\n }\n if (platform.includes(\"linux\") || userAgent.includes(\"linux\")) {\n return \"linux\";\n }\n\n return \"web\";\n}\n\n/**\n * Checks whether the current environment is running inside Tauri v1 or v2.\n */\nexport function isTauriEnvironment(): boolean {\n if (typeof window === \"undefined\") return false;\n const win = window as any;\n return Boolean(\n win.__TAURI_INTERNALS__ ||\n win.__TAURI__ ||\n win.__TAURI_PATTERN__ ||\n win.__TAURI_METADATA__ ||\n win.__TAURI_IPC__ ||\n win.__TAURI_WINDOW__\n );\n}\n\n/**\n * Checks whether the current environment is running inside Electron.\n */\nexport function isElectronEnvironment(): boolean {\n if (typeof window === \"undefined\") return false;\n const win = window as any;\n return Boolean(\n win.electron ||\n win.electronAPI ||\n win.ipcRenderer ||\n win.process?.versions?.electron ||\n (typeof navigator !== \"undefined\" && navigator.userAgent?.toLowerCase().includes(\"electron\"))\n );\n}\n\n/**\n * Direct low-level IPC invocation across Tauri v2 (__TAURI_INTERNALS__) and Tauri v1.\n */\nasync function invokeTauri(\n v2Cmd: string,\n v2Args?: Record<string, any>,\n v1CmdType?: string,\n v1Data?: any\n): Promise<any> {\n if (typeof window === \"undefined\") return undefined;\n const win = window as any;\n\n // 1. Try Tauri v2 direct IPC via injected __TAURI_INTERNALS__ or core.invoke\n const invokeV2 = win.__TAURI_INTERNALS__?.invoke || win.__TAURI__?.core?.invoke;\n if (typeof invokeV2 === \"function\") {\n try {\n return await invokeV2(`plugin:window|${v2Cmd}`, v2Args || {});\n } catch {\n // Fallback to secondary invoke mechanisms\n }\n }\n\n // 2. Try Tauri v1 / generic invoke\n const invokeV1 = win.__TAURI__?.invoke || win.__TAURI_INVOKE__;\n if (typeof invokeV1 === \"function\") {\n try {\n if (v1CmdType) {\n return await invokeV1(\"tauri\", {\n __tauriModule: \"Window\",\n message: {\n cmd: \"manage\",\n data: {\n cmd: {\n type: v1CmdType,\n ...(v1Data !== undefined ? { data: v1Data } : {}),\n },\n },\n },\n });\n }\n return await invokeV1(v2Cmd, v2Args);\n } catch {\n // Fallback\n }\n }\n\n return undefined;\n}\n\n/**\n * Helper to send Electron IPC messages if running in an Electron shell.\n */\nfunction sendElectronMessage(channel: string, ...args: any[]): boolean {\n if (typeof window === \"undefined\") return false;\n const win = window as any;\n\n try {\n if (win.electronAPI && typeof win.electronAPI[channel] === \"function\") {\n win.electronAPI[channel](...args);\n return true;\n }\n if (win.electron?.ipcRenderer?.send) {\n win.electron.ipcRenderer.send(channel, ...args);\n return true;\n }\n if (win.ipcRenderer?.send) {\n win.ipcRenderer.send(channel, ...args);\n return true;\n }\n if (typeof win.require === \"function\") {\n const electron = win.require(\"electron\");\n if (electron?.ipcRenderer?.send) {\n electron.ipcRenderer.send(channel, ...args);\n return true;\n }\n }\n } catch {\n // Ignored\n }\n return false;\n}\n\n/**\n * SolidJS reactive primitive for controlling and observing desktop window states.\n * Supports Tauri v2, Tauri v1, Electron, and browser preview environments.\n */\nexport function createTauriWindow(\n options: CreateTauriWindowOptions = {}\n): CreateTauriWindowReturn {\n const [isTauri, setIsTauri] = createSignal<boolean>(false);\n const [platform, setPlatform] = createSignal<TauriPlatform>(options.defaultPlatform || \"web\");\n const [isMaximized, setIsMaximized] = createSignal<boolean>(false);\n const [isMinimized, setIsMinimized] = createSignal<boolean>(false);\n const [isFullscreen, setIsFullscreen] = createSignal<boolean>(false);\n const [isFocused, setIsFocused] = createSignal<boolean>(true);\n\n let unlistenResize: (() => void) | null = null;\n let unlistenFocus: (() => void) | null = null;\n\n // Retrieve Tauri Window instance safely\n const getTauriWindow = async () => {\n if (typeof window === \"undefined\") return null;\n const win = window as any;\n try {\n if (win.__TAURI__?.window?.getCurrentWindow) {\n return win.__TAURI__.window.getCurrentWindow();\n }\n if (win.__TAURI__?.window?.appWindow) {\n return win.__TAURI__.window.appWindow;\n }\n if (win.__TAURI_WINDOW__?.getCurrentWindow) {\n return win.__TAURI_WINDOW__.getCurrentWindow();\n }\n if (win.__TAURI_WINDOW__?.appWindow) {\n return win.__TAURI_WINDOW__.appWindow;\n }\n // Dynamic import if bundled by user's build setup\n const moduleName = \"@tauri-apps/api/window\";\n // @ts-ignore - Optional runtime dependency in Tauri apps\n const tauriApi = await import(/* @vite-ignore */ moduleName).catch(() => null);\n if (tauriApi?.getCurrentWindow) {\n return tauriApi.getCurrentWindow();\n }\n if (tauriApi?.appWindow) {\n return tauriApi.appWindow;\n }\n } catch {\n // Running in standard web mode\n }\n return null;\n };\n\n const updateWindowStates = async () => {\n if (typeof window === \"undefined\") return;\n\n const tauriWin = await getTauriWindow();\n if (tauriWin) {\n setIsTauri(true);\n try {\n const [max, min, full, foc] = await Promise.all([\n typeof tauriWin.isMaximized === \"function\" ? tauriWin.isMaximized() : false,\n typeof tauriWin.isMinimized === \"function\" ? tauriWin.isMinimized() : false,\n typeof tauriWin.isFullscreen === \"function\" ? tauriWin.isFullscreen() : false,\n typeof tauriWin.isFocused === \"function\" ? tauriWin.isFocused() : true,\n ]);\n setIsMaximized(Boolean(max));\n setIsMinimized(Boolean(min));\n setIsFullscreen(Boolean(full));\n setIsFocused(Boolean(foc));\n return;\n } catch {\n // Fallback gracefully\n }\n }\n\n if (isTauriEnvironment()) {\n setIsTauri(true);\n try {\n const max = await invokeTauri(\"is_maximized\", {}, \"isMaximized\");\n if (typeof max === \"boolean\") setIsMaximized(max);\n const min = await invokeTauri(\"is_minimized\", {}, \"isMinimized\");\n if (typeof min === \"boolean\") setIsMinimized(min);\n const full = await invokeTauri(\"is_fullscreen\", {}, \"isFullscreen\");\n if (typeof full === \"boolean\") setIsFullscreen(full);\n const foc = await invokeTauri(\"is_focused\", {}, \"isFocused\");\n if (typeof foc === \"boolean\") setIsFocused(foc);\n return;\n } catch {\n // Fallback\n }\n }\n\n if (!isElectronEnvironment()) {\n setIsTauri(false);\n }\n if (typeof document !== \"undefined\") {\n setIsFullscreen(Boolean(document.fullscreenElement));\n }\n };\n\n onMount(async () => {\n if (typeof window === \"undefined\") return;\n\n setPlatform(detectPlatform());\n setIsTauri(isTauriEnvironment());\n await updateWindowStates();\n\n if (options.listenEvents !== false) {\n const handleWindowFocus = () => setIsFocused(true);\n const handleWindowBlur = () => setIsFocused(false);\n const handleFullscreenChange = () => setIsFullscreen(Boolean(document.fullscreenElement));\n const handleResize = () => {\n updateWindowStates();\n };\n\n window.addEventListener(\"focus\", handleWindowFocus);\n window.addEventListener(\"blur\", handleWindowBlur);\n window.addEventListener(\"resize\", handleResize);\n document.addEventListener(\"fullscreenchange\", handleFullscreenChange);\n\n const tauriWin = await getTauriWindow();\n if (tauriWin?.onResized) {\n try {\n unlistenResize = await tauriWin.onResized(() => updateWindowStates());\n unlistenFocus = await tauriWin.onFocusChanged(({ payload }: { payload: boolean }) => {\n setIsFocused(payload);\n });\n } catch {\n // Ignored in non-tauri contexts\n }\n }\n\n onCleanup(() => {\n window.removeEventListener(\"focus\", handleWindowFocus);\n window.removeEventListener(\"blur\", handleWindowBlur);\n window.removeEventListener(\"resize\", handleResize);\n document.removeEventListener(\"fullscreenchange\", handleFullscreenChange);\n if (unlistenResize) unlistenResize();\n if (unlistenFocus) unlistenFocus();\n });\n }\n });\n\n const minimize = async (): Promise<void> => {\n let executed = false;\n const tauriWin = await getTauriWindow();\n if (tauriWin?.minimize) {\n try {\n await tauriWin.minimize();\n executed = true;\n } catch {}\n }\n if (!executed && isTauriEnvironment()) {\n try {\n await invokeTauri(\"minimize\", {}, \"minimize\");\n executed = true;\n } catch {}\n }\n if (!executed && isElectronEnvironment()) {\n executed =\n sendElectronMessage(\"window-minimize\") ||\n sendElectronMessage(\"minimize\") ||\n sendElectronMessage(\"windowMinimize\");\n }\n\n setIsMinimized(true);\n setIsMaximized(false);\n };\n\n const maximize = async (): Promise<void> => {\n let executed = false;\n const tauriWin = await getTauriWindow();\n if (tauriWin?.maximize) {\n try {\n await tauriWin.maximize();\n executed = true;\n } catch {}\n }\n if (!executed && isTauriEnvironment()) {\n try {\n await invokeTauri(\"maximize\", {}, \"maximize\");\n executed = true;\n } catch {}\n }\n if (!executed && isElectronEnvironment()) {\n executed =\n sendElectronMessage(\"window-maximize\") ||\n sendElectronMessage(\"maximize\") ||\n sendElectronMessage(\"windowMaximize\");\n }\n\n setIsMaximized(true);\n setIsMinimized(false);\n };\n\n const unmaximize = async (): Promise<void> => {\n let executed = false;\n const tauriWin = await getTauriWindow();\n if (tauriWin?.unmaximize) {\n try {\n await tauriWin.unmaximize();\n executed = true;\n } catch {}\n }\n if (!executed && isTauriEnvironment()) {\n try {\n await invokeTauri(\"unmaximize\", {}, \"unmaximize\");\n executed = true;\n } catch {}\n }\n if (!executed && isElectronEnvironment()) {\n executed =\n sendElectronMessage(\"window-unmaximize\") ||\n sendElectronMessage(\"unmaximize\") ||\n sendElectronMessage(\"windowRestore\") ||\n sendElectronMessage(\"restore\");\n }\n\n setIsMaximized(false);\n };\n\n let lastToggleTime = 0;\n const toggleMaximize = async (): Promise<void> => {\n const now = Date.now();\n if (now - lastToggleTime < 350) {\n return; // Ignore duplicate rapid triggers within 350ms window\n }\n lastToggleTime = now;\n\n let executed = false;\n const tauriWin = await getTauriWindow();\n if (tauriWin) {\n try {\n if (typeof tauriWin.isMaximized === \"function\") {\n const isMax = await tauriWin.isMaximized();\n if (isMax && typeof tauriWin.unmaximize === \"function\") {\n await tauriWin.unmaximize();\n setIsMaximized(false);\n executed = true;\n } else if (!isMax && typeof tauriWin.maximize === \"function\") {\n await tauriWin.maximize();\n setIsMaximized(true);\n executed = true;\n }\n }\n if (!executed && typeof tauriWin.toggleMaximize === \"function\") {\n await tauriWin.toggleMaximize();\n executed = true;\n }\n } catch {}\n }\n\n if (!executed && isTauriEnvironment()) {\n try {\n const isMax = await invokeTauri(\"is_maximized\", {}, \"isMaximized\");\n if (typeof isMax === \"boolean\") {\n if (isMax) {\n await invokeTauri(\"unmaximize\", {}, \"unmaximize\");\n setIsMaximized(false);\n } else {\n await invokeTauri(\"maximize\", {}, \"maximize\");\n setIsMaximized(true);\n }\n executed = true;\n } else {\n await invokeTauri(\"toggle_maximize\", {}, \"toggleMaximize\");\n executed = true;\n }\n } catch {}\n }\n\n if (!executed && isElectronEnvironment()) {\n executed =\n sendElectronMessage(\"window-toggle-maximize\") ||\n sendElectronMessage(\"toggle-maximize\") ||\n sendElectronMessage(\"toggleMaximize\");\n }\n\n if (!executed) {\n setIsMaximized((prev) => !prev);\n }\n setIsMinimized(false);\n };\n\n const close = async (): Promise<void> => {\n let closed = false;\n const win = typeof window !== \"undefined\" ? (window as any) : null;\n const tauriWin = await getTauriWindow();\n\n // 1. Try Tauri Window API close\n if (tauriWin?.close) {\n try {\n await tauriWin.close();\n closed = true;\n } catch {}\n }\n\n // 2. Try Tauri IPC window close\n if (!closed && isTauriEnvironment()) {\n try {\n await invokeTauri(\"close\", {}, \"close\");\n closed = true;\n } catch {}\n }\n\n // 3. Try Tauri destroy fallback if close did not succeed\n if (!closed && tauriWin?.destroy) {\n try {\n await tauriWin.destroy();\n closed = true;\n } catch {}\n }\n if (!closed && isTauriEnvironment()) {\n try {\n await invokeTauri(\"destroy\", {}, \"destroy\");\n closed = true;\n } catch {}\n }\n\n // 4. Try Tauri process exit if in Tauri environment\n if (!closed && isTauriEnvironment()) {\n try {\n const invokeProcess = win?.__TAURI_INTERNALS__?.invoke || win?.__TAURI__?.core?.invoke;\n if (typeof invokeProcess === \"function\") {\n await invokeProcess(\"plugin:process|exit\", { code: 0 });\n closed = true;\n } else if (win?.__TAURI__?.process?.exit) {\n await win.__TAURI__.process.exit(0);\n closed = true;\n }\n } catch {}\n }\n\n // 5. Try Electron window close / app quit\n if (!closed && isElectronEnvironment()) {\n closed =\n sendElectronMessage(\"window-close\") ||\n sendElectronMessage(\"close\") ||\n sendElectronMessage(\"app-close\") ||\n sendElectronMessage(\"quit\");\n }\n\n // 6. Browser fallback & custom event dispatch\n if (typeof window !== \"undefined\") {\n try {\n window.dispatchEvent(new CustomEvent(\"nikala:window-close\"));\n } catch {}\n\n try {\n window.close();\n } catch {}\n }\n };\n\n const destroy = async (): Promise<void> => {\n const tauriWin = await getTauriWindow();\n if (tauriWin?.destroy) {\n try {\n await tauriWin.destroy();\n return;\n } catch {}\n }\n if (isTauriEnvironment()) {\n try {\n await invokeTauri(\"destroy\", {}, \"destroy\");\n return;\n } catch {}\n }\n await close();\n };\n\n const setFullscreen = async (fullscreen: boolean): Promise<void> => {\n let executed = false;\n const tauriWin = await getTauriWindow();\n if (tauriWin?.setFullscreen) {\n try {\n await tauriWin.setFullscreen(fullscreen);\n executed = true;\n } catch {}\n }\n if (!executed && isTauriEnvironment()) {\n try {\n await invokeTauri(\"set_fullscreen\", { value: fullscreen }, \"setFullscreen\", fullscreen);\n executed = true;\n } catch {}\n }\n if (!executed && isElectronEnvironment()) {\n executed =\n sendElectronMessage(\"window-set-fullscreen\", fullscreen) ||\n sendElectronMessage(\"set-fullscreen\", fullscreen);\n }\n\n if (typeof document !== \"undefined\") {\n if (fullscreen && document.documentElement.requestFullscreen) {\n try {\n await document.documentElement.requestFullscreen();\n } catch {}\n } else if (!fullscreen && document.fullscreenElement && document.exitFullscreen) {\n try {\n await document.exitFullscreen();\n } catch {}\n }\n }\n\n setIsFullscreen(fullscreen);\n };\n\n const startDragging = async (e?: MouseEvent): Promise<void> => {\n if (e) {\n if (e.button !== 0) return; // Only primary mouse button starts drag\n if (e.detail > 1) return; // Prevent drag initialization on double-click gesture\n }\n let dragged = false;\n const tauriWin = await getTauriWindow();\n if (tauriWin?.startDragging) {\n try {\n await tauriWin.startDragging();\n dragged = true;\n } catch {}\n }\n if (!dragged && isTauriEnvironment()) {\n try {\n await invokeTauri(\"start_dragging\", {}, \"startDragging\");\n dragged = true;\n } catch {}\n }\n if (!dragged && isElectronEnvironment()) {\n sendElectronMessage(\"window-drag\");\n }\n };\n\n return {\n isTauri,\n platform,\n isMaximized,\n isMinimized,\n isFullscreen,\n isFocused,\n minimize,\n maximize,\n unmaximize,\n toggleMaximize,\n close,\n destroy,\n setFullscreen,\n startDragging,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "create-timer",
3
+ "title": "createTimer",
4
+ "description": "SolidJS reactive primitives for recurring interval ticks and formatted countdown timers",
5
+ "type": "registry:hook",
6
+ "files": [
7
+ {
8
+ "path": "hooks/create-timer.ts",
9
+ "content": "import { createEffect, createSignal, onCleanup, type Accessor } from \"solid-js\";\n\nexport interface CreateTimerOptions {\n /** Whether the timer starts running automatically on mount. Defaults to false. */\n autostart?: boolean;\n}\n\nexport interface CreateTimerReturn {\n /** Accessor indicating if timer is actively running */\n isRunning: Accessor<boolean>;\n /** Function to start or resume the timer */\n start: () => void;\n /** Function to pause/stop the timer */\n stop: () => void;\n /** Function to reset and restart the timer */\n reset: () => void;\n /** Function to toggle running state */\n toggle: () => void;\n}\n\n/**\n * SolidJS reactive primitive for recurring interval timers.\n *\n * @param intervalMs Interval duration in milliseconds.\n * @param callback Function to execute on each interval tick.\n * @param options Configuration options including autostart.\n */\nexport function createTimer(\n intervalMs: number | Accessor<number>,\n callback: () => void,\n options: CreateTimerOptions = {}\n): CreateTimerReturn {\n const [isRunning, setIsRunning] = createSignal<boolean>(options.autostart ?? false);\n let timerId: ReturnType<typeof setInterval> | undefined;\n\n const getInterval = () => (typeof intervalMs === \"function\" ? intervalMs() : intervalMs);\n\n const stop = () => {\n if (timerId) {\n clearInterval(timerId);\n timerId = undefined;\n }\n setIsRunning(false);\n };\n\n const start = () => {\n stop();\n const delay = getInterval();\n if (delay <= 0) return;\n\n setIsRunning(true);\n timerId = setInterval(() => {\n callback();\n }, delay);\n };\n\n const reset = () => {\n start();\n };\n\n const toggle = () => {\n if (isRunning()) {\n stop();\n } else {\n start();\n }\n };\n\n createEffect(() => {\n if (isRunning()) {\n start();\n }\n });\n\n onCleanup(() => {\n stop();\n });\n\n return {\n isRunning,\n start,\n stop,\n reset,\n toggle,\n };\n}\n\nexport interface CreateCountdownOptions extends CreateTimerOptions {\n /** Callback fired when countdown reaches zero */\n onComplete?: () => void;\n}\n\nexport interface CreateCountdownReturn extends CreateTimerReturn {\n /** Accessor for remaining seconds */\n remainingSeconds: Accessor<number>;\n /** Accessor for formatted time string (MM:SS) */\n formatted: Accessor<string>;\n}\n\n/**\n * SolidJS reactive primitive for countdown timers.\n *\n * @param durationSeconds Total countdown duration in seconds.\n * @param options Configuration options including autostart and onComplete callback.\n */\nexport function createCountdown(\n durationSeconds: number | Accessor<number>,\n options: CreateCountdownOptions = {}\n): CreateCountdownReturn {\n const getInitialDuration = () =>\n typeof durationSeconds === \"function\" ? durationSeconds() : durationSeconds;\n\n const [remaining, setRemaining] = createSignal<number>(getInitialDuration());\n\n const timer = createTimer(\n 1000,\n () => {\n setRemaining((prev) => {\n if (prev <= 1) {\n timer.stop();\n options.onComplete?.();\n return 0;\n }\n return prev - 1;\n });\n },\n { autostart: options.autostart }\n );\n\n const reset = () => {\n setRemaining(getInitialDuration());\n timer.start();\n };\n\n const formatted = (): string => {\n const total = remaining();\n const minutes = Math.floor(total / 60);\n const seconds = total % 60;\n const mm = String(minutes).padStart(2, \"0\");\n const ss = String(seconds).padStart(2, \"0\");\n return `${mm}:${ss}`;\n };\n\n return {\n ...timer,\n remainingSeconds: remaining,\n formatted,\n reset,\n };\n}\n",
10
+ "type": "registry:hook"
11
+ }
12
+ ]
13
+ }