@lastbrain/ai-ui-react 1.0.62 → 1.0.64
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.
- package/dist/components/AiPromptPanel.js +19 -6
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +78 -11
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +129 -182
- package/dist/utils/modelManagement.d.ts.map +1 -1
- package/dist/utils/modelManagement.js +11 -5
- package/package.json +2 -2
- package/src/components/AiPromptPanel.tsx +25 -6
- package/src/components/AiStatusButton.tsx +122 -12
- package/src/context/LBAuthProvider.tsx +137 -209
- package/src/utils/modelManagement.ts +14 -5
|
@@ -93,10 +93,10 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
93
93
|
if (showAllModels) {
|
|
94
94
|
return categoryModels;
|
|
95
95
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
96
|
+
const enabledModels = categoryModels.filter((m) => effectiveUserModels.includes(m.id));
|
|
97
|
+
return enabledModels.length > 0 ? enabledModels : categoryModels;
|
|
99
98
|
};
|
|
99
|
+
const modelOptions = getFilteredModels();
|
|
100
100
|
// Fetch prompts when modal opens
|
|
101
101
|
useEffect(() => {
|
|
102
102
|
if (isOpen && (models.length > 0 || enableModelManagement)) {
|
|
@@ -117,7 +117,7 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
117
117
|
modelCategory,
|
|
118
118
|
]);
|
|
119
119
|
const handleSubmit = async () => {
|
|
120
|
-
const activeModelId = selectedModel ||
|
|
120
|
+
const activeModelId = selectedModel || modelOptions[0]?.id;
|
|
121
121
|
if (!activeModelId || !prompt.trim())
|
|
122
122
|
return;
|
|
123
123
|
setIsGenerating(true);
|
|
@@ -156,6 +156,19 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
156
156
|
}
|
|
157
157
|
};
|
|
158
158
|
}, []);
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
if (!isOpen) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (modelOptions.length === 0) {
|
|
164
|
+
setSelectedModel("");
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const hasSelected = modelOptions.some((model) => model.id === selectedModel);
|
|
168
|
+
if (!hasSelected) {
|
|
169
|
+
setSelectedModel(modelOptions[0].id);
|
|
170
|
+
}
|
|
171
|
+
}, [isOpen, modelOptions, selectedModel]);
|
|
159
172
|
const handleKeyDown = (e) => {
|
|
160
173
|
if (e.key === "Escape") {
|
|
161
174
|
handleClose();
|
|
@@ -186,7 +199,7 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
186
199
|
}, [prompt]);
|
|
187
200
|
if (!isOpen)
|
|
188
201
|
return null;
|
|
189
|
-
const activeModelId = selectedModel ||
|
|
202
|
+
const activeModelId = selectedModel || modelOptions[0]?.id || "";
|
|
190
203
|
const currentModelType = models.find((m) => m.id === activeModelId)?.type;
|
|
191
204
|
const filteredPrompts = prompts.filter((p) => {
|
|
192
205
|
const matchesType = currentModelType === "image" ? p.type === "image" : p.type !== "image";
|
|
@@ -337,7 +350,7 @@ function AiPromptPanelInternal({ isOpen, onClose, onSubmit, uiMode: _uiMode = "m
|
|
|
337
350
|
!effectiveUserModels.includes(m.id)).length] }))] }))] }), _jsxs("select", { id: "model-select", value: activeModelId, onChange: (e) => setSelectedModel(e.target.value), onFocus: () => setModelFocused(true), onBlur: () => setModelFocused(false), style: {
|
|
338
351
|
...aiStyles.select,
|
|
339
352
|
...(modelFocused && aiStyles.selectFocus),
|
|
340
|
-
}, children: [
|
|
353
|
+
}, children: [modelOptions.length === 0 && (_jsx("option", { value: "", children: "Loading models..." })), modelOptions.map((model) => {
|
|
341
354
|
const isActive = effectiveUserModels.includes(model.id);
|
|
342
355
|
return (_jsxs("option", { value: model.id, style: {
|
|
343
356
|
opacity: showAllModels && !isActive ? 0.6 : 1,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAmBtD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAe,EACf,SAAc,GACf,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"AiStatusButton.d.ts","sourceRoot":"","sources":["../../src/components/AiStatusButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAmBtD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAe,EACf,SAAc,GACf,EAAE,mBAAmB,2CAksCrB"}
|
|
@@ -22,6 +22,9 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
22
22
|
let lbStorageStatus = null;
|
|
23
23
|
let lbIsLoadingStatus = false;
|
|
24
24
|
let lbIsLoadingStorage = false;
|
|
25
|
+
let lbSelectedKey = null;
|
|
26
|
+
let lbRefreshBasicStatus;
|
|
27
|
+
let lbRefreshStorageStatus;
|
|
25
28
|
try {
|
|
26
29
|
const lbContext = useLB();
|
|
27
30
|
lbStatus = lbContext.status;
|
|
@@ -36,6 +39,9 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
36
39
|
lbStorageStatus = lbContext.storageStatus;
|
|
37
40
|
lbIsLoadingStatus = lbContext.isLoadingStatus || false;
|
|
38
41
|
lbIsLoadingStorage = lbContext.isLoadingStorage || false;
|
|
42
|
+
lbSelectedKey = lbContext.selectedKey || null;
|
|
43
|
+
lbRefreshBasicStatus = lbContext.refreshBasicStatus;
|
|
44
|
+
lbRefreshStorageStatus = lbContext.refreshStorageStatus;
|
|
39
45
|
}
|
|
40
46
|
catch {
|
|
41
47
|
// LBProvider n'est pas disponible, ignorer
|
|
@@ -43,13 +49,16 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
43
49
|
user = undefined;
|
|
44
50
|
logout = undefined;
|
|
45
51
|
}
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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;
|
|
53
62
|
// Récupérer refetchProviders depuis AiProvider si disponible
|
|
54
63
|
let refetchProviders;
|
|
55
64
|
try {
|
|
@@ -64,7 +73,6 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
64
73
|
const [showApiKeySelector, setShowApiKeySelector] = useState(false);
|
|
65
74
|
const [isSelectingApiKey, setIsSelectingApiKey] = useState(false);
|
|
66
75
|
const [isLoadingStatus, setIsLoadingStatus] = useState(false);
|
|
67
|
-
const formatNumber = (value) => typeof value === "number" ? value.toLocaleString() : "0";
|
|
68
76
|
const formatFixed = (value, digits) => typeof value === "number" ? value.toFixed(digits) : "0.00";
|
|
69
77
|
const formatStorage = (valueMb) => {
|
|
70
78
|
const mb = typeof valueMb === "number" ? valueMb : 0;
|
|
@@ -97,9 +105,11 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
97
105
|
};
|
|
98
106
|
const balanceUsage = effectiveStatus?.balance;
|
|
99
107
|
const storageUsage = effectiveStatus?.storage;
|
|
100
|
-
const
|
|
108
|
+
const balanceUsed = safeNumber(balanceUsage?.used);
|
|
109
|
+
const balanceRemaining = safeNumber(balanceUsage?.remaining);
|
|
110
|
+
const rawBalanceTotal = balanceUsage?.total ??
|
|
101
111
|
safeNumber(balanceUsage?.purchased) + safeNumber(balanceUsage?.quota);
|
|
102
|
-
const
|
|
112
|
+
const balanceTotal = rawBalanceTotal > 0 ? rawBalanceTotal : balanceUsed + balanceRemaining;
|
|
103
113
|
const balancePercentage = balanceUsage?.percentage ??
|
|
104
114
|
(balanceTotal > 0 ? Math.round((balanceUsed / balanceTotal) * 100) : 0);
|
|
105
115
|
const storageAllocated = storageUsage?.allocated_mb ?? storageUsage?.total_mb ?? 0;
|
|
@@ -114,6 +124,8 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
114
124
|
const buttonRef = useRef(null);
|
|
115
125
|
const tooltipRef = useRef(null);
|
|
116
126
|
const canPortal = typeof document !== "undefined";
|
|
127
|
+
const hasApiKeySelected = Boolean(effectiveStatus?.apiKey?.id || effectiveStatus?.api_key?.id || lbSelectedKey?.id);
|
|
128
|
+
const requiresApiKeySelection = lbStatus === "ready" && !hasApiKeySelected && apiKeys.length > 0;
|
|
117
129
|
useLayoutEffect(() => {
|
|
118
130
|
if (!showTooltip || !buttonRef.current) {
|
|
119
131
|
return;
|
|
@@ -178,10 +190,16 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
178
190
|
};
|
|
179
191
|
}, [showTooltip, canPortal]);
|
|
180
192
|
const handleMouseEnter = () => {
|
|
193
|
+
if (requiresApiKeySelection) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
181
196
|
setShowTooltip(true);
|
|
182
197
|
setIsHovered(true);
|
|
183
198
|
};
|
|
184
199
|
const handleMouseLeave = () => {
|
|
200
|
+
if (requiresApiKeySelection) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
185
203
|
// Keep tooltip visible if hovering over it
|
|
186
204
|
setTimeout(() => {
|
|
187
205
|
if (!tooltipRef.current?.matches(":hover") &&
|
|
@@ -191,12 +209,59 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
191
209
|
}
|
|
192
210
|
}, 100);
|
|
193
211
|
};
|
|
212
|
+
useLayoutEffect(() => {
|
|
213
|
+
if (!showTooltip || requiresApiKeySelection || lbStatus !== "ready") {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (!lbBasicStatus && !lbIsLoadingStatus && lbRefreshBasicStatus) {
|
|
217
|
+
lbRefreshBasicStatus().catch(() => undefined);
|
|
218
|
+
}
|
|
219
|
+
if (!lbStorageStatus?.storage &&
|
|
220
|
+
!lbIsLoadingStorage &&
|
|
221
|
+
lbRefreshStorageStatus) {
|
|
222
|
+
lbRefreshStorageStatus().catch(() => undefined);
|
|
223
|
+
}
|
|
224
|
+
}, [
|
|
225
|
+
showTooltip,
|
|
226
|
+
requiresApiKeySelection,
|
|
227
|
+
lbStatus,
|
|
228
|
+
lbBasicStatus,
|
|
229
|
+
lbStorageStatus,
|
|
230
|
+
lbIsLoadingStatus,
|
|
231
|
+
lbIsLoadingStorage,
|
|
232
|
+
lbRefreshBasicStatus,
|
|
233
|
+
lbRefreshStorageStatus,
|
|
234
|
+
]);
|
|
194
235
|
if (loading || isSelectingApiKey || isLoadingStatus || lbIsLoadingStatus) {
|
|
195
236
|
return (_jsx("button", { ref: buttonRef, style: {
|
|
196
237
|
...aiStyles.statusButton,
|
|
197
238
|
...aiStyles.statusButtonDisabled,
|
|
198
239
|
}, 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" }) }) }));
|
|
199
240
|
}
|
|
241
|
+
if (requiresApiKeySelection) {
|
|
242
|
+
return (_jsxs("div", { style: { position: "relative", display: "inline-block" }, children: [_jsx("button", { ref: buttonRef, style: {
|
|
243
|
+
...aiStyles.statusButton,
|
|
244
|
+
color: "#f59e0b",
|
|
245
|
+
...(isHovered && aiStyles.statusButtonHover),
|
|
246
|
+
}, 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" }) }) }), showApiKeySelector && apiKeys.length > 0 && (_jsx(LBApiKeySelector, { isOpen: showApiKeySelector, apiKeys: apiKeys, onSelect: async (keyId) => {
|
|
247
|
+
setIsSelectingApiKey(true);
|
|
248
|
+
try {
|
|
249
|
+
if (switchApiKey) {
|
|
250
|
+
await switchApiKey(keyId);
|
|
251
|
+
}
|
|
252
|
+
setShowApiKeySelector(false);
|
|
253
|
+
if (refetchProviders) {
|
|
254
|
+
await refetchProviders();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
console.error("Failed to select API key:", error);
|
|
259
|
+
}
|
|
260
|
+
finally {
|
|
261
|
+
setIsSelectingApiKey(false);
|
|
262
|
+
}
|
|
263
|
+
}, onCancel: () => setShowApiKeySelector(false) }))] }));
|
|
264
|
+
}
|
|
200
265
|
if (!effectiveStatus) {
|
|
201
266
|
// Si pas de statut API et pas de LBProvider, afficher message simple
|
|
202
267
|
if (!lbStatus && lbStatus !== "ready") {
|
|
@@ -434,7 +499,9 @@ export function AiStatusButton({ status, loading = false, className = "", }) {
|
|
|
434
499
|
effectiveStatus.api_key?.env ||
|
|
435
500
|
"N/A" })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Rate Limit:" }), _jsxs("span", { style: aiStyles.tooltipValue, children: [effectiveStatus.apiKey?.rate_limit_rpm ||
|
|
436
501
|
effectiveStatus.api_key?.rate_limit_rpm ||
|
|
437
|
-
0, " ", "req/min"] })] })
|
|
502
|
+
0, " ", "req/min"] })] }), _jsxs("div", { style: aiStyles.tooltipRow, children: [_jsx("span", { style: aiStyles.tooltipLabel, children: "Auth:" }), _jsx("span", { style: aiStyles.tooltipValue, children: lbIsLoadingStatus
|
|
503
|
+
? "..."
|
|
504
|
+
: 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:" }), _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: {
|
|
438
505
|
display: "flex",
|
|
439
506
|
alignItems: "center",
|
|
440
507
|
gap: "8px",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAIR,QAAQ,EACT,MAAM,uBAAuB,CAAC;AAG/B,UAAU,eAAe;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IACzD,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,UAAU,cAAe,SAAQ,WAAW;IAC1C,4BAA4B;IAC5B,KAAK,EAAE,CACL,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,8BAA8B;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,6CAA6C;IAC7C,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC3D,gEAAgE;IAChE,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,mDAAmD;IACnD,qBAAqB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,iEAAiE;IACjE,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,oCAAoC;IACpC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,2BAA2B;IAC3B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,SAAS,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,mEAAmE;IACnE,WAAW,EAAE,GAAG,CAAC;IACjB,uCAAuC;IACvC,aAAa,EAAE,GAAG,CAAC;IACnB,gDAAgD;IAChD,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,iEAAiE;IACjE,oBAAoB,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,kEAAkE;IAClE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,8DAA8D;IAC9D,eAAe,EAAE,OAAO,CAAC;IACzB,uDAAuD;IACvD,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAID,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,EAAE,QAA2B,EACpC,QAA2B,EAC3B,cAAc,EACd,YAAY,GACb,EAAE,eAAe,2CAufjB;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,cAAc,CAMtC"}
|