@invisibleloop/pulse 0.1.28 → 0.1.30
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/.github/workflows/publish.yml +11 -20
- package/README.md +1 -1
- package/docs/public/.pulse-ui-version +1 -1
- package/docs/public/docs.css +19 -1
- package/docs/public/pulse-ui.css +1 -0
- package/docs/server.js +5 -2
- package/docs/src/lib/highlight.js +57 -13
- package/docs/src/lib/layout.js +5 -2
- package/docs/src/pages/faq.js +5 -2
- package/docs/src/pages/home.js +9 -5
- package/docs/src/pages/meta.js +21 -0
- package/docs/src/pages/routing.js +12 -1
- package/package.json +1 -1
- package/public/pulse-ui.css +2 -2
- package/src/agent/guide-components.md +1 -1
- package/src/agent/guide-routing.md +20 -0
- package/src/agent/guide-spec.md +10 -1
- package/src/agent/guide-styles.md +16 -1
- package/src/agent/workflow.md +1 -1
- package/src/cli/scaffold.js +63 -2
- package/src/mcp/server.js +34 -18
- package/src/server/index.js +26 -7
- package/src/server/server.test.js +47 -0
- package/src/ui/stat.js +1 -1
- package/src/ui/ui.test.js +6 -0
- package/docs/public/dist/accessibility.boot-5DVTARJU.js +0 -115
- package/docs/public/dist/actions.boot-P66HKQEM.js +0 -164
- package/docs/public/dist/auth.boot-IMAJAUPH.js +0 -140
- package/docs/public/dist/caching.boot-DVR6KDE7.js +0 -53
- package/docs/public/dist/components--accordion.boot-3HVKMNWC.js +0 -11
- package/docs/public/dist/components--alert.boot-GCEXOZAC.js +0 -6
- package/docs/public/dist/components--app-badge.boot-DVT3GCHJ.js +0 -6
- package/docs/public/dist/components--avatar.boot-PSW24EVA.js +0 -5
- package/docs/public/dist/components--badge.boot-TYDY2RMK.js +0 -7
- package/docs/public/dist/components--banner.boot-EI5PZSZK.js +0 -7
- package/docs/public/dist/components--breadcrumbs.boot-SMA2E2GO.js +0 -34
- package/docs/public/dist/components--button.boot-J54BQM2E.js +0 -23
- package/docs/public/dist/components--card.boot-PZGNDIB6.js +0 -138
- package/docs/public/dist/components--carousel.boot-TP6LPFZZ.js +0 -12
- package/docs/public/dist/components--charts.boot-2EOYQWKL.js +0 -108
- package/docs/public/dist/components--checkbox.boot-DS5BSL6T.js +0 -54
- package/docs/public/dist/components--cluster.boot-HHVIBBJG.js +0 -9
- package/docs/public/dist/components--code-window.boot-2GR2DV33.js +0 -20
- package/docs/public/dist/components--container.boot-7LOOGK2K.js +0 -5
- package/docs/public/dist/components--cta.boot-FSNZ5YRT.js +0 -11
- package/docs/public/dist/components--divider.boot-3NI2C3QG.js +0 -6
- package/docs/public/dist/components--empty.boot-YX2UR3PV.js +0 -7
- package/docs/public/dist/components--feature.boot-MUD7NSUO.js +0 -13
- package/docs/public/dist/components--fieldset.boot-J7BYHMKF.js +0 -19
- package/docs/public/dist/components--fileupload.boot-NIKVTTPD.js +0 -52
- package/docs/public/dist/components--footer.boot-EYUK5FRG.js +0 -14
- package/docs/public/dist/components--grid.boot-URDQVDDR.js +0 -59
- package/docs/public/dist/components--heading.boot-BPQKU43E.js +0 -44
- package/docs/public/dist/components--hero.boot-4RAPRGAB.js +0 -17
- package/docs/public/dist/components--icons.boot-ZITNU5JP.js +0 -68
- package/docs/public/dist/components--image.boot-XEEGHQZF.js +0 -19
- package/docs/public/dist/components--input.boot-SGASZG5K.js +0 -7
- package/docs/public/dist/components--list.boot-W3XC5MHD.js +0 -55
- package/docs/public/dist/components--media.boot-5VFIETZO.js +0 -13
- package/docs/public/dist/components--modal.boot-RZUYXBN2.js +0 -47
- package/docs/public/dist/components--nav.boot-ODBOHU7O.js +0 -33
- package/docs/public/dist/components--pricing.boot-4AQ4ZVBY.js +0 -21
- package/docs/public/dist/components--progress.boot-GHAGYZOK.js +0 -30
- package/docs/public/dist/components--prose.boot-QANJL6JI.js +0 -67
- package/docs/public/dist/components--pullquote.boot-Q2WMNAZU.js +0 -22
- package/docs/public/dist/components--radio.boot-TJRDQ2OL.js +0 -75
- package/docs/public/dist/components--rating.boot-QBAN6DEL.js +0 -38
- package/docs/public/dist/components--search.boot-PXH5O5AG.js +0 -17
- package/docs/public/dist/components--section.boot-AQGIYHWW.js +0 -12
- package/docs/public/dist/components--segmented.boot-BEVTKEJO.js +0 -33
- package/docs/public/dist/components--select.boot-47X5RHOC.js +0 -10
- package/docs/public/dist/components--slider.boot-PSRRX7XL.js +0 -47
- package/docs/public/dist/components--spinner.boot-MZ5MO2OH.js +0 -22
- package/docs/public/dist/components--stack.boot-DI4NJXBF.js +0 -9
- package/docs/public/dist/components--stat.boot-QMFUWBQT.js +0 -9
- package/docs/public/dist/components--stepper.boot-34PP2NEV.js +0 -22
- package/docs/public/dist/components--table.boot-FCQGSFIQ.js +0 -11
- package/docs/public/dist/components--testimonial.boot-DWQPDKYG.js +0 -11
- package/docs/public/dist/components--textarea.boot-QVXLBOJ5.js +0 -4
- package/docs/public/dist/components--timeline.boot-26LN52P2.js +0 -95
- package/docs/public/dist/components--toggle.boot-IQQEI76S.js +0 -29
- package/docs/public/dist/components--tooltip.boot-LGHCO6NN.js +0 -9
- package/docs/public/dist/components.boot-SE6PQ4P7.js +0 -103
- package/docs/public/dist/config.boot-DTRRWUE6.js +0 -126
- package/docs/public/dist/constraints.boot-DUHDZBMC.js +0 -71
- package/docs/public/dist/deploy.boot-SLAD3NI2.js +0 -163
- package/docs/public/dist/docs-8e3d4b5c.css +0 -1
- package/docs/public/dist/extending.boot-UA3CN243.js +0 -159
- package/docs/public/dist/faq.boot-6EQAWLQR.js +0 -43
- package/docs/public/dist/getting-started.boot-TDKIFL5U.js +0 -86
- package/docs/public/dist/guard.boot-AUHAWTG4.js +0 -80
- package/docs/public/dist/home.boot-BVQXRH32.js +0 -383
- package/docs/public/dist/how-it-works.boot-LTWAKWKW.js +0 -104
- package/docs/public/dist/hydration.boot-JRM6IPJL.js +0 -78
- package/docs/public/dist/images.boot-M6ZVKTZS.js +0 -80
- package/docs/public/dist/manifest.json +0 -94
- package/docs/public/dist/meta.boot-7NXGPHR4.js +0 -79
- package/docs/public/dist/mutations.boot-F6F43UDX.js +0 -79
- package/docs/public/dist/navigation.boot-AOXWS3ZF.js +0 -57
- package/docs/public/dist/performance.boot-C3UPCOBK.js +0 -98
- package/docs/public/dist/persist.boot-WT32PQOQ.js +0 -61
- package/docs/public/dist/project-structure.boot-FB3LRVJ4.js +0 -63
- package/docs/public/dist/prompt-examples.boot-YKR4VDK4.js +0 -31
- package/docs/public/dist/pulse-ui-81a85c03.css +0 -1
- package/docs/public/dist/raw-responses.boot-M4KA5YXL.js +0 -104
- package/docs/public/dist/routing.boot-FNX5FDGH.js +0 -70
- package/docs/public/dist/runtime-B73WLANC.js +0 -1
- package/docs/public/dist/runtime-KO4BHUQ3.js +0 -49
- package/docs/public/dist/runtime-L2HNXIHW.js +0 -59
- package/docs/public/dist/runtime-QFURDKA2.js +0 -5
- package/docs/public/dist/runtime-UVPXO4IR.js +0 -375
- package/docs/public/dist/runtime-VMJA3Z4N.js +0 -10
- package/docs/public/dist/runtime-ZJ4FXT5O.js +0 -11
- package/docs/public/dist/server-api.boot-K7X3LCFB.js +0 -219
- package/docs/public/dist/server-data.boot-Y7HQYC4R.js +0 -157
- package/docs/public/dist/slash-commands.boot-V2UV7OW2.js +0 -26
- package/docs/public/dist/spec.boot-2WU7ZHCV.js +0 -159
- package/docs/public/dist/state.boot-B24GUE3R.js +0 -73
- package/docs/public/dist/store.boot-TLIB4XHH.js +0 -150
- package/docs/public/dist/streaming.boot-W2DZSMW4.js +0 -80
- package/docs/public/dist/stripe.boot-QN3C2GEL.js +0 -164
- package/docs/public/dist/supabase.boot-BG4XXLZE.js +0 -303
- package/docs/public/dist/testing.boot-6U4WKMTE.js +0 -130
- package/docs/public/dist/validation.boot-PQHYGW5B.js +0 -100
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import{a as o}from"./runtime-QFURDKA2.js";import{a as i,b as u,c as d,d as h,e,g as t,h as s,i as n}from"./runtime-L2HNXIHW.js";import{a as c,b as l}from"./runtime-B73WLANC.js";var{prev:p,next:A}=i("/auth"),a={route:"/auth",meta:{title:"Auth (Auth0) \u2014 Pulse Docs",description:"Integrating Auth0 OAuth authentication with Pulse using guard functions, ctx.setCookie, and raw response specs.",styles:["/docs.css"]},state:{},view:()=>u({currentHref:"/auth",prev:p,next:A,content:`
|
|
2
|
-
${d("Auth (Auth0)")}
|
|
3
|
-
${h("Pulse integrates with Auth0 \u2014 and any OAuth 2.0 provider \u2014 using plain HTTP redirects and a token exchange. No client-side auth SDK is required. Protected routes enforce access through <code>guard</code>, which runs before any data fetcher can execute.")}
|
|
4
|
-
|
|
5
|
-
${n("info","No Auth0 SDK required. The OAuth flow is plain HTTP redirects and a token exchange fetch \u2014 no client-side library needed.")}
|
|
6
|
-
|
|
7
|
-
${e("setup","Setup")}
|
|
8
|
-
<p>Register your application in Auth0 and note your credentials. Store them in environment variables \u2014 never hardcode them in specs.</p>
|
|
9
|
-
${t(o(`# .env
|
|
10
|
-
AUTH0_DOMAIN=your-tenant.auth0.com
|
|
11
|
-
AUTH0_CLIENT_ID=your_client_id
|
|
12
|
-
AUTH0_CLIENT_SECRET=your_client_secret
|
|
13
|
-
AUTH0_CALLBACK_URL=http://localhost:3000/auth/callback`,"bash"))}
|
|
14
|
-
|
|
15
|
-
${e("login","Login route")}
|
|
16
|
-
<p>The login route is a raw response spec that redirects the browser to Auth0's authorization endpoint.</p>
|
|
17
|
-
${t(o(`// src/pages/auth/login.js
|
|
18
|
-
const {
|
|
19
|
-
AUTH0_DOMAIN, AUTH0_CLIENT_ID, AUTH0_CALLBACK_URL
|
|
20
|
-
} = process.env
|
|
21
|
-
|
|
22
|
-
export default {
|
|
23
|
-
route: '/auth/login',
|
|
24
|
-
contentType: 'text/html',
|
|
25
|
-
|
|
26
|
-
render: (ctx) => {
|
|
27
|
-
const params = new URLSearchParams({
|
|
28
|
-
response_type: 'code',
|
|
29
|
-
client_id: AUTH0_CLIENT_ID,
|
|
30
|
-
redirect_uri: AUTH0_CALLBACK_URL,
|
|
31
|
-
scope: 'openid profile email',
|
|
32
|
-
state: crypto.randomUUID(),
|
|
33
|
-
})
|
|
34
|
-
ctx.setHeader('Location', \`https://\${AUTH0_DOMAIN}/authorize?\${params}\`)
|
|
35
|
-
return { redirect: \`https://\${AUTH0_DOMAIN}/authorize?\${params}\` }
|
|
36
|
-
},
|
|
37
|
-
}`,"js"))}
|
|
38
|
-
|
|
39
|
-
${e("callback","Callback route")}
|
|
40
|
-
<p>Auth0 redirects back to <code>/auth/callback</code> with a <code>code</code> query parameter. The server exchanges it for tokens, sets a session cookie, and redirects to the app.</p>
|
|
41
|
-
${t(o(`// src/pages/auth/callback.js
|
|
42
|
-
const {
|
|
43
|
-
AUTH0_DOMAIN, AUTH0_CLIENT_ID,
|
|
44
|
-
AUTH0_CLIENT_SECRET, AUTH0_CALLBACK_URL
|
|
45
|
-
} = process.env
|
|
46
|
-
|
|
47
|
-
export default {
|
|
48
|
-
route: '/auth/callback',
|
|
49
|
-
contentType: 'text/html',
|
|
50
|
-
|
|
51
|
-
server: {
|
|
52
|
-
session: async (ctx) => {
|
|
53
|
-
const { code } = ctx.query
|
|
54
|
-
if (!code) return null
|
|
55
|
-
|
|
56
|
-
// Exchange auth code for tokens
|
|
57
|
-
const res = await fetch(\`https://\${AUTH0_DOMAIN}/oauth/token\`, {
|
|
58
|
-
method: 'POST',
|
|
59
|
-
headers: { 'Content-Type': 'application/json' },
|
|
60
|
-
body: JSON.stringify({
|
|
61
|
-
grant_type: 'authorization_code',
|
|
62
|
-
client_id: AUTH0_CLIENT_ID,
|
|
63
|
-
client_secret: AUTH0_CLIENT_SECRET,
|
|
64
|
-
redirect_uri: AUTH0_CALLBACK_URL,
|
|
65
|
-
code,
|
|
66
|
-
}),
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
if (!res.ok) return null
|
|
70
|
-
const { access_token, id_token } = await res.json()
|
|
71
|
-
|
|
72
|
-
// Set a secure session cookie with the access token
|
|
73
|
-
ctx.setCookie('session', access_token, {
|
|
74
|
-
httpOnly: true,
|
|
75
|
-
secure: process.env.NODE_ENV === 'production',
|
|
76
|
-
sameSite: 'Lax',
|
|
77
|
-
maxAge: 86400, // 24 hours
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
return access_token
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
render: (ctx, server) => {
|
|
85
|
-
if (!server.session) return { redirect: '/auth/login' }
|
|
86
|
-
return { redirect: '/' }
|
|
87
|
-
},
|
|
88
|
-
}`,"js"))}
|
|
89
|
-
|
|
90
|
-
${e("logout","Logout route")}
|
|
91
|
-
<p>Clear the session cookie and redirect to Auth0's logout endpoint to invalidate the session there too.</p>
|
|
92
|
-
${t(o(`// src/pages/auth/logout.js
|
|
93
|
-
const { AUTH0_DOMAIN, AUTH0_CLIENT_ID } = process.env
|
|
94
|
-
|
|
95
|
-
export default {
|
|
96
|
-
route: '/auth/logout',
|
|
97
|
-
contentType: 'text/html',
|
|
98
|
-
|
|
99
|
-
render: (ctx) => {
|
|
100
|
-
// Expire the session cookie
|
|
101
|
-
ctx.setCookie('session', '', { maxAge: 0 })
|
|
102
|
-
|
|
103
|
-
const returnTo = encodeURIComponent('http://localhost:3000')
|
|
104
|
-
return { redirect: \`https://\${AUTH0_DOMAIN}/v2/logout?client_id=\${AUTH0_CLIENT_ID}&returnTo=\${returnTo}\` }
|
|
105
|
-
},
|
|
106
|
-
}`,"js"))}
|
|
107
|
-
|
|
108
|
-
${e("guard","Protecting routes")}
|
|
109
|
-
<p>Use <code>guard</code> to verify the session token before any server data is fetched. For production, verify the JWT signature locally rather than calling Auth0 on every request.</p>
|
|
110
|
-
${t(o(`// src/pages/dashboard.js
|
|
111
|
-
export default {
|
|
112
|
-
route: '/dashboard',
|
|
113
|
-
|
|
114
|
-
guard: async (ctx) => {
|
|
115
|
-
if (!ctx.cookies.session) return { redirect: '/auth/login' }
|
|
116
|
-
|
|
117
|
-
// Optional: verify JWT signature for production
|
|
118
|
-
// const user = await verifyJwt(ctx.cookies.session)
|
|
119
|
-
// if (!user) return { redirect: '/auth/login' }
|
|
120
|
-
},
|
|
121
|
-
|
|
122
|
-
server: {
|
|
123
|
-
profile: async (ctx) => fetchUserProfile(ctx.cookies.session),
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
state: {},
|
|
127
|
-
view: (state, server) => \`
|
|
128
|
-
<main id="main-content">
|
|
129
|
-
<h1>Welcome, \${server.profile.name}</h1>
|
|
130
|
-
</main>
|
|
131
|
-
\`,
|
|
132
|
-
}`,"js"))}
|
|
133
|
-
|
|
134
|
-
${n("info","Guard runs before server data fetchers \u2014 if the session is invalid the profile fetch never happens.")}
|
|
135
|
-
|
|
136
|
-
${e("ctx-reference","ctx reference")}
|
|
137
|
-
${s(["Method","Description"],[["<code>ctx.cookies.session</code>","Read the session cookie set during OAuth callback"],["<code>ctx.setCookie(name, value, opts)</code>","Set a response cookie \u2014 used in callback and logout routes"],["<code>ctx.setHeader(name, value)</code>","Set an arbitrary response header"]])}
|
|
138
|
-
|
|
139
|
-
${s(["setCookie option","Type","Description"],[["<code>httpOnly</code>","boolean","Prevents JS access \u2014 always use for session cookies"],["<code>secure</code>","boolean","HTTPS only \u2014 set <code>true</code> in production"],["<code>sameSite</code>",'<code>"Lax" | "Strict" | "None"</code>',"<code>Lax</code> works for most OAuth flows"],["<code>maxAge</code>","number","Lifetime in seconds \u2014 omit for session cookie"],["<code>path</code>","string","Defaults to <code>/</code>"],["<code>domain</code>","string","Scope to a domain \u2014 omit for current host"]])}
|
|
140
|
-
`})};var r=document.getElementById("pulse-root");r&&!r.dataset.pulseMounted&&(r.dataset.pulseMounted="1",c(a,r,window.__PULSE_SERVER__||{},{ssr:!0}),l(r,c));var y=a;export{y as default};
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import{a as s}from"./runtime-QFURDKA2.js";import{a as i,b as n,c as l,d as h,e,g as c,h as a,i as t}from"./runtime-L2HNXIHW.js";import{a as d,b as u}from"./runtime-B73WLANC.js";var{prev:p,next:m}=i("/caching"),r={route:"/caching",meta:{title:"Caching \u2014 Pulse Docs",description:"HTTP cache headers, in-process server data caching, and asset caching in Pulse.",styles:["/docs.css"]},state:{},view:()=>n({currentHref:"/caching",prev:p,next:m,content:`
|
|
2
|
-
${l("Caching")}
|
|
3
|
-
${h("Pulse handles asset caching automatically \u2014 production bundles are content-hashed and served with immutable cache headers. Page caching is controlled declaratively in the spec: <code>serverTtl</code> for in-process data, <code>cache</code> for HTTP headers. Nothing is cached by default unless you declare it.")}
|
|
4
|
-
|
|
5
|
-
${e("server-ttl","serverTtl \u2014 in-process data cache")}
|
|
6
|
-
<p><code>serverTtl</code> is a number of seconds to cache the result of <code>server.data()</code> in memory. Subsequent requests within the TTL window skip the async data fetch entirely and serve the cached result.</p>
|
|
7
|
-
${c(s(`export default {
|
|
8
|
-
route: '/homepage',
|
|
9
|
-
serverTtl: 60, // cache server data for 60 seconds
|
|
10
|
-
server: {
|
|
11
|
-
data: async () => ({
|
|
12
|
-
featured: await db.products.getFeatured(),
|
|
13
|
-
stats: await analytics.getGlobalStats(),
|
|
14
|
-
}),
|
|
15
|
-
},
|
|
16
|
-
}`,"js"))}
|
|
17
|
-
${a(["Value","Behaviour"],[["<code>undefined</code> (default)","No caching \u2014 <code>server.data()</code> runs on every request"],["<code>0</code>","No caching (same as undefined)"],["<code>60</code>","Cache for 60 seconds \u2014 at most one database hit per minute"],["<code>3600</code>","Cache for 1 hour"]])}
|
|
18
|
-
${t("note","The in-process cache is per-process and per-route. It is not shared across multiple server instances. Use a distributed cache (Redis, etc.) for cross-instance consistency.")}
|
|
19
|
-
${t("warning","In development, setting a long <code>serverTtl</code> can mask stale data. Set it to <code>0</code> or omit it during development, and add it when deploying to production.")}
|
|
20
|
-
|
|
21
|
-
${e("http-cache","cache \u2014 HTTP response headers")}
|
|
22
|
-
<p>The <code>cache</code> field controls the <code>Cache-Control</code> header sent with the page HTML response. This tells browsers and CDNs how to cache the response.</p>
|
|
23
|
-
${c(s(`export default {
|
|
24
|
-
route: '/blog/:slug',
|
|
25
|
-
cache: {
|
|
26
|
-
public: true, // allow CDN/proxy caching
|
|
27
|
-
maxAge: 300, // cache for 5 minutes
|
|
28
|
-
staleWhileRevalidate: 86400, // serve stale for up to 24 hours while revalidating
|
|
29
|
-
},
|
|
30
|
-
// ...
|
|
31
|
-
}`,"js"))}
|
|
32
|
-
|
|
33
|
-
${e("cache-fields","Cache field reference")}
|
|
34
|
-
${a(["Field","Type","Description"],[["<code>public</code>","<code>boolean</code>","If true, emits <code>public</code> \u2014 allows CDN/proxy caching. Default: <code>private</code>."],["<code>maxAge</code>","<code>number</code>","Seconds before the response is considered stale. Emits <code>max-age=N</code>."],["<code>staleWhileRevalidate</code>","<code>number</code>","Seconds to serve stale content while revalidating in the background. Emits <code>stale-while-revalidate=N</code>."]])}
|
|
35
|
-
${c(s(`// private page \u2014 user-specific content
|
|
36
|
-
cache: { public: false, maxAge: 0 }
|
|
37
|
-
// \u2192 Cache-Control: private, no-store
|
|
38
|
-
|
|
39
|
-
// public marketing page \u2014 cached at CDN
|
|
40
|
-
cache: { public: true, maxAge: 3600, staleWhileRevalidate: 86400 }
|
|
41
|
-
// \u2192 Cache-Control: public, max-age=3600, stale-while-revalidate=86400`,"js"))}
|
|
42
|
-
|
|
43
|
-
${e("html-default","Default HTML caching")}
|
|
44
|
-
<p>By default, Pulse sends <code>Cache-Control: no-store</code> for all HTML responses. Users always see fresh content \u2014 stale HTML is never served from browser or proxy caches unless you explicitly declare a <code>cache</code> policy in the spec.</p>
|
|
45
|
-
|
|
46
|
-
${e("asset-caching","Asset caching")}
|
|
47
|
-
<p>Static assets in <code>public/</code> receive <code>Cache-Control: max-age=3600</code> (one hour).</p>
|
|
48
|
-
<p>Production bundles in <code>public/dist/</code> receive <code>Cache-Control: public, max-age=31536000, immutable</code> (one year, immutable). This is guaranteed safe because bundle filenames include a content hash \u2014 code changes produce a new hash, and browsers fetch the updated file automatically. There is nothing to configure.</p>
|
|
49
|
-
|
|
50
|
-
${e("dev-vs-prod","Development vs production")}
|
|
51
|
-
${a(["Resource","Development","Production"],[["HTML pages","<code>no-store</code>","<code>no-store</code> (or your <code>cache</code> config)"],["Static assets (<code>/public/*</code>)","<code>max-age=3600</code>","<code>max-age=3600</code>"],["JS bundles (<code>/dist/*</code>)","N/A (source files served directly)","<code>immutable, max-age=31536000</code>"]])}
|
|
52
|
-
${t("tip","For maximum performance, combine <code>serverTtl</code> for expensive database queries with a short <code>cache.maxAge</code> and a generous <code>cache.staleWhileRevalidate</code>. Users get fast responses; data stays reasonably fresh.")}
|
|
53
|
-
`})};var o=document.getElementById("pulse-root");o&&!o.dataset.pulseMounted&&(o.dataset.pulseMounted="1",d(r,o,window.__PULSE_SERVER__||{},{ssr:!0}),u(o,d));var C=r;export{C as default};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import{a as i,b as a}from"./runtime-ZJ4FXT5O.js";import{Ka as c}from"./runtime-UVPXO4IR.js";import"./runtime-VMJA3Z4N.js";import"./runtime-QFURDKA2.js";import{a as n,h as r}from"./runtime-L2HNXIHW.js";import{a as o,b as s}from"./runtime-B73WLANC.js";var{prev:d,next:p}=n("/components/accordion"),t={route:"/components/accordion",meta:{title:"Accordion \u2014 Pulse Docs",description:"Accordion component for Pulse UI.",styles:["/pulse-ui.css","/docs.css"]},state:{},view:()=>a({currentHref:"/components/accordion",prev:d,next:p,name:"accordion",description:"Collapsible FAQ items built on native <code><details></code>/<code><summary></code> \u2014 no JavaScript required. The open/close animation is handled entirely by the browser.",content:`
|
|
2
|
-
${i(c({items:[{question:"Is there a free plan?",answer:"Yes \u2014 the free plan includes up to 3 pages and community support, with no credit card required."},{question:"Can I cancel anytime?",answer:"Absolutely. There are no contracts or lock-in periods. Cancel from your account settings at any time."},{question:"Does it work on older devices?",answer:"The app supports iOS 14+ and Android 8+, covering over 97% of active devices."}]}),`accordion({
|
|
3
|
-
items: [
|
|
4
|
-
{ question: 'Is there a free plan?', answer: 'Yes \u2014 up to 3 pages, no card required.' },
|
|
5
|
-
{ question: 'Can I cancel anytime?', answer: 'No contracts. Cancel from your account.' },
|
|
6
|
-
{ question: 'Works on older devices?', answer: 'iOS 14+ and Android 8+.' },
|
|
7
|
-
],
|
|
8
|
-
})`)}
|
|
9
|
-
|
|
10
|
-
${r(["Prop","Type","Default",""],[["<code>items</code>","array","[]","<code>{ question: string, answer: string }[]</code>"]])}
|
|
11
|
-
`})};var e=document.getElementById("pulse-root");e&&!e.dataset.pulseMounted&&(e.dataset.pulseMounted="1",o(t,e,window.__PULSE_SERVER__||{},{ssr:!0}),s(e,o));var q=t;export{q as default};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import{a as i,b as c}from"./runtime-ZJ4FXT5O.js";import{Ba as e}from"./runtime-UVPXO4IR.js";import"./runtime-VMJA3Z4N.js";import"./runtime-QFURDKA2.js";import{a as r}from"./runtime-L2HNXIHW.js";import{a as n,b as a}from"./runtime-B73WLANC.js";var{prev:s,next:d}=r("/components/alert"),o={route:"/components/alert",meta:{title:"Alert \u2014 Pulse Docs",description:"Alert component for Pulse UI.",styles:["/pulse-ui.css","/docs.css"]},state:{},view:()=>c({currentHref:"/components/alert",prev:s,next:d,name:"alert",description:'<code>error</code> and <code>warning</code> use <code>role="alert"</code> \u2014 screen readers announce them immediately. <code>info</code> and <code>success</code> use <code>role="status"</code> for a polite announcement.',content:`
|
|
2
|
-
${i(e({variant:"info",content:"Your account is pending email verification."})+e({variant:"success",title:"Saved",content:"Your changes have been saved."})+e({variant:"warning",title:"Heads up",content:"This action cannot be undone."})+e({variant:"error",title:"Failed",content:"Something went wrong. Please try again."}),`alert({ variant: 'info', content: 'Your account is pending email verification.' })
|
|
3
|
-
alert({ variant: 'success', title: 'Saved', content: 'Your changes have been saved.' })
|
|
4
|
-
alert({ variant: 'warning', title: 'Heads up', content: 'This action cannot be undone.' })
|
|
5
|
-
alert({ variant: 'error', title: 'Failed', content: escHtml(server.error) })`,{col:!0})}
|
|
6
|
-
`})};var t=document.getElementById("pulse-root");t&&!t.dataset.pulseMounted&&(t.dataset.pulseMounted="1",n(o,t,window.__PULSE_SERVER__||{},{ssr:!0}),a(t,n));var h=o;export{h as default};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import{a as n,b as d}from"./runtime-ZJ4FXT5O.js";import{Ma as t}from"./runtime-UVPXO4IR.js";import"./runtime-VMJA3Z4N.js";import"./runtime-QFURDKA2.js";import{a as r,h as a}from"./runtime-L2HNXIHW.js";import{a as o,b as s}from"./runtime-B73WLANC.js";var{prev:i,next:l}=r("/components/app-badge"),p={route:"/components/app-badge",meta:{title:"App Badge \u2014 Pulse Docs",description:"App Badge component for Pulse UI.",styles:["/pulse-ui.css","/docs.css"]},state:{},view:()=>d({currentHref:"/components/app-badge",prev:i,next:l,name:"appBadge",description:"App Store and Google Play download buttons. Designed to sit in a <code>hero()</code> actions slot, or anywhere a download CTA is needed.",content:`
|
|
2
|
-
${n('<div style="display:flex;gap:.75rem;flex-wrap:wrap">'+t({store:"apple",href:"#"})+t({store:"google",href:"#"})+"</div>",`appBadge({ store: 'apple', href: appStoreUrl })
|
|
3
|
-
appBadge({ store: 'google', href: playStoreUrl })`)}
|
|
4
|
-
|
|
5
|
-
${a(["Prop","Type","Default",""],[["<code>store</code>","string","'apple'","'apple' or 'google'"],["<code>href</code>","string","'#'","Link to the app store listing"]])}
|
|
6
|
-
`})};var e=document.getElementById("pulse-root");e&&!e.dataset.pulseMounted&&(e.dataset.pulseMounted="1",o(p,e,window.__PULSE_SERVER__||{},{ssr:!0}),s(e,o));var B=p;export{B as default};
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import{a as s,b as n}from"./runtime-ZJ4FXT5O.js";import{Da as e}from"./runtime-UVPXO4IR.js";import"./runtime-VMJA3Z4N.js";import"./runtime-QFURDKA2.js";import{a as r}from"./runtime-L2HNXIHW.js";import{a,b as i}from"./runtime-B73WLANC.js";var{prev:l,next:c}=r("/components/avatar"),o={route:"/components/avatar",meta:{title:"Avatar \u2014 Pulse Docs",description:"Avatar component for Pulse UI.",styles:["/pulse-ui.css","/docs.css"]},state:{},view:()=>n({currentHref:"/components/avatar",prev:l,next:c,name:"avatar",description:'When <code>src</code> is set, renders an <code><img></code> with <code>loading="lazy"</code>. Without <code>src</code>, shows initials derived from <code>alt</code>.',content:`
|
|
2
|
-
${s('<div style="display:flex;gap:1rem;align-items:center">'+e({alt:"Alice Smith",size:"sm"})+e({alt:"Bob Jones",size:"md"})+e({alt:"Carol White",size:"lg"})+e({alt:"Dan Brown",size:"xl"})+"</div>",`avatar({ src: user.avatarUrl, alt: user.name, size: 'md' })
|
|
3
|
-
avatar({ alt: 'Alice Smith' }) // renders initials "AS"
|
|
4
|
-
avatar({ alt: 'Alice', initials: 'A' }) // explicit initials`)}
|
|
5
|
-
`})};var t=document.getElementById("pulse-root");t&&!t.dataset.pulseMounted&&(t.dataset.pulseMounted="1",a(o,t,window.__PULSE_SERVER__||{},{ssr:!0}),i(t,a));var h=o;export{h as default};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import{a as s,b as i}from"./runtime-ZJ4FXT5O.js";import{b as e}from"./runtime-UVPXO4IR.js";import"./runtime-VMJA3Z4N.js";import"./runtime-QFURDKA2.js";import{a as n}from"./runtime-L2HNXIHW.js";import{a as t,b as o}from"./runtime-B73WLANC.js";var{prev:l,next:d}=n("/components/badge"),r={route:"/components/badge",meta:{title:"Badge \u2014 Pulse Docs",description:"Badge component for Pulse UI.",styles:["/pulse-ui.css","/docs.css"]},state:{},view:()=>i({currentHref:"/components/badge",prev:l,next:d,name:"badge",description:"Inline status and label indicator. Five semantic variants cover the most common states.",content:`
|
|
2
|
-
${s(e({label:"Default",variant:"default"})+" "+e({label:"Info",variant:"info"})+" "+e({label:"Success",variant:"success"})+" "+e({label:"Warning",variant:"warning"})+" "+e({label:"Error",variant:"error"}),`badge({ label: 'Default', variant: 'default' })
|
|
3
|
-
badge({ label: 'Info', variant: 'info' })
|
|
4
|
-
badge({ label: 'Success', variant: 'success' })
|
|
5
|
-
badge({ label: 'Warning', variant: 'warning' })
|
|
6
|
-
badge({ label: 'Error', variant: 'error' })`)}
|
|
7
|
-
`})};var a=document.getElementById("pulse-root");a&&!a.dataset.pulseMounted&&(a.dataset.pulseMounted="1",t(r,a,window.__PULSE_SERVER__||{},{ssr:!0}),o(a,t));var v=r;export{v as default};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import{a as s,b as c}from"./runtime-ZJ4FXT5O.js";import{Qa as m,Ta as t}from"./runtime-UVPXO4IR.js";import"./runtime-VMJA3Z4N.js";import"./runtime-QFURDKA2.js";import{a as r,h as a}from"./runtime-L2HNXIHW.js";import{a as e,b as i}from"./runtime-B73WLANC.js";var{prev:p,next:d}=r("/components/banner"),o={route:"/components/banner",meta:{title:"Banner \u2014 Pulse Docs",description:"Banner component for Pulse UI.",styles:["/pulse-ui.css","/docs.css"]},state:{},view:()=>c({currentHref:"/components/banner",prev:p,next:d,name:"banner",description:"Full-width announcement bar. Sits above the nav for promotions and launch announcements. The content slot accepts links and formatted text.",content:`
|
|
2
|
-
${s(m({gap:"sm",content:t({variant:"promo",content:`\u{1F389} Chippy Bird v2.0 is out \u2014 <a href="#" style="color:inherit;text-decoration:underline">see what's new</a>`})+t({variant:"info",content:'Android support is coming soon. <a href="#" style="color:inherit;text-decoration:underline">Join the waitlist</a>'})+t({variant:"warning",content:"Scheduled maintenance Sunday 2\u20134am UTC."})}),`banner({ variant: 'promo', content:
|
|
3
|
-
'\u{1F389} v2.0 is out \u2014 ' + button({ label: "See what's new", variant: 'ghost', size: 'sm', href: '/changelog' })
|
|
4
|
-
})`)}
|
|
5
|
-
|
|
6
|
-
${a(["Prop","Type","Default",""],[["<code>content</code>","string (HTML)","\u2014","Raw HTML slot"],["<code>variant</code>","string","'info'","'info' \xB7 'promo' \xB7 'warning'"]])}
|
|
7
|
-
`})};var n=document.getElementById("pulse-root");n&&!n.dataset.pulseMounted&&(n.dataset.pulseMounted="1",e(o,n,window.__PULSE_SERVER__||{},{ssr:!0}),i(n,e));var x=o;export{x as default};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import{a as r,b as d}from"./runtime-ZJ4FXT5O.js";import{pb as o}from"./runtime-UVPXO4IR.js";import"./runtime-VMJA3Z4N.js";import"./runtime-QFURDKA2.js";import{a as t,h as c}from"./runtime-L2HNXIHW.js";import{a as s,b as l}from"./runtime-B73WLANC.js";var{prev:n,next:m}=t("/components/breadcrumbs"),a={route:"/components/breadcrumbs",meta:{title:"Breadcrumbs \u2014 Pulse Docs",description:"Accessible breadcrumb navigation component for Pulse UI.",styles:["/pulse-ui.css","/docs.css"]},state:{},view:()=>d({currentHref:"/components/breadcrumbs",prev:n,next:m,name:"breadcrumbs",description:'Accessible breadcrumb navigation. The current page item renders as a <code><span></code> with <code>aria-current="page"</code>; all preceding items render as links.',content:`
|
|
2
|
-
|
|
3
|
-
<h2 class="doc-h2" id="basic">Basic</h2>
|
|
4
|
-
${r(o({items:[{label:"Home",href:"/"},{label:"Products",href:"/products"},{label:"Sneakers"}]}),`breadcrumbs({
|
|
5
|
-
items: [
|
|
6
|
-
{ label: 'Home', href: '/' },
|
|
7
|
-
{ label: 'Products', href: '/products' },
|
|
8
|
-
{ label: 'Sneakers' },
|
|
9
|
-
],
|
|
10
|
-
})`)}
|
|
11
|
-
|
|
12
|
-
<h2 class="doc-h2" id="longer">Longer trail</h2>
|
|
13
|
-
${r(o({items:[{label:"Home",href:"/"},{label:"Shop",href:"/shop"},{label:"Footwear",href:"/shop/footwear"},{label:"Sneakers",href:"/shop/footwear/sneakers"},{label:"Air Max 90"}]}),`breadcrumbs({
|
|
14
|
-
items: [
|
|
15
|
-
{ label: 'Home', href: '/' },
|
|
16
|
-
{ label: 'Shop', href: '/shop' },
|
|
17
|
-
{ label: 'Footwear', href: '/shop/footwear' },
|
|
18
|
-
{ label: 'Sneakers', href: '/shop/footwear/sneakers' },
|
|
19
|
-
{ label: 'Air Max 90' },
|
|
20
|
-
],
|
|
21
|
-
})`)}
|
|
22
|
-
|
|
23
|
-
<h2 class="doc-h2" id="separator">Custom separator</h2>
|
|
24
|
-
${r(o({separator:"\u203A",items:[{label:"Docs",href:"/docs"},{label:"Components",href:"/docs/components"},{label:"Breadcrumbs"}]}),`breadcrumbs({
|
|
25
|
-
separator: '\u203A',
|
|
26
|
-
items: [
|
|
27
|
-
{ label: 'Docs', href: '/docs' },
|
|
28
|
-
{ label: 'Components', href: '/docs/components' },
|
|
29
|
-
{ label: 'Breadcrumbs' },
|
|
30
|
-
],
|
|
31
|
-
})`)}
|
|
32
|
-
|
|
33
|
-
${c(["Prop","Type","Default",""],[["<code>items</code>","array","[]","Array of <code>{ label, href }</code>. The last item should have no <code>href</code> \u2014 it becomes the current page."],["<code>separator</code>","string","'/'","Character rendered between items"],["<code>class</code>","string","\u2014",""]])}
|
|
34
|
-
`})};var e=document.getElementById("pulse-root");e&&!e.dataset.pulseMounted&&(e.dataset.pulseMounted="1",s(a,e,window.__PULSE_SERVER__||{},{ssr:!0}),l(e,s));var S=a;export{S as default};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import{a as t,b as s}from"./runtime-ZJ4FXT5O.js";import{E as b,H as u,a as e,f as d,x as c}from"./runtime-UVPXO4IR.js";import"./runtime-VMJA3Z4N.js";import"./runtime-QFURDKA2.js";import{a as i,h as r}from"./runtime-L2HNXIHW.js";import{a as n,b as l}from"./runtime-B73WLANC.js";var{prev:m,next:p}=i("/components/button"),a={route:"/components/button",meta:{title:"Button \u2014 Pulse Docs",description:"Button component for Pulse UI.",styles:["/pulse-ui.css","/docs.css"]},state:{},view:()=>s({currentHref:"/components/button",prev:m,next:p,name:"button",description:"Renders as <code><button></code> by default. Pass <code>href</code> to get an <code><a></code> that looks identical. All four variants are shown below.",content:`
|
|
2
|
-
${t(e({label:"Primary",variant:"primary"})+e({label:"Secondary",variant:"secondary"})+e({label:"Ghost",variant:"ghost"})+e({label:"Danger",variant:"danger"}),`button({ label: 'Primary', variant: 'primary' })
|
|
3
|
-
button({ label: 'Secondary', variant: 'secondary' })
|
|
4
|
-
button({ label: 'Ghost', variant: 'ghost' })
|
|
5
|
-
button({ label: 'Danger', variant: 'danger' })`)}
|
|
6
|
-
|
|
7
|
-
${t(e({label:"Small",size:"sm"})+e({label:"Medium",size:"md"})+e({label:"Large",size:"lg"}),`button({ label: 'Small', size: 'sm' })
|
|
8
|
-
button({ label: 'Medium', size: 'md' })
|
|
9
|
-
button({ label: 'Large', size: 'lg' })`)}
|
|
10
|
-
|
|
11
|
-
${t(e({label:"Download",icon:b({size:14})})+e({label:"New item",icon:c({size:14}),variant:"secondary"})+e({label:"Continue",iconAfter:d({size:14})})+e({label:"Send",iconAfter:u({size:14}),variant:"ghost"}),`import { iconDownload, iconPlus, iconArrowRight, iconSend } from '@invisibleloop/pulse/ui'
|
|
12
|
-
|
|
13
|
-
button({ label: 'Download', icon: iconDownload({ size: 14 }) })
|
|
14
|
-
button({ label: 'New item', icon: iconPlus({ size: 14 }), variant: 'secondary' })
|
|
15
|
-
button({ label: 'Continue', iconAfter: iconArrowRight({ size: 14 }) })
|
|
16
|
-
button({ label: 'Send', iconAfter: iconSend({ size: 14 }), variant: 'ghost' })`)}
|
|
17
|
-
|
|
18
|
-
${t(e({label:"Disabled",disabled:!0})+e({label:"Link",href:"/docs"})+e({label:"Submit",type:"submit",variant:"primary"}),`button({ label: 'Disabled', disabled: true })
|
|
19
|
-
button({ label: 'Link', href: '/docs' })
|
|
20
|
-
button({ label: 'Submit', type: 'submit', variant: 'primary' })`)}
|
|
21
|
-
|
|
22
|
-
${r(["Prop","Type","Default",""],[["<code>label</code>","string","\u2014","Visible text"],["<code>variant</code>","string","primary","primary \xB7 secondary \xB7 ghost \xB7 danger"],["<code>size</code>","string","md","sm \xB7 md \xB7 lg"],["<code>href</code>","string","\u2014","Renders <code><a></code> instead of <code><button></code>"],["<code>disabled</code>","boolean","false",""],["<code>type</code>","string","button","button \xB7 submit \xB7 reset"],["<code>icon</code>","string","\u2014","SVG HTML prepended inside"],["<code>iconAfter</code>","string","\u2014","SVG HTML appended inside"],["<code>fullWidth</code>","boolean","false",""],["<code>class</code>","string","\u2014","Extra classes appended to the element"],["<code>attrs</code>","object","{}","Extra HTML attributes (button only)"]])}
|
|
23
|
-
`})};var o=document.getElementById("pulse-root");o&&!o.dataset.pulseMounted&&(o.dataset.pulseMounted="1",n(a,o,window.__PULSE_SERVER__||{},{ssr:!0}),l(o,n));var P=a;export{P as default};
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import{a as e,b as c}from"./runtime-ZJ4FXT5O.js";import{Pa as n,a as s,b as i,c as t}from"./runtime-UVPXO4IR.js";import"./runtime-VMJA3Z4N.js";import"./runtime-QFURDKA2.js";import{a as l,h as d}from"./runtime-L2HNXIHW.js";import{a as o,b as u}from"./runtime-B73WLANC.js";var{prev:p,next:m}=l("/components/card"),r={route:"/components/card",meta:{title:"Card \u2014 Pulse Docs",description:"Card component for Pulse UI.",styles:["/pulse-ui.css","/docs.css"]},state:{},view:()=>c({currentHref:"/components/card",prev:p,next:m,name:"card",description:"<code>content</code> and <code>footer</code> are raw HTML slots \u2014 they pass through as-is. Wrap user-supplied values in <code>escHtml()</code> before composing them into the string.",content:`
|
|
2
|
-
${e(t({title:"Recent activity",content:'<p class="u-text-muted u-text-sm" style="margin:0">No activity in the last 7 days.</p>',footer:s({label:"View all",href:"#",variant:"ghost",size:"sm"})}),`card({
|
|
3
|
-
title: 'Recent activity',
|
|
4
|
-
content: \`<p class="u-text-muted u-text-sm">No activity in the last 7 days.</p>\`,
|
|
5
|
-
footer: button({ label: 'View all', href: '/activity', variant: 'ghost', size: 'sm' }),
|
|
6
|
-
})`)}
|
|
7
|
-
|
|
8
|
-
<h2 class="doc-h2" id="image">With image</h2>
|
|
9
|
-
<p>Use <code>flush</code> to remove body padding, then add an image at the top and restore padding on the text content below it.</p>
|
|
10
|
-
|
|
11
|
-
${e(t({flush:!0,content:`
|
|
12
|
-
<div class="u-rounded-md u-overflow-hidden" style="height:180px;background:linear-gradient(135deg,#667eea,#764ba2);display:flex;align-items:center;justify-content:center;">
|
|
13
|
-
<span style="font-size:3rem">\u{1F304}</span>
|
|
14
|
-
</div>
|
|
15
|
-
<div class="u-p-5">
|
|
16
|
-
<p class="u-font-semibold u-mb-2" style="margin-top:0">Mountain retreat</p>
|
|
17
|
-
<p class="u-text-muted u-text-sm" style="margin:0">A peaceful escape in the highlands. Three nights, all-inclusive.</p>
|
|
18
|
-
</div>
|
|
19
|
-
`,footer:s({label:"Book now",href:"#",variant:"primary",size:"sm"})}),`card({
|
|
20
|
-
flush: true,
|
|
21
|
-
content: \`
|
|
22
|
-
<div class="u-rounded-md u-overflow-hidden" style="height:180px;background:...">
|
|
23
|
-
<img src="/img/retreat.jpg" alt="Mountain retreat" style="width:100%;height:100%;object-fit:cover">
|
|
24
|
-
</div>
|
|
25
|
-
<div class="u-p-5">
|
|
26
|
-
<p class="u-font-semibold u-mb-2">Mountain retreat</p>
|
|
27
|
-
<p class="u-text-muted u-text-sm">A peaceful escape in the highlands.</p>
|
|
28
|
-
</div>
|
|
29
|
-
\`,
|
|
30
|
-
footer: button({ label: 'Book now', href: '/book', variant: 'primary', size: 'sm' }),
|
|
31
|
-
})`)}
|
|
32
|
-
|
|
33
|
-
<h2 class="doc-h2" id="badges">With badges</h2>
|
|
34
|
-
<p>Compose <code>badge()</code> inside <code>content</code> to show status labels or category tags.</p>
|
|
35
|
-
|
|
36
|
-
${e(t({title:"API rate limits",content:`
|
|
37
|
-
<div class="u-flex u-gap-2 u-mb-4">
|
|
38
|
-
${i({label:"Production",variant:"success"})}
|
|
39
|
-
${i({label:"v2.1",variant:"info"})}
|
|
40
|
-
</div>
|
|
41
|
-
<p class="u-text-muted u-text-sm" style="margin:0">Requests are capped at 1,000/min per API key. Contact support to increase your limit.</p>
|
|
42
|
-
`,footer:`
|
|
43
|
-
<div class="u-flex u-gap-2">
|
|
44
|
-
${s({label:"View docs",href:"#",variant:"ghost",size:"sm"})}
|
|
45
|
-
${s({label:"Request increase",href:"#",variant:"secondary",size:"sm"})}
|
|
46
|
-
</div>
|
|
47
|
-
`}),`card({
|
|
48
|
-
title: 'API rate limits',
|
|
49
|
-
content: \`
|
|
50
|
-
<div class="u-flex u-gap-2 u-mb-4">
|
|
51
|
-
\${badge({ label: 'Production', variant: 'success' })}
|
|
52
|
-
\${badge({ label: 'v2.1', variant: 'info' })}
|
|
53
|
-
</div>
|
|
54
|
-
<p class="u-text-muted u-text-sm">Requests are capped at 1,000/min per API key.</p>
|
|
55
|
-
\`,
|
|
56
|
-
footer: \`
|
|
57
|
-
\${button({ label: 'View docs', href: '/docs', variant: 'ghost', size: 'sm' })}
|
|
58
|
-
\${button({ label: 'Request increase', href: '/contact', variant: 'secondary', size: 'sm' })}
|
|
59
|
-
\`,
|
|
60
|
-
})`)}
|
|
61
|
-
|
|
62
|
-
<h2 class="doc-h2" id="metadata">With metadata row</h2>
|
|
63
|
-
<p>A flex row inside <code>content</code> works well for author, date, read-time and similar metadata.</p>
|
|
64
|
-
|
|
65
|
-
${e(t({title:"Building with Pulse",content:`
|
|
66
|
-
<p class="u-text-muted u-text-sm u-mb-4">Learn how to scaffold a new project and ship your first spec-driven page in under ten minutes.</p>
|
|
67
|
-
<div class="u-flex u-gap-3" style="align-items:center;flex-wrap:wrap">
|
|
68
|
-
<div style="width:28px;height:28px;border-radius:50%;background:linear-gradient(135deg,#667eea,#764ba2);flex-shrink:0"></div>
|
|
69
|
-
<span class="u-text-sm u-text-muted">Jane Smith</span>
|
|
70
|
-
<span class="u-text-sm u-text-muted">\xB7</span>
|
|
71
|
-
<span class="u-text-sm u-text-muted">Mar 15, 2024</span>
|
|
72
|
-
<span class="u-text-sm u-text-muted u-ml-auto">5 min read</span>
|
|
73
|
-
</div>
|
|
74
|
-
`,footer:s({label:"Read article \u2192",href:"#",variant:"ghost",size:"sm"})}),`card({
|
|
75
|
-
title: 'Building with Pulse',
|
|
76
|
-
content: \`
|
|
77
|
-
<p class="u-text-muted u-text-sm u-mb-4">Learn how to scaffold a new project...</p>
|
|
78
|
-
<div class="u-flex u-gap-3" style="align-items:center;flex-wrap:wrap">
|
|
79
|
-
<img src="/img/avatar.jpg" class="u-rounded-full" style="width:28px;height:28px" alt="">
|
|
80
|
-
<span class="u-text-sm u-text-muted">Jane Smith</span>
|
|
81
|
-
<span class="u-text-sm u-text-muted">\xB7</span>
|
|
82
|
-
<span class="u-text-sm u-text-muted">Mar 15, 2024</span>
|
|
83
|
-
<span class="u-text-sm u-text-muted u-ml-auto">5 min read</span>
|
|
84
|
-
</div>
|
|
85
|
-
\`,
|
|
86
|
-
footer: button({ label: 'Read article \u2192', href: '/blog/pulse', variant: 'ghost', size: 'sm' }),
|
|
87
|
-
})`)}
|
|
88
|
-
|
|
89
|
-
<h2 class="doc-h2" id="stat">Stat / metric card</h2>
|
|
90
|
-
<p>Cards without a title work well as simple metric tiles. Use <code>grid()</code> to lay multiple cards out side by side.</p>
|
|
91
|
-
|
|
92
|
-
${e(n({cols:3,gap:"sm",content:[t({content:'<p class="u-text-muted u-text-sm u-mb-1" style="margin-top:0">Total revenue</p><p class="u-text-3xl u-font-bold" style="margin:0">\xA348,295</p><p class="u-text-sm u-text-green u-mb-0" style="margin-top:.25rem">\u2191 12% this month</p>'}),t({content:'<p class="u-text-muted u-text-sm u-mb-1" style="margin-top:0">Active users</p><p class="u-text-3xl u-font-bold" style="margin:0">3,842</p><p class="u-text-sm u-text-muted u-mb-0" style="margin-top:.25rem">Across all plans</p>'}),t({content:'<p class="u-text-muted u-text-sm u-mb-1" style="margin-top:0">Churn rate</p><p class="u-text-3xl u-font-bold" style="margin:0">1.4%</p><p class="u-text-sm u-text-red u-mb-0" style="margin-top:.25rem">\u2191 0.2% vs last month</p>'})].join("")}),`grid({
|
|
93
|
-
cols: 3,
|
|
94
|
-
gap: 'sm',
|
|
95
|
-
content: [
|
|
96
|
-
card({
|
|
97
|
-
content: \`
|
|
98
|
-
<p class="u-text-muted u-text-sm u-mb-1">Total revenue</p>
|
|
99
|
-
<p class="u-text-3xl u-font-bold">\xA348,295</p>
|
|
100
|
-
<p class="u-text-sm u-text-green">\u2191 12% this month</p>
|
|
101
|
-
\`,
|
|
102
|
-
}),
|
|
103
|
-
card({ ... }),
|
|
104
|
-
card({ ... }),
|
|
105
|
-
].join(''),
|
|
106
|
-
})`)}
|
|
107
|
-
|
|
108
|
-
<h2 class="doc-h2" id="grid-inside">Grid inside a card</h2>
|
|
109
|
-
<p>Components compose freely \u2014 pass a <code>grid()</code> into a card's <code>content</code> slot for structured layouts inside a surface.</p>
|
|
110
|
-
|
|
111
|
-
${e(t({title:"Plan comparison",content:n({cols:3,gap:"sm",content:[`<div class="u-text-center u-p-2">
|
|
112
|
-
<p class="u-font-semibold u-mb-1" style="margin-top:0">Starter</p>
|
|
113
|
-
<p class="u-text-2xl u-font-bold u-mb-1" style="margin:0">\xA30</p>
|
|
114
|
-
<p class="u-text-muted u-text-sm" style="margin:0">Up to 3 projects</p>
|
|
115
|
-
</div>`,`<div class="u-text-center u-p-2" style="border-left:1px solid var(--ui-border);border-right:1px solid var(--ui-border)">
|
|
116
|
-
<p class="u-font-semibold u-mb-1" style="margin-top:0;color:var(--ui-accent)">Pro</p>
|
|
117
|
-
<p class="u-text-2xl u-font-bold u-mb-1" style="margin:0">\xA312</p>
|
|
118
|
-
<p class="u-text-muted u-text-sm" style="margin:0">Unlimited projects</p>
|
|
119
|
-
</div>`,`<div class="u-text-center u-p-2">
|
|
120
|
-
<p class="u-font-semibold u-mb-1" style="margin-top:0">Enterprise</p>
|
|
121
|
-
<p class="u-text-2xl u-font-bold u-mb-1" style="margin:0">Custom</p>
|
|
122
|
-
<p class="u-text-muted u-text-sm" style="margin:0">SLA + support</p>
|
|
123
|
-
</div>`].join("")}),footer:s({label:"View full pricing",href:"#",variant:"ghost",size:"sm"})}),`card({
|
|
124
|
-
title: 'Plan comparison',
|
|
125
|
-
content: grid({
|
|
126
|
-
cols: 3,
|
|
127
|
-
gap: 'sm',
|
|
128
|
-
content: [
|
|
129
|
-
\`<div class="u-text-center u-p-2">...</div>\`,
|
|
130
|
-
\`<div class="u-text-center u-p-2">...</div>\`,
|
|
131
|
-
\`<div class="u-text-center u-p-2">...</div>\`,
|
|
132
|
-
].join(''),
|
|
133
|
-
}),
|
|
134
|
-
footer: button({ label: 'View full pricing', href: '/pricing', variant: 'ghost', size: 'sm' }),
|
|
135
|
-
})`)}
|
|
136
|
-
|
|
137
|
-
${d(["Prop","Type","Default",""],[["<code>title</code>","string","\u2014","Escaped automatically"],["<code>level</code>","number","3","Heading tag for the title (1\u20136). Visual style is unchanged \u2014 use this to keep the document outline correct when the surrounding context already has an h2 or h3."],["<code>content</code>","string","\u2014","HTML string \u2014 not escaped"],["<code>footer</code>","string","\u2014","HTML string \u2014 not escaped"],["<code>flush</code>","boolean","false","Removes body padding \u2014 useful for full-bleed images or tables"],["<code>class</code>","string","\u2014",""]])}
|
|
138
|
-
`})};var a=document.getElementById("pulse-root");a&&!a.dataset.pulseMounted&&(a.dataset.pulseMounted="1",o(r,a,window.__PULSE_SERVER__||{},{ssr:!0}),u(a,o));var P=r;export{P as default};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import{a as r,b as l}from"./runtime-ZJ4FXT5O.js";import{Ya as t}from"./runtime-UVPXO4IR.js";import"./runtime-VMJA3Z4N.js";import"./runtime-QFURDKA2.js";import{a as d,h as a}from"./runtime-L2HNXIHW.js";import{a as s,b as n}from"./runtime-B73WLANC.js";var{prev:p,next:f}=d("/components/carousel");function e(c,u){return`<div style="height:220px;display:flex;align-items:center;justify-content:center;background:${c};border-radius:10px;font-size:1.1rem;font-weight:700;color:var(--ui-text)">${u}</div>`}var i={route:"/components/carousel",meta:{title:"Carousel \u2014 Pulse Docs",description:"Carousel / slider component for Pulse UI.",styles:["/pulse-ui.css","/docs.css"]},state:{},view:()=>l({currentHref:"/components/carousel",prev:p,next:f,name:"carousel",description:"CSS scroll-snap carousel with optional prev/next arrows and dot navigation. Touch / swipe friendly out of the box. Requires <code>pulse-ui.js</code> for button and dot interactivity.",content:`
|
|
2
|
-
${r(t({slides:[e("var(--ui-surface-2)","Slide 1"),e("var(--ui-surface)","Slide 2"),e("var(--ui-surface-2)","Slide 3")]}),'carousel({\n slides: [\n `<div class="slide">Slide 1</div>`,\n `<div class="slide">Slide 2</div>`,\n `<div class="slide">Slide 3</div>`,\n ],\n})')}
|
|
3
|
-
|
|
4
|
-
<h3 class="doc-h3" id="arrows-dots"><a href="#arrows-dots" class="heading-anchor">Arrows and dots</a></h3>
|
|
5
|
-
${r(t({arrows:!0,dots:!1,slides:[e("var(--ui-surface-2)","Arrows only \u2014 Slide 1"),e("var(--ui-surface)","Arrows only \u2014 Slide 2")]}),`carousel({
|
|
6
|
-
slides: [ /* ... */ ],
|
|
7
|
-
arrows: true,
|
|
8
|
-
dots: false, // hide dot navigation
|
|
9
|
-
})`)}
|
|
10
|
-
|
|
11
|
-
${a(["Prop","Type","Default",""],[["<code>slides</code>","string[] (HTML)","[]","Array of raw HTML strings \u2014 one per slide"],["<code>arrows</code>","boolean","<code>true</code>","Show prev/next arrow buttons"],["<code>dots</code>","boolean","<code>true</code>","Show dot navigation (hidden when only one slide)"],["<code>class</code>","string","\u2014",""]])}
|
|
12
|
-
`})};var o=document.getElementById("pulse-root");o&&!o.dataset.pulseMounted&&(o.dataset.pulseMounted="1",s(i,o,window.__PULSE_SERVER__||{},{ssr:!0}),n(o,s));var b=i;export{b as default};
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import{a as e,b as m}from"./runtime-ZJ4FXT5O.js";import{Ca as n,Pa as i,c as l,ib as t,jb as r,kb as u,lb as a}from"./runtime-UVPXO4IR.js";import"./runtime-VMJA3Z4N.js";import"./runtime-QFURDKA2.js";import{a as p,h as o}from"./runtime-L2HNXIHW.js";import{a as d,b as v}from"./runtime-B73WLANC.js";var{prev:g,next:f}=p("/components/charts"),c=[{label:"Jan",value:42},{label:"Feb",value:78},{label:"Mar",value:55},{label:"Apr",value:91},{label:"May",value:63},{label:"Jun",value:84}],h=[{label:"Mon",value:1200},{label:"Tue",value:1850},{label:"Wed",value:1540},{label:"Thu",value:2100},{label:"Fri",value:1760},{label:"Sat",value:890},{label:"Sun",value:720}],b={route:"/components/charts",meta:{title:"Charts \u2014 Pulse Docs",description:"Server-rendered SVG charts for Pulse UI.",styles:["/pulse-ui.css","/docs.css"]},state:{},view:()=>m({currentHref:"/components/charts",prev:g,next:f,name:"charts",description:"Server-rendered SVG charts \u2014 no JavaScript, no external library. Pure functions that return SVG strings, composable with any layout component. All colours use design tokens and respond to light/dark theme.",content:`
|
|
2
|
-
|
|
3
|
-
<h2 class="doc-h2" id="bar">Bar chart</h2>
|
|
4
|
-
<p>Vertical bars with optional grid, value labels, and a zero baseline. All colour variants available.</p>
|
|
5
|
-
${e(t({data:c,color:"accent"}),`barChart({
|
|
6
|
-
data: [
|
|
7
|
-
{ label: 'Jan', value: 42 },
|
|
8
|
-
{ label: 'Feb', value: 78 },
|
|
9
|
-
{ label: 'Mar', value: 55 },
|
|
10
|
-
{ label: 'Apr', value: 91 },
|
|
11
|
-
{ label: 'May', value: 63 },
|
|
12
|
-
{ label: 'Jun', value: 84 },
|
|
13
|
-
],
|
|
14
|
-
color: 'accent',
|
|
15
|
-
})`)}
|
|
16
|
-
|
|
17
|
-
<h3 class="doc-h3">With value labels</h3>
|
|
18
|
-
${e(t({data:c,color:"success",showValues:!0}),"barChart({ data, color: 'success', showValues: true })")}
|
|
19
|
-
|
|
20
|
-
<h3 class="doc-h3">Large dataset with tight gap</h3>
|
|
21
|
-
${e(t({data:h,color:"blue",gap:.15}),"barChart({ data: traffic, color: 'blue', gap: 0.15 })")}
|
|
22
|
-
|
|
23
|
-
<h3 class="doc-h3">Negative values</h3>
|
|
24
|
-
${e(t({data:[{label:"Q1",value:24},{label:"Q2",value:-8},{label:"Q3",value:41},{label:"Q4",value:-15}],color:"warning",showValues:!0}),`barChart({
|
|
25
|
-
data: [
|
|
26
|
-
{ label: 'Q1', value: 24 },
|
|
27
|
-
{ label: 'Q2', value: -8 },
|
|
28
|
-
{ label: 'Q3', value: 41 },
|
|
29
|
-
{ label: 'Q4', value: -15 },
|
|
30
|
-
],
|
|
31
|
-
color: 'warning',
|
|
32
|
-
showValues: true,
|
|
33
|
-
})`)}
|
|
34
|
-
|
|
35
|
-
<h2 class="doc-h2" id="line">Line chart</h2>
|
|
36
|
-
<p>Connected data points with optional dots, area fill, and grid lines.</p>
|
|
37
|
-
${e(r({data:c,color:"accent"}),`lineChart({
|
|
38
|
-
data: [
|
|
39
|
-
{ label: 'Jan', value: 42 },
|
|
40
|
-
...
|
|
41
|
-
],
|
|
42
|
-
color: 'accent',
|
|
43
|
-
})`)}
|
|
44
|
-
|
|
45
|
-
<h3 class="doc-h3">With area fill</h3>
|
|
46
|
-
${e(r({data:c,color:"accent",area:!0}),"lineChart({ data, color: 'accent', area: true })")}
|
|
47
|
-
|
|
48
|
-
<h3 class="doc-h3">No dots, area fill, success colour</h3>
|
|
49
|
-
${e(r({data:h,color:"success",area:!0,showDots:!1}),"lineChart({ data, color: 'success', area: true, showDots: false })")}
|
|
50
|
-
|
|
51
|
-
<h2 class="doc-h2" id="donut">Donut chart</h2>
|
|
52
|
-
<p>Ring chart with multiple segments. Each item can override its colour. Pass <code>label</code> and <code>sublabel</code> for a centred annotation.</p>
|
|
53
|
-
${e('<div style="display:flex;justify-content:center">'+u({label:"73%",sublabel:"satisfied",data:[{label:"Satisfied",value:73,color:"success"},{label:"Neutral",value:18,color:"muted"},{label:"Unsatisfied",value:9,color:"error"}]})+"</div>",`donutChart({
|
|
54
|
-
label: '73%',
|
|
55
|
-
sublabel: 'satisfied',
|
|
56
|
-
data: [
|
|
57
|
-
{ label: 'Satisfied', value: 73, color: 'success' },
|
|
58
|
-
{ label: 'Neutral', value: 18, color: 'muted' },
|
|
59
|
-
{ label: 'Unsatisfied', value: 9, color: 'error' },
|
|
60
|
-
],
|
|
61
|
-
})`)}
|
|
62
|
-
|
|
63
|
-
<h3 class="doc-h3">Thinner ring</h3>
|
|
64
|
-
${e('<div style="display:flex;justify-content:center">'+u({size:180,thickness:22,label:"4",sublabel:"segments",data:[{label:"A",value:40,color:"accent"},{label:"B",value:30,color:"blue"},{label:"C",value:20,color:"success"},{label:"D",value:10,color:"warning"}]})+"</div>",`donutChart({
|
|
65
|
-
size: 180, thickness: 22,
|
|
66
|
-
label: '4', sublabel: 'segments',
|
|
67
|
-
data: [
|
|
68
|
-
{ label: 'A', value: 40, color: 'accent' },
|
|
69
|
-
{ label: 'B', value: 30, color: 'blue' },
|
|
70
|
-
{ label: 'C', value: 20, color: 'success' },
|
|
71
|
-
{ label: 'D', value: 10, color: 'warning' },
|
|
72
|
-
],
|
|
73
|
-
})`)}
|
|
74
|
-
|
|
75
|
-
<h2 class="doc-h2" id="sparkline">Sparkline</h2>
|
|
76
|
-
<p>Minimal inline trend line \u2014 pass a plain array of numbers. Designed to sit alongside <code>stat()</code> tiles or inside table cells.</p>
|
|
77
|
-
${e('<div style="display:flex;gap:.75rem;align-items:center;flex-wrap:wrap">'+a({data:[12,18,14,22,19,28,24,31],color:"accent",area:!0})+a({data:[31,24,28,19,22,14,18,12],color:"error",area:!0})+a({data:[12,18,14,22,19,28,24,31],color:"success",area:!1})+"</div>",`sparkline({ data: [12,18,14,22,19,28,24,31], color: 'accent', area: true })
|
|
78
|
-
sparkline({ data: [31,24,28,19,22,14,18,12], color: 'error', area: true })`)}
|
|
79
|
-
|
|
80
|
-
<h2 class="doc-h2" id="composition">Composition</h2>
|
|
81
|
-
<p>Charts compose with <code>card()</code>, <code>stat()</code>, <code>grid()</code> \u2014 drop any chart into any content slot.</p>
|
|
82
|
-
${e(i({cols:2,gap:"md",content:l({title:"Monthly signups",content:t({data:c,color:"accent",height:180})})+l({title:"Daily traffic",content:r({data:h,color:"blue",area:!0,height:180})})}),`grid({
|
|
83
|
-
cols: 2,
|
|
84
|
-
content:
|
|
85
|
-
card({ title: 'Monthly signups', content: barChart({ data, height: 180 }) }) +
|
|
86
|
-
card({ title: 'Daily traffic', content: lineChart({ data, color: 'blue', area: true, height: 180 }) }),
|
|
87
|
-
})`)}
|
|
88
|
-
|
|
89
|
-
<h3 class="doc-h3">Sparkline in stat tiles</h3>
|
|
90
|
-
${e(i({cols:3,gap:"md",content:l({content:n({label:"Revenue",value:"$18.2k",change:"+12%",trend:"up"})+`<div style="margin-top:.75rem">${a({data:[8,11,9,14,12,16,15,18],width:"100%",color:"success",area:!0})}</div>`})+l({content:n({label:"Users",value:"4,821",change:"+8.4%",trend:"up"})+`<div style="margin-top:.75rem">${a({data:[22,28,24,31,27,34,30,38],width:"100%",color:"accent",area:!0})}</div>`})+l({content:n({label:"Churn",value:"2.1%",change:"\u22120.3%",trend:"down"})+`<div style="margin-top:.75rem">${a({data:[8,6,7,5,6,4,5,3],width:"100%",color:"error",area:!0})}</div>`})}),`card({
|
|
91
|
-
content: stat({ label: 'Revenue', value: '$18.2k', change: '+12%', trend: 'up' }) +
|
|
92
|
-
\`<div style="margin-top:.75rem">\${sparkline({ data, color: 'success', area: true })}</div>\`,
|
|
93
|
-
})`)}
|
|
94
|
-
|
|
95
|
-
<h2 class="doc-h2" id="props">Props</h2>
|
|
96
|
-
|
|
97
|
-
<h3 class="doc-h3">barChart()</h3>
|
|
98
|
-
${o(["Prop","Type","Default",""],[["<code>data</code>","array","\u2014","<code>{ label, value }[]</code>"],["<code>height</code>","number","220","SVG height in px"],["<code>color</code>","string","'accent'","accent \xB7 success \xB7 warning \xB7 error \xB7 blue \xB7 muted"],["<code>showValues</code>","boolean","false","Value labels above each bar"],["<code>showGrid</code>","boolean","true","Horizontal grid lines"],["<code>gap</code>","number","0.25","Gap between bars as fraction 0\u20130.9"]])}
|
|
99
|
-
|
|
100
|
-
<h3 class="doc-h3" style="margin-top:2rem">lineChart()</h3>
|
|
101
|
-
${o(["Prop","Type","Default",""],[["<code>data</code>","array","\u2014","<code>{ label, value }[]</code>"],["<code>height</code>","number","220","SVG height in px"],["<code>color</code>","string","'accent'","accent \xB7 success \xB7 warning \xB7 error \xB7 blue \xB7 muted"],["<code>area</code>","boolean","false","Fill area under the line"],["<code>showDots</code>","boolean","true","Dots at each data point"],["<code>showGrid</code>","boolean","true","Horizontal grid lines"]])}
|
|
102
|
-
|
|
103
|
-
<h3 class="doc-h3" style="margin-top:2rem">donutChart()</h3>
|
|
104
|
-
${o(["Prop","Type","Default",""],[["<code>data</code>","array","\u2014","<code>{ label, value, color? }[]</code> \u2014 color per segment"],["<code>size</code>","number","200","Diameter in px"],["<code>thickness</code>","number","40","Ring thickness in px"],["<code>label</code>","string","\u2014","Large centre text"],["<code>sublabel</code>","string","\u2014","Smaller text below centre label"]])}
|
|
105
|
-
|
|
106
|
-
<h3 class="doc-h3" style="margin-top:2rem">sparkline()</h3>
|
|
107
|
-
${o(["Prop","Type","Default",""],[["<code>data</code>","number[]","\u2014","Plain array of numbers"],["<code>width</code>","number","80","SVG width in px"],["<code>height</code>","number","32","SVG height in px"],["<code>color</code>","string","'accent'","accent \xB7 success \xB7 warning \xB7 error \xB7 blue \xB7 muted"],["<code>area</code>","boolean","false","Fill area under the line"]])}
|
|
108
|
-
`})};var s=document.getElementById("pulse-root");s&&!s.dataset.pulseMounted&&(s.dataset.pulseMounted="1",d(b,s,window.__PULSE_SERVER__||{},{ssr:!0}),v(s,d));var V=b;export{V as default};
|