@lastbrain/ai-ui-react 1.0.73 → 1.0.75

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 +26 -12
  6. package/dist/components/AiImageButton.d.ts.map +1 -1
  7. package/dist/components/AiImageButton.js +35 -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 +23 -20
  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 +6 -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 +31 -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 +141 -1
  55. package/package.json +3 -3
  56. package/src/components/AiChipLabel.tsx +14 -8
  57. package/src/components/AiContextButton.tsx +53 -20
  58. package/src/components/AiImageButton.tsx +58 -25
  59. package/src/components/AiInput.tsx +20 -5
  60. package/src/components/AiModelSelect.tsx +5 -1
  61. package/src/components/AiPromptPanel.tsx +203 -76
  62. package/src/components/AiSelect.tsx +8 -3
  63. package/src/components/AiStatusButton.tsx +75 -46
  64. package/src/components/AiTextarea.tsx +24 -6
  65. package/src/components/ErrorToast.tsx +4 -2
  66. package/src/components/LBApiKeySelector.tsx +29 -9
  67. package/src/components/LBConnectButton.tsx +7 -3
  68. package/src/components/LBKeyPicker.tsx +10 -4
  69. package/src/components/LBSigninModal.tsx +33 -15
  70. package/src/components/UsageToast.tsx +4 -2
  71. package/src/context/I18nContext.tsx +75 -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 +38 -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 +141 -1
@@ -6,6 +6,7 @@ import { createPortal } from "react-dom";
6
6
  import { AlertCircle, Loader2, Lock, Mail, Sparkles, X } from "lucide-react";
7
7
  import { useLB } from "../context/LBAuthProvider";
8
8
  import { LBApiKeySelector } from "./LBApiKeySelector";
9
+ import { useI18n } from "../context/I18nContext";
9
10
 
10
11
  export interface LBSigninModalProps {
11
12
  isOpen: boolean;
@@ -14,6 +15,7 @@ export interface LBSigninModalProps {
14
15
 
15
16
  export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
16
17
  const lbContext = useLB();
18
+ const { t } = useI18n();
17
19
  const [portalRoot, setPortalRoot] = useState<HTMLElement | null>(null);
18
20
 
19
21
  const [email, setEmail] = useState("");
@@ -27,7 +29,10 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
27
29
 
28
30
  const canRender = Boolean(isOpen && lbContext && login);
29
31
 
30
- const panelTitle = useMemo(() => "Connexion LastBrain", []);
32
+ const panelTitle = useMemo(
33
+ () => t("auth.modal.title", "LastBrain Sign In"),
34
+ [t]
35
+ );
31
36
 
32
37
  useEffect(() => {
33
38
  setPortalRoot(document.body);
@@ -49,7 +54,7 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
49
54
  try {
50
55
  const result = await login(email, password);
51
56
  if (!result.success) {
52
- setError(result.error || "Échec de la connexion");
57
+ setError(result.error || t("auth.modal.loginFailed", "Login failed"));
53
58
  return;
54
59
  }
55
60
 
@@ -59,7 +64,7 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
59
64
  }
60
65
 
61
66
  if (!fetchApiKeys || !result.accessToken) {
62
- setError("Token d'accès non disponible");
67
+ setError(t("auth.modal.tokenMissing", "Access token unavailable"));
63
68
  return;
64
69
  }
65
70
 
@@ -69,11 +74,13 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
69
74
  setShowKeySelector(true);
70
75
  } catch (keyError) {
71
76
  console.error("Failed to fetch API keys:", keyError);
72
- setError("Erreur lors de la récupération des clés API");
77
+ setError(t("auth.modal.apiKeysFetchError", "Failed to fetch API keys"));
73
78
  }
74
79
  } catch (err) {
75
80
  setError(
76
- err instanceof Error ? err.message : "Une erreur s'est produite"
81
+ err instanceof Error
82
+ ? err.message
83
+ : t("auth.modal.genericError", "An error occurred")
77
84
  );
78
85
  } finally {
79
86
  setLoading(false);
@@ -91,7 +98,9 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
91
98
  onClose();
92
99
  } catch (err) {
93
100
  setError(
94
- err instanceof Error ? err.message : "Erreur lors de la sélection"
101
+ err instanceof Error
102
+ ? err.message
103
+ : t("auth.modal.selectionError", "Selection error")
95
104
  );
96
105
  setShowKeySelector(false);
97
106
  }
@@ -132,7 +141,7 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
132
141
  type="button"
133
142
  className="ai-icon-btn ai-signin-close"
134
143
  onClick={onClose}
135
- aria-label="Fermer"
144
+ aria-label={t("common.close", "Close")}
136
145
  >
137
146
  <X size={16} />
138
147
  </button>
@@ -145,7 +154,10 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
145
154
  </div>
146
155
  <h2 className="ai-signin-title">{panelTitle}</h2>
147
156
  <p className="ai-signin-subtitle">
148
- Connectez-vous pour activer les composants IA dans votre app.
157
+ {t(
158
+ "auth.modal.subtitle",
159
+ "Sign in to enable AI components in your app."
160
+ )}
149
161
  </p>
150
162
  </div>
151
163
 
@@ -157,7 +169,7 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
157
169
  className="ai-input-label ai-row"
158
170
  >
159
171
  <Mail size={14} className="ai-inline-icon" />
160
- Email
172
+ {t("auth.modal.email", "Email")}
161
173
  </label>
162
174
  <div className="ai-control-group ai-glow">
163
175
  <div className="ai-shell ai-size-md ai-radius-full">
@@ -170,7 +182,10 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
170
182
  required
171
183
  autoFocus
172
184
  autoComplete="email"
173
- placeholder="votre@email.com"
185
+ placeholder={t(
186
+ "auth.modal.emailPlaceholder",
187
+ "your@email.com"
188
+ )}
174
189
  />
175
190
  </div>
176
191
  </div>
@@ -182,7 +197,7 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
182
197
  className="ai-input-label ai-row"
183
198
  >
184
199
  <Lock size={14} className="ai-inline-icon" />
185
- Mot de passe
200
+ {t("auth.modal.password", "Password")}
186
201
  </label>
187
202
  <div className="ai-control-group ai-glow">
188
203
  <div className="ai-shell ai-size-md ai-radius-full">
@@ -194,7 +209,10 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
194
209
  onChange={(e) => setPassword(e.target.value)}
195
210
  required
196
211
  autoComplete="current-password"
197
- placeholder="••••••••"
212
+ placeholder={t(
213
+ "auth.modal.passwordPlaceholder",
214
+ "••••••••"
215
+ )}
198
216
  />
199
217
  </div>
200
218
  </div>
@@ -216,12 +234,12 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
216
234
  {loading ? (
217
235
  <>
218
236
  <Loader2 size={16} className="ai-spinner" />
219
- Connexion...
237
+ {t("auth.modal.connecting", "Signing in...")}
220
238
  </>
221
239
  ) : (
222
240
  <>
223
241
  <Sparkles size={16} />
224
- Se connecter
242
+ {t("auth.signIn", "Sign in")}
225
243
  </>
226
244
  )}
227
245
  </button>
@@ -232,7 +250,7 @@ export function LBSigninModal({ isOpen, onClose }: LBSigninModalProps) {
232
250
  rel="noopener noreferrer"
233
251
  className="ai-btn ai-btn--ghost"
234
252
  >
235
- Créer un compte
253
+ {t("auth.modal.createAccount", "Create account")}
236
254
  </a>
237
255
  </div>
238
256
  </form>
@@ -3,6 +3,7 @@
3
3
  import "../styles/register";
4
4
  import { useEffect, useRef, useState } from "react";
5
5
  import { X } from "lucide-react";
6
+ import { useI18n } from "../context/I18nContext";
6
7
 
7
8
  interface UsageToastProps {
8
9
  result: unknown;
@@ -15,6 +16,7 @@ export function UsageToast({
15
16
  position = "bottom-right",
16
17
  onComplete,
17
18
  }: UsageToastProps) {
19
+ const { t } = useI18n();
18
20
  const [isVisible, setIsVisible] = useState(false);
19
21
  const [isClosing, setIsClosing] = useState(false);
20
22
  const fadeTimeoutRef = useRef<number | null>(null);
@@ -83,7 +85,7 @@ export function UsageToast({
83
85
  // Remove trailing zeros
84
86
  formatted = parseFloat(formatted).toString();
85
87
 
86
- return `${formatted}$ used`;
88
+ return t("usage.toast.used", "{amount}$ used", { amount: formatted });
87
89
  };
88
90
 
89
91
  const message = extractUsageMessage(result);
@@ -154,7 +156,7 @@ export function UsageToast({
154
156
  onMouseLeave={(e) => {
155
157
  e.currentTarget.style.backgroundColor = "transparent";
156
158
  }}
157
- title="Close"
159
+ title={t("common.closeLabel", "Close")}
158
160
  >
159
161
  <X size={12} />
160
162
  </button>
@@ -0,0 +1,75 @@
1
+ "use client";
2
+
3
+ import { createContext, useContext, useMemo, type ReactNode } from "react";
4
+ import fr from "../i18n/fr.json";
5
+ import en from "../i18n/en.json";
6
+ import es from "../i18n/es.json";
7
+ import it from "../i18n/it.json";
8
+ import de from "../i18n/de.json";
9
+ import pt from "../i18n/pt.json";
10
+
11
+ export type LBSupportedLang = "fr" | "en" | "es" | "it" | "de" | "pt";
12
+
13
+ type Dictionary = Record<string, string>;
14
+
15
+ const dictionaries: Record<LBSupportedLang, Dictionary> = {
16
+ fr,
17
+ en,
18
+ es,
19
+ it,
20
+ de,
21
+ pt,
22
+ };
23
+
24
+ type TranslateParams = Record<string, string | number>;
25
+
26
+ interface I18nContextValue {
27
+ lang: LBSupportedLang;
28
+ t: (key: string, fallback?: string, params?: TranslateParams) => string;
29
+ }
30
+
31
+ const defaultLang: LBSupportedLang = "fr";
32
+
33
+ const I18nContext = createContext<I18nContextValue>({
34
+ lang: defaultLang,
35
+ t: (key, fallback, params) => {
36
+ const template = dictionaries[defaultLang][key] || fallback || key;
37
+ if (!params) return template;
38
+ return template.replace(/\{(\w+)\}/g, (_, p: string) =>
39
+ params[p] !== undefined ? String(params[p]) : `{${p}}`
40
+ );
41
+ },
42
+ });
43
+
44
+ export interface I18nProviderProps {
45
+ children: ReactNode;
46
+ lang?: LBSupportedLang;
47
+ }
48
+
49
+ export function I18nProvider({
50
+ children,
51
+ lang = defaultLang,
52
+ }: I18nProviderProps) {
53
+ const safeLang: LBSupportedLang = dictionaries[lang] ? lang : defaultLang;
54
+
55
+ const value = useMemo<I18nContextValue>(() => {
56
+ const dict = dictionaries[safeLang] || dictionaries[defaultLang];
57
+
58
+ const t = (key: string, fallback?: string, params?: TranslateParams) => {
59
+ const template =
60
+ dict[key] || dictionaries[defaultLang][key] || fallback || key;
61
+ if (!params) return template;
62
+ return template.replace(/\{(\w+)\}/g, (_, p: string) =>
63
+ params[p] !== undefined ? String(params[p]) : `{${p}}`
64
+ );
65
+ };
66
+
67
+ return { lang: safeLang, t };
68
+ }, [safeLang]);
69
+
70
+ return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
71
+ }
72
+
73
+ export function useI18n() {
74
+ return useContext(I18nContext);
75
+ }
@@ -22,6 +22,7 @@ import type {
22
22
  AiStatus,
23
23
  } from "@lastbrain/ai-ui-core";
24
24
  import { createLBClient } from "@lastbrain/ai-ui-core";
25
+ import { I18nProvider, type LBSupportedLang } from "./I18nContext";
25
26
 
26
27
  export interface ApiKeyUser {
27
28
  id?: string;
@@ -66,6 +67,8 @@ interface LBProviderProps {
66
67
  onStatusChange?: (status: LBAuthState["status"]) => void;
67
68
  /** Fonction appelée après signin/logout pour refresh les providers */
68
69
  onAuthChange?: () => void;
70
+ /** Langue UI globale des composants ai-ui-react */
71
+ lang?: LBSupportedLang;
69
72
  }
70
73
 
71
74
  interface LBContextValue extends LBAuthState {
@@ -124,6 +127,7 @@ export function LBProvider({
124
127
  proxyUrl = "/api/lastbrain",
125
128
  onStatusChange,
126
129
  onAuthChange,
130
+ lang = "fr",
127
131
  }: LBProviderProps) {
128
132
  const [state, setState] = useState<LBAuthState>({
129
133
  status: "loading",
@@ -643,7 +647,11 @@ export function LBProvider({
643
647
  isLoadingStorage,
644
648
  };
645
649
 
646
- return <LBContext.Provider value={value}>{children}</LBContext.Provider>;
650
+ return (
651
+ <I18nProvider lang={lang}>
652
+ <LBContext.Provider value={value}>{children}</LBContext.Provider>
653
+ </I18nProvider>
654
+ );
647
655
  }
648
656
 
649
657
  /**
@@ -4,129 +4,6 @@ import { useState, useCallback } from "react";
4
4
  import type { AiImageRequest, AiImageResponse } from "@lastbrain/ai-ui-core";
5
5
  import { useAiClient } from "./useAiClient";
6
6
 
7
- /**
8
- * Génère une image Canvas pour l'environnement de développement
9
- */
10
- async function generateDevImage(
11
- prompt: string,
12
- width: number,
13
- height: number
14
- ): Promise<string> {
15
- // Créer un canvas
16
- const canvas = document.createElement("canvas");
17
- const ctx = canvas.getContext("2d");
18
-
19
- if (!ctx) {
20
- throw new Error("Could not create canvas context");
21
- }
22
-
23
- canvas.width = width;
24
- canvas.height = height;
25
-
26
- // Générer des couleurs basées sur le prompt
27
- const promptHash = prompt
28
- .split("")
29
- .reduce((hash, char) => (hash * 31 + char.charCodeAt(0)) % 255, 0);
30
-
31
- // Couleurs de base dégradées
32
- const colors = [
33
- `hsl(${(promptHash * 7) % 360}, 70%, 60%)`,
34
- `hsl(${(promptHash * 11) % 360}, 60%, 70%)`,
35
- `hsl(${(promptHash * 13) % 360}, 50%, 80%)`,
36
- ];
37
-
38
- // Créer un dégradé radial
39
- const gradient = ctx.createRadialGradient(
40
- width / 2,
41
- height / 2,
42
- 0,
43
- width / 2,
44
- height / 2,
45
- Math.max(width, height) / 2
46
- );
47
-
48
- gradient.addColorStop(0, colors[0]);
49
- gradient.addColorStop(0.5, colors[1]);
50
- gradient.addColorStop(1, colors[2]);
51
-
52
- // Remplir le fond
53
- ctx.fillStyle = gradient;
54
- ctx.fillRect(0, 0, width, height);
55
-
56
- // Ajouter des formes géométriques basées sur le prompt
57
- const shapes = prompt.split(" ").slice(0, 5);
58
-
59
- shapes.forEach((word, index) => {
60
- const wordHash = word
61
- .split("")
62
- .reduce((hash, char) => (hash * 31 + char.charCodeAt(0)) % 1000, 0);
63
-
64
- ctx.save();
65
- ctx.globalAlpha = 0.3 + index * 0.1;
66
- ctx.fillStyle = `hsl(${(wordHash * 17) % 360}, 80%, 50%)`;
67
-
68
- // Formes différentes selon l'index
69
- const x = (wordHash % (width - 100)) + 50;
70
- const y = ((wordHash * 3) % (height - 100)) + 50;
71
- const size = 30 + (wordHash % 50);
72
-
73
- switch (index % 4) {
74
- case 0: // Cercle
75
- ctx.beginPath();
76
- ctx.arc(x, y, size, 0, Math.PI * 2);
77
- ctx.fill();
78
- break;
79
- case 1: // Carré
80
- ctx.fillRect(x - size / 2, y - size / 2, size, size);
81
- break;
82
- case 2: // Triangle
83
- ctx.beginPath();
84
- ctx.moveTo(x, y - size / 2);
85
- ctx.lineTo(x - size / 2, y + size / 2);
86
- ctx.lineTo(x + size / 2, y + size / 2);
87
- ctx.closePath();
88
- ctx.fill();
89
- break;
90
- case 3: // Losange
91
- ctx.save();
92
- ctx.translate(x, y);
93
- ctx.rotate((wordHash / 100) % (Math.PI * 2));
94
- ctx.fillRect(-size / 2, -size / 2, size, size);
95
- ctx.restore();
96
- break;
97
- }
98
- ctx.restore();
99
- });
100
-
101
- // Ajouter le texte du prompt en overlay
102
- ctx.save();
103
- ctx.fillStyle = "rgba(255, 255, 255, 0.9)";
104
- ctx.font = `bold ${Math.min(width, height) / 30}px system-ui, -apple-system, sans-serif`;
105
- ctx.textAlign = "center";
106
- ctx.textBaseline = "middle";
107
-
108
- // Fond semi-transparent pour le texte
109
- const textMetrics = ctx.measureText(prompt);
110
- const textWidth = textMetrics.width;
111
- const textHeight = parseInt(ctx.font);
112
-
113
- ctx.fillStyle = "rgba(0, 0, 0, 0.6)";
114
- ctx.fillRect(
115
- width / 2 - textWidth / 2 - 20,
116
- height / 2 - textHeight / 2 - 10,
117
- textWidth + 40,
118
- textHeight + 20
119
- );
120
-
121
- // Texte du prompt
122
- ctx.fillStyle = "white";
123
- ctx.fillText(prompt, width / 2, height / 2);
124
- ctx.restore();
125
-
126
- // Convertir en data URL
127
- return canvas.toDataURL("image/png", 0.8);
128
- }
129
-
130
7
  export interface UseAiCallImageOptions {
131
8
  baseUrl?: string;
132
9
  apiKeyId?: string;
@@ -150,31 +27,6 @@ export function useAiCallImage(
150
27
  setLoading(true);
151
28
  setError(null);
152
29
  try {
153
- // Vérifier si on est en mode dev (API key contient "dev")
154
- if (options?.apiKeyId?.includes("dev")) {
155
- // Simulation complète pour l'environnement dev
156
- await new Promise((resolve) => setTimeout(resolve, 2000)); // Délai réaliste pour simuler l'API
157
-
158
- // Parse size format "1024x1024" or default to "1024x1024"
159
- const sizeMatch = request.size?.match(/(\d+)x(\d+)/);
160
- const width = sizeMatch ? parseInt(sizeMatch[1]) : 1024;
161
- const height = sizeMatch ? parseInt(sizeMatch[2]) : 1024;
162
-
163
- // Créer une image générée avec Canvas
164
- const imageUrl = await generateDevImage(
165
- request.prompt,
166
- width,
167
- height
168
- );
169
-
170
- return {
171
- requestId: `dev-img-${Date.now()}`,
172
- url: imageUrl,
173
- debitTokens: 30, // Coût simulé
174
- };
175
- }
176
-
177
- // Mode production : appel API normal
178
30
  const result = await client.generateImage(request);
179
31
  return result;
180
32
  } catch (err) {
@@ -186,7 +38,7 @@ export function useAiCallImage(
186
38
  setLoading(false);
187
39
  }
188
40
  },
189
- [client, options?.apiKeyId]
41
+ [client]
190
42
  );
191
43
 
192
44
  return {
@@ -27,35 +27,6 @@ export function useAiCallText(
27
27
  setLoading(true);
28
28
  setError(null);
29
29
  try {
30
- // Vérifier si on est en mode dev (API key contient "dev")
31
- if (options?.apiKeyId?.includes("dev")) {
32
- // Simulation pour l'environnement dev
33
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Délai pour simuler l'API
34
-
35
- let simulatedResult = "";
36
-
37
- // Si le prompt contient des mots-clés pour les chips
38
- if (
39
- request.prompt.toLowerCase().includes("tags") ||
40
- request.prompt.toLowerCase().includes("chip") ||
41
- request.prompt.toLowerCase().includes("virgule")
42
- ) {
43
- simulatedResult =
44
- "react, typescript, javascript, frontend, backend, api, development, web, mobile, database";
45
- } else {
46
- // Lorem ipsum pour les autres cas
47
- simulatedResult =
48
- "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.";
49
- }
50
-
51
- return {
52
- requestId: `dev-${Date.now()}`,
53
- text: simulatedResult,
54
- debitTokens: 150,
55
- };
56
- }
57
-
58
- // Mode production : appel API normal
59
30
  const result = await client.generateText(request);
60
31
  return result;
61
32
  } catch (err) {
@@ -67,7 +38,7 @@ export function useAiCallText(
67
38
  setLoading(false);
68
39
  }
69
40
  },
70
- [client, options?.apiKeyId]
41
+ [client]
71
42
  );
72
43
 
73
44
  return {
@@ -0,0 +1,38 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+
5
+ function formatElapsed(seconds: number): string {
6
+ const mins = Math.floor(seconds / 60);
7
+ const secs = seconds % 60;
8
+ if (mins <= 0) return `${secs}s`;
9
+ return `${String(mins).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
10
+ }
11
+
12
+ export function useLoadingTimer(active: boolean) {
13
+ const [seconds, setSeconds] = useState(0);
14
+
15
+ useEffect(() => {
16
+ if (!active) {
17
+ const timeoutId = window.setTimeout(() => setSeconds(0), 0);
18
+ return () => window.clearTimeout(timeoutId);
19
+ }
20
+
21
+ const resetId = window.setTimeout(() => setSeconds(0), 0);
22
+ const intervalId = window.setInterval(() => {
23
+ setSeconds((prev) => prev + 1);
24
+ }, 1000);
25
+
26
+ return () => {
27
+ window.clearTimeout(resetId);
28
+ window.clearInterval(intervalId);
29
+ };
30
+ }, [active]);
31
+
32
+ const displaySeconds = active ? seconds : 0;
33
+
34
+ return {
35
+ seconds: displaySeconds,
36
+ formatted: formatElapsed(displaySeconds),
37
+ };
38
+ }
@@ -0,0 +1,62 @@
1
+ {
2
+ "common.close": "Schließen",
3
+ "common.cancel": "Abbrechen",
4
+ "common.loading": "Lädt...",
5
+ "common.search": "Suchen...",
6
+ "common.all": "Alle",
7
+ "common.unknown": "Unbekannt",
8
+ "common.error": "Ein Fehler ist aufgetreten",
9
+ "common.inactive": "Inaktiv",
10
+ "common.active": "Aktiv",
11
+ "common.save": "Speichern",
12
+ "common.download": "Herunterladen",
13
+ "common.continue": "Weiter",
14
+ "common.errorTitle": "Fehler",
15
+ "auth.signIn": "Anmelden",
16
+ "auth.signOut": "Abmelden",
17
+ "auth.required": "Authentifizierung erforderlich",
18
+ "auth.connectRequired": "Verbindung erforderlich",
19
+ "auth.modal.title": "LastBrain Anmeldung",
20
+ "auth.modal.subtitle": "Melde dich an, um KI-Komponenten in deiner App zu aktivieren.",
21
+ "auth.modal.email": "E-Mail",
22
+ "auth.modal.password": "Passwort",
23
+ "auth.modal.connecting": "Anmeldung...",
24
+ "auth.modal.createAccount": "Konto erstellen",
25
+ "status.title": "API-Status",
26
+ "status.view": "Status anzeigen",
27
+ "status.selectApiKey": "API-Schlüssel wählen",
28
+ "status.user": "Benutzer",
29
+ "status.apiKey": "API-Schlüssel",
30
+ "status.env": "Umgebung",
31
+ "status.rateLimit": "Rate Limit",
32
+ "status.auth": "Auth",
33
+ "status.wallet": "Wallet",
34
+ "status.storage": "Speicher",
35
+ "status.total": "Gesamt",
36
+ "status.changeApiKey": "API-Schlüssel wechseln",
37
+ "status.dashboard": "Dashboard",
38
+ "status.history": "Verlauf",
39
+ "status.settings": "Einstellungen",
40
+ "status.prompts": "Prompts",
41
+ "status.folders": "Ordner",
42
+ "status.logout": "Abmelden",
43
+ "prompt.modal.title": "KI Prompt Konfiguration",
44
+ "prompt.modal.aiModel": "KI-Modell",
45
+ "prompt.modal.manageModels": "Modelle verwalten",
46
+ "prompt.modal.loadingModels": "Modelle werden geladen...",
47
+ "prompt.modal.prompt": "Prompt",
48
+ "prompt.modal.browsePrompts": "Prompts durchsuchen",
49
+ "prompt.modal.cancel": "Abbrechen",
50
+ "prompt.modal.generate": "Mit KI generieren",
51
+ "prompt.modal.generating": "Generierung...",
52
+ "prompt.modal.transforming": "Transformation...",
53
+ "ai.setup": "KI einrichten",
54
+ "ai.generate": "Mit KI generieren",
55
+ "ai.loading.elapsed": "{seconds}",
56
+ "ai.generationSuccess": "KI-Generierung erfolgreich",
57
+ "ai.generationError": "Textgenerierung fehlgeschlagen",
58
+ "ai.image.generate": "Bild generieren",
59
+ "ai.image.generating": "Generierung...",
60
+ "ai.image.generatedSuccess": "Bild erfolgreich generiert",
61
+ "usage.toast.used": "{amount}$ verwendet"
62
+ }