@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
@@ -11,7 +11,10 @@ import { handleAIError } from "../utils/errorHandler";
11
11
  import { useLB } from "../context/LBAuthProvider";
12
12
  import { LBSigninModal } from "./LBSigninModal";
13
13
  import { useAiContext } from "../context/AiProvider";
14
+ import { useI18n } from "../context/I18nContext";
15
+ import { useLoadingTimer } from "../hooks/useLoadingTimer";
14
16
  export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", size = "md", radius = "lg", context, model, prompt, editMode = false, enableModelManagement, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...textareaProps }) {
17
+ const { t } = useI18n();
15
18
  const [isOpen, setIsOpen] = useState(false);
16
19
  const [showAuthModal, setShowAuthModal] = useState(false);
17
20
  const [textareaValue, setTextareaValue] = useState(textareaProps.value?.toString() ||
@@ -48,6 +51,7 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
48
51
  modelType: "text-or-language",
49
52
  });
50
53
  const { generateText, loading } = useAiCallText({ baseUrl, apiKeyId });
54
+ const { formatted: loadingElapsed } = useLoadingTimer(loading);
51
55
  const hasConfiguration = Boolean(model && prompt);
52
56
  const isAuthReady = lbStatus === "ready" || Boolean(process.env.LB_API_KEY);
53
57
  const shouldShowSparkles = isAuthReady && !disabled;
@@ -82,12 +86,18 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
82
86
  textareaRef.current.value = result.text;
83
87
  }
84
88
  onValue?.(result.text);
85
- onToast?.({ type: "success", message: "AI generation successful" });
89
+ onToast?.({
90
+ type: "success",
91
+ message: t("ai.generationSuccess", "AI generation successful"),
92
+ });
86
93
  showUsageToast(result);
87
94
  }
88
95
  }
89
96
  catch (error) {
90
- onToast?.({ type: "error", message: "Failed to generate text" });
97
+ onToast?.({
98
+ type: "error",
99
+ message: t("ai.generationError", "Failed to generate text"),
100
+ });
91
101
  }
92
102
  finally {
93
103
  setIsOpen(false);
@@ -114,7 +124,10 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
114
124
  textareaRef.current.value = result.text;
115
125
  }
116
126
  onValue?.(result.text);
117
- onToast?.({ type: "success", message: "AI generation successful" });
127
+ onToast?.({
128
+ type: "success",
129
+ message: t("ai.generationSuccess", "AI generation successful"),
130
+ });
118
131
  showUsageToast(result);
119
132
  }
120
133
  }
@@ -146,8 +159,8 @@ export function AiTextarea({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMod
146
159
  }, onBlur: (e) => {
147
160
  textareaProps.onBlur?.(e);
148
161
  }, "aria-invalid": Boolean(textareaProps["aria-invalid"]), disabled: disabled || loading }), _jsx("button", { className: `ai-control-action ai-spark ${sizeClass} ${radiusClass}`, onClick: hasConfiguration ? handleQuickGenerate : handleOpenPanel, disabled: disabled || loading || !isAuthReady, type: "button", title: !isAuthReady
149
- ? "Authentication required"
162
+ ? t("auth.required", "Authentication required")
150
163
  : hasConfiguration
151
- ? "Generate with AI"
152
- : "Setup AI", children: loading ? (_jsx(Loader2, { size: 16, className: "ai-spinner" })) : shouldShowSparkles ? (_jsx(Sparkles, { size: 16 })) : (_jsx(Lock, { size: 16 })) })] }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: [], modelCategory: "text", sourceText: textareaValue || undefined, baseUrl: baseUrl, apiKey: apiKeyId, enableModelManagement: enableModelManagement, showOnlyUserModels: true })), Boolean(toastData) && (_jsx(UsageToast, { result: toastData, position: "bottom-right", onComplete: clearToast }, toastKey)), _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) })] }));
164
+ ? t("ai.generate", "Generate with AI")
165
+ : t("ai.setup", "Setup AI"), children: loading ? (_jsx(Loader2, { size: 16, className: "ai-spinner" })) : shouldShowSparkles ? (_jsx(Sparkles, { size: 16 })) : (_jsx(Lock, { size: 16 })) })] }), loading ? (_jsx("span", { className: "ai-control-timer", children: t("ai.loading.elapsed", "{seconds}", { seconds: loadingElapsed }) })) : null, isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: [], modelCategory: "text", sourceText: textareaValue || undefined, baseUrl: baseUrl, apiKey: apiKeyId, enableModelManagement: enableModelManagement, showOnlyUserModels: true })), Boolean(toastData) && (_jsx(UsageToast, { result: toastData, position: "bottom-right", onComplete: clearToast }, toastKey)), _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) })] }));
153
166
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorToast.d.ts","sourceRoot":"","sources":["../../src/components/ErrorToast.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAI5B,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,eAAe;IACvB,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;IACrE,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAyB,EACzB,UAAU,GACX,EAAE,eAAe,kDA4IjB;AAED,wBAAgB,aAAa;4BAII,cAAc;;;;EAgB9C"}
1
+ {"version":3,"file":"ErrorToast.d.ts","sourceRoot":"","sources":["../../src/components/ErrorToast.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAK5B,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,eAAe;IACvB,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;IACrE,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAyB,EACzB,UAAU,GACX,EAAE,eAAe,kDA6IjB;AAED,wBAAgB,aAAa;4BAII,cAAc;;;;EAgB9C"}
@@ -3,7 +3,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import "../styles/register";
4
4
  import { useEffect, useRef, useState } from "react";
5
5
  import { X, AlertCircle } from "lucide-react";
6
+ import { useI18n } from "../context/I18nContext";
6
7
  export function ErrorToast({ error, position = "bottom-right", onComplete, }) {
8
+ const { t } = useI18n();
7
9
  const [isVisible, setIsVisible] = useState(false);
8
10
  const [isClosing, setIsClosing] = useState(false);
9
11
  const fadeTimeoutRef = useRef(null);
@@ -81,7 +83,7 @@ export function ErrorToast({ error, position = "bottom-right", onComplete, }) {
81
83
  WebkitBackdropFilter: "blur(8px)",
82
84
  maxWidth: "400px",
83
85
  minWidth: "280px",
84
- }, children: [_jsx(AlertCircle, { size: 16, style: { marginTop: "2px", flexShrink: 0 } }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsxs("div", { style: { fontWeight: 600, marginBottom: "2px" }, children: ["Erreur", error.code && (_jsxs("span", { style: {
86
+ }, children: [_jsx(AlertCircle, { size: 16, style: { marginTop: "2px", flexShrink: 0 } }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsxs("div", { style: { fontWeight: 600, marginBottom: "2px" }, children: [t("common.errorTitle", "Error"), error.code && (_jsxs("span", { style: {
85
87
  marginLeft: "8px",
86
88
  fontSize: "10px",
87
89
  opacity: 0.7,
@@ -102,7 +104,7 @@ export function ErrorToast({ error, position = "bottom-right", onComplete, }) {
102
104
  e.currentTarget.style.backgroundColor = "rgba(239, 68, 68, 0.2)";
103
105
  }, onMouseLeave: (e) => {
104
106
  e.currentTarget.style.backgroundColor = "transparent";
105
- }, title: "Fermer", children: _jsx(X, { size: 14 }) })] }));
107
+ }, title: t("common.closeLabel", "Close"), children: _jsx(X, { size: 14 }) })] }));
106
108
  }
107
109
  export function useErrorToast() {
108
110
  const [errorData, setErrorData] = useState(null);
@@ -1 +1 @@
1
- {"version":3,"file":"LBApiKeySelector.d.ts","sourceRoot":"","sources":["../../src/components/LBApiKeySelector.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,MAAM,GACP,EAAE,qBAAqB,kDAsIvB"}
1
+ {"version":3,"file":"LBApiKeySelector.d.ts","sourceRoot":"","sources":["../../src/components/LBApiKeySelector.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,MAAM,GACP,EAAE,qBAAqB,kDAuJvB"}
@@ -1,7 +1,10 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
3
  import { useState } from "react";
3
4
  import { CheckCircle2, KeyRound, Loader2, XCircle } from "lucide-react";
5
+ import { useI18n } from "../context/I18nContext";
4
6
  export function LBApiKeySelector({ apiKeys, onSelect, onCancel, isOpen, }) {
7
+ const { t } = useI18n();
5
8
  const [selectedKeyId, setSelectedKeyId] = useState(apiKeys.find((k) => k.isActive)?.id || apiKeys[0]?.id || "");
6
9
  const [loading, setLoading] = useState(false);
7
10
  const [error, setError] = useState("");
@@ -10,7 +13,7 @@ export function LBApiKeySelector({ apiKeys, onSelect, onCancel, isOpen, }) {
10
13
  const handleSubmit = async (e) => {
11
14
  e.preventDefault();
12
15
  if (!selectedKeyId) {
13
- setError("Veuillez sélectionner une clé API");
16
+ setError(t("status.selectApiKey", "Select an API key"));
14
17
  return;
15
18
  }
16
19
  setLoading(true);
@@ -19,13 +22,18 @@ export function LBApiKeySelector({ apiKeys, onSelect, onCancel, isOpen, }) {
19
22
  await onSelect(selectedKeyId);
20
23
  }
21
24
  catch (err) {
22
- setError(err instanceof Error ? err.message : "Erreur lors de la sélection");
25
+ setError(err instanceof Error
26
+ ? err.message
27
+ : t("auth.modal.selectionError", "Selection error"));
23
28
  setLoading(false);
24
29
  }
25
30
  };
26
- return (_jsx("div", { className: "ai-signin-overlay", onClick: onCancel, children: _jsxs("div", { className: "ai-signin-panel ai-key-modal-panel", onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { className: "ai-signin-header", children: [_jsx("div", { className: "ai-center mb-3", children: _jsx("span", { className: "ai-icon-badge", children: _jsx(KeyRound, { size: 20 }) }) }), _jsx("h2", { className: "ai-signin-title", children: "S\u00E9lectionnez une cl\u00E9 API" }), _jsx("p", { className: "ai-signin-subtitle", children: "Choisissez la cl\u00E9 API \u00E0 utiliser pour vos requ\u00EAtes IA." })] }), _jsx("div", { className: "ai-signin-content", children: _jsxs("form", { onSubmit: handleSubmit, children: [_jsx("div", { className: "ai-model-mgmt-list", style: { maxHeight: 300, overflowY: "auto", marginBottom: 16 }, children: apiKeys.map((key) => {
31
+ return (_jsx("div", { className: "ai-signin-overlay", onClick: onCancel, children: _jsxs("div", { className: "ai-signin-panel ai-key-modal-panel", onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { className: "ai-signin-header", children: [_jsx("div", { className: "ai-center mb-3", children: _jsx("span", { className: "ai-icon-badge", children: _jsx(KeyRound, { size: 20 }) }) }), _jsx("h2", { className: "ai-signin-title", children: t("status.selectApiKey", "Select an API key") }), _jsx("p", { className: "ai-signin-subtitle", children: t("status.selectApiKeySubtitle", "Choose the API key to use for your AI requests.") })] }), _jsx("div", { className: "ai-signin-content", children: _jsxs("form", { onSubmit: handleSubmit, children: [_jsx("div", { className: "ai-model-mgmt-list", style: { maxHeight: 300, overflowY: "auto", marginBottom: 16 }, children: apiKeys.map((key) => {
27
32
  const isSelected = key.id === selectedKeyId;
28
33
  const isActive = key.isActive;
29
- return (_jsxs("label", { className: `ai-model-item ${isSelected ? "ai-model-item--active" : ""} ${!isActive ? "ai-model-item--disabled" : ""}`, children: [_jsxs("div", { className: "ai-model-item-main", children: [_jsx("input", { type: "radio", name: "apiKey", value: key.id, checked: isSelected, disabled: !isActive, onChange: (e) => setSelectedKeyId(e.target.value), className: "ai-key-radio" }), _jsxs("div", { children: [_jsx("div", { className: "ai-model-item-title", children: key.name }), _jsx("div", { className: "ai-model-item-meta", children: _jsx("span", { children: key.keyPrefix || key.id.substring(0, 12) + "..." }) })] })] }), isActive ? (_jsxs("span", { className: "ai-pill ai-pill--cost", children: [_jsx(CheckCircle2, { size: 12 }), "Active"] })) : (_jsxs("span", { className: "ai-pill ai-pill--cost", children: [_jsx(XCircle, { size: 12 }), "Inactive"] }))] }, key.id));
30
- }) }), error ? (_jsxs("div", { className: "ai-signin-error", role: "alert", children: [_jsx(XCircle, { size: 16 }), _jsx("span", { children: error })] })) : null, _jsxs("div", { className: "ai-signin-actions", children: [_jsx("button", { type: "button", onClick: onCancel, disabled: loading, className: "ai-btn ai-btn--ghost", children: "Annuler" }), _jsx("button", { type: "submit", disabled: loading || !selectedKeyId, className: "ai-btn ai-btn--primary", children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 16, className: "ai-spinner" }), "Connexion..."] })) : ("Continuer") })] })] }) })] }) }));
34
+ const rawEnv = key.env;
35
+ const keyEnv = rawEnv === "dev" ? "DEV" : "PROD";
36
+ const isDev = rawEnv === "dev";
37
+ return (_jsxs("label", { className: `ai-model-item ${isSelected ? "ai-model-item--active" : ""} ${!isActive ? "ai-model-item--disabled" : ""}`, children: [_jsxs("div", { className: "ai-model-item-main", children: [_jsx("input", { type: "radio", name: "apiKey", value: key.id, checked: isSelected, disabled: !isActive, onChange: (e) => setSelectedKeyId(e.target.value), className: "ai-key-radio" }), _jsxs("div", { children: [_jsx("div", { className: "ai-model-item-title", children: key.name }), _jsxs("div", { className: "ai-model-item-meta", children: [_jsx("span", { children: key.keyPrefix || key.id.substring(0, 12) + "..." }), _jsx("span", { className: `ai-pill ai-pill--cost ${isDev ? "ai-pill--warning" : ""}`, style: { marginLeft: 8 }, children: keyEnv })] })] })] }), isActive ? (_jsxs("span", { className: "ai-pill ai-pill--cost", children: [_jsx(CheckCircle2, { size: 12 }), t("common.active", "Active")] })) : (_jsxs("span", { className: "ai-pill ai-pill--cost", children: [_jsx(XCircle, { size: 12 }), t("common.inactive", "Inactive")] }))] }, key.id));
38
+ }) }), error ? (_jsxs("div", { className: "ai-signin-error", role: "alert", children: [_jsx(XCircle, { size: 16 }), _jsx("span", { children: error })] })) : null, _jsxs("div", { className: "ai-signin-actions", children: [_jsx("button", { type: "button", onClick: onCancel, disabled: loading, className: "ai-btn ai-btn--ghost", children: t("common.cancel", "Cancel") }), _jsx("button", { type: "submit", disabled: loading || !selectedKeyId, className: "ai-btn ai-btn--primary", children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 16, className: "ai-spinner" }), t("auth.modal.connecting", "Signing in...")] })) : (t("common.continue", "Continue")) })] })] }) })] }) }));
31
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"LBConnectButton.d.ts","sourceRoot":"","sources":["../../src/components/LBConnectButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAM5B,UAAU,oBAAoB;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,wBAAgB,eAAe,CAAC,EAC9B,KAAsB,EACtB,SAAc,EACd,WAAW,EACX,WAAW,GACZ,EAAE,oBAAoB,2CAoDtB;AAED;;;GAGG;AACH,UAAU,gBAAgB;IACxB,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,EAAE,gBAAgB,2CAExD"}
1
+ {"version":3,"file":"LBConnectButton.d.ts","sourceRoot":"","sources":["../../src/components/LBConnectButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAO5B,UAAU,oBAAoB;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,SAAc,EACd,WAAW,EACX,WAAW,GACZ,EAAE,oBAAoB,2CAyDtB;AAED;;;GAGG;AACH,UAAU,gBAAgB;IACxB,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,EAAE,gBAAgB,2CAExD"}
@@ -5,7 +5,9 @@ import React from "react";
5
5
  import { Loader2, LogIn, LogOut } from "lucide-react";
6
6
  import { useLB } from "../context/LBAuthProvider";
7
7
  import { LBSigninModal } from "./LBSigninModal";
8
- export function LBConnectButton({ label = "Se connecter", className = "", onConnected, onOpenModal, }) {
8
+ import { useI18n } from "../context/I18nContext";
9
+ export function LBConnectButton({ label, className = "", onConnected, onOpenModal, }) {
10
+ const { t } = useI18n();
9
11
  const { status, user, logout } = useLB();
10
12
  const [showModal, setShowModal] = React.useState(false);
11
13
  React.useEffect(() => {
@@ -22,8 +24,11 @@ export function LBConnectButton({ label = "Se connecter", className = "", onConn
22
24
  setShowModal(true);
23
25
  onOpenModal?.();
24
26
  };
25
- const buttonLabel = status === "ready" && user ? "Déconnexion" : label;
26
- return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: handleClick, className: className || "ai-btn ai-btn--auth", disabled: status === "loading", children: status === "loading" ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 16, className: "animate-spin" }), "Chargement..."] })) : status === "ready" && user ? (_jsxs(_Fragment, { children: [_jsx(LogOut, { size: 16 }), buttonLabel] })) : (_jsxs(_Fragment, { children: [_jsx(LogIn, { size: 16 }), buttonLabel] })) }), _jsx(LBSigninModal, { isOpen: showModal, onClose: () => setShowModal(false) })] }));
27
+ const connectLabel = label || t("auth.signIn", "Sign in");
28
+ const buttonLabel = status === "ready" && user
29
+ ? t("auth.signOut", "Sign out")
30
+ : connectLabel;
31
+ return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: handleClick, className: className || "ai-btn ai-btn--auth", disabled: status === "loading", children: status === "loading" ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 16, className: "animate-spin" }), t("common.loading", "Loading...")] })) : status === "ready" && user ? (_jsxs(_Fragment, { children: [_jsx(LogOut, { size: 16 }), buttonLabel] })) : (_jsxs(_Fragment, { children: [_jsx(LogIn, { size: 16 }), buttonLabel] })) }), _jsx(LBSigninModal, { isOpen: showModal, onClose: () => setShowModal(false) })] }));
27
32
  }
28
33
  export function LBAuthModal({ onClose }) {
29
34
  return _jsx(LBSigninModal, { isOpen: true, onClose: () => onClose(false) });
@@ -1 +1 @@
1
- {"version":3,"file":"LBKeyPicker.d.ts","sourceRoot":"","sources":["../../src/components/LBKeyPicker.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAQ5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,UAAU,gBAAgB;IACxB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;CACxC;AAED,wBAAgB,WAAW,CAAC,EAC1B,SAAc,EACd,YAAY,GACb,EAAE,gBAAgB,kDA2HlB"}
1
+ {"version":3,"file":"LBKeyPicker.d.ts","sourceRoot":"","sources":["../../src/components/LBKeyPicker.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAQ5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,UAAU,gBAAgB;IACxB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;CACxC;AAED,wBAAgB,WAAW,CAAC,EAC1B,SAAc,EACd,YAAY,GACb,EAAE,gBAAgB,kDAgIlB"}
@@ -7,7 +7,9 @@ import "../styles/register";
7
7
  */
8
8
  import { useEffect, useState } from "react";
9
9
  import { useLB } from "../hooks/useLB";
10
+ import { useI18n } from "../context/I18nContext";
10
11
  export function LBKeyPicker({ className = "", onKeyChanged, }) {
12
+ const { t } = useI18n();
11
13
  const { status, selectedKey, fetchApiKeys, selectApiKey, accessToken } = useLB();
12
14
  const [apiKeys, setApiKeys] = useState([]);
13
15
  const [loading, setLoading] = useState(false);
@@ -27,7 +29,7 @@ export function LBKeyPicker({ className = "", onKeyChanged, }) {
27
29
  setApiKeys(keys);
28
30
  }
29
31
  catch (err) {
30
- setError("Impossible de charger les clés API");
32
+ setError(t("lb.keypicker.loadError", "Unable to load API keys"));
31
33
  }
32
34
  finally {
33
35
  setLoading(false);
@@ -47,7 +49,9 @@ export function LBKeyPicker({ className = "", onKeyChanged, }) {
47
49
  setShowDropdown(false);
48
50
  }
49
51
  catch (err) {
50
- setError(err instanceof Error ? err.message : "Échec du changement de clé");
52
+ setError(err instanceof Error
53
+ ? err.message
54
+ : t("lb.keypicker.switchError", "Failed to switch API key"));
51
55
  }
52
56
  finally {
53
57
  setLoading(false);
@@ -56,7 +60,7 @@ export function LBKeyPicker({ className = "", onKeyChanged, }) {
56
60
  if (status !== "ready" || !selectedKey) {
57
61
  return null;
58
62
  }
59
- return (_jsxs("div", { className: `relative ${className}`, children: [_jsxs("button", { onClick: () => setShowDropdown(!showDropdown), disabled: loading, className: "px-4 py-2 border rounded hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 flex items-center gap-2", children: [_jsxs("span", { className: "font-mono text-sm", children: [selectedKey.keyPrefix, "..."] }), _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), showDropdown && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-10", onClick: () => setShowDropdown(false) }), _jsxs("div", { className: "absolute right-0 mt-2 w-64 bg-white dark:bg-gray-800 border rounded-lg shadow-lg z-20", children: [_jsxs("div", { className: "p-2", children: [_jsx("div", { className: "text-sm font-medium text-gray-700 dark:text-gray-300 px-3 py-2", children: "Changer de cl\u00E9 API" }), _jsx("div", { className: "space-y-1", children: apiKeys.map((key) => (_jsxs("button", { onClick: () => handleSelectKey(key.id), disabled: !key.isActive || loading || key.id === selectedKey.id, className: `w-full text-left px-3 py-2 rounded text-sm ${key.id === selectedKey.id
63
+ return (_jsxs("div", { className: `relative ${className}`, children: [_jsxs("button", { onClick: () => setShowDropdown(!showDropdown), disabled: loading, className: "px-4 py-2 border rounded hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 flex items-center gap-2", children: [_jsxs("span", { className: "font-mono text-sm", children: [selectedKey.keyPrefix, "..."] }), _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), showDropdown && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-10", onClick: () => setShowDropdown(false) }), _jsxs("div", { className: "absolute right-0 mt-2 w-64 bg-white dark:bg-gray-800 border rounded-lg shadow-lg z-20", children: [_jsxs("div", { className: "p-2", children: [_jsx("div", { className: "text-sm font-medium text-gray-700 dark:text-gray-300 px-3 py-2", children: t("lb.keypicker.changeApiKey", "Switch API key") }), _jsx("div", { className: "space-y-1", children: apiKeys.map((key) => (_jsxs("button", { onClick: () => handleSelectKey(key.id), disabled: !key.isActive || loading || key.id === selectedKey.id, className: `w-full text-left px-3 py-2 rounded text-sm ${key.id === selectedKey.id
60
64
  ? "bg-blue-100 dark:bg-blue-900"
61
- : "hover:bg-gray-100 dark:hover:bg-gray-700"} disabled:opacity-50 disabled:cursor-not-allowed`, children: [_jsx("div", { className: "font-medium", children: key.name }), _jsxs("div", { className: "text-xs text-gray-500 font-mono", children: [key.keyPrefix, "..."] }), !key.isActive && (_jsx("div", { className: "text-xs text-red-600", children: "Inactive" }))] }, key.id))) })] }), error && (_jsx("div", { className: "px-3 py-2 text-xs text-red-600 border-t", children: error }))] })] }))] }));
65
+ : "hover:bg-gray-100 dark:hover:bg-gray-700"} disabled:opacity-50 disabled:cursor-not-allowed`, children: [_jsx("div", { className: "font-medium", children: key.name }), _jsxs("div", { className: "text-xs text-gray-500 font-mono", children: [key.keyPrefix, "..."] }), !key.isActive && (_jsx("div", { className: "text-xs text-red-600", children: t("common.inactive", "Inactive") }))] }, key.id))) })] }), error && (_jsx("div", { className: "px-3 py-2 text-xs text-red-600 border-t", children: error }))] })] }))] }));
62
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"LBSigninModal.d.ts","sourceRoot":"","sources":["../../src/components/LBSigninModal.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAO5B,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,sCAqOpE"}
1
+ {"version":3,"file":"LBSigninModal.d.ts","sourceRoot":"","sources":["../../src/components/LBSigninModal.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAQ5B,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,sCAoPpE"}
@@ -6,8 +6,10 @@ 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
  export function LBSigninModal({ isOpen, onClose }) {
10
11
  const lbContext = useLB();
12
+ const { t } = useI18n();
11
13
  const [portalRoot, setPortalRoot] = useState(null);
12
14
  const [email, setEmail] = useState("");
13
15
  const [password, setPassword] = useState("");
@@ -17,7 +19,7 @@ export function LBSigninModal({ isOpen, onClose }) {
17
19
  const [currentApiKeys, setCurrentApiKeys] = useState([]);
18
20
  const { login, selectApiKeyWithToken, fetchApiKeys } = lbContext || {};
19
21
  const canRender = Boolean(isOpen && lbContext && login);
20
- const panelTitle = useMemo(() => "Connexion LastBrain", []);
22
+ const panelTitle = useMemo(() => t("auth.modal.title", "LastBrain Sign In"), [t]);
21
23
  useEffect(() => {
22
24
  setPortalRoot(document.body);
23
25
  }, []);
@@ -34,7 +36,7 @@ export function LBSigninModal({ isOpen, onClose }) {
34
36
  try {
35
37
  const result = await login(email, password);
36
38
  if (!result.success) {
37
- setError(result.error || "Échec de la connexion");
39
+ setError(result.error || t("auth.modal.loginFailed", "Login failed"));
38
40
  return;
39
41
  }
40
42
  if (!result.needsKeySelection) {
@@ -42,7 +44,7 @@ export function LBSigninModal({ isOpen, onClose }) {
42
44
  return;
43
45
  }
44
46
  if (!fetchApiKeys || !result.accessToken) {
45
- setError("Token d'accès non disponible");
47
+ setError(t("auth.modal.tokenMissing", "Access token unavailable"));
46
48
  return;
47
49
  }
48
50
  try {
@@ -52,11 +54,13 @@ export function LBSigninModal({ isOpen, onClose }) {
52
54
  }
53
55
  catch (keyError) {
54
56
  console.error("Failed to fetch API keys:", keyError);
55
- setError("Erreur lors de la récupération des clés API");
57
+ setError(t("auth.modal.apiKeysFetchError", "Failed to fetch API keys"));
56
58
  }
57
59
  }
58
60
  catch (err) {
59
- setError(err instanceof Error ? err.message : "Une erreur s'est produite");
61
+ setError(err instanceof Error
62
+ ? err.message
63
+ : t("auth.modal.genericError", "An error occurred"));
60
64
  }
61
65
  finally {
62
66
  setLoading(false);
@@ -72,7 +76,9 @@ export function LBSigninModal({ isOpen, onClose }) {
72
76
  onClose();
73
77
  }
74
78
  catch (err) {
75
- setError(err instanceof Error ? err.message : "Erreur lors de la sélection");
79
+ setError(err instanceof Error
80
+ ? err.message
81
+ : t("auth.modal.selectionError", "Selection error"));
76
82
  setShowKeySelector(false);
77
83
  }
78
84
  };
@@ -87,5 +93,5 @@ export function LBSigninModal({ isOpen, onClose }) {
87
93
  if (e.key === "Escape") {
88
94
  onClose();
89
95
  }
90
- }, role: "dialog", "aria-modal": "true", "aria-label": panelTitle, children: _jsxs("div", { className: "ai-signin-panel", onClick: (e) => e.stopPropagation(), children: [_jsx("button", { type: "button", className: "ai-icon-btn ai-signin-close", onClick: onClose, "aria-label": "Fermer", children: _jsx(X, { size: 16 }) }), _jsxs("div", { className: "ai-signin-header", children: [_jsx("div", { className: "ai-center mb-3", children: _jsx("span", { className: "ai-icon-badge", children: _jsx(Sparkles, { size: 22 }) }) }), _jsx("h2", { className: "ai-signin-title", children: panelTitle }), _jsx("p", { className: "ai-signin-subtitle", children: "Connectez-vous pour activer les composants IA dans votre app." })] }), _jsx("div", { className: "ai-signin-content", children: _jsxs("form", { onSubmit: handleSubmit, children: [_jsxs("div", { className: "ai-input-row", children: [_jsxs("label", { htmlFor: "lb-signin-email", className: "ai-input-label ai-row", children: [_jsx(Mail, { size: 14, className: "ai-inline-icon" }), "Email"] }), _jsx("div", { className: "ai-control-group ai-glow", children: _jsx("div", { className: "ai-shell ai-size-md ai-radius-full", children: _jsx("input", { id: "lb-signin-email", className: "ai-control ai-control-input ai-size-md ai-radius-full", type: "email", value: email, onChange: (e) => setEmail(e.target.value), required: true, autoFocus: true, autoComplete: "email", placeholder: "votre@email.com" }) }) })] }), _jsxs("div", { className: "ai-input-row", children: [_jsxs("label", { htmlFor: "lb-signin-password", className: "ai-input-label ai-row", children: [_jsx(Lock, { size: 14, className: "ai-inline-icon" }), "Mot de passe"] }), _jsx("div", { className: "ai-control-group ai-glow", children: _jsx("div", { className: "ai-shell ai-size-md ai-radius-full", children: _jsx("input", { id: "lb-signin-password", className: "ai-control ai-control-input ai-size-md ai-radius-full", type: "password", value: password, onChange: (e) => setPassword(e.target.value), required: true, autoComplete: "current-password", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" }) }) })] }), error ? (_jsxs("div", { className: "ai-signin-error", role: "alert", children: [_jsx(AlertCircle, { size: 16 }), _jsx("span", { children: error })] })) : null, _jsxs("div", { className: "ai-signin-actions", children: [_jsx("button", { type: "submit", className: "ai-btn ai-btn--auth", disabled: loading, children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 16, className: "ai-spinner" }), "Connexion..."] })) : (_jsxs(_Fragment, { children: [_jsx(Sparkles, { size: 16 }), "Se connecter"] })) }), _jsx("a", { href: "https://prompt.lastbrain.io/signup", target: "_blank", rel: "noopener noreferrer", className: "ai-btn ai-btn--ghost", children: "Cr\u00E9er un compte" })] })] }) })] }) }), portalRoot);
96
+ }, role: "dialog", "aria-modal": "true", "aria-label": panelTitle, children: _jsxs("div", { className: "ai-signin-panel", onClick: (e) => e.stopPropagation(), children: [_jsx("button", { type: "button", className: "ai-icon-btn ai-signin-close", onClick: onClose, "aria-label": t("common.close", "Close"), children: _jsx(X, { size: 16 }) }), _jsxs("div", { className: "ai-signin-header", children: [_jsx("div", { className: "ai-center mb-3", children: _jsx("span", { className: "ai-icon-badge", children: _jsx(Sparkles, { size: 22 }) }) }), _jsx("h2", { className: "ai-signin-title", children: panelTitle }), _jsx("p", { className: "ai-signin-subtitle", children: t("auth.modal.subtitle", "Sign in to enable AI components in your app.") })] }), _jsx("div", { className: "ai-signin-content", children: _jsxs("form", { onSubmit: handleSubmit, children: [_jsxs("div", { className: "ai-input-row", children: [_jsxs("label", { htmlFor: "lb-signin-email", className: "ai-input-label ai-row", children: [_jsx(Mail, { size: 14, className: "ai-inline-icon" }), t("auth.modal.email", "Email")] }), _jsx("div", { className: "ai-control-group ai-glow", children: _jsx("div", { className: "ai-shell ai-size-md ai-radius-full", children: _jsx("input", { id: "lb-signin-email", className: "ai-control ai-control-input ai-size-md ai-radius-full", type: "email", value: email, onChange: (e) => setEmail(e.target.value), required: true, autoFocus: true, autoComplete: "email", placeholder: t("auth.modal.emailPlaceholder", "your@email.com") }) }) })] }), _jsxs("div", { className: "ai-input-row", children: [_jsxs("label", { htmlFor: "lb-signin-password", className: "ai-input-label ai-row", children: [_jsx(Lock, { size: 14, className: "ai-inline-icon" }), t("auth.modal.password", "Password")] }), _jsx("div", { className: "ai-control-group ai-glow", children: _jsx("div", { className: "ai-shell ai-size-md ai-radius-full", children: _jsx("input", { id: "lb-signin-password", className: "ai-control ai-control-input ai-size-md ai-radius-full", type: "password", value: password, onChange: (e) => setPassword(e.target.value), required: true, autoComplete: "current-password", placeholder: t("auth.modal.passwordPlaceholder", "••••••••") }) }) })] }), error ? (_jsxs("div", { className: "ai-signin-error", role: "alert", children: [_jsx(AlertCircle, { size: 16 }), _jsx("span", { children: error })] })) : null, _jsxs("div", { className: "ai-signin-actions", children: [_jsx("button", { type: "submit", className: "ai-btn ai-btn--auth", disabled: loading, children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 16, className: "ai-spinner" }), t("auth.modal.connecting", "Signing in...")] })) : (_jsxs(_Fragment, { children: [_jsx(Sparkles, { size: 16 }), t("auth.signIn", "Sign in")] })) }), _jsx("a", { href: "https://prompt.lastbrain.io/signup", target: "_blank", rel: "noopener noreferrer", className: "ai-btn ai-btn--ghost", children: t("auth.modal.createAccount", "Create account") })] })] }) })] }) }), portalRoot);
91
97
  }
@@ -1 +1 @@
1
- {"version":3,"file":"UsageToast.d.ts","sourceRoot":"","sources":["../../src/components/UsageToast.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAI5B,UAAU,eAAe;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;IACrE,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,QAAyB,EACzB,UAAU,GACX,EAAE,eAAe,kDAkJjB;AAED,wBAAgB,aAAa;6BAIK,OAAO;;;;EAgBxC"}
1
+ {"version":3,"file":"UsageToast.d.ts","sourceRoot":"","sources":["../../src/components/UsageToast.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAK5B,UAAU,eAAe;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;IACrE,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,QAAyB,EACzB,UAAU,GACX,EAAE,eAAe,kDAmJjB;AAED,wBAAgB,aAAa;6BAIK,OAAO;;;;EAgBxC"}
@@ -3,7 +3,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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
  export function UsageToast({ result, position = "bottom-right", onComplete, }) {
8
+ const { t } = useI18n();
7
9
  const [isVisible, setIsVisible] = useState(false);
8
10
  const [isClosing, setIsClosing] = useState(false);
9
11
  const fadeTimeoutRef = useRef(null);
@@ -67,7 +69,7 @@ export function UsageToast({ result, position = "bottom-right", onComplete, }) {
67
69
  }
68
70
  // Remove trailing zeros
69
71
  formatted = parseFloat(formatted).toString();
70
- return `${formatted}$ used`;
72
+ return t("usage.toast.used", "{amount}$ used", { amount: formatted });
71
73
  };
72
74
  const message = extractUsageMessage(result);
73
75
  if (!result)
@@ -125,7 +127,7 @@ export function UsageToast({ result, position = "bottom-right", onComplete, }) {
125
127
  e.currentTarget.style.backgroundColor = "rgba(22, 163, 74, 0.2)";
126
128
  }, onMouseLeave: (e) => {
127
129
  e.currentTarget.style.backgroundColor = "transparent";
128
- }, title: "Close", children: _jsx(X, { size: 12 }) })] }));
130
+ }, title: t("common.closeLabel", "Close"), children: _jsx(X, { size: 12 }) })] }));
129
131
  }
130
132
  export function useUsageToast() {
131
133
  const [toastData, setToastData] = useState(null);
@@ -0,0 +1,15 @@
1
+ import { type ReactNode } from "react";
2
+ export type LBSupportedLang = "fr" | "en" | "es" | "it" | "de" | "pt";
3
+ type TranslateParams = Record<string, string | number>;
4
+ interface I18nContextValue {
5
+ lang: LBSupportedLang;
6
+ t: (key: string, fallback?: string, params?: TranslateParams) => string;
7
+ }
8
+ export interface I18nProviderProps {
9
+ children: ReactNode;
10
+ lang?: LBSupportedLang;
11
+ }
12
+ export declare function I18nProvider({ children, lang }: I18nProviderProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function useI18n(): I18nContextValue;
14
+ export {};
15
+ //# sourceMappingURL=I18nContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"I18nContext.d.ts","sourceRoot":"","sources":["../../src/context/I18nContext.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAQ3E,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAatE,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AAEvD,UAAU,gBAAgB;IACxB,IAAI,EAAE,eAAe,CAAC;IACtB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC;CACzE;AAeD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,IAAkB,EAAE,EAAE,iBAAiB,2CAkB/E;AAED,wBAAgB,OAAO,qBAEtB"}
@@ -0,0 +1,44 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useContext, useMemo } 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
+ const dictionaries = {
11
+ fr,
12
+ en,
13
+ es,
14
+ it,
15
+ de,
16
+ pt,
17
+ };
18
+ const defaultLang = "fr";
19
+ const I18nContext = createContext({
20
+ lang: defaultLang,
21
+ t: (key, fallback, params) => {
22
+ const template = dictionaries[defaultLang][key] || fallback || key;
23
+ if (!params)
24
+ return template;
25
+ return template.replace(/\{(\w+)\}/g, (_, p) => params[p] !== undefined ? String(params[p]) : `{${p}}`);
26
+ },
27
+ });
28
+ export function I18nProvider({ children, lang = defaultLang }) {
29
+ const safeLang = dictionaries[lang] ? lang : defaultLang;
30
+ const value = useMemo(() => {
31
+ const dict = dictionaries[safeLang] || dictionaries[defaultLang];
32
+ const t = (key, fallback, params) => {
33
+ const template = dict[key] || dictionaries[defaultLang][key] || fallback || key;
34
+ if (!params)
35
+ return template;
36
+ return template.replace(/\{(\w+)\}/g, (_, p) => params[p] !== undefined ? String(params[p]) : `{${p}}`);
37
+ };
38
+ return { lang: safeLang, t };
39
+ }, [safeLang]);
40
+ return _jsx(I18nContext.Provider, { value: value, children: children });
41
+ }
42
+ export function useI18n() {
43
+ return useContext(I18nContext);
44
+ }
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { type ReactNode } from "react";
6
6
  import type { LBAuthState, LBApiKey, AiStatus } from "@lastbrain/ai-ui-core";
7
+ import { type LBSupportedLang } from "./I18nContext";
7
8
  export interface ApiKeyUser {
8
9
  id?: string;
9
10
  name?: string;
@@ -44,6 +45,8 @@ interface LBProviderProps {
44
45
  onStatusChange?: (status: LBAuthState["status"]) => void;
45
46
  /** Fonction appelée après signin/logout pour refresh les providers */
46
47
  onAuthChange?: () => void;
48
+ /** Langue UI globale des composants ai-ui-react */
49
+ lang?: LBSupportedLang;
47
50
  }
48
51
  interface LBContextValue extends LBAuthState {
49
52
  /** Fonction de connexion */
@@ -88,7 +91,7 @@ interface LBContextValue extends LBAuthState {
88
91
  }
89
92
  declare const LBContext: import("react").Context<LBContextValue | undefined>;
90
93
  export { LBContext };
91
- export declare function LBProvider({ children, baseUrl: _baseUrl, proxyUrl, onStatusChange, onAuthChange, }: LBProviderProps): import("react/jsx-runtime").JSX.Element;
94
+ export declare function LBProvider({ children, baseUrl: _baseUrl, proxyUrl, onStatusChange, onAuthChange, lang, }: LBProviderProps): import("react/jsx-runtime").JSX.Element;
92
95
  /**
93
96
  * Hook pour accéder au contexte LastBrain
94
97
  */
@@ -1 +1 @@
1
- {"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LBAuthProvider.d.ts","sourceRoot":"","sources":["../../src/context/LBAuthProvider.tsx"],"names":[],"mappings":"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"}
@@ -6,10 +6,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
6
6
  */
7
7
  import { createContext, useContext, useEffect, useCallback, useMemo, useState, } from "react";
8
8
  import { createLBClient } from "@lastbrain/ai-ui-core";
9
+ import { I18nProvider } from "./I18nContext";
9
10
  const LBContext = createContext(undefined);
10
11
  // Export pour usage dans d'autres composants
11
12
  export { LBContext };
12
- export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", proxyUrl = "/api/lastbrain", onStatusChange, onAuthChange, }) {
13
+ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", proxyUrl = "/api/lastbrain", onStatusChange, onAuthChange, lang = "fr", }) {
13
14
  const [state, setState] = useState({
14
15
  status: "loading",
15
16
  });
@@ -453,7 +454,7 @@ export function LBProvider({ children, baseUrl: _baseUrl = "/api/lastbrain", pro
453
454
  isLoadingStatus,
454
455
  isLoadingStorage,
455
456
  };
456
- return _jsx(LBContext.Provider, { value: value, children: children });
457
+ return (_jsx(I18nProvider, { lang: lang, children: _jsx(LBContext.Provider, { value: value, children: children }) }));
457
458
  }
458
459
  /**
459
460
  * Hook pour accéder au contexte LastBrain
@@ -1 +1 @@
1
- {"version":3,"file":"useAiCallImage.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiCallImage.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA8H7E,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IACrE,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,wBAAgB,cAAc,CAC5B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,oBAAoB,CAsDtB"}
1
+ {"version":3,"file":"useAiCallImage.d.ts","sourceRoot":"","sources":["../../src/hooks/useAiCallImage.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7E,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IACrE,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,wBAAgB,cAAc,CAC5B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,oBAAoB,CA6BtB"}