@serhii.olishevskyi/docs 1.2.12 → 1.2.13
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/{UserPopoverComponent-K7CJNYYL.mjs → UserPopoverComponent-LI5272PW.mjs} +1 -1
- package/dist/{chunk-FRDK4RQX.mjs → chunk-EW4DX6YG.mjs} +218 -82
- package/dist/index.d.mts +19 -6
- package/dist/index.d.ts +19 -6
- package/dist/index.js +216 -84
- package/dist/index.mjs +2 -4
- package/package.json +1 -1
|
@@ -1109,15 +1109,17 @@ import {
|
|
|
1109
1109
|
IconButton,
|
|
1110
1110
|
Tooltip,
|
|
1111
1111
|
Paper,
|
|
1112
|
-
Alert as Alert2
|
|
1112
|
+
Alert as Alert2,
|
|
1113
|
+
ToggleButtonGroup,
|
|
1114
|
+
ToggleButton
|
|
1113
1115
|
} from "@mui/material";
|
|
1114
1116
|
import AutoFixHighIcon from "@mui/icons-material/AutoFixHigh";
|
|
1115
1117
|
import TranslateIcon from "@mui/icons-material/Translate";
|
|
1116
1118
|
import LightbulbIcon from "@mui/icons-material/Lightbulb";
|
|
1119
|
+
import SmartToyIcon from "@mui/icons-material/SmartToy";
|
|
1117
1120
|
|
|
1118
1121
|
// src/services/translation.service.ts
|
|
1119
1122
|
var translationService = {
|
|
1120
|
-
// 1. Принимаем client первым аргументом
|
|
1121
1123
|
incrementVersion: async (client, namespace, langCodes) => {
|
|
1122
1124
|
try {
|
|
1123
1125
|
const { data } = await client.from("translation_versions").select("version, lang_code").eq("namespace", namespace);
|
|
@@ -1138,36 +1140,103 @@ var translationService = {
|
|
|
1138
1140
|
return null;
|
|
1139
1141
|
}
|
|
1140
1142
|
},
|
|
1141
|
-
|
|
1143
|
+
/**
|
|
1144
|
+
* Универсальный метод перевода с поддержкой Gemini, OpenAI и автоматическим fallback
|
|
1145
|
+
*/
|
|
1146
|
+
translateSmart: async (client, aiGenerateFn, params, openAiApiKey) => {
|
|
1142
1147
|
const languagesToTranslate = params.targetLangs || (params.targetLang ? [params.targetLang] : []);
|
|
1143
1148
|
try {
|
|
1144
1149
|
const { data, error } = await client.functions.invoke("translate-smart", { body: params });
|
|
1145
1150
|
if (!error && data) return data;
|
|
1146
1151
|
} catch {
|
|
1147
|
-
console.warn("Edge Function translate-smart \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430, \
|
|
1152
|
+
console.warn("[translationService]: Edge Function translate-smart \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430, \u043F\u0435\u0440\u0435\u0445\u043E\u0434\u0438\u043C \u043A AIProvider...");
|
|
1148
1153
|
}
|
|
1149
|
-
|
|
1150
|
-
const prompt = `Translate the source text into language codes: ${JSON.stringify(languagesToTranslate)}.
|
|
1154
|
+
const prompt = `Translate the source text into language codes: ${JSON.stringify(languagesToTranslate)}.
|
|
1151
1155
|
Source Text: "${params.text}"
|
|
1152
1156
|
${params.contextHint ? `Context Hint: "${params.contextHint}"` : ""}`;
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1157
|
+
const systemInstruction = 'You are a professional translator. Respond STRICTLY with a valid JSON object mapping language code to translated text. Example: {"en": "Hello", "uk": "\u041F\u0440\u0438\u0432\u0456\u0442"}';
|
|
1158
|
+
if (aiGenerateFn) {
|
|
1159
|
+
try {
|
|
1160
|
+
const translations = await aiGenerateFn({
|
|
1161
|
+
prompt,
|
|
1162
|
+
systemInstruction,
|
|
1163
|
+
jsonMode: true,
|
|
1164
|
+
provider: params.provider,
|
|
1165
|
+
modelName: params.modelName
|
|
1166
|
+
});
|
|
1167
|
+
if (translations) {
|
|
1168
|
+
if (params.targetLang) {
|
|
1169
|
+
return { translatedText: translations[params.targetLang] || params.text };
|
|
1170
|
+
}
|
|
1171
|
+
return { translations };
|
|
1172
|
+
}
|
|
1173
|
+
} catch (err) {
|
|
1174
|
+
console.warn(`[translationService]: \u041E\u0448\u0438\u0431\u043A\u0430 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0418\u0418 (${params.provider || "default"}), \u043F\u0440\u043E\u0431\u0443\u0435\u043C \u0440\u0435\u0437\u0435\u0440\u0432\u043D\u044B\u0435 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u044B...`, err);
|
|
1175
|
+
}
|
|
1176
|
+
if (params.provider === "gemini" || !params.provider) {
|
|
1177
|
+
try {
|
|
1178
|
+
console.warn("[translationService]: \u041F\u0440\u043E\u0431\u0443\u0435\u043C \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043D\u0430 OpenAI fallback...");
|
|
1179
|
+
const translations = await aiGenerateFn({
|
|
1180
|
+
prompt,
|
|
1181
|
+
systemInstruction,
|
|
1182
|
+
jsonMode: true,
|
|
1183
|
+
provider: "openai"
|
|
1184
|
+
});
|
|
1185
|
+
if (translations) {
|
|
1186
|
+
if (params.targetLang) {
|
|
1187
|
+
return { translatedText: translations[params.targetLang] || params.text };
|
|
1188
|
+
}
|
|
1189
|
+
return { translations };
|
|
1190
|
+
}
|
|
1191
|
+
} catch (fallbackErr) {
|
|
1192
|
+
console.warn("[translationService]: Fallback OpenAI \u0447\u0435\u0440\u0435\u0437 AIProvider \u043D\u0435 \u0443\u0434\u0430\u043B\u0441\u044F", fallbackErr);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
if (openAiApiKey) {
|
|
1197
|
+
try {
|
|
1198
|
+
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
1199
|
+
method: "POST",
|
|
1200
|
+
headers: {
|
|
1201
|
+
"Content-Type": "application/json",
|
|
1202
|
+
Authorization: `Bearer ${openAiApiKey}`
|
|
1203
|
+
},
|
|
1204
|
+
body: JSON.stringify({
|
|
1205
|
+
model: params.modelName || "gpt-4o-mini",
|
|
1206
|
+
response_format: { type: "json_object" },
|
|
1207
|
+
messages: [
|
|
1208
|
+
{ role: "system", content: systemInstruction },
|
|
1209
|
+
{ role: "user", content: prompt }
|
|
1210
|
+
],
|
|
1211
|
+
temperature: 0.3
|
|
1212
|
+
})
|
|
1213
|
+
});
|
|
1214
|
+
if (response.ok) {
|
|
1215
|
+
const result = await response.json();
|
|
1216
|
+
const content = result.choices?.[0]?.message?.content;
|
|
1217
|
+
if (content) {
|
|
1218
|
+
const translations = JSON.parse(content);
|
|
1219
|
+
if (params.targetLang) {
|
|
1220
|
+
return { translatedText: translations[params.targetLang] || params.text };
|
|
1221
|
+
}
|
|
1222
|
+
return { translations };
|
|
1223
|
+
}
|
|
1161
1224
|
}
|
|
1162
|
-
|
|
1225
|
+
} catch (err) {
|
|
1226
|
+
console.warn("[translationService]: Direct OpenAI API error, fallback to Google Translate", err);
|
|
1163
1227
|
}
|
|
1164
1228
|
}
|
|
1165
1229
|
if (params.targetLang) {
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1230
|
+
try {
|
|
1231
|
+
const fallbackUrl = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${params.targetLang}&dt=t&q=${encodeURIComponent(params.text)}`;
|
|
1232
|
+
const res = await fetch(fallbackUrl);
|
|
1233
|
+
if (res.ok) {
|
|
1234
|
+
const data = await res.json();
|
|
1235
|
+
const translatedText = data[0].map((x) => x[0]).join("");
|
|
1236
|
+
return { translatedText };
|
|
1237
|
+
}
|
|
1238
|
+
} catch (err) {
|
|
1239
|
+
console.error("[translationService]: Google Translate fallback failed:", err);
|
|
1171
1240
|
}
|
|
1172
1241
|
}
|
|
1173
1242
|
throw new Error("All translation strategies failed.");
|
|
@@ -1181,32 +1250,48 @@ import { jsx as jsx9 } from "react/jsx-runtime";
|
|
|
1181
1250
|
var AIContext = createContext9(null);
|
|
1182
1251
|
var AIProvider = ({
|
|
1183
1252
|
children,
|
|
1184
|
-
config,
|
|
1185
|
-
apiKey:
|
|
1253
|
+
config = {},
|
|
1254
|
+
apiKey: legacyApiKey,
|
|
1255
|
+
provider: legacyProvider
|
|
1186
1256
|
}) => {
|
|
1187
|
-
const
|
|
1188
|
-
const
|
|
1189
|
-
|
|
1257
|
+
const activeProvider = config.provider || legacyProvider || "gemini";
|
|
1258
|
+
const geminiConfig = {
|
|
1259
|
+
apiKey: config.gemini?.apiKey || (activeProvider === "gemini" ? legacyApiKey : void 0),
|
|
1260
|
+
modelName: config.gemini?.modelName || "gemini-2.5-flash"
|
|
1261
|
+
};
|
|
1262
|
+
const openaiConfig = {
|
|
1263
|
+
apiKey: config.openai?.apiKey || (activeProvider === "openai" ? legacyApiKey : void 0),
|
|
1264
|
+
modelName: config.openai?.modelName || "gpt-4o-mini"
|
|
1265
|
+
};
|
|
1190
1266
|
const contextValue = useMemo5(() => {
|
|
1191
1267
|
return {
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1268
|
+
activeProvider,
|
|
1269
|
+
config: {
|
|
1270
|
+
provider: activeProvider,
|
|
1271
|
+
gemini: geminiConfig,
|
|
1272
|
+
openai: openaiConfig
|
|
1273
|
+
},
|
|
1195
1274
|
generate: async ({
|
|
1196
1275
|
prompt,
|
|
1197
1276
|
systemInstruction,
|
|
1198
1277
|
jsonMode = false,
|
|
1199
|
-
temperature
|
|
1278
|
+
temperature,
|
|
1279
|
+
provider: overrideProvider,
|
|
1280
|
+
modelName: overrideModelName
|
|
1200
1281
|
}) => {
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1282
|
+
const targetProvider = overrideProvider || activeProvider;
|
|
1283
|
+
if (targetProvider === "gemini") {
|
|
1284
|
+
const key = geminiConfig.apiKey;
|
|
1285
|
+
const model = overrideModelName || geminiConfig.modelName || "gemini-2.5-flash";
|
|
1286
|
+
if (!key) {
|
|
1287
|
+
console.warn("[AIProvider]: API Key \u0434\u043B\u044F Gemini \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.");
|
|
1288
|
+
return null;
|
|
1289
|
+
}
|
|
1206
1290
|
try {
|
|
1207
|
-
const ai = new GoogleGenAI({ apiKey });
|
|
1291
|
+
const ai = new GoogleGenAI({ apiKey: key });
|
|
1208
1292
|
const response = await ai.models.generateContent({
|
|
1209
|
-
model
|
|
1293
|
+
model,
|
|
1294
|
+
// Теперь гарантированно string
|
|
1210
1295
|
contents: prompt,
|
|
1211
1296
|
config: {
|
|
1212
1297
|
systemInstruction,
|
|
@@ -1225,7 +1310,13 @@ var AIProvider = ({
|
|
|
1225
1310
|
return null;
|
|
1226
1311
|
}
|
|
1227
1312
|
}
|
|
1228
|
-
if (
|
|
1313
|
+
if (targetProvider === "openai") {
|
|
1314
|
+
const key = openaiConfig.apiKey;
|
|
1315
|
+
const model = overrideModelName || openaiConfig.modelName;
|
|
1316
|
+
if (!key) {
|
|
1317
|
+
console.warn("[AIProvider]: API Key \u0434\u043B\u044F OpenAI \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.");
|
|
1318
|
+
return null;
|
|
1319
|
+
}
|
|
1229
1320
|
try {
|
|
1230
1321
|
const messages = [];
|
|
1231
1322
|
if (systemInstruction) {
|
|
@@ -1236,15 +1327,19 @@ var AIProvider = ({
|
|
|
1236
1327
|
method: "POST",
|
|
1237
1328
|
headers: {
|
|
1238
1329
|
"Content-Type": "application/json",
|
|
1239
|
-
Authorization: `Bearer ${
|
|
1330
|
+
Authorization: `Bearer ${key}`
|
|
1240
1331
|
},
|
|
1241
1332
|
body: JSON.stringify({
|
|
1242
|
-
model
|
|
1333
|
+
model,
|
|
1243
1334
|
messages,
|
|
1244
1335
|
temperature,
|
|
1245
1336
|
...jsonMode ? { response_format: { type: "json_object" } } : {}
|
|
1246
1337
|
})
|
|
1247
1338
|
});
|
|
1339
|
+
if (!res.ok) {
|
|
1340
|
+
const errData = await res.json().catch(() => ({}));
|
|
1341
|
+
throw new Error(`OpenAI API status ${res.status}: ${JSON.stringify(errData)}`);
|
|
1342
|
+
}
|
|
1248
1343
|
const data = await res.json();
|
|
1249
1344
|
const content = data.choices?.[0]?.message?.content?.trim() || "";
|
|
1250
1345
|
if (jsonMode) {
|
|
@@ -1259,7 +1354,13 @@ var AIProvider = ({
|
|
|
1259
1354
|
return null;
|
|
1260
1355
|
}
|
|
1261
1356
|
};
|
|
1262
|
-
}, [
|
|
1357
|
+
}, [
|
|
1358
|
+
activeProvider,
|
|
1359
|
+
geminiConfig.apiKey,
|
|
1360
|
+
geminiConfig.modelName,
|
|
1361
|
+
openaiConfig.apiKey,
|
|
1362
|
+
openaiConfig.modelName
|
|
1363
|
+
]);
|
|
1263
1364
|
return /* @__PURE__ */ jsx9(AIContext.Provider, { value: contextValue, children });
|
|
1264
1365
|
};
|
|
1265
1366
|
var useAI = () => {
|
|
@@ -1280,10 +1381,11 @@ var TranslationDialog = ({
|
|
|
1280
1381
|
onSave
|
|
1281
1382
|
}) => {
|
|
1282
1383
|
const supabase = useSupabase();
|
|
1283
|
-
const { generate } = useAI();
|
|
1384
|
+
const { generate, activeProvider: defaultProvider } = useAI();
|
|
1284
1385
|
const [languages, setLanguages] = useState7([]);
|
|
1285
1386
|
const [values, setValues] = useState7({});
|
|
1286
1387
|
const [contextHint, setContextHint] = useState7("");
|
|
1388
|
+
const [selectedProvider, setSelectedProvider] = useState7("auto");
|
|
1287
1389
|
const [loading, setLoading] = useState7(false);
|
|
1288
1390
|
const [saving, setSaving] = useState7(false);
|
|
1289
1391
|
const [translatingCode, setTranslatingCode] = useState7(null);
|
|
@@ -1320,6 +1422,7 @@ var TranslationDialog = ({
|
|
|
1320
1422
|
};
|
|
1321
1423
|
fetchData();
|
|
1322
1424
|
}, [open, translationKey, namespace]);
|
|
1425
|
+
const effectiveProvider = selectedProvider === "auto" ? void 0 : selectedProvider;
|
|
1323
1426
|
const handleAutoTranslate = async (e, targetLang) => {
|
|
1324
1427
|
e.preventDefault();
|
|
1325
1428
|
e.stopPropagation();
|
|
@@ -1329,13 +1432,14 @@ var TranslationDialog = ({
|
|
|
1329
1432
|
const res = await translationService.translateSmart(supabase, generate, {
|
|
1330
1433
|
text: translationKey,
|
|
1331
1434
|
targetLang,
|
|
1332
|
-
contextHint
|
|
1435
|
+
contextHint,
|
|
1436
|
+
provider: effectiveProvider
|
|
1333
1437
|
});
|
|
1334
1438
|
if (res?.translatedText) {
|
|
1335
1439
|
setValues((prev) => ({ ...prev, [targetLang]: res.translatedText }));
|
|
1336
1440
|
}
|
|
1337
1441
|
} catch (err) {
|
|
1338
|
-
console.
|
|
1442
|
+
console.error(err);
|
|
1339
1443
|
setError(`Failed to translate into ${targetLang}. ${err?.message || ""}`);
|
|
1340
1444
|
} finally {
|
|
1341
1445
|
setTranslatingCode(null);
|
|
@@ -1352,7 +1456,8 @@ var TranslationDialog = ({
|
|
|
1352
1456
|
const res = await translationService.translateSmart(supabase, generate, {
|
|
1353
1457
|
text: translationKey,
|
|
1354
1458
|
targetLangs,
|
|
1355
|
-
contextHint
|
|
1459
|
+
contextHint,
|
|
1460
|
+
provider: effectiveProvider
|
|
1356
1461
|
});
|
|
1357
1462
|
if (res?.translations) {
|
|
1358
1463
|
setValues((prev) => ({ ...prev, ...res.translations }));
|
|
@@ -1395,36 +1500,76 @@ var TranslationDialog = ({
|
|
|
1395
1500
|
onClick: (e) => e.stopPropagation(),
|
|
1396
1501
|
onMouseDown: (e) => e.stopPropagation(),
|
|
1397
1502
|
children: [
|
|
1398
|
-
/* @__PURE__ */ jsxs3(
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
/* @__PURE__ */ jsxs3(
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1503
|
+
/* @__PURE__ */ jsxs3(
|
|
1504
|
+
DialogTitle,
|
|
1505
|
+
{
|
|
1506
|
+
sx: { pb: 1, display: "flex", justifyContent: "space-between", alignItems: "flex-start" },
|
|
1507
|
+
children: [
|
|
1508
|
+
/* @__PURE__ */ jsxs3(Box, { children: [
|
|
1509
|
+
"Context Translation",
|
|
1510
|
+
/* @__PURE__ */ jsxs3(
|
|
1511
|
+
Typography,
|
|
1512
|
+
{
|
|
1513
|
+
component: "div",
|
|
1514
|
+
variant: "subtitle2",
|
|
1515
|
+
color: "text.secondary",
|
|
1516
|
+
sx: { wordBreak: "break-all", mt: 0.5 },
|
|
1517
|
+
children: [
|
|
1518
|
+
"Original: ",
|
|
1519
|
+
/* @__PURE__ */ jsxs3("strong", { children: [
|
|
1520
|
+
'"',
|
|
1521
|
+
translationKey,
|
|
1522
|
+
'"'
|
|
1523
|
+
] })
|
|
1524
|
+
]
|
|
1525
|
+
}
|
|
1526
|
+
)
|
|
1527
|
+
] }),
|
|
1528
|
+
!loading && /* @__PURE__ */ jsx10(
|
|
1529
|
+
Button,
|
|
1530
|
+
{
|
|
1531
|
+
type: "button",
|
|
1532
|
+
size: "small",
|
|
1533
|
+
variant: "outlined",
|
|
1534
|
+
color: "secondary",
|
|
1535
|
+
startIcon: isTranslatingAll ? /* @__PURE__ */ jsx10(CircularProgress, { size: 16, color: "inherit" }) : /* @__PURE__ */ jsx10(TranslateIcon, { fontSize: "small" }),
|
|
1536
|
+
onClick: handleTranslateAll,
|
|
1537
|
+
disabled: isBusy,
|
|
1538
|
+
sx: { flexShrink: 0, ml: 2, whiteSpace: "nowrap" },
|
|
1539
|
+
children: isTranslatingAll ? "Translating..." : "Translate All"
|
|
1540
|
+
}
|
|
1541
|
+
)
|
|
1542
|
+
]
|
|
1543
|
+
}
|
|
1544
|
+
),
|
|
1425
1545
|
/* @__PURE__ */ jsxs3(DialogContent, { dividers: true, sx: { pt: 2 }, children: [
|
|
1426
1546
|
error && /* @__PURE__ */ jsx10(Alert2, { severity: "error", onClose: () => setError(null), sx: { mb: 2 }, children: error }),
|
|
1427
1547
|
loading ? /* @__PURE__ */ jsx10(Box, { sx: { display: "flex", justifyContent: "center", py: 4 }, children: /* @__PURE__ */ jsx10(CircularProgress, {}) }) : /* @__PURE__ */ jsxs3(Box, { sx: { display: "flex", flexDirection: "column", gap: 2.5 }, children: [
|
|
1548
|
+
/* @__PURE__ */ jsxs3(Box, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
|
|
1549
|
+
/* @__PURE__ */ jsxs3(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
|
|
1550
|
+
/* @__PURE__ */ jsx10(SmartToyIcon, { fontSize: "small", color: "action" }),
|
|
1551
|
+
/* @__PURE__ */ jsx10(Typography, { variant: "caption", sx: { fontWeight: 600, color: "text.secondary" }, children: "AI Engine:" })
|
|
1552
|
+
] }),
|
|
1553
|
+
/* @__PURE__ */ jsxs3(
|
|
1554
|
+
ToggleButtonGroup,
|
|
1555
|
+
{
|
|
1556
|
+
size: "small",
|
|
1557
|
+
value: selectedProvider,
|
|
1558
|
+
exclusive: true,
|
|
1559
|
+
onChange: (_, val) => val && setSelectedProvider(val),
|
|
1560
|
+
disabled: isBusy,
|
|
1561
|
+
children: [
|
|
1562
|
+
/* @__PURE__ */ jsxs3(ToggleButton, { value: "auto", sx: { px: 1.5, py: 0.2, fontSize: "0.75rem" }, children: [
|
|
1563
|
+
"Auto (",
|
|
1564
|
+
defaultProvider,
|
|
1565
|
+
")"
|
|
1566
|
+
] }),
|
|
1567
|
+
/* @__PURE__ */ jsx10(ToggleButton, { value: "gemini", sx: { px: 1.5, py: 0.2, fontSize: "0.75rem" }, children: "Gemini" }),
|
|
1568
|
+
/* @__PURE__ */ jsx10(ToggleButton, { value: "openai", sx: { px: 1.5, py: 0.2, fontSize: "0.75rem" }, children: "OpenAI" })
|
|
1569
|
+
]
|
|
1570
|
+
}
|
|
1571
|
+
)
|
|
1572
|
+
] }),
|
|
1428
1573
|
/* @__PURE__ */ jsxs3(Paper, { variant: "outlined", sx: { p: 2, bgcolor: "action.hover", borderRadius: 2 }, children: [
|
|
1429
1574
|
/* @__PURE__ */ jsxs3(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1 }, children: [
|
|
1430
1575
|
/* @__PURE__ */ jsx10(LightbulbIcon, { color: "warning", fontSize: "small" }),
|
|
@@ -1473,7 +1618,7 @@ var TranslationDialog = ({
|
|
|
1473
1618
|
disabled: isBusy
|
|
1474
1619
|
}
|
|
1475
1620
|
),
|
|
1476
|
-
/* @__PURE__ */ jsx10(Tooltip, { title:
|
|
1621
|
+
/* @__PURE__ */ jsx10(Tooltip, { title: `Translate using ${selectedProvider === "auto" ? defaultProvider : selectedProvider}`, children: /* @__PURE__ */ jsx10("span", { children: /* @__PURE__ */ jsx10(
|
|
1477
1622
|
IconButton,
|
|
1478
1623
|
{
|
|
1479
1624
|
type: "button",
|
|
@@ -1488,16 +1633,7 @@ var TranslationDialog = ({
|
|
|
1488
1633
|
] }),
|
|
1489
1634
|
/* @__PURE__ */ jsxs3(DialogActions, { sx: { px: 3, py: 2 }, children: [
|
|
1490
1635
|
/* @__PURE__ */ jsx10(Button, { onClick: onClose, disabled: isBusy, color: "inherit", type: "button", children: "Cancel" }),
|
|
1491
|
-
/* @__PURE__ */ jsx10(
|
|
1492
|
-
Button,
|
|
1493
|
-
{
|
|
1494
|
-
onClick: handleSaveAll,
|
|
1495
|
-
variant: "contained",
|
|
1496
|
-
disabled: isBusy,
|
|
1497
|
-
type: "button",
|
|
1498
|
-
children: saving ? "Saving..." : "Save All"
|
|
1499
|
-
}
|
|
1500
|
-
)
|
|
1636
|
+
/* @__PURE__ */ jsx10(Button, { onClick: handleSaveAll, variant: "contained", disabled: isBusy, type: "button", children: saving ? "Saving..." : "Save All" })
|
|
1501
1637
|
] })
|
|
1502
1638
|
]
|
|
1503
1639
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -408,28 +408,41 @@ declare const UI: {
|
|
|
408
408
|
};
|
|
409
409
|
|
|
410
410
|
type AIProviderType = 'gemini' | 'openai';
|
|
411
|
+
interface AIServiceConfig {
|
|
412
|
+
apiKey?: string;
|
|
413
|
+
modelName?: string;
|
|
414
|
+
}
|
|
415
|
+
interface AIConfig {
|
|
416
|
+
/** Активный провайдер по умолчанию */
|
|
417
|
+
provider?: AIProviderType;
|
|
418
|
+
/** Конфигурация для Gemini */
|
|
419
|
+
gemini?: AIServiceConfig;
|
|
420
|
+
/** Конфигурация для OpenAI */
|
|
421
|
+
openai?: AIServiceConfig;
|
|
422
|
+
}
|
|
411
423
|
interface AIGenerateOptions {
|
|
412
424
|
prompt: string;
|
|
413
425
|
systemInstruction?: string;
|
|
414
426
|
jsonMode?: boolean;
|
|
415
427
|
temperature?: number;
|
|
416
|
-
|
|
417
|
-
interface AIConfig {
|
|
418
|
-
apiKey?: string;
|
|
428
|
+
/** Возможность переопределить провайдера для конкретного запроса */
|
|
419
429
|
provider?: AIProviderType;
|
|
430
|
+
/** Возможность переопределить модель для конкретного запроса */
|
|
420
431
|
modelName?: string;
|
|
421
432
|
}
|
|
422
433
|
interface AIContextType {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
modelName: string;
|
|
434
|
+
activeProvider: AIProviderType;
|
|
435
|
+
config: AIConfig;
|
|
426
436
|
/** Универсальный метод генерации (текст или JSON) */
|
|
427
437
|
generate: <T = string>(options: AIGenerateOptions) => Promise<T | null>;
|
|
428
438
|
}
|
|
429
439
|
interface AIProviderProps {
|
|
430
440
|
children: React__default.ReactNode;
|
|
431
441
|
config?: AIConfig;
|
|
442
|
+
/** Устаревшая прямая передача ключа (для обратной совместимости) */
|
|
432
443
|
apiKey?: string;
|
|
444
|
+
/** Устаревший выбор провайдера (для обратной совместимости) */
|
|
445
|
+
provider?: AIProviderType;
|
|
433
446
|
}
|
|
434
447
|
declare const AIProvider: React__default.FC<AIProviderProps>;
|
|
435
448
|
declare const useAI: () => AIContextType;
|
package/dist/index.d.ts
CHANGED
|
@@ -408,28 +408,41 @@ declare const UI: {
|
|
|
408
408
|
};
|
|
409
409
|
|
|
410
410
|
type AIProviderType = 'gemini' | 'openai';
|
|
411
|
+
interface AIServiceConfig {
|
|
412
|
+
apiKey?: string;
|
|
413
|
+
modelName?: string;
|
|
414
|
+
}
|
|
415
|
+
interface AIConfig {
|
|
416
|
+
/** Активный провайдер по умолчанию */
|
|
417
|
+
provider?: AIProviderType;
|
|
418
|
+
/** Конфигурация для Gemini */
|
|
419
|
+
gemini?: AIServiceConfig;
|
|
420
|
+
/** Конфигурация для OpenAI */
|
|
421
|
+
openai?: AIServiceConfig;
|
|
422
|
+
}
|
|
411
423
|
interface AIGenerateOptions {
|
|
412
424
|
prompt: string;
|
|
413
425
|
systemInstruction?: string;
|
|
414
426
|
jsonMode?: boolean;
|
|
415
427
|
temperature?: number;
|
|
416
|
-
|
|
417
|
-
interface AIConfig {
|
|
418
|
-
apiKey?: string;
|
|
428
|
+
/** Возможность переопределить провайдера для конкретного запроса */
|
|
419
429
|
provider?: AIProviderType;
|
|
430
|
+
/** Возможность переопределить модель для конкретного запроса */
|
|
420
431
|
modelName?: string;
|
|
421
432
|
}
|
|
422
433
|
interface AIContextType {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
modelName: string;
|
|
434
|
+
activeProvider: AIProviderType;
|
|
435
|
+
config: AIConfig;
|
|
426
436
|
/** Универсальный метод генерации (текст или JSON) */
|
|
427
437
|
generate: <T = string>(options: AIGenerateOptions) => Promise<T | null>;
|
|
428
438
|
}
|
|
429
439
|
interface AIProviderProps {
|
|
430
440
|
children: React__default.ReactNode;
|
|
431
441
|
config?: AIConfig;
|
|
442
|
+
/** Устаревшая прямая передача ключа (для обратной совместимости) */
|
|
432
443
|
apiKey?: string;
|
|
444
|
+
/** Устаревший выбор провайдера (для обратной совместимости) */
|
|
445
|
+
provider?: AIProviderType;
|
|
433
446
|
}
|
|
434
447
|
declare const AIProvider: React__default.FC<AIProviderProps>;
|
|
435
448
|
declare const useAI: () => AIContextType;
|
package/dist/index.js
CHANGED
|
@@ -695,7 +695,6 @@ var init_translation_service = __esm({
|
|
|
695
695
|
"src/services/translation.service.ts"() {
|
|
696
696
|
"use strict";
|
|
697
697
|
translationService = {
|
|
698
|
-
// 1. Принимаем client первым аргументом
|
|
699
698
|
incrementVersion: async (client, namespace, langCodes) => {
|
|
700
699
|
try {
|
|
701
700
|
const { data } = await client.from("translation_versions").select("version, lang_code").eq("namespace", namespace);
|
|
@@ -716,36 +715,103 @@ var init_translation_service = __esm({
|
|
|
716
715
|
return null;
|
|
717
716
|
}
|
|
718
717
|
},
|
|
719
|
-
|
|
718
|
+
/**
|
|
719
|
+
* Универсальный метод перевода с поддержкой Gemini, OpenAI и автоматическим fallback
|
|
720
|
+
*/
|
|
721
|
+
translateSmart: async (client, aiGenerateFn, params, openAiApiKey) => {
|
|
720
722
|
const languagesToTranslate = params.targetLangs || (params.targetLang ? [params.targetLang] : []);
|
|
721
723
|
try {
|
|
722
724
|
const { data, error } = await client.functions.invoke("translate-smart", { body: params });
|
|
723
725
|
if (!error && data) return data;
|
|
724
726
|
} catch {
|
|
725
|
-
console.warn("Edge Function translate-smart \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430, \
|
|
727
|
+
console.warn("[translationService]: Edge Function translate-smart \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430, \u043F\u0435\u0440\u0435\u0445\u043E\u0434\u0438\u043C \u043A AIProvider...");
|
|
726
728
|
}
|
|
727
|
-
|
|
728
|
-
const prompt = `Translate the source text into language codes: ${JSON.stringify(languagesToTranslate)}.
|
|
729
|
+
const prompt = `Translate the source text into language codes: ${JSON.stringify(languagesToTranslate)}.
|
|
729
730
|
Source Text: "${params.text}"
|
|
730
731
|
${params.contextHint ? `Context Hint: "${params.contextHint}"` : ""}`;
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
732
|
+
const systemInstruction = 'You are a professional translator. Respond STRICTLY with a valid JSON object mapping language code to translated text. Example: {"en": "Hello", "uk": "\u041F\u0440\u0438\u0432\u0456\u0442"}';
|
|
733
|
+
if (aiGenerateFn) {
|
|
734
|
+
try {
|
|
735
|
+
const translations = await aiGenerateFn({
|
|
736
|
+
prompt,
|
|
737
|
+
systemInstruction,
|
|
738
|
+
jsonMode: true,
|
|
739
|
+
provider: params.provider,
|
|
740
|
+
modelName: params.modelName
|
|
741
|
+
});
|
|
742
|
+
if (translations) {
|
|
743
|
+
if (params.targetLang) {
|
|
744
|
+
return { translatedText: translations[params.targetLang] || params.text };
|
|
745
|
+
}
|
|
746
|
+
return { translations };
|
|
747
|
+
}
|
|
748
|
+
} catch (err) {
|
|
749
|
+
console.warn(`[translationService]: \u041E\u0448\u0438\u0431\u043A\u0430 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0418\u0418 (${params.provider || "default"}), \u043F\u0440\u043E\u0431\u0443\u0435\u043C \u0440\u0435\u0437\u0435\u0440\u0432\u043D\u044B\u0435 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u044B...`, err);
|
|
750
|
+
}
|
|
751
|
+
if (params.provider === "gemini" || !params.provider) {
|
|
752
|
+
try {
|
|
753
|
+
console.warn("[translationService]: \u041F\u0440\u043E\u0431\u0443\u0435\u043C \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043D\u0430 OpenAI fallback...");
|
|
754
|
+
const translations = await aiGenerateFn({
|
|
755
|
+
prompt,
|
|
756
|
+
systemInstruction,
|
|
757
|
+
jsonMode: true,
|
|
758
|
+
provider: "openai"
|
|
759
|
+
});
|
|
760
|
+
if (translations) {
|
|
761
|
+
if (params.targetLang) {
|
|
762
|
+
return { translatedText: translations[params.targetLang] || params.text };
|
|
763
|
+
}
|
|
764
|
+
return { translations };
|
|
765
|
+
}
|
|
766
|
+
} catch (fallbackErr) {
|
|
767
|
+
console.warn("[translationService]: Fallback OpenAI \u0447\u0435\u0440\u0435\u0437 AIProvider \u043D\u0435 \u0443\u0434\u0430\u043B\u0441\u044F", fallbackErr);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (openAiApiKey) {
|
|
772
|
+
try {
|
|
773
|
+
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
774
|
+
method: "POST",
|
|
775
|
+
headers: {
|
|
776
|
+
"Content-Type": "application/json",
|
|
777
|
+
Authorization: `Bearer ${openAiApiKey}`
|
|
778
|
+
},
|
|
779
|
+
body: JSON.stringify({
|
|
780
|
+
model: params.modelName || "gpt-4o-mini",
|
|
781
|
+
response_format: { type: "json_object" },
|
|
782
|
+
messages: [
|
|
783
|
+
{ role: "system", content: systemInstruction },
|
|
784
|
+
{ role: "user", content: prompt }
|
|
785
|
+
],
|
|
786
|
+
temperature: 0.3
|
|
787
|
+
})
|
|
788
|
+
});
|
|
789
|
+
if (response.ok) {
|
|
790
|
+
const result = await response.json();
|
|
791
|
+
const content = result.choices?.[0]?.message?.content;
|
|
792
|
+
if (content) {
|
|
793
|
+
const translations = JSON.parse(content);
|
|
794
|
+
if (params.targetLang) {
|
|
795
|
+
return { translatedText: translations[params.targetLang] || params.text };
|
|
796
|
+
}
|
|
797
|
+
return { translations };
|
|
798
|
+
}
|
|
739
799
|
}
|
|
740
|
-
|
|
800
|
+
} catch (err) {
|
|
801
|
+
console.warn("[translationService]: Direct OpenAI API error, fallback to Google Translate", err);
|
|
741
802
|
}
|
|
742
803
|
}
|
|
743
804
|
if (params.targetLang) {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
805
|
+
try {
|
|
806
|
+
const fallbackUrl = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${params.targetLang}&dt=t&q=${encodeURIComponent(params.text)}`;
|
|
807
|
+
const res = await fetch(fallbackUrl);
|
|
808
|
+
if (res.ok) {
|
|
809
|
+
const data = await res.json();
|
|
810
|
+
const translatedText = data[0].map((x) => x[0]).join("");
|
|
811
|
+
return { translatedText };
|
|
812
|
+
}
|
|
813
|
+
} catch (err) {
|
|
814
|
+
console.error("[translationService]: Google Translate fallback failed:", err);
|
|
749
815
|
}
|
|
750
816
|
}
|
|
751
817
|
throw new Error("All translation strategies failed.");
|
|
@@ -765,32 +831,48 @@ var init_AIProvider = __esm({
|
|
|
765
831
|
AIContext = (0, import_react4.createContext)(null);
|
|
766
832
|
AIProvider = ({
|
|
767
833
|
children,
|
|
768
|
-
config,
|
|
769
|
-
apiKey:
|
|
834
|
+
config = {},
|
|
835
|
+
apiKey: legacyApiKey,
|
|
836
|
+
provider: legacyProvider
|
|
770
837
|
}) => {
|
|
771
|
-
const
|
|
772
|
-
const
|
|
773
|
-
|
|
838
|
+
const activeProvider = config.provider || legacyProvider || "gemini";
|
|
839
|
+
const geminiConfig = {
|
|
840
|
+
apiKey: config.gemini?.apiKey || (activeProvider === "gemini" ? legacyApiKey : void 0),
|
|
841
|
+
modelName: config.gemini?.modelName || "gemini-2.5-flash"
|
|
842
|
+
};
|
|
843
|
+
const openaiConfig = {
|
|
844
|
+
apiKey: config.openai?.apiKey || (activeProvider === "openai" ? legacyApiKey : void 0),
|
|
845
|
+
modelName: config.openai?.modelName || "gpt-4o-mini"
|
|
846
|
+
};
|
|
774
847
|
const contextValue = (0, import_react4.useMemo)(() => {
|
|
775
848
|
return {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
849
|
+
activeProvider,
|
|
850
|
+
config: {
|
|
851
|
+
provider: activeProvider,
|
|
852
|
+
gemini: geminiConfig,
|
|
853
|
+
openai: openaiConfig
|
|
854
|
+
},
|
|
779
855
|
generate: async ({
|
|
780
856
|
prompt,
|
|
781
857
|
systemInstruction,
|
|
782
858
|
jsonMode = false,
|
|
783
|
-
temperature
|
|
859
|
+
temperature,
|
|
860
|
+
provider: overrideProvider,
|
|
861
|
+
modelName: overrideModelName
|
|
784
862
|
}) => {
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
863
|
+
const targetProvider = overrideProvider || activeProvider;
|
|
864
|
+
if (targetProvider === "gemini") {
|
|
865
|
+
const key = geminiConfig.apiKey;
|
|
866
|
+
const model = overrideModelName || geminiConfig.modelName || "gemini-2.5-flash";
|
|
867
|
+
if (!key) {
|
|
868
|
+
console.warn("[AIProvider]: API Key \u0434\u043B\u044F Gemini \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.");
|
|
869
|
+
return null;
|
|
870
|
+
}
|
|
790
871
|
try {
|
|
791
|
-
const ai = new import_genai.GoogleGenAI({ apiKey });
|
|
872
|
+
const ai = new import_genai.GoogleGenAI({ apiKey: key });
|
|
792
873
|
const response = await ai.models.generateContent({
|
|
793
|
-
model
|
|
874
|
+
model,
|
|
875
|
+
// Теперь гарантированно string
|
|
794
876
|
contents: prompt,
|
|
795
877
|
config: {
|
|
796
878
|
systemInstruction,
|
|
@@ -809,7 +891,13 @@ var init_AIProvider = __esm({
|
|
|
809
891
|
return null;
|
|
810
892
|
}
|
|
811
893
|
}
|
|
812
|
-
if (
|
|
894
|
+
if (targetProvider === "openai") {
|
|
895
|
+
const key = openaiConfig.apiKey;
|
|
896
|
+
const model = overrideModelName || openaiConfig.modelName;
|
|
897
|
+
if (!key) {
|
|
898
|
+
console.warn("[AIProvider]: API Key \u0434\u043B\u044F OpenAI \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.");
|
|
899
|
+
return null;
|
|
900
|
+
}
|
|
813
901
|
try {
|
|
814
902
|
const messages = [];
|
|
815
903
|
if (systemInstruction) {
|
|
@@ -820,15 +908,19 @@ var init_AIProvider = __esm({
|
|
|
820
908
|
method: "POST",
|
|
821
909
|
headers: {
|
|
822
910
|
"Content-Type": "application/json",
|
|
823
|
-
Authorization: `Bearer ${
|
|
911
|
+
Authorization: `Bearer ${key}`
|
|
824
912
|
},
|
|
825
913
|
body: JSON.stringify({
|
|
826
|
-
model
|
|
914
|
+
model,
|
|
827
915
|
messages,
|
|
828
916
|
temperature,
|
|
829
917
|
...jsonMode ? { response_format: { type: "json_object" } } : {}
|
|
830
918
|
})
|
|
831
919
|
});
|
|
920
|
+
if (!res.ok) {
|
|
921
|
+
const errData = await res.json().catch(() => ({}));
|
|
922
|
+
throw new Error(`OpenAI API status ${res.status}: ${JSON.stringify(errData)}`);
|
|
923
|
+
}
|
|
832
924
|
const data = await res.json();
|
|
833
925
|
const content = data.choices?.[0]?.message?.content?.trim() || "";
|
|
834
926
|
if (jsonMode) {
|
|
@@ -843,7 +935,13 @@ var init_AIProvider = __esm({
|
|
|
843
935
|
return null;
|
|
844
936
|
}
|
|
845
937
|
};
|
|
846
|
-
}, [
|
|
938
|
+
}, [
|
|
939
|
+
activeProvider,
|
|
940
|
+
geminiConfig.apiKey,
|
|
941
|
+
geminiConfig.modelName,
|
|
942
|
+
openaiConfig.apiKey,
|
|
943
|
+
openaiConfig.modelName
|
|
944
|
+
]);
|
|
847
945
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AIContext.Provider, { value: contextValue, children });
|
|
848
946
|
};
|
|
849
947
|
useAI = () => {
|
|
@@ -857,7 +955,7 @@ var init_AIProvider = __esm({
|
|
|
857
955
|
});
|
|
858
956
|
|
|
859
957
|
// src/i18n/TranslationDialog.tsx
|
|
860
|
-
var import_react5, import_material, import_AutoFixHigh, import_Translate, import_Lightbulb, import_jsx_runtime5, TranslationDialog;
|
|
958
|
+
var import_react5, import_material, import_AutoFixHigh, import_Translate, import_Lightbulb, import_SmartToy, import_jsx_runtime5, TranslationDialog;
|
|
861
959
|
var init_TranslationDialog = __esm({
|
|
862
960
|
"src/i18n/TranslationDialog.tsx"() {
|
|
863
961
|
"use strict";
|
|
@@ -866,6 +964,7 @@ var init_TranslationDialog = __esm({
|
|
|
866
964
|
import_AutoFixHigh = __toESM(require("@mui/icons-material/AutoFixHigh"));
|
|
867
965
|
import_Translate = __toESM(require("@mui/icons-material/Translate"));
|
|
868
966
|
import_Lightbulb = __toESM(require("@mui/icons-material/Lightbulb"));
|
|
967
|
+
import_SmartToy = __toESM(require("@mui/icons-material/SmartToy"));
|
|
869
968
|
init_translation_service();
|
|
870
969
|
init_SupabaseProvider();
|
|
871
970
|
init_AIProvider();
|
|
@@ -878,10 +977,11 @@ var init_TranslationDialog = __esm({
|
|
|
878
977
|
onSave
|
|
879
978
|
}) => {
|
|
880
979
|
const supabase = useSupabase();
|
|
881
|
-
const { generate } = useAI();
|
|
980
|
+
const { generate, activeProvider: defaultProvider } = useAI();
|
|
882
981
|
const [languages, setLanguages] = (0, import_react5.useState)([]);
|
|
883
982
|
const [values, setValues] = (0, import_react5.useState)({});
|
|
884
983
|
const [contextHint, setContextHint] = (0, import_react5.useState)("");
|
|
984
|
+
const [selectedProvider, setSelectedProvider] = (0, import_react5.useState)("auto");
|
|
885
985
|
const [loading, setLoading] = (0, import_react5.useState)(false);
|
|
886
986
|
const [saving, setSaving] = (0, import_react5.useState)(false);
|
|
887
987
|
const [translatingCode, setTranslatingCode] = (0, import_react5.useState)(null);
|
|
@@ -918,6 +1018,7 @@ var init_TranslationDialog = __esm({
|
|
|
918
1018
|
};
|
|
919
1019
|
fetchData();
|
|
920
1020
|
}, [open, translationKey, namespace]);
|
|
1021
|
+
const effectiveProvider = selectedProvider === "auto" ? void 0 : selectedProvider;
|
|
921
1022
|
const handleAutoTranslate = async (e, targetLang) => {
|
|
922
1023
|
e.preventDefault();
|
|
923
1024
|
e.stopPropagation();
|
|
@@ -927,13 +1028,14 @@ var init_TranslationDialog = __esm({
|
|
|
927
1028
|
const res = await translationService.translateSmart(supabase, generate, {
|
|
928
1029
|
text: translationKey,
|
|
929
1030
|
targetLang,
|
|
930
|
-
contextHint
|
|
1031
|
+
contextHint,
|
|
1032
|
+
provider: effectiveProvider
|
|
931
1033
|
});
|
|
932
1034
|
if (res?.translatedText) {
|
|
933
1035
|
setValues((prev) => ({ ...prev, [targetLang]: res.translatedText }));
|
|
934
1036
|
}
|
|
935
1037
|
} catch (err) {
|
|
936
|
-
console.
|
|
1038
|
+
console.error(err);
|
|
937
1039
|
setError(`Failed to translate into ${targetLang}. ${err?.message || ""}`);
|
|
938
1040
|
} finally {
|
|
939
1041
|
setTranslatingCode(null);
|
|
@@ -950,7 +1052,8 @@ var init_TranslationDialog = __esm({
|
|
|
950
1052
|
const res = await translationService.translateSmart(supabase, generate, {
|
|
951
1053
|
text: translationKey,
|
|
952
1054
|
targetLangs,
|
|
953
|
-
contextHint
|
|
1055
|
+
contextHint,
|
|
1056
|
+
provider: effectiveProvider
|
|
954
1057
|
});
|
|
955
1058
|
if (res?.translations) {
|
|
956
1059
|
setValues((prev) => ({ ...prev, ...res.translations }));
|
|
@@ -993,36 +1096,76 @@ var init_TranslationDialog = __esm({
|
|
|
993
1096
|
onClick: (e) => e.stopPropagation(),
|
|
994
1097
|
onMouseDown: (e) => e.stopPropagation(),
|
|
995
1098
|
children: [
|
|
996
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1099
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1100
|
+
import_material.DialogTitle,
|
|
1101
|
+
{
|
|
1102
|
+
sx: { pb: 1, display: "flex", justifyContent: "space-between", alignItems: "flex-start" },
|
|
1103
|
+
children: [
|
|
1104
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.Box, { children: [
|
|
1105
|
+
"Context Translation",
|
|
1106
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1107
|
+
import_material.Typography,
|
|
1108
|
+
{
|
|
1109
|
+
component: "div",
|
|
1110
|
+
variant: "subtitle2",
|
|
1111
|
+
color: "text.secondary",
|
|
1112
|
+
sx: { wordBreak: "break-all", mt: 0.5 },
|
|
1113
|
+
children: [
|
|
1114
|
+
"Original: ",
|
|
1115
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("strong", { children: [
|
|
1116
|
+
'"',
|
|
1117
|
+
translationKey,
|
|
1118
|
+
'"'
|
|
1119
|
+
] })
|
|
1120
|
+
]
|
|
1121
|
+
}
|
|
1122
|
+
)
|
|
1123
|
+
] }),
|
|
1124
|
+
!loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1125
|
+
import_material.Button,
|
|
1126
|
+
{
|
|
1127
|
+
type: "button",
|
|
1128
|
+
size: "small",
|
|
1129
|
+
variant: "outlined",
|
|
1130
|
+
color: "secondary",
|
|
1131
|
+
startIcon: isTranslatingAll ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.CircularProgress, { size: 16, color: "inherit" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_Translate.default, { fontSize: "small" }),
|
|
1132
|
+
onClick: handleTranslateAll,
|
|
1133
|
+
disabled: isBusy,
|
|
1134
|
+
sx: { flexShrink: 0, ml: 2, whiteSpace: "nowrap" },
|
|
1135
|
+
children: isTranslatingAll ? "Translating..." : "Translate All"
|
|
1136
|
+
}
|
|
1137
|
+
)
|
|
1138
|
+
]
|
|
1139
|
+
}
|
|
1140
|
+
),
|
|
1023
1141
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.DialogContent, { dividers: true, sx: { pt: 2 }, children: [
|
|
1024
1142
|
error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.Alert, { severity: "error", onClose: () => setError(null), sx: { mb: 2 }, children: error }),
|
|
1025
1143
|
loading ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.Box, { sx: { display: "flex", justifyContent: "center", py: 4 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.CircularProgress, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.Box, { sx: { display: "flex", flexDirection: "column", gap: 2.5 }, children: [
|
|
1144
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.Box, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
|
|
1145
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
|
|
1146
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_SmartToy.default, { fontSize: "small", color: "action" }),
|
|
1147
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.Typography, { variant: "caption", sx: { fontWeight: 600, color: "text.secondary" }, children: "AI Engine:" })
|
|
1148
|
+
] }),
|
|
1149
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1150
|
+
import_material.ToggleButtonGroup,
|
|
1151
|
+
{
|
|
1152
|
+
size: "small",
|
|
1153
|
+
value: selectedProvider,
|
|
1154
|
+
exclusive: true,
|
|
1155
|
+
onChange: (_, val) => val && setSelectedProvider(val),
|
|
1156
|
+
disabled: isBusy,
|
|
1157
|
+
children: [
|
|
1158
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.ToggleButton, { value: "auto", sx: { px: 1.5, py: 0.2, fontSize: "0.75rem" }, children: [
|
|
1159
|
+
"Auto (",
|
|
1160
|
+
defaultProvider,
|
|
1161
|
+
")"
|
|
1162
|
+
] }),
|
|
1163
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.ToggleButton, { value: "gemini", sx: { px: 1.5, py: 0.2, fontSize: "0.75rem" }, children: "Gemini" }),
|
|
1164
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.ToggleButton, { value: "openai", sx: { px: 1.5, py: 0.2, fontSize: "0.75rem" }, children: "OpenAI" })
|
|
1165
|
+
]
|
|
1166
|
+
}
|
|
1167
|
+
)
|
|
1168
|
+
] }),
|
|
1026
1169
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.Paper, { variant: "outlined", sx: { p: 2, bgcolor: "action.hover", borderRadius: 2 }, children: [
|
|
1027
1170
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1 }, children: [
|
|
1028
1171
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_Lightbulb.default, { color: "warning", fontSize: "small" }),
|
|
@@ -1071,7 +1214,7 @@ var init_TranslationDialog = __esm({
|
|
|
1071
1214
|
disabled: isBusy
|
|
1072
1215
|
}
|
|
1073
1216
|
),
|
|
1074
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.Tooltip, { title:
|
|
1217
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.Tooltip, { title: `Translate using ${selectedProvider === "auto" ? defaultProvider : selectedProvider}`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1075
1218
|
import_material.IconButton,
|
|
1076
1219
|
{
|
|
1077
1220
|
type: "button",
|
|
@@ -1086,16 +1229,7 @@ var init_TranslationDialog = __esm({
|
|
|
1086
1229
|
] }),
|
|
1087
1230
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.DialogActions, { sx: { px: 3, py: 2 }, children: [
|
|
1088
1231
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.Button, { onClick: onClose, disabled: isBusy, color: "inherit", type: "button", children: "Cancel" }),
|
|
1089
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1090
|
-
import_material.Button,
|
|
1091
|
-
{
|
|
1092
|
-
onClick: handleSaveAll,
|
|
1093
|
-
variant: "contained",
|
|
1094
|
-
disabled: isBusy,
|
|
1095
|
-
type: "button",
|
|
1096
|
-
children: saving ? "Saving..." : "Save All"
|
|
1097
|
-
}
|
|
1098
|
-
)
|
|
1232
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.Button, { onClick: handleSaveAll, variant: "contained", disabled: isBusy, type: "button", children: saving ? "Saving..." : "Save All" })
|
|
1099
1233
|
] })
|
|
1100
1234
|
]
|
|
1101
1235
|
}
|
|
@@ -4542,9 +4676,7 @@ var SwitchComponent = ({
|
|
|
4542
4676
|
}) => {
|
|
4543
4677
|
const currentLabel = label ?? (checked ? checkedLabel : uncheckedLabel);
|
|
4544
4678
|
const renderLabel = (node) => {
|
|
4545
|
-
console.log(node);
|
|
4546
4679
|
if (typeof node === "string") {
|
|
4547
|
-
console.log("string");
|
|
4548
4680
|
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(TranslateText, { overrideNamespace, children: node });
|
|
4549
4681
|
}
|
|
4550
4682
|
return node;
|
package/dist/index.mjs
CHANGED
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
useTranslationEdit,
|
|
44
44
|
useTranslationNamespace,
|
|
45
45
|
useUserSettings
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-EW4DX6YG.mjs";
|
|
47
47
|
|
|
48
48
|
// src/UI/components/inputs/TextFieldComponent.tsx
|
|
49
49
|
import { useState } from "react";
|
|
@@ -741,7 +741,7 @@ var EmailInputComponent = ({
|
|
|
741
741
|
import { useState as useState5, useEffect as useEffect2, lazy, Suspense } from "react";
|
|
742
742
|
import { Avatar, Tooltip as Tooltip3, Typography as Typography2 } from "@mui/material";
|
|
743
743
|
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
744
|
-
var LazyUserPopover = lazy(() => import("./UserPopoverComponent-
|
|
744
|
+
var LazyUserPopover = lazy(() => import("./UserPopoverComponent-LI5272PW.mjs"));
|
|
745
745
|
var getInitials = (name) => {
|
|
746
746
|
if (!name) return "?";
|
|
747
747
|
const parts = name.trim().split(" ");
|
|
@@ -1810,9 +1810,7 @@ var SwitchComponent = ({
|
|
|
1810
1810
|
}) => {
|
|
1811
1811
|
const currentLabel = label ?? (checked ? checkedLabel : uncheckedLabel);
|
|
1812
1812
|
const renderLabel = (node) => {
|
|
1813
|
-
console.log(node);
|
|
1814
1813
|
if (typeof node === "string") {
|
|
1815
|
-
console.log("string");
|
|
1816
1814
|
return /* @__PURE__ */ jsx12(TranslateText, { overrideNamespace, children: node });
|
|
1817
1815
|
}
|
|
1818
1816
|
return node;
|