@invisibleloop/pulse 0.1.21

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 (344) hide show
  1. package/.claude/commands/build-page.md +59 -0
  2. package/.claude/commands/new-doc-page.md +45 -0
  3. package/.claude/commands/verify.md +52 -0
  4. package/.claude/pulse-checklist.md +111 -0
  5. package/.claude/settings.local.json +102 -0
  6. package/.github/workflows/ci.yml +22 -0
  7. package/.github/workflows/publish.yml +41 -0
  8. package/.pulse/load-reports/home/1773432711417.json +22 -0
  9. package/CLAUDE.md +383 -0
  10. package/README.md +95 -0
  11. package/docs/.claude/pulse-checklist.md +111 -0
  12. package/docs/public/.pulse-ui-version +1 -0
  13. package/docs/public/dist/accessibility.boot-5DVTARJU.js +115 -0
  14. package/docs/public/dist/actions.boot-P66HKQEM.js +164 -0
  15. package/docs/public/dist/auth.boot-IMAJAUPH.js +140 -0
  16. package/docs/public/dist/caching.boot-DVR6KDE7.js +53 -0
  17. package/docs/public/dist/components--accordion.boot-3HVKMNWC.js +11 -0
  18. package/docs/public/dist/components--alert.boot-GCEXOZAC.js +6 -0
  19. package/docs/public/dist/components--app-badge.boot-DVT3GCHJ.js +6 -0
  20. package/docs/public/dist/components--avatar.boot-PSW24EVA.js +5 -0
  21. package/docs/public/dist/components--badge.boot-TYDY2RMK.js +7 -0
  22. package/docs/public/dist/components--banner.boot-EI5PZSZK.js +7 -0
  23. package/docs/public/dist/components--breadcrumbs.boot-SMA2E2GO.js +34 -0
  24. package/docs/public/dist/components--button.boot-J54BQM2E.js +23 -0
  25. package/docs/public/dist/components--card.boot-PZGNDIB6.js +138 -0
  26. package/docs/public/dist/components--carousel.boot-TP6LPFZZ.js +12 -0
  27. package/docs/public/dist/components--charts.boot-2EOYQWKL.js +108 -0
  28. package/docs/public/dist/components--checkbox.boot-DS5BSL6T.js +54 -0
  29. package/docs/public/dist/components--cluster.boot-HHVIBBJG.js +9 -0
  30. package/docs/public/dist/components--code-window.boot-2GR2DV33.js +20 -0
  31. package/docs/public/dist/components--container.boot-7LOOGK2K.js +5 -0
  32. package/docs/public/dist/components--cta.boot-FSNZ5YRT.js +11 -0
  33. package/docs/public/dist/components--divider.boot-3NI2C3QG.js +6 -0
  34. package/docs/public/dist/components--empty.boot-YX2UR3PV.js +7 -0
  35. package/docs/public/dist/components--feature.boot-MUD7NSUO.js +13 -0
  36. package/docs/public/dist/components--fieldset.boot-J7BYHMKF.js +19 -0
  37. package/docs/public/dist/components--fileupload.boot-NIKVTTPD.js +52 -0
  38. package/docs/public/dist/components--footer.boot-EYUK5FRG.js +14 -0
  39. package/docs/public/dist/components--grid.boot-URDQVDDR.js +59 -0
  40. package/docs/public/dist/components--heading.boot-BPQKU43E.js +44 -0
  41. package/docs/public/dist/components--hero.boot-4RAPRGAB.js +17 -0
  42. package/docs/public/dist/components--icons.boot-ZITNU5JP.js +68 -0
  43. package/docs/public/dist/components--image.boot-XEEGHQZF.js +19 -0
  44. package/docs/public/dist/components--input.boot-SGASZG5K.js +7 -0
  45. package/docs/public/dist/components--list.boot-W3XC5MHD.js +55 -0
  46. package/docs/public/dist/components--media.boot-5VFIETZO.js +13 -0
  47. package/docs/public/dist/components--modal.boot-RZUYXBN2.js +47 -0
  48. package/docs/public/dist/components--nav.boot-ODBOHU7O.js +33 -0
  49. package/docs/public/dist/components--pricing.boot-4AQ4ZVBY.js +21 -0
  50. package/docs/public/dist/components--progress.boot-GHAGYZOK.js +30 -0
  51. package/docs/public/dist/components--prose.boot-QANJL6JI.js +67 -0
  52. package/docs/public/dist/components--pullquote.boot-Q2WMNAZU.js +22 -0
  53. package/docs/public/dist/components--radio.boot-TJRDQ2OL.js +75 -0
  54. package/docs/public/dist/components--rating.boot-QBAN6DEL.js +38 -0
  55. package/docs/public/dist/components--search.boot-PXH5O5AG.js +17 -0
  56. package/docs/public/dist/components--section.boot-AQGIYHWW.js +12 -0
  57. package/docs/public/dist/components--segmented.boot-BEVTKEJO.js +33 -0
  58. package/docs/public/dist/components--select.boot-47X5RHOC.js +10 -0
  59. package/docs/public/dist/components--slider.boot-PSRRX7XL.js +47 -0
  60. package/docs/public/dist/components--spinner.boot-MZ5MO2OH.js +22 -0
  61. package/docs/public/dist/components--stack.boot-DI4NJXBF.js +9 -0
  62. package/docs/public/dist/components--stat.boot-QMFUWBQT.js +9 -0
  63. package/docs/public/dist/components--stepper.boot-34PP2NEV.js +22 -0
  64. package/docs/public/dist/components--table.boot-FCQGSFIQ.js +11 -0
  65. package/docs/public/dist/components--testimonial.boot-DWQPDKYG.js +11 -0
  66. package/docs/public/dist/components--textarea.boot-QVXLBOJ5.js +4 -0
  67. package/docs/public/dist/components--timeline.boot-26LN52P2.js +95 -0
  68. package/docs/public/dist/components--toggle.boot-IQQEI76S.js +29 -0
  69. package/docs/public/dist/components--tooltip.boot-LGHCO6NN.js +9 -0
  70. package/docs/public/dist/components.boot-SE6PQ4P7.js +103 -0
  71. package/docs/public/dist/config.boot-DTRRWUE6.js +126 -0
  72. package/docs/public/dist/constraints.boot-DUHDZBMC.js +71 -0
  73. package/docs/public/dist/deploy.boot-SLAD3NI2.js +163 -0
  74. package/docs/public/dist/docs-8e3d4b5c.css +1 -0
  75. package/docs/public/dist/extending.boot-UA3CN243.js +159 -0
  76. package/docs/public/dist/faq.boot-6EQAWLQR.js +43 -0
  77. package/docs/public/dist/getting-started.boot-TDKIFL5U.js +86 -0
  78. package/docs/public/dist/guard.boot-AUHAWTG4.js +80 -0
  79. package/docs/public/dist/home.boot-BVQXRH32.js +383 -0
  80. package/docs/public/dist/how-it-works.boot-LTWAKWKW.js +104 -0
  81. package/docs/public/dist/hydration.boot-JRM6IPJL.js +78 -0
  82. package/docs/public/dist/images.boot-M6ZVKTZS.js +80 -0
  83. package/docs/public/dist/manifest.json +94 -0
  84. package/docs/public/dist/meta.boot-7NXGPHR4.js +79 -0
  85. package/docs/public/dist/mutations.boot-F6F43UDX.js +79 -0
  86. package/docs/public/dist/navigation.boot-AOXWS3ZF.js +57 -0
  87. package/docs/public/dist/performance.boot-C3UPCOBK.js +98 -0
  88. package/docs/public/dist/persist.boot-WT32PQOQ.js +61 -0
  89. package/docs/public/dist/project-structure.boot-FB3LRVJ4.js +63 -0
  90. package/docs/public/dist/prompt-examples.boot-YKR4VDK4.js +31 -0
  91. package/docs/public/dist/pulse-ui-81a85c03.css +1 -0
  92. package/docs/public/dist/raw-responses.boot-M4KA5YXL.js +104 -0
  93. package/docs/public/dist/routing.boot-FNX5FDGH.js +70 -0
  94. package/docs/public/dist/runtime-B73WLANC.js +1 -0
  95. package/docs/public/dist/runtime-KO4BHUQ3.js +49 -0
  96. package/docs/public/dist/runtime-L2HNXIHW.js +59 -0
  97. package/docs/public/dist/runtime-QFURDKA2.js +5 -0
  98. package/docs/public/dist/runtime-UVPXO4IR.js +375 -0
  99. package/docs/public/dist/runtime-VMJA3Z4N.js +10 -0
  100. package/docs/public/dist/runtime-ZJ4FXT5O.js +11 -0
  101. package/docs/public/dist/server-api.boot-K7X3LCFB.js +219 -0
  102. package/docs/public/dist/server-data.boot-Y7HQYC4R.js +157 -0
  103. package/docs/public/dist/slash-commands.boot-V2UV7OW2.js +26 -0
  104. package/docs/public/dist/spec.boot-2WU7ZHCV.js +159 -0
  105. package/docs/public/dist/state.boot-B24GUE3R.js +73 -0
  106. package/docs/public/dist/store.boot-TLIB4XHH.js +150 -0
  107. package/docs/public/dist/streaming.boot-W2DZSMW4.js +80 -0
  108. package/docs/public/dist/stripe.boot-QN3C2GEL.js +164 -0
  109. package/docs/public/dist/supabase.boot-BG4XXLZE.js +303 -0
  110. package/docs/public/dist/testing.boot-6U4WKMTE.js +130 -0
  111. package/docs/public/dist/validation.boot-PQHYGW5B.js +100 -0
  112. package/docs/public/docs.css +2020 -0
  113. package/docs/public/menu.js +83 -0
  114. package/docs/public/pulse-ui.css +2739 -0
  115. package/docs/public/pulse-ui.js +236 -0
  116. package/docs/server.js +192 -0
  117. package/docs/src/lib/component-page.js +47 -0
  118. package/docs/src/lib/highlight.js +255 -0
  119. package/docs/src/lib/layout.js +131 -0
  120. package/docs/src/lib/metrics-store.js +6 -0
  121. package/docs/src/lib/nav.js +159 -0
  122. package/docs/src/lib/stats.js +81 -0
  123. package/docs/src/pages/accessibility.js +157 -0
  124. package/docs/src/pages/actions.js +191 -0
  125. package/docs/src/pages/auth.js +177 -0
  126. package/docs/src/pages/caching.js +95 -0
  127. package/docs/src/pages/components/accordion.js +48 -0
  128. package/docs/src/pages/components/alert.js +35 -0
  129. package/docs/src/pages/components/app-badge.js +41 -0
  130. package/docs/src/pages/components/avatar.js +35 -0
  131. package/docs/src/pages/components/badge.js +36 -0
  132. package/docs/src/pages/components/banner.js +45 -0
  133. package/docs/src/pages/components/breadcrumbs.js +94 -0
  134. package/docs/src/pages/components/button.js +84 -0
  135. package/docs/src/pages/components/card.js +225 -0
  136. package/docs/src/pages/components/carousel.js +72 -0
  137. package/docs/src/pages/components/charts.js +278 -0
  138. package/docs/src/pages/components/checkbox.js +129 -0
  139. package/docs/src/pages/components/cluster.js +47 -0
  140. package/docs/src/pages/components/code-window.js +57 -0
  141. package/docs/src/pages/components/container.js +40 -0
  142. package/docs/src/pages/components/cta.js +53 -0
  143. package/docs/src/pages/components/divider.js +37 -0
  144. package/docs/src/pages/components/empty.js +36 -0
  145. package/docs/src/pages/components/feature.js +60 -0
  146. package/docs/src/pages/components/fieldset.js +65 -0
  147. package/docs/src/pages/components/fileupload.js +127 -0
  148. package/docs/src/pages/components/footer.js +58 -0
  149. package/docs/src/pages/components/grid.js +165 -0
  150. package/docs/src/pages/components/heading.js +107 -0
  151. package/docs/src/pages/components/hero.js +65 -0
  152. package/docs/src/pages/components/icons.js +285 -0
  153. package/docs/src/pages/components/image.js +71 -0
  154. package/docs/src/pages/components/input.js +51 -0
  155. package/docs/src/pages/components/list.js +112 -0
  156. package/docs/src/pages/components/media.js +51 -0
  157. package/docs/src/pages/components/modal.js +111 -0
  158. package/docs/src/pages/components/nav.js +86 -0
  159. package/docs/src/pages/components/pricing.js +68 -0
  160. package/docs/src/pages/components/progress.js +102 -0
  161. package/docs/src/pages/components/prose.js +111 -0
  162. package/docs/src/pages/components/pullquote.js +71 -0
  163. package/docs/src/pages/components/radio.js +194 -0
  164. package/docs/src/pages/components/rating.js +106 -0
  165. package/docs/src/pages/components/search.js +61 -0
  166. package/docs/src/pages/components/section.js +59 -0
  167. package/docs/src/pages/components/segmented.js +121 -0
  168. package/docs/src/pages/components/select.js +45 -0
  169. package/docs/src/pages/components/slider.js +114 -0
  170. package/docs/src/pages/components/spinner.js +73 -0
  171. package/docs/src/pages/components/stack.js +48 -0
  172. package/docs/src/pages/components/stat.js +55 -0
  173. package/docs/src/pages/components/stepper.js +66 -0
  174. package/docs/src/pages/components/table.js +45 -0
  175. package/docs/src/pages/components/testimonial.js +49 -0
  176. package/docs/src/pages/components/textarea.js +31 -0
  177. package/docs/src/pages/components/timeline.js +227 -0
  178. package/docs/src/pages/components/toggle.js +84 -0
  179. package/docs/src/pages/components/tooltip.js +48 -0
  180. package/docs/src/pages/components.js +204 -0
  181. package/docs/src/pages/config.js +193 -0
  182. package/docs/src/pages/constraints.js +99 -0
  183. package/docs/src/pages/deploy.js +233 -0
  184. package/docs/src/pages/extending.js +198 -0
  185. package/docs/src/pages/faq.js +96 -0
  186. package/docs/src/pages/getting-started.js +106 -0
  187. package/docs/src/pages/guard.js +121 -0
  188. package/docs/src/pages/home.js +401 -0
  189. package/docs/src/pages/how-it-works.js +183 -0
  190. package/docs/src/pages/hydration.js +98 -0
  191. package/docs/src/pages/images.js +121 -0
  192. package/docs/src/pages/meta.js +120 -0
  193. package/docs/src/pages/mutations.js +106 -0
  194. package/docs/src/pages/navigation.js +85 -0
  195. package/docs/src/pages/performance.js +157 -0
  196. package/docs/src/pages/persist.js +88 -0
  197. package/docs/src/pages/project-structure.js +90 -0
  198. package/docs/src/pages/prompt-examples.js +186 -0
  199. package/docs/src/pages/raw-responses.js +124 -0
  200. package/docs/src/pages/routing.js +99 -0
  201. package/docs/src/pages/server-api.js +281 -0
  202. package/docs/src/pages/server-data.js +185 -0
  203. package/docs/src/pages/slash-commands.js +55 -0
  204. package/docs/src/pages/spec.js +207 -0
  205. package/docs/src/pages/state.js +101 -0
  206. package/docs/src/pages/store.js +181 -0
  207. package/docs/src/pages/streaming.js +108 -0
  208. package/docs/src/pages/stripe.js +193 -0
  209. package/docs/src/pages/supabase.js +323 -0
  210. package/docs/src/pages/testing.js +198 -0
  211. package/docs/src/pages/validation.js +138 -0
  212. package/examples/contact.js +166 -0
  213. package/examples/counter.js +94 -0
  214. package/examples/dev.server.js +91 -0
  215. package/examples/examples.test.js +394 -0
  216. package/examples/pricing.js +244 -0
  217. package/examples/products.js +191 -0
  218. package/examples/quiz.js +208 -0
  219. package/examples/shared.js +78 -0
  220. package/examples/todos.js +162 -0
  221. package/package.json +75 -0
  222. package/public/.pulse-ui-version +1 -0
  223. package/public/chippy-bird.css +246 -0
  224. package/public/examples/contact.css +119 -0
  225. package/public/examples/counter.css +79 -0
  226. package/public/examples/pricing.css +132 -0
  227. package/public/examples/products.css +100 -0
  228. package/public/examples/quiz.css +200 -0
  229. package/public/examples/todos.css +137 -0
  230. package/public/favicon.ico +0 -0
  231. package/public/log-dashboard.css +383 -0
  232. package/public/pulse-ui.css +2740 -0
  233. package/public/pulse-ui.js +236 -0
  234. package/public/pulse.css +149 -0
  235. package/scripts/build.js +411 -0
  236. package/src/agent/checklist.md +111 -0
  237. package/src/agent/coverage-check.js +66 -0
  238. package/src/agent/guide-components.md +274 -0
  239. package/src/agent/guide-examples.md +54 -0
  240. package/src/agent/guide-routing.md +36 -0
  241. package/src/agent/guide-server.md +258 -0
  242. package/src/agent/guide-spec.md +103 -0
  243. package/src/agent/guide-styles.md +191 -0
  244. package/src/agent/guide.md +979 -0
  245. package/src/agent/identity.md +106 -0
  246. package/src/agent/workflow.md +108 -0
  247. package/src/cli/cli.test.js +82 -0
  248. package/src/cli/dev.js +195 -0
  249. package/src/cli/discover.js +113 -0
  250. package/src/cli/index.js +361 -0
  251. package/src/cli/load-report.js +91 -0
  252. package/src/cli/load-runner.js +121 -0
  253. package/src/cli/report-server.js +723 -0
  254. package/src/cli/report.js +116 -0
  255. package/src/cli/scaffold.archive.js +1371 -0
  256. package/src/cli/scaffold.js +349 -0
  257. package/src/cli/start.js +74 -0
  258. package/src/html.js +19 -0
  259. package/src/mcp/server.js +884 -0
  260. package/src/mcp/validate-worker.js +110 -0
  261. package/src/runtime/image.js +74 -0
  262. package/src/runtime/image.test.js +111 -0
  263. package/src/runtime/index.js +621 -0
  264. package/src/runtime/navigate.js +146 -0
  265. package/src/runtime/runtime.test.js +773 -0
  266. package/src/runtime/ssr.js +464 -0
  267. package/src/runtime/ssr.test.js +421 -0
  268. package/src/runtime/store.js +92 -0
  269. package/src/runtime/toast.js +163 -0
  270. package/src/server/index.js +1386 -0
  271. package/src/server/server.test.js +1248 -0
  272. package/src/spec/schema.js +428 -0
  273. package/src/spec/schema.test.js +291 -0
  274. package/src/store/index.js +102 -0
  275. package/src/store/store.test.js +210 -0
  276. package/src/testing/html.js +283 -0
  277. package/src/testing/index.js +249 -0
  278. package/src/testing/testing.test.js +450 -0
  279. package/src/ui/accordion.js +28 -0
  280. package/src/ui/alert.js +43 -0
  281. package/src/ui/app-badge.js +48 -0
  282. package/src/ui/avatar.js +47 -0
  283. package/src/ui/badge.js +24 -0
  284. package/src/ui/banner.js +26 -0
  285. package/src/ui/breadcrumbs.js +38 -0
  286. package/src/ui/button.js +66 -0
  287. package/src/ui/card.js +34 -0
  288. package/src/ui/carousel.js +59 -0
  289. package/src/ui/charts.js +321 -0
  290. package/src/ui/checkbox.js +65 -0
  291. package/src/ui/cluster.js +44 -0
  292. package/src/ui/code-window.js +39 -0
  293. package/src/ui/container.js +24 -0
  294. package/src/ui/cta.js +37 -0
  295. package/src/ui/divider.js +29 -0
  296. package/src/ui/empty.js +33 -0
  297. package/src/ui/feature.js +33 -0
  298. package/src/ui/fieldset.js +37 -0
  299. package/src/ui/fileupload.js +89 -0
  300. package/src/ui/footer.js +38 -0
  301. package/src/ui/grid.js +36 -0
  302. package/src/ui/heading.js +45 -0
  303. package/src/ui/hero.js +37 -0
  304. package/src/ui/icons.js +161 -0
  305. package/src/ui/index.js +89 -0
  306. package/src/ui/input.js +74 -0
  307. package/src/ui/list.js +36 -0
  308. package/src/ui/media.js +44 -0
  309. package/src/ui/modal.js +80 -0
  310. package/src/ui/nav.js +61 -0
  311. package/src/ui/pricing.js +56 -0
  312. package/src/ui/progress.js +62 -0
  313. package/src/ui/prose.js +29 -0
  314. package/src/ui/pullquote.js +34 -0
  315. package/src/ui/radio.js +102 -0
  316. package/src/ui/rating.js +93 -0
  317. package/src/ui/search.js +77 -0
  318. package/src/ui/section.js +69 -0
  319. package/src/ui/segmented.js +50 -0
  320. package/src/ui/select.js +77 -0
  321. package/src/ui/slider.js +84 -0
  322. package/src/ui/spinner.js +34 -0
  323. package/src/ui/stack.js +36 -0
  324. package/src/ui/stat.js +52 -0
  325. package/src/ui/stepper.js +46 -0
  326. package/src/ui/switch.js +57 -0
  327. package/src/ui/table.js +45 -0
  328. package/src/ui/testimonial.js +48 -0
  329. package/src/ui/textarea.js +72 -0
  330. package/src/ui/timeline.js +72 -0
  331. package/src/ui/tooltip.js +28 -0
  332. package/src/ui/ui.test.js +1241 -0
  333. package/src/ui/uiimage.js +65 -0
  334. package/tsconfig.json +13 -0
  335. package/types/html.d.ts +17 -0
  336. package/types/image.d.ts +70 -0
  337. package/types/index.d.ts +7 -0
  338. package/types/navigate.d.ts +38 -0
  339. package/types/runtime.d.ts +63 -0
  340. package/types/schema.d.ts +243 -0
  341. package/types/server.d.ts +145 -0
  342. package/types/ssr.d.ts +110 -0
  343. package/types/testing.d.ts +154 -0
  344. package/types/ui.d.ts +704 -0
@@ -0,0 +1,236 @@
1
+ /**
2
+ * pulse-ui.js — Vanilla JS for interactive Pulse UI components
3
+ *
4
+ * Handles:
5
+ * - Slider (.ui-slider — live fill + value output)
6
+ * - File Upload (.ui-upload — drag-and-drop, click-to-open)
7
+ * - Modal (data-modal-open, <dialog>, backdrop close)
8
+ * - Nav (.ui-nav — mobile burger menu)
9
+ * - Carousel (.ui-carousel — prev/next/dots)
10
+ *
11
+ * No dependencies. No build step required.
12
+ * CSP-safe: no inline handlers — all behaviour via event delegation.
13
+ * Include once per page: <script src="/pulse-ui.js"></script>
14
+ */
15
+
16
+ // ─── Slider ──────────────────────────────────────────────────────────────────
17
+ // Update --slider-fill on .ui-field wrapper + optional live value output.
18
+
19
+ document.addEventListener('input', (e) => {
20
+ const el = e.target
21
+ if (!el.classList.contains('ui-slider')) return
22
+ const field = el.closest('.ui-field')
23
+ if (!field) return
24
+ const pct = ((el.value - el.min) / (el.max - el.min) * 100).toFixed(2) + '%'
25
+ field.style.setProperty('--slider-fill', pct)
26
+ const out = field.querySelector('.ui-slider-output')
27
+ if (out) out.textContent = el.value
28
+ })
29
+
30
+ // ─── File Upload ─────────────────────────────────────────────────────────────
31
+ // Prevent browser navigating to dropped file anywhere on the page.
32
+ // Handle drag visual feedback + file assignment on drop.
33
+ // Handle click-to-open and keyboard activation.
34
+
35
+ document.addEventListener('dragover', (e) => {
36
+ e.preventDefault()
37
+ if (!(e.target instanceof Element)) return
38
+ const zone = e.target.closest('.ui-upload:not(.ui-upload--disabled)')
39
+ if (zone) zone.classList.add('ui-upload--active')
40
+ })
41
+
42
+ document.addEventListener('dragleave', (e) => {
43
+ if (!(e.target instanceof Element)) return
44
+ const zone = e.target.closest('.ui-upload')
45
+ if (zone && !zone.contains(e.relatedTarget)) {
46
+ zone.classList.remove('ui-upload--active')
47
+ }
48
+ })
49
+
50
+ document.addEventListener('drop', (e) => {
51
+ e.preventDefault()
52
+ if (!(e.target instanceof Element)) return
53
+ const zone = e.target.closest('.ui-upload:not(.ui-upload--disabled)')
54
+ if (!zone) return
55
+ zone.classList.remove('ui-upload--active')
56
+ const input = zone.querySelector('.ui-upload-input')
57
+ if (!input || !e.dataTransfer.files.length) return
58
+ const dt = new DataTransfer()
59
+ Array.from(e.dataTransfer.files).forEach(f => dt.items.add(f))
60
+ input.files = dt.files
61
+ input.dispatchEvent(new Event('change', { bubbles: true }))
62
+ })
63
+
64
+ // File Upload: show selected filename in zone after file chosen
65
+ document.addEventListener('change', (e) => {
66
+ const input = e.target
67
+ if (!input.classList.contains('ui-upload-input')) return
68
+ const zone = input.closest('.ui-upload')
69
+ if (!zone) return
70
+ const textEl = zone.querySelector('.ui-upload-text')
71
+ if (!textEl) return
72
+ if (input.files && input.files.length > 0) {
73
+ const names = Array.from(input.files).map(f => f.name).join(', ')
74
+ textEl.textContent = names
75
+ zone.classList.add('ui-upload--selected')
76
+ } else {
77
+ textEl.innerHTML = 'Drag &amp; drop or <span class="ui-upload-browse">browse</span>'
78
+ zone.classList.remove('ui-upload--selected')
79
+ }
80
+ })
81
+
82
+ // ─── Modal ──────────────────────────────────────────────────────────────────
83
+
84
+ document.addEventListener('click', (e) => {
85
+ // File upload: click zone → open file picker
86
+ // Skip synthetic clicks emitted by input.click() itself to avoid loops
87
+ if (!e.target.classList.contains('ui-upload-input')) {
88
+ const zone = e.target.closest('.ui-upload')
89
+ if (zone) {
90
+ const input = zone.querySelector('.ui-upload-input')
91
+ if (input && !input.disabled) { input.click(); return }
92
+ }
93
+ }
94
+
95
+ // Dialog open: data-dialog-open="dialogId" (first-class) or data-modal-open="dialogId" (compat)
96
+ const trigger = e.target.closest('[data-dialog-open],[data-modal-open]')
97
+ if (trigger) {
98
+ const id = trigger.dataset.dialogOpen ?? trigger.dataset.modalOpen
99
+ const dialog = document.getElementById(id)
100
+ if (dialog && typeof dialog.showModal === 'function') {
101
+ dialog.showModal()
102
+ }
103
+ return
104
+ }
105
+
106
+ // Dialog close: data-dialog-close (closes nearest ancestor <dialog>)
107
+ const closeTarget = e.target.closest('[data-dialog-close]')
108
+ if (closeTarget) {
109
+ const dialog = closeTarget.closest('dialog')
110
+ if (dialog) dialog.close()
111
+ return
112
+ }
113
+
114
+ // Backdrop close — click lands on <dialog> element itself
115
+ if (e.target.tagName === 'DIALOG') {
116
+ e.target.close()
117
+ }
118
+ })
119
+
120
+ // File upload: Enter / Space on focused zone → open picker
121
+ document.addEventListener('keydown', (e) => {
122
+ if (e.key !== 'Enter' && e.key !== ' ') return
123
+ const zone = e.target.closest('.ui-upload')
124
+ if (!zone) return
125
+ const input = zone.querySelector('.ui-upload-input')
126
+ if (input && !input.disabled) { e.preventDefault(); input.click() }
127
+ })
128
+
129
+ // ─── Nav (mobile burger) ────────────────────────────────────────────────────
130
+
131
+ function initNav(el) {
132
+ const burger = el.querySelector('.ui-nav-burger')
133
+ const mobile = el.querySelector('.ui-nav-mobile')
134
+ if (!burger || !mobile) return
135
+
136
+ const open = () => { el.classList.add('ui-nav--open'); burger.setAttribute('aria-expanded', 'true') }
137
+ const close = () => { el.classList.remove('ui-nav--open'); burger.setAttribute('aria-expanded', 'false') }
138
+ const toggle = () => el.classList.contains('ui-nav--open') ? close() : open()
139
+
140
+ burger.addEventListener('click', toggle)
141
+
142
+ document.addEventListener('keydown', (e) => { if (e.key === 'Escape') close() })
143
+ document.addEventListener('click', (e) => { if (!el.contains(e.target)) close() })
144
+
145
+ // Close when a mobile link is clicked (navigating away)
146
+ mobile.querySelectorAll('.ui-nav-link').forEach(a => a.addEventListener('click', close))
147
+ }
148
+
149
+ function runNavs() { document.querySelectorAll('.ui-nav').forEach(initNav) }
150
+ document.readyState === 'loading'
151
+ ? document.addEventListener('DOMContentLoaded', runNavs)
152
+ : runNavs()
153
+ document.addEventListener('pulse:navigate', runNavs)
154
+
155
+ // ─── Carousel ───────────────────────────────────────────────────────────────
156
+
157
+ function initCarousel(el) {
158
+ const track = el.querySelector('.ui-carousel-track')
159
+ const prev = el.querySelector('.ui-carousel-prev')
160
+ const next = el.querySelector('.ui-carousel-next')
161
+ const dots = Array.from(el.querySelectorAll('.ui-carousel-dot'))
162
+ const slides = Array.from(el.querySelectorAll('.ui-carousel-slide'))
163
+
164
+ if (!track || slides.length === 0) return
165
+
166
+ let current = 0
167
+
168
+ const updateArrows = () => {
169
+ if (prev) prev.hidden = current === 0
170
+ if (next) next.hidden = current === slides.length - 1
171
+ }
172
+
173
+ const goTo = (i) => {
174
+ current = Math.max(0, Math.min(i, slides.length - 1))
175
+ track.scrollTo({ left: slides[current].offsetLeft, behavior: 'smooth' })
176
+ dots.forEach((d, j) => {
177
+ const active = j === current
178
+ d.classList.toggle('active', active)
179
+ d.setAttribute('aria-selected', String(active))
180
+ d.setAttribute('tabindex', active ? '0' : '-1')
181
+ })
182
+ updateArrows()
183
+ }
184
+
185
+ prev?.addEventListener('click', () => goTo(current - 1))
186
+ next?.addEventListener('click', () => goTo(current + 1))
187
+ dots.forEach((d, i) => d.addEventListener('click', () => goTo(i)))
188
+
189
+ // Keyboard navigation — roving tabindex for tablist
190
+ el.addEventListener('keydown', (e) => {
191
+ if (!e.target.classList.contains('ui-carousel-dot')) return
192
+ let next = null
193
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
194
+ e.preventDefault()
195
+ next = current > 0 ? current - 1 : slides.length - 1
196
+ } else if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
197
+ e.preventDefault()
198
+ next = current < slides.length - 1 ? current + 1 : 0
199
+ } else if (e.key === 'Home') {
200
+ e.preventDefault()
201
+ next = 0
202
+ } else if (e.key === 'End') {
203
+ e.preventDefault()
204
+ next = slides.length - 1
205
+ }
206
+ if (next !== null) {
207
+ goTo(next)
208
+ dots[next]?.focus()
209
+ }
210
+ })
211
+
212
+ // Sync dot state and arrows when user swipes / scrolls
213
+ track.addEventListener('scroll', () => {
214
+ const trackLeft = track.getBoundingClientRect().left
215
+ const idx = slides.findIndex((s) => Math.abs(s.getBoundingClientRect().left - trackLeft) < 10)
216
+ if (idx !== -1 && idx !== current) {
217
+ current = idx
218
+ dots.forEach((d, j) => {
219
+ const active = j === current
220
+ d.classList.toggle('active', active)
221
+ d.setAttribute('aria-selected', String(active))
222
+ d.setAttribute('tabindex', active ? '0' : '-1')
223
+ })
224
+ updateArrows()
225
+ }
226
+ }, { passive: true })
227
+ }
228
+
229
+ // Initialise all carousels on the page
230
+ const run = () => document.querySelectorAll('.ui-carousel').forEach(initCarousel)
231
+ document.readyState === 'loading'
232
+ ? document.addEventListener('DOMContentLoaded', run)
233
+ : run()
234
+
235
+ // Re-initialise after Pulse client-side navigation swaps the DOM
236
+ document.addEventListener('pulse:navigate', run)
package/docs/server.js ADDED
@@ -0,0 +1,192 @@
1
+ import { createServer } from '../src/server/index.js'
2
+ import { metrics } from './src/lib/stats.js'
3
+ import { metricsStore } from './src/lib/metrics-store.js'
4
+ metricsStore.current = metrics
5
+ import home from './src/pages/home.js'
6
+ import howItWorks from './src/pages/how-it-works.js'
7
+ import faq from './src/pages/faq.js'
8
+ import config from './src/pages/config.js'
9
+ import gettingStarted from './src/pages/getting-started.js'
10
+ import slashCommands from './src/pages/slash-commands.js'
11
+ import promptExamples from './src/pages/prompt-examples.js'
12
+ import components from './src/pages/components.js'
13
+ import projectStructure from './src/pages/project-structure.js'
14
+ import spec from './src/pages/spec.js'
15
+ import state from './src/pages/state.js'
16
+ import mutations from './src/pages/mutations.js'
17
+ import actions from './src/pages/actions.js'
18
+ import validation from './src/pages/validation.js'
19
+ import constraints from './src/pages/constraints.js'
20
+ import persist from './src/pages/persist.js'
21
+ import serverData from './src/pages/server-data.js'
22
+ import routing from './src/pages/routing.js'
23
+ import streaming from './src/pages/streaming.js'
24
+ import caching from './src/pages/caching.js'
25
+ import guard from './src/pages/guard.js'
26
+ import rawResponses from './src/pages/raw-responses.js'
27
+ import serverApi from './src/pages/server-api.js'
28
+ import hydration from './src/pages/hydration.js'
29
+ import navigation from './src/pages/navigation.js'
30
+ import images from './src/pages/images.js'
31
+ import extending from './src/pages/extending.js'
32
+ import deploy from './src/pages/deploy.js'
33
+ import supabase from './src/pages/supabase.js'
34
+ import auth from './src/pages/auth.js'
35
+ import stripe from './src/pages/stripe.js'
36
+ import meta from './src/pages/meta.js'
37
+ import performance from './src/pages/performance.js'
38
+ import accessibility from './src/pages/accessibility.js'
39
+ import testing from './src/pages/testing.js'
40
+
41
+ // Component pages
42
+ import compButton from './src/pages/components/button.js'
43
+ import compBadge from './src/pages/components/badge.js'
44
+ import compCard from './src/pages/components/card.js'
45
+ import compInput from './src/pages/components/input.js'
46
+ import compSelect from './src/pages/components/select.js'
47
+ import compTextarea from './src/pages/components/textarea.js'
48
+ import compAlert from './src/pages/components/alert.js'
49
+ import compStat from './src/pages/components/stat.js'
50
+ import compAvatar from './src/pages/components/avatar.js'
51
+ import compEmpty from './src/pages/components/empty.js'
52
+ import compTable from './src/pages/components/table.js'
53
+ import compNav from './src/pages/components/nav.js'
54
+ import compHero from './src/pages/components/hero.js'
55
+ import compAppBadge from './src/pages/components/app-badge.js'
56
+ import compFeature from './src/pages/components/feature.js'
57
+ import compTestimonial from './src/pages/components/testimonial.js'
58
+ import compPricing from './src/pages/components/pricing.js'
59
+ import compAccordion from './src/pages/components/accordion.js'
60
+ import compContainer from './src/pages/components/container.js'
61
+ import compSection from './src/pages/components/section.js'
62
+ import compGrid from './src/pages/components/grid.js'
63
+ import compStack from './src/pages/components/stack.js'
64
+ import compCluster from './src/pages/components/cluster.js'
65
+ import compDivider from './src/pages/components/divider.js'
66
+ import compBanner from './src/pages/components/banner.js'
67
+ import compMedia from './src/pages/components/media.js'
68
+ import compTooltip from './src/pages/components/tooltip.js'
69
+ import compModal from './src/pages/components/modal.js'
70
+ import compCarousel from './src/pages/components/carousel.js'
71
+ import compCta from './src/pages/components/cta.js'
72
+ import compFooter from './src/pages/components/footer.js'
73
+ import compCodeWindow from './src/pages/components/code-window.js'
74
+ import compFieldset from './src/pages/components/fieldset.js'
75
+ import compToggle from './src/pages/components/toggle.js'
76
+ import compCheckbox from './src/pages/components/checkbox.js'
77
+ import compFileUpload from './src/pages/components/fileupload.js'
78
+ import compSlider from './src/pages/components/slider.js'
79
+ import compSegmented from './src/pages/components/segmented.js'
80
+ import compRadio from './src/pages/components/radio.js'
81
+ import compRating from './src/pages/components/rating.js'
82
+ import compSearch from './src/pages/components/search.js'
83
+ import compSpinner from './src/pages/components/spinner.js'
84
+ import compProgress from './src/pages/components/progress.js'
85
+ import compBreadcrumbs from './src/pages/components/breadcrumbs.js'
86
+ import compStepper from './src/pages/components/stepper.js'
87
+ import compImage from './src/pages/components/image.js'
88
+ import compPullquote from './src/pages/components/pullquote.js'
89
+ import compProse from './src/pages/components/prose.js'
90
+ import compHeading from './src/pages/components/heading.js'
91
+ import compList from './src/pages/components/list.js'
92
+ import compTimeline from './src/pages/components/timeline.js'
93
+ import compCharts from './src/pages/components/charts.js'
94
+ import compIcons from './src/pages/components/icons.js'
95
+
96
+ createServer(
97
+ [
98
+ home,
99
+ howItWorks,
100
+ faq,
101
+ config,
102
+ gettingStarted,
103
+ slashCommands,
104
+ promptExamples,
105
+ components,
106
+ projectStructure,
107
+ spec,
108
+ state,
109
+ mutations,
110
+ actions,
111
+ validation,
112
+ constraints,
113
+ persist,
114
+ serverData,
115
+ routing,
116
+ streaming,
117
+ caching,
118
+ guard,
119
+ rawResponses,
120
+ serverApi,
121
+ hydration,
122
+ navigation,
123
+ images,
124
+ extending,
125
+ deploy,
126
+ supabase,
127
+ auth,
128
+ stripe,
129
+ meta,
130
+ performance,
131
+ accessibility,
132
+ testing,
133
+ // Component pages
134
+ compButton,
135
+ compBadge,
136
+ compCard,
137
+ compInput,
138
+ compSelect,
139
+ compTextarea,
140
+ compAlert,
141
+ compStat,
142
+ compAvatar,
143
+ compEmpty,
144
+ compTable,
145
+ compNav,
146
+ compHero,
147
+ compAppBadge,
148
+ compFeature,
149
+ compTestimonial,
150
+ compPricing,
151
+ compAccordion,
152
+ compContainer,
153
+ compSection,
154
+ compGrid,
155
+ compStack,
156
+ compCluster,
157
+ compDivider,
158
+ compBanner,
159
+ compMedia,
160
+ compTooltip,
161
+ compModal,
162
+ compCarousel,
163
+ compCta,
164
+ compFooter,
165
+ compCodeWindow,
166
+ compFieldset,
167
+ compToggle,
168
+ compCheckbox,
169
+ compFileUpload,
170
+ compSlider,
171
+ compSegmented,
172
+ compRadio,
173
+ compRating,
174
+ compSearch,
175
+ compSpinner,
176
+ compProgress,
177
+ compBreadcrumbs,
178
+ compStepper,
179
+ compImage,
180
+ compPullquote,
181
+ compProse,
182
+ compHeading,
183
+ compList,
184
+ compTimeline,
185
+ compCharts,
186
+ compIcons,
187
+ ],
188
+ {
189
+ port: process.env.PORT ? Number(process.env.PORT) : 4000,
190
+ staticDir: new URL('./public', import.meta.url).pathname,
191
+ }
192
+ )
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Pulse Docs — Component page factory
3
+ *
4
+ * renderComponentPage({ name, description, demos, props, prev, next, currentHref })
5
+ * Returns the HTML content string for a standard component doc page.
6
+ */
7
+
8
+ import { renderLayout, h1, lead, codeBlock } from './layout.js'
9
+ import { highlight } from './highlight.js'
10
+
11
+ export function demo(previewHtml, codeStr, { col = false, scroll = false } = {}) {
12
+ const previewClass = [
13
+ 'demo-preview',
14
+ col ? 'demo-preview--col' : '',
15
+ scroll ? 'demo-preview--scroll' : '',
16
+ ].filter(Boolean).join(' ')
17
+ const toggle = `<button class="demo-theme-toggle" aria-label="Toggle light/dark theme" title="Toggle theme">
18
+ <svg class="demo-theme-toggle__dark" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M21 12.79A9 9 0 1111.21 3a7 7 0 109.79 9.79z"/></svg>
19
+ <svg class="demo-theme-toggle__light" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><circle cx="12" cy="12" r="5"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
20
+ </button>`
21
+ return `<div class="component-demo">
22
+ <div class="${previewClass}">${toggle}<div class="demo-preview-inner">${previewHtml}</div></div>
23
+ <div class="demo-code">${codeBlock(highlight(codeStr, 'js'))}</div>
24
+ </div>`
25
+ }
26
+
27
+ /**
28
+ * @param {object} opts
29
+ * @param {string} opts.currentHref - The route of this page
30
+ * @param {string} opts.name - Component name displayed in h1
31
+ * @param {string} opts.description - Lead paragraph HTML
32
+ * @param {string} opts.content - Full HTML body (demos + props table)
33
+ * @param {object|null} opts.prev - { label, href } or null
34
+ * @param {object|null} opts.next - { label, href } or null
35
+ */
36
+ export function renderComponentPage({ currentHref, name, description, content, prev = null, next = null }) {
37
+ return renderLayout({
38
+ currentHref,
39
+ prev,
40
+ next,
41
+ content: `
42
+ ${h1(name)}
43
+ ${lead(description)}
44
+ ${content}
45
+ `,
46
+ })
47
+ }