@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
|
@@ -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?.({
|
|
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?.({
|
|
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?.({
|
|
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;
|
|
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: ["
|
|
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: "
|
|
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":"
|
|
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("
|
|
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
|
|
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: "
|
|
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
|
-
|
|
30
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
|
26
|
-
|
|
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;
|
|
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("
|
|
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
|
|
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: "
|
|
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;
|
|
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(() => "
|
|
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 || "
|
|
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("
|
|
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("
|
|
57
|
+
setError(t("auth.modal.apiKeysFetchError", "Failed to fetch API keys"));
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
60
|
catch (err) {
|
|
59
|
-
setError(err instanceof Error
|
|
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
|
|
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": "
|
|
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;
|
|
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
|
|
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":"AAEA;;;GAGG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EAGR,QAAQ,EACT,MAAM,uBAAuB,CAAC;
|
|
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;
|
|
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"}
|