@lastbrain/ai-ui-react 1.0.65 → 1.0.66

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,2CAssCrB"}
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,2CAm0CrB"}
@@ -118,6 +118,10 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
118
118
  (storageAllocated > 0
119
119
  ? Math.round((storageUsed / storageAllocated) * 100)
120
120
  : 0);
121
+ const showFastStatusSkeleton = lbStatus === "ready" &&
122
+ (lbIsLoadingStatus || isLoadingStatus) &&
123
+ !lbBasicStatus &&
124
+ !effectiveStatus;
121
125
  const [showTooltip, setShowTooltip] = useState(false);
122
126
  const [isHovered, setIsHovered] = useState(false);
123
127
  const [tooltipPosition, setTooltipPosition] = useState({});
@@ -232,9 +236,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
232
236
  lbRefreshBasicStatus,
233
237
  lbRefreshStorageStatus,
234
238
  ]);
235
- if (loading ||
236
- isSelectingApiKey ||
237
- ((isLoadingStatus || lbIsLoadingStatus) && !effectiveStatus)) {
239
+ if (loading || isSelectingApiKey) {
238
240
  return (_jsx("button", { ref: buttonRef, style: {
239
241
  ...aiStyles.statusButton,
240
242
  ...aiStyles.statusButtonDisabled,
@@ -293,10 +295,40 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
293
295
  ...aiStyles.tooltip,
294
296
  ...tooltipPosition,
295
297
  zIndex: 50,
296
- }, onMouseEnter: () => setShowTooltip(true), onMouseLeave: handleMouseLeave, children: lbStatus === "ready" && user ? (_jsxs(_Fragment, { children: [_jsx("div", { style: aiStyles.tooltipHeader, children: "LastBrain Connected" }), _jsx("div", { style: {
298
+ }, onMouseEnter: () => setShowTooltip(true), onMouseLeave: handleMouseLeave, children: lbStatus === "ready" && user ? (_jsxs(_Fragment, { children: [_jsx("div", { style: aiStyles.tooltipHeader, children: "API Status" }), _jsx("div", { style: {
297
299
  ...aiStyles.tooltipSection,
298
- paddingBottom: "8px",
299
- }, children: _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "User:" }), _jsx("span", { style: aiStyles.tooltipValue, children: user.email })] }) }), _jsxs("div", { style: {
300
+ ...aiStyles.tooltipSectionFirst,
301
+ }, children: _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "User:" }), _jsx("span", { style: aiStyles.tooltipValue, children: user.email })] }) }), showFastStatusSkeleton && (_jsxs(_Fragment, { children: [_jsxs("div", { style: aiStyles.tooltipSection, children: [_jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "API Key:" }), _jsx("div", { style: {
302
+ height: "16px",
303
+ width: "110px",
304
+ background: "rgba(139, 92, 246, 0.12)",
305
+ borderRadius: "4px",
306
+ animation: "pulse 2s ease-in-out infinite",
307
+ } })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Env:" }), _jsx("div", { style: {
308
+ height: "16px",
309
+ width: "48px",
310
+ background: "rgba(139, 92, 246, 0.12)",
311
+ borderRadius: "4px",
312
+ animation: "pulse 2s ease-in-out infinite",
313
+ } })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }), _jsx("div", { style: {
314
+ height: "16px",
315
+ width: "84px",
316
+ background: "rgba(139, 92, 246, 0.12)",
317
+ borderRadius: "4px",
318
+ animation: "pulse 2s ease-in-out infinite",
319
+ } })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Auth:" }), _jsx("div", { style: {
320
+ height: "16px",
321
+ width: "72px",
322
+ background: "rgba(139, 92, 246, 0.12)",
323
+ borderRadius: "4px",
324
+ animation: "pulse 2s ease-in-out infinite",
325
+ } })] })] }), _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:" }), _jsx("div", { style: {
326
+ height: "16px",
327
+ width: "120px",
328
+ background: "rgba(139, 92, 246, 0.12)",
329
+ borderRadius: "4px",
330
+ animation: "pulse 2s ease-in-out infinite",
331
+ } })] })] })] })), _jsxs("div", { style: {
300
332
  display: "flex",
301
333
  gap: "8px",
302
334
  borderTop: "1px solid var(--ai-border-primary, #374151)",
@@ -497,13 +529,43 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
497
529
  "rgba(139, 92, 246, 0.1)";
498
530
  e.currentTarget.style.borderColor =
499
531
  "rgba(139, 92, 246, 0.3)";
500
- }, title: "Change API Key", children: _jsx(ArrowRightLeft, { size: 12, style: { color: "rgba(139, 92, 246, 1)" } }) }))] })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Env:" }), _jsx("span", { style: aiStyles.tooltipValue, children: effectiveStatus.apiKey?.env ||
532
+ }, title: "Change API Key", children: _jsx(ArrowRightLeft, { size: 12, style: { color: "rgba(139, 92, 246, 1)" } }) }))] })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Env:" }), lbIsLoadingStatus && !effectiveStatus?.apiKey?.env ? (_jsx("div", { style: {
533
+ height: "16px",
534
+ width: "48px",
535
+ background: "rgba(139, 92, 246, 0.1)",
536
+ borderRadius: "4px",
537
+ animation: "pulse 2s ease-in-out infinite",
538
+ } })) : (_jsx("span", { style: aiStyles.tooltipValue, children: effectiveStatus.apiKey?.env ||
501
539
  effectiveStatus.api_key?.env ||
502
- "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 ||
540
+ "N/A" }))] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }), lbIsLoadingStatus &&
541
+ !effectiveStatus?.apiKey?.rate_limit_rpm &&
542
+ !effectiveStatus?.api_key?.rate_limit_rpm ? (_jsx("div", { style: {
543
+ height: "16px",
544
+ width: "92px",
545
+ background: "rgba(139, 92, 246, 0.1)",
546
+ borderRadius: "4px",
547
+ animation: "pulse 2s ease-in-out infinite",
548
+ } })) : (_jsxs("span", { style: aiStyles.tooltipValue, children: [effectiveStatus.apiKey?.rate_limit_rpm ||
503
549
  effectiveStatus.api_key?.rate_limit_rpm ||
504
- 0, " ", "req/min"] })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Auth:" }), _jsx("span", { style: aiStyles.tooltipValue, children: lbIsLoadingStatus
550
+ 0, " ", "req/min"] }))] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Auth:" }), _jsx("span", { style: aiStyles.tooltipValue, children: lbIsLoadingStatus
505
551
  ? "..."
506
- : effectiveStatus?.authType || lbStatus || "unknown" })] })] }), _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, 2), " / $", formatFixed(balanceTotal, 2)] }), 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: {
552
+ : effectiveStatus?.authType || lbStatus || "unknown" })] })] }), _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:" }), lbIsLoadingStatus && !effectiveStatus?.balance ? (_jsxs("div", { style: {
553
+ display: "flex",
554
+ alignItems: "center",
555
+ gap: "8px",
556
+ }, children: [_jsx("div", { style: {
557
+ height: "16px",
558
+ width: "120px",
559
+ background: "rgba(139, 92, 246, 0.1)",
560
+ borderRadius: "4px",
561
+ animation: "pulse 2s ease-in-out infinite",
562
+ } }), _jsx("div", { style: {
563
+ width: "28px",
564
+ height: "28px",
565
+ borderRadius: "50%",
566
+ background: "rgba(139, 92, 246, 0.1)",
567
+ animation: "pulse 2s ease-in-out infinite",
568
+ } })] })) : (_jsxs(_Fragment, { children: [_jsxs("span", { style: aiStyles.tooltipValue, children: ["$", formatFixed(balanceUsed, 2), " / $", formatFixed(balanceTotal, 2)] }), 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: {
507
569
  display: "flex",
508
570
  alignItems: "center",
509
571
  gap: "8px",
@@ -1 +1 @@
1
- {"version":3,"file":"useAiModels.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiModels.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,kBAAkB,CAAC;CAC1E;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CA8F3E"}
1
+ {"version":3,"file":"useAiModels.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiModels.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,kBAAkB,CAAC;CAC1E;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CA4F3E"}
@@ -7,10 +7,9 @@ import { useAiContext } from "../context/AiProvider";
7
7
  */
8
8
  export function useAiModels(options) {
9
9
  const context = useAiContext();
10
- // Si les options ne correspondent pas au contexte, on peut faire un appel direct
11
- // Mais dans la plupart des cas, on utilise le contexte
12
- const useContextData = (!options?.baseUrl || options.baseUrl === context.baseUrl) &&
13
- (!options?.apiKeyId || options.apiKeyId === context.apiKeyId);
10
+ // Utiliser le contexte dès que la base URL correspond.
11
+ // L'apiKeyId peut varier (session/api-key sélectionnée) sans invalider le contexte.
12
+ const useContextData = !options?.baseUrl || options.baseUrl === context.baseUrl;
14
13
  // Filtrer les modèles selon le type demandé
15
14
  const filteredModels = useMemo(() => {
16
15
  console.log("[useAiModels] Filtering models:", {
@@ -10,9 +10,10 @@ export function useModelManagement(options = {}) {
10
10
  const context = useAiContext();
11
11
  const [loading, setLoading] = useState(false);
12
12
  const [error, setError] = useState(null);
13
- // Utiliser les données du contexte si disponibles
14
- const useContextData = (!options.baseUrl || options.baseUrl === context.baseUrl) &&
15
- (!options.apiKey || options.apiKey === context.apiKeyId);
13
+ // Utiliser les données du contexte si la base URL correspond.
14
+ // L'apiKey peut changer selon le mode d'auth (lb_session/supabase/api_key) sans
15
+ // devoir casser le cache/provider de modèles déjà présent.
16
+ const useContextData = !options.baseUrl || options.baseUrl === context.baseUrl;
16
17
  // Filtrer par catégorie si nécessaire
17
18
  const filteredModels = useMemo(() => {
18
19
  if (!useContextData)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/ai-ui-react",
3
- "version": "1.0.65",
3
+ "version": "1.0.66",
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.49"
51
+ "@lastbrain/ai-ui-core": "1.0.50"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/react": "^19.2.0",
@@ -207,6 +207,11 @@ export function AiStatusButton({
207
207
  (storageAllocated > 0
208
208
  ? Math.round((storageUsed / storageAllocated) * 100)
209
209
  : 0);
210
+ const showFastStatusSkeleton =
211
+ lbStatus === "ready" &&
212
+ (lbIsLoadingStatus || isLoadingStatus) &&
213
+ !lbBasicStatus &&
214
+ !effectiveStatus;
210
215
 
211
216
  const [showTooltip, setShowTooltip] = useState(false);
212
217
  const [isHovered, setIsHovered] = useState(false);
@@ -345,11 +350,7 @@ export function AiStatusButton({
345
350
  lbRefreshStorageStatus,
346
351
  ]);
347
352
 
348
- if (
349
- loading ||
350
- isSelectingApiKey ||
351
- ((isLoadingStatus || lbIsLoadingStatus) && !effectiveStatus)
352
- ) {
353
+ if (loading || isSelectingApiKey) {
353
354
  return (
354
355
  <button
355
356
  ref={buttonRef}
@@ -518,13 +519,11 @@ export function AiStatusButton({
518
519
  >
519
520
  {lbStatus === "ready" && user ? (
520
521
  <>
521
- <div style={aiStyles.tooltipHeader}>
522
- LastBrain Connected
523
- </div>
522
+ <div style={aiStyles.tooltipHeader}>API Status</div>
524
523
  <div
525
524
  style={{
526
525
  ...aiStyles.tooltipSection,
527
- paddingBottom: "8px",
526
+ ...aiStyles.tooltipSectionFirst,
528
527
  }}
529
528
  >
530
529
  <div style={aiStyles.tooltipRow}>
@@ -532,6 +531,75 @@ export function AiStatusButton({
532
531
  <span style={aiStyles.tooltipValue}>{user.email}</span>
533
532
  </div>
534
533
  </div>
534
+ {showFastStatusSkeleton && (
535
+ <>
536
+ <div style={aiStyles.tooltipSection}>
537
+ <div style={aiStyles.tooltipRow}>
538
+ <span style={aiStyles.tooltipLabel}>API Key:</span>
539
+ <div
540
+ style={{
541
+ height: "16px",
542
+ width: "110px",
543
+ background: "rgba(139, 92, 246, 0.12)",
544
+ borderRadius: "4px",
545
+ animation: "pulse 2s ease-in-out infinite",
546
+ }}
547
+ />
548
+ </div>
549
+ <div style={aiStyles.tooltipRow}>
550
+ <span style={aiStyles.tooltipLabel}>Env:</span>
551
+ <div
552
+ style={{
553
+ height: "16px",
554
+ width: "48px",
555
+ background: "rgba(139, 92, 246, 0.12)",
556
+ borderRadius: "4px",
557
+ animation: "pulse 2s ease-in-out infinite",
558
+ }}
559
+ />
560
+ </div>
561
+ <div style={aiStyles.tooltipRow}>
562
+ <span style={aiStyles.tooltipLabel}>Rate Limit:</span>
563
+ <div
564
+ style={{
565
+ height: "16px",
566
+ width: "84px",
567
+ background: "rgba(139, 92, 246, 0.12)",
568
+ borderRadius: "4px",
569
+ animation: "pulse 2s ease-in-out infinite",
570
+ }}
571
+ />
572
+ </div>
573
+ <div style={aiStyles.tooltipRow}>
574
+ <span style={aiStyles.tooltipLabel}>Auth:</span>
575
+ <div
576
+ style={{
577
+ height: "16px",
578
+ width: "72px",
579
+ background: "rgba(139, 92, 246, 0.12)",
580
+ borderRadius: "4px",
581
+ animation: "pulse 2s ease-in-out infinite",
582
+ }}
583
+ />
584
+ </div>
585
+ </div>
586
+ <div style={aiStyles.tooltipSection}>
587
+ <div style={aiStyles.tooltipSubtitle}>Wallet</div>
588
+ <div style={aiStyles.tooltipRow}>
589
+ <span style={aiStyles.tooltipLabel}>Total:</span>
590
+ <div
591
+ style={{
592
+ height: "16px",
593
+ width: "120px",
594
+ background: "rgba(139, 92, 246, 0.12)",
595
+ borderRadius: "4px",
596
+ animation: "pulse 2s ease-in-out infinite",
597
+ }}
598
+ />
599
+ </div>
600
+ </div>
601
+ </>
602
+ )}
535
603
  <div
536
604
  style={{
537
605
  display: "flex",
@@ -919,20 +987,46 @@ export function AiStatusButton({
919
987
  </div>
920
988
  <div style={aiStyles.tooltipRow}>
921
989
  <span style={aiStyles.tooltipLabel}>Env:</span>
922
- <span style={aiStyles.tooltipValue}>
923
- {effectiveStatus.apiKey?.env ||
924
- effectiveStatus.api_key?.env ||
925
- "N/A"}
926
- </span>
990
+ {lbIsLoadingStatus && !effectiveStatus?.apiKey?.env ? (
991
+ <div
992
+ style={{
993
+ height: "16px",
994
+ width: "48px",
995
+ background: "rgba(139, 92, 246, 0.1)",
996
+ borderRadius: "4px",
997
+ animation: "pulse 2s ease-in-out infinite",
998
+ }}
999
+ />
1000
+ ) : (
1001
+ <span style={aiStyles.tooltipValue}>
1002
+ {effectiveStatus.apiKey?.env ||
1003
+ effectiveStatus.api_key?.env ||
1004
+ "N/A"}
1005
+ </span>
1006
+ )}
927
1007
  </div>
928
1008
  <div style={aiStyles.tooltipRow}>
929
1009
  <span style={aiStyles.tooltipLabel}>Rate Limit:</span>
930
- <span style={aiStyles.tooltipValue}>
931
- {effectiveStatus.apiKey?.rate_limit_rpm ||
932
- effectiveStatus.api_key?.rate_limit_rpm ||
933
- 0}{" "}
934
- req/min
935
- </span>
1010
+ {lbIsLoadingStatus &&
1011
+ !effectiveStatus?.apiKey?.rate_limit_rpm &&
1012
+ !effectiveStatus?.api_key?.rate_limit_rpm ? (
1013
+ <div
1014
+ style={{
1015
+ height: "16px",
1016
+ width: "92px",
1017
+ background: "rgba(139, 92, 246, 0.1)",
1018
+ borderRadius: "4px",
1019
+ animation: "pulse 2s ease-in-out infinite",
1020
+ }}
1021
+ />
1022
+ ) : (
1023
+ <span style={aiStyles.tooltipValue}>
1024
+ {effectiveStatus.apiKey?.rate_limit_rpm ||
1025
+ effectiveStatus.api_key?.rate_limit_rpm ||
1026
+ 0}{" "}
1027
+ req/min
1028
+ </span>
1029
+ )}
936
1030
  </div>
937
1031
  <div style={aiStyles.tooltipRow}>
938
1032
  <span style={aiStyles.tooltipLabel}>Auth:</span>
@@ -948,10 +1042,41 @@ export function AiStatusButton({
948
1042
  <div style={aiStyles.tooltipSubtitle}>Wallet</div>
949
1043
  <div style={aiStyles.tooltipRow}>
950
1044
  <span style={aiStyles.tooltipLabel}>Total:</span>
951
- <span style={aiStyles.tooltipValue}>
952
- ${formatFixed(balanceUsed, 2)} / ${formatFixed(balanceTotal, 2)}
953
- </span>
954
- {renderUsageCircle(balancePercentage)}
1045
+ {lbIsLoadingStatus && !effectiveStatus?.balance ? (
1046
+ <div
1047
+ style={{
1048
+ display: "flex",
1049
+ alignItems: "center",
1050
+ gap: "8px",
1051
+ }}
1052
+ >
1053
+ <div
1054
+ style={{
1055
+ height: "16px",
1056
+ width: "120px",
1057
+ background: "rgba(139, 92, 246, 0.1)",
1058
+ borderRadius: "4px",
1059
+ animation: "pulse 2s ease-in-out infinite",
1060
+ }}
1061
+ />
1062
+ <div
1063
+ style={{
1064
+ width: "28px",
1065
+ height: "28px",
1066
+ borderRadius: "50%",
1067
+ background: "rgba(139, 92, 246, 0.1)",
1068
+ animation: "pulse 2s ease-in-out infinite",
1069
+ }}
1070
+ />
1071
+ </div>
1072
+ ) : (
1073
+ <>
1074
+ <span style={aiStyles.tooltipValue}>
1075
+ ${formatFixed(balanceUsed, 2)} / ${formatFixed(balanceTotal, 2)}
1076
+ </span>
1077
+ {renderUsageCircle(balancePercentage)}
1078
+ </>
1079
+ )}
955
1080
  </div>
956
1081
  </div>
957
1082
 
@@ -24,11 +24,9 @@ export interface UseAiModelsResult {
24
24
  export function useAiModels(options?: UseAiModelsOptions): UseAiModelsResult {
25
25
  const context = useAiContext();
26
26
 
27
- // Si les options ne correspondent pas au contexte, on peut faire un appel direct
28
- // Mais dans la plupart des cas, on utilise le contexte
29
- const useContextData =
30
- (!options?.baseUrl || options.baseUrl === context.baseUrl) &&
31
- (!options?.apiKeyId || options.apiKeyId === context.apiKeyId);
27
+ // Utiliser le contexte dès que la base URL correspond.
28
+ // L'apiKeyId peut varier (session/api-key sélectionnée) sans invalider le contexte.
29
+ const useContextData = !options?.baseUrl || options.baseUrl === context.baseUrl;
32
30
 
33
31
  // Filtrer les modèles selon le type demandé
34
32
  const filteredModels = useMemo(() => {
@@ -41,10 +41,10 @@ export function useModelManagement(
41
41
  const [loading, setLoading] = useState(false);
42
42
  const [error, setError] = useState<string | null>(null);
43
43
 
44
- // Utiliser les données du contexte si disponibles
45
- const useContextData =
46
- (!options.baseUrl || options.baseUrl === context.baseUrl) &&
47
- (!options.apiKey || options.apiKey === context.apiKeyId);
44
+ // Utiliser les données du contexte si la base URL correspond.
45
+ // L'apiKey peut changer selon le mode d'auth (lb_session/supabase/api_key) sans
46
+ // devoir casser le cache/provider de modèles déjà présent.
47
+ const useContextData = !options.baseUrl || options.baseUrl === context.baseUrl;
48
48
 
49
49
  // Filtrer par catégorie si nécessaire
50
50
  const filteredModels = useMemo(() => {