@lastbrain/ai-ui-react 1.0.78 → 1.0.79
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 +1 -4
- package/dist/components/AiImageButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +1 -4
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +1 -4
- package/dist/components/AiSelect.d.ts.map +1 -1
- package/dist/components/AiSelect.js +1 -4
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +2 -5
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +1 -3
- package/dist/components/LBApiKeySelector.d.ts.map +1 -1
- package/dist/components/LBApiKeySelector.js +12 -2
- package/dist/components/UsageToast.js +1 -1
- package/dist/context/LBAuthProvider.d.ts +3 -1
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +48 -27
- package/dist/styles.css +6 -4
- package/package.json +1 -1
- package/src/components/AiContextButton.tsx +1 -4
- package/src/components/AiImageButton.tsx +1 -4
- package/src/components/AiInput.tsx +1 -4
- package/src/components/AiSelect.tsx +1 -4
- package/src/components/AiStatusButton.tsx +2 -5
- package/src/components/AiTextarea.tsx +1 -3
- package/src/components/LBApiKeySelector.tsx +14 -2
- package/src/components/UsageToast.tsx +1 -1
- package/src/context/LBAuthProvider.tsx +64 -28
- package/src/styles.css +6 -4
|
@@ -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,2CA4TtB"}
|
|
@@ -23,21 +23,18 @@ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId,
|
|
|
23
23
|
const resolvedContextDescription = contextDescription || t("ai.context.description", "Data to analyze");
|
|
24
24
|
let lbStatus;
|
|
25
25
|
let lbHasSession = false;
|
|
26
|
-
let lbHasSelectedKey = false;
|
|
27
26
|
let lbHasSelectedApiKeyCookie = false;
|
|
28
27
|
let hasLBProvider = false;
|
|
29
28
|
try {
|
|
30
29
|
const lbContext = useLB();
|
|
31
30
|
lbStatus = lbContext.status;
|
|
32
31
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
33
|
-
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
34
32
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
35
33
|
hasLBProvider = true;
|
|
36
34
|
}
|
|
37
35
|
catch {
|
|
38
36
|
lbStatus = undefined;
|
|
39
37
|
lbHasSession = false;
|
|
40
|
-
lbHasSelectedKey = false;
|
|
41
38
|
hasLBProvider = false;
|
|
42
39
|
}
|
|
43
40
|
const aiContext = useAiContext();
|
|
@@ -50,7 +47,7 @@ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId,
|
|
|
50
47
|
const needsApiKeySelection = hasLBProvider &&
|
|
51
48
|
lbStatus === "ready" &&
|
|
52
49
|
lbHasSession &&
|
|
53
|
-
|
|
50
|
+
!lbHasSelectedApiKeyCookie;
|
|
54
51
|
const isAuthReady = !needsApiKeySelection &&
|
|
55
52
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
56
53
|
const handleOpenPanel = () => {
|
|
@@ -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,2CAmSpB"}
|
|
@@ -23,14 +23,12 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
|
|
|
23
23
|
// Rendre l'authentification optionnelle
|
|
24
24
|
let lbStatus;
|
|
25
25
|
let lbHasSession = false;
|
|
26
|
-
let lbHasSelectedKey = false;
|
|
27
26
|
let lbHasSelectedApiKeyCookie = false;
|
|
28
27
|
let hasLBProvider = false;
|
|
29
28
|
try {
|
|
30
29
|
const lbContext = useLB();
|
|
31
30
|
lbStatus = lbContext.status;
|
|
32
31
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
33
|
-
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
34
32
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
35
33
|
hasLBProvider = true;
|
|
36
34
|
}
|
|
@@ -38,7 +36,6 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
|
|
|
38
36
|
// LBProvider n'est pas disponible, ignorer
|
|
39
37
|
lbStatus = undefined;
|
|
40
38
|
lbHasSession = false;
|
|
41
|
-
lbHasSelectedKey = false;
|
|
42
39
|
hasLBProvider = false;
|
|
43
40
|
}
|
|
44
41
|
// Récupérer le contexte AiProvider avec fallback sur les props
|
|
@@ -50,7 +47,7 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
|
|
|
50
47
|
const needsApiKeySelection = hasLBProvider &&
|
|
51
48
|
lbStatus === "ready" &&
|
|
52
49
|
lbHasSession &&
|
|
53
|
-
|
|
50
|
+
!lbHasSelectedApiKeyCookie;
|
|
54
51
|
const isAuthReady = !needsApiKeySelection &&
|
|
55
52
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
56
53
|
const handleOpenPanel = () => {
|
|
@@ -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,2CA6Od"}
|
|
@@ -23,14 +23,12 @@ export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode =
|
|
|
23
23
|
// Rendre l'authentification optionnelle
|
|
24
24
|
let lbStatus;
|
|
25
25
|
let lbHasSession = false;
|
|
26
|
-
let lbHasSelectedKey = false;
|
|
27
26
|
let lbHasSelectedApiKeyCookie = false;
|
|
28
27
|
let hasLBProvider = false;
|
|
29
28
|
try {
|
|
30
29
|
const lbContext = useLB();
|
|
31
30
|
lbStatus = lbContext.status;
|
|
32
31
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
33
|
-
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
34
32
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
35
33
|
hasLBProvider = true;
|
|
36
34
|
}
|
|
@@ -38,7 +36,6 @@ export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode =
|
|
|
38
36
|
// LBProvider n'est pas disponible, ignorer
|
|
39
37
|
lbStatus = undefined;
|
|
40
38
|
lbHasSession = false;
|
|
41
|
-
lbHasSelectedKey = false;
|
|
42
39
|
hasLBProvider = false;
|
|
43
40
|
}
|
|
44
41
|
let ctxBaseUrl;
|
|
@@ -65,7 +62,7 @@ export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode =
|
|
|
65
62
|
const needsApiKeySelection = hasLBProvider &&
|
|
66
63
|
lbStatus === "ready" &&
|
|
67
64
|
lbHasSession &&
|
|
68
|
-
|
|
65
|
+
!lbHasSelectedApiKeyCookie;
|
|
69
66
|
const isAuthReady = !needsApiKeySelection &&
|
|
70
67
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
71
68
|
const shouldShowSparkles = isAuthReady && !disabled;
|
|
@@ -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,2CA+Jf"}
|
|
@@ -20,14 +20,12 @@ export function AiSelect({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode
|
|
|
20
20
|
// Rendre l'authentification optionnelle
|
|
21
21
|
let lbStatus;
|
|
22
22
|
let lbHasSession = false;
|
|
23
|
-
let lbHasSelectedKey = false;
|
|
24
23
|
let lbHasSelectedApiKeyCookie = false;
|
|
25
24
|
let hasLBProvider = false;
|
|
26
25
|
try {
|
|
27
26
|
const lbContext = useLB();
|
|
28
27
|
lbStatus = lbContext.status;
|
|
29
28
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
30
|
-
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
31
29
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
32
30
|
hasLBProvider = true;
|
|
33
31
|
}
|
|
@@ -35,7 +33,6 @@ export function AiSelect({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode
|
|
|
35
33
|
// LBProvider n'est pas disponible, ignorer
|
|
36
34
|
lbStatus = undefined;
|
|
37
35
|
lbHasSession = false;
|
|
38
|
-
lbHasSelectedKey = false;
|
|
39
36
|
hasLBProvider = false;
|
|
40
37
|
}
|
|
41
38
|
let ctxBaseUrl;
|
|
@@ -60,7 +57,7 @@ export function AiSelect({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode
|
|
|
60
57
|
const needsApiKeySelection = hasLBProvider &&
|
|
61
58
|
lbStatus === "ready" &&
|
|
62
59
|
lbHasSession &&
|
|
63
|
-
|
|
60
|
+
!lbHasSelectedApiKeyCookie;
|
|
64
61
|
const isAuthReady = !needsApiKeySelection &&
|
|
65
62
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
66
63
|
const shouldShowSparkles = isAuthReady && !disabled;
|
|
@@ -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;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,
|
|
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,2CA0jBrB"}
|
|
@@ -133,10 +133,7 @@ export function AiStatusButton({ status, loading = false, className = "", size =
|
|
|
133
133
|
? effectiveStatus.authType
|
|
134
134
|
: undefined;
|
|
135
135
|
const hasLbSession = Boolean(lbSessionToken);
|
|
136
|
-
const requiresApiKeySelection = lbStatus === "ready" &&
|
|
137
|
-
hasLbSession &&
|
|
138
|
-
(!lbHasSelectedApiKeyCookie || !hasApiKeySelected) &&
|
|
139
|
-
apiKeys.length > 0;
|
|
136
|
+
const requiresApiKeySelection = lbStatus === "ready" && hasLbSession && !lbHasSelectedApiKeyCookie;
|
|
140
137
|
const isApiKeyAuthMode = authTypeValue === "api_key";
|
|
141
138
|
const [tooltipStyle, setTooltipStyle] = useState({});
|
|
142
139
|
useEffect(() => {
|
|
@@ -337,7 +334,7 @@ export function AiStatusButton({ status, loading = false, className = "", size =
|
|
|
337
334
|
}
|
|
338
335
|
}, disabled: loading || isSelectingApiKey, title: requiresApiKeySelection
|
|
339
336
|
? t("status.selectApiKey", "Select an API key")
|
|
340
|
-
: t("status.view", "View status"), "aria-label": t("status.aiStatusAria", "AI status"), children: renderTriggerIcon() }), showCornerLoading ? (_jsx("span", { className: "ai-status-loading-dot", "aria-hidden": "true", children: _jsx(Loader2, { size: 7, className: "ai-spinner" }) })) : null] }), tooltipNode, showApiKeySelector
|
|
337
|
+
: t("status.view", "View status"), "aria-label": t("status.aiStatusAria", "AI status"), children: renderTriggerIcon() }), showCornerLoading ? (_jsx("span", { className: "ai-status-loading-dot", "aria-hidden": "true", children: _jsx(Loader2, { size: 7, className: "ai-spinner" }) })) : null] }), tooltipNode, showApiKeySelector ? (_jsx(LBApiKeySelector, { isOpen: showApiKeySelector, apiKeys: apiKeys, onSelect: async (keyId) => {
|
|
341
338
|
setIsSelectingApiKey(true);
|
|
342
339
|
try {
|
|
343
340
|
if (!switchApiKey) {
|
|
@@ -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,2CAmQjB"}
|
|
@@ -25,14 +25,12 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
25
25
|
// Rendre l'authentification optionnelle
|
|
26
26
|
let lbStatus;
|
|
27
27
|
let lbHasSession = false;
|
|
28
|
-
let lbHasSelectedKey = false;
|
|
29
28
|
let lbHasSelectedApiKeyCookie = false;
|
|
30
29
|
let hasLBProvider = false;
|
|
31
30
|
try {
|
|
32
31
|
const lbContext = useLB();
|
|
33
32
|
lbStatus = lbContext.status;
|
|
34
33
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
35
|
-
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
36
34
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
37
35
|
hasLBProvider = true;
|
|
38
36
|
}
|
|
@@ -57,7 +55,7 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
|
|
|
57
55
|
const needsApiKeySelection = hasLBProvider &&
|
|
58
56
|
lbStatus === "ready" &&
|
|
59
57
|
lbHasSession &&
|
|
60
|
-
|
|
58
|
+
!lbHasSelectedApiKeyCookie;
|
|
61
59
|
const { models: _models } = useAiModels({
|
|
62
60
|
baseUrl,
|
|
63
61
|
apiKeyId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LBApiKeySelector.d.ts","sourceRoot":"","sources":["../../src/components/LBApiKeySelector.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LBApiKeySelector.d.ts","sourceRoot":"","sources":["../../src/components/LBApiKeySelector.tsx"],"names":[],"mappings":"AAKA,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,kDAmKvB"}
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from "react";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
4
|
import { CheckCircle2, KeyRound, Loader2, XCircle } from "lucide-react";
|
|
5
|
+
import { createPortal } from "react-dom";
|
|
5
6
|
import { useI18n } from "../context/I18nContext";
|
|
6
7
|
export function LBApiKeySelector({ apiKeys, onSelect, onCancel, isOpen, }) {
|
|
7
8
|
const { t } = useI18n();
|
|
9
|
+
const [canPortal, setCanPortal] = useState(false);
|
|
8
10
|
const [selectedKeyId, setSelectedKeyId] = useState(apiKeys.find((k) => k.isActive)?.id || apiKeys[0]?.id || "");
|
|
9
11
|
const [loading, setLoading] = useState(false);
|
|
10
12
|
const [error, setError] = useState("");
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
setCanPortal(true);
|
|
15
|
+
return () => setCanPortal(false);
|
|
16
|
+
}, []);
|
|
11
17
|
if (!isOpen)
|
|
12
18
|
return null;
|
|
13
19
|
const handleSubmit = async (e) => {
|
|
@@ -30,7 +36,7 @@ export function LBApiKeySelector({ apiKeys, onSelect, onCancel, isOpen, }) {
|
|
|
30
36
|
setLoading(false);
|
|
31
37
|
}
|
|
32
38
|
};
|
|
33
|
-
|
|
39
|
+
const modal = (_jsx("div", { className: "ai-signin-overlay", onClick: onCancel, children: _jsxs("div", { className: "ai-signin-panel ai-key-modal-panel", onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { className: "ai-signin-header", children: [_jsx("div", { className: "ai-center mb-3", children: _jsx("span", { className: "ai-icon-badge", children: _jsx(KeyRound, { size: 20 }) }) }), _jsx("h2", { className: "ai-signin-title", children: t("status.selectApiKey", "Select an API key") }), _jsx("p", { className: "ai-signin-subtitle", children: t("status.selectApiKeySubtitle", "Choose the API key to use for your AI requests.") })] }), _jsx("div", { className: "ai-signin-content", children: _jsxs("form", { onSubmit: handleSubmit, children: [_jsx("div", { className: "ai-model-mgmt-list", style: { maxHeight: 300, overflowY: "auto", marginBottom: 16 }, children: apiKeys.map((key) => {
|
|
34
40
|
const isSelected = key.id === selectedKeyId;
|
|
35
41
|
const isActive = key.isActive;
|
|
36
42
|
const rawEnv = key.env;
|
|
@@ -38,4 +44,8 @@ export function LBApiKeySelector({ apiKeys, onSelect, onCancel, isOpen, }) {
|
|
|
38
44
|
const isDev = rawEnv === "dev";
|
|
39
45
|
return (_jsxs("label", { className: `ai-model-item ${isSelected ? "ai-model-item--active" : ""} ${!isActive ? "ai-model-item--disabled" : ""}`, children: [_jsxs("div", { className: "ai-model-item-main", children: [_jsx("input", { type: "radio", name: "apiKey", value: key.id, checked: isSelected, disabled: !isActive, onChange: (e) => setSelectedKeyId(e.target.value), className: "ai-key-radio" }), _jsxs("div", { children: [_jsx("div", { className: "ai-model-item-title", children: key.name }), _jsxs("div", { className: "ai-model-item-meta", children: [_jsx("span", { children: key.keyPrefix || key.id.substring(0, 12) + "..." }), _jsx("span", { className: `ai-pill ai-pill--cost ${isDev ? "ai-pill--warning" : ""}`, style: { marginLeft: 8 }, children: keyEnv })] })] })] }), isActive ? (_jsxs("span", { className: "ai-pill ai-pill--cost", children: [_jsx(CheckCircle2, { size: 12 }), t("common.active", "Active")] })) : (_jsxs("span", { className: "ai-pill ai-pill--cost", children: [_jsx(XCircle, { size: 12 }), t("common.inactive", "Inactive")] }))] }, key.id));
|
|
40
46
|
}) }), error ? (_jsxs("div", { className: "ai-signin-error", role: "alert", children: [_jsx(XCircle, { size: 16 }), _jsx("span", { children: error })] })) : null, _jsxs("div", { className: "ai-signin-actions", children: [_jsx("button", { type: "button", onClick: onCancel, disabled: loading, className: "ai-btn ai-btn--ghost", children: t("common.cancel", "Cancel") }), _jsx("button", { type: "submit", disabled: loading || !selectedKeyId, className: "ai-btn ai-btn--primary", children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 16, className: "ai-spinner" }), t("auth.modal.connecting", "Signing in...")] })) : (t("common.continue", "Continue")) })] })] }) })] }) }));
|
|
47
|
+
if (!canPortal) {
|
|
48
|
+
return modal;
|
|
49
|
+
}
|
|
50
|
+
return createPortal(modal, document.body);
|
|
41
51
|
}
|
|
@@ -100,7 +100,7 @@ export function UsageToast({ result, position = "bottom-right", onComplete, }) {
|
|
|
100
100
|
padding: "4px 6px",
|
|
101
101
|
borderRadius: "6px",
|
|
102
102
|
marginBottom: "4px",
|
|
103
|
-
right: "
|
|
103
|
+
right: "52px",
|
|
104
104
|
background: "rgba(22, 163, 74, 0.12)",
|
|
105
105
|
border: "1px solid rgba(22, 163, 74, 0.3)",
|
|
106
106
|
color: "#16a34a",
|
|
@@ -25,6 +25,8 @@ export interface BasicStatus {
|
|
|
25
25
|
used?: number;
|
|
26
26
|
total?: number;
|
|
27
27
|
percentage?: number;
|
|
28
|
+
remaining?: number;
|
|
29
|
+
providerBudget?: number;
|
|
28
30
|
};
|
|
29
31
|
storage?: StorageStatus["storage"];
|
|
30
32
|
}
|
|
@@ -88,7 +90,7 @@ interface LBContextValue extends LBAuthState {
|
|
|
88
90
|
isLoadingStatus: boolean;
|
|
89
91
|
/** Indique si le storage est en cours de chargement */
|
|
90
92
|
isLoadingStorage: boolean;
|
|
91
|
-
/** True si le cookie api_key_selected est présent */
|
|
93
|
+
/** True si le cookie api_key_selected est présent (info backend) */
|
|
92
94
|
hasSelectedApiKeyCookie: boolean;
|
|
93
95
|
}
|
|
94
96
|
declare const LBContext: import("react").Context<LBContextValue | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAGR,QAAQ,EACT,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAEnE,MAAM,WAAW,UAAU;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE;QACL,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,IAAI,CAAC;IACT,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAGR,QAAQ,EACT,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAEnE,MAAM,WAAW,UAAU;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE;QACL,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,IAAI,CAAC;IACT,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,OAAO,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,IAAI,CAAC;CACV;AAgBD,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;IAC1B,mDAAmD;IACnD,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB;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,mEAAmE;IACnE,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,uCAAuC;IACvC,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;IACpC,gDAAgD;IAChD,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,iEAAiE;IACjE,oBAAoB,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,kEAAkE;IAClE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,8DAA8D;IAC9D,eAAe,EAAE,OAAO,CAAC;IACzB,uDAAuD;IACvD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,oEAAoE;IACpE,uBAAuB,EAAE,OAAO,CAAC;CAClC;AAED,QAAA,MAAM,SAAS,qDAAuD,CAAC;AAGvE,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,EAAE,QAA2B,EACpC,QAA2B,EAC3B,cAAc,EACd,YAAY,EACZ,IAAW,GACZ,EAAE,eAAe,2CAokBjB;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,cAAc,CAMtC;AAGD,YAAY,EAAE,QAAQ,EAAE,CAAC"}
|
|
@@ -7,6 +7,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
7
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
|
+
function hasServerSelectedApiKeyCookie(userData) {
|
|
11
|
+
if (!userData || typeof userData !== "object")
|
|
12
|
+
return false;
|
|
13
|
+
const value = userData.hasApiKeySelectedCookie;
|
|
14
|
+
return value === true;
|
|
15
|
+
}
|
|
10
16
|
const LBContext = createContext(undefined);
|
|
11
17
|
// Export pour usage dans d'autres composants
|
|
12
18
|
export { LBContext };
|
|
@@ -24,15 +30,6 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
24
30
|
const [storageLastFetch, setStorageLastFetch] = useState(0);
|
|
25
31
|
const [hasSelectedApiKeyCookie, setHasSelectedApiKeyCookie] = useState(false);
|
|
26
32
|
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
|
-
}, []);
|
|
36
33
|
const lbClient = useMemo(() => createLBClient({
|
|
37
34
|
baseUrl: proxyUrl,
|
|
38
35
|
mode: process.env.LB_API_KEY ? "env-key" : "auto",
|
|
@@ -46,6 +43,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
46
43
|
const session = await lbClient.verifySession();
|
|
47
44
|
if (session) {
|
|
48
45
|
const userData = await lbClient.getUser().catch(() => null);
|
|
46
|
+
const hasCookie = hasServerSelectedApiKeyCookie(userData);
|
|
49
47
|
const activeKey = userData?.apiKeyActive
|
|
50
48
|
? {
|
|
51
49
|
id: userData.apiKeyActive.id,
|
|
@@ -64,14 +62,16 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
64
62
|
id: session.userId,
|
|
65
63
|
email: userData?.user?.email || "",
|
|
66
64
|
},
|
|
67
|
-
selectedKey: activeKey,
|
|
65
|
+
selectedKey: hasCookie ? activeKey : undefined,
|
|
68
66
|
});
|
|
67
|
+
setHasSelectedApiKeyCookie(hasCookie);
|
|
69
68
|
onStatusChange?.("ready");
|
|
70
69
|
}
|
|
71
70
|
else {
|
|
72
71
|
// Supabase session mode (no lb_session cookie): try user endpoint directly
|
|
73
72
|
const userData = await lbClient.getUser().catch(() => null);
|
|
74
73
|
if (userData?.user?.id) {
|
|
74
|
+
const hasCookie = hasServerSelectedApiKeyCookie(userData);
|
|
75
75
|
const activeKey = userData?.apiKeyActive
|
|
76
76
|
? {
|
|
77
77
|
id: userData.apiKeyActive.id,
|
|
@@ -89,12 +89,14 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
89
89
|
id: userData.user.id,
|
|
90
90
|
email: userData.user.email || "",
|
|
91
91
|
},
|
|
92
|
-
selectedKey: activeKey,
|
|
92
|
+
selectedKey: hasCookie ? activeKey : undefined,
|
|
93
93
|
});
|
|
94
|
+
setHasSelectedApiKeyCookie(hasCookie);
|
|
94
95
|
onStatusChange?.("ready");
|
|
95
96
|
}
|
|
96
97
|
else {
|
|
97
98
|
setState({ status: "needs_auth" });
|
|
99
|
+
setHasSelectedApiKeyCookie(false);
|
|
98
100
|
onStatusChange?.("needs_auth");
|
|
99
101
|
}
|
|
100
102
|
}
|
|
@@ -102,19 +104,30 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
102
104
|
catch (error) {
|
|
103
105
|
console.error("[LBProvider] Session check failed:", error);
|
|
104
106
|
setState({ status: "needs_auth" });
|
|
107
|
+
setHasSelectedApiKeyCookie(false);
|
|
105
108
|
onStatusChange?.("needs_auth");
|
|
106
109
|
}
|
|
107
110
|
}, [lbClient, onStatusChange]);
|
|
108
111
|
useEffect(() => {
|
|
109
112
|
checkSession();
|
|
110
113
|
}, [checkSession]);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
const fetchWalletSummary = useCallback(async () => {
|
|
115
|
+
const headers = lbClient.getAuthHeaders(state.session?.sessionToken);
|
|
116
|
+
const response = await fetch(`${proxyUrl}/auth/wallet`, {
|
|
117
|
+
method: "GET",
|
|
118
|
+
credentials: "include",
|
|
119
|
+
headers,
|
|
120
|
+
});
|
|
121
|
+
if (!response.ok)
|
|
122
|
+
return null;
|
|
123
|
+
const data = (await response.json());
|
|
124
|
+
return {
|
|
125
|
+
used: Number(data.totalUsed || 0),
|
|
126
|
+
total: Number(data.totalAdded || 0),
|
|
127
|
+
percentage: Number(data.percentUsed ?? data.percentage ?? 0),
|
|
128
|
+
remaining: Number(data.walletSellValueUsd || 0),
|
|
129
|
+
};
|
|
130
|
+
}, [lbClient, proxyUrl, state.session?.sessionToken]);
|
|
118
131
|
/**
|
|
119
132
|
* Récupère les clés API de l'utilisateur
|
|
120
133
|
*/
|
|
@@ -153,7 +166,6 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
153
166
|
setAccessToken(undefined); // Nettoyer l'access token temporaire
|
|
154
167
|
setApiKeys([]); // Nettoyer les clés API temporaires
|
|
155
168
|
setHasSelectedApiKeyCookie(true);
|
|
156
|
-
setTimeout(() => syncSelectedApiKeyCookie(), 100);
|
|
157
169
|
onStatusChange?.("ready");
|
|
158
170
|
onAuthChange?.(); // Refresh provider after signin
|
|
159
171
|
}
|
|
@@ -165,7 +177,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
165
177
|
});
|
|
166
178
|
throw error;
|
|
167
179
|
}
|
|
168
|
-
}, [lbClient, onAuthChange, onStatusChange, state.user
|
|
180
|
+
}, [lbClient, onAuthChange, onStatusChange, state.user]);
|
|
169
181
|
/**
|
|
170
182
|
* Connexion utilisateur (étape 1 : login)
|
|
171
183
|
* Retourne le token et les clés API sans créer de session
|
|
@@ -273,16 +285,20 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
273
285
|
used: 0,
|
|
274
286
|
total: userData.balance?.sellValueUsd || 0,
|
|
275
287
|
percentage: 0,
|
|
288
|
+
remaining: userData.balance?.sellValueUsd || 0,
|
|
276
289
|
},
|
|
277
290
|
};
|
|
278
291
|
}
|
|
292
|
+
const userData = await lbClient.getUser().catch(() => null);
|
|
293
|
+
setHasSelectedApiKeyCookie(hasServerSelectedApiKeyCookie(userData));
|
|
294
|
+
const walletSummary = await fetchWalletSummary().catch(() => null);
|
|
279
295
|
const normalizedStatus = {
|
|
280
296
|
...data,
|
|
281
297
|
authType: data?.authType,
|
|
282
|
-
user: data?.user,
|
|
283
|
-
apiKey: data?.apiKey || data?.api_key,
|
|
284
|
-
api_key: data?.api_key || data?.apiKey,
|
|
285
|
-
balance: data?.balance || {
|
|
298
|
+
user: data?.user || userData?.user,
|
|
299
|
+
apiKey: data?.apiKey || data?.api_key || userData?.apiKeyActive,
|
|
300
|
+
api_key: data?.api_key || data?.apiKey || userData?.apiKeyActive,
|
|
301
|
+
balance: walletSummary || data?.balance || {
|
|
286
302
|
used: 0,
|
|
287
303
|
total: 0,
|
|
288
304
|
percentage: 0,
|
|
@@ -302,7 +318,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
302
318
|
finally {
|
|
303
319
|
setIsLoadingStatus(false);
|
|
304
320
|
}
|
|
305
|
-
}, [lbClient, state.status, storageStatus]);
|
|
321
|
+
}, [fetchWalletSummary, lbClient, state.status, storageStatus]);
|
|
306
322
|
/**
|
|
307
323
|
* Récupère le storage - LENT avec cache (5 minutes)
|
|
308
324
|
*/
|
|
@@ -382,7 +398,6 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
382
398
|
setHasSelectedApiKeyCookie(true);
|
|
383
399
|
await refreshBasicStatus();
|
|
384
400
|
setTimeout(() => refreshStorageStatus(), 100);
|
|
385
|
-
setTimeout(() => syncSelectedApiKeyCookie(), 100);
|
|
386
401
|
}
|
|
387
402
|
else {
|
|
388
403
|
throw new Error("No valid authentication method available");
|
|
@@ -395,7 +410,6 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
395
410
|
lbClient,
|
|
396
411
|
refreshBasicStatus,
|
|
397
412
|
refreshStorageStatus,
|
|
398
|
-
syncSelectedApiKeyCookie,
|
|
399
413
|
]);
|
|
400
414
|
/**
|
|
401
415
|
* Déconnexion
|
|
@@ -444,6 +458,13 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
444
458
|
setApiKeys([]);
|
|
445
459
|
}
|
|
446
460
|
}, [lbClient, state.status]);
|
|
461
|
+
useEffect(() => {
|
|
462
|
+
if (state.status !== "ready" || !state.session)
|
|
463
|
+
return;
|
|
464
|
+
if (hasSelectedApiKeyCookie)
|
|
465
|
+
return;
|
|
466
|
+
setState((prev) => ({ ...prev, selectedKey: undefined }));
|
|
467
|
+
}, [hasSelectedApiKeyCookie, state.session, state.status]);
|
|
447
468
|
// Refresh status uniquement lors de la transition vers "ready"
|
|
448
469
|
// (évite les boucles status/user causées par des callbacks recréés)
|
|
449
470
|
useEffect(() => {
|
package/dist/styles.css
CHANGED
|
@@ -310,7 +310,7 @@
|
|
|
310
310
|
min-height: 96px;
|
|
311
311
|
resize: none;
|
|
312
312
|
overflow: hidden;
|
|
313
|
-
padding: 6px 2px
|
|
313
|
+
padding: 6px 2px 32px;
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
.ai-control[aria-invalid="true"],
|
|
@@ -641,7 +641,7 @@
|
|
|
641
641
|
|
|
642
642
|
.ai-status-tooltip {
|
|
643
643
|
width: min(370px, calc(100vw - 16px));
|
|
644
|
-
z-index:
|
|
644
|
+
z-index: 2147483646;
|
|
645
645
|
}
|
|
646
646
|
|
|
647
647
|
.ai-status-actions {
|
|
@@ -685,7 +685,7 @@
|
|
|
685
685
|
white-space: nowrap;
|
|
686
686
|
opacity: 0;
|
|
687
687
|
pointer-events: none;
|
|
688
|
-
z-index:
|
|
688
|
+
z-index: 2147483646;
|
|
689
689
|
padding: 4px 8px;
|
|
690
690
|
border-radius: 8px;
|
|
691
691
|
border: 1px solid var(--ai-border);
|
|
@@ -772,7 +772,7 @@
|
|
|
772
772
|
[data-ai-settings-panel] {
|
|
773
773
|
position: fixed;
|
|
774
774
|
inset: 0;
|
|
775
|
-
z-index:
|
|
775
|
+
z-index: 2147483647;
|
|
776
776
|
display: flex;
|
|
777
777
|
align-items: center;
|
|
778
778
|
justify-content: center;
|
|
@@ -1109,6 +1109,8 @@
|
|
|
1109
1109
|
|
|
1110
1110
|
.ai-key-modal-panel {
|
|
1111
1111
|
max-width: 520px;
|
|
1112
|
+
position: relative;
|
|
1113
|
+
z-index: 2147483647;
|
|
1112
1114
|
}
|
|
1113
1115
|
|
|
1114
1116
|
.ai-key-radio {
|
package/package.json
CHANGED
|
@@ -84,20 +84,17 @@ export function AiContextButton({
|
|
|
84
84
|
|
|
85
85
|
let lbStatus: string | undefined;
|
|
86
86
|
let lbHasSession = false;
|
|
87
|
-
let lbHasSelectedKey = false;
|
|
88
87
|
let lbHasSelectedApiKeyCookie = false;
|
|
89
88
|
let hasLBProvider = false;
|
|
90
89
|
try {
|
|
91
90
|
const lbContext = useLB();
|
|
92
91
|
lbStatus = lbContext.status;
|
|
93
92
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
94
|
-
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
95
93
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
96
94
|
hasLBProvider = true;
|
|
97
95
|
} catch {
|
|
98
96
|
lbStatus = undefined;
|
|
99
97
|
lbHasSession = false;
|
|
100
|
-
lbHasSelectedKey = false;
|
|
101
98
|
hasLBProvider = false;
|
|
102
99
|
}
|
|
103
100
|
|
|
@@ -114,7 +111,7 @@ export function AiContextButton({
|
|
|
114
111
|
hasLBProvider &&
|
|
115
112
|
lbStatus === "ready" &&
|
|
116
113
|
lbHasSession &&
|
|
117
|
-
|
|
114
|
+
!lbHasSelectedApiKeyCookie;
|
|
118
115
|
const isAuthReady =
|
|
119
116
|
!needsApiKeySelection &&
|
|
120
117
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
@@ -80,21 +80,18 @@ export function AiImageButton({
|
|
|
80
80
|
// Rendre l'authentification optionnelle
|
|
81
81
|
let lbStatus: string | undefined;
|
|
82
82
|
let lbHasSession = false;
|
|
83
|
-
let lbHasSelectedKey = false;
|
|
84
83
|
let lbHasSelectedApiKeyCookie = false;
|
|
85
84
|
let hasLBProvider = false;
|
|
86
85
|
try {
|
|
87
86
|
const lbContext = useLB();
|
|
88
87
|
lbStatus = lbContext.status;
|
|
89
88
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
90
|
-
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
91
89
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
92
90
|
hasLBProvider = true;
|
|
93
91
|
} catch {
|
|
94
92
|
// LBProvider n'est pas disponible, ignorer
|
|
95
93
|
lbStatus = undefined;
|
|
96
94
|
lbHasSession = false;
|
|
97
|
-
lbHasSelectedKey = false;
|
|
98
95
|
hasLBProvider = false;
|
|
99
96
|
}
|
|
100
97
|
|
|
@@ -110,7 +107,7 @@ export function AiImageButton({
|
|
|
110
107
|
hasLBProvider &&
|
|
111
108
|
lbStatus === "ready" &&
|
|
112
109
|
lbHasSession &&
|
|
113
|
-
|
|
110
|
+
!lbHasSelectedApiKeyCookie;
|
|
114
111
|
const isAuthReady =
|
|
115
112
|
!needsApiKeySelection &&
|
|
116
113
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
@@ -61,21 +61,18 @@ export function AiInput({
|
|
|
61
61
|
// Rendre l'authentification optionnelle
|
|
62
62
|
let lbStatus: string | undefined;
|
|
63
63
|
let lbHasSession = false;
|
|
64
|
-
let lbHasSelectedKey = false;
|
|
65
64
|
let lbHasSelectedApiKeyCookie = false;
|
|
66
65
|
let hasLBProvider = false;
|
|
67
66
|
try {
|
|
68
67
|
const lbContext = useLB();
|
|
69
68
|
lbStatus = lbContext.status;
|
|
70
69
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
71
|
-
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
72
70
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
73
71
|
hasLBProvider = true;
|
|
74
72
|
} catch {
|
|
75
73
|
// LBProvider n'est pas disponible, ignorer
|
|
76
74
|
lbStatus = undefined;
|
|
77
75
|
lbHasSession = false;
|
|
78
|
-
lbHasSelectedKey = false;
|
|
79
76
|
hasLBProvider = false;
|
|
80
77
|
}
|
|
81
78
|
|
|
@@ -106,7 +103,7 @@ export function AiInput({
|
|
|
106
103
|
hasLBProvider &&
|
|
107
104
|
lbStatus === "ready" &&
|
|
108
105
|
lbHasSession &&
|
|
109
|
-
|
|
106
|
+
!lbHasSelectedApiKeyCookie;
|
|
110
107
|
const isAuthReady =
|
|
111
108
|
!needsApiKeySelection &&
|
|
112
109
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
@@ -50,21 +50,18 @@ export function AiSelect({
|
|
|
50
50
|
// Rendre l'authentification optionnelle
|
|
51
51
|
let lbStatus: string | undefined;
|
|
52
52
|
let lbHasSession = false;
|
|
53
|
-
let lbHasSelectedKey = false;
|
|
54
53
|
let lbHasSelectedApiKeyCookie = false;
|
|
55
54
|
let hasLBProvider = false;
|
|
56
55
|
try {
|
|
57
56
|
const lbContext = useLB();
|
|
58
57
|
lbStatus = lbContext.status;
|
|
59
58
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
60
|
-
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
61
59
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
62
60
|
hasLBProvider = true;
|
|
63
61
|
} catch {
|
|
64
62
|
// LBProvider n'est pas disponible, ignorer
|
|
65
63
|
lbStatus = undefined;
|
|
66
64
|
lbHasSession = false;
|
|
67
|
-
lbHasSelectedKey = false;
|
|
68
65
|
hasLBProvider = false;
|
|
69
66
|
}
|
|
70
67
|
|
|
@@ -93,7 +90,7 @@ export function AiSelect({
|
|
|
93
90
|
hasLBProvider &&
|
|
94
91
|
lbStatus === "ready" &&
|
|
95
92
|
lbHasSession &&
|
|
96
|
-
|
|
93
|
+
!lbHasSelectedApiKeyCookie;
|
|
97
94
|
const isAuthReady =
|
|
98
95
|
!needsApiKeySelection &&
|
|
99
96
|
(lbStatus === "ready" || Boolean(process.env.LB_API_KEY));
|
|
@@ -243,10 +243,7 @@ export function AiStatusButton({
|
|
|
243
243
|
: undefined;
|
|
244
244
|
const hasLbSession = Boolean(lbSessionToken);
|
|
245
245
|
const requiresApiKeySelection =
|
|
246
|
-
lbStatus === "ready" &&
|
|
247
|
-
hasLbSession &&
|
|
248
|
-
(!lbHasSelectedApiKeyCookie || !hasApiKeySelected) &&
|
|
249
|
-
apiKeys.length > 0;
|
|
246
|
+
lbStatus === "ready" && hasLbSession && !lbHasSelectedApiKeyCookie;
|
|
250
247
|
const isApiKeyAuthMode = authTypeValue === "api_key";
|
|
251
248
|
|
|
252
249
|
const [tooltipStyle, setTooltipStyle] = useState<Record<string, string>>({});
|
|
@@ -702,7 +699,7 @@ export function AiStatusButton({
|
|
|
702
699
|
|
|
703
700
|
{tooltipNode}
|
|
704
701
|
|
|
705
|
-
{showApiKeySelector
|
|
702
|
+
{showApiKeySelector ? (
|
|
706
703
|
<LBApiKeySelector
|
|
707
704
|
isOpen={showApiKeySelector}
|
|
708
705
|
apiKeys={apiKeys}
|
|
@@ -64,14 +64,12 @@ export function AiTextarea({
|
|
|
64
64
|
// Rendre l'authentification optionnelle
|
|
65
65
|
let lbStatus: string | undefined;
|
|
66
66
|
let lbHasSession = false;
|
|
67
|
-
let lbHasSelectedKey = false;
|
|
68
67
|
let lbHasSelectedApiKeyCookie = false;
|
|
69
68
|
let hasLBProvider = false;
|
|
70
69
|
try {
|
|
71
70
|
const lbContext = useLB();
|
|
72
71
|
lbStatus = lbContext.status;
|
|
73
72
|
lbHasSession = Boolean(lbContext.session?.sessionToken);
|
|
74
|
-
lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
|
|
75
73
|
lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
|
|
76
74
|
hasLBProvider = true;
|
|
77
75
|
} catch {
|
|
@@ -97,7 +95,7 @@ export function AiTextarea({
|
|
|
97
95
|
hasLBProvider &&
|
|
98
96
|
lbStatus === "ready" &&
|
|
99
97
|
lbHasSession &&
|
|
100
|
-
|
|
98
|
+
!lbHasSelectedApiKeyCookie;
|
|
101
99
|
|
|
102
100
|
const { models: _models } = useAiModels({
|
|
103
101
|
baseUrl,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import React, { useState } from "react";
|
|
3
|
+
import React, { useEffect, useState } from "react";
|
|
4
4
|
import { CheckCircle2, KeyRound, Loader2, XCircle } from "lucide-react";
|
|
5
|
+
import { createPortal } from "react-dom";
|
|
5
6
|
import type { LBApiKey } from "@lastbrain/ai-ui-core";
|
|
6
7
|
import { useI18n } from "../context/I18nContext";
|
|
7
8
|
|
|
@@ -19,12 +20,18 @@ export function LBApiKeySelector({
|
|
|
19
20
|
isOpen,
|
|
20
21
|
}: LBApiKeySelectorProps) {
|
|
21
22
|
const { t } = useI18n();
|
|
23
|
+
const [canPortal, setCanPortal] = useState(false);
|
|
22
24
|
const [selectedKeyId, setSelectedKeyId] = useState<string>(
|
|
23
25
|
apiKeys.find((k) => k.isActive)?.id || apiKeys[0]?.id || ""
|
|
24
26
|
);
|
|
25
27
|
const [loading, setLoading] = useState(false);
|
|
26
28
|
const [error, setError] = useState("");
|
|
27
29
|
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
setCanPortal(true);
|
|
32
|
+
return () => setCanPortal(false);
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
28
35
|
if (!isOpen) return null;
|
|
29
36
|
|
|
30
37
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
@@ -50,7 +57,7 @@ export function LBApiKeySelector({
|
|
|
50
57
|
}
|
|
51
58
|
};
|
|
52
59
|
|
|
53
|
-
|
|
60
|
+
const modal = (
|
|
54
61
|
<div className="ai-signin-overlay" onClick={onCancel}>
|
|
55
62
|
<div
|
|
56
63
|
className="ai-signin-panel ai-key-modal-panel"
|
|
@@ -169,4 +176,9 @@ export function LBApiKeySelector({
|
|
|
169
176
|
</div>
|
|
170
177
|
</div>
|
|
171
178
|
);
|
|
179
|
+
|
|
180
|
+
if (!canPortal) {
|
|
181
|
+
return modal;
|
|
182
|
+
}
|
|
183
|
+
return createPortal(modal, document.body);
|
|
172
184
|
}
|
|
@@ -121,7 +121,7 @@ export function UsageToast({
|
|
|
121
121
|
padding: "4px 6px",
|
|
122
122
|
borderRadius: "6px",
|
|
123
123
|
marginBottom: "4px",
|
|
124
|
-
right: "
|
|
124
|
+
right: "52px",
|
|
125
125
|
background: "rgba(22, 163, 74, 0.12)",
|
|
126
126
|
border: "1px solid rgba(22, 163, 74, 0.3)",
|
|
127
127
|
color: "#16a34a",
|
|
@@ -46,6 +46,8 @@ export interface BasicStatus {
|
|
|
46
46
|
used?: number;
|
|
47
47
|
total?: number;
|
|
48
48
|
percentage?: number;
|
|
49
|
+
remaining?: number;
|
|
50
|
+
providerBudget?: number;
|
|
49
51
|
};
|
|
50
52
|
storage?: StorageStatus["storage"];
|
|
51
53
|
}
|
|
@@ -58,6 +60,20 @@ export interface StorageStatus {
|
|
|
58
60
|
} | null;
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
interface WalletStatusResponse {
|
|
64
|
+
walletSellValueUsd?: number;
|
|
65
|
+
totalAdded?: number;
|
|
66
|
+
totalUsed?: number;
|
|
67
|
+
percentUsed?: number;
|
|
68
|
+
percentage?: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function hasServerSelectedApiKeyCookie(userData: unknown): boolean {
|
|
72
|
+
if (!userData || typeof userData !== "object") return false;
|
|
73
|
+
const value = (userData as Record<string, unknown>).hasApiKeySelectedCookie;
|
|
74
|
+
return value === true;
|
|
75
|
+
}
|
|
76
|
+
|
|
61
77
|
interface LBProviderProps {
|
|
62
78
|
children: ReactNode;
|
|
63
79
|
/** URL de l'API LastBrain (ex: https://api.lastbrain.io) */
|
|
@@ -115,7 +131,7 @@ interface LBContextValue extends LBAuthState {
|
|
|
115
131
|
isLoadingStatus: boolean;
|
|
116
132
|
/** Indique si le storage est en cours de chargement */
|
|
117
133
|
isLoadingStorage: boolean;
|
|
118
|
-
/** True si le cookie api_key_selected est présent */
|
|
134
|
+
/** True si le cookie api_key_selected est présent (info backend) */
|
|
119
135
|
hasSelectedApiKeyCookie: boolean;
|
|
120
136
|
}
|
|
121
137
|
|
|
@@ -145,18 +161,10 @@ export function LBProvider({
|
|
|
145
161
|
const [isLoadingStatus, setIsLoadingStatus] = useState(false);
|
|
146
162
|
const [isLoadingStorage, setIsLoadingStorage] = useState(false);
|
|
147
163
|
const [storageLastFetch, setStorageLastFetch] = useState<number>(0);
|
|
148
|
-
const [hasSelectedApiKeyCookie, setHasSelectedApiKeyCookie] =
|
|
164
|
+
const [hasSelectedApiKeyCookie, setHasSelectedApiKeyCookie] =
|
|
165
|
+
useState(false);
|
|
149
166
|
const previousStatusRef = useRef<LBAuthState["status"]>("loading");
|
|
150
167
|
|
|
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
|
-
}, []);
|
|
159
|
-
|
|
160
168
|
const lbClient = useMemo(
|
|
161
169
|
() =>
|
|
162
170
|
createLBClient({
|
|
@@ -175,6 +183,7 @@ export function LBProvider({
|
|
|
175
183
|
const session = await lbClient.verifySession();
|
|
176
184
|
if (session) {
|
|
177
185
|
const userData = await lbClient.getUser().catch(() => null);
|
|
186
|
+
const hasCookie = hasServerSelectedApiKeyCookie(userData);
|
|
178
187
|
const activeKey = userData?.apiKeyActive
|
|
179
188
|
? {
|
|
180
189
|
id: userData.apiKeyActive.id,
|
|
@@ -193,13 +202,15 @@ export function LBProvider({
|
|
|
193
202
|
id: session.userId,
|
|
194
203
|
email: userData?.user?.email || "",
|
|
195
204
|
},
|
|
196
|
-
selectedKey: activeKey,
|
|
205
|
+
selectedKey: hasCookie ? activeKey : undefined,
|
|
197
206
|
});
|
|
207
|
+
setHasSelectedApiKeyCookie(hasCookie);
|
|
198
208
|
onStatusChange?.("ready");
|
|
199
209
|
} else {
|
|
200
210
|
// Supabase session mode (no lb_session cookie): try user endpoint directly
|
|
201
211
|
const userData = await lbClient.getUser().catch(() => null);
|
|
202
212
|
if (userData?.user?.id) {
|
|
213
|
+
const hasCookie = hasServerSelectedApiKeyCookie(userData);
|
|
203
214
|
const activeKey = userData?.apiKeyActive
|
|
204
215
|
? {
|
|
205
216
|
id: userData.apiKeyActive.id,
|
|
@@ -218,17 +229,20 @@ export function LBProvider({
|
|
|
218
229
|
id: userData.user.id,
|
|
219
230
|
email: userData.user.email || "",
|
|
220
231
|
},
|
|
221
|
-
selectedKey: activeKey,
|
|
232
|
+
selectedKey: hasCookie ? activeKey : undefined,
|
|
222
233
|
});
|
|
234
|
+
setHasSelectedApiKeyCookie(hasCookie);
|
|
223
235
|
onStatusChange?.("ready");
|
|
224
236
|
} else {
|
|
225
237
|
setState({ status: "needs_auth" });
|
|
238
|
+
setHasSelectedApiKeyCookie(false);
|
|
226
239
|
onStatusChange?.("needs_auth");
|
|
227
240
|
}
|
|
228
241
|
}
|
|
229
242
|
} catch (error) {
|
|
230
243
|
console.error("[LBProvider] Session check failed:", error);
|
|
231
244
|
setState({ status: "needs_auth" });
|
|
245
|
+
setHasSelectedApiKeyCookie(false);
|
|
232
246
|
onStatusChange?.("needs_auth");
|
|
233
247
|
}
|
|
234
248
|
}, [lbClient, onStatusChange]);
|
|
@@ -237,12 +251,27 @@ export function LBProvider({
|
|
|
237
251
|
checkSession();
|
|
238
252
|
}, [checkSession]);
|
|
239
253
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
254
|
+
const fetchWalletSummary = useCallback(async (): Promise<{
|
|
255
|
+
used?: number;
|
|
256
|
+
total?: number;
|
|
257
|
+
percentage?: number;
|
|
258
|
+
remaining?: number;
|
|
259
|
+
} | null> => {
|
|
260
|
+
const headers = lbClient.getAuthHeaders(state.session?.sessionToken);
|
|
261
|
+
const response = await fetch(`${proxyUrl}/auth/wallet`, {
|
|
262
|
+
method: "GET",
|
|
263
|
+
credentials: "include",
|
|
264
|
+
headers,
|
|
265
|
+
});
|
|
266
|
+
if (!response.ok) return null;
|
|
267
|
+
const data = (await response.json()) as WalletStatusResponse;
|
|
268
|
+
return {
|
|
269
|
+
used: Number(data.totalUsed || 0),
|
|
270
|
+
total: Number(data.totalAdded || 0),
|
|
271
|
+
percentage: Number(data.percentUsed ?? data.percentage ?? 0),
|
|
272
|
+
remaining: Number(data.walletSellValueUsd || 0),
|
|
273
|
+
};
|
|
274
|
+
}, [lbClient, proxyUrl, state.session?.sessionToken]);
|
|
246
275
|
|
|
247
276
|
/**
|
|
248
277
|
* Récupère les clés API de l'utilisateur
|
|
@@ -294,7 +323,6 @@ export function LBProvider({
|
|
|
294
323
|
setAccessToken(undefined); // Nettoyer l'access token temporaire
|
|
295
324
|
setApiKeys([]); // Nettoyer les clés API temporaires
|
|
296
325
|
setHasSelectedApiKeyCookie(true);
|
|
297
|
-
setTimeout(() => syncSelectedApiKeyCookie(), 100);
|
|
298
326
|
onStatusChange?.("ready");
|
|
299
327
|
onAuthChange?.(); // Refresh provider after signin
|
|
300
328
|
} catch (error) {
|
|
@@ -307,7 +335,7 @@ export function LBProvider({
|
|
|
307
335
|
throw error;
|
|
308
336
|
}
|
|
309
337
|
},
|
|
310
|
-
[lbClient, onAuthChange, onStatusChange, state.user
|
|
338
|
+
[lbClient, onAuthChange, onStatusChange, state.user]
|
|
311
339
|
);
|
|
312
340
|
|
|
313
341
|
/**
|
|
@@ -452,16 +480,20 @@ export function LBProvider({
|
|
|
452
480
|
used: 0,
|
|
453
481
|
total: userData.balance?.sellValueUsd || 0,
|
|
454
482
|
percentage: 0,
|
|
483
|
+
remaining: userData.balance?.sellValueUsd || 0,
|
|
455
484
|
},
|
|
456
485
|
};
|
|
457
486
|
}
|
|
487
|
+
const userData = await lbClient.getUser().catch(() => null);
|
|
488
|
+
setHasSelectedApiKeyCookie(hasServerSelectedApiKeyCookie(userData));
|
|
489
|
+
const walletSummary = await fetchWalletSummary().catch(() => null);
|
|
458
490
|
const normalizedStatus = {
|
|
459
491
|
...data,
|
|
460
492
|
authType: data?.authType,
|
|
461
|
-
user: data?.user,
|
|
462
|
-
apiKey: data?.apiKey || data?.api_key,
|
|
463
|
-
api_key: data?.api_key || data?.apiKey,
|
|
464
|
-
balance: data?.balance || {
|
|
493
|
+
user: data?.user || userData?.user,
|
|
494
|
+
apiKey: data?.apiKey || data?.api_key || userData?.apiKeyActive,
|
|
495
|
+
api_key: data?.api_key || data?.apiKey || userData?.apiKeyActive,
|
|
496
|
+
balance: walletSummary || data?.balance || {
|
|
465
497
|
used: 0,
|
|
466
498
|
total: 0,
|
|
467
499
|
percentage: 0,
|
|
@@ -480,7 +512,7 @@ export function LBProvider({
|
|
|
480
512
|
} finally {
|
|
481
513
|
setIsLoadingStatus(false);
|
|
482
514
|
}
|
|
483
|
-
}, [lbClient, state.status, storageStatus]);
|
|
515
|
+
}, [fetchWalletSummary, lbClient, state.status, storageStatus]);
|
|
484
516
|
|
|
485
517
|
/**
|
|
486
518
|
* Récupère le storage - LENT avec cache (5 minutes)
|
|
@@ -574,7 +606,6 @@ export function LBProvider({
|
|
|
574
606
|
setHasSelectedApiKeyCookie(true);
|
|
575
607
|
await refreshBasicStatus();
|
|
576
608
|
setTimeout(() => refreshStorageStatus(), 100);
|
|
577
|
-
setTimeout(() => syncSelectedApiKeyCookie(), 100);
|
|
578
609
|
} else {
|
|
579
610
|
throw new Error("No valid authentication method available");
|
|
580
611
|
}
|
|
@@ -587,7 +618,6 @@ export function LBProvider({
|
|
|
587
618
|
lbClient,
|
|
588
619
|
refreshBasicStatus,
|
|
589
620
|
refreshStorageStatus,
|
|
590
|
-
syncSelectedApiKeyCookie,
|
|
591
621
|
]
|
|
592
622
|
);
|
|
593
623
|
|
|
@@ -639,6 +669,12 @@ export function LBProvider({
|
|
|
639
669
|
}
|
|
640
670
|
}, [lbClient, state.status]);
|
|
641
671
|
|
|
672
|
+
useEffect(() => {
|
|
673
|
+
if (state.status !== "ready" || !state.session) return;
|
|
674
|
+
if (hasSelectedApiKeyCookie) return;
|
|
675
|
+
setState((prev) => ({ ...prev, selectedKey: undefined }));
|
|
676
|
+
}, [hasSelectedApiKeyCookie, state.session, state.status]);
|
|
677
|
+
|
|
642
678
|
// Refresh status uniquement lors de la transition vers "ready"
|
|
643
679
|
// (évite les boucles status/user causées par des callbacks recréés)
|
|
644
680
|
useEffect(() => {
|
package/src/styles.css
CHANGED
|
@@ -310,7 +310,7 @@
|
|
|
310
310
|
min-height: 96px;
|
|
311
311
|
resize: none;
|
|
312
312
|
overflow: hidden;
|
|
313
|
-
padding: 6px 2px
|
|
313
|
+
padding: 6px 2px 32px;
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
.ai-control[aria-invalid="true"],
|
|
@@ -641,7 +641,7 @@
|
|
|
641
641
|
|
|
642
642
|
.ai-status-tooltip {
|
|
643
643
|
width: min(370px, calc(100vw - 16px));
|
|
644
|
-
z-index:
|
|
644
|
+
z-index: 2147483646;
|
|
645
645
|
}
|
|
646
646
|
|
|
647
647
|
.ai-status-actions {
|
|
@@ -685,7 +685,7 @@
|
|
|
685
685
|
white-space: nowrap;
|
|
686
686
|
opacity: 0;
|
|
687
687
|
pointer-events: none;
|
|
688
|
-
z-index:
|
|
688
|
+
z-index: 2147483646;
|
|
689
689
|
padding: 4px 8px;
|
|
690
690
|
border-radius: 8px;
|
|
691
691
|
border: 1px solid var(--ai-border);
|
|
@@ -772,7 +772,7 @@
|
|
|
772
772
|
[data-ai-settings-panel] {
|
|
773
773
|
position: fixed;
|
|
774
774
|
inset: 0;
|
|
775
|
-
z-index:
|
|
775
|
+
z-index: 2147483647;
|
|
776
776
|
display: flex;
|
|
777
777
|
align-items: center;
|
|
778
778
|
justify-content: center;
|
|
@@ -1109,6 +1109,8 @@
|
|
|
1109
1109
|
|
|
1110
1110
|
.ai-key-modal-panel {
|
|
1111
1111
|
max-width: 520px;
|
|
1112
|
+
position: relative;
|
|
1113
|
+
z-index: 2147483647;
|
|
1112
1114
|
}
|
|
1113
1115
|
|
|
1114
1116
|
.ai-key-radio {
|