@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.
- package/dist/components/AiContextButton.d.ts +1 -1
- package/dist/components/AiContextButton.d.ts.map +1 -1
- package/dist/components/AiContextButton.js +17 -1
- package/dist/components/AiImageButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +19 -3
- package/dist/components/AiInput.d.ts +1 -1
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +21 -5
- package/dist/components/AiPromptPanel.js +30 -6
- package/dist/components/AiSelect.d.ts +1 -1
- package/dist/components/AiSelect.d.ts.map +1 -1
- package/dist/components/AiSelect.js +18 -2
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +18 -4
- package/dist/components/AiTextarea.d.ts +2 -1
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +25 -6
- package/dist/components/ErrorToast.js +3 -3
- package/dist/components/LBKeyPicker.js +9 -9
- package/dist/components/LBSigninModal.d.ts.map +1 -1
- package/dist/components/UsageToast.d.ts +3 -3
- package/dist/components/UsageToast.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.d.ts +2 -0
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +43 -10
- package/dist/examples/AiImageGenerator.js +1 -1
- package/dist/hooks/useAiStatus.d.ts.map +1 -1
- package/dist/hooks/useAiStatus.js +58 -5
- package/dist/styles.css +3 -3
- package/dist/utils/errorHandler.d.ts +2 -2
- package/dist/utils/errorHandler.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/AiContextButton.tsx +20 -2
- package/src/components/AiImageButton.tsx +21 -3
- package/src/components/AiInput.tsx +28 -5
- package/src/components/AiPromptPanel.tsx +31 -6
- package/src/components/AiSelect.tsx +21 -3
- package/src/components/AiStatusButton.tsx +35 -10
- package/src/components/AiTextarea.tsx +33 -9
- package/src/components/ErrorToast.tsx +3 -3
- package/src/components/LBKeyPicker.tsx +10 -10
- package/src/components/LBSigninModal.tsx +2 -1
- package/src/components/UsageToast.tsx +4 -4
- package/src/context/LBAuthProvider.tsx +46 -9
- package/src/examples/AiImageGenerator.tsx +1 -1
- package/src/hooks/useAiStatus.ts +62 -5
- package/src/styles.css +3 -3
- package/src/utils/errorHandler.ts +3 -3
- package/src/utils/modelManagement.ts +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
}, [
|
|
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;
|
|
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
|
|
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:
|
|
366
|
-
bottom:
|
|
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:
|
|
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:
|
|
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,
|
|
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.
|
|
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
|
+
"@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]:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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
|
|
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={
|
|
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={
|
|
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
|
-
...
|
|
449
|
+
...panelContentStyle,
|
|
430
450
|
opacity: isClosing ? 0 : 1,
|
|
431
|
-
transform: isClosing
|
|
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
|
|
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 = () => {
|