@lastbrain/ai-ui-react 1.0.76 → 1.0.77

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