@lastbrain/ai-ui-react 1.0.68 → 1.0.69

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.
Files changed (60) hide show
  1. package/dist/components/AiChipLabel.d.ts +8 -3
  2. package/dist/components/AiChipLabel.d.ts.map +1 -1
  3. package/dist/components/AiChipLabel.js +21 -70
  4. package/dist/components/AiContextButton.d.ts +5 -1
  5. package/dist/components/AiContextButton.d.ts.map +1 -1
  6. package/dist/components/AiContextButton.js +67 -291
  7. package/dist/components/AiImageButton.d.ts +5 -1
  8. package/dist/components/AiImageButton.d.ts.map +1 -1
  9. package/dist/components/AiImageButton.js +6 -142
  10. package/dist/components/AiInput.d.ts +5 -3
  11. package/dist/components/AiInput.d.ts.map +1 -1
  12. package/dist/components/AiInput.js +13 -25
  13. package/dist/components/AiPromptPanel.d.ts.map +1 -1
  14. package/dist/components/AiPromptPanel.js +58 -212
  15. package/dist/components/AiSelect.d.ts +5 -3
  16. package/dist/components/AiSelect.d.ts.map +1 -1
  17. package/dist/components/AiSelect.js +21 -30
  18. package/dist/components/AiStatusButton.d.ts +4 -1
  19. package/dist/components/AiStatusButton.d.ts.map +1 -1
  20. package/dist/components/AiStatusButton.js +198 -668
  21. package/dist/components/AiTextarea.d.ts +4 -2
  22. package/dist/components/AiTextarea.d.ts.map +1 -1
  23. package/dist/components/AiTextarea.js +14 -26
  24. package/dist/components/LBApiKeySelector.d.ts.map +1 -1
  25. package/dist/components/LBApiKeySelector.js +5 -166
  26. package/dist/components/LBConnectButton.d.ts +4 -7
  27. package/dist/components/LBConnectButton.d.ts.map +1 -1
  28. package/dist/components/LBConnectButton.js +17 -86
  29. package/dist/components/LBSigninModal.d.ts +1 -1
  30. package/dist/components/LBSigninModal.d.ts.map +1 -1
  31. package/dist/components/LBSigninModal.js +42 -320
  32. package/dist/examples/AiUiPremiumShowcase.d.ts +2 -0
  33. package/dist/examples/AiUiPremiumShowcase.d.ts.map +1 -0
  34. package/dist/examples/AiUiPremiumShowcase.js +15 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +2 -0
  38. package/dist/styles/inline.d.ts +1 -0
  39. package/dist/styles/inline.d.ts.map +1 -1
  40. package/dist/styles/inline.js +25 -129
  41. package/dist/styles.css +1268 -369
  42. package/dist/types.d.ts +3 -0
  43. package/dist/types.d.ts.map +1 -1
  44. package/package.json +2 -2
  45. package/src/components/AiChipLabel.tsx +64 -101
  46. package/src/components/AiContextButton.tsx +138 -430
  47. package/src/components/AiImageButton.tsx +29 -190
  48. package/src/components/AiInput.tsx +49 -74
  49. package/src/components/AiPromptPanel.tsx +71 -254
  50. package/src/components/AiSelect.tsx +61 -69
  51. package/src/components/AiStatusButton.tsx +477 -1313
  52. package/src/components/AiTextarea.tsx +49 -64
  53. package/src/components/LBApiKeySelector.tsx +86 -274
  54. package/src/components/LBConnectButton.tsx +46 -334
  55. package/src/components/LBSigninModal.tsx +140 -481
  56. package/src/examples/AiUiPremiumShowcase.tsx +91 -0
  57. package/src/index.ts +3 -0
  58. package/src/styles/inline.ts +27 -148
  59. package/src/styles.css +1268 -369
  60. package/src/types.ts +3 -0
@@ -1,21 +1,72 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useRef, useLayoutEffect } from "react";
3
+ import { useLayoutEffect, useMemo, useRef, useState } from "react";
4
4
  import { createPortal } from "react-dom";
5
- import { BarChart3, Settings, FileText, History as HistoryIcon, FolderPlus, Power, LogOut, ArrowRightLeft, } from "lucide-react";
6
- import { aiStyles, calculateTooltipPosition } from "../styles/inline";
5
+ import { ArrowRightLeft, BarChart3, FileText, Folder, History, Loader2, LogOut, Settings, Shield, } from "lucide-react";
7
6
  import { useLB } from "../context/LBAuthProvider";
8
7
  import { useAiContext } from "../context/AiProvider";
9
- import { LBSigninModal } from "./LBSigninModal";
10
8
  import { LBApiKeySelector } from "./LBApiKeySelector";
11
- export function AiStatusButton({ status, loading = false, className = "", }) {
12
- // Rendre l'authentification optionnelle
9
+ import { LBSigninModal } from "./LBSigninModal";
10
+ const QUICK_LINKS = [
11
+ {
12
+ href: "https://prompt.lastbrain.io/auth/ai/tokens",
13
+ title: "Dashboard",
14
+ icon: BarChart3,
15
+ },
16
+ {
17
+ href: "https://prompt.lastbrain.io/auth/ai/history",
18
+ title: "Historique",
19
+ icon: History,
20
+ },
21
+ {
22
+ href: "https://prompt.lastbrain.io/auth/ai/settings",
23
+ title: "Settings",
24
+ icon: Settings,
25
+ },
26
+ {
27
+ href: "https://prompt.lastbrain.io/auth/ai/prompts",
28
+ title: "Prompts",
29
+ icon: FileText,
30
+ },
31
+ {
32
+ href: "https://prompt.lastbrain.io/auth/folder",
33
+ title: "Dossiers",
34
+ icon: Folder,
35
+ },
36
+ ];
37
+ const clamp = (value) => Math.min(100, Math.max(0, value || 0));
38
+ const num = (value) => (typeof value === "number" ? value : 0);
39
+ const fixed = (value, digits) => num(value).toFixed(digits);
40
+ function formatStorage(mb) {
41
+ const value = num(mb);
42
+ if (value >= 1024) {
43
+ return `${(value / 1024).toFixed(2)} GB`;
44
+ }
45
+ return `${value.toFixed(2)} MB`;
46
+ }
47
+ function UsageCircle({ percentage }) {
48
+ const safe = clamp(percentage);
49
+ const toneClass = safe > 90
50
+ ? "ai-usage-circle-value--high"
51
+ : safe > 75
52
+ ? "ai-usage-circle-value--warn"
53
+ : "ai-usage-circle-value--low";
54
+ const size = 28;
55
+ const stroke = 3;
56
+ const radius = (size - stroke) / 2;
57
+ const circumference = 2 * Math.PI * radius;
58
+ const dashOffset = circumference - (safe / 100) * circumference;
59
+ return (_jsxs("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, children: [_jsx("circle", { cx: size / 2, cy: size / 2, r: radius, className: "ai-usage-circle-track", strokeWidth: stroke, fill: "transparent" }), _jsx("circle", { cx: size / 2, cy: size / 2, r: radius, className: toneClass, strokeWidth: stroke, fill: "transparent", strokeDasharray: circumference, strokeDashoffset: dashOffset, strokeLinecap: "round", transform: `rotate(-90 ${size / 2} ${size / 2})` }), _jsxs("text", { x: size / 2, y: size / 2, textAnchor: "middle", dominantBaseline: "central", fontSize: "7px", className: toneClass === "ai-usage-circle-value--high"
60
+ ? "ai-usage-circle-text--high"
61
+ : toneClass === "ai-usage-circle-value--warn"
62
+ ? "ai-usage-circle-text--warn"
63
+ : "ai-usage-circle-text--low", fontWeight: "700", children: [safe.toFixed(0), "%"] })] }));
64
+ }
65
+ export function AiStatusButton({ status, loading = false, className = "", size = "md", radius = "full", }) {
13
66
  let lbStatus;
14
67
  let user;
15
68
  let logout;
16
69
  let apiKeys = [];
17
- let accessToken;
18
- let selectApiKeyWithToken;
19
70
  let switchApiKey;
20
71
  let lbApiStatus = null;
21
72
  let lbBasicStatus = null;
@@ -31,8 +82,6 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
31
82
  user = lbContext.user;
32
83
  logout = lbContext.logout;
33
84
  apiKeys = lbContext.apiKeys || [];
34
- accessToken = lbContext.accessToken;
35
- selectApiKeyWithToken = lbContext.selectApiKeyWithToken;
36
85
  switchApiKey = lbContext.switchApiKey;
37
86
  lbApiStatus = lbContext.apiStatus;
38
87
  lbBasicStatus = lbContext.basicStatus;
@@ -44,180 +93,76 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
44
93
  lbRefreshStorageStatus = lbContext.refreshStorageStatus;
45
94
  }
46
95
  catch {
47
- // LBProvider n'est pas disponible, ignorer
48
96
  lbStatus = undefined;
49
- user = undefined;
50
- logout = undefined;
51
97
  }
52
- // Toujours prioriser les données du contexte LB quand disponibles
53
- // pour éviter d'afficher un status externe obsolète (Unknown/0).
54
- const lbEffectiveStatus = lbStatus === "ready"
55
- ? {
56
- ...(lbApiStatus || {}),
57
- ...(lbBasicStatus || {}),
58
- storage: lbStorageStatus?.storage || lbApiStatus?.storage,
59
- }
60
- : null;
61
- const effectiveStatus = lbEffectiveStatus || status || null;
62
- // Récupérer refetchProviders depuis AiProvider si disponible
63
98
  let refetchProviders;
64
99
  try {
65
- const aiContext = useAiContext();
66
- refetchProviders = aiContext.refetchProviders;
100
+ refetchProviders = useAiContext().refetchProviders;
67
101
  }
68
102
  catch {
69
- // AiProvider n'est pas disponible, ignorer
70
103
  refetchProviders = undefined;
71
104
  }
72
105
  const [showSigninModal, setShowSigninModal] = useState(false);
73
106
  const [showApiKeySelector, setShowApiKeySelector] = useState(false);
74
- const [isSelectingApiKey, setIsSelectingApiKey] = useState(false);
75
- const [isLoadingStatus, setIsLoadingStatus] = useState(false);
76
- const formatFixed = (value, digits) => typeof value === "number" ? value.toFixed(digits) : "0.00";
77
- const formatStorage = (valueMb) => {
78
- const mb = typeof valueMb === "number" ? valueMb : 0;
79
- if (mb >= 1024) {
80
- const gb = mb / 1024;
81
- return `${gb.toFixed(2)} GB`;
82
- }
83
- return `${mb.toFixed(2)} MB`;
84
- };
85
- const safeNumber = (value) => typeof value === "number" ? value : 0;
86
- const clampPercentage = (value) => Math.min(100, Math.max(0, safeNumber(value)));
87
- const getUsageColor = (percentage) => {
88
- if (percentage > 90) {
89
- return aiStyles.tooltipValueWarning.color;
90
- }
91
- if (percentage > 75) {
92
- return aiStyles.tooltipValueWarning.color;
93
- }
94
- return aiStyles.tooltipValueSuccess.color;
95
- };
96
- const renderUsageCircle = (percentageValue) => {
97
- const percentage = clampPercentage(percentageValue);
98
- const size = 28;
99
- const stroke = 3;
100
- const radius = (size - stroke) / 2;
101
- const circumference = 2 * Math.PI * radius;
102
- const offset = circumference - (percentage / 100) * circumference;
103
- const color = getUsageColor(percentage);
104
- return (_jsxs("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, children: [_jsx("circle", { cx: size / 2, cy: size / 2, r: radius, stroke: aiStyles.tooltipLabel.color, strokeWidth: stroke, fill: "transparent", opacity: 0.3 }), _jsx("circle", { cx: size / 2, cy: size / 2, r: radius, stroke: color, strokeWidth: stroke, fill: "transparent", strokeDasharray: circumference, strokeDashoffset: offset, strokeLinecap: "round", transform: `rotate(-90 ${size / 2} ${size / 2})` }), _jsxs("text", { x: size / 2, y: size / 2, textAnchor: "middle", dominantBaseline: "central", fontSize: "7px", fill: color, fontWeight: "600", children: [percentage.toFixed(0), "%"] })] }));
105
- };
106
- const balanceUsage = effectiveStatus?.balance;
107
- const storageUsage = effectiveStatus?.storage;
108
- const balanceUsed = safeNumber(balanceUsage?.used);
109
- const balanceRemaining = safeNumber(balanceUsage?.remaining);
110
- const rawBalanceTotal = balanceUsage?.total ??
111
- safeNumber(balanceUsage?.purchased) + safeNumber(balanceUsage?.quota);
112
- const balanceTotal = rawBalanceTotal > 0 ? rawBalanceTotal : balanceUsed + balanceRemaining;
113
- const balancePercentage = balanceUsage?.percentage ??
114
- (balanceTotal > 0 ? Math.round((balanceUsed / balanceTotal) * 100) : 0);
115
- const storageAllocated = storageUsage?.allocated_mb ?? storageUsage?.total_mb ?? 0;
116
- const storageUsed = storageUsage?.used_mb ?? storageUsage?.total_mb ?? 0;
117
- const storagePercentage = storageUsage?.percentage ??
118
- (storageAllocated > 0
119
- ? Math.round((storageUsed / storageAllocated) * 100)
120
- : 0);
121
- const showFastStatusSkeleton = lbStatus === "ready" &&
122
- (lbIsLoadingStatus || isLoadingStatus) &&
123
- !lbBasicStatus &&
124
- !effectiveStatus;
125
- const showCornerLoadingIndicator = lbStatus === "ready" &&
126
- (showFastStatusSkeleton || lbIsLoadingStatus || lbIsLoadingStorage);
127
107
  const [showTooltip, setShowTooltip] = useState(false);
128
- const [isHovered, setIsHovered] = useState(false);
129
- const [tooltipPosition, setTooltipPosition] = useState({});
108
+ const [isSelectingApiKey, setIsSelectingApiKey] = useState(false);
130
109
  const buttonRef = useRef(null);
131
110
  const tooltipRef = useRef(null);
132
111
  const canPortal = typeof document !== "undefined";
133
- const hasApiKeySelected = Boolean(effectiveStatus?.apiKey?.id || effectiveStatus?.api_key?.id || lbSelectedKey?.id);
134
- const isApiKeyAuthMode = effectiveStatus?.authType === "api_key";
112
+ const effectiveStatus = lbStatus === "ready"
113
+ ? {
114
+ ...(lbApiStatus || {}),
115
+ ...(lbBasicStatus || {}),
116
+ storage: lbStorageStatus?.storage || lbApiStatus?.storage,
117
+ }
118
+ : status || null;
119
+ const hasApiKeySelected = Boolean(effectiveStatus?.apiKey?.id ||
120
+ effectiveStatus?.api_key?.id ||
121
+ lbSelectedKey?.id);
135
122
  const requiresApiKeySelection = lbStatus === "ready" && !hasApiKeySelected && apiKeys.length > 0;
123
+ const isApiKeyAuthMode = effectiveStatus?.authType === "api_key";
124
+ const [tooltipStyle, setTooltipStyle] = useState({});
136
125
  useLayoutEffect(() => {
137
- if (!showTooltip || !buttonRef.current) {
126
+ if (!showTooltip || !buttonRef.current || !canPortal) {
138
127
  return;
139
128
  }
140
- const updatePosition = () => {
141
- if (!buttonRef.current) {
129
+ const update = () => {
130
+ if (!buttonRef.current)
142
131
  return;
143
- }
144
- const buttonRect = buttonRef.current.getBoundingClientRect();
145
- if (canPortal) {
146
- const tooltipRect = tooltipRef.current?.getBoundingClientRect();
147
- const tooltipWidth = tooltipRect?.width ?? 360;
148
- const tooltipHeight = tooltipRect?.height ?? 520;
149
- const viewportWidth = window.innerWidth;
150
- const viewportHeight = window.innerHeight;
151
- const margin = 8;
152
- const spaceBelow = viewportHeight - buttonRect.bottom;
153
- const spaceAbove = buttonRect.top;
154
- const spaceRight = viewportWidth - buttonRect.right;
155
- const spaceLeft = buttonRect.left;
156
- const preferBelow = spaceBelow >= spaceAbove;
157
- const preferRight = spaceRight >= spaceLeft;
158
- let top = preferBelow
159
- ? buttonRect.bottom + margin
160
- : buttonRect.top - tooltipHeight - margin;
161
- if (top < margin) {
162
- top = margin;
163
- }
164
- if (top + tooltipHeight > viewportHeight - margin) {
165
- top = Math.max(margin, viewportHeight - tooltipHeight - margin);
166
- }
167
- let left = preferRight
168
- ? buttonRect.left
169
- : buttonRect.right - tooltipWidth;
170
- if (left < margin) {
171
- left = margin;
172
- }
173
- if (left + tooltipWidth > viewportWidth - margin) {
174
- left = Math.max(margin, viewportWidth - tooltipWidth - margin);
175
- }
176
- setTooltipPosition({
177
- top: `${top}px`,
178
- left: `${left}px`,
179
- position: "fixed",
180
- });
181
- }
182
- else {
183
- const position = calculateTooltipPosition(buttonRect);
184
- setTooltipPosition(position);
185
- }
132
+ const rect = buttonRef.current.getBoundingClientRect();
133
+ const tipRect = tooltipRef.current?.getBoundingClientRect();
134
+ const tipWidth = tipRect?.width ?? 360;
135
+ const tipHeight = tipRect?.height ?? 520;
136
+ const margin = 8;
137
+ const viewportW = window.innerWidth;
138
+ const viewportH = window.innerHeight;
139
+ const placeBelow = viewportH - rect.bottom >= rect.top;
140
+ let top = placeBelow
141
+ ? rect.bottom + margin
142
+ : rect.top - tipHeight - margin;
143
+ top = Math.max(margin, Math.min(top, viewportH - tipHeight - margin));
144
+ const placeRight = viewportW - rect.right >= rect.left;
145
+ let left = placeRight ? rect.left : rect.right - tipWidth;
146
+ left = Math.max(margin, Math.min(left, viewportW - tipWidth - margin));
147
+ setTooltipStyle({
148
+ position: "fixed",
149
+ top: `${top}px`,
150
+ left: `${left}px`,
151
+ });
186
152
  };
187
- const rafId = requestAnimationFrame(updatePosition);
188
- const rafId2 = requestAnimationFrame(updatePosition);
189
- const handleResize = () => updatePosition();
190
- window.addEventListener("resize", handleResize);
191
- window.addEventListener("scroll", handleResize, true);
153
+ const raf1 = requestAnimationFrame(update);
154
+ const raf2 = requestAnimationFrame(update);
155
+ window.addEventListener("resize", update);
156
+ window.addEventListener("scroll", update, true);
192
157
  return () => {
193
- cancelAnimationFrame(rafId);
194
- cancelAnimationFrame(rafId2);
195
- window.removeEventListener("resize", handleResize);
196
- window.removeEventListener("scroll", handleResize, true);
158
+ cancelAnimationFrame(raf1);
159
+ cancelAnimationFrame(raf2);
160
+ window.removeEventListener("resize", update);
161
+ window.removeEventListener("scroll", update, true);
197
162
  };
198
163
  }, [showTooltip, canPortal]);
199
- const handleMouseEnter = () => {
200
- if (requiresApiKeySelection) {
201
- return;
202
- }
203
- setShowTooltip(true);
204
- setIsHovered(true);
205
- };
206
- const handleMouseLeave = () => {
207
- if (requiresApiKeySelection) {
208
- return;
209
- }
210
- // Keep tooltip visible if hovering over it
211
- setTimeout(() => {
212
- if (!tooltipRef.current?.matches(":hover") &&
213
- !buttonRef.current?.matches(":hover")) {
214
- setShowTooltip(false);
215
- setIsHovered(false);
216
- }
217
- }, 100);
218
- };
219
164
  useLayoutEffect(() => {
220
- if (!showTooltip || requiresApiKeySelection || lbStatus !== "ready") {
165
+ if (!showTooltip || lbStatus !== "ready" || requiresApiKeySelection) {
221
166
  return;
222
167
  }
223
168
  if (!lbBasicStatus && !lbIsLoadingStatus && lbRefreshBasicStatus) {
@@ -230,8 +175,8 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
230
175
  }
231
176
  }, [
232
177
  showTooltip,
233
- requiresApiKeySelection,
234
178
  lbStatus,
179
+ requiresApiKeySelection,
235
180
  lbBasicStatus,
236
181
  lbStorageStatus,
237
182
  lbIsLoadingStatus,
@@ -239,493 +184,88 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
239
184
  lbRefreshBasicStatus,
240
185
  lbRefreshStorageStatus,
241
186
  ]);
242
- if (loading || isSelectingApiKey) {
243
- return (_jsx("button", { ref: buttonRef, style: {
244
- ...aiStyles.statusButton,
245
- ...aiStyles.statusButtonDisabled,
246
- }, className: className, disabled: true, children: _jsx("svg", { style: aiStyles.spinner, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) }) }));
247
- }
248
- if (requiresApiKeySelection) {
249
- return (_jsxs("div", { style: { position: "relative", display: "inline-block" }, children: [_jsx("button", { ref: buttonRef, style: {
250
- ...aiStyles.statusButton,
251
- color: "#f59e0b",
252
- ...(isHovered && aiStyles.statusButtonHover),
253
- }, className: className, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onClick: () => setShowApiKeySelector(true), title: "Select an API key to enable AI status and generation", children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" }) }) }), showCornerLoadingIndicator && (_jsx("div", { style: {
254
- position: "absolute",
255
- top: -2,
256
- right: -2,
257
- width: 12,
258
- height: 12,
259
- borderRadius: "999px",
260
- background: "rgba(2, 6, 23, 0.95)",
261
- border: "1px solid rgba(139, 92, 246, 0.55)",
262
- display: "flex",
263
- alignItems: "center",
264
- justifyContent: "center",
265
- pointerEvents: "none",
266
- }, children: _jsx("svg", { style: aiStyles.spinner, width: "8", height: "8", viewBox: "0 0 24 24", fill: "none", stroke: "#8b5cf6", strokeWidth: "2", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) }) })), showApiKeySelector && apiKeys.length > 0 && (_jsx(LBApiKeySelector, { isOpen: showApiKeySelector, apiKeys: apiKeys, onSelect: async (keyId) => {
267
- setIsSelectingApiKey(true);
268
- try {
269
- if (switchApiKey) {
270
- await switchApiKey(keyId);
271
- }
272
- setShowApiKeySelector(false);
273
- if (refetchProviders) {
274
- await refetchProviders();
275
- }
276
- }
277
- catch (error) {
278
- console.error("Failed to select API key:", error);
279
- }
280
- finally {
281
- setIsSelectingApiKey(false);
282
- }
283
- }, onCancel: () => setShowApiKeySelector(false) }))] }));
284
- }
285
- if (!effectiveStatus) {
286
- // Si pas de statut API et pas de LBProvider, afficher message simple
287
- if (!lbStatus && lbStatus !== "ready") {
288
- return (_jsxs("div", { style: { position: "relative", display: "inline-block" }, children: [_jsx("button", { ref: buttonRef, style: {
289
- ...aiStyles.statusButton,
290
- color: "#ef4444",
291
- ...(isHovered && aiStyles.statusButtonHover),
292
- }, className: className, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" }) }) }), showTooltip &&
293
- canPortal &&
294
- createPortal(_jsx("div", { ref: tooltipRef, style: {
295
- ...aiStyles.tooltip,
296
- ...tooltipPosition,
297
- zIndex: 50,
298
- }, onMouseEnter: () => setShowTooltip(true), onMouseLeave: handleMouseLeave, children: "No status available" }), document.body)] }));
187
+ const balance = (effectiveStatus?.balance || {});
188
+ const storage = (effectiveStatus?.storage || {});
189
+ const balanceUsed = num(balance.used);
190
+ const balanceRemaining = num(balance.remaining);
191
+ const rawBalanceTotal = balance.total ?? num(balance.purchased) + num(balance.quota);
192
+ const balanceTotal = rawBalanceTotal > 0 ? rawBalanceTotal : balanceUsed + balanceRemaining;
193
+ const balancePct = balance.percentage ??
194
+ (balanceTotal > 0 ? Math.round((balanceUsed / balanceTotal) * 100) : 0);
195
+ const storageUsed = storage.used_mb ?? storage.total_mb ?? 0;
196
+ const storageTotal = storage.allocated_mb ?? storage.total_mb ?? 0;
197
+ const storagePct = storage.percentage ??
198
+ (storageTotal > 0 ? Math.round((storageUsed / storageTotal) * 100) : 0);
199
+ const showFastSkeleton = lbStatus === "ready" &&
200
+ lbIsLoadingStatus &&
201
+ !lbBasicStatus &&
202
+ !effectiveStatus;
203
+ const showCornerLoading = lbStatus === "ready" &&
204
+ (showFastSkeleton || lbIsLoadingStatus || lbIsLoadingStorage);
205
+ const triggerTone = useMemo(() => {
206
+ if (requiresApiKeySelection)
207
+ return "warning";
208
+ if (lbStatus && lbStatus !== "ready")
209
+ return "danger";
210
+ if (effectiveStatus)
211
+ return "success";
212
+ return "neutral";
213
+ }, [requiresApiKeySelection, lbStatus, effectiveStatus]);
214
+ const openTooltip = () => {
215
+ if (requiresApiKeySelection) {
216
+ return;
299
217
  }
300
- return (_jsxs(_Fragment, { children: [_jsxs("div", { style: { position: "relative", display: "inline-block" }, children: [_jsx("button", { ref: buttonRef, style: {
301
- ...aiStyles.statusButton,
302
- color: lbStatus === "ready" ? "#10b981" : "#ef4444",
303
- ...(isHovered && aiStyles.statusButtonHover),
304
- }, className: className, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onClick: () => {
305
- if (lbStatus !== "ready") {
306
- setShowSigninModal(true);
307
- }
308
- }, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" }) }) }), showCornerLoadingIndicator && (_jsx("div", { style: {
309
- position: "absolute",
310
- top: -2,
311
- right: -2,
312
- width: 12,
313
- height: 12,
314
- borderRadius: "999px",
315
- background: "rgba(2, 6, 23, 0.95)",
316
- border: "1px solid rgba(139, 92, 246, 0.55)",
317
- display: "flex",
318
- alignItems: "center",
319
- justifyContent: "center",
320
- pointerEvents: "none",
321
- }, children: _jsx("svg", { style: aiStyles.spinner, width: "8", height: "8", viewBox: "0 0 24 24", fill: "none", stroke: "#8b5cf6", strokeWidth: "2", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) }) })), showTooltip &&
322
- canPortal &&
323
- createPortal(_jsx("div", { ref: tooltipRef, style: {
324
- ...aiStyles.tooltip,
325
- ...tooltipPosition,
326
- zIndex: 50,
327
- }, onMouseEnter: () => setShowTooltip(true), onMouseLeave: handleMouseLeave, children: lbStatus === "ready" && user ? (_jsxs(_Fragment, { children: [_jsx("div", { style: aiStyles.tooltipHeader, children: "API Status" }), _jsx("div", { style: {
328
- ...aiStyles.tooltipSection,
329
- ...aiStyles.tooltipSectionFirst,
330
- }, children: _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "User:" }), _jsx("span", { style: aiStyles.tooltipValue, children: user.email })] }) }), showFastStatusSkeleton && (_jsxs(_Fragment, { children: [_jsxs("div", { style: aiStyles.tooltipSection, children: [_jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "API Key:" }), _jsx("div", { style: {
331
- height: "16px",
332
- width: "110px",
333
- background: "rgba(139, 92, 246, 0.12)",
334
- borderRadius: "4px",
335
- animation: "pulse 2s ease-in-out infinite",
336
- } })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Env:" }), _jsx("div", { style: {
337
- height: "16px",
338
- width: "48px",
339
- background: "rgba(139, 92, 246, 0.12)",
340
- borderRadius: "4px",
341
- animation: "pulse 2s ease-in-out infinite",
342
- } })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }), _jsx("div", { style: {
343
- height: "16px",
344
- width: "84px",
345
- background: "rgba(139, 92, 246, 0.12)",
346
- borderRadius: "4px",
347
- animation: "pulse 2s ease-in-out infinite",
348
- } })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Auth:" }), _jsx("div", { style: {
349
- height: "16px",
350
- width: "72px",
351
- background: "rgba(139, 92, 246, 0.12)",
352
- borderRadius: "4px",
353
- animation: "pulse 2s ease-in-out infinite",
354
- } })] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Wallet" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), _jsx("div", { style: {
355
- height: "16px",
356
- width: "120px",
357
- background: "rgba(139, 92, 246, 0.12)",
358
- borderRadius: "4px",
359
- animation: "pulse 2s ease-in-out infinite",
360
- } })] })] })] })), _jsxs("div", { style: {
361
- display: "flex",
362
- gap: "8px",
363
- borderTop: "1px solid var(--ai-border-primary, #374151)",
364
- paddingTop: "12px",
365
- }, children: [_jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/dashboard", "_blank"), style: {
366
- flex: 1,
367
- background: "transparent",
368
- border: "none",
369
- padding: "14px",
370
- cursor: "pointer",
371
- display: "flex",
372
- alignItems: "center",
373
- justifyContent: "center",
374
- color: "#8b5cf6",
375
- transition: "all 0.2s ease",
376
- }, onMouseEnter: (e) => {
377
- Object.assign(e.currentTarget.style, {
378
- background: "rgba(139, 92, 246, 0.1)",
379
- });
380
- }, onMouseLeave: (e) => {
381
- Object.assign(e.currentTarget.style, {
382
- background: "transparent",
383
- });
384
- }, title: "View Metrics", children: _jsx(BarChart3, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/ai/settings", "_blank"), style: {
385
- flex: 1,
386
- background: "transparent",
387
- border: "none",
388
- padding: "14px",
389
- cursor: "pointer",
390
- display: "flex",
391
- alignItems: "center",
392
- justifyContent: "center",
393
- color: "#8b5cf6",
394
- transition: "all 0.2s ease",
395
- }, onMouseEnter: (e) => {
396
- Object.assign(e.currentTarget.style, {
397
- background: "rgba(139, 92, 246, 0.1)",
398
- });
399
- }, onMouseLeave: (e) => {
400
- Object.assign(e.currentTarget.style, {
401
- background: "transparent",
402
- });
403
- }, title: "Settings", children: _jsx(Settings, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/ai/prompts", "_blank"), style: {
404
- flex: 1,
405
- background: "transparent",
406
- border: "none",
407
- padding: "14px",
408
- cursor: "pointer",
409
- display: "flex",
410
- alignItems: "center",
411
- justifyContent: "center",
412
- color: "#8b5cf6",
413
- transition: "all 0.2s ease",
414
- }, onMouseEnter: (e) => {
415
- Object.assign(e.currentTarget.style, {
416
- background: "rgba(139, 92, 246, 0.1)",
417
- });
418
- }, onMouseLeave: (e) => {
419
- Object.assign(e.currentTarget.style, {
420
- background: "transparent",
421
- });
422
- }, title: "My Prompts", children: _jsx(FileText, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/folder", "_blank"), style: {
423
- flex: 1,
424
- background: "transparent",
425
- border: "none",
426
- padding: "14px",
427
- cursor: "pointer",
428
- display: "flex",
429
- alignItems: "center",
430
- justifyContent: "center",
431
- color: "#8b5cf6",
432
- transition: "all 0.2s ease",
433
- }, onMouseEnter: (e) => {
434
- Object.assign(e.currentTarget.style, {
435
- background: "rgba(139, 92, 246, 0.1)",
436
- });
437
- }, onMouseLeave: (e) => {
438
- Object.assign(e.currentTarget.style, {
439
- background: "transparent",
440
- });
441
- }, title: "New Folder", children: _jsx(FolderPlus, { size: 18 }) }), _jsx("button", { onClick: async () => {
442
- if (logout) {
443
- await logout();
444
- // Refresh provider data after logout
445
- if (refetchProviders) {
446
- await refetchProviders();
447
- }
448
- }
449
- setShowTooltip(false);
450
- }, style: {
451
- flex: 1,
452
- background: "transparent",
453
- border: "none",
454
- padding: "14px",
455
- cursor: "pointer",
456
- display: "flex",
457
- alignItems: "center",
458
- justifyContent: "center",
459
- color: "#ef4444",
460
- transition: "all 0.2s ease",
461
- }, onMouseEnter: (e) => {
462
- Object.assign(e.currentTarget.style, {
463
- background: "rgba(239, 68, 68, 0.1)",
464
- });
465
- }, onMouseLeave: (e) => {
466
- Object.assign(e.currentTarget.style, {
467
- background: "transparent",
468
- });
469
- }, title: "Logout", children: _jsx(Power, { size: 18 }) })] })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { style: aiStyles.tooltipHeader, children: "LastBrain Authentication" }), _jsx("div", { style: {
470
- paddingBottom: "12px",
471
- }, children: _jsx("p", { style: {
472
- margin: 0,
473
- fontSize: "13px",
474
- color: "var(--ai-text-secondary, #9ca3af)",
475
- lineHeight: "1.5",
476
- }, children: "Connectez-vous pour acc\u00E9der aux fonctionnalit\u00E9s IA" }) }), _jsx("button", { onClick: () => {
477
- setShowSigninModal(true);
478
- setShowTooltip(false);
479
- }, style: {
480
- width: "100%",
481
- padding: "10px",
482
- background: "linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)",
483
- border: "none",
484
- borderRadius: "6px",
485
- color: "#ffffff",
486
- fontSize: "13px",
487
- fontWeight: 600,
488
- cursor: "pointer",
489
- transition: "all 0.2s ease",
490
- }, onMouseEnter: (e) => {
491
- e.currentTarget.style.transform = "translateY(-1px)";
492
- e.currentTarget.style.boxShadow =
493
- "0 4px 12px rgba(139, 92, 246, 0.3)";
494
- }, onMouseLeave: (e) => {
495
- e.currentTarget.style.transform = "translateY(0)";
496
- e.currentTarget.style.boxShadow = "none";
497
- }, children: "\uD83D\uDD10 Se connecter" })] })) }), document.body)] }), _jsx(LBSigninModal, { isOpen: showSigninModal, onClose: () => setShowSigninModal(false) })] }));
498
- }
499
- return (_jsxs("div", { style: { position: "relative", display: "inline-block" }, children: [_jsx("button", { ref: buttonRef, style: {
500
- ...aiStyles.statusButton,
501
- color: "#10b981",
502
- ...(isHovered && aiStyles.statusButtonHover),
503
- }, className: className, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: _jsx("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" }) }) }), showCornerLoadingIndicator && (_jsx("div", { style: {
504
- position: "absolute",
505
- top: -2,
506
- right: -2,
507
- width: 12,
508
- height: 12,
509
- borderRadius: "999px",
510
- background: "rgba(2, 6, 23, 0.95)",
511
- border: "1px solid rgba(139, 92, 246, 0.55)",
512
- display: "flex",
513
- alignItems: "center",
514
- justifyContent: "center",
515
- pointerEvents: "none",
516
- }, children: _jsx("svg", { style: aiStyles.spinner, width: "8", height: "8", viewBox: "0 0 24 24", fill: "none", stroke: "#8b5cf6", strokeWidth: "2", children: _jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" }) }) })), showTooltip &&
517
- canPortal &&
518
- createPortal(_jsxs("div", { ref: tooltipRef, style: {
519
- ...aiStyles.tooltip,
520
- ...tooltipPosition,
521
- zIndex: 50,
522
- }, onMouseEnter: () => setShowTooltip(true), onMouseLeave: handleMouseLeave, children: [_jsx("div", { style: aiStyles.tooltipHeader, children: "API Status" }), effectiveStatus.user?.email && (_jsx("div", { style: {
523
- ...aiStyles.tooltipSection,
524
- ...aiStyles.tooltipSectionFirst,
525
- }, children: _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "User:" }), _jsx("span", { style: {
526
- ...aiStyles.tooltipValue,
527
- fontSize: "12px",
528
- maxWidth: "200px",
529
- overflow: "hidden",
530
- textOverflow: "ellipsis",
531
- }, children: effectiveStatus.user.email })] }) })), _jsxs("div", { style: {
532
- ...aiStyles.tooltipSection,
533
- ...(effectiveStatus.user?.email
534
- ? {}
535
- : aiStyles.tooltipSectionFirst),
536
- }, children: [_jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "API Key:" }), _jsxs("div", { style: {
537
- display: "flex",
538
- alignItems: "center",
539
- gap: "8px",
540
- }, children: [_jsx("span", { style: aiStyles.tooltipValue, children: lbIsLoadingStatus ? (_jsx("div", { style: {
541
- height: "16px",
542
- width: "100px",
543
- background: "rgba(139, 92, 246, 0.1)",
544
- borderRadius: "4px",
545
- animation: "pulse 2s ease-in-out infinite",
546
- } })) : (effectiveStatus?.apiKey?.name ||
218
+ setShowTooltip(true);
219
+ };
220
+ const closeTooltip = () => {
221
+ if (requiresApiKeySelection) {
222
+ return;
223
+ }
224
+ setTimeout(() => {
225
+ if (!tooltipRef.current?.matches(":hover") &&
226
+ !buttonRef.current?.matches(":hover")) {
227
+ setShowTooltip(false);
228
+ }
229
+ }, 100);
230
+ };
231
+ const renderTriggerIcon = () => {
232
+ if (loading || isSelectingApiKey) {
233
+ return _jsx(Loader2, { size: 14, className: "ai-spinner" });
234
+ }
235
+ return _jsx(Shield, { size: 14 });
236
+ };
237
+ const triggerClass = [
238
+ "ai-status-trigger",
239
+ `ai-size-${size}`,
240
+ `ai-radius-${radius}`,
241
+ triggerTone === "warning" ? "ai-status-trigger--warning" : "",
242
+ triggerTone === "danger" ? "ai-status-trigger--danger" : "",
243
+ triggerTone === "success" ? "ai-status-trigger--success" : "",
244
+ className,
245
+ ]
246
+ .filter(Boolean)
247
+ .join(" ");
248
+ const tooltipNode = showTooltip && canPortal
249
+ ? createPortal(_jsx("div", { ref: tooltipRef, className: "ai-popover ai-tooltip ai-status-tooltip", style: tooltipStyle, onMouseEnter: () => setShowTooltip(true), onMouseLeave: closeTooltip, children: lbStatus === "ready" && user ? (_jsx(_Fragment, { children: _jsxs("div", { className: "ai-popover-body", children: [_jsx("div", { className: "ai-popover-header", children: "API Status" }), _jsx("div", { className: "ai-popover-section ai-popover-section--first", children: _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: "User" }), _jsx("span", { className: "ai-popover-value ai-truncate max-w-[200px]", children: user.email })] }) }), _jsxs("div", { className: "ai-popover-section", children: [_jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: "API Key" }), _jsxs("div", { className: "ai-row", children: [lbIsLoadingStatus ? (_jsx("div", { className: "ai-kv-skeleton w-[110px]" })) : (_jsx("span", { className: "ai-popover-value", children: effectiveStatus?.apiKey?.name ||
547
250
  effectiveStatus?.api_key?.name ||
548
- "Unknown") }), switchApiKey && (_jsx("button", { onClick: (e) => {
251
+ "Unknown" })), switchApiKey ? (_jsx("button", { type: "button", className: "ai-icon-btn", onClick: (e) => {
549
252
  e.stopPropagation();
550
253
  setShowTooltip(false);
551
254
  setShowApiKeySelector(true);
552
- }, style: {
553
- background: "rgba(139, 92, 246, 0.1)",
554
- border: "1px solid rgba(139, 92, 246, 0.3)",
555
- borderRadius: "50%",
556
- width: "24px",
557
- height: "24px",
558
- display: "flex",
559
- alignItems: "center",
560
- justifyContent: "center",
561
- cursor: "pointer",
562
- padding: 0,
563
- transition: "all 0.2s ease",
564
- }, onMouseEnter: (e) => {
565
- e.currentTarget.style.background =
566
- "rgba(139, 92, 246, 0.2)";
567
- e.currentTarget.style.borderColor =
568
- "rgba(139, 92, 246, 0.5)";
569
- }, onMouseLeave: (e) => {
570
- e.currentTarget.style.background =
571
- "rgba(139, 92, 246, 0.1)";
572
- e.currentTarget.style.borderColor =
573
- "rgba(139, 92, 246, 0.3)";
574
- }, title: "Change API Key", children: _jsx(ArrowRightLeft, { size: 12, style: { color: "rgba(139, 92, 246, 1)" } }) }))] })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Env:" }), lbIsLoadingStatus && !effectiveStatus?.apiKey?.env ? (_jsx("div", { style: {
575
- height: "16px",
576
- width: "48px",
577
- background: "rgba(139, 92, 246, 0.1)",
578
- borderRadius: "4px",
579
- animation: "pulse 2s ease-in-out infinite",
580
- } })) : (_jsx("span", { style: aiStyles.tooltipValue, children: effectiveStatus.apiKey?.env ||
581
- effectiveStatus.api_key?.env ||
582
- "N/A" }))] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }), lbIsLoadingStatus &&
583
- !effectiveStatus?.apiKey?.rate_limit_rpm &&
584
- !effectiveStatus?.api_key?.rate_limit_rpm ? (_jsx("div", { style: {
585
- height: "16px",
586
- width: "92px",
587
- background: "rgba(139, 92, 246, 0.1)",
588
- borderRadius: "4px",
589
- animation: "pulse 2s ease-in-out infinite",
590
- } })) : (_jsxs("span", { style: aiStyles.tooltipValue, children: [effectiveStatus.apiKey?.rate_limit_rpm ||
591
- effectiveStatus.api_key?.rate_limit_rpm ||
592
- 0, " ", "req/min"] }))] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Auth:" }), _jsx("span", { style: aiStyles.tooltipValue, children: lbIsLoadingStatus
255
+ }, title: "Changer de cl\u00E9 API", children: _jsx(ArrowRightLeft, { size: 12 }) })) : null] })] }), _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: "Env" }), lbIsLoadingStatus && !effectiveStatus?.apiKey?.env ? (_jsx("div", { className: "ai-kv-skeleton w-12" })) : (_jsx("span", { className: "ai-popover-value", children: effectiveStatus?.apiKey?.env ||
256
+ effectiveStatus?.api_key?.env ||
257
+ "N/A" }))] }), _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: "Rate Limit" }), lbIsLoadingStatus &&
258
+ !effectiveStatus?.apiKey?.rate_limit_rpm ? (_jsx("div", { className: "ai-kv-skeleton w-[92px]" })) : (_jsxs("span", { className: "ai-popover-value", children: [effectiveStatus?.apiKey?.rate_limit_rpm ||
259
+ effectiveStatus?.api_key?.rate_limit_rpm ||
260
+ 0, " ", "req/min"] }))] }), _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: "Auth" }), _jsx("span", { className: "ai-popover-value", children: lbIsLoadingStatus
593
261
  ? "..."
594
- : effectiveStatus?.authType || lbStatus || "unknown" })] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Wallet" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), lbIsLoadingStatus && !effectiveStatus?.balance ? (_jsxs("div", { style: {
595
- display: "flex",
596
- alignItems: "center",
597
- gap: "8px",
598
- }, children: [_jsx("div", { style: {
599
- height: "16px",
600
- width: "120px",
601
- background: "rgba(139, 92, 246, 0.1)",
602
- borderRadius: "4px",
603
- animation: "pulse 2s ease-in-out infinite",
604
- } }), _jsx("div", { style: {
605
- width: "28px",
606
- height: "28px",
607
- borderRadius: "50%",
608
- background: "rgba(139, 92, 246, 0.1)",
609
- animation: "pulse 2s ease-in-out infinite",
610
- } })] })) : (_jsxs(_Fragment, { children: [_jsxs("span", { style: aiStyles.tooltipValue, children: ["$", formatFixed(balanceUsed, 2), " / $", formatFixed(balanceTotal, 2)] }), renderUsageCircle(balancePercentage)] }))] })] }), _jsxs("div", { style: aiStyles.tooltipSection, children: [_jsx("div", { style: aiStyles.tooltipSubtitle, children: "Storage" }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Total:" }), lbIsLoadingStorage ? (_jsxs("div", { style: {
611
- display: "flex",
612
- alignItems: "center",
613
- gap: "8px",
614
- }, children: [_jsx("div", { style: {
615
- height: "16px",
616
- width: "120px",
617
- background: "rgba(139, 92, 246, 0.1)",
618
- borderRadius: "4px",
619
- animation: "pulse 2s ease-in-out infinite",
620
- } }), _jsx("div", { style: {
621
- width: "28px",
622
- height: "28px",
623
- borderRadius: "50%",
624
- background: "rgba(139, 92, 246, 0.1)",
625
- animation: "pulse 2s ease-in-out infinite",
626
- } })] })) : (_jsxs(_Fragment, { children: [_jsxs("span", { style: aiStyles.tooltipValue, children: [formatStorage(storageUsed), " /", " ", formatStorage(storageAllocated)] }), renderUsageCircle(storagePercentage)] }))] })] }), _jsxs("div", { style: {
627
- ...aiStyles.tooltipActions,
628
- width: "100%",
629
- flexDirection: "row",
630
- }, children: [_jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/ai/tokens", "_blank"), style: {
631
- background: "transparent",
632
- border: "none",
633
- borderRadius: "4px",
634
- padding: "14px",
635
- cursor: "pointer",
636
- display: "flex",
637
- alignItems: "center",
638
- justifyContent: "center",
639
- color: "#8b5cf6",
640
- transition: "all 0.2s ease",
641
- }, onMouseEnter: (e) => {
642
- Object.assign(e.currentTarget.style, {
643
- background: "rgba(139, 92, 246, 0.1)",
644
- });
645
- }, onMouseLeave: (e) => {
646
- Object.assign(e.currentTarget.style, {
647
- background: "transparent",
648
- });
649
- }, title: "Dashboard", children: _jsx(BarChart3, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/ai/history", "_blank"), style: {
650
- background: "transparent",
651
- border: "none",
652
- borderRadius: "4px",
653
- padding: "14px",
654
- cursor: "pointer",
655
- display: "flex",
656
- alignItems: "center",
657
- justifyContent: "center",
658
- color: "#8b5cf6",
659
- transition: "all 0.2s ease",
660
- }, onMouseEnter: (e) => {
661
- Object.assign(e.currentTarget.style, {
662
- background: "rgba(139, 92, 246, 0.1)",
663
- });
664
- }, onMouseLeave: (e) => {
665
- Object.assign(e.currentTarget.style, {
666
- background: "transparent",
667
- });
668
- }, title: "History", children: _jsx(HistoryIcon, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/ai/settings", "_blank"), style: {
669
- background: "transparent",
670
- border: "none",
671
- borderRadius: "4px",
672
- padding: "14px",
673
- cursor: "pointer",
674
- display: "flex",
675
- alignItems: "center",
676
- justifyContent: "center",
677
- color: "#8b5cf6",
678
- transition: "all 0.2s ease",
679
- }, onMouseEnter: (e) => {
680
- Object.assign(e.currentTarget.style, {
681
- background: "rgba(139, 92, 246, 0.1)",
682
- });
683
- }, onMouseLeave: (e) => {
684
- Object.assign(e.currentTarget.style, {
685
- background: "transparent",
686
- });
687
- }, title: "Settings", children: _jsx(Settings, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/ai/prompts", "_blank"), style: {
688
- background: "transparent",
689
- border: "none",
690
- borderRadius: "4px",
691
- padding: "14px",
692
- cursor: "pointer",
693
- display: "flex",
694
- alignItems: "center",
695
- justifyContent: "center",
696
- color: "#8b5cf6",
697
- transition: "all 0.2s ease",
698
- }, onMouseEnter: (e) => {
699
- Object.assign(e.currentTarget.style, {
700
- background: "rgba(139, 92, 246, 0.1)",
701
- });
702
- }, onMouseLeave: (e) => {
703
- Object.assign(e.currentTarget.style, {
704
- background: "transparent",
705
- });
706
- }, title: "New Prompt", children: _jsx(FileText, { size: 18 }) }), _jsx("button", { onClick: () => window.open("https://prompt.lastbrain.io/auth/folder", "_blank"), style: {
707
- background: "transparent",
708
- border: "none",
709
- padding: "14px",
710
- cursor: "pointer",
711
- display: "flex",
712
- alignItems: "center",
713
- justifyContent: "center",
714
- color: "#8b5cf6",
715
- transition: "all 0.2s ease",
716
- }, onMouseEnter: (e) => {
717
- Object.assign(e.currentTarget.style, {
718
- background: "rgba(139, 92, 246, 0.1)",
719
- });
720
- }, onMouseLeave: (e) => {
721
- Object.assign(e.currentTarget.style, {
722
- background: "transparent",
723
- });
724
- }, title: "New Folder", children: _jsx(FolderPlus, { size: 18 }) }), logout && !isApiKeyAuthMode && (_jsx("button", { onClick: async () => {
262
+ : effectiveStatus?.authType || lbStatus || "unknown" })] })] }), _jsxs("div", { className: "ai-popover-section", children: [_jsx("div", { className: "ai-popover-header text-xs mb-2", children: "Wallet" }), _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: "Total" }), lbIsLoadingStatus && !effectiveStatus?.balance ? (_jsxs("div", { className: "ai-row", children: [_jsx("div", { className: "ai-kv-skeleton w-[120px]" }), _jsx("div", { className: "ai-kv-skeleton w-7 h-7 rounded-full" })] })) : (_jsxs("div", { className: "ai-row", children: [_jsxs("span", { className: "ai-popover-value", children: ["$", fixed(balanceUsed, 2), " / $", fixed(balanceTotal, 2)] }), _jsx(UsageCircle, { percentage: balancePct })] }))] })] }), _jsxs("div", { className: "ai-popover-section", children: [_jsx("div", { className: "ai-popover-header text-xs mb-2", children: "Storage" }), _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: "Total" }), lbIsLoadingStorage ? (_jsxs("div", { className: "ai-row", children: [_jsx("div", { className: "ai-kv-skeleton w-[120px]" }), _jsx("div", { className: "ai-kv-skeleton w-7 h-7 rounded-full" })] })) : (_jsxs("div", { className: "ai-row", children: [_jsxs("span", { className: "ai-popover-value", children: [formatStorage(storageUsed), " /", " ", formatStorage(storageTotal)] }), _jsx(UsageCircle, { percentage: storagePct })] }))] })] }), _jsxs("div", { className: "ai-status-actions", children: [QUICK_LINKS.map((item) => {
263
+ const Icon = item.icon;
264
+ return (_jsx("button", { type: "button", className: "ai-status-action-btn", onClick: () => window.open(item.href, "_blank"), title: item.title, children: _jsx(Icon, { size: 17 }) }, item.href));
265
+ }), logout && !isApiKeyAuthMode ? (_jsx("button", { type: "button", className: "ai-status-action-btn ai-status-action-btn--danger", onClick: async () => {
725
266
  try {
726
267
  await logout();
727
268
  setShowTooltip(false);
728
- // Refresh provider data after logout
729
269
  if (refetchProviders) {
730
270
  await refetchProviders();
731
271
  }
@@ -733,39 +273,30 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
733
273
  catch (error) {
734
274
  console.error("Logout failed:", error);
735
275
  }
736
- }, style: {
737
- background: "transparent",
738
- border: "none",
739
- padding: "14px",
740
- cursor: "pointer",
741
- display: "flex",
742
- alignItems: "center",
743
- justifyContent: "center",
744
- color: "#ef4444",
745
- transition: "all 0.2s ease",
746
- borderRadius: "4px",
747
- }, onMouseEnter: (e) => {
748
- Object.assign(e.currentTarget.style, {
749
- background: "rgba(239, 68, 68, 0.1)",
750
- });
751
- }, onMouseLeave: (e) => {
752
- Object.assign(e.currentTarget.style, {
753
- background: "transparent",
754
- });
755
- }, title: "Logout", children: _jsx(LogOut, { size: 18 }) }))] })] }), document.body), showApiKeySelector && apiKeys.length > 0 && (_jsx(LBApiKeySelector, { isOpen: showApiKeySelector, apiKeys: apiKeys, onSelect: async (keyId) => {
276
+ }, title: "Logout", children: _jsx(LogOut, { size: 17 }) })) : null] })] }) })) : (_jsxs("div", { className: "ai-popover-body", children: [_jsx("div", { className: "ai-popover-header", children: "LastBrain Authentication" }), _jsx("p", { className: "ai-signin-subtitle mt-0", children: "Connectez-vous pour acc\u00E9der aux fonctionnalit\u00E9s IA." }), _jsx("button", { type: "button", className: "ai-btn ai-btn--auth w-full mt-2", onClick: () => {
277
+ setShowSigninModal(true);
278
+ setShowTooltip(false);
279
+ }, children: "Se connecter" })] })) }), document.body)
280
+ : null;
281
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative inline-block", children: [_jsx("button", { ref: buttonRef, className: triggerClass, onMouseEnter: openTooltip, onMouseLeave: closeTooltip, onClick: () => {
282
+ if (requiresApiKeySelection) {
283
+ setShowApiKeySelector(true);
284
+ return;
285
+ }
286
+ if (!effectiveStatus && lbStatus !== "ready") {
287
+ setShowSigninModal(true);
288
+ }
289
+ }, disabled: loading || isSelectingApiKey, title: requiresApiKeySelection
290
+ ? "Sélectionnez une clé API"
291
+ : "Voir le status", "aria-label": "AI status", children: renderTriggerIcon() }), showCornerLoading ? (_jsx("span", { className: "ai-status-loading-dot", "aria-hidden": "true", children: _jsx(Loader2, { size: 7, className: "ai-spinner text-[var(--ai-primary)]" }) })) : null] }), tooltipNode, showApiKeySelector && apiKeys.length > 0 ? (_jsx(LBApiKeySelector, { isOpen: showApiKeySelector, apiKeys: apiKeys, onSelect: async (keyId) => {
756
292
  setIsSelectingApiKey(true);
757
293
  try {
758
- // Utiliser la nouvelle fonction switchApiKey qui gère automatiquement le contexte
759
- if (switchApiKey) {
760
- await switchApiKey(keyId);
761
- }
762
- else {
294
+ if (!switchApiKey) {
763
295
  throw new Error("Switch API key function not available");
764
296
  }
297
+ await switchApiKey(keyId);
765
298
  setShowApiKeySelector(false);
766
299
  setShowTooltip(false);
767
- setIsLoadingStatus(true);
768
- // Refresh provider data after API key selection
769
300
  if (refetchProviders) {
770
301
  await refetchProviders();
771
302
  }
@@ -775,7 +306,6 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
775
306
  }
776
307
  finally {
777
308
  setIsSelectingApiKey(false);
778
- setIsLoadingStatus(false);
779
309
  }
780
- }, onCancel: () => setShowApiKeySelector(false) }))] }));
310
+ }, onCancel: () => setShowApiKeySelector(false) })) : null, _jsx(LBSigninModal, { isOpen: showSigninModal, onClose: () => setShowSigninModal(false) })] }));
781
311
  }