@lastbrain/ai-ui-react 1.0.41 → 1.0.43

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.
@@ -9,8 +9,11 @@ import {
9
9
  FileText,
10
10
  History as HistoryIcon,
11
11
  FolderPlus,
12
+ Power,
12
13
  } from "lucide-react";
13
14
  import { aiStyles, calculateTooltipPosition } from "../styles/inline";
15
+ import { useLB } from "../context/LBAuthProvider";
16
+ import { LBSigninModal } from "./LBSigninModal";
14
17
 
15
18
  export interface AiStatusButtonProps {
16
19
  status: AiStatus | null;
@@ -23,6 +26,25 @@ export function AiStatusButton({
23
26
  loading = false,
24
27
  className = "",
25
28
  }: AiStatusButtonProps) {
29
+ // Rendre l'authentification optionnelle
30
+ let lbStatus: string | undefined;
31
+ let user: any;
32
+ let logout: (() => Promise<void>) | undefined;
33
+
34
+ try {
35
+ const lbContext = useLB();
36
+ lbStatus = lbContext.status;
37
+ user = lbContext.user;
38
+ logout = lbContext.logout;
39
+ } catch {
40
+ // LBProvider n'est pas disponible, ignorer
41
+ lbStatus = undefined;
42
+ user = undefined;
43
+ logout = undefined;
44
+ }
45
+
46
+ const [showSigninModal, setShowSigninModal] = useState(false);
47
+
26
48
  type BalanceUsage = {
27
49
  used?: number;
28
50
  total?: number;
@@ -257,47 +279,343 @@ export function AiStatusButton({
257
279
  }
258
280
 
259
281
  if (!status) {
282
+ // Si pas de statut API et pas de LBProvider, afficher message simple
283
+ if (!lbStatus && lbStatus !== "ready") {
284
+ return (
285
+ <div style={{ position: "relative", display: "inline-block" }}>
286
+ <button
287
+ ref={buttonRef}
288
+ style={{
289
+ ...aiStyles.statusButton,
290
+ color: "#ef4444",
291
+ ...(isHovered && aiStyles.statusButtonHover),
292
+ }}
293
+ className={className}
294
+ onMouseEnter={handleMouseEnter}
295
+ onMouseLeave={handleMouseLeave}
296
+ >
297
+ <svg
298
+ width="16"
299
+ height="16"
300
+ viewBox="0 0 24 24"
301
+ fill="none"
302
+ stroke="currentColor"
303
+ >
304
+ <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
305
+ </svg>
306
+ </button>
307
+ {showTooltip &&
308
+ canPortal &&
309
+ createPortal(
310
+ <div
311
+ ref={tooltipRef}
312
+ style={{
313
+ ...aiStyles.tooltip,
314
+ ...tooltipPosition,
315
+ zIndex: 50,
316
+ }}
317
+ onMouseEnter={() => setShowTooltip(true)}
318
+ onMouseLeave={handleMouseLeave}
319
+ >
320
+ No status available
321
+ </div>,
322
+ document.body
323
+ )}
324
+ </div>
325
+ );
326
+ }
327
+
260
328
  return (
261
- <div style={{ position: "relative", display: "inline-block" }}>
262
- <button
263
- ref={buttonRef}
264
- style={{
265
- ...aiStyles.statusButton,
266
- color: "#ef4444",
267
- ...(isHovered && aiStyles.statusButtonHover),
268
- }}
269
- className={className}
270
- onMouseEnter={handleMouseEnter}
271
- onMouseLeave={handleMouseLeave}
272
- >
273
- <svg
274
- width="16"
275
- height="16"
276
- viewBox="0 0 24 24"
277
- fill="none"
278
- stroke="currentColor"
329
+ <>
330
+ <div style={{ position: "relative", display: "inline-block" }}>
331
+ <button
332
+ ref={buttonRef}
333
+ style={{
334
+ ...aiStyles.statusButton,
335
+ color: lbStatus === "ready" ? "#10b981" : "#ef4444",
336
+ ...(isHovered && aiStyles.statusButtonHover),
337
+ }}
338
+ className={className}
339
+ onMouseEnter={handleMouseEnter}
340
+ onMouseLeave={handleMouseLeave}
341
+ onClick={() => {
342
+ if (lbStatus !== "ready") {
343
+ setShowSigninModal(true);
344
+ }
345
+ }}
279
346
  >
280
- <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
281
- </svg>
282
- </button>
283
- {showTooltip &&
284
- canPortal &&
285
- createPortal(
286
- <div
287
- ref={tooltipRef}
288
- style={{
289
- ...aiStyles.tooltip,
290
- ...tooltipPosition,
291
- zIndex: 50,
292
- }}
293
- onMouseEnter={() => setShowTooltip(true)}
294
- onMouseLeave={handleMouseLeave}
347
+ <svg
348
+ width="16"
349
+ height="16"
350
+ viewBox="0 0 24 24"
351
+ fill="none"
352
+ stroke="currentColor"
295
353
  >
296
- No status available
297
- </div>,
298
- document.body
299
- )}
300
- </div>
354
+ <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
355
+ </svg>
356
+ </button>
357
+ {showTooltip &&
358
+ canPortal &&
359
+ createPortal(
360
+ <div
361
+ ref={tooltipRef}
362
+ style={{
363
+ ...aiStyles.tooltip,
364
+ ...tooltipPosition,
365
+ zIndex: 50,
366
+ }}
367
+ onMouseEnter={() => setShowTooltip(true)}
368
+ onMouseLeave={handleMouseLeave}
369
+ >
370
+ {lbStatus === "ready" && user ? (
371
+ <>
372
+ <div style={aiStyles.tooltipHeader}>
373
+ LastBrain Connected
374
+ </div>
375
+ <div
376
+ style={{
377
+ ...aiStyles.tooltipSection,
378
+ paddingBottom: "8px",
379
+ }}
380
+ >
381
+ <div style={aiStyles.tooltipRow}>
382
+ <span style={aiStyles.tooltipLabel}>User:</span>
383
+ <span style={aiStyles.tooltipValue}>{user.email}</span>
384
+ </div>
385
+ </div>
386
+ <div
387
+ style={{
388
+ display: "flex",
389
+ gap: "8px",
390
+ borderTop:
391
+ "1px solid var(--ai-border-primary, #374151)",
392
+ paddingTop: "12px",
393
+ }}
394
+ >
395
+ <button
396
+ onClick={() =>
397
+ window.open("https://lastbrain.io/metrics", "_blank")
398
+ }
399
+ style={{
400
+ flex: 1,
401
+ background: "transparent",
402
+ border: "none",
403
+ padding: "14px",
404
+ cursor: "pointer",
405
+ display: "flex",
406
+ alignItems: "center",
407
+ justifyContent: "center",
408
+ color: "#8b5cf6",
409
+ transition: "all 0.2s ease",
410
+ }}
411
+ onMouseEnter={(e) => {
412
+ Object.assign(e.currentTarget.style, {
413
+ background: "rgba(139, 92, 246, 0.1)",
414
+ });
415
+ }}
416
+ onMouseLeave={(e) => {
417
+ Object.assign(e.currentTarget.style, {
418
+ background: "transparent",
419
+ });
420
+ }}
421
+ title="View Metrics"
422
+ >
423
+ <BarChart3 size={18} />
424
+ </button>
425
+ <button
426
+ onClick={() =>
427
+ window.open("https://lastbrain.io/settings", "_blank")
428
+ }
429
+ style={{
430
+ flex: 1,
431
+ background: "transparent",
432
+ border: "none",
433
+ padding: "14px",
434
+ cursor: "pointer",
435
+ display: "flex",
436
+ alignItems: "center",
437
+ justifyContent: "center",
438
+ color: "#8b5cf6",
439
+ transition: "all 0.2s ease",
440
+ }}
441
+ onMouseEnter={(e) => {
442
+ Object.assign(e.currentTarget.style, {
443
+ background: "rgba(139, 92, 246, 0.1)",
444
+ });
445
+ }}
446
+ onMouseLeave={(e) => {
447
+ Object.assign(e.currentTarget.style, {
448
+ background: "transparent",
449
+ });
450
+ }}
451
+ title="Settings"
452
+ >
453
+ <Settings size={18} />
454
+ </button>
455
+ <button
456
+ onClick={() =>
457
+ window.open(
458
+ "https://prompt.lastbrain.io/auth/prompts",
459
+ "_blank"
460
+ )
461
+ }
462
+ style={{
463
+ flex: 1,
464
+ background: "transparent",
465
+ border: "none",
466
+ padding: "14px",
467
+ cursor: "pointer",
468
+ display: "flex",
469
+ alignItems: "center",
470
+ justifyContent: "center",
471
+ color: "#8b5cf6",
472
+ transition: "all 0.2s ease",
473
+ }}
474
+ onMouseEnter={(e) => {
475
+ Object.assign(e.currentTarget.style, {
476
+ background: "rgba(139, 92, 246, 0.1)",
477
+ });
478
+ }}
479
+ onMouseLeave={(e) => {
480
+ Object.assign(e.currentTarget.style, {
481
+ background: "transparent",
482
+ });
483
+ }}
484
+ title="My Prompts"
485
+ >
486
+ <FileText size={18} />
487
+ </button>
488
+ <button
489
+ onClick={() =>
490
+ window.open(
491
+ "https://prompt.lastbrain.io/auth/folder",
492
+ "_blank"
493
+ )
494
+ }
495
+ style={{
496
+ flex: 1,
497
+ background: "transparent",
498
+ border: "none",
499
+ padding: "14px",
500
+ cursor: "pointer",
501
+ display: "flex",
502
+ alignItems: "center",
503
+ justifyContent: "center",
504
+ color: "#8b5cf6",
505
+ transition: "all 0.2s ease",
506
+ }}
507
+ onMouseEnter={(e) => {
508
+ Object.assign(e.currentTarget.style, {
509
+ background: "rgba(139, 92, 246, 0.1)",
510
+ });
511
+ }}
512
+ onMouseLeave={(e) => {
513
+ Object.assign(e.currentTarget.style, {
514
+ background: "transparent",
515
+ });
516
+ }}
517
+ title="New Folder"
518
+ >
519
+ <FolderPlus size={18} />
520
+ </button>
521
+ <button
522
+ onClick={async () => {
523
+ if (logout) {
524
+ await logout();
525
+ }
526
+ setShowTooltip(false);
527
+ }}
528
+ style={{
529
+ flex: 1,
530
+ background: "transparent",
531
+ border: "none",
532
+ padding: "14px",
533
+ cursor: "pointer",
534
+ display: "flex",
535
+ alignItems: "center",
536
+ justifyContent: "center",
537
+ color: "#ef4444",
538
+ transition: "all 0.2s ease",
539
+ }}
540
+ onMouseEnter={(e) => {
541
+ Object.assign(e.currentTarget.style, {
542
+ background: "rgba(239, 68, 68, 0.1)",
543
+ });
544
+ }}
545
+ onMouseLeave={(e) => {
546
+ Object.assign(e.currentTarget.style, {
547
+ background: "transparent",
548
+ });
549
+ }}
550
+ title="Logout"
551
+ >
552
+ <Power size={18} />
553
+ </button>
554
+ </div>
555
+ </>
556
+ ) : (
557
+ <>
558
+ <div style={aiStyles.tooltipHeader}>
559
+ LastBrain Authentication
560
+ </div>
561
+ <div
562
+ style={{
563
+ ...aiStyles.tooltipSection,
564
+ paddingBottom: "12px",
565
+ }}
566
+ >
567
+ <p
568
+ style={{
569
+ margin: 0,
570
+ fontSize: "13px",
571
+ color: "var(--ai-text-secondary, #9ca3af)",
572
+ lineHeight: "1.5",
573
+ }}
574
+ >
575
+ Connectez-vous pour accéder aux fonctionnalités IA
576
+ </p>
577
+ </div>
578
+ <button
579
+ onClick={() => {
580
+ setShowSigninModal(true);
581
+ setShowTooltip(false);
582
+ }}
583
+ style={{
584
+ width: "100%",
585
+ padding: "10px",
586
+ background:
587
+ "linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)",
588
+ border: "none",
589
+ borderRadius: "6px",
590
+ color: "#ffffff",
591
+ fontSize: "13px",
592
+ fontWeight: 600,
593
+ cursor: "pointer",
594
+ transition: "all 0.2s ease",
595
+ }}
596
+ onMouseEnter={(e) => {
597
+ e.currentTarget.style.transform = "translateY(-1px)";
598
+ e.currentTarget.style.boxShadow =
599
+ "0 4px 12px rgba(139, 92, 246, 0.3)";
600
+ }}
601
+ onMouseLeave={(e) => {
602
+ e.currentTarget.style.transform = "translateY(0)";
603
+ e.currentTarget.style.boxShadow = "none";
604
+ }}
605
+ >
606
+ 🔐 Se connecter
607
+ </button>
608
+ </>
609
+ )}
610
+ </div>,
611
+ document.body
612
+ )}
613
+ </div>
614
+ <LBSigninModal
615
+ isOpen={showSigninModal}
616
+ onClose={() => setShowSigninModal(false)}
617
+ />
618
+ </>
301
619
  );
302
620
  }
303
621
 
@@ -15,7 +15,7 @@ import { UsageToast, useUsageToast } from "./UsageToast";
15
15
  import { aiStyles } from "../styles/inline";
16
16
  import { handleAIError } from "../utils/errorHandler";
17
17
  import { useLB } from "../context/LBAuthProvider";
18
- import { LBAuthModal } from "./LBConnectButton";
18
+ import { LBSigninModal } from "./LBSigninModal";
19
19
 
20
20
  export interface AiTextareaProps
21
21
  extends
@@ -52,7 +52,16 @@ export function AiTextarea({
52
52
  const [isButtonHovered, setIsButtonHovered] = useState(false);
53
53
  const textareaRef = useRef<HTMLTextAreaElement>(null);
54
54
  const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
55
- const { status: lbStatus } = useLB();
55
+
56
+ // Rendre l'authentification optionnelle
57
+ let lbStatus: string | undefined;
58
+ try {
59
+ const lbContext = useLB();
60
+ lbStatus = lbContext.status;
61
+ } catch {
62
+ // LBProvider n'est pas disponible, ignorer
63
+ lbStatus = undefined;
64
+ }
56
65
 
57
66
  const { models } = useAiModels({
58
67
  baseUrl,
@@ -259,6 +268,10 @@ export function AiTextarea({
259
268
  onComplete={clearToast}
260
269
  />
261
270
  )}
271
+ <LBSigninModal
272
+ isOpen={showAuthModal}
273
+ onClose={() => setShowAuthModal(false)}
274
+ />
262
275
  </div>
263
276
  );
264
277
  }