@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.
@@ -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,2CA+TtB"}
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
- (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
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,2CAsSpB"}
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
- (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
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,2CAgPd"}
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
- (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
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,2CAkKf"}
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
- (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
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,2CA6jBrB"}
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 && apiKeys.length > 0 ? (_jsx(LBApiKeySelector, { isOpen: showApiKeySelector, apiKeys: apiKeys, onSelect: async (keyId) => {
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,2CAqQjB"}
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
- (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
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":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,MAAM,GACP,EAAE,qBAAqB,kDAwJvB"}
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
- return (_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) => {
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: "6px",
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":"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"}
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
- useEffect(() => {
112
- syncSelectedApiKeyCookie();
113
- if (typeof window === "undefined")
114
- return;
115
- const interval = window.setInterval(syncSelectedApiKeyCookie, 1000);
116
- return () => window.clearInterval(interval);
117
- }, [syncSelectedApiKeyCookie]);
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, syncSelectedApiKeyCookie]);
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 8px;
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: 2147483647;
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: 2147483647;
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: 2147483645;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/ai-ui-react",
3
- "version": "1.0.78",
3
+ "version": "1.0.79",
4
4
  "description": "Headless React components for LastBrain AI UI Kit",
5
5
  "private": false,
6
6
  "type": "module",
@@ -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
- (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
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
- (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
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
- (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
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
- (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
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 && apiKeys.length > 0 ? (
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
- (!lbHasSelectedKey || !lbHasSelectedApiKeyCookie);
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
- return (
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: "6px",
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] = useState(false);
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
- useEffect(() => {
241
- syncSelectedApiKeyCookie();
242
- if (typeof window === "undefined") return;
243
- const interval = window.setInterval(syncSelectedApiKeyCookie, 1000);
244
- return () => window.clearInterval(interval);
245
- }, [syncSelectedApiKeyCookie]);
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, syncSelectedApiKeyCookie]
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 8px;
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: 2147483647;
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: 2147483647;
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: 2147483645;
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 {