@lastbrain/ai-ui-react 1.0.77 → 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 +3 -6
- 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 +14 -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 +50 -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 +3 -6
- package/src/components/AiTextarea.tsx +1 -3
- package/src/components/LBApiKeySelector.tsx +15 -2
- package/src/components/UsageToast.tsx +1 -1
- package/src/context/LBAuthProvider.tsx +66 -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) {
|
|
@@ -347,7 +344,7 @@ export function AiStatusButton({ status, loading = false, className = "", size =
|
|
|
347
344
|
setShowApiKeySelector(false);
|
|
348
345
|
setShowTooltip(false);
|
|
349
346
|
if (refetchProviders) {
|
|
350
|
-
|
|
347
|
+
void refetchProviders();
|
|
351
348
|
}
|
|
352
349
|
}
|
|
353
350
|
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,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) => {
|
|
@@ -25,10 +31,12 @@ export function LBApiKeySelector({ apiKeys, onSelect, onCancel, isOpen, }) {
|
|
|
25
31
|
setError(err instanceof Error
|
|
26
32
|
? err.message
|
|
27
33
|
: t("auth.modal.selectionError", "Selection error"));
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
28
36
|
setLoading(false);
|
|
29
37
|
}
|
|
30
38
|
};
|
|
31
|
-
|
|
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) => {
|
|
32
40
|
const isSelected = key.id === selectedKeyId;
|
|
33
41
|
const isActive = key.isActive;
|
|
34
42
|
const rawEnv = key.env;
|
|
@@ -36,4 +44,8 @@ export function LBApiKeySelector({ apiKeys, onSelect, onCancel, isOpen, }) {
|
|
|
36
44
|
const isDev = rawEnv === "dev";
|
|
37
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));
|
|
38
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);
|
|
39
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":"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"}
|
|
@@ -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
|
*/
|
|
@@ -152,7 +165,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
152
165
|
});
|
|
153
166
|
setAccessToken(undefined); // Nettoyer l'access token temporaire
|
|
154
167
|
setApiKeys([]); // Nettoyer les clés API temporaires
|
|
155
|
-
|
|
168
|
+
setHasSelectedApiKeyCookie(true);
|
|
156
169
|
onStatusChange?.("ready");
|
|
157
170
|
onAuthChange?.(); // Refresh provider after signin
|
|
158
171
|
}
|
|
@@ -164,7 +177,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
164
177
|
});
|
|
165
178
|
throw error;
|
|
166
179
|
}
|
|
167
|
-
}, [lbClient, onAuthChange, onStatusChange, state.user
|
|
180
|
+
}, [lbClient, onAuthChange, onStatusChange, state.user]);
|
|
168
181
|
/**
|
|
169
182
|
* Connexion utilisateur (étape 1 : login)
|
|
170
183
|
* Retourne le token et les clés API sans créer de session
|
|
@@ -272,16 +285,20 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
272
285
|
used: 0,
|
|
273
286
|
total: userData.balance?.sellValueUsd || 0,
|
|
274
287
|
percentage: 0,
|
|
288
|
+
remaining: userData.balance?.sellValueUsd || 0,
|
|
275
289
|
},
|
|
276
290
|
};
|
|
277
291
|
}
|
|
292
|
+
const userData = await lbClient.getUser().catch(() => null);
|
|
293
|
+
setHasSelectedApiKeyCookie(hasServerSelectedApiKeyCookie(userData));
|
|
294
|
+
const walletSummary = await fetchWalletSummary().catch(() => null);
|
|
278
295
|
const normalizedStatus = {
|
|
279
296
|
...data,
|
|
280
297
|
authType: data?.authType,
|
|
281
|
-
user: data?.user,
|
|
282
|
-
apiKey: data?.apiKey || data?.api_key,
|
|
283
|
-
api_key: data?.api_key || data?.apiKey,
|
|
284
|
-
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 || {
|
|
285
302
|
used: 0,
|
|
286
303
|
total: 0,
|
|
287
304
|
percentage: 0,
|
|
@@ -301,7 +318,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
301
318
|
finally {
|
|
302
319
|
setIsLoadingStatus(false);
|
|
303
320
|
}
|
|
304
|
-
}, [lbClient, state.status, storageStatus]);
|
|
321
|
+
}, [fetchWalletSummary, lbClient, state.status, storageStatus]);
|
|
305
322
|
/**
|
|
306
323
|
* Récupère le storage - LENT avec cache (5 minutes)
|
|
307
324
|
*/
|
|
@@ -378,9 +395,9 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
378
395
|
selectedKey,
|
|
379
396
|
}));
|
|
380
397
|
}
|
|
398
|
+
setHasSelectedApiKeyCookie(true);
|
|
381
399
|
await refreshBasicStatus();
|
|
382
400
|
setTimeout(() => refreshStorageStatus(), 100);
|
|
383
|
-
setTimeout(() => syncSelectedApiKeyCookie(), 100);
|
|
384
401
|
}
|
|
385
402
|
else {
|
|
386
403
|
throw new Error("No valid authentication method available");
|
|
@@ -393,7 +410,6 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
393
410
|
lbClient,
|
|
394
411
|
refreshBasicStatus,
|
|
395
412
|
refreshStorageStatus,
|
|
396
|
-
syncSelectedApiKeyCookie,
|
|
397
413
|
]);
|
|
398
414
|
/**
|
|
399
415
|
* Déconnexion
|
|
@@ -442,6 +458,13 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
|
|
|
442
458
|
setApiKeys([]);
|
|
443
459
|
}
|
|
444
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]);
|
|
445
468
|
// Refresh status uniquement lors de la transition vers "ready"
|
|
446
469
|
// (évite les boucles status/user causées par des callbacks recréés)
|
|
447
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}
|
|
@@ -716,7 +713,7 @@ export function AiStatusButton({
|
|
|
716
713
|
setShowApiKeySelector(false);
|
|
717
714
|
setShowTooltip(false);
|
|
718
715
|
if (refetchProviders) {
|
|
719
|
-
|
|
716
|
+
void refetchProviders();
|
|
720
717
|
}
|
|
721
718
|
} catch (error) {
|
|
722
719
|
console.error("Failed to select API key:", error);
|
|
@@ -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) => {
|
|
@@ -45,11 +52,12 @@ export function LBApiKeySelector({
|
|
|
45
52
|
? err.message
|
|
46
53
|
: t("auth.modal.selectionError", "Selection error")
|
|
47
54
|
);
|
|
55
|
+
} finally {
|
|
48
56
|
setLoading(false);
|
|
49
57
|
}
|
|
50
58
|
};
|
|
51
59
|
|
|
52
|
-
|
|
60
|
+
const modal = (
|
|
53
61
|
<div className="ai-signin-overlay" onClick={onCancel}>
|
|
54
62
|
<div
|
|
55
63
|
className="ai-signin-panel ai-key-modal-panel"
|
|
@@ -168,4 +176,9 @@ export function LBApiKeySelector({
|
|
|
168
176
|
</div>
|
|
169
177
|
</div>
|
|
170
178
|
);
|
|
179
|
+
|
|
180
|
+
if (!canPortal) {
|
|
181
|
+
return modal;
|
|
182
|
+
}
|
|
183
|
+
return createPortal(modal, document.body);
|
|
171
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
|
|
@@ -293,7 +322,7 @@ export function LBProvider({
|
|
|
293
322
|
|
|
294
323
|
setAccessToken(undefined); // Nettoyer l'access token temporaire
|
|
295
324
|
setApiKeys([]); // Nettoyer les clés API temporaires
|
|
296
|
-
|
|
325
|
+
setHasSelectedApiKeyCookie(true);
|
|
297
326
|
onStatusChange?.("ready");
|
|
298
327
|
onAuthChange?.(); // Refresh provider after signin
|
|
299
328
|
} catch (error) {
|
|
@@ -306,7 +335,7 @@ export function LBProvider({
|
|
|
306
335
|
throw error;
|
|
307
336
|
}
|
|
308
337
|
},
|
|
309
|
-
[lbClient, onAuthChange, onStatusChange, state.user
|
|
338
|
+
[lbClient, onAuthChange, onStatusChange, state.user]
|
|
310
339
|
);
|
|
311
340
|
|
|
312
341
|
/**
|
|
@@ -451,16 +480,20 @@ export function LBProvider({
|
|
|
451
480
|
used: 0,
|
|
452
481
|
total: userData.balance?.sellValueUsd || 0,
|
|
453
482
|
percentage: 0,
|
|
483
|
+
remaining: userData.balance?.sellValueUsd || 0,
|
|
454
484
|
},
|
|
455
485
|
};
|
|
456
486
|
}
|
|
487
|
+
const userData = await lbClient.getUser().catch(() => null);
|
|
488
|
+
setHasSelectedApiKeyCookie(hasServerSelectedApiKeyCookie(userData));
|
|
489
|
+
const walletSummary = await fetchWalletSummary().catch(() => null);
|
|
457
490
|
const normalizedStatus = {
|
|
458
491
|
...data,
|
|
459
492
|
authType: data?.authType,
|
|
460
|
-
user: data?.user,
|
|
461
|
-
apiKey: data?.apiKey || data?.api_key,
|
|
462
|
-
api_key: data?.api_key || data?.apiKey,
|
|
463
|
-
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 || {
|
|
464
497
|
used: 0,
|
|
465
498
|
total: 0,
|
|
466
499
|
percentage: 0,
|
|
@@ -479,7 +512,7 @@ export function LBProvider({
|
|
|
479
512
|
} finally {
|
|
480
513
|
setIsLoadingStatus(false);
|
|
481
514
|
}
|
|
482
|
-
}, [lbClient, state.status, storageStatus]);
|
|
515
|
+
}, [fetchWalletSummary, lbClient, state.status, storageStatus]);
|
|
483
516
|
|
|
484
517
|
/**
|
|
485
518
|
* Récupère le storage - LENT avec cache (5 minutes)
|
|
@@ -570,9 +603,9 @@ export function LBProvider({
|
|
|
570
603
|
selectedKey,
|
|
571
604
|
}));
|
|
572
605
|
}
|
|
606
|
+
setHasSelectedApiKeyCookie(true);
|
|
573
607
|
await refreshBasicStatus();
|
|
574
608
|
setTimeout(() => refreshStorageStatus(), 100);
|
|
575
|
-
setTimeout(() => syncSelectedApiKeyCookie(), 100);
|
|
576
609
|
} else {
|
|
577
610
|
throw new Error("No valid authentication method available");
|
|
578
611
|
}
|
|
@@ -585,7 +618,6 @@ export function LBProvider({
|
|
|
585
618
|
lbClient,
|
|
586
619
|
refreshBasicStatus,
|
|
587
620
|
refreshStorageStatus,
|
|
588
|
-
syncSelectedApiKeyCookie,
|
|
589
621
|
]
|
|
590
622
|
);
|
|
591
623
|
|
|
@@ -637,6 +669,12 @@ export function LBProvider({
|
|
|
637
669
|
}
|
|
638
670
|
}, [lbClient, state.status]);
|
|
639
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
|
+
|
|
640
678
|
// Refresh status uniquement lors de la transition vers "ready"
|
|
641
679
|
// (évite les boucles status/user causées par des callbacks recréés)
|
|
642
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 {
|