diff --git a/app/config/server.ts b/app/config/server.ts
index 676b0174f..6544fe564 100644
--- a/app/config/server.ts
+++ b/app/config/server.ts
@@ -154,8 +154,8 @@ export const getServerSideConfig = () => {
// `[Server Config] using ${randomIndex + 1} of ${apiKeys.length} api key`,
// );
- const allowedWebDevEndpoints = (
- process.env.WHITE_WEBDEV_ENDPOINTS ?? ""
+ const allowedWebDavEndpoints = (
+ process.env.WHITE_WEBDAV_ENDPOINTS ?? ""
).split(",");
return {
@@ -229,6 +229,6 @@ export const getServerSideConfig = () => {
disableFastLink: !!process.env.DISABLE_FAST_LINK,
customModels,
defaultModel,
- allowedWebDevEndpoints,
+ allowedWebDavEndpoints,
};
};
diff --git a/app/constant.ts b/app/constant.ts
index 3d33a047e..a54a973da 100644
--- a/app/constant.ts
+++ b/app/constant.ts
@@ -1,5 +1,3 @@
-import path from "path";
-
export const OWNER = "ChatGPTNextWeb";
export const REPO = "ChatGPT-Next-Web";
export const REPO_URL = `https://github.com/${OWNER}/${REPO}`;
@@ -152,6 +150,7 @@ export const Anthropic = {
export const OpenaiPath = {
ChatPath: "v1/chat/completions",
+ SpeechPath: "v1/audio/speech",
ImagePath: "v1/images/generations",
UsagePath: "dashboard/billing/usage",
SubsPath: "dashboard/billing/subscription",
@@ -258,6 +257,20 @@ export const KnowledgeCutOffDate: Record
= {
"gemini-pro-vision": "2023-12",
};
+export const DEFAULT_TTS_ENGINE = "OpenAI-TTS";
+export const DEFAULT_TTS_ENGINES = ["OpenAI-TTS", "Edge-TTS"];
+export const DEFAULT_TTS_MODEL = "tts-1";
+export const DEFAULT_TTS_VOICE = "alloy";
+export const DEFAULT_TTS_MODELS = ["tts-1", "tts-1-hd"];
+export const DEFAULT_TTS_VOICES = [
+ "alloy",
+ "echo",
+ "fable",
+ "onyx",
+ "nova",
+ "shimmer",
+];
+
const openaiModels = [
"gpt-3.5-turbo",
"gpt-3.5-turbo-1106",
@@ -279,7 +292,7 @@ const openaiModels = [
"gpt-4-1106-preview",
"dall-e-3",
"o1-mini",
- "o1-preview"
+ "o1-preview",
];
const googleModels = [
@@ -488,3 +501,6 @@ export const PLUGINS = [
{ name: "Stable Diffusion", path: Path.Sd },
{ name: "Search Chat", path: Path.SearchChat },
];
+
+export const SAAS_CHAT_URL = "https://nextchat.dev/chat";
+export const SAAS_CHAT_UTM_URL = "https://nextchat.dev/chat?utm=github";
diff --git a/app/icons/arrow.svg b/app/icons/arrow.svg
new file mode 100644
index 000000000..ddd69e614
--- /dev/null
+++ b/app/icons/arrow.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/icons/fire.svg b/app/icons/fire.svg
new file mode 100644
index 000000000..446d532aa
--- /dev/null
+++ b/app/icons/fire.svg
@@ -0,0 +1 @@
+
diff --git a/app/icons/logo.svg b/app/icons/logo.svg
new file mode 100644
index 000000000..b80263b86
--- /dev/null
+++ b/app/icons/logo.svg
@@ -0,0 +1,19 @@
+
diff --git a/app/icons/speak-stop.svg b/app/icons/speak-stop.svg
new file mode 100644
index 000000000..926ae7bb3
--- /dev/null
+++ b/app/icons/speak-stop.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/icons/speak.svg b/app/icons/speak.svg
new file mode 100644
index 000000000..e02212c9a
--- /dev/null
+++ b/app/icons/speak.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/icons/voice-white.svg b/app/icons/voice-white.svg
new file mode 100644
index 000000000..e7d5cbcc8
--- /dev/null
+++ b/app/icons/voice-white.svg
@@ -0,0 +1,16 @@
+
diff --git a/app/locales/ar.ts b/app/locales/ar.ts
index 9bd491083..e2fad3494 100644
--- a/app/locales/ar.ts
+++ b/app/locales/ar.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const ar: PartialLocaleType = {
WIP: "قريبًا...",
Error: {
Unauthorized: isApp
- ? "تم اكتشاف مفتاح API غير صالح، يرجى الذهاب إلى [الإعدادات](/#/settings) للتحقق من صحة مفتاح API."
- : "كلمة المرور غير صحيحة أو فارغة، يرجى الذهاب إلى [تسجيل الدخول](/#/auth) لإدخال كلمة مرور صحيحة، أو أدخل مفتاح OpenAI API الخاص بك في [الإعدادات](/#/settings).",
+ ? `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق:
+ \\ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️`
+ : `😆 واجهت المحادثة بعض المشكلات، لا داعي للقلق:
+ \ 1️⃣ إذا كنت ترغب في تجربة دون إعداد، [انقر هنا لبدء المحادثة فورًا 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ إذا كنت تستخدم إصدار النشر الخاص، انقر [هنا](/#/auth) لإدخال مفتاح الوصول 🔑
+ \ 3️⃣ إذا كنت تريد استخدام موارد OpenAI الخاصة بك، انقر [هنا](/#/settings) لتعديل الإعدادات ⚙️
+ `,
},
Auth: {
Title: "تحتاج إلى كلمة مرور",
@@ -18,6 +24,10 @@ const ar: PartialLocaleType = {
Input: "أدخل رمز الوصول هنا",
Confirm: "تأكيد",
Later: "في وقت لاحق",
+ Return: "عودة",
+ SaasTips: "الإعدادات معقدة، أريد استخدامه على الفور",
+ TopTips:
+ "🥳 عرض NextChat AI الأول، افتح الآن OpenAI o1, GPT-4o, Claude-3.5 وأحدث النماذج الكبيرة",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} محادثة`,
@@ -43,6 +53,8 @@ const ar: PartialLocaleType = {
PinToastAction: "عرض",
Delete: "حذف",
Edit: "تحرير",
+ RefreshTitle: "تحديث العنوان",
+ RefreshToast: "تم إرسال طلب تحديث العنوان",
},
Commands: {
new: "دردشة جديدة",
@@ -279,6 +291,13 @@ const ar: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "استخدام NextChat AI",
+ Label: "(أفضل حل من حيث التكلفة)",
+ SubTitle:
+ "مدعوم رسميًا من NextChat، جاهز للاستخدام بدون إعداد، يدعم أحدث النماذج الكبيرة مثل OpenAI o1 و GPT-4o و Claude-3.5",
+ ChatNow: "الدردشة الآن",
+ },
AccessCode: {
Title: "كلمة المرور للوصول",
SubTitle: "قام المشرف بتمكين الوصول المشفر",
@@ -404,6 +423,10 @@ const ar: PartialLocaleType = {
},
Model: "النموذج",
+ CompressModel: {
+ Title: "نموذج الضغط",
+ SubTitle: "النموذج المستخدم لضغط السجل التاريخي",
+ },
Temperature: {
Title: "العشوائية (temperature)",
SubTitle: "كلما زادت القيمة، زادت العشوائية في الردود",
diff --git a/app/locales/bn.ts b/app/locales/bn.ts
index acabc8e2a..f52f101ce 100644
--- a/app/locales/bn.ts
+++ b/app/locales/bn.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const bn: PartialLocaleType = {
WIP: "শীঘ্রই আসছে...",
Error: {
Unauthorized: isApp
- ? "অবৈধ API কী সনাক্ত করা হয়েছে, অনুগ্রহ করে [সেটিংস](/#/settings) পৃষ্ঠায় যান এবং নিশ্চিত করুন যে API কী সঠিকভাবে কনফিগার করা হয়েছে।"
- : "অ্যাক্সেস পাসওয়ার্ড সঠিক নয় বা খালি, অনুগ্রহ করে [লগইন](/#/auth) পৃষ্ঠায় যান এবং সঠিক অ্যাক্সেস পাসওয়ার্ড প্রবেশ করান, অথবা [সেটিংস](/#/settings) পৃষ্ঠায় আপনার OpenAI API কী প্রবেশ করান।",
+ ? `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই:
+ \\ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️`
+ : `😆 কথোপকথনে কিছু সমস্যা হয়েছে, চিন্তার কিছু নেই:
+ \ 1️⃣ যদি আপনি শূন্য কনফিগারেশনে শুরু করতে চান, তাহলে [এখানে ক্লিক করে অবিলম্বে কথোপকথন শুরু করুন 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ যদি আপনি একটি প্রাইভেট ডেপ্লয়মেন্ট সংস্করণ ব্যবহার করেন, তাহলে [এখানে ক্লিক করুন](/#/auth) প্রবেশাধিকার কীগুলি প্রবেশ করতে 🔑
+ \ 3️⃣ যদি আপনি আপনার নিজস্ব OpenAI সম্পদ ব্যবহার করতে চান, তাহলে [এখানে ক্লিক করুন](/#/settings) সেটিংস পরিবর্তন করতে ⚙️
+ `,
},
Auth: {
Title: "পাসওয়ার্ড প্রয়োজন",
@@ -18,6 +24,10 @@ const bn: PartialLocaleType = {
Input: "এখানে অ্যাক্সেস কোড লিখুন",
Confirm: "নিশ্চিত করুন",
Later: "পরে বলুন",
+ Return: "ফিরে আসা",
+ SaasTips: "কনফিগারেশন খুব কঠিন, আমি অবিলম্বে ব্যবহার করতে চাই",
+ TopTips:
+ "🥳 NextChat AI প্রথম প্রকাশের অফার, এখনই OpenAI o1, GPT-4o, Claude-3.5 এবং সর্বশেষ বড় মডেলগুলি আনলক করুন",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} টি চ্যাট`,
@@ -43,6 +53,8 @@ const bn: PartialLocaleType = {
PinToastAction: "দেখুন",
Delete: "মুছে ফেলুন",
Edit: "সম্পাদনা করুন",
+ RefreshTitle: "শিরোনাম রিফ্রেশ করুন",
+ RefreshToast: "শিরোনাম রিফ্রেশ অনুরোধ পাঠানো হয়েছে",
},
Commands: {
new: "নতুন চ্যাট",
@@ -282,6 +294,14 @@ const bn: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "NextChat AI ব্যবহার করুন",
+ Label: "(সেরা মূল্যসাশ্রয়ী সমাধান)",
+ SubTitle:
+ "NextChat কর্তৃক অফিসিয়াল রক্ষণাবেক্ষণ, শূন্য কনফিগারেশন ব্যবহার শুরু করুন, OpenAI o1, GPT-4o, Claude-3.5 সহ সর্বশেষ বড় মডেলগুলি সমর্থন করে",
+ ChatNow: "এখনই চ্যাট করুন",
+ },
+
AccessCode: {
Title: "অ্যাক্সেস পাসওয়ার্ড",
SubTitle: "অ্যাডমিন এনক্রিপ্টেড অ্যাক্সেস সক্রিয় করেছেন",
@@ -411,6 +431,10 @@ const bn: PartialLocaleType = {
},
Model: "মডেল (model)",
+ CompressModel: {
+ Title: "সংকোচন মডেল",
+ SubTitle: "ইতিহাস সংকুচিত করার জন্য ব্যবহৃত মডেল",
+ },
Temperature: {
Title: "যাদুকরিতা (temperature)",
SubTitle: "মান বাড়ালে উত্তর বেশি এলোমেলো হবে",
diff --git a/app/locales/cn.ts b/app/locales/cn.ts
index 6af0d8433..83fcef5d1 100644
--- a/app/locales/cn.ts
+++ b/app/locales/cn.ts
@@ -1,6 +1,6 @@
-import { ShortcutKeyModal } from "../components/chat";
import { getClientConfig } from "../config/client";
import { SubmitKey } from "../store/config";
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
@@ -8,16 +8,26 @@ const cn = {
WIP: "该功能仍在开发中……",
Error: {
Unauthorized: isApp
- ? "检测到无效 API Key,请前往[设置](/#/settings)页检查 API Key 是否配置正确。"
- : "访问密码不正确或为空,请前往[登录](/#/auth)页输入正确的访问密码,或者在[设置](/#/settings)页填入你自己的 OpenAI API Key。",
+ ? `😆 对话遇到了一些问题,不用慌:
+ \\ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️`
+ : `😆 对话遇到了一些问题,不用慌:
+ \ 1️⃣ 想要零配置开箱即用,[点击这里立刻开启对话 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ 如果你正在使用私有部署版本,点击[这里](/#/auth)输入访问秘钥 🔑
+ \ 3️⃣ 如果你想消耗自己的 OpenAI 资源,点击[这里](/#/settings)修改设置 ⚙️
+ `,
},
Auth: {
+ Return: "返回",
Title: "需要密码",
Tips: "管理员开启了密码验证,请在下方填入访问码",
- SubTips: "或者输入你的 OpenAI 或 Google API 密钥",
+ SubTips: "或者输入你的 OpenAI 或 Google AI 密钥",
Input: "在此处填写访问码",
Confirm: "确认",
Later: "稍后再说",
+ SaasTips: "配置太麻烦,想要立即使用",
+ TopTips:
+ "🥳 NextChat AI 首发优惠,立刻解锁 OpenAI o1, GPT-4o, Claude-3.5 等最新大模型",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} 条对话`,
@@ -44,6 +54,10 @@ const cn = {
Delete: "删除",
Edit: "编辑",
FullScreen: "全屏",
+ RefreshTitle: "刷新标题",
+ RefreshToast: "已发送刷新标题请求",
+ Speech: "朗读",
+ StopSpeech: "停止",
},
Commands: {
new: "新建聊天",
@@ -52,6 +66,7 @@ const cn = {
next: "下一个聊天",
prev: "上一个聊天",
clear: "清除上下文",
+ fork: "复制聊天",
del: "删除聊天",
},
InputActions: {
@@ -78,6 +93,8 @@ const cn = {
return inputHints + ",/ 触发补全,: 触发命令";
},
Send: "发送",
+ StartSpeak: "说话",
+ StopSpeak: "停止",
Config: {
Reset: "清除记忆",
SaveAs: "存为面具",
@@ -296,6 +313,13 @@ const cn = {
},
Access: {
+ SaasStart: {
+ Title: "使用 NextChat AI",
+ Label: "(性价比最高的方案)",
+ SubTitle:
+ "由 NextChat 官方维护, 零配置开箱即用,支持 OpenAI o1, GPT-4o, Claude-3.5 等最新大模型",
+ ChatNow: "立刻对话",
+ },
AccessCode: {
Title: "访问密码",
SubTitle: "管理员已开启加密访问",
@@ -359,7 +383,7 @@ const cn = {
ApiKey: {
Title: "API 密钥",
SubTitle: "从 Google AI 获取您的 API 密钥",
- Placeholder: "输入您的 Google AI Studio API 密钥",
+ Placeholder: "Google AI API KEY",
},
Endpoint: {
@@ -475,6 +499,10 @@ const cn = {
},
Model: "模型 (model)",
+ CompressModel: {
+ Title: "压缩模型",
+ SubTitle: "用于压缩历史记录的模型",
+ },
Temperature: {
Title: "随机性 (temperature)",
SubTitle: "值越大,回复越随机",
@@ -495,6 +523,26 @@ const cn = {
Title: "频率惩罚度 (frequency_penalty)",
SubTitle: "值越大,越有可能降低重复字词",
},
+ TTS: {
+ Enable: {
+ Title: "启用文本转语音",
+ SubTitle: "启用文本生成语音服务",
+ },
+ Autoplay: {
+ Title: "启用自动朗读",
+ SubTitle: "自动生成语音并播放,需先开启文本转语音开关",
+ },
+ Model: "模型",
+ Engine: "转换引擎",
+ Voice: {
+ Title: "声音",
+ SubTitle: "生成语音时使用的声音",
+ },
+ Speed: {
+ Title: "速度",
+ SubTitle: "生成语音的速度",
+ },
+ },
},
Store: {
DefaultTopic: "新的聊天",
diff --git a/app/locales/cs.ts b/app/locales/cs.ts
index d16c474e8..d62a20367 100644
--- a/app/locales/cs.ts
+++ b/app/locales/cs.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const cs: PartialLocaleType = {
WIP: "V přípravě...",
Error: {
Unauthorized: isApp
- ? "Byl zjištěn neplatný API Key, prosím přejděte na stránku [Nastavení](/#/settings) a zkontrolujte, zda je API Key správně nakonfigurován."
- : "Heslo je nesprávné nebo prázdné, prosím přejděte na stránku [Přihlášení](/#/auth) a zadejte správné heslo, nebo na stránku [Nastavení](/#/settings) a zadejte svůj vlastní OpenAI API Key.",
+ ? `😆 Rozhovor narazil na nějaké problémy, nebojte se:
+ \\ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️`
+ : `😆 Rozhovor narazil na nějaké problémy, nebojte se:
+ \ 1️⃣ Pokud chcete začít bez konfigurace, [klikněte sem pro okamžitý začátek chatu 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ Pokud používáte verzi soukromého nasazení, klikněte [sem](/#/auth) a zadejte přístupový klíč 🔑
+ \ 3️⃣ Pokud chcete využít své vlastní zdroje OpenAI, klikněte [sem](/#/settings) a upravte nastavení ⚙️
+ `,
},
Auth: {
Title: "Potřebné heslo",
@@ -18,6 +24,10 @@ const cs: PartialLocaleType = {
Input: "Zadejte přístupový kód zde",
Confirm: "Potvrdit",
Later: "Později",
+ Return: "Návrat",
+ SaasTips: "Konfigurace je příliš složitá, chci okamžitě začít používat",
+ TopTips:
+ "🥳 Uvítací nabídka NextChat AI, okamžitě odemkněte OpenAI o1, GPT-4o, Claude-3.5 a nejnovější velké modely",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} konverzací`,
@@ -43,6 +53,8 @@ const cs: PartialLocaleType = {
PinToastAction: "Zobrazit",
Delete: "Smazat",
Edit: "Upravit",
+ RefreshTitle: "Obnovit název",
+ RefreshToast: "Požadavek na obnovení názvu byl odeslán",
},
Commands: {
new: "Nová konverzace",
@@ -282,6 +294,14 @@ const cs: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "Použití NextChat AI",
+ Label: "(Nejlepší nákladově efektivní řešení)",
+ SubTitle:
+ "Oficiálně udržováno NextChat, připraveno k použití bez konfigurace, podporuje nejnovější velké modely jako OpenAI o1, GPT-4o, Claude-3.5",
+ ChatNow: "Začněte chatovat nyní",
+ },
+
AccessCode: {
Title: "Přístupový kód",
SubTitle: "Administrátor aktivoval šifrovaný přístup",
@@ -410,6 +430,10 @@ const cs: PartialLocaleType = {
},
Model: "Model (model)",
+ CompressModel: {
+ Title: "Kompresní model",
+ SubTitle: "Model používaný pro kompresi historie",
+ },
Temperature: {
Title: "Náhodnost (temperature)",
SubTitle: "Čím vyšší hodnota, tím náhodnější odpovědi",
diff --git a/app/locales/de.ts b/app/locales/de.ts
index a1f817047..3490190a8 100644
--- a/app/locales/de.ts
+++ b/app/locales/de.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const de: PartialLocaleType = {
WIP: "In Bearbeitung...",
Error: {
Unauthorized: isApp
- ? "Ungültiger API-Schlüssel erkannt. Bitte gehen Sie zur [Einstellungen](/#/settings) Seite, um zu überprüfen, ob der API-Schlüssel korrekt konfiguriert ist."
- : "Das Passwort ist falsch oder leer. Bitte gehen Sie zur [Login](/#/auth) Seite, um das richtige Passwort einzugeben, oder fügen Sie Ihren OpenAI API-Schlüssel auf der [Einstellungen](/#/settings) Seite hinzu.",
+ ? `😆 Das Gespräch hatte einige Probleme, keine Sorge:
+ \\ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️`
+ : `😆 Das Gespräch hatte einige Probleme, keine Sorge:
+ \ 1️⃣ Wenn du ohne Konfiguration sofort starten möchtest, [klicke hier, um sofort zu chatten 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ Wenn du eine private Bereitstellung verwendest, klicke [hier](/#/auth), um den Zugriffsschlüssel einzugeben 🔑
+ \ 3️⃣ Wenn du deine eigenen OpenAI-Ressourcen verwenden möchtest, klicke [hier](/#/settings), um die Einstellungen zu ändern ⚙️
+ `,
},
Auth: {
Title: "Passwort erforderlich",
@@ -18,6 +24,11 @@ const de: PartialLocaleType = {
Input: "Geben Sie hier den Zugangscode ein",
Confirm: "Bestätigen",
Later: "Später",
+ Return: "Zurück",
+ SaasTips:
+ "Die Konfiguration ist zu kompliziert, ich möchte es sofort nutzen",
+ TopTips:
+ "🥳 NextChat AI Einführungsangebot, schalte jetzt OpenAI o1, GPT-4o, Claude-3.5 und die neuesten großen Modelle frei",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} Gespräche`,
@@ -43,6 +54,8 @@ const de: PartialLocaleType = {
PinToastAction: "Ansehen",
Delete: "Löschen",
Edit: "Bearbeiten",
+ RefreshTitle: "Titel aktualisieren",
+ RefreshToast: "Anfrage zur Titelaktualisierung gesendet",
},
Commands: {
new: "Neues Gespräch",
@@ -289,6 +302,14 @@ const de: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "NextChat AI verwenden",
+ Label: "(Die kosteneffektivste Lösung)",
+ SubTitle:
+ "Offiziell von NextChat verwaltet, sofort einsatzbereit ohne Konfiguration, unterstützt die neuesten großen Modelle wie OpenAI o1, GPT-4o und Claude-3.5",
+ ChatNow: "Jetzt chatten",
+ },
+
AccessCode: {
Title: "Zugangscode",
SubTitle:
@@ -421,6 +442,10 @@ const de: PartialLocaleType = {
},
Model: "Modell",
+ CompressModel: {
+ Title: "Kompressionsmodell",
+ SubTitle: "Modell zur Komprimierung des Verlaufs",
+ },
Temperature: {
Title: "Zufälligkeit (temperature)",
SubTitle: "Je höher der Wert, desto zufälliger die Antwort",
diff --git a/app/locales/en.ts b/app/locales/en.ts
index 32cd2d58b..b6e7ed367 100644
--- a/app/locales/en.ts
+++ b/app/locales/en.ts
@@ -1,7 +1,7 @@
import { getClientConfig } from "../config/client";
import { SubmitKey } from "../store/config";
import { LocaleType } from "./index";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
// if you are adding a new translation, please use PartialLocaleType instead of LocaleType
const isApp = !!getClientConfig()?.isApp;
@@ -9,16 +9,26 @@ const en: LocaleType = {
WIP: "Coming Soon...",
Error: {
Unauthorized: isApp
- ? "Invalid API Key, please check it in [Settings](/#/settings) page."
- : "Unauthorized access, please enter access code in [auth](/#/auth) page, or enter your OpenAI API Key.",
+ ? `😆 Oops, there's an issue. No worries:
+ \\ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️`
+ : `😆 Oops, there's an issue. Let's fix it:
+ \ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ Using a private setup? [Click here](/#/auth) to enter your key 🔑
+ \ 3️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️
+ `,
},
Auth: {
+ Return: "Return",
Title: "Need Access Code",
Tips: "Please enter access code below",
SubTips: "Or enter your OpenAI or Google API Key",
Input: "access code",
Confirm: "Confirm",
Later: "Later",
+ SaasTips: "Too Complex, Use Immediately Now",
+ TopTips:
+ "🥳 NextChat AI launch promotion: Instantly unlock the latest models like OpenAI o1, GPT-4o, Claude-3.5!",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} messages`,
@@ -45,6 +55,10 @@ const en: LocaleType = {
Delete: "Delete",
Edit: "Edit",
FullScreen: "FullScreen",
+ RefreshTitle: "Refresh Title",
+ RefreshToast: "Title refresh request sent",
+ Speech: "Play",
+ StopSpeech: "Stop",
},
Commands: {
new: "Start a new chat",
@@ -53,6 +67,7 @@ const en: LocaleType = {
next: "Next Chat",
prev: "Previous Chat",
clear: "Clear Context",
+ fork: "Copy Chat",
del: "Delete Chat",
},
InputActions: {
@@ -79,6 +94,8 @@ const en: LocaleType = {
return inputHints + ", / to search prompts, : to use commands";
},
Send: "Send",
+ StartSpeak: "Start Speak",
+ StopSpeak: "Stop Speak",
Config: {
Reset: "Reset to Default",
SaveAs: "Save as Mask",
@@ -300,6 +317,14 @@ const en: LocaleType = {
NoAccess: "Enter API Key to check balance",
},
Access: {
+ SaasStart: {
+ Title: "Use NextChat AI",
+ Label: " (Most Cost-Effective Option)",
+ SubTitle:
+ "Maintained by NextChat, zero setup needed, unlock OpenAI o1, GPT-4o," +
+ " Claude-3.5 and more",
+ ChatNow: "Start Now",
+ },
AccessCode: {
Title: "Access Code",
SubTitle: "Access control Enabled",
@@ -460,7 +485,7 @@ const en: LocaleType = {
ApiKey: {
Title: "API Key",
SubTitle: "Obtain your API Key from Google AI",
- Placeholder: "Enter your Google AI Studio API Key",
+ Placeholder: "Google AI API Key",
},
Endpoint: {
@@ -480,6 +505,10 @@ const en: LocaleType = {
},
Model: "Model",
+ CompressModel: {
+ Title: "Compression Model",
+ SubTitle: "Model used to compress history",
+ },
Temperature: {
Title: "Temperature",
SubTitle: "A larger value makes the more random output",
@@ -502,6 +531,27 @@ const en: LocaleType = {
SubTitle:
"A larger value decreasing the likelihood to repeat the same line",
},
+ TTS: {
+ Enable: {
+ Title: "Enable TTS",
+ SubTitle: "Enable text-to-speech service",
+ },
+ Autoplay: {
+ Title: "Enable Autoplay",
+ SubTitle:
+ "Automatically generate speech and play, you need to enable the text-to-speech switch first",
+ },
+ Model: "Model",
+ Voice: {
+ Title: "Voice",
+ SubTitle: "The voice to use when generating the audio",
+ },
+ Speed: {
+ Title: "Speed",
+ SubTitle: "The speed of the generated audio",
+ },
+ Engine: "TTS Engine",
+ },
},
Store: {
DefaultTopic: "New Conversation",
diff --git a/app/locales/es.ts b/app/locales/es.ts
index 5e4f900b7..03af9b439 100644
--- a/app/locales/es.ts
+++ b/app/locales/es.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const es: PartialLocaleType = {
WIP: "En construcción...",
Error: {
Unauthorized: isApp
- ? "Se detectó una clave API inválida. Por favor, ve a la página de [Configuración](/#/settings) para verificar si la clave API está configurada correctamente."
- : "La contraseña de acceso es incorrecta o está vacía. Por favor, ve a la página de [Iniciar sesión](/#/auth) para ingresar la contraseña correcta, o en la página de [Configuración](/#/settings) para introducir tu propia clave API de OpenAI.",
+ ? `😆 La conversación encontró algunos problemas, no te preocupes:
+ \\ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️`
+ : `😆 La conversación encontró algunos problemas, no te preocupes:
+ \ 1️⃣ Si deseas comenzar sin configuración, [haz clic aquí para empezar a chatear inmediatamente 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ Si estás utilizando una versión de implementación privada, haz clic [aquí](/#/auth) para ingresar la clave de acceso 🔑
+ \ 3️⃣ Si deseas usar tus propios recursos de OpenAI, haz clic [aquí](/#/settings) para modificar la configuración ⚙️
+ `,
},
Auth: {
Title: "Se requiere contraseña",
@@ -18,6 +24,11 @@ const es: PartialLocaleType = {
Input: "Introduce el código de acceso aquí",
Confirm: "Confirmar",
Later: "Más tarde",
+ Return: "Regresar",
+ SaasTips:
+ "La configuración es demasiado complicada, quiero usarlo de inmediato",
+ TopTips:
+ "🥳 Oferta de lanzamiento de NextChat AI, desbloquea OpenAI o1, GPT-4o, Claude-3.5 y los últimos grandes modelos",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} conversaciones`,
@@ -44,6 +55,8 @@ const es: PartialLocaleType = {
PinToastAction: "Ver",
Delete: "Eliminar",
Edit: "Editar",
+ RefreshTitle: "Actualizar título",
+ RefreshToast: "Se ha enviado la solicitud de actualización del título",
},
Commands: {
new: "Nueva conversación",
@@ -292,6 +305,14 @@ const es: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "Use NextChat AI",
+ Label: "(The most cost-effective solution)",
+ SubTitle:
+ "Officially maintained by NextChat, zero configuration ready to use, supports the latest large models like OpenAI o1, GPT-4o, and Claude-3.5",
+ ChatNow: "Chat Now",
+ },
+
AccessCode: {
Title: "Contraseña de acceso",
SubTitle: "El administrador ha habilitado el acceso encriptado",
@@ -423,6 +444,10 @@ const es: PartialLocaleType = {
},
Model: "Modelo (model)",
+ CompressModel: {
+ Title: "Modelo de compresión",
+ SubTitle: "Modelo utilizado para comprimir el historial",
+ },
Temperature: {
Title: "Aleatoriedad (temperature)",
SubTitle: "Cuanto mayor sea el valor, más aleatorio será el resultado",
diff --git a/app/locales/fr.ts b/app/locales/fr.ts
index 65efc32b8..d25c60eb6 100644
--- a/app/locales/fr.ts
+++ b/app/locales/fr.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const fr: PartialLocaleType = {
WIP: "Prochainement...",
Error: {
Unauthorized: isApp
- ? "Clé API invalide détectée. Veuillez vérifier si la clé API est correctement configurée dans la page [Paramètres](/#/settings)."
- : "Le mot de passe d'accès est incorrect ou manquant. Veuillez entrer le mot de passe d'accès correct sur la page [Connexion](/#/auth) ou entrer votre propre clé API OpenAI sur la page [Paramètres](/#/settings).",
+ ? `😆 La conversation a rencontré quelques problèmes, pas de panique :
+ \\ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️`
+ : `😆 La conversation a rencontré quelques problèmes, pas de panique :
+ \ 1️⃣ Si vous souhaitez commencer sans configuration, [cliquez ici pour démarrer la conversation immédiatement 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ Si vous utilisez une version déployée privée, cliquez [ici](/#/auth) pour entrer la clé d'accès 🔑
+ \ 3️⃣ Si vous souhaitez utiliser vos propres ressources OpenAI, cliquez [ici](/#/settings) pour modifier les paramètres ⚙️
+ `,
},
Auth: {
Title: "Mot de passe requis",
@@ -18,6 +24,11 @@ const fr: PartialLocaleType = {
Input: "Entrez le code d'accès ici",
Confirm: "Confirmer",
Later: "Plus tard",
+ Return: "Retour",
+ SaasTips:
+ "La configuration est trop compliquée, je veux l'utiliser immédiatement",
+ TopTips:
+ "🥳 Offre de lancement NextChat AI, débloquez OpenAI o1, GPT-4o, Claude-3.5 et les derniers grands modèles",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} conversations`,
@@ -43,6 +54,8 @@ const fr: PartialLocaleType = {
PinToastAction: "Voir",
Delete: "Supprimer",
Edit: "Modifier",
+ RefreshTitle: "Actualiser le titre",
+ RefreshToast: "Demande d'actualisation du titre envoyée",
},
Commands: {
new: "Nouvelle discussion",
@@ -292,6 +305,14 @@ const fr: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "Utiliser NextChat AI",
+ Label: "(La solution la plus rentable)",
+ SubTitle:
+ "Officiellement maintenu par NextChat, prêt à l'emploi sans configuration, prend en charge les derniers grands modèles comme OpenAI o1, GPT-4o et Claude-3.5",
+ ChatNow: "Discuter maintenant",
+ },
+
AccessCode: {
Title: "Mot de passe d'accès",
SubTitle: "L'administrateur a activé l'accès sécurisé",
@@ -422,6 +443,10 @@ const fr: PartialLocaleType = {
},
Model: "Modèle",
+ CompressModel: {
+ Title: "Modèle de compression",
+ SubTitle: "Modèle utilisé pour compresser l'historique",
+ },
Temperature: {
Title: "Aléatoire (temperature)",
SubTitle: "Plus la valeur est élevée, plus les réponses sont aléatoires",
diff --git a/app/locales/id.ts b/app/locales/id.ts
index 3ac7af490..af96fd272 100644
--- a/app/locales/id.ts
+++ b/app/locales/id.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const id: PartialLocaleType = {
WIP: "Coming Soon...",
Error: {
Unauthorized: isApp
- ? "API Key tidak valid terdeteksi, silakan periksa apakah API Key telah dikonfigurasi dengan benar di halaman [Pengaturan](/#/settings)."
- : "Kata sandi akses tidak benar atau kosong, silakan masukkan kata sandi akses yang benar di halaman [Masuk](/#/auth), atau masukkan OpenAI API Key Anda di halaman [Pengaturan](/#/settings).",
+ ? `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir:
+ \\ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️`
+ : `😆 Percakapan mengalami beberapa masalah, tidak perlu khawatir:
+ \ 1️⃣ Jika Anda ingin memulai tanpa konfigurasi, [klik di sini untuk mulai mengobrol segera 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ Jika Anda menggunakan versi penyebaran pribadi, klik [di sini](/#/auth) untuk memasukkan kunci akses 🔑
+ \ 3️⃣ Jika Anda ingin menggunakan sumber daya OpenAI Anda sendiri, klik [di sini](/#/settings) untuk mengubah pengaturan ⚙️
+`,
},
Auth: {
Title: "Kebutuhan Kata Sandi",
@@ -18,6 +24,10 @@ const id: PartialLocaleType = {
Input: "Masukkan kode akses di sini",
Confirm: "Konfirmasi",
Later: "Nanti",
+ Return: "Kembali",
+ SaasTips: "Konfigurasi terlalu rumit, saya ingin menggunakannya segera",
+ TopTips:
+ "🥳 Penawaran Peluncuran NextChat AI, buka OpenAI o1, GPT-4o, Claude-3.5 dan model besar terbaru sekarang",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} percakapan`,
@@ -43,6 +53,8 @@ const id: PartialLocaleType = {
PinToastAction: "Lihat",
Delete: "Hapus",
Edit: "Edit",
+ RefreshTitle: "Segarkan Judul",
+ RefreshToast: "Permintaan penyegaran judul telah dikirim",
},
Commands: {
new: "Obrolan Baru",
@@ -283,6 +295,14 @@ const id: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "Gunakan NextChat AI",
+ Label: "(Solusi paling hemat biaya)",
+ SubTitle:
+ "Dikelola secara resmi oleh NextChat, siap digunakan tanpa konfigurasi, mendukung model besar terbaru seperti OpenAI o1, GPT-4o, dan Claude-3.5",
+ ChatNow: "Chat Sekarang",
+ },
+
AccessCode: {
Title: "Kata Sandi Akses",
SubTitle: "Administrator telah mengaktifkan akses terenkripsi",
@@ -411,6 +431,10 @@ const id: PartialLocaleType = {
},
Model: "Model",
+ CompressModel: {
+ Title: "Model Kompresi",
+ SubTitle: "Model yang digunakan untuk mengompres riwayat",
+ },
Temperature: {
Title: "Randomness (temperature)",
SubTitle: "Semakin tinggi nilainya, semakin acak responsnya",
diff --git a/app/locales/index.ts b/app/locales/index.ts
index ff7e3a262..c8eb64df6 100644
--- a/app/locales/index.ts
+++ b/app/locales/index.ts
@@ -134,3 +134,34 @@ export function getISOLang() {
const lang = getLang();
return isoLangString[lang] ?? lang;
}
+
+const DEFAULT_STT_LANG = "zh-CN";
+export const STT_LANG_MAP: Record = {
+ cn: "zh-CN",
+ en: "en-US",
+ pt: "pt-BR",
+ tw: "zh-TW",
+ jp: "ja-JP",
+ ko: "ko-KR",
+ id: "id-ID",
+ fr: "fr-FR",
+ es: "es-ES",
+ it: "it-IT",
+ tr: "tr-TR",
+ de: "de-DE",
+ vi: "vi-VN",
+ ru: "ru-RU",
+ cs: "cs-CZ",
+ no: "no-NO",
+ ar: "ar-SA",
+ bn: "bn-BD",
+ sk: "sk-SK",
+};
+
+export function getSTTLang(): string {
+ try {
+ return STT_LANG_MAP[getLang()];
+ } catch {
+ return DEFAULT_STT_LANG;
+ }
+}
diff --git a/app/locales/it.ts b/app/locales/it.ts
index 1a54cfa43..59bc1eb15 100644
--- a/app/locales/it.ts
+++ b/app/locales/it.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const it: PartialLocaleType = {
WIP: "Work in progress...",
Error: {
Unauthorized: isApp
- ? "API Key non valido rilevato. Vai alla pagina [Impostazioni](/#/settings) per controllare se l'API Key è configurata correttamente."
- : "La password di accesso è errata o mancante. Vai alla pagina [Login](/#/auth) per inserire la password corretta o inserisci la tua API Key OpenAI nella pagina [Impostazioni](/#/settings).",
+ ? `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti:
+ \\ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️`
+ : `😆 La conversazione ha incontrato alcuni problemi, non preoccuparti:
+ \ 1️⃣ Se vuoi iniziare senza configurazione, [clicca qui per iniziare a chattare immediatamente 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ Se stai utilizzando una versione di distribuzione privata, clicca [qui](/#/auth) per inserire la chiave di accesso 🔑
+ \ 3️⃣ Se vuoi utilizzare le tue risorse OpenAI, clicca [qui](/#/settings) per modificare le impostazioni ⚙️
+ `,
},
Auth: {
Title: "Password richiesta",
@@ -18,6 +24,11 @@ const it: PartialLocaleType = {
Input: "Inserisci il codice di accesso qui",
Confirm: "Conferma",
Later: "Più tardi",
+ Return: "Ritorna",
+ SaasTips:
+ "La configurazione è troppo complicata, voglio usarlo immediatamente",
+ TopTips:
+ "🥳 Offerta di lancio NextChat AI, sblocca OpenAI o1, GPT-4o, Claude-3.5 e i più recenti modelli di grandi dimensioni",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} conversazioni`,
@@ -43,6 +54,8 @@ const it: PartialLocaleType = {
PinToastAction: "Visualizza",
Delete: "Elimina",
Edit: "Modifica",
+ RefreshTitle: "Aggiorna titolo",
+ RefreshToast: "Richiesta di aggiornamento del titolo inviata",
},
Commands: {
new: "Nuova chat",
@@ -293,6 +306,14 @@ const it: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "Usa NextChat AI",
+ Label: "(La soluzione più conveniente)",
+ SubTitle:
+ "Mantenuto ufficialmente da NextChat, pronto all'uso senza configurazione, supporta i modelli più recenti come OpenAI o1, GPT-4o e Claude-3.5",
+ ChatNow: "Chatta ora",
+ },
+
AccessCode: {
Title: "Password di accesso",
SubTitle: "L'amministratore ha abilitato l'accesso criptato",
@@ -423,6 +444,10 @@ const it: PartialLocaleType = {
},
Model: "Modello (model)",
+ CompressModel: {
+ Title: "Modello di compressione",
+ SubTitle: "Modello utilizzato per comprimere la cronologia",
+ },
Temperature: {
Title: "Casualità (temperature)",
SubTitle: "Valore più alto, risposte più casuali",
diff --git a/app/locales/jp.ts b/app/locales/jp.ts
index 6aaf0ba67..e7c81e186 100644
--- a/app/locales/jp.ts
+++ b/app/locales/jp.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const jp: PartialLocaleType = {
WIP: "この機能は開発中です",
Error: {
Unauthorized: isApp
- ? "無効なAPIキーが検出されました。[設定](/#/settings)ページでAPIキーが正しく設定されているか確認してください。"
- : "アクセスパスワードが正しくないか空です。[ログイン](/#/auth)ページで正しいアクセスパスワードを入力するか、[設定](/#/settings)ページで自分のOpenAI APIキーを入力してください。",
+ ? `😆 会話中に問題が発生しましたが、心配しないでください:
+ \\ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️`
+ : `😆 会話中に問題が発生しましたが、心配しないでください:
+ \ 1️⃣ 設定なしで始めたい場合は、[ここをクリックしてすぐにチャットを開始 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ プライベートデプロイ版を使用している場合は、[ここをクリックして](/#/auth)アクセストークンを入力してください 🔑
+ \ 3️⃣ 自分のOpenAIリソースを使用したい場合は、[ここをクリックして](/#/settings)設定を変更してください ⚙️
+ `,
},
Auth: {
Title: "パスワードが必要です",
@@ -18,6 +24,10 @@ const jp: PartialLocaleType = {
Input: "ここにアクセスコードを入力",
Confirm: "確認",
Later: "後で",
+ Return: "戻る",
+ SaasTips: "設定が面倒すぎる、すぐに使いたい",
+ TopTips:
+ "🥳 NextChat AIの発売特典で、OpenAI o1、GPT-4o、Claude-3.5などの最新の大規模モデルを今すぐアンロック",
},
ChatItem: {
ChatItemCount: (count: number) => `${count}件の会話`,
@@ -43,6 +53,8 @@ const jp: PartialLocaleType = {
PinToastAction: "見る",
Delete: "削除",
Edit: "編集",
+ RefreshTitle: "タイトルを更新",
+ RefreshToast: "タイトル更新リクエストが送信されました",
},
Commands: {
new: "新しいチャット",
@@ -280,6 +292,14 @@ const jp: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "NextChat AIを使用する",
+ Label: "(コストパフォーマンスの最も高いソリューション)",
+ SubTitle:
+ "NextChatによって公式に管理されており、設定なしですぐに使用でき、OpenAI o1、GPT-4o、Claude-3.5などの最新の大規模モデルをサポートしています",
+ ChatNow: "今すぐチャット",
+ },
+
AccessCode: {
Title: "アクセスパスワード",
SubTitle: "管理者が暗号化アクセスを有効にしました",
@@ -407,6 +427,10 @@ const jp: PartialLocaleType = {
},
Model: "モデル (model)",
+ CompressModel: {
+ Title: "圧縮モデル",
+ SubTitle: "履歴を圧縮するために使用されるモデル",
+ },
Temperature: {
Title: "ランダム性 (temperature)",
SubTitle: "値が大きいほど応答がランダムになります",
diff --git a/app/locales/ko.ts b/app/locales/ko.ts
index 563827fb9..f2c433b76 100644
--- a/app/locales/ko.ts
+++ b/app/locales/ko.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const ko: PartialLocaleType = {
WIP: "곧 출시 예정...",
Error: {
Unauthorized: isApp
- ? "유효하지 않은 API 키가 감지되었습니다. [설정](/#/settings) 페이지에서 API 키가 올바르게 구성되었는지 확인하십시오."
- : "잘못된 접근 비밀번호이거나 비밀번호가 비어 있습니다. [로그인](/#/auth) 페이지에서 올바른 접근 비밀번호를 입력하거나 [설정](/#/settings) 페이지에서 OpenAI API 키를 입력하십시오.",
+ ? `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요:
+ \\ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️`
+ : `😆 대화 중 문제가 발생했습니다, 걱정하지 마세요:
+ \ 1️⃣ 제로 구성으로 시작하고 싶다면, [여기를 클릭하여 즉시 대화를 시작하세요 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ 개인 배포 버전을 사용하고 있다면, [여기를 클릭하여](/#/auth) 접근 키를 입력하세요 🔑
+ \ 3️⃣ 자신의 OpenAI 리소스를 사용하고 싶다면, [여기를 클릭하여](/#/settings) 설정을 수정하세요 ⚙️
+ `,
},
Auth: {
Title: "비밀번호 필요",
@@ -18,6 +24,10 @@ const ko: PartialLocaleType = {
Input: "여기에 접근 코드를 입력하십시오.",
Confirm: "확인",
Later: "나중에 하기",
+ Return: "돌아가기",
+ SaasTips: "설정이 너무 복잡합니다. 즉시 사용하고 싶습니다.",
+ TopTips:
+ "🥳 NextChat AI 출시 기념 할인, 지금 OpenAI o1, GPT-4o, Claude-3.5 및 최신 대형 모델을 해제하세요",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} 개의 대화`,
@@ -43,6 +53,8 @@ const ko: PartialLocaleType = {
PinToastAction: "보기",
Delete: "삭제",
Edit: "편집",
+ RefreshTitle: "제목 새로고침",
+ RefreshToast: "제목 새로고침 요청이 전송되었습니다",
},
Commands: {
new: "새 채팅",
@@ -279,6 +291,14 @@ const ko: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "NextChat AI 사용하기",
+ Label: "(가장 비용 효율적인 솔루션)",
+ SubTitle:
+ "NextChat에 의해 공식적으로 유지 관리되며, 제로 구성으로 즉시 사용할 수 있으며, OpenAI o1, GPT-4o, Claude-3.5와 같은 최신 대형 모델을 지원합니다",
+ ChatNow: "지금 채팅하기",
+ },
+
AccessCode: {
Title: "접근 비밀번호",
SubTitle: "관리자가 암호화된 접근을 활성화했습니다.",
@@ -404,6 +424,10 @@ const ko: PartialLocaleType = {
},
Model: "모델 (model)",
+ CompressModel: {
+ Title: "압축 모델",
+ SubTitle: "기록을 압축하는 데 사용되는 모델",
+ },
Temperature: {
Title: "무작위성 (temperature)",
SubTitle: "값이 클수록 응답이 더 무작위적",
diff --git a/app/locales/no.ts b/app/locales/no.ts
index d7dc16b3f..f056ef12f 100644
--- a/app/locales/no.ts
+++ b/app/locales/no.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const no: PartialLocaleType = {
WIP: "Arbeid pågår ...",
Error: {
Unauthorized: isApp
- ? "Ugyldig API-nøkkel oppdaget. Gå til [innstillinger](/#/settings) for å sjekke om API-nøkkelen er riktig konfigurert."
- : "Adgangskoden er feil eller tom. Gå til [innlogging](/#/auth) for å oppgi riktig adgangskode, eller fyll inn din egen OpenAI API-nøkkel på [innstillinger](/#/settings) siden.",
+ ? `😆 Samtalen har støtt på noen problemer, ikke bekymre deg:
+ \\ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️`
+ : `😆 Samtalen har støtt på noen problemer, ikke bekymre deg:
+ \ 1️⃣ Hvis du vil starte uten konfigurasjon, [klikk her for å begynne å chatte umiddelbart 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ Hvis du bruker en privat distribusjonsversjon, klikk [her](/#/auth) for å skrive inn tilgangsnøkkelen 🔑
+ \ 3️⃣ Hvis du vil bruke dine egne OpenAI-ressurser, klikk [her](/#/settings) for å endre innstillingene ⚙️
+ `,
},
Auth: {
Title: "Passord påkrevd",
@@ -18,6 +24,11 @@ const no: PartialLocaleType = {
Input: "Skriv tilgangskoden her",
Confirm: "Bekreft",
Later: "Kom tilbake senere",
+ Return: "Tilbake",
+ SaasTips:
+ "Konfigurasjonen er for komplisert, jeg vil bruke det med en gang",
+ TopTips:
+ "🥳 NextChat AI lanseringstilbud, lås opp OpenAI o1, GPT-4o, Claude-3.5 og de nyeste store modellene nå",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} samtaler`,
@@ -44,6 +55,8 @@ const no: PartialLocaleType = {
PinToastAction: "Se",
Delete: "Slett",
Edit: "Rediger",
+ RefreshTitle: "Oppdater tittel",
+ RefreshToast: "Forespørsel om titteloppdatering sendt",
},
Commands: {
new: "Ny samtale",
@@ -286,6 +299,14 @@ const no: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "Bruk NextChat AI",
+ Label: "(Den mest kostnadseffektive løsningen)",
+ SubTitle:
+ "Offisielt vedlikeholdt av NextChat, klar til bruk uten konfigurasjon, støtter de nyeste store modellene som OpenAI o1, GPT-4o og Claude-3.5",
+ ChatNow: "Chat nå",
+ },
+
AccessCode: {
Title: "Adgangskode",
SubTitle: "Administrator har aktivert kryptert tilgang",
@@ -415,6 +436,10 @@ const no: PartialLocaleType = {
},
Model: "Modell",
+ CompressModel: {
+ Title: "Komprimeringsmodell",
+ SubTitle: "Modell brukt for å komprimere historikken",
+ },
Temperature: {
Title: "Tilfeldighet (temperature)",
SubTitle: "Høyere verdi gir mer tilfeldige svar",
diff --git a/app/locales/pt.ts b/app/locales/pt.ts
index 9fd13ba1c..152f50228 100644
--- a/app/locales/pt.ts
+++ b/app/locales/pt.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import { PartialLocaleType } from "../locales/index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const pt: PartialLocaleType = {
WIP: "Em breve...",
Error: {
Unauthorized: isApp
- ? "Chave API inválida, por favor verifique em [Configurações](/#/settings)."
- : "Acesso não autorizado, por favor insira o código de acesso em [auth](/#/auth) ou insira sua Chave API OpenAI.",
+ ? `😆 A conversa encontrou alguns problemas, não se preocupe:
+ \\ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️`
+ : `😆 A conversa encontrou alguns problemas, não se preocupe:
+ \ 1️⃣ Se você quiser começar sem configuração, [clique aqui para começar a conversar imediatamente 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ Se você estiver usando uma versão de implantação privada, clique [aqui](/#/auth) para inserir a chave de acesso 🔑
+ \ 3️⃣ Se você deseja usar seus próprios recursos OpenAI, clique [aqui](/#/settings) para modificar as configurações ⚙️
+`,
},
Auth: {
Title: "Necessário Código de Acesso",
@@ -18,6 +24,10 @@ const pt: PartialLocaleType = {
Input: "código de acesso",
Confirm: "Confirmar",
Later: "Depois",
+ Return: "Voltar",
+ SaasTips: "A configuração é muito complicada, quero usá-la imediatamente",
+ TopTips:
+ "🥳 Oferta de Lançamento do NextChat AI, desbloqueie o OpenAI o1, GPT-4o, Claude-3.5 e os mais recentes grandes modelos agora",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} mensagens`,
@@ -43,6 +53,8 @@ const pt: PartialLocaleType = {
PinToastAction: "Visualizar",
Delete: "Deletar",
Edit: "Editar",
+ RefreshTitle: "Atualizar Título",
+ RefreshToast: "Solicitação de atualização de título enviada",
},
Commands: {
new: "Iniciar um novo chat",
@@ -279,6 +291,14 @@ const pt: PartialLocaleType = {
NoAccess: "Insira a Chave API para verificar o saldo",
},
Access: {
+ SaasStart: {
+ Title: "Usar NextChat AI",
+ Label: "(A solução mais econômica)",
+ SubTitle:
+ "Mantido oficialmente pelo NextChat, pronto para uso sem configuração, suporta os mais recentes grandes modelos como OpenAI o1, GPT-4o e Claude-3.5",
+ ChatNow: "Conversar agora",
+ },
+
AccessCode: {
Title: "Código de Acesso",
SubTitle: "Controle de Acesso Habilitado",
@@ -346,6 +366,10 @@ const pt: PartialLocaleType = {
},
Model: "Modelo",
+ CompressModel: {
+ Title: "Modelo de Compressão",
+ SubTitle: "Modelo usado para comprimir o histórico",
+ },
Temperature: {
Title: "Temperatura",
SubTitle: "Um valor maior torna a saída mais aleatória",
diff --git a/app/locales/ru.ts b/app/locales/ru.ts
index e983dcddb..4294a3b34 100644
--- a/app/locales/ru.ts
+++ b/app/locales/ru.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import { PartialLocaleType } from "../locales/index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const ru: PartialLocaleType = {
WIP: "Скоро...",
Error: {
Unauthorized: isApp
- ? "Обнаружен недействительный API-ключ. Пожалуйста, перейдите на страницу [Настройки](/#/settings), чтобы проверить правильность конфигурации API-ключа."
- : "Неверный или пустой пароль доступа. Пожалуйста, перейдите на страницу [Вход](/#/auth), чтобы ввести правильный пароль доступа, или на страницу [Настройки](/#/settings), чтобы ввести ваш собственный API-ключ OpenAI.",
+ ? `😆 В разговоре возникли некоторые проблемы, не переживайте:
+ \\ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️`
+ : `😆 В разговоре возникли некоторые проблемы, не переживайте:
+ \ 1️⃣ Если вы хотите начать без настройки, [нажмите здесь, чтобы немедленно начать разговор 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ Если вы используете частную версию развертывания, нажмите [здесь](/#/auth), чтобы ввести ключ доступа 🔑
+ \ 3️⃣ Если вы хотите использовать свои ресурсы OpenAI, нажмите [здесь](/#/settings), чтобы изменить настройки ⚙️
+ `,
},
Auth: {
Title: "Требуется пароль",
@@ -18,6 +24,10 @@ const ru: PartialLocaleType = {
Input: "Введите код доступа здесь",
Confirm: "Подтвердить",
Later: "Позже",
+ Return: "Назад",
+ SaasTips: "Настройка слишком сложна, я хочу использовать это немедленно",
+ TopTips:
+ "🥳 Предложение по запуску NextChat AI: разблокируйте OpenAI o1, GPT-4o, Claude-3.5 и новейшие большие модели прямо сейчас",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} бесед`,
@@ -43,6 +53,8 @@ const ru: PartialLocaleType = {
PinToastAction: "Просмотреть",
Delete: "Удалить",
Edit: "Редактировать",
+ RefreshTitle: "Обновить заголовок",
+ RefreshToast: "Запрос на обновление заголовка отправлен",
},
Commands: {
new: "Новый чат",
@@ -284,6 +296,14 @@ const ru: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "Используйте NextChat AI",
+ Label: "(Самое экономичное решение)",
+ SubTitle:
+ "Официально поддерживается NextChat, готов к использованию без настройки, поддерживает последние крупные модели, такие как OpenAI o1, GPT-4o и Claude-3.5",
+ ChatNow: "Начать чат",
+ },
+
AccessCode: {
Title: "Пароль доступа",
SubTitle: "Администратор включил защиту паролем",
@@ -414,6 +434,10 @@ const ru: PartialLocaleType = {
},
Model: "Модель",
+ CompressModel: {
+ Title: "Модель сжатия",
+ SubTitle: "Модель, используемая для сжатия истории",
+ },
Temperature: {
Title: "Случайность (temperature)",
SubTitle: "Чем больше значение, тем более случайные ответы",
diff --git a/app/locales/sk.ts b/app/locales/sk.ts
index 2586aaaa7..36454de75 100644
--- a/app/locales/sk.ts
+++ b/app/locales/sk.ts
@@ -1,8 +1,7 @@
import { getClientConfig } from "../config/client";
import { SubmitKey } from "../store/config";
-import { LocaleType } from "./index";
import type { PartialLocaleType } from "./index";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
// if you are adding a new translation, please use PartialLocaleType instead of LocaleType
const isApp = !!getClientConfig()?.isApp;
@@ -10,8 +9,14 @@ const sk: PartialLocaleType = {
WIP: "Už čoskoro...",
Error: {
Unauthorized: isApp
- ? "Neplatný API kľúč, prosím skontrolujte ho na stránke [Nastavenia](/#/settings)."
- : "Neoprávnený prístup, prosím zadajte prístupový kód na stránke [auth](/#/auth), alebo zadajte váš OpenAI API kľúč.",
+ ? `😆 Rozhovor narazil na nejaké problémy, nebojte sa:
+ \\ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️`
+ : `😆 Rozhovor narazil na nejaké problémy, nebojte sa:
+ \ 1️⃣ Ak chcete začať bez konfigurácie, [kliknite sem, aby ste okamžite začali chatovať 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ Ak používate verziu súkromného nasadenia, kliknite [sem](/#/auth), aby ste zadali prístupový kľúč 🔑
+ \ 3️⃣ Ak chcete používať svoje vlastné zdroje OpenAI, kliknite [sem](/#/settings), aby ste upravili nastavenia ⚙️
+ `,
},
Auth: {
Title: "Potrebný prístupový kód",
@@ -20,6 +25,10 @@ const sk: PartialLocaleType = {
Input: "prístupový kód",
Confirm: "Potvrdiť",
Later: "Neskôr",
+ Return: "Návrat",
+ SaasTips: "Nastavenie je príliš zložité, chcem to okamžite použiť",
+ TopTips:
+ "🥳 Uvítacia ponuka NextChat AI, okamžite odomknite OpenAI o1, GPT-4o, Claude-3.5 a najnovšie veľké modely",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} správ`,
@@ -45,6 +54,8 @@ const sk: PartialLocaleType = {
PinToastAction: "Zobraziť",
Delete: "Vymazať",
Edit: "Upraviť",
+ RefreshTitle: "Obnoviť názov",
+ RefreshToast: "Požiadavka na obnovenie názvu bola odoslaná",
},
Commands: {
new: "Začať nový chat",
@@ -280,6 +291,14 @@ const sk: PartialLocaleType = {
NoAccess: "Zadajte API kľúč na skontrolovanie zostatku",
},
Access: {
+ SaasStart: {
+ Title: "Použite NextChat AI",
+ Label: "(Najvýhodnejšie riešenie)",
+ SubTitle:
+ "Oficiálne udržiavané NextChat, pripravené na použitie bez konfigurácie, podporuje najnovšie veľké modely ako OpenAI o1, GPT-4o a Claude-3.5",
+ ChatNow: "Chatovať teraz",
+ },
+
AccessCode: {
Title: "Prístupový kód",
SubTitle: "Povolený prístupový kód",
@@ -365,6 +384,10 @@ const sk: PartialLocaleType = {
},
Model: "Model",
+ CompressModel: {
+ Title: "Kompresný model",
+ SubTitle: "Model používaný na kompresiu histórie",
+ },
Temperature: {
Title: "Teplota",
SubTitle: "Vyššia hodnota robí výstup náhodnejším",
diff --git a/app/locales/tr.ts b/app/locales/tr.ts
index ac410615e..2082488a5 100644
--- a/app/locales/tr.ts
+++ b/app/locales/tr.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const tr: PartialLocaleType = {
WIP: "Çalışma devam ediyor...",
Error: {
Unauthorized: isApp
- ? "Geçersiz API Anahtarı tespit edildi, lütfen API Anahtarını doğru şekilde yapılandırmak için [Ayarlar](/#/settings) sayfasına gidin."
- : "Erişim şifresi yanlış veya boş, lütfen doğru erişim şifresini girmek için [Giriş](/#/auth) sayfasına gidin veya kendi OpenAI API Anahtarınızı [Ayarlar](/#/settings) sayfasına girin.",
+ ? `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin:
+ \\ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️`
+ : `😆 Sohbet bazı sorunlarla karşılaştı, endişelenmeyin:
+ \ 1️⃣ Eğer sıfır yapılandırma ile başlamak istiyorsanız, [buraya tıklayarak hemen sohbete başlayın 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ Eğer özel dağıtım sürümü kullanıyorsanız, [buraya tıklayarak](/#/auth) erişim anahtarını girin 🔑
+ \ 3️⃣ Kendi OpenAI kaynaklarınızı kullanmak istiyorsanız, [buraya tıklayarak](/#/settings) ayarları değiştirin ⚙️
+ `,
},
Auth: {
Title: "Şifre Gerekli",
@@ -18,6 +24,10 @@ const tr: PartialLocaleType = {
Input: "Erişim kodunu buraya girin",
Confirm: "Onayla",
Later: "Sonra",
+ Return: "Geri",
+ SaasTips: "Ayarlar çok karmaşık, hemen kullanmak istiyorum",
+ TopTips:
+ "🥳 NextChat AI lansman teklifi, OpenAI o1, GPT-4o, Claude-3.5 ve en son büyük modelleri şimdi açın",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} konuşma`,
@@ -43,6 +53,8 @@ const tr: PartialLocaleType = {
PinToastAction: "Görünüm",
Delete: "Sil",
Edit: "Düzenle",
+ RefreshTitle: "Başlığı Yenile",
+ RefreshToast: "Başlık yenileme isteği gönderildi",
},
Commands: {
new: "Yeni sohbet",
@@ -284,6 +296,14 @@ const tr: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "NextChat AI kullanın",
+ Label: "(En maliyet etkin çözüm)",
+ SubTitle:
+ "NextChat tarafından resmi olarak yönetilmektedir, yapılandırma olmadan hemen kullanıma hazırdır, OpenAI o1, GPT-4o, Claude-3.5 gibi en son büyük modelleri destekler",
+ ChatNow: "Şimdi sohbet et",
+ },
+
AccessCode: {
Title: "Erişim Şifresi",
SubTitle: "Yönetici şifreli erişimi etkinleştirdi",
@@ -414,6 +434,10 @@ const tr: PartialLocaleType = {
},
Model: "Model (model)",
+ CompressModel: {
+ Title: "Sıkıştırma Modeli",
+ SubTitle: "Geçmişi sıkıştırmak için kullanılan model",
+ },
Temperature: {
Title: "Rastgelelik (temperature)",
SubTitle: "Değer arttıkça yanıt daha rastgele olur",
diff --git a/app/locales/tw.ts b/app/locales/tw.ts
index c54a7b8c5..7cb846708 100644
--- a/app/locales/tw.ts
+++ b/app/locales/tw.ts
@@ -1,14 +1,20 @@
import { getClientConfig } from "../config/client";
import { SubmitKey } from "../store/config";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const tw = {
WIP: "此功能仍在開發中……",
Error: {
Unauthorized: isApp
- ? "偵測到無效的 API Key,請前往[設定](/#/settings)頁面檢查 API Key 是否設定正確。"
- : "存取密碼不正確或尚未填寫,請前往[登入](/#/auth)頁面輸入正確的存取密碼,或者在[設定](/#/settings)頁面填入你自己的 OpenAI API Key。",
+ ? `😆 對話遇到了一些問題,不用慌:
+ \\ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ 如果你想消耗自己的 OpenAI 資源,點擊[這裡](/#/settings)修改設定 ⚙️`
+ : `😆 對話遇到了一些問題,不用慌:
+ \ 1️⃣ 想要零配置開箱即用,[點擊這裡立刻開啟對話 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ 如果你正在使用私有部署版本,點擊[這裡](/#/auth)輸入訪問秘鑰 🔑
+ \ 3️⃣ 如果你想消耗自己的 OpenAI 資源,點擊[這裡](/#/settings)修改設定 ⚙️
+ `,
},
Auth: {
@@ -18,6 +24,10 @@ const tw = {
Input: "在此處填寫存取密碼",
Confirm: "確認",
Later: "稍候再說",
+ Return: "返回",
+ SaasTips: "配置太麻煩,想要立即使用",
+ TopTips:
+ "🥳 NextChat AI 首發優惠,立刻解鎖 OpenAI o1, GPT-4o, Claude-3.5 等最新大模型",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} 則對話`,
@@ -43,6 +53,8 @@ const tw = {
PinToastAction: "檢視",
Delete: "刪除",
Edit: "編輯",
+ RefreshTitle: "刷新標題",
+ RefreshToast: "已發送刷新標題請求",
},
Commands: {
new: "新建聊天",
@@ -285,6 +297,14 @@ const tw = {
},
Access: {
+ SaasStart: {
+ Title: "使用 NextChat AI",
+ Label: "(性價比最高的方案)",
+ SubTitle:
+ "由 NextChat 官方維護,零配置開箱即用,支持 OpenAI o1、GPT-4o、Claude-3.5 等最新大模型",
+ ChatNow: "立刻對話",
+ },
+
AccessCode: {
Title: "存取密碼",
SubTitle: "管理員已開啟加密存取",
@@ -368,6 +388,10 @@ const tw = {
},
Model: "模型 (model)",
+ CompressModel: {
+ Title: "壓縮模型",
+ SubTitle: "用於壓縮歷史記錄的模型",
+ },
Temperature: {
Title: "隨機性 (temperature)",
SubTitle: "值越大,回應越隨機",
diff --git a/app/locales/vi.ts b/app/locales/vi.ts
index 9a21ee406..c53baf35d 100644
--- a/app/locales/vi.ts
+++ b/app/locales/vi.ts
@@ -1,15 +1,21 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
import { getClientConfig } from "../config/client";
-
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
const isApp = !!getClientConfig()?.isApp;
const vi: PartialLocaleType = {
WIP: "Sắp ra mắt...",
Error: {
Unauthorized: isApp
- ? "Phát hiện khóa API không hợp lệ, vui lòng truy cập trang [Cài đặt](/#/settings) để kiểm tra xem khóa API có được cấu hình chính xác không."
- : "Mật khẩu truy cập không đúng hoặc để trống, vui lòng truy cập trang [Đăng nhập](/#/auth) để nhập mật khẩu truy cập chính xác, hoặc điền khóa API OpenAI của bạn vào trang [Cài đặt](/#/settings).",
+ ? `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng:
+ \\ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️`
+ : `😆 Cuộc trò chuyện gặp một số vấn đề, đừng lo lắng:
+ \ 1️⃣ Nếu bạn muốn bắt đầu mà không cần cấu hình, [nhấp vào đây để bắt đầu trò chuyện ngay lập tức 🚀](${SAAS_CHAT_UTM_URL})
+ \ 2️⃣ Nếu bạn đang sử dụng phiên bản triển khai riêng, hãy nhấp [vào đây](/#/auth) để nhập khóa truy cập 🔑
+ \ 3️⃣ Nếu bạn muốn sử dụng tài nguyên OpenAI của riêng mình, hãy nhấp [vào đây](/#/settings) để thay đổi cài đặt ⚙️
+ `,
},
Auth: {
Title: "Cần mật khẩu",
@@ -18,6 +24,10 @@ const vi: PartialLocaleType = {
Input: "Nhập mã truy cập tại đây",
Confirm: "Xác nhận",
Later: "Để sau",
+ Return: "Trở lại",
+ SaasTips: "Cấu hình quá phức tạp, tôi muốn sử dụng ngay lập tức",
+ TopTips:
+ "🥳 Ưu đãi ra mắt NextChat AI, mở khóa OpenAI o1, GPT-4o, Claude-3.5 và các mô hình lớn mới nhất ngay bây giờ",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} cuộc trò chuyện`,
@@ -43,6 +53,8 @@ const vi: PartialLocaleType = {
PinToastAction: "Xem",
Delete: "Xóa",
Edit: "Chỉnh sửa",
+ RefreshTitle: "Làm mới tiêu đề",
+ RefreshToast: "Đã gửi yêu cầu làm mới tiêu đề",
},
Commands: {
new: "Tạo cuộc trò chuyện mới",
@@ -281,6 +293,14 @@ const vi: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "Sử dụng NextChat AI",
+ Label: "(Giải pháp tiết kiệm chi phí nhất)",
+ SubTitle:
+ "Được NextChat chính thức duy trì, sẵn sàng sử dụng mà không cần cấu hình, hỗ trợ các mô hình lớn mới nhất như OpenAI o1, GPT-4o và Claude-3.5",
+ ChatNow: "Chat ngay",
+ },
+
AccessCode: {
Title: "Mật khẩu truy cập",
SubTitle: "Quản trị viên đã bật truy cập mã hóa",
@@ -410,6 +430,10 @@ const vi: PartialLocaleType = {
},
Model: "Mô hình (model)",
+ CompressModel: {
+ Title: "Mô hình nén",
+ SubTitle: "Mô hình được sử dụng để nén lịch sử",
+ },
Temperature: {
Title: "Độ ngẫu nhiên (temperature)",
SubTitle: "Giá trị càng lớn, câu trả lời càng ngẫu nhiên",
diff --git a/app/masks/index.ts b/app/masks/index.ts
index 92f21c6ae..bff5c9bbe 100644
--- a/app/masks/index.ts
+++ b/app/masks/index.ts
@@ -1,7 +1,4 @@
import { Mask } from "../store/mask";
-import { CN_MASKS } from "./cn";
-import { TW_MASKS } from "./tw";
-import { EN_MASKS } from "./en";
import { type BuiltinMask } from "./typing";
export { type BuiltinMask } from "./typing";
diff --git a/app/store/access.ts b/app/store/access.ts
index fff0c3b24..4ddb5e8ae 100644
--- a/app/store/access.ts
+++ b/app/store/access.ts
@@ -120,6 +120,9 @@ const DEFAULT_ACCESS_STATE = {
disableFastLink: false,
customModels: "",
defaultModel: "",
+
+ // tts config
+ edgeTTSVoiceName: "zh-CN-YunxiNeural",
};
export const useAccessStore = createPersistStore(
@@ -132,6 +135,12 @@ export const useAccessStore = createPersistStore(
return get().needCode;
},
+ edgeVoiceName() {
+ this.fetch();
+
+ return get().edgeTTSVoiceName;
+ },
+
isValidOpenAI() {
return ensure(get(), ["openaiApiKey"]);
},
@@ -204,8 +213,8 @@ export const useAccessStore = createPersistStore(
.then((res) => {
// Set default model from env request
let defaultModel = res.defaultModel ?? "";
- DEFAULT_CONFIG.modelConfig.model =
- defaultModel !== "" ? defaultModel : "gpt-3.5-turbo";
+ if (defaultModel !== "")
+ DEFAULT_CONFIG.modelConfig.model = defaultModel;
return res;
})
.then((res: DangerConfig) => {
diff --git a/app/store/chat.ts b/app/store/chat.ts
index bbb914710..b133a3c02 100644
--- a/app/store/chat.ts
+++ b/app/store/chat.ts
@@ -1,38 +1,34 @@
import {
- trimTopic,
getMessageTextContent,
+ trimTopic,
removeOutdatedEntries,
} from "../utils";
-import Locale, { getLang } from "../locales";
+import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
+import { nanoid } from "nanoid";
+import type {
+ ClientApi,
+ MultimodalContent,
+ RequestMessage,
+} from "../client/api";
+import { getClientApi } from "../client/api";
+import { ChatControllerPool } from "../client/controller";
import { showToast } from "../components/ui-lib";
-import { ModelConfig, ModelType, useAppConfig } from "./config";
-import { createEmptyMask, Mask } from "./mask";
import {
DEFAULT_INPUT_TEMPLATE,
DEFAULT_MODELS,
DEFAULT_SYSTEM_TEMPLATE,
KnowledgeCutOffDate,
StoreKey,
- SUMMARIZE_MODEL,
- GEMINI_SUMMARIZE_MODEL,
} from "../constant";
-import { getClientApi } from "../client/api";
-import type {
- ClientApi,
- RequestMessage,
- MultimodalContent,
-} from "../client/api";
-import { ChatControllerPool } from "../client/controller";
-import { prettyObject } from "../utils/format";
-import { estimateTokenLength } from "../utils/token";
-import { nanoid } from "nanoid";
-import { createPersistStore } from "../utils/store";
-import { collectModelsWithDefaultModel } from "../utils/model";
-import { useAccessStore } from "./access";
+import Locale, { getLang } from "../locales";
import { isDalle3, safeLocalStorage } from "../utils";
+import { prettyObject } from "../utils/format";
+import { createPersistStore } from "../utils/store";
+import { estimateTokenLength } from "../utils/token";
+import { ModelConfig, ModelType, useAppConfig } from "./config";
+import { createEmptyMask, Mask } from "./mask";
import { useSyncStore } from "./sync";
-import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
const localStorage = safeLocalStorage();
@@ -113,27 +109,6 @@ function createEmptySession(): ChatSession {
};
}
-function getSummarizeModel(currentModel: string) {
- // if it is using gpt-* models, force to use 4o-mini to summarize
- if (currentModel.startsWith("gpt") || currentModel.startsWith("chatgpt")) {
- const configStore = useAppConfig.getState();
- const accessStore = useAccessStore.getState();
- const allModel = collectModelsWithDefaultModel(
- configStore.models,
- [configStore.customModels, accessStore.customModels].join(","),
- accessStore.defaultModel,
- );
- const summarizeModel = allModel.find(
- (m) => m.name === SUMMARIZE_MODEL && m.available,
- );
- return summarizeModel?.name ?? currentModel;
- }
- if (currentModel.startsWith("gemini")) {
- return GEMINI_SUMMARIZE_MODEL;
- }
- return currentModel;
-}
-
function countMessages(msgs: ChatMessage[]) {
return msgs.reduce(
(pre, cur) => pre + estimateTokenLength(getMessageTextContent(cur)),
@@ -212,6 +187,28 @@ export const useChatStore = createPersistStore(
}
const methods = {
+ forkSession() {
+ // 获取当前会话
+ const currentSession = get().currentSession();
+ if (!currentSession) return;
+
+ const newSession = createEmptySession();
+
+ newSession.topic = currentSession.topic;
+ newSession.messages = [...currentSession.messages];
+ newSession.mask = {
+ ...currentSession.mask,
+ modelConfig: {
+ ...currentSession.mask.modelConfig,
+ },
+ };
+
+ set((state) => ({
+ currentSessionIndex: 0,
+ sessions: [newSession, ...state.sessions],
+ }));
+ },
+
clearSessions() {
set(() => ({
sessions: [createEmptySession()],
@@ -647,7 +644,7 @@ export const useChatStore = createPersistStore(
});
},
- summarizeSession() {
+ summarizeSession(refreshTitle: boolean = false) {
const config = useAppConfig.getState();
const session = get().currentSession();
const modelConfig = session.mask.modelConfig;
@@ -656,7 +653,7 @@ export const useChatStore = createPersistStore(
return;
}
- const providerName = modelConfig.providerName;
+ const providerName = modelConfig.compressProviderName;
const api: ClientApi = getClientApi(providerName);
// remove error messages if any
@@ -665,24 +662,35 @@ export const useChatStore = createPersistStore(
// should summarize topic after chating more than 50 words
const SUMMARIZE_MIN_LEN = 50;
if (
- config.enableAutoGenerateTitle &&
- session.topic === DEFAULT_TOPIC &&
- countMessages(messages) >= SUMMARIZE_MIN_LEN
+ (config.enableAutoGenerateTitle &&
+ session.topic === DEFAULT_TOPIC &&
+ countMessages(messages) >= SUMMARIZE_MIN_LEN) ||
+ refreshTitle
) {
- const topicMessages = messages.concat(
- createMessage({
- role: "user",
- content: Locale.Store.Prompt.Topic,
- }),
+ const startIndex = Math.max(
+ 0,
+ messages.length - modelConfig.historyMessageCount,
);
+ const topicMessages = messages
+ .slice(
+ startIndex < messages.length ? startIndex : messages.length - 1,
+ messages.length,
+ )
+ .concat(
+ createMessage({
+ role: "user",
+ content: Locale.Store.Prompt.Topic,
+ }),
+ );
api.llm.chat({
messages: topicMessages,
config: {
- model: getSummarizeModel(session.mask.modelConfig.model),
+ model: modelConfig.compressModel,
stream: false,
providerName,
},
onFinish(message) {
+ if (!isValidMessage(message)) return;
get().updateCurrentSession(
(session) =>
(session.topic =
@@ -741,7 +749,7 @@ export const useChatStore = createPersistStore(
config: {
...modelcfg,
stream: true,
- model: getSummarizeModel(session.mask.modelConfig.model),
+ model: modelConfig.compressModel,
},
onUpdate(message) {
session.memoryPrompt = message;
@@ -758,6 +766,10 @@ export const useChatStore = createPersistStore(
},
});
}
+
+ function isValidMessage(message: any): boolean {
+ return typeof message === "string" && !message.startsWith("```json");
+ }
},
updateStat(message: ChatMessage) {
@@ -790,7 +802,7 @@ export const useChatStore = createPersistStore(
},
{
name: StoreKey.Chat,
- version: 3.1,
+ version: 3.2,
migrate(persistedState, version) {
const state = persistedState as any;
const newState = JSON.parse(
@@ -837,6 +849,16 @@ export const useChatStore = createPersistStore(
});
}
+ // add default summarize model for every session
+ if (version < 3.2) {
+ newState.sessions.forEach((s) => {
+ const config = useAppConfig.getState();
+ s.mask.modelConfig.compressModel = config.modelConfig.compressModel;
+ s.mask.modelConfig.compressProviderName =
+ config.modelConfig.compressProviderName;
+ });
+ }
+
return newState as any;
},
},
diff --git a/app/store/config.ts b/app/store/config.ts
index e8e3c9863..3dcd4d86b 100644
--- a/app/store/config.ts
+++ b/app/store/config.ts
@@ -5,12 +5,21 @@ import {
DEFAULT_INPUT_TEMPLATE,
DEFAULT_MODELS,
DEFAULT_SIDEBAR_WIDTH,
+ DEFAULT_TTS_ENGINE,
+ DEFAULT_TTS_ENGINES,
+ DEFAULT_TTS_MODEL,
+ DEFAULT_TTS_MODELS,
+ DEFAULT_TTS_VOICE,
+ DEFAULT_TTS_VOICES,
StoreKey,
ServiceProvider,
} from "../constant";
import { createPersistStore } from "../utils/store";
export type ModelType = (typeof DEFAULT_MODELS)[number]["name"];
+export type TTSModelType = (typeof DEFAULT_TTS_MODELS)[number];
+export type TTSVoiceType = (typeof DEFAULT_TTS_VOICES)[number];
+export type TTSEngineType = (typeof DEFAULT_TTS_ENGINES)[number];
export enum SubmitKey {
Enter = "Enter",
@@ -41,6 +50,8 @@ export const DEFAULT_CONFIG = {
enableAutoGenerateTitle: true,
sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
+ enableArtifacts: true, // show artifacts config
+
disablePromptHint: false,
dontShowMaskSplashScreen: false, // dont show splash screen when create chat
@@ -50,7 +61,7 @@ export const DEFAULT_CONFIG = {
models: DEFAULT_MODELS as any as LLMModel[],
modelConfig: {
- model: "gpt-3.5-turbo" as ModelType,
+ model: "gpt-4o-mini" as ModelType,
providerName: "OpenAI" as ServiceProvider,
temperature: 0.5,
top_p: 1,
@@ -60,17 +71,29 @@ export const DEFAULT_CONFIG = {
sendMemory: true,
historyMessageCount: 4,
compressMessageLengthThreshold: 1000,
+ compressModel: "gpt-4o-mini" as ModelType,
+ compressProviderName: "OpenAI" as ServiceProvider,
enableInjectSystemPrompts: true,
template: config?.template ?? DEFAULT_INPUT_TEMPLATE,
size: "1024x1024" as DalleSize,
quality: "standard" as DalleQuality,
style: "vivid" as DalleStyle,
},
+
+ ttsConfig: {
+ enable: false,
+ autoplay: false,
+ engine: DEFAULT_TTS_ENGINE,
+ model: DEFAULT_TTS_MODEL,
+ voice: DEFAULT_TTS_VOICE,
+ speed: 1.0,
+ },
};
export type ChatConfig = typeof DEFAULT_CONFIG;
export type ModelConfig = ChatConfig["modelConfig"];
+export type TTSConfig = ChatConfig["ttsConfig"];
export function limitNumber(
x: number,
@@ -85,6 +108,21 @@ export function limitNumber(
return Math.min(max, Math.max(min, x));
}
+export const TTSConfigValidator = {
+ engine(x: string) {
+ return x as TTSEngineType;
+ },
+ model(x: string) {
+ return x as TTSModelType;
+ },
+ voice(x: string) {
+ return x as TTSVoiceType;
+ },
+ speed(x: number) {
+ return limitNumber(x, 0.25, 4.0, 1.0);
+ },
+};
+
export const ModalConfigValidator = {
model(x: string) {
return x as ModelType;
@@ -140,7 +178,22 @@ export const useAppConfig = createPersistStore(
}),
{
name: StoreKey.Config,
- version: 3.9,
+ version: 4,
+
+ merge(persistedState, currentState) {
+ const state = persistedState as ChatConfig | undefined;
+ if (!state) return { ...currentState };
+ const models = currentState.models.slice();
+ state.models.forEach((pModel) => {
+ const idx = models.findIndex(
+ (v) => v.name === pModel.name && v.provider === pModel.provider,
+ );
+ if (idx !== -1) models[idx] = pModel;
+ else models.push(pModel);
+ });
+ return { ...currentState, ...state, models: models };
+ },
+
migrate(persistedState, version) {
const state = persistedState as ChatConfig;
@@ -178,6 +231,13 @@ export const useAppConfig = createPersistStore(
: config?.template ?? DEFAULT_INPUT_TEMPLATE;
}
+ if (version < 4) {
+ state.modelConfig.compressModel =
+ DEFAULT_CONFIG.modelConfig.compressModel;
+ state.modelConfig.compressProviderName =
+ DEFAULT_CONFIG.modelConfig.compressProviderName;
+ }
+
return state as any;
},
},
diff --git a/app/store/plugin.ts b/app/store/plugin.ts
index 2356c6db0..84ae0816e 100644
--- a/app/store/plugin.ts
+++ b/app/store/plugin.ts
@@ -1,10 +1,13 @@
import OpenAPIClientAxios from "openapi-client-axios";
-import { getLang, Lang } from "../locales";
import { StoreKey } from "../constant";
import { nanoid } from "nanoid";
import { createPersistStore } from "../utils/store";
+import { getClientConfig } from "../config/client";
import yaml from "js-yaml";
import { adapter } from "../utils";
+import { useAccessStore } from "./access";
+
+const isApp = getClientConfig()?.isApp;
export type Plugin = {
id: string;
@@ -17,7 +20,6 @@ export type Plugin = {
authLocation?: string;
authHeader?: string;
authToken?: string;
- usingProxy?: boolean;
};
export type FunctionToolItem = {
@@ -47,18 +49,25 @@ export const FunctionToolService = {
plugin?.authType == "basic"
? `Basic ${plugin?.authToken}`
: plugin?.authType == "bearer"
- ? ` Bearer ${plugin?.authToken}`
+ ? `Bearer ${plugin?.authToken}`
: plugin?.authToken;
const authLocation = plugin?.authLocation || "header";
const definition = yaml.load(plugin.content) as any;
const serverURL = definition?.servers?.[0]?.url;
- const baseURL = !!plugin?.usingProxy ? "/api/proxy" : serverURL;
+ const baseURL = !isApp ? "/api/proxy" : serverURL;
const headers: Record = {
- "X-Base-URL": !!plugin?.usingProxy ? serverURL : undefined,
+ "X-Base-URL": !isApp ? serverURL : undefined,
};
if (authLocation == "header") {
headers[headerName] = tokenValue;
}
+ // try using openaiApiKey for Dalle3 Plugin.
+ if (!tokenValue && plugin.id === "dalle3") {
+ const openaiApiKey = useAccessStore.getState().openaiApiKey;
+ if (openaiApiKey) {
+ headers[headerName] = `Bearer ${openaiApiKey}`;
+ }
+ }
const api = new OpenAPIClientAxios({
definition: yaml.load(plugin.content) as any,
axiosConfigDefaults: {
@@ -166,7 +175,7 @@ export const usePluginStore = createPersistStore(
(set, get) => ({
create(plugin?: Partial) {
const plugins = get().plugins;
- const id = nanoid();
+ const id = plugin?.id || nanoid();
plugins[id] = {
...createEmptyPlugin(),
...plugin,
@@ -221,5 +230,42 @@ export const usePluginStore = createPersistStore(
{
name: StoreKey.Plugin,
version: 1,
+ onRehydrateStorage(state) {
+ // Skip store rehydration on server side
+ if (typeof window === "undefined") {
+ return;
+ }
+
+ fetch("./plugins.json")
+ .then((res) => res.json())
+ .then((res) => {
+ Promise.all(
+ res.map((item: any) =>
+ // skip get schema
+ state.get(item.id)
+ ? item
+ : fetch(item.schema)
+ .then((res) => res.text())
+ .then((content) => ({
+ ...item,
+ content,
+ }))
+ .catch((e) => item),
+ ),
+ ).then((builtinPlugins: any) => {
+ builtinPlugins
+ .filter((item: any) => item?.content)
+ .forEach((item: any) => {
+ const plugin = state.create(item);
+ state.updatePlugin(plugin.id, (plugin) => {
+ const tool = FunctionToolService.add(plugin, true);
+ plugin.title = tool.api.definition.info.title;
+ plugin.version = tool.api.definition.info.version;
+ plugin.builtin = true;
+ });
+ });
+ });
+ });
+ },
},
);
diff --git a/app/store/prompt.ts b/app/store/prompt.ts
index a25cda581..c06edb282 100644
--- a/app/store/prompt.ts
+++ b/app/store/prompt.ts
@@ -1,7 +1,7 @@
import Fuse from "fuse.js";
-import { getLang } from "../locales";
-import { StoreKey } from "../constant";
import { nanoid } from "nanoid";
+import { StoreKey } from "../constant";
+import { getLang } from "../locales";
import { createPersistStore } from "../utils/store";
export interface Prompt {
@@ -147,6 +147,11 @@ export const usePromptStore = createPersistStore(
},
onRehydrateStorage(state) {
+ // Skip store rehydration on server side
+ if (typeof window === "undefined") {
+ return;
+ }
+
const PROMPT_URL = "./prompts.json";
type PromptList = Array<[string, string]>;
diff --git a/app/store/sync.ts b/app/store/sync.ts
index ca3f3283d..df52cc642 100644
--- a/app/store/sync.ts
+++ b/app/store/sync.ts
@@ -1,5 +1,4 @@
import { getClientConfig } from "../config/client";
-import { Updater } from "../typing";
import { ApiPath, STORAGE_KEY, StoreKey } from "../constant";
import { createPersistStore } from "../utils/store";
import {
diff --git a/app/store/update.ts b/app/store/update.ts
index 7253caffc..e68fde369 100644
--- a/app/store/update.ts
+++ b/app/store/update.ts
@@ -8,8 +8,6 @@ import { getClientConfig } from "../config/client";
import { createPersistStore } from "../utils/store";
import ChatGptIcon from "../icons/chatgpt.png";
import Locale from "../locales";
-import { use } from "react";
-import { useAppConfig } from ".";
import { ClientApi } from "../client/api";
const ONE_MINUTE = 60 * 1000;
diff --git a/app/utils.ts b/app/utils.ts
index ef0f4963a..445f3342f 100644
--- a/app/utils.ts
+++ b/app/utils.ts
@@ -3,8 +3,7 @@ import { showToast } from "./components/ui-lib";
import Locale from "./locales";
import { RequestMessage } from "./client/api";
import { ServiceProvider, REQUEST_TIMEOUT_MS } from "./constant";
-import isObject from "lodash-es/isObject";
-import { fetch as tauriFetch, Body, ResponseType } from "@tauri-apps/api/http";
+import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http";
export function trimTopic(topic: string) {
// Fix an issue where double quotes still show in the Indonesian language
diff --git a/app/utils/audio.ts b/app/utils/audio.ts
new file mode 100644
index 000000000..f6828c7aa
--- /dev/null
+++ b/app/utils/audio.ts
@@ -0,0 +1,45 @@
+type TTSPlayer = {
+ init: () => void;
+ play: (audioBuffer: ArrayBuffer, onended: () => void | null) => Promise;
+ stop: () => void;
+};
+
+export function createTTSPlayer(): TTSPlayer {
+ let audioContext: AudioContext | null = null;
+ let audioBufferSourceNode: AudioBufferSourceNode | null = null;
+
+ const init = () => {
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
+ audioContext.suspend();
+ };
+
+ const play = async (audioBuffer: ArrayBuffer, onended: () => void | null) => {
+ if (audioBufferSourceNode) {
+ audioBufferSourceNode.stop();
+ audioBufferSourceNode.disconnect();
+ }
+
+ const buffer = await audioContext!.decodeAudioData(audioBuffer);
+ audioBufferSourceNode = audioContext!.createBufferSource();
+ audioBufferSourceNode.buffer = buffer;
+ audioBufferSourceNode.connect(audioContext!.destination);
+ audioContext!.resume().then(() => {
+ audioBufferSourceNode!.start();
+ });
+ audioBufferSourceNode.onended = onended;
+ };
+
+ const stop = () => {
+ if (audioBufferSourceNode) {
+ audioBufferSourceNode.stop();
+ audioBufferSourceNode.disconnect();
+ audioBufferSourceNode = null;
+ }
+ if (audioContext) {
+ audioContext.close();
+ audioContext = null;
+ }
+ };
+
+ return { init, play, stop };
+}
diff --git a/app/utils/auth-settings-events.ts b/app/utils/auth-settings-events.ts
new file mode 100644
index 000000000..4f354c5f0
--- /dev/null
+++ b/app/utils/auth-settings-events.ts
@@ -0,0 +1,19 @@
+import { sendGAEvent } from "@next/third-parties/google";
+
+export function trackConversationGuideToCPaymentClick() {
+ sendGAEvent("event", "ConversationGuideToCPaymentClick", { value: 1 });
+}
+
+export function trackAuthorizationPageButtonToCPaymentClick() {
+ sendGAEvent("event", "AuthorizationPageButtonToCPaymentClick", { value: 1 });
+}
+
+export function trackAuthorizationPageBannerToCPaymentClick() {
+ sendGAEvent("event", "AuthorizationPageBannerToCPaymentClick", {
+ value: 1,
+ });
+}
+
+export function trackSettingsPageGuideToCPaymentClick() {
+ sendGAEvent("event", "SettingsPageGuideToCPaymentClick", { value: 1 });
+}
diff --git a/app/utils/cors.ts b/app/utils/cors.ts
index fa348f9bf..f5e5ce6f0 100644
--- a/app/utils/cors.ts
+++ b/app/utils/cors.ts
@@ -1,5 +1,5 @@
import { getClientConfig } from "../config/client";
-import { ApiPath, DEFAULT_API_HOST } from "../constant";
+import { DEFAULT_API_HOST } from "../constant";
export function corsPath(path: string) {
const baseUrl = getClientConfig()?.isApp ? `${DEFAULT_API_HOST}` : "";
diff --git a/app/utils/ms_edge_tts.ts b/app/utils/ms_edge_tts.ts
new file mode 100644
index 000000000..f291ebada
--- /dev/null
+++ b/app/utils/ms_edge_tts.ts
@@ -0,0 +1,391 @@
+// import axios from "axios";
+import { Buffer } from "buffer";
+import { randomBytes } from "crypto";
+import { Readable } from "stream";
+
+// Modified according to https://github.com/Migushthe2nd/MsEdgeTTS
+
+/**
+ * https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,volume,-Indicates%20the%20volume
+ */
+export enum VOLUME {
+ SILENT = "silent",
+ X_SOFT = "x-soft",
+ SOFT = "soft",
+ MEDIUM = "medium",
+ LOUD = "loud",
+ X_LOUD = "x-LOUD",
+ DEFAULT = "default",
+}
+
+/**
+ * https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,rate,-Indicates%20the%20speaking
+ */
+export enum RATE {
+ X_SLOW = "x-slow",
+ SLOW = "slow",
+ MEDIUM = "medium",
+ FAST = "fast",
+ X_FAST = "x-fast",
+ DEFAULT = "default",
+}
+
+/**
+ * https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,pitch,-Indicates%20the%20baseline
+ */
+export enum PITCH {
+ X_LOW = "x-low",
+ LOW = "low",
+ MEDIUM = "medium",
+ HIGH = "high",
+ X_HIGH = "x-high",
+ DEFAULT = "default",
+}
+
+/**
+ * Only a few of the [possible formats](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/rest-text-to-speech#audio-outputs) are accepted.
+ */
+export enum OUTPUT_FORMAT {
+ // Streaming =============================
+ // AMR_WB_16000HZ = "amr-wb-16000hz",
+ // AUDIO_16KHZ_16BIT_32KBPS_MONO_OPUS = "audio-16khz-16bit-32kbps-mono-opus",
+ // AUDIO_16KHZ_32KBITRATE_MONO_MP3 = "audio-16khz-32kbitrate-mono-mp3",
+ // AUDIO_16KHZ_64KBITRATE_MONO_MP3 = "audio-16khz-64kbitrate-mono-mp3",
+ // AUDIO_16KHZ_128KBITRATE_MONO_MP3 = "audio-16khz-128kbitrate-mono-mp3",
+ // AUDIO_24KHZ_16BIT_24KBPS_MONO_OPUS = "audio-24khz-16bit-24kbps-mono-opus",
+ // AUDIO_24KHZ_16BIT_48KBPS_MONO_OPUS = "audio-24khz-16bit-48kbps-mono-opus",
+ AUDIO_24KHZ_48KBITRATE_MONO_MP3 = "audio-24khz-48kbitrate-mono-mp3",
+ AUDIO_24KHZ_96KBITRATE_MONO_MP3 = "audio-24khz-96kbitrate-mono-mp3",
+ // AUDIO_24KHZ_160KBITRATE_MONO_MP3 = "audio-24khz-160kbitrate-mono-mp3",
+ // AUDIO_48KHZ_96KBITRATE_MONO_MP3 = "audio-48khz-96kbitrate-mono-mp3",
+ // AUDIO_48KHZ_192KBITRATE_MONO_MP3 = "audio-48khz-192kbitrate-mono-mp3",
+ // OGG_16KHZ_16BIT_MONO_OPUS = "ogg-16khz-16bit-mono-opus",
+ // OGG_24KHZ_16BIT_MONO_OPUS = "ogg-24khz-16bit-mono-opus",
+ // OGG_48KHZ_16BIT_MONO_OPUS = "ogg-48khz-16bit-mono-opus",
+ // RAW_8KHZ_8BIT_MONO_ALAW = "raw-8khz-8bit-mono-alaw",
+ // RAW_8KHZ_8BIT_MONO_MULAW = "raw-8khz-8bit-mono-mulaw",
+ // RAW_8KHZ_16BIT_MONO_PCM = "raw-8khz-16bit-mono-pcm",
+ // RAW_16KHZ_16BIT_MONO_PCM = "raw-16khz-16bit-mono-pcm",
+ // RAW_16KHZ_16BIT_MONO_TRUESILK = "raw-16khz-16bit-mono-truesilk",
+ // RAW_22050HZ_16BIT_MONO_PCM = "raw-22050hz-16bit-mono-pcm",
+ // RAW_24KHZ_16BIT_MONO_PCM = "raw-24khz-16bit-mono-pcm",
+ // RAW_24KHZ_16BIT_MONO_TRUESILK = "raw-24khz-16bit-mono-truesilk",
+ // RAW_44100HZ_16BIT_MONO_PCM = "raw-44100hz-16bit-mono-pcm",
+ // RAW_48KHZ_16BIT_MONO_PCM = "raw-48khz-16bit-mono-pcm",
+ // WEBM_16KHZ_16BIT_MONO_OPUS = "webm-16khz-16bit-mono-opus",
+ // WEBM_24KHZ_16BIT_24KBPS_MONO_OPUS = "webm-24khz-16bit-24kbps-mono-opus",
+ WEBM_24KHZ_16BIT_MONO_OPUS = "webm-24khz-16bit-mono-opus",
+ // Non-streaming =============================
+ // RIFF_8KHZ_8BIT_MONO_ALAW = "riff-8khz-8bit-mono-alaw",
+ // RIFF_8KHZ_8BIT_MONO_MULAW = "riff-8khz-8bit-mono-mulaw",
+ // RIFF_8KHZ_16BIT_MONO_PCM = "riff-8khz-16bit-mono-pcm",
+ // RIFF_22050HZ_16BIT_MONO_PCM = "riff-22050hz-16bit-mono-pcm",
+ // RIFF_24KHZ_16BIT_MONO_PCM = "riff-24khz-16bit-mono-pcm",
+ // RIFF_44100HZ_16BIT_MONO_PCM = "riff-44100hz-16bit-mono-pcm",
+ // RIFF_48KHZ_16BIT_MONO_PCM = "riff-48khz-16bit-mono-pcm",
+}
+
+export type Voice = {
+ Name: string;
+ ShortName: string;
+ Gender: string;
+ Locale: string;
+ SuggestedCodec: string;
+ FriendlyName: string;
+ Status: string;
+};
+
+export class ProsodyOptions {
+ /**
+ * The pitch to use.
+ * Can be any {@link PITCH}, or a relative frequency in Hz (+50Hz), a relative semitone (+2st), or a relative percentage (+50%).
+ * [SSML documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,pitch,-Indicates%20the%20baseline)
+ */
+ pitch?: PITCH | string = "+0Hz";
+ /**
+ * The rate to use.
+ * Can be any {@link RATE}, or a relative number (0.5), or string with a relative percentage (+50%).
+ * [SSML documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,rate,-Indicates%20the%20speaking)
+ */
+ rate?: RATE | string | number = 1.0;
+ /**
+ * The volume to use.
+ * Can be any {@link VOLUME}, or an absolute number (0, 100), a string with a relative number (+50), or a relative percentage (+50%).
+ * [SSML documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-voice#:~:text=Optional-,volume,-Indicates%20the%20volume)
+ */
+ volume?: VOLUME | string | number = 100.0;
+}
+
+export class MsEdgeTTS {
+ static OUTPUT_FORMAT = OUTPUT_FORMAT;
+ private static TRUSTED_CLIENT_TOKEN = "6A5AA1D4EAFF4E9FB37E23D68491D6F4";
+ private static VOICES_URL = `https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list?trustedclienttoken=${MsEdgeTTS.TRUSTED_CLIENT_TOKEN}`;
+ private static SYNTH_URL = `wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=${MsEdgeTTS.TRUSTED_CLIENT_TOKEN}`;
+ private static BINARY_DELIM = "Path:audio\r\n";
+ private static VOICE_LANG_REGEX = /\w{2}-\w{2}/;
+ private readonly _enableLogger;
+ private _ws: WebSocket | undefined;
+ private _voice: any;
+ private _voiceLocale: any;
+ private _outputFormat: any;
+ private _streams: { [key: string]: Readable } = {};
+ private _startTime = 0;
+
+ private _log(...o: any[]) {
+ if (this._enableLogger) {
+ console.log(...o);
+ }
+ }
+
+ /**
+ * Create a new `MsEdgeTTS` instance.
+ *
+ * @param agent (optional, **NOT SUPPORTED IN BROWSER**) Use a custom http.Agent implementation like [https-proxy-agent](https://github.com/TooTallNate/proxy-agents) or [socks-proxy-agent](https://github.com/TooTallNate/proxy-agents/tree/main/packages/socks-proxy-agent).
+ * @param enableLogger=false whether to enable the built-in logger. This logs connections inits, disconnects, and incoming data to the console
+ */
+ public constructor(enableLogger: boolean = false) {
+ this._enableLogger = enableLogger;
+ }
+
+ private async _send(message: any) {
+ for (let i = 1; i <= 3 && this._ws!.readyState !== this._ws!.OPEN; i++) {
+ if (i == 1) {
+ this._startTime = Date.now();
+ }
+ this._log("connecting: ", i);
+ await this._initClient();
+ }
+ this._ws!.send(message);
+ }
+
+ private _initClient() {
+ this._ws = new WebSocket(MsEdgeTTS.SYNTH_URL);
+
+ this._ws.binaryType = "arraybuffer";
+ return new Promise((resolve, reject) => {
+ this._ws!.onopen = () => {
+ this._log(
+ "Connected in",
+ (Date.now() - this._startTime) / 1000,
+ "seconds",
+ );
+ this._send(
+ `Content-Type:application/json; charset=utf-8\r\nPath:speech.config\r\n\r\n
+ {
+ "context": {
+ "synthesis": {
+ "audio": {
+ "metadataoptions": {
+ "sentenceBoundaryEnabled": "false",
+ "wordBoundaryEnabled": "false"
+ },
+ "outputFormat": "${this._outputFormat}"
+ }
+ }
+ }
+ }
+ `,
+ ).then(resolve);
+ };
+ this._ws!.onmessage = (m: any) => {
+ const buffer = Buffer.from(m.data as ArrayBuffer);
+ const message = buffer.toString();
+ const requestId = /X-RequestId:(.*?)\r\n/gm.exec(message)![1];
+ if (message.includes("Path:turn.start")) {
+ // start of turn, ignore
+ } else if (message.includes("Path:turn.end")) {
+ // end of turn, close stream
+ this._streams[requestId].push(null);
+ } else if (message.includes("Path:response")) {
+ // context response, ignore
+ } else if (
+ message.includes("Path:audio") &&
+ m.data instanceof ArrayBuffer
+ ) {
+ this._pushAudioData(buffer, requestId);
+ } else {
+ this._log("UNKNOWN MESSAGE", message);
+ }
+ };
+ this._ws!.onclose = () => {
+ this._log(
+ "disconnected after:",
+ (Date.now() - this._startTime) / 1000,
+ "seconds",
+ );
+ for (const requestId in this._streams) {
+ this._streams[requestId].push(null);
+ }
+ };
+ this._ws!.onerror = function (error: any) {
+ reject("Connect Error: " + error);
+ };
+ });
+ }
+
+ private _pushAudioData(audioBuffer: Buffer, requestId: string) {
+ const audioStartIndex =
+ audioBuffer.indexOf(MsEdgeTTS.BINARY_DELIM) +
+ MsEdgeTTS.BINARY_DELIM.length;
+ const audioData = audioBuffer.subarray(audioStartIndex);
+ this._streams[requestId].push(audioData);
+ this._log("received audio chunk, size: ", audioData?.length);
+ }
+
+ private _SSMLTemplate(input: string, options: ProsodyOptions = {}): string {
+ // in case future updates to the edge API block these elements, we'll be concatenating strings.
+ options = { ...new ProsodyOptions(), ...options };
+ return `
+
+
+ ${input}
+
+
+ `;
+ }
+
+ /**
+ * Fetch the list of voices available in Microsoft Edge.
+ * These, however, are not all. The complete list of voices supported by this module [can be found here](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/language-support) (neural, standard, and preview).
+ */
+ // getVoices(): Promise {
+ // return new Promise((resolve, reject) => {
+ // axios
+ // .get(MsEdgeTTS.VOICES_URL)
+ // .then((res) => resolve(res.data))
+ // .catch(reject);
+ // });
+ // }
+ getVoices(): Promise {
+ return fetch(MsEdgeTTS.VOICES_URL)
+ .then((response) => {
+ if (!response.ok) {
+ throw new Error("Network response was not ok");
+ }
+ return response.json();
+ })
+ .then((data) => data as Voice[])
+ .catch((error) => {
+ throw error;
+ });
+ }
+
+ /**
+ * Sets the required information for the speech to be synthesised and inits a new WebSocket connection.
+ * Must be called at least once before text can be synthesised.
+ * Saved in this instance. Can be called at any time times to update the metadata.
+ *
+ * @param voiceName a string with any `ShortName`. A list of all available neural voices can be found [here](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/language-support#neural-voices). However, it is not limited to neural voices: standard voices can also be used. A list of standard voices can be found [here](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/language-support#standard-voices)
+ * @param outputFormat any {@link OUTPUT_FORMAT}
+ * @param voiceLocale (optional) any voice locale that is supported by the voice. See the list of all voices for compatibility. If not provided, the locale will be inferred from the `voiceName`
+ */
+ async setMetadata(
+ voiceName: string,
+ outputFormat: OUTPUT_FORMAT,
+ voiceLocale?: string,
+ ) {
+ const oldVoice = this._voice;
+ const oldVoiceLocale = this._voiceLocale;
+ const oldOutputFormat = this._outputFormat;
+
+ this._voice = voiceName;
+ this._voiceLocale = voiceLocale;
+ if (!this._voiceLocale) {
+ const voiceLangMatch = MsEdgeTTS.VOICE_LANG_REGEX.exec(this._voice);
+ if (!voiceLangMatch)
+ throw new Error("Could not infer voiceLocale from voiceName!");
+ this._voiceLocale = voiceLangMatch[0];
+ }
+ this._outputFormat = outputFormat;
+
+ const changed =
+ oldVoice !== this._voice ||
+ oldVoiceLocale !== this._voiceLocale ||
+ oldOutputFormat !== this._outputFormat;
+
+ // create new client
+ if (changed || this._ws!.readyState !== this._ws!.OPEN) {
+ this._startTime = Date.now();
+ await this._initClient();
+ }
+ }
+
+ private _metadataCheck() {
+ if (!this._ws)
+ throw new Error(
+ "Speech synthesis not configured yet. Run setMetadata before calling toStream or toFile.",
+ );
+ }
+
+ /**
+ * Close the WebSocket connection.
+ */
+ close() {
+ this._ws!.close();
+ }
+
+ /**
+ * Writes raw audio synthesised from text in real-time to a {@link Readable}. Uses a basic {@link _SSMLTemplate SML template}.
+ *
+ * @param input the text to synthesise. Can include SSML elements.
+ * @param options (optional) {@link ProsodyOptions}
+ * @returns {Readable} - a `stream.Readable` with the audio data
+ */
+ toStream(input: string, options?: ProsodyOptions): Readable {
+ const { stream } = this._rawSSMLRequest(this._SSMLTemplate(input, options));
+ return stream;
+ }
+
+ toArrayBuffer(input: string, options?: ProsodyOptions): Promise {
+ return new Promise((resolve, reject) => {
+ let data: Uint8Array[] = [];
+ const readable = this.toStream(input, options);
+ readable.on("data", (chunk) => {
+ data.push(chunk);
+ });
+
+ readable.on("end", () => {
+ resolve(Buffer.concat(data).buffer);
+ });
+
+ readable.on("error", (err) => {
+ reject(err);
+ });
+ });
+ }
+
+ /**
+ * Writes raw audio synthesised from a request in real-time to a {@link Readable}. Has no SSML template. Basic SSML should be provided in the request.
+ *
+ * @param requestSSML the SSML to send. SSML elements required in order to work.
+ * @returns {Readable} - a `stream.Readable` with the audio data
+ */
+ rawToStream(requestSSML: string): Readable {
+ const { stream } = this._rawSSMLRequest(requestSSML);
+ return stream;
+ }
+
+ private _rawSSMLRequest(requestSSML: string): {
+ stream: Readable;
+ requestId: string;
+ } {
+ this._metadataCheck();
+
+ const requestId = randomBytes(16).toString("hex");
+ const request =
+ `X-RequestId:${requestId}\r\nContent-Type:application/ssml+xml\r\nPath:ssml\r\n\r\n
+ ` + requestSSML.trim();
+ // https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/speech-synthesis-markup
+ const self = this;
+ const stream = new Readable({
+ read() {},
+ destroy(error: Error | null, callback: (error: Error | null) => void) {
+ delete self._streams[requestId];
+ callback(error);
+ },
+ });
+ this._streams[requestId] = stream;
+ this._send(request).then();
+ return { stream, requestId };
+ }
+}
diff --git a/package.json b/package.json
index ca5fcc0f5..8696f83b5 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
"idb-keyval": "^6.2.1",
"lodash-es": "^4.17.21",
"mermaid": "^10.6.1",
+ "markdown-to-txt": "^2.0.1",
"nanoid": "^5.0.3",
"next": "^14.1.1",
"node-fetch": "^3.3.1",
@@ -66,6 +67,7 @@
"eslint-config-next": "13.4.19",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^5.1.3",
+ "eslint-plugin-unused-imports": "^3.2.0",
"husky": "^8.0.0",
"lint-staged": "^13.2.2",
"prettier": "^3.0.2",
@@ -78,4 +80,4 @@
"lint-staged/yaml": "^2.2.2"
},
"packageManager": "yarn@1.22.19"
-}
+}
\ No newline at end of file
diff --git a/public/plugins.json b/public/plugins.json
new file mode 100644
index 000000000..c4d7ec46a
--- /dev/null
+++ b/public/plugins.json
@@ -0,0 +1,17 @@
+[
+ {
+ "id": "dalle3",
+ "name": "Dalle3",
+ "schema": "https://ghp.ci/https://raw.githubusercontent.com/ChatGPTNextWeb/NextChat-Awesome-Plugins/main/plugins/dalle/openapi.json"
+ },
+ {
+ "id": "arxivsearch",
+ "name": "ArxivSearch",
+ "schema": "https://ghp.ci/https://raw.githubusercontent.com/ChatGPTNextWeb/NextChat-Awesome-Plugins/main/plugins/arxivsearch/openapi.json"
+ },
+ {
+ "id": "duckduckgolite",
+ "name": "DuckDuckGoLiteSearch",
+ "schema": "https://ghp.ci/https://raw.githubusercontent.com/ChatGPTNextWeb/NextChat-Awesome-Plugins/main/plugins/duckduckgolite/openapi.json"
+ }
+]
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 2a19c9332..eb0d411cb 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -9,7 +9,7 @@
},
"package": {
"productName": "NextChat",
- "version": "2.15.2"
+ "version": "2.15.3"
},
"tauri": {
"allowlist": {
diff --git a/yarn.lock b/yarn.lock
index 4979e4d99..7e7dd3484 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3367,6 +3367,18 @@ eslint-plugin-react@^7.31.7:
semver "^6.3.0"
string.prototype.matchall "^4.0.8"
+eslint-plugin-unused-imports@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.2.0.tgz#63a98c9ad5f622cd9f830f70bc77739f25ccfe0d"
+ integrity sha512-6uXyn6xdINEpxE1MtDjxQsyXB37lfyO2yKGVVgtD7WEWQGORSOZjgrD6hBhvGv4/SO+TOlS+UnC6JppRqbuwGQ==
+ dependencies:
+ eslint-rule-composer "^0.3.0"
+
+eslint-rule-composer@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"
+ integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==
+
eslint-scope@5.1.1:
version "5.1.1"
resolved "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
@@ -4443,11 +4455,21 @@ lodash.debounce@^4.0.8:
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
+lodash.escape@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98"
+ integrity sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==
+
lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+lodash.unescape@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
+ integrity sha512-DhhGRshNS1aX6s5YdBE3njCCouPgnG29ebyHvImlZzXZf2SHgt+J08DHgytTPnpywNbO1Y8mNUFyQuIDBq2JZg==
+
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
@@ -4503,6 +4525,20 @@ markdown-table@^3.0.0:
resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd"
integrity sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==
+markdown-to-txt@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/markdown-to-txt/-/markdown-to-txt-2.0.1.tgz#bfd6233a2635443cc24900a158b60c6af36ce9c5"
+ integrity sha512-Hsj7KTN8k1gutlLum3vosHwVZGnv8/cbYKWVkUyo/D1rzOYddbDesILebRfOsaVfjIBJank/AVOySBlHAYqfZw==
+ dependencies:
+ lodash.escape "^4.0.1"
+ lodash.unescape "^4.0.1"
+ marked "^4.0.14"
+
+marked@^4.0.14:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3"
+ integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==
+
mdast-util-definitions@^5.0.0:
version "5.1.2"
resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz#9910abb60ac5d7115d6819b57ae0bcef07a3f7a7"