@lastbrain/ai-ui-react 1.0.56 → 1.0.58

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;AAmBtD,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,2CA0iCrB"}
1
+ {"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAmBtD,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,2CA8kCrB"}
@@ -18,7 +18,10 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
18
18
  let selectApiKeyWithToken;
19
19
  let switchApiKey;
20
20
  let lbApiStatus = null;
21
+ let lbBasicStatus = null;
22
+ let lbStorageStatus = null;
21
23
  let lbIsLoadingStatus = false;
24
+ let lbIsLoadingStorage = false;
22
25
  try {
23
26
  const lbContext = useLB();
24
27
  lbStatus = lbContext.status;
@@ -29,7 +32,10 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
29
32
  selectApiKeyWithToken = lbContext.selectApiKeyWithToken;
30
33
  switchApiKey = lbContext.switchApiKey;
31
34
  lbApiStatus = lbContext.apiStatus;
35
+ lbBasicStatus = lbContext.basicStatus;
36
+ lbStorageStatus = lbContext.storageStatus;
32
37
  lbIsLoadingStatus = lbContext.isLoadingStatus || false;
38
+ lbIsLoadingStorage = lbContext.isLoadingStorage || false;
33
39
  }
34
40
  catch {
35
41
  // LBProvider n'est pas disponible, ignorer
@@ -38,7 +44,12 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
38
44
  logout = undefined;
39
45
  }
40
46
  // Utiliser le status du contexte LB si pas de prop status
41
- const effectiveStatus = status || lbApiStatus;
47
+ // Combinaison du basic status et storage status pour backward compatibility
48
+ const effectiveStatus = status || {
49
+ ...lbApiStatus,
50
+ ...lbBasicStatus,
51
+ storage: lbStorageStatus?.storage
52
+ };
42
53
  // Récupérer refetchProviders depuis AiProvider si disponible
43
54
  let refetchProviders;
44
55
  try {
@@ -423,7 +434,19 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
423
434
  effectiveStatus.api_key?.env ||
424
435
  "N/A" })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [effectiveStatus.apiKey?.rate_limit_rpm ||
425
436
  effectiveStatus.api_key?.rate_limit_rpm ||
426
- 0, " ", "req/min"] })] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Wallet" }), _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: {
437
+ 0, " ", "req/min"] })] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Wallet" }), _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:" }), lbIsLoadingStorage ? (_jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [_jsx("div", { style: {
438
+ height: "16px",
439
+ width: "120px",
440
+ background: "rgba(139, 92, 246, 0.1)",
441
+ borderRadius: "4px",
442
+ animation: "pulse 2s ease-in-out infinite",
443
+ } }), _jsx("div", { style: {
444
+ width: "28px",
445
+ height: "28px",
446
+ borderRadius: "50%",
447
+ background: "rgba(139, 92, 246, 0.1)",
448
+ animation: "pulse 2s ease-in-out infinite",
449
+ } })] })) : (_jsxs(_Fragment, { children: [_jsxs("span", { style: aiStyles.tooltipValue, children: [formatStorage(storageUsed), " /", " ", formatStorage(storageAllocated)] }), renderUsageCircle(storagePercentage)] }))] })] }), _jsxs("div", { style: {
427
450
  ...aiStyles.tooltipActions,
428
451
  width: "100%",
429
452
  flexDirection: "row",
@@ -1 +1 @@
1
- {"version":3,"file":"LBConnectButton.d.ts","sourceRoot":"","sources":["../../src/components/LBConnectButton.tsx"],"names":[],"mappings":"AAWA,UAAU,oBAAoB;IAC5B,sBAAsB;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,wBAAgB,eAAe,CAAC,EAC9B,KAAkC,EAClC,SAAc,EACd,WAAW,EACX,WAAW,GACZ,EAAE,oBAAoB,2CA4CtB;AAED;;GAEG;AACH,UAAU,gBAAgB;IACxB,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC;AAED,iBAAS,WAAW,CAAC,EAAE,OAAO,EAAE,EAAE,gBAAgB,2CA+RjD;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"LBConnectButton.d.ts","sourceRoot":"","sources":["../../src/components/LBConnectButton.tsx"],"names":[],"mappings":"AAWA,UAAU,oBAAoB;IAC5B,sBAAsB;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,wBAAgB,eAAe,CAAC,EAC9B,KAAkC,EAClC,SAAc,EACd,WAAW,EACX,WAAW,GACZ,EAAE,oBAAoB,2CAuDtB;AAED;;GAEG;AACH,UAAU,gBAAgB;IACxB,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC;AAED,iBAAS,WAAW,CAAC,EAAE,OAAO,EAAE,EAAE,gBAAgB,2CA+RjD;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -12,7 +12,17 @@ export function LBConnectButton({ label = "Se connecter à LastBrain", className
12
12
  const handleClick = () => {
13
13
  if (status === "ready" && user) {
14
14
  // Déjà connecté, proposer de se déconnecter
15
- logout();
15
+ logout().then(() => {
16
+ // Redirection propre après logout
17
+ if (typeof window !== "undefined") {
18
+ // Garder la langue actuelle de l'URL
19
+ const currentPath = window.location.pathname;
20
+ const langMatch = currentPath.match(/^\/([a-z]{2})\//);
21
+ const currentLang = langMatch ? langMatch[1] : "en";
22
+ // Rediriger vers la page d'accueil dans la langue actuelle
23
+ window.location.href = `/${currentLang}`;
24
+ }
25
+ });
16
26
  }
17
27
  else {
18
28
  // Pas connecté, ouvrir la modal
@@ -1 +1 @@
1
- {"version":3,"file":"LBSigninModal.d.ts","sourceRoot":"","sources":["../../src/components/LBSigninModal.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,kDA8jBpE"}
1
+ {"version":3,"file":"LBSigninModal.d.ts","sourceRoot":"","sources":["../../src/components/LBSigninModal.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,kDAijBpE"}
@@ -12,25 +12,14 @@ export function LBSigninModal({ isOpen, onClose }) {
12
12
  const [error, setError] = useState("");
13
13
  const [showKeySelector, setShowKeySelector] = useState(false);
14
14
  const [currentApiKeys, setCurrentApiKeys] = useState([]); // Stocker les clés API localement
15
- // Vérifier si LBProvider est disponible
16
- let login;
17
- let selectApiKeyWithToken;
18
- let fetchApiKeys;
19
- let apiKeys = [];
20
- let lbStatus;
21
- let lbContext;
22
- try {
23
- lbContext = useLB();
24
- login = lbContext.login;
25
- selectApiKeyWithToken = lbContext.selectApiKeyWithToken;
26
- fetchApiKeys = lbContext.fetchApiKeys;
27
- apiKeys = lbContext.apiKeys || [];
28
- lbStatus = lbContext.status;
29
- }
30
- catch {
31
- // LBProvider n'est pas disponible, ne pas rendre le modal
15
+ // Appeler useLB() inconditionnellement - hook doit toujours être appelé
16
+ const lbContext = useLB();
17
+ // Vérifier si le contexte est valide
18
+ if (!lbContext || !isOpen) {
32
19
  return null;
33
20
  }
21
+ // Extraire les valeurs du contexte
22
+ const { login, selectApiKeyWithToken, fetchApiKeys, apiKeys = [], status: lbStatus, } = lbContext;
34
23
  if (!isOpen || !login)
35
24
  return null;
36
25
  const handleSubmit = async (e) => {
@@ -41,10 +41,20 @@ interface LBContextValue extends LBAuthState {
41
41
  accessToken?: string;
42
42
  /** Status API (balance, storage, API key info) */
43
43
  apiStatus: AiStatus | null;
44
- /** Fonction pour rafraîchir le status */
44
+ /** Status basique (rapide) - balance, API key info sans storage */
45
+ basicStatus: any;
46
+ /** Status storage (lent) avec cache */
47
+ storageStatus: any;
48
+ /** Fonction pour rafraîchir le status rapide */
49
+ refreshBasicStatus: () => Promise<void>;
50
+ /** Fonction pour rafraîchir le storage (avec cache optionnel) */
51
+ refreshStorageStatus: (force?: boolean) => Promise<void>;
52
+ /** Fonction pour rafraîchir le status (backward compatibility) */
45
53
  refreshStatus: () => Promise<void>;
46
- /** Indique si le status est en cours de chargement */
54
+ /** Indique si le status basique est en cours de chargement */
47
55
  isLoadingStatus: boolean;
56
+ /** Indique si le storage est en cours de chargement */
57
+ isLoadingStorage: boolean;
48
58
  }
49
59
  export declare function LBProvider({ children, baseUrl: _baseUrl, proxyUrl, onStatusChange, onAuthChange, }: LBProviderProps): import("react/jsx-runtime").JSX.Element;
50
60
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAIR,QAAQ,EACT,MAAM,uBAAuB,CAAC;AAE/B,UAAU,eAAe;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IACzD,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,UAAU,cAAe,SAAQ,WAAW;IAC1C,4BAA4B;IAC5B,KAAK,EAAE,CACL,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,8BAA8B;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,6CAA6C;IAC7C,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3D,gEAAgE;IAChE,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,mDAAmD;IACnD,qBAAqB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,iEAAiE;IACjE,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,oCAAoC;IACpC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,2BAA2B;IAC3B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,yCAAyC;IACzC,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,sDAAsD;IACtD,eAAe,EAAE,OAAO,CAAC;CAC1B;AAID,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,EAAE,QAA2B,EACpC,QAA2B,EAC3B,cAAc,EACd,YAAY,GACb,EAAE,eAAe,2CA4djB;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,cAAc,CAMtC"}
1
+ {"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAIR,QAAQ,EACT,MAAM,uBAAuB,CAAC;AAE/B,UAAU,eAAe;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IACzD,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,UAAU,cAAe,SAAQ,WAAW;IAC1C,4BAA4B;IAC5B,KAAK,EAAE,CACL,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,8BAA8B;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,6CAA6C;IAC7C,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3D,gEAAgE;IAChE,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,mDAAmD;IACnD,qBAAqB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,iEAAiE;IACjE,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,oCAAoC;IACpC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,2BAA2B;IAC3B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,mEAAmE;IACnE,WAAW,EAAE,GAAG,CAAC;IACjB,uCAAuC;IACvC,aAAa,EAAE,GAAG,CAAC;IACnB,gDAAgD;IAChD,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,iEAAiE;IACjE,oBAAoB,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,kEAAkE;IAClE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,8DAA8D;IAC9D,eAAe,EAAE,OAAO,CAAC;IACzB,uDAAuD;IACvD,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAID,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,EAAE,QAA2B,EACpC,QAA2B,EAC3B,cAAc,EACd,YAAY,GACb,EAAE,eAAe,2CA+hBjB;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,cAAc,CAMtC"}
@@ -13,7 +13,11 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
13
13
  const [apiKeys, setApiKeys] = useState([]);
14
14
  const [accessToken, setAccessToken] = useState();
15
15
  const [apiStatus, setApiStatus] = useState(null);
16
+ const [basicStatus, setBasicStatus] = useState(null);
17
+ const [storageStatus, setStorageStatus] = useState(null);
16
18
  const [isLoadingStatus, setIsLoadingStatus] = useState(false);
19
+ const [isLoadingStorage, setIsLoadingStorage] = useState(false);
20
+ const [storageLastFetch, setStorageLastFetch] = useState(0);
17
21
  /**
18
22
  * Vérifie si une session existe au chargement
19
23
  */
@@ -257,35 +261,83 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
257
261
  }
258
262
  }, [proxyUrl, fetchApiKeys, selectApiKey]);
259
263
  /**
260
- * Récupère le status API (balance, storage, API key info)
264
+ * Récupère le status API basique (balance, API key info) - RAPIDE
261
265
  */
262
- const refreshStatus = useCallback(async () => {
266
+ const refreshBasicStatus = useCallback(async () => {
263
267
  if (state.status !== "ready") {
264
- setApiStatus(null);
268
+ setBasicStatus(null);
265
269
  setIsLoadingStatus(false);
266
270
  return;
267
271
  }
268
272
  setIsLoadingStatus(true);
269
273
  try {
270
- const response = await fetch(`${proxyUrl}/auth/status`, {
274
+ const response = await fetch(`${proxyUrl}/auth/status?fast=true`, {
271
275
  credentials: "include",
272
276
  });
273
277
  if (response.ok) {
274
278
  const data = await response.json();
275
- setApiStatus(data);
279
+ setBasicStatus(data);
280
+ // Combiner avec le storage existant si disponible
281
+ const combinedStatus = {
282
+ ...data,
283
+ storage: storageStatus?.storage || data.storage,
284
+ };
285
+ setApiStatus(combinedStatus);
276
286
  }
277
287
  else {
278
- setApiStatus(null);
288
+ setBasicStatus(null);
279
289
  }
280
290
  }
281
291
  catch (error) {
282
- console.error("[LBProvider] Failed to fetch status:", error);
283
- setApiStatus(null);
292
+ console.error("[LBProvider] Failed to fetch basic status:", error);
293
+ setBasicStatus(null);
284
294
  }
285
295
  finally {
286
296
  setIsLoadingStatus(false);
287
297
  }
288
- }, [proxyUrl, state.status]);
298
+ }, [proxyUrl, state.status, storageStatus]);
299
+ /**
300
+ * Récupère le storage - LENT avec cache (5 minutes)
301
+ */
302
+ const refreshStorageStatus = useCallback(async (force = false) => {
303
+ if (state.status !== "ready") {
304
+ setStorageStatus(null);
305
+ return;
306
+ }
307
+ // Cache de 5 minutes sauf si force=true
308
+ const now = Date.now();
309
+ const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
310
+ if (!force && storageLastFetch && (now - storageLastFetch) < CACHE_DURATION) {
311
+ console.log("[LBProvider] Storage cache still valid, skipping fetch");
312
+ return;
313
+ }
314
+ setIsLoadingStorage(true);
315
+ try {
316
+ const response = await fetch(`${proxyUrl}/auth/status/storage`, {
317
+ credentials: "include",
318
+ });
319
+ if (response.ok) {
320
+ const data = await response.json();
321
+ setStorageStatus(data);
322
+ setStorageLastFetch(now);
323
+ // Combiner avec le basic status
324
+ const combinedStatus = {
325
+ ...basicStatus,
326
+ storage: data.storage,
327
+ };
328
+ setApiStatus(combinedStatus);
329
+ }
330
+ else {
331
+ console.warn("[LBProvider] Failed to fetch storage status:", response.status);
332
+ }
333
+ }
334
+ catch (error) {
335
+ console.error("[LBProvider] Failed to fetch storage status:", error);
336
+ }
337
+ finally {
338
+ setIsLoadingStorage(false);
339
+ }
340
+ }, [proxyUrl, state.status, basicStatus, storageLastFetch]);
289
341
  /**
290
342
  * Sélectionne une clé API avec le token déjà stocké (après login)
291
343
  */
@@ -312,7 +364,9 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
312
364
  throw new Error(errorData.error || "Failed to switch API key");
313
365
  }
314
366
  // Refresh le status après le changement
315
- await refreshStatus();
367
+ await refreshBasicStatus();
368
+ // Refresh storage en arrière-plan
369
+ setTimeout(() => refreshStorageStatus(), 100);
316
370
  }
317
371
  else if (accessToken) {
318
372
  // Utiliser la méthode avec access token
@@ -321,7 +375,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
321
375
  else {
322
376
  throw new Error("No valid authentication method available");
323
377
  }
324
- }, [state.status, proxyUrl, accessToken, selectApiKey, refreshStatus]);
378
+ }, [state.status, proxyUrl, accessToken, selectApiKey, refreshBasicStatus, refreshStorageStatus]);
325
379
  /**
326
380
  * Déconnexion
327
381
  */
@@ -380,14 +434,19 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
380
434
  // Refresh status quand la session devient ready
381
435
  useEffect(() => {
382
436
  if (state.status === "ready") {
383
- refreshStatus();
437
+ // Appel rapide d'abord
438
+ refreshBasicStatus();
439
+ // Storage en arrière-plan après 100ms
440
+ setTimeout(() => refreshStorageStatus(), 100);
384
441
  fetchApiKeysWithSession(); // Also fetch API keys list
385
442
  }
386
443
  else {
387
444
  setApiStatus(null);
445
+ setBasicStatus(null);
446
+ setStorageStatus(null);
388
447
  setApiKeys([]);
389
448
  }
390
- }, [state.status, refreshStatus, fetchApiKeysWithSession]);
449
+ }, [state.status, refreshBasicStatus, refreshStorageStatus, fetchApiKeysWithSession]);
391
450
  const value = {
392
451
  ...state,
393
452
  login,
@@ -400,8 +459,13 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
400
459
  apiKeys,
401
460
  accessToken,
402
461
  apiStatus,
403
- refreshStatus,
462
+ basicStatus,
463
+ storageStatus,
464
+ refreshStatus: refreshBasicStatus, // backward compatibility
465
+ refreshBasicStatus,
466
+ refreshStorageStatus,
404
467
  isLoadingStatus,
468
+ isLoadingStorage,
405
469
  };
406
470
  return _jsx(LBContext.Provider, { value: value, children: children });
407
471
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/ai-ui-react",
3
- "version": "1.0.56",
3
+ "version": "1.0.58",
4
4
  "description": "Headless React components for LastBrain AI UI Kit",
5
5
  "private": false,
6
6
  "type": "module",
@@ -39,7 +39,10 @@ export function AiStatusButton({
39
39
  let selectApiKeyWithToken: ((apiKeyId: string) => Promise<void>) | undefined;
40
40
  let switchApiKey: ((apiKeyId: string) => Promise<void>) | undefined;
41
41
  let lbApiStatus: any = null;
42
+ let lbBasicStatus: any = null;
43
+ let lbStorageStatus: any = null;
42
44
  let lbIsLoadingStatus: boolean = false;
45
+ let lbIsLoadingStorage: boolean = false;
43
46
 
44
47
  try {
45
48
  const lbContext = useLB();
@@ -51,7 +54,10 @@ export function AiStatusButton({
51
54
  selectApiKeyWithToken = lbContext.selectApiKeyWithToken;
52
55
  switchApiKey = lbContext.switchApiKey;
53
56
  lbApiStatus = lbContext.apiStatus;
57
+ lbBasicStatus = lbContext.basicStatus;
58
+ lbStorageStatus = lbContext.storageStatus;
54
59
  lbIsLoadingStatus = lbContext.isLoadingStatus || false;
60
+ lbIsLoadingStorage = lbContext.isLoadingStorage || false;
55
61
  } catch {
56
62
  // LBProvider n'est pas disponible, ignorer
57
63
  lbStatus = undefined;
@@ -60,7 +66,12 @@ export function AiStatusButton({
60
66
  }
61
67
 
62
68
  // Utiliser le status du contexte LB si pas de prop status
63
- const effectiveStatus = status || lbApiStatus;
69
+ // Combinaison du basic status et storage status pour backward compatibility
70
+ const effectiveStatus = status || {
71
+ ...lbApiStatus,
72
+ ...lbBasicStatus,
73
+ storage: lbStorageStatus?.storage
74
+ };
64
75
 
65
76
  // Récupérer refetchProviders depuis AiProvider si disponible
66
77
  let refetchProviders: (() => Promise<void>) | undefined;
@@ -834,11 +845,36 @@ export function AiStatusButton({
834
845
  <div style={aiStyles.tooltipSubtitle}>Storage</div>
835
846
  <div style={aiStyles.tooltipRow}>
836
847
  <span style={aiStyles.tooltipLabel}>Total:</span>
837
- <span style={aiStyles.tooltipValue}>
838
- {formatStorage(storageUsed)} /{" "}
839
- {formatStorage(storageAllocated)}
840
- </span>
841
- {renderUsageCircle(storagePercentage)}
848
+ {lbIsLoadingStorage ? (
849
+ <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
850
+ <div
851
+ style={{
852
+ height: "16px",
853
+ width: "120px",
854
+ background: "rgba(139, 92, 246, 0.1)",
855
+ borderRadius: "4px",
856
+ animation: "pulse 2s ease-in-out infinite",
857
+ }}
858
+ />
859
+ <div
860
+ style={{
861
+ width: "28px",
862
+ height: "28px",
863
+ borderRadius: "50%",
864
+ background: "rgba(139, 92, 246, 0.1)",
865
+ animation: "pulse 2s ease-in-out infinite",
866
+ }}
867
+ />
868
+ </div>
869
+ ) : (
870
+ <>
871
+ <span style={aiStyles.tooltipValue}>
872
+ {formatStorage(storageUsed)} /{" "}
873
+ {formatStorage(storageAllocated)}
874
+ </span>
875
+ {renderUsageCircle(storagePercentage)}
876
+ </>
877
+ )}
842
878
  </div>
843
879
  </div>
844
880
 
@@ -32,7 +32,18 @@ export function LBConnectButton({
32
32
  const handleClick = () => {
33
33
  if (status === "ready" && user) {
34
34
  // Déjà connecté, proposer de se déconnecter
35
- logout();
35
+ logout().then(() => {
36
+ // Redirection propre après logout
37
+ if (typeof window !== "undefined") {
38
+ // Garder la langue actuelle de l'URL
39
+ const currentPath = window.location.pathname;
40
+ const langMatch = currentPath.match(/^\/([a-z]{2})\//);
41
+ const currentLang = langMatch ? langMatch[1] : "en";
42
+
43
+ // Rediriger vers la page d'accueil dans la langue actuelle
44
+ window.location.href = `/${currentLang}`;
45
+ }
46
+ });
36
47
  } else {
37
48
  // Pas connecté, ouvrir la modal
38
49
  setShowModal(true);
@@ -19,36 +19,23 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
19
19
  const [showKeySelector, setShowKeySelector] = useState(false);
20
20
  const [currentApiKeys, setCurrentApiKeys] = useState<any[]>([]); // Stocker les clés API localement
21
21
 
22
- // Vérifier si LBProvider est disponible
23
- let login:
24
- | ((
25
- email: string,
26
- password: string
27
- ) => Promise<{
28
- success: boolean;
29
- error?: string;
30
- needsKeySelection?: boolean;
31
- accessToken?: string;
32
- }>)
33
- | undefined;
34
- let selectApiKeyWithToken: ((apiKeyId: string) => Promise<void>) | undefined;
35
- let fetchApiKeys: ((accessToken: string) => Promise<any[]>) | undefined;
36
- let apiKeys: any[] = [];
37
- let lbStatus: string | undefined;
22
+ // Appeler useLB() inconditionnellement - hook doit toujours être appelé
23
+ const lbContext = useLB();
38
24
 
39
- let lbContext: any;
40
- try {
41
- lbContext = useLB();
42
- login = lbContext.login;
43
- selectApiKeyWithToken = lbContext.selectApiKeyWithToken;
44
- fetchApiKeys = lbContext.fetchApiKeys;
45
- apiKeys = lbContext.apiKeys || [];
46
- lbStatus = lbContext.status;
47
- } catch {
48
- // LBProvider n'est pas disponible, ne pas rendre le modal
25
+ // Vérifier si le contexte est valide
26
+ if (!lbContext || !isOpen) {
49
27
  return null;
50
28
  }
51
29
 
30
+ // Extraire les valeurs du contexte
31
+ const {
32
+ login,
33
+ selectApiKeyWithToken,
34
+ fetchApiKeys,
35
+ apiKeys = [],
36
+ status: lbStatus,
37
+ } = lbContext;
38
+
52
39
  if (!isOpen || !login) return null;
53
40
 
54
41
  const handleSubmit = async (e: React.FormEvent) => {
@@ -63,10 +63,20 @@ interface LBContextValue extends LBAuthState {
63
63
  accessToken?: string;
64
64
  /** Status API (balance, storage, API key info) */
65
65
  apiStatus: AiStatus | null;
66
- /** Fonction pour rafraîchir le status */
66
+ /** Status basique (rapide) - balance, API key info sans storage */
67
+ basicStatus: any;
68
+ /** Status storage (lent) avec cache */
69
+ storageStatus: any;
70
+ /** Fonction pour rafraîchir le status rapide */
71
+ refreshBasicStatus: () => Promise<void>;
72
+ /** Fonction pour rafraîchir le storage (avec cache optionnel) */
73
+ refreshStorageStatus: (force?: boolean) => Promise<void>;
74
+ /** Fonction pour rafraîchir le status (backward compatibility) */
67
75
  refreshStatus: () => Promise<void>;
68
- /** Indique si le status est en cours de chargement */
76
+ /** Indique si le status basique est en cours de chargement */
69
77
  isLoadingStatus: boolean;
78
+ /** Indique si le storage est en cours de chargement */
79
+ isLoadingStorage: boolean;
70
80
  }
71
81
 
72
82
  const LBContext = createContext<LBContextValue | undefined>(undefined);
@@ -84,7 +94,11 @@ export function LBProvider({
84
94
  const [apiKeys, setApiKeys] = useState<LBApiKey[]>([]);
85
95
  const [accessToken, setAccessToken] = useState<string>();
86
96
  const [apiStatus, setApiStatus] = useState<AiStatus | null>(null);
97
+ const [basicStatus, setBasicStatus] = useState<any>(null);
98
+ const [storageStatus, setStorageStatus] = useState<any>(null);
87
99
  const [isLoadingStatus, setIsLoadingStatus] = useState(false);
100
+ const [isLoadingStorage, setIsLoadingStorage] = useState(false);
101
+ const [storageLastFetch, setStorageLastFetch] = useState<number>(0);
88
102
 
89
103
  /**
90
104
  * Vérifie si une session existe au chargement
@@ -393,34 +407,85 @@ export function LBProvider({
393
407
  );
394
408
 
395
409
  /**
396
- * Récupère le status API (balance, storage, API key info)
410
+ * Récupère le status API basique (balance, API key info) - RAPIDE
397
411
  */
398
- const refreshStatus = useCallback(async (): Promise<void> => {
412
+ const refreshBasicStatus = useCallback(async (): Promise<void> => {
399
413
  if (state.status !== "ready") {
400
- setApiStatus(null);
414
+ setBasicStatus(null);
401
415
  setIsLoadingStatus(false);
402
416
  return;
403
417
  }
404
418
 
405
419
  setIsLoadingStatus(true);
406
420
  try {
407
- const response = await fetch(`${proxyUrl}/auth/status`, {
421
+ const response = await fetch(`${proxyUrl}/auth/status?fast=true`, {
408
422
  credentials: "include",
409
423
  });
410
424
 
411
425
  if (response.ok) {
412
426
  const data = await response.json();
413
- setApiStatus(data);
427
+ setBasicStatus(data);
428
+
429
+ // Combiner avec le storage existant si disponible
430
+ const combinedStatus = {
431
+ ...data,
432
+ storage: storageStatus?.storage || data.storage,
433
+ };
434
+ setApiStatus(combinedStatus);
414
435
  } else {
415
- setApiStatus(null);
436
+ setBasicStatus(null);
416
437
  }
417
438
  } catch (error) {
418
- console.error("[LBProvider] Failed to fetch status:", error);
419
- setApiStatus(null);
439
+ console.error("[LBProvider] Failed to fetch basic status:", error);
440
+ setBasicStatus(null);
420
441
  } finally {
421
442
  setIsLoadingStatus(false);
422
443
  }
423
- }, [proxyUrl, state.status]);
444
+ }, [proxyUrl, state.status, storageStatus]);
445
+
446
+ /**
447
+ * Récupère le storage - LENT avec cache (5 minutes)
448
+ */
449
+ const refreshStorageStatus = useCallback(async (force = false): Promise<void> => {
450
+ if (state.status !== "ready") {
451
+ setStorageStatus(null);
452
+ return;
453
+ }
454
+
455
+ // Cache de 5 minutes sauf si force=true
456
+ const now = Date.now();
457
+ const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
458
+ if (!force && storageLastFetch && (now - storageLastFetch) < CACHE_DURATION) {
459
+ console.log("[LBProvider] Storage cache still valid, skipping fetch");
460
+ return;
461
+ }
462
+
463
+ setIsLoadingStorage(true);
464
+ try {
465
+ const response = await fetch(`${proxyUrl}/auth/status/storage`, {
466
+ credentials: "include",
467
+ });
468
+
469
+ if (response.ok) {
470
+ const data = await response.json();
471
+ setStorageStatus(data);
472
+ setStorageLastFetch(now);
473
+
474
+ // Combiner avec le basic status
475
+ const combinedStatus = {
476
+ ...basicStatus,
477
+ storage: data.storage,
478
+ };
479
+ setApiStatus(combinedStatus);
480
+ } else {
481
+ console.warn("[LBProvider] Failed to fetch storage status:", response.status);
482
+ }
483
+ } catch (error) {
484
+ console.error("[LBProvider] Failed to fetch storage status:", error);
485
+ } finally {
486
+ setIsLoadingStorage(false);
487
+ }
488
+ }, [proxyUrl, state.status, basicStatus, storageLastFetch]);
424
489
 
425
490
  /**
426
491
  * Sélectionne une clé API avec le token déjà stocké (après login)
@@ -458,7 +523,9 @@ export function LBProvider({
458
523
  }
459
524
 
460
525
  // Refresh le status après le changement
461
- await refreshStatus();
526
+ await refreshBasicStatus();
527
+ // Refresh storage en arrière-plan
528
+ setTimeout(() => refreshStorageStatus(), 100);
462
529
  } else if (accessToken) {
463
530
  // Utiliser la méthode avec access token
464
531
  await selectApiKey(accessToken, apiKeyId);
@@ -466,7 +533,7 @@ export function LBProvider({
466
533
  throw new Error("No valid authentication method available");
467
534
  }
468
535
  },
469
- [state.status, proxyUrl, accessToken, selectApiKey, refreshStatus]
536
+ [state.status, proxyUrl, accessToken, selectApiKey, refreshBasicStatus, refreshStorageStatus]
470
537
  );
471
538
 
472
539
  /**
@@ -528,13 +595,18 @@ export function LBProvider({
528
595
  // Refresh status quand la session devient ready
529
596
  useEffect(() => {
530
597
  if (state.status === "ready") {
531
- refreshStatus();
598
+ // Appel rapide d'abord
599
+ refreshBasicStatus();
600
+ // Storage en arrière-plan après 100ms
601
+ setTimeout(() => refreshStorageStatus(), 100);
532
602
  fetchApiKeysWithSession(); // Also fetch API keys list
533
603
  } else {
534
604
  setApiStatus(null);
605
+ setBasicStatus(null);
606
+ setStorageStatus(null);
535
607
  setApiKeys([]);
536
608
  }
537
- }, [state.status, refreshStatus, fetchApiKeysWithSession]);
609
+ }, [state.status, refreshBasicStatus, refreshStorageStatus, fetchApiKeysWithSession]);
538
610
 
539
611
  const value: LBContextValue = {
540
612
  ...state,
@@ -548,8 +620,13 @@ export function LBProvider({
548
620
  apiKeys,
549
621
  accessToken,
550
622
  apiStatus,
551
- refreshStatus,
623
+ basicStatus,
624
+ storageStatus,
625
+ refreshStatus: refreshBasicStatus, // backward compatibility
626
+ refreshBasicStatus,
627
+ refreshStorageStatus,
552
628
  isLoadingStatus,
629
+ isLoadingStorage,
553
630
  };
554
631
 
555
632
  return <LBContext.Provider value={value}>{children}</LBContext.Provider>;