@lastbrain/ai-ui-react 1.0.76 → 1.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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;
@@ -333,7 +347,7 @@ export function AiStatusButton({ status, loading = false, className = "", size =
333
347
  setShowApiKeySelector(false);
334
348
  setShowTooltip(false);
335
349
  if (refetchProviders) {
336
- await refetchProviders();
350
+ void refetchProviders();
337
351
  }
338
352
  }
339
353
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"AiTextarea.d.ts","sourceRoot":"","sources":["../../src/components/AiTextarea.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAc,EAIZ,KAAK,sBAAsB,EAC5B,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAY9D,MAAM,WAAW,eACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,sBAAsB,CAAC,mBAAmB,CAAC,EAAE,SAAS,CAAC;IAC9D,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/C;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAa,EACb,iBAAiB,EACjB,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAQ,EAAE,SAAiB,EAC3B,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,aAAa,EACjB,EAAE,eAAe,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) {
@@ -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,kDAuJvB"}
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"}
@@ -25,6 +25,8 @@ export function LBApiKeySelector({ apiKeys, onSelect, onCancel, isOpen, }) {
25
25
  setError(err instanceof Error
26
26
  ? err.message
27
27
  : t("auth.modal.selectionError", "Selection error"));
28
+ }
29
+ finally {
28
30
  setLoading(false);
29
31
  }
30
32
  };
@@ -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,8 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
134
152
  });
135
153
  setAccessToken(undefined); // Nettoyer l'access token temporaire
136
154
  setApiKeys([]); // Nettoyer les clés API temporaires
155
+ setHasSelectedApiKeyCookie(true);
156
+ setTimeout(() => syncSelectedApiKeyCookie(), 100);
137
157
  onStatusChange?.("ready");
138
158
  onAuthChange?.(); // Refresh provider after signin
139
159
  }
@@ -145,7 +165,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
145
165
  });
146
166
  throw error;
147
167
  }
148
- }, [lbClient, state.user, onStatusChange, onAuthChange]);
168
+ }, [lbClient, onAuthChange, onStatusChange, state.user, syncSelectedApiKeyCookie]);
149
169
  /**
150
170
  * Connexion utilisateur (étape 1 : login)
151
171
  * Retourne le token et les clés API sans créer de session
@@ -359,8 +379,10 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
359
379
  selectedKey,
360
380
  }));
361
381
  }
382
+ setHasSelectedApiKeyCookie(true);
362
383
  await refreshBasicStatus();
363
384
  setTimeout(() => refreshStorageStatus(), 100);
385
+ setTimeout(() => syncSelectedApiKeyCookie(), 100);
364
386
  }
365
387
  else {
366
388
  throw new Error("No valid authentication method available");
@@ -373,6 +395,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
373
395
  lbClient,
374
396
  refreshBasicStatus,
375
397
  refreshStorageStatus,
398
+ syncSelectedApiKeyCookie,
376
399
  ]);
377
400
  /**
378
401
  * Déconnexion
@@ -393,6 +416,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
393
416
  setBasicStatus(null);
394
417
  setStorageStatus(null);
395
418
  setStorageLastFetch(0);
419
+ setHasSelectedApiKeyCookie(false);
396
420
  onStatusChange?.("needs_auth");
397
421
  onAuthChange?.(); // Refresh provider after logout
398
422
  }
@@ -420,16 +444,19 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
420
444
  setApiKeys([]);
421
445
  }
422
446
  }, [lbClient, state.status]);
423
- // Refresh status quand la session devient ready
447
+ // Refresh status uniquement lors de la transition vers "ready"
448
+ // (évite les boucles status/user causées par des callbacks recréés)
424
449
  useEffect(() => {
425
- if (state.status === "ready") {
450
+ const wasReady = previousStatusRef.current === "ready";
451
+ previousStatusRef.current = state.status;
452
+ if (state.status === "ready" && !wasReady) {
426
453
  // Appel rapide d'abord
427
454
  refreshBasicStatus();
428
455
  // Storage en arrière-plan après 100ms
429
456
  setTimeout(() => refreshStorageStatus(), 100);
430
457
  fetchApiKeysWithSession(); // Also fetch API keys list
431
458
  }
432
- else {
459
+ else if (state.status !== "ready") {
433
460
  setApiStatus(null);
434
461
  setBasicStatus(null);
435
462
  setStorageStatus(null);
@@ -460,6 +487,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
460
487
  refreshStorageStatus,
461
488
  isLoadingStatus,
462
489
  isLoadingStorage,
490
+ hasSelectedApiKeyCookie,
463
491
  };
464
492
  return (_jsx(I18nProvider, { lang: lang, children: _jsx(LBContext.Provider, { value: value, children: children }) }));
465
493
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useAiStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiStatus.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB,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.78",
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;
@@ -694,7 +716,7 @@ export function AiStatusButton({
694
716
  setShowApiKeySelector(false);
695
717
  setShowTooltip(false);
696
718
  if (refetchProviders) {
697
- await refetchProviders();
719
+ void refetchProviders();
698
720
  }
699
721
  } catch (error) {
700
722
  console.error("Failed to select API key:", error);
@@ -63,10 +63,16 @@ export function AiTextarea({
63
63
 
64
64
  // Rendre l'authentification optionnelle
65
65
  let lbStatus: string | undefined;
66
+ let lbHasSession = false;
67
+ let lbHasSelectedKey = false;
68
+ let lbHasSelectedApiKeyCookie = false;
66
69
  let hasLBProvider = false;
67
70
  try {
68
71
  const lbContext = useLB();
69
72
  lbStatus = lbContext.status;
73
+ lbHasSession = Boolean(lbContext.session?.sessionToken);
74
+ lbHasSelectedKey = Boolean(lbContext.selectedKey?.id);
75
+ lbHasSelectedApiKeyCookie = lbContext.hasSelectedApiKeyCookie;
70
76
  hasLBProvider = true;
71
77
  } catch {
72
78
  // LBProvider n'est pas disponible, ignorer
@@ -87,9 +93,11 @@ export function AiTextarea({
87
93
 
88
94
  const baseUrl = propBaseUrl ?? ctxBaseUrl;
89
95
  const apiKeyId = propApiKeyId ?? ctxApiKeyId;
90
- const 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 = () => {
@@ -45,6 +45,7 @@ export function LBApiKeySelector({
45
45
  ? err.message
46
46
  : t("auth.modal.selectionError", "Selection error")
47
47
  );
48
+ } finally {
48
49
  setLoading(false);
49
50
  }
50
51
  };
@@ -11,6 +11,7 @@ import {
11
11
  useEffect,
12
12
  useCallback,
13
13
  useMemo,
14
+ useRef,
14
15
  useState,
15
16
  type ReactNode,
16
17
  } from "react";
@@ -114,6 +115,8 @@ interface LBContextValue extends LBAuthState {
114
115
  isLoadingStatus: boolean;
115
116
  /** Indique si le storage est en cours de chargement */
116
117
  isLoadingStorage: boolean;
118
+ /** True si le cookie api_key_selected est présent */
119
+ hasSelectedApiKeyCookie: boolean;
117
120
  }
118
121
 
119
122
  const LBContext = createContext<LBContextValue | undefined>(undefined);
@@ -142,6 +145,17 @@ export function LBProvider({
142
145
  const [isLoadingStatus, setIsLoadingStatus] = useState(false);
143
146
  const [isLoadingStorage, setIsLoadingStorage] = useState(false);
144
147
  const [storageLastFetch, setStorageLastFetch] = useState<number>(0);
148
+ const [hasSelectedApiKeyCookie, setHasSelectedApiKeyCookie] = useState(false);
149
+ const previousStatusRef = useRef<LBAuthState["status"]>("loading");
150
+
151
+ const syncSelectedApiKeyCookie = useCallback(() => {
152
+ if (typeof document === "undefined") return;
153
+ const hasCookie = document.cookie
154
+ .split(";")
155
+ .map((part) => part.trim())
156
+ .some((part) => part.startsWith("api_key_selected=") && part.length > 17);
157
+ setHasSelectedApiKeyCookie(hasCookie);
158
+ }, []);
145
159
 
146
160
  const lbClient = useMemo(
147
161
  () =>
@@ -223,6 +237,13 @@ export function LBProvider({
223
237
  checkSession();
224
238
  }, [checkSession]);
225
239
 
240
+ useEffect(() => {
241
+ syncSelectedApiKeyCookie();
242
+ if (typeof window === "undefined") return;
243
+ const interval = window.setInterval(syncSelectedApiKeyCookie, 1000);
244
+ return () => window.clearInterval(interval);
245
+ }, [syncSelectedApiKeyCookie]);
246
+
226
247
  /**
227
248
  * Récupère les clés API de l'utilisateur
228
249
  */
@@ -272,6 +293,8 @@ export function LBProvider({
272
293
 
273
294
  setAccessToken(undefined); // Nettoyer l'access token temporaire
274
295
  setApiKeys([]); // Nettoyer les clés API temporaires
296
+ setHasSelectedApiKeyCookie(true);
297
+ setTimeout(() => syncSelectedApiKeyCookie(), 100);
275
298
  onStatusChange?.("ready");
276
299
  onAuthChange?.(); // Refresh provider after signin
277
300
  } catch (error) {
@@ -284,7 +307,7 @@ export function LBProvider({
284
307
  throw error;
285
308
  }
286
309
  },
287
- [lbClient, state.user, onStatusChange, onAuthChange]
310
+ [lbClient, onAuthChange, onStatusChange, state.user, syncSelectedApiKeyCookie]
288
311
  );
289
312
 
290
313
  /**
@@ -548,8 +571,10 @@ export function LBProvider({
548
571
  selectedKey,
549
572
  }));
550
573
  }
574
+ setHasSelectedApiKeyCookie(true);
551
575
  await refreshBasicStatus();
552
576
  setTimeout(() => refreshStorageStatus(), 100);
577
+ setTimeout(() => syncSelectedApiKeyCookie(), 100);
553
578
  } else {
554
579
  throw new Error("No valid authentication method available");
555
580
  }
@@ -562,6 +587,7 @@ export function LBProvider({
562
587
  lbClient,
563
588
  refreshBasicStatus,
564
589
  refreshStorageStatus,
590
+ syncSelectedApiKeyCookie,
565
591
  ]
566
592
  );
567
593
 
@@ -582,6 +608,7 @@ export function LBProvider({
582
608
  setBasicStatus(null);
583
609
  setStorageStatus(null);
584
610
  setStorageLastFetch(0);
611
+ setHasSelectedApiKeyCookie(false);
585
612
  onStatusChange?.("needs_auth");
586
613
  onAuthChange?.(); // Refresh provider after logout
587
614
  }
@@ -612,15 +639,19 @@ export function LBProvider({
612
639
  }
613
640
  }, [lbClient, state.status]);
614
641
 
615
- // Refresh status quand la session devient ready
642
+ // Refresh status uniquement lors de la transition vers "ready"
643
+ // (évite les boucles status/user causées par des callbacks recréés)
616
644
  useEffect(() => {
617
- if (state.status === "ready") {
645
+ const wasReady = previousStatusRef.current === "ready";
646
+ previousStatusRef.current = state.status;
647
+
648
+ if (state.status === "ready" && !wasReady) {
618
649
  // Appel rapide d'abord
619
650
  refreshBasicStatus();
620
651
  // Storage en arrière-plan après 100ms
621
652
  setTimeout(() => refreshStorageStatus(), 100);
622
653
  fetchApiKeysWithSession(); // Also fetch API keys list
623
- } else {
654
+ } else if (state.status !== "ready") {
624
655
  setApiStatus(null);
625
656
  setBasicStatus(null);
626
657
  setStorageStatus(null);
@@ -652,6 +683,7 @@ export function LBProvider({
652
683
  refreshStorageStatus,
653
684
  isLoadingStatus,
654
685
  isLoadingStorage,
686
+ hasSelectedApiKeyCookie,
655
687
  };
656
688
 
657
689
  return (
@@ -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 &&