@llmnative/react 2.0.1 → 2.2.0
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/bin/cli.js +117 -117
- package/dist/index.css +1 -1
- package/dist/index.js +21 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10468 -8488
- package/dist/index.mjs.map +1 -1
- package/dist/types/src/App.d.ts +19 -1
- package/dist/types/src/I18n.d.ts +91 -0
- package/dist/types/src/Theme.d.ts +16 -0
- package/dist/types/src/components/auth/AuthGate.d.ts +26 -0
- package/dist/types/src/components/auth/AuthLayout.d.ts +38 -0
- package/dist/types/src/components/auth/ForgotPassword.d.ts +19 -0
- package/dist/types/src/components/auth/OneTimeCodeInput.d.ts +27 -0
- package/dist/types/src/components/auth/ResetPassword.d.ts +31 -0
- package/dist/types/src/components/auth/SignIn.d.ts +42 -0
- package/dist/types/src/components/auth/SignUp.d.ts +43 -0
- package/dist/types/src/components/auth/VerifyCode.d.ts +32 -0
- package/dist/types/src/components/auth/VerifyEmail.d.ts +31 -0
- package/dist/types/src/components/auth/index.d.ts +12 -0
- package/dist/types/src/components/auth/providers.d.ts +22 -0
- package/dist/types/src/components/auth/shared.d.ts +124 -0
- package/dist/types/src/components/auth/useAuthFlows.d.ts +46 -0
- package/dist/types/src/index.d.ts +5 -1
- package/dist/types/src/providers/auth/AuthProvider.d.ts +51 -0
- package/dist/types/src/providers/auth/firebase/FirebaseAuthProvider.d.ts +8 -1
- package/dist/types/src/providers/auth/github/GitHubAuthProvider.d.ts +2 -0
- package/dist/types/src/providers/auth/mock/MockAuthProvider.d.ts +55 -0
- package/dist/types/src/providers/auth/supabase/SupabaseAuthProvider.d.ts +16 -1
- package/dist/types/src/providers/manifest.d.ts +4 -1
- package/llms-full.txt +96 -2
- package/package.json +1 -1
- package/scripts/cli/setup-project.js +678 -647
- package/templates/_shared/conf/access.tsx +23 -0
- package/templates/_shared/pages/access/ForgotPasswordPage.tsx +13 -0
- package/templates/_shared/pages/access/ResetPasswordPage.tsx +19 -0
- package/templates/_shared/pages/access/SignInPage.tsx +21 -0
- package/templates/_shared/pages/access/SignUpPage.tsx +23 -0
- package/templates/_shared/pages/access/VerifyEmailPage.tsx +20 -0
- package/templates/_shared/pages/access/paths.ts +12 -0
- package/templates/_shared/sections/Footer.tsx +1 -1
- package/templates/_shared/sections/Header.tsx +2 -2
- package/themes/default.ts +13 -12
- package/themes/tokyo-night-light.ts +24 -2
- package/themes/tokyo-night.ts +24 -2
- package/themes/vscode-dark-modern.ts +24 -2
- package/themes/vscode-dark-plus.ts +24 -2
- package/themes/vscode-light-modern.ts +24 -2
- package/themes/vscode-light-plus.ts +24 -2
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { AuthProviderAdapter, UserProfile } from '../../providers/auth/AuthProvider';
|
|
2
|
+
import type { SignInProps } from './SignIn';
|
|
3
|
+
import type { SignUpProps } from './SignUp';
|
|
4
|
+
import type { ForgotPasswordProps } from './ForgotPassword';
|
|
5
|
+
import type { ResetPasswordProps } from './ResetPassword';
|
|
6
|
+
import type { VerifyEmailProps } from './VerifyEmail';
|
|
7
|
+
export interface AuthSession {
|
|
8
|
+
/** The signed-in user, `null` without a session. */
|
|
9
|
+
user: UserProfile | null;
|
|
10
|
+
/** `false` while the session of the last visit is being restored: show neither the page nor the sign-in. */
|
|
11
|
+
ready: boolean;
|
|
12
|
+
signOut: () => Promise<void>;
|
|
13
|
+
adapter: AuthProviderAdapter;
|
|
14
|
+
}
|
|
15
|
+
/** Who is signed in with an auth provider of the app, and whether it is known yet (CR-127). */
|
|
16
|
+
export declare function useAuthSession(provider?: string): AuthSession;
|
|
17
|
+
export interface AuthFlowsOptions {
|
|
18
|
+
/** The page the confirmation link of a new account opens (your `VerifyEmail`), e.g. `/verify-email`. */
|
|
19
|
+
verifyEmailPath?: string;
|
|
20
|
+
/** The page the link to choose a new password opens (your `ResetPassword`), e.g. `/reset-password`. */
|
|
21
|
+
resetPasswordPath?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface AuthFlows {
|
|
24
|
+
adapter: AuthProviderAdapter;
|
|
25
|
+
/** For `<SignIn {...flows.signIn} />`: password, code by email, providers, as the adapter offers them. */
|
|
26
|
+
signIn: Pick<SignInProps, 'onPasswordSignIn' | 'onRequestCode' | 'onVerifyCode' | 'onProviderSignIn' | 'providers'>;
|
|
27
|
+
/** For `<SignUp {...flows.signUp} />`; `undefined` when the adapter cannot create accounts. */
|
|
28
|
+
signUp?: Pick<SignUpProps, 'onSignUp' | 'onResendVerification'>;
|
|
29
|
+
/** For `<ForgotPassword {...flows.forgotPassword} />`; `undefined` without password reset. */
|
|
30
|
+
forgotPassword?: Pick<ForgotPasswordProps, 'onSendReset'>;
|
|
31
|
+
/** For `<ResetPassword {...flows.resetPassword(code)} />`, with the code of the link. */
|
|
32
|
+
resetPassword: (code: string | null | undefined) => Pick<ResetPasswordProps, 'onResetPassword' | 'invalidLink'>;
|
|
33
|
+
/** For `<VerifyEmail {...flows.verifyEmail} code={…} />`. */
|
|
34
|
+
verifyEmail: Pick<VerifyEmailProps, 'onVerify' | 'onResend'>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Binds the access screens to an auth provider of the app (CR-127): each screen gets the functions of
|
|
38
|
+
* the methods the adapter has. An adapter that can reset a password (Firebase, Supabase, mock) signs in
|
|
39
|
+
* with email and password; one that cannot (Google, GitHub, Dropbox) offers its own button.
|
|
40
|
+
*/
|
|
41
|
+
export declare function useAuthFlows(provider?: string, options?: AuthFlowsOptions): AuthFlows;
|
|
42
|
+
/**
|
|
43
|
+
* The code of the link an email opened, whatever the service calls it: `oobCode` (Firebase),
|
|
44
|
+
* `token_hash` (Supabase), `code`.
|
|
45
|
+
*/
|
|
46
|
+
export declare function linkCode(search?: string | URLSearchParams): string | null;
|
|
@@ -13,6 +13,7 @@ export type { I18nDict, I18nConfig, I18nController, I18nTranslations, I18nLocale
|
|
|
13
13
|
export * as locales from './conf/i18n';
|
|
14
14
|
export type { AppThemeProviderConfig, BuiltInThemeId, CardTheme, ColorScale, GalleryTheme, ListCardTheme, MenuTheme, ModalTheme, ProviderSwitcherTheme, ToolbarTheme, TableTheme, Theme, ThemeConfig, ThemeController, ThemeMode, ThemeDefinition, ThemePresetConfig, } from './Theme';
|
|
15
15
|
export { AuthButton, useAccessToken, getAccessToken } from './auth';
|
|
16
|
+
export * from './components/auth';
|
|
16
17
|
export { useGlobalVars, getGlobalVars, setGlobalVars, removeGlobalVars } from './Global';
|
|
17
18
|
export type { DataProviderAdapter, FieldValue, RecordProps, RecordArray, DatabaseOptions, DBConfig, ReadOptions, WhereClause, OrderClause, Condition } from './providers/data/DataProvider';
|
|
18
19
|
export type { OrderConfig, OrderDirection } from './libs/order';
|
|
@@ -50,7 +51,10 @@ export { FirebaseStorageProvider } from './providers/storage/firebase';
|
|
|
50
51
|
export type { FirebaseStorageProviderConfig } from './providers/storage/firebase';
|
|
51
52
|
export { SupabaseStorageProvider } from './providers/storage/supabase';
|
|
52
53
|
export { useStorageProvider, StorageProvider } from './providers/storage/StorageProviderContext';
|
|
53
|
-
export type { AuthProviderAdapter, AuthIntent, AuthSignInOptions, UserProfile } from './providers/auth/AuthProvider';
|
|
54
|
+
export type { AuthProviderAdapter, AuthIntent, AuthSignInOptions, UserProfile, AuthSignUpInput, AuthSignUpResult, AuthLinkOptions, AuthEmailCodeOptions, AuthErrorReason, } from './providers/auth/AuthProvider';
|
|
55
|
+
export { AuthFlowError } from './providers/auth/AuthProvider';
|
|
56
|
+
export { MockAuthProvider } from './providers/auth/mock/MockAuthProvider';
|
|
57
|
+
export type { MockAuthProviderOptions, MockAuthAccount } from './providers/auth/mock/MockAuthProvider';
|
|
54
58
|
export { useAuthProvider, AuthProvider } from './providers/auth/AuthProviderContext';
|
|
55
59
|
export { GoogleAuthProvider } from './providers/auth/google/GoogleAuthProvider';
|
|
56
60
|
export type { FirebaseAuthProviderOptions, FirebaseSignInOptions } from './providers/auth/firebase/FirebaseAuthProvider';
|
|
@@ -27,4 +27,55 @@ export interface AuthProviderAdapter extends ProviderConfigurable {
|
|
|
27
27
|
* re-verifies the raw token itself rather than trusting client-read claims.
|
|
28
28
|
*/
|
|
29
29
|
getIdTokenClaims?(forceRefresh?: boolean): Promise<Record<string, unknown> | null>;
|
|
30
|
+
/** Resolves once the session of the last visit is restored (signed in or not): `AuthGate` waits for it. */
|
|
31
|
+
whenReady?(): Promise<void>;
|
|
32
|
+
/** Creates an account with email and password; says whether the email must be confirmed first. */
|
|
33
|
+
signUp?(input: AuthSignUpInput): Promise<AuthSignUpResult>;
|
|
34
|
+
/** Sends the link to choose a new password. Never says whether the account exists. */
|
|
35
|
+
sendPasswordReset?(email: string, options?: AuthLinkOptions): Promise<void>;
|
|
36
|
+
/** Sets the new password with the code of the link (`oobCode`, `token_hash`, …). */
|
|
37
|
+
confirmPasswordReset?(code: string, newPassword: string): Promise<void>;
|
|
38
|
+
/** Sends again the link that confirms the email: of the signed-in user, or of `email` when the service allows it. */
|
|
39
|
+
sendEmailVerification?(options?: AuthLinkOptions & {
|
|
40
|
+
email?: string;
|
|
41
|
+
}): Promise<void>;
|
|
42
|
+
/** Confirms the email with the code of the link. */
|
|
43
|
+
verifyEmail?(code: string): Promise<void>;
|
|
44
|
+
/** Sends a one-time code by email to sign in. */
|
|
45
|
+
requestEmailCode?(email: string, options?: AuthEmailCodeOptions): Promise<void>;
|
|
46
|
+
/** Signs in with the code received by email. */
|
|
47
|
+
verifyEmailCode?(email: string, code: string): Promise<UserProfile | null>;
|
|
48
|
+
}
|
|
49
|
+
export interface AuthSignUpInput {
|
|
50
|
+
email: string;
|
|
51
|
+
password: string;
|
|
52
|
+
/** The full name, when the screen asks for it. */
|
|
53
|
+
name?: string;
|
|
54
|
+
/** Where the confirmation link brings back (the page that calls `verifyEmail`). */
|
|
55
|
+
redirectTo?: string;
|
|
56
|
+
}
|
|
57
|
+
export interface AuthSignUpResult {
|
|
58
|
+
user: UserProfile | null;
|
|
59
|
+
/** The account works once the email is confirmed: the screen says to check the inbox. */
|
|
60
|
+
needsEmailVerification: boolean;
|
|
61
|
+
}
|
|
62
|
+
export interface AuthLinkOptions {
|
|
63
|
+
/** The page the link of the email opens. */
|
|
64
|
+
redirectTo?: string;
|
|
65
|
+
}
|
|
66
|
+
export interface AuthEmailCodeOptions {
|
|
67
|
+
/** Creates the account when the email has none (default `false`: only existing accounts sign in). */
|
|
68
|
+
createUser?: boolean;
|
|
69
|
+
}
|
|
70
|
+
/** Why an access flow failed, in words a screen can translate (`authErrorMessage`). */
|
|
71
|
+
export type AuthErrorReason = 'credentials' | 'emailInUse' | 'weakPassword' | 'invalidEmail' | 'tooManyRequests' | 'network' | 'invalidCode' | 'expiredLink' | 'disabled' | 'cancelled' | 'notSupported' | 'generic';
|
|
72
|
+
/**
|
|
73
|
+
* An error of an access flow with its reason: a screen shows the translated message of the reason,
|
|
74
|
+
* never the raw text of the service. Throw it from your own functions (e.g. a code checked by your server).
|
|
75
|
+
*/
|
|
76
|
+
export declare class AuthFlowError extends Error {
|
|
77
|
+
readonly reason: AuthErrorReason;
|
|
78
|
+
constructor(reason: AuthErrorReason, message?: string, options?: {
|
|
79
|
+
cause?: unknown;
|
|
80
|
+
});
|
|
30
81
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthProviderAdapter, AuthSignInOptions, UserProfile } from '../AuthProvider';
|
|
1
|
+
import type { AuthLinkOptions, AuthProviderAdapter, AuthSignInOptions, AuthSignUpInput, AuthSignUpResult, UserProfile } from '../AuthProvider';
|
|
2
2
|
import type { ProviderConfigurationState } from '../../ProviderConfiguration';
|
|
3
3
|
import type { FirebaseConfig } from '../../../Config';
|
|
4
4
|
/**
|
|
@@ -46,4 +46,11 @@ export declare class FirebaseAuthProvider implements AuthProviderAdapter {
|
|
|
46
46
|
onAuthChange(callback: (user: UserProfile | null) => void): () => void;
|
|
47
47
|
getAccessToken(): Promise<string>;
|
|
48
48
|
getIdTokenClaims(forceRefresh?: boolean): Promise<Record<string, unknown> | null>;
|
|
49
|
+
private requireAuth;
|
|
50
|
+
whenReady(): Promise<void>;
|
|
51
|
+
signUp({ email, password, name, redirectTo }: AuthSignUpInput): Promise<AuthSignUpResult>;
|
|
52
|
+
sendPasswordReset(email: string, options?: AuthLinkOptions): Promise<void>;
|
|
53
|
+
confirmPasswordReset(code: string, newPassword: string): Promise<void>;
|
|
54
|
+
sendEmailVerification(options?: AuthLinkOptions): Promise<void>;
|
|
55
|
+
verifyEmail(code: string): Promise<void>;
|
|
49
56
|
}
|
|
@@ -32,6 +32,8 @@ export declare class GitHubAuthProvider implements AuthProviderAdapter {
|
|
|
32
32
|
refresh(): Promise<UserProfile | null>;
|
|
33
33
|
/** Leaves for GitHub through the server, and comes back to the page it left from. */
|
|
34
34
|
signIn(options?: AuthSignInOptions): Promise<UserProfile | null>;
|
|
35
|
+
/** Resolves once the server said who is signed in (CR-127): `AuthGate` waits for it. */
|
|
36
|
+
whenReady(): Promise<void>;
|
|
35
37
|
signOut(): Promise<void>;
|
|
36
38
|
onAuthChange(callback: (user: UserProfile | null) => void): () => void;
|
|
37
39
|
private setUser;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { AuthEmailCodeOptions, AuthProviderAdapter, AuthSignInOptions, AuthSignUpInput, AuthSignUpResult, UserProfile } from '../AuthProvider';
|
|
2
|
+
import type { ProviderConfigurationState } from '../../ProviderConfiguration';
|
|
3
|
+
/** An account of the mock: its password, and whether its email is confirmed. */
|
|
4
|
+
export interface MockAuthAccount {
|
|
5
|
+
email: string;
|
|
6
|
+
password: string;
|
|
7
|
+
displayName?: string;
|
|
8
|
+
emailVerified?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface MockAuthProviderOptions {
|
|
11
|
+
/**
|
|
12
|
+
* The accounts that exist. Omitted: any email and any password sign in (a demo); given: only these,
|
|
13
|
+
* and a wrong password fails as a real service would.
|
|
14
|
+
*/
|
|
15
|
+
accounts?: MockAuthAccount[];
|
|
16
|
+
/** The code every email "receives" (default `123456`); any other code fails. */
|
|
17
|
+
code?: string;
|
|
18
|
+
/** Where the session is kept between reloads (default `sessionStorage`); `null` keeps it in memory only. */
|
|
19
|
+
storage?: Storage | null;
|
|
20
|
+
/** A pause before each answer, in ms, to see the loading states (default `400`). */
|
|
21
|
+
latency?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* An auth provider for demos and tests, with no service behind it (CR-127): every flow of the access
|
|
25
|
+
* screens works in memory. Never use it in production: it trusts any credential it is configured to.
|
|
26
|
+
*/
|
|
27
|
+
export declare class MockAuthProvider implements AuthProviderAdapter {
|
|
28
|
+
private user;
|
|
29
|
+
private readonly listeners;
|
|
30
|
+
private readonly accounts;
|
|
31
|
+
private readonly open;
|
|
32
|
+
private readonly code;
|
|
33
|
+
private readonly storage;
|
|
34
|
+
private readonly latency;
|
|
35
|
+
constructor(options?: MockAuthProviderOptions);
|
|
36
|
+
getConfigurationState(): ProviderConfigurationState;
|
|
37
|
+
isConfigured(): boolean;
|
|
38
|
+
getUser(): UserProfile | null;
|
|
39
|
+
isAuthenticated(): boolean;
|
|
40
|
+
whenReady(): Promise<void>;
|
|
41
|
+
onAuthChange(callback: (user: UserProfile | null) => void): () => void;
|
|
42
|
+
signIn(options?: AuthSignInOptions): Promise<UserProfile | null>;
|
|
43
|
+
signOut(): Promise<void>;
|
|
44
|
+
signUp({ email, password, name }: AuthSignUpInput): Promise<AuthSignUpResult>;
|
|
45
|
+
sendPasswordReset(): Promise<void>;
|
|
46
|
+
confirmPasswordReset(code: string, newPassword: string): Promise<void>;
|
|
47
|
+
sendEmailVerification(): Promise<void>;
|
|
48
|
+
verifyEmail(code: string): Promise<void>;
|
|
49
|
+
requestEmailCode(email: string, options?: AuthEmailCodeOptions): Promise<void>;
|
|
50
|
+
verifyEmailCode(email: string, code: string): Promise<UserProfile | null>;
|
|
51
|
+
private profile;
|
|
52
|
+
private setUser;
|
|
53
|
+
private restore;
|
|
54
|
+
private pause;
|
|
55
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthProviderAdapter, AuthSignInOptions, UserProfile } from '../AuthProvider';
|
|
1
|
+
import type { AuthEmailCodeOptions, AuthLinkOptions, AuthProviderAdapter, AuthSignInOptions, AuthSignUpInput, AuthSignUpResult, UserProfile } from '../AuthProvider';
|
|
2
2
|
import type { ProviderConfigurationState } from '../../ProviderConfiguration';
|
|
3
3
|
export interface SupabaseAuthConfig {
|
|
4
4
|
url: string;
|
|
@@ -45,4 +45,19 @@ export declare class SupabaseAuthProvider implements AuthProviderAdapter {
|
|
|
45
45
|
signOut(): Promise<void>;
|
|
46
46
|
onAuthChange(callback: (user: UserProfile | null) => void): () => void;
|
|
47
47
|
getAccessToken(): Promise<string>;
|
|
48
|
+
whenReady(): Promise<void>;
|
|
49
|
+
signUp({ email, password, name, redirectTo }: AuthSignUpInput): Promise<AuthSignUpResult>;
|
|
50
|
+
sendPasswordReset(email: string, options?: AuthLinkOptions): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* `code` is the `token_hash` of the recovery email (template `{{ .TokenHash }}`); empty when the link
|
|
53
|
+
* already opened a recovery session (the default template), and then only the password is set.
|
|
54
|
+
*/
|
|
55
|
+
confirmPasswordReset(code: string, newPassword: string): Promise<void>;
|
|
56
|
+
sendEmailVerification(options?: AuthLinkOptions & {
|
|
57
|
+
email?: string;
|
|
58
|
+
}): Promise<void>;
|
|
59
|
+
/** `code` is the `token_hash` of the confirmation email (template `{{ .TokenHash }}`). */
|
|
60
|
+
verifyEmail(code: string): Promise<void>;
|
|
61
|
+
requestEmailCode(email: string, options?: AuthEmailCodeOptions): Promise<void>;
|
|
62
|
+
verifyEmailCode(email: string, code: string): Promise<UserProfile | null>;
|
|
48
63
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CredentialsAdapter } from './credentials/CredentialsProvider';
|
|
2
2
|
import { MockDataProvider } from './data/mock';
|
|
3
|
+
import { type MockAuthProviderOptions } from './auth/mock/MockAuthProvider';
|
|
3
4
|
import type { AIProviderAdapter } from './ai/AIProvider';
|
|
4
5
|
import type { DataProviderAdapter } from './data/DataProvider';
|
|
5
6
|
import type { StorageProviderAdapter } from './storage/StorageProvider';
|
|
@@ -23,6 +24,8 @@ export type GoogleProviderConfig = GoogleOAuth2 & {
|
|
|
23
24
|
export type SupabaseProviderConfig = SupabaseConfig;
|
|
24
25
|
export type MockProviderConfig = {
|
|
25
26
|
data?: ConstructorParameters<typeof MockDataProvider>[0];
|
|
27
|
+
/** Registers `mockAuth` (CR-127), a provider of access with no service behind it: demos and tests only. */
|
|
28
|
+
auth?: MockAuthProviderOptions | boolean;
|
|
26
29
|
};
|
|
27
30
|
export declare const FIREBASE_MANIFEST: DriverManifest<FirebaseConfig>;
|
|
28
31
|
export declare const GOOGLE_MANIFEST: DriverManifest<GoogleProviderConfig>;
|
|
@@ -34,7 +37,7 @@ export declare const AI_MANIFEST: DriverManifest<AIConfig>;
|
|
|
34
37
|
export declare const PROVIDER_MANIFESTS: Record<string, DriverManifest<any>>;
|
|
35
38
|
export type DataDriverName = 'dbRealtime' | 'firestoreDb' | 'supabaseDb' | 'mock';
|
|
36
39
|
export type StorageDriverName = 'firestorage' | 'supabaseStorage';
|
|
37
|
-
export type AuthDriverName = 'googleAuth' | 'firebaseAuth' | 'dropboxAuth' | 'supabaseAuth' | 'githubAuth';
|
|
40
|
+
export type AuthDriverName = 'googleAuth' | 'firebaseAuth' | 'dropboxAuth' | 'supabaseAuth' | 'githubAuth' | 'mockAuth';
|
|
38
41
|
export type EmailDriverName = 'gmail';
|
|
39
42
|
export type AIDriverName = 'openai' | 'openrouter' | 'opencode' | 'opencode-go' | 'openai-compatible' | 'deepseek' | 'gemini' | 'anthropic' | 'mistral' | 'cloudflare' | 'groq';
|
|
40
43
|
export type CredentialsDriverName = 'googleServiceAccount';
|
package/llms-full.txt
CHANGED
|
@@ -98,6 +98,15 @@ The VS Code and Tokyo Night themes are compiled from the original VS Code theme
|
|
|
98
98
|
|
|
99
99
|
`ColorScale` tokens: `background`, `foreground`, `canvas` (page floor behind cards and panels, `bg-canvas`; falls back to `background`), `card`, `popover`, `primary`, `secondary`, `muted`, `accent`, `destructive`, `success`, `warning`, `info` (each with its `*Foreground`), `border`, `input` (border of form fields), `ring`. A consumer bridging the tokens in its own `@theme inline` adds `--color-canvas: hsl(var(--rf-canvas, var(--rf-background)));`.
|
|
100
100
|
|
|
101
|
+
**The frame of the app** (CR-126), as the title bar, side bar and status bar of the IDE: `header`, `headerForeground`, `headerBorder`, `sidebar`, `sidebarForeground`, `sidebarBorder`, `sidebarAccent` (the selected entry), `sidebarAccentForeground`, `footer`, `footerForeground`, `footerBorder`. The IDE themes carry them (VS Code Light+: gray title bar, blue status bar); a theme without them falls back to the base tokens (header and footer on `background`, sidebar on `card`, borders on `border`). Utilities: `bg-header text-header-foreground border-header-border`, `bg-sidebar …`, `bg-footer …`. `SideNav` draws itself with the sidebar tokens. The top bar and status bar of a layout:
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
<header className="flex h-14 shrink-0 items-center gap-3 border-b border-header-border bg-header px-4 text-header-foreground">…</header>
|
|
105
|
+
<footer className="shrink-0 border-t border-footer-border bg-footer px-4 py-1.5 text-xs text-footer-foreground">…</footer>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
A border without a color class (`border`, `border-b`) is the theme's `border`, not the text color. The scaffold (`npx @llmnative/react create`) starts in `vscode-light-modern` and bridges all these tokens in `src/styles/globals.css`; a consumer scaffolded before adds the `--color-header…`/`--color-sidebar…`/`--color-footer…` lines of `src/globals.css` to its own `@theme inline`.
|
|
109
|
+
|
|
101
110
|
---
|
|
102
111
|
|
|
103
112
|
## Provider System
|
|
@@ -147,6 +156,27 @@ export const menuConfig: MenuConfig = {
|
|
|
147
156
|
|
|
148
157
|
La home va dichiarata con `path: '/'`: senza quella voce, `/` mostra "Missing Page".
|
|
149
158
|
|
|
159
|
+
### Voci di menu e rotte
|
|
160
|
+
|
|
161
|
+
`SideNav menuKey="main"` e `Menu menuKey="main"` mostrano solo le **voci**: le route di quella chiave con `title`,
|
|
162
|
+
senza `hidden: true`, senza parametri (`:token`) e senza `*`. Le altre sono rotte e basta (CR-126). Ogni voce
|
|
163
|
+
mostrata ha `title` **e** `icon`. Una pagina raggiunta da un link (accesso, invito, impostazioni dal menu utente)
|
|
164
|
+
sta in una chiave che nessun menu mostra, oppure ha `hidden: true`:
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
export const menuConfig: MenuConfig = {
|
|
168
|
+
main: [
|
|
169
|
+
{ path: '/', title: 'Dashboard', icon: 'layout-dashboard', page: HomePage },
|
|
170
|
+
{ path: '/contacts', title: 'Contatti', icon: 'users', page: ContactsPage },
|
|
171
|
+
{ path: '/settings', title: 'Impostazioni', icon: 'settings', page: SettingsPage, hidden: true },
|
|
172
|
+
],
|
|
173
|
+
routes: [
|
|
174
|
+
{ path: '/invite/:token', page: InvitePage, layout: AccessLayout },
|
|
175
|
+
{ path: '*', page: NotFoundPage },
|
|
176
|
+
],
|
|
177
|
+
};
|
|
178
|
+
```
|
|
179
|
+
|
|
150
180
|
### Hooks
|
|
151
181
|
|
|
152
182
|
```ts
|
|
@@ -218,6 +248,66 @@ await auth.signIn({ method: 'oauth', provider: 'github' })
|
|
|
218
248
|
const token = await auth.getAccessToken('https://www.googleapis.com/auth/gmail.send')
|
|
219
249
|
```
|
|
220
250
|
|
|
251
|
+
### Schermate di accesso e gate senza redirect (CR-127)
|
|
252
|
+
|
|
253
|
+
Le schermate sono pronte: non scriverne a mano. Non conoscono il backend: ricevono le funzioni da
|
|
254
|
+
chiamare (una promessa) e mostrano l'errore lanciato tradotto, mai il testo grezzo del servizio
|
|
255
|
+
(`authErrorMessage`: codici di Firebase e Supabase, `AuthFlowError(reason)` per le funzioni tue).
|
|
256
|
+
Testi nel namespace `access` delle 6 lingue. Ognuna ha `brand`, `toolbar`, `footer`, `bare` (solo il
|
|
257
|
+
contenuto, in una modale), `errorMessage(error)`.
|
|
258
|
+
|
|
259
|
+
| Componente | Cosa fa | Funzioni |
|
|
260
|
+
|---|---|---|
|
|
261
|
+
| `SignIn` | provider esterni, email + password, codice via email, in qualsiasi combinazione | `onPasswordSignIn({email,password})`, `onRequestCode(email)` + `onVerifyCode(email,code)`, `providers` + `onProviderSignIn(id)` |
|
|
262
|
+
| `SignUp` | nome, email, password con i requisiti mentre si scrive, termini; poi "controlla la tua email" | `onSignUp({name,email,password})` → `{ needsEmailVerification }`, `onResendVerification(email)` |
|
|
263
|
+
| `ForgotPassword` | email → stessa risposta con o senza account, nuovo invio dopo un'attesa | `onSendReset(email)` |
|
|
264
|
+
| `ResetPassword` | nuova password dal link dell'email; link scaduto | `onResetPassword(password)`, `invalidLink` |
|
|
265
|
+
| `VerifyEmail` | attivazione: con `code` verifica subito, senza aspetta la conferma | `onVerify(code)`, `onResend()`, `onContinue()` |
|
|
266
|
+
| `VerifyCode` | codice di N cifre in caselle, inviato a codice completo | `onVerify(code)`, `onResend()`, `onChangeEmail()` |
|
|
267
|
+
| `OneTimeCodeInput` | le caselle da sole (incolla, avanti/indietro, `one-time-code`) | `value`, `onChange`, `onComplete` |
|
|
268
|
+
| `AuthLayout` | la cornice di una schermata (marchio, card, titolo, piè di pagina) | — |
|
|
269
|
+
| `AuthGate` | la pagina solo con la sessione; senza, `fallback` al suo posto | `allow(user)`, `denied`, `loading` |
|
|
270
|
+
|
|
271
|
+
```tsx
|
|
272
|
+
import { App, SignIn, AuthFlowError } from '@llmnative/react';
|
|
273
|
+
|
|
274
|
+
// Tutta l'app riservata: senza sessione ogni pagina mostra l'accesso al suo posto, l'indirizzo
|
|
275
|
+
// resta quello aperto e dopo l'accesso compare la pagina. Nessuna pagina /login, nessun redirect.
|
|
276
|
+
<App
|
|
277
|
+
menuConfig={{ main: [...], access: [{ path: '/invito/:token', page: InvitePage, public: true }] }}
|
|
278
|
+
authGate={{
|
|
279
|
+
fallback: <SignIn providers={['google', 'apple']} onProviderSignIn={enter}
|
|
280
|
+
onRequestCode={sendCode} onVerifyCode={checkCode} appName="CRM" />,
|
|
281
|
+
allow: async (user) => (await isMember(user)), // altrimenti "Accesso non consentito"
|
|
282
|
+
}}
|
|
283
|
+
/>
|
|
284
|
+
|
|
285
|
+
// Una funzione tua che rifiuta: la schermata dice "Il codice non è corretto o è scaduto."
|
|
286
|
+
async function checkCode(email: string, code: string) {
|
|
287
|
+
const { token } = await callFunction('verifyLoginCode', { email, code }).catch(() => {
|
|
288
|
+
throw new AuthFlowError('invalidCode');
|
|
289
|
+
});
|
|
290
|
+
await signInWithCustomToken(token);
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Con gli adapter del framework `useAuthFlows(provider?, { verifyEmailPath, resetPasswordPath })` dà le
|
|
295
|
+
funzioni pronte (`<SignIn {...flows.signIn} />`, `flows.signUp`, `flows.forgotPassword`,
|
|
296
|
+
`flows.resetPassword(linkCode())`, `flows.verifyEmail`). Metodi opzionali di `AuthProviderAdapter`:
|
|
297
|
+
|
|
298
|
+
| Metodo | Firebase | Supabase | `mockAuth` |
|
|
299
|
+
|---|---|---|---|
|
|
300
|
+
| `whenReady` (il gate aspetta la sessione) | ✅ | ✅ | ✅ (anche `githubAuth`) |
|
|
301
|
+
| `signUp`, `sendPasswordReset`, `confirmPasswordReset` | ✅ | ✅ | ✅ |
|
|
302
|
+
| `sendEmailVerification`, `verifyEmail` | ✅ | ✅ | ✅ |
|
|
303
|
+
| `requestEmailCode`, `verifyEmailCode` | — (serve una funzione tua) | ✅ | ✅ |
|
|
304
|
+
|
|
305
|
+
`useAuthSession(provider?)` dà `{ user, ready, signOut }`. `linkCode()` legge il codice del link
|
|
306
|
+
(`oobCode`, `token_hash`, `code`). `mockAuth` (`providers.mock.auth: true`) è solo per demo e test:
|
|
307
|
+
qualsiasi email e password, codice `123456`; con `accounts` rifiuta le credenziali sbagliate.
|
|
308
|
+
Link delle email: in Firebase imposta l'URL di azione sulle tue pagine (`/reset-password`,
|
|
309
|
+
`/verify-email`); in Supabase usa `{{ .TokenHash }}` nei template delle email.
|
|
310
|
+
|
|
221
311
|
### CredentialsAdapter (Google Service Account)
|
|
222
312
|
```tsx
|
|
223
313
|
// Chiama Google APIs lato browser senza Node.js
|
|
@@ -1436,6 +1526,7 @@ src/
|
|
|
1436
1526
|
conf/
|
|
1437
1527
|
app.ts ← providers config letta da .env (VITE_PROVIDER, VITE_AI_PROVIDER...)
|
|
1438
1528
|
menu.ts ← MenuConfig con path, title, icon, page (layout opzionale: c'è LayoutDefault)
|
|
1529
|
+
access.tsx ← rotte pubbliche delle pagine di accesso (CR-127)
|
|
1439
1530
|
data/
|
|
1440
1531
|
mockData.ts ← dati fake per MockDataProvider
|
|
1441
1532
|
layouts/
|
|
@@ -1444,6 +1535,7 @@ src/
|
|
|
1444
1535
|
Header.tsx / Sidebar.tsx / PageHeader.tsx / Footer.tsx ← solo componenti pubblici (SideNav, AuthButton, ActionButton, Brand, Breadcrumbs)
|
|
1445
1536
|
pages/
|
|
1446
1537
|
home/HomePage.tsx
|
|
1538
|
+
access/ ← SignInPage (il fallback del gate), SignUp, ForgotPassword, ResetPassword, VerifyEmail
|
|
1447
1539
|
[altre pagine dal template scelto]
|
|
1448
1540
|
styles/
|
|
1449
1541
|
globals.css ← @import '@llmnative/react/dist/index.css' + Tailwind override
|
|
@@ -1461,11 +1553,12 @@ const env = import.meta.env
|
|
|
1461
1553
|
export const providers: AppProvidersConfig = {
|
|
1462
1554
|
mock: { data: mockData },
|
|
1463
1555
|
ai: { openaiApiKey: env.VITE_OPENAI_API_KEY ?? '' }, // AI dentro providers, non prop <App>
|
|
1464
|
-
|
|
1556
|
+
mock: { data: mockData, auth: true }, // mockAuth: accesso di prova
|
|
1557
|
+
services: { data: 'mock', auth: 'mockAuth' },
|
|
1465
1558
|
}
|
|
1466
1559
|
```
|
|
1467
1560
|
|
|
1468
|
-
Driver per `VITE_PROVIDER`: `firebase` → `firestoreDb` + `firestorage` + `firebaseAuth`; `supabase` → `supabaseDb` + `supabaseStorage` + `supabaseAuth`; `mock` → `mock` + `
|
|
1561
|
+
Driver per `VITE_PROVIDER`: `firebase` → `firestoreDb` + `firestorage` + `firebaseAuth`; `supabase` → `supabaseDb` + `supabaseStorage` + `supabaseAuth`; `mock` → `mock` + `mockAuth`. `index.tsx` passa `LayoutDefault={Default}`, le traduzioni del framework (`translations: { ...locales }`) e `authGate={{ fallback: <SignInPage /> }}`: senza sessione ogni pagina mostra l'accesso al suo posto. Le pagine di accesso (`pages/access/`: SignInPage, SignUpPage, ForgotPasswordPage, ResetPasswordPage, VerifyEmailPage) e le loro rotte pubbliche (`conf/access.tsx`) sono uguali per ogni template; per un'app aperta a tutti togli `authGate`.
|
|
1469
1562
|
|
|
1470
1563
|
---
|
|
1471
1564
|
|
|
@@ -1477,6 +1570,7 @@ Driver per `VITE_PROVIDER`: `firebase` → `firestoreDb` + `firestorage` + `fire
|
|
|
1477
1570
|
| `inputType` o `Input.Number`/`Input.Email` non esistono | Il tipo HTML è la prop `type` | `<Input type="number">`, `<Input type="email">`, `<Input type="date">` |
|
|
1478
1571
|
| `Menu context=` non esiste | La prop si chiama `menuKey` | `<Menu menuKey="main" />` |
|
|
1479
1572
|
| `SignInButton` non esiste | Rinominato | `<AuthButton />` |
|
|
1573
|
+
| Pagina `/login` con redirect, form di accesso fatti a mano | Le schermate e il gate ci sono (CR-127) | `<App authGate={{ fallback: <SignIn … /> }}>`, rotte aperte con `public: true` |
|
|
1480
1574
|
| `firebase: { config: ... }` errato | FirebaseConfig è flat | `firebase: { apiKey, authDomain, databaseURL, ... }` |
|
|
1481
1575
|
| `google: { oAuth2: { clientId } }` errato | GoogleProviderConfig è flat | `google: { clientId, scope }` |
|
|
1482
1576
|
| `children` su `Grid` | Grid non accetta children | Usa `form={<Fields />}` per il form inline |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llmnative/react",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "LLM-native React framework for deterministic UI generation. Data-driven by default, schema-driven optional. Ultra-low token consumption — built for AI agents, not just humans.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|