@lastbrain/ai-ui-react 1.0.72 → 1.0.74
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/AiChipLabel.d.ts.map +1 -1
- package/dist/components/AiChipLabel.js +10 -7
- package/dist/components/AiContextButton.d.ts +1 -1
- package/dist/components/AiContextButton.d.ts.map +1 -1
- package/dist/components/AiContextButton.js +25 -12
- package/dist/components/AiImageButton.d.ts.map +1 -1
- package/dist/components/AiImageButton.js +32 -16
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +15 -5
- package/dist/components/AiModelSelect.d.ts.map +1 -1
- package/dist/components/AiModelSelect.js +3 -1
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +72 -47
- package/dist/components/AiSelect.d.ts.map +1 -1
- package/dist/components/AiSelect.js +8 -3
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +55 -28
- package/dist/components/AiTextarea.d.ts.map +1 -1
- package/dist/components/AiTextarea.js +19 -6
- package/dist/components/ErrorToast.d.ts.map +1 -1
- package/dist/components/ErrorToast.js +4 -2
- package/dist/components/LBApiKeySelector.d.ts.map +1 -1
- package/dist/components/LBApiKeySelector.js +13 -5
- package/dist/components/LBConnectButton.d.ts.map +1 -1
- package/dist/components/LBConnectButton.js +8 -3
- package/dist/components/LBKeyPicker.d.ts.map +1 -1
- package/dist/components/LBKeyPicker.js +8 -4
- package/dist/components/LBSigninModal.d.ts.map +1 -1
- package/dist/components/LBSigninModal.js +13 -7
- package/dist/components/UsageToast.d.ts.map +1 -1
- package/dist/components/UsageToast.js +4 -2
- package/dist/context/I18nContext.d.ts +15 -0
- package/dist/context/I18nContext.d.ts.map +1 -0
- package/dist/context/I18nContext.js +44 -0
- package/dist/context/LBAuthProvider.d.ts +4 -1
- package/dist/context/LBAuthProvider.d.ts.map +1 -1
- package/dist/context/LBAuthProvider.js +3 -2
- package/dist/hooks/useAiCallImage.d.ts.map +1 -1
- package/dist/hooks/useAiCallImage.js +1 -107
- package/dist/hooks/useAiCallText.d.ts.map +1 -1
- package/dist/hooks/useAiCallText.js +1 -25
- package/dist/hooks/useLoadingTimer.d.ts +5 -0
- package/dist/hooks/useLoadingTimer.d.ts.map +1 -0
- package/dist/hooks/useLoadingTimer.js +27 -0
- package/dist/i18n/de.json +62 -0
- package/dist/i18n/en.json +128 -0
- package/dist/i18n/es.json +70 -0
- package/dist/i18n/fr.json +128 -0
- package/dist/i18n/it.json +62 -0
- package/dist/i18n/pt.json +62 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/styles.css +142 -1
- package/package.json +3 -3
- package/src/components/AiChipLabel.tsx +17 -8
- package/src/components/AiContextButton.tsx +44 -20
- package/src/components/AiImageButton.tsx +52 -25
- package/src/components/AiInput.tsx +20 -5
- package/src/components/AiModelSelect.tsx +3 -1
- package/src/components/AiPromptPanel.tsx +177 -59
- package/src/components/AiSelect.tsx +8 -3
- package/src/components/AiStatusButton.tsx +100 -57
- package/src/components/AiTextarea.tsx +24 -6
- package/src/components/ErrorToast.tsx +4 -2
- package/src/components/LBApiKeySelector.tsx +33 -13
- package/src/components/LBConnectButton.tsx +9 -3
- package/src/components/LBKeyPicker.tsx +10 -4
- package/src/components/LBSigninModal.tsx +31 -15
- package/src/components/UsageToast.tsx +4 -2
- package/src/context/I18nContext.tsx +71 -0
- package/src/context/LBAuthProvider.tsx +9 -1
- package/src/hooks/useAiCallImage.ts +1 -149
- package/src/hooks/useAiCallText.ts +1 -30
- package/src/hooks/useLoadingTimer.ts +32 -0
- package/src/i18n/de.json +62 -0
- package/src/i18n/en.json +128 -0
- package/src/i18n/es.json +70 -0
- package/src/i18n/fr.json +128 -0
- package/src/i18n/it.json +62 -0
- package/src/i18n/pt.json +62 -0
- package/src/index.ts +2 -0
- package/src/styles.css +142 -1
|
@@ -20,6 +20,8 @@ import { useAiContext } from "../context/AiProvider";
|
|
|
20
20
|
import { handleAIError } from "../utils/errorHandler";
|
|
21
21
|
import { useLB } from "../context/LBAuthProvider";
|
|
22
22
|
import { LBSigninModal } from "./LBSigninModal";
|
|
23
|
+
import { useI18n } from "../context/I18nContext";
|
|
24
|
+
import { useLoadingTimer } from "../hooks/useLoadingTimer";
|
|
23
25
|
|
|
24
26
|
export interface AiImageButtonProps
|
|
25
27
|
extends
|
|
@@ -63,6 +65,7 @@ export function AiImageButton({
|
|
|
63
65
|
variant = "default",
|
|
64
66
|
...buttonProps
|
|
65
67
|
}: AiImageButtonProps) {
|
|
68
|
+
const { t } = useI18n();
|
|
66
69
|
const [isOpen, setIsOpen] = useState(false);
|
|
67
70
|
const [showAuthModal, setShowAuthModal] = useState(false);
|
|
68
71
|
const [generatedImage, setGeneratedImage] = useState<{
|
|
@@ -90,6 +93,7 @@ export function AiImageButton({
|
|
|
90
93
|
const apiKeyId = propApiKeyId ?? aiContext.apiKeyId;
|
|
91
94
|
|
|
92
95
|
const { generateImage, loading } = useAiCallImage({ baseUrl, apiKeyId });
|
|
96
|
+
const { formatted: loadingElapsed } = useLoadingTimer(loading);
|
|
93
97
|
|
|
94
98
|
const isAuthReady = lbStatus === "ready" || Boolean(process.env.LB_API_KEY);
|
|
95
99
|
|
|
@@ -118,9 +122,12 @@ export function AiImageButton({
|
|
|
118
122
|
if (!generatedImage || !onImageSave) return;
|
|
119
123
|
try {
|
|
120
124
|
await onImageSave(generatedImage.url);
|
|
121
|
-
onToast?.({ type: "success", message: "Image
|
|
125
|
+
onToast?.({ type: "success", message: t("ai.image.savedSuccess", "Image saved") });
|
|
122
126
|
} catch (_error) {
|
|
123
|
-
onToast?.({
|
|
127
|
+
onToast?.({
|
|
128
|
+
type: "error",
|
|
129
|
+
message: t("ai.image.saveError", "Error while saving"),
|
|
130
|
+
});
|
|
124
131
|
}
|
|
125
132
|
};
|
|
126
133
|
|
|
@@ -149,32 +156,37 @@ export function AiImageButton({
|
|
|
149
156
|
});
|
|
150
157
|
|
|
151
158
|
if (result.url) {
|
|
159
|
+
const safeRequestId = result.requestId || `img-${Date.now()}`;
|
|
160
|
+
const safeTokens = result.debitTokens || 0;
|
|
152
161
|
// Stocker l'image générée
|
|
153
162
|
const imageData = {
|
|
154
163
|
url: result.url,
|
|
155
164
|
prompt: selectedPrompt,
|
|
156
|
-
requestId:
|
|
157
|
-
tokens:
|
|
165
|
+
requestId: safeRequestId,
|
|
166
|
+
tokens: safeTokens,
|
|
158
167
|
};
|
|
159
168
|
setGeneratedImage(imageData);
|
|
160
169
|
console.log("[AiImageButton] Image data stored:", imageData);
|
|
161
170
|
|
|
162
171
|
onImage?.(result.url, {
|
|
163
|
-
requestId:
|
|
164
|
-
tokens:
|
|
172
|
+
requestId: safeRequestId,
|
|
173
|
+
tokens: safeTokens,
|
|
174
|
+
});
|
|
175
|
+
onToast?.({
|
|
176
|
+
type: "success",
|
|
177
|
+
message: t("ai.image.generatedSuccess", "Image generated successfully"),
|
|
165
178
|
});
|
|
166
|
-
onToast?.({ type: "success", message: "Image générée avec succès" });
|
|
167
179
|
|
|
168
180
|
// Afficher le toast de coût même en mode dev
|
|
169
181
|
showUsageToast({
|
|
170
|
-
requestId:
|
|
171
|
-
debitTokens:
|
|
182
|
+
requestId: safeRequestId,
|
|
183
|
+
debitTokens: safeTokens,
|
|
172
184
|
usage: {
|
|
173
|
-
total_tokens:
|
|
174
|
-
prompt_tokens: Math.floor(
|
|
175
|
-
completion_tokens: Math.floor(
|
|
185
|
+
total_tokens: safeTokens,
|
|
186
|
+
prompt_tokens: Math.floor(safeTokens * 0.8),
|
|
187
|
+
completion_tokens: Math.floor(safeTokens * 0.2),
|
|
176
188
|
},
|
|
177
|
-
cost: apiKeyId?.includes("dev") ? 0 :
|
|
189
|
+
cost: apiKeyId?.includes("dev") ? 0 : safeTokens * 0.002, // Coût simulé
|
|
178
190
|
});
|
|
179
191
|
}
|
|
180
192
|
} catch (error) {
|
|
@@ -198,25 +210,40 @@ export function AiImageButton({
|
|
|
198
210
|
className={`ai-btn ai-image-btn ${variantClass} ${sizeClass} ${radiusClass} ${className || ""}`}
|
|
199
211
|
style={buttonProps.style}
|
|
200
212
|
data-ai-image-button
|
|
201
|
-
title={
|
|
213
|
+
title={
|
|
214
|
+
!isAuthReady
|
|
215
|
+
? t("auth.required", "Authentication required")
|
|
216
|
+
: t("ai.image.generate", "Generate image")
|
|
217
|
+
}
|
|
202
218
|
>
|
|
203
219
|
{loading ? (
|
|
204
|
-
|
|
205
|
-
<
|
|
206
|
-
|
|
207
|
-
|
|
220
|
+
<span className="ai-loading-stack">
|
|
221
|
+
<span className="ai-loading-row">
|
|
222
|
+
<Loader2 size={18} className="ai-spinner" />
|
|
223
|
+
<span className="ai-text-microtracking">
|
|
224
|
+
{t("ai.image.generating", "Generating...")}
|
|
225
|
+
</span>
|
|
226
|
+
</span>
|
|
227
|
+
<span className="ai-loading-meta">
|
|
228
|
+
{t("ai.loading.elapsed", "{seconds}", {
|
|
229
|
+
seconds: loadingElapsed,
|
|
230
|
+
})}
|
|
231
|
+
</span>
|
|
232
|
+
</span>
|
|
208
233
|
) : !isAuthReady ? (
|
|
209
234
|
<>
|
|
210
235
|
<Lock size={18} />
|
|
211
236
|
|
|
212
|
-
{children ||
|
|
237
|
+
{children || (
|
|
238
|
+
<span>{t("auth.connectRequired", "Connection required")}</span>
|
|
239
|
+
)}
|
|
213
240
|
</>
|
|
214
241
|
) : (
|
|
215
242
|
<>
|
|
216
243
|
<ImageIcon size={18} />
|
|
217
244
|
|
|
218
245
|
<span className="ai-text-microtracking">
|
|
219
|
-
{children || "
|
|
246
|
+
{children || t("ai.image.generate", "Generate image")}
|
|
220
247
|
</span>
|
|
221
248
|
</>
|
|
222
249
|
)}
|
|
@@ -271,20 +298,20 @@ export function AiImageButton({
|
|
|
271
298
|
<button
|
|
272
299
|
onClick={handleDownload}
|
|
273
300
|
className="ai-btn ai-btn--primary"
|
|
274
|
-
title="
|
|
301
|
+
title={t("ai.image.download", "Download image")}
|
|
275
302
|
>
|
|
276
303
|
<Download size={16} />
|
|
277
|
-
|
|
304
|
+
{t("ai.image.download", "Download image")}
|
|
278
305
|
</button>
|
|
279
306
|
|
|
280
307
|
{onImageSave && (
|
|
281
308
|
<button
|
|
282
309
|
onClick={handleSave}
|
|
283
310
|
className="ai-btn"
|
|
284
|
-
title="
|
|
311
|
+
title={t("ai.image.saveToDb", "Save to database")}
|
|
285
312
|
>
|
|
286
313
|
<ExternalLink size={14} />
|
|
287
|
-
|
|
314
|
+
{t("common.save", "Save")}
|
|
288
315
|
</button>
|
|
289
316
|
)}
|
|
290
317
|
</div>
|
|
@@ -292,7 +319,7 @@ export function AiImageButton({
|
|
|
292
319
|
{/* Metadata */}
|
|
293
320
|
<div className="ai-image-meta">
|
|
294
321
|
<div className="flex justify-center">
|
|
295
|
-
<span>ID: {generatedImage.requestId.slice(-8)}</span>
|
|
322
|
+
<span>ID: {(generatedImage.requestId || "N/A").slice(-8)}</span>
|
|
296
323
|
</div>
|
|
297
324
|
</div>
|
|
298
325
|
</div>
|
|
@@ -12,6 +12,8 @@ import { handleAIError } from "../utils/errorHandler";
|
|
|
12
12
|
import { useLB } from "../context/LBAuthProvider";
|
|
13
13
|
import { LBSigninModal } from "./LBSigninModal";
|
|
14
14
|
import { useAiContext } from "../context/AiProvider";
|
|
15
|
+
import { useI18n } from "../context/I18nContext";
|
|
16
|
+
import { useLoadingTimer } from "../hooks/useLoadingTimer";
|
|
15
17
|
|
|
16
18
|
export interface AiInputProps
|
|
17
19
|
extends
|
|
@@ -42,6 +44,7 @@ export function AiInput({
|
|
|
42
44
|
className,
|
|
43
45
|
...inputProps
|
|
44
46
|
}: AiInputProps) {
|
|
47
|
+
const { t } = useI18n();
|
|
45
48
|
const [isOpen, setIsOpen] = useState(false);
|
|
46
49
|
const [showAuthModal, setShowAuthModal] = useState(false);
|
|
47
50
|
const [inputValue, setInputValue] = useState(
|
|
@@ -80,6 +83,7 @@ export function AiInput({
|
|
|
80
83
|
modelType: "text-or-language",
|
|
81
84
|
});
|
|
82
85
|
const { generateText, loading } = useAiCallText({ baseUrl, apiKeyId });
|
|
86
|
+
const { formatted: loadingElapsed } = useLoadingTimer(loading);
|
|
83
87
|
|
|
84
88
|
const hasConfiguration = Boolean(model && prompt);
|
|
85
89
|
const isAuthReady = lbStatus === "ready" || Boolean(process.env.LB_API_KEY);
|
|
@@ -125,7 +129,10 @@ export function AiInput({
|
|
|
125
129
|
inputRef.current.value = result.text;
|
|
126
130
|
}
|
|
127
131
|
onValue?.(result.text);
|
|
128
|
-
onToast?.({
|
|
132
|
+
onToast?.({
|
|
133
|
+
type: "success",
|
|
134
|
+
message: t("ai.generationSuccess", "AI generation successful"),
|
|
135
|
+
});
|
|
129
136
|
}
|
|
130
137
|
} catch (error) {
|
|
131
138
|
console.error("AiInput error:", error);
|
|
@@ -159,7 +166,10 @@ export function AiInput({
|
|
|
159
166
|
inputRef.current.value = result.text;
|
|
160
167
|
}
|
|
161
168
|
onValue?.(result.text);
|
|
162
|
-
onToast?.({
|
|
169
|
+
onToast?.({
|
|
170
|
+
type: "success",
|
|
171
|
+
message: t("ai.generationSuccess", "AI generation successful"),
|
|
172
|
+
});
|
|
163
173
|
}
|
|
164
174
|
} catch (error) {
|
|
165
175
|
console.error("AiInput handleQuickGenerate error:", error);
|
|
@@ -201,10 +211,10 @@ export function AiInput({
|
|
|
201
211
|
type="button"
|
|
202
212
|
title={
|
|
203
213
|
!isAuthReady
|
|
204
|
-
? "Authentication required"
|
|
214
|
+
? t("auth.required", "Authentication required")
|
|
205
215
|
: hasConfiguration
|
|
206
|
-
? "Generate with AI"
|
|
207
|
-
: "Setup AI"
|
|
216
|
+
? t("ai.generate", "Generate with AI")
|
|
217
|
+
: t("ai.setup", "Setup AI")
|
|
208
218
|
}
|
|
209
219
|
>
|
|
210
220
|
{loading ? (
|
|
@@ -216,6 +226,11 @@ export function AiInput({
|
|
|
216
226
|
)}
|
|
217
227
|
</button>
|
|
218
228
|
</div>
|
|
229
|
+
{loading ? (
|
|
230
|
+
<span className="ai-control-timer">
|
|
231
|
+
{t("ai.loading.elapsed", "{seconds}", { seconds: loadingElapsed })}
|
|
232
|
+
</span>
|
|
233
|
+
) : null}
|
|
219
234
|
{isOpen && (
|
|
220
235
|
<AiPromptPanel
|
|
221
236
|
isOpen={isOpen}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import "../styles/register";
|
|
4
4
|
import React from "react";
|
|
5
5
|
import type { ModelRef } from "@lastbrain/ai-ui-core";
|
|
6
|
+
import { useI18n } from "../context/I18nContext";
|
|
6
7
|
|
|
7
8
|
export interface AiModelSelectProps {
|
|
8
9
|
models: ModelRef[];
|
|
@@ -19,6 +20,7 @@ export function AiModelSelect({
|
|
|
19
20
|
className,
|
|
20
21
|
disabled,
|
|
21
22
|
}: AiModelSelectProps) {
|
|
23
|
+
const { t } = useI18n();
|
|
22
24
|
return (
|
|
23
25
|
<select
|
|
24
26
|
value={value}
|
|
@@ -27,7 +29,7 @@ export function AiModelSelect({
|
|
|
27
29
|
disabled={disabled}
|
|
28
30
|
data-ai-model-select
|
|
29
31
|
>
|
|
30
|
-
<option value="">Select a model</option>
|
|
32
|
+
<option value="">{t("ai.select.modelPlaceholder", "Select a model")}</option>
|
|
31
33
|
{models.map((model) => (
|
|
32
34
|
<option key={model.id} value={model.id}>
|
|
33
35
|
{model.name}
|