@lastbrain/ai-ui-react 1.0.66 → 1.0.68

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,2CAm0CrB"}
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,2CAi6CrB"}
@@ -122,6 +122,8 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
122
122
  (lbIsLoadingStatus || isLoadingStatus) &&
123
123
  !lbBasicStatus &&
124
124
  !effectiveStatus;
125
+ const showCornerLoadingIndicator = lbStatus === "ready" &&
126
+ (showFastStatusSkeleton || lbIsLoadingStatus || lbIsLoadingStorage);
125
127
  const [showTooltip, setShowTooltip] = useState(false);
126
128
  const [isHovered, setIsHovered] = useState(false);
127
129
  const [tooltipPosition, setTooltipPosition] = useState({});
@@ -129,6 +131,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
129
131
  const tooltipRef = useRef(null);
130
132
  const canPortal = typeof document !== "undefined";
131
133
  const hasApiKeySelected = Boolean(effectiveStatus?.apiKey?.id || effectiveStatus?.api_key?.id || lbSelectedKey?.id);
134
+ const isApiKeyAuthMode = effectiveStatus?.authType === "api_key";
132
135
  const requiresApiKeySelection = lbStatus === "ready" && !hasApiKeySelected && apiKeys.length > 0;
133
136
  useLayoutEffect(() => {
134
137
  if (!showTooltip || !buttonRef.current) {
@@ -247,7 +250,20 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
247
250
  ...aiStyles.statusButton,
248
251
  color: "#f59e0b",
249
252
  ...(isHovered && aiStyles.statusButtonHover),
250
- }, className: className, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onClick: () => setShowApiKeySelector(true), title: "Select an API key to enable AI status and generation", children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" }) }) }), showApiKeySelector && apiKeys.length > 0 && (_jsx(LBApiKeySelector, { isOpen: showApiKeySelector, apiKeys: apiKeys, onSelect: async (keyId) => {
253
+ }, className: className, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onClick: () => setShowApiKeySelector(true), title: "Select an API key to enable AI status and generation", children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" }) }) }), showCornerLoadingIndicator && (_jsx("div", { style: {
254
+ position: "absolute",
255
+ top: -2,
256
+ right: -2,
257
+ width: 12,
258
+ height: 12,
259
+ borderRadius: "999px",
260
+ background: "rgba(2, 6, 23, 0.95)",
261
+ border: "1px solid rgba(139, 92, 246, 0.55)",
262
+ display: "flex",
263
+ alignItems: "center",
264
+ justifyContent: "center",
265
+ pointerEvents: "none",
266
+ }, children: _jsx("svg", { style: aiStyles.spinner, width: "8", height: "8", viewBox: "0 0 24 24", fill: "none", stroke: "#8b5cf6", strokeWidth: "2", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) }) })), showApiKeySelector && apiKeys.length > 0 && (_jsx(LBApiKeySelector, { isOpen: showApiKeySelector, apiKeys: apiKeys, onSelect: async (keyId) => {
251
267
  setIsSelectingApiKey(true);
252
268
  try {
253
269
  if (switchApiKey) {
@@ -289,7 +305,20 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
289
305
  if (lbStatus !== "ready") {
290
306
  setShowSigninModal(true);
291
307
  }
292
- }, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" }) }) }), showTooltip &&
308
+ }, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" }) }) }), showCornerLoadingIndicator && (_jsx("div", { style: {
309
+ position: "absolute",
310
+ top: -2,
311
+ right: -2,
312
+ width: 12,
313
+ height: 12,
314
+ borderRadius: "999px",
315
+ background: "rgba(2, 6, 23, 0.95)",
316
+ border: "1px solid rgba(139, 92, 246, 0.55)",
317
+ display: "flex",
318
+ alignItems: "center",
319
+ justifyContent: "center",
320
+ pointerEvents: "none",
321
+ }, children: _jsx("svg", { style: aiStyles.spinner, width: "8", height: "8", viewBox: "0 0 24 24", fill: "none", stroke: "#8b5cf6", strokeWidth: "2", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) }) })), showTooltip &&
293
322
  canPortal &&
294
323
  createPortal(_jsx("div", { ref: tooltipRef, style: {
295
324
  ...aiStyles.tooltip,
@@ -471,7 +500,20 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
471
500
  ...aiStyles.statusButton,
472
501
  color: "#10b981",
473
502
  ...(isHovered && aiStyles.statusButtonHover),
474
- }, className: className, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" }) }) }), showTooltip &&
503
+ }, className: className, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" }) }) }), showCornerLoadingIndicator && (_jsx("div", { style: {
504
+ position: "absolute",
505
+ top: -2,
506
+ right: -2,
507
+ width: 12,
508
+ height: 12,
509
+ borderRadius: "999px",
510
+ background: "rgba(2, 6, 23, 0.95)",
511
+ border: "1px solid rgba(139, 92, 246, 0.55)",
512
+ display: "flex",
513
+ alignItems: "center",
514
+ justifyContent: "center",
515
+ pointerEvents: "none",
516
+ }, children: _jsx("svg", { style: aiStyles.spinner, width: "8", height: "8", viewBox: "0 0 24 24", fill: "none", stroke: "#8b5cf6", strokeWidth: "2", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) }) })), showTooltip &&
475
517
  canPortal &&
476
518
  createPortal(_jsxs("div", { ref: tooltipRef, style: {
477
519
  ...aiStyles.tooltip,
@@ -679,7 +721,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
679
721
  Object.assign(e.currentTarget.style, {
680
722
  background: "transparent",
681
723
  });
682
- }, title: "New Folder", children: _jsx(FolderPlus, { size: 18 }) }), logout && (_jsx("button", { onClick: async () => {
724
+ }, title: "New Folder", children: _jsx(FolderPlus, { size: 18 }) }), logout && !isApiKeyAuthMode && (_jsx("button", { onClick: async () => {
683
725
  try {
684
726
  await logout();
685
727
  setShowTooltip(false);
@@ -1 +1 @@
1
- {"version":3,"file":"AiProvider.d.ts","sourceRoot":"","sources":["../../src/context/AiProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,QAAQ,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IAEf,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IAErB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,OAAO,CAAC;IAE3B,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,eAAe,EAAE,CACf,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,KAC1C,QAAQ,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,QAAQ,EAAE,CAAC;IAChC,cAAc,EAAE,MAAM,QAAQ,EAAE,CAAC;CAClC;AAED,QAAA,MAAM,SAAS,qDAAuD,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,QAAQ,GACT,EAAE,eAAe,2CA6QjB;AAED,wBAAgB,YAAY,IAAI,cAAc,CAM7C;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"AiProvider.d.ts","sourceRoot":"","sources":["../../src/context/AiProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAKtD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,QAAQ,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IAEf,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IAErB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,OAAO,CAAC;IAE3B,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,eAAe,EAAE,CACf,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,KAC1C,QAAQ,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,QAAQ,EAAE,CAAC;IAChC,cAAc,EAAE,MAAM,QAAQ,EAAE,CAAC;CAClC;AAED,QAAA,MAAM,SAAS,qDAAuD,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,QAAQ,EACR,MAAgB,EAChB,QAAQ,GACT,EAAE,eAAe,2CAuSjB;AAED,wBAAgB,YAAY,IAAI,cAAc,CAM7C;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -3,8 +3,20 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createContext, useContext, useState, useEffect, useCallback, useRef, } from "react";
4
4
  import { createClient } from "@lastbrain/ai-ui-core";
5
5
  import { getUserModels } from "../utils/modelManagement";
6
+ import { useLB } from "./LBAuthProvider";
6
7
  const AiContext = createContext(undefined);
7
8
  export function AiProvider({ baseUrl, apiKeyId, uiMode = "modal", children, }) {
9
+ let lbStatus;
10
+ let lbSelectedKeyId;
11
+ try {
12
+ const lb = useLB();
13
+ lbStatus = lb.status;
14
+ lbSelectedKeyId = lb.selectedKey?.id;
15
+ }
16
+ catch {
17
+ lbStatus = undefined;
18
+ lbSelectedKeyId = undefined;
19
+ }
8
20
  const [providers, setProviders] = useState([]);
9
21
  const [allModels, setAllModels] = useState([]);
10
22
  const [availableModels, setAvailableModels] = useState([]);
@@ -188,6 +200,18 @@ export function AiProvider({ baseUrl, apiKeyId, uiMode = "modal", children, }) {
188
200
  fetchProviders(); // Fetch providers + available models en même temps
189
201
  fetchUserModels();
190
202
  }, [fetchProviders, fetchUserModels]);
203
+ // Après authentification (ex: lb_session), forcer un refetch propre des modèles.
204
+ useEffect(() => {
205
+ if (lbStatus !== "ready") {
206
+ return;
207
+ }
208
+ hasFetchedProviders.current = false;
209
+ hasFetchedUserModels.current = false;
210
+ providersAvailable.current = true;
211
+ userModelsAvailable.current = true;
212
+ fetchProviders();
213
+ fetchUserModels();
214
+ }, [lbStatus, lbSelectedKeyId, fetchProviders, fetchUserModels]);
191
215
  // Helpers pour filtrer les modèles par type
192
216
  const getModelsByType = useCallback((type) => {
193
217
  const filtered = allModels.filter((model) => model.type === type);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/ai-ui-react",
3
- "version": "1.0.66",
3
+ "version": "1.0.68",
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.50"
51
+ "@lastbrain/ai-ui-core": "1.0.52"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/react": "^19.2.0",
@@ -212,6 +212,9 @@ export function AiStatusButton({
212
212
  (lbIsLoadingStatus || isLoadingStatus) &&
213
213
  !lbBasicStatus &&
214
214
  !effectiveStatus;
215
+ const showCornerLoadingIndicator =
216
+ lbStatus === "ready" &&
217
+ (showFastStatusSkeleton || lbIsLoadingStatus || lbIsLoadingStorage);
215
218
 
216
219
  const [showTooltip, setShowTooltip] = useState(false);
217
220
  const [isHovered, setIsHovered] = useState(false);
@@ -222,6 +225,7 @@ export function AiStatusButton({
222
225
  const hasApiKeySelected = Boolean(
223
226
  effectiveStatus?.apiKey?.id || effectiveStatus?.api_key?.id || lbSelectedKey?.id
224
227
  );
228
+ const isApiKeyAuthMode = effectiveStatus?.authType === "api_key";
225
229
  const requiresApiKeySelection =
226
230
  lbStatus === "ready" && !hasApiKeySelected && apiKeys.length > 0;
227
231
 
@@ -401,6 +405,36 @@ export function AiStatusButton({
401
405
  <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
402
406
  </svg>
403
407
  </button>
408
+ {showCornerLoadingIndicator && (
409
+ <div
410
+ style={{
411
+ position: "absolute",
412
+ top: -2,
413
+ right: -2,
414
+ width: 12,
415
+ height: 12,
416
+ borderRadius: "999px",
417
+ background: "rgba(2, 6, 23, 0.95)",
418
+ border: "1px solid rgba(139, 92, 246, 0.55)",
419
+ display: "flex",
420
+ alignItems: "center",
421
+ justifyContent: "center",
422
+ pointerEvents: "none",
423
+ }}
424
+ >
425
+ <svg
426
+ style={aiStyles.spinner}
427
+ width="8"
428
+ height="8"
429
+ viewBox="0 0 24 24"
430
+ fill="none"
431
+ stroke="#8b5cf6"
432
+ strokeWidth="2"
433
+ >
434
+ <path d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" />
435
+ </svg>
436
+ </div>
437
+ )}
404
438
  {showApiKeySelector && apiKeys.length > 0 && (
405
439
  <LBApiKeySelector
406
440
  isOpen={showApiKeySelector}
@@ -504,6 +538,36 @@ export function AiStatusButton({
504
538
  <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
505
539
  </svg>
506
540
  </button>
541
+ {showCornerLoadingIndicator && (
542
+ <div
543
+ style={{
544
+ position: "absolute",
545
+ top: -2,
546
+ right: -2,
547
+ width: 12,
548
+ height: 12,
549
+ borderRadius: "999px",
550
+ background: "rgba(2, 6, 23, 0.95)",
551
+ border: "1px solid rgba(139, 92, 246, 0.55)",
552
+ display: "flex",
553
+ alignItems: "center",
554
+ justifyContent: "center",
555
+ pointerEvents: "none",
556
+ }}
557
+ >
558
+ <svg
559
+ style={aiStyles.spinner}
560
+ width="8"
561
+ height="8"
562
+ viewBox="0 0 24 24"
563
+ fill="none"
564
+ stroke="#8b5cf6"
565
+ strokeWidth="2"
566
+ >
567
+ <path d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" />
568
+ </svg>
569
+ </div>
570
+ )}
507
571
  {showTooltip &&
508
572
  canPortal &&
509
573
  createPortal(
@@ -868,6 +932,36 @@ export function AiStatusButton({
868
932
  <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
869
933
  </svg>
870
934
  </button>
935
+ {showCornerLoadingIndicator && (
936
+ <div
937
+ style={{
938
+ position: "absolute",
939
+ top: -2,
940
+ right: -2,
941
+ width: 12,
942
+ height: 12,
943
+ borderRadius: "999px",
944
+ background: "rgba(2, 6, 23, 0.95)",
945
+ border: "1px solid rgba(139, 92, 246, 0.55)",
946
+ display: "flex",
947
+ alignItems: "center",
948
+ justifyContent: "center",
949
+ pointerEvents: "none",
950
+ }}
951
+ >
952
+ <svg
953
+ style={aiStyles.spinner}
954
+ width="8"
955
+ height="8"
956
+ viewBox="0 0 24 24"
957
+ fill="none"
958
+ stroke="#8b5cf6"
959
+ strokeWidth="2"
960
+ >
961
+ <path d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" />
962
+ </svg>
963
+ </div>
964
+ )}
871
965
 
872
966
  {showTooltip &&
873
967
  canPortal &&
@@ -1297,7 +1391,7 @@ export function AiStatusButton({
1297
1391
  </button>
1298
1392
 
1299
1393
  {/* Logout Button */}
1300
- {logout && (
1394
+ {logout && !isApiKeyAuthMode && (
1301
1395
  <button
1302
1396
  onClick={async () => {
1303
1397
  try {
@@ -13,6 +13,7 @@ import type { UiMode } from "../types";
13
13
  import type { ModelRef } from "@lastbrain/ai-ui-core";
14
14
  import { createClient } from "@lastbrain/ai-ui-core";
15
15
  import { getUserModels } from "../utils/modelManagement";
16
+ import { useLB } from "./LBAuthProvider";
16
17
 
17
18
  export interface AIModel {
18
19
  id: string;
@@ -70,6 +71,17 @@ export function AiProvider({
70
71
  uiMode = "modal",
71
72
  children,
72
73
  }: AiProviderProps) {
74
+ let lbStatus: string | undefined;
75
+ let lbSelectedKeyId: string | undefined;
76
+ try {
77
+ const lb = useLB();
78
+ lbStatus = lb.status;
79
+ lbSelectedKeyId = lb.selectedKey?.id;
80
+ } catch {
81
+ lbStatus = undefined;
82
+ lbSelectedKeyId = undefined;
83
+ }
84
+
73
85
  const [providers, setProviders] = useState<ProviderData[]>([]);
74
86
  const [allModels, setAllModels] = useState<ModelRef[]>([]);
75
87
  const [availableModels, setAvailableModels] = useState<AIModel[]>([]);
@@ -289,6 +301,21 @@ export function AiProvider({
289
301
  fetchUserModels();
290
302
  }, [fetchProviders, fetchUserModels]);
291
303
 
304
+ // Après authentification (ex: lb_session), forcer un refetch propre des modèles.
305
+ useEffect(() => {
306
+ if (lbStatus !== "ready") {
307
+ return;
308
+ }
309
+
310
+ hasFetchedProviders.current = false;
311
+ hasFetchedUserModels.current = false;
312
+ providersAvailable.current = true;
313
+ userModelsAvailable.current = true;
314
+
315
+ fetchProviders();
316
+ fetchUserModels();
317
+ }, [lbStatus, lbSelectedKeyId, fetchProviders, fetchUserModels]);
318
+
292
319
  // Helpers pour filtrer les modèles par type
293
320
  const getModelsByType = useCallback(
294
321
  (type: "text" | "language" | "image" | "embed") => {