From a58fba1ba6f3ae22cf4540874034e69ba67adec5 Mon Sep 17 00:00:00 2001 From: spacedevin Date: Mon, 12 Oct 2026 23:52:15 -0700 Subject: [PATCH 1/3] 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. --- auth-demo/app/api-demo/page.tsx | 170 ++----- auth-demo/app/callback/page.tsx | 35 +- auth-demo/app/checkout/page.tsx | 92 ++-- auth-demo/app/examples/page.tsx | 731 +++++++++++---------------- auth-demo/app/globals.css | 202 +++++--- auth-demo/app/layout.tsx | 22 +- auth-demo/app/modals/page.tsx | 106 ++-- auth-demo/app/page.tsx | 354 +++++-------- auth-demo/app/protected/page.tsx | 124 ++--- auth-demo/components/site-header.tsx | 94 ++++ auth-demo/components/ui.tsx | 178 +++++++ auth-demo/package-lock.json | 88 +++- auth-demo/package.json | 1 + auth-demo/tailwind.config.ts | 14 - 14 files changed, 1115 insertions(+), 1096 deletions(-) create mode 100644 auth-demo/components/site-header.tsx create mode 100644 auth-demo/components/ui.tsx delete mode 100644 auth-demo/tailwind.config.ts diff --git a/auth-demo/app/api-demo/page.tsx b/auth-demo/app/api-demo/page.tsx index af7ee38..d491af6 100644 --- a/auth-demo/app/api-demo/page.tsx +++ b/auth-demo/app/api-demo/page.tsx @@ -1,7 +1,8 @@ 'use client'; import { Protect, useHyperyAuth } from '@hyperyai/sdk'; -import Link from 'next/link'; +import { Terminal, TriangleAlert } from 'lucide-react'; +import { BackLink, C, Card, CodeBlock, Note, Page, PageHeader, button } from '@/components/ui'; import { useState } from 'react'; export default function ApiDemoPage() { @@ -64,121 +65,53 @@ function ApiDemoContent() { }; return ( -
-
- - - - - Back to Home - - -
-
-
- - - -
-

API Demo

-
- -

- Test authenticated API requests using the getAccessToken() hook. -

- - {/* Actions */} -
-
- -

- Retrieves your current access token (auto-refreshes if expired) -

-
- -
- -

- Calls /api/user/me with Bearer token -

+ ))} + + + {token && ( +
+

Access token

+
{token}
+

Send it in the Authorization header.

+ )} - {/* Token Display */} - {token && ( -
-

Access Token:

-
- {token} -
-

- This token is automatically included in the Authorization header -

-
- )} - - {/* Response Display */} - {response && ( -
-

API Response:

-
-
-                  {JSON.stringify(response, null, 2)}
-                
-
-
- )} - - {/* Error Display */} - {error && ( -
-
-

Error:

-

{error}

-
-
- )} + {response && } - {/* Code Example */} -
-

Example Code:

-
-{`import { useHyperyAuth } from '@hyperyai/sdk';
+        {error && (
+          } title="Request failed">
+            {error}
+          
+        )}
+
+         {
     // Get valid token (auto-refreshes if needed)
     const token = await getAccessToken();
-    
-    // Make authenticated request
+
     const response = await fetch('/api/endpoint', {
-      headers: {
-        Authorization: \`Bearer \${token}\`,
-      },
+      headers: { Authorization: \`Bearer \${token}\` },
     });
-    
     return response.json();
   };
 }`}
-            
-
-
+ />
-
+ ); } - diff --git a/auth-demo/app/callback/page.tsx b/auth-demo/app/callback/page.tsx index 2e466d0..37ec6f8 100644 --- a/auth-demo/app/callback/page.tsx +++ b/auth-demo/app/callback/page.tsx @@ -3,6 +3,8 @@ import { useEffect } from 'react'; import { useHyperyAuth } from '@hyperyai/sdk'; import { useRouter } from 'next/navigation'; +import { X } from 'lucide-react'; +import { Spinner } from '@/components/ui'; export default function CallbackPage() { const { isAuthenticated, isLoading, error } = useHyperyAuth(); @@ -31,38 +33,25 @@ export default function CallbackPage() { }, [isAuthenticated, isLoading, error, router]); return ( -
-
+
+
{error ? ( <> -
- - - +
+
-

Authentication Failed

-

{error}

-

Redirecting back to home...

+

Authentication failed

+

{error}

+

Redirecting back to home…

) : ( <> -
-

Completing Sign In

-

Please wait while we authenticate you...

+ +

Completing sign in

+

One moment while we authenticate you…

)}
); } - diff --git a/auth-demo/app/checkout/page.tsx b/auth-demo/app/checkout/page.tsx index 63700f4..10e899c 100644 --- a/auth-demo/app/checkout/page.tsx +++ b/auth-demo/app/checkout/page.tsx @@ -10,6 +10,8 @@ import { BuyButton, useCheckout, useHyperyAuth } from '@hyperyai/sdk'; import { useState } from 'react'; +import { CreditCard } from 'lucide-react'; +import { C, Card, Page, PageHeader, button } from '@/components/ui'; export default function CheckoutDemoPage() { const { isAuthenticated, interactionMode } = useHyperyAuth(); @@ -31,51 +33,49 @@ export default function CheckoutDemoPage() { }; return ( -
-

Auth + charge demo

-

- Signed in: {isAuthenticated ? 'yes' : 'no'} · interaction mode:{' '} - {interactionMode} -

- -
-

Marketplace purchase (<BuyButton>)

-

- Charges a seller app via Stripe Connect. Logs you in and adds a card first if needed. -

- alert(`Paid! paymentIntent=${r.paymentIntentId}`)} - onError={(e) => alert(`Purchase failed: ${e.message}`)} - /> -
- -
-

Buy AI credits (useCheckout topup)

-

- Tops up your own credit balance. Same login → add-card → charge chain. -

- - {topupResult &&

{topupResult}

} -
-
+ + } + title="Auth + charge" + description="Neither button needs you signed in: the SDK walks you through sign-in, adding a card and the charge." + /> +
+ + Signed in: {isAuthenticated ? 'yes' : 'no'} + + + Interaction mode: {interactionMode} + +
+
+ +

Marketplace purchase

+

+ {''} charges a seller app via Stripe Connect. +

+
+ alert(`Paid! paymentIntent=${r.paymentIntentId}`)} + onError={(e) => alert(`Purchase failed: ${e.message}`)} + /> +
+
+ +

Buy AI credits

+

+ useCheckout() tops up your own balance. +

+
+ +
+ {topupResult &&

{topupResult}

} +
+
+
); } diff --git a/auth-demo/app/examples/page.tsx b/auth-demo/app/examples/page.tsx index 2dfa1d6..9c0c914 100644 --- a/auth-demo/app/examples/page.tsx +++ b/auth-demo/app/examples/page.tsx @@ -10,431 +10,311 @@ import { UserProfile, useAuth, useUser, - Protect, - RedirectToSignIn, AuthModal, AuthButton, } from '@hyperyai/sdk'; import Link from 'next/link'; import { useState } from 'react'; +import { CircleCheck, Info, TriangleAlert } from 'lucide-react'; +import { Card, CodeBlock, Note, Page, PageHeader, Section, button, cx } from '@/components/ui'; + +function Hint({ children }: { children: React.ReactNode }) { + return

{children}

; +} + +const SIGNED_IN_HINT = "You're already signed in. Sign out to try this."; export default function ExamplesPage() { return ( -
- {/* Header */} -
-
-
-
- -
-

Hypery Auth

- - | - Examples -
- - - - -
-
-
- -
-
-

Component Examples

-

- Complete showcase of all @hyperyai/sdk components and hooks -

-
+ + -
- {/* Modal Components */} -
-
- +
+
+ router.push('/dashboard')} > Sign In `} - > - -
- Sign In - - Get Started - - - Outline Style - -
-
- -

- You're already signed in! Sign out to test this. -

-
-
- - + +
+ Sign In + + Get Started + + + Outline Style + +
+
+ + {SIGNED_IN_HINT} + +
+ + setIsOpen(false)} initialMode="signin" - onSuccess={() => console.log('Success!')} + showEmailPassword + branding={{ appName: 'Acme' }} />`} - > - - - - -

- You're already signed in! Sign out to test this. -

-
-
-
-
- - {/* Authentication Components */} -
-
- + + + + + {SIGNED_IN_HINT} + + +
+
+ +
+
+ `} - > - + > + +
- - -

- You're already signed in! Sign out to test this. -

-
- - - + + + {SIGNED_IN_HINT} + + + + `} + > + +
+ +
+
+ + {SIGNED_IN_HINT} + +
+ + console.log('Signed in!')} />`} - > - - - - -

- You're already signed in! Sign out to test this. -

-
-
- - `} - > - - - - -

- You're already signed in! Sign out to test this. -

-
-
- + > + + + + + {SIGNED_IN_HINT} + + + +
`} > - +
+ +
-

- Sign in to see the user button -

+ Sign in to see the user button.
`} + description="The signed-in user as a card." + code={``} > -

- Sign in to see your profile -

+ Sign in to see your profile.
-
- - {/* Control Components */} -
-
- +
+
+ +
+
+

You can see this!

`} - > -
- -
-

- ✓ You're signed in, so you can see this! -

-
-
- -
-

- You're not signed in. The SignedIn component won't show. -

-
-
-
-
- - + > + + }> + You're signed in, so you can see this. + + + + }>You're signed out, so SignedIn renders nothing. + + + + `} - > -
- -
-

- ✓ You're signed out, so you can see this! -

- -
-
- -
-

- You're signed in. The SignedOut component won't show. -

-
-
+ > + +
+ }> + You're signed out, so you can see this. + +
- - - } -> +
+ + }>You're signed in, so SignedOut renders nothing. + + + + }> `} - > -
-

- See the protected page for a live example -

-
-
- - `} - > - -

- You're signed in, so it won't redirect -

-
- -

- ⚠️ Disabled in this example to prevent redirect loop -

-
-
-
-
- - {/* Hooks */} -
-
- + }> + See the{' '} + + protected page + {' '} + for a live example. + + + + `} + > + + You're signed in, so it won't redirect. + + + }> + Disabled on this page to avoid a redirect loop. + + + +
+
+ +
+
+ - - + > + + - - - -
-
- - {/* Styling Examples */} -
-
- `} - > - - - - -
- Primary Style -
-
-
- + > + + +
+
+ +
+
+ {(['primary', 'secondary', 'outline'] as const).map((variant) => ( `} + key={variant} + title={`variant="${variant}"`} + description={ + variant === 'primary' ? 'The brand fill.' : variant === 'secondary' ? 'A quiet filled button.' : 'A hairline outline.' + } + code={``} > - - - -
- Secondary Style +
+
- - - - `} - > - - -
- Outline Style -
+ {SIGNED_IN_HINT}
-
-
-
- - {/* Back to Home */} -
- - - - - Back to Home - -
-
-
- ); -} - -function Section({ - title, - description, - children, -}: { - title: string; - description: string; - children: React.ReactNode; -}) { - return ( -
-
-

{title}

-

{description}

+ ))} +
+ +
- {children} - + ); } @@ -449,64 +329,74 @@ function ExampleCard({ code: string; children: React.ReactNode; }) { - const [showCode, setShowCode] = useState(false); + const [tab, setTab] = useState<'preview' | 'code'>('preview'); return ( -
-
-
-
-

{title}

-

{description}

-
- + +
+
+

{title}

+

{description}

+
+ {(['preview', 'code'] as const).map((t) => ( + + ))} +
+
- {showCode && ( -
-
-              {code}
-            
+
+ {tab === 'code' ? ( + + ) : ( +
+
{children}
)} - -
{children}
+ + ); +} + +function Row({ k, v, tone }: { k: string; v: string; tone?: 'success' | 'danger' | 'muted' }) { + return ( +
+ {k} + + {v} +
); } function UseAuthExample() { - const { isAuthenticated, isLoading, user, login, logout } = useAuth(); + const { isAuthenticated, isLoading, user } = useAuth(); return ( -
-
- isAuthenticated: - - {String(isAuthenticated)} - -
-
- isLoading: - {String(isLoading)} -
-
- user: - - {user ? 'object' : 'null'} - -
-
-
Methods:
-
• login()
-
• logout()
-
+
+ + + +
); } @@ -514,27 +404,16 @@ function UseAuthExample() { function UseUserExample() { const { user, isLoading } = useUser(); - if (isLoading) { - return

Loading...

; - } - - if (!user) { - return

Not signed in

; - } + if (isLoading) return Loading…; + if (!user) return Not signed in, so user is null.; return ( -
-
-
user.name
-
{user.name}
-
-
-
user.email
-
{user.email}
-
-
-
user.id
-
{user.id}
+
+ + +
+ user.id + {user.id}
); @@ -544,28 +423,24 @@ function AuthModalExample() { const [isOpen, setIsOpen] = useState(false); return ( - <> - - + setIsOpen(false)} initialMode="signin" + showEmailPassword onSuccess={() => { setIsOpen(false); alert('Successfully signed in!'); }} branding={{ appName: 'Hypery', - primaryColor: '#8b5cf6', }} /> - +
); } - diff --git a/auth-demo/app/globals.css b/auth-demo/app/globals.css index 4d7f4e0..0565b44 100644 --- a/auth-demo/app/globals.css +++ b/auth-demo/app/globals.css @@ -1,94 +1,156 @@ +/* Tailwind 4, CSS-first: no tailwind.config file. */ @import "tailwindcss"; @import "tw-animate-css"; -@custom-variant dark (&:is(.dark *)); +@custom-variant dark (&:where(.dark, .dark *)); + +/* Hypery design tokens (same values as hypery.ai). The SDK components read + the matching --hy-* tokens; brand both at once by setting them here. */ +:root { + color-scheme: light; + --radius: 0.625rem; + + --background: oklch(0.982 0.006 240); + --surface-sunken: oklch(0.962 0.01 240); + --card: oklch(1 0 0); + --popover: oklch(1 0 0); + --foreground: oklch(0.27 0.06 255); + --muted-foreground: oklch(0.49 0.035 252); + --subtle-foreground: oklch(0.63 0.025 250); + --muted: oklch(0.955 0.01 240); + --accent: oklch(0.955 0.012 240); + --accent-foreground: oklch(0.30 0.06 255); + --field: oklch(0.938 0.012 240); + --field-hover: oklch(0.918 0.014 240); + --border: oklch(0.27 0.06 255 / 0.07); + --border-strong: oklch(0.27 0.06 255 / 0.18); + --ring: oklch(0.62 0.15 232); + + --primary: oklch(0.50 0.14 236); + --primary-foreground: oklch(0.99 0 0); + --primary-hover: oklch(0.43 0.13 240); + --primary-subtle: oklch(0.955 0.03 225); + --primary-subtle-foreground: oklch(0.42 0.13 240); + + --success: oklch(0.56 0.13 155); + --success-subtle: oklch(0.955 0.04 155); + --success-foreground: oklch(0.38 0.10 155); + --warning: oklch(0.70 0.15 75); + --warning-subtle: oklch(0.96 0.05 85); + --warning-foreground: oklch(0.42 0.10 65); + --danger-subtle: oklch(0.955 0.03 25); + --danger-foreground: oklch(0.42 0.16 27); + --destructive: oklch(0.55 0.21 27); + + --code: oklch(0.21 0.034 258); + --code-foreground: oklch(0.92 0.01 240); + --syntax-comment: oklch(0.60 0.03 250); + + --brand-1: oklch(0.70 0.15 210); + --brand-2: oklch(0.58 0.19 262); + --brand-3: oklch(0.62 0.21 300); + + --elevation-xs: 0 1px 2px 0 oklch(0.3 0.08 262 / 0.08), 0 1px 1px 0 oklch(0 0 0 / 0.03); + --elevation-card: 0 1px 2px 0 oklch(0.3 0.08 262 / 0.07), 0 4px 14px -4px oklch(0.3 0.08 262 / 0.10); + --elevation-card-hover: 0 8px 16px -4px oklch(0.3 0.08 262 / 0.14), 0 20px 40px -16px oklch(0.3 0.08 262 / 0.22); +} + +.dark { + color-scheme: dark; + --background: oklch(0.17 0.03 258); + --surface-sunken: oklch(0.145 0.028 258); + --card: oklch(0.21 0.034 258); + --popover: oklch(0.245 0.038 258); + --foreground: oklch(0.96 0.008 240); + --muted-foreground: oklch(0.74 0.03 248); + --subtle-foreground: oklch(0.60 0.03 250); + --muted: oklch(0.26 0.036 258); + --accent: oklch(0.27 0.04 258); + --accent-foreground: oklch(0.96 0.008 240); + --field: oklch(0.255 0.036 258); + --field-hover: oklch(0.30 0.04 258); + --border: oklch(1 0 0 / 0.06); + --border-strong: oklch(1 0 0 / 0.16); + --ring: oklch(0.74 0.14 222); + --primary: oklch(0.74 0.14 222); + --primary-foreground: oklch(0.17 0.04 250); + --primary-hover: oklch(0.80 0.13 220); + --primary-subtle: oklch(0.30 0.07 240); + --primary-subtle-foreground: oklch(0.86 0.09 220); + --success: oklch(0.72 0.15 155); + --success-subtle: oklch(0.26 0.05 155); + --success-foreground: oklch(0.85 0.12 155); + --warning: oklch(0.80 0.14 80); + --warning-subtle: oklch(0.28 0.05 75); + --warning-foreground: oklch(0.88 0.11 85); + --danger-subtle: oklch(0.27 0.06 25); + --danger-foreground: oklch(0.86 0.10 25); + --destructive: oklch(0.64 0.19 25); + --code: oklch(0.145 0.028 258); + --elevation-xs: 0 1px 2px 0 oklch(0 0 0 / 0.4); + --elevation-card: 0 0 0 1px oklch(1 0 0 / 0.05), 0 8px 24px -12px oklch(0 0 0 / 0.5); + --elevation-card-hover: 0 0 0 1px oklch(1 0 0 / 0.08), 0 20px 40px -16px oklch(0 0 0 / 0.75); +} @theme inline { + --font-sans: var(--font-instrument-sans), ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; + --font-mono: var(--font-jetbrains-mono), ui-monospace, SFMono-Regular, Menlo, monospace; + --color-background: var(--background); + --color-surface-sunken: var(--surface-sunken); + --color-card: var(--card); + --color-popover: var(--popover); --color-foreground: var(--foreground); - --color-ring: var(--ring); - --color-input: var(--input); - --color-border: var(--border); - --color-destructive: var(--destructive); - --color-accent-foreground: var(--accent-foreground); - --color-accent: var(--accent); - --color-muted-foreground: var(--muted-foreground); --color-muted: var(--muted); - --color-secondary-foreground: var(--secondary-foreground); - --color-secondary: var(--secondary); - --color-primary-foreground: var(--primary-foreground); + --color-muted-foreground: var(--muted-foreground); + --color-subtle-foreground: var(--subtle-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-field: var(--field); + --color-field-hover: var(--field-hover); + --color-border: var(--border); + --color-border-strong: var(--border-strong); + --color-ring: var(--ring); --color-primary: var(--primary); - --color-popover-foreground: var(--popover-foreground); - --color-popover: var(--popover); - --color-card-foreground: var(--card-foreground); - --color-card: var(--card); + --color-primary-foreground: var(--primary-foreground); + --color-primary-hover: var(--primary-hover); + --color-primary-subtle: var(--primary-subtle); + --color-primary-subtle-foreground: var(--primary-subtle-foreground); + --color-success: var(--success); + --color-success-subtle: var(--success-subtle); + --color-success-foreground: var(--success-foreground); + --color-warning: var(--warning); + --color-warning-subtle: var(--warning-subtle); + --color-warning-foreground: var(--warning-foreground); + --color-danger-subtle: var(--danger-subtle); + --color-danger-foreground: var(--danger-foreground); + --color-destructive: var(--destructive); + --color-code: var(--code); + --color-code-foreground: var(--code-foreground); + --color-syntax-comment: var(--syntax-comment); + --color-brand-1: var(--brand-1); + --color-brand-2: var(--brand-2); + --color-brand-3: var(--brand-3); + --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); --radius-xl: calc(var(--radius) + 4px); -} + --radius-2xl: calc(var(--radius) + 6px); -:root { - color-scheme: light; - --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.129 0.042 264.695); - --card: oklch(1 0 0); - --card-foreground: oklch(0.129 0.042 264.695); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.129 0.042 264.695); - --primary: oklch(0.208 0.042 265.755); - --primary-foreground: oklch(0.984 0.003 247.858); - --secondary: oklch(0.968 0.007 247.896); - --secondary-foreground: oklch(0.208 0.042 265.755); - --muted: oklch(0.968 0.007 247.896); - --muted-foreground: oklch(0.554 0.046 257.417); - --accent: oklch(0.968 0.007 247.896); - --accent-foreground: oklch(0.208 0.042 265.755); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.929 0.013 255.508); - --input: oklch(0.929 0.013 255.508); - --ring: oklch(0.704 0.04 256.788); + --shadow-xs: var(--elevation-xs); + --shadow-card: var(--elevation-card); + --shadow-card-hover: var(--elevation-card-hover); } @layer base { * { @apply border-border outline-ring/50; } - body { - @apply bg-background text-foreground; - } -} - -@layer utilities { - @keyframes collapsible-down { - from { - height: 0; - opacity: 0; - } - to { - height: var(--radix-collapsible-content-height); - opacity: 1; - } - } - - @keyframes collapsible-up { - from { - height: var(--radix-collapsible-content-height); - opacity: 1; - } - to { - height: 0; - opacity: 0; - } + @apply bg-background font-sans text-foreground antialiased; } - - .animate-collapsible-down { - animation: collapsible-down 0.2s ease-out; - } - - .animate-collapsible-up { - animation: collapsible-up 0.2s ease-out; + ::selection { + @apply bg-primary-subtle text-primary-subtle-foreground; } } diff --git a/auth-demo/app/layout.tsx b/auth-demo/app/layout.tsx index 2d94463..454d301 100644 --- a/auth-demo/app/layout.tsx +++ b/auth-demo/app/layout.tsx @@ -1,20 +1,33 @@ import type { Metadata } from 'next'; +import { Instrument_Sans, JetBrains_Mono } from 'next/font/google'; import { HyperyProvider } from '@hyperyai/sdk'; +// The SDK's components ship their own stylesheet: import it once. +import '@hyperyai/sdk/styles.css'; import './globals.css'; +import { SiteHeader } from '@/components/site-header'; + +const sans = Instrument_Sans({ subsets: ['latin'], variable: '--font-instrument-sans' }); +const mono = JetBrains_Mono({ subsets: ['latin'], variable: '--font-jetbrains-mono' }); export const metadata: Metadata = { title: 'Hypery Auth Demo', description: 'Demo app showcasing @hyperyai/sdk package', }; +// Picks light/dark before paint: a saved choice, else the OS preference. +const themeScript = `(function(){try{var t=localStorage.getItem('theme');if(!t){t=matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'}document.documentElement.classList.add(t)}catch(e){}})()`; + export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( - - + + +