@lastbrain/ai-ui-react 1.0.68 → 1.0.70
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/components/AiChipLabel.d.ts +8 -3
- package/dist/components/AiChipLabel.d.ts.map +1 -1
- package/dist/components/AiChipLabel.js +23 -70
- package/dist/components/AiContextButton.d.ts +10 -2
- package/dist/components/AiContextButton.d.ts.map +1 -1
- package/dist/components/AiContextButton.js +73 -291
- package/dist/components/AiImageButton.d.ts +5 -1
- package/dist/components/AiImageButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +6 -142
- package/dist/components/AiInput.d.ts +5 -3
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +13 -25
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +64 -212
- package/dist/components/AiSelect.d.ts +5 -3
- package/dist/components/AiSelect.d.ts.map +1 -1
- package/dist/components/AiSelect.js +21 -30
- package/dist/components/AiStatusButton.d.ts +4 -1
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +211 -676
- package/dist/components/AiTextarea.d.ts +4 -2
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +14 -26
- package/dist/components/LBApiKeySelector.d.ts.map +1 -1
- package/dist/components/LBApiKeySelector.js +5 -166
- package/dist/components/LBConnectButton.d.ts +4 -7
- package/dist/components/LBConnectButton.d.ts.map +1 -1
- package/dist/components/LBConnectButton.js +17 -86
- package/dist/components/LBSigninModal.d.ts +1 -1
- package/dist/components/LBSigninModal.d.ts.map +1 -1
- package/dist/components/LBSigninModal.js +42 -320
- package/dist/context/LBAuthProvider.d.ts +35 -3
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +2 -0
- package/dist/examples/AiUiPremiumShowcase.d.ts +2 -0
- package/dist/examples/AiUiPremiumShowcase.d.ts.map +1 -0
- package/dist/examples/AiUiPremiumShowcase.js +15 -0
- package/dist/hooks/useAiModels.d.ts.map +1 -1
- package/dist/hooks/useModelManagement.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/styles/inline.d.ts +1 -0
- package/dist/styles/inline.d.ts.map +1 -1
- package/dist/styles/inline.js +25 -129
- package/dist/styles.css +1268 -369
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/errorHandler.d.ts +2 -2
- package/dist/utils/errorHandler.d.ts.map +1 -1
- package/dist/utils/errorHandler.js +8 -1
- package/dist/utils/modelManagement.d.ts +13 -10
- package/dist/utils/modelManagement.d.ts.map +1 -1
- package/dist/utils/modelManagement.js +19 -2
- package/package.json +2 -2
- package/src/components/AiChipLabel.tsx +68 -101
- package/src/components/AiContextButton.tsx +142 -413
- package/src/components/AiImageButton.tsx +29 -190
- package/src/components/AiInput.tsx +49 -74
- package/src/components/AiPromptPanel.tsx +81 -260
- package/src/components/AiSelect.tsx +61 -69
- package/src/components/AiStatusButton.tsx +496 -1327
- package/src/components/AiTextarea.tsx +50 -63
- package/src/components/LBApiKeySelector.tsx +93 -271
- package/src/components/LBConnectButton.tsx +39 -336
- package/src/components/LBSigninModal.tsx +141 -472
- package/src/context/LBAuthProvider.tsx +45 -6
- package/src/examples/AiUiPremiumShowcase.tsx +94 -0
- package/src/hooks/useAiModels.ts +2 -1
- package/src/hooks/useModelManagement.ts +2 -1
- package/src/index.ts +3 -0
- package/src/styles/inline.ts +27 -148
- package/src/styles.css +1268 -369
- package/src/types.ts +3 -0
- package/src/utils/errorHandler.ts +16 -3
- package/src/utils/modelManagement.ts +53 -15
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useState } from "react";
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { createPortal } from "react-dom";
|
|
5
|
+
import { AlertCircle, Loader2, Lock, Mail, Sparkles, X } from "lucide-react";
|
|
4
6
|
import { useLB } from "../context/LBAuthProvider";
|
|
5
7
|
import { LBApiKeySelector } from "./LBApiKeySelector";
|
|
6
|
-
import { Mail, Lock, Sparkles, X, Loader2, AlertCircle } from "lucide-react";
|
|
7
8
|
|
|
8
9
|
export interface LBSigninModalProps {
|
|
9
10
|
isOpen: boolean;
|
|
@@ -11,62 +12,63 @@ export interface LBSigninModalProps {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
14
|
-
|
|
15
|
+
const lbContext = useLB();
|
|
16
|
+
const [portalRoot, setPortalRoot] = useState<HTMLElement | null>(null);
|
|
17
|
+
|
|
15
18
|
const [email, setEmail] = useState("");
|
|
16
19
|
const [password, setPassword] = useState("");
|
|
17
20
|
const [loading, setLoading] = useState(false);
|
|
18
21
|
const [error, setError] = useState("");
|
|
19
22
|
const [showKeySelector, setShowKeySelector] = useState(false);
|
|
20
|
-
const [currentApiKeys, setCurrentApiKeys] = useState<any[]>([]);
|
|
23
|
+
const [currentApiKeys, setCurrentApiKeys] = useState<any[]>([]);
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
const lbContext = useLB();
|
|
25
|
+
const { login, selectApiKeyWithToken, fetchApiKeys } = lbContext || {};
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
if (!lbContext || !isOpen) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
27
|
+
const canRender = Boolean(isOpen && lbContext && login);
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
const {
|
|
32
|
-
login,
|
|
33
|
-
selectApiKeyWithToken,
|
|
34
|
-
fetchApiKeys,
|
|
35
|
-
apiKeys = [],
|
|
36
|
-
status: lbStatus,
|
|
37
|
-
} = lbContext;
|
|
29
|
+
const panelTitle = useMemo(() => "Connexion LastBrain", []);
|
|
38
30
|
|
|
39
|
-
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
setPortalRoot(document.body);
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
if (!canRender || !portalRoot) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
40
38
|
|
|
41
39
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
42
40
|
e.preventDefault();
|
|
41
|
+
if (!login) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
43
45
|
setError("");
|
|
44
46
|
setLoading(true);
|
|
45
47
|
|
|
46
48
|
try {
|
|
47
49
|
const result = await login(email, password);
|
|
48
|
-
if (result.success) {
|
|
49
|
-
if (result.needsKeySelection) {
|
|
50
|
-
// L'utilisateur doit choisir une clé API
|
|
51
|
-
// Utiliser l'access token retourné directement par login
|
|
52
|
-
if (fetchApiKeys && result.accessToken) {
|
|
53
|
-
try {
|
|
54
|
-
const keys = await fetchApiKeys(result.accessToken);
|
|
55
|
-
setCurrentApiKeys(keys);
|
|
56
|
-
setShowKeySelector(true);
|
|
57
|
-
} catch (keyError) {
|
|
58
|
-
setError("Erreur lors de la récupération des clés API");
|
|
59
|
-
console.error("Failed to fetch API keys:", keyError);
|
|
60
|
-
}
|
|
61
|
-
} else {
|
|
62
|
-
setError("Token d'accès non disponible");
|
|
63
|
-
}
|
|
64
|
-
} else {
|
|
65
|
-
// Connexion réussie, fermer le modal
|
|
66
|
-
onClose();
|
|
67
|
-
}
|
|
68
|
-
} else {
|
|
50
|
+
if (!result.success) {
|
|
69
51
|
setError(result.error || "Échec de la connexion");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!result.needsKeySelection) {
|
|
56
|
+
onClose();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!fetchApiKeys || !result.accessToken) {
|
|
61
|
+
setError("Token d'accès non disponible");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const keys = await fetchApiKeys(result.accessToken);
|
|
67
|
+
setCurrentApiKeys(keys || []);
|
|
68
|
+
setShowKeySelector(true);
|
|
69
|
+
} catch (keyError) {
|
|
70
|
+
console.error("Failed to fetch API keys:", keyError);
|
|
71
|
+
setError("Erreur lors de la récupération des clés API");
|
|
70
72
|
}
|
|
71
73
|
} catch (err) {
|
|
72
74
|
setError(
|
|
@@ -78,7 +80,9 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
78
80
|
};
|
|
79
81
|
|
|
80
82
|
const handleKeySelect = async (apiKeyId: string) => {
|
|
81
|
-
if (!selectApiKeyWithToken)
|
|
83
|
+
if (!selectApiKeyWithToken) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
82
86
|
|
|
83
87
|
try {
|
|
84
88
|
await selectApiKeyWithToken(apiKeyId);
|
|
@@ -94,481 +98,146 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
94
98
|
|
|
95
99
|
const handleCancelKeySelection = () => {
|
|
96
100
|
setShowKeySelector(false);
|
|
97
|
-
// Fermer complètement le modal au lieu de rester dans l'état de sélection
|
|
98
101
|
onClose();
|
|
99
102
|
};
|
|
100
103
|
|
|
101
|
-
// Si on doit afficher le sélecteur de clés
|
|
102
104
|
if (showKeySelector && currentApiKeys.length > 0) {
|
|
103
|
-
return (
|
|
105
|
+
return createPortal(
|
|
104
106
|
<LBApiKeySelector
|
|
105
107
|
apiKeys={currentApiKeys}
|
|
106
108
|
onSelect={handleKeySelect}
|
|
107
109
|
onCancel={handleCancelKeySelection}
|
|
108
|
-
isOpen
|
|
109
|
-
|
|
110
|
+
isOpen
|
|
111
|
+
/>,
|
|
112
|
+
portalRoot
|
|
110
113
|
);
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
|
|
114
|
-
if (e.key === "Escape") {
|
|
115
|
-
onClose();
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
return (
|
|
116
|
+
return createPortal(
|
|
120
117
|
<div
|
|
121
|
-
|
|
122
|
-
position: "fixed",
|
|
123
|
-
top: 0,
|
|
124
|
-
left: 0,
|
|
125
|
-
right: 0,
|
|
126
|
-
bottom: 0,
|
|
127
|
-
width: "100vw",
|
|
128
|
-
height: "100vh",
|
|
129
|
-
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
|
130
|
-
backdropFilter: "blur(12px)",
|
|
131
|
-
WebkitBackdropFilter: "blur(12px)",
|
|
132
|
-
display: "flex",
|
|
133
|
-
alignItems: "center",
|
|
134
|
-
justifyContent: "center",
|
|
135
|
-
zIndex: 9999,
|
|
136
|
-
padding: "16px",
|
|
137
|
-
animation: "fadeIn 0.2s ease-out",
|
|
138
|
-
overflow: "auto",
|
|
139
|
-
}}
|
|
118
|
+
className="ai-signin-overlay"
|
|
140
119
|
onClick={onClose}
|
|
141
|
-
onKeyDown={
|
|
120
|
+
onKeyDown={(e) => {
|
|
121
|
+
if (e.key === "Escape") {
|
|
122
|
+
onClose();
|
|
123
|
+
}
|
|
124
|
+
}}
|
|
125
|
+
role="dialog"
|
|
126
|
+
aria-modal="true"
|
|
127
|
+
aria-label={panelTitle}
|
|
142
128
|
>
|
|
143
|
-
<div
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
maxWidth: "440px",
|
|
150
|
-
width: "100%",
|
|
151
|
-
border:
|
|
152
|
-
"1px solid light-dark(rgba(226, 232, 240, 0.8), rgba(139, 92, 246, 0.3))",
|
|
153
|
-
display: "flex",
|
|
154
|
-
flexDirection: "column",
|
|
155
|
-
maxHeight: "90vh",
|
|
156
|
-
overflow: "hidden",
|
|
157
|
-
position: "relative",
|
|
158
|
-
animation: "slideUp 0.3s ease-out",
|
|
159
|
-
}}
|
|
160
|
-
onClick={(e) => e.stopPropagation()}
|
|
161
|
-
>
|
|
162
|
-
{/* Gradient Background Effect */}
|
|
163
|
-
<div
|
|
164
|
-
style={{
|
|
165
|
-
position: "absolute",
|
|
166
|
-
top: 0,
|
|
167
|
-
left: 0,
|
|
168
|
-
right: 0,
|
|
169
|
-
height: "200px",
|
|
170
|
-
background:
|
|
171
|
-
"radial-gradient(ellipse at top, rgba(139, 92, 246, 0.15), transparent 70%)",
|
|
172
|
-
pointerEvents: "none",
|
|
173
|
-
zIndex: 0,
|
|
174
|
-
}}
|
|
175
|
-
/>
|
|
176
|
-
|
|
177
|
-
{/* Header */}
|
|
178
|
-
<div
|
|
179
|
-
style={{
|
|
180
|
-
padding: "32px 32px 24px",
|
|
181
|
-
display: "flex",
|
|
182
|
-
flexDirection: "column",
|
|
183
|
-
alignItems: "center",
|
|
184
|
-
gap: "16px",
|
|
185
|
-
position: "relative",
|
|
186
|
-
zIndex: 1,
|
|
187
|
-
}}
|
|
129
|
+
<div className="ai-signin-panel" onClick={(e) => e.stopPropagation()}>
|
|
130
|
+
<button
|
|
131
|
+
type="button"
|
|
132
|
+
className="ai-icon-btn ai-signin-close"
|
|
133
|
+
onClick={onClose}
|
|
134
|
+
aria-label="Fermer"
|
|
188
135
|
>
|
|
189
|
-
{
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
alignItems: "center",
|
|
198
|
-
justifyContent: "center",
|
|
199
|
-
boxShadow: "0 8px 24px rgba(139, 92, 246, 0.3)",
|
|
200
|
-
animation: "pulse 2s ease-in-out infinite",
|
|
201
|
-
}}
|
|
202
|
-
>
|
|
203
|
-
<Sparkles size={32} color="#ffffff" strokeWidth={2.5} />
|
|
204
|
-
</div>
|
|
205
|
-
|
|
206
|
-
{/* Title */}
|
|
207
|
-
<div style={{ textAlign: "center" }}>
|
|
208
|
-
<h2
|
|
209
|
-
style={{
|
|
210
|
-
margin: "0 0 8px 0",
|
|
211
|
-
fontSize: "24px",
|
|
212
|
-
fontWeight: 700,
|
|
213
|
-
color: "light-dark(#1e293b, #f8fafc)",
|
|
214
|
-
letterSpacing: "-0.02em",
|
|
215
|
-
}}
|
|
216
|
-
>
|
|
217
|
-
Connexion LastBrain
|
|
218
|
-
</h2>
|
|
219
|
-
<p
|
|
220
|
-
style={{
|
|
221
|
-
margin: 0,
|
|
222
|
-
fontSize: "14px",
|
|
223
|
-
color: "light-dark(#64748b, #94a3b8)",
|
|
224
|
-
fontWeight: 400,
|
|
225
|
-
}}
|
|
226
|
-
>
|
|
227
|
-
Accédez à vos outils d'intelligence artificielle
|
|
228
|
-
</p>
|
|
136
|
+
<X size={16} />
|
|
137
|
+
</button>
|
|
138
|
+
|
|
139
|
+
<div className="ai-signin-header">
|
|
140
|
+
<div className="ai-center mb-3">
|
|
141
|
+
<span className="ai-icon-badge">
|
|
142
|
+
<Sparkles size={22} />
|
|
143
|
+
</span>
|
|
229
144
|
</div>
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
style={{
|
|
235
|
-
position: "absolute",
|
|
236
|
-
top: "24px",
|
|
237
|
-
right: "24px",
|
|
238
|
-
background: "transparent",
|
|
239
|
-
border: "none",
|
|
240
|
-
color: "light-dark(#64748b, #94a3b8)",
|
|
241
|
-
cursor: "pointer",
|
|
242
|
-
padding: "8px",
|
|
243
|
-
borderRadius: "10px",
|
|
244
|
-
transition: "all 0.2s ease",
|
|
245
|
-
display: "flex",
|
|
246
|
-
alignItems: "center",
|
|
247
|
-
justifyContent: "center",
|
|
248
|
-
}}
|
|
249
|
-
onMouseEnter={(e) => {
|
|
250
|
-
e.currentTarget.style.background =
|
|
251
|
-
"light-dark(rgba(100, 116, 139, 0.1), rgba(148, 163, 184, 0.15))";
|
|
252
|
-
e.currentTarget.style.color = "light-dark(#1e293b, #f8fafc)";
|
|
253
|
-
}}
|
|
254
|
-
onMouseLeave={(e) => {
|
|
255
|
-
e.currentTarget.style.background = "transparent";
|
|
256
|
-
e.currentTarget.style.color = "light-dark(#64748b, #94a3b8)";
|
|
257
|
-
}}
|
|
258
|
-
aria-label="Close"
|
|
259
|
-
>
|
|
260
|
-
<X size={20} strokeWidth={2} />
|
|
261
|
-
</button>
|
|
145
|
+
<h2 className="ai-signin-title">{panelTitle}</h2>
|
|
146
|
+
<p className="ai-signin-subtitle">
|
|
147
|
+
Connectez-vous pour activer les composants IA dans votre app.
|
|
148
|
+
</p>
|
|
262
149
|
</div>
|
|
263
150
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
padding: "0 32px 32px",
|
|
268
|
-
overflow: "auto",
|
|
269
|
-
flex: 1,
|
|
270
|
-
position: "relative",
|
|
271
|
-
zIndex: 1,
|
|
272
|
-
}}
|
|
273
|
-
>
|
|
274
|
-
<form
|
|
275
|
-
onSubmit={handleSubmit}
|
|
276
|
-
style={{ display: "flex", flexDirection: "column", gap: "20px" }}
|
|
277
|
-
>
|
|
278
|
-
{/* Email Input */}
|
|
279
|
-
<div>
|
|
151
|
+
<div className="ai-signin-content">
|
|
152
|
+
<form onSubmit={handleSubmit}>
|
|
153
|
+
<div className="ai-input-row">
|
|
280
154
|
<label
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
alignItems: "center",
|
|
284
|
-
gap: "8px",
|
|
285
|
-
fontSize: "13px",
|
|
286
|
-
fontWeight: 600,
|
|
287
|
-
color: "light-dark(#1e293b, #f8fafc)",
|
|
288
|
-
marginBottom: "10px",
|
|
289
|
-
letterSpacing: "0.01em",
|
|
290
|
-
}}
|
|
155
|
+
htmlFor="lb-signin-email"
|
|
156
|
+
className="ai-input-label ai-row"
|
|
291
157
|
>
|
|
292
|
-
<Mail size={
|
|
158
|
+
<Mail size={14} className="ai-inline-icon" />
|
|
293
159
|
Email
|
|
294
160
|
</label>
|
|
295
|
-
<div
|
|
296
|
-
<
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
borderRadius: "12px",
|
|
310
|
-
background: "light-dark(#f8fafc, #0f172a)",
|
|
311
|
-
color: "light-dark(#1e293b, #f8fafc)",
|
|
312
|
-
outline: "none",
|
|
313
|
-
transition: "all 0.2s ease",
|
|
314
|
-
fontWeight: 500,
|
|
315
|
-
}}
|
|
316
|
-
onFocus={(e) => {
|
|
317
|
-
e.currentTarget.style.borderColor =
|
|
318
|
-
"rgba(139, 92, 246, 0.6)";
|
|
319
|
-
e.currentTarget.style.boxShadow =
|
|
320
|
-
"0 0 0 4px rgba(139, 92, 246, 0.15)";
|
|
321
|
-
e.currentTarget.style.background =
|
|
322
|
-
"light-dark(#ffffff, #1e293b)";
|
|
323
|
-
}}
|
|
324
|
-
onBlur={(e) => {
|
|
325
|
-
e.currentTarget.style.borderColor =
|
|
326
|
-
"light-dark(#e2e8f0, #334155)";
|
|
327
|
-
e.currentTarget.style.boxShadow = "none";
|
|
328
|
-
e.currentTarget.style.background =
|
|
329
|
-
"light-dark(#f8fafc, #0f172a)";
|
|
330
|
-
}}
|
|
331
|
-
/>
|
|
161
|
+
<div className="ai-control-group ai-glow">
|
|
162
|
+
<div className="ai-shell ai-size-md ai-radius-full">
|
|
163
|
+
<input
|
|
164
|
+
id="lb-signin-email"
|
|
165
|
+
className="ai-control ai-control-input ai-size-md ai-radius-full"
|
|
166
|
+
type="email"
|
|
167
|
+
value={email}
|
|
168
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
169
|
+
required
|
|
170
|
+
autoFocus
|
|
171
|
+
autoComplete="email"
|
|
172
|
+
placeholder="votre@email.com"
|
|
173
|
+
/>
|
|
174
|
+
</div>
|
|
332
175
|
</div>
|
|
333
176
|
</div>
|
|
334
177
|
|
|
335
|
-
|
|
336
|
-
<div>
|
|
178
|
+
<div className="ai-input-row">
|
|
337
179
|
<label
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
alignItems: "center",
|
|
341
|
-
gap: "8px",
|
|
342
|
-
fontSize: "13px",
|
|
343
|
-
fontWeight: 600,
|
|
344
|
-
color: "light-dark(#1e293b, #f8fafc)",
|
|
345
|
-
marginBottom: "10px",
|
|
346
|
-
letterSpacing: "0.01em",
|
|
347
|
-
}}
|
|
180
|
+
htmlFor="lb-signin-password"
|
|
181
|
+
className="ai-input-label ai-row"
|
|
348
182
|
>
|
|
349
|
-
<Lock size={
|
|
183
|
+
<Lock size={14} className="ai-inline-icon" />
|
|
350
184
|
Mot de passe
|
|
351
185
|
</label>
|
|
352
|
-
<div
|
|
353
|
-
<
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
borderRadius: "12px",
|
|
366
|
-
background: "light-dark(#f8fafc, #0f172a)",
|
|
367
|
-
color: "light-dark(#1e293b, #f8fafc)",
|
|
368
|
-
outline: "none",
|
|
369
|
-
transition: "all 0.2s ease",
|
|
370
|
-
letterSpacing: "0.15em",
|
|
371
|
-
fontWeight: 500,
|
|
372
|
-
}}
|
|
373
|
-
onFocus={(e) => {
|
|
374
|
-
e.currentTarget.style.borderColor =
|
|
375
|
-
"rgba(139, 92, 246, 0.6)";
|
|
376
|
-
e.currentTarget.style.boxShadow =
|
|
377
|
-
"0 0 0 4px rgba(139, 92, 246, 0.15)";
|
|
378
|
-
e.currentTarget.style.background =
|
|
379
|
-
"light-dark(#ffffff, #1e293b)";
|
|
380
|
-
}}
|
|
381
|
-
onBlur={(e) => {
|
|
382
|
-
e.currentTarget.style.borderColor =
|
|
383
|
-
"light-dark(#e2e8f0, #334155)";
|
|
384
|
-
e.currentTarget.style.boxShadow = "none";
|
|
385
|
-
e.currentTarget.style.background =
|
|
386
|
-
"light-dark(#f8fafc, #0f172a)";
|
|
387
|
-
}}
|
|
388
|
-
/>
|
|
186
|
+
<div className="ai-control-group ai-glow">
|
|
187
|
+
<div className="ai-shell ai-size-md ai-radius-full">
|
|
188
|
+
<input
|
|
189
|
+
id="lb-signin-password"
|
|
190
|
+
className="ai-control ai-control-input ai-size-md ai-radius-full"
|
|
191
|
+
type="password"
|
|
192
|
+
value={password}
|
|
193
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
194
|
+
required
|
|
195
|
+
autoComplete="current-password"
|
|
196
|
+
placeholder="••••••••"
|
|
197
|
+
/>
|
|
198
|
+
</div>
|
|
389
199
|
</div>
|
|
390
200
|
</div>
|
|
391
201
|
|
|
392
|
-
{
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
padding: "14px 16px",
|
|
397
|
-
background: "rgba(239, 68, 68, 0.1)",
|
|
398
|
-
border: "2px solid rgba(239, 68, 68, 0.35)",
|
|
399
|
-
borderRadius: "12px",
|
|
400
|
-
color: "light-dark(#dc2626, #fca5a5)",
|
|
401
|
-
fontSize: "14px",
|
|
402
|
-
display: "flex",
|
|
403
|
-
alignItems: "start",
|
|
404
|
-
gap: "12px",
|
|
405
|
-
animation: "shake 0.4s ease",
|
|
406
|
-
}}
|
|
407
|
-
>
|
|
408
|
-
<AlertCircle
|
|
409
|
-
size={20}
|
|
410
|
-
style={{ flexShrink: 0, marginTop: "2px" }}
|
|
411
|
-
strokeWidth={2.5}
|
|
412
|
-
/>
|
|
413
|
-
<span style={{ flex: 1, lineHeight: "1.5", fontWeight: 500 }}>
|
|
414
|
-
{error}
|
|
415
|
-
</span>
|
|
202
|
+
{error ? (
|
|
203
|
+
<div className="ai-signin-error" role="alert">
|
|
204
|
+
<AlertCircle size={16} />
|
|
205
|
+
<span>{error}</span>
|
|
416
206
|
</div>
|
|
417
|
-
)}
|
|
207
|
+
) : null}
|
|
418
208
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
width: "100%",
|
|
425
|
-
padding: "16px",
|
|
426
|
-
fontSize: "15px",
|
|
427
|
-
fontWeight: 700,
|
|
428
|
-
color: "#ffffff",
|
|
429
|
-
background: loading
|
|
430
|
-
? "light-dark(#cbd5e1, #475569)"
|
|
431
|
-
: "linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)",
|
|
432
|
-
border: "none",
|
|
433
|
-
borderRadius: "12px",
|
|
434
|
-
cursor: loading ? "not-allowed" : "pointer",
|
|
435
|
-
transition: "all 0.2s ease",
|
|
436
|
-
display: "flex",
|
|
437
|
-
alignItems: "center",
|
|
438
|
-
justifyContent: "center",
|
|
439
|
-
gap: "10px",
|
|
440
|
-
letterSpacing: "0.01em",
|
|
441
|
-
opacity: loading ? 0.6 : 1,
|
|
442
|
-
boxShadow: loading
|
|
443
|
-
? "none"
|
|
444
|
-
: "0 4px 14px rgba(139, 92, 246, 0.4)",
|
|
445
|
-
}}
|
|
446
|
-
onMouseEnter={(e) => {
|
|
447
|
-
if (!loading) {
|
|
448
|
-
e.currentTarget.style.transform = "translateY(-2px)";
|
|
449
|
-
e.currentTarget.style.boxShadow =
|
|
450
|
-
"0 8px 24px rgba(139, 92, 246, 0.6)";
|
|
451
|
-
}
|
|
452
|
-
}}
|
|
453
|
-
onMouseLeave={(e) => {
|
|
454
|
-
e.currentTarget.style.transform = "translateY(0)";
|
|
455
|
-
e.currentTarget.style.boxShadow =
|
|
456
|
-
"0 4px 14px rgba(139, 92, 246, 0.4)";
|
|
457
|
-
}}
|
|
458
|
-
>
|
|
459
|
-
{loading ? (
|
|
460
|
-
<>
|
|
461
|
-
<Loader2
|
|
462
|
-
size={20}
|
|
463
|
-
strokeWidth={2.5}
|
|
464
|
-
style={{ animation: "spin 1s linear infinite" }}
|
|
465
|
-
/>
|
|
466
|
-
Connexion en cours...
|
|
467
|
-
</>
|
|
468
|
-
) : (
|
|
469
|
-
<>
|
|
470
|
-
<Sparkles size={20} strokeWidth={2.5} />
|
|
471
|
-
Se connecter
|
|
472
|
-
</>
|
|
473
|
-
)}
|
|
474
|
-
</button>
|
|
475
|
-
|
|
476
|
-
{/* Signup Link */}
|
|
477
|
-
<div
|
|
478
|
-
style={{
|
|
479
|
-
marginTop: "8px",
|
|
480
|
-
padding: "20px",
|
|
481
|
-
background: "rgba(139, 92, 246, 0.05)",
|
|
482
|
-
border: "1px solid rgba(139, 92, 246, 0.2)",
|
|
483
|
-
borderRadius: "16px",
|
|
484
|
-
textAlign: "center",
|
|
485
|
-
}}
|
|
486
|
-
>
|
|
487
|
-
<p
|
|
488
|
-
style={{
|
|
489
|
-
margin: "0 0 14px 0",
|
|
490
|
-
fontSize: "14px",
|
|
491
|
-
color: "light-dark(#64748b, #94a3b8)",
|
|
492
|
-
fontWeight: 500,
|
|
493
|
-
}}
|
|
209
|
+
<div className="ai-signin-actions">
|
|
210
|
+
<button
|
|
211
|
+
type="submit"
|
|
212
|
+
className="ai-btn ai-btn--auth"
|
|
213
|
+
disabled={loading}
|
|
494
214
|
>
|
|
495
|
-
|
|
496
|
-
|
|
215
|
+
{loading ? (
|
|
216
|
+
<>
|
|
217
|
+
<Loader2 size={16} className="ai-spinner" />
|
|
218
|
+
Connexion...
|
|
219
|
+
</>
|
|
220
|
+
) : (
|
|
221
|
+
<>
|
|
222
|
+
<Sparkles size={16} />
|
|
223
|
+
Se connecter
|
|
224
|
+
</>
|
|
225
|
+
)}
|
|
226
|
+
</button>
|
|
227
|
+
|
|
497
228
|
<a
|
|
498
229
|
href="https://prompt.lastbrain.io/signup"
|
|
499
230
|
target="_blank"
|
|
500
231
|
rel="noopener noreferrer"
|
|
501
|
-
|
|
502
|
-
display: "inline-flex",
|
|
503
|
-
alignItems: "center",
|
|
504
|
-
gap: "8px",
|
|
505
|
-
padding: "10px 20px",
|
|
506
|
-
fontSize: "14px",
|
|
507
|
-
fontWeight: 600,
|
|
508
|
-
color: "#8b5cf6",
|
|
509
|
-
textDecoration: "none",
|
|
510
|
-
border: "2px solid rgba(139, 92, 246, 0.3)",
|
|
511
|
-
borderRadius: "10px",
|
|
512
|
-
transition: "all 0.2s ease",
|
|
513
|
-
}}
|
|
514
|
-
onMouseEnter={(e) => {
|
|
515
|
-
e.currentTarget.style.background = "rgba(139, 92, 246, 0.1)";
|
|
516
|
-
e.currentTarget.style.borderColor = "rgba(139, 92, 246, 0.5)";
|
|
517
|
-
e.currentTarget.style.transform = "translateY(-1px)";
|
|
518
|
-
}}
|
|
519
|
-
onMouseLeave={(e) => {
|
|
520
|
-
e.currentTarget.style.background = "transparent";
|
|
521
|
-
e.currentTarget.style.borderColor = "rgba(139, 92, 246, 0.3)";
|
|
522
|
-
e.currentTarget.style.transform = "translateY(0)";
|
|
523
|
-
}}
|
|
232
|
+
className="ai-btn ai-btn--ghost"
|
|
524
233
|
>
|
|
525
|
-
|
|
526
|
-
Créer un compte gratuitement
|
|
234
|
+
Créer un compte
|
|
527
235
|
</a>
|
|
528
236
|
</div>
|
|
529
237
|
</form>
|
|
530
238
|
</div>
|
|
531
239
|
</div>
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
{`
|
|
535
|
-
@keyframes fadeIn {
|
|
536
|
-
from { opacity: 0; }
|
|
537
|
-
to { opacity: 1; }
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
@keyframes slideUp {
|
|
541
|
-
from {
|
|
542
|
-
opacity: 0;
|
|
543
|
-
transform: translateY(20px) scale(0.95);
|
|
544
|
-
}
|
|
545
|
-
to {
|
|
546
|
-
opacity: 1;
|
|
547
|
-
transform: translateY(0) scale(1);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
@keyframes spin {
|
|
552
|
-
from { transform: rotate(0deg); }
|
|
553
|
-
to { transform: rotate(360deg); }
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
@keyframes pulse {
|
|
557
|
-
0%, 100% {
|
|
558
|
-
box-shadow: 0 8px 24px rgba(139, 92, 246, 0.3);
|
|
559
|
-
}
|
|
560
|
-
50% {
|
|
561
|
-
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.5);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
@keyframes shake {
|
|
566
|
-
0%, 100% { transform: translateX(0); }
|
|
567
|
-
25% { transform: translateX(-8px); }
|
|
568
|
-
75% { transform: translateX(8px); }
|
|
569
|
-
}
|
|
570
|
-
`}
|
|
571
|
-
</style>
|
|
572
|
-
</div>
|
|
240
|
+
</div>,
|
|
241
|
+
portalRoot
|
|
573
242
|
);
|
|
574
243
|
}
|