@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.
- package/.claude/commands/build-page.md +59 -0
- package/.claude/commands/new-doc-page.md +45 -0
- package/.claude/commands/verify.md +52 -0
- package/.claude/pulse-checklist.md +111 -0
- package/.claude/settings.local.json +102 -0
- package/.github/workflows/ci.yml +22 -0
- package/.github/workflows/publish.yml +41 -0
- package/.pulse/load-reports/home/1773432711417.json +22 -0
- package/CLAUDE.md +383 -0
- package/README.md +95 -0
- package/docs/.claude/pulse-checklist.md +111 -0
- package/docs/public/.pulse-ui-version +1 -0
- package/docs/public/dist/accessibility.boot-5DVTARJU.js +115 -0
- package/docs/public/dist/actions.boot-P66HKQEM.js +164 -0
- package/docs/public/dist/auth.boot-IMAJAUPH.js +140 -0
- package/docs/public/dist/caching.boot-DVR6KDE7.js +53 -0
- package/docs/public/dist/components--accordion.boot-3HVKMNWC.js +11 -0
- package/docs/public/dist/components--alert.boot-GCEXOZAC.js +6 -0
- package/docs/public/dist/components--app-badge.boot-DVT3GCHJ.js +6 -0
- package/docs/public/dist/components--avatar.boot-PSW24EVA.js +5 -0
- package/docs/public/dist/components--badge.boot-TYDY2RMK.js +7 -0
- package/docs/public/dist/components--banner.boot-EI5PZSZK.js +7 -0
- package/docs/public/dist/components--breadcrumbs.boot-SMA2E2GO.js +34 -0
- package/docs/public/dist/components--button.boot-J54BQM2E.js +23 -0
- package/docs/public/dist/components--card.boot-PZGNDIB6.js +138 -0
- package/docs/public/dist/components--carousel.boot-TP6LPFZZ.js +12 -0
- package/docs/public/dist/components--charts.boot-2EOYQWKL.js +108 -0
- package/docs/public/dist/components--checkbox.boot-DS5BSL6T.js +54 -0
- package/docs/public/dist/components--cluster.boot-HHVIBBJG.js +9 -0
- package/docs/public/dist/components--code-window.boot-2GR2DV33.js +20 -0
- package/docs/public/dist/components--container.boot-7LOOGK2K.js +5 -0
- package/docs/public/dist/components--cta.boot-FSNZ5YRT.js +11 -0
- package/docs/public/dist/components--divider.boot-3NI2C3QG.js +6 -0
- package/docs/public/dist/components--empty.boot-YX2UR3PV.js +7 -0
- package/docs/public/dist/components--feature.boot-MUD7NSUO.js +13 -0
- package/docs/public/dist/components--fieldset.boot-J7BYHMKF.js +19 -0
- package/docs/public/dist/components--fileupload.boot-NIKVTTPD.js +52 -0
- package/docs/public/dist/components--footer.boot-EYUK5FRG.js +14 -0
- package/docs/public/dist/components--grid.boot-URDQVDDR.js +59 -0
- package/docs/public/dist/components--heading.boot-BPQKU43E.js +44 -0
- package/docs/public/dist/components--hero.boot-4RAPRGAB.js +17 -0
- package/docs/public/dist/components--icons.boot-ZITNU5JP.js +68 -0
- package/docs/public/dist/components--image.boot-XEEGHQZF.js +19 -0
- package/docs/public/dist/components--input.boot-SGASZG5K.js +7 -0
- package/docs/public/dist/components--list.boot-W3XC5MHD.js +55 -0
- package/docs/public/dist/components--media.boot-5VFIETZO.js +13 -0
- package/docs/public/dist/components--modal.boot-RZUYXBN2.js +47 -0
- package/docs/public/dist/components--nav.boot-ODBOHU7O.js +33 -0
- package/docs/public/dist/components--pricing.boot-4AQ4ZVBY.js +21 -0
- package/docs/public/dist/components--progress.boot-GHAGYZOK.js +30 -0
- package/docs/public/dist/components--prose.boot-QANJL6JI.js +67 -0
- package/docs/public/dist/components--pullquote.boot-Q2WMNAZU.js +22 -0
- package/docs/public/dist/components--radio.boot-TJRDQ2OL.js +75 -0
- package/docs/public/dist/components--rating.boot-QBAN6DEL.js +38 -0
- package/docs/public/dist/components--search.boot-PXH5O5AG.js +17 -0
- package/docs/public/dist/components--section.boot-AQGIYHWW.js +12 -0
- package/docs/public/dist/components--segmented.boot-BEVTKEJO.js +33 -0
- package/docs/public/dist/components--select.boot-47X5RHOC.js +10 -0
- package/docs/public/dist/components--slider.boot-PSRRX7XL.js +47 -0
- package/docs/public/dist/components--spinner.boot-MZ5MO2OH.js +22 -0
- package/docs/public/dist/components--stack.boot-DI4NJXBF.js +9 -0
- package/docs/public/dist/components--stat.boot-QMFUWBQT.js +9 -0
- package/docs/public/dist/components--stepper.boot-34PP2NEV.js +22 -0
- package/docs/public/dist/components--table.boot-FCQGSFIQ.js +11 -0
- package/docs/public/dist/components--testimonial.boot-DWQPDKYG.js +11 -0
- package/docs/public/dist/components--textarea.boot-QVXLBOJ5.js +4 -0
- package/docs/public/dist/components--timeline.boot-26LN52P2.js +95 -0
- package/docs/public/dist/components--toggle.boot-IQQEI76S.js +29 -0
- package/docs/public/dist/components--tooltip.boot-LGHCO6NN.js +9 -0
- package/docs/public/dist/components.boot-SE6PQ4P7.js +103 -0
- package/docs/public/dist/config.boot-DTRRWUE6.js +126 -0
- package/docs/public/dist/constraints.boot-DUHDZBMC.js +71 -0
- package/docs/public/dist/deploy.boot-SLAD3NI2.js +163 -0
- package/docs/public/dist/docs-8e3d4b5c.css +1 -0
- package/docs/public/dist/extending.boot-UA3CN243.js +159 -0
- package/docs/public/dist/faq.boot-6EQAWLQR.js +43 -0
- package/docs/public/dist/getting-started.boot-TDKIFL5U.js +86 -0
- package/docs/public/dist/guard.boot-AUHAWTG4.js +80 -0
- package/docs/public/dist/home.boot-BVQXRH32.js +383 -0
- package/docs/public/dist/how-it-works.boot-LTWAKWKW.js +104 -0
- package/docs/public/dist/hydration.boot-JRM6IPJL.js +78 -0
- package/docs/public/dist/images.boot-M6ZVKTZS.js +80 -0
- package/docs/public/dist/manifest.json +94 -0
- package/docs/public/dist/meta.boot-7NXGPHR4.js +79 -0
- package/docs/public/dist/mutations.boot-F6F43UDX.js +79 -0
- package/docs/public/dist/navigation.boot-AOXWS3ZF.js +57 -0
- package/docs/public/dist/performance.boot-C3UPCOBK.js +98 -0
- package/docs/public/dist/persist.boot-WT32PQOQ.js +61 -0
- package/docs/public/dist/project-structure.boot-FB3LRVJ4.js +63 -0
- package/docs/public/dist/prompt-examples.boot-YKR4VDK4.js +31 -0
- package/docs/public/dist/pulse-ui-81a85c03.css +1 -0
- package/docs/public/dist/raw-responses.boot-M4KA5YXL.js +104 -0
- package/docs/public/dist/routing.boot-FNX5FDGH.js +70 -0
- package/docs/public/dist/runtime-B73WLANC.js +1 -0
- package/docs/public/dist/runtime-KO4BHUQ3.js +49 -0
- package/docs/public/dist/runtime-L2HNXIHW.js +59 -0
- package/docs/public/dist/runtime-QFURDKA2.js +5 -0
- package/docs/public/dist/runtime-UVPXO4IR.js +375 -0
- package/docs/public/dist/runtime-VMJA3Z4N.js +10 -0
- package/docs/public/dist/runtime-ZJ4FXT5O.js +11 -0
- package/docs/public/dist/server-api.boot-K7X3LCFB.js +219 -0
- package/docs/public/dist/server-data.boot-Y7HQYC4R.js +157 -0
- package/docs/public/dist/slash-commands.boot-V2UV7OW2.js +26 -0
- package/docs/public/dist/spec.boot-2WU7ZHCV.js +159 -0
- package/docs/public/dist/state.boot-B24GUE3R.js +73 -0
- package/docs/public/dist/store.boot-TLIB4XHH.js +150 -0
- package/docs/public/dist/streaming.boot-W2DZSMW4.js +80 -0
- package/docs/public/dist/stripe.boot-QN3C2GEL.js +164 -0
- package/docs/public/dist/supabase.boot-BG4XXLZE.js +303 -0
- package/docs/public/dist/testing.boot-6U4WKMTE.js +130 -0
- package/docs/public/dist/validation.boot-PQHYGW5B.js +100 -0
- package/docs/public/docs.css +2020 -0
- package/docs/public/menu.js +83 -0
- package/docs/public/pulse-ui.css +2739 -0
- package/docs/public/pulse-ui.js +236 -0
- package/docs/server.js +192 -0
- package/docs/src/lib/component-page.js +47 -0
- package/docs/src/lib/highlight.js +255 -0
- package/docs/src/lib/layout.js +131 -0
- package/docs/src/lib/metrics-store.js +6 -0
- package/docs/src/lib/nav.js +159 -0
- package/docs/src/lib/stats.js +81 -0
- package/docs/src/pages/accessibility.js +157 -0
- package/docs/src/pages/actions.js +191 -0
- package/docs/src/pages/auth.js +177 -0
- package/docs/src/pages/caching.js +95 -0
- package/docs/src/pages/components/accordion.js +48 -0
- package/docs/src/pages/components/alert.js +35 -0
- package/docs/src/pages/components/app-badge.js +41 -0
- package/docs/src/pages/components/avatar.js +35 -0
- package/docs/src/pages/components/badge.js +36 -0
- package/docs/src/pages/components/banner.js +45 -0
- package/docs/src/pages/components/breadcrumbs.js +94 -0
- package/docs/src/pages/components/button.js +84 -0
- package/docs/src/pages/components/card.js +225 -0
- package/docs/src/pages/components/carousel.js +72 -0
- package/docs/src/pages/components/charts.js +278 -0
- package/docs/src/pages/components/checkbox.js +129 -0
- package/docs/src/pages/components/cluster.js +47 -0
- package/docs/src/pages/components/code-window.js +57 -0
- package/docs/src/pages/components/container.js +40 -0
- package/docs/src/pages/components/cta.js +53 -0
- package/docs/src/pages/components/divider.js +37 -0
- package/docs/src/pages/components/empty.js +36 -0
- package/docs/src/pages/components/feature.js +60 -0
- package/docs/src/pages/components/fieldset.js +65 -0
- package/docs/src/pages/components/fileupload.js +127 -0
- package/docs/src/pages/components/footer.js +58 -0
- package/docs/src/pages/components/grid.js +165 -0
- package/docs/src/pages/components/heading.js +107 -0
- package/docs/src/pages/components/hero.js +65 -0
- package/docs/src/pages/components/icons.js +285 -0
- package/docs/src/pages/components/image.js +71 -0
- package/docs/src/pages/components/input.js +51 -0
- package/docs/src/pages/components/list.js +112 -0
- package/docs/src/pages/components/media.js +51 -0
- package/docs/src/pages/components/modal.js +111 -0
- package/docs/src/pages/components/nav.js +86 -0
- package/docs/src/pages/components/pricing.js +68 -0
- package/docs/src/pages/components/progress.js +102 -0
- package/docs/src/pages/components/prose.js +111 -0
- package/docs/src/pages/components/pullquote.js +71 -0
- package/docs/src/pages/components/radio.js +194 -0
- package/docs/src/pages/components/rating.js +106 -0
- package/docs/src/pages/components/search.js +61 -0
- package/docs/src/pages/components/section.js +59 -0
- package/docs/src/pages/components/segmented.js +121 -0
- package/docs/src/pages/components/select.js +45 -0
- package/docs/src/pages/components/slider.js +114 -0
- package/docs/src/pages/components/spinner.js +73 -0
- package/docs/src/pages/components/stack.js +48 -0
- package/docs/src/pages/components/stat.js +55 -0
- package/docs/src/pages/components/stepper.js +66 -0
- package/docs/src/pages/components/table.js +45 -0
- package/docs/src/pages/components/testimonial.js +49 -0
- package/docs/src/pages/components/textarea.js +31 -0
- package/docs/src/pages/components/timeline.js +227 -0
- package/docs/src/pages/components/toggle.js +84 -0
- package/docs/src/pages/components/tooltip.js +48 -0
- package/docs/src/pages/components.js +204 -0
- package/docs/src/pages/config.js +193 -0
- package/docs/src/pages/constraints.js +99 -0
- package/docs/src/pages/deploy.js +233 -0
- package/docs/src/pages/extending.js +198 -0
- package/docs/src/pages/faq.js +96 -0
- package/docs/src/pages/getting-started.js +106 -0
- package/docs/src/pages/guard.js +121 -0
- package/docs/src/pages/home.js +401 -0
- package/docs/src/pages/how-it-works.js +183 -0
- package/docs/src/pages/hydration.js +98 -0
- package/docs/src/pages/images.js +121 -0
- package/docs/src/pages/meta.js +120 -0
- package/docs/src/pages/mutations.js +106 -0
- package/docs/src/pages/navigation.js +85 -0
- package/docs/src/pages/performance.js +157 -0
- package/docs/src/pages/persist.js +88 -0
- package/docs/src/pages/project-structure.js +90 -0
- package/docs/src/pages/prompt-examples.js +186 -0
- package/docs/src/pages/raw-responses.js +124 -0
- package/docs/src/pages/routing.js +99 -0
- package/docs/src/pages/server-api.js +281 -0
- package/docs/src/pages/server-data.js +185 -0
- package/docs/src/pages/slash-commands.js +55 -0
- package/docs/src/pages/spec.js +207 -0
- package/docs/src/pages/state.js +101 -0
- package/docs/src/pages/store.js +181 -0
- package/docs/src/pages/streaming.js +108 -0
- package/docs/src/pages/stripe.js +193 -0
- package/docs/src/pages/supabase.js +323 -0
- package/docs/src/pages/testing.js +198 -0
- package/docs/src/pages/validation.js +138 -0
- package/examples/contact.js +166 -0
- package/examples/counter.js +94 -0
- package/examples/dev.server.js +91 -0
- package/examples/examples.test.js +394 -0
- package/examples/pricing.js +244 -0
- package/examples/products.js +191 -0
- package/examples/quiz.js +208 -0
- package/examples/shared.js +78 -0
- package/examples/todos.js +162 -0
- package/package.json +75 -0
- package/public/.pulse-ui-version +1 -0
- package/public/chippy-bird.css +246 -0
- package/public/examples/contact.css +119 -0
- package/public/examples/counter.css +79 -0
- package/public/examples/pricing.css +132 -0
- package/public/examples/products.css +100 -0
- package/public/examples/quiz.css +200 -0
- package/public/examples/todos.css +137 -0
- package/public/favicon.ico +0 -0
- package/public/log-dashboard.css +383 -0
- package/public/pulse-ui.css +2740 -0
- package/public/pulse-ui.js +236 -0
- package/public/pulse.css +149 -0
- package/scripts/build.js +411 -0
- package/src/agent/checklist.md +111 -0
- package/src/agent/coverage-check.js +66 -0
- package/src/agent/guide-components.md +274 -0
- package/src/agent/guide-examples.md +54 -0
- package/src/agent/guide-routing.md +36 -0
- package/src/agent/guide-server.md +258 -0
- package/src/agent/guide-spec.md +103 -0
- package/src/agent/guide-styles.md +191 -0
- package/src/agent/guide.md +979 -0
- package/src/agent/identity.md +106 -0
- package/src/agent/workflow.md +108 -0
- package/src/cli/cli.test.js +82 -0
- package/src/cli/dev.js +195 -0
- package/src/cli/discover.js +113 -0
- package/src/cli/index.js +361 -0
- package/src/cli/load-report.js +91 -0
- package/src/cli/load-runner.js +121 -0
- package/src/cli/report-server.js +723 -0
- package/src/cli/report.js +116 -0
- package/src/cli/scaffold.archive.js +1371 -0
- package/src/cli/scaffold.js +349 -0
- package/src/cli/start.js +74 -0
- package/src/html.js +19 -0
- package/src/mcp/server.js +884 -0
- package/src/mcp/validate-worker.js +110 -0
- package/src/runtime/image.js +74 -0
- package/src/runtime/image.test.js +111 -0
- package/src/runtime/index.js +621 -0
- package/src/runtime/navigate.js +146 -0
- package/src/runtime/runtime.test.js +773 -0
- package/src/runtime/ssr.js +464 -0
- package/src/runtime/ssr.test.js +421 -0
- package/src/runtime/store.js +92 -0
- package/src/runtime/toast.js +163 -0
- package/src/server/index.js +1386 -0
- package/src/server/server.test.js +1248 -0
- package/src/spec/schema.js +428 -0
- package/src/spec/schema.test.js +291 -0
- package/src/store/index.js +102 -0
- package/src/store/store.test.js +210 -0
- package/src/testing/html.js +283 -0
- package/src/testing/index.js +249 -0
- package/src/testing/testing.test.js +450 -0
- package/src/ui/accordion.js +28 -0
- package/src/ui/alert.js +43 -0
- package/src/ui/app-badge.js +48 -0
- package/src/ui/avatar.js +47 -0
- package/src/ui/badge.js +24 -0
- package/src/ui/banner.js +26 -0
- package/src/ui/breadcrumbs.js +38 -0
- package/src/ui/button.js +66 -0
- package/src/ui/card.js +34 -0
- package/src/ui/carousel.js +59 -0
- package/src/ui/charts.js +321 -0
- package/src/ui/checkbox.js +65 -0
- package/src/ui/cluster.js +44 -0
- package/src/ui/code-window.js +39 -0
- package/src/ui/container.js +24 -0
- package/src/ui/cta.js +37 -0
- package/src/ui/divider.js +29 -0
- package/src/ui/empty.js +33 -0
- package/src/ui/feature.js +33 -0
- package/src/ui/fieldset.js +37 -0
- package/src/ui/fileupload.js +89 -0
- package/src/ui/footer.js +38 -0
- package/src/ui/grid.js +36 -0
- package/src/ui/heading.js +45 -0
- package/src/ui/hero.js +37 -0
- package/src/ui/icons.js +161 -0
- package/src/ui/index.js +89 -0
- package/src/ui/input.js +74 -0
- package/src/ui/list.js +36 -0
- package/src/ui/media.js +44 -0
- package/src/ui/modal.js +80 -0
- package/src/ui/nav.js +61 -0
- package/src/ui/pricing.js +56 -0
- package/src/ui/progress.js +62 -0
- package/src/ui/prose.js +29 -0
- package/src/ui/pullquote.js +34 -0
- package/src/ui/radio.js +102 -0
- package/src/ui/rating.js +93 -0
- package/src/ui/search.js +77 -0
- package/src/ui/section.js +69 -0
- package/src/ui/segmented.js +50 -0
- package/src/ui/select.js +77 -0
- package/src/ui/slider.js +84 -0
- package/src/ui/spinner.js +34 -0
- package/src/ui/stack.js +36 -0
- package/src/ui/stat.js +52 -0
- package/src/ui/stepper.js +46 -0
- package/src/ui/switch.js +57 -0
- package/src/ui/table.js +45 -0
- package/src/ui/testimonial.js +48 -0
- package/src/ui/textarea.js +72 -0
- package/src/ui/timeline.js +72 -0
- package/src/ui/tooltip.js +28 -0
- package/src/ui/ui.test.js +1241 -0
- package/src/ui/uiimage.js +65 -0
- package/tsconfig.json +13 -0
- package/types/html.d.ts +17 -0
- package/types/image.d.ts +70 -0
- package/types/index.d.ts +7 -0
- package/types/navigate.d.ts +38 -0
- package/types/runtime.d.ts +63 -0
- package/types/schema.d.ts +243 -0
- package/types/server.d.ts +145 -0
- package/types/ssr.d.ts +110 -0
- package/types/testing.d.ts +154 -0
- 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 & 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
|
+
}
|