@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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  UserPopoverComponent,
3
3
  UserPopoverComponent_default
4
- } from "./chunk-FRDK4RQX.mjs";
4
+ } from "./chunk-EW4DX6YG.mjs";
5
5
  export {
6
6
  UserPopoverComponent,
7
7
  UserPopoverComponent_default as default
@@ -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
- translateSmart: async (client, aiGenerateFn, params) => {
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, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u043C AIProvider...");
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
- if (aiGenerateFn) {
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
- const translations = await aiGenerateFn({
1154
- prompt,
1155
- systemInstruction: 'Respond STRICTLY with a valid JSON mapping lang code to text. Example: {"en": "Hello"}',
1156
- jsonMode: true
1157
- });
1158
- if (translations) {
1159
- if (params.targetLang) {
1160
- return { translatedText: translations[params.targetLang] || params.text };
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
- return { translations };
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
- const fallbackUrl = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${params.targetLang}&dt=t&q=${encodeURIComponent(params.text)}`;
1167
- const res = await fetch(fallbackUrl);
1168
- if (res.ok) {
1169
- const data = await res.json();
1170
- return { translatedText: data[0].map((x) => x[0]).join("") };
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: directApiKey
1253
+ config = {},
1254
+ apiKey: legacyApiKey,
1255
+ provider: legacyProvider
1186
1256
  }) => {
1187
- const apiKey = directApiKey || config?.apiKey;
1188
- const provider = config?.provider || "gemini";
1189
- const modelName = config?.modelName || (provider === "gemini" ? "gemini-2.5-flash" : "gpt-4o-mini");
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
- apiKey,
1193
- provider,
1194
- modelName,
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
- if (!apiKey) {
1202
- console.warn("[AIProvider]: API Key \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.");
1203
- return null;
1204
- }
1205
- if (provider === "gemini") {
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: modelName,
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 (provider === "openai") {
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 ${apiKey}`
1330
+ Authorization: `Bearer ${key}`
1240
1331
  },
1241
1332
  body: JSON.stringify({
1242
- model: modelName,
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
- }, [apiKey, provider, modelName]);
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.log(err);
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(DialogTitle, { sx: { pb: 1, display: "flex", justifyContent: "space-between", alignItems: "flex-start" }, children: [
1399
- /* @__PURE__ */ jsxs3(Box, { children: [
1400
- "Context Translation",
1401
- /* @__PURE__ */ jsxs3(Typography, { component: "div", variant: "subtitle2", color: "text.secondary", sx: { wordBreak: "break-all", mt: 0.5 }, children: [
1402
- "Original: ",
1403
- /* @__PURE__ */ jsxs3("strong", { children: [
1404
- '"',
1405
- translationKey,
1406
- '"'
1407
- ] })
1408
- ] })
1409
- ] }),
1410
- !loading && /* @__PURE__ */ jsx10(
1411
- Button,
1412
- {
1413
- type: "button",
1414
- size: "small",
1415
- variant: "outlined",
1416
- color: "secondary",
1417
- startIcon: isTranslatingAll ? /* @__PURE__ */ jsx10(CircularProgress, { size: 16, color: "inherit" }) : /* @__PURE__ */ jsx10(TranslateIcon, { fontSize: "small" }),
1418
- onClick: handleTranslateAll,
1419
- disabled: isBusy,
1420
- sx: { flexShrink: 0, ml: 2, whiteSpace: "nowrap" },
1421
- children: isTranslatingAll ? "Translating..." : "Translate All"
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: "Translate with context hint", children: /* @__PURE__ */ jsx10("span", { children: /* @__PURE__ */ jsx10(
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
- apiKey?: string;
424
- provider: AIProviderType;
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
- apiKey?: string;
424
- provider: AIProviderType;
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
- translateSmart: async (client, aiGenerateFn, params) => {
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, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u043C AIProvider...");
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
- if (aiGenerateFn) {
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
- const translations = await aiGenerateFn({
732
- prompt,
733
- systemInstruction: 'Respond STRICTLY with a valid JSON mapping lang code to text. Example: {"en": "Hello"}',
734
- jsonMode: true
735
- });
736
- if (translations) {
737
- if (params.targetLang) {
738
- return { translatedText: translations[params.targetLang] || params.text };
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
- return { translations };
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
- const fallbackUrl = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${params.targetLang}&dt=t&q=${encodeURIComponent(params.text)}`;
745
- const res = await fetch(fallbackUrl);
746
- if (res.ok) {
747
- const data = await res.json();
748
- return { translatedText: data[0].map((x) => x[0]).join("") };
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: directApiKey
834
+ config = {},
835
+ apiKey: legacyApiKey,
836
+ provider: legacyProvider
770
837
  }) => {
771
- const apiKey = directApiKey || config?.apiKey;
772
- const provider = config?.provider || "gemini";
773
- const modelName = config?.modelName || (provider === "gemini" ? "gemini-2.5-flash" : "gpt-4o-mini");
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
- apiKey,
777
- provider,
778
- modelName,
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
- if (!apiKey) {
786
- console.warn("[AIProvider]: API Key \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.");
787
- return null;
788
- }
789
- if (provider === "gemini") {
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: modelName,
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 (provider === "openai") {
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 ${apiKey}`
911
+ Authorization: `Bearer ${key}`
824
912
  },
825
913
  body: JSON.stringify({
826
- model: modelName,
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
- }, [apiKey, provider, modelName]);
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.log(err);
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)(import_material.DialogTitle, { sx: { pb: 1, display: "flex", justifyContent: "space-between", alignItems: "flex-start" }, children: [
997
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.Box, { children: [
998
- "Context Translation",
999
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.Typography, { component: "div", variant: "subtitle2", color: "text.secondary", sx: { wordBreak: "break-all", mt: 0.5 }, children: [
1000
- "Original: ",
1001
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("strong", { children: [
1002
- '"',
1003
- translationKey,
1004
- '"'
1005
- ] })
1006
- ] })
1007
- ] }),
1008
- !loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1009
- import_material.Button,
1010
- {
1011
- type: "button",
1012
- size: "small",
1013
- variant: "outlined",
1014
- color: "secondary",
1015
- 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" }),
1016
- onClick: handleTranslateAll,
1017
- disabled: isBusy,
1018
- sx: { flexShrink: 0, ml: 2, whiteSpace: "nowrap" },
1019
- children: isTranslatingAll ? "Translating..." : "Translate All"
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: "Translate with context hint", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
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-FRDK4RQX.mjs";
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-K7CJNYYL.mjs"));
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serhii.olishevskyi/docs",
3
- "version": "1.2.12",
3
+ "version": "1.2.13",
4
4
  "description": "DOCS.UA react packages",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",