@lastbrain/ai-ui-react 1.0.53 → 1.0.56
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/AiPromptPanel.js +12 -14
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +32 -27
- package/dist/components/LBApiKeySelector.d.ts.map +1 -1
- package/dist/components/LBApiKeySelector.js +1 -0
- package/dist/components/LBSigninModal.d.ts.map +1 -1
- package/dist/components/LBSigninModal.js +24 -12
- package/dist/context/LBAuthProvider.d.ts +5 -0
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +61 -26
- package/dist/styles/inline.d.ts.map +1 -1
- package/dist/styles/inline.js +8 -1
- package/dist/utils/modelManagement.d.ts.map +1 -1
- package/dist/utils/modelManagement.js +9 -3
- package/package.json +2 -2
- package/src/components/AiPromptPanel.tsx +12 -14
- package/src/components/AiStatusButton.tsx +35 -40
- package/src/components/LBApiKeySelector.tsx +1 -0
- package/src/components/LBSigninModal.tsx +23 -13
- package/src/context/LBAuthProvider.tsx +74 -27
- package/src/styles/inline.ts +8 -1
- package/src/utils/modelManagement.ts +8 -3
|
@@ -279,7 +279,6 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
279
279
|
position: "sticky",
|
|
280
280
|
top: 0,
|
|
281
281
|
zIndex: 5,
|
|
282
|
-
borderBottom: "1px solid var(--ai-border-primary, #374151)",
|
|
283
282
|
backdropFilter: "blur(8px)",
|
|
284
283
|
}, children: [_jsx("h2", { style: aiStyles.modalTitle, children: showPromptLibrary ? "Select a Prompt" : "AI Prompt Configuration" }), _jsx("button", { style: {
|
|
285
284
|
...aiStyles.modalCloseButton,
|
|
@@ -529,7 +528,6 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
529
528
|
position: "sticky",
|
|
530
529
|
bottom: 0,
|
|
531
530
|
zIndex: 5,
|
|
532
|
-
borderTop: "1px solid var(--ai-border-primary, #374151)",
|
|
533
531
|
backdropFilter: "blur(8px)",
|
|
534
532
|
}, children: [_jsx("button", { onClick: handleClose, onMouseEnter: () => setIsCancelHovered(true), onMouseLeave: () => setIsCancelHovered(false), style: {
|
|
535
533
|
...aiStyles.button,
|
|
@@ -611,30 +609,30 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
611
609
|
alignItems: "center",
|
|
612
610
|
justifyContent: "space-between",
|
|
613
611
|
padding: "16px 18px",
|
|
614
|
-
border: "2px solid",
|
|
615
|
-
borderColor: isActive
|
|
616
|
-
|
|
617
|
-
|
|
612
|
+
// border: "2px solid",
|
|
613
|
+
// borderColor: isActive
|
|
614
|
+
// ? "#10b981"
|
|
615
|
+
// : "var(--ai-border-primary, #374151)",
|
|
618
616
|
borderRadius: "10px",
|
|
619
617
|
backgroundColor: isActive
|
|
620
|
-
? "rgba(16, 185, 129, 0.
|
|
621
|
-
: "var(--ai-bg-secondary, rgba(31, 41, 55, 0.
|
|
618
|
+
? "rgba(16, 185, 129, 0.03)"
|
|
619
|
+
: "var(--ai-bg-secondary, rgba(31, 41, 55, 0.2))",
|
|
622
620
|
transition: "all 0.2s",
|
|
623
621
|
cursor: "pointer",
|
|
624
622
|
backdropFilter: "blur(8px)",
|
|
625
623
|
}, onClick: () => !isLoading &&
|
|
626
624
|
handleModelToggle(modelData.id, !isActive), onMouseEnter: (e) => {
|
|
627
|
-
e.currentTarget.style.borderColor = isActive
|
|
628
|
-
|
|
629
|
-
|
|
625
|
+
// e.currentTarget.style.borderColor = isActive
|
|
626
|
+
// ? "#059669"
|
|
627
|
+
// : "#4b5563";
|
|
630
628
|
e.currentTarget.style.transform = "translateY(-2px)";
|
|
631
629
|
e.currentTarget.style.boxShadow = isActive
|
|
632
630
|
? "0 8px 16px rgba(16, 185, 129, 0.2)"
|
|
633
631
|
: "0 8px 16px rgba(0, 0, 0, 0.15)";
|
|
634
632
|
}, onMouseLeave: (e) => {
|
|
635
|
-
e.currentTarget.style.borderColor = isActive
|
|
636
|
-
|
|
637
|
-
|
|
633
|
+
// e.currentTarget.style.borderColor = isActive
|
|
634
|
+
// ? "#10b981"
|
|
635
|
+
// : "var(--ai-border-primary, #374151)";
|
|
638
636
|
e.currentTarget.style.transform = "translateY(0)";
|
|
639
637
|
e.currentTarget.style.boxShadow = "none";
|
|
640
638
|
}, children: [_jsxs("div", { style: {
|
|
@@ -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;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,2CA0iCrB"}
|
|
@@ -2,7 +2,7 @@
|
|
|
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,
|
|
5
|
+
import { BarChart3, Settings, FileText, History as HistoryIcon, FolderPlus, Power, LogOut, ArrowRightLeft, } from "lucide-react";
|
|
6
6
|
import { aiStyles, calculateTooltipPosition } from "../styles/inline";
|
|
7
7
|
import { useLB } from "../context/LBAuthProvider";
|
|
8
8
|
import { useAiContext } from "../context/AiProvider";
|
|
@@ -16,7 +16,9 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
16
16
|
let apiKeys = [];
|
|
17
17
|
let accessToken;
|
|
18
18
|
let selectApiKeyWithToken;
|
|
19
|
+
let switchApiKey;
|
|
19
20
|
let lbApiStatus = null;
|
|
21
|
+
let lbIsLoadingStatus = false;
|
|
20
22
|
try {
|
|
21
23
|
const lbContext = useLB();
|
|
22
24
|
lbStatus = lbContext.status;
|
|
@@ -25,7 +27,9 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
25
27
|
apiKeys = lbContext.apiKeys || [];
|
|
26
28
|
accessToken = lbContext.accessToken;
|
|
27
29
|
selectApiKeyWithToken = lbContext.selectApiKeyWithToken;
|
|
30
|
+
switchApiKey = lbContext.switchApiKey;
|
|
28
31
|
lbApiStatus = lbContext.apiStatus;
|
|
32
|
+
lbIsLoadingStatus = lbContext.isLoadingStatus || false;
|
|
29
33
|
}
|
|
30
34
|
catch {
|
|
31
35
|
// LBProvider n'est pas disponible, ignorer
|
|
@@ -47,6 +51,8 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
47
51
|
}
|
|
48
52
|
const [showSigninModal, setShowSigninModal] = useState(false);
|
|
49
53
|
const [showApiKeySelector, setShowApiKeySelector] = useState(false);
|
|
54
|
+
const [isSelectingApiKey, setIsSelectingApiKey] = useState(false);
|
|
55
|
+
const [isLoadingStatus, setIsLoadingStatus] = useState(false);
|
|
50
56
|
const formatNumber = (value) => typeof value === "number" ? value.toLocaleString() : "0";
|
|
51
57
|
const formatFixed = (value, digits) => typeof value === "number" ? value.toFixed(digits) : "0.00";
|
|
52
58
|
const formatStorage = (valueMb) => {
|
|
@@ -174,7 +180,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
174
180
|
}
|
|
175
181
|
}, 100);
|
|
176
182
|
};
|
|
177
|
-
if (loading) {
|
|
183
|
+
if (loading || isSelectingApiKey || isLoadingStatus || lbIsLoadingStatus) {
|
|
178
184
|
return (_jsx("button", { ref: buttonRef, style: {
|
|
179
185
|
...aiStyles.statusButton,
|
|
180
186
|
...aiStyles.statusButtonDisabled,
|
|
@@ -379,9 +385,15 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
379
385
|
display: "flex",
|
|
380
386
|
alignItems: "center",
|
|
381
387
|
gap: "8px",
|
|
382
|
-
}, children: [_jsx("span", { style: aiStyles.tooltipValue, children:
|
|
383
|
-
|
|
384
|
-
|
|
388
|
+
}, children: [_jsx("span", { style: aiStyles.tooltipValue, children: lbIsLoadingStatus ? (_jsx("div", { style: {
|
|
389
|
+
height: "16px",
|
|
390
|
+
width: "100px",
|
|
391
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
392
|
+
borderRadius: "4px",
|
|
393
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
394
|
+
} })) : (effectiveStatus?.apiKey?.name ||
|
|
395
|
+
effectiveStatus?.api_key?.name ||
|
|
396
|
+
"Unknown") }), switchApiKey && (_jsx("button", { onClick: (e) => {
|
|
385
397
|
e.stopPropagation();
|
|
386
398
|
setShowTooltip(false);
|
|
387
399
|
setShowApiKeySelector(true);
|
|
@@ -509,26 +521,7 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
509
521
|
Object.assign(e.currentTarget.style, {
|
|
510
522
|
background: "transparent",
|
|
511
523
|
});
|
|
512
|
-
}, title: "New Folder", children: _jsx(FolderPlus, { size: 18 }) }),
|
|
513
|
-
background: "transparent",
|
|
514
|
-
border: "none",
|
|
515
|
-
padding: "14px",
|
|
516
|
-
cursor: "pointer",
|
|
517
|
-
display: "flex",
|
|
518
|
-
alignItems: "center",
|
|
519
|
-
justifyContent: "center",
|
|
520
|
-
color: "#8b5cf6",
|
|
521
|
-
transition: "all 0.2s ease",
|
|
522
|
-
borderRadius: "4px",
|
|
523
|
-
}, onMouseEnter: (e) => {
|
|
524
|
-
Object.assign(e.currentTarget.style, {
|
|
525
|
-
background: "rgba(139, 92, 246, 0.1)",
|
|
526
|
-
});
|
|
527
|
-
}, onMouseLeave: (e) => {
|
|
528
|
-
Object.assign(e.currentTarget.style, {
|
|
529
|
-
background: "transparent",
|
|
530
|
-
});
|
|
531
|
-
}, title: "Change API Key", children: _jsx(Key, { size: 18 }) })), logout && (_jsx("button", { onClick: async () => {
|
|
524
|
+
}, title: "New Folder", children: _jsx(FolderPlus, { size: 18 }) }), logout && (_jsx("button", { onClick: async () => {
|
|
532
525
|
try {
|
|
533
526
|
await logout();
|
|
534
527
|
setShowTooltip(false);
|
|
@@ -559,11 +552,19 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
559
552
|
Object.assign(e.currentTarget.style, {
|
|
560
553
|
background: "transparent",
|
|
561
554
|
});
|
|
562
|
-
}, title: "Logout", children: _jsx(LogOut, { size: 18 }) }))] })] }), document.body), showApiKeySelector &&
|
|
555
|
+
}, title: "Logout", children: _jsx(LogOut, { size: 18 }) }))] })] }), document.body), showApiKeySelector && apiKeys.length > 0 && (_jsx(LBApiKeySelector, { isOpen: showApiKeySelector, apiKeys: apiKeys, onSelect: async (keyId) => {
|
|
556
|
+
setIsSelectingApiKey(true);
|
|
563
557
|
try {
|
|
564
|
-
|
|
558
|
+
// Utiliser la nouvelle fonction switchApiKey qui gère automatiquement le contexte
|
|
559
|
+
if (switchApiKey) {
|
|
560
|
+
await switchApiKey(keyId);
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
throw new Error("Switch API key function not available");
|
|
564
|
+
}
|
|
565
565
|
setShowApiKeySelector(false);
|
|
566
566
|
setShowTooltip(false);
|
|
567
|
+
setIsLoadingStatus(true);
|
|
567
568
|
// Refresh provider data after API key selection
|
|
568
569
|
if (refetchProviders) {
|
|
569
570
|
await refetchProviders();
|
|
@@ -572,5 +573,9 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
572
573
|
catch (error) {
|
|
573
574
|
console.error("Failed to select API key:", error);
|
|
574
575
|
}
|
|
576
|
+
finally {
|
|
577
|
+
setIsSelectingApiKey(false);
|
|
578
|
+
setIsLoadingStatus(false);
|
|
579
|
+
}
|
|
575
580
|
}, onCancel: () => setShowApiKeySelector(false) }))] }));
|
|
576
581
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LBApiKeySelector.d.ts","sourceRoot":"","sources":["../../src/components/LBApiKeySelector.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,MAAM,GACP,EAAE,qBAAqB,
|
|
1
|
+
{"version":3,"file":"LBApiKeySelector.d.ts","sourceRoot":"","sources":["../../src/components/LBApiKeySelector.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,MAAM,GACP,EAAE,qBAAqB,kDAyTvB"}
|
|
@@ -41,6 +41,7 @@ export function LBApiKeySelector({ apiKeys, onSelect, onCancel, isOpen, }) {
|
|
|
41
41
|
bottom: 0,
|
|
42
42
|
background: "rgba(0, 0, 0, 0.75)",
|
|
43
43
|
backdropFilter: "blur(8px)",
|
|
44
|
+
height: "110%",
|
|
44
45
|
} }), _jsxs("div", { style: {
|
|
45
46
|
position: "relative",
|
|
46
47
|
background: "light-dark(#ffffff, #1e293b)",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LBSigninModal.d.ts","sourceRoot":"","sources":["../../src/components/LBSigninModal.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"LBSigninModal.d.ts","sourceRoot":"","sources":["../../src/components/LBSigninModal.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,kDA8jBpE"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useState
|
|
3
|
+
import { useState } from "react";
|
|
4
4
|
import { useLB } from "../context/LBAuthProvider";
|
|
5
5
|
import { LBApiKeySelector } from "./LBApiKeySelector";
|
|
6
6
|
import { Mail, Lock, Sparkles, X, Loader2, AlertCircle } from "lucide-react";
|
|
@@ -11,15 +11,19 @@ export function LBSigninModal({ isOpen, onClose }) {
|
|
|
11
11
|
const [loading, setLoading] = useState(false);
|
|
12
12
|
const [error, setError] = useState("");
|
|
13
13
|
const [showKeySelector, setShowKeySelector] = useState(false);
|
|
14
|
+
const [currentApiKeys, setCurrentApiKeys] = useState([]); // Stocker les clés API localement
|
|
14
15
|
// Vérifier si LBProvider est disponible
|
|
15
16
|
let login;
|
|
16
17
|
let selectApiKeyWithToken;
|
|
18
|
+
let fetchApiKeys;
|
|
17
19
|
let apiKeys = [];
|
|
18
20
|
let lbStatus;
|
|
21
|
+
let lbContext;
|
|
19
22
|
try {
|
|
20
|
-
|
|
23
|
+
lbContext = useLB();
|
|
21
24
|
login = lbContext.login;
|
|
22
25
|
selectApiKeyWithToken = lbContext.selectApiKeyWithToken;
|
|
26
|
+
fetchApiKeys = lbContext.fetchApiKeys;
|
|
23
27
|
apiKeys = lbContext.apiKeys || [];
|
|
24
28
|
lbStatus = lbContext.status;
|
|
25
29
|
}
|
|
@@ -27,12 +31,6 @@ export function LBSigninModal({ isOpen, onClose }) {
|
|
|
27
31
|
// LBProvider n'est pas disponible, ne pas rendre le modal
|
|
28
32
|
return null;
|
|
29
33
|
}
|
|
30
|
-
// Si le status est "needs_key_selection", afficher le sélecteur
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
if (lbStatus === "needs_key_selection" && apiKeys.length > 0 && !showKeySelector) {
|
|
33
|
-
setShowKeySelector(true);
|
|
34
|
-
}
|
|
35
|
-
}, [lbStatus, apiKeys.length, showKeySelector]);
|
|
36
34
|
if (!isOpen || !login)
|
|
37
35
|
return null;
|
|
38
36
|
const handleSubmit = async (e) => {
|
|
@@ -44,7 +42,21 @@ export function LBSigninModal({ isOpen, onClose }) {
|
|
|
44
42
|
if (result.success) {
|
|
45
43
|
if (result.needsKeySelection) {
|
|
46
44
|
// L'utilisateur doit choisir une clé API
|
|
47
|
-
|
|
45
|
+
// Utiliser l'access token retourné directement par login
|
|
46
|
+
if (fetchApiKeys && result.accessToken) {
|
|
47
|
+
try {
|
|
48
|
+
const keys = await fetchApiKeys(result.accessToken);
|
|
49
|
+
setCurrentApiKeys(keys);
|
|
50
|
+
setShowKeySelector(true);
|
|
51
|
+
}
|
|
52
|
+
catch (keyError) {
|
|
53
|
+
setError("Erreur lors de la récupération des clés API");
|
|
54
|
+
console.error("Failed to fetch API keys:", keyError);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
setError("Token d'accès non disponible");
|
|
59
|
+
}
|
|
48
60
|
}
|
|
49
61
|
else {
|
|
50
62
|
// Connexion réussie, fermer le modal
|
|
@@ -81,8 +93,8 @@ export function LBSigninModal({ isOpen, onClose }) {
|
|
|
81
93
|
onClose();
|
|
82
94
|
};
|
|
83
95
|
// Si on doit afficher le sélecteur de clés
|
|
84
|
-
if (showKeySelector &&
|
|
85
|
-
return (_jsx(LBApiKeySelector, { apiKeys:
|
|
96
|
+
if (showKeySelector && currentApiKeys.length > 0) {
|
|
97
|
+
return (_jsx(LBApiKeySelector, { apiKeys: currentApiKeys, onSelect: handleKeySelect, onCancel: handleCancelKeySelection, isOpen: true }));
|
|
86
98
|
}
|
|
87
99
|
const handleKeyDown = (e) => {
|
|
88
100
|
if (e.key === "Escape") {
|
|
@@ -307,7 +319,7 @@ export function LBSigninModal({ isOpen, onClose }) {
|
|
|
307
319
|
fontSize: "14px",
|
|
308
320
|
color: "light-dark(#64748b, #94a3b8)",
|
|
309
321
|
fontWeight: 500,
|
|
310
|
-
}, children: "Pas encore de compte ?" }), _jsxs("a", { href: "https://lastbrain.io/signup", target: "_blank", rel: "noopener noreferrer", style: {
|
|
322
|
+
}, children: "Pas encore de compte ?" }), _jsxs("a", { href: "https://prompt.lastbrain.io/signup", target: "_blank", rel: "noopener noreferrer", style: {
|
|
311
323
|
display: "inline-flex",
|
|
312
324
|
alignItems: "center",
|
|
313
325
|
gap: "8px",
|
|
@@ -21,6 +21,7 @@ interface LBContextValue extends LBAuthState {
|
|
|
21
21
|
success: boolean;
|
|
22
22
|
error?: string;
|
|
23
23
|
needsKeySelection?: boolean;
|
|
24
|
+
accessToken?: string;
|
|
24
25
|
}>;
|
|
25
26
|
/** Fonction de déconnexion */
|
|
26
27
|
logout: () => Promise<void>;
|
|
@@ -30,6 +31,8 @@ interface LBContextValue extends LBAuthState {
|
|
|
30
31
|
selectApiKey: (accessToken: string, apiKeyId: string) => Promise<void>;
|
|
31
32
|
/** Sélectionne une clé API avec le token stocké */
|
|
32
33
|
selectApiKeyWithToken: (apiKeyId: string) => Promise<void>;
|
|
34
|
+
/** Change l'API key active (fonctionne avec session ou token) */
|
|
35
|
+
switchApiKey: (apiKeyId: string) => Promise<void>;
|
|
33
36
|
/** Recharge l'état de la session */
|
|
34
37
|
refreshSession: () => Promise<void>;
|
|
35
38
|
/** Clés API disponibles */
|
|
@@ -40,6 +43,8 @@ interface LBContextValue extends LBAuthState {
|
|
|
40
43
|
apiStatus: AiStatus | null;
|
|
41
44
|
/** Fonction pour rafraîchir le status */
|
|
42
45
|
refreshStatus: () => Promise<void>;
|
|
46
|
+
/** Indique si le status est en cours de chargement */
|
|
47
|
+
isLoadingStatus: boolean;
|
|
43
48
|
}
|
|
44
49
|
export declare function LBProvider({ children, baseUrl: _baseUrl, proxyUrl, onStatusChange, onAuthChange, }: LBProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
45
50
|
/**
|
|
@@ -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,EAIR,QAAQ,EACT,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;
|
|
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,EAIR,QAAQ,EACT,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;QAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,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,iEAAiE;IACjE,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,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;IACrB,kDAAkD;IAClD,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,yCAAyC;IACzC,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,sDAAsD;IACtD,eAAe,EAAE,OAAO,CAAC;CAC1B;AAID,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,EAAE,QAA2B,EACpC,QAA2B,EAC3B,cAAc,EACd,YAAY,GACb,EAAE,eAAe,2CA4djB;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,cAAc,CAMtC"}
|
|
@@ -13,6 +13,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
13
13
|
const [apiKeys, setApiKeys] = useState([]);
|
|
14
14
|
const [accessToken, setAccessToken] = useState();
|
|
15
15
|
const [apiStatus, setApiStatus] = useState(null);
|
|
16
|
+
const [isLoadingStatus, setIsLoadingStatus] = useState(false);
|
|
16
17
|
/**
|
|
17
18
|
* Vérifie si une session existe au chargement
|
|
18
19
|
*/
|
|
@@ -228,7 +229,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
228
229
|
status: "needs_key_selection",
|
|
229
230
|
user: result.user,
|
|
230
231
|
});
|
|
231
|
-
return { success: true, needsKeySelection: true };
|
|
232
|
+
return { success: true, needsKeySelection: true, accessToken: token };
|
|
232
233
|
}
|
|
233
234
|
catch (keyError) {
|
|
234
235
|
console.error("[LBProvider] Failed to fetch API keys:");
|
|
@@ -255,6 +256,36 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
255
256
|
return { success: false, error: message };
|
|
256
257
|
}
|
|
257
258
|
}, [proxyUrl, fetchApiKeys, selectApiKey]);
|
|
259
|
+
/**
|
|
260
|
+
* Récupère le status API (balance, storage, API key info)
|
|
261
|
+
*/
|
|
262
|
+
const refreshStatus = useCallback(async () => {
|
|
263
|
+
if (state.status !== "ready") {
|
|
264
|
+
setApiStatus(null);
|
|
265
|
+
setIsLoadingStatus(false);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
setIsLoadingStatus(true);
|
|
269
|
+
try {
|
|
270
|
+
const response = await fetch(`${proxyUrl}/auth/status`, {
|
|
271
|
+
credentials: "include",
|
|
272
|
+
});
|
|
273
|
+
if (response.ok) {
|
|
274
|
+
const data = await response.json();
|
|
275
|
+
setApiStatus(data);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
setApiStatus(null);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
console.error("[LBProvider] Failed to fetch status:", error);
|
|
283
|
+
setApiStatus(null);
|
|
284
|
+
}
|
|
285
|
+
finally {
|
|
286
|
+
setIsLoadingStatus(false);
|
|
287
|
+
}
|
|
288
|
+
}, [proxyUrl, state.status]);
|
|
258
289
|
/**
|
|
259
290
|
* Sélectionne une clé API avec le token déjà stocké (après login)
|
|
260
291
|
*/
|
|
@@ -264,6 +295,33 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
264
295
|
}
|
|
265
296
|
await selectApiKey(accessToken, apiKeyId);
|
|
266
297
|
}, [accessToken, selectApiKey]);
|
|
298
|
+
/**
|
|
299
|
+
* Change l'API key active - utilise la bonne méthode selon le contexte
|
|
300
|
+
*/
|
|
301
|
+
const switchApiKey = useCallback(async (apiKeyId) => {
|
|
302
|
+
if (state.status === "ready") {
|
|
303
|
+
// Utiliser la route de switch avec session
|
|
304
|
+
const response = await fetch(`${proxyUrl}/auth/session/switch-api-key`, {
|
|
305
|
+
method: "POST",
|
|
306
|
+
credentials: "include",
|
|
307
|
+
headers: { "Content-Type": "application/json" },
|
|
308
|
+
body: JSON.stringify({ api_key_id: apiKeyId }),
|
|
309
|
+
});
|
|
310
|
+
if (!response.ok) {
|
|
311
|
+
const errorData = await response.json().catch(() => ({}));
|
|
312
|
+
throw new Error(errorData.error || "Failed to switch API key");
|
|
313
|
+
}
|
|
314
|
+
// Refresh le status après le changement
|
|
315
|
+
await refreshStatus();
|
|
316
|
+
}
|
|
317
|
+
else if (accessToken) {
|
|
318
|
+
// Utiliser la méthode avec access token
|
|
319
|
+
await selectApiKey(accessToken, apiKeyId);
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
throw new Error("No valid authentication method available");
|
|
323
|
+
}
|
|
324
|
+
}, [state.status, proxyUrl, accessToken, selectApiKey, refreshStatus]);
|
|
267
325
|
/**
|
|
268
326
|
* Déconnexion
|
|
269
327
|
*/
|
|
@@ -291,31 +349,6 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
291
349
|
const refreshSession = useCallback(async () => {
|
|
292
350
|
await checkSession();
|
|
293
351
|
}, [checkSession]);
|
|
294
|
-
/**
|
|
295
|
-
* Récupère le status API (balance, storage, API key info)
|
|
296
|
-
*/
|
|
297
|
-
const refreshStatus = useCallback(async () => {
|
|
298
|
-
if (state.status !== "ready") {
|
|
299
|
-
setApiStatus(null);
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
try {
|
|
303
|
-
const response = await fetch(`${proxyUrl}/auth/status`, {
|
|
304
|
-
credentials: "include",
|
|
305
|
-
});
|
|
306
|
-
if (response.ok) {
|
|
307
|
-
const data = await response.json();
|
|
308
|
-
setApiStatus(data);
|
|
309
|
-
}
|
|
310
|
-
else {
|
|
311
|
-
setApiStatus(null);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
catch (error) {
|
|
315
|
-
console.error("[LBProvider] Failed to fetch status:", error);
|
|
316
|
-
setApiStatus(null);
|
|
317
|
-
}
|
|
318
|
-
}, [proxyUrl, state.status]);
|
|
319
352
|
/**
|
|
320
353
|
* Récupère les clés API avec la session active
|
|
321
354
|
*/
|
|
@@ -362,11 +395,13 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
362
395
|
fetchApiKeys,
|
|
363
396
|
selectApiKey,
|
|
364
397
|
selectApiKeyWithToken,
|
|
398
|
+
switchApiKey,
|
|
365
399
|
refreshSession,
|
|
366
400
|
apiKeys,
|
|
367
401
|
accessToken,
|
|
368
402
|
apiStatus,
|
|
369
403
|
refreshStatus,
|
|
404
|
+
isLoadingStatus,
|
|
370
405
|
};
|
|
371
406
|
return _jsx(LBContext.Provider, { value: value, children: children });
|
|
372
407
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../../src/styles/inline.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6HH,eAAO,MAAM,QAAQ;WAgBd,KAAK,CAAC,aAAa;gBAKnB,KAAK,CAAC,aAAa;gBAInB,KAAK,CAAC,aAAa;;;;;mBAyBnB,KAAK,CAAC,aAAa;wBAMnB,KAAK,CAAC,aAAa;cAmBnB,KAAK,CAAC,aAAa;mBAKnB,KAAK,CAAC,aAAa;;;;;sBAwBnB,KAAK,CAAC,aAAa;2BAMnB,KAAK,CAAC,aAAa;YAoBnB,KAAK,CAAC,aAAa;iBAMnB,KAAK,CAAC,aAAa;oBAMnB,KAAK,CAAC,aAAa;YA2BnB,KAAK,CAAC,aAAa;iBAKnB,KAAK,CAAC,aAAa;kBAgBnB,KAAK,CAAC,aAAa;uBAMnB,KAAK,CAAC,aAAa;0BAKnB,KAAK,CAAC,aAAa;aAgBnB,KAAK,CAAC,aAAa;mBASnB,KAAK,CAAC,aAAa;oBAKnB,KAAK,CAAC,aAAa;yBAKnB,KAAK,CAAC,aAAa;qBASnB,KAAK,CAAC,aAAa;gBAQnB,KAAK,CAAC,aAAa;kBAKnB,KAAK,CAAC,aAAa;kBAQnB,KAAK,CAAC,aAAa;sBAKnB,KAAK,CAAC,aAAa;yBAKnB,KAAK,CAAC,aAAa;yBAKnB,KAAK,CAAC,aAAa;oBASnB,KAAK,CAAC,aAAa;iBAcnB,KAAK,CAAC,aAAa;sBAKnB,KAAK,CAAC,aAAa;UAcnB,KAAK,CAAC,aAAa;WAenB,KAAK,CAAC,aAAa;kBAWnB,KAAK,CAAC,aAAa;kBAanB,KAAK,CAAC,aAAa;iBAQnB,KAAK,CAAC,aAAa;gBAOnB,KAAK,CAAC,aAAa;sBAiBnB,KAAK,CAAC,aAAa;2BAKnB,KAAK,CAAC,aAAa;eAMnB,KAAK,CAAC,aAAa;iBAQnB,KAAK,CAAC,aAAa;gBAQnB,KAAK,CAAC,aAAa;qBAInB,KAAK,CAAC,aAAa;
|
|
1
|
+
{"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../../src/styles/inline.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6HH,eAAO,MAAM,QAAQ;WAgBd,KAAK,CAAC,aAAa;gBAKnB,KAAK,CAAC,aAAa;gBAInB,KAAK,CAAC,aAAa;;;;;mBAyBnB,KAAK,CAAC,aAAa;wBAMnB,KAAK,CAAC,aAAa;cAmBnB,KAAK,CAAC,aAAa;mBAKnB,KAAK,CAAC,aAAa;;;;;sBAwBnB,KAAK,CAAC,aAAa;2BAMnB,KAAK,CAAC,aAAa;YAoBnB,KAAK,CAAC,aAAa;iBAMnB,KAAK,CAAC,aAAa;oBAMnB,KAAK,CAAC,aAAa;YA2BnB,KAAK,CAAC,aAAa;iBAKnB,KAAK,CAAC,aAAa;kBAgBnB,KAAK,CAAC,aAAa;uBAMnB,KAAK,CAAC,aAAa;0BAKnB,KAAK,CAAC,aAAa;aAgBnB,KAAK,CAAC,aAAa;mBASnB,KAAK,CAAC,aAAa;oBAKnB,KAAK,CAAC,aAAa;yBAKnB,KAAK,CAAC,aAAa;qBASnB,KAAK,CAAC,aAAa;gBAQnB,KAAK,CAAC,aAAa;kBAKnB,KAAK,CAAC,aAAa;kBAQnB,KAAK,CAAC,aAAa;sBAKnB,KAAK,CAAC,aAAa;yBAKnB,KAAK,CAAC,aAAa;yBAKnB,KAAK,CAAC,aAAa;oBASnB,KAAK,CAAC,aAAa;iBAcnB,KAAK,CAAC,aAAa;sBAKnB,KAAK,CAAC,aAAa;UAcnB,KAAK,CAAC,aAAa;WAenB,KAAK,CAAC,aAAa;kBAWnB,KAAK,CAAC,aAAa;kBAanB,KAAK,CAAC,aAAa;iBAQnB,KAAK,CAAC,aAAa;gBAOnB,KAAK,CAAC,aAAa;sBAiBnB,KAAK,CAAC,aAAa;2BAKnB,KAAK,CAAC,aAAa;eAMnB,KAAK,CAAC,aAAa;iBAQnB,KAAK,CAAC,aAAa;gBAQnB,KAAK,CAAC,aAAa;qBAInB,KAAK,CAAC,aAAa;qBASnB,KAAK,CAAC,aAAa;0BAKnB,KAAK,CAAC,aAAa;aAUnB,KAAK,CAAC,aAAa;CACzB,CAAC;AAoCF,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,OAAO,EACnB,YAAY,GAAE,MAAY,EAC1B,aAAa,GAAE,MAAY,GAC1B;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAoDlE"}
|
package/dist/styles/inline.js
CHANGED
|
@@ -489,7 +489,9 @@ export const aiStyles = {
|
|
|
489
489
|
buttonSecondary: {
|
|
490
490
|
background: themeVars.bgSecondary,
|
|
491
491
|
color: themeVars.text,
|
|
492
|
-
|
|
492
|
+
borderWidth: "1px",
|
|
493
|
+
borderStyle: "solid",
|
|
494
|
+
borderColor: themeVars.border,
|
|
493
495
|
},
|
|
494
496
|
buttonSecondaryHover: {
|
|
495
497
|
background: themeVars.bgTertiary,
|
|
@@ -529,6 +531,11 @@ if (typeof document !== "undefined") {
|
|
|
529
531
|
transform: translateY(0);
|
|
530
532
|
}
|
|
531
533
|
}
|
|
534
|
+
|
|
535
|
+
@keyframes pulse {
|
|
536
|
+
0%, 100% { opacity: 1; }
|
|
537
|
+
50% { opacity: 0.6; }
|
|
538
|
+
}
|
|
532
539
|
`;
|
|
533
540
|
document.head.appendChild(styleSheet);
|
|
534
541
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modelManagement.d.ts","sourceRoot":"","sources":["../../src/utils/modelManagement.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,OAAO,EACjB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CACR,KAAK,CAAC;IACJ,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,CAAC,CACH,CA6CA;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,MAAM,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"modelManagement.d.ts","sourceRoot":"","sources":["../../src/utils/modelManagement.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,OAAO,EACjB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CACR,KAAK,CAAC;IACJ,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,CAAC,CACH,CA6CA;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,MAAM,EAAE,CAAC,CAkDnB"}
|
|
@@ -85,9 +85,15 @@ export async function getUserModels(options = {}) {
|
|
|
85
85
|
: `/api/ai/auth/user-models`; // fallback
|
|
86
86
|
console.log("[getUserModels] isExternalProxy:", isExternalProxy, "isPublicApi:", isPublicApi, "endpoint:", endpoint);
|
|
87
87
|
const headers = {};
|
|
88
|
-
// Ajouter la clé API pour les appels
|
|
89
|
-
if (
|
|
90
|
-
|
|
88
|
+
// Ajouter la clé API pour tous les types d'appels si disponible
|
|
89
|
+
if (apiKey) {
|
|
90
|
+
if (isPublicApi) {
|
|
91
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Pour les routes auth internes, utiliser aussi le Bearer token si pas de session
|
|
95
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
96
|
+
}
|
|
91
97
|
}
|
|
92
98
|
const response = await fetch(endpoint, {
|
|
93
99
|
method: "GET",
|
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.56",
|
|
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.43"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/react": "^19.2.0",
|
|
@@ -425,7 +425,6 @@ function AiPromptPanelInternal({
|
|
|
425
425
|
top: 0,
|
|
426
426
|
zIndex: 5,
|
|
427
427
|
|
|
428
|
-
borderBottom: "1px solid var(--ai-border-primary, #374151)",
|
|
429
428
|
backdropFilter: "blur(8px)",
|
|
430
429
|
}}
|
|
431
430
|
>
|
|
@@ -1004,7 +1003,6 @@ function AiPromptPanelInternal({
|
|
|
1004
1003
|
bottom: 0,
|
|
1005
1004
|
zIndex: 5,
|
|
1006
1005
|
|
|
1007
|
-
borderTop: "1px solid var(--ai-border-primary, #374151)",
|
|
1008
1006
|
backdropFilter: "blur(8px)",
|
|
1009
1007
|
}}
|
|
1010
1008
|
>
|
|
@@ -1183,14 +1181,14 @@ function AiPromptPanelInternal({
|
|
|
1183
1181
|
alignItems: "center",
|
|
1184
1182
|
justifyContent: "space-between",
|
|
1185
1183
|
padding: "16px 18px",
|
|
1186
|
-
border: "2px solid",
|
|
1187
|
-
borderColor: isActive
|
|
1188
|
-
|
|
1189
|
-
|
|
1184
|
+
// border: "2px solid",
|
|
1185
|
+
// borderColor: isActive
|
|
1186
|
+
// ? "#10b981"
|
|
1187
|
+
// : "var(--ai-border-primary, #374151)",
|
|
1190
1188
|
borderRadius: "10px",
|
|
1191
1189
|
backgroundColor: isActive
|
|
1192
|
-
? "rgba(16, 185, 129, 0.
|
|
1193
|
-
: "var(--ai-bg-secondary, rgba(31, 41, 55, 0.
|
|
1190
|
+
? "rgba(16, 185, 129, 0.03)"
|
|
1191
|
+
: "var(--ai-bg-secondary, rgba(31, 41, 55, 0.2))",
|
|
1194
1192
|
transition: "all 0.2s",
|
|
1195
1193
|
cursor: "pointer",
|
|
1196
1194
|
backdropFilter: "blur(8px)",
|
|
@@ -1200,18 +1198,18 @@ function AiPromptPanelInternal({
|
|
|
1200
1198
|
handleModelToggle(modelData.id, !isActive)
|
|
1201
1199
|
}
|
|
1202
1200
|
onMouseEnter={(e) => {
|
|
1203
|
-
e.currentTarget.style.borderColor = isActive
|
|
1204
|
-
|
|
1205
|
-
|
|
1201
|
+
// e.currentTarget.style.borderColor = isActive
|
|
1202
|
+
// ? "#059669"
|
|
1203
|
+
// : "#4b5563";
|
|
1206
1204
|
e.currentTarget.style.transform = "translateY(-2px)";
|
|
1207
1205
|
e.currentTarget.style.boxShadow = isActive
|
|
1208
1206
|
? "0 8px 16px rgba(16, 185, 129, 0.2)"
|
|
1209
1207
|
: "0 8px 16px rgba(0, 0, 0, 0.15)";
|
|
1210
1208
|
}}
|
|
1211
1209
|
onMouseLeave={(e) => {
|
|
1212
|
-
e.currentTarget.style.borderColor = isActive
|
|
1213
|
-
|
|
1214
|
-
|
|
1210
|
+
// e.currentTarget.style.borderColor = isActive
|
|
1211
|
+
// ? "#10b981"
|
|
1212
|
+
// : "var(--ai-border-primary, #374151)";
|
|
1215
1213
|
e.currentTarget.style.transform = "translateY(0)";
|
|
1216
1214
|
e.currentTarget.style.boxShadow = "none";
|
|
1217
1215
|
}}
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
FolderPlus,
|
|
12
12
|
Power,
|
|
13
13
|
LogOut,
|
|
14
|
-
Key,
|
|
15
14
|
ArrowRightLeft,
|
|
16
15
|
} from "lucide-react";
|
|
17
16
|
import { aiStyles, calculateTooltipPosition } from "../styles/inline";
|
|
@@ -38,7 +37,9 @@ export function AiStatusButton({
|
|
|
38
37
|
let apiKeys: any[] = [];
|
|
39
38
|
let accessToken: string | undefined;
|
|
40
39
|
let selectApiKeyWithToken: ((apiKeyId: string) => Promise<void>) | undefined;
|
|
40
|
+
let switchApiKey: ((apiKeyId: string) => Promise<void>) | undefined;
|
|
41
41
|
let lbApiStatus: any = null;
|
|
42
|
+
let lbIsLoadingStatus: boolean = false;
|
|
42
43
|
|
|
43
44
|
try {
|
|
44
45
|
const lbContext = useLB();
|
|
@@ -48,7 +49,9 @@ export function AiStatusButton({
|
|
|
48
49
|
apiKeys = lbContext.apiKeys || [];
|
|
49
50
|
accessToken = lbContext.accessToken;
|
|
50
51
|
selectApiKeyWithToken = lbContext.selectApiKeyWithToken;
|
|
52
|
+
switchApiKey = lbContext.switchApiKey;
|
|
51
53
|
lbApiStatus = lbContext.apiStatus;
|
|
54
|
+
lbIsLoadingStatus = lbContext.isLoadingStatus || false;
|
|
52
55
|
} catch {
|
|
53
56
|
// LBProvider n'est pas disponible, ignorer
|
|
54
57
|
lbStatus = undefined;
|
|
@@ -71,6 +74,8 @@ export function AiStatusButton({
|
|
|
71
74
|
|
|
72
75
|
const [showSigninModal, setShowSigninModal] = useState(false);
|
|
73
76
|
const [showApiKeySelector, setShowApiKeySelector] = useState(false);
|
|
77
|
+
const [isSelectingApiKey, setIsSelectingApiKey] = useState(false);
|
|
78
|
+
const [isLoadingStatus, setIsLoadingStatus] = useState(false);
|
|
74
79
|
|
|
75
80
|
type BalanceUsage = {
|
|
76
81
|
used?: number;
|
|
@@ -280,7 +285,7 @@ export function AiStatusButton({
|
|
|
280
285
|
}, 100);
|
|
281
286
|
};
|
|
282
287
|
|
|
283
|
-
if (loading) {
|
|
288
|
+
if (loading || isSelectingApiKey || isLoadingStatus || lbIsLoadingStatus) {
|
|
284
289
|
return (
|
|
285
290
|
<button
|
|
286
291
|
ref={buttonRef}
|
|
@@ -737,11 +742,23 @@ export function AiStatusButton({
|
|
|
737
742
|
}}
|
|
738
743
|
>
|
|
739
744
|
<span style={aiStyles.tooltipValue}>
|
|
740
|
-
{
|
|
741
|
-
|
|
742
|
-
|
|
745
|
+
{lbIsLoadingStatus ? (
|
|
746
|
+
<div
|
|
747
|
+
style={{
|
|
748
|
+
height: "16px",
|
|
749
|
+
width: "100px",
|
|
750
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
751
|
+
borderRadius: "4px",
|
|
752
|
+
animation: "pulse 2s ease-in-out infinite",
|
|
753
|
+
}}
|
|
754
|
+
/>
|
|
755
|
+
) : (
|
|
756
|
+
effectiveStatus?.apiKey?.name ||
|
|
757
|
+
effectiveStatus?.api_key?.name ||
|
|
758
|
+
"Unknown"
|
|
759
|
+
)}
|
|
743
760
|
</span>
|
|
744
|
-
{
|
|
761
|
+
{switchApiKey && (
|
|
745
762
|
<button
|
|
746
763
|
onClick={(e) => {
|
|
747
764
|
e.stopPropagation();
|
|
@@ -998,38 +1015,6 @@ export function AiStatusButton({
|
|
|
998
1015
|
<FolderPlus size={18} />
|
|
999
1016
|
</button>
|
|
1000
1017
|
|
|
1001
|
-
{/* Change API Key Button */}
|
|
1002
|
-
{selectApiKeyWithToken && apiKeys.length > 1 && (
|
|
1003
|
-
<button
|
|
1004
|
-
onClick={() => setShowApiKeySelector(true)}
|
|
1005
|
-
style={{
|
|
1006
|
-
background: "transparent",
|
|
1007
|
-
border: "none",
|
|
1008
|
-
padding: "14px",
|
|
1009
|
-
cursor: "pointer",
|
|
1010
|
-
display: "flex",
|
|
1011
|
-
alignItems: "center",
|
|
1012
|
-
justifyContent: "center",
|
|
1013
|
-
color: "#8b5cf6",
|
|
1014
|
-
transition: "all 0.2s ease",
|
|
1015
|
-
borderRadius: "4px",
|
|
1016
|
-
}}
|
|
1017
|
-
onMouseEnter={(e) => {
|
|
1018
|
-
Object.assign(e.currentTarget.style, {
|
|
1019
|
-
background: "rgba(139, 92, 246, 0.1)",
|
|
1020
|
-
});
|
|
1021
|
-
}}
|
|
1022
|
-
onMouseLeave={(e) => {
|
|
1023
|
-
Object.assign(e.currentTarget.style, {
|
|
1024
|
-
background: "transparent",
|
|
1025
|
-
});
|
|
1026
|
-
}}
|
|
1027
|
-
title="Change API Key"
|
|
1028
|
-
>
|
|
1029
|
-
<Key size={18} />
|
|
1030
|
-
</button>
|
|
1031
|
-
)}
|
|
1032
|
-
|
|
1033
1018
|
{/* Logout Button */}
|
|
1034
1019
|
{logout && (
|
|
1035
1020
|
<button
|
|
@@ -1078,21 +1063,31 @@ export function AiStatusButton({
|
|
|
1078
1063
|
)}
|
|
1079
1064
|
|
|
1080
1065
|
{/* API Key Selector Modal */}
|
|
1081
|
-
{showApiKeySelector &&
|
|
1066
|
+
{showApiKeySelector && apiKeys.length > 0 && (
|
|
1082
1067
|
<LBApiKeySelector
|
|
1083
1068
|
isOpen={showApiKeySelector}
|
|
1084
1069
|
apiKeys={apiKeys}
|
|
1085
1070
|
onSelect={async (keyId) => {
|
|
1071
|
+
setIsSelectingApiKey(true);
|
|
1086
1072
|
try {
|
|
1087
|
-
|
|
1073
|
+
// Utiliser la nouvelle fonction switchApiKey qui gère automatiquement le contexte
|
|
1074
|
+
if (switchApiKey) {
|
|
1075
|
+
await switchApiKey(keyId);
|
|
1076
|
+
} else {
|
|
1077
|
+
throw new Error("Switch API key function not available");
|
|
1078
|
+
}
|
|
1088
1079
|
setShowApiKeySelector(false);
|
|
1089
1080
|
setShowTooltip(false);
|
|
1081
|
+
setIsLoadingStatus(true);
|
|
1090
1082
|
// Refresh provider data after API key selection
|
|
1091
1083
|
if (refetchProviders) {
|
|
1092
1084
|
await refetchProviders();
|
|
1093
1085
|
}
|
|
1094
1086
|
} catch (error) {
|
|
1095
1087
|
console.error("Failed to select API key:", error);
|
|
1088
|
+
} finally {
|
|
1089
|
+
setIsSelectingApiKey(false);
|
|
1090
|
+
setIsLoadingStatus(false);
|
|
1096
1091
|
}
|
|
1097
1092
|
}}
|
|
1098
1093
|
onCancel={() => setShowApiKeySelector(false)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useState
|
|
3
|
+
import { useState } from "react";
|
|
4
4
|
import { useLB } from "../context/LBAuthProvider";
|
|
5
5
|
import { LBApiKeySelector } from "./LBApiKeySelector";
|
|
6
6
|
import { Mail, Lock, Sparkles, X, Loader2, AlertCircle } from "lucide-react";
|
|
@@ -17,6 +17,7 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
17
17
|
const [loading, setLoading] = useState(false);
|
|
18
18
|
const [error, setError] = useState("");
|
|
19
19
|
const [showKeySelector, setShowKeySelector] = useState(false);
|
|
20
|
+
const [currentApiKeys, setCurrentApiKeys] = useState<any[]>([]); // Stocker les clés API localement
|
|
20
21
|
|
|
21
22
|
// Vérifier si LBProvider est disponible
|
|
22
23
|
let login:
|
|
@@ -27,16 +28,20 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
27
28
|
success: boolean;
|
|
28
29
|
error?: string;
|
|
29
30
|
needsKeySelection?: boolean;
|
|
31
|
+
accessToken?: string;
|
|
30
32
|
}>)
|
|
31
33
|
| undefined;
|
|
32
34
|
let selectApiKeyWithToken: ((apiKeyId: string) => Promise<void>) | undefined;
|
|
35
|
+
let fetchApiKeys: ((accessToken: string) => Promise<any[]>) | undefined;
|
|
33
36
|
let apiKeys: any[] = [];
|
|
34
37
|
let lbStatus: string | undefined;
|
|
35
38
|
|
|
39
|
+
let lbContext: any;
|
|
36
40
|
try {
|
|
37
|
-
|
|
41
|
+
lbContext = useLB();
|
|
38
42
|
login = lbContext.login;
|
|
39
43
|
selectApiKeyWithToken = lbContext.selectApiKeyWithToken;
|
|
44
|
+
fetchApiKeys = lbContext.fetchApiKeys;
|
|
40
45
|
apiKeys = lbContext.apiKeys || [];
|
|
41
46
|
lbStatus = lbContext.status;
|
|
42
47
|
} catch {
|
|
@@ -44,13 +49,6 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
44
49
|
return null;
|
|
45
50
|
}
|
|
46
51
|
|
|
47
|
-
// Si le status est "needs_key_selection", afficher le sélecteur
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
if (lbStatus === "needs_key_selection" && apiKeys.length > 0 && !showKeySelector) {
|
|
50
|
-
setShowKeySelector(true);
|
|
51
|
-
}
|
|
52
|
-
}, [lbStatus, apiKeys.length, showKeySelector]);
|
|
53
|
-
|
|
54
52
|
if (!isOpen || !login) return null;
|
|
55
53
|
|
|
56
54
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
@@ -63,7 +61,19 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
63
61
|
if (result.success) {
|
|
64
62
|
if (result.needsKeySelection) {
|
|
65
63
|
// L'utilisateur doit choisir une clé API
|
|
66
|
-
|
|
64
|
+
// Utiliser l'access token retourné directement par login
|
|
65
|
+
if (fetchApiKeys && result.accessToken) {
|
|
66
|
+
try {
|
|
67
|
+
const keys = await fetchApiKeys(result.accessToken);
|
|
68
|
+
setCurrentApiKeys(keys);
|
|
69
|
+
setShowKeySelector(true);
|
|
70
|
+
} catch (keyError) {
|
|
71
|
+
setError("Erreur lors de la récupération des clés API");
|
|
72
|
+
console.error("Failed to fetch API keys:", keyError);
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
setError("Token d'accès non disponible");
|
|
76
|
+
}
|
|
67
77
|
} else {
|
|
68
78
|
// Connexion réussie, fermer le modal
|
|
69
79
|
onClose();
|
|
@@ -102,10 +112,10 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
102
112
|
};
|
|
103
113
|
|
|
104
114
|
// Si on doit afficher le sélecteur de clés
|
|
105
|
-
if (showKeySelector &&
|
|
115
|
+
if (showKeySelector && currentApiKeys.length > 0) {
|
|
106
116
|
return (
|
|
107
117
|
<LBApiKeySelector
|
|
108
|
-
apiKeys={
|
|
118
|
+
apiKeys={currentApiKeys}
|
|
109
119
|
onSelect={handleKeySelect}
|
|
110
120
|
onCancel={handleCancelKeySelection}
|
|
111
121
|
isOpen={true}
|
|
@@ -498,7 +508,7 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
|
|
|
498
508
|
Pas encore de compte ?
|
|
499
509
|
</p>
|
|
500
510
|
<a
|
|
501
|
-
href="https://lastbrain.io/signup"
|
|
511
|
+
href="https://prompt.lastbrain.io/signup"
|
|
502
512
|
target="_blank"
|
|
503
513
|
rel="noopener noreferrer"
|
|
504
514
|
style={{
|
|
@@ -43,6 +43,7 @@ interface LBContextValue extends LBAuthState {
|
|
|
43
43
|
success: boolean;
|
|
44
44
|
error?: string;
|
|
45
45
|
needsKeySelection?: boolean;
|
|
46
|
+
accessToken?: string;
|
|
46
47
|
}>;
|
|
47
48
|
/** Fonction de déconnexion */
|
|
48
49
|
logout: () => Promise<void>;
|
|
@@ -52,6 +53,8 @@ interface LBContextValue extends LBAuthState {
|
|
|
52
53
|
selectApiKey: (accessToken: string, apiKeyId: string) => Promise<void>;
|
|
53
54
|
/** Sélectionne une clé API avec le token stocké */
|
|
54
55
|
selectApiKeyWithToken: (apiKeyId: string) => Promise<void>;
|
|
56
|
+
/** Change l'API key active (fonctionne avec session ou token) */
|
|
57
|
+
switchApiKey: (apiKeyId: string) => Promise<void>;
|
|
55
58
|
/** Recharge l'état de la session */
|
|
56
59
|
refreshSession: () => Promise<void>;
|
|
57
60
|
/** Clés API disponibles */
|
|
@@ -62,6 +65,8 @@ interface LBContextValue extends LBAuthState {
|
|
|
62
65
|
apiStatus: AiStatus | null;
|
|
63
66
|
/** Fonction pour rafraîchir le status */
|
|
64
67
|
refreshStatus: () => Promise<void>;
|
|
68
|
+
/** Indique si le status est en cours de chargement */
|
|
69
|
+
isLoadingStatus: boolean;
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
const LBContext = createContext<LBContextValue | undefined>(undefined);
|
|
@@ -79,6 +84,7 @@ export function LBProvider({
|
|
|
79
84
|
const [apiKeys, setApiKeys] = useState<LBApiKey[]>([]);
|
|
80
85
|
const [accessToken, setAccessToken] = useState<string>();
|
|
81
86
|
const [apiStatus, setApiStatus] = useState<AiStatus | null>(null);
|
|
87
|
+
const [isLoadingStatus, setIsLoadingStatus] = useState(false);
|
|
82
88
|
|
|
83
89
|
/**
|
|
84
90
|
* Vérifie si une session existe au chargement
|
|
@@ -263,6 +269,7 @@ export function LBProvider({
|
|
|
263
269
|
success: boolean;
|
|
264
270
|
error?: string;
|
|
265
271
|
needsKeySelection?: boolean;
|
|
272
|
+
accessToken?: string;
|
|
266
273
|
}> => {
|
|
267
274
|
try {
|
|
268
275
|
console.log("[LBProvider] Login attempt:", email);
|
|
@@ -349,7 +356,7 @@ export function LBProvider({
|
|
|
349
356
|
status: "needs_key_selection",
|
|
350
357
|
user: result.user,
|
|
351
358
|
});
|
|
352
|
-
return { success: true, needsKeySelection: true };
|
|
359
|
+
return { success: true, needsKeySelection: true, accessToken: token };
|
|
353
360
|
} catch (keyError) {
|
|
354
361
|
console.error("[LBProvider] Failed to fetch API keys:");
|
|
355
362
|
console.error("[LBProvider] Error details:", keyError);
|
|
@@ -385,6 +392,36 @@ export function LBProvider({
|
|
|
385
392
|
[proxyUrl, fetchApiKeys, selectApiKey]
|
|
386
393
|
);
|
|
387
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Récupère le status API (balance, storage, API key info)
|
|
397
|
+
*/
|
|
398
|
+
const refreshStatus = useCallback(async (): Promise<void> => {
|
|
399
|
+
if (state.status !== "ready") {
|
|
400
|
+
setApiStatus(null);
|
|
401
|
+
setIsLoadingStatus(false);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
setIsLoadingStatus(true);
|
|
406
|
+
try {
|
|
407
|
+
const response = await fetch(`${proxyUrl}/auth/status`, {
|
|
408
|
+
credentials: "include",
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
if (response.ok) {
|
|
412
|
+
const data = await response.json();
|
|
413
|
+
setApiStatus(data);
|
|
414
|
+
} else {
|
|
415
|
+
setApiStatus(null);
|
|
416
|
+
}
|
|
417
|
+
} catch (error) {
|
|
418
|
+
console.error("[LBProvider] Failed to fetch status:", error);
|
|
419
|
+
setApiStatus(null);
|
|
420
|
+
} finally {
|
|
421
|
+
setIsLoadingStatus(false);
|
|
422
|
+
}
|
|
423
|
+
}, [proxyUrl, state.status]);
|
|
424
|
+
|
|
388
425
|
/**
|
|
389
426
|
* Sélectionne une clé API avec le token déjà stocké (après login)
|
|
390
427
|
*/
|
|
@@ -398,6 +435,40 @@ export function LBProvider({
|
|
|
398
435
|
[accessToken, selectApiKey]
|
|
399
436
|
);
|
|
400
437
|
|
|
438
|
+
/**
|
|
439
|
+
* Change l'API key active - utilise la bonne méthode selon le contexte
|
|
440
|
+
*/
|
|
441
|
+
const switchApiKey = useCallback(
|
|
442
|
+
async (apiKeyId: string): Promise<void> => {
|
|
443
|
+
if (state.status === "ready") {
|
|
444
|
+
// Utiliser la route de switch avec session
|
|
445
|
+
const response = await fetch(
|
|
446
|
+
`${proxyUrl}/auth/session/switch-api-key`,
|
|
447
|
+
{
|
|
448
|
+
method: "POST",
|
|
449
|
+
credentials: "include",
|
|
450
|
+
headers: { "Content-Type": "application/json" },
|
|
451
|
+
body: JSON.stringify({ api_key_id: apiKeyId }),
|
|
452
|
+
}
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
if (!response.ok) {
|
|
456
|
+
const errorData = await response.json().catch(() => ({}));
|
|
457
|
+
throw new Error(errorData.error || "Failed to switch API key");
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Refresh le status après le changement
|
|
461
|
+
await refreshStatus();
|
|
462
|
+
} else if (accessToken) {
|
|
463
|
+
// Utiliser la méthode avec access token
|
|
464
|
+
await selectApiKey(accessToken, apiKeyId);
|
|
465
|
+
} else {
|
|
466
|
+
throw new Error("No valid authentication method available");
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
[state.status, proxyUrl, accessToken, selectApiKey, refreshStatus]
|
|
470
|
+
);
|
|
471
|
+
|
|
401
472
|
/**
|
|
402
473
|
* Déconnexion
|
|
403
474
|
*/
|
|
@@ -425,32 +496,6 @@ export function LBProvider({
|
|
|
425
496
|
await checkSession();
|
|
426
497
|
}, [checkSession]);
|
|
427
498
|
|
|
428
|
-
/**
|
|
429
|
-
* Récupère le status API (balance, storage, API key info)
|
|
430
|
-
*/
|
|
431
|
-
const refreshStatus = useCallback(async (): Promise<void> => {
|
|
432
|
-
if (state.status !== "ready") {
|
|
433
|
-
setApiStatus(null);
|
|
434
|
-
return;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
try {
|
|
438
|
-
const response = await fetch(`${proxyUrl}/auth/status`, {
|
|
439
|
-
credentials: "include",
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
if (response.ok) {
|
|
443
|
-
const data = await response.json();
|
|
444
|
-
setApiStatus(data);
|
|
445
|
-
} else {
|
|
446
|
-
setApiStatus(null);
|
|
447
|
-
}
|
|
448
|
-
} catch (error) {
|
|
449
|
-
console.error("[LBProvider] Failed to fetch status:", error);
|
|
450
|
-
setApiStatus(null);
|
|
451
|
-
}
|
|
452
|
-
}, [proxyUrl, state.status]);
|
|
453
|
-
|
|
454
499
|
/**
|
|
455
500
|
* Récupère les clés API avec la session active
|
|
456
501
|
*/
|
|
@@ -498,11 +543,13 @@ export function LBProvider({
|
|
|
498
543
|
fetchApiKeys,
|
|
499
544
|
selectApiKey,
|
|
500
545
|
selectApiKeyWithToken,
|
|
546
|
+
switchApiKey,
|
|
501
547
|
refreshSession,
|
|
502
548
|
apiKeys,
|
|
503
549
|
accessToken,
|
|
504
550
|
apiStatus,
|
|
505
551
|
refreshStatus,
|
|
552
|
+
isLoadingStatus,
|
|
506
553
|
};
|
|
507
554
|
|
|
508
555
|
return <LBContext.Provider value={value}>{children}</LBContext.Provider>;
|
package/src/styles/inline.ts
CHANGED
|
@@ -557,7 +557,9 @@ export const aiStyles = {
|
|
|
557
557
|
buttonSecondary: {
|
|
558
558
|
background: themeVars.bgSecondary,
|
|
559
559
|
color: themeVars.text,
|
|
560
|
-
|
|
560
|
+
borderWidth: "1px",
|
|
561
|
+
borderStyle: "solid",
|
|
562
|
+
borderColor: themeVars.border,
|
|
561
563
|
} as React.CSSProperties,
|
|
562
564
|
|
|
563
565
|
buttonSecondaryHover: {
|
|
@@ -600,6 +602,11 @@ if (typeof document !== "undefined") {
|
|
|
600
602
|
transform: translateY(0);
|
|
601
603
|
}
|
|
602
604
|
}
|
|
605
|
+
|
|
606
|
+
@keyframes pulse {
|
|
607
|
+
0%, 100% { opacity: 1; }
|
|
608
|
+
50% { opacity: 0.6; }
|
|
609
|
+
}
|
|
603
610
|
`;
|
|
604
611
|
document.head.appendChild(styleSheet);
|
|
605
612
|
}
|
|
@@ -147,9 +147,14 @@ export async function getUserModels(
|
|
|
147
147
|
|
|
148
148
|
const headers: Record<string, string> = {};
|
|
149
149
|
|
|
150
|
-
// Ajouter la clé API pour les appels
|
|
151
|
-
if (
|
|
152
|
-
|
|
150
|
+
// Ajouter la clé API pour tous les types d'appels si disponible
|
|
151
|
+
if (apiKey) {
|
|
152
|
+
if (isPublicApi) {
|
|
153
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
154
|
+
} else {
|
|
155
|
+
// Pour les routes auth internes, utiliser aussi le Bearer token si pas de session
|
|
156
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
157
|
+
}
|
|
153
158
|
}
|
|
154
159
|
|
|
155
160
|
const response = await fetch(endpoint, {
|