@lastbrain/ai-ui-react 1.0.47 → 1.0.49
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/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +60 -10
- package/dist/context/LBAuthProvider.d.ts +3 -1
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +45 -11
- package/package.json +2 -2
- package/src/components/AiStatusButton.tsx +84 -16
- package/src/context/LBAuthProvider.tsx +48 -10
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAoBtD,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,2CAiiCrB"}
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useRef, useLayoutEffect } from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
|
-
import { BarChart3, Settings, FileText, History as HistoryIcon, FolderPlus, Power, LogOut, Key, } from "lucide-react";
|
|
5
|
+
import { BarChart3, Settings, FileText, History as HistoryIcon, FolderPlus, Power, LogOut, Key, RefreshCw, } from "lucide-react";
|
|
6
6
|
import { aiStyles, calculateTooltipPosition } from "../styles/inline";
|
|
7
7
|
import { useLB } from "../context/LBAuthProvider";
|
|
8
|
+
import { useAiContext } from "../context/AiProvider";
|
|
8
9
|
import { LBSigninModal } from "./LBSigninModal";
|
|
9
10
|
import { LBApiKeySelector } from "./LBApiKeySelector";
|
|
10
11
|
export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
@@ -30,6 +31,16 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
30
31
|
user = undefined;
|
|
31
32
|
logout = undefined;
|
|
32
33
|
}
|
|
34
|
+
// Récupérer refetchProviders depuis AiProvider si disponible
|
|
35
|
+
let refetchProviders;
|
|
36
|
+
try {
|
|
37
|
+
const aiContext = useAiContext();
|
|
38
|
+
refetchProviders = aiContext.refetchProviders;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// AiProvider n'est pas disponible, ignorer
|
|
42
|
+
refetchProviders = undefined;
|
|
43
|
+
}
|
|
33
44
|
const [showSigninModal, setShowSigninModal] = useState(false);
|
|
34
45
|
const [showApiKeySelector, setShowApiKeySelector] = useState(false);
|
|
35
46
|
const formatNumber = (value) => typeof value === "number" ? value.toLocaleString() : "0";
|
|
@@ -202,7 +213,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
202
213
|
gap: "8px",
|
|
203
214
|
borderTop: "1px solid var(--ai-border-primary, #374151)",
|
|
204
215
|
paddingTop: "12px",
|
|
205
|
-
}, children: [_jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/
|
|
216
|
+
}, children: [_jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/dashboard", "_blank"), style: {
|
|
206
217
|
flex: 1,
|
|
207
218
|
background: "transparent",
|
|
208
219
|
border: "none",
|
|
@@ -221,7 +232,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
221
232
|
Object.assign(e.currentTarget.style, {
|
|
222
233
|
background: "transparent",
|
|
223
234
|
});
|
|
224
|
-
}, title: "View Metrics", children: _jsx(BarChart3, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/settings", "_blank"), style: {
|
|
235
|
+
}, title: "View Metrics", children: _jsx(BarChart3, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/ai/settings", "_blank"), style: {
|
|
225
236
|
flex: 1,
|
|
226
237
|
background: "transparent",
|
|
227
238
|
border: "none",
|
|
@@ -240,7 +251,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
240
251
|
Object.assign(e.currentTarget.style, {
|
|
241
252
|
background: "transparent",
|
|
242
253
|
});
|
|
243
|
-
}, title: "Settings", children: _jsx(Settings, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/prompts", "_blank"), style: {
|
|
254
|
+
}, title: "Settings", children: _jsx(Settings, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/ai/prompts", "_blank"), style: {
|
|
244
255
|
flex: 1,
|
|
245
256
|
background: "transparent",
|
|
246
257
|
border: "none",
|
|
@@ -281,6 +292,10 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
281
292
|
}, title: "New Folder", children: _jsx(FolderPlus, { size: 18 }) }), _jsx("button", { onClick: async () => {
|
|
282
293
|
if (logout) {
|
|
283
294
|
await logout();
|
|
295
|
+
// Refresh provider data after logout
|
|
296
|
+
if (refetchProviders) {
|
|
297
|
+
await refetchProviders();
|
|
298
|
+
}
|
|
284
299
|
}
|
|
285
300
|
setShowTooltip(false);
|
|
286
301
|
}, style: {
|
|
@@ -354,12 +369,39 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
354
369
|
textOverflow: "ellipsis",
|
|
355
370
|
}, children: status.user.email })] }) })), _jsxs("div", { style: {
|
|
356
371
|
...aiStyles.tooltipSection,
|
|
357
|
-
...(status.user?.email
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
372
|
+
...(status.user?.email ? {} : aiStyles.tooltipSectionFirst),
|
|
373
|
+
}, children: [_jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "API Key:" }), _jsxs("div", { style: {
|
|
374
|
+
display: "flex",
|
|
375
|
+
alignItems: "center",
|
|
376
|
+
gap: "8px",
|
|
377
|
+
}, children: [_jsx("span", { style: aiStyles.tooltipValue, children: status.apiKey?.name || status.api_key?.name || "Unknown" }), apiKeys.length > 1 && selectApiKeyWithToken && (_jsx("button", { onClick: (e) => {
|
|
378
|
+
e.stopPropagation();
|
|
379
|
+
setShowApiKeySelector(true);
|
|
380
|
+
}, style: {
|
|
381
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
382
|
+
border: "1px solid rgba(139, 92, 246, 0.3)",
|
|
383
|
+
borderRadius: "50%",
|
|
384
|
+
width: "24px",
|
|
385
|
+
height: "24px",
|
|
386
|
+
display: "flex",
|
|
387
|
+
alignItems: "center",
|
|
388
|
+
justifyContent: "center",
|
|
389
|
+
cursor: "pointer",
|
|
390
|
+
padding: 0,
|
|
391
|
+
transition: "all 0.2s ease",
|
|
392
|
+
}, onMouseEnter: (e) => {
|
|
393
|
+
e.currentTarget.style.background =
|
|
394
|
+
"rgba(139, 92, 246, 0.2)";
|
|
395
|
+
e.currentTarget.style.borderColor =
|
|
396
|
+
"rgba(139, 92, 246, 0.5)";
|
|
397
|
+
}, onMouseLeave: (e) => {
|
|
398
|
+
e.currentTarget.style.background =
|
|
399
|
+
"rgba(139, 92, 246, 0.1)";
|
|
400
|
+
e.currentTarget.style.borderColor =
|
|
401
|
+
"rgba(139, 92, 246, 0.3)";
|
|
402
|
+
}, title: "Change API Key", children: _jsx(RefreshCw, { 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: status.apiKey?.env || status.api_key?.env || "N/A" })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [status.apiKey?.rate_limit_rpm ||
|
|
403
|
+
status.api_key?.rate_limit_rpm ||
|
|
404
|
+
0, " ", "req/min"] })] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Balance" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: ["$", formatFixed(balanceUsed, 6), " / $", formatNumber(balanceTotal)] }), renderUsageCircle(balancePercentage)] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Storage" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [formatStorage(storageUsed), " /", " ", formatStorage(storageAllocated)] }), renderUsageCircle(storagePercentage)] })] }), _jsxs("div", { style: {
|
|
363
405
|
...aiStyles.tooltipActions,
|
|
364
406
|
width: "100%",
|
|
365
407
|
flexDirection: "row",
|
|
@@ -479,6 +521,10 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
479
521
|
try {
|
|
480
522
|
await logout();
|
|
481
523
|
setShowTooltip(false);
|
|
524
|
+
// Refresh provider data after logout
|
|
525
|
+
if (refetchProviders) {
|
|
526
|
+
await refetchProviders();
|
|
527
|
+
}
|
|
482
528
|
}
|
|
483
529
|
catch (error) {
|
|
484
530
|
console.error("Logout failed:", error);
|
|
@@ -506,6 +552,10 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
506
552
|
await selectApiKeyWithToken(keyId);
|
|
507
553
|
setShowApiKeySelector(false);
|
|
508
554
|
setShowTooltip(false);
|
|
555
|
+
// Refresh provider data after API key selection
|
|
556
|
+
if (refetchProviders) {
|
|
557
|
+
await refetchProviders();
|
|
558
|
+
}
|
|
509
559
|
}
|
|
510
560
|
catch (error) {
|
|
511
561
|
console.error("Failed to select API key:", error);
|
|
@@ -12,6 +12,8 @@ interface LBProviderProps {
|
|
|
12
12
|
proxyUrl?: string;
|
|
13
13
|
/** Fonction appelée lors des changements d'état */
|
|
14
14
|
onStatusChange?: (status: LBAuthState["status"]) => void;
|
|
15
|
+
/** Fonction appelée après signin/logout pour refresh les providers */
|
|
16
|
+
onAuthChange?: () => void;
|
|
15
17
|
}
|
|
16
18
|
interface LBContextValue extends LBAuthState {
|
|
17
19
|
/** Fonction de connexion */
|
|
@@ -35,7 +37,7 @@ interface LBContextValue extends LBAuthState {
|
|
|
35
37
|
/** Access token temporaire (après login) */
|
|
36
38
|
accessToken?: string;
|
|
37
39
|
}
|
|
38
|
-
export declare function LBProvider({ children, baseUrl: _baseUrl, proxyUrl, onStatusChange, }: LBProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
export declare function LBProvider({ children, baseUrl: _baseUrl, proxyUrl, onStatusChange, onAuthChange, }: LBProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
39
41
|
/**
|
|
40
42
|
* Hook pour accéder au contexte LastBrain
|
|
41
43
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAIT,MAAM,uBAAuB,CAAC;AAE/B,UAAU,eAAe;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAIT,MAAM,uBAAuB,CAAC;AAE/B,UAAU,eAAe;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IACzD,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,UAAU,cAAe,SAAQ,WAAW;IAC1C,4BAA4B;IAC5B,KAAK,EAAE,CACL,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;KAC7B,CAAC,CAAC;IACH,8BAA8B;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,6CAA6C;IAC7C,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3D,gEAAgE;IAChE,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,mDAAmD;IACnD,qBAAqB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,oCAAoC;IACpC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,2BAA2B;IAC3B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,EAAE,QAA2B,EACpC,QAA2B,EAC3B,cAAc,EACd,YAAY,GACb,EAAE,eAAe,2CA6WjB;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,cAAc,CAMtC"}
|
|
@@ -6,7 +6,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
6
6
|
*/
|
|
7
7
|
import { createContext, useContext, useEffect, useCallback, useState, } from "react";
|
|
8
8
|
const LBContext = createContext(undefined);
|
|
9
|
-
export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", proxyUrl = "/api/lastbrain", onStatusChange, }) {
|
|
9
|
+
export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", proxyUrl = "/api/lastbrain", onStatusChange, onAuthChange, }) {
|
|
10
10
|
const [state, setState] = useState({
|
|
11
11
|
status: "loading",
|
|
12
12
|
});
|
|
@@ -22,14 +22,46 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
22
22
|
});
|
|
23
23
|
if (response.ok) {
|
|
24
24
|
const session = await response.json();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
// Récupérer les infos utilisateur depuis /auth/status
|
|
26
|
+
try {
|
|
27
|
+
const statusResponse = await fetch(`${proxyUrl}/auth/status`, {
|
|
28
|
+
credentials: "include",
|
|
29
|
+
});
|
|
30
|
+
if (statusResponse.ok) {
|
|
31
|
+
const statusData = await statusResponse.json();
|
|
32
|
+
setState({
|
|
33
|
+
status: "ready",
|
|
34
|
+
session,
|
|
35
|
+
user: {
|
|
36
|
+
id: session.userId,
|
|
37
|
+
email: statusData.user?.email || "",
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Fallback sans email
|
|
43
|
+
setState({
|
|
44
|
+
status: "ready",
|
|
45
|
+
session,
|
|
46
|
+
user: {
|
|
47
|
+
id: session.userId,
|
|
48
|
+
email: "",
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (statusError) {
|
|
54
|
+
console.error("[LBProvider] Failed to fetch status:", statusError);
|
|
55
|
+
// Fallback sans email
|
|
56
|
+
setState({
|
|
57
|
+
status: "ready",
|
|
58
|
+
session,
|
|
59
|
+
user: {
|
|
60
|
+
id: session.userId,
|
|
61
|
+
email: "",
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
33
65
|
onStatusChange?.("ready");
|
|
34
66
|
}
|
|
35
67
|
else {
|
|
@@ -114,6 +146,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
114
146
|
setAccessToken(undefined); // Nettoyer l'access token temporaire
|
|
115
147
|
setApiKeys([]); // Nettoyer les clés API temporaires
|
|
116
148
|
onStatusChange?.("ready");
|
|
149
|
+
onAuthChange?.(); // Refresh provider after signin
|
|
117
150
|
}
|
|
118
151
|
catch (error) {
|
|
119
152
|
const message = error instanceof Error ? error.message : "Failed to select API key";
|
|
@@ -123,7 +156,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
123
156
|
});
|
|
124
157
|
throw error;
|
|
125
158
|
}
|
|
126
|
-
}, [proxyUrl, state.user, onStatusChange]);
|
|
159
|
+
}, [proxyUrl, state.user, onStatusChange, onAuthChange]);
|
|
127
160
|
/**
|
|
128
161
|
* Connexion utilisateur (étape 1 : login)
|
|
129
162
|
* Retourne le token et les clés API sans créer de session
|
|
@@ -248,8 +281,9 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
248
281
|
setApiKeys([]);
|
|
249
282
|
setAccessToken(undefined);
|
|
250
283
|
onStatusChange?.("needs_auth");
|
|
284
|
+
onAuthChange?.(); // Refresh provider after logout
|
|
251
285
|
}
|
|
252
|
-
}, [proxyUrl, onStatusChange]);
|
|
286
|
+
}, [proxyUrl, onStatusChange, onAuthChange]);
|
|
253
287
|
/**
|
|
254
288
|
* Recharge la session
|
|
255
289
|
*/
|
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.49",
|
|
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.
|
|
51
|
+
"@lastbrain/ai-ui-core": "1.0.39"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/react": "^19.2.0",
|
|
@@ -12,9 +12,11 @@ import {
|
|
|
12
12
|
Power,
|
|
13
13
|
LogOut,
|
|
14
14
|
Key,
|
|
15
|
+
RefreshCw,
|
|
15
16
|
} from "lucide-react";
|
|
16
17
|
import { aiStyles, calculateTooltipPosition } from "../styles/inline";
|
|
17
18
|
import { useLB } from "../context/LBAuthProvider";
|
|
19
|
+
import { useAiContext } from "../context/AiProvider";
|
|
18
20
|
import { LBSigninModal } from "./LBSigninModal";
|
|
19
21
|
import { LBApiKeySelector } from "./LBApiKeySelector";
|
|
20
22
|
|
|
@@ -35,9 +37,7 @@ export function AiStatusButton({
|
|
|
35
37
|
let logout: (() => Promise<void>) | undefined;
|
|
36
38
|
let apiKeys: any[] = [];
|
|
37
39
|
let accessToken: string | undefined;
|
|
38
|
-
let selectApiKeyWithToken:
|
|
39
|
-
| ((apiKeyId: string) => Promise<void>)
|
|
40
|
-
| undefined;
|
|
40
|
+
let selectApiKeyWithToken: ((apiKeyId: string) => Promise<void>) | undefined;
|
|
41
41
|
|
|
42
42
|
try {
|
|
43
43
|
const lbContext = useLB();
|
|
@@ -54,6 +54,16 @@ export function AiStatusButton({
|
|
|
54
54
|
logout = undefined;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
// Récupérer refetchProviders depuis AiProvider si disponible
|
|
58
|
+
let refetchProviders: (() => Promise<void>) | undefined;
|
|
59
|
+
try {
|
|
60
|
+
const aiContext = useAiContext();
|
|
61
|
+
refetchProviders = aiContext.refetchProviders;
|
|
62
|
+
} catch {
|
|
63
|
+
// AiProvider n'est pas disponible, ignorer
|
|
64
|
+
refetchProviders = undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
const [showSigninModal, setShowSigninModal] = useState(false);
|
|
58
68
|
const [showApiKeySelector, setShowApiKeySelector] = useState(false);
|
|
59
69
|
|
|
@@ -407,7 +417,7 @@ export function AiStatusButton({
|
|
|
407
417
|
<button
|
|
408
418
|
onClick={() =>
|
|
409
419
|
window.open(
|
|
410
|
-
"https://prompt.lastbrain.io/
|
|
420
|
+
"https://prompt.lastbrain.io/auth/dashboard",
|
|
411
421
|
"_blank"
|
|
412
422
|
)
|
|
413
423
|
}
|
|
@@ -440,7 +450,7 @@ export function AiStatusButton({
|
|
|
440
450
|
<button
|
|
441
451
|
onClick={() =>
|
|
442
452
|
window.open(
|
|
443
|
-
"https://prompt.lastbrain.io/settings",
|
|
453
|
+
"https://prompt.lastbrain.io/auth/ai/settings",
|
|
444
454
|
"_blank"
|
|
445
455
|
)
|
|
446
456
|
}
|
|
@@ -473,7 +483,7 @@ export function AiStatusButton({
|
|
|
473
483
|
<button
|
|
474
484
|
onClick={() =>
|
|
475
485
|
window.open(
|
|
476
|
-
"https://prompt.lastbrain.io/auth/prompts",
|
|
486
|
+
"https://prompt.lastbrain.io/auth/ai/prompts",
|
|
477
487
|
"_blank"
|
|
478
488
|
)
|
|
479
489
|
}
|
|
@@ -540,6 +550,10 @@ export function AiStatusButton({
|
|
|
540
550
|
onClick={async () => {
|
|
541
551
|
if (logout) {
|
|
542
552
|
await logout();
|
|
553
|
+
// Refresh provider data after logout
|
|
554
|
+
if (refetchProviders) {
|
|
555
|
+
await refetchProviders();
|
|
556
|
+
}
|
|
543
557
|
}
|
|
544
558
|
setShowTooltip(false);
|
|
545
559
|
}}
|
|
@@ -704,29 +718,75 @@ export function AiStatusButton({
|
|
|
704
718
|
<div
|
|
705
719
|
style={{
|
|
706
720
|
...aiStyles.tooltipSection,
|
|
707
|
-
...(status.user?.email
|
|
708
|
-
? {}
|
|
709
|
-
: aiStyles.tooltipSectionFirst),
|
|
721
|
+
...(status.user?.email ? {} : aiStyles.tooltipSectionFirst),
|
|
710
722
|
}}
|
|
711
723
|
>
|
|
712
724
|
<div style={aiStyles.tooltipRow}>
|
|
713
725
|
<span style={aiStyles.tooltipLabel}>API Key:</span>
|
|
714
|
-
<
|
|
715
|
-
{
|
|
716
|
-
|
|
717
|
-
"
|
|
718
|
-
|
|
726
|
+
<div
|
|
727
|
+
style={{
|
|
728
|
+
display: "flex",
|
|
729
|
+
alignItems: "center",
|
|
730
|
+
gap: "8px",
|
|
731
|
+
}}
|
|
732
|
+
>
|
|
733
|
+
<span style={aiStyles.tooltipValue}>
|
|
734
|
+
{status.apiKey?.name || status.api_key?.name || "Unknown"}
|
|
735
|
+
</span>
|
|
736
|
+
{apiKeys.length > 1 && selectApiKeyWithToken && (
|
|
737
|
+
<button
|
|
738
|
+
onClick={(e) => {
|
|
739
|
+
e.stopPropagation();
|
|
740
|
+
setShowApiKeySelector(true);
|
|
741
|
+
}}
|
|
742
|
+
style={{
|
|
743
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
744
|
+
border: "1px solid rgba(139, 92, 246, 0.3)",
|
|
745
|
+
borderRadius: "50%",
|
|
746
|
+
width: "24px",
|
|
747
|
+
height: "24px",
|
|
748
|
+
display: "flex",
|
|
749
|
+
alignItems: "center",
|
|
750
|
+
justifyContent: "center",
|
|
751
|
+
cursor: "pointer",
|
|
752
|
+
padding: 0,
|
|
753
|
+
transition: "all 0.2s ease",
|
|
754
|
+
}}
|
|
755
|
+
onMouseEnter={(e) => {
|
|
756
|
+
e.currentTarget.style.background =
|
|
757
|
+
"rgba(139, 92, 246, 0.2)";
|
|
758
|
+
e.currentTarget.style.borderColor =
|
|
759
|
+
"rgba(139, 92, 246, 0.5)";
|
|
760
|
+
}}
|
|
761
|
+
onMouseLeave={(e) => {
|
|
762
|
+
e.currentTarget.style.background =
|
|
763
|
+
"rgba(139, 92, 246, 0.1)";
|
|
764
|
+
e.currentTarget.style.borderColor =
|
|
765
|
+
"rgba(139, 92, 246, 0.3)";
|
|
766
|
+
}}
|
|
767
|
+
title="Change API Key"
|
|
768
|
+
>
|
|
769
|
+
<RefreshCw
|
|
770
|
+
size={12}
|
|
771
|
+
style={{ color: "rgba(139, 92, 246, 1)" }}
|
|
772
|
+
/>
|
|
773
|
+
</button>
|
|
774
|
+
)}
|
|
775
|
+
</div>
|
|
719
776
|
</div>
|
|
720
777
|
<div style={aiStyles.tooltipRow}>
|
|
721
778
|
<span style={aiStyles.tooltipLabel}>Env:</span>
|
|
722
779
|
<span style={aiStyles.tooltipValue}>
|
|
723
|
-
{status.api_key?.env || "N/A"}
|
|
780
|
+
{status.apiKey?.env || status.api_key?.env || "N/A"}
|
|
724
781
|
</span>
|
|
725
782
|
</div>
|
|
726
783
|
<div style={aiStyles.tooltipRow}>
|
|
727
784
|
<span style={aiStyles.tooltipLabel}>Rate Limit:</span>
|
|
728
785
|
<span style={aiStyles.tooltipValue}>
|
|
729
|
-
{status.
|
|
786
|
+
{status.apiKey?.rate_limit_rpm ||
|
|
787
|
+
status.api_key?.rate_limit_rpm ||
|
|
788
|
+
0}{" "}
|
|
789
|
+
req/min
|
|
730
790
|
</span>
|
|
731
791
|
</div>
|
|
732
792
|
</div>
|
|
@@ -965,6 +1025,10 @@ export function AiStatusButton({
|
|
|
965
1025
|
try {
|
|
966
1026
|
await logout();
|
|
967
1027
|
setShowTooltip(false);
|
|
1028
|
+
// Refresh provider data after logout
|
|
1029
|
+
if (refetchProviders) {
|
|
1030
|
+
await refetchProviders();
|
|
1031
|
+
}
|
|
968
1032
|
} catch (error) {
|
|
969
1033
|
console.error("Logout failed:", error);
|
|
970
1034
|
}
|
|
@@ -1010,6 +1074,10 @@ export function AiStatusButton({
|
|
|
1010
1074
|
await selectApiKeyWithToken(keyId);
|
|
1011
1075
|
setShowApiKeySelector(false);
|
|
1012
1076
|
setShowTooltip(false);
|
|
1077
|
+
// Refresh provider data after API key selection
|
|
1078
|
+
if (refetchProviders) {
|
|
1079
|
+
await refetchProviders();
|
|
1080
|
+
}
|
|
1013
1081
|
} catch (error) {
|
|
1014
1082
|
console.error("Failed to select API key:", error);
|
|
1015
1083
|
}
|
|
@@ -29,6 +29,8 @@ interface LBProviderProps {
|
|
|
29
29
|
proxyUrl?: string;
|
|
30
30
|
/** Fonction appelée lors des changements d'état */
|
|
31
31
|
onStatusChange?: (status: LBAuthState["status"]) => void;
|
|
32
|
+
/** Fonction appelée après signin/logout pour refresh les providers */
|
|
33
|
+
onAuthChange?: () => void;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
interface LBContextValue extends LBAuthState {
|
|
@@ -64,6 +66,7 @@ export function LBProvider({
|
|
|
64
66
|
baseUrl: _baseUrl = "/api/lastbrain",
|
|
65
67
|
proxyUrl = "/api/lastbrain",
|
|
66
68
|
onStatusChange,
|
|
69
|
+
onAuthChange,
|
|
67
70
|
}: LBProviderProps) {
|
|
68
71
|
const [state, setState] = useState<LBAuthState>({
|
|
69
72
|
status: "loading",
|
|
@@ -82,14 +85,47 @@ export function LBProvider({
|
|
|
82
85
|
|
|
83
86
|
if (response.ok) {
|
|
84
87
|
const session: LBSession = await response.json();
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
|
|
89
|
+
// Récupérer les infos utilisateur depuis /auth/status
|
|
90
|
+
try {
|
|
91
|
+
const statusResponse = await fetch(`${proxyUrl}/auth/status`, {
|
|
92
|
+
credentials: "include",
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (statusResponse.ok) {
|
|
96
|
+
const statusData = await statusResponse.json();
|
|
97
|
+
setState({
|
|
98
|
+
status: "ready",
|
|
99
|
+
session,
|
|
100
|
+
user: {
|
|
101
|
+
id: session.userId,
|
|
102
|
+
email: statusData.user?.email || "",
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
// Fallback sans email
|
|
107
|
+
setState({
|
|
108
|
+
status: "ready",
|
|
109
|
+
session,
|
|
110
|
+
user: {
|
|
111
|
+
id: session.userId,
|
|
112
|
+
email: "",
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
} catch (statusError) {
|
|
117
|
+
console.error("[LBProvider] Failed to fetch status:", statusError);
|
|
118
|
+
// Fallback sans email
|
|
119
|
+
setState({
|
|
120
|
+
status: "ready",
|
|
121
|
+
session,
|
|
122
|
+
user: {
|
|
123
|
+
id: session.userId,
|
|
124
|
+
email: "",
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
93
129
|
onStatusChange?.("ready");
|
|
94
130
|
} else {
|
|
95
131
|
setState({ status: "needs_auth" });
|
|
@@ -195,6 +231,7 @@ export function LBProvider({
|
|
|
195
231
|
setAccessToken(undefined); // Nettoyer l'access token temporaire
|
|
196
232
|
setApiKeys([]); // Nettoyer les clés API temporaires
|
|
197
233
|
onStatusChange?.("ready");
|
|
234
|
+
onAuthChange?.(); // Refresh provider after signin
|
|
198
235
|
} catch (error) {
|
|
199
236
|
const message =
|
|
200
237
|
error instanceof Error ? error.message : "Failed to select API key";
|
|
@@ -205,7 +242,7 @@ export function LBProvider({
|
|
|
205
242
|
throw error;
|
|
206
243
|
}
|
|
207
244
|
},
|
|
208
|
-
[proxyUrl, state.user, onStatusChange]
|
|
245
|
+
[proxyUrl, state.user, onStatusChange, onAuthChange]
|
|
209
246
|
);
|
|
210
247
|
|
|
211
248
|
/**
|
|
@@ -371,8 +408,9 @@ export function LBProvider({
|
|
|
371
408
|
setApiKeys([]);
|
|
372
409
|
setAccessToken(undefined);
|
|
373
410
|
onStatusChange?.("needs_auth");
|
|
411
|
+
onAuthChange?.(); // Refresh provider after logout
|
|
374
412
|
}
|
|
375
|
-
}, [proxyUrl, onStatusChange]);
|
|
413
|
+
}, [proxyUrl, onStatusChange, onAuthChange]);
|
|
376
414
|
|
|
377
415
|
/**
|
|
378
416
|
* Recharge la session
|