@hanzo/ui 5.6.1 → 5.7.1
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/dist/3d/carousel.js +1 -1
- package/dist/3d/carousel.mjs +1 -1
- package/dist/animation/animated-background.js +1 -1
- package/dist/animation/animated-background.mjs +1 -1
- package/dist/animation/animated-icon.js +2 -2
- package/dist/animation/animated-icon.mjs +2 -2
- package/dist/animation/animated-list.js +1 -1
- package/dist/animation/animated-list.mjs +1 -1
- package/dist/animation/animated-number.js +1 -1
- package/dist/animation/animated-number.mjs +1 -1
- package/dist/animation/index.js +2 -2
- package/dist/animation/index.mjs +2 -2
- package/dist/auth/index.js +2 -2
- package/dist/auth/index.mjs +2 -2
- package/dist/billing/index.js +2 -2
- package/dist/billing/index.mjs +2 -2
- package/dist/drawer.js +1 -1
- package/dist/drawer.mjs +1 -1
- package/dist/models/index.js +1 -1
- package/dist/models/index.mjs +1 -1
- package/dist/navigation/hanzo-shell.js +1 -1
- package/dist/navigation/hanzo-shell.mjs +1 -1
- package/dist/navigation/index.js +2 -2
- package/dist/navigation/index.mjs +2 -2
- package/dist/primitives/drawer.d.ts +2 -2
- package/dist/primitives/drawer.d.ts.map +1 -1
- package/dist/primitives/form.d.ts +1 -1
- package/dist/primitives/form.d.ts.map +1 -1
- package/dist/src/auth/AuthGuard.d.ts.map +1 -1
- package/dist/src/auth/AuthGuard.js +3 -7
- package/dist/src/auth/IAMLoginButton.d.ts +10 -3
- package/dist/src/auth/IAMLoginButton.d.ts.map +1 -1
- package/dist/src/auth/IAMLoginButton.js +14 -19
- package/dist/src/auth/PasswordForm.d.ts +22 -0
- package/dist/src/auth/PasswordForm.d.ts.map +1 -0
- package/dist/src/auth/PasswordForm.js +28 -0
- package/dist/src/auth/SignIn.d.ts +37 -0
- package/dist/src/auth/SignIn.d.ts.map +1 -0
- package/dist/src/auth/SignIn.js +26 -0
- package/dist/src/auth/SocialButton.d.ts +27 -0
- package/dist/src/auth/SocialButton.d.ts.map +1 -0
- package/dist/src/auth/SocialButton.js +37 -0
- package/dist/src/auth/Web3Connect.d.ts +9 -0
- package/dist/src/auth/Web3Connect.d.ts.map +1 -0
- package/dist/src/auth/Web3Connect.js +11 -0
- package/dist/src/auth/iam.d.ts +56 -0
- package/dist/src/auth/iam.d.ts.map +1 -0
- package/dist/src/auth/iam.js +78 -0
- package/dist/src/auth/index.d.ts +11 -0
- package/dist/src/auth/index.d.ts.map +1 -1
- package/dist/src/auth/index.js +9 -0
- package/dist/src/auth/types.d.ts +43 -0
- package/dist/src/auth/types.d.ts.map +1 -0
- package/dist/src/auth/types.js +8 -0
- package/dist/src/billing/components/payment-manager.d.ts.map +1 -1
- package/dist/src/billing/components/payment-manager.js +13 -1
- package/dist/src/form/form.d.ts +4 -2
- package/dist/src/form/form.d.ts.map +1 -1
- package/dist/src/models/ModelCard.js +2 -2
- package/dist/src/navigation/hanzo-shell/AppSwitcher.js +1 -1
- package/dist/src/navigation/hanzo-shell/types.d.ts +1 -0
- package/dist/src/navigation/hanzo-shell/types.d.ts.map +1 -1
- package/dist/src/navigation/hanzo-shell/types.js +11 -0
- package/dist/src/navigation/hanzo-shell/useHanzoAuth.js +2 -2
- package/dist/util/index.mjs +1 -1
- package/package.json +7 -15
|
@@ -1,27 +1,22 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useState } from 'react';
|
|
4
|
+
import { cn } from '../utils';
|
|
5
|
+
import { startIamLogin } from './iam';
|
|
4
6
|
/**
|
|
5
|
-
* "Sign in
|
|
6
|
-
*
|
|
7
|
+
* Single "Sign in" button that initiates the canonical IAM authorization-code +
|
|
8
|
+
* PKCE-S256 flow (HIP-0111). Brand-neutral — colors come from CSS tokens
|
|
9
|
+
* (`bg-primary`, `border-input`, `bg-background`), never literals.
|
|
7
10
|
*/
|
|
8
|
-
export function IAMLoginButton({ iamUrl = 'https://hanzo.id', clientId, redirectUri, label = 'Sign in
|
|
11
|
+
export function IAMLoginButton({ iamUrl = 'https://hanzo.id', clientId, redirectUri, provider, label = 'Sign in', className, variant = 'default', }) {
|
|
9
12
|
const [loading, setLoading] = useState(false);
|
|
10
13
|
const handleClick = useCallback(() => {
|
|
11
14
|
setLoading(true);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
url.searchParams.set('state', state);
|
|
20
|
-
window.location.href = url.toString();
|
|
21
|
-
}, [iamUrl, clientId, redirectUri]);
|
|
22
|
-
const baseStyles = 'inline-flex items-center justify-center gap-2 rounded-md px-4 py-2.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50';
|
|
23
|
-
const variantStyles = variant === 'outline'
|
|
24
|
-
? 'border border-[#333] bg-transparent text-white hover:bg-[#1a1a1f]'
|
|
25
|
-
: 'bg-white text-black hover:bg-[#e5e5e5]';
|
|
26
|
-
return (_jsxs("button", { type: "button", className: `${baseStyles} ${variantStyles} ${className}`, onClick: handleClick, disabled: loading, children: [_jsx("svg", { width: "16", height: "16", viewBox: "0 0 32 32", xmlns: "http://www.w3.org/2000/svg", children: _jsx("text", { x: "4", y: "26", fontFamily: "-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif", fontSize: "28", fontWeight: "700", fill: "currentColor", children: "H" }) }), loading ? 'Redirecting...' : label] }));
|
|
15
|
+
void startIamLogin({ serverUrl: iamUrl, clientId, redirectUri, provider }).catch(() => {
|
|
16
|
+
setLoading(false);
|
|
17
|
+
});
|
|
18
|
+
}, [iamUrl, clientId, redirectUri, provider]);
|
|
19
|
+
return (_jsx("button", { type: "button", className: cn('inline-flex items-center justify-center gap-2 rounded-md px-4 py-2.5 text-sm font-medium transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2', 'disabled:pointer-events-none disabled:opacity-50', variant === 'outline'
|
|
20
|
+
? 'border border-input bg-background text-foreground hover:bg-accent hover:text-accent-foreground'
|
|
21
|
+
: 'bg-primary text-primary-foreground hover:bg-primary/90', className), onClick: handleClick, disabled: loading, children: loading ? 'Redirecting…' : label }));
|
|
27
22
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PasswordSubmit } from './types';
|
|
2
|
+
export interface PasswordFormProps {
|
|
3
|
+
/**
|
|
4
|
+
* Submit handler. The canonical implementation is the `@hanzo/iam` SDK's
|
|
5
|
+
* `loginWithPassword` (embedded credential → PKCE-bound code). Kept injected
|
|
6
|
+
* so `@hanzo/ui` stays dependency-light. When omitted the form is inert.
|
|
7
|
+
*/
|
|
8
|
+
onSubmit?: PasswordSubmit;
|
|
9
|
+
/** Optional "forgot password" affordance the host wires. */
|
|
10
|
+
onForgot?: () => void;
|
|
11
|
+
/** Submit button label. */
|
|
12
|
+
submitLabel?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Atomic email + password form. Brand-neutral: borders/text/background come
|
|
17
|
+
* from CSS tokens (`bg-background`, `border-input`, `text-foreground`,
|
|
18
|
+
* `bg-primary`), never literals. No token exchange — it just collects the
|
|
19
|
+
* credential and calls `onSubmit`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function PasswordForm({ onSubmit, onForgot, submitLabel, className, }: PasswordFormProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
//# sourceMappingURL=PasswordForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PasswordForm.d.ts","sourceRoot":"","sources":["../../../src/auth/PasswordForm.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,2BAA2B;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,QAAQ,EACR,WAAuB,EACvB,SAAS,GACV,EAAE,iBAAiB,2CAoFnB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useState } from 'react';
|
|
4
|
+
import { cn } from '../utils';
|
|
5
|
+
/**
|
|
6
|
+
* Atomic email + password form. Brand-neutral: borders/text/background come
|
|
7
|
+
* from CSS tokens (`bg-background`, `border-input`, `text-foreground`,
|
|
8
|
+
* `bg-primary`), never literals. No token exchange — it just collects the
|
|
9
|
+
* credential and calls `onSubmit`.
|
|
10
|
+
*/
|
|
11
|
+
export function PasswordForm({ onSubmit, onForgot, submitLabel = 'Sign in', className, }) {
|
|
12
|
+
const [email, setEmail] = useState('');
|
|
13
|
+
const [password, setPassword] = useState('');
|
|
14
|
+
const [busy, setBusy] = useState(false);
|
|
15
|
+
const [error, setError] = useState(null);
|
|
16
|
+
const handleSubmit = useCallback((e) => {
|
|
17
|
+
e.preventDefault();
|
|
18
|
+
if (!onSubmit)
|
|
19
|
+
return;
|
|
20
|
+
setBusy(true);
|
|
21
|
+
setError(null);
|
|
22
|
+
void Promise.resolve(onSubmit({ email, password }))
|
|
23
|
+
.catch((err) => setError(err instanceof Error ? err.message : 'Sign-in failed'))
|
|
24
|
+
.finally(() => setBusy(false));
|
|
25
|
+
}, [onSubmit, email, password]);
|
|
26
|
+
const fieldCls = cn('w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground', 'placeholder:text-muted-foreground', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2');
|
|
27
|
+
return (_jsxs("form", { onSubmit: handleSubmit, className: cn('flex flex-col gap-3', className), children: [_jsxs("label", { className: "flex flex-col gap-1.5", children: [_jsx("span", { className: "text-sm font-medium text-foreground", children: "Email" }), _jsx("input", { type: "email", autoComplete: "email", required: true, value: email, onChange: (e) => setEmail(e.target.value), placeholder: "you@example.com", className: fieldCls })] }), _jsxs("label", { className: "flex flex-col gap-1.5", children: [_jsxs("span", { className: "flex items-center justify-between text-sm font-medium text-foreground", children: ["Password", onForgot && (_jsx("button", { type: "button", onClick: onForgot, className: "text-xs font-normal text-muted-foreground hover:text-foreground", children: "Forgot?" }))] }), _jsx("input", { type: "password", autoComplete: "current-password", required: true, value: password, onChange: (e) => setPassword(e.target.value), placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", className: fieldCls })] }), error && (_jsx("p", { role: "alert", className: "text-sm text-destructive", children: error })), _jsx("button", { type: "submit", disabled: busy || !onSubmit, className: cn('inline-flex w-full items-center justify-center rounded-md bg-primary px-4 py-2.5', 'text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2', 'disabled:pointer-events-none disabled:opacity-50'), children: busy ? 'Signing in…' : submitLabel })] }));
|
|
28
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { SignInConfig, SignInMethod, LoginStarter, PasswordSubmit } from './types';
|
|
3
|
+
export interface SignInProps {
|
|
4
|
+
/**
|
|
5
|
+
* The method set to render, in order. `'password'` renders the embedded
|
|
6
|
+
* form; everything else is a provider hint (`'google'`, `'github'`,
|
|
7
|
+
* `'web3'`, …). This is the ONLY app-level configuration — apps pass
|
|
8
|
+
* `brand.providers` and write zero auth code.
|
|
9
|
+
*/
|
|
10
|
+
providers: SignInMethod[];
|
|
11
|
+
/** Brand IAM wiring threaded to every social/web3 atom. */
|
|
12
|
+
config?: SignInConfig;
|
|
13
|
+
/** Injected login starter (defaults to the embedded `startIamLogin`). */
|
|
14
|
+
onLogin?: LoginStarter;
|
|
15
|
+
/** Password submit handler (canonical: the SDK's `loginWithPassword`). */
|
|
16
|
+
onPasswordSubmit?: PasswordSubmit;
|
|
17
|
+
/** "Forgot password" affordance. */
|
|
18
|
+
onForgot?: () => void;
|
|
19
|
+
/** Optional heading rendered above the methods. */
|
|
20
|
+
title?: React.ReactNode;
|
|
21
|
+
className?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The composable sign-in surface. Renders the chosen method set — a
|
|
25
|
+
* `<PasswordForm>` and/or one `<SocialButton>` per provider (`<Web3Connect>`
|
|
26
|
+
* for `'web3'`) — each wired to the SDK. Brand-neutral: all color comes from
|
|
27
|
+
* the host's CSS tokens, nothing hardcoded. Apps just choose `providers`.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* <SignIn
|
|
31
|
+
* providers={['password', 'google', 'github', 'web3']}
|
|
32
|
+
* config={{ serverUrl: 'https://hanzo.id', clientId: 'hanzo-app' }}
|
|
33
|
+
* onPasswordSubmit={(c) => iam.loginWithPassword(c.email, c.password)}
|
|
34
|
+
* />
|
|
35
|
+
*/
|
|
36
|
+
export declare function SignIn({ providers, config, onLogin, onPasswordSubmit, onForgot, title, className, }: SignInProps): import("react/jsx-runtime").JSX.Element;
|
|
37
|
+
//# sourceMappingURL=SignIn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SignIn.d.ts","sourceRoot":"","sources":["../../../src/auth/SignIn.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAEvF,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,SAAS,EAAE,YAAY,EAAE,CAAA;IACzB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,yEAAyE;IACzE,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,cAAc,CAAA;IACjC,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,mDAAmD;IACnD,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,EACrB,SAAS,EACT,MAAM,EACN,OAAO,EACP,gBAAgB,EAChB,QAAQ,EACR,KAAK,EACL,SAAS,GACV,EAAE,WAAW,2CAkCb"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from '../utils';
|
|
4
|
+
import { SocialButton } from './SocialButton';
|
|
5
|
+
import { Web3Connect } from './Web3Connect';
|
|
6
|
+
import { PasswordForm } from './PasswordForm';
|
|
7
|
+
const isPassword = (m) => m === 'password';
|
|
8
|
+
/**
|
|
9
|
+
* The composable sign-in surface. Renders the chosen method set — a
|
|
10
|
+
* `<PasswordForm>` and/or one `<SocialButton>` per provider (`<Web3Connect>`
|
|
11
|
+
* for `'web3'`) — each wired to the SDK. Brand-neutral: all color comes from
|
|
12
|
+
* the host's CSS tokens, nothing hardcoded. Apps just choose `providers`.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <SignIn
|
|
16
|
+
* providers={['password', 'google', 'github', 'web3']}
|
|
17
|
+
* config={{ serverUrl: 'https://hanzo.id', clientId: 'hanzo-app' }}
|
|
18
|
+
* onPasswordSubmit={(c) => iam.loginWithPassword(c.email, c.password)}
|
|
19
|
+
* />
|
|
20
|
+
*/
|
|
21
|
+
export function SignIn({ providers, config, onLogin, onPasswordSubmit, onForgot, title, className, }) {
|
|
22
|
+
const social = providers.filter((p) => !isPassword(p));
|
|
23
|
+
const hasPassword = providers.some(isPassword);
|
|
24
|
+
const showDivider = hasPassword && social.length > 0;
|
|
25
|
+
return (_jsxs("div", { className: cn('flex w-full max-w-sm flex-col gap-4', className), children: [title && _jsx("div", { className: "text-lg font-semibold text-foreground", children: title }), hasPassword && (_jsx(PasswordForm, { onSubmit: onPasswordSubmit, onForgot: onForgot })), showDivider && (_jsxs("div", { className: "flex items-center gap-3", "aria-hidden": true, children: [_jsx("span", { className: "h-px flex-1 bg-border" }), _jsx("span", { className: "text-xs uppercase tracking-wide text-muted-foreground", children: "or" }), _jsx("span", { className: "h-px flex-1 bg-border" })] })), social.length > 0 && (_jsx("div", { className: "flex flex-col gap-2", children: social.map((provider) => provider === 'web3' ? (_jsx(Web3Connect, { config: config, onLogin: onLogin }, provider)) : (_jsx(SocialButton, { provider: provider, config: config, onLogin: onLogin }, provider))) }))] }));
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { SignInConfig, LoginStarter } from './types';
|
|
3
|
+
export interface SocialButtonProps {
|
|
4
|
+
/** Provider hint delegated to IAM's shared OAuth client (e.g. "google"). */
|
|
5
|
+
provider: string;
|
|
6
|
+
/** Brand IAM wiring. Required unless an `onLogin` starter is injected. */
|
|
7
|
+
config?: SignInConfig;
|
|
8
|
+
/**
|
|
9
|
+
* Injected login starter (composition over coupling). Defaults to the
|
|
10
|
+
* `@hanzo/iam`-shaped `startIamLogin`. Pass the SDK's `startLogin` to drop
|
|
11
|
+
* the embedded copy.
|
|
12
|
+
*/
|
|
13
|
+
onLogin?: LoginStarter;
|
|
14
|
+
/** Override the label (default derived from provider). */
|
|
15
|
+
label?: string;
|
|
16
|
+
/** Override the glyph (default derived from provider; `currentColor`). */
|
|
17
|
+
icon?: React.ReactNode;
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* One social / web3 sign-in method. Atomic and brand-neutral: colors come from
|
|
22
|
+
* the host's CSS tokens (`bg-background`, `text-foreground`, `border-input`,
|
|
23
|
+
* `hover:bg-accent`), never literals. Delegates to the login starter with the
|
|
24
|
+
* `provider` knob — no token exchange here.
|
|
25
|
+
*/
|
|
26
|
+
export declare function SocialButton({ provider, config, onLogin, label, icon, className, }: SocialButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
//# sourceMappingURL=SocialButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SocialButton.d.ts","sourceRoot":"","sources":["../../../src/auth/SocialButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAgC,MAAM,OAAO,CAAA;AAGpD,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAiCzD,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAA;IAChB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB;;;;OAIG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0EAA0E;IAC1E,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,MAAM,EACN,OAAO,EACP,KAAK,EACL,IAAI,EACJ,SAAS,GACV,EAAE,iBAAiB,2CAiCnB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useState } from 'react';
|
|
4
|
+
import { cn } from '../utils';
|
|
5
|
+
import { startIamLogin } from './iam';
|
|
6
|
+
/** Built-in provider glyphs. `currentColor` only — the button stays monochrome
|
|
7
|
+
* and inherits the brand from CSS tokens. Pass `icon` to override. */
|
|
8
|
+
const GLYPHS = {
|
|
9
|
+
google: (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: _jsx("path", { d: "M21.35 11.1h-9.17v2.96h5.27c-.23 1.4-1.66 4.1-5.27 4.1-3.17 0-5.76-2.62-5.76-5.86s2.59-5.86 5.76-5.86c1.8 0 3.01.77 3.7 1.43l2.52-2.43C16.9 3.5 14.76 2.6 12.18 2.6 6.98 2.6 2.8 6.78 2.8 12.3s4.18 9.7 9.38 9.7c5.42 0 9-3.8 9-9.16 0-.62-.07-1.09-.18-1.74z", fill: "currentColor" }) })),
|
|
10
|
+
github: (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: _jsx("path", { d: "M12 2C6.48 2 2 6.58 2 12.25c0 4.53 2.87 8.37 6.84 9.73.5.1.68-.22.68-.49v-1.7c-2.78.62-3.37-1.22-3.37-1.22-.45-1.18-1.11-1.5-1.11-1.5-.91-.64.07-.62.07-.62 1 .07 1.53 1.06 1.53 1.06.9 1.56 2.36 1.11 2.94.85.09-.66.35-1.11.63-1.37-2.22-.26-4.56-1.14-4.56-5.07 0-1.12.39-2.03 1.03-2.75-.1-.26-.45-1.3.1-2.71 0 0 .84-.27 2.75 1.05A9.36 9.36 0 0 1 12 6.84c.85 0 1.71.12 2.51.34 1.91-1.32 2.75-1.05 2.75-1.05.55 1.41.2 2.45.1 2.71.64.72 1.03 1.63 1.03 2.75 0 3.94-2.34 4.81-4.57 5.06.36.32.68.94.68 1.9v2.82c0 .27.18.59.69.49A10.26 10.26 0 0 0 22 12.25C22 6.58 17.52 2 12 2z" }) })),
|
|
11
|
+
web3: (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: [_jsx("path", { d: "M3 7l9-4 9 4-9 4-9-4z", stroke: "currentColor", strokeWidth: "1.6", strokeLinejoin: "round" }), _jsx("path", { d: "M3 12l9 4 9-4M3 17l9 4 9-4", stroke: "currentColor", strokeWidth: "1.6", strokeLinejoin: "round" })] })),
|
|
12
|
+
};
|
|
13
|
+
/** Human label for a provider. Override with `label`. */
|
|
14
|
+
const LABELS = {
|
|
15
|
+
google: 'Continue with Google',
|
|
16
|
+
github: 'Continue with GitHub',
|
|
17
|
+
web3: 'Connect Wallet',
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* One social / web3 sign-in method. Atomic and brand-neutral: colors come from
|
|
21
|
+
* the host's CSS tokens (`bg-background`, `text-foreground`, `border-input`,
|
|
22
|
+
* `hover:bg-accent`), never literals. Delegates to the login starter with the
|
|
23
|
+
* `provider` knob — no token exchange here.
|
|
24
|
+
*/
|
|
25
|
+
export function SocialButton({ provider, config, onLogin, label, icon, className, }) {
|
|
26
|
+
const [loading, setLoading] = useState(false);
|
|
27
|
+
const handleClick = useCallback(() => {
|
|
28
|
+
const start = onLogin ??
|
|
29
|
+
((opts) => startIamLogin(opts));
|
|
30
|
+
if (!onLogin && !config) {
|
|
31
|
+
throw new Error('SocialButton: provide `config` (serverUrl + clientId) or an `onLogin` starter.');
|
|
32
|
+
}
|
|
33
|
+
setLoading(true);
|
|
34
|
+
void Promise.resolve(start({ ...config, provider })).catch(() => setLoading(false));
|
|
35
|
+
}, [onLogin, config, provider]);
|
|
36
|
+
return (_jsxs("button", { type: "button", onClick: handleClick, disabled: loading, "data-provider": provider, className: cn('inline-flex w-full items-center justify-center gap-2 rounded-md border border-input bg-background px-4 py-2.5', 'text-sm font-medium text-foreground transition-colors', 'hover:bg-accent hover:text-accent-foreground', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2', 'disabled:pointer-events-none disabled:opacity-50', className), children: [icon ?? GLYPHS[provider] ?? null, loading ? 'Redirecting…' : label ?? LABELS[provider] ?? `Continue with ${provider}`] }));
|
|
37
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type SocialButtonProps } from './SocialButton';
|
|
2
|
+
export type Web3ConnectProps = Omit<SocialButtonProps, 'provider'>;
|
|
3
|
+
/**
|
|
4
|
+
* Wallet sign-in. Thin specialization of `<SocialButton provider="web3">` — the
|
|
5
|
+
* web3 hop is just another provider delegated to IAM. Atomic, brand-neutral,
|
|
6
|
+
* composes freely; no separate flow.
|
|
7
|
+
*/
|
|
8
|
+
export declare function Web3Connect(props: Web3ConnectProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=Web3Connect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Web3Connect.d.ts","sourceRoot":"","sources":["../../../src/auth/Web3Connect.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAErE,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAA;AAElE;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CAElD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { SocialButton } from './SocialButton';
|
|
4
|
+
/**
|
|
5
|
+
* Wallet sign-in. Thin specialization of `<SocialButton provider="web3">` — the
|
|
6
|
+
* web3 hop is just another provider delegated to IAM. Atomic, brand-neutral,
|
|
7
|
+
* composes freely; no separate flow.
|
|
8
|
+
*/
|
|
9
|
+
export function Web3Connect(props) {
|
|
10
|
+
return _jsx(SocialButton, { provider: "web3", ...props });
|
|
11
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Hanzo IAM OIDC wiring for the zero-dependency `@hanzo/ui` auth
|
|
3
|
+
* components (HIP-0111).
|
|
4
|
+
*
|
|
5
|
+
* The full `@hanzo/iam` SDK is the canonical client everywhere it can be a
|
|
6
|
+
* dependency. `@hanzo/ui` is intentionally dependency-light, so this module
|
|
7
|
+
* mirrors the SDK's contract for the two things these components need —
|
|
8
|
+
* the OIDC endpoint paths and a PKCE-S256 authorize redirect — and nothing
|
|
9
|
+
* else. The paths below are byte-for-byte the SDK's `OIDC_PATHS`.
|
|
10
|
+
*
|
|
11
|
+
* There is no `/api/` prefix, no legacy `/oauth/*` or `/login/oauth/*`, and
|
|
12
|
+
* PKCE (S256) is always on. A bare authorize redirect without PKCE is a
|
|
13
|
+
* security gap, not just a path nit.
|
|
14
|
+
*/
|
|
15
|
+
/** OIDC endpoint paths, relative to the brand `serverUrl`. Mirrors `@hanzo/iam` `OIDC_PATHS`. */
|
|
16
|
+
export declare const IAM_OIDC_PATHS: {
|
|
17
|
+
readonly authorize: "/v1/iam/oauth/authorize";
|
|
18
|
+
readonly token: "/v1/iam/oauth/token";
|
|
19
|
+
readonly userinfo: "/v1/iam/oauth/userinfo";
|
|
20
|
+
readonly jwks: "/v1/iam/.well-known/jwks";
|
|
21
|
+
readonly logout: "/v1/iam/oauth/logout";
|
|
22
|
+
};
|
|
23
|
+
/** Absolute IAM endpoint URL from a brand origin and a path key. */
|
|
24
|
+
export declare function iamUrl(serverUrl: string, key: keyof typeof IAM_OIDC_PATHS): string;
|
|
25
|
+
export interface StartIamLoginOptions {
|
|
26
|
+
/** Brand IAM origin, e.g. https://hanzo.id. */
|
|
27
|
+
serverUrl: string;
|
|
28
|
+
/** OAuth client id (`<org>-<app>`). */
|
|
29
|
+
clientId: string;
|
|
30
|
+
/** Redirect URI; defaults to the current origin + `/auth/callback`. */
|
|
31
|
+
redirectUri?: string;
|
|
32
|
+
/** Scopes; defaults to `openid profile email`. */
|
|
33
|
+
scope?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Social/web3 provider hint (e.g. "google", "github", "web3"). The ONE knob
|
|
36
|
+
* that selects a method — omit it for the IAM login page, or name a provider
|
|
37
|
+
* to delegate the social hop to IAM's shared org-level OAuth client. It rides
|
|
38
|
+
* as `&provider=<name>` on `/v1/iam/oauth/authorize`; the app NEVER registers
|
|
39
|
+
* a per-app Google/GitHub client. Same shape as `@hanzo/iam`'s `startLogin`.
|
|
40
|
+
*/
|
|
41
|
+
provider?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Build the canonical IAM authorization-code + PKCE-S256 authorize URL and
|
|
45
|
+
* stash the verifier + state in `sessionStorage` for the callback. Returned
|
|
46
|
+
* without redirecting — useful for `<a href>` or tests. `provider` (if given)
|
|
47
|
+
* rides as `&provider=<name>`; one flow, parameterized.
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildIamAuthorizeUrl(opts: StartIamLoginOptions): Promise<string>;
|
|
50
|
+
/**
|
|
51
|
+
* Begin the canonical IAM authorization-code + PKCE-S256 flow by redirecting
|
|
52
|
+
* the browser to `/v1/iam/oauth/authorize`. The verifier + state are stashed in
|
|
53
|
+
* `sessionStorage` for the callback to consume. `provider` selects the method.
|
|
54
|
+
*/
|
|
55
|
+
export declare function startIamLogin(opts: StartIamLoginOptions): Promise<void>;
|
|
56
|
+
//# sourceMappingURL=iam.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iam.d.ts","sourceRoot":"","sources":["../../../src/auth/iam.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,iGAAiG;AACjG,eAAO,MAAM,cAAc;;;;;;CAMjB,CAAA;AAIV,oEAAoE;AACpE,wBAAgB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,OAAO,cAAc,GAAG,MAAM,CAElF;AAuBD,MAAM,WAAW,oBAAoB;IACnC,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAA;IAChB,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBtF;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7E"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Hanzo IAM OIDC wiring for the zero-dependency `@hanzo/ui` auth
|
|
3
|
+
* components (HIP-0111).
|
|
4
|
+
*
|
|
5
|
+
* The full `@hanzo/iam` SDK is the canonical client everywhere it can be a
|
|
6
|
+
* dependency. `@hanzo/ui` is intentionally dependency-light, so this module
|
|
7
|
+
* mirrors the SDK's contract for the two things these components need —
|
|
8
|
+
* the OIDC endpoint paths and a PKCE-S256 authorize redirect — and nothing
|
|
9
|
+
* else. The paths below are byte-for-byte the SDK's `OIDC_PATHS`.
|
|
10
|
+
*
|
|
11
|
+
* There is no `/api/` prefix, no legacy `/oauth/*` or `/login/oauth/*`, and
|
|
12
|
+
* PKCE (S256) is always on. A bare authorize redirect without PKCE is a
|
|
13
|
+
* security gap, not just a path nit.
|
|
14
|
+
*/
|
|
15
|
+
/** OIDC endpoint paths, relative to the brand `serverUrl`. Mirrors `@hanzo/iam` `OIDC_PATHS`. */
|
|
16
|
+
export const IAM_OIDC_PATHS = {
|
|
17
|
+
authorize: '/v1/iam/oauth/authorize',
|
|
18
|
+
token: '/v1/iam/oauth/token',
|
|
19
|
+
userinfo: '/v1/iam/oauth/userinfo',
|
|
20
|
+
jwks: '/v1/iam/.well-known/jwks',
|
|
21
|
+
logout: '/v1/iam/oauth/logout',
|
|
22
|
+
};
|
|
23
|
+
const trim = (s) => s.replace(/\/+$/, '');
|
|
24
|
+
/** Absolute IAM endpoint URL from a brand origin and a path key. */
|
|
25
|
+
export function iamUrl(serverUrl, key) {
|
|
26
|
+
return `${trim(serverUrl)}${IAM_OIDC_PATHS[key]}`;
|
|
27
|
+
}
|
|
28
|
+
const PKCE_VERIFIER_KEY = 'hanzo_iam_pkce_verifier';
|
|
29
|
+
const PKCE_STATE_KEY = 'hanzo_iam_state';
|
|
30
|
+
function base64UrlEncode(bytes) {
|
|
31
|
+
const b = new Uint8Array(bytes);
|
|
32
|
+
let s = '';
|
|
33
|
+
for (const x of b)
|
|
34
|
+
s += String.fromCharCode(x);
|
|
35
|
+
return btoa(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
36
|
+
}
|
|
37
|
+
function randomVerifier() {
|
|
38
|
+
const a = new Uint8Array(48);
|
|
39
|
+
crypto.getRandomValues(a);
|
|
40
|
+
return base64UrlEncode(a.buffer);
|
|
41
|
+
}
|
|
42
|
+
async function challengeFor(verifier) {
|
|
43
|
+
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
|
|
44
|
+
return base64UrlEncode(digest);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build the canonical IAM authorization-code + PKCE-S256 authorize URL and
|
|
48
|
+
* stash the verifier + state in `sessionStorage` for the callback. Returned
|
|
49
|
+
* without redirecting — useful for `<a href>` or tests. `provider` (if given)
|
|
50
|
+
* rides as `&provider=<name>`; one flow, parameterized.
|
|
51
|
+
*/
|
|
52
|
+
export async function buildIamAuthorizeUrl(opts) {
|
|
53
|
+
const redirectUri = opts.redirectUri || `${window.location.origin}/auth/callback`;
|
|
54
|
+
const verifier = randomVerifier();
|
|
55
|
+
const challenge = await challengeFor(verifier);
|
|
56
|
+
const state = crypto.randomUUID();
|
|
57
|
+
sessionStorage.setItem(PKCE_VERIFIER_KEY, verifier);
|
|
58
|
+
sessionStorage.setItem(PKCE_STATE_KEY, state);
|
|
59
|
+
const url = new URL(iamUrl(opts.serverUrl, 'authorize'));
|
|
60
|
+
url.searchParams.set('client_id', opts.clientId);
|
|
61
|
+
url.searchParams.set('response_type', 'code');
|
|
62
|
+
url.searchParams.set('redirect_uri', redirectUri);
|
|
63
|
+
url.searchParams.set('scope', opts.scope || 'openid profile email');
|
|
64
|
+
url.searchParams.set('state', state);
|
|
65
|
+
url.searchParams.set('code_challenge', challenge);
|
|
66
|
+
url.searchParams.set('code_challenge_method', 'S256');
|
|
67
|
+
if (opts.provider)
|
|
68
|
+
url.searchParams.set('provider', opts.provider);
|
|
69
|
+
return url.toString();
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Begin the canonical IAM authorization-code + PKCE-S256 flow by redirecting
|
|
73
|
+
* the browser to `/v1/iam/oauth/authorize`. The verifier + state are stashed in
|
|
74
|
+
* `sessionStorage` for the callback to consume. `provider` selects the method.
|
|
75
|
+
*/
|
|
76
|
+
export async function startIamLogin(opts) {
|
|
77
|
+
window.location.href = await buildIamAuthorizeUrl(opts);
|
|
78
|
+
}
|
package/dist/src/auth/index.d.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
+
export { SignIn } from './SignIn';
|
|
2
|
+
export type { SignInProps } from './SignIn';
|
|
3
|
+
export { SocialButton } from './SocialButton';
|
|
4
|
+
export type { SocialButtonProps } from './SocialButton';
|
|
5
|
+
export { PasswordForm } from './PasswordForm';
|
|
6
|
+
export type { PasswordFormProps } from './PasswordForm';
|
|
7
|
+
export { Web3Connect } from './Web3Connect';
|
|
8
|
+
export type { Web3ConnectProps } from './Web3Connect';
|
|
1
9
|
export { IAMLoginButton } from './IAMLoginButton';
|
|
2
10
|
export type { IAMLoginButtonProps } from './IAMLoginButton';
|
|
3
11
|
export { AuthGuard } from './AuthGuard';
|
|
4
12
|
export type { AuthGuardProps } from './AuthGuard';
|
|
13
|
+
export { startIamLogin, buildIamAuthorizeUrl, iamUrl, IAM_OIDC_PATHS } from './iam';
|
|
14
|
+
export type { StartIamLoginOptions } from './iam';
|
|
15
|
+
export type { SignInConfig, SignInMethod, LoginStarter, PasswordSubmit } from './types';
|
|
5
16
|
export { useHanzoAuth } from '../navigation/hanzo-shell/useHanzoAuth';
|
|
6
17
|
export { UserOrgDropdown } from '../navigation/hanzo-shell/UserOrgDropdown';
|
|
7
18
|
export type { HanzoUser, HanzoOrg } from '../navigation/hanzo-shell/types';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAGrD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACjD,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGjD,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AACnF,YAAY,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AACjD,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAGvF,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAA;AAC3E,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAA"}
|
package/dist/src/auth/index.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
// Composable sign-in surface — atoms + the <SignIn> composer.
|
|
2
|
+
// Brand-neutral (CSS tokens), each wired to the canonical IAM PKCE flow.
|
|
3
|
+
export { SignIn } from './SignIn';
|
|
4
|
+
export { SocialButton } from './SocialButton';
|
|
5
|
+
export { PasswordForm } from './PasswordForm';
|
|
6
|
+
export { Web3Connect } from './Web3Connect';
|
|
7
|
+
// Single-button entry + guard (kept for the "Sign in with Hanzo" affordance).
|
|
1
8
|
export { IAMLoginButton } from './IAMLoginButton';
|
|
2
9
|
export { AuthGuard } from './AuthGuard';
|
|
10
|
+
// Mechanism mirror (paths + PKCE authorize) — same contract as `@hanzo/iam`.
|
|
11
|
+
export { startIamLogin, buildIamAuthorizeUrl, iamUrl, IAM_OIDC_PATHS } from './iam';
|
|
3
12
|
// Re-export auth-related navigation components for convenience
|
|
4
13
|
export { useHanzoAuth } from '../navigation/hanzo-shell/useHanzoAuth';
|
|
5
14
|
export { UserOrgDropdown } from '../navigation/hanzo-shell/UserOrgDropdown';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the composable `@hanzo/ui` sign-in surface.
|
|
3
|
+
*
|
|
4
|
+
* The presentation layer knows nothing of token exchange — it only knows how
|
|
5
|
+
* to START a login for a given method. `provider` is the one knob; everything
|
|
6
|
+
* else is brand config the host supplies once.
|
|
7
|
+
*/
|
|
8
|
+
import type { StartIamLoginOptions } from './iam';
|
|
9
|
+
/**
|
|
10
|
+
* A sign-in method. `'password'` renders the embedded credential form; any
|
|
11
|
+
* other string is a provider hint delegated to IAM's shared org-level OAuth
|
|
12
|
+
* client (`'google'`, `'github'`, `'web3'`, …). One union, parameterized —
|
|
13
|
+
* never a component per provider.
|
|
14
|
+
*/
|
|
15
|
+
export type SignInMethod = 'password' | 'google' | 'github' | 'web3' | (string & {});
|
|
16
|
+
/** Brand IAM wiring the host supplies once and threads to every atom. */
|
|
17
|
+
export interface SignInConfig {
|
|
18
|
+
/** Brand IAM origin, e.g. https://hanzo.id. */
|
|
19
|
+
serverUrl: string;
|
|
20
|
+
/** OAuth client id (`<org>-<app>`). */
|
|
21
|
+
clientId: string;
|
|
22
|
+
/** OAuth callback; defaults to the current origin + `/auth/callback`. */
|
|
23
|
+
redirectUri?: string;
|
|
24
|
+
/** Scopes; defaults to `openid profile email`. */
|
|
25
|
+
scope?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The injected login starter — composition over coupling. Defaults to the
|
|
29
|
+
* `@hanzo/iam`-shaped `startIamLogin`, but a host can pass its own (e.g. the
|
|
30
|
+
* `@hanzo/iam` SDK's `startLogin`) so `@hanzo/ui` stays dependency-light.
|
|
31
|
+
*/
|
|
32
|
+
export type LoginStarter = (opts: StartIamLoginOptions) => void | Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Submit handler for the embedded password form. Defaults to undefined, in
|
|
35
|
+
* which case `<PasswordForm>` is inert until a host wires it (the SDK's
|
|
36
|
+
* `loginWithPassword` is the canonical implementation). Kept out of the
|
|
37
|
+
* dependency-light core deliberately.
|
|
38
|
+
*/
|
|
39
|
+
export type PasswordSubmit = (creds: {
|
|
40
|
+
email: string;
|
|
41
|
+
password: string;
|
|
42
|
+
}) => void | Promise<void>;
|
|
43
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/auth/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAEjD;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;AAEpF,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAA;IAChB,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAE/E;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the composable `@hanzo/ui` sign-in surface.
|
|
3
|
+
*
|
|
4
|
+
* The presentation layer knows nothing of token exchange — it only knows how
|
|
5
|
+
* to START a login for a given method. `provider` is the one knob; everything
|
|
6
|
+
* else is brand config the host supplies once.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payment-manager.d.ts","sourceRoot":"","sources":["../../../../src/billing/components/payment-manager.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAoC,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"payment-manager.d.ts","sourceRoot":"","sources":["../../../../src/billing/components/payment-manager.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAoC,MAAM,UAAU,CAAA;AAI/E,MAAM,WAAW,yBAAyB;IACxC,cAAc,CAAC,EAAE,aAAa,EAAE,CAAA;IAChC,cAAc,CAAC,EAAE,aAAa,EAAE,CAAA;IAChC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAChD,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAA;IAC/C,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC;AA+FD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,2CAmLpE"}
|
|
@@ -4,6 +4,7 @@ import * as React from 'react';
|
|
|
4
4
|
import { ConnectButton } from '@rainbow-me/rainbowkit';
|
|
5
5
|
import { useAccount, useChainId } from 'wagmi';
|
|
6
6
|
import { AnimatedCard } from './animated-card';
|
|
7
|
+
import { SquareCardForm } from './square-card-form';
|
|
7
8
|
// ── Display helpers ──────────────────────────────────────────────────────
|
|
8
9
|
const CRYPTO_NETWORK_LABELS = {
|
|
9
10
|
bitcoin: 'Bitcoin (BTC)',
|
|
@@ -141,7 +142,18 @@ export function PaymentMethodManager(props) {
|
|
|
141
142
|
['card', 'Card'],
|
|
142
143
|
['crypto', 'Crypto'],
|
|
143
144
|
['wire', 'Bank Wire'],
|
|
144
|
-
].map(([id, label]) => (_jsx("button", { type: "button", onClick: () => { setActiveTab(id); setError(null); }, className: `${TAB_CLASS} ${activeTab === id ? TAB_ACTIVE : TAB_INACTIVE}`, children: label }, id))) }), activeTab === 'card' && (_jsx(
|
|
145
|
+
].map(([id, label]) => (_jsx("button", { type: "button", onClick: () => { setActiveTab(id); setError(null); }, className: `${TAB_CLASS} ${activeTab === id ? TAB_ACTIVE : TAB_INACTIVE}`, children: label }, id))) }), activeTab === 'card' && (_jsx(SquareCardForm, { onToken: async (sourceId) => {
|
|
146
|
+
const method = {
|
|
147
|
+
id: `pm_${Math.random().toString(36).slice(2, 12)}`,
|
|
148
|
+
type: 'card',
|
|
149
|
+
is_default: false,
|
|
150
|
+
isDefault: false,
|
|
151
|
+
created_at: new Date().toISOString(),
|
|
152
|
+
_sourceToken: sourceId,
|
|
153
|
+
card: { brand: 'card', last4: '••••', exp_month: 0, exp_year: 0, funding: 'credit' },
|
|
154
|
+
};
|
|
155
|
+
await addMethod(method);
|
|
156
|
+
} })), activeTab === 'crypto' && _jsx(CryptoForm, { onAdd: addMethod, setError: setError }), activeTab === 'wire' && _jsx(WireForm, { onAdd: addMethod }), error && _jsx("p", { className: "mt-3 text-sm text-danger", children: error })] })), status && (_jsx("div", { className: "border-b border-border px-4 py-2", children: _jsx("p", { className: "text-sm text-success", children: status }) })), _jsx("div", { className: "divide-y divide-border", children: methods.length === 0 ? (_jsx("div", { className: "p-6 text-sm text-text-muted", children: "No payment methods on file." })) : (methods.map(method => {
|
|
145
157
|
const isDefault = method.is_default || method.isDefault;
|
|
146
158
|
const isBusy = busyMethodId === method.id;
|
|
147
159
|
const sub = methodSubtext(method);
|
package/dist/src/form/form.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
3
3
|
import { type ControllerProps, type FieldPath, type FieldValues } from "react-hook-form";
|
|
4
|
-
declare const Form: <TFieldValues extends FieldValues, TContext = any, TTransformedValues = TFieldValues>(
|
|
4
|
+
declare const Form: <TFieldValues extends FieldValues, TContext = any, TTransformedValues = TFieldValues>({ children, watch, getValues, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, handleSubmit, unregister, control, register, setFocus, subscribe, }: import("react-hook-form").FormProviderProps<TFieldValues, TContext, TTransformedValues>) => React.JSX.Element;
|
|
5
5
|
declare const FormField: <TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ ...props }: ControllerProps<TFieldValues, TName>) => import("react/jsx-runtime").JSX.Element;
|
|
6
6
|
declare const useFormField: () => {
|
|
7
7
|
invalid: boolean;
|
|
@@ -17,7 +17,9 @@ declare const useFormField: () => {
|
|
|
17
17
|
};
|
|
18
18
|
declare const FormItem: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
19
19
|
declare const FormLabel: React.ForwardRefExoticComponent<Omit<LabelPrimitive.LabelProps & React.RefAttributes<HTMLLabelElement>, "ref"> & React.RefAttributes<HTMLLabelElement>>;
|
|
20
|
-
declare const FormControl: React.ForwardRefExoticComponent<Omit<
|
|
20
|
+
declare const FormControl: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLElement> & {
|
|
21
|
+
children?: React.ReactNode;
|
|
22
|
+
} & React.RefAttributes<HTMLElement>, "ref"> & React.RefAttributes<HTMLElement>>;
|
|
21
23
|
declare const FormDescription: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & React.RefAttributes<HTMLParagraphElement>>;
|
|
22
24
|
declare const FormMessage: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & React.RefAttributes<HTMLParagraphElement>>;
|
|
23
25
|
export { useFormField, Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormField, };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../../src/form/form.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,cAAc,MAAM,uBAAuB,CAAA;AAEvD,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAA;AAKxB,QAAA,MAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../../src/form/form.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,cAAc,MAAM,uBAAuB,CAAA;AAEvD,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAA;AAKxB,QAAA,MAAM,IAAI,0YAAe,CAAA;AAWzB,QAAA,MAAM,SAAS,GACb,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,KAAK,SAAS,SAAS,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC,YAAY,CAAC,EAC/D,cAEC,eAAe,CAAC,YAAY,EAAE,KAAK,CAAC,4CAMtC,CAAA;AAED,QAAA,MAAM,YAAY;;;;;;;;;;;CAyBjB,CAAA;AAQD,QAAA,MAAM,QAAQ,6GAWZ,CAAA;AAGF,QAAA,MAAM,SAAS,yJAcb,CAAA;AAGF,QAAA,MAAM,WAAW;;gFAmBf,CAAA;AAGF,QAAA,MAAM,eAAe,yHAcnB,CAAA;AAGF,QAAA,MAAM,WAAW,yHAqBf,CAAA;AAGF,OAAO,EACL,YAAY,EACZ,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,WAAW,EACX,eAAe,EACf,WAAW,EACX,SAAS,GACV,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { ExternalLink, MessageSquare,
|
|
3
|
+
import { ExternalLink, MessageSquare, Code, FileText } from 'lucide-react';
|
|
4
4
|
function fmtCtx(ctx) {
|
|
5
5
|
if (!ctx)
|
|
6
6
|
return '';
|
|
@@ -68,6 +68,6 @@ export function ModelCard({ model, onNavigate, linkPrefix = '/docs/models/', cha
|
|
|
68
68
|
if (e.key === 'Enter' || e.key === ' ')
|
|
69
69
|
handleClick();
|
|
70
70
|
};
|
|
71
|
-
return (_jsxs("div", { role: isClickable ? 'link' : undefined, tabIndex: isClickable ? 0 : undefined, onClick: isClickable ? handleClick : undefined, onKeyDown: isClickable ? handleKey : undefined, className: `rounded-lg border p-4 transition hover:border-primary/40 ${cardBg} ${isClickable ? 'cursor-pointer' : ''}`, children: [_jsxs("div", { className: "flex items-center gap-2 mb-1", children: [_jsx("span", { className: "font-semibold text-sm", children: model.id }), badge && (_jsx("span", { className: `text-[9px] font-semibold tracking-wider uppercase px-1.5 py-0.5 rounded-full ${badge.className}`, children: badge.label }))] }), fullSpec && (_jsx("div", { className: "text-xs font-mono text-muted-foreground mb-2", children: fullSpec })), _jsx("p", { className: "text-xs text-muted-foreground mb-3", children: model.description }), _jsxs("div", { className: "flex flex-wrap gap-1.5 mt-auto", children: [status === 'contact-sales' && (_jsxs("a", { href: requestAccessUrl, target: "_blank", rel: "noopener noreferrer", onClick: (e) => { e.stopPropagation(); }, className: "inline-flex items-center gap-1 text-xs bg-purple-500/20 text-purple-400 border border-purple-500/30 px-2 py-0.5 rounded hover:bg-purple-500/30 transition", children: [_jsx(ExternalLink, { className: "h-2.5 w-2.5" }), " Request Access"] })), (status === 'available' || status === 'preview' || status === 'cloud-only') && (_jsxs("a", { href: `${chatBaseUrl}?model=${model.id}`, target: "_blank", rel: "noopener noreferrer", onClick: (e) => { e.stopPropagation(); }, className: "inline-flex items-center gap-1 text-xs bg-primary text-primary-foreground px-2 py-0.5 rounded hover:opacity-90 transition", children: [_jsx(MessageSquare, { className: "h-2.5 w-2.5" }), " Chat"] })), model.huggingface && (_jsxs("a", { href: model.huggingface, target: "_blank", rel: "noopener noreferrer", onClick: (e) => { e.stopPropagation(); }, className: "inline-flex items-center gap-1 text-xs text-primary hover:underline", children: [_jsx(ExternalLink, { className: "h-2.5 w-2.5" }), " Weights"] })), model.github && (_jsxs("a", { href: model.github, target: "_blank", rel: "noopener noreferrer", onClick: (e) => { e.stopPropagation(); }, className: "inline-flex items-center gap-1 text-xs text-muted-foreground hover:underline", children: [_jsx(
|
|
71
|
+
return (_jsxs("div", { role: isClickable ? 'link' : undefined, tabIndex: isClickable ? 0 : undefined, onClick: isClickable ? handleClick : undefined, onKeyDown: isClickable ? handleKey : undefined, className: `rounded-lg border p-4 transition hover:border-primary/40 ${cardBg} ${isClickable ? 'cursor-pointer' : ''}`, children: [_jsxs("div", { className: "flex items-center gap-2 mb-1", children: [_jsx("span", { className: "font-semibold text-sm", children: model.id }), badge && (_jsx("span", { className: `text-[9px] font-semibold tracking-wider uppercase px-1.5 py-0.5 rounded-full ${badge.className}`, children: badge.label }))] }), fullSpec && (_jsx("div", { className: "text-xs font-mono text-muted-foreground mb-2", children: fullSpec })), _jsx("p", { className: "text-xs text-muted-foreground mb-3", children: model.description }), _jsxs("div", { className: "flex flex-wrap gap-1.5 mt-auto", children: [status === 'contact-sales' && (_jsxs("a", { href: requestAccessUrl, target: "_blank", rel: "noopener noreferrer", onClick: (e) => { e.stopPropagation(); }, className: "inline-flex items-center gap-1 text-xs bg-purple-500/20 text-purple-400 border border-purple-500/30 px-2 py-0.5 rounded hover:bg-purple-500/30 transition", children: [_jsx(ExternalLink, { className: "h-2.5 w-2.5" }), " Request Access"] })), (status === 'available' || status === 'preview' || status === 'cloud-only') && (_jsxs("a", { href: `${chatBaseUrl}?model=${model.id}`, target: "_blank", rel: "noopener noreferrer", onClick: (e) => { e.stopPropagation(); }, className: "inline-flex items-center gap-1 text-xs bg-primary text-primary-foreground px-2 py-0.5 rounded hover:opacity-90 transition", children: [_jsx(MessageSquare, { className: "h-2.5 w-2.5" }), " Chat"] })), model.huggingface && (_jsxs("a", { href: model.huggingface, target: "_blank", rel: "noopener noreferrer", onClick: (e) => { e.stopPropagation(); }, className: "inline-flex items-center gap-1 text-xs text-primary hover:underline", children: [_jsx(ExternalLink, { className: "h-2.5 w-2.5" }), " Weights"] })), model.github && (_jsxs("a", { href: model.github, target: "_blank", rel: "noopener noreferrer", onClick: (e) => { e.stopPropagation(); }, className: "inline-flex items-center gap-1 text-xs text-muted-foreground hover:underline", children: [_jsx(Code, { className: "h-2.5 w-2.5" }), " Repo"] })), model.paper && (_jsxs("a", { href: model.paper, target: "_blank", rel: "noopener noreferrer", onClick: (e) => { e.stopPropagation(); }, className: "inline-flex items-center gap-1 text-xs text-muted-foreground hover:underline", children: [_jsx(FileText, { className: "h-2.5 w-2.5" }), " Paper"] }))] })] }));
|
|
72
72
|
}
|
|
73
73
|
export default ModelCard;
|