@lastbrain/ai-ui-react 1.0.75 → 1.0.77

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.
Files changed (49) hide show
  1. package/dist/components/AiContextButton.d.ts +1 -1
  2. package/dist/components/AiContextButton.d.ts.map +1 -1
  3. package/dist/components/AiContextButton.js +17 -1
  4. package/dist/components/AiImageButton.d.ts.map +1 -1
  5. package/dist/components/AiImageButton.js +19 -3
  6. package/dist/components/AiInput.d.ts +1 -1
  7. package/dist/components/AiInput.d.ts.map +1 -1
  8. package/dist/components/AiInput.js +21 -5
  9. package/dist/components/AiPromptPanel.js +30 -6
  10. package/dist/components/AiSelect.d.ts +1 -1
  11. package/dist/components/AiSelect.d.ts.map +1 -1
  12. package/dist/components/AiSelect.js +18 -2
  13. package/dist/components/AiStatusButton.d.ts.map +1 -1
  14. package/dist/components/AiStatusButton.js +18 -4
  15. package/dist/components/AiTextarea.d.ts +2 -1
  16. package/dist/components/AiTextarea.d.ts.map +1 -1
  17. package/dist/components/AiTextarea.js +25 -6
  18. package/dist/components/ErrorToast.js +3 -3
  19. package/dist/components/LBKeyPicker.js +9 -9
  20. package/dist/components/LBSigninModal.d.ts.map +1 -1
  21. package/dist/components/UsageToast.d.ts +3 -3
  22. package/dist/components/UsageToast.d.ts.map +1 -1
  23. package/dist/context/LBAuthProvider.d.ts +2 -0
  24. package/dist/context/LBAuthProvider.d.ts.map +1 -1
  25. package/dist/context/LBAuthProvider.js +43 -10
  26. package/dist/examples/AiImageGenerator.js +1 -1
  27. package/dist/hooks/useAiStatus.d.ts.map +1 -1
  28. package/dist/hooks/useAiStatus.js +58 -5
  29. package/dist/styles.css +3 -3
  30. package/dist/utils/errorHandler.d.ts +2 -2
  31. package/dist/utils/errorHandler.d.ts.map +1 -1
  32. package/package.json +2 -2
  33. package/src/components/AiContextButton.tsx +20 -2
  34. package/src/components/AiImageButton.tsx +21 -3
  35. package/src/components/AiInput.tsx +28 -5
  36. package/src/components/AiPromptPanel.tsx +31 -6
  37. package/src/components/AiSelect.tsx +21 -3
  38. package/src/components/AiStatusButton.tsx +35 -10
  39. package/src/components/AiTextarea.tsx +33 -9
  40. package/src/components/ErrorToast.tsx +3 -3
  41. package/src/components/LBKeyPicker.tsx +10 -10
  42. package/src/components/LBSigninModal.tsx +2 -1
  43. package/src/components/UsageToast.tsx +4 -4
  44. package/src/context/LBAuthProvider.tsx +46 -9
  45. package/src/examples/AiImageGenerator.tsx +1 -1
  46. package/src/hooks/useAiStatus.ts +62 -5
  47. package/src/styles.css +3 -3
  48. package/src/utils/errorHandler.ts +3 -3
  49. package/src/utils/modelManagement.ts +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
4
4
  * Provider d'authentification LastBrain
5
5
  * Gère l'état de connexion, la session et les appels IA
6
6
  */
7
- import { createContext, useContext, useEffect, useCallback, useMemo, useState, } from "react";
7
+ import { createContext, useContext, useEffect, useCallback, useMemo, useRef, useState, } from "react";
8
8
  import { createLBClient } from "@lastbrain/ai-ui-core";
9
9
  import { I18nProvider } from "./I18nContext";
10
10
  const LBContext = createContext(undefined);
@@ -22,6 +22,17 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
22
22
  const [isLoadingStatus, setIsLoadingStatus] = useState(false);
23
23
  const [isLoadingStorage, setIsLoadingStorage] = useState(false);
24
24
  const [storageLastFetch, setStorageLastFetch] = useState(0);
25
+ const [hasSelectedApiKeyCookie, setHasSelectedApiKeyCookie] = useState(false);
26
+ const previousStatusRef = useRef("loading");
27
+ const syncSelectedApiKeyCookie = useCallback(() => {
28
+ if (typeof document === "undefined")
29
+ return;
30
+ const hasCookie = document.cookie
31
+ .split(";")
32
+ .map((part) => part.trim())
33
+ .some((part) => part.startsWith("api_key_selected=") && part.length > 17);
34
+ setHasSelectedApiKeyCookie(hasCookie);
35
+ }, []);
25
36
  const lbClient = useMemo(() => createLBClient({
26
37
  baseUrl: proxyUrl,
27
38
  mode: process.env.LB_API_KEY ? "env-key" : "auto",
@@ -97,6 +108,13 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
97
108
  useEffect(() => {
98
109
  checkSession();
99
110
  }, [checkSession]);
111
+ useEffect(() => {
112
+ syncSelectedApiKeyCookie();
113
+ if (typeof window === "undefined")
114
+ return;
115
+ const interval = window.setInterval(syncSelectedApiKeyCookie, 1000);
116
+ return () => window.clearInterval(interval);
117
+ }, [syncSelectedApiKeyCookie]);
100
118
  /**
101
119
  * Récupère les clés API de l'utilisateur
102
120
  */
@@ -134,6 +152,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
134
152
  });
135
153
  setAccessToken(undefined); // Nettoyer l'access token temporaire
136
154
  setApiKeys([]); // Nettoyer les clés API temporaires
155
+ setTimeout(() => syncSelectedApiKeyCookie(), 100);
137
156
  onStatusChange?.("ready");
138
157
  onAuthChange?.(); // Refresh provider after signin
139
158
  }
@@ -145,7 +164,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
145
164
  });
146
165
  throw error;
147
166
  }
148
- }, [lbClient, state.user, onStatusChange, onAuthChange]);
167
+ }, [lbClient, onAuthChange, onStatusChange, state.user, syncSelectedApiKeyCookie]);
149
168
  /**
150
169
  * Connexion utilisateur (étape 1 : login)
151
170
  * Retourne le token et les clés API sans créer de session
@@ -239,7 +258,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
239
258
  try {
240
259
  let data;
241
260
  try {
242
- data = await lbClient.getStatus();
261
+ data = (await lbClient.getStatus());
243
262
  }
244
263
  catch {
245
264
  // Backward compatibility: older backends may not expose /auth/status
@@ -302,14 +321,16 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
302
321
  }
303
322
  setIsLoadingStorage(true);
304
323
  try {
305
- const data = await lbClient.getStorageStatus();
306
- const storageData = data?.storage ? { storage: data.storage } : data;
324
+ const data = (await lbClient.getStorageStatus());
325
+ const storageData = data?.storage
326
+ ? { storage: data.storage }
327
+ : data;
307
328
  setStorageStatus(storageData);
308
329
  setStorageLastFetch(now);
309
330
  // Combiner avec le basic status
310
331
  const combinedStatus = {
311
332
  ...basicStatus,
312
- storage: storageData?.storage,
333
+ storage: storageData.storage,
313
334
  };
314
335
  setApiStatus(combinedStatus);
315
336
  }
@@ -359,6 +380,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
359
380
  }
360
381
  await refreshBasicStatus();
361
382
  setTimeout(() => refreshStorageStatus(), 100);
383
+ setTimeout(() => syncSelectedApiKeyCookie(), 100);
362
384
  }
363
385
  else {
364
386
  throw new Error("No valid authentication method available");
@@ -371,6 +393,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
371
393
  lbClient,
372
394
  refreshBasicStatus,
373
395
  refreshStorageStatus,
396
+ syncSelectedApiKeyCookie,
374
397
  ]);
375
398
  /**
376
399
  * Déconnexion
@@ -391,6 +414,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
391
414
  setBasicStatus(null);
392
415
  setStorageStatus(null);
393
416
  setStorageLastFetch(0);
417
+ setHasSelectedApiKeyCookie(false);
394
418
  onStatusChange?.("needs_auth");
395
419
  onAuthChange?.(); // Refresh provider after logout
396
420
  }
@@ -418,22 +442,30 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
418
442
  setApiKeys([]);
419
443
  }
420
444
  }, [lbClient, state.status]);
421
- // Refresh status quand la session devient ready
445
+ // Refresh status uniquement lors de la transition vers "ready"
446
+ // (évite les boucles status/user causées par des callbacks recréés)
422
447
  useEffect(() => {
423
- if (state.status === "ready") {
448
+ const wasReady = previousStatusRef.current === "ready";
449
+ previousStatusRef.current = state.status;
450
+ if (state.status === "ready" && !wasReady) {
424
451
  // Appel rapide d'abord
425
452
  refreshBasicStatus();
426
453
  // Storage en arrière-plan après 100ms
427
454
  setTimeout(() => refreshStorageStatus(), 100);
428
455
  fetchApiKeysWithSession(); // Also fetch API keys list
429
456
  }
430
- else {
457
+ else if (state.status !== "ready") {
431
458
  setApiStatus(null);
432
459
  setBasicStatus(null);
433
460
  setStorageStatus(null);
434
461
  setApiKeys([]);
435
462
  }
436
- }, [state.status]); // Supprimer les fonctions des dépendances pour éviter la boucle
463
+ }, [
464
+ fetchApiKeysWithSession,
465
+ refreshBasicStatus,
466
+ refreshStorageStatus,
467
+ state.status,
468
+ ]);
437
469
  const value = {
438
470
  ...state,
439
471
  login,
@@ -453,6 +485,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
453
485
  refreshStorageStatus,
454
486
  isLoadingStatus,
455
487
  isLoadingStorage,
488
+ hasSelectedApiKeyCookie,
456
489
  };
457
490
  return (_jsx(I18nProvider, { lang: lang, children: _jsx(LBContext.Provider, { value: value, children: children }) }));
458
491
  }
@@ -53,7 +53,7 @@ export function ModelManagementStats({ apiKey, baseUrl, category = "image", } =
53
53
  * Composant exemple pour afficher et gérer une liste de modèles
54
54
  */
55
55
  export function ModelManagementList({ apiKey, baseUrl, category = "image", onModelToggle, }) {
56
- const { availableModels, userModels, loading, error, toggleModel, isModelActive, } = useModelManagement({
56
+ const { availableModels, userModels: _userModels, loading, error, toggleModel, isModelActive, } = useModelManagement({
57
57
  apiKey,
58
58
  baseUrl,
59
59
  category,
@@ -1 +1 @@
1
- {"version":3,"file":"useAiStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiStatus.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;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CAmC3E"}
1
+ {"version":3,"file":"useAiStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiStatus.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,CA2F3E"}
@@ -1,12 +1,49 @@
1
1
  "use client";
2
- import { useState, useEffect, useCallback } from "react";
2
+ import { useState, useEffect, useCallback, useContext, useMemo } from "react";
3
3
  import { useAiClient } from "./useAiClient";
4
+ import { LBContext } from "../context/LBAuthProvider";
4
5
  export function useAiStatus(options) {
5
6
  const client = useAiClient(options);
7
+ const lbContext = useContext(LBContext);
6
8
  const [status, setStatus] = useState(null);
7
9
  const [loading, setLoading] = useState(false);
8
10
  const [error, setError] = useState(null);
11
+ const statusFromLB = useMemo(() => {
12
+ if (!lbContext || lbContext.status !== "ready") {
13
+ return null;
14
+ }
15
+ if (lbContext.apiStatus) {
16
+ return lbContext.apiStatus;
17
+ }
18
+ if (lbContext.basicStatus) {
19
+ return {
20
+ ...lbContext.basicStatus,
21
+ storage: lbContext.storageStatus?.storage ||
22
+ lbContext.basicStatus?.storage,
23
+ };
24
+ }
25
+ return null;
26
+ }, [lbContext]);
9
27
  const fetchStatus = useCallback(async () => {
28
+ // If LBProvider exists, never call getStatus directly here:
29
+ // - ready: delegate refresh to provider
30
+ // - non-ready: avoid unauthorized polling loops
31
+ if (lbContext) {
32
+ if (lbContext.status === "ready") {
33
+ try {
34
+ await lbContext.refreshBasicStatus();
35
+ }
36
+ catch {
37
+ // Ignore: provider already tracks errors/status
38
+ }
39
+ }
40
+ else {
41
+ setStatus(null);
42
+ setLoading(false);
43
+ setError(null);
44
+ }
45
+ return;
46
+ }
10
47
  console.log("[useAiStatus] Starting status fetch");
11
48
  setLoading(true);
12
49
  setError(null);
@@ -22,13 +59,29 @@ export function useAiStatus(options) {
22
59
  finally {
23
60
  setLoading(false);
24
61
  }
25
- }, [client]);
62
+ }, [client, lbContext]);
26
63
  useEffect(() => {
64
+ if (lbContext && lbContext.status !== "ready") {
65
+ setStatus(null);
66
+ setLoading(false);
67
+ setError(null);
68
+ return;
69
+ }
70
+ if (statusFromLB) {
71
+ setStatus(statusFromLB);
72
+ setLoading(false);
73
+ setError(null);
74
+ return;
75
+ }
27
76
  fetchStatus();
28
- }, [fetchStatus]);
77
+ }, [fetchStatus, lbContext, statusFromLB]);
78
+ const isLoadingFromLB = !!lbContext &&
79
+ lbContext.status === "ready" &&
80
+ !statusFromLB &&
81
+ (lbContext.isLoadingStatus || lbContext.isLoadingStorage);
29
82
  return {
30
- status,
31
- loading,
83
+ status: statusFromLB || status,
84
+ loading: isLoadingFromLB || loading,
32
85
  error,
33
86
  refetch: fetchStatus,
34
87
  };
package/dist/styles.css CHANGED
@@ -195,7 +195,7 @@
195
195
  align-items: center;
196
196
  gap: 10px;
197
197
  min-height: var(--ai-control-h, var(--ai-size-md-h));
198
- padding: 0 12px;
198
+ padding: 0 8px;
199
199
  border-radius: var(--ai-radius-current, var(--ai-radius-full));
200
200
  border: 1px solid var(--ai-border);
201
201
  background:
@@ -362,8 +362,8 @@
362
362
  .ai-shell--textarea .ai-control-action,
363
363
  .ai-shell--textarea .ai-spark {
364
364
  position: absolute;
365
- right: 12px;
366
- bottom: 12px;
365
+ right: 8px;
366
+ bottom: 8px;
367
367
  }
368
368
 
369
369
  /* Generic buttons */
@@ -6,7 +6,7 @@ export interface ParsedError {
6
6
  /**
7
7
  * Parse et uniformise la gestion des erreurs des composants AI
8
8
  */
9
- export declare function parseAIError(error: unknown): ParsedError;
9
+ export declare function parseAIError(error: any): ParsedError;
10
10
  /**
11
11
  * Interface pour la callback de gestion d'erreur uniforme
12
12
  */
@@ -23,7 +23,7 @@ export interface ErrorToastCallback {
23
23
  * @param onToast Callback externe optionnelle fournie par le parent
24
24
  * @param showInternalToast Callback interne optionnelle pour afficher un toast dans le composant
25
25
  */
26
- export declare function handleAIError(error: unknown, onToast?: ErrorToastCallback, showInternalToast?: (error: {
26
+ export declare function handleAIError(error: any, onToast?: ErrorToastCallback, showInternalToast?: (error: {
27
27
  message: string;
28
28
  code?: string;
29
29
  }) => void): void;
@@ -1 +1 @@
1
- {"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/utils/errorHandler.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,OAAO,CAAC;CACzB;AAeD;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CA8CxD;AAyFD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAClE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,kBAAkB,EAC5B,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,GACtE,IAAI,CAuBN"}
1
+ {"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/utils/errorHandler.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,OAAO,CAAC;CACzB;AAeD;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,WAAW,CA8CpD;AAyFD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAClE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,GAAG,EACV,OAAO,CAAC,EAAE,kBAAkB,EAC5B,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,GACtE,IAAI,CAuBN"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/ai-ui-react",
3
- "version": "1.0.75",
3
+ "version": "1.0.77",
4
4
  "description": "Headless React components for LastBrain AI UI Kit",
5
5
  "private": false,
6
6
  "type": "module",
@@ -51,7 +51,7 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "lucide-react": "^0.257.0",
54
- "@lastbrain/ai-ui-core": "1.0.54"
54
+ "@lastbrain/ai-ui-core": "1.0.55"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/react": "^19.2.0",
@@ -22,7 +22,7 @@ type ContextData =
22
22
  | boolean
23
23
  | object
24
24
  | unknown[]
25
- | { [key: string]: unknown };
25
+ | { [key: string]: any };
26
26
 
27
27
  export interface AiContextButtonProps
28
28
  extends
@@ -83,11 +83,22 @@ export function AiContextButton({
83
83
  contextDescription || t("ai.context.description", "Data to analyze");
84
84
 
85
85
  let lbStatus: string | undefined;
86
+ let lbHasSession = false;
87
+ let lbHasSelectedKey = false;
88
+ let lbHasSelectedApiKeyCookie = false;
89
+ let hasLBProvider = false;
86
90
  try {
87
91
  const lbContext = useLB();
88
92
  lbStatus = lbContext.status;
93
+ lbHasSession = Boolean(lbContext.session?.sessionToken);
94
+ lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
95
+ lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
96
+ hasLBProvider = true;
89
97
  } catch {
90
98
  lbStatus = undefined;
99
+ lbHasSession = false;
100
+ lbHasSelectedKey = false;
101
+ hasLBProvider = false;
91
102
  }
92
103
 
93
104
  const aiContext = useAiContext();
@@ -99,7 +110,14 @@ export function AiContextButton({
99
110
  apiKeyId,
100
111
  });
101
112
 
102
- const isAuthReady = lbStatus === "ready" || Boolean(process.env.LB_API_KEY);
113
+ const needsApiKeySelection =
114
+ hasLBProvider &&
115
+ lbStatus === "ready" &&
116
+ lbHasSession &&
117
+ (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
118
+ const isAuthReady =
119
+ !needsApiKeySelection &&
120
+ (lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
103
121
 
104
122
  const handleOpenPanel = () => {
105
123
  if (!isAuthReady) {
@@ -74,17 +74,28 @@ export function AiImageButton({
74
74
  requestId: string;
75
75
  tokens: number;
76
76
  } | null>(null);
77
- const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
77
+ const { showUsageToast } = useUsageToast();
78
78
  const { showErrorToast, errorData, errorKey, clearError } = useErrorToast();
79
79
 
80
80
  // Rendre l'authentification optionnelle
81
81
  let lbStatus: string | undefined;
82
+ let lbHasSession = false;
83
+ let lbHasSelectedKey = false;
84
+ let lbHasSelectedApiKeyCookie = false;
85
+ let hasLBProvider = false;
82
86
  try {
83
87
  const lbContext = useLB();
84
88
  lbStatus = lbContext.status;
89
+ lbHasSession = Boolean(lbContext.session?.sessionToken);
90
+ lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
91
+ lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
92
+ hasLBProvider = true;
85
93
  } catch {
86
94
  // LBProvider n'est pas disponible, ignorer
87
95
  lbStatus = undefined;
96
+ lbHasSession = false;
97
+ lbHasSelectedKey = false;
98
+ hasLBProvider = false;
88
99
  }
89
100
 
90
101
  // Récupérer le contexte AiProvider avec fallback sur les props
@@ -95,7 +106,14 @@ export function AiImageButton({
95
106
  const { generateImage, loading } = useAiCallImage({ baseUrl, apiKeyId });
96
107
  const { formatted: loadingElapsed } = useLoadingTimer(loading);
97
108
 
98
- const isAuthReady = lbStatus === "ready" || Boolean(process.env.LB_API_KEY);
109
+ const needsApiKeySelection =
110
+ hasLBProvider &&
111
+ lbStatus === "ready" &&
112
+ lbHasSession &&
113
+ (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
114
+ const isAuthReady =
115
+ !needsApiKeySelection &&
116
+ (lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
99
117
 
100
118
  const handleOpenPanel = () => {
101
119
  if (!isAuthReady) {
@@ -126,7 +144,7 @@ export function AiImageButton({
126
144
  type: "success",
127
145
  message: t("ai.image.savedSuccess", "Image saved"),
128
146
  });
129
- } catch (_error) {
147
+ } catch {
130
148
  onToast?.({
131
149
  type: "error",
132
150
  message: t("ai.image.saveError", "Error while saving"),
@@ -34,7 +34,7 @@ export function AiInput({
34
34
  context,
35
35
  model,
36
36
  prompt,
37
- editMode = false,
37
+ editMode: _editMode = false,
38
38
  enableModelManagement = true,
39
39
  storeOutputs,
40
40
  artifactTitle,
@@ -51,16 +51,32 @@ export function AiInput({
51
51
  inputProps.value?.toString() || inputProps.defaultValue?.toString() || ""
52
52
  );
53
53
  const inputRef = useRef<HTMLInputElement>(null);
54
- const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
54
+ const {
55
+ showUsageToast: _showUsageToast,
56
+ toastData,
57
+ toastKey,
58
+ clearToast,
59
+ } = useUsageToast();
55
60
 
56
61
  // Rendre l'authentification optionnelle
57
62
  let lbStatus: string | undefined;
63
+ let lbHasSession = false;
64
+ let lbHasSelectedKey = false;
65
+ let lbHasSelectedApiKeyCookie = false;
66
+ let hasLBProvider = false;
58
67
  try {
59
68
  const lbContext = useLB();
60
69
  lbStatus = lbContext.status;
70
+ lbHasSession = Boolean(lbContext.session?.sessionToken);
71
+ lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
72
+ lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
73
+ hasLBProvider = true;
61
74
  } catch {
62
75
  // LBProvider n'est pas disponible, ignorer
63
76
  lbStatus = undefined;
77
+ lbHasSession = false;
78
+ lbHasSelectedKey = false;
79
+ hasLBProvider = false;
64
80
  }
65
81
 
66
82
  let ctxBaseUrl: string | undefined;
@@ -77,7 +93,7 @@ export function AiInput({
77
93
  const baseUrl = propBaseUrl ?? ctxBaseUrl;
78
94
  const apiKeyId = propApiKeyId ?? ctxApiKeyId;
79
95
 
80
- const { models } = useAiModels({
96
+ const { models: _models } = useAiModels({
81
97
  baseUrl,
82
98
  apiKeyId,
83
99
  modelType: "text-or-language",
@@ -86,7 +102,14 @@ export function AiInput({
86
102
  const { formatted: loadingElapsed } = useLoadingTimer(loading);
87
103
 
88
104
  const hasConfiguration = Boolean(model && prompt);
89
- const isAuthReady = lbStatus === "ready" || Boolean(process.env.LB_API_KEY);
105
+ const needsApiKeySelection =
106
+ hasLBProvider &&
107
+ lbStatus === "ready" &&
108
+ lbHasSession &&
109
+ (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
110
+ const isAuthReady =
111
+ !needsApiKeySelection &&
112
+ (lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
90
113
  const shouldShowSparkles = isAuthReady && !disabled;
91
114
 
92
115
  const handleOpenPanel = () => {
@@ -104,7 +127,7 @@ export function AiInput({
104
127
  const handleSubmit = async (
105
128
  selectedModel: string,
106
129
  selectedPrompt: string,
107
- promptId?: string
130
+ _promptId?: string
108
131
  ) => {
109
132
  try {
110
133
  const resolvedContext = inputValue || context || undefined;
@@ -102,7 +102,7 @@ function AiPromptPanelInternal({
102
102
  isOpen,
103
103
  onClose,
104
104
  onSubmit,
105
- uiMode: _uiMode = "modal",
105
+ uiMode = "modal",
106
106
  models = [],
107
107
  sourceText,
108
108
  children,
@@ -405,9 +405,29 @@ function AiPromptPanelInternal({
405
405
  return null;
406
406
  }
407
407
 
408
+ const isDrawer = uiMode === "drawer";
409
+ const panelContainerStyle = isDrawer
410
+ ? {
411
+ ...aiStyles.modal,
412
+ alignItems: "stretch",
413
+ justifyContent: "flex-end",
414
+ padding: "0",
415
+ }
416
+ : aiStyles.modal;
417
+ const panelContentStyle = isDrawer
418
+ ? {
419
+ ...aiStyles.modalContent,
420
+ width: "min(92vw, 640px)",
421
+ maxWidth: "640px",
422
+ maxHeight: "100vh",
423
+ height: "100vh",
424
+ borderRadius: "16px 0 0 16px",
425
+ }
426
+ : aiStyles.modalContent;
427
+
408
428
  if (children) {
409
429
  return createPortal(
410
- <div style={aiStyles.modal} onKeyDown={handleKeyDown}>
430
+ <div style={panelContainerStyle} onKeyDown={handleKeyDown}>
411
431
  {children(renderProps)}
412
432
  </div>,
413
433
  portalRoot
@@ -415,7 +435,7 @@ function AiPromptPanelInternal({
415
435
  }
416
436
 
417
437
  return createPortal(
418
- <div style={aiStyles.modal} onKeyDown={handleKeyDown}>
438
+ <div style={panelContainerStyle} onKeyDown={handleKeyDown}>
419
439
  <div
420
440
  style={{
421
441
  ...aiStyles.modalOverlay,
@@ -426,13 +446,18 @@ function AiPromptPanelInternal({
426
446
  />
427
447
  <div
428
448
  style={{
429
- ...aiStyles.modalContent,
449
+ ...panelContentStyle,
430
450
  opacity: isClosing ? 0 : 1,
431
- transform: isClosing ? "translateY(12px)" : "translateY(0)",
451
+ transform: isClosing
452
+ ? isDrawer
453
+ ? "translateX(16px)"
454
+ : "translateY(12px)"
455
+ : isDrawer
456
+ ? "translateX(0)"
457
+ : "translateY(0)",
432
458
  transition: "opacity 200ms ease, transform 200ms ease",
433
459
  display: "flex",
434
460
  flexDirection: "column",
435
- maxHeight: "85vh",
436
461
  overflow: "hidden",
437
462
  }}
438
463
  >
@@ -31,8 +31,8 @@ export function AiSelect({
31
31
  size = "md",
32
32
  radius = "full",
33
33
  context,
34
- model,
35
- prompt,
34
+ model: _model,
35
+ prompt: _prompt,
36
36
  storeOutputs,
37
37
  artifactTitle,
38
38
  onValue,
@@ -49,12 +49,23 @@ export function AiSelect({
49
49
 
50
50
  // Rendre l'authentification optionnelle
51
51
  let lbStatus: string | undefined;
52
+ let lbHasSession = false;
53
+ let lbHasSelectedKey = false;
54
+ let lbHasSelectedApiKeyCookie = false;
55
+ let hasLBProvider = false;
52
56
  try {
53
57
  const lbContext = useLB();
54
58
  lbStatus = lbContext.status;
59
+ lbHasSession = Boolean(lbContext.session?.sessionToken);
60
+ lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
61
+ lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
62
+ hasLBProvider = true;
55
63
  } catch {
56
64
  // LBProvider n'est pas disponible, ignorer
57
65
  lbStatus = undefined;
66
+ lbHasSession = false;
67
+ lbHasSelectedKey = false;
68
+ hasLBProvider = false;
58
69
  }
59
70
 
60
71
  let ctxBaseUrl: string | undefined;
@@ -78,7 +89,14 @@ export function AiSelect({
78
89
  });
79
90
  const { generateText, loading } = useAiCallText({ baseUrl, apiKeyId });
80
91
 
81
- const isAuthReady = lbStatus === "ready" || Boolean(process.env.LB_API_KEY);
92
+ const needsApiKeySelection =
93
+ hasLBProvider &&
94
+ lbStatus === "ready" &&
95
+ lbHasSession &&
96
+ (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
97
+ const isAuthReady =
98
+ !needsApiKeySelection &&
99
+ (lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
82
100
  const shouldShowSparkles = isAuthReady && !disabled;
83
101
 
84
102
  const handleOpenPanel = () => {