@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,401 @@
|
|
|
1
|
+
import { highlight } from '../lib/highlight.js'
|
|
2
|
+
import { metricsStore } from '../lib/metrics-store.js'
|
|
3
|
+
import { codeWindow } from '../../../src/ui/code-window.js'
|
|
4
|
+
|
|
5
|
+
const exampleSpec = highlight(`export default {
|
|
6
|
+
route: '/dashboard',
|
|
7
|
+
meta: {
|
|
8
|
+
title: 'Dashboard — My App',
|
|
9
|
+
styles: ['/app.css'],
|
|
10
|
+
},
|
|
11
|
+
server: {
|
|
12
|
+
data: async (ctx) => {
|
|
13
|
+
const user = await db.users.find(ctx.cookies.userId)
|
|
14
|
+
return { user, stats: await db.stats.forUser(user.id) }
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
state: { filter: 'all' },
|
|
18
|
+
mutations: {
|
|
19
|
+
setFilter: (state, event) => ({ filter: event.target.value }),
|
|
20
|
+
},
|
|
21
|
+
view: (state, server) => \`
|
|
22
|
+
<main id="main-content">
|
|
23
|
+
<h1>Hello, \${server.data.user.name}</h1>
|
|
24
|
+
<select data-event="change:setFilter">
|
|
25
|
+
<option value="all">All time</option>
|
|
26
|
+
<option value="week">This week</option>
|
|
27
|
+
</select>
|
|
28
|
+
<p>\${server.data.stats[state.filter].total} requests</p>
|
|
29
|
+
</main>
|
|
30
|
+
\`,
|
|
31
|
+
}`, 'js')
|
|
32
|
+
|
|
33
|
+
export default {
|
|
34
|
+
route: '/',
|
|
35
|
+
meta: {
|
|
36
|
+
title: 'Pulse — The spec-first web framework',
|
|
37
|
+
description: 'Pulse is a server-first web framework with streaming SSR, zero client JS by default, and Lighthouse 100 built into the architecture. One spec format. One way to build. Production quality by design.',
|
|
38
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
39
|
+
},
|
|
40
|
+
state: {},
|
|
41
|
+
server: {
|
|
42
|
+
metrics: () => metricsStore.current,
|
|
43
|
+
},
|
|
44
|
+
view: (state, server) => `
|
|
45
|
+
<div class="home">
|
|
46
|
+
<nav class="home-nav" aria-label="Site navigation">
|
|
47
|
+
<a href="/" class="logo-link">
|
|
48
|
+
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
49
|
+
<path d="M13 2L4.5 13.5H11L10 22L19.5 10.5H13L13 2Z" fill="var(--accent)" stroke="var(--accent)" stroke-width="1" stroke-linejoin="round"/>
|
|
50
|
+
</svg>
|
|
51
|
+
Pulse
|
|
52
|
+
</a>
|
|
53
|
+
<div class="home-nav-links">
|
|
54
|
+
<a href="/getting-started">Docs</a>
|
|
55
|
+
<a href="https://github.com/invisibleloop/pulse" target="_blank" rel="noopener">GitHub</a>
|
|
56
|
+
</div>
|
|
57
|
+
</nav>
|
|
58
|
+
|
|
59
|
+
<main id="main-content">
|
|
60
|
+
<section class="hero">
|
|
61
|
+
<div class="hero-icon" aria-hidden="true">
|
|
62
|
+
<svg width="48" height="48" viewBox="0 0 24 24" fill="none">
|
|
63
|
+
<path d="M13 2L4.5 13.5H11L10 22L19.5 10.5H13L13 2Z" fill="var(--accent)" stroke="var(--accent)" stroke-width="1" stroke-linejoin="round"/>
|
|
64
|
+
</svg>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="hero-badge">v0.1 — EARLY ACCESS</div>
|
|
67
|
+
<h1 class="hero-title">Describe the outcome. Pulse guarantees it.</h1>
|
|
68
|
+
<p class="hero-subtitle">One spec object per page — server data, state, mutations, and view, co-located in plain JS. Streaming SSR, security headers, and production caching are enforced by the framework, not left to configuration.<br><br>Designed for AI agents. Production-quality architecture.</p>
|
|
69
|
+
<div class="hero-ctas">
|
|
70
|
+
<a href="/getting-started" class="btn-primary">Get Started</a>
|
|
71
|
+
<a href="/spec" class="btn-secondary">Read the Spec</a>
|
|
72
|
+
</div>
|
|
73
|
+
</section>
|
|
74
|
+
|
|
75
|
+
<section class="home-stats">
|
|
76
|
+
<div class="home-stat">
|
|
77
|
+
<span class="home-stat-value">Fast LCP</span>
|
|
78
|
+
<span class="home-stat-label">SSR-first architecture</span>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="home-stat-divider"></div>
|
|
81
|
+
<div class="home-stat">
|
|
82
|
+
<span class="home-stat-value">3.5 kB</span>
|
|
83
|
+
<span class="home-stat-label">Runtime JS, first visit (brotli)</span>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="home-stat-divider"></div>
|
|
86
|
+
<div class="home-stat">
|
|
87
|
+
<span class="home-stat-value">0.00</span>
|
|
88
|
+
<span class="home-stat-label">Cumulative Layout Shift</span>
|
|
89
|
+
</div>
|
|
90
|
+
<div class="home-stat-divider"></div>
|
|
91
|
+
<div class="home-stat">
|
|
92
|
+
<span class="home-stat-value">100</span>
|
|
93
|
+
<span class="home-stat-label">Lighthouse score</span>
|
|
94
|
+
</div>
|
|
95
|
+
</section>
|
|
96
|
+
|
|
97
|
+
<section class="how">
|
|
98
|
+
<div class="how-inner">
|
|
99
|
+
<h2 class="section-label">How it works</h2>
|
|
100
|
+
<div class="how-steps">
|
|
101
|
+
<div class="how-step">
|
|
102
|
+
<div class="how-step-num">1</div>
|
|
103
|
+
<h3>Write a spec</h3>
|
|
104
|
+
<p>Everything for a page lives in one object: server data, state, mutations, view. One format, no conventions to learn.</p>
|
|
105
|
+
</div>
|
|
106
|
+
<div class="how-connector" aria-hidden="true"></div>
|
|
107
|
+
<div class="how-step">
|
|
108
|
+
<div class="how-step-num">2</div>
|
|
109
|
+
<h3>Validate automatically</h3>
|
|
110
|
+
<p>The schema enforces a single, correct structure. Either the spec is valid, or it’s rejected — no ambiguity, no runtime surprises.</p>
|
|
111
|
+
</div>
|
|
112
|
+
<div class="how-connector" aria-hidden="true"></div>
|
|
113
|
+
<div class="how-step">
|
|
114
|
+
<div class="how-step-num">3</div>
|
|
115
|
+
<h3>Ship with it built in</h3>
|
|
116
|
+
<p>Streaming SSR, security headers, and immutable caching come from the framework — not your config. Follow the spec, and the results follow.</p>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</section>
|
|
121
|
+
|
|
122
|
+
<section class="home-code">
|
|
123
|
+
<div class="home-code-inner">
|
|
124
|
+
<div class="home-code-header">
|
|
125
|
+
<h2>Everything in one object</h2>
|
|
126
|
+
<p>Server data, state, mutations, and view are co-located. No split files. No hidden conventions. The spec <strong>is</strong> the page.</p>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="home-code-block">
|
|
129
|
+
${codeWindow({ content: exampleSpec, filename: 'src/pages/dashboard.js', lang: 'JavaScript' })}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</section>
|
|
133
|
+
|
|
134
|
+
<section class="ai-first">
|
|
135
|
+
<div class="section-label">Why Pulse + AI</div>
|
|
136
|
+
<h2 class="ai-first-title">Designed for AI agents. Enforced by the framework.</h2>
|
|
137
|
+
<p class="ai-first-lead"><strong>Traditional frameworks</strong> were built for humans — multiple valid patterns, optional decisions, enough surface area for output to drift. Pulse is different.</p>
|
|
138
|
+
<div class="ai-cols">
|
|
139
|
+
<div class="ai-col">
|
|
140
|
+
<h3 class="ai-col-title ai-col-title--bad">AI + existing frameworks</h3>
|
|
141
|
+
<ul class="ai-col-list">
|
|
142
|
+
<li>Multiple valid patterns per page — the agent picks one, the next picks another.</li>
|
|
143
|
+
<li>Security headers, SSR config, and caching are optional decisions the agent can miss.</li>
|
|
144
|
+
<li>Output drifts over time as different agents make different choices.</li>
|
|
145
|
+
<li>Reviewing AI output requires knowing every pattern it could have used.</li>
|
|
146
|
+
</ul>
|
|
147
|
+
</div>
|
|
148
|
+
<div class="ai-col ai-col--pulse">
|
|
149
|
+
<h3 class="ai-col-title ai-col-title--good">Pulse + AI enforces structure</h3>
|
|
150
|
+
<ul class="ai-col-list">
|
|
151
|
+
<li>One spec format per page.</li>
|
|
152
|
+
<li>Architecture enforces SSR, security, and caching.</li>
|
|
153
|
+
<li>Agents fill in the contract, never off-pattern.</li>
|
|
154
|
+
<li>Reading AI output means reading one JS object — nothing hidden.</li>
|
|
155
|
+
</ul>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</section>
|
|
159
|
+
|
|
160
|
+
<section class="versus">
|
|
161
|
+
<div class="section-label">How Pulse compares</div>
|
|
162
|
+
<h2 class="versus-title">Constraints enforced. Not recommended.</h2>
|
|
163
|
+
<p class="versus-sub">Pulse enforces constraints and correctness out of the box; other frameworks leave it to the developer.</p>
|
|
164
|
+
<div class="versus-table-wrap table-sticky-col">
|
|
165
|
+
<table class="versus-table">
|
|
166
|
+
<thead>
|
|
167
|
+
<tr>
|
|
168
|
+
<th></th>
|
|
169
|
+
<th>
|
|
170
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true" style="vertical-align:middle;margin-right:.35rem">
|
|
171
|
+
<path d="M13 2L4.5 13.5H11L10 22L19.5 10.5H13L13 2Z" fill="var(--accent)" stroke="var(--accent)" stroke-width="1" stroke-linejoin="round"/>
|
|
172
|
+
</svg>Pulse
|
|
173
|
+
</th>
|
|
174
|
+
<th>Next.js / Remix</th>
|
|
175
|
+
<th>SvelteKit</th>
|
|
176
|
+
</tr>
|
|
177
|
+
</thead>
|
|
178
|
+
<tbody>
|
|
179
|
+
<tr>
|
|
180
|
+
<th scope="row">Ways to write a page</th>
|
|
181
|
+
<td class="v-yes">One — the spec schema</td>
|
|
182
|
+
<td class="v-no">App Router, Pages Router, RSC, client components, loaders…</td>
|
|
183
|
+
<td class="v-no">+page.svelte, +page.server.js, load(), form actions…</td>
|
|
184
|
+
</tr>
|
|
185
|
+
<tr>
|
|
186
|
+
<th scope="row">Agent-readable structure</th>
|
|
187
|
+
<td class="v-yes">One JS object per page</td>
|
|
188
|
+
<td class="v-no">Files, folders, magic exports spread across dirs</td>
|
|
189
|
+
<td class="v-no">Files, folders, Svelte syntax</td>
|
|
190
|
+
</tr>
|
|
191
|
+
<tr>
|
|
192
|
+
<th scope="row">SSR out of the box</th>
|
|
193
|
+
<td class="v-yes">Streaming SSR, zero config</td>
|
|
194
|
+
<td class="v-partial">Yes, but client hydration adds JS overhead on every page</td>
|
|
195
|
+
<td class="v-partial">Yes, but requires an adapter and client runtime on every page</td>
|
|
196
|
+
</tr>
|
|
197
|
+
<tr>
|
|
198
|
+
<th scope="row">Client JS shipped</th>
|
|
199
|
+
<td class="v-yes">3.5 kB brotli (shared runtime, first visit)</td>
|
|
200
|
+
<td class="v-no">50-200 kB+ depending on features</td>
|
|
201
|
+
<td class="v-partial">~15 kB brotli</td>
|
|
202
|
+
</tr>
|
|
203
|
+
<tr>
|
|
204
|
+
<th scope="row">Security headers</th>
|
|
205
|
+
<td class="v-yes">On every response, built in</td>
|
|
206
|
+
<td class="v-no">Manual middleware or plugin</td>
|
|
207
|
+
<td class="v-no">Manual hooks setup</td>
|
|
208
|
+
</tr>
|
|
209
|
+
<tr>
|
|
210
|
+
<th scope="row">CLS</th>
|
|
211
|
+
<td class="v-yes">Targets 0.00 — shell renders before data arrives</td>
|
|
212
|
+
<td class="v-partial">Depends on implementation</td>
|
|
213
|
+
<td class="v-partial">Depends on implementation</td>
|
|
214
|
+
</tr>
|
|
215
|
+
<tr>
|
|
216
|
+
<th scope="row">Runtime dependencies</th>
|
|
217
|
+
<td class="v-yes">Zero — pure Node.js HTTP</td>
|
|
218
|
+
<td class="v-no">React, 50+ transitive packages</td>
|
|
219
|
+
<td class="v-no">Svelte runtime + adapters</td>
|
|
220
|
+
</tr>
|
|
221
|
+
<tr>
|
|
222
|
+
<th scope="row">Production build step</th>
|
|
223
|
+
<td class="v-yes">None — <code>node server.js</code> is production</td>
|
|
224
|
+
<td class="v-no">Required — <code>next build</code></td>
|
|
225
|
+
<td class="v-no">Required — <code>vite build</code></td>
|
|
226
|
+
</tr>
|
|
227
|
+
</tbody>
|
|
228
|
+
</table>
|
|
229
|
+
</div>
|
|
230
|
+
</section>
|
|
231
|
+
|
|
232
|
+
<section class="usp-blocks">
|
|
233
|
+
|
|
234
|
+
<div class="usp-block">
|
|
235
|
+
<div class="usp-block-aside">
|
|
236
|
+
<div class="usp-icon">
|
|
237
|
+
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
238
|
+
<path d="M13 2L4.5 13.5H11L10 22L19.5 10.5H13L13 2Z" fill="var(--accent)" stroke="var(--accent)" stroke-width="1.5" stroke-linejoin="round"/>
|
|
239
|
+
</svg>
|
|
240
|
+
</div>
|
|
241
|
+
<h2>Performance by design</h2>
|
|
242
|
+
<p>Pulse does not offer performance as an option — it enforces it structurally. A high Lighthouse score is the baseline. There is nothing to configure because there is nothing to get wrong.</p>
|
|
243
|
+
</div>
|
|
244
|
+
<ul class="usp-points">
|
|
245
|
+
<li>
|
|
246
|
+
<strong>Fast LCP by design.</strong>
|
|
247
|
+
The shell renders and streams instantly. Deferred segments arrive as data resolves — no blocking, no flash.
|
|
248
|
+
</li>
|
|
249
|
+
<li>
|
|
250
|
+
<strong>3.5 kB of JS on first visit.</strong>
|
|
251
|
+
The shared runtime is brotli-compressed and cached across all navigations. Subsequent pages cost 0.35–0.5 kB.
|
|
252
|
+
</li>
|
|
253
|
+
<li>
|
|
254
|
+
<strong>Zero CLS.</strong>
|
|
255
|
+
The shell occupies the correct layout before data arrives. No placeholder juggling, no layout shift.
|
|
256
|
+
</li>
|
|
257
|
+
<li>
|
|
258
|
+
<strong>Immutable bundle caching.</strong>
|
|
259
|
+
Production bundles are content-hashed and served with <code>immutable, max-age=31536000</code>. Browsers cache them forever — deploys are instant for returning visitors.
|
|
260
|
+
</li>
|
|
261
|
+
</ul>
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
<div class="usp-block usp-block-alt">
|
|
265
|
+
<div class="usp-block-aside">
|
|
266
|
+
<div class="usp-icon">
|
|
267
|
+
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
268
|
+
<rect x="3" y="11" width="18" height="11" rx="2"/>
|
|
269
|
+
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
|
270
|
+
</svg>
|
|
271
|
+
</div>
|
|
272
|
+
<h2>Safe by design</h2>
|
|
273
|
+
<p>Security is not a plugin or a checklist in Pulse — it is part of the response pipeline. Every page ships the headers most frameworks leave to the developer to remember.</p>
|
|
274
|
+
</div>
|
|
275
|
+
<ul class="usp-points">
|
|
276
|
+
<li>
|
|
277
|
+
<strong>Security headers on every response.</strong>
|
|
278
|
+
<code>X-Frame-Options</code>, <code>X-Content-Type-Options</code>, <code>Referrer-Policy</code>, <code>Permissions-Policy</code>, <code>Cross-Origin-Opener-Policy</code> — all set automatically, including on 404 and 500 pages.
|
|
279
|
+
</li>
|
|
280
|
+
<li>
|
|
281
|
+
<strong>Declarative state constraints.</strong>
|
|
282
|
+
<code>constraints</code> enforce min/max bounds on state after every mutation. The value can never go out of range regardless of what the client sends.
|
|
283
|
+
</li>
|
|
284
|
+
<li>
|
|
285
|
+
<strong>Co-located validation.</strong>
|
|
286
|
+
Validation rules live next to the state they guard. The agent can see what is being validated and why, in one place.
|
|
287
|
+
</li>
|
|
288
|
+
<li>
|
|
289
|
+
<strong>Guard before data.</strong>
|
|
290
|
+
The <code>guard</code> function runs before any server fetcher executes — authentication and authorisation checks cannot be accidentally bypassed.
|
|
291
|
+
</li>
|
|
292
|
+
</ul>
|
|
293
|
+
</div>
|
|
294
|
+
|
|
295
|
+
<div class="usp-block">
|
|
296
|
+
<div class="usp-block-aside">
|
|
297
|
+
<div class="usp-icon">
|
|
298
|
+
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
299
|
+
<circle cx="12" cy="12" r="10"/>
|
|
300
|
+
<line x1="12" y1="8" x2="12" y2="12"/>
|
|
301
|
+
<line x1="12" y1="16" x2="12.01" y2="16"/>
|
|
302
|
+
</svg>
|
|
303
|
+
</div>
|
|
304
|
+
<h2>Nothing to configure</h2>
|
|
305
|
+
<p>No bundler config. No framework boilerplate. No runtime dependencies to install, audit, or upgrade. Pulse eliminates the category of problems that come from misconfiguration.</p>
|
|
306
|
+
</div>
|
|
307
|
+
<ul class="usp-points">
|
|
308
|
+
<li>
|
|
309
|
+
<strong>Zero runtime dependencies.</strong>
|
|
310
|
+
The server is pure Node.js HTTP. No Express, no Fastify, no React. Nothing to add to <code>package.json</code> to run a production server.
|
|
311
|
+
</li>
|
|
312
|
+
<li>
|
|
313
|
+
<strong>No production build step.</strong>
|
|
314
|
+
<code>node server.js</code> is production. The build step is only needed to generate content-hashed client bundles — the server runs without it in dev.
|
|
315
|
+
</li>
|
|
316
|
+
<li>
|
|
317
|
+
<strong>esbuild only in development.</strong>
|
|
318
|
+
The one dev dependency that compiles client bundles is esbuild. Fast, no plugins to configure, never part of the production runtime.
|
|
319
|
+
</li>
|
|
320
|
+
<li>
|
|
321
|
+
<strong>No framework upgrades breaking your app.</strong>
|
|
322
|
+
Because the spec is a plain JS object with no framework imports in page files, there is no framework API surface to break across versions.
|
|
323
|
+
</li>
|
|
324
|
+
</ul>
|
|
325
|
+
</div>
|
|
326
|
+
|
|
327
|
+
</section>
|
|
328
|
+
|
|
329
|
+
${server.metrics ? `<section class="metrics-report" aria-labelledby="metrics-title">
|
|
330
|
+
<div class="metrics-header">
|
|
331
|
+
<div class="section-label">By the numbers</div>
|
|
332
|
+
<h2 id="metrics-title" class="metrics-title">Performance you can measure.</h2>
|
|
333
|
+
<p class="metrics-generated">Report generated ${server.metrics.generatedAt} · measured from a real Pulse build</p>
|
|
334
|
+
</div>
|
|
335
|
+
<div class="metrics-groups">
|
|
336
|
+
<div class="metrics-group">
|
|
337
|
+
<div class="metrics-group-label">Lighthouse</div>
|
|
338
|
+
<div class="metrics-items">
|
|
339
|
+
${server.metrics.lighthouse.map(m => `
|
|
340
|
+
<div class="metric-item">
|
|
341
|
+
<span class="metric-val metric-val--green">${m.value}</span>
|
|
342
|
+
<span class="metric-label">${m.label}</span>
|
|
343
|
+
</div>`).join('')}
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
<div class="metrics-group">
|
|
347
|
+
<div class="metrics-group-label">Bundle sizes</div>
|
|
348
|
+
<div class="metrics-items">
|
|
349
|
+
${server.metrics.bundles.map(m => `
|
|
350
|
+
<div class="metric-item">
|
|
351
|
+
<span class="metric-val">${m.value}</span>
|
|
352
|
+
<span class="metric-label">${m.label}</span>
|
|
353
|
+
</div>`).join('')}
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
<div class="metrics-group">
|
|
357
|
+
<div class="metrics-group-label">Web Vitals</div>
|
|
358
|
+
<div class="metrics-items">
|
|
359
|
+
${server.metrics.vitals.map(m => `
|
|
360
|
+
<div class="metric-item">
|
|
361
|
+
<span class="metric-val metric-val--green">${m.value}</span>
|
|
362
|
+
<span class="metric-label">${m.label}</span>
|
|
363
|
+
</div>`).join('')}
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
<div class="metrics-group">
|
|
367
|
+
<div class="metrics-group-label">Architecture</div>
|
|
368
|
+
<div class="metrics-items">
|
|
369
|
+
${server.metrics.architecture.map(m => `
|
|
370
|
+
<div class="metric-item">
|
|
371
|
+
<span class="metric-val metric-val--accent">${m.value}</span>
|
|
372
|
+
<span class="metric-label">${m.label}</span>
|
|
373
|
+
</div>`).join('')}
|
|
374
|
+
</div>
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
</section>` : ''}
|
|
378
|
+
|
|
379
|
+
<section class="home-cta">
|
|
380
|
+
<h2>The spec is the contract.<br>Your agent fills it in.</h2>
|
|
381
|
+
<ul class="home-cta-checks">
|
|
382
|
+
<li>Writes the spec</li>
|
|
383
|
+
<li>Validates against the schema</li>
|
|
384
|
+
<li>Checks Lighthouse — desktop and mobile</li>
|
|
385
|
+
<li>Runs the tests</li>
|
|
386
|
+
<li>Ships production quality</li>
|
|
387
|
+
</ul>
|
|
388
|
+
<p>MIT licensed and available now. Production quality is not the goal. It is the starting point.</p>
|
|
389
|
+
<div class="home-cta-actions">
|
|
390
|
+
<a href="/getting-started" class="btn-primary">Get Started</a>
|
|
391
|
+
<a href="/spec" class="btn-secondary">Read the Spec</a>
|
|
392
|
+
</div>
|
|
393
|
+
</section>
|
|
394
|
+
|
|
395
|
+
</main>
|
|
396
|
+
<footer class="home-footer">
|
|
397
|
+
<p>MIT License · <a href="https://github.com/invisibleloop/pulse" target="_blank" rel="noopener">GitHub</a> · <a href="/getting-started">Get started in 2 minutes</a></p>
|
|
398
|
+
</footer>
|
|
399
|
+
</div>
|
|
400
|
+
`,
|
|
401
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { renderLayout, h1, lead, section, callout } from '../lib/layout.js'
|
|
2
|
+
import { prevNext } from '../lib/nav.js'
|
|
3
|
+
import { timeline, card } from '../../../src/ui/index.js'
|
|
4
|
+
|
|
5
|
+
const { prev, next } = prevNext('/how-it-works')
|
|
6
|
+
|
|
7
|
+
const agentFlow = timeline({
|
|
8
|
+
items: [
|
|
9
|
+
{
|
|
10
|
+
dot: '1',
|
|
11
|
+
dotColor: 'accent',
|
|
12
|
+
label: 'Understand',
|
|
13
|
+
content: card({ content: `<strong style="color:var(--ui-text)">Read the guide and inspect the project</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">The agent fetches <code>pulse://workflow</code> and the relevant <code>pulse://guide/*</code> sections, then calls <code>pulse_list_structure</code> to see every existing page, component, and store. Ambiguities are surfaced before any files are touched.</p>` }),
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
dot: '2',
|
|
17
|
+
dotColor: 'accent',
|
|
18
|
+
label: 'Plan',
|
|
19
|
+
content: card({ content: `<strong style="color:var(--ui-text)">Present a plan — wait for confirmation</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">Before writing anything the agent describes what it intends to build: the route, state shape, server data, interactions, and any components or integrations it will use. The task does not proceed until you confirm.</p>` }),
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
dot: '3',
|
|
23
|
+
dotColor: 'accent',
|
|
24
|
+
label: 'Build',
|
|
25
|
+
content: card({ content: `<strong style="color:var(--ui-text)">Write the spec and any supporting files</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">The spec is written as a plain JS object: route, state, server data, mutations, actions, view. The guide constrains every decision — where state lives, how validation is wired, how the view is structured.</p>` }),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
dot: '4',
|
|
29
|
+
dotColor: 'accent',
|
|
30
|
+
label: 'Validate',
|
|
31
|
+
content: card({ content: `<strong style="color:var(--ui-text)">Call <code>pulse_validate</code> — fix all errors and warnings</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">The spec is checked against the Pulse schema. Every error and warning is resolved before moving on — missing hydrate, heading order violations, missing escaping, structural mistakes. A clean output is the gate to the next phase.</p>` }),
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
dot: '5',
|
|
35
|
+
dotColor: 'accent',
|
|
36
|
+
label: 'Browser',
|
|
37
|
+
content: card({ content: `<strong style="color:var(--ui-text)">Screenshot + Lighthouse — desktop and mobile</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">The agent navigates to the route, takes a screenshot to confirm the rendered output, then runs Lighthouse on both desktop and mobile. Accessibility, Best Practices, and SEO must all be 100. Any failure is fixed and re-verified before continuing.</p>` }),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
dot: '6',
|
|
41
|
+
dotColor: 'accent',
|
|
42
|
+
label: 'Tests',
|
|
43
|
+
content: card({ content: `<strong style="color:var(--ui-text)">Write tests, run them, fix failures</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">Unit tests cover any pure logic extracted from the spec. View tests use <code>renderSync</code> / <code>render</code> to assert HTML output. All tests must pass. When fixing a bug, a failing test is written first to pin the behaviour.</p>` }),
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
dot: '7',
|
|
47
|
+
dotColor: 'accent',
|
|
48
|
+
label: 'Review',
|
|
49
|
+
content: card({ content: `<strong style="color:var(--ui-text)">Call <code>pulse_review</code> — only after phases 4–6 all pass</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">The agent switches into reviewer mode — reading the source and rendered output against the full spec checklist. Accessibility, empty states, error handling, component usage, and security are all checked. The review agent is always last.</p>` }),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
dot: '8',
|
|
53
|
+
dotColor: 'accent',
|
|
54
|
+
label: 'Fix and re-verify',
|
|
55
|
+
content: card({ content: `<strong style="color:var(--ui-text)">Fix every review issue — re-run affected gates</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">Every issue raised in review is resolved. Validate, Lighthouse, and tests are re-run to confirm all gates still pass. The task is complete only when every phase clears cleanly.</p>` }),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
dot: '✓',
|
|
59
|
+
dotColor: 'success',
|
|
60
|
+
label: 'Done',
|
|
61
|
+
content: card({ content: `<strong style="color:var(--ui-text)">The spec is the source of truth</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">Validate clean. Lighthouse 100. Tests passing. Review clear. When all four gates pass, the page is done.</p>` }),
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
export default {
|
|
67
|
+
route: '/how-it-works',
|
|
68
|
+
meta: {
|
|
69
|
+
title: 'How It Works — Pulse Docs',
|
|
70
|
+
description: 'How the Pulse MCP server gives an AI agent the knowledge and tools to build Pulse apps correctly.',
|
|
71
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
72
|
+
},
|
|
73
|
+
state: {},
|
|
74
|
+
view: () => renderLayout({
|
|
75
|
+
currentHref: '/how-it-works',
|
|
76
|
+
prev,
|
|
77
|
+
next,
|
|
78
|
+
content: `
|
|
79
|
+
${h1('How It Works')}
|
|
80
|
+
${lead('When you run <code>pulse</code> in a project directory, a Model Context Protocol (MCP) server starts alongside the dev server. That MCP server is what gives your AI agent the knowledge, tools, and guardrails to build Pulse pages correctly — without you having to explain the framework in every prompt.')}
|
|
81
|
+
|
|
82
|
+
${section('mcp', 'The Pulse MCP server')}
|
|
83
|
+
<p>MCP is a standard protocol that connects AI agents to external tools and knowledge. When Claude (or any MCP-compatible agent) opens a Pulse project, the MCP server is automatically detected and two things happen:</p>
|
|
84
|
+
<ul>
|
|
85
|
+
<li>The agent gains access to <strong>resources</strong> — read-only documents it can fetch at any time</li>
|
|
86
|
+
<li>The agent gains access to <strong>tools</strong> — functions it can call to inspect and modify the project</li>
|
|
87
|
+
</ul>
|
|
88
|
+
<p>Together these replace the need to paste documentation into a system prompt or rely on the agent's training data to know how Pulse works.</p>
|
|
89
|
+
|
|
90
|
+
${section('resources', 'Resources: what the agent knows')}
|
|
91
|
+
<p>The MCP server exposes two resources the agent reads before doing any work:</p>
|
|
92
|
+
<ul>
|
|
93
|
+
<li><strong><code>pulse://guide</code></strong> — the complete framework reference, split into focused sections so each fits comfortably in a single read. The agent fetches whichever sections are relevant to the task at hand.</li>
|
|
94
|
+
<li><strong><code>pulse://persona</code></strong> — the quality bar the agent holds itself to: always write correct SSR, always handle errors, never skip empty states, never hardcode colours, never use <code>data-event</code> on text inputs. The persona defines what "done" means.</li>
|
|
95
|
+
</ul>
|
|
96
|
+
|
|
97
|
+
<p>The guide sections are:</p>
|
|
98
|
+
<dl class="definition-list">
|
|
99
|
+
<dt><code>pulse://guide/spec</code></dt>
|
|
100
|
+
<dd>Spec format — state, mutations, actions, streaming SSR, validation, key rules, and form layout.</dd>
|
|
101
|
+
|
|
102
|
+
<dt><code>pulse://guide/server</code></dt>
|
|
103
|
+
<dd>Server data fetchers, global store, persist, cookies, redirects, and POST handling.</dd>
|
|
104
|
+
|
|
105
|
+
<dt><code>pulse://guide/styles</code></dt>
|
|
106
|
+
<dd>CSS tokens, theming, custom fonts, and utility classes.</dd>
|
|
107
|
+
|
|
108
|
+
<dt><code>pulse://guide/routing</code></dt>
|
|
109
|
+
<dd>Client-side navigation, page discovery, and dynamic routes.</dd>
|
|
110
|
+
|
|
111
|
+
<dt><code>pulse://guide/components</code></dt>
|
|
112
|
+
<dd>All UI components and their props, icons, charts, and composition patterns.</dd>
|
|
113
|
+
|
|
114
|
+
<dt><code>pulse://guide/examples</code></dt>
|
|
115
|
+
<dd>Complete working page examples covering common patterns.</dd>
|
|
116
|
+
</dl>
|
|
117
|
+
|
|
118
|
+
${callout('note', 'The guide is authoritative. When there is a question about how something should be structured — where state lives, how validation is wired, how streaming works — the guide answers it. The agent does not guess.')}
|
|
119
|
+
|
|
120
|
+
${section('tools', 'Tools: what the agent can do')}
|
|
121
|
+
<p>The MCP server provides tools across four categories:</p>
|
|
122
|
+
|
|
123
|
+
<h3 class="doc-h3">Scaffolding</h3>
|
|
124
|
+
<dl class="definition-list">
|
|
125
|
+
<dt><code>pulse_create_page</code></dt>
|
|
126
|
+
<dd>Creates a new page spec file from a correct template. The filename determines the route — <code>about.js</code> becomes <code>/about</code>, <code>posts/[slug].js</code> becomes <code>/posts/:slug</code>. The file is written to <code>src/pages/</code> and picked up by the server automatically.</dd>
|
|
127
|
+
|
|
128
|
+
<dt><code>pulse_create_component</code></dt>
|
|
129
|
+
<dd>Creates a reusable view component in <code>src/components/</code>. Components export named functions that return HTML strings — no classes, no JSX, no lifecycle hooks.</dd>
|
|
130
|
+
|
|
131
|
+
<dt><code>pulse_create_store</code></dt>
|
|
132
|
+
<dd>Creates a global store module in <code>src/store/</code>. Stores hold shared state (cart, user session, theme) that multiple pages can subscribe to and mutate.</dd>
|
|
133
|
+
|
|
134
|
+
<dt><code>pulse_create_action</code></dt>
|
|
135
|
+
<dd>Scaffolds a reusable server action — useful for shared form submission logic, API calls, or mutations that appear on more than one page.</dd>
|
|
136
|
+
</dl>
|
|
137
|
+
|
|
138
|
+
<h3 class="doc-h3">Inspection</h3>
|
|
139
|
+
<dl class="definition-list">
|
|
140
|
+
<dt><code>pulse_list_structure</code></dt>
|
|
141
|
+
<dd>Lists every page, component, and store that already exists in the project. The agent calls this before creating anything — to avoid duplication and to understand what the codebase already provides.</dd>
|
|
142
|
+
|
|
143
|
+
<dt><code>pulse_fetch_page</code></dt>
|
|
144
|
+
<dd>Reads the full source of an existing spec file. Used when the agent needs to understand an existing page before editing it, or when a review of the current state is needed.</dd>
|
|
145
|
+
</dl>
|
|
146
|
+
|
|
147
|
+
<h3 class="doc-h3">Validation & review</h3>
|
|
148
|
+
<dl class="definition-list">
|
|
149
|
+
<dt><code>pulse_validate</code></dt>
|
|
150
|
+
<dd>Validates a spec against the Pulse schema. Returns errors (which block progress) and warnings (which must also be resolved). The agent calls this after writing or editing every spec file.</dd>
|
|
151
|
+
|
|
152
|
+
<dt><code>pulse_review</code></dt>
|
|
153
|
+
<dd>Switches the agent into reviewer mode. Returns the spec source, the rendered HTML output, and a structured checklist covering accessibility, empty states, error handling, component usage, security, and correctness. The agent reads its own output critically and fixes every issue before continuing.</dd>
|
|
154
|
+
</dl>
|
|
155
|
+
|
|
156
|
+
<h3 class="doc-h3">Server</h3>
|
|
157
|
+
<dl class="definition-list">
|
|
158
|
+
<dt><code>pulse_restart_server</code></dt>
|
|
159
|
+
<dd>Restarts the Pulse dev server. Called after structural changes — adding a new page, modifying the server entry — to reload all specs and re-register routes.</dd>
|
|
160
|
+
|
|
161
|
+
<dt><code>pulse_build</code></dt>
|
|
162
|
+
<dd>Builds the project for production. Bundles specs, hashes assets, and writes the manifest. Called when the agent needs to verify the production build or prepare a release.</dd>
|
|
163
|
+
</dl>
|
|
164
|
+
|
|
165
|
+
<h3 class="doc-h3">Maintenance</h3>
|
|
166
|
+
<dl class="definition-list">
|
|
167
|
+
<dt><code>pulse_check_version</code></dt>
|
|
168
|
+
<dd>Reports the installed Pulse version and whether an update is available.</dd>
|
|
169
|
+
|
|
170
|
+
<dt><code>pulse_update</code></dt>
|
|
171
|
+
<dd>Updates the Pulse package to the latest version.</dd>
|
|
172
|
+
</dl>
|
|
173
|
+
|
|
174
|
+
${section('flow', 'What happens when you ask for something')}
|
|
175
|
+
|
|
176
|
+
${agentFlow}
|
|
177
|
+
|
|
178
|
+
${section('why-mcp', 'Why MCP instead of a system prompt')}
|
|
179
|
+
<p>A system prompt is static — it cannot see your project, cannot validate your code, and cannot guarantee the agent uses the right version of the framework. The MCP server is dynamic: it reads the guide from the installed version of Pulse, inspects the actual files in your project, and calls real validation on the spec the agent just wrote.</p>
|
|
180
|
+
<p>The result is that the agent builds correctly on the first attempt far more often — not because it is smarter, but because the feedback loop is tighter and the knowledge is always current.</p>
|
|
181
|
+
`,
|
|
182
|
+
}),
|
|
183
|
+
}
|