Skip to content

Commit a255cfe

Browse files
authored
fix(examples): Tailwind 4 auth-demo restyle, component gallery and screenshot script (#48)
* fix(examples): restyle auth-demo with Tailwind 4 and the SDK stylesheet The demo kept a v3-style tailwind.config.ts whose content glob pointed at the SDK's dist; Tailwind 4 ignores it, so the SDK components rendered unstyled. The config is gone, globals.css is CSS-first with Hypery's tokens, the app imports @hyperyai/sdk/styles.css, and every page is redesigned in the hypery.ai look with light and dark themes. Needs the SDK release that ships styles.css. * feat(examples): add a component gallery and a screenshot script /gallery renders every SDK component in its own frame, signed in as a sample user through HyperyTestProvider with sample gateway data, with ?theme= and ?shot= to show one at a time. scripts/screenshots.mjs captures each frame in light and dark at 2x with Playwright, which is how the SDK's docs images are made. * fix(examples): require @hyperyai/sdk 1.6.0, which ships styles.css and /testing
1 parent 98caa05 commit a255cfe

16 files changed

Lines changed: 1536 additions & 1103 deletions

File tree

‎auth-demo/app/api-demo/page.tsx‎

Lines changed: 48 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
'use client';
22

33
import { Protect, useHyperyAuth } from '@hyperyai/sdk';
4-
import Link from 'next/link';
4+
import { Terminal, TriangleAlert } from 'lucide-react';
5+
import { BackLink, C, Card, CodeBlock, Note, Page, PageHeader, button } from '@/components/ui';
56
import { useState } from 'react';
67

78
export default function ApiDemoPage() {
@@ -64,144 +65,69 @@ function ApiDemoContent() {
6465
};
6566

6667
return (
67-
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-purple-50">
68-
<div className="max-w-4xl mx-auto px-4 py-12">
69-
<Link
70-
href="/"
71-
className="inline-flex items-center text-blue-600 hover:text-blue-700 mb-8"
72-
>
73-
<svg
74-
className="w-4 h-4 mr-2"
75-
fill="none"
76-
stroke="currentColor"
77-
viewBox="0 0 24 24"
78-
>
79-
<path
80-
strokeLinecap="round"
81-
strokeLinejoin="round"
82-
strokeWidth={2}
83-
d="M15 19l-7-7 7-7"
84-
/>
85-
</svg>
86-
Back to Home
87-
</Link>
88-
89-
<div className="bg-white rounded-2xl shadow-xl p-8">
90-
<div className="flex items-center space-x-2 mb-6">
91-
<div className="w-10 h-10 bg-purple-100 rounded-full flex items-center justify-center">
92-
<svg
93-
className="w-6 h-6 text-purple-600"
94-
fill="none"
95-
stroke="currentColor"
96-
viewBox="0 0 24 24"
97-
>
98-
<path
99-
strokeLinecap="round"
100-
strokeLinejoin="round"
101-
strokeWidth={2}
102-
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
103-
/>
104-
</svg>
105-
</div>
106-
<h1 className="text-3xl font-bold">API Demo</h1>
107-
</div>
108-
109-
<p className="text-gray-600 mb-6">
110-
Test authenticated API requests using the <code className="bg-gray-100 px-2 py-1 rounded text-sm">getAccessToken()</code> hook.
111-
</p>
112-
113-
{/* Actions */}
114-
<div className="space-y-4 mb-8">
115-
<div>
116-
<button
117-
onClick={handleGetToken}
118-
disabled={loading}
119-
className="w-full bg-blue-600 text-white py-3 px-6 rounded-lg font-semibold hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition"
120-
>
121-
{loading ? 'Loading...' : '1. Get Access Token'}
122-
</button>
123-
<p className="text-xs text-gray-500 mt-2">
124-
Retrieves your current access token (auto-refreshes if expired)
125-
</p>
126-
</div>
127-
128-
<div>
129-
<button
130-
onClick={handleMakeRequest}
131-
disabled={loading}
132-
className="w-full bg-purple-600 text-white py-3 px-6 rounded-lg font-semibold hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed transition"
133-
>
134-
{loading ? 'Loading...' : '2. Make Authenticated Request'}
68+
<Page narrow>
69+
<BackLink />
70+
<PageHeader
71+
icon={<Terminal />}
72+
title="API demo"
73+
description={<>Make authenticated requests with the token from <C>getAccessToken()</C>.</>}
74+
/>
75+
<div className="space-y-6">
76+
<Card className="divide-y divide-border">
77+
{[
78+
{ n: 1, label: 'Get access token', hint: 'Returns your current token, refreshed if it expired.', run: handleGetToken, primary: false },
79+
{ n: 2, label: 'Make authenticated request', hint: 'Calls /api/user/me with a Bearer token.', run: handleMakeRequest, primary: true },
80+
].map((step) => (
81+
<div key={step.n} className="flex flex-wrap items-center gap-4 p-5">
82+
<span className="flex size-7 shrink-0 items-center justify-center rounded-full bg-primary-subtle text-xs font-semibold text-primary-subtle-foreground">
83+
{step.n}
84+
</span>
85+
<div className="min-w-0 flex-1">
86+
<p className="text-sm font-semibold">{step.label}</p>
87+
<p className="text-sm text-muted-foreground">{step.hint}</p>
88+
</div>
89+
<button onClick={step.run} disabled={loading} className={step.primary ? button.primary : button.secondary}>
90+
{loading ? 'Loading…' : 'Run'}
13591
</button>
136-
<p className="text-xs text-gray-500 mt-2">
137-
Calls <code className="bg-gray-100 px-1 rounded">/api/user/me</code> with Bearer token
138-
</p>
13992
</div>
93+
))}
94+
</Card>
95+
96+
{token && (
97+
<div>
98+
<h3 className="mb-2 text-sm font-semibold">Access token</h3>
99+
<div className="break-all rounded-xl bg-surface-sunken p-4 font-mono text-xs text-muted-foreground">{token}</div>
100+
<p className="mt-2 text-xs text-subtle-foreground">Send it in the <C>Authorization</C> header.</p>
140101
</div>
102+
)}
141103

142-
{/* Token Display */}
143-
{token && (
144-
<div className="mb-6">
145-
<h3 className="font-semibold mb-2">Access Token:</h3>
146-
<div className="bg-gray-50 rounded-lg p-4">
147-
<code className="text-xs break-all">{token}</code>
148-
</div>
149-
<p className="text-xs text-gray-500 mt-2">
150-
This token is automatically included in the <code>Authorization</code> header
151-
</p>
152-
</div>
153-
)}
154-
155-
{/* Response Display */}
156-
{response && (
157-
<div className="mb-6">
158-
<h3 className="font-semibold mb-2">API Response:</h3>
159-
<div className="bg-green-50 rounded-lg p-4">
160-
<pre className="text-xs overflow-x-auto">
161-
{JSON.stringify(response, null, 2)}
162-
</pre>
163-
</div>
164-
</div>
165-
)}
166-
167-
{/* Error Display */}
168-
{error && (
169-
<div className="mb-6">
170-
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
171-
<h3 className="font-semibold text-red-900 mb-2">Error:</h3>
172-
<p className="text-sm text-red-800">{error}</p>
173-
</div>
174-
</div>
175-
)}
104+
{response && <CodeBlock title="Response" code={JSON.stringify(response, null, 2)} />}
176105

177-
{/* Code Example */}
178-
<div className="bg-gray-50 rounded-lg p-4">
179-
<h3 className="font-semibold mb-3">Example Code:</h3>
180-
<pre className="text-xs overflow-x-auto">
181-
{`import { useHyperyAuth } from '@hyperyai/sdk';
106+
{error && (
107+
<Note tone="warning" icon={<TriangleAlert />} title="Request failed">
108+
{error}
109+
</Note>
110+
)}
111+
112+
<CodeBlock
113+
title="Example"
114+
code={`import { useHyperyAuth } from '@hyperyai/sdk';
182115
183116
function MyComponent() {
184117
const { getAccessToken } = useHyperyAuth();
185118
186119
const makeRequest = async () => {
187120
// Get valid token (auto-refreshes if needed)
188121
const token = await getAccessToken();
189-
190-
// Make authenticated request
122+
191123
const response = await fetch('/api/endpoint', {
192-
headers: {
193-
Authorization: \`Bearer \${token}\`,
194-
},
124+
headers: { Authorization: \`Bearer \${token}\` },
195125
});
196-
197126
return response.json();
198127
};
199128
}`}
200-
</pre>
201-
</div>
202-
</div>
129+
/>
203130
</div>
204-
</div>
131+
</Page>
205132
);
206133
}
207-

‎auth-demo/app/callback/page.tsx‎

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import { useEffect } from 'react';
44
import { useHyperyAuth } from '@hyperyai/sdk';
55
import { useRouter } from 'next/navigation';
6+
import { X } from 'lucide-react';
7+
import { Spinner } from '@/components/ui';
68

79
export default function CallbackPage() {
810
const { isAuthenticated, isLoading, error } = useHyperyAuth();
@@ -31,38 +33,25 @@ export default function CallbackPage() {
3133
}, [isAuthenticated, isLoading, error, router]);
3234

3335
return (
34-
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-purple-50 flex items-center justify-center">
35-
<div className="bg-white rounded-2xl shadow-xl p-8 max-w-md w-full text-center">
36+
<div className="flex min-h-screen items-center justify-center p-4">
37+
<div className="w-full max-w-sm rounded-2xl bg-card p-8 text-center shadow-card">
3638
{error ? (
3739
<>
38-
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4">
39-
<svg
40-
className="w-8 h-8 text-red-600"
41-
fill="none"
42-
stroke="currentColor"
43-
viewBox="0 0 24 24"
44-
>
45-
<path
46-
strokeLinecap="round"
47-
strokeLinejoin="round"
48-
strokeWidth={2}
49-
d="M6 18L18 6M6 6l12 12"
50-
/>
51-
</svg>
40+
<div className="mx-auto mb-4 flex size-11 items-center justify-center rounded-full bg-danger-subtle text-danger-foreground">
41+
<X className="size-5" />
5242
</div>
53-
<h2 className="text-2xl font-bold mb-2">Authentication Failed</h2>
54-
<p className="text-gray-600 mb-4">{error}</p>
55-
<p className="text-sm text-gray-500">Redirecting back to home...</p>
43+
<h2 className="text-xl font-semibold tracking-tight">Authentication failed</h2>
44+
<p className="mt-2 text-sm text-muted-foreground">{error}</p>
45+
<p className="mt-4 text-xs text-subtle-foreground">Redirecting back to home…</p>
5646
</>
5747
) : (
5848
<>
59-
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4" />
60-
<h2 className="text-2xl font-bold mb-2">Completing Sign In</h2>
61-
<p className="text-gray-600">Please wait while we authenticate you...</p>
49+
<Spinner className="mx-auto mb-4 block size-8" />
50+
<h2 className="text-xl font-semibold tracking-tight">Completing sign in</h2>
51+
<p className="mt-2 text-sm text-muted-foreground">One moment while we authenticate you…</p>
6252
</>
6353
)}
6454
</div>
6555
</div>
6656
);
6757
}
68-

‎auth-demo/app/checkout/page.tsx‎

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
import { BuyButton, useCheckout, useHyperyAuth } from '@hyperyai/sdk';
1212
import { useState } from 'react';
13+
import { CreditCard } from 'lucide-react';
14+
import { C, Card, Page, PageHeader, button } from '@/components/ui';
1315

1416
export default function CheckoutDemoPage() {
1517
const { isAuthenticated, interactionMode } = useHyperyAuth();
@@ -31,51 +33,49 @@ export default function CheckoutDemoPage() {
3133
};
3234

3335
return (
34-
<main style={{ maxWidth: 560, margin: '48px auto', padding: '0 20px', fontFamily: 'system-ui' }}>
35-
<h1 style={{ fontSize: 24, fontWeight: 700 }}>Auth + charge demo</h1>
36-
<p style={{ color: '#555', marginTop: 8 }}>
37-
Signed in: <b>{isAuthenticated ? 'yes' : 'no'}</b> · interaction mode:{' '}
38-
<b>{interactionMode}</b>
39-
</p>
40-
41-
<section style={{ marginTop: 32 }}>
42-
<h2 style={{ fontSize: 16, fontWeight: 600 }}>Marketplace purchase (&lt;BuyButton&gt;)</h2>
43-
<p style={{ color: '#777', fontSize: 14, margin: '4px 0 12px' }}>
44-
Charges a seller app via Stripe Connect. Logs you in and adds a card first if needed.
45-
</p>
46-
<BuyButton
47-
appId={process.env.NEXT_PUBLIC_DEMO_SELLER_APP_ID || 'app_demo_seller'}
48-
amountCents={499}
49-
description="Pro upgrade (demo)"
50-
onSuccess={(r) => alert(`Paid! paymentIntent=${r.paymentIntentId}`)}
51-
onError={(e) => alert(`Purchase failed: ${e.message}`)}
52-
/>
53-
</section>
54-
55-
<section style={{ marginTop: 40 }}>
56-
<h2 style={{ fontSize: 16, fontWeight: 600 }}>Buy AI credits (useCheckout topup)</h2>
57-
<p style={{ color: '#777', fontSize: 14, margin: '4px 0 12px' }}>
58-
Tops up your own credit balance. Same login → add-card → charge chain.
59-
</p>
60-
<button
61-
type="button"
62-
onClick={buyCredits}
63-
disabled={isRunning}
64-
style={{
65-
padding: '10px 20px',
66-
borderRadius: 8,
67-
border: 'none',
68-
background: '#111',
69-
color: '#fff',
70-
fontWeight: 600,
71-
cursor: isRunning ? 'not-allowed' : 'pointer',
72-
opacity: isRunning ? 0.6 : 1,
73-
}}
74-
>
75-
{isRunning ? `${status}…` : 'Add $5 credits'}
76-
</button>
77-
{topupResult && <p style={{ marginTop: 12, fontSize: 14 }}>{topupResult}</p>}
78-
</section>
79-
</main>
36+
<Page narrow>
37+
<PageHeader
38+
icon={<CreditCard />}
39+
title="Auth + charge"
40+
description="Neither button needs you signed in: the SDK walks you through sign-in, adding a card and the charge."
41+
/>
42+
<div className="mb-6 flex flex-wrap gap-2 text-xs">
43+
<span className="rounded-md bg-muted px-2 py-1 text-muted-foreground">
44+
Signed in: <b className="text-foreground">{isAuthenticated ? 'yes' : 'no'}</b>
45+
</span>
46+
<span className="rounded-md bg-muted px-2 py-1 text-muted-foreground">
47+
Interaction mode: <b className="text-foreground">{interactionMode}</b>
48+
</span>
49+
</div>
50+
<div className="grid gap-5 sm:grid-cols-2">
51+
<Card className="flex flex-col p-6">
52+
<h2 className="font-semibold">Marketplace purchase</h2>
53+
<p className="mt-1 flex-1 text-sm text-muted-foreground">
54+
<C>{'<BuyButton>'}</C> charges a seller app via Stripe Connect.
55+
</p>
56+
<div className="mt-5">
57+
<BuyButton
58+
appId={process.env.NEXT_PUBLIC_DEMO_SELLER_APP_ID || 'app_demo_seller'}
59+
amountCents={499}
60+
description="Pro upgrade (demo)"
61+
onSuccess={(r) => alert(`Paid! paymentIntent=${r.paymentIntentId}`)}
62+
onError={(e) => alert(`Purchase failed: ${e.message}`)}
63+
/>
64+
</div>
65+
</Card>
66+
<Card className="flex flex-col p-6">
67+
<h2 className="font-semibold">Buy AI credits</h2>
68+
<p className="mt-1 flex-1 text-sm text-muted-foreground">
69+
<C>useCheckout()</C> tops up your own balance.
70+
</p>
71+
<div className="mt-5">
72+
<button type="button" onClick={buyCredits} disabled={isRunning} className={button.secondary}>
73+
{isRunning ? `${status}…` : 'Add $5 credits'}
74+
</button>
75+
</div>
76+
{topupResult && <p className="mt-3 text-sm text-muted-foreground">{topupResult}</p>}
77+
</Card>
78+
</div>
79+
</Page>
8080
);
8181
}

0 commit comments

Comments
 (0)