@lastbrain/ai-ui-react 1.0.68 → 1.0.70

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 (76) 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 +23 -70
  4. package/dist/components/AiContextButton.d.ts +10 -2
  5. package/dist/components/AiContextButton.d.ts.map +1 -1
  6. package/dist/components/AiContextButton.js +73 -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 +64 -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 +211 -676
  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/context/LBAuthProvider.d.ts +35 -3
  33. package/dist/context/LBAuthProvider.d.ts.map +1 -1
  34. package/dist/context/LBAuthProvider.js +2 -0
  35. package/dist/examples/AiUiPremiumShowcase.d.ts +2 -0
  36. package/dist/examples/AiUiPremiumShowcase.d.ts.map +1 -0
  37. package/dist/examples/AiUiPremiumShowcase.js +15 -0
  38. package/dist/hooks/useAiModels.d.ts.map +1 -1
  39. package/dist/hooks/useModelManagement.d.ts.map +1 -1
  40. package/dist/index.d.ts +2 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +2 -0
  43. package/dist/styles/inline.d.ts +1 -0
  44. package/dist/styles/inline.d.ts.map +1 -1
  45. package/dist/styles/inline.js +25 -129
  46. package/dist/styles.css +1268 -369
  47. package/dist/types.d.ts +3 -0
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/utils/errorHandler.d.ts +2 -2
  50. package/dist/utils/errorHandler.d.ts.map +1 -1
  51. package/dist/utils/errorHandler.js +8 -1
  52. package/dist/utils/modelManagement.d.ts +13 -10
  53. package/dist/utils/modelManagement.d.ts.map +1 -1
  54. package/dist/utils/modelManagement.js +19 -2
  55. package/package.json +2 -2
  56. package/src/components/AiChipLabel.tsx +68 -101
  57. package/src/components/AiContextButton.tsx +142 -413
  58. package/src/components/AiImageButton.tsx +29 -190
  59. package/src/components/AiInput.tsx +49 -74
  60. package/src/components/AiPromptPanel.tsx +81 -260
  61. package/src/components/AiSelect.tsx +61 -69
  62. package/src/components/AiStatusButton.tsx +496 -1327
  63. package/src/components/AiTextarea.tsx +50 -63
  64. package/src/components/LBApiKeySelector.tsx +93 -271
  65. package/src/components/LBConnectButton.tsx +39 -336
  66. package/src/components/LBSigninModal.tsx +141 -472
  67. package/src/context/LBAuthProvider.tsx +45 -6
  68. package/src/examples/AiUiPremiumShowcase.tsx +94 -0
  69. package/src/hooks/useAiModels.ts +2 -1
  70. package/src/hooks/useModelManagement.ts +2 -1
  71. package/src/index.ts +3 -0
  72. package/src/styles/inline.ts +27 -148
  73. package/src/styles.css +1268 -369
  74. package/src/types.ts +3 -0
  75. package/src/utils/errorHandler.ts +16 -3
  76. package/src/utils/modelManagement.ts +53 -15
@@ -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, useContext } 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";
7
- import { useLB } from "../context/LBAuthProvider";
8
- import { useAiContext } from "../context/AiProvider";
9
- import { LBSigninModal } from "./LBSigninModal";
5
+ import { ArrowRightLeft, BarChart3, FileText, Folder, History, Loader2, LogOut, Settings, Shield, } from "lucide-react";
6
+ import { LBContext } from "../context/LBAuthProvider";
7
+ import { AiContext } from "../context/AiProvider";
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
- let user;
67
+ let user = null;
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;
@@ -25,14 +76,12 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
25
76
  let lbSelectedKey = null;
26
77
  let lbRefreshBasicStatus;
27
78
  let lbRefreshStorageStatus;
28
- try {
29
- const lbContext = useLB();
79
+ const lbContext = useContext(LBContext);
80
+ if (lbContext) {
30
81
  lbStatus = lbContext.status;
31
- user = lbContext.user;
82
+ user = lbContext.user ?? null;
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;
@@ -43,181 +92,78 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
43
92
  lbRefreshBasicStatus = lbContext.refreshBasicStatus;
44
93
  lbRefreshStorageStatus = lbContext.refreshStorageStatus;
45
94
  }
46
- catch {
47
- // LBProvider n'est pas disponible, ignorer
95
+ else {
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
98
+ const aiContext = useContext(AiContext);
63
99
  let refetchProviders;
64
- try {
65
- const aiContext = useAiContext();
100
+ if (aiContext) {
66
101
  refetchProviders = aiContext.refetchProviders;
67
102
  }
68
- catch {
69
- // AiProvider n'est pas disponible, ignorer
103
+ else {
70
104
  refetchProviders = undefined;
71
105
  }
72
106
  const [showSigninModal, setShowSigninModal] = useState(false);
73
107
  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
108
  const [showTooltip, setShowTooltip] = useState(false);
128
- const [isHovered, setIsHovered] = useState(false);
129
- const [tooltipPosition, setTooltipPosition] = useState({});
109
+ const [isSelectingApiKey, setIsSelectingApiKey] = useState(false);
130
110
  const buttonRef = useRef(null);
131
111
  const tooltipRef = useRef(null);
132
112
  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";
113
+ const effectiveStatus = lbStatus === "ready"
114
+ ? {
115
+ ...(lbApiStatus || {}),
116
+ ...(lbBasicStatus || {}),
117
+ storage: lbStorageStatus?.storage || lbApiStatus?.storage,
118
+ }
119
+ : status || null;
120
+ const hasApiKeySelected = Boolean(effectiveStatus?.apiKey?.id ||
121
+ effectiveStatus?.api_key?.id ||
122
+ lbSelectedKey?.id);
135
123
  const requiresApiKeySelection = lbStatus === "ready" && !hasApiKeySelected && apiKeys.length > 0;
124
+ const isApiKeyAuthMode = effectiveStatus && 'authType' in effectiveStatus && effectiveStatus.authType === "api_key";
125
+ const [tooltipStyle, setTooltipStyle] = useState({});
136
126
  useLayoutEffect(() => {
137
- if (!showTooltip || !buttonRef.current) {
127
+ if (!showTooltip || !buttonRef.current || !canPortal) {
138
128
  return;
139
129
  }
140
- const updatePosition = () => {
141
- if (!buttonRef.current) {
130
+ const update = () => {
131
+ if (!buttonRef.current)
142
132
  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
- }
133
+ const rect = buttonRef.current.getBoundingClientRect();
134
+ const tipRect = tooltipRef.current?.getBoundingClientRect();
135
+ const tipWidth = tipRect?.width ?? 360;
136
+ const tipHeight = tipRect?.height ?? 520;
137
+ const margin = 8;
138
+ const viewportW = window.innerWidth;
139
+ const viewportH = window.innerHeight;
140
+ const placeBelow = viewportH - rect.bottom >= rect.top;
141
+ let top = placeBelow
142
+ ? rect.bottom + margin
143
+ : rect.top - tipHeight - margin;
144
+ top = Math.max(margin, Math.min(top, viewportH - tipHeight - margin));
145
+ const placeRight = viewportW - rect.right >= rect.left;
146
+ let left = placeRight ? rect.left : rect.right - tipWidth;
147
+ left = Math.max(margin, Math.min(left, viewportW - tipWidth - margin));
148
+ setTooltipStyle({
149
+ position: "fixed",
150
+ top: `${top}px`,
151
+ left: `${left}px`,
152
+ });
186
153
  };
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);
154
+ const raf1 = requestAnimationFrame(update);
155
+ const raf2 = requestAnimationFrame(update);
156
+ window.addEventListener("resize", update);
157
+ window.addEventListener("scroll", update, true);
192
158
  return () => {
193
- cancelAnimationFrame(rafId);
194
- cancelAnimationFrame(rafId2);
195
- window.removeEventListener("resize", handleResize);
196
- window.removeEventListener("scroll", handleResize, true);
159
+ cancelAnimationFrame(raf1);
160
+ cancelAnimationFrame(raf2);
161
+ window.removeEventListener("resize", update);
162
+ window.removeEventListener("scroll", update, true);
197
163
  };
198
164
  }, [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
165
  useLayoutEffect(() => {
220
- if (!showTooltip || requiresApiKeySelection || lbStatus !== "ready") {
166
+ if (!showTooltip || lbStatus !== "ready" || requiresApiKeySelection) {
221
167
  return;
222
168
  }
223
169
  if (!lbBasicStatus && !lbIsLoadingStatus && lbRefreshBasicStatus) {
@@ -230,8 +176,8 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
230
176
  }
231
177
  }, [
232
178
  showTooltip,
233
- requiresApiKeySelection,
234
179
  lbStatus,
180
+ requiresApiKeySelection,
235
181
  lbBasicStatus,
236
182
  lbStorageStatus,
237
183
  lbIsLoadingStatus,
@@ -239,493 +185,92 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
239
185
  lbRefreshBasicStatus,
240
186
  lbRefreshStorageStatus,
241
187
  ]);
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)] }));
188
+ const balance = (effectiveStatus?.balance || {});
189
+ const storage = (effectiveStatus?.storage || {});
190
+ const balanceUsed = num(balance.used);
191
+ const balanceRemaining = num(balance.remaining);
192
+ const rawBalanceTotal = balance.total ?? num(balance.purchased) + num(balance.quota);
193
+ const balanceTotal = rawBalanceTotal > 0 ? rawBalanceTotal : balanceUsed + balanceRemaining;
194
+ const balancePct = balance.percentage ??
195
+ (balanceTotal > 0 ? Math.round((balanceUsed / balanceTotal) * 100) : 0);
196
+ const storageUsed = storage.used_mb ?? storage.total_mb ?? 0;
197
+ const storageTotal = storage.allocated_mb ?? storage.total_mb ?? 0;
198
+ const storagePct = storage.percentage ??
199
+ (storageTotal > 0 ? Math.round((storageUsed / storageTotal) * 100) : 0);
200
+ const showFastSkeleton = lbStatus === "ready" &&
201
+ lbIsLoadingStatus &&
202
+ !lbBasicStatus &&
203
+ !effectiveStatus;
204
+ const showCornerLoading = lbStatus === "ready" &&
205
+ (showFastSkeleton || lbIsLoadingStatus || lbIsLoadingStorage);
206
+ const triggerTone = useMemo(() => {
207
+ if (requiresApiKeySelection)
208
+ return "warning";
209
+ if (lbStatus && lbStatus !== "ready")
210
+ return "danger";
211
+ if (effectiveStatus)
212
+ return "success";
213
+ return "neutral";
214
+ }, [requiresApiKeySelection, lbStatus, effectiveStatus]);
215
+ const openTooltip = () => {
216
+ if (requiresApiKeySelection) {
217
+ return;
299
218
  }
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 ||
219
+ setShowTooltip(true);
220
+ };
221
+ const closeTooltip = () => {
222
+ if (requiresApiKeySelection) {
223
+ return;
224
+ }
225
+ setTimeout(() => {
226
+ if (!tooltipRef.current?.matches(":hover") &&
227
+ !buttonRef.current?.matches(":hover")) {
228
+ setShowTooltip(false);
229
+ }
230
+ }, 100);
231
+ };
232
+ const renderTriggerIcon = () => {
233
+ if (loading || isSelectingApiKey) {
234
+ return _jsx(Loader2, { size: 14, className: "ai-spinner" });
235
+ }
236
+ return _jsx(Shield, { size: 14 });
237
+ };
238
+ const triggerClass = [
239
+ "ai-status-trigger",
240
+ `ai-size-${size}`,
241
+ `ai-radius-${radius}`,
242
+ triggerTone === "warning" ? "ai-status-trigger--warning" : "",
243
+ triggerTone === "danger" ? "ai-status-trigger--danger" : "",
244
+ triggerTone === "success" ? "ai-status-trigger--success" : "",
245
+ className,
246
+ ]
247
+ .filter(Boolean)
248
+ .join(" ");
249
+ const tooltipNode = showTooltip && canPortal
250
+ ? 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
251
  effectiveStatus?.api_key?.name ||
548
- "Unknown") }), switchApiKey && (_jsx("button", { onClick: (e) => {
252
+ "Unknown" })), switchApiKey ? (_jsx("button", { type: "button", className: "ai-icon-btn", onClick: (e) => {
549
253
  e.stopPropagation();
550
254
  setShowTooltip(false);
551
255
  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
256
+ }, 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 ||
257
+ effectiveStatus?.api_key?.env ||
258
+ "N/A" }))] }), _jsxs("div", { className: "ai-popover-row", children: [_jsx("span", { className: "ai-popover-label", children: "Rate Limit" }), lbIsLoadingStatus &&
259
+ !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 ||
260
+ effectiveStatus?.api_key?.rate_limit_rpm ||
261
+ 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
262
  ? "..."
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 () => {
263
+ : (effectiveStatus &&
264
+ "authType" in effectiveStatus &&
265
+ effectiveStatus.authType) ||
266
+ lbStatus ||
267
+ "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) => {
268
+ const Icon = item.icon;
269
+ 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));
270
+ }), logout && !isApiKeyAuthMode ? (_jsx("button", { type: "button", className: "ai-status-action-btn ai-status-action-btn--danger", onClick: async () => {
725
271
  try {
726
272
  await logout();
727
273
  setShowTooltip(false);
728
- // Refresh provider data after logout
729
274
  if (refetchProviders) {
730
275
  await refetchProviders();
731
276
  }
@@ -733,39 +278,30 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
733
278
  catch (error) {
734
279
  console.error("Logout failed:", error);
735
280
  }
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) => {
281
+ }, 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: () => {
282
+ setShowSigninModal(true);
283
+ setShowTooltip(false);
284
+ }, children: "Se connecter" })] })) }), document.body)
285
+ : null;
286
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative inline-block", children: [_jsx("button", { ref: buttonRef, className: triggerClass, onMouseEnter: openTooltip, onMouseLeave: closeTooltip, onClick: () => {
287
+ if (requiresApiKeySelection) {
288
+ setShowApiKeySelector(true);
289
+ return;
290
+ }
291
+ if (!effectiveStatus && lbStatus !== "ready") {
292
+ setShowSigninModal(true);
293
+ }
294
+ }, disabled: loading || isSelectingApiKey, title: requiresApiKeySelection
295
+ ? "Sélectionnez une clé API"
296
+ : "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
297
  setIsSelectingApiKey(true);
757
298
  try {
758
- // Utiliser la nouvelle fonction switchApiKey qui gère automatiquement le contexte
759
- if (switchApiKey) {
760
- await switchApiKey(keyId);
761
- }
762
- else {
299
+ if (!switchApiKey) {
763
300
  throw new Error("Switch API key function not available");
764
301
  }
302
+ await switchApiKey(keyId);
765
303
  setShowApiKeySelector(false);
766
304
  setShowTooltip(false);
767
- setIsLoadingStatus(true);
768
- // Refresh provider data after API key selection
769
305
  if (refetchProviders) {
770
306
  await refetchProviders();
771
307
  }
@@ -775,7 +311,6 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
775
311
  }
776
312
  finally {
777
313
  setIsSelectingApiKey(false);
778
- setIsLoadingStatus(false);
779
314
  }
780
- }, onCancel: () => setShowApiKeySelector(false) }))] }));
315
+ }, onCancel: () => setShowApiKeySelector(false) })) : null, _jsx(LBSigninModal, { isOpen: showSigninModal, onClose: () => setShowSigninModal(false) })] }));
781
316
  }