@lastbrain/ai-ui-react 1.0.49 → 1.0.52

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.
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useState, useEffect } from "react";
4
4
  import { useLB } from "../context/LBAuthProvider";
5
5
  import { LBApiKeySelector } from "./LBApiKeySelector";
6
+ import { Mail, Lock, Sparkles, X, Loader2, AlertCircle } from "lucide-react";
6
7
  export function LBSigninModal({ isOpen, onClose }) {
7
8
  // Vérifier si LBProvider est disponible
8
9
  let login;
@@ -90,187 +91,277 @@ export function LBSigninModal({ isOpen, onClose }) {
90
91
  };
91
92
  return (_jsxs("div", { style: {
92
93
  position: "fixed",
93
- inset: 0,
94
- backgroundColor: "rgba(0, 0, 0, 0.6)",
95
- backdropFilter: "blur(8px)",
96
- WebkitBackdropFilter: "blur(8px)",
94
+ top: 0,
95
+ left: 0,
96
+ right: 0,
97
+ bottom: 0,
98
+ width: "100vw",
99
+ height: "100vh",
100
+ backgroundColor: "rgba(0, 0, 0, 0.75)",
101
+ backdropFilter: "blur(12px)",
102
+ WebkitBackdropFilter: "blur(12px)",
97
103
  display: "flex",
98
104
  alignItems: "center",
99
105
  justifyContent: "center",
100
106
  zIndex: 9999,
101
107
  padding: "16px",
108
+ animation: "fadeIn 0.2s ease-out",
109
+ overflow: "auto",
102
110
  }, onClick: onClose, onKeyDown: handleKeyDown, children: [_jsxs("div", { style: {
103
- backgroundColor: "var(--ai-bg-primary, #1f2937)",
104
- borderRadius: "12px",
105
- boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)",
106
- maxWidth: "420px",
111
+ backgroundColor: "light-dark(#ffffff, #1e293b)",
112
+ borderRadius: "20px",
113
+ boxShadow: "0 0 0 1px rgba(139, 92, 246, 0.2), 0 20px 70px rgba(139, 92, 246, 0.25), 0 4px 6px rgba(0, 0, 0, 0.1)",
114
+ maxWidth: "440px",
107
115
  width: "100%",
108
- border: "1px solid var(--ai-border-primary, #374151)",
116
+ border: "1px solid light-dark(rgba(226, 232, 240, 0.8), rgba(139, 92, 246, 0.3))",
109
117
  display: "flex",
110
118
  flexDirection: "column",
111
- maxHeight: "85vh",
119
+ maxHeight: "90vh",
112
120
  overflow: "hidden",
113
- }, onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { style: {
114
- padding: "20px 24px",
115
- borderBottom: "1px solid var(--ai-border-primary, #374151)",
121
+ position: "relative",
122
+ animation: "slideUp 0.3s ease-out",
123
+ }, onClick: (e) => e.stopPropagation(), children: [_jsx("div", { style: {
124
+ position: "absolute",
125
+ top: 0,
126
+ left: 0,
127
+ right: 0,
128
+ height: "200px",
129
+ background: "radial-gradient(ellipse at top, rgba(139, 92, 246, 0.15), transparent 70%)",
130
+ pointerEvents: "none",
131
+ zIndex: 0,
132
+ } }), _jsxs("div", { style: {
133
+ padding: "32px 32px 24px",
116
134
  display: "flex",
117
- justifyContent: "space-between",
135
+ flexDirection: "column",
118
136
  alignItems: "center",
119
- background: "var(--ai-bg-secondary, #111827)",
120
- }, children: [_jsx("h2", { style: {
121
- margin: 0,
122
- fontSize: "18px",
123
- fontWeight: 600,
124
- color: "var(--ai-text-primary, #f9fafb)",
137
+ gap: "16px",
138
+ position: "relative",
139
+ zIndex: 1,
140
+ }, children: [_jsx("div", { style: {
141
+ width: "64px",
142
+ height: "64px",
143
+ borderRadius: "16px",
144
+ background: "linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)",
125
145
  display: "flex",
126
146
  alignItems: "center",
127
- gap: "8px",
128
- }, children: "\uD83D\uDD10 Connexion LastBrain" }), _jsx("button", { onClick: onClose, style: {
147
+ justifyContent: "center",
148
+ boxShadow: "0 8px 24px rgba(139, 92, 246, 0.3)",
149
+ animation: "pulse 2s ease-in-out infinite",
150
+ }, children: _jsx(Sparkles, { size: 32, color: "#ffffff", strokeWidth: 2.5 }) }), _jsxs("div", { style: { textAlign: "center" }, children: [_jsx("h2", { style: {
151
+ margin: "0 0 8px 0",
152
+ fontSize: "24px",
153
+ fontWeight: 700,
154
+ color: "light-dark(#1e293b, #f8fafc)",
155
+ letterSpacing: "-0.02em",
156
+ }, children: "Connexion LastBrain" }), _jsx("p", { style: {
157
+ margin: 0,
158
+ fontSize: "14px",
159
+ color: "light-dark(#64748b, #94a3b8)",
160
+ fontWeight: 400,
161
+ }, children: "Acc\u00E9dez \u00E0 vos outils d'intelligence artificielle" })] }), _jsx("button", { onClick: onClose, style: {
162
+ position: "absolute",
163
+ top: "24px",
164
+ right: "24px",
129
165
  background: "transparent",
130
166
  border: "none",
131
- color: "var(--ai-text-secondary, #9ca3af)",
167
+ color: "light-dark(#64748b, #94a3b8)",
132
168
  cursor: "pointer",
133
- fontSize: "24px",
134
- lineHeight: 1,
135
- padding: "4px 8px",
136
- borderRadius: "6px",
169
+ padding: "8px",
170
+ borderRadius: "10px",
137
171
  transition: "all 0.2s ease",
172
+ display: "flex",
173
+ alignItems: "center",
174
+ justifyContent: "center",
138
175
  }, onMouseEnter: (e) => {
139
176
  e.currentTarget.style.background =
140
- "var(--ai-bg-tertiary, #374151)";
141
- e.currentTarget.style.color = "var(--ai-text-primary, #f9fafb)";
177
+ "light-dark(rgba(100, 116, 139, 0.1), rgba(148, 163, 184, 0.15))";
178
+ e.currentTarget.style.color = "light-dark(#1e293b, #f8fafc)";
142
179
  }, onMouseLeave: (e) => {
143
180
  e.currentTarget.style.background = "transparent";
144
- e.currentTarget.style.color = "var(--ai-text-secondary, #9ca3af)";
145
- }, "aria-label": "Close", children: "\u00D7" })] }), _jsx("div", { style: {
146
- padding: "24px",
181
+ e.currentTarget.style.color = "light-dark(#64748b, #94a3b8)";
182
+ }, "aria-label": "Close", children: _jsx(X, { size: 20, strokeWidth: 2 }) })] }), _jsx("div", { style: {
183
+ padding: "0 32px 32px",
147
184
  overflow: "auto",
148
185
  flex: 1,
149
- }, children: _jsxs("form", { onSubmit: handleSubmit, style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [_jsxs("div", { children: [_jsx("label", { style: {
150
- display: "block",
186
+ position: "relative",
187
+ zIndex: 1,
188
+ }, children: _jsxs("form", { onSubmit: handleSubmit, style: { display: "flex", flexDirection: "column", gap: "20px" }, children: [_jsxs("div", { children: [_jsxs("label", { style: {
189
+ display: "flex",
190
+ alignItems: "center",
191
+ gap: "8px",
151
192
  fontSize: "13px",
152
193
  fontWeight: 600,
153
- color: "var(--ai-text-secondary, #9ca3af)",
154
- marginBottom: "8px",
155
- letterSpacing: "0.02em",
156
- }, children: "\uD83D\uDCE7 Email" }), _jsx("input", { type: "email", value: email, onChange: (e) => setEmail(e.target.value), required: true, autoFocus: true, autoComplete: "email", placeholder: "votre@email.com", style: {
157
- width: "100%",
158
- padding: "10px 12px",
159
- fontSize: "14px",
160
- border: "1px solid var(--ai-border-primary, #374151)",
161
- borderRadius: "8px",
162
- background: "var(--ai-bg-secondary, #111827)",
163
- color: "var(--ai-text-primary, #f9fafb)",
164
- outline: "none",
165
- transition: "all 0.2s ease",
194
+ color: "light-dark(#1e293b, #f8fafc)",
195
+ marginBottom: "10px",
166
196
  letterSpacing: "0.01em",
167
- }, onFocus: (e) => {
168
- e.currentTarget.style.borderColor = "#8b5cf6";
169
- e.currentTarget.style.boxShadow =
170
- "0 0 0 3px rgba(139, 92, 246, 0.1)";
171
- }, onBlur: (e) => {
172
- e.currentTarget.style.borderColor =
173
- "var(--ai-border-primary, #374151)";
174
- e.currentTarget.style.boxShadow = "none";
175
- } })] }), _jsxs("div", { children: [_jsx("label", { style: {
176
- display: "block",
197
+ }, children: [_jsx(Mail, { size: 16, strokeWidth: 2.5 }), "Email"] }), _jsx("div", { style: { position: "relative" }, children: _jsx("input", { type: "email", value: email, onChange: (e) => setEmail(e.target.value), required: true, autoFocus: true, autoComplete: "email", placeholder: "votre@email.com", style: {
198
+ width: "100%",
199
+ padding: "14px 16px",
200
+ fontSize: "15px",
201
+ border: "2px solid light-dark(#e2e8f0, #334155)",
202
+ borderRadius: "12px",
203
+ background: "light-dark(#f8fafc, #0f172a)",
204
+ color: "light-dark(#1e293b, #f8fafc)",
205
+ outline: "none",
206
+ transition: "all 0.2s ease",
207
+ fontWeight: 500,
208
+ }, onFocus: (e) => {
209
+ e.currentTarget.style.borderColor =
210
+ "rgba(139, 92, 246, 0.6)";
211
+ e.currentTarget.style.boxShadow =
212
+ "0 0 0 4px rgba(139, 92, 246, 0.15)";
213
+ e.currentTarget.style.background =
214
+ "light-dark(#ffffff, #1e293b)";
215
+ }, onBlur: (e) => {
216
+ e.currentTarget.style.borderColor =
217
+ "light-dark(#e2e8f0, #334155)";
218
+ e.currentTarget.style.boxShadow = "none";
219
+ e.currentTarget.style.background =
220
+ "light-dark(#f8fafc, #0f172a)";
221
+ } }) })] }), _jsxs("div", { children: [_jsxs("label", { style: {
222
+ display: "flex",
223
+ alignItems: "center",
224
+ gap: "8px",
177
225
  fontSize: "13px",
178
226
  fontWeight: 600,
179
- color: "var(--ai-text-secondary, #9ca3af)",
180
- marginBottom: "8px",
181
- letterSpacing: "0.02em",
182
- }, children: "\uD83D\uDD12 Mot de passe" }), _jsx("input", { type: "password", value: password, onChange: (e) => setPassword(e.target.value), required: true, autoComplete: "current-password", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", style: {
183
- width: "100%",
184
- padding: "10px 12px",
185
- fontSize: "14px",
186
- border: "1px solid var(--ai-border-primary, #374151)",
187
- borderRadius: "8px",
188
- background: "var(--ai-bg-secondary, #111827)",
189
- color: "var(--ai-text-primary, #f9fafb)",
190
- outline: "none",
191
- transition: "all 0.2s ease",
192
- letterSpacing: "0.15em",
193
- }, onFocus: (e) => {
194
- e.currentTarget.style.borderColor = "#8b5cf6";
195
- e.currentTarget.style.boxShadow =
196
- "0 0 0 3px rgba(139, 92, 246, 0.1)";
197
- }, onBlur: (e) => {
198
- e.currentTarget.style.borderColor =
199
- "var(--ai-border-primary, #374151)";
200
- e.currentTarget.style.boxShadow = "none";
201
- } })] }), error && (_jsxs("div", { style: {
202
- padding: "12px",
227
+ color: "light-dark(#1e293b, #f8fafc)",
228
+ marginBottom: "10px",
229
+ letterSpacing: "0.01em",
230
+ }, children: [_jsx(Lock, { size: 16, strokeWidth: 2.5 }), "Mot de passe"] }), _jsx("div", { style: { position: "relative" }, children: _jsx("input", { type: "password", value: password, onChange: (e) => setPassword(e.target.value), required: true, autoComplete: "current-password", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", style: {
231
+ width: "100%",
232
+ padding: "14px 16px",
233
+ fontSize: "15px",
234
+ border: "2px solid light-dark(#e2e8f0, #334155)",
235
+ borderRadius: "12px",
236
+ background: "light-dark(#f8fafc, #0f172a)",
237
+ color: "light-dark(#1e293b, #f8fafc)",
238
+ outline: "none",
239
+ transition: "all 0.2s ease",
240
+ letterSpacing: "0.15em",
241
+ fontWeight: 500,
242
+ }, onFocus: (e) => {
243
+ e.currentTarget.style.borderColor =
244
+ "rgba(139, 92, 246, 0.6)";
245
+ e.currentTarget.style.boxShadow =
246
+ "0 0 0 4px rgba(139, 92, 246, 0.15)";
247
+ e.currentTarget.style.background =
248
+ "light-dark(#ffffff, #1e293b)";
249
+ }, onBlur: (e) => {
250
+ e.currentTarget.style.borderColor =
251
+ "light-dark(#e2e8f0, #334155)";
252
+ e.currentTarget.style.boxShadow = "none";
253
+ e.currentTarget.style.background =
254
+ "light-dark(#f8fafc, #0f172a)";
255
+ } }) })] }), error && (_jsxs("div", { style: {
256
+ padding: "14px 16px",
203
257
  background: "rgba(239, 68, 68, 0.1)",
204
- border: "1px solid rgba(239, 68, 68, 0.3)",
205
- borderRadius: "8px",
206
- color: "#ef4444",
207
- fontSize: "13px",
258
+ border: "2px solid rgba(239, 68, 68, 0.35)",
259
+ borderRadius: "12px",
260
+ color: "light-dark(#dc2626, #fca5a5)",
261
+ fontSize: "14px",
208
262
  display: "flex",
209
263
  alignItems: "start",
210
- gap: "8px",
211
- }, children: [_jsx("span", { style: { fontSize: "16px", flexShrink: 0 }, children: "\u26A0\uFE0F" }), _jsx("span", { style: { flex: 1, lineHeight: "1.5" }, children: error })] })), _jsx("button", { type: "submit", disabled: loading, style: {
264
+ gap: "12px",
265
+ animation: "shake 0.4s ease",
266
+ }, children: [_jsx(AlertCircle, { size: 20, style: { flexShrink: 0, marginTop: "2px" }, strokeWidth: 2.5 }), _jsx("span", { style: { flex: 1, lineHeight: "1.5", fontWeight: 500 }, children: error })] })), _jsx("button", { type: "submit", disabled: loading, style: {
212
267
  width: "100%",
213
- padding: "12px",
214
- fontSize: "14px",
215
- fontWeight: 600,
268
+ padding: "16px",
269
+ fontSize: "15px",
270
+ fontWeight: 700,
216
271
  color: "#ffffff",
217
272
  background: loading
218
- ? "var(--ai-bg-tertiary, #374151)"
273
+ ? "light-dark(#cbd5e1, #475569)"
219
274
  : "linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)",
220
275
  border: "none",
221
- borderRadius: "8px",
276
+ borderRadius: "12px",
222
277
  cursor: loading ? "not-allowed" : "pointer",
223
278
  transition: "all 0.2s ease",
224
279
  display: "flex",
225
280
  alignItems: "center",
226
281
  justifyContent: "center",
227
- gap: "8px",
228
- letterSpacing: "0.02em",
282
+ gap: "10px",
283
+ letterSpacing: "0.01em",
229
284
  opacity: loading ? 0.6 : 1,
285
+ boxShadow: loading
286
+ ? "none"
287
+ : "0 4px 14px rgba(139, 92, 246, 0.4)",
230
288
  }, onMouseEnter: (e) => {
231
289
  if (!loading) {
232
- e.currentTarget.style.transform = "translateY(-1px)";
290
+ e.currentTarget.style.transform = "translateY(-2px)";
233
291
  e.currentTarget.style.boxShadow =
234
- "0 10px 20px rgba(139, 92, 246, 0.3)";
292
+ "0 8px 24px rgba(139, 92, 246, 0.6)";
235
293
  }
236
294
  }, onMouseLeave: (e) => {
237
295
  e.currentTarget.style.transform = "translateY(0)";
238
- e.currentTarget.style.boxShadow = "none";
239
- }, children: loading ? (_jsxs(_Fragment, { children: [_jsxs("svg", { style: {
240
- animation: "spin 1s linear infinite",
241
- width: "16px",
242
- height: "16px",
243
- }, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4", opacity: "0.25" }), _jsx("path", { d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z", fill: "currentColor", opacity: "0.75" })] }), "Connexion en cours..."] })) : (_jsxs(_Fragment, { children: [_jsx("span", { children: "\uD83D\uDE80" }), "Se connecter"] })) }), _jsxs("div", { style: {
296
+ e.currentTarget.style.boxShadow =
297
+ "0 4px 14px rgba(139, 92, 246, 0.4)";
298
+ }, children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 20, strokeWidth: 2.5, style: { animation: "spin 1s linear infinite" } }), "Connexion en cours..."] })) : (_jsxs(_Fragment, { children: [_jsx(Sparkles, { size: 20, strokeWidth: 2.5 }), "Se connecter"] })) }), _jsxs("div", { style: {
244
299
  marginTop: "8px",
245
- padding: "16px",
300
+ padding: "20px",
246
301
  background: "rgba(139, 92, 246, 0.05)",
247
302
  border: "1px solid rgba(139, 92, 246, 0.2)",
248
- borderRadius: "8px",
303
+ borderRadius: "16px",
249
304
  textAlign: "center",
250
305
  }, children: [_jsx("p", { style: {
251
- margin: "0 0 12px 0",
252
- fontSize: "13px",
253
- color: "var(--ai-text-secondary, #9ca3af)",
254
- }, children: "Pas encore de compte ?" }), _jsx("a", { href: "https://lastbrain.io/signup", target: "_blank", rel: "noopener noreferrer", style: {
255
- display: "inline-block",
256
- padding: "8px 16px",
257
- fontSize: "13px",
306
+ margin: "0 0 14px 0",
307
+ fontSize: "14px",
308
+ color: "light-dark(#64748b, #94a3b8)",
309
+ fontWeight: 500,
310
+ }, children: "Pas encore de compte ?" }), _jsxs("a", { href: "https://lastbrain.io/signup", target: "_blank", rel: "noopener noreferrer", style: {
311
+ display: "inline-flex",
312
+ alignItems: "center",
313
+ gap: "8px",
314
+ padding: "10px 20px",
315
+ fontSize: "14px",
258
316
  fontWeight: 600,
259
317
  color: "#8b5cf6",
260
318
  textDecoration: "none",
261
- border: "1px solid rgba(139, 92, 246, 0.3)",
262
- borderRadius: "6px",
319
+ border: "2px solid rgba(139, 92, 246, 0.3)",
320
+ borderRadius: "10px",
263
321
  transition: "all 0.2s ease",
264
322
  }, onMouseEnter: (e) => {
265
323
  e.currentTarget.style.background = "rgba(139, 92, 246, 0.1)";
266
- e.currentTarget.style.borderColor = "#8b5cf6";
324
+ e.currentTarget.style.borderColor = "rgba(139, 92, 246, 0.5)";
325
+ e.currentTarget.style.transform = "translateY(-1px)";
267
326
  }, onMouseLeave: (e) => {
268
327
  e.currentTarget.style.background = "transparent";
269
328
  e.currentTarget.style.borderColor = "rgba(139, 92, 246, 0.3)";
270
- }, children: "\u2728 Cr\u00E9er un compte gratuitement" })] })] }) })] }), _jsx("style", { children: `
329
+ e.currentTarget.style.transform = "translateY(0)";
330
+ }, children: [_jsx(Sparkles, { size: 16, strokeWidth: 2.5 }), "Cr\u00E9er un compte gratuitement"] })] })] }) })] }), _jsx("style", { children: `
331
+ @keyframes fadeIn {
332
+ from { opacity: 0; }
333
+ to { opacity: 1; }
334
+ }
335
+
336
+ @keyframes slideUp {
337
+ from {
338
+ opacity: 0;
339
+ transform: translateY(20px) scale(0.95);
340
+ }
341
+ to {
342
+ opacity: 1;
343
+ transform: translateY(0) scale(1);
344
+ }
345
+ }
346
+
271
347
  @keyframes spin {
272
348
  from { transform: rotate(0deg); }
273
349
  to { transform: rotate(360deg); }
274
350
  }
351
+
352
+ @keyframes pulse {
353
+ 0%, 100% {
354
+ box-shadow: 0 8px 24px rgba(139, 92, 246, 0.3);
355
+ }
356
+ 50% {
357
+ box-shadow: 0 8px 32px rgba(139, 92, 246, 0.5);
358
+ }
359
+ }
360
+
361
+ @keyframes shake {
362
+ 0%, 100% { transform: translateX(0); }
363
+ 25% { transform: translateX(-8px); }
364
+ 75% { transform: translateX(8px); }
365
+ }
275
366
  ` })] }));
276
367
  }
@@ -3,7 +3,7 @@
3
3
  * Gère l'état de connexion, la session et les appels IA
4
4
  */
5
5
  import { type ReactNode } from "react";
6
- import type { LBAuthState, LBApiKey } from "@lastbrain/ai-ui-core";
6
+ import type { LBAuthState, LBApiKey, AiStatus } from "@lastbrain/ai-ui-core";
7
7
  interface LBProviderProps {
8
8
  children: ReactNode;
9
9
  /** URL de l'API LastBrain (ex: https://api.lastbrain.io) */
@@ -36,6 +36,10 @@ interface LBContextValue extends LBAuthState {
36
36
  apiKeys: LBApiKey[];
37
37
  /** Access token temporaire (après login) */
38
38
  accessToken?: string;
39
+ /** Status API (balance, storage, API key info) */
40
+ apiStatus: AiStatus | null;
41
+ /** Fonction pour rafraîchir le status */
42
+ refreshStatus: () => Promise<void>;
39
43
  }
40
44
  export declare function LBProvider({ children, baseUrl: _baseUrl, proxyUrl, onStatusChange, onAuthChange, }: LBProviderProps): import("react/jsx-runtime").JSX.Element;
41
45
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAIT,MAAM,uBAAuB,CAAC;AAE/B,UAAU,eAAe;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IACzD,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,UAAU,cAAe,SAAQ,WAAW;IAC1C,4BAA4B;IAC5B,KAAK,EAAE,CACL,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;KAC7B,CAAC,CAAC;IACH,8BAA8B;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,6CAA6C;IAC7C,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3D,gEAAgE;IAChE,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,mDAAmD;IACnD,qBAAqB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,oCAAoC;IACpC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,2BAA2B;IAC3B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,EAAE,QAA2B,EACpC,QAA2B,EAC3B,cAAc,EACd,YAAY,GACb,EAAE,eAAe,2CA6WjB;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,cAAc,CAMtC"}
1
+ {"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAIR,QAAQ,EACT,MAAM,uBAAuB,CAAC;AAE/B,UAAU,eAAe;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IACzD,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,UAAU,cAAe,SAAQ,WAAW;IAC1C,4BAA4B;IAC5B,KAAK,EAAE,CACL,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;KAC7B,CAAC,CAAC;IACH,8BAA8B;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,6CAA6C;IAC7C,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3D,gEAAgE;IAChE,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,mDAAmD;IACnD,qBAAqB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,oCAAoC;IACpC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,2BAA2B;IAC3B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,yCAAyC;IACzC,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAID,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,EAAE,QAA2B,EACpC,QAA2B,EAC3B,cAAc,EACd,YAAY,GACb,EAAE,eAAe,2CAkbjB;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,cAAc,CAMtC"}
@@ -12,6 +12,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
12
12
  });
13
13
  const [apiKeys, setApiKeys] = useState([]);
14
14
  const [accessToken, setAccessToken] = useState();
15
+ const [apiStatus, setApiStatus] = useState(null);
15
16
  /**
16
17
  * Vérifie si une session existe au chargement
17
18
  */
@@ -290,6 +291,70 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
290
291
  const refreshSession = useCallback(async () => {
291
292
  await checkSession();
292
293
  }, [checkSession]);
294
+ /**
295
+ * Récupère le status API (balance, storage, API key info)
296
+ */
297
+ const refreshStatus = useCallback(async () => {
298
+ if (state.status !== "ready") {
299
+ setApiStatus(null);
300
+ return;
301
+ }
302
+ try {
303
+ const response = await fetch(`${proxyUrl}/auth/status`, {
304
+ credentials: "include",
305
+ });
306
+ if (response.ok) {
307
+ const data = await response.json();
308
+ setApiStatus(data);
309
+ }
310
+ else {
311
+ setApiStatus(null);
312
+ }
313
+ }
314
+ catch (error) {
315
+ console.error("[LBProvider] Failed to fetch status:", error);
316
+ setApiStatus(null);
317
+ }
318
+ }, [proxyUrl, state.status]);
319
+ /**
320
+ * Récupère les clés API avec la session active
321
+ */
322
+ const fetchApiKeysWithSession = useCallback(async () => {
323
+ if (state.status !== "ready") {
324
+ setApiKeys([]);
325
+ return;
326
+ }
327
+ try {
328
+ console.log("[LBProvider] Fetching API keys with session...");
329
+ const response = await fetch(`${proxyUrl}/auth/api-keys`, {
330
+ credentials: "include",
331
+ });
332
+ if (response.ok) {
333
+ const data = await response.json();
334
+ console.log("[LBProvider] API keys received:", data);
335
+ setApiKeys(data.apiKeys || []);
336
+ }
337
+ else {
338
+ console.warn("[LBProvider] Failed to fetch API keys:", response.status);
339
+ setApiKeys([]);
340
+ }
341
+ }
342
+ catch (error) {
343
+ console.error("[LBProvider] Failed to fetch API keys:", error);
344
+ setApiKeys([]);
345
+ }
346
+ }, [proxyUrl, state.status]);
347
+ // Refresh status quand la session devient ready
348
+ useEffect(() => {
349
+ if (state.status === "ready") {
350
+ refreshStatus();
351
+ fetchApiKeysWithSession(); // Also fetch API keys list
352
+ }
353
+ else {
354
+ setApiStatus(null);
355
+ setApiKeys([]);
356
+ }
357
+ }, [state.status, refreshStatus, fetchApiKeysWithSession]);
293
358
  const value = {
294
359
  ...state,
295
360
  login,
@@ -300,6 +365,8 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
300
365
  refreshSession,
301
366
  apiKeys,
302
367
  accessToken,
368
+ apiStatus,
369
+ refreshStatus,
303
370
  };
304
371
  return _jsx(LBContext.Provider, { value: value, children: children });
305
372
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/ai-ui-react",
3
- "version": "1.0.49",
3
+ "version": "1.0.52",
4
4
  "description": "Headless React components for LastBrain AI UI Kit",
5
5
  "private": false,
6
6
  "type": "module",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "lucide-react": "^0.257.0",
51
- "@lastbrain/ai-ui-core": "1.0.39"
51
+ "@lastbrain/ai-ui-core": "1.0.41"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/react": "^19.2.0",
@@ -1,12 +1,14 @@
1
1
  "use client";
2
2
 
3
3
  import React, { useState, useRef, KeyboardEvent } from "react";
4
- import { X, Sparkles } from "lucide-react";
4
+ import { X, Sparkles, Lock } from "lucide-react";
5
5
  import { aiStyles } from "../styles/inline";
6
6
  import { AiPromptPanel } from "./AiPromptPanel";
7
+ import { LBSigninModal } from "./LBSigninModal";
7
8
  import { useAiCallText } from "../hooks/useAiCallText";
8
9
  import { useAiModels } from "../hooks/useAiModels";
9
10
  import { useAiContext } from "../context/AiProvider";
11
+ import { useLB } from "../context/LBAuthProvider";
10
12
  import { handleAIError } from "../utils/errorHandler";
11
13
 
12
14
  export interface AiChipLabelProps {
@@ -85,6 +87,7 @@ export function AiChipInput({
85
87
  }: AiChipInputProps) {
86
88
  const [inputValue, setInputValue] = useState("");
87
89
  const [showPromptPanel, setShowPromptPanel] = useState(false);
90
+ const [showSigninModal, setShowSigninModal] = useState(false);
88
91
  const inputRef = useRef<HTMLInputElement>(null);
89
92
 
90
93
  // Récupérer le contexte AiProvider avec fallback sur les props
@@ -92,6 +95,16 @@ export function AiChipInput({
92
95
  const baseUrl = propBaseUrl ?? aiContext.baseUrl;
93
96
  const apiKeyId = propApiKeyId ?? aiContext.apiKeyId;
94
97
 
98
+ // Vérifier si l'utilisateur est connecté (session LB)
99
+ let isAuthenticated = false;
100
+ try {
101
+ const lbContext = useLB();
102
+ isAuthenticated = lbContext.status === "ready";
103
+ } catch {
104
+ // LBProvider n'est pas disponible, considérer non connecté
105
+ isAuthenticated = false;
106
+ }
107
+
95
108
  // Hooks pour l'IA avec les valeurs du contexte
96
109
  const { models } = useAiModels({
97
110
  baseUrl,
@@ -205,7 +218,13 @@ Exemple de réponse attendue: javascript, react, frontend, api, development`;
205
218
  }}
206
219
  />
207
220
  <button
208
- onClick={handleGenerateChips}
221
+ onClick={() => {
222
+ if (isAuthenticated) {
223
+ handleGenerateChips();
224
+ } else {
225
+ setShowSigninModal(true);
226
+ }
227
+ }}
209
228
  style={{
210
229
  position: "absolute",
211
230
  right: "8px",
@@ -218,11 +237,15 @@ Exemple de réponse attendue: javascript, react, frontend, api, development`;
218
237
  borderRadius: "4px",
219
238
  display: "flex",
220
239
  alignItems: "center",
221
- color: "#6366f1",
240
+ color: isAuthenticated ? "#6366f1" : "#ef4444",
222
241
  }}
223
- title="Générer des tags avec l'IA"
242
+ title={
243
+ isAuthenticated
244
+ ? "Générer des tags avec l'IA"
245
+ : "Se connecter pour utiliser l'IA"
246
+ }
224
247
  >
225
- <Sparkles size={16} />
248
+ {isAuthenticated ? <Sparkles size={16} /> : <Lock size={16} />}
226
249
  </button>
227
250
  </div>
228
251
 
@@ -274,6 +297,12 @@ Exemple de réponse attendue: javascript, react, frontend, api, development`;
274
297
  baseUrl={baseUrl}
275
298
  sourceText={context ? `Contexte: ${context}` : undefined}
276
299
  />
300
+
301
+ {/* Modal signin pour les utilisateurs non connectés */}
302
+ <LBSigninModal
303
+ isOpen={showSigninModal}
304
+ onClose={() => setShowSigninModal(false)}
305
+ />
277
306
  </div>
278
307
  );
279
308
  }