@lastbrain/ai-ui-react 1.0.46 → 1.0.47

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.
@@ -1 +1 @@
1
- {"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAetD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAe,EACf,SAAc,GACf,EAAE,mBAAmB,2CAk2BrB"}
1
+ {"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAkBtD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAe,EACf,SAAc,GACf,EAAE,mBAAmB,2CA+9BrB"}
@@ -2,20 +2,27 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState, useRef, useLayoutEffect } from "react";
4
4
  import { createPortal } from "react-dom";
5
- import { BarChart3, Settings, FileText, History as HistoryIcon, FolderPlus, Power, } from "lucide-react";
5
+ import { BarChart3, Settings, FileText, History as HistoryIcon, FolderPlus, Power, LogOut, Key, } from "lucide-react";
6
6
  import { aiStyles, calculateTooltipPosition } from "../styles/inline";
7
7
  import { useLB } from "../context/LBAuthProvider";
8
8
  import { LBSigninModal } from "./LBSigninModal";
9
+ import { LBApiKeySelector } from "./LBApiKeySelector";
9
10
  export function AiStatusButton({ status, loading = false, className = "", }) {
10
11
  // Rendre l'authentification optionnelle
11
12
  let lbStatus;
12
13
  let user;
13
14
  let logout;
15
+ let apiKeys = [];
16
+ let accessToken;
17
+ let selectApiKeyWithToken;
14
18
  try {
15
19
  const lbContext = useLB();
16
20
  lbStatus = lbContext.status;
17
21
  user = lbContext.user;
18
22
  logout = lbContext.logout;
23
+ apiKeys = lbContext.apiKeys || [];
24
+ accessToken = lbContext.accessToken;
25
+ selectApiKeyWithToken = lbContext.selectApiKeyWithToken;
19
26
  }
20
27
  catch {
21
28
  // LBProvider n'est pas disponible, ignorer
@@ -24,6 +31,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
24
31
  logout = undefined;
25
32
  }
26
33
  const [showSigninModal, setShowSigninModal] = useState(false);
34
+ const [showApiKeySelector, setShowApiKeySelector] = useState(false);
27
35
  const formatNumber = (value) => typeof value === "number" ? value.toLocaleString() : "0";
28
36
  const formatFixed = (value, digits) => typeof value === "number" ? value.toFixed(digits) : "0.00";
29
37
  const formatStorage = (valueMb) => {
@@ -335,10 +343,23 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
335
343
  ...aiStyles.tooltip,
336
344
  ...tooltipPosition,
337
345
  zIndex: 50,
338
- }, onMouseEnter: () => setShowTooltip(true), onMouseLeave: handleMouseLeave, children: [_jsx("div", { style: aiStyles.tooltipHeader, children: "API Status" }), _jsxs("div", { style: {
346
+ }, onMouseEnter: () => setShowTooltip(true), onMouseLeave: handleMouseLeave, children: [_jsx("div", { style: aiStyles.tooltipHeader, children: "API Status" }), status.user?.email && (_jsx("div", { style: {
339
347
  ...aiStyles.tooltipSection,
340
348
  ...aiStyles.tooltipSectionFirst,
341
- }, children: [_jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "API Key:" }), _jsx("span", { style: aiStyles.tooltipValue, children: status.api_key?.name || "N/A" })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Env:" }), _jsx("span", { style: aiStyles.tooltipValue, children: status.api_key?.env || "N/A" })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [status.api_key?.rate_limit_rpm || 0, " req/min"] })] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Balance" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: ["$", formatFixed(balanceUsed, 6), " / $", formatNumber(balanceTotal)] }), renderUsageCircle(balancePercentage)] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Storage" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [formatStorage(storageUsed), " /", " ", formatStorage(storageAllocated)] }), renderUsageCircle(storagePercentage)] })] }), _jsxs("div", { style: {
349
+ }, children: _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "User:" }), _jsx("span", { style: {
350
+ ...aiStyles.tooltipValue,
351
+ fontSize: "12px",
352
+ maxWidth: "200px",
353
+ overflow: "hidden",
354
+ textOverflow: "ellipsis",
355
+ }, children: status.user.email })] }) })), _jsxs("div", { style: {
356
+ ...aiStyles.tooltipSection,
357
+ ...(status.user?.email
358
+ ? {}
359
+ : aiStyles.tooltipSectionFirst),
360
+ }, children: [_jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "API Key:" }), _jsx("span", { style: aiStyles.tooltipValue, children: status.apiKey?.name ||
361
+ status.api_key?.name ||
362
+ "N/A" })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Env:" }), _jsx("span", { style: aiStyles.tooltipValue, children: status.api_key?.env || "N/A" })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [status.api_key?.rate_limit_rpm || 0, " req/min"] })] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Balance" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: ["$", formatFixed(balanceUsed, 6), " / $", formatNumber(balanceTotal)] }), renderUsageCircle(balancePercentage)] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Storage" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [formatStorage(storageUsed), " /", " ", formatStorage(storageAllocated)] }), renderUsageCircle(storagePercentage)] })] }), _jsxs("div", { style: {
342
363
  ...aiStyles.tooltipActions,
343
364
  width: "100%",
344
365
  flexDirection: "row",
@@ -436,5 +457,58 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
436
457
  Object.assign(e.currentTarget.style, {
437
458
  background: "transparent",
438
459
  });
439
- }, title: "New Folder", children: _jsx(FolderPlus, { size: 18 }) })] })] }), document.body)] }));
460
+ }, title: "New Folder", children: _jsx(FolderPlus, { size: 18 }) }), selectApiKeyWithToken && apiKeys.length > 1 && (_jsx("button", { onClick: () => setShowApiKeySelector(true), style: {
461
+ background: "transparent",
462
+ border: "none",
463
+ padding: "14px",
464
+ cursor: "pointer",
465
+ display: "flex",
466
+ alignItems: "center",
467
+ justifyContent: "center",
468
+ color: "#8b5cf6",
469
+ transition: "all 0.2s ease",
470
+ }, onMouseEnter: (e) => {
471
+ Object.assign(e.currentTarget.style, {
472
+ background: "rgba(139, 92, 246, 0.1)",
473
+ });
474
+ }, onMouseLeave: (e) => {
475
+ Object.assign(e.currentTarget.style, {
476
+ background: "transparent",
477
+ });
478
+ }, title: "Change API Key", children: _jsx(Key, { size: 18 }) })), logout && (_jsx("button", { onClick: async () => {
479
+ try {
480
+ await logout();
481
+ setShowTooltip(false);
482
+ }
483
+ catch (error) {
484
+ console.error("Logout failed:", error);
485
+ }
486
+ }, style: {
487
+ background: "transparent",
488
+ border: "none",
489
+ padding: "14px",
490
+ cursor: "pointer",
491
+ display: "flex",
492
+ alignItems: "center",
493
+ justifyContent: "center",
494
+ color: "#ef4444",
495
+ transition: "all 0.2s ease",
496
+ }, onMouseEnter: (e) => {
497
+ Object.assign(e.currentTarget.style, {
498
+ background: "rgba(239, 68, 68, 0.1)",
499
+ });
500
+ }, onMouseLeave: (e) => {
501
+ Object.assign(e.currentTarget.style, {
502
+ background: "transparent",
503
+ });
504
+ }, title: "Logout", children: _jsx(LogOut, { size: 18 }) }))] })] }), document.body), showApiKeySelector && selectApiKeyWithToken && (_jsx(LBApiKeySelector, { isOpen: showApiKeySelector, apiKeys: apiKeys, onSelect: async (keyId) => {
505
+ try {
506
+ await selectApiKeyWithToken(keyId);
507
+ setShowApiKeySelector(false);
508
+ setShowTooltip(false);
509
+ }
510
+ catch (error) {
511
+ console.error("Failed to select API key:", error);
512
+ }
513
+ }, onCancel: () => setShowApiKeySelector(false) }))] }));
440
514
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/ai-ui-react",
3
- "version": "1.0.46",
3
+ "version": "1.0.47",
4
4
  "description": "Headless React components for LastBrain AI UI Kit",
5
5
  "private": false,
6
6
  "type": "module",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "lucide-react": "^0.257.0",
51
- "@lastbrain/ai-ui-core": "1.0.36"
51
+ "@lastbrain/ai-ui-core": "1.0.38"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/react": "^19.2.0",
@@ -10,10 +10,13 @@ import {
10
10
  History as HistoryIcon,
11
11
  FolderPlus,
12
12
  Power,
13
+ LogOut,
14
+ Key,
13
15
  } from "lucide-react";
14
16
  import { aiStyles, calculateTooltipPosition } from "../styles/inline";
15
17
  import { useLB } from "../context/LBAuthProvider";
16
18
  import { LBSigninModal } from "./LBSigninModal";
19
+ import { LBApiKeySelector } from "./LBApiKeySelector";
17
20
 
18
21
  export interface AiStatusButtonProps {
19
22
  status: AiStatus | null;
@@ -30,12 +33,20 @@ export function AiStatusButton({
30
33
  let lbStatus: string | undefined;
31
34
  let user: any;
32
35
  let logout: (() => Promise<void>) | undefined;
36
+ let apiKeys: any[] = [];
37
+ let accessToken: string | undefined;
38
+ let selectApiKeyWithToken:
39
+ | ((apiKeyId: string) => Promise<void>)
40
+ | undefined;
33
41
 
34
42
  try {
35
43
  const lbContext = useLB();
36
44
  lbStatus = lbContext.status;
37
45
  user = lbContext.user;
38
46
  logout = lbContext.logout;
47
+ apiKeys = lbContext.apiKeys || [];
48
+ accessToken = lbContext.accessToken;
49
+ selectApiKeyWithToken = lbContext.selectApiKeyWithToken;
39
50
  } catch {
40
51
  // LBProvider n'est pas disponible, ignorer
41
52
  lbStatus = undefined;
@@ -44,6 +55,7 @@ export function AiStatusButton({
44
55
  }
45
56
 
46
57
  const [showSigninModal, setShowSigninModal] = useState(false);
58
+ const [showApiKeySelector, setShowApiKeySelector] = useState(false);
47
59
 
48
60
  type BalanceUsage = {
49
61
  used?: number;
@@ -664,16 +676,45 @@ export function AiStatusButton({
664
676
  >
665
677
  <div style={aiStyles.tooltipHeader}>API Status</div>
666
678
 
679
+ {/* User Info Section */}
680
+ {status.user?.email && (
681
+ <div
682
+ style={{
683
+ ...aiStyles.tooltipSection,
684
+ ...aiStyles.tooltipSectionFirst,
685
+ }}
686
+ >
687
+ <div style={aiStyles.tooltipRow}>
688
+ <span style={aiStyles.tooltipLabel}>User:</span>
689
+ <span
690
+ style={{
691
+ ...aiStyles.tooltipValue,
692
+ fontSize: "12px",
693
+ maxWidth: "200px",
694
+ overflow: "hidden",
695
+ textOverflow: "ellipsis",
696
+ }}
697
+ >
698
+ {status.user.email}
699
+ </span>
700
+ </div>
701
+ </div>
702
+ )}
703
+
667
704
  <div
668
705
  style={{
669
706
  ...aiStyles.tooltipSection,
670
- ...aiStyles.tooltipSectionFirst,
707
+ ...(status.user?.email
708
+ ? {}
709
+ : aiStyles.tooltipSectionFirst),
671
710
  }}
672
711
  >
673
712
  <div style={aiStyles.tooltipRow}>
674
713
  <span style={aiStyles.tooltipLabel}>API Key:</span>
675
714
  <span style={aiStyles.tooltipValue}>
676
- {status.api_key?.name || "N/A"}
715
+ {status.apiKey?.name ||
716
+ status.api_key?.name ||
717
+ "N/A"}
677
718
  </span>
678
719
  </div>
679
720
  <div style={aiStyles.tooltipRow}>
@@ -885,10 +926,97 @@ export function AiStatusButton({
885
926
  >
886
927
  <FolderPlus size={18} />
887
928
  </button>
929
+
930
+ {/* Change API Key Button */}
931
+ {selectApiKeyWithToken && apiKeys.length > 1 && (
932
+ <button
933
+ onClick={() => setShowApiKeySelector(true)}
934
+ style={{
935
+ background: "transparent",
936
+ border: "none",
937
+ padding: "14px",
938
+ cursor: "pointer",
939
+ display: "flex",
940
+ alignItems: "center",
941
+ justifyContent: "center",
942
+ color: "#8b5cf6",
943
+ transition: "all 0.2s ease",
944
+ }}
945
+ onMouseEnter={(e) => {
946
+ Object.assign(e.currentTarget.style, {
947
+ background: "rgba(139, 92, 246, 0.1)",
948
+ });
949
+ }}
950
+ onMouseLeave={(e) => {
951
+ Object.assign(e.currentTarget.style, {
952
+ background: "transparent",
953
+ });
954
+ }}
955
+ title="Change API Key"
956
+ >
957
+ <Key size={18} />
958
+ </button>
959
+ )}
960
+
961
+ {/* Logout Button */}
962
+ {logout && (
963
+ <button
964
+ onClick={async () => {
965
+ try {
966
+ await logout();
967
+ setShowTooltip(false);
968
+ } catch (error) {
969
+ console.error("Logout failed:", error);
970
+ }
971
+ }}
972
+ style={{
973
+ background: "transparent",
974
+ border: "none",
975
+ padding: "14px",
976
+ cursor: "pointer",
977
+ display: "flex",
978
+ alignItems: "center",
979
+ justifyContent: "center",
980
+ color: "#ef4444",
981
+ transition: "all 0.2s ease",
982
+ }}
983
+ onMouseEnter={(e) => {
984
+ Object.assign(e.currentTarget.style, {
985
+ background: "rgba(239, 68, 68, 0.1)",
986
+ });
987
+ }}
988
+ onMouseLeave={(e) => {
989
+ Object.assign(e.currentTarget.style, {
990
+ background: "transparent",
991
+ });
992
+ }}
993
+ title="Logout"
994
+ >
995
+ <LogOut size={18} />
996
+ </button>
997
+ )}
888
998
  </div>
889
999
  </div>,
890
1000
  document.body
891
1001
  )}
1002
+
1003
+ {/* API Key Selector Modal */}
1004
+ {showApiKeySelector && selectApiKeyWithToken && (
1005
+ <LBApiKeySelector
1006
+ isOpen={showApiKeySelector}
1007
+ apiKeys={apiKeys}
1008
+ onSelect={async (keyId) => {
1009
+ try {
1010
+ await selectApiKeyWithToken(keyId);
1011
+ setShowApiKeySelector(false);
1012
+ setShowTooltip(false);
1013
+ } catch (error) {
1014
+ console.error("Failed to select API key:", error);
1015
+ }
1016
+ }}
1017
+ onCancel={() => setShowApiKeySelector(false)}
1018
+ />
1019
+ )}
892
1020
  </div>
893
1021
  );
894
1022
  }