@lastbrain/ai-ui-react 1.0.57 → 1.0.59

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,2CAolCrB"}
@@ -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,23 @@ 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: {
438
+ display: "flex",
439
+ alignItems: "center",
440
+ gap: "8px",
441
+ }, children: [_jsx("div", { style: {
442
+ height: "16px",
443
+ width: "120px",
444
+ background: "rgba(139, 92, 246, 0.1)",
445
+ borderRadius: "4px",
446
+ animation: "pulse 2s ease-in-out infinite",
447
+ } }), _jsx("div", { style: {
448
+ width: "28px",
449
+ height: "28px",
450
+ borderRadius: "50%",
451
+ background: "rgba(139, 92, 246, 0.1)",
452
+ animation: "pulse 2s ease-in-out infinite",
453
+ } })] })) : (_jsxs(_Fragment, { children: [_jsxs("span", { style: aiStyles.tooltipValue, children: [formatStorage(storageUsed), " /", " ", formatStorage(storageAllocated)] }), renderUsageCircle(storagePercentage)] }))] })] }), _jsxs("div", { style: {
427
454
  ...aiStyles.tooltipActions,
428
455
  width: "100%",
429
456
  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
@@ -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,2CAskBjB;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,99 @@ 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 &&
311
+ storageLastFetch &&
312
+ now - storageLastFetch < CACHE_DURATION) {
313
+ console.log("[LBProvider] Storage cache still valid, skipping fetch");
314
+ return;
315
+ }
316
+ setIsLoadingStorage(true);
317
+ try {
318
+ // Essayer l'endpoint spécialisé storage d'abord
319
+ let response = await fetch(`${proxyUrl}/auth/status/storage`, {
320
+ credentials: "include",
321
+ });
322
+ // Si 404, faire un fallback vers l'endpoint normal (backward compatibility)
323
+ if (!response.ok && response.status === 404) {
324
+ console.log("[LBProvider] Storage endpoint not available, using fallback");
325
+ response = await fetch(`${proxyUrl}/auth/status`, {
326
+ credentials: "include",
327
+ });
328
+ }
329
+ if (response.ok) {
330
+ const data = await response.json();
331
+ // Si c'est la réponse complète (fallback), extraire juste le storage
332
+ const storageData = data.storage ? { storage: data.storage } : data;
333
+ setStorageStatus(storageData);
334
+ setStorageLastFetch(now);
335
+ // Combiner avec le basic status
336
+ const combinedStatus = {
337
+ ...basicStatus,
338
+ storage: storageData.storage,
339
+ };
340
+ setApiStatus(combinedStatus);
341
+ }
342
+ else {
343
+ console.warn("[LBProvider] Failed to fetch storage status:", response.status);
344
+ // Arrêter les tentatives répétées si échec persistant
345
+ setStorageLastFetch(now); // Marquer comme essayé pour éviter la boucle
346
+ }
347
+ }
348
+ catch (error) {
349
+ console.error("[LBProvider] Failed to fetch storage status:", error);
350
+ // Arrêter les tentatives répétées si erreur persistante
351
+ setStorageLastFetch(now); // Marquer comme essayé pour éviter la boucle
352
+ }
353
+ finally {
354
+ setIsLoadingStorage(false);
355
+ }
356
+ }, [proxyUrl, state.status, basicStatus, storageLastFetch]);
289
357
  /**
290
358
  * Sélectionne une clé API avec le token déjà stocké (après login)
291
359
  */
@@ -312,7 +380,9 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
312
380
  throw new Error(errorData.error || "Failed to switch API key");
313
381
  }
314
382
  // Refresh le status après le changement
315
- await refreshStatus();
383
+ await refreshBasicStatus();
384
+ // Refresh storage en arrière-plan
385
+ setTimeout(() => refreshStorageStatus(), 100);
316
386
  }
317
387
  else if (accessToken) {
318
388
  // Utiliser la méthode avec access token
@@ -321,7 +391,14 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
321
391
  else {
322
392
  throw new Error("No valid authentication method available");
323
393
  }
324
- }, [state.status, proxyUrl, accessToken, selectApiKey, refreshStatus]);
394
+ }, [
395
+ state.status,
396
+ proxyUrl,
397
+ accessToken,
398
+ selectApiKey,
399
+ refreshBasicStatus,
400
+ refreshStorageStatus,
401
+ ]);
325
402
  /**
326
403
  * Déconnexion
327
404
  */
@@ -380,14 +457,24 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
380
457
  // Refresh status quand la session devient ready
381
458
  useEffect(() => {
382
459
  if (state.status === "ready") {
383
- refreshStatus();
460
+ // Appel rapide d'abord
461
+ refreshBasicStatus();
462
+ // Storage en arrière-plan après 100ms
463
+ setTimeout(() => refreshStorageStatus(), 100);
384
464
  fetchApiKeysWithSession(); // Also fetch API keys list
385
465
  }
386
466
  else {
387
467
  setApiStatus(null);
468
+ setBasicStatus(null);
469
+ setStorageStatus(null);
388
470
  setApiKeys([]);
389
471
  }
390
- }, [state.status, refreshStatus, fetchApiKeysWithSession]);
472
+ }, [
473
+ state.status,
474
+ refreshBasicStatus,
475
+ refreshStorageStatus,
476
+ fetchApiKeysWithSession,
477
+ ]);
391
478
  const value = {
392
479
  ...state,
393
480
  login,
@@ -400,8 +487,13 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
400
487
  apiKeys,
401
488
  accessToken,
402
489
  apiStatus,
403
- refreshStatus,
490
+ basicStatus,
491
+ storageStatus,
492
+ refreshStatus: refreshBasicStatus, // backward compatibility
493
+ refreshBasicStatus,
494
+ refreshStorageStatus,
404
495
  isLoadingStatus,
496
+ isLoadingStorage,
405
497
  };
406
498
  return _jsx(LBContext.Provider, { value: value, children: children });
407
499
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/ai-ui-react",
3
- "version": "1.0.57",
3
+ "version": "1.0.59",
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,42 @@ 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
850
+ style={{
851
+ display: "flex",
852
+ alignItems: "center",
853
+ gap: "8px",
854
+ }}
855
+ >
856
+ <div
857
+ style={{
858
+ height: "16px",
859
+ width: "120px",
860
+ background: "rgba(139, 92, 246, 0.1)",
861
+ borderRadius: "4px",
862
+ animation: "pulse 2s ease-in-out infinite",
863
+ }}
864
+ />
865
+ <div
866
+ style={{
867
+ width: "28px",
868
+ height: "28px",
869
+ borderRadius: "50%",
870
+ background: "rgba(139, 92, 246, 0.1)",
871
+ animation: "pulse 2s ease-in-out infinite",
872
+ }}
873
+ />
874
+ </div>
875
+ ) : (
876
+ <>
877
+ <span style={aiStyles.tooltipValue}>
878
+ {formatStorage(storageUsed)} /{" "}
879
+ {formatStorage(storageAllocated)}
880
+ </span>
881
+ {renderUsageCircle(storagePercentage)}
882
+ </>
883
+ )}
842
884
  </div>
843
885
  </div>
844
886
 
@@ -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);
@@ -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,112 @@ 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(
450
+ async (force = false): Promise<void> => {
451
+ if (state.status !== "ready") {
452
+ setStorageStatus(null);
453
+ return;
454
+ }
455
+
456
+ // Cache de 5 minutes sauf si force=true
457
+ const now = Date.now();
458
+ const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
459
+ if (
460
+ !force &&
461
+ storageLastFetch &&
462
+ now - storageLastFetch < CACHE_DURATION
463
+ ) {
464
+ console.log("[LBProvider] Storage cache still valid, skipping fetch");
465
+ return;
466
+ }
467
+
468
+ setIsLoadingStorage(true);
469
+ try {
470
+ // Essayer l'endpoint spécialisé storage d'abord
471
+ let response = await fetch(`${proxyUrl}/auth/status/storage`, {
472
+ credentials: "include",
473
+ });
474
+
475
+ // Si 404, faire un fallback vers l'endpoint normal (backward compatibility)
476
+ if (!response.ok && response.status === 404) {
477
+ console.log("[LBProvider] Storage endpoint not available, using fallback");
478
+ response = await fetch(`${proxyUrl}/auth/status`, {
479
+ credentials: "include",
480
+ });
481
+ }
482
+
483
+ if (response.ok) {
484
+ const data = await response.json();
485
+
486
+ // Si c'est la réponse complète (fallback), extraire juste le storage
487
+ const storageData = data.storage ? { storage: data.storage } : data;
488
+
489
+ setStorageStatus(storageData);
490
+ setStorageLastFetch(now);
491
+
492
+ // Combiner avec le basic status
493
+ const combinedStatus = {
494
+ ...basicStatus,
495
+ storage: storageData.storage,
496
+ };
497
+ setApiStatus(combinedStatus);
498
+ } else {
499
+ console.warn(
500
+ "[LBProvider] Failed to fetch storage status:",
501
+ response.status
502
+ );
503
+ // Arrêter les tentatives répétées si échec persistant
504
+ setStorageLastFetch(now); // Marquer comme essayé pour éviter la boucle
505
+ }
506
+ } catch (error) {
507
+ console.error("[LBProvider] Failed to fetch storage status:", error);
508
+ // Arrêter les tentatives répétées si erreur persistante
509
+ setStorageLastFetch(now); // Marquer comme essayé pour éviter la boucle
510
+ } finally {
511
+ setIsLoadingStorage(false);
512
+ }
513
+ },
514
+ [proxyUrl, state.status, basicStatus, storageLastFetch]
515
+ );
424
516
 
425
517
  /**
426
518
  * Sélectionne une clé API avec le token déjà stocké (après login)
@@ -458,7 +550,9 @@ export function LBProvider({
458
550
  }
459
551
 
460
552
  // Refresh le status après le changement
461
- await refreshStatus();
553
+ await refreshBasicStatus();
554
+ // Refresh storage en arrière-plan
555
+ setTimeout(() => refreshStorageStatus(), 100);
462
556
  } else if (accessToken) {
463
557
  // Utiliser la méthode avec access token
464
558
  await selectApiKey(accessToken, apiKeyId);
@@ -466,7 +560,14 @@ export function LBProvider({
466
560
  throw new Error("No valid authentication method available");
467
561
  }
468
562
  },
469
- [state.status, proxyUrl, accessToken, selectApiKey, refreshStatus]
563
+ [
564
+ state.status,
565
+ proxyUrl,
566
+ accessToken,
567
+ selectApiKey,
568
+ refreshBasicStatus,
569
+ refreshStorageStatus,
570
+ ]
470
571
  );
471
572
 
472
573
  /**
@@ -528,13 +629,23 @@ export function LBProvider({
528
629
  // Refresh status quand la session devient ready
529
630
  useEffect(() => {
530
631
  if (state.status === "ready") {
531
- refreshStatus();
632
+ // Appel rapide d'abord
633
+ refreshBasicStatus();
634
+ // Storage en arrière-plan après 100ms
635
+ setTimeout(() => refreshStorageStatus(), 100);
532
636
  fetchApiKeysWithSession(); // Also fetch API keys list
533
637
  } else {
534
638
  setApiStatus(null);
639
+ setBasicStatus(null);
640
+ setStorageStatus(null);
535
641
  setApiKeys([]);
536
642
  }
537
- }, [state.status, refreshStatus, fetchApiKeysWithSession]);
643
+ }, [
644
+ state.status,
645
+ refreshBasicStatus,
646
+ refreshStorageStatus,
647
+ fetchApiKeysWithSession,
648
+ ]);
538
649
 
539
650
  const value: LBContextValue = {
540
651
  ...state,
@@ -548,8 +659,13 @@ export function LBProvider({
548
659
  apiKeys,
549
660
  accessToken,
550
661
  apiStatus,
551
- refreshStatus,
662
+ basicStatus,
663
+ storageStatus,
664
+ refreshStatus: refreshBasicStatus, // backward compatibility
665
+ refreshBasicStatus,
666
+ refreshStorageStatus,
552
667
  isLoadingStatus,
668
+ isLoadingStorage,
553
669
  };
554
670
 
555
671
  return <LBContext.Provider value={value}>{children}</LBContext.Provider>;