@lastbrain/ai-ui-react 1.0.76 → 1.0.78
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.map +1 -1
- package/dist/components/AiContextButton.js +17 -1
- package/dist/components/AiImageButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +17 -1
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +17 -1
- package/dist/components/AiSelect.d.ts.map +1 -1
- package/dist/components/AiSelect.js +17 -1
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +17 -3
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +14 -5
- package/dist/components/LBApiKeySelector.d.ts.map +1 -1
- package/dist/components/LBApiKeySelector.js +2 -0
- package/dist/context/LBAuthProvider.d.ts +2 -0
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +33 -5
- package/dist/hooks/useAiStatus.d.ts.map +1 -1
- package/dist/hooks/useAiStatus.js +22 -7
- package/package.json +1 -1
- package/src/components/AiContextButton.tsx +19 -1
- package/src/components/AiImageButton.tsx +19 -1
- package/src/components/AiInput.tsx +19 -1
- package/src/components/AiSelect.tsx +19 -1
- package/src/components/AiStatusButton.tsx +25 -3
- package/src/components/AiTextarea.tsx +15 -7
- package/src/components/LBApiKeySelector.tsx +1 -0
- package/src/context/LBAuthProvider.tsx +36 -4
- package/src/hooks/useAiStatus.ts +22 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiContextButton.d.ts","sourceRoot":"","sources":["../../src/components/AiContextButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAY5D,KAAK,WAAW,GACZ,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,OAAO,EAAE,GACT;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,CAAC;AAE3B,MAAM,WAAW,oBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvE,WAAW,EAAE,WAAW,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,CACT,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,IAAW,EACX,MAAe,EACf,OAAmB,EACnB,OAAO,EAAE,QAAQ,EACjB,GAAG,WAAW,EACf,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"AiContextButton.d.ts","sourceRoot":"","sources":["../../src/components/AiContextButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAY5D,KAAK,WAAW,GACZ,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,OAAO,EAAE,GACT;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,CAAC;AAE3B,MAAM,WAAW,oBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvE,WAAW,EAAE,WAAW,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,CACT,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,IAAW,EACX,MAAe,EACf,OAAmB,EACnB,OAAO,EAAE,QAAQ,EACjB,GAAG,WAAW,EACf,EAAE,oBAAoB,2CA+TtB"}
|
|
@@ -22,12 +22,23 @@ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId,
|
|
|
22
22
|
const { showErrorToast, errorData, errorKey, clearError } = useErrorToast();
|
|
23
23
|
const resolvedContextDescription = contextDescription || t("ai.context.description", "Data to analyze");
|
|
24
24
|
let lbStatus;
|
|
25
|
+
let lbHasSession = false;
|
|
26
|
+
let lbHasSelectedKey = false;
|
|
27
|
+
let lbHasSelectedApiKeyCookie = false;
|
|
28
|
+
let hasLBProvider = false;
|
|
25
29
|
try {
|
|
26
30
|
const lbContext = useLB();
|
|
27
31
|
lbStatus = lbContext.status;
|
|
32
|
+
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
33
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
34
|
+
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
35
|
+
hasLBProvider = true;
|
|
28
36
|
}
|
|
29
37
|
catch {
|
|
30
38
|
lbStatus = undefined;
|
|
39
|
+
lbHasSession = false;
|
|
40
|
+
lbHasSelectedKey = false;
|
|
41
|
+
hasLBProvider = false;
|
|
31
42
|
}
|
|
32
43
|
const aiContext = useAiContext();
|
|
33
44
|
const baseUrl = propBaseUrl ?? aiContext.baseUrl;
|
|
@@ -36,7 +47,12 @@ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId,
|
|
|
36
47
|
baseUrl,
|
|
37
48
|
apiKeyId,
|
|
38
49
|
});
|
|
39
|
-
const
|
|
50
|
+
const needsApiKeySelection = hasLBProvider &&
|
|
51
|
+
lbStatus === "ready" &&
|
|
52
|
+
lbHasSession &&
|
|
53
|
+
(!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
|
|
54
|
+
const isAuthReady = !needsApiKeySelection &&
|
|
55
|
+
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
40
56
|
const handleOpenPanel = () => {
|
|
41
57
|
if (!isAuthReady) {
|
|
42
58
|
setShowAuthModal(true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiImageButton.d.ts","sourceRoot":"","sources":["../../src/components/AiImageButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAS5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAY5D,MAAM,WAAW,kBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvE,OAAO,CAAC,EAAE,CACR,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,aAAoB,EACpB,WAAW,EACX,YAAY,EACZ,aAAa,EACb,IAAW,EACX,MAAe,EACf,OAAmB,EACnB,GAAG,WAAW,EACf,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"AiImageButton.d.ts","sourceRoot":"","sources":["../../src/components/AiImageButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAS5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAY5D,MAAM,WAAW,kBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvE,OAAO,CAAC,EAAE,CACR,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,aAAoB,EACpB,WAAW,EACX,YAAY,EACZ,aAAa,EACb,IAAW,EACX,MAAe,EACf,OAAmB,EACnB,GAAG,WAAW,EACf,EAAE,kBAAkB,2CAsSpB"}
|
|
@@ -22,13 +22,24 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
|
|
|
22
22
|
const { showErrorToast, errorData, errorKey, clearError } = useErrorToast();
|
|
23
23
|
// Rendre l'authentification optionnelle
|
|
24
24
|
let lbStatus;
|
|
25
|
+
let lbHasSession = false;
|
|
26
|
+
let lbHasSelectedKey = false;
|
|
27
|
+
let lbHasSelectedApiKeyCookie = false;
|
|
28
|
+
let hasLBProvider = false;
|
|
25
29
|
try {
|
|
26
30
|
const lbContext = useLB();
|
|
27
31
|
lbStatus = lbContext.status;
|
|
32
|
+
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
33
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
34
|
+
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
35
|
+
hasLBProvider = true;
|
|
28
36
|
}
|
|
29
37
|
catch {
|
|
30
38
|
// LBProvider n'est pas disponible, ignorer
|
|
31
39
|
lbStatus = undefined;
|
|
40
|
+
lbHasSession = false;
|
|
41
|
+
lbHasSelectedKey = false;
|
|
42
|
+
hasLBProvider = false;
|
|
32
43
|
}
|
|
33
44
|
// Récupérer le contexte AiProvider avec fallback sur les props
|
|
34
45
|
const aiContext = useAiContext();
|
|
@@ -36,7 +47,12 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
|
|
|
36
47
|
const apiKeyId = propApiKeyId ?? aiContext.apiKeyId;
|
|
37
48
|
const { generateImage, loading } = useAiCallImage({ baseUrl, apiKeyId });
|
|
38
49
|
const { formatted: loadingElapsed } = useLoadingTimer(loading);
|
|
39
|
-
const
|
|
50
|
+
const needsApiKeySelection = hasLBProvider &&
|
|
51
|
+
lbStatus === "ready" &&
|
|
52
|
+
lbHasSession &&
|
|
53
|
+
(!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
|
|
54
|
+
const isAuthReady = !needsApiKeySelection &&
|
|
55
|
+
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
40
56
|
const handleOpenPanel = () => {
|
|
41
57
|
if (!isAuthReady) {
|
|
42
58
|
setShowAuthModal(true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiInput.d.ts","sourceRoot":"","sources":["../../src/components/AiInput.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAc,EAAoB,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAY9D,MAAM,WAAW,YACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACjE,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAe,EACf,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAQ,EAAE,SAAiB,EAC3B,qBAA4B,EAC5B,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,UAAU,EACd,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"AiInput.d.ts","sourceRoot":"","sources":["../../src/components/AiInput.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAc,EAAoB,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAY9D,MAAM,WAAW,YACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACjE,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAe,EACf,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAQ,EAAE,SAAiB,EAC3B,qBAA4B,EAC5B,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,UAAU,EACd,EAAE,YAAY,2CAgPd"}
|
|
@@ -22,13 +22,24 @@ export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode =
|
|
|
22
22
|
const { showUsageToast: _showUsageToast, toastData, toastKey, clearToast, } = useUsageToast();
|
|
23
23
|
// Rendre l'authentification optionnelle
|
|
24
24
|
let lbStatus;
|
|
25
|
+
let lbHasSession = false;
|
|
26
|
+
let lbHasSelectedKey = false;
|
|
27
|
+
let lbHasSelectedApiKeyCookie = false;
|
|
28
|
+
let hasLBProvider = false;
|
|
25
29
|
try {
|
|
26
30
|
const lbContext = useLB();
|
|
27
31
|
lbStatus = lbContext.status;
|
|
32
|
+
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
33
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
34
|
+
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
35
|
+
hasLBProvider = true;
|
|
28
36
|
}
|
|
29
37
|
catch {
|
|
30
38
|
// LBProvider n'est pas disponible, ignorer
|
|
31
39
|
lbStatus = undefined;
|
|
40
|
+
lbHasSession = false;
|
|
41
|
+
lbHasSelectedKey = false;
|
|
42
|
+
hasLBProvider = false;
|
|
32
43
|
}
|
|
33
44
|
let ctxBaseUrl;
|
|
34
45
|
let ctxApiKeyId;
|
|
@@ -51,7 +62,12 @@ export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode =
|
|
|
51
62
|
const { generateText, loading } = useAiCallText({ baseUrl, apiKeyId });
|
|
52
63
|
const { formatted: loadingElapsed } = useLoadingTimer(loading);
|
|
53
64
|
const hasConfiguration = Boolean(model && prompt);
|
|
54
|
-
const
|
|
65
|
+
const needsApiKeySelection = hasLBProvider &&
|
|
66
|
+
lbStatus === "ready" &&
|
|
67
|
+
lbHasSession &&
|
|
68
|
+
(!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
|
|
69
|
+
const isAuthReady = !needsApiKeySelection &&
|
|
70
|
+
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
55
71
|
const shouldShowSparkles = isAuthReady && !disabled;
|
|
56
72
|
const handleOpenPanel = () => {
|
|
57
73
|
if (!isAuthReady) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiSelect.d.ts","sourceRoot":"","sources":["../../src/components/AiSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAEnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAW9D,MAAM,WAAW,aACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACnE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAe,EACf,OAAO,EACP,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,GAAG,WAAW,EACf,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"AiSelect.d.ts","sourceRoot":"","sources":["../../src/components/AiSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAEnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAW9D,MAAM,WAAW,aACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACnE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAe,EACf,OAAO,EACP,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,GAAG,WAAW,EACf,EAAE,aAAa,2CAkKf"}
|
|
@@ -19,13 +19,24 @@ export function AiSelect({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode
|
|
|
19
19
|
const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
|
|
20
20
|
// Rendre l'authentification optionnelle
|
|
21
21
|
let lbStatus;
|
|
22
|
+
let lbHasSession = false;
|
|
23
|
+
let lbHasSelectedKey = false;
|
|
24
|
+
let lbHasSelectedApiKeyCookie = false;
|
|
25
|
+
let hasLBProvider = false;
|
|
22
26
|
try {
|
|
23
27
|
const lbContext = useLB();
|
|
24
28
|
lbStatus = lbContext.status;
|
|
29
|
+
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
30
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
31
|
+
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
32
|
+
hasLBProvider = true;
|
|
25
33
|
}
|
|
26
34
|
catch {
|
|
27
35
|
// LBProvider n'est pas disponible, ignorer
|
|
28
36
|
lbStatus = undefined;
|
|
37
|
+
lbHasSession = false;
|
|
38
|
+
lbHasSelectedKey = false;
|
|
39
|
+
hasLBProvider = false;
|
|
29
40
|
}
|
|
30
41
|
let ctxBaseUrl;
|
|
31
42
|
let ctxApiKeyId;
|
|
@@ -46,7 +57,12 @@ export function AiSelect({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode
|
|
|
46
57
|
modelType: "text-or-language",
|
|
47
58
|
});
|
|
48
59
|
const { generateText, loading } = useAiCallText({ baseUrl, apiKeyId });
|
|
49
|
-
const
|
|
60
|
+
const needsApiKeySelection = hasLBProvider &&
|
|
61
|
+
lbStatus === "ready" &&
|
|
62
|
+
lbHasSession &&
|
|
63
|
+
(!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
|
|
64
|
+
const isAuthReady = !needsApiKeySelection &&
|
|
65
|
+
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
50
66
|
const shouldShowSparkles = isAuthReady && !disabled;
|
|
51
67
|
const handleOpenPanel = () => {
|
|
52
68
|
if (!isAuthReady) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,QAAQ,EAAU,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,QAAQ,EAAU,MAAM,uBAAuB,CAAC;AA8B9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGjD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAmHD,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAe,EACf,SAAc,EACd,IAAW,EACX,MAAe,GAChB,EAAE,mBAAmB,2CA6jBrB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import "../styles/register";
|
|
4
|
-
import { useLayoutEffect, useMemo, useRef, useState, useContext } from "react";
|
|
4
|
+
import { useLayoutEffect, useMemo, useRef, useState, useContext, useEffect, } from "react";
|
|
5
5
|
import { createPortal } from "react-dom";
|
|
6
6
|
import { ArrowRightLeft, BarChart3, FileText, Folder, History, Loader2, LogOut, Settings, Shield, } from "lucide-react";
|
|
7
7
|
import { LBContext, } from "../context/LBAuthProvider";
|
|
@@ -79,6 +79,8 @@ export function AiStatusButton({ status, loading = false, className = "", size =
|
|
|
79
79
|
let lbSelectedKey = null;
|
|
80
80
|
let lbRefreshBasicStatus;
|
|
81
81
|
let lbRefreshStorageStatus;
|
|
82
|
+
let lbSessionToken;
|
|
83
|
+
let lbHasSelectedApiKeyCookie = false;
|
|
82
84
|
const lbContext = useContext(LBContext);
|
|
83
85
|
if (lbContext) {
|
|
84
86
|
lbStatus = lbContext.status;
|
|
@@ -94,6 +96,8 @@ export function AiStatusButton({ status, loading = false, className = "", size =
|
|
|
94
96
|
lbSelectedKey = lbContext.selectedKey || null;
|
|
95
97
|
lbRefreshBasicStatus = lbContext.refreshBasicStatus;
|
|
96
98
|
lbRefreshStorageStatus = lbContext.refreshStorageStatus;
|
|
99
|
+
lbSessionToken = lbContext.session?.sessionToken;
|
|
100
|
+
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
97
101
|
}
|
|
98
102
|
else {
|
|
99
103
|
lbStatus = undefined;
|
|
@@ -128,9 +132,19 @@ export function AiStatusButton({ status, loading = false, className = "", size =
|
|
|
128
132
|
typeof effectiveStatus.authType === "string"
|
|
129
133
|
? effectiveStatus.authType
|
|
130
134
|
: undefined;
|
|
131
|
-
const
|
|
135
|
+
const hasLbSession = Boolean(lbSessionToken);
|
|
136
|
+
const requiresApiKeySelection = lbStatus === "ready" &&
|
|
137
|
+
hasLbSession &&
|
|
138
|
+
(!lbHasSelectedApiKeyCookie || !hasApiKeySelected) &&
|
|
139
|
+
apiKeys.length > 0;
|
|
132
140
|
const isApiKeyAuthMode = authTypeValue === "api_key";
|
|
133
141
|
const [tooltipStyle, setTooltipStyle] = useState({});
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (requiresApiKeySelection) {
|
|
144
|
+
setShowApiKeySelector(true);
|
|
145
|
+
setShowTooltip(false);
|
|
146
|
+
}
|
|
147
|
+
}, [requiresApiKeySelection]);
|
|
134
148
|
useLayoutEffect(() => {
|
|
135
149
|
if (!showTooltip || !buttonRef.current || !canPortal) {
|
|
136
150
|
return;
|
|
@@ -333,7 +347,7 @@ export function AiStatusButton({ status, loading = false, className = "", size =
|
|
|
333
347
|
setShowApiKeySelector(false);
|
|
334
348
|
setShowTooltip(false);
|
|
335
349
|
if (refetchProviders) {
|
|
336
|
-
|
|
350
|
+
void refetchProviders();
|
|
337
351
|
}
|
|
338
352
|
}
|
|
339
353
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiTextarea.d.ts","sourceRoot":"","sources":["../../src/components/AiTextarea.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAc,EAIZ,KAAK,sBAAsB,EAC5B,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAY9D,MAAM,WAAW,eACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,sBAAsB,CAAC,mBAAmB,CAAC,EAAE,SAAS,CAAC;IAC9D,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/C;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAa,EACb,iBAAiB,EACjB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAQ,EAAE,SAAiB,EAC3B,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,aAAa,EACjB,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"AiTextarea.d.ts","sourceRoot":"","sources":["../../src/components/AiTextarea.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAc,EAIZ,KAAK,sBAAsB,EAC5B,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAY9D,MAAM,WAAW,eACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,sBAAsB,CAAC,mBAAmB,CAAC,EAAE,SAAS,CAAC;IAC9D,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/C;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAa,EACb,iBAAiB,EACjB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAQ,EAAE,SAAiB,EAC3B,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,aAAa,EACjB,EAAE,eAAe,2CAqQjB"}
|
|
@@ -24,10 +24,16 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
24
24
|
const { showUsageToast, toastData, toastKey, clearToast } = useUsageToast();
|
|
25
25
|
// Rendre l'authentification optionnelle
|
|
26
26
|
let lbStatus;
|
|
27
|
+
let lbHasSession = false;
|
|
28
|
+
let lbHasSelectedKey = false;
|
|
29
|
+
let lbHasSelectedApiKeyCookie = false;
|
|
27
30
|
let hasLBProvider = false;
|
|
28
31
|
try {
|
|
29
32
|
const lbContext = useLB();
|
|
30
33
|
lbStatus = lbContext.status;
|
|
34
|
+
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
35
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
36
|
+
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
31
37
|
hasLBProvider = true;
|
|
32
38
|
}
|
|
33
39
|
catch {
|
|
@@ -48,8 +54,10 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
48
54
|
}
|
|
49
55
|
const baseUrl = propBaseUrl ?? ctxBaseUrl;
|
|
50
56
|
const apiKeyId = propApiKeyId ?? ctxApiKeyId;
|
|
51
|
-
const
|
|
52
|
-
|
|
57
|
+
const needsApiKeySelection = hasLBProvider &&
|
|
58
|
+
lbStatus === "ready" &&
|
|
59
|
+
lbHasSession &&
|
|
60
|
+
(!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
|
|
53
61
|
const { models: _models } = useAiModels({
|
|
54
62
|
baseUrl,
|
|
55
63
|
apiKeyId,
|
|
@@ -58,9 +66,10 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
58
66
|
const { generateText, loading } = useAiCallText({ baseUrl, apiKeyId });
|
|
59
67
|
const { formatted: loadingElapsed } = useLoadingTimer(loading);
|
|
60
68
|
const hasConfiguration = Boolean(model && prompt);
|
|
61
|
-
const isAuthReady =
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
const isAuthReady = hasLBProvider
|
|
70
|
+
? !needsApiKeySelection &&
|
|
71
|
+
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY))
|
|
72
|
+
: Boolean(process.env.LB_API_KEY);
|
|
64
73
|
const shouldShowSparkles = isAuthReady && !disabled;
|
|
65
74
|
const handleOpenPanel = () => {
|
|
66
75
|
if (!isAuthReady) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LBApiKeySelector.d.ts","sourceRoot":"","sources":["../../src/components/LBApiKeySelector.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,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":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,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,kDAwJvB"}
|
|
@@ -88,6 +88,8 @@ interface LBContextValue extends LBAuthState {
|
|
|
88
88
|
isLoadingStatus: boolean;
|
|
89
89
|
/** Indique si le storage est en cours de chargement */
|
|
90
90
|
isLoadingStorage: boolean;
|
|
91
|
+
/** True si le cookie api_key_selected est présent */
|
|
92
|
+
hasSelectedApiKeyCookie: boolean;
|
|
91
93
|
}
|
|
92
94
|
declare const LBContext: import("react").Context<LBContextValue | undefined>;
|
|
93
95
|
export { LBContext };
|
|
@@ -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,8 @@ 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
|
+
setHasSelectedApiKeyCookie(true);
|
|
156
|
+
setTimeout(() => syncSelectedApiKeyCookie(), 100);
|
|
137
157
|
onStatusChange?.("ready");
|
|
138
158
|
onAuthChange?.(); // Refresh provider after signin
|
|
139
159
|
}
|
|
@@ -145,7 +165,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
145
165
|
});
|
|
146
166
|
throw error;
|
|
147
167
|
}
|
|
148
|
-
}, [lbClient, state.user,
|
|
168
|
+
}, [lbClient, onAuthChange, onStatusChange, state.user, syncSelectedApiKeyCookie]);
|
|
149
169
|
/**
|
|
150
170
|
* Connexion utilisateur (étape 1 : login)
|
|
151
171
|
* Retourne le token et les clés API sans créer de session
|
|
@@ -359,8 +379,10 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
359
379
|
selectedKey,
|
|
360
380
|
}));
|
|
361
381
|
}
|
|
382
|
+
setHasSelectedApiKeyCookie(true);
|
|
362
383
|
await refreshBasicStatus();
|
|
363
384
|
setTimeout(() => refreshStorageStatus(), 100);
|
|
385
|
+
setTimeout(() => syncSelectedApiKeyCookie(), 100);
|
|
364
386
|
}
|
|
365
387
|
else {
|
|
366
388
|
throw new Error("No valid authentication method available");
|
|
@@ -373,6 +395,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
373
395
|
lbClient,
|
|
374
396
|
refreshBasicStatus,
|
|
375
397
|
refreshStorageStatus,
|
|
398
|
+
syncSelectedApiKeyCookie,
|
|
376
399
|
]);
|
|
377
400
|
/**
|
|
378
401
|
* Déconnexion
|
|
@@ -393,6 +416,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
393
416
|
setBasicStatus(null);
|
|
394
417
|
setStorageStatus(null);
|
|
395
418
|
setStorageLastFetch(0);
|
|
419
|
+
setHasSelectedApiKeyCookie(false);
|
|
396
420
|
onStatusChange?.("needs_auth");
|
|
397
421
|
onAuthChange?.(); // Refresh provider after logout
|
|
398
422
|
}
|
|
@@ -420,16 +444,19 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
420
444
|
setApiKeys([]);
|
|
421
445
|
}
|
|
422
446
|
}, [lbClient, state.status]);
|
|
423
|
-
// Refresh status
|
|
447
|
+
// Refresh status uniquement lors de la transition vers "ready"
|
|
448
|
+
// (évite les boucles status/user causées par des callbacks recréés)
|
|
424
449
|
useEffect(() => {
|
|
425
|
-
|
|
450
|
+
const wasReady = previousStatusRef.current === "ready";
|
|
451
|
+
previousStatusRef.current = state.status;
|
|
452
|
+
if (state.status === "ready" && !wasReady) {
|
|
426
453
|
// Appel rapide d'abord
|
|
427
454
|
refreshBasicStatus();
|
|
428
455
|
// Storage en arrière-plan après 100ms
|
|
429
456
|
setTimeout(() => refreshStorageStatus(), 100);
|
|
430
457
|
fetchApiKeysWithSession(); // Also fetch API keys list
|
|
431
458
|
}
|
|
432
|
-
else {
|
|
459
|
+
else if (state.status !== "ready") {
|
|
433
460
|
setApiStatus(null);
|
|
434
461
|
setBasicStatus(null);
|
|
435
462
|
setStorageStatus(null);
|
|
@@ -460,6 +487,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
460
487
|
refreshStorageStatus,
|
|
461
488
|
isLoadingStatus,
|
|
462
489
|
isLoadingStorage,
|
|
490
|
+
hasSelectedApiKeyCookie,
|
|
463
491
|
};
|
|
464
492
|
return (_jsx(I18nProvider, { lang: lang, children: _jsx(LBContext.Provider, { value: value, children: children }) }));
|
|
465
493
|
}
|
|
@@ -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;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,
|
|
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"}
|
|
@@ -25,13 +25,22 @@ export function useAiStatus(options) {
|
|
|
25
25
|
return null;
|
|
26
26
|
}, [lbContext]);
|
|
27
27
|
const fetchStatus = useCallback(async () => {
|
|
28
|
-
// If LBProvider
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
+
}
|
|
32
39
|
}
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
else {
|
|
41
|
+
setStatus(null);
|
|
42
|
+
setLoading(false);
|
|
43
|
+
setError(null);
|
|
35
44
|
}
|
|
36
45
|
return;
|
|
37
46
|
}
|
|
@@ -52,6 +61,12 @@ export function useAiStatus(options) {
|
|
|
52
61
|
}
|
|
53
62
|
}, [client, lbContext]);
|
|
54
63
|
useEffect(() => {
|
|
64
|
+
if (lbContext && lbContext.status !== "ready") {
|
|
65
|
+
setStatus(null);
|
|
66
|
+
setLoading(false);
|
|
67
|
+
setError(null);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
55
70
|
if (statusFromLB) {
|
|
56
71
|
setStatus(statusFromLB);
|
|
57
72
|
setLoading(false);
|
|
@@ -59,7 +74,7 @@ export function useAiStatus(options) {
|
|
|
59
74
|
return;
|
|
60
75
|
}
|
|
61
76
|
fetchStatus();
|
|
62
|
-
}, [fetchStatus, statusFromLB]);
|
|
77
|
+
}, [fetchStatus, lbContext, statusFromLB]);
|
|
63
78
|
const isLoadingFromLB = !!lbContext &&
|
|
64
79
|
lbContext.status === "ready" &&
|
|
65
80
|
!statusFromLB &&
|
package/package.json
CHANGED
|
@@ -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) {
|
|
@@ -79,12 +79,23 @@ export function AiImageButton({
|
|
|
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) {
|
|
@@ -60,12 +60,23 @@ export function AiInput({
|
|
|
60
60
|
|
|
61
61
|
// Rendre l'authentification optionnelle
|
|
62
62
|
let lbStatus: string | undefined;
|
|
63
|
+
let lbHasSession = false;
|
|
64
|
+
let lbHasSelectedKey = false;
|
|
65
|
+
let lbHasSelectedApiKeyCookie = false;
|
|
66
|
+
let hasLBProvider = false;
|
|
63
67
|
try {
|
|
64
68
|
const lbContext = useLB();
|
|
65
69
|
lbStatus = lbContext.status;
|
|
70
|
+
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
71
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
72
|
+
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
73
|
+
hasLBProvider = true;
|
|
66
74
|
} catch {
|
|
67
75
|
// LBProvider n'est pas disponible, ignorer
|
|
68
76
|
lbStatus = undefined;
|
|
77
|
+
lbHasSession = false;
|
|
78
|
+
lbHasSelectedKey = false;
|
|
79
|
+
hasLBProvider = false;
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
let ctxBaseUrl: string | undefined;
|
|
@@ -91,7 +102,14 @@ export function AiInput({
|
|
|
91
102
|
const { formatted: loadingElapsed } = useLoadingTimer(loading);
|
|
92
103
|
|
|
93
104
|
const hasConfiguration = Boolean(model && prompt);
|
|
94
|
-
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));
|
|
95
113
|
const shouldShowSparkles = isAuthReady && !disabled;
|
|
96
114
|
|
|
97
115
|
const handleOpenPanel = () => {
|
|
@@ -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 = () => {
|
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import "../styles/register";
|
|
4
4
|
import type { AiStatus, LBUser } from "@lastbrain/ai-ui-core";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
useLayoutEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
useContext,
|
|
11
|
+
useEffect,
|
|
12
|
+
} from "react";
|
|
6
13
|
import { createPortal } from "react-dom";
|
|
7
14
|
import {
|
|
8
15
|
ArrowRightLeft,
|
|
@@ -169,6 +176,8 @@ export function AiStatusButton({
|
|
|
169
176
|
let lbSelectedKey: LBApiKey | null = null;
|
|
170
177
|
let lbRefreshBasicStatus: (() => Promise<void>) | undefined;
|
|
171
178
|
let lbRefreshStorageStatus: ((force?: boolean) => Promise<void>) | undefined;
|
|
179
|
+
let lbSessionToken: string | undefined;
|
|
180
|
+
let lbHasSelectedApiKeyCookie = false;
|
|
172
181
|
|
|
173
182
|
const lbContext = useContext(LBContext);
|
|
174
183
|
if (lbContext) {
|
|
@@ -185,6 +194,8 @@ export function AiStatusButton({
|
|
|
185
194
|
lbSelectedKey = lbContext.selectedKey || null;
|
|
186
195
|
lbRefreshBasicStatus = lbContext.refreshBasicStatus;
|
|
187
196
|
lbRefreshStorageStatus = lbContext.refreshStorageStatus;
|
|
197
|
+
lbSessionToken = lbContext.session?.sessionToken;
|
|
198
|
+
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
188
199
|
} else {
|
|
189
200
|
lbStatus = undefined;
|
|
190
201
|
}
|
|
@@ -230,12 +241,23 @@ export function AiStatusButton({
|
|
|
230
241
|
typeof effectiveStatus.authType === "string"
|
|
231
242
|
? effectiveStatus.authType
|
|
232
243
|
: undefined;
|
|
244
|
+
const hasLbSession = Boolean(lbSessionToken);
|
|
233
245
|
const requiresApiKeySelection =
|
|
234
|
-
lbStatus === "ready" &&
|
|
246
|
+
lbStatus === "ready" &&
|
|
247
|
+
hasLbSession &&
|
|
248
|
+
(!lbHasSelectedApiKeyCookie || !hasApiKeySelected) &&
|
|
249
|
+
apiKeys.length > 0;
|
|
235
250
|
const isApiKeyAuthMode = authTypeValue === "api_key";
|
|
236
251
|
|
|
237
252
|
const [tooltipStyle, setTooltipStyle] = useState<Record<string, string>>({});
|
|
238
253
|
|
|
254
|
+
useEffect(() => {
|
|
255
|
+
if (requiresApiKeySelection) {
|
|
256
|
+
setShowApiKeySelector(true);
|
|
257
|
+
setShowTooltip(false);
|
|
258
|
+
}
|
|
259
|
+
}, [requiresApiKeySelection]);
|
|
260
|
+
|
|
239
261
|
useLayoutEffect(() => {
|
|
240
262
|
if (!showTooltip || !buttonRef.current || !canPortal) {
|
|
241
263
|
return;
|
|
@@ -694,7 +716,7 @@ export function AiStatusButton({
|
|
|
694
716
|
setShowApiKeySelector(false);
|
|
695
717
|
setShowTooltip(false);
|
|
696
718
|
if (refetchProviders) {
|
|
697
|
-
|
|
719
|
+
void refetchProviders();
|
|
698
720
|
}
|
|
699
721
|
} catch (error) {
|
|
700
722
|
console.error("Failed to select API key:", error);
|
|
@@ -63,10 +63,16 @@ export function AiTextarea({
|
|
|
63
63
|
|
|
64
64
|
// Rendre l'authentification optionnelle
|
|
65
65
|
let lbStatus: string | undefined;
|
|
66
|
+
let lbHasSession = false;
|
|
67
|
+
let lbHasSelectedKey = false;
|
|
68
|
+
let lbHasSelectedApiKeyCookie = false;
|
|
66
69
|
let hasLBProvider = false;
|
|
67
70
|
try {
|
|
68
71
|
const lbContext = useLB();
|
|
69
72
|
lbStatus = lbContext.status;
|
|
73
|
+
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
74
|
+
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
75
|
+
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
70
76
|
hasLBProvider = true;
|
|
71
77
|
} catch {
|
|
72
78
|
// LBProvider n'est pas disponible, ignorer
|
|
@@ -87,9 +93,11 @@ export function AiTextarea({
|
|
|
87
93
|
|
|
88
94
|
const baseUrl = propBaseUrl ?? ctxBaseUrl;
|
|
89
95
|
const apiKeyId = propApiKeyId ?? ctxApiKeyId;
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
const needsApiKeySelection =
|
|
97
|
+
hasLBProvider &&
|
|
98
|
+
lbStatus === "ready" &&
|
|
99
|
+
lbHasSession &&
|
|
100
|
+
(!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
|
|
93
101
|
|
|
94
102
|
const { models: _models } = useAiModels({
|
|
95
103
|
baseUrl,
|
|
@@ -100,10 +108,10 @@ export function AiTextarea({
|
|
|
100
108
|
const { formatted: loadingElapsed } = useLoadingTimer(loading);
|
|
101
109
|
|
|
102
110
|
const hasConfiguration = Boolean(model && prompt);
|
|
103
|
-
const isAuthReady =
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
Boolean(process.env.LB_API_KEY);
|
|
111
|
+
const isAuthReady = hasLBProvider
|
|
112
|
+
? !needsApiKeySelection &&
|
|
113
|
+
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY))
|
|
114
|
+
: Boolean(process.env.LB_API_KEY);
|
|
107
115
|
const shouldShowSparkles = isAuthReady && !disabled;
|
|
108
116
|
|
|
109
117
|
const handleOpenPanel = () => {
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
useEffect,
|
|
12
12
|
useCallback,
|
|
13
13
|
useMemo,
|
|
14
|
+
useRef,
|
|
14
15
|
useState,
|
|
15
16
|
type ReactNode,
|
|
16
17
|
} from "react";
|
|
@@ -114,6 +115,8 @@ interface LBContextValue extends LBAuthState {
|
|
|
114
115
|
isLoadingStatus: boolean;
|
|
115
116
|
/** Indique si le storage est en cours de chargement */
|
|
116
117
|
isLoadingStorage: boolean;
|
|
118
|
+
/** True si le cookie api_key_selected est présent */
|
|
119
|
+
hasSelectedApiKeyCookie: boolean;
|
|
117
120
|
}
|
|
118
121
|
|
|
119
122
|
const LBContext = createContext<LBContextValue | undefined>(undefined);
|
|
@@ -142,6 +145,17 @@ export function LBProvider({
|
|
|
142
145
|
const [isLoadingStatus, setIsLoadingStatus] = useState(false);
|
|
143
146
|
const [isLoadingStorage, setIsLoadingStorage] = useState(false);
|
|
144
147
|
const [storageLastFetch, setStorageLastFetch] = useState<number>(0);
|
|
148
|
+
const [hasSelectedApiKeyCookie, setHasSelectedApiKeyCookie] = useState(false);
|
|
149
|
+
const previousStatusRef = useRef<LBAuthState["status"]>("loading");
|
|
150
|
+
|
|
151
|
+
const syncSelectedApiKeyCookie = useCallback(() => {
|
|
152
|
+
if (typeof document === "undefined") return;
|
|
153
|
+
const hasCookie = document.cookie
|
|
154
|
+
.split(";")
|
|
155
|
+
.map((part) => part.trim())
|
|
156
|
+
.some((part) => part.startsWith("api_key_selected=") && part.length > 17);
|
|
157
|
+
setHasSelectedApiKeyCookie(hasCookie);
|
|
158
|
+
}, []);
|
|
145
159
|
|
|
146
160
|
const lbClient = useMemo(
|
|
147
161
|
() =>
|
|
@@ -223,6 +237,13 @@ export function LBProvider({
|
|
|
223
237
|
checkSession();
|
|
224
238
|
}, [checkSession]);
|
|
225
239
|
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
syncSelectedApiKeyCookie();
|
|
242
|
+
if (typeof window === "undefined") return;
|
|
243
|
+
const interval = window.setInterval(syncSelectedApiKeyCookie, 1000);
|
|
244
|
+
return () => window.clearInterval(interval);
|
|
245
|
+
}, [syncSelectedApiKeyCookie]);
|
|
246
|
+
|
|
226
247
|
/**
|
|
227
248
|
* Récupère les clés API de l'utilisateur
|
|
228
249
|
*/
|
|
@@ -272,6 +293,8 @@ export function LBProvider({
|
|
|
272
293
|
|
|
273
294
|
setAccessToken(undefined); // Nettoyer l'access token temporaire
|
|
274
295
|
setApiKeys([]); // Nettoyer les clés API temporaires
|
|
296
|
+
setHasSelectedApiKeyCookie(true);
|
|
297
|
+
setTimeout(() => syncSelectedApiKeyCookie(), 100);
|
|
275
298
|
onStatusChange?.("ready");
|
|
276
299
|
onAuthChange?.(); // Refresh provider after signin
|
|
277
300
|
} catch (error) {
|
|
@@ -284,7 +307,7 @@ export function LBProvider({
|
|
|
284
307
|
throw error;
|
|
285
308
|
}
|
|
286
309
|
},
|
|
287
|
-
[lbClient, state.user,
|
|
310
|
+
[lbClient, onAuthChange, onStatusChange, state.user, syncSelectedApiKeyCookie]
|
|
288
311
|
);
|
|
289
312
|
|
|
290
313
|
/**
|
|
@@ -548,8 +571,10 @@ export function LBProvider({
|
|
|
548
571
|
selectedKey,
|
|
549
572
|
}));
|
|
550
573
|
}
|
|
574
|
+
setHasSelectedApiKeyCookie(true);
|
|
551
575
|
await refreshBasicStatus();
|
|
552
576
|
setTimeout(() => refreshStorageStatus(), 100);
|
|
577
|
+
setTimeout(() => syncSelectedApiKeyCookie(), 100);
|
|
553
578
|
} else {
|
|
554
579
|
throw new Error("No valid authentication method available");
|
|
555
580
|
}
|
|
@@ -562,6 +587,7 @@ export function LBProvider({
|
|
|
562
587
|
lbClient,
|
|
563
588
|
refreshBasicStatus,
|
|
564
589
|
refreshStorageStatus,
|
|
590
|
+
syncSelectedApiKeyCookie,
|
|
565
591
|
]
|
|
566
592
|
);
|
|
567
593
|
|
|
@@ -582,6 +608,7 @@ export function LBProvider({
|
|
|
582
608
|
setBasicStatus(null);
|
|
583
609
|
setStorageStatus(null);
|
|
584
610
|
setStorageLastFetch(0);
|
|
611
|
+
setHasSelectedApiKeyCookie(false);
|
|
585
612
|
onStatusChange?.("needs_auth");
|
|
586
613
|
onAuthChange?.(); // Refresh provider after logout
|
|
587
614
|
}
|
|
@@ -612,15 +639,19 @@ export function LBProvider({
|
|
|
612
639
|
}
|
|
613
640
|
}, [lbClient, state.status]);
|
|
614
641
|
|
|
615
|
-
// Refresh status
|
|
642
|
+
// Refresh status uniquement lors de la transition vers "ready"
|
|
643
|
+
// (évite les boucles status/user causées par des callbacks recréés)
|
|
616
644
|
useEffect(() => {
|
|
617
|
-
|
|
645
|
+
const wasReady = previousStatusRef.current === "ready";
|
|
646
|
+
previousStatusRef.current = state.status;
|
|
647
|
+
|
|
648
|
+
if (state.status === "ready" && !wasReady) {
|
|
618
649
|
// Appel rapide d'abord
|
|
619
650
|
refreshBasicStatus();
|
|
620
651
|
// Storage en arrière-plan après 100ms
|
|
621
652
|
setTimeout(() => refreshStorageStatus(), 100);
|
|
622
653
|
fetchApiKeysWithSession(); // Also fetch API keys list
|
|
623
|
-
} else {
|
|
654
|
+
} else if (state.status !== "ready") {
|
|
624
655
|
setApiStatus(null);
|
|
625
656
|
setBasicStatus(null);
|
|
626
657
|
setStorageStatus(null);
|
|
@@ -652,6 +683,7 @@ export function LBProvider({
|
|
|
652
683
|
refreshStorageStatus,
|
|
653
684
|
isLoadingStatus,
|
|
654
685
|
isLoadingStorage,
|
|
686
|
+
hasSelectedApiKeyCookie,
|
|
655
687
|
};
|
|
656
688
|
|
|
657
689
|
return (
|
package/src/hooks/useAiStatus.ts
CHANGED
|
@@ -43,12 +43,20 @@ export function useAiStatus(options?: UseAiStatusOptions): UseAiStatusResult {
|
|
|
43
43
|
}, [lbContext]);
|
|
44
44
|
|
|
45
45
|
const fetchStatus = useCallback(async () => {
|
|
46
|
-
// If LBProvider
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
// If LBProvider exists, never call getStatus directly here:
|
|
47
|
+
// - ready: delegate refresh to provider
|
|
48
|
+
// - non-ready: avoid unauthorized polling loops
|
|
49
|
+
if (lbContext) {
|
|
50
|
+
if (lbContext.status === "ready") {
|
|
51
|
+
try {
|
|
52
|
+
await lbContext.refreshBasicStatus();
|
|
53
|
+
} catch {
|
|
54
|
+
// Ignore: provider already tracks errors/status
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
setStatus(null);
|
|
58
|
+
setLoading(false);
|
|
59
|
+
setError(null);
|
|
52
60
|
}
|
|
53
61
|
return;
|
|
54
62
|
}
|
|
@@ -72,6 +80,13 @@ export function useAiStatus(options?: UseAiStatusOptions): UseAiStatusResult {
|
|
|
72
80
|
}, [client, lbContext]);
|
|
73
81
|
|
|
74
82
|
useEffect(() => {
|
|
83
|
+
if (lbContext && lbContext.status !== "ready") {
|
|
84
|
+
setStatus(null);
|
|
85
|
+
setLoading(false);
|
|
86
|
+
setError(null);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
if (statusFromLB) {
|
|
76
91
|
setStatus(statusFromLB);
|
|
77
92
|
setLoading(false);
|
|
@@ -79,7 +94,7 @@ export function useAiStatus(options?: UseAiStatusOptions): UseAiStatusResult {
|
|
|
79
94
|
return;
|
|
80
95
|
}
|
|
81
96
|
fetchStatus();
|
|
82
|
-
}, [fetchStatus, statusFromLB]);
|
|
97
|
+
}, [fetchStatus, lbContext, statusFromLB]);
|
|
83
98
|
|
|
84
99
|
const isLoadingFromLB =
|
|
85
100
|
!!lbContext &&
|