@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.
Files changed (83) hide show
  1. package/dist/components/AiChipLabel.d.ts.map +1 -1
  2. package/dist/components/AiChipLabel.js +10 -7
  3. package/dist/components/AiContextButton.d.ts +1 -1
  4. package/dist/components/AiContextButton.d.ts.map +1 -1
  5. package/dist/components/AiContextButton.js +25 -12
  6. package/dist/components/AiImageButton.d.ts.map +1 -1
  7. package/dist/components/AiImageButton.js +32 -16
  8. package/dist/components/AiInput.d.ts.map +1 -1
  9. package/dist/components/AiInput.js +15 -5
  10. package/dist/components/AiModelSelect.d.ts.map +1 -1
  11. package/dist/components/AiModelSelect.js +3 -1
  12. package/dist/components/AiPromptPanel.d.ts.map +1 -1
  13. package/dist/components/AiPromptPanel.js +72 -47
  14. package/dist/components/AiSelect.d.ts.map +1 -1
  15. package/dist/components/AiSelect.js +8 -3
  16. package/dist/components/AiStatusButton.d.ts.map +1 -1
  17. package/dist/components/AiStatusButton.js +55 -28
  18. package/dist/components/AiTextarea.d.ts.map +1 -1
  19. package/dist/components/AiTextarea.js +19 -6
  20. package/dist/components/ErrorToast.d.ts.map +1 -1
  21. package/dist/components/ErrorToast.js +4 -2
  22. package/dist/components/LBApiKeySelector.d.ts.map +1 -1
  23. package/dist/components/LBApiKeySelector.js +13 -5
  24. package/dist/components/LBConnectButton.d.ts.map +1 -1
  25. package/dist/components/LBConnectButton.js +8 -3
  26. package/dist/components/LBKeyPicker.d.ts.map +1 -1
  27. package/dist/components/LBKeyPicker.js +8 -4
  28. package/dist/components/LBSigninModal.d.ts.map +1 -1
  29. package/dist/components/LBSigninModal.js +13 -7
  30. package/dist/components/UsageToast.d.ts.map +1 -1
  31. package/dist/components/UsageToast.js +4 -2
  32. package/dist/context/I18nContext.d.ts +15 -0
  33. package/dist/context/I18nContext.d.ts.map +1 -0
  34. package/dist/context/I18nContext.js +44 -0
  35. package/dist/context/LBAuthProvider.d.ts +4 -1
  36. package/dist/context/LBAuthProvider.d.ts.map +1 -1
  37. package/dist/context/LBAuthProvider.js +3 -2
  38. package/dist/hooks/useAiCallImage.d.ts.map +1 -1
  39. package/dist/hooks/useAiCallImage.js +1 -107
  40. package/dist/hooks/useAiCallText.d.ts.map +1 -1
  41. package/dist/hooks/useAiCallText.js +1 -25
  42. package/dist/hooks/useLoadingTimer.d.ts +5 -0
  43. package/dist/hooks/useLoadingTimer.d.ts.map +1 -0
  44. package/dist/hooks/useLoadingTimer.js +27 -0
  45. package/dist/i18n/de.json +62 -0
  46. package/dist/i18n/en.json +128 -0
  47. package/dist/i18n/es.json +70 -0
  48. package/dist/i18n/fr.json +128 -0
  49. package/dist/i18n/it.json +62 -0
  50. package/dist/i18n/pt.json +62 -0
  51. package/dist/index.d.ts +2 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +2 -0
  54. package/dist/styles.css +142 -1
  55. package/package.json +3 -3
  56. package/src/components/AiChipLabel.tsx +17 -8
  57. package/src/components/AiContextButton.tsx +44 -20
  58. package/src/components/AiImageButton.tsx +52 -25
  59. package/src/components/AiInput.tsx +20 -5
  60. package/src/components/AiModelSelect.tsx +3 -1
  61. package/src/components/AiPromptPanel.tsx +177 -59
  62. package/src/components/AiSelect.tsx +8 -3
  63. package/src/components/AiStatusButton.tsx +100 -57
  64. package/src/components/AiTextarea.tsx +24 -6
  65. package/src/components/ErrorToast.tsx +4 -2
  66. package/src/components/LBApiKeySelector.tsx +33 -13
  67. package/src/components/LBConnectButton.tsx +9 -3
  68. package/src/components/LBKeyPicker.tsx +10 -4
  69. package/src/components/LBSigninModal.tsx +31 -15
  70. package/src/components/UsageToast.tsx +4 -2
  71. package/src/context/I18nContext.tsx +71 -0
  72. package/src/context/LBAuthProvider.tsx +9 -1
  73. package/src/hooks/useAiCallImage.ts +1 -149
  74. package/src/hooks/useAiCallText.ts +1 -30
  75. package/src/hooks/useLoadingTimer.ts +32 -0
  76. package/src/i18n/de.json +62 -0
  77. package/src/i18n/en.json +128 -0
  78. package/src/i18n/es.json +70 -0
  79. package/src/i18n/fr.json +128 -0
  80. package/src/i18n/it.json +62 -0
  81. package/src/i18n/pt.json +62 -0
  82. package/src/index.ts +2 -0
  83. 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 sauvegardée" });
125
+ onToast?.({ type: "success", message: t("ai.image.savedSuccess", "Image saved") });
122
126
  } catch (_error) {
123
- onToast?.({ type: "error", message: "Erreur lors de la sauvegarde" });
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: result.requestId,
157
- tokens: result.debitTokens,
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: result.requestId,
164
- tokens: result.debitTokens,
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: result.requestId,
171
- debitTokens: result.debitTokens,
182
+ requestId: safeRequestId,
183
+ debitTokens: safeTokens,
172
184
  usage: {
173
- total_tokens: result.debitTokens,
174
- prompt_tokens: Math.floor(result.debitTokens * 0.8),
175
- completion_tokens: Math.floor(result.debitTokens * 0.2),
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 : result.debitTokens * 0.002, // Coût simulé
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={!isAuthReady ? "Authentication required" : "Générer une image"}
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
- <Loader2 size={18} className="ai-spinner" />
206
- <span className="ai-text-microtracking">Génération...</span>
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 || <span>Connexion requise</span>}
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 || "Générer une image"}
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="Télécharger l'image"
301
+ title={t("ai.image.download", "Download image")}
275
302
  >
276
303
  <Download size={16} />
277
- Télécharger l'image
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="Sauvegarder en base"
311
+ title={t("ai.image.saveToDb", "Save to database")}
285
312
  >
286
313
  <ExternalLink size={14} />
287
- Sauvegarder
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?.({ type: "success", message: "AI generation successful" });
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?.({ type: "success", message: "AI generation successful" });
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}