@serhii.olishevskyi/docs 1.2.12 → 1.2.14

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,5 +1,5 @@
1
1
  // src/UI/components/data.display/UserPopoverComponent.tsx
2
- import { useEffect as useEffect9, useState as useState13 } from "react";
2
+ import { useEffect as useEffect10, useState as useState14 } from "react";
3
3
  import {
4
4
  Popover,
5
5
  Box as Box4,
@@ -1070,7 +1070,7 @@ var useToast = () => {
1070
1070
  };
1071
1071
 
1072
1072
  // src/context/ConfirmationProvider.tsx
1073
- import { createContext as createContext10, useContext as useContext10, useState as useState9, useCallback as useCallback4 } from "react";
1073
+ import { createContext as createContext10, useContext as useContext10, useState as useState10, useCallback as useCallback5 } from "react";
1074
1074
  import {
1075
1075
  Button as Button2,
1076
1076
  Dialog as Dialog2,
@@ -1088,14 +1088,14 @@ import {
1088
1088
  } from "@mui/icons-material";
1089
1089
 
1090
1090
  // src/i18n/TranslateText.tsx
1091
- import { useState as useState8, useRef as useRef2, useEffect as useEffect6 } from "react";
1091
+ import { useState as useState9, useRef as useRef2, useEffect as useEffect7 } from "react";
1092
1092
  import { useTranslation as useTranslation2 } from "react-i18next";
1093
1093
  import { Box as Box2, Popper, Paper as Paper2, MenuList, MenuItem, ListItemIcon, ListItemText } from "@mui/material";
1094
1094
  import TranslateIcon2 from "@mui/icons-material/Translate";
1095
1095
  import LaunchIcon from "@mui/icons-material/Launch";
1096
1096
 
1097
1097
  // src/i18n/TranslationDialog.tsx
1098
- import { useEffect as useEffect5, useState as useState7 } from "react";
1098
+ import { useEffect as useEffect6, useState as useState8 } from "react";
1099
1099
  import {
1100
1100
  Dialog,
1101
1101
  DialogTitle,
@@ -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 };
1161
1172
  }
1162
- return { translations };
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
+ }
1224
+ }
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.");
@@ -1175,38 +1244,96 @@ ${params.contextHint ? `Context Hint: "${params.contextHint}"` : ""}`;
1175
1244
  };
1176
1245
 
1177
1246
  // src/context/AIProvider.tsx
1178
- import { createContext as createContext9, useContext as useContext9, useMemo as useMemo5 } from "react";
1247
+ import { createContext as createContext9, useContext as useContext9, useEffect as useEffect5, useMemo as useMemo5, useState as useState7, useCallback as useCallback4 } from "react";
1179
1248
  import { GoogleGenAI } from "@google/genai";
1180
1249
  import { jsx as jsx9 } from "react/jsx-runtime";
1181
1250
  var AIContext = createContext9(null);
1182
- var AIProvider = ({
1183
- children,
1184
- config,
1185
- apiKey: directApiKey
1186
- }) => {
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");
1251
+ var AIProvider = ({ children, config: initialConfig = {} }) => {
1252
+ const supabase = useSupabase();
1253
+ const [dbConfig, setDbConfig] = useState7({});
1254
+ const loadSettingsFromDb = useCallback4(async () => {
1255
+ try {
1256
+ const { data } = await supabase.from("app_settings").select("key, value").in("key", [
1257
+ "ai_provider",
1258
+ "gemini_api_key",
1259
+ "gemini_model",
1260
+ "openai_api_key",
1261
+ "openai_model"
1262
+ ]);
1263
+ if (!data) return;
1264
+ const parseVal = (val) => {
1265
+ if (typeof val === "string") return val;
1266
+ return JSON.stringify(val).replace(/^"|"$/g, "");
1267
+ };
1268
+ let provider;
1269
+ let geminiApiKey;
1270
+ let geminiModel;
1271
+ let openaiApiKey;
1272
+ let openaiModel;
1273
+ data.forEach((item) => {
1274
+ const val = parseVal(item.value);
1275
+ if (item.key === "ai_provider" && (val === "gemini" || val === "openai")) {
1276
+ provider = val;
1277
+ }
1278
+ if (item.key === "gemini_api_key") geminiApiKey = val;
1279
+ if (item.key === "gemini_model") geminiModel = val;
1280
+ if (item.key === "openai_api_key") openaiApiKey = val;
1281
+ if (item.key === "openai_model") openaiModel = val;
1282
+ });
1283
+ setDbConfig({
1284
+ provider,
1285
+ gemini: { apiKey: geminiApiKey, modelName: geminiModel },
1286
+ openai: { apiKey: openaiApiKey, modelName: openaiModel }
1287
+ });
1288
+ } catch (err) {
1289
+ console.warn("[AIProvider]: Failed to load AI settings from app_settings", err);
1290
+ }
1291
+ }, [supabase]);
1292
+ useEffect5(() => {
1293
+ loadSettingsFromDb();
1294
+ }, [loadSettingsFromDb]);
1295
+ const activeProvider = dbConfig.provider || initialConfig.provider || "gemini";
1296
+ const geminiConfig = {
1297
+ apiKey: dbConfig.gemini?.apiKey || initialConfig.gemini?.apiKey || "",
1298
+ //import.meta.env.VITE_GEMINI_API_KEY,
1299
+ modelName: dbConfig.gemini?.modelName || initialConfig.gemini?.modelName || "" || //import.meta.env.VITE_GEMINI_MODEL ||
1300
+ "gemini-2.5-flash"
1301
+ };
1302
+ const openaiConfig = {
1303
+ apiKey: dbConfig.openai?.apiKey || initialConfig.openai?.apiKey || "",
1304
+ //import.meta.env.VITE_OPENAI_API_KEY,
1305
+ modelName: dbConfig.openai?.modelName || initialConfig.openai?.modelName || "" || //import.meta.env.VITE_OPENAI_MODEL ||
1306
+ "gpt-4o-mini"
1307
+ };
1190
1308
  const contextValue = useMemo5(() => {
1191
1309
  return {
1192
- apiKey,
1193
- provider,
1194
- modelName,
1310
+ activeProvider,
1311
+ config: {
1312
+ provider: activeProvider,
1313
+ gemini: geminiConfig,
1314
+ openai: openaiConfig
1315
+ },
1316
+ reloadConfig: loadSettingsFromDb,
1195
1317
  generate: async ({
1196
1318
  prompt,
1197
1319
  systemInstruction,
1198
1320
  jsonMode = false,
1199
- temperature
1321
+ temperature,
1322
+ provider: overrideProvider,
1323
+ modelName: overrideModelName
1200
1324
  }) => {
1201
- if (!apiKey) {
1202
- console.warn("[AIProvider]: API Key \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.");
1203
- return null;
1204
- }
1205
- if (provider === "gemini") {
1325
+ const targetProvider = overrideProvider || activeProvider;
1326
+ if (targetProvider === "gemini") {
1327
+ const key = geminiConfig.apiKey;
1328
+ const model = overrideModelName || geminiConfig.modelName || "gemini-2.5-flash";
1329
+ if (!key) {
1330
+ console.warn("[AIProvider]: API Key \u0434\u043B\u044F Gemini \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.");
1331
+ return null;
1332
+ }
1206
1333
  try {
1207
- const ai = new GoogleGenAI({ apiKey });
1334
+ const ai = new GoogleGenAI({ apiKey: key });
1208
1335
  const response = await ai.models.generateContent({
1209
- model: modelName,
1336
+ model,
1210
1337
  contents: prompt,
1211
1338
  config: {
1212
1339
  systemInstruction,
@@ -1225,7 +1352,13 @@ var AIProvider = ({
1225
1352
  return null;
1226
1353
  }
1227
1354
  }
1228
- if (provider === "openai") {
1355
+ if (targetProvider === "openai") {
1356
+ const key = openaiConfig.apiKey;
1357
+ const model = overrideModelName || openaiConfig.modelName || "gpt-4o-mini";
1358
+ if (!key) {
1359
+ console.warn("[AIProvider]: API Key \u0434\u043B\u044F OpenAI \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.");
1360
+ return null;
1361
+ }
1229
1362
  try {
1230
1363
  const messages = [];
1231
1364
  if (systemInstruction) {
@@ -1236,15 +1369,19 @@ var AIProvider = ({
1236
1369
  method: "POST",
1237
1370
  headers: {
1238
1371
  "Content-Type": "application/json",
1239
- Authorization: `Bearer ${apiKey}`
1372
+ Authorization: `Bearer ${key}`
1240
1373
  },
1241
1374
  body: JSON.stringify({
1242
- model: modelName,
1375
+ model,
1243
1376
  messages,
1244
1377
  temperature,
1245
1378
  ...jsonMode ? { response_format: { type: "json_object" } } : {}
1246
1379
  })
1247
1380
  });
1381
+ if (!res.ok) {
1382
+ const errData = await res.json().catch(() => ({}));
1383
+ throw new Error(`OpenAI API status ${res.status}: ${JSON.stringify(errData)}`);
1384
+ }
1248
1385
  const data = await res.json();
1249
1386
  const content = data.choices?.[0]?.message?.content?.trim() || "";
1250
1387
  if (jsonMode) {
@@ -1259,7 +1396,14 @@ var AIProvider = ({
1259
1396
  return null;
1260
1397
  }
1261
1398
  };
1262
- }, [apiKey, provider, modelName]);
1399
+ }, [
1400
+ activeProvider,
1401
+ geminiConfig.apiKey,
1402
+ geminiConfig.modelName,
1403
+ openaiConfig.apiKey,
1404
+ openaiConfig.modelName,
1405
+ loadSettingsFromDb
1406
+ ]);
1263
1407
  return /* @__PURE__ */ jsx9(AIContext.Provider, { value: contextValue, children });
1264
1408
  };
1265
1409
  var useAI = () => {
@@ -1280,16 +1424,17 @@ var TranslationDialog = ({
1280
1424
  onSave
1281
1425
  }) => {
1282
1426
  const supabase = useSupabase();
1283
- const { generate } = useAI();
1284
- const [languages, setLanguages] = useState7([]);
1285
- const [values, setValues] = useState7({});
1286
- const [contextHint, setContextHint] = useState7("");
1287
- const [loading, setLoading] = useState7(false);
1288
- const [saving, setSaving] = useState7(false);
1289
- const [translatingCode, setTranslatingCode] = useState7(null);
1290
- const [isTranslatingAll, setIsTranslatingAll] = useState7(false);
1291
- const [error, setError] = useState7(null);
1292
- useEffect5(() => {
1427
+ const { generate, activeProvider: defaultProvider } = useAI();
1428
+ const [languages, setLanguages] = useState8([]);
1429
+ const [values, setValues] = useState8({});
1430
+ const [contextHint, setContextHint] = useState8("");
1431
+ const [selectedProvider, setSelectedProvider] = useState8("auto");
1432
+ const [loading, setLoading] = useState8(false);
1433
+ const [saving, setSaving] = useState8(false);
1434
+ const [translatingCode, setTranslatingCode] = useState8(null);
1435
+ const [isTranslatingAll, setIsTranslatingAll] = useState8(false);
1436
+ const [error, setError] = useState8(null);
1437
+ useEffect6(() => {
1293
1438
  if (!open) return;
1294
1439
  const fetchData = async () => {
1295
1440
  setLoading(true);
@@ -1320,6 +1465,7 @@ var TranslationDialog = ({
1320
1465
  };
1321
1466
  fetchData();
1322
1467
  }, [open, translationKey, namespace]);
1468
+ const effectiveProvider = selectedProvider === "auto" ? void 0 : selectedProvider;
1323
1469
  const handleAutoTranslate = async (e, targetLang) => {
1324
1470
  e.preventDefault();
1325
1471
  e.stopPropagation();
@@ -1329,13 +1475,14 @@ var TranslationDialog = ({
1329
1475
  const res = await translationService.translateSmart(supabase, generate, {
1330
1476
  text: translationKey,
1331
1477
  targetLang,
1332
- contextHint
1478
+ contextHint,
1479
+ provider: effectiveProvider
1333
1480
  });
1334
1481
  if (res?.translatedText) {
1335
1482
  setValues((prev) => ({ ...prev, [targetLang]: res.translatedText }));
1336
1483
  }
1337
1484
  } catch (err) {
1338
- console.log(err);
1485
+ console.error(err);
1339
1486
  setError(`Failed to translate into ${targetLang}. ${err?.message || ""}`);
1340
1487
  } finally {
1341
1488
  setTranslatingCode(null);
@@ -1352,7 +1499,8 @@ var TranslationDialog = ({
1352
1499
  const res = await translationService.translateSmart(supabase, generate, {
1353
1500
  text: translationKey,
1354
1501
  targetLangs,
1355
- contextHint
1502
+ contextHint,
1503
+ provider: effectiveProvider
1356
1504
  });
1357
1505
  if (res?.translations) {
1358
1506
  setValues((prev) => ({ ...prev, ...res.translations }));
@@ -1395,36 +1543,76 @@ var TranslationDialog = ({
1395
1543
  onClick: (e) => e.stopPropagation(),
1396
1544
  onMouseDown: (e) => e.stopPropagation(),
1397
1545
  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
- ] }),
1546
+ /* @__PURE__ */ jsxs3(
1547
+ DialogTitle,
1548
+ {
1549
+ sx: { pb: 1, display: "flex", justifyContent: "space-between", alignItems: "flex-start" },
1550
+ children: [
1551
+ /* @__PURE__ */ jsxs3(Box, { children: [
1552
+ "Context Translation",
1553
+ /* @__PURE__ */ jsxs3(
1554
+ Typography,
1555
+ {
1556
+ component: "div",
1557
+ variant: "subtitle2",
1558
+ color: "text.secondary",
1559
+ sx: { wordBreak: "break-all", mt: 0.5 },
1560
+ children: [
1561
+ "Original: ",
1562
+ /* @__PURE__ */ jsxs3("strong", { children: [
1563
+ '"',
1564
+ translationKey,
1565
+ '"'
1566
+ ] })
1567
+ ]
1568
+ }
1569
+ )
1570
+ ] }),
1571
+ !loading && /* @__PURE__ */ jsx10(
1572
+ Button,
1573
+ {
1574
+ type: "button",
1575
+ size: "small",
1576
+ variant: "outlined",
1577
+ color: "secondary",
1578
+ startIcon: isTranslatingAll ? /* @__PURE__ */ jsx10(CircularProgress, { size: 16, color: "inherit" }) : /* @__PURE__ */ jsx10(TranslateIcon, { fontSize: "small" }),
1579
+ onClick: handleTranslateAll,
1580
+ disabled: isBusy,
1581
+ sx: { flexShrink: 0, ml: 2, whiteSpace: "nowrap" },
1582
+ children: isTranslatingAll ? "Translating..." : "Translate All"
1583
+ }
1584
+ )
1585
+ ]
1586
+ }
1587
+ ),
1425
1588
  /* @__PURE__ */ jsxs3(DialogContent, { dividers: true, sx: { pt: 2 }, children: [
1426
1589
  error && /* @__PURE__ */ jsx10(Alert2, { severity: "error", onClose: () => setError(null), sx: { mb: 2 }, children: error }),
1427
1590
  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: [
1591
+ /* @__PURE__ */ jsxs3(Box, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1592
+ /* @__PURE__ */ jsxs3(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
1593
+ /* @__PURE__ */ jsx10(SmartToyIcon, { fontSize: "small", color: "action" }),
1594
+ /* @__PURE__ */ jsx10(Typography, { variant: "caption", sx: { fontWeight: 600, color: "text.secondary" }, children: "AI Engine:" })
1595
+ ] }),
1596
+ /* @__PURE__ */ jsxs3(
1597
+ ToggleButtonGroup,
1598
+ {
1599
+ size: "small",
1600
+ value: selectedProvider,
1601
+ exclusive: true,
1602
+ onChange: (_, val) => val && setSelectedProvider(val),
1603
+ disabled: isBusy,
1604
+ children: [
1605
+ /* @__PURE__ */ jsxs3(ToggleButton, { value: "auto", sx: { px: 1.5, py: 0.2, fontSize: "0.75rem" }, children: [
1606
+ "Auto (",
1607
+ defaultProvider,
1608
+ ")"
1609
+ ] }),
1610
+ /* @__PURE__ */ jsx10(ToggleButton, { value: "gemini", sx: { px: 1.5, py: 0.2, fontSize: "0.75rem" }, children: "Gemini" }),
1611
+ /* @__PURE__ */ jsx10(ToggleButton, { value: "openai", sx: { px: 1.5, py: 0.2, fontSize: "0.75rem" }, children: "OpenAI" })
1612
+ ]
1613
+ }
1614
+ )
1615
+ ] }),
1428
1616
  /* @__PURE__ */ jsxs3(Paper, { variant: "outlined", sx: { p: 2, bgcolor: "action.hover", borderRadius: 2 }, children: [
1429
1617
  /* @__PURE__ */ jsxs3(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1 }, children: [
1430
1618
  /* @__PURE__ */ jsx10(LightbulbIcon, { color: "warning", fontSize: "small" }),
@@ -1473,7 +1661,7 @@ var TranslationDialog = ({
1473
1661
  disabled: isBusy
1474
1662
  }
1475
1663
  ),
1476
- /* @__PURE__ */ jsx10(Tooltip, { title: "Translate with context hint", children: /* @__PURE__ */ jsx10("span", { children: /* @__PURE__ */ jsx10(
1664
+ /* @__PURE__ */ jsx10(Tooltip, { title: `Translate using ${selectedProvider === "auto" ? defaultProvider : selectedProvider}`, children: /* @__PURE__ */ jsx10("span", { children: /* @__PURE__ */ jsx10(
1477
1665
  IconButton,
1478
1666
  {
1479
1667
  type: "button",
@@ -1488,16 +1676,7 @@ var TranslationDialog = ({
1488
1676
  ] }),
1489
1677
  /* @__PURE__ */ jsxs3(DialogActions, { sx: { px: 3, py: 2 }, children: [
1490
1678
  /* @__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
- )
1679
+ /* @__PURE__ */ jsx10(Button, { onClick: handleSaveAll, variant: "contained", disabled: isBusy, type: "button", children: saving ? "Saving..." : "Save All" })
1501
1680
  ] })
1502
1681
  ]
1503
1682
  }
@@ -1515,11 +1694,11 @@ var TranslateText = ({
1515
1694
  const { t, i18n: i18n2 } = useTranslation2(namespace);
1516
1695
  const { isTranslationMode, canEdit } = useTranslationEdit();
1517
1696
  const supabase = useSupabase();
1518
- const [anchorEl, setAnchorEl] = useState8(null);
1519
- const [isDialogOpen, setIsDialogOpen] = useState8(false);
1697
+ const [anchorEl, setAnchorEl] = useState9(null);
1698
+ const [isDialogOpen, setIsDialogOpen] = useState9(false);
1520
1699
  const timeoutRef = useRef2(null);
1521
1700
  const translatedValue = t(children, values);
1522
- useEffect6(() => {
1701
+ useEffect7(() => {
1523
1702
  return () => {
1524
1703
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
1525
1704
  };
@@ -1685,10 +1864,10 @@ var SEVERITY_ICONS = {
1685
1864
  success: /* @__PURE__ */ jsx12(SuccessIcon, { color: "success", sx: { fontSize: 40 } })
1686
1865
  };
1687
1866
  var ConfirmationProvider = ({ overrideNamespace, children }) => {
1688
- const [open, setOpen] = useState9(false);
1689
- const [options, setOptions] = useState9(null);
1690
- const [resolver, setResolver] = useState9(null);
1691
- const confirm = useCallback4((opts) => {
1867
+ const [open, setOpen] = useState10(false);
1868
+ const [options, setOptions] = useState10(null);
1869
+ const [resolver, setResolver] = useState10(null);
1870
+ const confirm = useCallback5((opts) => {
1692
1871
  setOptions(opts);
1693
1872
  setOpen(true);
1694
1873
  return new Promise((resolve) => {
@@ -1763,7 +1942,7 @@ var useConfirmation = () => {
1763
1942
  };
1764
1943
 
1765
1944
  // src/context/AppSettingsProvider.tsx
1766
- import { createContext as createContext11, useContext as useContext11, useEffect as useEffect7, useState as useState10, useCallback as useCallback5 } from "react";
1945
+ import { createContext as createContext11, useContext as useContext11, useEffect as useEffect8, useState as useState11, useCallback as useCallback6 } from "react";
1767
1946
  import { jsx as jsx13 } from "react/jsx-runtime";
1768
1947
  var DEFAULT_SETTINGS = {
1769
1948
  max_group_members: 100
@@ -1771,10 +1950,10 @@ var DEFAULT_SETTINGS = {
1771
1950
  var AppSettingsContext = createContext11(void 0);
1772
1951
  var AppSettingsProvider = ({ children }) => {
1773
1952
  const supabase = useSupabase();
1774
- const [settings, setSettings] = useState10(DEFAULT_SETTINGS);
1775
- const [loading, setLoading] = useState10(true);
1776
- const [error, setError] = useState10(null);
1777
- const fetchSettings = useCallback5(async () => {
1953
+ const [settings, setSettings] = useState11(DEFAULT_SETTINGS);
1954
+ const [loading, setLoading] = useState11(true);
1955
+ const [error, setError] = useState11(null);
1956
+ const fetchSettings = useCallback6(async () => {
1778
1957
  try {
1779
1958
  setLoading(true);
1780
1959
  setError(null);
@@ -1794,10 +1973,10 @@ var AppSettingsProvider = ({ children }) => {
1794
1973
  setLoading(false);
1795
1974
  }
1796
1975
  }, []);
1797
- useEffect7(() => {
1976
+ useEffect8(() => {
1798
1977
  fetchSettings();
1799
1978
  }, [fetchSettings]);
1800
- const getSetting = useCallback5((key, defaultValue) => {
1979
+ const getSetting = useCallback6((key, defaultValue) => {
1801
1980
  if (key in settings && settings[key] !== null && settings[key] !== void 0) {
1802
1981
  return settings[key];
1803
1982
  }
@@ -1826,7 +2005,7 @@ var useAppSettings = () => {
1826
2005
  };
1827
2006
 
1828
2007
  // src/context/UserSettingsProvider.tsx
1829
- import { createContext as createContext12, useContext as useContext12, useEffect as useEffect8, useState as useState11, useCallback as useCallback6 } from "react";
2008
+ import { createContext as createContext12, useContext as useContext12, useEffect as useEffect9, useState as useState12, useCallback as useCallback7 } from "react";
1830
2009
 
1831
2010
  // src/types/user.types.ts
1832
2011
  var defaultUserSettings = {
@@ -1843,14 +2022,14 @@ var UserSettingsProvider = ({ children }) => {
1843
2022
  const supabase = useSupabase();
1844
2023
  const { user, isAuthenticated, loading: isAuthLoading } = useAuth();
1845
2024
  const userId = user?.id;
1846
- const [settings, setSettings] = useState11({
2025
+ const [settings, setSettings] = useState12({
1847
2026
  id: "",
1848
2027
  ...defaultUserSettings
1849
2028
  });
1850
- const [loading, setLoading] = useState11(true);
1851
- const [error, setError] = useState11(null);
1852
- const [activeChatUserId, setActiveChatUserId] = useState11(null);
1853
- const fetchSettings = useCallback6(async () => {
2029
+ const [loading, setLoading] = useState12(true);
2030
+ const [error, setError] = useState12(null);
2031
+ const [activeChatUserId, setActiveChatUserId] = useState12(null);
2032
+ const fetchSettings = useCallback7(async () => {
1854
2033
  if (isAuthLoading) return;
1855
2034
  if (!isAuthenticated || !userId) {
1856
2035
  setSettings({ id: "", ...defaultUserSettings });
@@ -1903,10 +2082,10 @@ var UserSettingsProvider = ({ children }) => {
1903
2082
  setLoading(false);
1904
2083
  }
1905
2084
  }, [isAuthenticated, isAuthLoading, userId]);
1906
- useEffect8(() => {
2085
+ useEffect9(() => {
1907
2086
  fetchSettings();
1908
2087
  }, [fetchSettings]);
1909
- const toggleSidebar = useCallback6(
2088
+ const toggleSidebar = useCallback7(
1910
2089
  async (open) => {
1911
2090
  const isExplicitBoolean = typeof open === "boolean";
1912
2091
  const nextState = isExplicitBoolean ? open : !settings.sidebar_open;
@@ -1922,7 +2101,7 @@ var UserSettingsProvider = ({ children }) => {
1922
2101
  },
1923
2102
  [settings.sidebar_open, userId]
1924
2103
  );
1925
- const toggleChat = useCallback6(
2104
+ const toggleChat = useCallback7(
1926
2105
  async (open) => {
1927
2106
  const isExplicitBoolean = typeof open === "boolean";
1928
2107
  const nextState = isExplicitBoolean ? open : !settings.chat_open;
@@ -1938,7 +2117,7 @@ var UserSettingsProvider = ({ children }) => {
1938
2117
  },
1939
2118
  [settings.chat_open, userId]
1940
2119
  );
1941
- const openChatWithUser = useCallback6(
2120
+ const openChatWithUser = useCallback7(
1942
2121
  async (targetUserId) => {
1943
2122
  setActiveChatUserId(targetUserId);
1944
2123
  if (!settings.chat_open) {
@@ -1947,7 +2126,7 @@ var UserSettingsProvider = ({ children }) => {
1947
2126
  },
1948
2127
  [settings.chat_open, toggleChat]
1949
2128
  );
1950
- const setLanguage = useCallback6(
2129
+ const setLanguage = useCallback7(
1951
2130
  async (newLanguage) => {
1952
2131
  const previousLanguage = settings.language;
1953
2132
  setSettings((prev) => ({ ...prev, language: newLanguage }));
@@ -1962,7 +2141,7 @@ var UserSettingsProvider = ({ children }) => {
1962
2141
  },
1963
2142
  [settings.language, userId]
1964
2143
  );
1965
- const setThemeMode = useCallback6(
2144
+ const setThemeMode = useCallback7(
1966
2145
  async (newMode) => {
1967
2146
  const previousMode = settings.theme_mode;
1968
2147
  setSettings((prev) => ({ ...prev, theme_mode: newMode }));
@@ -2137,7 +2316,7 @@ var UserRoleChipComponent = ({ overrideNamespace, role }) => {
2137
2316
  };
2138
2317
 
2139
2318
  // src/UI/components/buttons/CopyClipboardButtonComponent.tsx
2140
- import { useState as useState12 } from "react";
2319
+ import { useState as useState13 } from "react";
2141
2320
  import { IconButton as IconButton3 } from "@mui/material";
2142
2321
  import CheckIcon from "@mui/icons-material/Check";
2143
2322
  import CopyIcon from "@mui/icons-material/ContentCopy";
@@ -2189,7 +2368,7 @@ var CopyClipboardButtonComponent = ({
2189
2368
  sx,
2190
2369
  ...iconButtonProps
2191
2370
  }) => {
2192
- const [copied, setCopied] = useState12(false);
2371
+ const [copied, setCopied] = useState13(false);
2193
2372
  const handleCopy = async (e) => {
2194
2373
  e.stopPropagation();
2195
2374
  if (!textToCopy) return;
@@ -2222,9 +2401,9 @@ var CopyClipboardButtonComponent = ({
2222
2401
  // src/UI/components/data.display/UserPopoverComponent.tsx
2223
2402
  import { Fragment as Fragment2, jsx as jsx20, jsxs as jsxs6 } from "react/jsx-runtime";
2224
2403
  var useUserStatus = (supabase, userId, open) => {
2225
- const [lastSeenAt, setLastSeenAt] = useState13(null);
2226
- const [loading, setLoading] = useState13(true);
2227
- useEffect9(() => {
2404
+ const [lastSeenAt, setLastSeenAt] = useState14(null);
2405
+ const [loading, setLoading] = useState14(true);
2406
+ useEffect10(() => {
2228
2407
  if (!userId || !open) {
2229
2408
  setLastSeenAt(null);
2230
2409
  setLoading(false);
@@ -2273,13 +2452,13 @@ var UserPopoverComponent = ({
2273
2452
  const { activeCompanyId, membership: currentMembership } = useAuth();
2274
2453
  const { openChatWithUser } = useUserSettings();
2275
2454
  const { formatDate, formatRelativeTime } = useLocalization();
2276
- const [profileData, setProfileData] = useState13(null);
2277
- const [membershipData, setMembershipData] = useState13(null);
2278
- const [avatarUrl, setAvatarUrl] = useState13(null);
2279
- const [coverUrl, setCoverUrl] = useState13(null);
2280
- const [loading, setLoading] = useState13(true);
2455
+ const [profileData, setProfileData] = useState14(null);
2456
+ const [membershipData, setMembershipData] = useState14(null);
2457
+ const [avatarUrl, setAvatarUrl] = useState14(null);
2458
+ const [coverUrl, setCoverUrl] = useState14(null);
2459
+ const [loading, setLoading] = useState14(true);
2281
2460
  const { lastSeenAt, isOnline } = useUserStatus(supabase, userId, open);
2282
- useEffect9(() => {
2461
+ useEffect10(() => {
2283
2462
  if (!open || !userId || !activeCompanyId) return;
2284
2463
  let isMounted = true;
2285
2464
  setLoading(true);