@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.
package/dist/index.d.mts CHANGED
@@ -408,28 +408,33 @@ 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
+ provider?: AIProviderType;
417
+ gemini?: AIServiceConfig;
418
+ openai?: AIServiceConfig;
419
+ }
411
420
  interface AIGenerateOptions {
412
421
  prompt: string;
413
422
  systemInstruction?: string;
414
423
  jsonMode?: boolean;
415
424
  temperature?: number;
416
- }
417
- interface AIConfig {
418
- apiKey?: string;
419
425
  provider?: AIProviderType;
420
426
  modelName?: string;
421
427
  }
422
428
  interface AIContextType {
423
- apiKey?: string;
424
- provider: AIProviderType;
425
- modelName: string;
426
- /** Универсальный метод генерации (текст или JSON) */
429
+ activeProvider: AIProviderType;
430
+ config: AIConfig;
427
431
  generate: <T = string>(options: AIGenerateOptions) => Promise<T | null>;
432
+ /** Метод для перезагрузки настроек из базы данных */
433
+ reloadConfig: () => Promise<void>;
428
434
  }
429
435
  interface AIProviderProps {
430
436
  children: React__default.ReactNode;
431
437
  config?: AIConfig;
432
- apiKey?: string;
433
438
  }
434
439
  declare const AIProvider: React__default.FC<AIProviderProps>;
435
440
  declare const useAI: () => AIContextType;
package/dist/index.d.ts CHANGED
@@ -408,28 +408,33 @@ 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
+ provider?: AIProviderType;
417
+ gemini?: AIServiceConfig;
418
+ openai?: AIServiceConfig;
419
+ }
411
420
  interface AIGenerateOptions {
412
421
  prompt: string;
413
422
  systemInstruction?: string;
414
423
  jsonMode?: boolean;
415
424
  temperature?: number;
416
- }
417
- interface AIConfig {
418
- apiKey?: string;
419
425
  provider?: AIProviderType;
420
426
  modelName?: string;
421
427
  }
422
428
  interface AIContextType {
423
- apiKey?: string;
424
- provider: AIProviderType;
425
- modelName: string;
426
- /** Универсальный метод генерации (текст или JSON) */
429
+ activeProvider: AIProviderType;
430
+ config: AIConfig;
427
431
  generate: <T = string>(options: AIGenerateOptions) => Promise<T | null>;
432
+ /** Метод для перезагрузки настроек из базы данных */
433
+ reloadConfig: () => Promise<void>;
428
434
  }
429
435
  interface AIProviderProps {
430
436
  children: React__default.ReactNode;
431
437
  config?: AIConfig;
432
- apiKey?: string;
433
438
  }
434
439
  declare const AIProvider: React__default.FC<AIProviderProps>;
435
440
  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.");
@@ -761,36 +827,95 @@ var init_AIProvider = __esm({
761
827
  "use strict";
762
828
  import_react4 = require("react");
763
829
  import_genai = require("@google/genai");
830
+ init_SupabaseProvider();
764
831
  import_jsx_runtime4 = require("react/jsx-runtime");
765
832
  AIContext = (0, import_react4.createContext)(null);
766
- AIProvider = ({
767
- children,
768
- config,
769
- apiKey: directApiKey
770
- }) => {
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");
833
+ AIProvider = ({ children, config: initialConfig = {} }) => {
834
+ const supabase = useSupabase();
835
+ const [dbConfig, setDbConfig] = (0, import_react4.useState)({});
836
+ const loadSettingsFromDb = (0, import_react4.useCallback)(async () => {
837
+ try {
838
+ const { data } = await supabase.from("app_settings").select("key, value").in("key", [
839
+ "ai_provider",
840
+ "gemini_api_key",
841
+ "gemini_model",
842
+ "openai_api_key",
843
+ "openai_model"
844
+ ]);
845
+ if (!data) return;
846
+ const parseVal = (val) => {
847
+ if (typeof val === "string") return val;
848
+ return JSON.stringify(val).replace(/^"|"$/g, "");
849
+ };
850
+ let provider;
851
+ let geminiApiKey;
852
+ let geminiModel;
853
+ let openaiApiKey;
854
+ let openaiModel;
855
+ data.forEach((item) => {
856
+ const val = parseVal(item.value);
857
+ if (item.key === "ai_provider" && (val === "gemini" || val === "openai")) {
858
+ provider = val;
859
+ }
860
+ if (item.key === "gemini_api_key") geminiApiKey = val;
861
+ if (item.key === "gemini_model") geminiModel = val;
862
+ if (item.key === "openai_api_key") openaiApiKey = val;
863
+ if (item.key === "openai_model") openaiModel = val;
864
+ });
865
+ setDbConfig({
866
+ provider,
867
+ gemini: { apiKey: geminiApiKey, modelName: geminiModel },
868
+ openai: { apiKey: openaiApiKey, modelName: openaiModel }
869
+ });
870
+ } catch (err) {
871
+ console.warn("[AIProvider]: Failed to load AI settings from app_settings", err);
872
+ }
873
+ }, [supabase]);
874
+ (0, import_react4.useEffect)(() => {
875
+ loadSettingsFromDb();
876
+ }, [loadSettingsFromDb]);
877
+ const activeProvider = dbConfig.provider || initialConfig.provider || "gemini";
878
+ const geminiConfig = {
879
+ apiKey: dbConfig.gemini?.apiKey || initialConfig.gemini?.apiKey || "",
880
+ //import.meta.env.VITE_GEMINI_API_KEY,
881
+ modelName: dbConfig.gemini?.modelName || initialConfig.gemini?.modelName || "" || //import.meta.env.VITE_GEMINI_MODEL ||
882
+ "gemini-2.5-flash"
883
+ };
884
+ const openaiConfig = {
885
+ apiKey: dbConfig.openai?.apiKey || initialConfig.openai?.apiKey || "",
886
+ //import.meta.env.VITE_OPENAI_API_KEY,
887
+ modelName: dbConfig.openai?.modelName || initialConfig.openai?.modelName || "" || //import.meta.env.VITE_OPENAI_MODEL ||
888
+ "gpt-4o-mini"
889
+ };
774
890
  const contextValue = (0, import_react4.useMemo)(() => {
775
891
  return {
776
- apiKey,
777
- provider,
778
- modelName,
892
+ activeProvider,
893
+ config: {
894
+ provider: activeProvider,
895
+ gemini: geminiConfig,
896
+ openai: openaiConfig
897
+ },
898
+ reloadConfig: loadSettingsFromDb,
779
899
  generate: async ({
780
900
  prompt,
781
901
  systemInstruction,
782
902
  jsonMode = false,
783
- temperature
903
+ temperature,
904
+ provider: overrideProvider,
905
+ modelName: overrideModelName
784
906
  }) => {
785
- if (!apiKey) {
786
- console.warn("[AIProvider]: API Key \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.");
787
- return null;
788
- }
789
- if (provider === "gemini") {
907
+ const targetProvider = overrideProvider || activeProvider;
908
+ if (targetProvider === "gemini") {
909
+ const key = geminiConfig.apiKey;
910
+ const model = overrideModelName || geminiConfig.modelName || "gemini-2.5-flash";
911
+ if (!key) {
912
+ console.warn("[AIProvider]: API Key \u0434\u043B\u044F Gemini \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.");
913
+ return null;
914
+ }
790
915
  try {
791
- const ai = new import_genai.GoogleGenAI({ apiKey });
916
+ const ai = new import_genai.GoogleGenAI({ apiKey: key });
792
917
  const response = await ai.models.generateContent({
793
- model: modelName,
918
+ model,
794
919
  contents: prompt,
795
920
  config: {
796
921
  systemInstruction,
@@ -809,7 +934,13 @@ var init_AIProvider = __esm({
809
934
  return null;
810
935
  }
811
936
  }
812
- if (provider === "openai") {
937
+ if (targetProvider === "openai") {
938
+ const key = openaiConfig.apiKey;
939
+ const model = overrideModelName || openaiConfig.modelName || "gpt-4o-mini";
940
+ if (!key) {
941
+ console.warn("[AIProvider]: API Key \u0434\u043B\u044F OpenAI \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.");
942
+ return null;
943
+ }
813
944
  try {
814
945
  const messages = [];
815
946
  if (systemInstruction) {
@@ -820,15 +951,19 @@ var init_AIProvider = __esm({
820
951
  method: "POST",
821
952
  headers: {
822
953
  "Content-Type": "application/json",
823
- Authorization: `Bearer ${apiKey}`
954
+ Authorization: `Bearer ${key}`
824
955
  },
825
956
  body: JSON.stringify({
826
- model: modelName,
957
+ model,
827
958
  messages,
828
959
  temperature,
829
960
  ...jsonMode ? { response_format: { type: "json_object" } } : {}
830
961
  })
831
962
  });
963
+ if (!res.ok) {
964
+ const errData = await res.json().catch(() => ({}));
965
+ throw new Error(`OpenAI API status ${res.status}: ${JSON.stringify(errData)}`);
966
+ }
832
967
  const data = await res.json();
833
968
  const content = data.choices?.[0]?.message?.content?.trim() || "";
834
969
  if (jsonMode) {
@@ -843,7 +978,14 @@ var init_AIProvider = __esm({
843
978
  return null;
844
979
  }
845
980
  };
846
- }, [apiKey, provider, modelName]);
981
+ }, [
982
+ activeProvider,
983
+ geminiConfig.apiKey,
984
+ geminiConfig.modelName,
985
+ openaiConfig.apiKey,
986
+ openaiConfig.modelName,
987
+ loadSettingsFromDb
988
+ ]);
847
989
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AIContext.Provider, { value: contextValue, children });
848
990
  };
849
991
  useAI = () => {
@@ -857,7 +999,7 @@ var init_AIProvider = __esm({
857
999
  });
858
1000
 
859
1001
  // src/i18n/TranslationDialog.tsx
860
- var import_react5, import_material, import_AutoFixHigh, import_Translate, import_Lightbulb, import_jsx_runtime5, TranslationDialog;
1002
+ var import_react5, import_material, import_AutoFixHigh, import_Translate, import_Lightbulb, import_SmartToy, import_jsx_runtime5, TranslationDialog;
861
1003
  var init_TranslationDialog = __esm({
862
1004
  "src/i18n/TranslationDialog.tsx"() {
863
1005
  "use strict";
@@ -866,6 +1008,7 @@ var init_TranslationDialog = __esm({
866
1008
  import_AutoFixHigh = __toESM(require("@mui/icons-material/AutoFixHigh"));
867
1009
  import_Translate = __toESM(require("@mui/icons-material/Translate"));
868
1010
  import_Lightbulb = __toESM(require("@mui/icons-material/Lightbulb"));
1011
+ import_SmartToy = __toESM(require("@mui/icons-material/SmartToy"));
869
1012
  init_translation_service();
870
1013
  init_SupabaseProvider();
871
1014
  init_AIProvider();
@@ -878,10 +1021,11 @@ var init_TranslationDialog = __esm({
878
1021
  onSave
879
1022
  }) => {
880
1023
  const supabase = useSupabase();
881
- const { generate } = useAI();
1024
+ const { generate, activeProvider: defaultProvider } = useAI();
882
1025
  const [languages, setLanguages] = (0, import_react5.useState)([]);
883
1026
  const [values, setValues] = (0, import_react5.useState)({});
884
1027
  const [contextHint, setContextHint] = (0, import_react5.useState)("");
1028
+ const [selectedProvider, setSelectedProvider] = (0, import_react5.useState)("auto");
885
1029
  const [loading, setLoading] = (0, import_react5.useState)(false);
886
1030
  const [saving, setSaving] = (0, import_react5.useState)(false);
887
1031
  const [translatingCode, setTranslatingCode] = (0, import_react5.useState)(null);
@@ -918,6 +1062,7 @@ var init_TranslationDialog = __esm({
918
1062
  };
919
1063
  fetchData();
920
1064
  }, [open, translationKey, namespace]);
1065
+ const effectiveProvider = selectedProvider === "auto" ? void 0 : selectedProvider;
921
1066
  const handleAutoTranslate = async (e, targetLang) => {
922
1067
  e.preventDefault();
923
1068
  e.stopPropagation();
@@ -927,13 +1072,14 @@ var init_TranslationDialog = __esm({
927
1072
  const res = await translationService.translateSmart(supabase, generate, {
928
1073
  text: translationKey,
929
1074
  targetLang,
930
- contextHint
1075
+ contextHint,
1076
+ provider: effectiveProvider
931
1077
  });
932
1078
  if (res?.translatedText) {
933
1079
  setValues((prev) => ({ ...prev, [targetLang]: res.translatedText }));
934
1080
  }
935
1081
  } catch (err) {
936
- console.log(err);
1082
+ console.error(err);
937
1083
  setError(`Failed to translate into ${targetLang}. ${err?.message || ""}`);
938
1084
  } finally {
939
1085
  setTranslatingCode(null);
@@ -950,7 +1096,8 @@ var init_TranslationDialog = __esm({
950
1096
  const res = await translationService.translateSmart(supabase, generate, {
951
1097
  text: translationKey,
952
1098
  targetLangs,
953
- contextHint
1099
+ contextHint,
1100
+ provider: effectiveProvider
954
1101
  });
955
1102
  if (res?.translations) {
956
1103
  setValues((prev) => ({ ...prev, ...res.translations }));
@@ -993,36 +1140,76 @@ var init_TranslationDialog = __esm({
993
1140
  onClick: (e) => e.stopPropagation(),
994
1141
  onMouseDown: (e) => e.stopPropagation(),
995
1142
  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
- ] }),
1143
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1144
+ import_material.DialogTitle,
1145
+ {
1146
+ sx: { pb: 1, display: "flex", justifyContent: "space-between", alignItems: "flex-start" },
1147
+ children: [
1148
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.Box, { children: [
1149
+ "Context Translation",
1150
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1151
+ import_material.Typography,
1152
+ {
1153
+ component: "div",
1154
+ variant: "subtitle2",
1155
+ color: "text.secondary",
1156
+ sx: { wordBreak: "break-all", mt: 0.5 },
1157
+ children: [
1158
+ "Original: ",
1159
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("strong", { children: [
1160
+ '"',
1161
+ translationKey,
1162
+ '"'
1163
+ ] })
1164
+ ]
1165
+ }
1166
+ )
1167
+ ] }),
1168
+ !loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1169
+ import_material.Button,
1170
+ {
1171
+ type: "button",
1172
+ size: "small",
1173
+ variant: "outlined",
1174
+ color: "secondary",
1175
+ 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" }),
1176
+ onClick: handleTranslateAll,
1177
+ disabled: isBusy,
1178
+ sx: { flexShrink: 0, ml: 2, whiteSpace: "nowrap" },
1179
+ children: isTranslatingAll ? "Translating..." : "Translate All"
1180
+ }
1181
+ )
1182
+ ]
1183
+ }
1184
+ ),
1023
1185
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.DialogContent, { dividers: true, sx: { pt: 2 }, children: [
1024
1186
  error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.Alert, { severity: "error", onClose: () => setError(null), sx: { mb: 2 }, children: error }),
1025
1187
  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: [
1188
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.Box, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1189
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
1190
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_SmartToy.default, { fontSize: "small", color: "action" }),
1191
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.Typography, { variant: "caption", sx: { fontWeight: 600, color: "text.secondary" }, children: "AI Engine:" })
1192
+ ] }),
1193
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1194
+ import_material.ToggleButtonGroup,
1195
+ {
1196
+ size: "small",
1197
+ value: selectedProvider,
1198
+ exclusive: true,
1199
+ onChange: (_, val) => val && setSelectedProvider(val),
1200
+ disabled: isBusy,
1201
+ children: [
1202
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.ToggleButton, { value: "auto", sx: { px: 1.5, py: 0.2, fontSize: "0.75rem" }, children: [
1203
+ "Auto (",
1204
+ defaultProvider,
1205
+ ")"
1206
+ ] }),
1207
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.ToggleButton, { value: "gemini", sx: { px: 1.5, py: 0.2, fontSize: "0.75rem" }, children: "Gemini" }),
1208
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.ToggleButton, { value: "openai", sx: { px: 1.5, py: 0.2, fontSize: "0.75rem" }, children: "OpenAI" })
1209
+ ]
1210
+ }
1211
+ )
1212
+ ] }),
1026
1213
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.Paper, { variant: "outlined", sx: { p: 2, bgcolor: "action.hover", borderRadius: 2 }, children: [
1027
1214
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1 }, children: [
1028
1215
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_Lightbulb.default, { color: "warning", fontSize: "small" }),
@@ -1071,7 +1258,7 @@ var init_TranslationDialog = __esm({
1071
1258
  disabled: isBusy
1072
1259
  }
1073
1260
  ),
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)(
1261
+ /* @__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
1262
  import_material.IconButton,
1076
1263
  {
1077
1264
  type: "button",
@@ -1086,16 +1273,7 @@ var init_TranslationDialog = __esm({
1086
1273
  ] }),
1087
1274
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_material.DialogActions, { sx: { px: 3, py: 2 }, children: [
1088
1275
  /* @__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
- )
1276
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material.Button, { onClick: handleSaveAll, variant: "contained", disabled: isBusy, type: "button", children: saving ? "Saving..." : "Save All" })
1099
1277
  ] })
1100
1278
  ]
1101
1279
  }
@@ -4542,9 +4720,7 @@ var SwitchComponent = ({
4542
4720
  }) => {
4543
4721
  const currentLabel = label ?? (checked ? checkedLabel : uncheckedLabel);
4544
4722
  const renderLabel = (node) => {
4545
- console.log(node);
4546
4723
  if (typeof node === "string") {
4547
- console.log("string");
4548
4724
  return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(TranslateText, { overrideNamespace, children: node });
4549
4725
  }
4550
4726
  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-7WZQFX6U.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-Z7YPC3AV.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.14",
4
4
  "description": "DOCS.UA react packages",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",