@lastbrain/ai-ui-react 1.0.49 → 1.0.52

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.
@@ -3,6 +3,7 @@
3
3
  import { useState, useEffect } from "react";
4
4
  import { useLB } from "../context/LBAuthProvider";
5
5
  import { LBApiKeySelector } from "./LBApiKeySelector";
6
+ import { Mail, Lock, Sparkles, X, Loader2, AlertCircle } from "lucide-react";
6
7
 
7
8
  export interface LBSigninModalProps {
8
9
  isOpen: boolean;
@@ -122,294 +123,376 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
122
123
  <div
123
124
  style={{
124
125
  position: "fixed",
125
- inset: 0,
126
- backgroundColor: "rgba(0, 0, 0, 0.6)",
127
- backdropFilter: "blur(8px)",
128
- WebkitBackdropFilter: "blur(8px)",
126
+ top: 0,
127
+ left: 0,
128
+ right: 0,
129
+ bottom: 0,
130
+ width: "100vw",
131
+ height: "100vh",
132
+ backgroundColor: "rgba(0, 0, 0, 0.75)",
133
+ backdropFilter: "blur(12px)",
134
+ WebkitBackdropFilter: "blur(12px)",
129
135
  display: "flex",
130
136
  alignItems: "center",
131
137
  justifyContent: "center",
132
138
  zIndex: 9999,
133
139
  padding: "16px",
140
+ animation: "fadeIn 0.2s ease-out",
141
+ overflow: "auto",
134
142
  }}
135
143
  onClick={onClose}
136
144
  onKeyDown={handleKeyDown}
137
145
  >
138
146
  <div
139
147
  style={{
140
- backgroundColor: "var(--ai-bg-primary, #1f2937)",
141
- borderRadius: "12px",
142
- boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)",
143
- maxWidth: "420px",
148
+ backgroundColor: "light-dark(#ffffff, #1e293b)",
149
+ borderRadius: "20px",
150
+ boxShadow:
151
+ "0 0 0 1px rgba(139, 92, 246, 0.2), 0 20px 70px rgba(139, 92, 246, 0.25), 0 4px 6px rgba(0, 0, 0, 0.1)",
152
+ maxWidth: "440px",
144
153
  width: "100%",
145
- border: "1px solid var(--ai-border-primary, #374151)",
154
+ border:
155
+ "1px solid light-dark(rgba(226, 232, 240, 0.8), rgba(139, 92, 246, 0.3))",
146
156
  display: "flex",
147
157
  flexDirection: "column",
148
- maxHeight: "85vh",
158
+ maxHeight: "90vh",
149
159
  overflow: "hidden",
160
+ position: "relative",
161
+ animation: "slideUp 0.3s ease-out",
150
162
  }}
151
163
  onClick={(e) => e.stopPropagation()}
152
164
  >
165
+ {/* Gradient Background Effect */}
166
+ <div
167
+ style={{
168
+ position: "absolute",
169
+ top: 0,
170
+ left: 0,
171
+ right: 0,
172
+ height: "200px",
173
+ background:
174
+ "radial-gradient(ellipse at top, rgba(139, 92, 246, 0.15), transparent 70%)",
175
+ pointerEvents: "none",
176
+ zIndex: 0,
177
+ }}
178
+ />
179
+
153
180
  {/* Header */}
154
181
  <div
155
182
  style={{
156
- padding: "20px 24px",
157
- borderBottom: "1px solid var(--ai-border-primary, #374151)",
183
+ padding: "32px 32px 24px",
158
184
  display: "flex",
159
- justifyContent: "space-between",
185
+ flexDirection: "column",
160
186
  alignItems: "center",
161
- background: "var(--ai-bg-secondary, #111827)",
187
+ gap: "16px",
188
+ position: "relative",
189
+ zIndex: 1,
162
190
  }}
163
191
  >
164
- <h2
192
+ {/* Logo/Icon */}
193
+ <div
165
194
  style={{
166
- margin: 0,
167
- fontSize: "18px",
168
- fontWeight: 600,
169
- color: "var(--ai-text-primary, #f9fafb)",
195
+ width: "64px",
196
+ height: "64px",
197
+ borderRadius: "16px",
198
+ background: "linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)",
170
199
  display: "flex",
171
200
  alignItems: "center",
172
- gap: "8px",
201
+ justifyContent: "center",
202
+ boxShadow: "0 8px 24px rgba(139, 92, 246, 0.3)",
203
+ animation: "pulse 2s ease-in-out infinite",
173
204
  }}
174
205
  >
175
- 🔐 Connexion LastBrain
176
- </h2>
206
+ <Sparkles size={32} color="#ffffff" strokeWidth={2.5} />
207
+ </div>
208
+
209
+ {/* Title */}
210
+ <div style={{ textAlign: "center" }}>
211
+ <h2
212
+ style={{
213
+ margin: "0 0 8px 0",
214
+ fontSize: "24px",
215
+ fontWeight: 700,
216
+ color: "light-dark(#1e293b, #f8fafc)",
217
+ letterSpacing: "-0.02em",
218
+ }}
219
+ >
220
+ Connexion LastBrain
221
+ </h2>
222
+ <p
223
+ style={{
224
+ margin: 0,
225
+ fontSize: "14px",
226
+ color: "light-dark(#64748b, #94a3b8)",
227
+ fontWeight: 400,
228
+ }}
229
+ >
230
+ Accédez à vos outils d'intelligence artificielle
231
+ </p>
232
+ </div>
233
+
234
+ {/* Close Button */}
177
235
  <button
178
236
  onClick={onClose}
179
237
  style={{
238
+ position: "absolute",
239
+ top: "24px",
240
+ right: "24px",
180
241
  background: "transparent",
181
242
  border: "none",
182
- color: "var(--ai-text-secondary, #9ca3af)",
243
+ color: "light-dark(#64748b, #94a3b8)",
183
244
  cursor: "pointer",
184
- fontSize: "24px",
185
- lineHeight: 1,
186
- padding: "4px 8px",
187
- borderRadius: "6px",
245
+ padding: "8px",
246
+ borderRadius: "10px",
188
247
  transition: "all 0.2s ease",
248
+ display: "flex",
249
+ alignItems: "center",
250
+ justifyContent: "center",
189
251
  }}
190
252
  onMouseEnter={(e) => {
191
253
  e.currentTarget.style.background =
192
- "var(--ai-bg-tertiary, #374151)";
193
- e.currentTarget.style.color = "var(--ai-text-primary, #f9fafb)";
254
+ "light-dark(rgba(100, 116, 139, 0.1), rgba(148, 163, 184, 0.15))";
255
+ e.currentTarget.style.color = "light-dark(#1e293b, #f8fafc)";
194
256
  }}
195
257
  onMouseLeave={(e) => {
196
258
  e.currentTarget.style.background = "transparent";
197
- e.currentTarget.style.color = "var(--ai-text-secondary, #9ca3af)";
259
+ e.currentTarget.style.color = "light-dark(#64748b, #94a3b8)";
198
260
  }}
199
261
  aria-label="Close"
200
262
  >
201
- ×
263
+ <X size={20} strokeWidth={2} />
202
264
  </button>
203
265
  </div>
204
266
 
205
267
  {/* Body */}
206
268
  <div
207
269
  style={{
208
- padding: "24px",
270
+ padding: "0 32px 32px",
209
271
  overflow: "auto",
210
272
  flex: 1,
273
+ position: "relative",
274
+ zIndex: 1,
211
275
  }}
212
276
  >
213
277
  <form
214
278
  onSubmit={handleSubmit}
215
- style={{ display: "flex", flexDirection: "column", gap: "16px" }}
279
+ style={{ display: "flex", flexDirection: "column", gap: "20px" }}
216
280
  >
281
+ {/* Email Input */}
217
282
  <div>
218
283
  <label
219
284
  style={{
220
- display: "block",
285
+ display: "flex",
286
+ alignItems: "center",
287
+ gap: "8px",
221
288
  fontSize: "13px",
222
289
  fontWeight: 600,
223
- color: "var(--ai-text-secondary, #9ca3af)",
224
- marginBottom: "8px",
225
- letterSpacing: "0.02em",
290
+ color: "light-dark(#1e293b, #f8fafc)",
291
+ marginBottom: "10px",
292
+ letterSpacing: "0.01em",
226
293
  }}
227
294
  >
228
- 📧 Email
295
+ <Mail size={16} strokeWidth={2.5} />
296
+ Email
229
297
  </label>
230
- <input
231
- type="email"
232
- value={email}
233
- onChange={(e) => setEmail(e.target.value)}
234
- required
235
- autoFocus
236
- autoComplete="email"
237
- placeholder="votre@email.com"
238
- style={{
239
- width: "100%",
240
- padding: "10px 12px",
241
- fontSize: "14px",
242
- border: "1px solid var(--ai-border-primary, #374151)",
243
- borderRadius: "8px",
244
- background: "var(--ai-bg-secondary, #111827)",
245
- color: "var(--ai-text-primary, #f9fafb)",
246
- outline: "none",
247
- transition: "all 0.2s ease",
248
- letterSpacing: "0.01em",
249
- }}
250
- onFocus={(e) => {
251
- e.currentTarget.style.borderColor = "#8b5cf6";
252
- e.currentTarget.style.boxShadow =
253
- "0 0 0 3px rgba(139, 92, 246, 0.1)";
254
- }}
255
- onBlur={(e) => {
256
- e.currentTarget.style.borderColor =
257
- "var(--ai-border-primary, #374151)";
258
- e.currentTarget.style.boxShadow = "none";
259
- }}
260
- />
298
+ <div style={{ position: "relative" }}>
299
+ <input
300
+ type="email"
301
+ value={email}
302
+ onChange={(e) => setEmail(e.target.value)}
303
+ required
304
+ autoFocus
305
+ autoComplete="email"
306
+ placeholder="votre@email.com"
307
+ style={{
308
+ width: "100%",
309
+ padding: "14px 16px",
310
+ fontSize: "15px",
311
+ border: "2px solid light-dark(#e2e8f0, #334155)",
312
+ borderRadius: "12px",
313
+ background: "light-dark(#f8fafc, #0f172a)",
314
+ color: "light-dark(#1e293b, #f8fafc)",
315
+ outline: "none",
316
+ transition: "all 0.2s ease",
317
+ fontWeight: 500,
318
+ }}
319
+ onFocus={(e) => {
320
+ e.currentTarget.style.borderColor =
321
+ "rgba(139, 92, 246, 0.6)";
322
+ e.currentTarget.style.boxShadow =
323
+ "0 0 0 4px rgba(139, 92, 246, 0.15)";
324
+ e.currentTarget.style.background =
325
+ "light-dark(#ffffff, #1e293b)";
326
+ }}
327
+ onBlur={(e) => {
328
+ e.currentTarget.style.borderColor =
329
+ "light-dark(#e2e8f0, #334155)";
330
+ e.currentTarget.style.boxShadow = "none";
331
+ e.currentTarget.style.background =
332
+ "light-dark(#f8fafc, #0f172a)";
333
+ }}
334
+ />
335
+ </div>
261
336
  </div>
262
337
 
338
+ {/* Password Input */}
263
339
  <div>
264
340
  <label
265
341
  style={{
266
- display: "block",
342
+ display: "flex",
343
+ alignItems: "center",
344
+ gap: "8px",
267
345
  fontSize: "13px",
268
346
  fontWeight: 600,
269
- color: "var(--ai-text-secondary, #9ca3af)",
270
- marginBottom: "8px",
271
- letterSpacing: "0.02em",
347
+ color: "light-dark(#1e293b, #f8fafc)",
348
+ marginBottom: "10px",
349
+ letterSpacing: "0.01em",
272
350
  }}
273
351
  >
274
- 🔒 Mot de passe
352
+ <Lock size={16} strokeWidth={2.5} />
353
+ Mot de passe
275
354
  </label>
276
- <input
277
- type="password"
278
- value={password}
279
- onChange={(e) => setPassword(e.target.value)}
280
- required
281
- autoComplete="current-password"
282
- placeholder="••••••••"
283
- style={{
284
- width: "100%",
285
- padding: "10px 12px",
286
- fontSize: "14px",
287
- border: "1px solid var(--ai-border-primary, #374151)",
288
- borderRadius: "8px",
289
- background: "var(--ai-bg-secondary, #111827)",
290
- color: "var(--ai-text-primary, #f9fafb)",
291
- outline: "none",
292
- transition: "all 0.2s ease",
293
- letterSpacing: "0.15em",
294
- }}
295
- onFocus={(e) => {
296
- e.currentTarget.style.borderColor = "#8b5cf6";
297
- e.currentTarget.style.boxShadow =
298
- "0 0 0 3px rgba(139, 92, 246, 0.1)";
299
- }}
300
- onBlur={(e) => {
301
- e.currentTarget.style.borderColor =
302
- "var(--ai-border-primary, #374151)";
303
- e.currentTarget.style.boxShadow = "none";
304
- }}
305
- />
355
+ <div style={{ position: "relative" }}>
356
+ <input
357
+ type="password"
358
+ value={password}
359
+ onChange={(e) => setPassword(e.target.value)}
360
+ required
361
+ autoComplete="current-password"
362
+ placeholder="••••••••"
363
+ style={{
364
+ width: "100%",
365
+ padding: "14px 16px",
366
+ fontSize: "15px",
367
+ border: "2px solid light-dark(#e2e8f0, #334155)",
368
+ borderRadius: "12px",
369
+ background: "light-dark(#f8fafc, #0f172a)",
370
+ color: "light-dark(#1e293b, #f8fafc)",
371
+ outline: "none",
372
+ transition: "all 0.2s ease",
373
+ letterSpacing: "0.15em",
374
+ fontWeight: 500,
375
+ }}
376
+ onFocus={(e) => {
377
+ e.currentTarget.style.borderColor =
378
+ "rgba(139, 92, 246, 0.6)";
379
+ e.currentTarget.style.boxShadow =
380
+ "0 0 0 4px rgba(139, 92, 246, 0.15)";
381
+ e.currentTarget.style.background =
382
+ "light-dark(#ffffff, #1e293b)";
383
+ }}
384
+ onBlur={(e) => {
385
+ e.currentTarget.style.borderColor =
386
+ "light-dark(#e2e8f0, #334155)";
387
+ e.currentTarget.style.boxShadow = "none";
388
+ e.currentTarget.style.background =
389
+ "light-dark(#f8fafc, #0f172a)";
390
+ }}
391
+ />
392
+ </div>
306
393
  </div>
307
394
 
395
+ {/* Error Message */}
308
396
  {error && (
309
397
  <div
310
398
  style={{
311
- padding: "12px",
399
+ padding: "14px 16px",
312
400
  background: "rgba(239, 68, 68, 0.1)",
313
- border: "1px solid rgba(239, 68, 68, 0.3)",
314
- borderRadius: "8px",
315
- color: "#ef4444",
316
- fontSize: "13px",
401
+ border: "2px solid rgba(239, 68, 68, 0.35)",
402
+ borderRadius: "12px",
403
+ color: "light-dark(#dc2626, #fca5a5)",
404
+ fontSize: "14px",
317
405
  display: "flex",
318
406
  alignItems: "start",
319
- gap: "8px",
407
+ gap: "12px",
408
+ animation: "shake 0.4s ease",
320
409
  }}
321
410
  >
322
- <span style={{ fontSize: "16px", flexShrink: 0 }}>⚠️</span>
323
- <span style={{ flex: 1, lineHeight: "1.5" }}>{error}</span>
411
+ <AlertCircle
412
+ size={20}
413
+ style={{ flexShrink: 0, marginTop: "2px" }}
414
+ strokeWidth={2.5}
415
+ />
416
+ <span style={{ flex: 1, lineHeight: "1.5", fontWeight: 500 }}>
417
+ {error}
418
+ </span>
324
419
  </div>
325
420
  )}
326
421
 
422
+ {/* Submit Button */}
327
423
  <button
328
424
  type="submit"
329
425
  disabled={loading}
330
426
  style={{
331
427
  width: "100%",
332
- padding: "12px",
333
- fontSize: "14px",
334
- fontWeight: 600,
428
+ padding: "16px",
429
+ fontSize: "15px",
430
+ fontWeight: 700,
335
431
  color: "#ffffff",
336
432
  background: loading
337
- ? "var(--ai-bg-tertiary, #374151)"
433
+ ? "light-dark(#cbd5e1, #475569)"
338
434
  : "linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)",
339
435
  border: "none",
340
- borderRadius: "8px",
436
+ borderRadius: "12px",
341
437
  cursor: loading ? "not-allowed" : "pointer",
342
438
  transition: "all 0.2s ease",
343
439
  display: "flex",
344
440
  alignItems: "center",
345
441
  justifyContent: "center",
346
- gap: "8px",
347
- letterSpacing: "0.02em",
442
+ gap: "10px",
443
+ letterSpacing: "0.01em",
348
444
  opacity: loading ? 0.6 : 1,
445
+ boxShadow: loading
446
+ ? "none"
447
+ : "0 4px 14px rgba(139, 92, 246, 0.4)",
349
448
  }}
350
449
  onMouseEnter={(e) => {
351
450
  if (!loading) {
352
- e.currentTarget.style.transform = "translateY(-1px)";
451
+ e.currentTarget.style.transform = "translateY(-2px)";
353
452
  e.currentTarget.style.boxShadow =
354
- "0 10px 20px rgba(139, 92, 246, 0.3)";
453
+ "0 8px 24px rgba(139, 92, 246, 0.6)";
355
454
  }
356
455
  }}
357
456
  onMouseLeave={(e) => {
358
457
  e.currentTarget.style.transform = "translateY(0)";
359
- e.currentTarget.style.boxShadow = "none";
458
+ e.currentTarget.style.boxShadow =
459
+ "0 4px 14px rgba(139, 92, 246, 0.4)";
360
460
  }}
361
461
  >
362
462
  {loading ? (
363
463
  <>
364
- <svg
365
- style={{
366
- animation: "spin 1s linear infinite",
367
- width: "16px",
368
- height: "16px",
369
- }}
370
- viewBox="0 0 24 24"
371
- fill="none"
372
- xmlns="http://www.w3.org/2000/svg"
373
- >
374
- <circle
375
- cx="12"
376
- cy="12"
377
- r="10"
378
- stroke="currentColor"
379
- strokeWidth="4"
380
- opacity="0.25"
381
- />
382
- <path
383
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
384
- fill="currentColor"
385
- opacity="0.75"
386
- />
387
- </svg>
464
+ <Loader2
465
+ size={20}
466
+ strokeWidth={2.5}
467
+ style={{ animation: "spin 1s linear infinite" }}
468
+ />
388
469
  Connexion en cours...
389
470
  </>
390
471
  ) : (
391
472
  <>
392
- <span>🚀</span>
473
+ <Sparkles size={20} strokeWidth={2.5} />
393
474
  Se connecter
394
475
  </>
395
476
  )}
396
477
  </button>
397
478
 
479
+ {/* Signup Link */}
398
480
  <div
399
481
  style={{
400
482
  marginTop: "8px",
401
- padding: "16px",
483
+ padding: "20px",
402
484
  background: "rgba(139, 92, 246, 0.05)",
403
485
  border: "1px solid rgba(139, 92, 246, 0.2)",
404
- borderRadius: "8px",
486
+ borderRadius: "16px",
405
487
  textAlign: "center",
406
488
  }}
407
489
  >
408
490
  <p
409
491
  style={{
410
- margin: "0 0 12px 0",
411
- fontSize: "13px",
412
- color: "var(--ai-text-secondary, #9ca3af)",
492
+ margin: "0 0 14px 0",
493
+ fontSize: "14px",
494
+ color: "light-dark(#64748b, #94a3b8)",
495
+ fontWeight: 500,
413
496
  }}
414
497
  >
415
498
  Pas encore de compte ?
@@ -419,26 +502,31 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
419
502
  target="_blank"
420
503
  rel="noopener noreferrer"
421
504
  style={{
422
- display: "inline-block",
423
- padding: "8px 16px",
424
- fontSize: "13px",
505
+ display: "inline-flex",
506
+ alignItems: "center",
507
+ gap: "8px",
508
+ padding: "10px 20px",
509
+ fontSize: "14px",
425
510
  fontWeight: 600,
426
511
  color: "#8b5cf6",
427
512
  textDecoration: "none",
428
- border: "1px solid rgba(139, 92, 246, 0.3)",
429
- borderRadius: "6px",
513
+ border: "2px solid rgba(139, 92, 246, 0.3)",
514
+ borderRadius: "10px",
430
515
  transition: "all 0.2s ease",
431
516
  }}
432
517
  onMouseEnter={(e) => {
433
518
  e.currentTarget.style.background = "rgba(139, 92, 246, 0.1)";
434
- e.currentTarget.style.borderColor = "#8b5cf6";
519
+ e.currentTarget.style.borderColor = "rgba(139, 92, 246, 0.5)";
520
+ e.currentTarget.style.transform = "translateY(-1px)";
435
521
  }}
436
522
  onMouseLeave={(e) => {
437
523
  e.currentTarget.style.background = "transparent";
438
524
  e.currentTarget.style.borderColor = "rgba(139, 92, 246, 0.3)";
525
+ e.currentTarget.style.transform = "translateY(0)";
439
526
  }}
440
527
  >
441
- Créer un compte gratuitement
528
+ <Sparkles size={16} strokeWidth={2.5} />
529
+ Créer un compte gratuitement
442
530
  </a>
443
531
  </div>
444
532
  </form>
@@ -447,10 +535,41 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
447
535
 
448
536
  <style>
449
537
  {`
538
+ @keyframes fadeIn {
539
+ from { opacity: 0; }
540
+ to { opacity: 1; }
541
+ }
542
+
543
+ @keyframes slideUp {
544
+ from {
545
+ opacity: 0;
546
+ transform: translateY(20px) scale(0.95);
547
+ }
548
+ to {
549
+ opacity: 1;
550
+ transform: translateY(0) scale(1);
551
+ }
552
+ }
553
+
450
554
  @keyframes spin {
451
555
  from { transform: rotate(0deg); }
452
556
  to { transform: rotate(360deg); }
453
557
  }
558
+
559
+ @keyframes pulse {
560
+ 0%, 100% {
561
+ box-shadow: 0 8px 24px rgba(139, 92, 246, 0.3);
562
+ }
563
+ 50% {
564
+ box-shadow: 0 8px 32px rgba(139, 92, 246, 0.5);
565
+ }
566
+ }
567
+
568
+ @keyframes shake {
569
+ 0%, 100% { transform: translateX(0); }
570
+ 25% { transform: translateX(-8px); }
571
+ 75% { transform: translateX(8px); }
572
+ }
454
573
  `}
455
574
  </style>
456
575
  </div>