diff --git a/app/components/realtime-chat/index.ts b/app/components/realtime-chat/index.ts
new file mode 100644
index 00000000000..fdf090f4195
--- /dev/null
+++ b/app/components/realtime-chat/index.ts
@@ -0,0 +1 @@
+export * from "./realtime-chat";
diff --git a/app/components/realtime-chat/realtime-chat.module.scss b/app/components/realtime-chat/realtime-chat.module.scss
new file mode 100644
index 00000000000..ef58bebb655
--- /dev/null
+++ b/app/components/realtime-chat/realtime-chat.module.scss
@@ -0,0 +1,74 @@
+.realtime-chat {
+ width: 100%;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ padding: 20px;
+ box-sizing: border-box;
+ .circle-mic {
+ width: 150px;
+ height: 150px;
+ border-radius: 50%;
+ background: linear-gradient(to bottom right, #a0d8ef, #f0f8ff);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+ .icon-center {
+ font-size: 24px;
+ }
+
+ .bottom-icons {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ position: absolute;
+ bottom: 20px;
+ box-sizing: border-box;
+ padding: 0 20px;
+ }
+
+ .icon-left,
+ .icon-right {
+ width: 46px;
+ height: 46px;
+ font-size: 36px;
+ background: var(--second);
+ border-radius: 50%;
+ padding: 2px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ &:hover {
+ opacity: 0.8;
+ }
+ }
+
+ &.mobile {
+ display: none;
+ }
+}
+
+.pulse {
+ animation: pulse 1.5s infinite;
+}
+
+@keyframes pulse {
+ 0% {
+ transform: scale(1);
+ opacity: 0.7;
+ }
+ 50% {
+ transform: scale(1.1);
+ opacity: 1;
+ }
+ 100% {
+ transform: scale(1);
+ opacity: 0.7;
+ }
+}
diff --git a/app/components/realtime-chat/realtime-chat.tsx b/app/components/realtime-chat/realtime-chat.tsx
new file mode 100644
index 00000000000..faa36373a2c
--- /dev/null
+++ b/app/components/realtime-chat/realtime-chat.tsx
@@ -0,0 +1,359 @@
+import VoiceIcon from "@/app/icons/voice.svg";
+import VoiceOffIcon from "@/app/icons/voice-off.svg";
+import PowerIcon from "@/app/icons/power.svg";
+
+import styles from "./realtime-chat.module.scss";
+import clsx from "clsx";
+
+import { useState, useRef, useEffect } from "react";
+
+import { useChatStore, createMessage, useAppConfig } from "@/app/store";
+
+import { IconButton } from "@/app/components/button";
+
+import {
+ Modality,
+ RTClient,
+ RTInputAudioItem,
+ RTResponse,
+ TurnDetection,
+} from "rt-client";
+import { AudioHandler } from "@/app/lib/audio";
+import { uploadImage } from "@/app/utils/chat";
+import { VoicePrint } from "@/app/components/voice-print";
+
+interface RealtimeChatProps {
+ onClose?: () => void;
+ onStartVoice?: () => void;
+ onPausedVoice?: () => void;
+}
+
+export function RealtimeChat({
+ onClose,
+ onStartVoice,
+ onPausedVoice,
+}: RealtimeChatProps) {
+ const chatStore = useChatStore();
+ const session = chatStore.currentSession();
+ const config = useAppConfig();
+ const [status, setStatus] = useState("");
+ const [isRecording, setIsRecording] = useState(false);
+ const [isConnected, setIsConnected] = useState(false);
+ const [isConnecting, setIsConnecting] = useState(false);
+ const [modality, setModality] = useState("audio");
+ const [useVAD, setUseVAD] = useState(true);
+ const [frequencies, setFrequencies] = useState();
+
+ const clientRef = useRef(null);
+ const audioHandlerRef = useRef(null);
+ const initRef = useRef(false);
+
+ const temperature = config.realtimeConfig.temperature;
+ const apiKey = config.realtimeConfig.apiKey;
+ const model = config.realtimeConfig.model;
+ const azure = config.realtimeConfig.provider === "Azure";
+ const azureEndpoint = config.realtimeConfig.azure.endpoint;
+ const azureDeployment = config.realtimeConfig.azure.deployment;
+ const voice = config.realtimeConfig.voice;
+
+ const handleConnect = async () => {
+ if (isConnecting) return;
+ if (!isConnected) {
+ try {
+ setIsConnecting(true);
+ clientRef.current = azure
+ ? new RTClient(
+ new URL(azureEndpoint),
+ { key: apiKey },
+ { deployment: azureDeployment },
+ )
+ : new RTClient({ key: apiKey }, { model });
+ const modalities: Modality[] =
+ modality === "audio" ? ["text", "audio"] : ["text"];
+ const turnDetection: TurnDetection = useVAD
+ ? { type: "server_vad" }
+ : null;
+ await clientRef.current.configure({
+ instructions: "",
+ voice,
+ input_audio_transcription: { model: "whisper-1" },
+ turn_detection: turnDetection,
+ tools: [],
+ temperature,
+ modalities,
+ });
+ startResponseListener();
+
+ setIsConnected(true);
+ // TODO
+ // try {
+ // const recentMessages = chatStore.getMessagesWithMemory();
+ // for (const message of recentMessages) {
+ // const { role, content } = message;
+ // if (typeof content === "string") {
+ // await clientRef.current.sendItem({
+ // type: "message",
+ // role: role as any,
+ // content: [
+ // {
+ // type: (role === "assistant" ? "text" : "input_text") as any,
+ // text: content as string,
+ // },
+ // ],
+ // });
+ // }
+ // }
+ // // await clientRef.current.generateResponse();
+ // } catch (error) {
+ // console.error("Set message failed:", error);
+ // }
+ } catch (error) {
+ console.error("Connection failed:", error);
+ setStatus("Connection failed");
+ } finally {
+ setIsConnecting(false);
+ }
+ } else {
+ await disconnect();
+ }
+ };
+
+ const disconnect = async () => {
+ if (clientRef.current) {
+ try {
+ await clientRef.current.close();
+ clientRef.current = null;
+ setIsConnected(false);
+ } catch (error) {
+ console.error("Disconnect failed:", error);
+ }
+ }
+ };
+
+ const startResponseListener = async () => {
+ if (!clientRef.current) return;
+
+ try {
+ for await (const serverEvent of clientRef.current.events()) {
+ if (serverEvent.type === "response") {
+ await handleResponse(serverEvent);
+ } else if (serverEvent.type === "input_audio") {
+ await handleInputAudio(serverEvent);
+ }
+ }
+ } catch (error) {
+ if (clientRef.current) {
+ console.error("Response iteration error:", error);
+ }
+ }
+ };
+
+ const handleResponse = async (response: RTResponse) => {
+ for await (const item of response) {
+ if (item.type === "message" && item.role === "assistant") {
+ const botMessage = createMessage({
+ role: item.role,
+ content: "",
+ });
+ // add bot message first
+ chatStore.updateTargetSession(session, (session) => {
+ session.messages = session.messages.concat([botMessage]);
+ });
+ let hasAudio = false;
+ for await (const content of item) {
+ if (content.type === "text") {
+ for await (const text of content.textChunks()) {
+ botMessage.content += text;
+ }
+ } else if (content.type === "audio") {
+ const textTask = async () => {
+ for await (const text of content.transcriptChunks()) {
+ botMessage.content += text;
+ }
+ };
+ const audioTask = async () => {
+ audioHandlerRef.current?.startStreamingPlayback();
+ for await (const audio of content.audioChunks()) {
+ hasAudio = true;
+ audioHandlerRef.current?.playChunk(audio);
+ }
+ };
+ await Promise.all([textTask(), audioTask()]);
+ }
+ // update message.content
+ chatStore.updateTargetSession(session, (session) => {
+ session.messages = session.messages.concat();
+ });
+ }
+ if (hasAudio) {
+ // upload audio get audio_url
+ const blob = audioHandlerRef.current?.savePlayFile();
+ uploadImage(blob!).then((audio_url) => {
+ botMessage.audio_url = audio_url;
+ // update text and audio_url
+ chatStore.updateTargetSession(session, (session) => {
+ session.messages = session.messages.concat();
+ });
+ });
+ }
+ }
+ }
+ };
+
+ const handleInputAudio = async (item: RTInputAudioItem) => {
+ await item.waitForCompletion();
+ if (item.transcription) {
+ const userMessage = createMessage({
+ role: "user",
+ content: item.transcription,
+ });
+ chatStore.updateTargetSession(session, (session) => {
+ session.messages = session.messages.concat([userMessage]);
+ });
+ // save input audio_url, and update session
+ const { audioStartMillis, audioEndMillis } = item;
+ // upload audio get audio_url
+ const blob = audioHandlerRef.current?.saveRecordFile(
+ audioStartMillis,
+ audioEndMillis,
+ );
+ uploadImage(blob!).then((audio_url) => {
+ userMessage.audio_url = audio_url;
+ chatStore.updateTargetSession(session, (session) => {
+ session.messages = session.messages.concat();
+ });
+ });
+ }
+ // stop streaming play after get input audio.
+ audioHandlerRef.current?.stopStreamingPlayback();
+ };
+
+ const toggleRecording = async () => {
+ if (!isRecording && clientRef.current) {
+ try {
+ if (!audioHandlerRef.current) {
+ audioHandlerRef.current = new AudioHandler();
+ await audioHandlerRef.current.initialize();
+ }
+ await audioHandlerRef.current.startRecording(async (chunk) => {
+ await clientRef.current?.sendAudio(chunk);
+ });
+ setIsRecording(true);
+ } catch (error) {
+ console.error("Failed to start recording:", error);
+ }
+ } else if (audioHandlerRef.current) {
+ try {
+ audioHandlerRef.current.stopRecording();
+ if (!useVAD) {
+ const inputAudio = await clientRef.current?.commitAudio();
+ await handleInputAudio(inputAudio!);
+ await clientRef.current?.generateResponse();
+ }
+ setIsRecording(false);
+ } catch (error) {
+ console.error("Failed to stop recording:", error);
+ }
+ }
+ };
+
+ useEffect(() => {
+ // 防止重复初始化
+ if (initRef.current) return;
+ initRef.current = true;
+
+ const initAudioHandler = async () => {
+ const handler = new AudioHandler();
+ await handler.initialize();
+ audioHandlerRef.current = handler;
+ await handleConnect();
+ await toggleRecording();
+ };
+
+ initAudioHandler().catch((error) => {
+ setStatus(error);
+ console.error(error);
+ });
+
+ return () => {
+ if (isRecording) {
+ toggleRecording();
+ }
+ audioHandlerRef.current?.close().catch(console.error);
+ disconnect();
+ };
+ }, []);
+
+ useEffect(() => {
+ let animationFrameId: number;
+
+ if (isConnected && isRecording) {
+ const animationFrame = () => {
+ if (audioHandlerRef.current) {
+ const freqData = audioHandlerRef.current.getByteFrequencyData();
+ setFrequencies(freqData);
+ }
+ animationFrameId = requestAnimationFrame(animationFrame);
+ };
+
+ animationFrameId = requestAnimationFrame(animationFrame);
+ } else {
+ setFrequencies(undefined);
+ }
+
+ return () => {
+ if (animationFrameId) {
+ cancelAnimationFrame(animationFrameId);
+ }
+ };
+ }, [isConnected, isRecording]);
+
+ // update session params
+ useEffect(() => {
+ clientRef.current?.configure({ voice });
+ }, [voice]);
+ useEffect(() => {
+ clientRef.current?.configure({ temperature });
+ }, [temperature]);
+
+ const handleClose = async () => {
+ onClose?.();
+ if (isRecording) {
+ await toggleRecording();
+ }
+ disconnect().catch(console.error);
+ };
+
+ return (
+
+
+
+
+
+
+
+ : }
+ onClick={toggleRecording}
+ disabled={!isConnected}
+ shadow
+ bordered
+ />
+
+
{status}
+
+ }
+ onClick={handleClose}
+ shadow
+ bordered
+ />
+
+
+
+ );
+}
diff --git a/app/components/realtime-chat/realtime-config.tsx b/app/components/realtime-chat/realtime-config.tsx
new file mode 100644
index 00000000000..08809afda2f
--- /dev/null
+++ b/app/components/realtime-chat/realtime-config.tsx
@@ -0,0 +1,173 @@
+import { RealtimeConfig } from "@/app/store";
+
+import Locale from "@/app/locales";
+import { ListItem, Select, PasswordInput } from "@/app/components/ui-lib";
+
+import { InputRange } from "@/app/components/input-range";
+import { Voice } from "rt-client";
+import { ServiceProvider } from "@/app/constant";
+
+const providers = [ServiceProvider.OpenAI, ServiceProvider.Azure];
+
+const models = ["gpt-4o-realtime-preview-2024-10-01"];
+
+const voice = ["alloy", "shimmer", "echo"];
+
+export function RealtimeConfigList(props: {
+ realtimeConfig: RealtimeConfig;
+ updateConfig: (updater: (config: RealtimeConfig) => void) => void;
+}) {
+ const azureConfigComponent = props.realtimeConfig.provider ===
+ ServiceProvider.Azure && (
+ <>
+
+ {
+ props.updateConfig(
+ (config) => (config.azure.endpoint = e.currentTarget.value),
+ );
+ }}
+ />
+
+
+ {
+ props.updateConfig(
+ (config) => (config.azure.deployment = e.currentTarget.value),
+ );
+ }}
+ />
+
+ >
+ );
+
+ return (
+ <>
+
+
+ props.updateConfig(
+ (config) => (config.enable = e.currentTarget.checked),
+ )
+ }
+ >
+
+
+ {props.realtimeConfig.enable && (
+ <>
+
+ {
+ props.updateConfig(
+ (config) =>
+ (config.provider = e.target.value as ServiceProvider),
+ );
+ }}
+ >
+ {providers.map((v, i) => (
+
+ {v}
+
+ ))}
+
+
+
+ {
+ props.updateConfig((config) => (config.model = e.target.value));
+ }}
+ >
+ {models.map((v, i) => (
+
+ {v}
+
+ ))}
+
+
+
+ {
+ props.updateConfig(
+ (config) => (config.apiKey = e.currentTarget.value),
+ );
+ }}
+ />
+
+ {azureConfigComponent}
+
+ {
+ props.updateConfig(
+ (config) => (config.voice = e.currentTarget.value as Voice),
+ );
+ }}
+ >
+ {voice.map((v, i) => (
+
+ {v}
+
+ ))}
+
+
+
+ {
+ props.updateConfig(
+ (config) =>
+ (config.temperature = e.currentTarget.valueAsNumber),
+ );
+ }}
+ >
+
+ >
+ )}
+ >
+ );
+}
diff --git a/app/components/sd/sd-panel.tsx b/app/components/sd/sd-panel.tsx
index a71e560ddef..15aff0ab608 100644
--- a/app/components/sd/sd-panel.tsx
+++ b/app/components/sd/sd-panel.tsx
@@ -4,6 +4,7 @@ import { Select } from "@/app/components/ui-lib";
import { IconButton } from "@/app/components/button";
import Locale from "@/app/locales";
import { useSdStore } from "@/app/store/sd";
+import clsx from "clsx";
export const params = [
{
@@ -136,7 +137,7 @@ export function ControlParamItem(props: {
className?: string;
}) {
return (
-
+
diff --git a/app/components/sd/sd.tsx b/app/components/sd/sd.tsx
index 0ace62a83cb..1ccc0647e4c 100644
--- a/app/components/sd/sd.tsx
+++ b/app/components/sd/sd.tsx
@@ -36,6 +36,7 @@ import { removeImage } from "@/app/utils/chat";
import { SideBar } from "./sd-sidebar";
import { WindowContent } from "@/app/components/home";
import { params } from "./sd-panel";
+import clsx from "clsx";
function getSdTaskStatus(item: any) {
let s: string;
@@ -104,7 +105,7 @@ export function Sd() {
return (
<>
-
+
@@ -121,7 +122,10 @@ export function Sd() {
)}
Stability AI
diff --git a/app/components/settings.module.scss b/app/components/settings.module.scss
index c6aec4203cf..60664f93aec 100644
--- a/app/components/settings.module.scss
+++ b/app/components/settings.module.scss
@@ -72,3 +72,9 @@
}
}
}
+
+.subtitle-button {
+ button {
+ overflow:visible ;
+ }
+}
diff --git a/app/components/settings.tsx b/app/components/settings.tsx
index ca0a5a18796..881c12caeb3 100644
--- a/app/components/settings.tsx
+++ b/app/components/settings.tsx
@@ -9,6 +9,7 @@ import CopyIcon from "../icons/copy.svg";
import ClearIcon from "../icons/clear.svg";
import LoadingIcon from "../icons/three-dots.svg";
import EditIcon from "../icons/edit.svg";
+import FireIcon from "../icons/fire.svg";
import EyeIcon from "../icons/eye.svg";
import DownloadIcon from "../icons/download.svg";
import UploadIcon from "../icons/upload.svg";
@@ -18,7 +19,7 @@ import ConfirmIcon from "../icons/confirm.svg";
import ConnectionIcon from "../icons/connection.svg";
import CloudSuccessIcon from "../icons/cloud-success.svg";
import CloudFailIcon from "../icons/cloud-fail.svg";
-
+import { trackSettingsPageGuideToCPaymentClick } from "../utils/auth-settings-events";
import {
Input,
List,
@@ -48,7 +49,7 @@ import Locale, {
changeLang,
getLang,
} from "../locales";
-import { copyToClipboard } from "../utils";
+import { copyToClipboard, clientUpdate, semverCompare } from "../utils";
import Link from "next/link";
import {
Anthropic,
@@ -58,6 +59,7 @@ import {
ByteDance,
Alibaba,
Moonshot,
+ XAI,
Google,
GoogleSafetySettingsThreshold,
OPENAI_BASE_URL,
@@ -69,6 +71,11 @@ import {
UPDATE_URL,
Stability,
Iflytek,
+ SAAS_CHAT_URL,
+ ChatGLM,
+ DeepSeek,
+ SiliconFlow,
+ AI302,
} from "../constant";
import { Prompt, SearchService, usePromptStore } from "../store/prompt";
import { ErrorBoundary } from "./error";
@@ -80,6 +87,8 @@ import { useSyncStore } from "../store/sync";
import { nanoid } from "nanoid";
import { useMaskStore } from "../store/mask";
import { ProviderType } from "../utils/cloud";
+import { TTSConfigList } from "./tts-config";
+import { RealtimeConfigList } from "./realtime-chat/realtime-config";
function EditPromptModal(props: { id: string; onClose: () => void }) {
const promptStore = usePromptStore();
@@ -582,7 +591,7 @@ export function Settings() {
const [checkingUpdate, setCheckingUpdate] = useState(false);
const currentVersion = updateStore.formatVersion(updateStore.version);
const remoteId = updateStore.formatVersion(updateStore.remoteVersion);
- const hasNewVersion = currentVersion !== remoteId;
+ const hasNewVersion = semverCompare(currentVersion, remoteId) === -1;
const updateUrl = getClientConfig()?.isApp ? RELEASE_URL : UPDATE_URL;
function checkUpdate(force = false) {
@@ -685,6 +694,31 @@ export function Settings() {
);
+ const saasStartComponent = (
+
+ }
+ type={"primary"}
+ text={Locale.Settings.Access.SaasStart.ChatNow}
+ onClick={() => {
+ trackSettingsPageGuideToCPaymentClick();
+ window.location.href = SAAS_CHAT_URL;
+ }}
+ />
+
+ );
+
const useCustomConfigComponent = // Conditionally render the following ListItem based on clientConfig.isApp
!clientConfig?.isApp && ( // only show if isApp is false
);
+ const deepseekConfigComponent = accessStore.provider ===
+ ServiceProvider.DeepSeek && (
+ <>
+
+
+ accessStore.update(
+ (access) => (access.deepseekUrl = e.currentTarget.value),
+ )
+ }
+ >
+
+
+ {
+ accessStore.update(
+ (access) => (access.deepseekApiKey = e.currentTarget.value),
+ );
+ }}
+ />
+
+ >
+ );
+
+ const XAIConfigComponent = accessStore.provider === ServiceProvider.XAI && (
+ <>
+
+
+ accessStore.update(
+ (access) => (access.xaiUrl = e.currentTarget.value),
+ )
+ }
+ >
+
+
+ {
+ accessStore.update(
+ (access) => (access.xaiApiKey = e.currentTarget.value),
+ );
+ }}
+ />
+
+ >
+ );
+
+ const chatglmConfigComponent = accessStore.provider ===
+ ServiceProvider.ChatGLM && (
+ <>
+
+
+ accessStore.update(
+ (access) => (access.chatglmUrl = e.currentTarget.value),
+ )
+ }
+ >
+
+
+ {
+ accessStore.update(
+ (access) => (access.chatglmApiKey = e.currentTarget.value),
+ );
+ }}
+ />
+
+ >
+ );
+ const siliconflowConfigComponent = accessStore.provider ===
+ ServiceProvider.SiliconFlow && (
+ <>
+
+
+ accessStore.update(
+ (access) => (access.siliconflowUrl = e.currentTarget.value),
+ )
+ }
+ >
+
+
+ {
+ accessStore.update(
+ (access) => (access.siliconflowApiKey = e.currentTarget.value),
+ );
+ }}
+ />
+
+ >
+ );
+
const stabilityConfigComponent = accessStore.provider ===
ServiceProvider.Stability && (
<>
@@ -1264,6 +1459,46 @@ export function Settings() {
>
);
+ const ai302ConfigComponent = accessStore.provider === ServiceProvider["302.AI"] && (
+ <>
+
+
+ accessStore.update(
+ (access) => (access.ai302Url = e.currentTarget.value),
+ )
+ }
+ >
+
+
+ {
+ accessStore.update(
+ (access) => (access.ai302ApiKey = e.currentTarget.value),
+ );
+ }}
+ />
+
+ >
+ );
+
return (
@@ -1329,9 +1564,17 @@ export function Settings() {
{checkingUpdate ? (
) : hasNewVersion ? (
-
- {Locale.Settings.Update.GoToUpdate}
-
+ clientConfig?.isApp ? (
+ }
+ text={Locale.Settings.Update.GoToUpdate}
+ onClick={() => clientUpdate()}
+ />
+ ) : (
+
+ {Locale.Settings.Update.GoToUpdate}
+
+ )
) : (
}
@@ -1464,6 +1707,39 @@ export function Settings() {
}
>
+
+
+
+ updateConfig(
+ (config) =>
+ (config.enableArtifacts = e.currentTarget.checked),
+ )
+ }
+ >
+
+
+
+ updateConfig(
+ (config) => (config.enableCodeFold = e.currentTarget.checked),
+ )
+ }
+ >
+
@@ -1540,6 +1816,7 @@ export function Settings() {
+ {saasStartComponent}
{accessCodeComponent}
{!accessStore.hideUserApiKey && (
@@ -1580,8 +1857,13 @@ export function Settings() {
{alibabaConfigComponent}
{tencentConfigComponent}
{moonshotConfigComponent}
+ {deepseekConfigComponent}
{stabilityConfigComponent}
{lflytekConfigComponent}
+ {XAIConfigComponent}
+ {chatglmConfigComponent}
+ {siliconflowConfigComponent}
+ {ai302ConfigComponent}
>
)}
>
@@ -1616,9 +1898,11 @@ export function Settings() {
setShowPromptModal(false)} />
)}
+
+ {
+ const realtimeConfig = { ...config.realtimeConfig };
+ updater(realtimeConfig);
+ config.update(
+ (config) => (config.realtimeConfig = realtimeConfig),
+ );
+ }}
+ />
+
+
+ {
+ const ttsConfig = { ...config.ttsConfig };
+ updater(ttsConfig);
+ config.update((config) => (config.ttsConfig = ttsConfig));
+ }}
+ />
+
diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx
index 4ec0f8c84f5..56bc5bb4327 100644
--- a/app/components/sidebar.tsx
+++ b/app/components/sidebar.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef, useMemo, useState, Fragment } from "react";
+import React, { Fragment, useEffect, useMemo, useRef, useState } from "react";
import styles from "./home.module.scss";
@@ -7,9 +7,9 @@ import SettingsIcon from "../icons/settings.svg";
import GithubIcon from "../icons/github.svg";
import ChatGptIcon from "../icons/chatgpt.svg";
import AddIcon from "../icons/add.svg";
-import CloseIcon from "../icons/close.svg";
import DeleteIcon from "../icons/delete.svg";
import MaskIcon from "../icons/mask.svg";
+import McpIcon from "../icons/mcp.svg";
import DragIcon from "../icons/drag.svg";
import DiscoveryIcon from "../icons/discovery.svg";
@@ -23,14 +23,21 @@ import {
MIN_SIDEBAR_WIDTH,
NARROW_SIDEBAR_WIDTH,
Path,
- PLUGINS,
REPO_URL,
} from "../constant";
import { Link, useNavigate } from "react-router-dom";
import { isIOS, useMobileScreen } from "../utils";
import dynamic from "next/dynamic";
-import { showConfirm, Selector } from "./ui-lib";
+import { Selector, showConfirm } from "./ui-lib";
+import clsx from "clsx";
+import { isMcpEnabled } from "../mcp/actions";
+
+const DISCOVERY = [
+ { name: Locale.Plugin.Name, path: Path.Plugins },
+ { name: "Stable Diffusion", path: Path.Sd },
+ { name: Locale.SearchChat.Page.Title, path: Path.SearchChat },
+];
const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, {
loading: () => null,
@@ -128,6 +135,7 @@ export function useDragSideBar() {
shouldNarrow,
};
}
+
export function SideBarContainer(props: {
children: React.ReactNode;
onDragStart: (e: MouseEvent) => void;
@@ -142,9 +150,9 @@ export function SideBarContainer(props: {
const { children, className, onDragStart, shouldNarrow } = props;
return (
-
+
{children}
@@ -213,10 +227,21 @@ export function SideBarTail(props: {
export function SideBar(props: { className?: string }) {
useHotKey();
const { onDragStart, shouldNarrow } = useDragSideBar();
- const [showPluginSelector, setShowPluginSelector] = useState(false);
+ const [showDiscoverySelector, setshowDiscoverySelector] = useState(false);
const navigate = useNavigate();
const config = useAppConfig();
const chatStore = useChatStore();
+ const [mcpEnabled, setMcpEnabled] = useState(false);
+
+ useEffect(() => {
+ // 检查 MCP 是否启用
+ const checkMcpStatus = async () => {
+ const enabled = await isMcpEnabled();
+ setMcpEnabled(enabled);
+ console.log("[SideBar] MCP enabled:", enabled);
+ };
+ checkMcpStatus();
+ }, []);
return (
}
+ shouldNarrow={shouldNarrow}
>
+ {mcpEnabled && (
+ }
+ text={shouldNarrow ? undefined : Locale.Mcp.Name}
+ className={styles["sidebar-bar-button"]}
+ onClick={() => {
+ navigate(Path.McpMarket, { state: { fromHome: true } });
+ }}
+ shadow
+ />
+ )}
}
text={shouldNarrow ? undefined : Locale.Discovery.Name}
className={styles["sidebar-bar-button"]}
- onClick={() => setShowPluginSelector(true)}
+ onClick={() => setshowDiscoverySelector(true)}
shadow
/>
- {showPluginSelector && (
+ {showDiscoverySelector && (
{
+ ...DISCOVERY.map((item) => {
return {
title: item.name,
value: item.path,
};
}),
]}
- onClose={() => setShowPluginSelector(false)}
+ onClose={() => setshowDiscoverySelector(false)}
onSelection={(s) => {
navigate(s[0], { state: { fromHome: true } });
}}
@@ -285,7 +317,7 @@ export function SideBar(props: { className?: string }) {
-
+
}
onClick={async () => {
diff --git a/app/components/tts-config.tsx b/app/components/tts-config.tsx
new file mode 100644
index 00000000000..39ae85730c2
--- /dev/null
+++ b/app/components/tts-config.tsx
@@ -0,0 +1,133 @@
+import { TTSConfig, TTSConfigValidator } from "../store";
+
+import Locale from "../locales";
+import { ListItem, Select } from "./ui-lib";
+import {
+ DEFAULT_TTS_ENGINE,
+ DEFAULT_TTS_ENGINES,
+ DEFAULT_TTS_MODELS,
+ DEFAULT_TTS_VOICES,
+} from "../constant";
+import { InputRange } from "./input-range";
+
+export function TTSConfigList(props: {
+ ttsConfig: TTSConfig;
+ updateConfig: (updater: (config: TTSConfig) => void) => void;
+}) {
+ return (
+ <>
+
+
+ props.updateConfig(
+ (config) => (config.enable = e.currentTarget.checked),
+ )
+ }
+ >
+
+ {/*
+
+ props.updateConfig(
+ (config) => (config.autoplay = e.currentTarget.checked),
+ )
+ }
+ >
+ */}
+
+ {
+ props.updateConfig(
+ (config) =>
+ (config.engine = TTSConfigValidator.engine(
+ e.currentTarget.value,
+ )),
+ );
+ }}
+ >
+ {DEFAULT_TTS_ENGINES.map((v, i) => (
+
+ {v}
+
+ ))}
+
+
+ {props.ttsConfig.engine === DEFAULT_TTS_ENGINE && (
+ <>
+
+ {
+ props.updateConfig(
+ (config) =>
+ (config.model = TTSConfigValidator.model(
+ e.currentTarget.value,
+ )),
+ );
+ }}
+ >
+ {DEFAULT_TTS_MODELS.map((v, i) => (
+
+ {v}
+
+ ))}
+
+
+
+ {
+ props.updateConfig(
+ (config) =>
+ (config.voice = TTSConfigValidator.voice(
+ e.currentTarget.value,
+ )),
+ );
+ }}
+ >
+ {DEFAULT_TTS_VOICES.map((v, i) => (
+
+ {v}
+
+ ))}
+
+
+
+ {
+ props.updateConfig(
+ (config) =>
+ (config.speed = TTSConfigValidator.speed(
+ e.currentTarget.valueAsNumber,
+ )),
+ );
+ }}
+ >
+
+ >
+ )}
+ >
+ );
+}
diff --git a/app/components/tts.module.scss b/app/components/tts.module.scss
new file mode 100644
index 00000000000..ba9f382e40b
--- /dev/null
+++ b/app/components/tts.module.scss
@@ -0,0 +1,119 @@
+@import "../styles/animation.scss";
+.plugin-page {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+
+ .plugin-page-body {
+ padding: 20px;
+ overflow-y: auto;
+
+ .plugin-filter {
+ width: 100%;
+ max-width: 100%;
+ margin-bottom: 20px;
+ animation: slide-in ease 0.3s;
+ height: 40px;
+
+ display: flex;
+
+ .search-bar {
+ flex-grow: 1;
+ max-width: 100%;
+ min-width: 0;
+ outline: none;
+ }
+
+ .search-bar:focus {
+ border: 1px solid var(--primary);
+ }
+
+ .plugin-filter-lang {
+ height: 100%;
+ margin-left: 10px;
+ }
+
+ .plugin-create {
+ height: 100%;
+ margin-left: 10px;
+ box-sizing: border-box;
+ min-width: 80px;
+ }
+ }
+
+ .plugin-item {
+ display: flex;
+ justify-content: space-between;
+ padding: 20px;
+ border: var(--border-in-light);
+ animation: slide-in ease 0.3s;
+
+ &:not(:last-child) {
+ border-bottom: 0;
+ }
+
+ &:first-child {
+ border-top-left-radius: 10px;
+ border-top-right-radius: 10px;
+ }
+
+ &:last-child {
+ border-bottom-left-radius: 10px;
+ border-bottom-right-radius: 10px;
+ }
+
+ .plugin-header {
+ display: flex;
+ align-items: center;
+
+ .plugin-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 10px;
+ }
+
+ .plugin-title {
+ .plugin-name {
+ font-size: 14px;
+ font-weight: bold;
+ }
+ .plugin-info {
+ font-size: 12px;
+ }
+ .plugin-runtime-warning {
+ font-size: 12px;
+ color: #f86c6c;
+ }
+ }
+ }
+
+ .plugin-actions {
+ display: flex;
+ flex-wrap: nowrap;
+ transition: all ease 0.3s;
+ justify-content: center;
+ align-items: center;
+ }
+
+ @media screen and (max-width: 600px) {
+ display: flex;
+ flex-direction: column;
+ padding-bottom: 10px;
+ border-radius: 10px;
+ margin-bottom: 20px;
+ box-shadow: var(--card-shadow);
+
+ &:not(:last-child) {
+ border-bottom: var(--border-in-light);
+ }
+
+ .plugin-actions {
+ width: 100%;
+ justify-content: space-between;
+ padding-top: 10px;
+ }
+ }
+ }
+ }
+}
diff --git a/app/components/ui-lib.module.scss b/app/components/ui-lib.module.scss
index 20bf62a188b..56aeac311ae 100644
--- a/app/components/ui-lib.module.scss
+++ b/app/components/ui-lib.module.scss
@@ -62,14 +62,14 @@
}
}
- &.vertical{
+ &.vertical {
flex-direction: column;
align-items: start;
- .list-header{
- .list-item-title{
+ .list-header {
+ .list-item-title {
margin-bottom: 5px;
}
- .list-item-sub-title{
+ .list-item-sub-title {
margin-bottom: 2px;
}
}
@@ -310,7 +310,7 @@
justify-content: center;
z-index: 999;
- .selector-item-disabled{
+ .selector-item-disabled {
opacity: 0.6;
}
@@ -336,3 +336,4 @@
}
}
}
+
diff --git a/app/components/ui-lib.tsx b/app/components/ui-lib.tsx
index 4af37dbba1c..7b9f5ace028 100644
--- a/app/components/ui-lib.tsx
+++ b/app/components/ui-lib.tsx
@@ -23,6 +23,8 @@ import React, {
useRef,
} from "react";
import { IconButton } from "./button";
+import { Avatar } from "./emoji";
+import clsx from "clsx";
export function Popover(props: {
children: JSX.Element;
@@ -45,7 +47,7 @@ export function Popover(props: {
export function Card(props: { children: JSX.Element[]; className?: string }) {
return (
-
{props.children}
+
{props.children}
);
}
@@ -60,11 +62,13 @@ export function ListItem(props: {
}) {
return (
@@ -135,9 +139,9 @@ export function Modal(props: ModalProps) {
return (
{props.title}
@@ -260,7 +264,7 @@ export function Input(props: InputProps) {
return (
);
}
@@ -301,9 +305,13 @@ export function Select(
const { className, children, align, ...otherProps } = props;
return (
{children}
@@ -509,12 +517,13 @@ export function Selector(props: {
const selected = selectedValues.includes(item.value);
return (
}
onClick={(e) => {
if (item.disable) {
e.stopPropagation();
diff --git a/app/components/voice-print/index.ts b/app/components/voice-print/index.ts
new file mode 100644
index 00000000000..221a695387d
--- /dev/null
+++ b/app/components/voice-print/index.ts
@@ -0,0 +1 @@
+export * from "./voice-print";
diff --git a/app/components/voice-print/voice-print.module.scss b/app/components/voice-print/voice-print.module.scss
new file mode 100644
index 00000000000..b6e51fff409
--- /dev/null
+++ b/app/components/voice-print/voice-print.module.scss
@@ -0,0 +1,11 @@
+.voice-print {
+ width: 100%;
+ height: 60px;
+ margin: 20px 0;
+
+ canvas {
+ width: 100%;
+ height: 100%;
+ filter: brightness(1.2); // 增加整体亮度
+ }
+}
diff --git a/app/components/voice-print/voice-print.tsx b/app/components/voice-print/voice-print.tsx
new file mode 100644
index 00000000000..793210c1930
--- /dev/null
+++ b/app/components/voice-print/voice-print.tsx
@@ -0,0 +1,180 @@
+import { useEffect, useRef, useCallback } from "react";
+import styles from "./voice-print.module.scss";
+
+interface VoicePrintProps {
+ frequencies?: Uint8Array;
+ isActive?: boolean;
+}
+
+export function VoicePrint({ frequencies, isActive }: VoicePrintProps) {
+ // Canvas引用,用于获取绘图上下文
+ const canvasRef = useRef(null);
+ // 存储历史频率数据,用于平滑处理
+ const historyRef = useRef([]);
+ // 控制保留的历史数据帧数,影响平滑度
+ const historyLengthRef = useRef(10);
+ // 存储动画帧ID,用于清理
+ const animationFrameRef = useRef();
+
+ /**
+ * 更新频率历史数据
+ * 使用FIFO队列维护固定长度的历史记录
+ */
+ const updateHistory = useCallback((freqArray: number[]) => {
+ historyRef.current.push(freqArray);
+ if (historyRef.current.length > historyLengthRef.current) {
+ historyRef.current.shift();
+ }
+ }, []);
+
+ useEffect(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ const ctx = canvas.getContext("2d");
+ if (!ctx) return;
+
+ /**
+ * 处理高DPI屏幕显示
+ * 根据设备像素比例调整canvas实际渲染分辨率
+ */
+ const dpr = window.devicePixelRatio || 1;
+ canvas.width = canvas.offsetWidth * dpr;
+ canvas.height = canvas.offsetHeight * dpr;
+ ctx.scale(dpr, dpr);
+
+ /**
+ * 主要绘制函数
+ * 使用requestAnimationFrame实现平滑动画
+ * 包含以下步骤:
+ * 1. 清空画布
+ * 2. 更新历史数据
+ * 3. 计算波形点
+ * 4. 绘制上下对称的声纹
+ */
+ const draw = () => {
+ // 清空画布
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ if (!frequencies || !isActive) {
+ historyRef.current = [];
+ return;
+ }
+
+ const freqArray = Array.from(frequencies);
+ updateHistory(freqArray);
+
+ // 绘制声纹
+ const points: [number, number][] = [];
+ const centerY = canvas.height / 2;
+ const width = canvas.width;
+ const sliceWidth = width / (frequencies.length - 1);
+
+ // 绘制主波形
+ ctx.beginPath();
+ ctx.moveTo(0, centerY);
+
+ /**
+ * 声纹绘制算法:
+ * 1. 使用历史数据平均值实现平滑过渡
+ * 2. 通过正弦函数添加自然波动
+ * 3. 使用贝塞尔曲线连接点,使曲线更平滑
+ * 4. 绘制对称部分形成完整声纹
+ */
+ for (let i = 0; i < frequencies.length; i++) {
+ const x = i * sliceWidth;
+ let avgFrequency = frequencies[i];
+
+ /**
+ * 波形平滑处理:
+ * 1. 收集历史数据中对应位置的频率值
+ * 2. 计算当前值与历史值的加权平均
+ * 3. 根据平均值计算实际显示高度
+ */
+ if (historyRef.current.length > 0) {
+ const historicalValues = historyRef.current.map((h) => h[i] || 0);
+ avgFrequency =
+ (avgFrequency + historicalValues.reduce((a, b) => a + b, 0)) /
+ (historyRef.current.length + 1);
+ }
+
+ /**
+ * 波形变换:
+ * 1. 归一化频率值到0-1范围
+ * 2. 添加时间相关的正弦变换
+ * 3. 使用贝塞尔曲线平滑连接点
+ */
+ const normalized = avgFrequency / 255.0;
+ const height = normalized * (canvas.height / 2);
+ const y = centerY + height * Math.sin(i * 0.2 + Date.now() * 0.002);
+
+ points.push([x, y]);
+
+ if (i === 0) {
+ ctx.moveTo(x, y);
+ } else {
+ // 使用贝塞尔曲线使波形更平滑
+ const prevPoint = points[i - 1];
+ const midX = (prevPoint[0] + x) / 2;
+ ctx.quadraticCurveTo(
+ prevPoint[0],
+ prevPoint[1],
+ midX,
+ (prevPoint[1] + y) / 2,
+ );
+ }
+ }
+
+ // 绘制对称的下半部分
+ for (let i = points.length - 1; i >= 0; i--) {
+ const [x, y] = points[i];
+ const symmetricY = centerY - (y - centerY);
+ if (i === points.length - 1) {
+ ctx.lineTo(x, symmetricY);
+ } else {
+ const nextPoint = points[i + 1];
+ const midX = (nextPoint[0] + x) / 2;
+ ctx.quadraticCurveTo(
+ nextPoint[0],
+ centerY - (nextPoint[1] - centerY),
+ midX,
+ centerY - ((nextPoint[1] + y) / 2 - centerY),
+ );
+ }
+ }
+
+ ctx.closePath();
+
+ /**
+ * 渐变效果:
+ * 从左到右应用三色渐变,带透明度
+ * 使用蓝色系配色提升视觉效果
+ */
+ const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
+ gradient.addColorStop(0, "rgba(100, 180, 255, 0.95)");
+ gradient.addColorStop(0.5, "rgba(140, 200, 255, 0.9)");
+ gradient.addColorStop(1, "rgba(180, 220, 255, 0.95)");
+
+ ctx.fillStyle = gradient;
+ ctx.fill();
+
+ animationFrameRef.current = requestAnimationFrame(draw);
+ };
+
+ // 启动动画循环
+ draw();
+
+ // 清理函数:在组件卸载时取消动画
+ return () => {
+ if (animationFrameRef.current) {
+ cancelAnimationFrame(animationFrameRef.current);
+ }
+ };
+ }, [frequencies, isActive, updateHistory]);
+
+ return (
+
+
+
+ );
+}
diff --git a/app/config/server.ts b/app/config/server.ts
index 676b0174f24..14175eadc8c 100644
--- a/app/config/server.ts
+++ b/app/config/server.ts
@@ -1,5 +1,6 @@
import md5 from "spark-md5";
import { DEFAULT_MODELS, DEFAULT_GA_ID } from "../constant";
+import { isGPT4Model } from "../utils/model";
declare global {
namespace NodeJS {
@@ -22,6 +23,7 @@ declare global {
DISABLE_FAST_LINK?: string; // disallow parse settings from url or not
CUSTOM_MODELS?: string; // to control custom models
DEFAULT_MODEL?: string; // to control default model in every new chat window
+ VISION_MODELS?: string; // to control vision models
// stability only
STABILITY_URL?: string;
@@ -71,8 +73,29 @@ declare global {
IFLYTEK_API_KEY?: string;
IFLYTEK_API_SECRET?: string;
+ DEEPSEEK_URL?: string;
+ DEEPSEEK_API_KEY?: string;
+
+ // xai only
+ XAI_URL?: string;
+ XAI_API_KEY?: string;
+
+ // chatglm only
+ CHATGLM_URL?: string;
+ CHATGLM_API_KEY?: string;
+
+ // siliconflow only
+ SILICONFLOW_URL?: string;
+ SILICONFLOW_API_KEY?: string;
+
+ // 302.AI only
+ AI302_URL?: string;
+ AI302_API_KEY?: string;
+
// custom template for preprocessing user input
DEFAULT_INPUT_TEMPLATE?: string;
+
+ ENABLE_MCP?: string; // enable mcp functionality
}
}
}
@@ -116,22 +139,16 @@ export const getServerSideConfig = () => {
const disableGPT4 = !!process.env.DISABLE_GPT4;
let customModels = process.env.CUSTOM_MODELS ?? "";
let defaultModel = process.env.DEFAULT_MODEL ?? "";
+ let visionModels = process.env.VISION_MODELS ?? "";
if (disableGPT4) {
if (customModels) customModels += ",";
- customModels += DEFAULT_MODELS.filter(
- (m) =>
- (m.name.startsWith("gpt-4") || m.name.startsWith("chatgpt-4o")) &&
- !m.name.startsWith("gpt-4o-mini"),
- )
+ customModels += DEFAULT_MODELS.filter((m) => isGPT4Model(m.name))
.map((m) => "-" + m.name)
.join(",");
- if (
- (defaultModel.startsWith("gpt-4") ||
- defaultModel.startsWith("chatgpt-4o")) &&
- !defaultModel.startsWith("gpt-4o-mini")
- )
+ if (defaultModel && isGPT4Model(defaultModel)) {
defaultModel = "";
+ }
}
const isStability = !!process.env.STABILITY_API_KEY;
@@ -146,6 +163,11 @@ export const getServerSideConfig = () => {
const isAlibaba = !!process.env.ALIBABA_API_KEY;
const isMoonshot = !!process.env.MOONSHOT_API_KEY;
const isIflytek = !!process.env.IFLYTEK_API_KEY;
+ const isDeepSeek = !!process.env.DEEPSEEK_API_KEY;
+ const isXAI = !!process.env.XAI_API_KEY;
+ const isChatGLM = !!process.env.CHATGLM_API_KEY;
+ const isSiliconFlow = !!process.env.SILICONFLOW_API_KEY;
+ const isAI302 = !!process.env.AI302_API_KEY;
// const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? "";
// const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim());
// const randomIndex = Math.floor(Math.random() * apiKeys.length);
@@ -154,8 +176,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 {
@@ -208,11 +230,31 @@ export const getServerSideConfig = () => {
iflytekApiKey: process.env.IFLYTEK_API_KEY,
iflytekApiSecret: process.env.IFLYTEK_API_SECRET,
+ isDeepSeek,
+ deepseekUrl: process.env.DEEPSEEK_URL,
+ deepseekApiKey: getApiKey(process.env.DEEPSEEK_API_KEY),
+
+ isXAI,
+ xaiUrl: process.env.XAI_URL,
+ xaiApiKey: getApiKey(process.env.XAI_API_KEY),
+
+ isChatGLM,
+ chatglmUrl: process.env.CHATGLM_URL,
+ chatglmApiKey: getApiKey(process.env.CHATGLM_API_KEY),
+
cloudflareAccountId: process.env.CLOUDFLARE_ACCOUNT_ID,
cloudflareKVNamespaceId: process.env.CLOUDFLARE_KV_NAMESPACE_ID,
cloudflareKVApiKey: getApiKey(process.env.CLOUDFLARE_KV_API_KEY),
cloudflareKVTTL: process.env.CLOUDFLARE_KV_TTL,
+ isSiliconFlow,
+ siliconFlowUrl: process.env.SILICONFLOW_URL,
+ siliconFlowApiKey: getApiKey(process.env.SILICONFLOW_API_KEY),
+
+ isAI302,
+ ai302Url: process.env.AI302_URL,
+ ai302ApiKey: getApiKey(process.env.AI302_API_KEY),
+
gtmId: process.env.GTM_ID,
gaId: process.env.GA_ID || DEFAULT_GA_ID,
@@ -229,6 +271,8 @@ export const getServerSideConfig = () => {
disableFastLink: !!process.env.DISABLE_FAST_LINK,
customModels,
defaultModel,
- allowedWebDevEndpoints,
+ visionModels,
+ allowedWebDavEndpoints,
+ enableMcp: process.env.ENABLE_MCP === "true",
};
};
diff --git a/app/constant.ts b/app/constant.ts
index 3d33a047e90..5f36ba25c66 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}`;
@@ -13,7 +11,6 @@ export const RUNTIME_CONFIG_DOM = "danger-runtime-config";
export const STABILITY_BASE_URL = "https://api.stability.ai";
-export const DEFAULT_API_HOST = "https://api.nextchat.dev";
export const OPENAI_BASE_URL = "https://api.openai.com";
export const ANTHROPIC_BASE_URL = "https://api.anthropic.com";
@@ -28,9 +25,19 @@ export const ALIBABA_BASE_URL = "https://dashscope.aliyuncs.com/api/";
export const TENCENT_BASE_URL = "https://hunyuan.tencentcloudapi.com";
-export const MOONSHOT_BASE_URL = "https://api.moonshot.cn";
+export const MOONSHOT_BASE_URL = "https://api.moonshot.ai";
export const IFLYTEK_BASE_URL = "https://spark-api-open.xf-yun.com";
+export const DEEPSEEK_BASE_URL = "https://api.deepseek.com";
+
+export const XAI_BASE_URL = "https://api.x.ai";
+
+export const CHATGLM_BASE_URL = "https://open.bigmodel.cn";
+
+export const SILICONFLOW_BASE_URL = "https://api.siliconflow.cn";
+
+export const AI302_BASE_URL = "https://api.302.ai";
+
export const CACHE_URL_PREFIX = "/api/cache";
export const UPLOAD_URL = `${CACHE_URL_PREFIX}/upload`;
@@ -46,6 +53,7 @@ export enum Path {
SdNew = "/sd-new",
Artifacts = "/artifacts",
SearchChat = "/search-chat",
+ McpMarket = "/mcp-market",
}
export enum ApiPath {
@@ -62,6 +70,11 @@ export enum ApiPath {
Iflytek = "/api/iflytek",
Stability = "/api/stability",
Artifacts = "/api/artifacts",
+ XAI = "/api/xai",
+ ChatGLM = "/api/chatglm",
+ DeepSeek = "/api/deepseek",
+ SiliconFlow = "/api/siliconflow",
+ "302.AI" = "/api/302ai",
}
export enum SlotID {
@@ -84,6 +97,7 @@ export enum StoreKey {
Update = "chat-update",
Sync = "sync",
SdList = "sd-list",
+ Mcp = "mcp-store",
}
export const DEFAULT_SIDEBAR_WIDTH = 300;
@@ -99,6 +113,7 @@ export const UNFINISHED_INPUT = (id: string) => "unfinished-input-" + id;
export const STORAGE_KEY = "chatgpt-next-web";
export const REQUEST_TIMEOUT_MS = 60000;
+export const REQUEST_TIMEOUT_MS_FOR_THINKING = REQUEST_TIMEOUT_MS * 5;
export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown";
@@ -114,6 +129,11 @@ export enum ServiceProvider {
Moonshot = "Moonshot",
Stability = "Stability",
Iflytek = "Iflytek",
+ XAI = "XAI",
+ ChatGLM = "ChatGLM",
+ DeepSeek = "DeepSeek",
+ SiliconFlow = "SiliconFlow",
+ "302.AI" = "302.AI",
}
// Google API safety settings, see https://ai.google.dev/gemini-api/docs/safety-settings
@@ -136,6 +156,11 @@ export enum ModelProvider {
Hunyuan = "Hunyuan",
Moonshot = "Moonshot",
Iflytek = "Iflytek",
+ XAI = "XAI",
+ ChatGLM = "ChatGLM",
+ DeepSeek = "DeepSeek",
+ SiliconFlow = "SiliconFlow",
+ "302.AI" = "302.AI",
}
export const Stability = {
@@ -152,6 +177,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",
@@ -200,7 +226,12 @@ export const ByteDance = {
export const Alibaba = {
ExampleEndpoint: ALIBABA_BASE_URL,
- ChatPath: "v1/services/aigc/text-generation/generation",
+ ChatPath: (modelName: string) => {
+ if (modelName.includes("vl") || modelName.includes("omni")) {
+ return "v1/services/aigc/multimodal-generation/generation";
+ }
+ return `v1/services/aigc/text-generation/generation`;
+ },
};
export const Tencent = {
@@ -217,6 +248,36 @@ export const Iflytek = {
ChatPath: "v1/chat/completions",
};
+export const DeepSeek = {
+ ExampleEndpoint: DEEPSEEK_BASE_URL,
+ ChatPath: "chat/completions",
+};
+
+export const XAI = {
+ ExampleEndpoint: XAI_BASE_URL,
+ ChatPath: "v1/chat/completions",
+};
+
+export const ChatGLM = {
+ ExampleEndpoint: CHATGLM_BASE_URL,
+ ChatPath: "api/paas/v4/chat/completions",
+ ImagePath: "api/paas/v4/images/generations",
+ VideoPath: "api/paas/v4/videos/generations",
+};
+
+export const SiliconFlow = {
+ ExampleEndpoint: SILICONFLOW_BASE_URL,
+ ChatPath: "v1/chat/completions",
+ ListModelPath: "v1/models?&sub_type=chat",
+};
+
+export const AI302 = {
+ ExampleEndpoint: AI302_BASE_URL,
+ ChatPath: "v1/chat/completions",
+ EmbeddingsPath: "jina/v1/embeddings",
+ ListModelPath: "v1/models?llm=1",
+};
+
export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang
// export const DEFAULT_SYSTEM_TEMPLATE = `
// You are ChatGPT, a large language model trained by {{ServiceProvider}}.
@@ -235,30 +296,211 @@ Latex inline: \\(x^2\\)
Latex block: $$e=mc^2$$
`;
+export const MCP_TOOLS_TEMPLATE = `
+[clientId]
+{{ clientId }}
+[tools]
+{{ tools }}
+`;
+
+export const MCP_SYSTEM_TEMPLATE = `
+You are an AI assistant with access to system tools. Your role is to help users by combining natural language understanding with tool operations when needed.
+
+1. AVAILABLE TOOLS:
+{{ MCP_TOOLS }}
+
+2. WHEN TO USE TOOLS:
+ - ALWAYS USE TOOLS when they can help answer user questions
+ - DO NOT just describe what you could do - TAKE ACTION immediately
+ - If you're not sure whether to use a tool, USE IT
+ - Common triggers for tool use:
+ * Questions about files or directories
+ * Requests to check, list, or manipulate system resources
+ * Any query that can be answered with available tools
+
+3. HOW TO USE TOOLS:
+ A. Tool Call Format:
+ - Use markdown code blocks with format: \`\`\`json:mcp:{clientId}\`\`\`
+ - Always include:
+ * method: "tools/call"(Only this method is supported)
+ * params:
+ - name: must match an available primitive name
+ - arguments: required parameters for the primitive
+
+ B. Response Format:
+ - Tool responses will come as user messages
+ - Format: \`\`\`json:mcp-response:{clientId}\`\`\`
+ - Wait for response before making another tool call
+
+ C. Important Rules:
+ - Only use tools/call method
+ - Only ONE tool call per message
+ - ALWAYS TAKE ACTION instead of just describing what you could do
+ - Include the correct clientId in code block language tag
+ - Verify arguments match the primitive's requirements
+
+4. INTERACTION FLOW:
+ A. When user makes a request:
+ - IMMEDIATELY use appropriate tool if available
+ - DO NOT ask if user wants you to use the tool
+ - DO NOT just describe what you could do
+ B. After receiving tool response:
+ - Explain results clearly
+ - Take next appropriate action if needed
+ C. If tools fail:
+ - Explain the error
+ - Try alternative approach immediately
+
+5. EXAMPLE INTERACTION:
+
+ good example:
+
+ \`\`\`json:mcp:filesystem
+ {
+ "method": "tools/call",
+ "params": {
+ "name": "list_allowed_directories",
+ "arguments": {}
+ }
+ }
+ \`\`\`"
+
+
+ \`\`\`json:mcp-response:filesystem
+ {
+ "method": "tools/call",
+ "params": {
+ "name": "write_file",
+ "arguments": {
+ "path": "/Users/river/dev/nextchat/test/joke.txt",
+ "content": "为什么数学书总是感到忧伤?因为它有太多的问题。"
+ }
+ }
+ }
+\`\`\`
+
+ follwing is the wrong! mcp json example:
+
+ \`\`\`json:mcp:filesystem
+ {
+ "method": "write_file",
+ "params": {
+ "path": "NextChat_Information.txt",
+ "content": "1"
+ }
+ }
+ \`\`\`
+
+ This is wrong because the method is not tools/call.
+
+ \`\`\`{
+ "method": "search_repositories",
+ "params": {
+ "query": "2oeee"
+ }
+}
+ \`\`\`
+
+ This is wrong because the method is not tools/call.!!!!!!!!!!!
+
+ the right format is:
+ \`\`\`json:mcp:filesystem
+ {
+ "method": "tools/call",
+ "params": {
+ "name": "search_repositories",
+ "arguments": {
+ "query": "2oeee"
+ }
+ }
+ }
+ \`\`\`
+
+ please follow the format strictly ONLY use tools/call method!!!!!!!!!!!
+
+`;
+
export const SUMMARIZE_MODEL = "gpt-4o-mini";
export const GEMINI_SUMMARIZE_MODEL = "gemini-pro";
+export const DEEPSEEK_SUMMARIZE_MODEL = "deepseek-chat";
export const KnowledgeCutOffDate: Record = {
default: "2021-09",
"gpt-4-turbo": "2023-12",
"gpt-4-turbo-2024-04-09": "2023-12",
"gpt-4-turbo-preview": "2023-12",
+ "gpt-4.1": "2024-06",
+ "gpt-4.1-2025-04-14": "2024-06",
+ "gpt-4.1-mini": "2024-06",
+ "gpt-4.1-mini-2025-04-14": "2024-06",
+ "gpt-4.1-nano": "2024-06",
+ "gpt-4.1-nano-2025-04-14": "2024-06",
+ "gpt-4.5-preview": "2023-10",
+ "gpt-4.5-preview-2025-02-27": "2023-10",
"gpt-4o": "2023-10",
"gpt-4o-2024-05-13": "2023-10",
"gpt-4o-2024-08-06": "2023-10",
+ "gpt-4o-2024-11-20": "2023-10",
"chatgpt-4o-latest": "2023-10",
"gpt-4o-mini": "2023-10",
"gpt-4o-mini-2024-07-18": "2023-10",
"gpt-4-vision-preview": "2023-04",
+ "o1-mini-2024-09-12": "2023-10",
"o1-mini": "2023-10",
+ "o1-preview-2024-09-12": "2023-10",
"o1-preview": "2023-10",
+ "o1-2024-12-17": "2023-10",
+ o1: "2023-10",
+ "o3-mini-2025-01-31": "2023-10",
+ "o3-mini": "2023-10",
// After improvements,
// it's now easier to add "KnowledgeCutOffDate" instead of stupid hardcoding it, as was done previously.
"gemini-pro": "2023-12",
"gemini-pro-vision": "2023-12",
+ "deepseek-chat": "2024-07",
+ "deepseek-coder": "2024-07",
};
+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",
+];
+
+export const VISION_MODEL_REGEXES = [
+ /vision/,
+ /gpt-4o/,
+ /gpt-4\.1/,
+ /claude.*[34]/,
+ /gemini-1\.5/,
+ /gemini-exp/,
+ /gemini-2\.[05]/,
+ /learnlm/,
+ /qwen-vl/,
+ /qwen2-vl/,
+ /gpt-4-turbo(?!.*preview)/,
+ /^dall-e-3$/,
+ /glm-4v/,
+ /vl/i,
+ /o3/,
+ /o4-mini/,
+ /grok-4/i,
+ /gpt-5/
+];
+
+export const EXCLUDE_VISION_MODEL_REGEXES = [/claude-3-5-haiku-20241022/];
+
const openaiModels = [
+ // As of July 2024, gpt-4o-mini should be used in place of gpt-3.5-turbo,
+ // as it is cheaper, more capable, multimodal, and just as fast. gpt-3.5-turbo is still available for use in the API.
"gpt-3.5-turbo",
"gpt-3.5-turbo-1106",
"gpt-3.5-turbo-0125",
@@ -268,9 +510,23 @@ const openaiModels = [
"gpt-4-32k-0613",
"gpt-4-turbo",
"gpt-4-turbo-preview",
+ "gpt-4.1",
+ "gpt-4.1-2025-04-14",
+ "gpt-4.1-mini",
+ "gpt-4.1-mini-2025-04-14",
+ "gpt-4.1-nano",
+ "gpt-4.1-nano-2025-04-14",
+ "gpt-4.5-preview",
+ "gpt-4.5-preview-2025-02-27",
+ "gpt-5-chat",
+ "gpt-5-mini",
+ "gpt-5-nano",
+ "gpt-5",
+ "gpt-5-chat-2025-01-01-preview",
"gpt-4o",
"gpt-4o-2024-05-13",
"gpt-4o-2024-08-06",
+ "gpt-4o-2024-11-20",
"chatgpt-4o-latest",
"gpt-4o-mini",
"gpt-4o-mini-2024-07-18",
@@ -279,14 +535,33 @@ const openaiModels = [
"gpt-4-1106-preview",
"dall-e-3",
"o1-mini",
- "o1-preview"
+ "o1-preview",
+ "o3-mini",
+ "o3",
+ "o4-mini",
];
const googleModels = [
- "gemini-1.0-pro",
"gemini-1.5-pro-latest",
+ "gemini-1.5-pro",
+ "gemini-1.5-pro-002",
"gemini-1.5-flash-latest",
- "gemini-pro-vision",
+ "gemini-1.5-flash-8b-latest",
+ "gemini-1.5-flash",
+ "gemini-1.5-flash-8b",
+ "gemini-1.5-flash-002",
+ "learnlm-1.5-pro-experimental",
+ "gemini-exp-1206",
+ "gemini-2.0-flash",
+ "gemini-2.0-flash-exp",
+ "gemini-2.0-flash-lite-preview-02-05",
+ "gemini-2.0-flash-thinking-exp",
+ "gemini-2.0-flash-thinking-exp-1219",
+ "gemini-2.0-flash-thinking-exp-01-21",
+ "gemini-2.0-pro-exp",
+ "gemini-2.0-pro-exp-02-05",
+ "gemini-2.5-pro-preview-06-05",
+ "gemini-2.5-pro"
];
const anthropicModels = [
@@ -295,8 +570,17 @@ const anthropicModels = [
"claude-2.1",
"claude-3-sonnet-20240229",
"claude-3-opus-20240229",
+ "claude-3-opus-latest",
"claude-3-haiku-20240307",
+ "claude-3-5-haiku-20241022",
+ "claude-3-5-haiku-latest",
"claude-3-5-sonnet-20240620",
+ "claude-3-5-sonnet-20241022",
+ "claude-3-5-sonnet-latest",
+ "claude-3-7-sonnet-20250219",
+ "claude-3-7-sonnet-latest",
+ "claude-sonnet-4-20250514",
+ "claude-opus-4-20250514",
];
const baiduModels = [
@@ -330,6 +614,9 @@ const alibabaModes = [
"qwen-max-0403",
"qwen-max-0107",
"qwen-max-longcontext",
+ "qwen-omni-turbo",
+ "qwen-vl-plus",
+ "qwen-vl-max",
];
const tencentModels = [
@@ -342,7 +629,18 @@ const tencentModels = [
"hunyuan-vision",
];
-const moonshotModes = ["moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k"];
+const moonshotModels = [
+ "moonshot-v1-auto",
+ "moonshot-v1-8k",
+ "moonshot-v1-32k",
+ "moonshot-v1-128k",
+ "moonshot-v1-8k-vision-preview",
+ "moonshot-v1-32k-vision-preview",
+ "moonshot-v1-128k-vision-preview",
+ "kimi-thinking-preview",
+ "kimi-k2-0711-preview",
+ "kimi-latest",
+];
const iflytekModels = [
"general",
@@ -352,6 +650,93 @@ const iflytekModels = [
"4.0Ultra",
];
+const deepseekModels = ["deepseek-chat", "deepseek-coder", "deepseek-reasoner"];
+
+const xAIModes = [
+ "grok-beta",
+ "grok-2",
+ "grok-2-1212",
+ "grok-2-latest",
+ "grok-vision-beta",
+ "grok-2-vision-1212",
+ "grok-2-vision",
+ "grok-2-vision-latest",
+ "grok-3-mini-fast-beta",
+ "grok-3-mini-fast",
+ "grok-3-mini-fast-latest",
+ "grok-3-mini-beta",
+ "grok-3-mini",
+ "grok-3-mini-latest",
+ "grok-3-fast-beta",
+ "grok-3-fast",
+ "grok-3-fast-latest",
+ "grok-3-beta",
+ "grok-3",
+ "grok-3-latest",
+];
+
+const chatglmModels = [
+ "glm-4-plus",
+ "glm-4-0520",
+ "glm-4",
+ "glm-4-air",
+ "glm-4-airx",
+ "glm-4-long",
+ "glm-4-flashx",
+ "glm-4-flash",
+ "glm-4v-plus",
+ "glm-4v",
+ "glm-4v-flash", // free
+ "cogview-3-plus",
+ "cogview-3",
+ "cogview-3-flash", // free
+ // 目前无法适配轮询任务
+ // "cogvideox",
+ // "cogvideox-flash", // free
+];
+
+const siliconflowModels = [
+ "Qwen/Qwen2.5-7B-Instruct",
+ "Qwen/Qwen2.5-72B-Instruct",
+ "deepseek-ai/DeepSeek-R1",
+ "deepseek-ai/DeepSeek-R1-Distill-Llama-70B",
+ "deepseek-ai/DeepSeek-R1-Distill-Llama-8B",
+ "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B",
+ "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B",
+ "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
+ "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B",
+ "deepseek-ai/DeepSeek-V3",
+ "meta-llama/Llama-3.3-70B-Instruct",
+ "THUDM/glm-4-9b-chat",
+ "Pro/deepseek-ai/DeepSeek-R1",
+ "Pro/deepseek-ai/DeepSeek-V3",
+];
+
+const ai302Models = [
+ "deepseek-chat",
+ "gpt-4o",
+ "chatgpt-4o-latest",
+ "llama3.3-70b",
+ "deepseek-reasoner",
+ "gemini-2.0-flash",
+ "claude-3-7-sonnet-20250219",
+ "claude-3-7-sonnet-latest",
+ "grok-3-beta",
+ "grok-3-mini-beta",
+ "gpt-4.1",
+ "gpt-4.1-mini",
+ "o3",
+ "o4-mini",
+ "qwen3-235b-a22b",
+ "qwen3-32b",
+ "gemini-2.5-pro-preview-05-06",
+ "llama-4-maverick",
+ "gemini-2.5-flash",
+ "claude-sonnet-4-20250514",
+ "claude-opus-4-20250514",
+ "gemini-2.5-pro",
+];
+
let seq = 1000; // 内置的模型序号生成器从1000开始
export const DEFAULT_MODELS = [
...openaiModels.map((name) => ({
@@ -442,7 +827,7 @@ export const DEFAULT_MODELS = [
sorted: 8,
},
})),
- ...moonshotModes.map((name) => ({
+ ...moonshotModels.map((name) => ({
name,
available: true,
sorted: seq++,
@@ -464,6 +849,61 @@ export const DEFAULT_MODELS = [
sorted: 10,
},
})),
+ ...xAIModes.map((name) => ({
+ name,
+ available: true,
+ sorted: seq++,
+ provider: {
+ id: "xai",
+ providerName: "XAI",
+ providerType: "xai",
+ sorted: 11,
+ },
+ })),
+ ...chatglmModels.map((name) => ({
+ name,
+ available: true,
+ sorted: seq++,
+ provider: {
+ id: "chatglm",
+ providerName: "ChatGLM",
+ providerType: "chatglm",
+ sorted: 12,
+ },
+ })),
+ ...deepseekModels.map((name) => ({
+ name,
+ available: true,
+ sorted: seq++,
+ provider: {
+ id: "deepseek",
+ providerName: "DeepSeek",
+ providerType: "deepseek",
+ sorted: 13,
+ },
+ })),
+ ...siliconflowModels.map((name) => ({
+ name,
+ available: true,
+ sorted: seq++,
+ provider: {
+ id: "siliconflow",
+ providerName: "SiliconFlow",
+ providerType: "siliconflow",
+ sorted: 14,
+ },
+ })),
+ ...ai302Models.map((name) => ({
+ name,
+ available: true,
+ sorted: seq++,
+ provider: {
+ id: "ai302",
+ providerName: "302.AI",
+ providerType: "ai302",
+ sorted: 15,
+ },
+ })),
] as const;
export const CHAT_PAGE_SIZE = 15;
@@ -483,8 +923,6 @@ export const internalAllowedWebDavEndpoints = [
];
export const DEFAULT_GA_ID = "G-89WN60ZK2E";
-export const PLUGINS = [
- { name: "Plugins", path: Path.Plugins },
- { name: "Stable Diffusion", path: Path.Sd },
- { name: "Search Chat", path: Path.SearchChat },
-];
+
+export const SAAS_CHAT_URL = "https://nextchat.club";
+export const SAAS_CHAT_UTM_URL = "https://nextchat.club?utm=github";
diff --git a/app/global.d.ts b/app/global.d.ts
index 8ee636bcd3c..897871fec37 100644
--- a/app/global.d.ts
+++ b/app/global.d.ts
@@ -26,6 +26,13 @@ declare interface Window {
isPermissionGranted(): Promise;
sendNotification(options: string | Options): void;
};
+ updater: {
+ checkUpdate(): Promise;
+ installUpdate(): Promise;
+ onUpdaterEvent(
+ handler: (status: UpdateStatusResult) => void,
+ ): Promise;
+ };
http: {
fetch(
url: string,
diff --git a/app/icons/arrow.svg b/app/icons/arrow.svg
new file mode 100644
index 00000000000..ddd69e61472
--- /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 00000000000..446d532aaca
--- /dev/null
+++ b/app/icons/fire.svg
@@ -0,0 +1 @@
+
diff --git a/app/icons/headphone.svg b/app/icons/headphone.svg
new file mode 100644
index 00000000000..287e3add858
--- /dev/null
+++ b/app/icons/headphone.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/chatglm.svg b/app/icons/llm-icons/chatglm.svg
new file mode 100644
index 00000000000..642750f3ece
--- /dev/null
+++ b/app/icons/llm-icons/chatglm.svg
@@ -0,0 +1,14 @@
+
+ ChatGLM
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/claude.svg b/app/icons/llm-icons/claude.svg
new file mode 100644
index 00000000000..ca8e447bb28
--- /dev/null
+++ b/app/icons/llm-icons/claude.svg
@@ -0,0 +1,8 @@
+
+ Claude
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/deepseek.svg b/app/icons/llm-icons/deepseek.svg
new file mode 100644
index 00000000000..30440e3166c
--- /dev/null
+++ b/app/icons/llm-icons/deepseek.svg
@@ -0,0 +1,8 @@
+
+ DeepSeek
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/default.svg b/app/icons/llm-icons/default.svg
new file mode 100644
index 00000000000..2ebff6b3f71
--- /dev/null
+++ b/app/icons/llm-icons/default.svg
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/doubao.svg b/app/icons/llm-icons/doubao.svg
new file mode 100644
index 00000000000..79b1b822ad6
--- /dev/null
+++ b/app/icons/llm-icons/doubao.svg
@@ -0,0 +1,14 @@
+
+ Doubao
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/gemini.svg b/app/icons/llm-icons/gemini.svg
new file mode 100644
index 00000000000..5876691353e
--- /dev/null
+++ b/app/icons/llm-icons/gemini.svg
@@ -0,0 +1,15 @@
+
+ Gemini
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/gemma.svg b/app/icons/llm-icons/gemma.svg
new file mode 100644
index 00000000000..daf1a035c9e
--- /dev/null
+++ b/app/icons/llm-icons/gemma.svg
@@ -0,0 +1,15 @@
+
+ Gemma
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/grok.svg b/app/icons/llm-icons/grok.svg
new file mode 100644
index 00000000000..8125cd610a7
--- /dev/null
+++ b/app/icons/llm-icons/grok.svg
@@ -0,0 +1,8 @@
+
+ Grok
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/hunyuan.svg b/app/icons/llm-icons/hunyuan.svg
new file mode 100644
index 00000000000..f67930c9831
--- /dev/null
+++ b/app/icons/llm-icons/hunyuan.svg
@@ -0,0 +1,17 @@
+
+ Hunyuan
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/meta.svg b/app/icons/llm-icons/meta.svg
new file mode 100644
index 00000000000..75dc40df79d
--- /dev/null
+++ b/app/icons/llm-icons/meta.svg
@@ -0,0 +1,93 @@
+
+ Meta
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/mistral.svg b/app/icons/llm-icons/mistral.svg
new file mode 100644
index 00000000000..e577faca501
--- /dev/null
+++ b/app/icons/llm-icons/mistral.svg
@@ -0,0 +1,15 @@
+
+ Mistral
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/moonshot.svg b/app/icons/llm-icons/moonshot.svg
new file mode 100644
index 00000000000..5206e0f12a5
--- /dev/null
+++ b/app/icons/llm-icons/moonshot.svg
@@ -0,0 +1,8 @@
+
+ MoonshotAI
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/openai.svg b/app/icons/llm-icons/openai.svg
new file mode 100644
index 00000000000..564cd5e87b7
--- /dev/null
+++ b/app/icons/llm-icons/openai.svg
@@ -0,0 +1,8 @@
+
+ OpenAI
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/qwen.svg b/app/icons/llm-icons/qwen.svg
new file mode 100644
index 00000000000..857ce218639
--- /dev/null
+++ b/app/icons/llm-icons/qwen.svg
@@ -0,0 +1,14 @@
+
+ Qwen
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/llm-icons/wenxin.svg b/app/icons/llm-icons/wenxin.svg
new file mode 100644
index 00000000000..0030b0e01e4
--- /dev/null
+++ b/app/icons/llm-icons/wenxin.svg
@@ -0,0 +1,18 @@
+
+ Wenxin
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/logo.svg b/app/icons/logo.svg
new file mode 100644
index 00000000000..b80263b8638
--- /dev/null
+++ b/app/icons/logo.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/icons/mcp.svg b/app/icons/mcp.svg
new file mode 100644
index 00000000000..aaf0bbc7431
--- /dev/null
+++ b/app/icons/mcp.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/pause.svg b/app/icons/pause.svg
index 4e81ef06732..08a6572d6cf 100644
--- a/app/icons/pause.svg
+++ b/app/icons/pause.svg
@@ -1 +1,3 @@
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/app/icons/play.svg b/app/icons/play.svg
new file mode 100644
index 00000000000..4a2515c6f1e
--- /dev/null
+++ b/app/icons/play.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/icons/power.svg b/app/icons/power.svg
new file mode 100644
index 00000000000..f60fc426678
--- /dev/null
+++ b/app/icons/power.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/icons/speak-stop.svg b/app/icons/speak-stop.svg
new file mode 100644
index 00000000000..926ae7bb3d6
--- /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 00000000000..e02212c9a42
--- /dev/null
+++ b/app/icons/speak.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/icons/tool.svg b/app/icons/tool.svg
new file mode 100644
index 00000000000..add538457ce
--- /dev/null
+++ b/app/icons/tool.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/icons/voice-off.svg b/app/icons/voice-off.svg
new file mode 100644
index 00000000000..d4aae988a82
--- /dev/null
+++ b/app/icons/voice-off.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
\ 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 00000000000..e7d5cbcc86f
--- /dev/null
+++ b/app/icons/voice-white.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/icons/voice.svg b/app/icons/voice.svg
new file mode 100644
index 00000000000..2d85360427f
--- /dev/null
+++ b/app/icons/voice.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/layout.tsx b/app/layout.tsx
index fa087636a44..47c058fb300 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -5,9 +5,8 @@ import "./styles/highlight.scss";
import { getClientConfig } from "./config/client";
import type { Metadata, Viewport } from "next";
import { SpeedInsights } from "@vercel/speed-insights/next";
-import { getServerSideConfig } from "./config/server";
import { GoogleTagManager, GoogleAnalytics } from "@next/third-parties/google";
-const serverConfig = getServerSideConfig();
+import { getServerSideConfig } from "./config/server";
export const metadata: Metadata = {
title: "NextChat",
@@ -33,6 +32,8 @@ export default function RootLayout({
}: {
children: React.ReactNode;
}) {
+ const serverConfig = getServerSideConfig();
+
return (
@@ -41,7 +42,11 @@ export default function RootLayout({
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
-
+
diff --git a/app/lib/audio.ts b/app/lib/audio.ts
new file mode 100644
index 00000000000..a4937d773ee
--- /dev/null
+++ b/app/lib/audio.ts
@@ -0,0 +1,200 @@
+export class AudioHandler {
+ private context: AudioContext;
+ private mergeNode: ChannelMergerNode;
+ private analyserData: Uint8Array;
+ public analyser: AnalyserNode;
+ private workletNode: AudioWorkletNode | null = null;
+ private stream: MediaStream | null = null;
+ private source: MediaStreamAudioSourceNode | null = null;
+ private recordBuffer: Int16Array[] = [];
+ private readonly sampleRate = 24000;
+
+ private nextPlayTime: number = 0;
+ private isPlaying: boolean = false;
+ private playbackQueue: AudioBufferSourceNode[] = [];
+ private playBuffer: Int16Array[] = [];
+
+ constructor() {
+ this.context = new AudioContext({ sampleRate: this.sampleRate });
+ // using ChannelMergerNode to get merged audio data, and then get analyser data.
+ this.mergeNode = new ChannelMergerNode(this.context, { numberOfInputs: 2 });
+ this.analyser = new AnalyserNode(this.context, { fftSize: 256 });
+ this.analyserData = new Uint8Array(this.analyser.frequencyBinCount);
+ this.mergeNode.connect(this.analyser);
+ }
+
+ getByteFrequencyData() {
+ this.analyser.getByteFrequencyData(this.analyserData);
+ return this.analyserData;
+ }
+
+ async initialize() {
+ await this.context.audioWorklet.addModule("/audio-processor.js");
+ }
+
+ async startRecording(onChunk: (chunk: Uint8Array) => void) {
+ try {
+ if (!this.workletNode) {
+ await this.initialize();
+ }
+
+ this.stream = await navigator.mediaDevices.getUserMedia({
+ audio: {
+ channelCount: 1,
+ sampleRate: this.sampleRate,
+ echoCancellation: true,
+ noiseSuppression: true,
+ },
+ });
+
+ await this.context.resume();
+ this.source = this.context.createMediaStreamSource(this.stream);
+ this.workletNode = new AudioWorkletNode(
+ this.context,
+ "audio-recorder-processor",
+ );
+
+ this.workletNode.port.onmessage = (event) => {
+ if (event.data.eventType === "audio") {
+ const float32Data = event.data.audioData;
+ const int16Data = new Int16Array(float32Data.length);
+
+ for (let i = 0; i < float32Data.length; i++) {
+ const s = Math.max(-1, Math.min(1, float32Data[i]));
+ int16Data[i] = s < 0 ? s * 0x8000 : s * 0x7fff;
+ }
+
+ const uint8Data = new Uint8Array(int16Data.buffer);
+ onChunk(uint8Data);
+ // save recordBuffer
+ // @ts-ignore
+ this.recordBuffer.push.apply(this.recordBuffer, int16Data);
+ }
+ };
+
+ this.source.connect(this.workletNode);
+ this.source.connect(this.mergeNode, 0, 0);
+ this.workletNode.connect(this.context.destination);
+
+ this.workletNode.port.postMessage({ command: "START_RECORDING" });
+ } catch (error) {
+ console.error("Error starting recording:", error);
+ throw error;
+ }
+ }
+
+ stopRecording() {
+ if (!this.workletNode || !this.source || !this.stream) {
+ throw new Error("Recording not started");
+ }
+
+ this.workletNode.port.postMessage({ command: "STOP_RECORDING" });
+
+ this.workletNode.disconnect();
+ this.source.disconnect();
+ this.stream.getTracks().forEach((track) => track.stop());
+ }
+ startStreamingPlayback() {
+ this.isPlaying = true;
+ this.nextPlayTime = this.context.currentTime;
+ }
+
+ stopStreamingPlayback() {
+ this.isPlaying = false;
+ this.playbackQueue.forEach((source) => source.stop());
+ this.playbackQueue = [];
+ this.playBuffer = [];
+ }
+
+ playChunk(chunk: Uint8Array) {
+ if (!this.isPlaying) return;
+
+ const int16Data = new Int16Array(chunk.buffer);
+ // @ts-ignore
+ this.playBuffer.push.apply(this.playBuffer, int16Data); // save playBuffer
+
+ const float32Data = new Float32Array(int16Data.length);
+ for (let i = 0; i < int16Data.length; i++) {
+ float32Data[i] = int16Data[i] / (int16Data[i] < 0 ? 0x8000 : 0x7fff);
+ }
+
+ const audioBuffer = this.context.createBuffer(
+ 1,
+ float32Data.length,
+ this.sampleRate,
+ );
+ audioBuffer.getChannelData(0).set(float32Data);
+
+ const source = this.context.createBufferSource();
+ source.buffer = audioBuffer;
+ source.connect(this.context.destination);
+ source.connect(this.mergeNode, 0, 1);
+
+ const chunkDuration = audioBuffer.length / this.sampleRate;
+
+ source.start(this.nextPlayTime);
+
+ this.playbackQueue.push(source);
+ source.onended = () => {
+ const index = this.playbackQueue.indexOf(source);
+ if (index > -1) {
+ this.playbackQueue.splice(index, 1);
+ }
+ };
+
+ this.nextPlayTime += chunkDuration;
+
+ if (this.nextPlayTime < this.context.currentTime) {
+ this.nextPlayTime = this.context.currentTime;
+ }
+ }
+ _saveData(data: Int16Array, bytesPerSample = 16): Blob {
+ const headerLength = 44;
+ const numberOfChannels = 1;
+ const byteLength = data.buffer.byteLength;
+ const header = new Uint8Array(headerLength);
+ const view = new DataView(header.buffer);
+ view.setUint32(0, 1380533830, false); // RIFF identifier 'RIFF'
+ view.setUint32(4, 36 + byteLength, true); // file length minus RIFF identifier length and file description length
+ view.setUint32(8, 1463899717, false); // RIFF type 'WAVE'
+ view.setUint32(12, 1718449184, false); // format chunk identifier 'fmt '
+ view.setUint32(16, 16, true); // format chunk length
+ view.setUint16(20, 1, true); // sample format (raw)
+ view.setUint16(22, numberOfChannels, true); // channel count
+ view.setUint32(24, this.sampleRate, true); // sample rate
+ view.setUint32(28, this.sampleRate * 4, true); // byte rate (sample rate * block align)
+ view.setUint16(32, numberOfChannels * 2, true); // block align (channel count * bytes per sample)
+ view.setUint16(34, bytesPerSample, true); // bits per sample
+ view.setUint32(36, 1684108385, false); // data chunk identifier 'data'
+ view.setUint32(40, byteLength, true); // data chunk length
+
+ // using data.buffer, so no need to setUint16 to view.
+ return new Blob([view, data.buffer], { type: "audio/mpeg" });
+ }
+ savePlayFile() {
+ // @ts-ignore
+ return this._saveData(new Int16Array(this.playBuffer));
+ }
+ saveRecordFile(
+ audioStartMillis: number | undefined,
+ audioEndMillis: number | undefined,
+ ) {
+ const startIndex = audioStartMillis
+ ? Math.floor((audioStartMillis * this.sampleRate) / 1000)
+ : 0;
+ const endIndex = audioEndMillis
+ ? Math.floor((audioEndMillis * this.sampleRate) / 1000)
+ : this.recordBuffer.length;
+ return this._saveData(
+ // @ts-ignore
+ new Int16Array(this.recordBuffer.slice(startIndex, endIndex)),
+ );
+ }
+ async close() {
+ this.recordBuffer = [];
+ this.workletNode?.disconnect();
+ this.source?.disconnect();
+ this.stream?.getTracks().forEach((track) => track.stop());
+ await this.context.close();
+ }
+}
diff --git a/app/locales/ar.ts b/app/locales/ar.ts
index 464519e2055..6237e11b323 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} محادثة`,
@@ -281,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: "قام المشرف بتمكين الوصول المشفر",
@@ -399,6 +416,17 @@ const ar: PartialLocaleType = {
SubTitle: "مثال:",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "مفتاح 302.AI API",
+ SubTitle: "استخدم مفتاح 302.AI API مخصص",
+ Placeholder: "مفتاح 302.AI API",
+ },
+ Endpoint: {
+ Title: "عنوان الواجهة",
+ SubTitle: "مثال:",
+ },
+ },
CustomModel: {
Title: "اسم النموذج المخصص",
SubTitle: "أضف خيارات نموذج مخصص، مفصولة بفواصل إنجليزية",
diff --git a/app/locales/bn.ts b/app/locales/bn.ts
index 945c442df57..6ec3606f346 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} টি চ্যাট`,
@@ -284,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: "অ্যাডমিন এনক্রিপ্টেড অ্যাক্সেস সক্রিয় করেছেন",
@@ -405,6 +423,17 @@ const bn: PartialLocaleType = {
SubTitle: "উদাহরণ:",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "ইন্টারফেস কী",
+ SubTitle: "স্বনির্ধারিত 302.AI API কী ব্যবহার করুন",
+ Placeholder: "302.AI API কী",
+ },
+ Endpoint: {
+ Title: "ইন্টারফেস ঠিকানা",
+ SubTitle: "উদাহরণ:",
+ },
+ },
CustomModel: {
Title: "স্বনির্ধারিত মডেল নাম",
SubTitle:
diff --git a/app/locales/cn.ts b/app/locales/cn.ts
index fcbdb6f627c..2cb7dd1e535 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} 条对话`,
@@ -46,6 +56,8 @@ const cn = {
FullScreen: "全屏",
RefreshTitle: "刷新标题",
RefreshToast: "已发送刷新标题请求",
+ Speech: "朗读",
+ StopSpeech: "停止",
},
Commands: {
new: "新建聊天",
@@ -53,6 +65,7 @@ const cn = {
next: "下一个聊天",
prev: "上一个聊天",
clear: "清除上下文",
+ fork: "复制聊天",
del: "删除聊天",
},
InputActions: {
@@ -79,6 +92,8 @@ const cn = {
return inputHints + ",/ 触发补全,: 触发命令";
},
Send: "发送",
+ StartSpeak: "说话",
+ StopSpeak: "停止",
Config: {
Reset: "清除记忆",
SaveAs: "存为面具",
@@ -91,6 +106,7 @@ const cn = {
copyLastMessage: "复制最后一个回复",
copyLastCode: "复制最后一个代码块",
showShortcutKey: "显示快捷方式",
+ clearContext: "清除上下文",
},
},
Export: {
@@ -161,7 +177,7 @@ const cn = {
},
},
Lang: {
- Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
+ Name: "Language", // 注意:如果要添加新的翻译,请不要翻译此值,将它保留为 `Language`
All: "所有语言",
},
Avatar: "头像",
@@ -190,6 +206,8 @@ const cn = {
IsChecking: "正在检查更新...",
FoundUpdate: (x: string) => `发现新版本:${x}`,
GoToUpdate: "前往更新",
+ Success: "更新成功!",
+ Failed: "更新失败",
},
SendKey: "发送键",
Theme: "主题",
@@ -293,6 +311,13 @@ const cn = {
},
Access: {
+ SaasStart: {
+ Title: "使用 NextChat AI",
+ Label: "(性价比最高的方案)",
+ SubTitle:
+ "由 NextChat 官方维护, 零配置开箱即用,支持 OpenAI o1, GPT-4o, Claude-3.5 等最新大模型",
+ ChatNow: "立刻对话",
+ },
AccessCode: {
Title: "访问密码",
SubTitle: "管理员已开启加密访问",
@@ -356,7 +381,7 @@ const cn = {
ApiKey: {
Title: "API 密钥",
SubTitle: "从 Google AI 获取您的 API 密钥",
- Placeholder: "输入您的 Google AI Studio API 密钥",
+ Placeholder: "Google AI API KEY",
},
Endpoint: {
@@ -438,6 +463,50 @@ const cn = {
SubTitle: "样例:",
},
},
+ DeepSeek: {
+ ApiKey: {
+ Title: "接口密钥",
+ SubTitle: "使用自定义DeepSeek API Key",
+ Placeholder: "DeepSeek API Key",
+ },
+ Endpoint: {
+ Title: "接口地址",
+ SubTitle: "样例:",
+ },
+ },
+ XAI: {
+ ApiKey: {
+ Title: "接口密钥",
+ SubTitle: "使用自定义XAI API Key",
+ Placeholder: "XAI API Key",
+ },
+ Endpoint: {
+ Title: "接口地址",
+ SubTitle: "样例:",
+ },
+ },
+ ChatGLM: {
+ ApiKey: {
+ Title: "接口密钥",
+ SubTitle: "使用自定义 ChatGLM API Key",
+ Placeholder: "ChatGLM API Key",
+ },
+ Endpoint: {
+ Title: "接口地址",
+ SubTitle: "样例:",
+ },
+ },
+ SiliconFlow: {
+ ApiKey: {
+ Title: "接口密钥",
+ SubTitle: "使用自定义硅基流动 API Key",
+ Placeholder: "硅基流动 API Key",
+ },
+ Endpoint: {
+ Title: "接口地址",
+ SubTitle: "样例:",
+ },
+ },
Stability: {
ApiKey: {
Title: "接口密钥",
@@ -469,12 +538,23 @@ const cn = {
Title: "自定义模型名",
SubTitle: "增加自定义模型可选项,使用英文逗号隔开",
},
+ AI302: {
+ ApiKey: {
+ Title: "接口密钥",
+ SubTitle: "使用自定义302.AI API Key",
+ Placeholder: "302.AI API Key",
+ },
+ Endpoint: {
+ Title: "接口地址",
+ SubTitle: "样例:",
+ },
+ },
},
Model: "模型 (model)",
CompressModel: {
- Title: "压缩模型",
- SubTitle: "用于压缩历史记录的模型",
+ Title: "对话摘要模型",
+ SubTitle: "用于压缩历史记录、生成对话标题的模型",
},
Temperature: {
Title: "随机性 (temperature)",
@@ -496,6 +576,59 @@ const cn = {
Title: "频率惩罚度 (frequency_penalty)",
SubTitle: "值越大,越有可能降低重复字词",
},
+ TTS: {
+ Enable: {
+ Title: "启用文本转语音",
+ SubTitle: "启用文本生成语音服务",
+ },
+ Autoplay: {
+ Title: "启用自动朗读",
+ SubTitle: "自动生成语音并播放,需先开启文本转语音开关",
+ },
+ Model: "模型",
+ Engine: "转换引擎",
+ Voice: {
+ Title: "声音",
+ SubTitle: "生成语音时使用的声音",
+ },
+ Speed: {
+ Title: "速度",
+ SubTitle: "生成语音的速度",
+ },
+ },
+ Realtime: {
+ Enable: {
+ Title: "实时聊天",
+ SubTitle: "开启实时聊天功能",
+ },
+ Provider: {
+ Title: "模型服务商",
+ SubTitle: "切换不同的服务商",
+ },
+ Model: {
+ Title: "模型",
+ SubTitle: "选择一个模型",
+ },
+ ApiKey: {
+ Title: "API Key",
+ SubTitle: "API Key",
+ Placeholder: "API Key",
+ },
+ Azure: {
+ Endpoint: {
+ Title: "接口地址",
+ SubTitle: "接口地址",
+ },
+ Deployment: {
+ Title: "部署名称",
+ SubTitle: "部署名称",
+ },
+ },
+ Temperature: {
+ Title: "随机性 (temperature)",
+ SubTitle: "值越大,回复越随机",
+ },
+ },
},
Store: {
DefaultTopic: "新的聊天",
@@ -527,11 +660,14 @@ const cn = {
Discovery: {
Name: "发现",
},
+ Mcp: {
+ Name: "MCP",
+ },
FineTuned: {
Sysmessage: "你是一个助手",
},
SearchChat: {
- Name: "搜索",
+ Name: "搜索聊天记录",
Page: {
Title: "搜索聊天记录",
Search: "输入搜索关键词",
@@ -623,6 +759,10 @@ const cn = {
Title: "启用Artifacts",
SubTitle: "启用之后可以直接渲染HTML页面",
},
+ CodeFold: {
+ Title: "启用代码折叠",
+ SubTitle: "启用之后可以自动折叠/展开过长的代码块",
+ },
Share: {
Title: "分享此面具",
SubTitle: "生成此面具的直达链接",
diff --git a/app/locales/cs.ts b/app/locales/cs.ts
index 5a132b3ce6d..c4ce2653ac3 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í`,
@@ -284,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",
@@ -405,6 +423,17 @@ const cs: PartialLocaleType = {
SubTitle: "Příklad:",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "Rozhraní klíč",
+ SubTitle: "Použijte vlastní 302.AI API Key",
+ Placeholder: "302.AI API Key",
+ },
+ Endpoint: {
+ Title: "Adresa rozhraní",
+ SubTitle: "Příklad:",
+ },
+ },
CustomModel: {
Title: "Vlastní názvy modelů",
SubTitle: "Přidejte možnosti vlastních modelů, oddělené čárkami",
diff --git a/app/locales/da.ts b/app/locales/da.ts
new file mode 100644
index 00000000000..7c976188a6b
--- /dev/null
+++ b/app/locales/da.ts
@@ -0,0 +1,843 @@
+import { getClientConfig } from "../config/client";
+import { SubmitKey } from "../store/config";
+import { SAAS_CHAT_UTM_URL } from "@/app/constant";
+import { PartialLocaleType } from "./index";
+
+const isApp = !!getClientConfig()?.isApp;
+const da: PartialLocaleType = {
+ WIP: "Der kommer snart mere...",
+ Error: {
+ Unauthorized: isApp
+ ? `Hov, der skete en fejl. Sådan kan du komme videre:
+ \\ 1️⃣ Er du ny her? [Tryk for at starte nu 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Vil du bruge dine egne OpenAI-nøgler? [Tryk her](/#/settings) for at ændre indstillinger ⚙️`
+ : `Hov, der skete en fejl. Lad os løse det:
+ \\ 1️⃣ Er du ny her? [Tryk for at starte nu 🚀](${SAAS_CHAT_UTM_URL})
+ \\ 2️⃣ Bruger du en privat opsætning? [Tryk her](/#/auth) for at taste din nøgle 🔑
+ \\ 3️⃣ Vil du bruge dine egne OpenAI-nøgler? [Tryk her](/#/settings) for at ændre indstillinger ⚙️
+ `,
+ },
+ Auth: {
+ Return: "Tilbage",
+ Title: "Adgangskode",
+ Tips: "Skriv venligst koden herunder",
+ SubTips: "Eller brug din egen OpenAI- eller Google-nøgle",
+ Input: "Adgangskode",
+ Confirm: "OK",
+ Later: "Senere",
+ SaasTips: "Hvis det er for svært, kan du starte nu",
+ },
+ ChatItem: {
+ ChatItemCount: (count: number) => `${count} beskeder`,
+ },
+ Chat: {
+ SubTitle: (count: number) => `${count} beskeder`,
+ EditMessage: {
+ Title: "Rediger beskeder",
+ Topic: {
+ Title: "Emne",
+ SubTitle: "Skift emne for denne chat",
+ },
+ },
+ Actions: {
+ ChatList: "Gå til chatliste",
+ CompressedHistory: "Komprimeret historie",
+ Export: "Eksporter alle beskeder som Markdown",
+ Copy: "Kopiér",
+ Stop: "Stop",
+ Retry: "Prøv igen",
+ Pin: "Fastgør",
+ PinToastContent: "1 besked er nu fastgjort",
+ PinToastAction: "Se",
+ Delete: "Slet",
+ Edit: "Rediger",
+ FullScreen: "Fuld skærm",
+ RefreshTitle: "Opdatér titel",
+ RefreshToast: "Anmodning om ny titel sendt",
+ Speech: "Afspil",
+ StopSpeech: "Stop",
+ },
+ Commands: {
+ new: "Ny chat",
+ newm: "Ny chat med persona",
+ next: "Næste chat",
+ prev: "Forrige chat",
+ clear: "Ryd alt før",
+ fork: "Kopiér chat",
+ del: "Slet chat",
+ },
+ InputActions: {
+ Stop: "Stop",
+ ToBottom: "Ned til nyeste",
+ Theme: {
+ auto: "Automatisk",
+ light: "Lyst tema",
+ dark: "Mørkt tema",
+ },
+ Prompt: "Prompts",
+ Masks: "Personaer",
+ Clear: "Ryd kontekst",
+ Settings: "Indstillinger",
+ UploadImage: "Upload billeder",
+ },
+ Rename: "Omdøb chat",
+ Typing: "Skriver…",
+ Input: (submitKey: string) => {
+ let inputHints = `${submitKey} for at sende`;
+ if (submitKey === String(SubmitKey.Enter)) {
+ inputHints += ", Shift + Enter for ny linje";
+ }
+ return (
+ inputHints + ", / for at søge i prompts, : for at bruge kommandoer"
+ );
+ },
+ Send: "Send",
+ StartSpeak: "Start oplæsning",
+ StopSpeak: "Stop oplæsning",
+ Config: {
+ Reset: "Nulstil til standard",
+ SaveAs: "Gem som persona",
+ },
+ IsContext: "Ekstra prompt til baggrund",
+ ShortcutKey: {
+ Title: "Hurtigtaster",
+ newChat: "Åbn ny chat",
+ focusInput: "Fokus på tekstfeltet",
+ copyLastMessage: "Kopiér sidste svar",
+ copyLastCode: "Kopiér sidste kodeblok",
+ showShortcutKey: "Vis hurtigtaster",
+ clearContext: "Ryd kontekst",
+ },
+ },
+ Export: {
+ Title: "Eksportér beskeder",
+ Copy: "Kopiér alt",
+ Download: "Download",
+ MessageFromYou: "Fra dig",
+ MessageFromChatGPT: "Fra ChatGPT",
+ Share: "Del til ShareGPT",
+ Format: {
+ Title: "Filformat",
+ SubTitle: "Vælg enten Markdown eller PNG-billede",
+ },
+ IncludeContext: {
+ Title: "Tag baggrund med",
+ SubTitle: "Skal ekstra baggrund (persona) med i eksporten?",
+ },
+ Steps: {
+ Select: "Vælg",
+ Preview: "Forhåndsvis",
+ },
+ Image: {
+ Toast: "Laver billede...",
+ Modal: "Tryk længe eller højreklik for at gemme",
+ },
+ Artifacts: {
+ Title: "Del side",
+ Error: "Fejl ved deling",
+ },
+ },
+ Select: {
+ Search: "Søg",
+ All: "Vælg alle",
+ Latest: "Vælg nyeste",
+ Clear: "Ryd alt",
+ },
+ Memory: {
+ Title: "Huskesætning",
+ EmptyContent: "Ingenting lige nu.",
+ Send: "Send huskesætning",
+ Copy: "Kopiér huskesætning",
+ Reset: "Nulstil chat",
+ ResetConfirm:
+ "Dette sletter nuværende samtale og hukommelse. Er du sikker?",
+ },
+ Home: {
+ NewChat: "Ny Chat",
+ DeleteChat: "Vil du slette den valgte chat?",
+ DeleteToast: "Chat slettet",
+ Revert: "Fortryd",
+ },
+ Settings: {
+ Title: "Indstillinger",
+ SubTitle: "Alle indstillinger",
+ ShowPassword: "Vis kodeord",
+ Danger: {
+ Reset: {
+ Title: "Nulstil alle indstillinger",
+ SubTitle: "Gendan alt til standard",
+ Action: "Nulstil",
+ Confirm: "Vil du virkelig nulstille alt?",
+ },
+ Clear: {
+ Title: "Slet alle data",
+ SubTitle: "Sletter alt om beskeder og indstillinger",
+ Action: "Slet",
+ Confirm: "Er du sikker på, at du vil slette alt?",
+ },
+ },
+ Lang: {
+ Name: "Language",
+ All: "Alle sprog",
+ },
+ Avatar: "Avatar",
+ FontSize: {
+ Title: "Skriftstørrelse",
+ SubTitle: "Vælg, hvor stor teksten skal være",
+ },
+ FontFamily: {
+ Title: "Skrifttype",
+ SubTitle: "Hvis tom, bruger den standard skrifttype",
+ Placeholder: "Skrifttype-navn",
+ },
+ InjectSystemPrompts: {
+ Title: "Tilføj system-prompt",
+ SubTitle: "Læg altid en ekstra prompt først i anmodninger",
+ },
+ InputTemplate: {
+ Title: "Tekstskabelon",
+ SubTitle: "Den seneste besked placeres i denne skabelon",
+ },
+ Update: {
+ Version: (x: string) => `Version: ${x}`,
+ IsLatest: "Du har nyeste version",
+ CheckUpdate: "Tjek efter opdatering",
+ IsChecking: "Tjekker...",
+ FoundUpdate: (x: string) => `Ny version fundet: ${x}`,
+ GoToUpdate: "Opdatér",
+ Success: "Opdatering lykkedes.",
+ Failed: "Opdatering mislykkedes.",
+ },
+ SendKey: "Tast for send",
+ Theme: "Tema",
+ TightBorder: "Stram kant",
+ SendPreviewBubble: {
+ Title: "Forhåndsvisnings-boble",
+ SubTitle: "Vis tekst, før den sendes",
+ },
+ AutoGenerateTitle: {
+ Title: "Lav titel automatisk",
+ SubTitle: "Foreslå en titel ud fra chatten",
+ },
+ Sync: {
+ CloudState: "Seneste opdatering",
+ NotSyncYet: "Endnu ikke synkroniseret",
+ Success: "Synkronisering lykkedes",
+ Fail: "Synkronisering mislykkedes",
+ Config: {
+ Modal: {
+ Title: "Indstil synk",
+ Check: "Tjek forbindelse",
+ },
+ SyncType: {
+ Title: "Synk-type",
+ SubTitle: "Vælg en synk-tjeneste",
+ },
+ Proxy: {
+ Title: "Aktivér proxy",
+ SubTitle: "Brug proxy for at undgå netværksproblemer",
+ },
+ ProxyUrl: {
+ Title: "Proxy-adresse",
+ SubTitle: "Bruges kun til projektets egen proxy",
+ },
+ WebDav: {
+ Endpoint: "WebDAV-adresse",
+ UserName: "Brugernavn",
+ Password: "Kodeord",
+ },
+ UpStash: {
+ Endpoint: "UpStash Redis REST URL",
+ UserName: "Backup-navn",
+ Password: "UpStash Redis REST Token",
+ },
+ },
+ LocalState: "Lokale data",
+ Overview: (overview: any) =>
+ `${overview.chat} chats, ${overview.message} beskeder, ${overview.prompt} prompts, ${overview.mask} personaer`,
+ ImportFailed: "Import mislykkedes",
+ },
+ Mask: {
+ Splash: {
+ Title: "Persona-forside",
+ SubTitle: "Vis denne side, når du opretter ny chat",
+ },
+ Builtin: {
+ Title: "Skjul indbyggede personaer",
+ SubTitle: "Vis ikke de indbyggede personaer i listen",
+ },
+ },
+ Prompt: {
+ Disable: {
+ Title: "Slå auto-forslag fra",
+ SubTitle: "Tast / for at få forslag",
+ },
+ List: "Prompt-liste",
+ ListCount: (builtin: number, custom: number) =>
+ `${builtin} indbygget, ${custom} brugerdefineret`,
+ Edit: "Rediger",
+ Modal: {
+ Title: "Prompt-liste",
+ Add: "Tilføj",
+ Search: "Søg prompts",
+ },
+ EditModal: {
+ Title: "Rediger prompt",
+ },
+ },
+ HistoryCount: {
+ Title: "Antal beskeder, der følger med",
+ SubTitle: "Hvor mange af de tidligere beskeder, der sendes hver gang",
+ },
+ CompressThreshold: {
+ Title: "Komprimeringsgrænse",
+ SubTitle:
+ "Hvis chatten bliver for lang, vil den komprimeres efter dette antal tegn",
+ },
+ Usage: {
+ Title: "Brug og saldo",
+ SubTitle(used: any, total: any) {
+ return `Du har brugt $${used} i denne måned, og din grænse er $${total}.`;
+ },
+ IsChecking: "Tjekker...",
+ Check: "Tjek igen",
+ NoAccess: "Indtast API-nøgle for at se forbrug",
+ },
+ Access: {
+ AccessCode: {
+ Title: "Adgangskode",
+ SubTitle: "Adgangskontrol er slået til",
+ Placeholder: "Skriv kode her",
+ },
+ CustomEndpoint: {
+ Title: "Brugerdefineret adresse",
+ SubTitle: "Brug Azure eller OpenAI fra egen server",
+ },
+ Provider: {
+ Title: "Model-udbyder",
+ SubTitle: "Vælg Azure eller OpenAI",
+ },
+ OpenAI: {
+ ApiKey: {
+ Title: "OpenAI API-nøgle",
+ SubTitle: "Brug din egen nøgle",
+ Placeholder: "sk-xxx",
+ },
+ Endpoint: {
+ Title: "OpenAI Endpoint",
+ SubTitle: "Skal starte med http(s):// eller /api/openai som standard",
+ },
+ },
+ Azure: {
+ ApiKey: {
+ Title: "Azure Api Key",
+ SubTitle: "Hent din nøgle fra Azure-portalen",
+ Placeholder: "Azure Api Key",
+ },
+ Endpoint: {
+ Title: "Azure Endpoint",
+ SubTitle: "F.eks.: ",
+ },
+ ApiVerion: {
+ Title: "Azure Api Version",
+ SubTitle: "Hentet fra Azure-portalen",
+ },
+ },
+ Anthropic: {
+ ApiKey: {
+ Title: "Anthropic API-nøgle",
+ SubTitle: "Brug din egen Anthropic-nøgle",
+ Placeholder: "Anthropic API Key",
+ },
+ Endpoint: {
+ Title: "Endpoint-adresse",
+ SubTitle: "F.eks.: ",
+ },
+ ApiVerion: {
+ Title: "API-version (Claude)",
+ SubTitle: "Vælg den ønskede version",
+ },
+ },
+ Baidu: {
+ ApiKey: {
+ Title: "Baidu-nøgle",
+ SubTitle: "Din egen Baidu-nøgle",
+ Placeholder: "Baidu API Key",
+ },
+ SecretKey: {
+ Title: "Baidu hemmelig nøgle",
+ SubTitle: "Din egen hemmelige nøgle fra Baidu",
+ Placeholder: "Baidu Secret Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "Kan ikke ændres, se .env",
+ },
+ },
+ Tencent: {
+ ApiKey: {
+ Title: "Tencent-nøgle",
+ SubTitle: "Din egen nøgle fra Tencent",
+ Placeholder: "Tencent API Key",
+ },
+ SecretKey: {
+ Title: "Tencent hemmelig nøgle",
+ SubTitle: "Din egen hemmelige nøgle fra Tencent",
+ Placeholder: "Tencent Secret Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "Kan ikke ændres, se .env",
+ },
+ },
+ ByteDance: {
+ ApiKey: {
+ Title: "ByteDance-nøgle",
+ SubTitle: "Din egen nøgle til ByteDance",
+ Placeholder: "ByteDance API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ Alibaba: {
+ ApiKey: {
+ Title: "Alibaba-nøgle",
+ SubTitle: "Din egen Alibaba Cloud-nøgle",
+ Placeholder: "Alibaba Cloud API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ Moonshot: {
+ ApiKey: {
+ Title: "Moonshot-nøgle",
+ SubTitle: "Din egen Moonshot-nøgle",
+ Placeholder: "Moonshot API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ DeepSeek: {
+ ApiKey: {
+ Title: "DeepSeek-nøgle",
+ SubTitle: "Din egen DeepSeek-nøgle",
+ Placeholder: "DeepSeek API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ XAI: {
+ ApiKey: {
+ Title: "XAI-nøgle",
+ SubTitle: "Din egen XAI-nøgle",
+ Placeholder: "XAI API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ ChatGLM: {
+ ApiKey: {
+ Title: "ChatGLM-nøgle",
+ SubTitle: "Din egen ChatGLM-nøgle",
+ Placeholder: "ChatGLM API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ SiliconFlow: {
+ ApiKey: {
+ Title: "SiliconFlow-nøgle",
+ SubTitle: "Din egen SiliconFlow-nøgle",
+ Placeholder: "SiliconFlow API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ Stability: {
+ ApiKey: {
+ Title: "Stability-nøgle",
+ SubTitle: "Din egen Stability-nøgle",
+ Placeholder: "Stability API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ Iflytek: {
+ ApiKey: {
+ Title: "Iflytek API Key",
+ SubTitle: "Nøgle fra Iflytek",
+ Placeholder: "Iflytek API Key",
+ },
+ ApiSecret: {
+ Title: "Iflytek hemmelig nøgle",
+ SubTitle: "Hentet fra Iflytek",
+ Placeholder: "Iflytek API Secret",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ },
+ CustomModel: {
+ Title: "Egne modelnavne",
+ SubTitle: "Skriv komma-adskilte navne",
+ },
+ Google: {
+ ApiKey: {
+ Title: "Google-nøgle",
+ SubTitle: "Få din nøgle hos Google AI",
+ Placeholder: "Google AI API Key",
+ },
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "F.eks.: ",
+ },
+ ApiVersion: {
+ Title: "API-version (til gemini-pro)",
+ SubTitle: "Vælg en bestemt version",
+ },
+ GoogleSafetySettings: {
+ Title: "Google sikkerhedsindstillinger",
+ SubTitle: "Vælg et niveau for indholdskontrol",
+ },
+ },
+ AI302: {
+ ApiKey: {
+ Title: "302.AI API Key",
+ SubTitle: "Brug en custom 302.AI API Key",
+ Placeholder: "302.AI API Key",
+ },
+ Endpoint: {
+ Title: "Endpoint-adresse",
+ SubTitle: "Eksempel: ",
+ },
+ },
+ },
+ Model: "Model",
+ CompressModel: {
+ Title: "Opsummeringsmodel",
+ SubTitle: "Bruges til at korte historik ned og lave titel",
+ },
+ Temperature: {
+ Title: "Temperatur",
+ SubTitle: "Jo højere tal, jo mere kreativt svar",
+ },
+ TopP: {
+ Title: "Top P",
+ SubTitle: "Skal ikke ændres sammen med temperatur",
+ },
+ MaxTokens: {
+ Title: "Maks. længde",
+ SubTitle: "Hvor mange tokens (ord/stykker tekst) der kan bruges",
+ },
+ PresencePenalty: {
+ Title: "Nye emner",
+ SubTitle: "Jo højere tal, jo mere nyt indhold",
+ },
+ FrequencyPenalty: {
+ Title: "Gentagelsesstraf",
+ SubTitle: "Jo højere tal, jo mindre gentagelse",
+ },
+ TTS: {
+ Enable: {
+ Title: "Tænd for oplæsning (TTS)",
+ SubTitle: "Slå tekst-til-tale til",
+ },
+ Autoplay: {
+ Title: "Automatisk oplæsning",
+ SubTitle: "Laver lyd automatisk, hvis TTS er slået til",
+ },
+ Model: "Model",
+ Voice: {
+ Title: "Stemme",
+ SubTitle: "Hvilken stemme der bruges til lyd",
+ },
+ Speed: {
+ Title: "Hastighed",
+ SubTitle: "Hvor hurtigt der oplæses",
+ },
+ Engine: "TTS-motor",
+ },
+ Realtime: {
+ Enable: {
+ Title: "Live-chat",
+ SubTitle: "Slå live-svar til",
+ },
+ Provider: {
+ Title: "Modeludbyder",
+ SubTitle: "Vælg forskellig udbyder",
+ },
+ Model: {
+ Title: "Model",
+ SubTitle: "Vælg en model",
+ },
+ ApiKey: {
+ Title: "API-nøgle",
+ SubTitle: "Din nøgle",
+ Placeholder: "API-nøgle",
+ },
+ Azure: {
+ Endpoint: {
+ Title: "Adresse",
+ SubTitle: "Endpoint til Azure",
+ },
+ Deployment: {
+ Title: "Udrulningsnavn",
+ SubTitle: "Navn for dit Azure-setup",
+ },
+ },
+ Temperature: {
+ Title: "Temperatur",
+ SubTitle: "Højere tal = mere varierede svar",
+ },
+ },
+ },
+ Store: {
+ DefaultTopic: "Ny samtale",
+ BotHello: "Hej! Hvordan kan jeg hjælpe dig i dag?",
+ Error: "Noget gik galt. Prøv igen senere.",
+ Prompt: {
+ History: (content: string) =>
+ "Her er et kort resume af, hvad vi har snakket om: " + content,
+ Topic:
+ "Find en kort overskrift med 4-5 ord om emnet. Ingen tegnsætning eller anførselstegn.",
+ Summarize:
+ "Skriv et kort resumé (under 200 ord) af vores samtale til senere brug.",
+ },
+ },
+ Copy: {
+ Success: "Kopieret",
+ Failed: "Kunne ikke kopiere. Giv adgang til udklipsholder.",
+ },
+ Download: {
+ Success: "Filen er downloadet.",
+ Failed: "Download fejlede.",
+ },
+ Context: {
+ Toast: (x: any) => `Inkluderer ${x} ekstra prompts`,
+ Edit: "Chatindstillinger",
+ Add: "Tilføj prompt",
+ Clear: "Kontekst ryddet",
+ Revert: "Fortryd",
+ },
+ Discovery: {
+ Name: "Søgning og plugins",
+ },
+ Mcp: {
+ Name: "MCP",
+ },
+ FineTuned: {
+ Sysmessage: "Du er en hjælper, der skal...",
+ },
+ SearchChat: {
+ Name: "Søg",
+ Page: {
+ Title: "Søg i tidligere chats",
+ Search: "Skriv her for at søge",
+ NoResult: "Ingen resultater",
+ NoData: "Ingen data",
+ Loading: "Henter...",
+ SubTitle: (count: number) => `Fandt ${count} resultater`,
+ },
+ Item: {
+ View: "Vis",
+ },
+ },
+ Plugin: {
+ Name: "Plugin",
+ Page: {
+ Title: "Plugins",
+ SubTitle: (count: number) => `${count} plugins`,
+ Search: "Søg plugin",
+ Create: "Opret nyt",
+ Find: "Du kan finde flere plugins på GitHub: ",
+ },
+ Item: {
+ Info: (count: number) => `${count} metode`,
+ View: "Vis",
+ Edit: "Rediger",
+ Delete: "Slet",
+ DeleteConfirm: "Vil du slette?",
+ },
+ Auth: {
+ None: "Ingen",
+ Basic: "Basic",
+ Bearer: "Bearer",
+ Custom: "Tilpasset",
+ CustomHeader: "Parameternavn",
+ Token: "Token",
+ Proxy: "Brug Proxy",
+ ProxyDescription: "Løs CORS-problemer med Proxy",
+ Location: "Sted",
+ LocationHeader: "Header",
+ LocationQuery: "Query",
+ LocationBody: "Body",
+ },
+ EditModal: {
+ Title: (readonly: boolean) =>
+ `Rediger Plugin ${readonly ? "(skrivebeskyttet)" : ""}`,
+ Download: "Download",
+ Auth: "Godkendelsestype",
+ Content: "OpenAPI Schema",
+ Load: "Hent fra URL",
+ Method: "Metode",
+ Error: "Fejl i OpenAPI Schema",
+ },
+ },
+ Mask: {
+ Name: "Persona",
+ Page: {
+ Title: "Prompts som personaer",
+ SubTitle: (count: number) => `${count} skabeloner`,
+ Search: "Søg skabeloner",
+ Create: "Opret ny",
+ },
+ Item: {
+ Info: (count: number) => `${count} prompts`,
+ Chat: "Chat",
+ View: "Vis",
+ Edit: "Rediger",
+ Delete: "Slet",
+ DeleteConfirm: "Vil du slette?",
+ },
+ EditModal: {
+ Title: (readonly: boolean) =>
+ `Rediger skabelon ${readonly ? "(skrivebeskyttet)" : ""}`,
+ Download: "Download",
+ Clone: "Klon",
+ },
+ Config: {
+ Avatar: "Chat-avatar",
+ Name: "Chat-navn",
+ Sync: {
+ Title: "Brug globale indstillinger",
+ SubTitle: "Gældende for denne chat",
+ Confirm: "Erstat nuværende indstillinger med globale?",
+ },
+ HideContext: {
+ Title: "Skjul ekstra prompts",
+ SubTitle: "Vis dem ikke på chat-skærmen",
+ },
+ Artifacts: {
+ Title: "Brug Artefakter",
+ SubTitle: "Gør det muligt at vise HTML-sider",
+ },
+ CodeFold: {
+ Title: "Fold kode sammen",
+ SubTitle: "Luk/åbn lange kodestykker automatisk",
+ },
+ Share: {
+ Title: "Del denne persona",
+ SubTitle: "Få et link til denne skabelon",
+ Action: "Kopiér link",
+ },
+ },
+ },
+ NewChat: {
+ Return: "Tilbage",
+ Skip: "Start straks",
+ Title: "Vælg en persona",
+ SubTitle: "Chat med den persona, du vælger",
+ More: "Se flere",
+ NotShow: "Vis ikke igen",
+ ConfirmNoShow:
+ "Er du sikker på, at du ikke vil se det igen? Du kan altid slå det til under indstillinger.",
+ },
+ UI: {
+ Confirm: "OK",
+ Cancel: "Fortryd",
+ Close: "Luk",
+ Create: "Opret",
+ Edit: "Rediger",
+ Export: "Eksporter",
+ Import: "Importér",
+ Sync: "Synk",
+ Config: "Konfigurer",
+ },
+ Exporter: {
+ Description: {
+ Title: "Kun beskeder efter sidste rydning vises",
+ },
+ Model: "Model",
+ Messages: "Beskeder",
+ Topic: "Emne",
+ Time: "Tid",
+ },
+ URLCommand: {
+ Code: "Så ud til, at der var en kode i linket. Vil du bruge den?",
+ Settings: "Så ud til, at der var indstillinger i linket. Vil du bruge dem?",
+ },
+ SdPanel: {
+ Prompt: "Prompt",
+ NegativePrompt: "Negativ prompt",
+ PleaseInput: (name: string) => `Indtast: ${name}`,
+ AspectRatio: "Billedformat",
+ ImageStyle: "Stil",
+ OutFormat: "Uddataformat",
+ AIModel: "AI-model",
+ ModelVersion: "Version",
+ Submit: "Send",
+ ParamIsRequired: (name: string) => `${name} er krævet`,
+ Styles: {
+ D3Model: "3d-model",
+ AnalogFilm: "analog-film",
+ Anime: "anime",
+ Cinematic: "cinematisk",
+ ComicBook: "tegneserie",
+ DigitalArt: "digital-art",
+ Enhance: "enhance",
+ FantasyArt: "fantasy-art",
+ Isometric: "isometric",
+ LineArt: "line-art",
+ LowPoly: "low-poly",
+ ModelingCompound: "modeling-compound",
+ NeonPunk: "neon-punk",
+ Origami: "origami",
+ Photographic: "fotografisk",
+ PixelArt: "pixel-art",
+ TileTexture: "tile-texture",
+ },
+ },
+ Sd: {
+ SubTitle: (count: number) => `${count} billeder`,
+ Actions: {
+ Params: "Se indstillinger",
+ Copy: "Kopiér prompt",
+ Delete: "Slet",
+ Retry: "Prøv igen",
+ ReturnHome: "Til forsiden",
+ History: "Historik",
+ },
+ EmptyRecord: "Ingen billeder endnu",
+ Status: {
+ Name: "Status",
+ Success: "Ok",
+ Error: "Fejl",
+ Wait: "Venter",
+ Running: "I gang",
+ },
+ Danger: {
+ Delete: "Vil du slette?",
+ },
+ GenerateParams: "Genereringsvalg",
+ Detail: "Detaljer",
+ },
+};
+
+export default da;
diff --git a/app/locales/de.ts b/app/locales/de.ts
index ebe7aff2d71..1e5c7598582 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`,
@@ -291,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:
@@ -415,6 +434,17 @@ const de: PartialLocaleType = {
SubTitle: "Beispiel:",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "Schnittstellenschlüssel",
+ SubTitle: "Verwenden Sie einen benutzerdefinierten 302.AI API-Schlüssel",
+ Placeholder: "302.AI API-Schlüssel",
+ },
+ Endpoint: {
+ Title: "Endpunktadresse",
+ SubTitle: "Beispiel:",
+ },
+ },
CustomModel: {
Title: "Benutzerdefinierter Modellname",
SubTitle:
diff --git a/app/locales/en.ts b/app/locales/en.ts
index 049447569f7..a6d1919045c 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`,
@@ -47,6 +57,8 @@ const en: LocaleType = {
FullScreen: "FullScreen",
RefreshTitle: "Refresh Title",
RefreshToast: "Title refresh request sent",
+ Speech: "Play",
+ StopSpeech: "Stop",
},
Commands: {
new: "Start a new chat",
@@ -54,6 +66,7 @@ const en: LocaleType = {
next: "Next Chat",
prev: "Previous Chat",
clear: "Clear Context",
+ fork: "Copy Chat",
del: "Delete Chat",
},
InputActions: {
@@ -80,6 +93,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",
@@ -92,6 +107,7 @@ const en: LocaleType = {
copyLastMessage: "Copy Last Reply",
copyLastCode: "Copy Last Code Block",
showShortcutKey: "Show Shortcuts",
+ clearContext: "Clear Context",
},
},
Export: {
@@ -192,6 +208,8 @@ const en: LocaleType = {
IsChecking: "Checking update...",
FoundUpdate: (x: string) => `Found new version: ${x}`,
GoToUpdate: "Update",
+ Success: "Update Successful.",
+ Failed: "Update Failed.",
},
SendKey: "Send Key",
Theme: "Theme",
@@ -296,6 +314,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",
@@ -421,6 +447,50 @@ const en: LocaleType = {
SubTitle: "Example: ",
},
},
+ DeepSeek: {
+ ApiKey: {
+ Title: "DeepSeek API Key",
+ SubTitle: "Use a custom DeepSeek API Key",
+ Placeholder: "DeepSeek API Key",
+ },
+ Endpoint: {
+ Title: "Endpoint Address",
+ SubTitle: "Example: ",
+ },
+ },
+ XAI: {
+ ApiKey: {
+ Title: "XAI API Key",
+ SubTitle: "Use a custom XAI API Key",
+ Placeholder: "XAI API Key",
+ },
+ Endpoint: {
+ Title: "Endpoint Address",
+ SubTitle: "Example: ",
+ },
+ },
+ ChatGLM: {
+ ApiKey: {
+ Title: "ChatGLM API Key",
+ SubTitle: "Use a custom ChatGLM API Key",
+ Placeholder: "ChatGLM API Key",
+ },
+ Endpoint: {
+ Title: "Endpoint Address",
+ SubTitle: "Example: ",
+ },
+ },
+ SiliconFlow: {
+ ApiKey: {
+ Title: "SiliconFlow API Key",
+ SubTitle: "Use a custom SiliconFlow API Key",
+ Placeholder: "SiliconFlow API Key",
+ },
+ Endpoint: {
+ Title: "Endpoint Address",
+ SubTitle: "Example: ",
+ },
+ },
Stability: {
ApiKey: {
Title: "Stability API Key",
@@ -456,7 +526,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: {
@@ -473,12 +543,23 @@ const en: LocaleType = {
SubTitle: "Select a safety filtering level",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "302.AI API Key",
+ SubTitle: "Use a custom 302.AI API Key",
+ Placeholder: "302.AI API Key",
+ },
+ Endpoint: {
+ Title: "Endpoint Address",
+ SubTitle: "Example: ",
+ },
+ },
},
Model: "Model",
CompressModel: {
- Title: "Compression Model",
- SubTitle: "Model used to compress history",
+ Title: "Summary Model",
+ SubTitle: "Model used to compress history and generate title",
},
Temperature: {
Title: "Temperature",
@@ -502,6 +583,60 @@ 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",
+ },
+ Realtime: {
+ Enable: {
+ Title: "Realtime Chat",
+ SubTitle: "Enable realtime chat feature",
+ },
+ Provider: {
+ Title: "Model Provider",
+ SubTitle: "Switch between different providers",
+ },
+ Model: {
+ Title: "Model",
+ SubTitle: "Select a model",
+ },
+ ApiKey: {
+ Title: "API Key",
+ SubTitle: "API Key",
+ Placeholder: "API Key",
+ },
+ Azure: {
+ Endpoint: {
+ Title: "Endpoint",
+ SubTitle: "Endpoint",
+ },
+ Deployment: {
+ Title: "Deployment Name",
+ SubTitle: "Deployment Name",
+ },
+ },
+ Temperature: {
+ Title: "Randomness (temperature)",
+ SubTitle: "Higher values result in more random responses",
+ },
+ },
},
Store: {
DefaultTopic: "New Conversation",
@@ -534,6 +669,9 @@ const en: LocaleType = {
Discovery: {
Name: "Discovery",
},
+ Mcp: {
+ Name: "MCP",
+ },
FineTuned: {
Sysmessage: "You are an assistant that",
},
@@ -631,6 +769,11 @@ const en: LocaleType = {
Title: "Enable Artifacts",
SubTitle: "Can render HTML page when enable artifacts.",
},
+ CodeFold: {
+ Title: "Enable CodeFold",
+ SubTitle:
+ "Automatically collapse/expand overly long code blocks when CodeFold is enabled",
+ },
Share: {
Title: "Share This Mask",
SubTitle: "Generate a link to this mask",
diff --git a/app/locales/es.ts b/app/locales/es.ts
index c1caae2d385..be229c20cc2 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`,
@@ -294,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",
@@ -417,6 +436,17 @@ const es: PartialLocaleType = {
SubTitle: "Ejemplo:",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "Clave de interfaz",
+ SubTitle: "Usa una clave API de 302.AI personalizada",
+ Placeholder: "Clave API de 302.AI",
+ },
+ Endpoint: {
+ Title: "Dirección del endpoint",
+ SubTitle: "Ejemplo:",
+ },
+ },
CustomModel: {
Title: "Nombre del modelo personalizado",
SubTitle:
diff --git a/app/locales/fr.ts b/app/locales/fr.ts
index 97fb79e3238..0675d925cc0 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`,
@@ -294,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é",
@@ -416,6 +435,17 @@ const fr: PartialLocaleType = {
SubTitle: "Exemple :",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "Clé d'interface",
+ SubTitle: "Utiliser une clé API 302.AI personnalisée",
+ Placeholder: "Clé API 302.AI",
+ },
+ Endpoint: {
+ Title: "Adresse de l'endpoint",
+ SubTitle: "Exemple :",
+ },
+ },
CustomModel: {
Title: "Nom du modèle personnalisé",
SubTitle:
diff --git a/app/locales/id.ts b/app/locales/id.ts
index 1eff667ed9e..68cf43e3023 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`,
@@ -285,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",
@@ -406,6 +424,17 @@ const id: PartialLocaleType = {
SubTitle: "Contoh:",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "Kunci Antarmuka",
+ SubTitle: "Gunakan 302.AI API Key kustom",
+ Placeholder: "302.AI API Key",
+ },
+ Endpoint: {
+ Title: "Alamat Antarmuka",
+ SubTitle: "Contoh:",
+ },
+ },
CustomModel: {
Title: "Nama Model Kustom",
SubTitle: "Tambahkan opsi model kustom, pisahkan dengan koma",
diff --git a/app/locales/index.ts b/app/locales/index.ts
index ff7e3a26257..43b17ae81fc 100644
--- a/app/locales/index.ts
+++ b/app/locales/index.ts
@@ -2,6 +2,7 @@ import cn from "./cn";
import en from "./en";
import pt from "./pt";
import tw from "./tw";
+import da from "./da";
import id from "./id";
import fr from "./fr";
import es from "./es";
@@ -30,6 +31,7 @@ const ALL_LANGS = {
en,
tw,
pt,
+ da,
jp,
ko,
id,
@@ -56,6 +58,7 @@ export const ALL_LANG_OPTIONS: Record = {
en: "English",
pt: "Português",
tw: "繁體中文",
+ da: "Dansk",
jp: "日本語",
ko: "한국어",
id: "Indonesia",
@@ -134,3 +137,35 @@ 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",
+ da: "da-DK",
+ 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 9b0d965d351..c098f63d17d 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`,
@@ -295,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",
@@ -417,6 +436,17 @@ const it: PartialLocaleType = {
SubTitle: "Esempio:",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "Chiave dell'interfaccia",
+ SubTitle: "Utilizza una chiave API 302.AI personalizzata",
+ Placeholder: "Chiave API 302.AI",
+ },
+ Endpoint: {
+ Title: "Indirizzo dell'interfaccia",
+ SubTitle: "Esempio:",
+ },
+ },
CustomModel: {
Title: "Nome del modello personalizzato",
SubTitle:
diff --git a/app/locales/jp.ts b/app/locales/jp.ts
index 1511afc2c5f..d605b578f12 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}件の会話`,
@@ -282,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: "管理者が暗号化アクセスを有効にしました",
@@ -402,6 +420,17 @@ const jp: PartialLocaleType = {
SubTitle: "例:",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "APIキー",
+ SubTitle: "カスタム302.AI APIキーを使用",
+ Placeholder: "302.AI APIキー",
+ },
+ Endpoint: {
+ Title: "エンドポイント",
+ SubTitle: "例:",
+ },
+ },
CustomModel: {
Title: "カスタムモデル名",
SubTitle: "カスタムモデルの選択肢を追加、英語のカンマで区切る",
diff --git a/app/locales/ko.ts b/app/locales/ko.ts
index 11e6aa4eb29..023edca4abc 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,8 +53,11 @@ const ko: PartialLocaleType = {
PinToastAction: "보기",
Delete: "삭제",
Edit: "편집",
+ FullScreen: "전체 화면",
RefreshTitle: "제목 새로고침",
RefreshToast: "제목 새로고침 요청이 전송되었습니다",
+ Speech: "재생",
+ StopSpeech: "정지",
},
Commands: {
new: "새 채팅",
@@ -52,6 +65,7 @@ const ko: PartialLocaleType = {
next: "다음 채팅",
prev: "이전 채팅",
clear: "컨텍스트 지우기",
+ fork: "채팅 복사",
del: "채팅 삭제",
},
InputActions: {
@@ -78,11 +92,22 @@ const ko: PartialLocaleType = {
return inputHints + ",/ 자동 완성,: 명령어 입력";
},
Send: "전송",
+ StartSpeak: "재생 시작",
+ StopSpeak: "재생 정지",
Config: {
Reset: "기억 지우기",
SaveAs: "마스크로 저장",
},
IsContext: "프롬프트 설정",
+ ShortcutKey: {
+ Title: "키보드 단축키",
+ newChat: "새 채팅 열기",
+ focusInput: "입력 필드 포커스",
+ copyLastMessage: "마지막 답변 복사",
+ copyLastCode: "마지막 코드 블록 복사",
+ showShortcutKey: "단축키 보기",
+ clearContext: "컨텍스트 지우기",
+ },
},
Export: {
Title: "채팅 기록 공유",
@@ -104,9 +129,13 @@ const ko: PartialLocaleType = {
Preview: "미리보기",
},
Image: {
- Toast: "스크린샷 생성 중",
+ Toast: "스크린샷 생성 중...",
Modal: "길게 누르거나 오른쪽 클릭하여 이미지를 저장하십시오.",
},
+ Artifacts: {
+ Title: "공유 아티팩트",
+ Error: "공유 오류",
+ },
},
Select: {
Search: "메시지 검색",
@@ -131,7 +160,7 @@ const ko: PartialLocaleType = {
Settings: {
Title: "설정",
SubTitle: "모든 설정 옵션",
-
+ ShowPassword: "비밀번호 보기",
Danger: {
Reset: {
Title: "모든 설정 초기화",
@@ -177,8 +206,10 @@ const ko: PartialLocaleType = {
IsChecking: "업데이트 확인 중...",
FoundUpdate: (x: string) => `새 버전 발견: ${x}`,
GoToUpdate: "업데이트로 이동",
+ Success: "업데이트 성공",
+ Failed: "업데이트 실패",
},
- SendKey: "전송 키",
+ SendKey: "키 전송",
Theme: "테마",
TightBorder: "테두리 없는 모드",
SendPreviewBubble: {
@@ -211,7 +242,7 @@ const ko: PartialLocaleType = {
},
ProxyUrl: {
Title: "프록시 주소",
- SubTitle: "이 프로젝트에서 제공하는 교차 출처 프록시만 해당",
+ SubTitle: "이 프로젝트에서 제공하는 CORS 프록시만 해당",
},
WebDav: {
@@ -281,6 +312,14 @@ const ko: PartialLocaleType = {
},
Access: {
+ SaasStart: {
+ Title: "NextChat AI 사용하기",
+ Label: "(가장 비용 효율적인 솔루션)",
+ SubTitle:
+ "NextChat에 의해 공식적으로 유지 관리되며, 설정 없이 즉시 사용할 수 있으며, OpenAI o1, GPT-4o, Claude-3.5와 같은 최신 대형 모델을 지원합니다",
+ ChatNow: "지금 채팅하기",
+ },
+
AccessCode: {
Title: "접근 비밀번호",
SubTitle: "관리자가 암호화된 접근을 활성화했습니다.",
@@ -377,6 +416,22 @@ const ko: PartialLocaleType = {
SubTitle: "커스터마이즈는 .env에서 설정",
},
},
+ Tencent: {
+ ApiKey: {
+ Title: "Tencent API 키",
+ SubTitle: "커스텀 Tencent API 키 사용",
+ Placeholder: "Tencent API 키",
+ },
+ SecretKey: {
+ Title: "Tencent Secret 키",
+ SubTitle: "커스텀 Tencent Secret 키 사용",
+ Placeholder: "Tencent Secret 키",
+ },
+ Endpoint: {
+ Title: "엔드포인트 주소",
+ SubTitle: "지원되지 않음, .env에서 설정",
+ },
+ },
ByteDance: {
ApiKey: {
Title: "엔드포인트 키",
@@ -399,10 +454,103 @@ const ko: PartialLocaleType = {
SubTitle: "예: ",
},
},
+ Moonshot: {
+ ApiKey: {
+ Title: "Moonshot API 키",
+ SubTitle: "커스텀 Moonshot API 키 사용",
+ Placeholder: "Moonshot API 키",
+ },
+ Endpoint: {
+ Title: "엔드포인트 주소",
+ SubTitle: "예: ",
+ },
+ },
+ DeepSeek: {
+ ApiKey: {
+ Title: "DeepSeek API 키",
+ SubTitle: "커스텀 DeepSeek API 키 사용",
+ Placeholder: "DeepSeek API 키",
+ },
+ Endpoint: {
+ Title: "엔드포인트 주소",
+ SubTitle: "예: ",
+ },
+ },
+ XAI: {
+ ApiKey: {
+ Title: "XAI API 키",
+ SubTitle: "커스텀 XAI API 키 사용",
+ Placeholder: "XAI API 키",
+ },
+ Endpoint: {
+ Title: "엔드포인트 주소",
+ SubTitle: "예: ",
+ },
+ },
+ ChatGLM: {
+ ApiKey: {
+ Title: "ChatGLM API 키",
+ SubTitle: "커스텀 ChatGLM API 키 사용",
+ Placeholder: "ChatGLM API 키",
+ },
+ Endpoint: {
+ Title: "엔드포인트 주소",
+ SubTitle: "예: ",
+ },
+ },
+ SiliconFlow: {
+ ApiKey: {
+ Title: "SiliconFlow API 키",
+ SubTitle: "커스텀 SiliconFlow API 키 사용",
+ Placeholder: "SiliconFlow API 키",
+ },
+ Endpoint: {
+ Title: "엔드포인트 주소",
+ SubTitle: "예: ",
+ },
+ },
+ Stability: {
+ ApiKey: {
+ Title: "Stability API 키",
+ SubTitle: "커스텀 Stability API 키 사용",
+ Placeholder: "Stability API 키",
+ },
+ Endpoint: {
+ Title: "엔드포인트 주소",
+ SubTitle: "예: ",
+ },
+ },
+ Iflytek: {
+ ApiKey: {
+ Title: "Iflytek API 키",
+ SubTitle: "커스텀 Iflytek API 키 사용",
+ Placeholder: "Iflytek API 키",
+ },
+ ApiSecret: {
+ Title: "Iflytek API Secret",
+ SubTitle: "커스텀 Iflytek API Secret 키 사용",
+ Placeholder: "Iflytek API Secret 키",
+ },
+ Endpoint: {
+ Title: "엔드포인트 주소",
+ SubTitle: "예: ",
+ },
+ },
CustomModel: {
Title: "커스텀 모델 이름",
SubTitle: "커스텀 모델 옵션 추가, 영어 쉼표로 구분",
},
+ AI302: {
+ ApiKey: {
+ Title: "엔드포인트 키",
+ SubTitle: "커스텀 302.AI API 키 사용",
+ Placeholder: "302.AI API 키",
+ },
+ Endpoint: {
+ Title: "엔드포인트 주소",
+ SubTitle: "예: ",
+ },
+ },
},
Model: "모델 (model)",
@@ -430,13 +578,67 @@ const ko: PartialLocaleType = {
Title: "빈도 벌점 (frequency_penalty)",
SubTitle: "값이 클수록 중복 단어 감소 가능성 높음",
},
+ TTS: {
+ Enable: {
+ Title: "TTS 활성화",
+ SubTitle: "TTS 서비스 활성화",
+ },
+ Autoplay: {
+ Title: "자동 재생 활성화",
+ SubTitle:
+ "자동으로 음성을 생성하고 재생, 먼저 TTS 스위치를 활성화해야 함",
+ },
+ Model: "모델",
+ Voice: {
+ Title: "음성",
+ SubTitle: "음성을 생성할 때 사용할 음성",
+ },
+ Speed: {
+ Title: "속도",
+ SubTitle: "생성된 음성의 속도",
+ },
+ Engine: "TTS Engine",
+ },
+ Realtime: {
+ Enable: {
+ Title: "실시간 채팅",
+ SubTitle: "실시간 채팅 기능 활성화",
+ },
+ Provider: {
+ Title: "모델 제공업체",
+ SubTitle: "다른 제공업체 간 전환",
+ },
+ Model: {
+ Title: "모델",
+ SubTitle: "모델 선택",
+ },
+ ApiKey: {
+ Title: "API 키",
+ SubTitle: "API 키",
+ Placeholder: "API 키",
+ },
+ Azure: {
+ Endpoint: {
+ Title: "엔드포인트",
+ SubTitle: "엔드포인트",
+ },
+ Deployment: {
+ Title: "배포 이름",
+ SubTitle: "배포 이름",
+ },
+ },
+ Temperature: {
+ Title: "무작위성 (temperature)",
+ SubTitle: "값이 클수록 응답이 더 무작위적",
+ },
+ },
},
Store: {
DefaultTopic: "새 채팅",
BotHello: "무엇을 도와드릴까요?",
Error: "오류가 발생했습니다. 나중에 다시 시도해 주세요.",
Prompt: {
- History: (content: string) => "이것은 이전 채팅 요약입니다: " + content,
+ History: (content: string) => "이전 채팅 요약: " + content,
Topic:
"네 글자에서 다섯 글자로 이 문장의 간략한 주제를 반환하세요. 설명이나 문장 부호, 어미, 불필요한 텍스트, 굵은 글씨는 필요 없습니다. 주제가 없다면 '잡담'이라고만 반환하세요.",
Summarize:
@@ -458,8 +660,11 @@ const ko: PartialLocaleType = {
Clear: "컨텍스트가 지워졌습니다.",
Revert: "컨텍스트 복원",
},
- Plugin: {
- Name: "플러그인",
+ Discovery: {
+ Name: "디스커버리",
+ },
+ Mcp: {
+ Name: "MCP 플러그인",
},
FineTuned: {
Sysmessage: "당신은 보조자입니다.",
@@ -471,7 +676,7 @@ const ko: PartialLocaleType = {
Search: "검색어 입력",
NoResult: "결과를 찾을 수 없습니다",
NoData: "데이터가 없습니다",
- Loading: "로딩 중",
+ Loading: "로딩 중...",
SubTitle: (count: number) => `${count}개의 결과를 찾았습니다`,
},
@@ -479,6 +684,47 @@ const ko: PartialLocaleType = {
View: "보기",
},
},
+ Plugin: {
+ Name: "플러그인",
+ Page: {
+ Title: "플러그인",
+ SubTitle: (count: number) => `${count} 개의 플러그인`,
+ Search: "플러그인 검색",
+ Create: "새로 만들기",
+ Find: "github에서 멋진 플러그인을 찾을 수 있습니다: ",
+ },
+ Item: {
+ Info: (count: number) => `${count} 개의 메서드`,
+ View: "보기",
+ Edit: "편집",
+ Delete: "삭제",
+ DeleteConfirm: "삭제하시겠습니까?",
+ },
+ Auth: {
+ None: "없음",
+ Basic: "기본",
+ Bearer: "Bearer",
+ Custom: "커스텀",
+ CustomHeader: "파라미터 이름",
+ Token: "토큰",
+ Proxy: "프록시 사용",
+ ProxyDescription: "CORS 오류 해결을 위해 프록시 사용",
+ Location: "위치",
+ LocationHeader: "헤더",
+ LocationQuery: "쿼리",
+ LocationBody: "바디",
+ },
+ EditModal: {
+ Title: (readonly: boolean) =>
+ `플러그인 편집 ${readonly ? "(읽기 전용)" : ""}`,
+ Download: "다운로드",
+ Auth: "인증 유형",
+ Content: "OpenAPI Schema",
+ Load: "URL에서 로드",
+ Method: "메서드",
+ Error: "OpenAPI Schema 오류",
+ },
+ },
Mask: {
Name: "마스크",
Page: {
@@ -558,6 +804,61 @@ const ko: PartialLocaleType = {
Topic: "주제",
Time: "시간",
},
+ SdPanel: {
+ Prompt: "프롬프트",
+ NegativePrompt: "부정적 프롬프트",
+ PleaseInput: (name: string) => `${name}을 입력하세요`,
+ AspectRatio: "비율",
+ ImageStyle: "이미지 스타일",
+ OutFormat: "출력 형식",
+ AIModel: "AI 모델",
+ ModelVersion: "모델 버전",
+ Submit: "제출",
+ ParamIsRequired: (name: string) => `${name}은 필수 입력 항목입니다`,
+ Styles: {
+ D3Model: "3d-model",
+ AnalogFilm: "analog-film",
+ Anime: "anime",
+ Cinematic: "cinematic",
+ ComicBook: "comic-book",
+ DigitalArt: "digital-art",
+ Enhance: "enhance",
+ FantasyArt: "fantasy-art",
+ Isometric: "isometric",
+ LineArt: "line-art",
+ LowPoly: "low-poly",
+ ModelingCompound: "modeling-compound",
+ NeonPunk: "neon-punk",
+ Origami: "origami",
+ Photographic: "photographic",
+ PixelArt: "pixel-art",
+ TileTexture: "tile-texture",
+ },
+ },
+ Sd: {
+ SubTitle: (count: number) => `${count} 개의 이미지`,
+ Actions: {
+ Params: "파라미터 보기",
+ Copy: "프롬프트 복사",
+ Delete: "삭제",
+ Retry: "다시 시도",
+ ReturnHome: "홈으로 돌아가기",
+ History: "기록",
+ },
+ EmptyRecord: "아직 이미지가 없습니다",
+ Status: {
+ Name: "상태",
+ Success: "성공",
+ Error: "오류",
+ Wait: "대기",
+ Running: "실행 중",
+ },
+ Danger: {
+ Delete: "삭제하시겠습니까?",
+ },
+ GenerateParams: "파라미터 생성",
+ Detail: "상세",
+ },
};
export default ko;
diff --git a/app/locales/no.ts b/app/locales/no.ts
index 6fc8e86c787..e0556a85372 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`,
@@ -288,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",
@@ -414,6 +433,17 @@ const no: PartialLocaleType = {
Title: "Egendefinert modellnavn",
SubTitle: "Legg til egendefinerte modellalternativer, skill med komma",
},
+ AI302: {
+ ApiKey: {
+ Title: "API-nøkkel",
+ SubTitle: "Bruk egendefinert 302.AI API-nøkkel",
+ Placeholder: "302.AI API-nøkkel",
+ },
+ Endpoint: {
+ Title: "API-adresse",
+ SubTitle: "Eksempel:",
+ },
+ },
},
Model: "Modell",
diff --git a/app/locales/pt.ts b/app/locales/pt.ts
index c04081a8b36..6b80c2ec1a4 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`,
@@ -281,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",
@@ -341,6 +359,17 @@ const pt: PartialLocaleType = {
SubTitle: "Verifique sua versão API do console Anthropic",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "Chave API 302.AI",
+ SubTitle: "Use uma chave API 302.AI personalizada",
+ Placeholder: "302.AI API Key",
+ },
+ Endpoint: {
+ Title: "Endpoint Address",
+ SubTitle: "Exemplo: ",
+ },
+ },
CustomModel: {
Title: "Modelos Personalizados",
SubTitle: "Opções de modelo personalizado, separados por vírgula",
diff --git a/app/locales/ru.ts b/app/locales/ru.ts
index 0fcf73a249f..a4f0c949cb0 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} бесед`,
@@ -286,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: "Администратор включил защиту паролем",
@@ -408,6 +426,17 @@ const ru: PartialLocaleType = {
SubTitle: "Пример:",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "Ключ интерфейса",
+ SubTitle: "Использовать пользовательский 302.AI API-ключ",
+ Placeholder: "302.AI API-ключ",
+ },
+ Endpoint: {
+ Title: "Адрес интерфейса",
+ SubTitle: "Пример:",
+ },
+ },
CustomModel: {
Title: "Название пользовательской модели",
SubTitle:
diff --git a/app/locales/sk.ts b/app/locales/sk.ts
index 8f83c3ba73a..6508f7f3831 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`,
@@ -282,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",
@@ -364,6 +381,17 @@ const sk: PartialLocaleType = {
SubTitle: "Vyberte špecifickú verziu časti",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "API kľúč",
+ SubTitle: "Použiť vlastný API kľúč 302.AI",
+ Placeholder: "302.AI API kľúč",
+ },
+ Endpoint: {
+ Title: "Adresa koncového bodu",
+ SubTitle: "Príklad:",
+ },
+ },
},
Model: "Model",
diff --git a/app/locales/tr.ts b/app/locales/tr.ts
index b7f14104750..15d21fb7d09 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`,
@@ -286,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",
@@ -408,6 +426,17 @@ const tr: PartialLocaleType = {
SubTitle: "Örnek:",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "API Anahtarı",
+ SubTitle: "Özelleştirilmiş 302.AI API Anahtarı kullanın",
+ Placeholder: "302.AI API Anahtarı",
+ },
+ Endpoint: {
+ Title: "API Adresi",
+ SubTitle: "Örnek:",
+ },
+ },
CustomModel: {
Title: "Özelleştirilmiş Model Adı",
SubTitle:
diff --git a/app/locales/tw.ts b/app/locales/tw.ts
index b0602a08174..d09465d9e1f 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,8 +53,8 @@ const tw = {
PinToastAction: "檢視",
Delete: "刪除",
Edit: "編輯",
- RefreshTitle: "刷新標題",
- RefreshToast: "已發送刷新標題請求",
+ RefreshTitle: "重新整理標題",
+ RefreshToast: "已傳送重新整理標題請求",
},
Commands: {
new: "新建聊天",
@@ -85,11 +95,12 @@ const tw = {
IsContext: "預設提示詞",
ShortcutKey: {
Title: "鍵盤快捷方式",
- newChat: "打開新聊天",
+ newChat: "開啟新聊天",
focusInput: "聚焦輸入框",
copyLastMessage: "複製最後一個回覆",
- copyLastCode: "複製最後一個代碼塊",
+ copyLastCode: "複製最後一個程式碼區塊",
showShortcutKey: "顯示快捷方式",
+ clearContext: "清除上下文",
},
},
Export: {
@@ -164,9 +175,9 @@ const tw = {
SubTitle: "聊天內容的字型大小",
},
FontFamily: {
- Title: "聊天字體",
- SubTitle: "聊天內容的字體,若置空則應用全局默認字體",
- Placeholder: "字體名稱",
+ Title: "聊天字型",
+ SubTitle: "聊天內容的字型,若留空則套用全域預設字型",
+ Placeholder: "字型名稱",
},
InjectSystemPrompts: {
Title: "匯入系統提示",
@@ -287,6 +298,14 @@ const tw = {
},
Access: {
+ SaasStart: {
+ Title: "使用 NextChat AI",
+ Label: "(性價比最高的方案)",
+ SubTitle:
+ "由 NextChat 官方維護,無須設定開箱即用,支援 OpenAI o1、GPT-4o、Claude-3.5 等最新的大型語言模型",
+ ChatNow: "立刻開始對話",
+ },
+
AccessCode: {
Title: "存取密碼",
SubTitle: "管理員已開啟加密存取",
@@ -363,6 +382,17 @@ const tw = {
SubTitle: "選擇一個特定的 API 版本",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "API 金鑰",
+ SubTitle: "使用自訂 302.AI API 金鑰",
+ Placeholder: "302.AI API 金鑰",
+ },
+ Endpoint: {
+ Title: "端點位址",
+ SubTitle: "範例:",
+ },
+ },
CustomModel: {
Title: "自訂模型名稱",
SubTitle: "增加自訂模型可選擇項目,使用英文逗號隔開",
@@ -467,18 +497,18 @@ const tw = {
},
},
SearchChat: {
- Name: "搜索",
+ Name: "搜尋聊天記錄",
Page: {
- Title: "搜索聊天記錄",
- Search: "輸入搜索關鍵詞",
+ Title: "搜尋聊天記錄",
+ Search: "輸入搜尋關鍵詞",
NoResult: "沒有找到結果",
- NoData: "沒有數據",
- Loading: "加載中",
+ NoData: "沒有資料",
+ Loading: "載入中",
SubTitle: (count: number) => `找到 ${count} 條結果`,
},
Item: {
- View: "查看",
+ View: "檢視",
},
},
NewChat: {
diff --git a/app/locales/vi.ts b/app/locales/vi.ts
index 1f20e15a0b1..7f6ed3f6f4e 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`,
@@ -283,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",
@@ -404,6 +422,17 @@ const vi: PartialLocaleType = {
SubTitle: "Ví dụ:",
},
},
+ AI302: {
+ ApiKey: {
+ Title: "Khóa API 302.AI",
+ SubTitle: "Sử dụng khóa API 302.AI tùy chỉnh",
+ Placeholder: "302.AI API Key",
+ },
+ Endpoint: {
+ Title: "Địa chỉ giao diện",
+ SubTitle: "Ví dụ:",
+ },
+ },
CustomModel: {
Title: "Tên mô hình tùy chỉnh",
SubTitle:
diff --git a/app/masks/cn.ts b/app/masks/cn.ts
index ed507d7344e..64842f6e892 100644
--- a/app/masks/cn.ts
+++ b/app/masks/cn.ts
@@ -3,7 +3,7 @@ import { BuiltinMask } from "./typing";
export const CN_MASKS: BuiltinMask[] = [
{
avatar: "1f5bc-fe0f",
- name: "以文搜图",
+ name: "AI文生图",
context: [
{
id: "text-to-pic-0",
@@ -28,7 +28,7 @@ export const CN_MASKS: BuiltinMask[] = [
id: "text-to-pic-3",
role: "system",
content:
- "助手善于判断用户意图,当确定需要提供图片时,助手会变得沉默寡言,只使用以下格式输出markdown图片:,因为这个语法可以自动按照提示生成并渲染图片。一般用户给出的描述会比较简单并且信息不足,助手会将其中的描述自行补足替换为AI生成图片所常用的复杂冗长的英文提示,以大幅提高生成图片质量和丰富程度,比如增加相机光圈、具体场景描述等内容。助手会避免用代码块或原始块包围markdown标记,因为那样只会渲染出代码块或原始块而不是图片。",
+ "助手善于判断用户意图,当确定需要提供图片时,助手会变得沉默寡言,只使用以下格式输出markdown图片:,因为这个语法可以自动按照提示生成并渲染图片。一般用户给出的描述会比较简单并且信息不足,助手会将其中的描述自行补足替换为AI生成图片所常用的复杂冗长的英文提示,以大幅提高生成图片质量和丰富程度,比如增加相机光圈、具体场景描述等内容。助手会避免用代码块或原始块包围markdown标记,因为那样只会渲染出代码块或原始块而不是图片。url中的空格等符号需要转义。",
date: "",
},
],
diff --git a/app/masks/index.ts b/app/masks/index.ts
index 92f21c6aea7..bff5c9bbe0a 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/mcp/actions.ts b/app/mcp/actions.ts
new file mode 100644
index 00000000000..e8b1ad1d00f
--- /dev/null
+++ b/app/mcp/actions.ts
@@ -0,0 +1,385 @@
+"use server";
+import {
+ createClient,
+ executeRequest,
+ listTools,
+ removeClient,
+} from "./client";
+import { MCPClientLogger } from "./logger";
+import {
+ DEFAULT_MCP_CONFIG,
+ McpClientData,
+ McpConfigData,
+ McpRequestMessage,
+ ServerConfig,
+ ServerStatusResponse,
+} from "./types";
+import fs from "fs/promises";
+import path from "path";
+import { getServerSideConfig } from "../config/server";
+
+const logger = new MCPClientLogger("MCP Actions");
+const CONFIG_PATH = path.join(process.cwd(), "app/mcp/mcp_config.json");
+
+const clientsMap = new Map();
+
+// 获取客户端状态
+export async function getClientsStatus(): Promise<
+ Record
+> {
+ const config = await getMcpConfigFromFile();
+ const result: Record = {};
+
+ for (const clientId of Object.keys(config.mcpServers)) {
+ const status = clientsMap.get(clientId);
+ const serverConfig = config.mcpServers[clientId];
+
+ if (!serverConfig) {
+ result[clientId] = { status: "undefined", errorMsg: null };
+ continue;
+ }
+
+ if (serverConfig.status === "paused") {
+ result[clientId] = { status: "paused", errorMsg: null };
+ continue;
+ }
+
+ if (!status) {
+ result[clientId] = { status: "undefined", errorMsg: null };
+ continue;
+ }
+
+ if (
+ status.client === null &&
+ status.tools === null &&
+ status.errorMsg === null
+ ) {
+ result[clientId] = { status: "initializing", errorMsg: null };
+ continue;
+ }
+
+ if (status.errorMsg) {
+ result[clientId] = { status: "error", errorMsg: status.errorMsg };
+ continue;
+ }
+
+ if (status.client) {
+ result[clientId] = { status: "active", errorMsg: null };
+ continue;
+ }
+
+ result[clientId] = { status: "error", errorMsg: "Client not found" };
+ }
+
+ return result;
+}
+
+// 获取客户端工具
+export async function getClientTools(clientId: string) {
+ return clientsMap.get(clientId)?.tools ?? null;
+}
+
+// 获取可用客户端数量
+export async function getAvailableClientsCount() {
+ let count = 0;
+ clientsMap.forEach((map) => !map.errorMsg && count++);
+ return count;
+}
+
+// 获取所有客户端工具
+export async function getAllTools() {
+ const result = [];
+ for (const [clientId, status] of clientsMap.entries()) {
+ result.push({
+ clientId,
+ tools: status.tools,
+ });
+ }
+ return result;
+}
+
+// 初始化单个客户端
+async function initializeSingleClient(
+ clientId: string,
+ serverConfig: ServerConfig,
+) {
+ // 如果服务器状态是暂停,则不初始化
+ if (serverConfig.status === "paused") {
+ logger.info(`Skipping initialization for paused client [${clientId}]`);
+ return;
+ }
+
+ logger.info(`Initializing client [${clientId}]...`);
+
+ // 先设置初始化状态
+ clientsMap.set(clientId, {
+ client: null,
+ tools: null,
+ errorMsg: null, // null 表示正在初始化
+ });
+
+ // 异步初始化
+ createClient(clientId, serverConfig)
+ .then(async (client) => {
+ const tools = await listTools(client);
+ logger.info(
+ `Supported tools for [${clientId}]: ${JSON.stringify(tools, null, 2)}`,
+ );
+ clientsMap.set(clientId, { client, tools, errorMsg: null });
+ logger.success(`Client [${clientId}] initialized successfully`);
+ })
+ .catch((error) => {
+ clientsMap.set(clientId, {
+ client: null,
+ tools: null,
+ errorMsg: error instanceof Error ? error.message : String(error),
+ });
+ logger.error(`Failed to initialize client [${clientId}]: ${error}`);
+ });
+}
+
+// 初始化系统
+export async function initializeMcpSystem() {
+ logger.info("MCP Actions starting...");
+ try {
+ // 检查是否已有活跃的客户端
+ if (clientsMap.size > 0) {
+ logger.info("MCP system already initialized, skipping...");
+ return;
+ }
+
+ const config = await getMcpConfigFromFile();
+ // 初始化所有客户端
+ for (const [clientId, serverConfig] of Object.entries(config.mcpServers)) {
+ await initializeSingleClient(clientId, serverConfig);
+ }
+ return config;
+ } catch (error) {
+ logger.error(`Failed to initialize MCP system: ${error}`);
+ throw error;
+ }
+}
+
+// 添加服务器
+export async function addMcpServer(clientId: string, config: ServerConfig) {
+ try {
+ const currentConfig = await getMcpConfigFromFile();
+ const isNewServer = !(clientId in currentConfig.mcpServers);
+
+ // 如果是新服务器,设置默认状态为 active
+ if (isNewServer && !config.status) {
+ config.status = "active";
+ }
+
+ const newConfig = {
+ ...currentConfig,
+ mcpServers: {
+ ...currentConfig.mcpServers,
+ [clientId]: config,
+ },
+ };
+ await updateMcpConfig(newConfig);
+
+ // 只有新服务器或状态为 active 的服务器才初始化
+ if (isNewServer || config.status === "active") {
+ await initializeSingleClient(clientId, config);
+ }
+
+ return newConfig;
+ } catch (error) {
+ logger.error(`Failed to add server [${clientId}]: ${error}`);
+ throw error;
+ }
+}
+
+// 暂停服务器
+export async function pauseMcpServer(clientId: string) {
+ try {
+ const currentConfig = await getMcpConfigFromFile();
+ const serverConfig = currentConfig.mcpServers[clientId];
+ if (!serverConfig) {
+ throw new Error(`Server ${clientId} not found`);
+ }
+
+ // 先更新配置
+ const newConfig: McpConfigData = {
+ ...currentConfig,
+ mcpServers: {
+ ...currentConfig.mcpServers,
+ [clientId]: {
+ ...serverConfig,
+ status: "paused",
+ },
+ },
+ };
+ await updateMcpConfig(newConfig);
+
+ // 然后关闭客户端
+ const client = clientsMap.get(clientId);
+ if (client?.client) {
+ await removeClient(client.client);
+ }
+ clientsMap.delete(clientId);
+
+ return newConfig;
+ } catch (error) {
+ logger.error(`Failed to pause server [${clientId}]: ${error}`);
+ throw error;
+ }
+}
+
+// 恢复服务器
+export async function resumeMcpServer(clientId: string): Promise {
+ try {
+ const currentConfig = await getMcpConfigFromFile();
+ const serverConfig = currentConfig.mcpServers[clientId];
+ if (!serverConfig) {
+ throw new Error(`Server ${clientId} not found`);
+ }
+
+ // 先尝试初始化客户端
+ logger.info(`Trying to initialize client [${clientId}]...`);
+ try {
+ const client = await createClient(clientId, serverConfig);
+ const tools = await listTools(client);
+ clientsMap.set(clientId, { client, tools, errorMsg: null });
+ logger.success(`Client [${clientId}] initialized successfully`);
+
+ // 初始化成功后更新配置
+ const newConfig: McpConfigData = {
+ ...currentConfig,
+ mcpServers: {
+ ...currentConfig.mcpServers,
+ [clientId]: {
+ ...serverConfig,
+ status: "active" as const,
+ },
+ },
+ };
+ await updateMcpConfig(newConfig);
+ } catch (error) {
+ const currentConfig = await getMcpConfigFromFile();
+ const serverConfig = currentConfig.mcpServers[clientId];
+
+ // 如果配置中存在该服务器,则更新其状态为 error
+ if (serverConfig) {
+ serverConfig.status = "error";
+ await updateMcpConfig(currentConfig);
+ }
+
+ // 初始化失败
+ clientsMap.set(clientId, {
+ client: null,
+ tools: null,
+ errorMsg: error instanceof Error ? error.message : String(error),
+ });
+ logger.error(`Failed to initialize client [${clientId}]: ${error}`);
+ throw error;
+ }
+ } catch (error) {
+ logger.error(`Failed to resume server [${clientId}]: ${error}`);
+ throw error;
+ }
+}
+
+// 移除服务器
+export async function removeMcpServer(clientId: string) {
+ try {
+ const currentConfig = await getMcpConfigFromFile();
+ const { [clientId]: _, ...rest } = currentConfig.mcpServers;
+ const newConfig = {
+ ...currentConfig,
+ mcpServers: rest,
+ };
+ await updateMcpConfig(newConfig);
+
+ // 关闭并移除客户端
+ const client = clientsMap.get(clientId);
+ if (client?.client) {
+ await removeClient(client.client);
+ }
+ clientsMap.delete(clientId);
+
+ return newConfig;
+ } catch (error) {
+ logger.error(`Failed to remove server [${clientId}]: ${error}`);
+ throw error;
+ }
+}
+
+// 重启所有客户端
+export async function restartAllClients() {
+ logger.info("Restarting all clients...");
+ try {
+ // 关闭所有客户端
+ for (const client of clientsMap.values()) {
+ if (client.client) {
+ await removeClient(client.client);
+ }
+ }
+
+ // 清空状态
+ clientsMap.clear();
+
+ // 重新初始化
+ const config = await getMcpConfigFromFile();
+ for (const [clientId, serverConfig] of Object.entries(config.mcpServers)) {
+ await initializeSingleClient(clientId, serverConfig);
+ }
+ return config;
+ } catch (error) {
+ logger.error(`Failed to restart clients: ${error}`);
+ throw error;
+ }
+}
+
+// 执行 MCP 请求
+export async function executeMcpAction(
+ clientId: string,
+ request: McpRequestMessage,
+) {
+ try {
+ const client = clientsMap.get(clientId);
+ if (!client?.client) {
+ throw new Error(`Client ${clientId} not found`);
+ }
+ logger.info(`Executing request for [${clientId}]`);
+ return await executeRequest(client.client, request);
+ } catch (error) {
+ logger.error(`Failed to execute request for [${clientId}]: ${error}`);
+ throw error;
+ }
+}
+
+// 获取 MCP 配置文件
+export async function getMcpConfigFromFile(): Promise {
+ try {
+ const configStr = await fs.readFile(CONFIG_PATH, "utf-8");
+ return JSON.parse(configStr);
+ } catch (error) {
+ logger.error(`Failed to load MCP config, using default config: ${error}`);
+ return DEFAULT_MCP_CONFIG;
+ }
+}
+
+// 更新 MCP 配置文件
+async function updateMcpConfig(config: McpConfigData): Promise {
+ try {
+ // 确保目录存在
+ await fs.mkdir(path.dirname(CONFIG_PATH), { recursive: true });
+ await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));
+ } catch (error) {
+ throw error;
+ }
+}
+
+// 检查 MCP 是否启用
+export async function isMcpEnabled() {
+ try {
+ const serverConfig = getServerSideConfig();
+ return serverConfig.enableMcp;
+ } catch (error) {
+ logger.error(`Failed to check MCP status: ${error}`);
+ return false;
+ }
+}
diff --git a/app/mcp/client.ts b/app/mcp/client.ts
new file mode 100644
index 00000000000..5c2f071e301
--- /dev/null
+++ b/app/mcp/client.ts
@@ -0,0 +1,55 @@
+import { Client } from "@modelcontextprotocol/sdk/client/index.js";
+import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
+import { MCPClientLogger } from "./logger";
+import { ListToolsResponse, McpRequestMessage, ServerConfig } from "./types";
+import { z } from "zod";
+
+const logger = new MCPClientLogger();
+
+export async function createClient(
+ id: string,
+ config: ServerConfig,
+): Promise {
+ logger.info(`Creating client for ${id}...`);
+
+ const transport = new StdioClientTransport({
+ command: config.command,
+ args: config.args,
+ env: {
+ ...Object.fromEntries(
+ Object.entries(process.env)
+ .filter(([_, v]) => v !== undefined)
+ .map(([k, v]) => [k, v as string]),
+ ),
+ ...(config.env || {}),
+ },
+ });
+
+ const client = new Client(
+ {
+ name: `nextchat-mcp-client-${id}`,
+ version: "1.0.0",
+ },
+ {
+ capabilities: {},
+ },
+ );
+ await client.connect(transport);
+ return client;
+}
+
+export async function removeClient(client: Client) {
+ logger.info(`Removing client...`);
+ await client.close();
+}
+
+export async function listTools(client: Client): Promise {
+ return client.listTools();
+}
+
+export async function executeRequest(
+ client: Client,
+ request: McpRequestMessage,
+) {
+ return client.request(request, z.any());
+}
diff --git a/app/mcp/logger.ts b/app/mcp/logger.ts
new file mode 100644
index 00000000000..25129c592c3
--- /dev/null
+++ b/app/mcp/logger.ts
@@ -0,0 +1,65 @@
+// ANSI color codes for terminal output
+const colors = {
+ reset: "\x1b[0m",
+ bright: "\x1b[1m",
+ dim: "\x1b[2m",
+ green: "\x1b[32m",
+ yellow: "\x1b[33m",
+ red: "\x1b[31m",
+ blue: "\x1b[34m",
+};
+
+export class MCPClientLogger {
+ private readonly prefix: string;
+ private readonly debugMode: boolean;
+
+ constructor(
+ prefix: string = "NextChat MCP Client",
+ debugMode: boolean = false,
+ ) {
+ this.prefix = prefix;
+ this.debugMode = debugMode;
+ }
+
+ info(message: any) {
+ this.print(colors.blue, message);
+ }
+
+ success(message: any) {
+ this.print(colors.green, message);
+ }
+
+ error(message: any) {
+ this.print(colors.red, message);
+ }
+
+ warn(message: any) {
+ this.print(colors.yellow, message);
+ }
+
+ debug(message: any) {
+ if (this.debugMode) {
+ this.print(colors.dim, message);
+ }
+ }
+
+ /**
+ * Format message to string, if message is object, convert to JSON string
+ */
+ private formatMessage(message: any): string {
+ return typeof message === "object"
+ ? JSON.stringify(message, null, 2)
+ : message;
+ }
+
+ /**
+ * Print formatted message to console
+ */
+ private print(color: string, message: any) {
+ const formattedMessage = this.formatMessage(message);
+ const logMessage = `${color}${colors.bright}[${this.prefix}]${colors.reset} ${formattedMessage}`;
+
+ // 只使用 console.log,这样日志会显示在 Tauri 的终端中
+ console.log(logMessage);
+ }
+}
diff --git a/app/mcp/mcp_config.default.json b/app/mcp/mcp_config.default.json
new file mode 100644
index 00000000000..da39e4ffafe
--- /dev/null
+++ b/app/mcp/mcp_config.default.json
@@ -0,0 +1,3 @@
+{
+ "mcpServers": {}
+}
diff --git a/app/mcp/types.ts b/app/mcp/types.ts
new file mode 100644
index 00000000000..45d1d979a98
--- /dev/null
+++ b/app/mcp/types.ts
@@ -0,0 +1,180 @@
+// ref: https://spec.modelcontextprotocol.io/specification/basic/messages/
+
+import { z } from "zod";
+import { Client } from "@modelcontextprotocol/sdk/client/index.js";
+
+export interface McpRequestMessage {
+ jsonrpc?: "2.0";
+ id?: string | number;
+ method: "tools/call" | string;
+ params?: {
+ [key: string]: unknown;
+ };
+}
+
+export const McpRequestMessageSchema: z.ZodType = z.object({
+ jsonrpc: z.literal("2.0").optional(),
+ id: z.union([z.string(), z.number()]).optional(),
+ method: z.string(),
+ params: z.record(z.unknown()).optional(),
+});
+
+export interface McpResponseMessage {
+ jsonrpc?: "2.0";
+ id?: string | number;
+ result?: {
+ [key: string]: unknown;
+ };
+ error?: {
+ code: number;
+ message: string;
+ data?: unknown;
+ };
+}
+
+export const McpResponseMessageSchema: z.ZodType = z.object(
+ {
+ jsonrpc: z.literal("2.0").optional(),
+ id: z.union([z.string(), z.number()]).optional(),
+ result: z.record(z.unknown()).optional(),
+ error: z
+ .object({
+ code: z.number(),
+ message: z.string(),
+ data: z.unknown().optional(),
+ })
+ .optional(),
+ },
+);
+
+export interface McpNotifications {
+ jsonrpc?: "2.0";
+ method: string;
+ params?: {
+ [key: string]: unknown;
+ };
+}
+
+export const McpNotificationsSchema: z.ZodType = z.object({
+ jsonrpc: z.literal("2.0").optional(),
+ method: z.string(),
+ params: z.record(z.unknown()).optional(),
+});
+
+////////////
+// Next Chat
+////////////
+export interface ListToolsResponse {
+ tools: {
+ name?: string;
+ description?: string;
+ inputSchema?: object;
+ [key: string]: any;
+ };
+}
+
+export type McpClientData =
+ | McpActiveClient
+ | McpErrorClient
+ | McpInitializingClient;
+
+interface McpInitializingClient {
+ client: null;
+ tools: null;
+ errorMsg: null;
+}
+
+interface McpActiveClient {
+ client: Client;
+ tools: ListToolsResponse;
+ errorMsg: null;
+}
+
+interface McpErrorClient {
+ client: null;
+ tools: null;
+ errorMsg: string;
+}
+
+// 服务器状态类型
+export type ServerStatus =
+ | "undefined"
+ | "active"
+ | "paused"
+ | "error"
+ | "initializing";
+
+export interface ServerStatusResponse {
+ status: ServerStatus;
+ errorMsg: string | null;
+}
+
+// MCP 服务器配置相关类型
+export interface ServerConfig {
+ command: string;
+ args: string[];
+ env?: Record;
+ status?: "active" | "paused" | "error";
+}
+
+export interface McpConfigData {
+ // MCP Server 的配置
+ mcpServers: Record;
+}
+
+export const DEFAULT_MCP_CONFIG: McpConfigData = {
+ mcpServers: {},
+};
+
+export interface ArgsMapping {
+ // 参数映射的类型
+ type: "spread" | "single" | "env";
+
+ // 参数映射的位置
+ position?: number;
+
+ // 参数映射的 key
+ key?: string;
+}
+
+export interface PresetServer {
+ // MCP Server 的唯一标识,作为最终配置文件 Json 的 key
+ id: string;
+
+ // MCP Server 的显示名称
+ name: string;
+
+ // MCP Server 的描述
+ description: string;
+
+ // MCP Server 的仓库地址
+ repo: string;
+
+ // MCP Server 的标签
+ tags: string[];
+
+ // MCP Server 的命令
+ command: string;
+
+ // MCP Server 的参数
+ baseArgs: string[];
+
+ // MCP Server 是否需要配置
+ configurable: boolean;
+
+ // MCP Server 的配置 schema
+ configSchema?: {
+ properties: Record<
+ string,
+ {
+ type: string;
+ description?: string;
+ required?: boolean;
+ minItems?: number;
+ }
+ >;
+ };
+
+ // MCP Server 的参数映射
+ argsMapping?: Record;
+}
diff --git a/app/mcp/utils.ts b/app/mcp/utils.ts
new file mode 100644
index 00000000000..b74509881ef
--- /dev/null
+++ b/app/mcp/utils.ts
@@ -0,0 +1,11 @@
+export function isMcpJson(content: string) {
+ return content.match(/```json:mcp:([^{\s]+)([\s\S]*?)```/);
+}
+
+export function extractMcpJson(content: string) {
+ const match = content.match(/```json:mcp:([^{\s]+)([\s\S]*?)```/);
+ if (match && match.length === 3) {
+ return { clientId: match[1], mcp: JSON.parse(match[2]) };
+ }
+ return null;
+}
diff --git a/app/page.tsx b/app/page.tsx
index b3f169a9b74..c748d42c71a 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,7 +1,5 @@
import { Analytics } from "@vercel/analytics/react";
-
import { Home } from "./components/home";
-
import { getServerSideConfig } from "./config/server";
const serverConfig = getServerSideConfig();
diff --git a/app/store/access.ts b/app/store/access.ts
index a1014610e39..fd55fbdd3d1 100644
--- a/app/store/access.ts
+++ b/app/store/access.ts
@@ -1,59 +1,66 @@
import {
- ApiPath,
- DEFAULT_API_HOST,
GoogleSafetySettingsThreshold,
ServiceProvider,
StoreKey,
+ ApiPath,
+ OPENAI_BASE_URL,
+ ANTHROPIC_BASE_URL,
+ GEMINI_BASE_URL,
+ BAIDU_BASE_URL,
+ BYTEDANCE_BASE_URL,
+ ALIBABA_BASE_URL,
+ TENCENT_BASE_URL,
+ MOONSHOT_BASE_URL,
+ STABILITY_BASE_URL,
+ IFLYTEK_BASE_URL,
+ DEEPSEEK_BASE_URL,
+ XAI_BASE_URL,
+ CHATGLM_BASE_URL,
+ SILICONFLOW_BASE_URL,
+ AI302_BASE_URL,
} from "../constant";
import { getHeaders } from "../client/api";
import { getClientConfig } from "../config/client";
import { createPersistStore } from "../utils/store";
import { ensure } from "../utils/clone";
import { DEFAULT_CONFIG } from "./config";
+import { getModelProvider } from "../utils/model";
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
const isApp = getClientConfig()?.buildMode === "export";
-const DEFAULT_OPENAI_URL = isApp
- ? DEFAULT_API_HOST + "/api/proxy/openai"
- : ApiPath.OpenAI;
+const DEFAULT_OPENAI_URL = isApp ? OPENAI_BASE_URL : ApiPath.OpenAI;
+
+const DEFAULT_GOOGLE_URL = isApp ? GEMINI_BASE_URL : ApiPath.Google;
+
+const DEFAULT_ANTHROPIC_URL = isApp ? ANTHROPIC_BASE_URL : ApiPath.Anthropic;
+
+const DEFAULT_BAIDU_URL = isApp ? BAIDU_BASE_URL : ApiPath.Baidu;
+
+const DEFAULT_BYTEDANCE_URL = isApp ? BYTEDANCE_BASE_URL : ApiPath.ByteDance;
+
+const DEFAULT_ALIBABA_URL = isApp ? ALIBABA_BASE_URL : ApiPath.Alibaba;
-const DEFAULT_GOOGLE_URL = isApp
- ? DEFAULT_API_HOST + "/api/proxy/google"
- : ApiPath.Google;
+const DEFAULT_TENCENT_URL = isApp ? TENCENT_BASE_URL : ApiPath.Tencent;
-const DEFAULT_ANTHROPIC_URL = isApp
- ? DEFAULT_API_HOST + "/api/proxy/anthropic"
- : ApiPath.Anthropic;
+const DEFAULT_MOONSHOT_URL = isApp ? MOONSHOT_BASE_URL : ApiPath.Moonshot;
-const DEFAULT_BAIDU_URL = isApp
- ? DEFAULT_API_HOST + "/api/proxy/baidu"
- : ApiPath.Baidu;
+const DEFAULT_STABILITY_URL = isApp ? STABILITY_BASE_URL : ApiPath.Stability;
-const DEFAULT_BYTEDANCE_URL = isApp
- ? DEFAULT_API_HOST + "/api/proxy/bytedance"
- : ApiPath.ByteDance;
+const DEFAULT_IFLYTEK_URL = isApp ? IFLYTEK_BASE_URL : ApiPath.Iflytek;
-const DEFAULT_ALIBABA_URL = isApp
- ? DEFAULT_API_HOST + "/api/proxy/alibaba"
- : ApiPath.Alibaba;
+const DEFAULT_DEEPSEEK_URL = isApp ? DEEPSEEK_BASE_URL : ApiPath.DeepSeek;
-const DEFAULT_TENCENT_URL = isApp
- ? DEFAULT_API_HOST + "/api/proxy/tencent"
- : ApiPath.Tencent;
+const DEFAULT_XAI_URL = isApp ? XAI_BASE_URL : ApiPath.XAI;
-const DEFAULT_MOONSHOT_URL = isApp
- ? DEFAULT_API_HOST + "/api/proxy/moonshot"
- : ApiPath.Moonshot;
+const DEFAULT_CHATGLM_URL = isApp ? CHATGLM_BASE_URL : ApiPath.ChatGLM;
-const DEFAULT_STABILITY_URL = isApp
- ? DEFAULT_API_HOST + "/api/proxy/stability"
- : ApiPath.Stability;
+const DEFAULT_SILICONFLOW_URL = isApp
+ ? SILICONFLOW_BASE_URL
+ : ApiPath.SiliconFlow;
-const DEFAULT_IFLYTEK_URL = isApp
- ? DEFAULT_API_HOST + "/api/proxy/iflytek"
- : ApiPath.Iflytek;
+const DEFAULT_AI302_URL = isApp ? AI302_BASE_URL : ApiPath["302.AI"];
const DEFAULT_ACCESS_STATE = {
accessCode: "",
@@ -112,6 +119,26 @@ const DEFAULT_ACCESS_STATE = {
iflytekApiKey: "",
iflytekApiSecret: "",
+ // deepseek
+ deepseekUrl: DEFAULT_DEEPSEEK_URL,
+ deepseekApiKey: "",
+
+ // xai
+ xaiUrl: DEFAULT_XAI_URL,
+ xaiApiKey: "",
+
+ // chatglm
+ chatglmUrl: DEFAULT_CHATGLM_URL,
+ chatglmApiKey: "",
+
+ // siliconflow
+ siliconflowUrl: DEFAULT_SILICONFLOW_URL,
+ siliconflowApiKey: "",
+
+ // 302.AI
+ ai302Url: DEFAULT_AI302_URL,
+ ai302ApiKey: "",
+
// server config
needCode: true,
hideUserApiKey: false,
@@ -120,6 +147,10 @@ const DEFAULT_ACCESS_STATE = {
disableFastLink: false,
customModels: "",
defaultModel: "",
+ visionModels: "",
+
+ // tts config
+ edgeTTSVoiceName: "zh-CN-YunxiNeural",
};
export const useAccessStore = createPersistStore(
@@ -131,6 +162,15 @@ export const useAccessStore = createPersistStore(
return get().needCode;
},
+ getVisionModels() {
+ this.fetch();
+ return get().visionModels;
+ },
+ edgeVoiceName() {
+ this.fetch();
+
+ return get().edgeTTSVoiceName;
+ },
isValidOpenAI() {
return ensure(get(), ["openaiApiKey"]);
@@ -170,6 +210,21 @@ export const useAccessStore = createPersistStore(
isValidIflytek() {
return ensure(get(), ["iflytekApiKey"]);
},
+ isValidDeepSeek() {
+ return ensure(get(), ["deepseekApiKey"]);
+ },
+
+ isValidXAI() {
+ return ensure(get(), ["xaiApiKey"]);
+ },
+
+ isValidChatGLM() {
+ return ensure(get(), ["chatglmApiKey"]);
+ },
+
+ isValidSiliconFlow() {
+ return ensure(get(), ["siliconflowApiKey"]);
+ },
isAuthorized() {
this.fetch();
@@ -186,6 +241,10 @@ export const useAccessStore = createPersistStore(
this.isValidTencent() ||
this.isValidMoonshot() ||
this.isValidIflytek() ||
+ this.isValidDeepSeek() ||
+ this.isValidXAI() ||
+ this.isValidChatGLM() ||
+ this.isValidSiliconFlow() ||
!this.enabledAccessControl() ||
(this.enabledAccessControl() && ensure(get(), ["accessCode"]))
);
@@ -202,10 +261,13 @@ export const useAccessStore = createPersistStore(
})
.then((res) => res.json())
.then((res) => {
- // Set default model from env request
- let defaultModel = res.defaultModel ?? "";
- DEFAULT_CONFIG.modelConfig.model =
- defaultModel !== "" ? defaultModel : "gpt-3.5-turbo";
+ const defaultModel = res.defaultModel ?? "";
+ if (defaultModel !== "") {
+ const [model, providerName] = getModelProvider(defaultModel);
+ DEFAULT_CONFIG.modelConfig.model = model;
+ DEFAULT_CONFIG.modelConfig.providerName = providerName as any;
+ }
+
return res;
})
.then((res: DangerConfig) => {
diff --git a/app/store/chat.ts b/app/store/chat.ts
index 3bcda75389a..87c1a8beba0 100644
--- a/app/store/chat.ts
+++ b/app/store/chat.ts
@@ -1,4 +1,9 @@
-import { getMessageTextContent, trimTopic } from "../utils";
+import {
+ getMessageTextContent,
+ isDalle3,
+ safeLocalStorage,
+ trimTopic,
+} from "../utils";
import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
import { nanoid } from "nanoid";
@@ -14,16 +19,25 @@ import {
DEFAULT_INPUT_TEMPLATE,
DEFAULT_MODELS,
DEFAULT_SYSTEM_TEMPLATE,
+ GEMINI_SUMMARIZE_MODEL,
+ DEEPSEEK_SUMMARIZE_MODEL,
KnowledgeCutOffDate,
+ MCP_SYSTEM_TEMPLATE,
+ MCP_TOOLS_TEMPLATE,
+ ServiceProvider,
StoreKey,
+ SUMMARIZE_MODEL,
} from "../constant";
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 { useAccessStore } from "./access";
+import { collectModelsWithDefaultModel } from "../utils/model";
import { createEmptyMask, Mask } from "./mask";
+import { executeMcpAction, getAllTools, isMcpEnabled } from "../mcp/actions";
+import { extractMcpJson, isMcpJson } from "../mcp/utils";
const localStorage = safeLocalStorage();
@@ -37,6 +51,7 @@ export type ChatMessageTool = {
};
content?: string;
isError?: boolean;
+ errorMsg?: string;
};
export type ChatMessage = RequestMessage & {
@@ -46,6 +61,8 @@ export type ChatMessage = RequestMessage & {
id: string;
model?: ModelType;
tools?: ChatMessageTool[];
+ audio_url?: string;
+ isMcpResponse?: boolean;
};
export function createMessage(override: Partial): ChatMessage {
@@ -102,6 +119,38 @@ function createEmptySession(): ChatSession {
};
}
+function getSummarizeModel(
+ currentModel: string,
+ providerName: string,
+): 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,
+ );
+ if (summarizeModel) {
+ return [
+ summarizeModel.name,
+ summarizeModel.provider?.providerName as string,
+ ];
+ }
+ }
+ if (currentModel.startsWith("gemini")) {
+ return [GEMINI_SUMMARIZE_MODEL, ServiceProvider.Google];
+ } else if (currentModel.startsWith("deepseek-")) {
+ return [DEEPSEEK_SUMMARIZE_MODEL, ServiceProvider.DeepSeek];
+ }
+
+ return [currentModel, providerName];
+}
+
function countMessages(msgs: ChatMessage[]) {
return msgs.reduce(
(pre, cur) => pre + estimateTokenLength(getMessageTextContent(cur)),
@@ -153,6 +202,27 @@ function fillTemplateWith(input: string, modelConfig: ModelConfig) {
return output;
}
+async function getMcpSystemPrompt(): Promise {
+ const tools = await getAllTools();
+
+ let toolsStr = "";
+
+ tools.forEach((i) => {
+ // error client has no tools
+ if (!i.tools) return;
+
+ toolsStr += MCP_TOOLS_TEMPLATE.replace(
+ "{{ clientId }}",
+ i.clientId,
+ ).replace(
+ "{{ tools }}",
+ i.tools.tools.map((p: object) => JSON.stringify(p, null, 2)).join("\n"),
+ );
+ });
+
+ return MCP_SYSTEM_TEMPLATE.replace("{{ MCP_TOOLS }}", toolsStr);
+}
+
const DEFAULT_CHAT_STATE = {
sessions: [createEmptySession()],
currentSessionIndex: 0,
@@ -170,6 +240,32 @@ 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.map((msg) => ({
+ ...msg,
+ id: nanoid(), // 生成新的消息 ID
+ }));
+ newSession.mask = {
+ ...currentSession.mask,
+ modelConfig: {
+ ...currentSession.mask.modelConfig,
+ },
+ };
+
+ set((state) => ({
+ currentSessionIndex: 0,
+ sessions: [newSession, ...state.sessions],
+ }));
+ },
+
clearSessions() {
set(() => ({
sessions: [createEmptySession()],
@@ -295,45 +391,46 @@ export const useChatStore = createPersistStore(
return session;
},
- onNewMessage(message: ChatMessage) {
- get().updateCurrentSession((session) => {
+ onNewMessage(message: ChatMessage, targetSession: ChatSession) {
+ get().updateTargetSession(targetSession, (session) => {
session.messages = session.messages.concat();
session.lastUpdate = Date.now();
});
- get().updateStat(message);
- get().summarizeSession();
+
+ get().updateStat(message, targetSession);
+
+ get().checkMcpJson(message);
+
+ get().summarizeSession(false, targetSession);
},
- async onUserInput(content: string, attachImages?: string[]) {
+ async onUserInput(
+ content: string,
+ attachImages?: string[],
+ isMcpResponse?: boolean,
+ ) {
const session = get().currentSession();
const modelConfig = session.mask.modelConfig;
- const userContent = fillTemplateWith(content, modelConfig);
- console.log("[User Input] after template: ", userContent);
-
- let mContent: string | MultimodalContent[] = userContent;
+ // MCP Response no need to fill template
+ let mContent: string | MultimodalContent[] = isMcpResponse
+ ? content
+ : fillTemplateWith(content, modelConfig);
- if (attachImages && attachImages.length > 0) {
+ if (!isMcpResponse && attachImages && attachImages.length > 0) {
mContent = [
- {
- type: "text",
- text: userContent,
- },
+ ...(content ? [{ type: "text" as const, text: content }] : []),
+ ...attachImages.map((url) => ({
+ type: "image_url" as const,
+ image_url: { url },
+ })),
];
- mContent = mContent.concat(
- attachImages.map((url) => {
- return {
- type: "image_url",
- image_url: {
- url: url,
- },
- };
- }),
- );
}
+
let userMessage: ChatMessage = createMessage({
role: "user",
content: mContent,
+ isMcpResponse,
});
const botMessage: ChatMessage = createMessage({
@@ -343,12 +440,12 @@ export const useChatStore = createPersistStore(
});
// get recent messages
- const recentMessages = get().getMessagesWithMemory();
+ const recentMessages = await get().getMessagesWithMemory();
const sendMessages = recentMessages.concat(userMessage);
- const messageIndex = get().currentSession().messages.length + 1;
+ const messageIndex = session.messages.length + 1;
// save user's and bot's message
- get().updateCurrentSession((session) => {
+ get().updateTargetSession(session, (session) => {
const savedUserMessage = {
...userMessage,
content: mContent,
@@ -369,21 +466,22 @@ export const useChatStore = createPersistStore(
if (message) {
botMessage.content = message;
}
- get().updateCurrentSession((session) => {
+ get().updateTargetSession(session, (session) => {
session.messages = session.messages.concat();
});
},
- onFinish(message) {
+ async onFinish(message) {
botMessage.streaming = false;
if (message) {
botMessage.content = message;
- get().onNewMessage(botMessage);
+ botMessage.date = new Date().toLocaleString();
+ get().onNewMessage(botMessage, session);
}
ChatControllerPool.remove(session.id, botMessage.id);
},
onBeforeTool(tool: ChatMessageTool) {
(botMessage.tools = botMessage?.tools || []).push(tool);
- get().updateCurrentSession((session) => {
+ get().updateTargetSession(session, (session) => {
session.messages = session.messages.concat();
});
},
@@ -393,7 +491,7 @@ export const useChatStore = createPersistStore(
tools[i] = { ...tool };
}
});
- get().updateCurrentSession((session) => {
+ get().updateTargetSession(session, (session) => {
session.messages = session.messages.concat();
});
},
@@ -408,7 +506,7 @@ export const useChatStore = createPersistStore(
botMessage.streaming = false;
userMessage.isError = !isAborted;
botMessage.isError = !isAborted;
- get().updateCurrentSession((session) => {
+ get().updateTargetSession(session, (session) => {
session.messages = session.messages.concat();
});
ChatControllerPool.remove(
@@ -441,7 +539,7 @@ export const useChatStore = createPersistStore(
}
},
- getMessagesWithMemory() {
+ async getMessagesWithMemory() {
const session = get().currentSession();
const modelConfig = session.mask.modelConfig;
const clearContextIndex = session.clearContextIndex ?? 0;
@@ -457,19 +555,32 @@ export const useChatStore = createPersistStore(
(session.mask.modelConfig.model.startsWith("gpt-") ||
session.mask.modelConfig.model.startsWith("chatgpt-"));
+ const mcpEnabled = await isMcpEnabled();
+ const mcpSystemPrompt = mcpEnabled ? await getMcpSystemPrompt() : "";
+
var systemPrompts: ChatMessage[] = [];
- systemPrompts = shouldInjectSystemPrompts
- ? [
- createMessage({
- role: "system",
- content: fillTemplateWith("", {
+
+ if (shouldInjectSystemPrompts) {
+ systemPrompts = [
+ createMessage({
+ role: "system",
+ content:
+ fillTemplateWith("", {
...modelConfig,
template: DEFAULT_SYSTEM_TEMPLATE,
- }),
- }),
- ]
- : [];
- if (shouldInjectSystemPrompts) {
+ }) + mcpSystemPrompt,
+ }),
+ ];
+ } else if (mcpEnabled) {
+ systemPrompts = [
+ createMessage({
+ role: "system",
+ content: mcpSystemPrompt,
+ }),
+ ];
+ }
+
+ if (shouldInjectSystemPrompts || mcpEnabled) {
console.log(
"[Global System Prompt] ",
systemPrompts.at(0)?.content ?? "empty",
@@ -540,24 +651,33 @@ export const useChatStore = createPersistStore(
set(() => ({ sessions }));
},
- resetSession() {
- get().updateCurrentSession((session) => {
+ resetSession(session: ChatSession) {
+ get().updateTargetSession(session, (session) => {
session.messages = [];
session.memoryPrompt = "";
});
},
- summarizeSession(refreshTitle: boolean = false) {
+ summarizeSession(
+ refreshTitle: boolean = false,
+ targetSession: ChatSession,
+ ) {
const config = useAppConfig.getState();
- const session = get().currentSession();
+ const session = targetSession;
const modelConfig = session.mask.modelConfig;
// skip summarize when using dalle3?
if (isDalle3(modelConfig.model)) {
return;
}
- const providerName = modelConfig.compressProviderName;
- const api: ClientApi = getClientApi(providerName);
+ // if not config compressModel, then using getSummarizeModel
+ const [model, providerName] = modelConfig.compressModel
+ ? [modelConfig.compressModel, modelConfig.compressProviderName]
+ : getSummarizeModel(
+ session.mask.modelConfig.model,
+ session.mask.modelConfig.providerName,
+ );
+ const api: ClientApi = getClientApi(providerName as ServiceProvider);
// remove error messages if any
const messages = session.messages;
@@ -588,16 +708,19 @@ export const useChatStore = createPersistStore(
api.llm.chat({
messages: topicMessages,
config: {
- model: modelConfig.compressModel,
+ model,
stream: false,
providerName,
},
- onFinish(message) {
- get().updateCurrentSession(
- (session) =>
- (session.topic =
- message.length > 0 ? trimTopic(message) : DEFAULT_TOPIC),
- );
+ onFinish(message, responseRes) {
+ if (responseRes?.status === 200) {
+ get().updateTargetSession(
+ session,
+ (session) =>
+ (session.topic =
+ message.length > 0 ? trimTopic(message) : DEFAULT_TOPIC),
+ );
+ }
},
});
}
@@ -611,7 +734,7 @@ export const useChatStore = createPersistStore(
const historyMsgLength = countMessages(toBeSummarizedMsgs);
- if (historyMsgLength > modelConfig?.max_tokens ?? 4000) {
+ if (historyMsgLength > (modelConfig?.max_tokens || 4000)) {
const n = toBeSummarizedMsgs.length;
toBeSummarizedMsgs = toBeSummarizedMsgs.slice(
Math.max(0, n - modelConfig.historyMessageCount),
@@ -651,17 +774,20 @@ export const useChatStore = createPersistStore(
config: {
...modelcfg,
stream: true,
- model: modelConfig.compressModel,
+ model,
+ providerName,
},
onUpdate(message) {
session.memoryPrompt = message;
},
- onFinish(message) {
- console.log("[Memory] ", message);
- get().updateCurrentSession((session) => {
- session.lastSummarizeIndex = lastSummarizeIndex;
- session.memoryPrompt = message; // Update the memory prompt for stored it in local storage
- });
+ onFinish(message, responseRes) {
+ if (responseRes?.status === 200) {
+ console.log("[Memory] ", message);
+ get().updateTargetSession(session, (session) => {
+ session.lastSummarizeIndex = lastSummarizeIndex;
+ session.memoryPrompt = message; // Update the memory prompt for stored it in local storage
+ });
+ }
},
onError(err) {
console.error("[Summarize] ", err);
@@ -670,20 +796,22 @@ export const useChatStore = createPersistStore(
}
},
- updateStat(message: ChatMessage) {
- get().updateCurrentSession((session) => {
+ updateStat(message: ChatMessage, session: ChatSession) {
+ get().updateTargetSession(session, (session) => {
session.stat.charCount += message.content.length;
// TODO: should update chat count and word count
});
},
-
- updateCurrentSession(updater: (session: ChatSession) => void) {
+ updateTargetSession(
+ targetSession: ChatSession,
+ updater: (session: ChatSession) => void,
+ ) {
const sessions = get().sessions;
- const index = get().currentSessionIndex;
+ const index = sessions.findIndex((s) => s.id === targetSession.id);
+ if (index < 0) return;
updater(sessions[index]);
set(() => ({ sessions }));
},
-
async clearAllData() {
await indexedDBStorage.clear();
localStorage.clear();
@@ -694,13 +822,45 @@ export const useChatStore = createPersistStore(
lastInput,
});
},
+
+ /** check if the message contains MCP JSON and execute the MCP action */
+ checkMcpJson(message: ChatMessage) {
+ const mcpEnabled = isMcpEnabled();
+ if (!mcpEnabled) return;
+ const content = getMessageTextContent(message);
+ if (isMcpJson(content)) {
+ try {
+ const mcpRequest = extractMcpJson(content);
+ if (mcpRequest) {
+ console.debug("[MCP Request]", mcpRequest);
+
+ executeMcpAction(mcpRequest.clientId, mcpRequest.mcp)
+ .then((result) => {
+ console.log("[MCP Response]", result);
+ const mcpResponse =
+ typeof result === "object"
+ ? JSON.stringify(result)
+ : String(result);
+ get().onUserInput(
+ `\`\`\`json:mcp-response:${mcpRequest.clientId}\n${mcpResponse}\n\`\`\``,
+ [],
+ true,
+ );
+ })
+ .catch((error) => showToast("MCP execution failed", error));
+ }
+ } catch (error) {
+ console.error("[Check MCP JSON]", error);
+ }
+ }
+ },
};
return methods;
},
{
name: StoreKey.Chat,
- version: 3.2,
+ version: 3.3,
migrate(persistedState, version) {
const state = persistedState as any;
const newState = JSON.parse(
@@ -756,6 +916,14 @@ export const useChatStore = createPersistStore(
config.modelConfig.compressProviderName;
});
}
+ // revert default summarize model for every session
+ if (version < 3.3) {
+ newState.sessions.forEach((s) => {
+ const config = useAppConfig.getState();
+ s.mask.modelConfig.compressModel = "";
+ s.mask.modelConfig.compressProviderName = "";
+ });
+ }
return newState as any;
},
diff --git a/app/store/config.ts b/app/store/config.ts
index 9985b9e768c..45e21b02697 100644
--- a/app/store/config.ts
+++ b/app/store/config.ts
@@ -1,16 +1,26 @@
import { LLMModel } from "../client/api";
-import { DalleSize, DalleQuality, DalleStyle } from "../typing";
+import { DalleQuality, DalleStyle, ModelSize } from "../typing";
import { getClientConfig } from "../config/client";
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";
+import type { Voice } from "rt-client";
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 +51,10 @@ export const DEFAULT_CONFIG = {
enableAutoGenerateTitle: true,
sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
+ enableArtifacts: true, // show artifacts config
+
+ enableCodeFold: true, // code fold config
+
disablePromptHint: false,
dontShowMaskSplashScreen: false, // dont show splash screen when create chat
@@ -60,19 +74,43 @@ export const DEFAULT_CONFIG = {
sendMemory: true,
historyMessageCount: 4,
compressMessageLengthThreshold: 1000,
- compressModel: "gpt-4o-mini" as ModelType,
- compressProviderName: "OpenAI" as ServiceProvider,
+ compressModel: "",
+ compressProviderName: "",
enableInjectSystemPrompts: true,
template: config?.template ?? DEFAULT_INPUT_TEMPLATE,
- size: "1024x1024" as DalleSize,
+ size: "1024x1024" as ModelSize,
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,
+ },
+
+ realtimeConfig: {
+ enable: false,
+ provider: "OpenAI" as ServiceProvider,
+ model: "gpt-4o-realtime-preview-2024-10-01",
+ apiKey: "",
+ azure: {
+ endpoint: "",
+ deployment: "",
+ },
+ temperature: 0.9,
+ voice: "alloy" as Voice,
+ },
};
export type ChatConfig = typeof DEFAULT_CONFIG;
export type ModelConfig = ChatConfig["modelConfig"];
+export type TTSConfig = ChatConfig["ttsConfig"];
+export type RealtimeConfig = ChatConfig["realtimeConfig"];
export function limitNumber(
x: number,
@@ -87,6 +125,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;
@@ -142,7 +195,22 @@ export const useAppConfig = createPersistStore(
}),
{
name: StoreKey.Config,
- version: 4,
+ version: 4.1,
+
+ 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;
@@ -180,7 +248,7 @@ export const useAppConfig = createPersistStore(
: config?.template ?? DEFAULT_INPUT_TEMPLATE;
}
- if (version < 4) {
+ if (version < 4.1) {
state.modelConfig.compressModel =
DEFAULT_CONFIG.modelConfig.compressModel;
state.modelConfig.compressProviderName =
diff --git a/app/store/mask.ts b/app/store/mask.ts
index 0c74a892e56..850abeef6f4 100644
--- a/app/store/mask.ts
+++ b/app/store/mask.ts
@@ -19,6 +19,7 @@ export type Mask = {
builtin: boolean;
plugin?: string[];
enableArtifacts?: boolean;
+ enableCodeFold?: boolean;
};
export const DEFAULT_MASK_STATE = {
diff --git a/app/store/plugin.ts b/app/store/plugin.ts
index 2356c6db0a7..b3d9f6d8ce6 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 { adapter, getOperationId } from "../utils";
+import { useAccessStore } from "./access";
+
+const isApp = getClientConfig()?.isApp !== false;
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: {
@@ -107,7 +116,7 @@ export const FunctionToolService = {
return {
type: "function",
function: {
- name: o.operationId,
+ name: getOperationId(o),
description: o.description || o.summary,
parameters: parameters,
},
@@ -115,7 +124,7 @@ export const FunctionToolService = {
}),
funcs: operations.reduce((s, o) => {
// @ts-ignore
- s[o.operationId] = function (args) {
+ s[getOperationId(o)] = function (args) {
const parameters: Record = {};
if (o.parameters instanceof Array) {
o.parameters.forEach((p) => {
@@ -130,8 +139,8 @@ export const FunctionToolService = {
} else if (authLocation == "body") {
args[headerName] = tokenValue;
}
- // @ts-ignore
- return api.client[o.operationId](
+ // @ts-ignore if o.operationId is null, then using o.path and o.method
+ return api.client.paths[o.path][o.method](
parameters,
args,
api.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 a25cda5813a..c06edb2823c 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 d3582e3c935..8477c1e4ba7 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 {
@@ -13,7 +12,6 @@ import { downloadAs, readFromFile } from "../utils";
import { showToast } from "../components/ui-lib";
import Locale from "../locales";
import { createSyncClient, ProviderType } from "../utils/cloud";
-import { corsPath } from "../utils/cors";
export interface WebDavConfig {
server: string;
@@ -27,7 +25,7 @@ export type SyncStore = GetStoreState;
const DEFAULT_SYNC_STATE = {
provider: ProviderType.WebDAV,
useProxy: true,
- proxyUrl: corsPath(ApiPath.Cors),
+ proxyUrl: ApiPath.Cors as string,
webdav: {
endpoint: "",
@@ -100,15 +98,17 @@ export const useSyncStore = createPersistStore(
const remoteState = await client.get(config.username);
if (!remoteState || remoteState === "") {
await client.set(config.username, JSON.stringify(localState));
- console.log("[Sync] Remote state is empty, using local state instead.");
- return
+ console.log(
+ "[Sync] Remote state is empty, using local state instead.",
+ );
+ return;
} else {
const parsedRemoteState = JSON.parse(
await client.get(config.username),
) as AppState;
mergeAppState(localState, parsedRemoteState);
setLocalAppState(localState);
- }
+ }
} catch (e) {
console.log("[Sync] failed to get remote state", e);
throw e;
diff --git a/app/store/update.ts b/app/store/update.ts
index 7253caffcb9..327dc5e88f9 100644
--- a/app/store/update.ts
+++ b/app/store/update.ts
@@ -6,10 +6,9 @@ import {
} from "../constant";
import { getClientConfig } from "../config/client";
import { createPersistStore } from "../utils/store";
+import { clientUpdate } from "../utils";
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;
@@ -121,6 +120,7 @@ export const useUpdateStore = createPersistStore(
icon: `${ChatGptIcon.src}`,
sound: "Default",
});
+ clientUpdate();
}
}
});
diff --git a/app/typing.ts b/app/typing.ts
index 0336be75d39..ecb327936fd 100644
--- a/app/typing.ts
+++ b/app/typing.ts
@@ -11,3 +11,14 @@ export interface RequestMessage {
export type DalleSize = "1024x1024" | "1792x1024" | "1024x1792";
export type DalleQuality = "standard" | "hd";
export type DalleStyle = "vivid" | "natural";
+
+export type ModelSize =
+ | "1024x1024"
+ | "1792x1024"
+ | "1024x1792"
+ | "768x1344"
+ | "864x1152"
+ | "1344x768"
+ | "1152x864"
+ | "1440x720"
+ | "720x1440";
diff --git a/app/utils.ts b/app/utils.ts
index bf745092913..6183e03b057 100644
--- a/app/utils.ts
+++ b/app/utils.ts
@@ -2,9 +2,16 @@ import { useEffect, useState } from "react";
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 {
+ REQUEST_TIMEOUT_MS,
+ REQUEST_TIMEOUT_MS_FOR_THINKING,
+ ServiceProvider,
+} from "./constant";
+// import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http";
+import { fetch as tauriStreamFetch } from "./utils/stream";
+import { VISION_MODEL_REGEXES, EXCLUDE_VISION_MODEL_REGEXES } from "./constant";
+import { useAccessStore } from "./store";
+import { ModelSize } from "./typing";
export function trimTopic(topic: string) {
// Fix an issue where double quotes still show in the Indonesian language
@@ -238,6 +245,28 @@ export function getMessageTextContent(message: RequestMessage) {
return "";
}
+export function getMessageTextContentWithoutThinking(message: RequestMessage) {
+ let content = "";
+
+ if (typeof message.content === "string") {
+ content = message.content;
+ } else {
+ for (const c of message.content) {
+ if (c.type === "text") {
+ content = c.text ?? "";
+ break;
+ }
+ }
+ }
+
+ // Filter out thinking lines (starting with "> ")
+ return content
+ .split("\n")
+ .filter((line) => !line.startsWith("> ") && line.trim() !== "")
+ .join("\n")
+ .trim();
+}
+
export function getMessageImages(message: RequestMessage): string[] {
if (typeof message.content === "string") {
return [];
@@ -252,21 +281,14 @@ export function getMessageImages(message: RequestMessage): string[] {
}
export function isVisionModel(model: string) {
- // Note: This is a better way using the TypeScript feature instead of `&&` or `||` (ts v5.5.0-dev.20240314 I've been using)
-
- const visionKeywords = [
- "vision",
- "claude-3",
- "gemini-1.5-pro",
- "gemini-1.5-flash",
- "gpt-4o",
- "gpt-4o-mini",
- ];
- const isGpt4Turbo =
- model.includes("gpt-4-turbo") && !model.includes("preview");
-
+ const visionModels = useAccessStore.getState().visionModels;
+ const envVisionModels = visionModels?.split(",").map((m) => m.trim());
+ if (envVisionModels?.includes(model)) {
+ return true;
+ }
return (
- visionKeywords.some((keyword) => model.includes(keyword)) || isGpt4Turbo
+ !EXCLUDE_VISION_MODEL_REGEXES.some((regex) => regex.test(model)) &&
+ VISION_MODEL_REGEXES.some((regex) => regex.test(model))
);
}
@@ -274,17 +296,57 @@ export function isDalle3(model: string) {
return "dall-e-3" === model;
}
+export function getTimeoutMSByModel(model: string) {
+ model = model.toLowerCase();
+ if (
+ model.startsWith("dall-e") ||
+ model.startsWith("dalle") ||
+ model.startsWith("o1") ||
+ model.startsWith("o3") ||
+ model.includes("deepseek-r") ||
+ model.includes("-thinking")
+ )
+ return REQUEST_TIMEOUT_MS_FOR_THINKING;
+ return REQUEST_TIMEOUT_MS;
+}
+
+export function getModelSizes(model: string): ModelSize[] {
+ if (isDalle3(model)) {
+ return ["1024x1024", "1792x1024", "1024x1792"];
+ }
+ if (model.toLowerCase().includes("cogview")) {
+ return [
+ "1024x1024",
+ "768x1344",
+ "864x1152",
+ "1344x768",
+ "1152x864",
+ "1440x720",
+ "720x1440",
+ ];
+ }
+ return [];
+}
+
+export function supportsCustomSize(model: string): boolean {
+ return getModelSizes(model).length > 0;
+}
+
export function showPlugins(provider: ServiceProvider, model: string) {
if (
provider == ServiceProvider.OpenAI ||
provider == ServiceProvider.Azure ||
- provider == ServiceProvider.Moonshot
+ provider == ServiceProvider.Moonshot ||
+ provider == ServiceProvider.ChatGLM
) {
return true;
}
if (provider == ServiceProvider.Anthropic && !model.includes("claude-2")) {
return true;
}
+ if (provider == ServiceProvider.Google && !model.includes("vision")) {
+ return true;
+ }
return false;
}
@@ -293,30 +355,23 @@ export function fetch(
options?: Record,
): Promise {
if (window.__TAURI__) {
- const payload = options?.body || options?.data;
- return tauriFetch(url, {
- ...options,
- body:
- payload &&
- ({
- type: "Text",
- payload,
- } as any),
- timeout: ((options?.timeout as number) || REQUEST_TIMEOUT_MS) / 1000,
- responseType:
- options?.responseType == "text" ? ResponseType.Text : ResponseType.JSON,
- } as any);
+ return tauriStreamFetch(url, options);
}
return window.fetch(url, options);
}
export function adapter(config: Record) {
- const { baseURL, url, params, ...rest } = config;
+ const { baseURL, url, params, data: body, ...rest } = config;
const path = baseURL ? `${baseURL}${url}` : url;
const fetchUrl = params
? `${path}?${new URLSearchParams(params as any).toString()}`
: path;
- return fetch(fetchUrl as string, { ...rest, responseType: "text" });
+ return fetch(fetchUrl as string, { ...rest, body }).then((res) => {
+ const { status, headers, statusText } = res;
+ return res
+ .text()
+ .then((data: string) => ({ status, statusText, headers, data }));
+ });
}
export function safeLocalStorage(): {
@@ -378,3 +433,49 @@ export function safeLocalStorage(): {
},
};
}
+
+export function getOperationId(operation: {
+ operationId?: string;
+ method: string;
+ path: string;
+}) {
+ // pattern '^[a-zA-Z0-9_-]+$'
+ return (
+ operation?.operationId ||
+ `${operation.method.toUpperCase()}${operation.path.replaceAll("/", "_")}`
+ );
+}
+
+export function clientUpdate() {
+ // this a wild for updating client app
+ return window.__TAURI__?.updater
+ .checkUpdate()
+ .then((updateResult) => {
+ if (updateResult.shouldUpdate) {
+ window.__TAURI__?.updater
+ .installUpdate()
+ .then((result) => {
+ showToast(Locale.Settings.Update.Success);
+ })
+ .catch((e) => {
+ console.error("[Install Update Error]", e);
+ showToast(Locale.Settings.Update.Failed);
+ });
+ }
+ })
+ .catch((e) => {
+ console.error("[Check Update Error]", e);
+ showToast(Locale.Settings.Update.Failed);
+ });
+}
+
+// https://gist.github.com/iwill/a83038623ba4fef6abb9efca87ae9ccb
+export function semverCompare(a: string, b: string) {
+ if (a.startsWith(b + "-")) return -1;
+ if (b.startsWith(a + "-")) return 1;
+ return a.localeCompare(b, undefined, {
+ numeric: true,
+ sensitivity: "case",
+ caseFirst: "upper",
+ });
+}
diff --git a/app/utils/audio.ts b/app/utils/audio.ts
new file mode 100644
index 00000000000..f6828c7aac4
--- /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 00000000000..4f354c5f020
--- /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/chat.ts b/app/utils/chat.ts
index 7f3bb23c58e..cae775512ad 100644
--- a/app/utils/chat.ts
+++ b/app/utils/chat.ts
@@ -3,13 +3,14 @@ import {
UPLOAD_URL,
REQUEST_TIMEOUT_MS,
} from "@/app/constant";
-import { RequestMessage } from "@/app/client/api";
+import { MultimodalContent, RequestMessage } from "@/app/client/api";
import Locale from "@/app/locales";
import {
EventStreamContentType,
fetchEventSource,
} from "@fortaine/fetch-event-source";
import { prettyObject } from "./format";
+import { fetch as tauriFetch } from "./stream";
export function compressImage(file: Blob, maxSize: number): Promise {
return new Promise((resolve, reject) => {
@@ -69,8 +70,9 @@ export function compressImage(file: Blob, maxSize: number): Promise {
});
}
-export async function preProcessImageContent(
+export async function preProcessImageContentBase(
content: RequestMessage["content"],
+ transformImageUrl: (url: string) => Promise<{ [key: string]: any }>,
) {
if (typeof content === "string") {
return content;
@@ -80,7 +82,7 @@ export async function preProcessImageContent(
if (part?.type == "image_url" && part?.image_url?.url) {
try {
const url = await cacheImageToBase64Image(part?.image_url?.url);
- result.push({ type: part.type, image_url: { url } });
+ result.push(await transformImageUrl(url));
} catch (error) {
console.error("Error processing image URL:", error);
}
@@ -91,6 +93,23 @@ export async function preProcessImageContent(
return result;
}
+export async function preProcessImageContent(
+ content: RequestMessage["content"],
+) {
+ return preProcessImageContentBase(content, async (url) => ({
+ type: "image_url",
+ image_url: { url },
+ })) as Promise;
+}
+
+export async function preProcessImageContentForAlibabaDashScope(
+ content: RequestMessage["content"],
+) {
+ return preProcessImageContentBase(content, async (url) => ({
+ image: url,
+ }));
+}
+
const imageCaches: Record = {};
export function cacheImageToBase64Image(imageUrl: string) {
if (imageUrl.includes(CACHE_URL_PREFIX)) {
@@ -137,7 +156,7 @@ export function uploadImage(file: Blob): Promise {
})
.then((res) => res.json())
.then((res) => {
- console.log("res", res);
+ // console.log("res", res);
if (res?.code == 0 && res?.data) {
return res?.data;
}
@@ -173,6 +192,7 @@ export function stream(
let finished = false;
let running = false;
let runTools: any[] = [];
+ let responseRes: Response;
// animate response to make it looks smooth
function animateResponseText() {
@@ -221,7 +241,12 @@ export function stream(
),
)
.then((res) => {
- const content = JSON.stringify(res.data);
+ let content = res.data || res?.statusText;
+ // hotfix #5614
+ content =
+ typeof content === "string"
+ ? content
+ : JSON.stringify(content);
if (res.status >= 300) {
return Promise.reject(content);
}
@@ -236,10 +261,15 @@ export function stream(
return content;
})
.catch((e) => {
- options?.onAfterTool?.({ ...tool, isError: true });
+ options?.onAfterTool?.({
+ ...tool,
+ isError: true,
+ errorMsg: e.toString(),
+ });
return e.toString();
})
.then((content) => ({
+ name: tool.function.name,
role: "tool",
content,
tool_call_id: tool.id,
@@ -261,7 +291,7 @@ export function stream(
}
console.debug("[ChatAPI] end");
finished = true;
- options.onFinish(responseText + remainText);
+ options.onFinish(responseText + remainText, responseRes); // 将res传递给onFinish
}
};
@@ -287,11 +317,13 @@ export function stream(
REQUEST_TIMEOUT_MS,
);
fetchEventSource(chatPath, {
+ fetch: tauriFetch as any,
...chatPayload,
async onopen(res) {
clearTimeout(requestTimeoutId);
const contentType = res.headers.get("content-type");
console.log("[Request] response content type: ", contentType);
+ responseRes = res;
if (contentType?.startsWith("text/plain")) {
responseText = await res.clone().text();
@@ -330,8 +362,12 @@ export function stream(
return finish();
}
const text = msg.data;
+ // Skip empty messages
+ if (!text || text.trim().length === 0) {
+ return;
+ }
try {
- const chunk = parseSSE(msg.data, runTools);
+ const chunk = parseSSE(text, runTools);
if (chunk) {
remainText += chunk;
}
@@ -352,3 +388,280 @@ export function stream(
console.debug("[ChatAPI] start");
chatApi(chatPath, headers, requestPayload, tools); // call fetchEventSource
}
+
+export function streamWithThink(
+ chatPath: string,
+ requestPayload: any,
+ headers: any,
+ tools: any[],
+ funcs: Record,
+ controller: AbortController,
+ parseSSE: (
+ text: string,
+ runTools: any[],
+ ) => {
+ isThinking: boolean;
+ content: string | undefined;
+ },
+ processToolMessage: (
+ requestPayload: any,
+ toolCallMessage: any,
+ toolCallResult: any[],
+ ) => void,
+ options: any,
+) {
+ let responseText = "";
+ let remainText = "";
+ let finished = false;
+ let running = false;
+ let runTools: any[] = [];
+ let responseRes: Response;
+ let isInThinkingMode = false;
+ let lastIsThinking = false;
+ let lastIsThinkingTagged = false; //between and tags
+
+ // animate response to make it looks smooth
+ function animateResponseText() {
+ if (finished || controller.signal.aborted) {
+ responseText += remainText;
+ console.log("[Response Animation] finished");
+ if (responseText?.length === 0) {
+ options.onError?.(new Error("empty response from server"));
+ }
+ return;
+ }
+
+ if (remainText.length > 0) {
+ const fetchCount = Math.max(1, Math.round(remainText.length / 60));
+ const fetchText = remainText.slice(0, fetchCount);
+ responseText += fetchText;
+ remainText = remainText.slice(fetchCount);
+ options.onUpdate?.(responseText, fetchText);
+ }
+
+ requestAnimationFrame(animateResponseText);
+ }
+
+ // start animaion
+ animateResponseText();
+
+ const finish = () => {
+ if (!finished) {
+ if (!running && runTools.length > 0) {
+ const toolCallMessage = {
+ role: "assistant",
+ tool_calls: [...runTools],
+ };
+ running = true;
+ runTools.splice(0, runTools.length); // empty runTools
+ return Promise.all(
+ toolCallMessage.tool_calls.map((tool) => {
+ options?.onBeforeTool?.(tool);
+ return Promise.resolve(
+ // @ts-ignore
+ funcs[tool.function.name](
+ // @ts-ignore
+ tool?.function?.arguments
+ ? JSON.parse(tool?.function?.arguments)
+ : {},
+ ),
+ )
+ .then((res) => {
+ let content = res.data || res?.statusText;
+ // hotfix #5614
+ content =
+ typeof content === "string"
+ ? content
+ : JSON.stringify(content);
+ if (res.status >= 300) {
+ return Promise.reject(content);
+ }
+ return content;
+ })
+ .then((content) => {
+ options?.onAfterTool?.({
+ ...tool,
+ content,
+ isError: false,
+ });
+ return content;
+ })
+ .catch((e) => {
+ options?.onAfterTool?.({
+ ...tool,
+ isError: true,
+ errorMsg: e.toString(),
+ });
+ return e.toString();
+ })
+ .then((content) => ({
+ name: tool.function.name,
+ role: "tool",
+ content,
+ tool_call_id: tool.id,
+ }));
+ }),
+ ).then((toolCallResult) => {
+ processToolMessage(requestPayload, toolCallMessage, toolCallResult);
+ setTimeout(() => {
+ // call again
+ console.debug("[ChatAPI] restart");
+ running = false;
+ chatApi(chatPath, headers, requestPayload, tools); // call fetchEventSource
+ }, 60);
+ });
+ return;
+ }
+ if (running) {
+ return;
+ }
+ console.debug("[ChatAPI] end");
+ finished = true;
+ options.onFinish(responseText + remainText, responseRes);
+ }
+ };
+
+ controller.signal.onabort = finish;
+
+ function chatApi(
+ chatPath: string,
+ headers: any,
+ requestPayload: any,
+ tools: any,
+ ) {
+ const chatPayload = {
+ method: "POST",
+ body: JSON.stringify({
+ ...requestPayload,
+ tools: tools && tools.length ? tools : undefined,
+ }),
+ signal: controller.signal,
+ headers,
+ };
+ const requestTimeoutId = setTimeout(
+ () => controller.abort(),
+ REQUEST_TIMEOUT_MS,
+ );
+ fetchEventSource(chatPath, {
+ fetch: tauriFetch as any,
+ ...chatPayload,
+ async onopen(res) {
+ clearTimeout(requestTimeoutId);
+ const contentType = res.headers.get("content-type");
+ console.log("[Request] response content type: ", contentType);
+ responseRes = res;
+
+ if (contentType?.startsWith("text/plain")) {
+ responseText = await res.clone().text();
+ return finish();
+ }
+
+ if (
+ !res.ok ||
+ !res.headers
+ .get("content-type")
+ ?.startsWith(EventStreamContentType) ||
+ res.status !== 200
+ ) {
+ const responseTexts = [responseText];
+ let extraInfo = await res.clone().text();
+ try {
+ const resJson = await res.clone().json();
+ extraInfo = prettyObject(resJson);
+ } catch {}
+
+ if (res.status === 401) {
+ responseTexts.push(Locale.Error.Unauthorized);
+ }
+
+ if (extraInfo) {
+ responseTexts.push(extraInfo);
+ }
+
+ responseText = responseTexts.join("\n\n");
+
+ return finish();
+ }
+ },
+ onmessage(msg) {
+ if (msg.data === "[DONE]" || finished) {
+ return finish();
+ }
+ const text = msg.data;
+ // Skip empty messages
+ if (!text || text.trim().length === 0) {
+ return;
+ }
+ try {
+ const chunk = parseSSE(text, runTools);
+ // Skip if content is empty
+ if (!chunk?.content || chunk.content.length === 0) {
+ return;
+ }
+
+ // deal with and tags start
+ if (!chunk.isThinking) {
+ if (chunk.content.startsWith("")) {
+ chunk.isThinking = true;
+ chunk.content = chunk.content.slice(7).trim();
+ lastIsThinkingTagged = true;
+ } else if (chunk.content.endsWith(" ")) {
+ chunk.isThinking = false;
+ chunk.content = chunk.content.slice(0, -8).trim();
+ lastIsThinkingTagged = false;
+ } else if (lastIsThinkingTagged) {
+ chunk.isThinking = true;
+ }
+ }
+ // deal with and tags start
+
+ // Check if thinking mode changed
+ const isThinkingChanged = lastIsThinking !== chunk.isThinking;
+ lastIsThinking = chunk.isThinking;
+
+ if (chunk.isThinking) {
+ // If in thinking mode
+ if (!isInThinkingMode || isThinkingChanged) {
+ // If this is a new thinking block or mode changed, add prefix
+ isInThinkingMode = true;
+ if (remainText.length > 0) {
+ remainText += "\n";
+ }
+ remainText += "> " + chunk.content;
+ } else {
+ // Handle newlines in thinking content
+ if (chunk.content.includes("\n\n")) {
+ const lines = chunk.content.split("\n\n");
+ remainText += lines.join("\n\n> ");
+ } else {
+ remainText += chunk.content;
+ }
+ }
+ } else {
+ // If in normal mode
+ if (isInThinkingMode || isThinkingChanged) {
+ // If switching from thinking mode to normal mode
+ isInThinkingMode = false;
+ remainText += "\n\n" + chunk.content;
+ } else {
+ remainText += chunk.content;
+ }
+ }
+ } catch (e) {
+ console.error("[Request] parse error", text, msg, e);
+ // Don't throw error for parse failures, just log them
+ }
+ },
+ onclose() {
+ finish();
+ },
+ onerror(e) {
+ options?.onError?.(e);
+ throw e;
+ },
+ openWhenHidden: true,
+ });
+ }
+ console.debug("[ChatAPI] start");
+ chatApi(chatPath, headers, requestPayload, tools); // call fetchEventSource
+}
diff --git a/app/utils/cors.ts b/app/utils/cors.ts
deleted file mode 100644
index fa348f9bf5d..00000000000
--- a/app/utils/cors.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { getClientConfig } from "../config/client";
-import { ApiPath, DEFAULT_API_HOST } from "../constant";
-
-export function corsPath(path: string) {
- const baseUrl = getClientConfig()?.isApp ? `${DEFAULT_API_HOST}` : "";
-
- if (baseUrl === "" && path === "") {
- return "";
- }
- if (!path.startsWith("/")) {
- path = "/" + path;
- }
-
- if (!path.endsWith("/")) {
- path += "/";
- }
-
- return `${baseUrl}${path}`;
-}
diff --git a/app/utils/model.ts b/app/utils/model.ts
index 0b62b53be09..f460babcd25 100644
--- a/app/utils/model.ts
+++ b/app/utils/model.ts
@@ -1,4 +1,4 @@
-import { DEFAULT_MODELS } from "../constant";
+import { DEFAULT_MODELS, ServiceProvider } from "../constant";
import { LLMModel } from "../client/api";
const CustomSeq = {
@@ -37,6 +37,17 @@ const sortModelTable = (models: ReturnType) =>
}
});
+/**
+ * get model name and provider from a formatted string,
+ * e.g. `gpt-4@OpenAi` or `claude-3-5-sonnet@20240620@Google`
+ * @param modelWithProvider model name with provider separated by last `@` char,
+ * @returns [model, provider] tuple, if no `@` char found, provider is undefined
+ */
+export function getModelProvider(modelWithProvider: string): [string, string?] {
+ const [model, provider] = modelWithProvider.split(/@(?!.*@)/);
+ return [model, provider];
+}
+
export function collectModelTable(
models: readonly LLMModel[],
customModels: string,
@@ -79,10 +90,10 @@ export function collectModelTable(
);
} else {
// 1. find model by name, and set available value
- const [customModelName, customProviderName] = name.split("@");
+ const [customModelName, customProviderName] = getModelProvider(name);
let count = 0;
for (const fullName in modelTable) {
- const [modelName, providerName] = fullName.split("@");
+ const [modelName, providerName] = getModelProvider(fullName);
if (
customModelName == modelName &&
(customProviderName === undefined ||
@@ -102,7 +113,7 @@ export function collectModelTable(
}
// 2. if model not exists, create new model with available value
if (count === 0) {
- let [customModelName, customProviderName] = name.split("@");
+ let [customModelName, customProviderName] = getModelProvider(name);
const provider = customProvider(
customProviderName || customModelName,
);
@@ -139,7 +150,7 @@ export function collectModelTableWithDefaultModel(
for (const key of Object.keys(modelTable)) {
if (
modelTable[key].available &&
- key.split("@").shift() == defaultModel
+ getModelProvider(key)[0] == defaultModel
) {
modelTable[key].isDefault = true;
break;
@@ -191,3 +202,57 @@ export function isModelAvailableInServer(
const modelTable = collectModelTable(DEFAULT_MODELS, customModels);
return modelTable[fullName]?.available === false;
}
+
+/**
+ * Check if the model name is a GPT-4 related model
+ *
+ * @param modelName The name of the model to check
+ * @returns True if the model is a GPT-4 related model (excluding gpt-4o-mini)
+ */
+export function isGPT4Model(modelName: string): boolean {
+ return (
+ (modelName.startsWith("gpt-4") ||
+ modelName.startsWith("chatgpt-4o") ||
+ modelName.startsWith("o1")) &&
+ !modelName.startsWith("gpt-4o-mini")
+ );
+}
+
+/**
+ * Checks if a model is not available on any of the specified providers in the server.
+ *
+ * @param {string} customModels - A string of custom models, comma-separated.
+ * @param {string} modelName - The name of the model to check.
+ * @param {string|string[]} providerNames - A string or array of provider names to check against.
+ *
+ * @returns {boolean} True if the model is not available on any of the specified providers, false otherwise.
+ */
+export function isModelNotavailableInServer(
+ customModels: string,
+ modelName: string,
+ providerNames: string | string[],
+): boolean {
+ // Check DISABLE_GPT4 environment variable
+ if (
+ process.env.DISABLE_GPT4 === "1" &&
+ isGPT4Model(modelName.toLowerCase())
+ ) {
+ return true;
+ }
+
+ const modelTable = collectModelTable(DEFAULT_MODELS, customModels);
+
+ const providerNamesArray = Array.isArray(providerNames)
+ ? providerNames
+ : [providerNames];
+ for (const providerName of providerNamesArray) {
+ // if model provider is bytedance, use model config name to check if not avaliable
+ if (providerName === ServiceProvider.ByteDance) {
+ return !Object.values(modelTable).filter((v) => v.name === modelName)?.[0]
+ ?.available;
+ }
+ const fullName = `${modelName}@${providerName.toLowerCase()}`;
+ if (modelTable?.[fullName]?.available === true) return false;
+ }
+ return true;
+}
diff --git a/app/utils/ms_edge_tts.ts b/app/utils/ms_edge_tts.ts
new file mode 100644
index 00000000000..f291ebada93
--- /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/app/utils/stream.ts b/app/utils/stream.ts
new file mode 100644
index 00000000000..f186730f6da
--- /dev/null
+++ b/app/utils/stream.ts
@@ -0,0 +1,108 @@
+// using tauri command to send request
+// see src-tauri/src/stream.rs, and src-tauri/src/main.rs
+// 1. invoke('stream_fetch', {url, method, headers, body}), get response with headers.
+// 2. listen event: `stream-response` multi times to get body
+
+type ResponseEvent = {
+ id: number;
+ payload: {
+ request_id: number;
+ status?: number;
+ chunk?: number[];
+ };
+};
+
+type StreamResponse = {
+ request_id: number;
+ status: number;
+ status_text: string;
+ headers: Record;
+};
+
+export function fetch(url: string, options?: RequestInit): Promise {
+ if (window.__TAURI__) {
+ const {
+ signal,
+ method = "GET",
+ headers: _headers = {},
+ body = [],
+ } = options || {};
+ let unlisten: Function | undefined;
+ let setRequestId: Function | undefined;
+ const requestIdPromise = new Promise((resolve) => (setRequestId = resolve));
+ const ts = new TransformStream();
+ const writer = ts.writable.getWriter();
+
+ let closed = false;
+ const close = () => {
+ if (closed) return;
+ closed = true;
+ unlisten && unlisten();
+ writer.ready.then(() => {
+ writer.close().catch((e) => console.error(e));
+ });
+ };
+
+ if (signal) {
+ signal.addEventListener("abort", () => close());
+ }
+ // @ts-ignore 2. listen response multi times, and write to Response.body
+ window.__TAURI__.event
+ .listen("stream-response", (e: ResponseEvent) =>
+ requestIdPromise.then((request_id) => {
+ const { request_id: rid, chunk, status } = e?.payload || {};
+ if (request_id != rid) {
+ return;
+ }
+ if (chunk) {
+ writer.ready.then(() => {
+ writer.write(new Uint8Array(chunk));
+ });
+ } else if (status === 0) {
+ // end of body
+ close();
+ }
+ }),
+ )
+ .then((u: Function) => (unlisten = u));
+
+ const headers: Record = {
+ Accept: "application/json, text/plain, */*",
+ "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
+ "User-Agent": navigator.userAgent,
+ };
+ for (const item of new Headers(_headers || {})) {
+ headers[item[0]] = item[1];
+ }
+ return window.__TAURI__
+ .invoke("stream_fetch", {
+ method: method.toUpperCase(),
+ url,
+ headers,
+ // TODO FormData
+ body:
+ typeof body === "string"
+ ? Array.from(new TextEncoder().encode(body))
+ : [],
+ })
+ .then((res: StreamResponse) => {
+ const { request_id, status, status_text: statusText, headers } = res;
+ setRequestId?.(request_id);
+ const response = new Response(ts.readable, {
+ status,
+ statusText,
+ headers,
+ });
+ if (status >= 300) {
+ setTimeout(close, 100);
+ }
+ return response;
+ })
+ .catch((e) => {
+ console.error("stream error", e);
+ // throw e;
+ return new Response("", { status: 599 });
+ });
+ }
+ return window.fetch(url, options);
+}
diff --git a/docs/bt-cn.md b/docs/bt-cn.md
new file mode 100644
index 00000000000..115fbbd7007
--- /dev/null
+++ b/docs/bt-cn.md
@@ -0,0 +1,29 @@
+# 宝塔面板 的部署说明
+
+## 拥有自己的宝塔
+当你需要通过 宝塔面板 部署本项目之前,需要在服务器上先安装好 宝塔面板工具。 接下来的 部署流程 都建立在已有宝塔面板的前提下。宝塔安装请参考 ([宝塔官网](https://www.bt.cn/new/download.html))
+
+> 注意:本项目需要宝塔面板版本 9.2.0 及以上
+
+## 一键安装
+
+1. 在 宝塔面板 -> Docker -> 应用商店 页面,搜索 ChatGPT-Next-Web 找到本项目的docker应用;
+2. 点击 安装 开始部署本项目
+
+
+1. 在项目配置页,根据要求开始配置环境变量;
+2. 如勾选 允许外部访问 配置,请注意为配置的 web端口 开放安全组端口访问权限;
+3. 请确保你添加了正确的 Open Api Key,否则无法使用;当配置 OpenAI官方 提供的key(国内无法访问),请配置代理地址;
+4. 建议配置 访问权限密码,否则部署后所有人均可使用已配置的 Open Api Key(当允许外部访问时);
+5. 点击 确认 开始自动部署。
+
+## 如何访问
+
+通过根据 服务器IP地址 和配置的 web端口 http://$(host):$(port),在浏览器中打开 ChatGPT-Next-Web。
+
+
+若配置了 访问权限密码,访问大模型前需要登录,请点击 登录,获取访问权限。
+
+
+
+
diff --git a/docs/cloudflare-pages-ko.md b/docs/cloudflare-pages-ko.md
index 3b489a729b6..07240218370 100644
--- a/docs/cloudflare-pages-ko.md
+++ b/docs/cloudflare-pages-ko.md
@@ -34,6 +34,6 @@
12. "저장 후 배포"를 클릭합니다.
13. 호환성 플래그를 입력해야 하므로 "배포 취소"를 클릭합니다.
14. "빌드 설정", "기능"으로 이동하여 "호환성 플래그"를 찾습니다.
-"프로덕션 호환성 플래그 구성" 및 "프리뷰 호환성 플래그 구성"에서 "nodejs_compat"를 입력합니다.
+15. "프로덕션 호환성 플래그 구성" 및 "프리뷰 호환성 플래그 구성"에서 "nodejs_compat"를 입력합니다.
16. "배포"로 이동하여 "배포 다시 시도"를 클릭합니다.
17. 즐기세요!
\ No newline at end of file
diff --git a/docs/images/bt/bt-install-1.jpeg b/docs/images/bt/bt-install-1.jpeg
new file mode 100644
index 00000000000..fff3406d656
Binary files /dev/null and b/docs/images/bt/bt-install-1.jpeg differ
diff --git a/docs/images/bt/bt-install-2.jpeg b/docs/images/bt/bt-install-2.jpeg
new file mode 100644
index 00000000000..77256ef8d36
Binary files /dev/null and b/docs/images/bt/bt-install-2.jpeg differ
diff --git a/docs/images/bt/bt-install-3.jpeg b/docs/images/bt/bt-install-3.jpeg
new file mode 100644
index 00000000000..7790f89e850
Binary files /dev/null and b/docs/images/bt/bt-install-3.jpeg differ
diff --git a/docs/images/bt/bt-install-4.jpeg b/docs/images/bt/bt-install-4.jpeg
new file mode 100644
index 00000000000..38d7caee4cb
Binary files /dev/null and b/docs/images/bt/bt-install-4.jpeg differ
diff --git a/docs/images/bt/bt-install-5.jpeg b/docs/images/bt/bt-install-5.jpeg
new file mode 100644
index 00000000000..aa1a7963cb9
Binary files /dev/null and b/docs/images/bt/bt-install-5.jpeg differ
diff --git a/docs/images/bt/bt-install-6.jpeg b/docs/images/bt/bt-install-6.jpeg
new file mode 100644
index 00000000000..42359e65ba6
Binary files /dev/null and b/docs/images/bt/bt-install-6.jpeg differ
diff --git a/docs/user-manual-cn.md b/docs/user-manual-cn.md
index 6109fcf57a4..4b0fd6e3229 100644
--- a/docs/user-manual-cn.md
+++ b/docs/user-manual-cn.md
@@ -82,7 +82,7 @@
同时为了让 ChatGPT 理解我们对话的上下文,往往会携带多条历史消息来提供上下文信息,而当对话进行一段时间之后,很容易就会触发长度限制。
-为了解决此问题,我们增加了历史记录压缩功能,假设阈值为 1000 字符,那么每次用户产生的聊天记录超过 1000 字符时,都会将没有被总结过的消息,发送给 ChatGPT,让其产生一个 100 字所有的摘要。
+为了解决此问题,我们增加了历史记录压缩功能,假设阈值为 1000 字符,那么每次用户产生的聊天记录超过 1000 字符时,都会将没有被总结过的消息,发送给 ChatGPT,让其产生一个 100 字左右的摘要。
这样,历史信息就从 1000 字压缩到了 100 字,这是一种有损压缩,但已能满足大多数使用场景。
diff --git a/docs/vercel-ko.md b/docs/vercel-ko.md
index 725a827dc9a..b6ba71f5813 100644
--- a/docs/vercel-ko.md
+++ b/docs/vercel-ko.md
@@ -9,7 +9,7 @@
3. 프로젝트를 선택합니다.

-1. Git 리포지토리 가져오기에서 chatgpt-next-web을 검색합니다. 2. 새 포크를 선택합니다;
+1. Git 리포지토리 가져오기에서 chatgpt-next-web을 검색합니다.
2. 새로 포크된 프로젝트를 선택하고 가져오기를 클릭합니다.

diff --git a/jest.config.ts b/jest.config.ts
new file mode 100644
index 00000000000..5f095dffb73
--- /dev/null
+++ b/jest.config.ts
@@ -0,0 +1,23 @@
+import type { Config } from "jest";
+import nextJest from "next/jest.js";
+
+const createJestConfig = nextJest({
+ // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
+ dir: "./",
+});
+
+// Add any custom config to be passed to Jest
+const config: Config = {
+ coverageProvider: "v8",
+ testEnvironment: "jsdom",
+ testMatch: ["**/*.test.js", "**/*.test.ts", "**/*.test.jsx", "**/*.test.tsx"],
+ setupFilesAfterEnv: ["/jest.setup.ts"],
+ moduleNameMapper: {
+ "^@/(.*)$": "/$1",
+ },
+ extensionsToTreatAsEsm: [".ts", ".tsx"],
+ injectGlobals: true,
+};
+
+// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
+export default createJestConfig(config);
diff --git a/jest.setup.ts b/jest.setup.ts
new file mode 100644
index 00000000000..9ba27fef27f
--- /dev/null
+++ b/jest.setup.ts
@@ -0,0 +1,22 @@
+// Learn more: https://github.com/testing-library/jest-dom
+import "@testing-library/jest-dom";
+import { jest } from "@jest/globals";
+
+global.fetch = jest.fn(() =>
+ Promise.resolve({
+ ok: true,
+ status: 200,
+ json: () => Promise.resolve([]),
+ headers: new Headers(),
+ redirected: false,
+ statusText: "OK",
+ type: "basic",
+ url: "",
+ body: null,
+ bodyUsed: false,
+ arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)),
+ blob: () => Promise.resolve(new Blob()),
+ formData: () => Promise.resolve(new FormData()),
+ text: () => Promise.resolve(""),
+ } as Response),
+);
diff --git a/next.config.mjs b/next.config.mjs
index 26dadca4c9e..0e1105d5647 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -71,8 +71,10 @@ if (mode !== "export") {
// },
{
// https://{resource_name}.openai.azure.com/openai/deployments/{deploy_name}/chat/completions
- source: "/api/proxy/azure/:resource_name/deployments/:deploy_name/:path*",
- destination: "https://:resource_name.openai.azure.com/openai/deployments/:deploy_name/:path*",
+ source:
+ "/api/proxy/azure/:resource_name/deployments/:deploy_name/:path*",
+ destination:
+ "https://:resource_name.openai.azure.com/openai/deployments/:deploy_name/:path*",
},
{
source: "/api/proxy/google/:path*",
@@ -94,6 +96,10 @@ if (mode !== "export") {
source: "/sharegpt",
destination: "https://sharegpt.com/api/conversations",
},
+ {
+ source: "/api/proxy/alibaba/:path*",
+ destination: "https://dashscope.aliyuncs.com/api/:path*",
+ },
];
return {
diff --git a/package.json b/package.json
index ca5fcc0f5df..ceb92d7fc35 100644
--- a/package.json
+++ b/package.json
@@ -13,24 +13,30 @@
"export:dev": "concurrently -r \"yarn mask:watch\" \"cross-env BUILD_MODE=export BUILD_APP=1 next dev\"",
"app:dev": "concurrently -r \"yarn mask:watch\" \"yarn tauri dev\"",
"app:build": "yarn mask && yarn tauri build",
+ "app:clear": "yarn tauri dev",
"prompts": "node ./scripts/fetch-prompts.mjs",
"prepare": "husky install",
- "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev"
+ "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev",
+ "test": "node --no-warnings --experimental-vm-modules $(yarn bin jest) --watch",
+ "test:ci": "node --no-warnings --experimental-vm-modules $(yarn bin jest) --ci"
},
"dependencies": {
"@fortaine/fetch-event-source": "^3.0.6",
"@hello-pangea/dnd": "^16.5.0",
+ "@modelcontextprotocol/sdk": "^1.0.4",
"@next/third-parties": "^14.1.0",
"@svgr/webpack": "^6.5.1",
"@vercel/analytics": "^0.1.11",
"@vercel/speed-insights": "^1.0.2",
"axios": "^1.7.5",
+ "clsx": "^2.1.1",
"emoji-picker-react": "^4.9.2",
"fuse.js": "^7.0.0",
"heic2any": "^0.0.4",
"html-to-image": "^1.11.11",
"idb-keyval": "^6.2.1",
"lodash-es": "^4.17.21",
+ "markdown-to-txt": "^2.0.1",
"mermaid": "^10.6.1",
"nanoid": "^5.0.3",
"next": "^14.1.1",
@@ -45,14 +51,20 @@
"remark-breaks": "^3.0.2",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
+ "rt-client": "https://github.com/Azure-Samples/aoai-realtime-audio-sdk/releases/download/js/v0.5.0/rt-client-0.5.0.tgz",
"sass": "^1.59.2",
"spark-md5": "^3.0.2",
"use-debounce": "^9.0.4",
+ "zod": "^3.24.1",
"zustand": "^4.3.8"
},
"devDependencies": {
- "@tauri-apps/api": "^1.6.0",
+ "@tauri-apps/api": "^2.1.1",
"@tauri-apps/cli": "1.5.11",
+ "@testing-library/dom": "^10.4.0",
+ "@testing-library/jest-dom": "^6.6.3",
+ "@testing-library/react": "^16.1.0",
+ "@types/jest": "^29.5.14",
"@types/js-yaml": "4.0.9",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.11.30",
@@ -66,9 +78,13 @@
"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",
+ "jest": "^29.7.0",
+ "jest-environment-jsdom": "^29.7.0",
"lint-staged": "^13.2.2",
"prettier": "^3.0.2",
+ "ts-node": "^10.9.2",
"tsx": "^4.16.0",
"typescript": "5.2.2",
"watch": "^1.0.2",
diff --git a/public/audio-processor.js b/public/audio-processor.js
new file mode 100644
index 00000000000..4fae6ea1a6f
--- /dev/null
+++ b/public/audio-processor.js
@@ -0,0 +1,48 @@
+// @ts-nocheck
+class AudioRecorderProcessor extends AudioWorkletProcessor {
+ constructor() {
+ super();
+ this.isRecording = false;
+ this.bufferSize = 2400; // 100ms at 24kHz
+ this.currentBuffer = [];
+
+ this.port.onmessage = (event) => {
+ if (event.data.command === "START_RECORDING") {
+ this.isRecording = true;
+ } else if (event.data.command === "STOP_RECORDING") {
+ this.isRecording = false;
+
+ if (this.currentBuffer.length > 0) {
+ this.sendBuffer();
+ }
+ }
+ };
+ }
+
+ sendBuffer() {
+ if (this.currentBuffer.length > 0) {
+ const audioData = new Float32Array(this.currentBuffer);
+ this.port.postMessage({
+ eventType: "audio",
+ audioData: audioData,
+ });
+ this.currentBuffer = [];
+ }
+ }
+
+ process(inputs) {
+ const input = inputs[0];
+ if (input.length > 0 && this.isRecording) {
+ const audioData = input[0];
+
+ this.currentBuffer.push(...audioData);
+
+ if (this.currentBuffer.length >= this.bufferSize) {
+ this.sendBuffer();
+ }
+ }
+ return true;
+ }
+}
+
+registerProcessor("audio-recorder-processor", AudioRecorderProcessor);
diff --git a/public/plugins.json b/public/plugins.json
new file mode 100644
index 00000000000..c4d7ec46a05
--- /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/public/prompts.json b/public/prompts.json
index 24ea3a0731d..39bb67df7f4 100644
--- a/public/prompts.json
+++ b/public/prompts.json
@@ -398,7 +398,7 @@
],
[
"担任高级前端开发人员",
- "我希望你担任高级前端开发人员。我将描述您将使用以下工具编写项目代码的项目详细信息:Create React App、yarn、Ant Design、List、Redux Toolkit、createSlice、thunk、axios。您应该将文件合并到单个 index.js 文件中,别无其他。不要写解释。我的第一个请求是“创建 Pokemon 应用程序,列出带有来自 PokeAPI 精灵端点的图像的宠物小精灵”\n"
+ "我希望你担任高级前端开发人员。我将描述您将使用以下工具编写项目代码的项目详细信息:Vite、yarn、Ant Design、List、Redux Toolkit、createSlice、thunk、axios。您应该将文件合并到单个 index.js 文件中,别无其他。不要写解释。我的第一个请求是“创建 Pokemon 应用程序,列出带有来自 PokeAPI 精灵端点的图像的宠物小精灵”\n"
],
[
"充当 Solr 搜索引擎",
@@ -884,7 +884,7 @@
],
[
"擔任資深前端開發人員",
- "我希望你擔任資深前端開發人員。我將介紹你將使用以下工具撰寫專案程式碼的專案細節:Create React App、yarn、Ant Design、List、Redux Toolkit、createSlice、thunk、axios。你應該將檔案整合到單一 index.js 檔案中,別無其他。不需撰寫解釋。我的第一個請求是「建立 Pokemon 應用程式,列出帶有來自 PokeAPI 精靈端點的圖片的寶可夢」\n"
+ "我希望你擔任資深前端開發人員。我將介紹你將使用以下工具撰寫專案程式碼的專案細節:Vite、yarn、Ant Design、List、Redux Toolkit、createSlice、thunk、axios。你應該將檔案整合到單一 index.js 檔案中,別無其他。不需撰寫解釋。我的第一個請求是「建立 Pokemon 應用程式,列出帶有來自 PokeAPI 精靈端點的圖片的寶可夢」\n"
],
[
"模擬 Solr 搜尋引擎",
@@ -1502,7 +1502,7 @@
],
[
"Senior Frontend Developer",
- "I want you to act as a Senior Frontend developer. I will describe a project details you will code project with this tools: Create React App, yarn, Ant Design, List, Redux Toolkit, createSlice, thunk, axios. You should merge files in single index.js file and nothing else. Do not write explanations. My first request is Create Pokemon App that lists pokemons with images that come from PokeAPI sprites endpoint"
+ "I want you to act as a Senior Frontend developer. I will describe a project details you will code project with this tools: Vite, yarn, Ant Design, List, Redux Toolkit, createSlice, thunk, axios. You should merge files in single index.js file and nothing else. Do not write explanations. My first request is Create Pokemon App that lists pokemons with images that come from PokeAPI sprites endpoint"
],
[
"Solr Search Engine",
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 47d12e1190b..c9baffc0acc 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -348,9 +348,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
-version = "1.4.0"
+version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
dependencies = [
"serde",
]
@@ -942,9 +942,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
-version = "1.1.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
@@ -970,9 +970,9 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-executor"
@@ -987,9 +987,9 @@ dependencies = [
[[package]]
name = "futures-io"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-lite"
@@ -1008,9 +1008,9 @@ dependencies = [
[[package]]
name = "futures-macro"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
@@ -1019,21 +1019,21 @@ dependencies = [
[[package]]
name = "futures-sink"
-version = "0.3.29"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-core",
"futures-io",
@@ -1555,9 +1555,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
-version = "0.3.0"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
@@ -1986,6 +1986,10 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
name = "nextchat"
version = "0.1.0"
dependencies = [
+ "bytes",
+ "futures-util",
+ "percent-encoding",
+ "reqwest",
"serde",
"serde_json",
"tauri",
@@ -2281,9 +2285,9 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]]
name = "percent-encoding"
-version = "2.2.0"
+version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "phf"
@@ -2545,9 +2549,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
-version = "1.0.58"
+version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
@@ -3889,9 +3893,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "url"
-version = "2.3.1"
+version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
+checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
dependencies = [
"form_urlencoded",
"idna",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 387584491ba..8a11c3b6f98 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -37,6 +37,10 @@ tauri = { version = "1.5.4", features = [ "http-all",
"window-unminimize",
] }
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
+percent-encoding = "2.3.1"
+reqwest = "0.11.18"
+futures-util = "0.3.30"
+bytes = "1.7.2"
[features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index ed3ec32f37b..d04969c043b 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -1,8 +1,11 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
+mod stream;
+
fn main() {
tauri::Builder::default()
+ .invoke_handler(tauri::generate_handler![stream::stream_fetch])
.plugin(tauri_plugin_window_state::Builder::default().build())
.run(tauri::generate_context!())
.expect("error while running tauri application");
diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs
new file mode 100644
index 00000000000..8320db3e48a
--- /dev/null
+++ b/src-tauri/src/stream.rs
@@ -0,0 +1,145 @@
+//
+//
+
+use std::time::Duration;
+use std::error::Error;
+use std::sync::atomic::{AtomicU32, Ordering};
+use std::collections::HashMap;
+use futures_util::{StreamExt};
+use reqwest::Client;
+use reqwest::header::{HeaderName, HeaderMap};
+
+static REQUEST_COUNTER: AtomicU32 = AtomicU32::new(0);
+
+#[derive(Debug, Clone, serde::Serialize)]
+pub struct StreamResponse {
+ request_id: u32,
+ status: u16,
+ status_text: String,
+ headers: HashMap
+}
+
+#[derive(Clone, serde::Serialize)]
+pub struct EndPayload {
+ request_id: u32,
+ status: u16,
+}
+
+#[derive(Clone, serde::Serialize)]
+pub struct ChunkPayload {
+ request_id: u32,
+ chunk: bytes::Bytes,
+}
+
+#[tauri::command]
+pub async fn stream_fetch(
+ window: tauri::Window,
+ method: String,
+ url: String,
+ headers: HashMap,
+ body: Vec,
+) -> Result {
+
+ let event_name = "stream-response";
+ let request_id = REQUEST_COUNTER.fetch_add(1, Ordering::SeqCst);
+
+ let mut _headers = HeaderMap::new();
+ for (key, value) in &headers {
+ _headers.insert(key.parse::().unwrap(), value.parse().unwrap());
+ }
+
+ // println!("method: {:?}", method);
+ // println!("url: {:?}", url);
+ // println!("headers: {:?}", headers);
+ // println!("headers: {:?}", _headers);
+
+ let method = method.parse::().map_err(|err| format!("failed to parse method: {}", err))?;
+ let client = Client::builder()
+ .default_headers(_headers)
+ .redirect(reqwest::redirect::Policy::limited(3))
+ .connect_timeout(Duration::new(3, 0))
+ .build()
+ .map_err(|err| format!("failed to generate client: {}", err))?;
+
+ let mut request = client.request(
+ method.clone(),
+ url.parse::().map_err(|err| format!("failed to parse url: {}", err))?
+ );
+
+ if method == reqwest::Method::POST || method == reqwest::Method::PUT || method == reqwest::Method::PATCH {
+ let body = bytes::Bytes::from(body);
+ // println!("body: {:?}", body);
+ request = request.body(body);
+ }
+
+ // println!("client: {:?}", client);
+ // println!("request: {:?}", request);
+
+ let response_future = request.send();
+
+ let res = response_future.await;
+ let response = match res {
+ Ok(res) => {
+ // get response and emit to client
+ let mut headers = HashMap::new();
+ for (name, value) in res.headers() {
+ headers.insert(
+ name.as_str().to_string(),
+ std::str::from_utf8(value.as_bytes()).unwrap().to_string()
+ );
+ }
+ let status = res.status().as_u16();
+
+ tauri::async_runtime::spawn(async move {
+ let mut stream = res.bytes_stream();
+
+ while let Some(chunk) = stream.next().await {
+ match chunk {
+ Ok(bytes) => {
+ // println!("chunk: {:?}", bytes);
+ if let Err(e) = window.emit(event_name, ChunkPayload{ request_id, chunk: bytes }) {
+ println!("Failed to emit chunk payload: {:?}", e);
+ }
+ }
+ Err(err) => {
+ println!("Error chunk: {:?}", err);
+ }
+ }
+ }
+ if let Err(e) = window.emit(event_name, EndPayload{ request_id, status: 0 }) {
+ println!("Failed to emit end payload: {:?}", e);
+ }
+ });
+
+ StreamResponse {
+ request_id,
+ status,
+ status_text: "OK".to_string(),
+ headers,
+ }
+ }
+ Err(err) => {
+ let error: String = err.source()
+ .map(|e| e.to_string())
+ .unwrap_or_else(|| "Unknown error occurred".to_string());
+ println!("Error response: {:?}", error);
+ tauri::async_runtime::spawn( async move {
+ if let Err(e) = window.emit(event_name, ChunkPayload{ request_id, chunk: error.into() }) {
+ println!("Failed to emit chunk payload: {:?}", e);
+ }
+ if let Err(e) = window.emit(event_name, EndPayload{ request_id, status: 0 }) {
+ println!("Failed to emit end payload: {:?}", e);
+ }
+ });
+ StreamResponse {
+ request_id,
+ status: 599,
+ status_text: "Error".to_string(),
+ headers: HashMap::new(),
+ }
+ }
+ };
+ // println!("Response: {:?}", response);
+ Ok(response)
+}
+
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 2a19c933205..bfa82b298df 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.16.1"
},
"tauri": {
"allowlist": {
@@ -99,7 +99,7 @@
"endpoints": [
"https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/releases/latest/download/latest.json"
],
- "dialog": false,
+ "dialog": true,
"windows": {
"installMode": "passive"
},
diff --git a/test/model-available.test.ts b/test/model-available.test.ts
new file mode 100644
index 00000000000..5c9fa9977d2
--- /dev/null
+++ b/test/model-available.test.ts
@@ -0,0 +1,80 @@
+import { isModelNotavailableInServer } from "../app/utils/model";
+
+describe("isModelNotavailableInServer", () => {
+ test("test model will return false, which means the model is available", () => {
+ const customModels = "";
+ const modelName = "gpt-4";
+ const providerNames = "OpenAI";
+ const result = isModelNotavailableInServer(
+ customModels,
+ modelName,
+ providerNames,
+ );
+ expect(result).toBe(false);
+ });
+
+ test("test model will return true when model is not available in custom models", () => {
+ const customModels = "-all,gpt-4o-mini";
+ const modelName = "gpt-4";
+ const providerNames = "OpenAI";
+ const result = isModelNotavailableInServer(
+ customModels,
+ modelName,
+ providerNames,
+ );
+ expect(result).toBe(true);
+ });
+
+ test("should respect DISABLE_GPT4 setting", () => {
+ process.env.DISABLE_GPT4 = "1";
+ const result = isModelNotavailableInServer("", "gpt-4", "OpenAI");
+ expect(result).toBe(true);
+ });
+
+ test("should handle empty provider names", () => {
+ const result = isModelNotavailableInServer("-all,gpt-4", "gpt-4", "");
+ expect(result).toBe(true);
+ });
+
+ test("should be case insensitive for model names", () => {
+ const result = isModelNotavailableInServer("-all,GPT-4", "gpt-4", "OpenAI");
+ expect(result).toBe(true);
+ });
+
+ test("support passing multiple providers, model unavailable on one of the providers will return true", () => {
+ const customModels = "-all,gpt-4@google";
+ const modelName = "gpt-4";
+ const providerNames = ["OpenAI", "Azure"];
+ const result = isModelNotavailableInServer(
+ customModels,
+ modelName,
+ providerNames,
+ );
+ expect(result).toBe(true);
+ });
+
+ // FIXME: 这个测试用例有问题,需要修复
+ // test("support passing multiple providers, model available on one of the providers will return false", () => {
+ // const customModels = "-all,gpt-4@google";
+ // const modelName = "gpt-4";
+ // const providerNames = ["OpenAI", "Google"];
+ // const result = isModelNotavailableInServer(
+ // customModels,
+ // modelName,
+ // providerNames,
+ // );
+ // expect(result).toBe(false);
+ // });
+
+ test("test custom model without setting provider", () => {
+ const customModels = "-all,mistral-large";
+ const modelName = "mistral-large";
+ const providerNames = modelName;
+ const result = isModelNotavailableInServer(
+ customModels,
+ modelName,
+ providerNames,
+ );
+ expect(result).toBe(false);
+ });
+});
diff --git a/test/model-provider.test.ts b/test/model-provider.test.ts
new file mode 100644
index 00000000000..41f14be026c
--- /dev/null
+++ b/test/model-provider.test.ts
@@ -0,0 +1,31 @@
+import { getModelProvider } from "../app/utils/model";
+
+describe("getModelProvider", () => {
+ test("should return model and provider when input contains '@'", () => {
+ const input = "model@provider";
+ const [model, provider] = getModelProvider(input);
+ expect(model).toBe("model");
+ expect(provider).toBe("provider");
+ });
+
+ test("should return model and undefined provider when input does not contain '@'", () => {
+ const input = "model";
+ const [model, provider] = getModelProvider(input);
+ expect(model).toBe("model");
+ expect(provider).toBeUndefined();
+ });
+
+ test("should handle multiple '@' characters correctly", () => {
+ const input = "model@provider@extra";
+ const [model, provider] = getModelProvider(input);
+ expect(model).toBe("model@provider");
+ expect(provider).toBe("extra");
+ });
+
+ test("should return empty strings when input is empty", () => {
+ const input = "";
+ const [model, provider] = getModelProvider(input);
+ expect(model).toBe("");
+ expect(provider).toBeUndefined();
+ });
+});
diff --git a/test/sum-module.test.ts b/test/sum-module.test.ts
new file mode 100644
index 00000000000..4773d19eb87
--- /dev/null
+++ b/test/sum-module.test.ts
@@ -0,0 +1,9 @@
+function sum(a: number, b: number) {
+ return a + b;
+}
+
+describe("sum module", () => {
+ test("adds 1 + 2 to equal 3", () => {
+ expect(sum(1, 2)).toBe(3);
+ });
+});
diff --git a/test/vision-model-checker.test.ts b/test/vision-model-checker.test.ts
new file mode 100644
index 00000000000..c25d92337d7
--- /dev/null
+++ b/test/vision-model-checker.test.ts
@@ -0,0 +1,68 @@
+import { jest } from "@jest/globals";
+import { isVisionModel } from "../app/utils";
+
+describe("isVisionModel", () => {
+ const originalEnv = process.env;
+
+ beforeEach(() => {
+ jest.resetModules();
+ process.env = { ...originalEnv };
+ });
+
+ afterEach(() => {
+ process.env = originalEnv;
+ });
+
+ test("should identify vision models using regex patterns", () => {
+ const visionModels = [
+ "gpt-4-vision",
+ "claude-3-opus",
+ "gemini-1.5-pro",
+ "gemini-2.0",
+ "gemini-exp-vision",
+ "learnlm-vision",
+ "qwen-vl-max",
+ "qwen2-vl-max",
+ "gpt-4-turbo",
+ "dall-e-3",
+ ];
+
+ visionModels.forEach((model) => {
+ expect(isVisionModel(model)).toBe(true);
+ });
+ });
+
+ test("should exclude specific models", () => {
+ expect(isVisionModel("claude-3-5-haiku-20241022")).toBe(false);
+ });
+
+ test("should not identify non-vision models", () => {
+ const nonVisionModels = [
+ "gpt-3.5-turbo",
+ "gpt-4-turbo-preview",
+ "claude-2",
+ "regular-model",
+ ];
+
+ nonVisionModels.forEach((model) => {
+ expect(isVisionModel(model)).toBe(false);
+ });
+ });
+
+ test("should identify models from VISION_MODELS env var", () => {
+ process.env.VISION_MODELS = "custom-vision-model,another-vision-model";
+
+ expect(isVisionModel("custom-vision-model")).toBe(true);
+ expect(isVisionModel("another-vision-model")).toBe(true);
+ expect(isVisionModel("unrelated-model")).toBe(false);
+ });
+
+ test("should handle empty or missing VISION_MODELS", () => {
+ process.env.VISION_MODELS = "";
+ expect(isVisionModel("unrelated-model")).toBe(false);
+
+ delete process.env.VISION_MODELS;
+ expect(isVisionModel("unrelated-model")).toBe(false);
+ expect(isVisionModel("gpt-4-vision")).toBe(true);
+ });
+});
diff --git a/tsconfig.json b/tsconfig.json
index c73eef3e876..6d24b42f1de 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,6 @@
{
"compilerOptions": {
- "target": "ES2015",
+ "target": "ES2022",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
@@ -23,6 +23,6 @@
"@/*": ["./*"]
}
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "app/calcTextareaHeight.ts"],
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
diff --git a/yarn.lock b/yarn.lock
index 4979e4d995e..af53517c48f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7,6 +7,11 @@
resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
+"@adobe/css-tools@^4.4.0":
+ version "4.4.0"
+ resolved "https://registry.npmmirror.com/@adobe/css-tools/-/css-tools-4.4.0.tgz#728c484f4e10df03d5a3acd0d8adcbbebff8ad63"
+ integrity sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==
+
"@ampproject/remapping@^2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
@@ -22,6 +27,23 @@
dependencies:
"@babel/highlight" "^7.18.6"
+"@babel/code-frame@^7.10.4":
+ version "7.26.0"
+ resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.26.0.tgz#9374b5cd068d128dac0b94ff482594273b1c2815"
+ integrity sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.25.9"
+ js-tokens "^4.0.0"
+ picocolors "^1.0.0"
+
+"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465"
+ integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==
+ dependencies:
+ "@babel/highlight" "^7.24.7"
+ picocolors "^1.0.0"
+
"@babel/code-frame@^7.22.13":
version "7.22.13"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e"
@@ -35,6 +57,32 @@
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298"
integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==
+"@babel/compat-data@^7.25.2":
+ version "7.25.2"
+ resolved "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.25.2.tgz#e41928bd33475305c586f6acbbb7e3ade7a6f7f5"
+ integrity sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==
+
+"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9":
+ version "7.25.2"
+ resolved "https://registry.npmmirror.com/@babel/core/-/core-7.25.2.tgz#ed8eec275118d7613e77a352894cd12ded8eba77"
+ integrity sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==
+ dependencies:
+ "@ampproject/remapping" "^2.2.0"
+ "@babel/code-frame" "^7.24.7"
+ "@babel/generator" "^7.25.0"
+ "@babel/helper-compilation-targets" "^7.25.2"
+ "@babel/helper-module-transforms" "^7.25.2"
+ "@babel/helpers" "^7.25.0"
+ "@babel/parser" "^7.25.0"
+ "@babel/template" "^7.25.0"
+ "@babel/traverse" "^7.25.2"
+ "@babel/types" "^7.25.2"
+ convert-source-map "^2.0.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.2"
+ json5 "^2.2.3"
+ semver "^6.3.1"
+
"@babel/core@^7.19.6":
version "7.21.3"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e"
@@ -76,6 +124,16 @@
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
+"@babel/generator@^7.25.0", "@babel/generator@^7.7.2":
+ version "7.25.0"
+ resolved "https://registry.npmmirror.com/@babel/generator/-/generator-7.25.0.tgz#f858ddfa984350bc3d3b7f125073c9af6988f18e"
+ integrity sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==
+ dependencies:
+ "@babel/types" "^7.25.0"
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.25"
+ jsesc "^2.5.1"
+
"@babel/helper-annotate-as-pure@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb"
@@ -102,6 +160,17 @@
lru-cache "^5.1.1"
semver "^6.3.0"
+"@babel/helper-compilation-targets@^7.25.2":
+ version "7.25.2"
+ resolved "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz#e1d9410a90974a3a5a66e84ff55ef62e3c02d06c"
+ integrity sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==
+ dependencies:
+ "@babel/compat-data" "^7.25.2"
+ "@babel/helper-validator-option" "^7.24.8"
+ browserslist "^4.23.1"
+ lru-cache "^5.1.1"
+ semver "^6.3.1"
+
"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0":
version "7.21.0"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9"
@@ -197,6 +266,14 @@
dependencies:
"@babel/types" "^7.18.6"
+"@babel/helper-module-imports@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz#f2f980392de5b84c3328fc71d38bd81bbb83042b"
+ integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==
+ dependencies:
+ "@babel/traverse" "^7.24.7"
+ "@babel/types" "^7.24.7"
+
"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.2":
version "7.21.2"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2"
@@ -211,6 +288,16 @@
"@babel/traverse" "^7.21.2"
"@babel/types" "^7.21.2"
+"@babel/helper-module-transforms@^7.25.2":
+ version "7.25.2"
+ resolved "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz#ee713c29768100f2776edf04d4eb23b8d27a66e6"
+ integrity sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==
+ dependencies:
+ "@babel/helper-module-imports" "^7.24.7"
+ "@babel/helper-simple-access" "^7.24.7"
+ "@babel/helper-validator-identifier" "^7.24.7"
+ "@babel/traverse" "^7.25.2"
+
"@babel/helper-optimise-call-expression@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe"
@@ -223,6 +310,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629"
integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==
+"@babel/helper-plugin-utils@^7.24.7":
+ version "7.24.8"
+ resolved "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz#94ee67e8ec0e5d44ea7baeb51e571bd26af07878"
+ integrity sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==
+
"@babel/helper-remap-async-to-generator@^7.18.9":
version "7.18.9"
resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519"
@@ -252,6 +344,14 @@
dependencies:
"@babel/types" "^7.20.2"
+"@babel/helper-simple-access@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.npmmirror.com/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz#bcade8da3aec8ed16b9c4953b74e506b51b5edb3"
+ integrity sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==
+ dependencies:
+ "@babel/traverse" "^7.24.7"
+ "@babel/types" "^7.24.7"
+
"@babel/helper-skip-transparent-expression-wrappers@^7.20.0":
version "7.20.0"
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684"
@@ -283,6 +383,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f"
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
+"@babel/helper-string-parser@^7.24.8":
+ version "7.24.8"
+ resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d"
+ integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==
+
"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1":
version "7.19.1"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
@@ -293,11 +398,26 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
+"@babel/helper-validator-identifier@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db"
+ integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==
+
+"@babel/helper-validator-identifier@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
+ integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
+
"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0":
version "7.21.0"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180"
integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==
+"@babel/helper-validator-option@^7.24.8":
+ version "7.24.8"
+ resolved "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz#3725cdeea8b480e86d34df15304806a06975e33d"
+ integrity sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==
+
"@babel/helper-wrap-function@^7.18.9":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3"
@@ -317,6 +437,14 @@
"@babel/traverse" "^7.21.0"
"@babel/types" "^7.21.0"
+"@babel/helpers@^7.25.0":
+ version "7.25.0"
+ resolved "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.25.0.tgz#e69beb7841cb93a6505531ede34f34e6a073650a"
+ integrity sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==
+ dependencies:
+ "@babel/template" "^7.25.0"
+ "@babel/types" "^7.25.0"
+
"@babel/highlight@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
@@ -335,6 +463,23 @@
chalk "^2.4.2"
js-tokens "^4.0.0"
+"@babel/highlight@^7.24.7":
+ version "7.24.7"
+ resolved "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d"
+ integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.24.7"
+ chalk "^2.4.2"
+ js-tokens "^4.0.0"
+ picocolors "^1.0.0"
+
+"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.23.9", "@babel/parser@^7.25.0", "@babel/parser@^7.25.3":
+ version "7.25.3"
+ resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.25.3.tgz#91fb126768d944966263f0657ab222a642b82065"
+ integrity sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==
+ dependencies:
+ "@babel/types" "^7.25.2"
+
"@babel/parser@^7.20.7", "@babel/parser@^7.21.3":
version "7.21.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3"
@@ -497,7 +642,14 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
-"@babel/plugin-syntax-class-properties@^7.12.13":
+"@babel/plugin-syntax-bigint@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.npmmirror.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea"
+ integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10"
integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==
@@ -532,6 +684,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.19.0"
+"@babel/plugin-syntax-import-meta@^7.8.3":
+ version "7.10.4"
+ resolved "https://registry.npmmirror.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51"
+ integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-syntax-json-strings@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
@@ -546,7 +705,14 @@
dependencies:
"@babel/helper-plugin-utils" "^7.18.6"
-"@babel/plugin-syntax-logical-assignment-operators@^7.10.4":
+"@babel/plugin-syntax-jsx@^7.7.2":
+ version "7.24.7"
+ resolved "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz#39a1fa4a7e3d3d7f34e2acc6be585b718d30e02d"
+ integrity sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==
@@ -560,7 +726,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
-"@babel/plugin-syntax-numeric-separator@^7.10.4":
+"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97"
integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==
@@ -595,7 +761,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.14.5"
-"@babel/plugin-syntax-top-level-await@^7.14.5":
+"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c"
integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==
@@ -609,6 +775,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.19.0"
+"@babel/plugin-syntax-typescript@^7.7.2":
+ version "7.24.7"
+ resolved "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz#58d458271b4d3b6bb27ee6ac9525acbb259bad1c"
+ integrity sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.24.7"
+
"@babel/plugin-transform-arrow-functions@^7.18.6":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551"
@@ -1028,14 +1201,7 @@
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
-"@babel/runtime@^7.12.1", "@babel/runtime@^7.20.7", "@babel/runtime@^7.23.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
- version "7.23.6"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.6.tgz#c05e610dc228855dc92ef1b53d07389ed8ab521d"
- integrity sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==
- dependencies:
- regenerator-runtime "^0.14.0"
-
-"@babel/runtime@^7.21.0":
+"@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.25.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.0.tgz#3af9a91c1b739c569d5d80cc917280919c544ecb"
integrity sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==
@@ -1060,6 +1226,15 @@
"@babel/parser" "^7.22.15"
"@babel/types" "^7.22.15"
+"@babel/template@^7.25.0", "@babel/template@^7.3.3":
+ version "7.25.0"
+ resolved "https://registry.npmmirror.com/@babel/template/-/template-7.25.0.tgz#e733dc3134b4fede528c15bc95e89cb98c52592a"
+ integrity sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==
+ dependencies:
+ "@babel/code-frame" "^7.24.7"
+ "@babel/parser" "^7.25.0"
+ "@babel/types" "^7.25.0"
+
"@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3":
version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
@@ -1076,6 +1251,28 @@
debug "^4.1.0"
globals "^11.1.0"
+"@babel/traverse@^7.24.7", "@babel/traverse@^7.25.2":
+ version "7.25.3"
+ resolved "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.25.3.tgz#f1b901951c83eda2f3e29450ce92743783373490"
+ integrity sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==
+ dependencies:
+ "@babel/code-frame" "^7.24.7"
+ "@babel/generator" "^7.25.0"
+ "@babel/parser" "^7.25.3"
+ "@babel/template" "^7.25.0"
+ "@babel/types" "^7.25.2"
+ debug "^4.3.1"
+ globals "^11.1.0"
+
+"@babel/types@^7.0.0", "@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.3.3":
+ version "7.25.2"
+ resolved "https://registry.npmmirror.com/@babel/types/-/types-7.25.2.tgz#55fb231f7dc958cd69ea141a4c2997e819646125"
+ integrity sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==
+ dependencies:
+ "@babel/helper-string-parser" "^7.24.8"
+ "@babel/helper-validator-identifier" "^7.24.7"
+ to-fast-properties "^2.0.0"
+
"@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.4.4":
version "7.21.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05"
@@ -1094,11 +1291,23 @@
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
+"@bcoe/v8-coverage@^0.2.3":
+ version "0.2.3"
+ resolved "https://registry.npmmirror.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
+ integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
+
"@braintree/sanitize-url@^6.0.1":
version "6.0.4"
resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz#923ca57e173c6b232bbbb07347b1be982f03e783"
integrity sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==
+"@cspotcode/source-map-support@^0.8.0":
+ version "0.8.1"
+ resolved "https://registry.npmmirror.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
+ integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
+ dependencies:
+ "@jridgewell/trace-mapping" "0.3.9"
+
"@esbuild/aix-ppc64@0.21.5":
version "0.21.5"
resolved "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
@@ -1283,6 +1492,214 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+"@istanbuljs/load-nyc-config@^1.0.0":
+ version "1.1.0"
+ resolved "https://registry.npmmirror.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
+ integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==
+ dependencies:
+ camelcase "^5.3.1"
+ find-up "^4.1.0"
+ get-package-type "^0.1.0"
+ js-yaml "^3.13.1"
+ resolve-from "^5.0.0"
+
+"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3":
+ version "0.1.3"
+ resolved "https://registry.npmmirror.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
+ integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
+
+"@jest/console@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc"
+ integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ jest-message-util "^29.7.0"
+ jest-util "^29.7.0"
+ slash "^3.0.0"
+
+"@jest/core@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f"
+ integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==
+ dependencies:
+ "@jest/console" "^29.7.0"
+ "@jest/reporters" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ ansi-escapes "^4.2.1"
+ chalk "^4.0.0"
+ ci-info "^3.2.0"
+ exit "^0.1.2"
+ graceful-fs "^4.2.9"
+ jest-changed-files "^29.7.0"
+ jest-config "^29.7.0"
+ jest-haste-map "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-regex-util "^29.6.3"
+ jest-resolve "^29.7.0"
+ jest-resolve-dependencies "^29.7.0"
+ jest-runner "^29.7.0"
+ jest-runtime "^29.7.0"
+ jest-snapshot "^29.7.0"
+ jest-util "^29.7.0"
+ jest-validate "^29.7.0"
+ jest-watcher "^29.7.0"
+ micromatch "^4.0.4"
+ pretty-format "^29.7.0"
+ slash "^3.0.0"
+ strip-ansi "^6.0.0"
+
+"@jest/environment@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7"
+ integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==
+ dependencies:
+ "@jest/fake-timers" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ jest-mock "^29.7.0"
+
+"@jest/expect-utils@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6"
+ integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==
+ dependencies:
+ jest-get-type "^29.6.3"
+
+"@jest/expect@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2"
+ integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==
+ dependencies:
+ expect "^29.7.0"
+ jest-snapshot "^29.7.0"
+
+"@jest/fake-timers@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565"
+ integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ "@sinonjs/fake-timers" "^10.0.2"
+ "@types/node" "*"
+ jest-message-util "^29.7.0"
+ jest-mock "^29.7.0"
+ jest-util "^29.7.0"
+
+"@jest/globals@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d"
+ integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==
+ dependencies:
+ "@jest/environment" "^29.7.0"
+ "@jest/expect" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ jest-mock "^29.7.0"
+
+"@jest/reporters@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7"
+ integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==
+ dependencies:
+ "@bcoe/v8-coverage" "^0.2.3"
+ "@jest/console" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@jridgewell/trace-mapping" "^0.3.18"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ collect-v8-coverage "^1.0.0"
+ exit "^0.1.2"
+ glob "^7.1.3"
+ graceful-fs "^4.2.9"
+ istanbul-lib-coverage "^3.0.0"
+ istanbul-lib-instrument "^6.0.0"
+ istanbul-lib-report "^3.0.0"
+ istanbul-lib-source-maps "^4.0.0"
+ istanbul-reports "^3.1.3"
+ jest-message-util "^29.7.0"
+ jest-util "^29.7.0"
+ jest-worker "^29.7.0"
+ slash "^3.0.0"
+ string-length "^4.0.1"
+ strip-ansi "^6.0.0"
+ v8-to-istanbul "^9.0.1"
+
+"@jest/schemas@^29.6.3":
+ version "29.6.3"
+ resolved "https://registry.npmmirror.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03"
+ integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==
+ dependencies:
+ "@sinclair/typebox" "^0.27.8"
+
+"@jest/source-map@^29.6.3":
+ version "29.6.3"
+ resolved "https://registry.npmmirror.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4"
+ integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==
+ dependencies:
+ "@jridgewell/trace-mapping" "^0.3.18"
+ callsites "^3.0.0"
+ graceful-fs "^4.2.9"
+
+"@jest/test-result@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c"
+ integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==
+ dependencies:
+ "@jest/console" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/istanbul-lib-coverage" "^2.0.0"
+ collect-v8-coverage "^1.0.0"
+
+"@jest/test-sequencer@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce"
+ integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==
+ dependencies:
+ "@jest/test-result" "^29.7.0"
+ graceful-fs "^4.2.9"
+ jest-haste-map "^29.7.0"
+ slash "^3.0.0"
+
+"@jest/transform@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c"
+ integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==
+ dependencies:
+ "@babel/core" "^7.11.6"
+ "@jest/types" "^29.6.3"
+ "@jridgewell/trace-mapping" "^0.3.18"
+ babel-plugin-istanbul "^6.1.1"
+ chalk "^4.0.0"
+ convert-source-map "^2.0.0"
+ fast-json-stable-stringify "^2.1.0"
+ graceful-fs "^4.2.9"
+ jest-haste-map "^29.7.0"
+ jest-regex-util "^29.6.3"
+ jest-util "^29.7.0"
+ micromatch "^4.0.4"
+ pirates "^4.0.4"
+ slash "^3.0.0"
+ write-file-atomic "^4.0.2"
+
+"@jest/types@^29.6.3":
+ version "29.6.3"
+ resolved "https://registry.npmmirror.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59"
+ integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==
+ dependencies:
+ "@jest/schemas" "^29.6.3"
+ "@types/istanbul-lib-coverage" "^2.0.0"
+ "@types/istanbul-reports" "^3.0.0"
+ "@types/node" "*"
+ "@types/yargs" "^17.0.8"
+ chalk "^4.0.0"
+
"@jridgewell/gen-mapping@^0.1.0":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
@@ -1309,16 +1726,35 @@
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
+"@jridgewell/gen-mapping@^0.3.5":
+ version "0.3.5"
+ resolved "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
+ integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
+ dependencies:
+ "@jridgewell/set-array" "^1.2.1"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
"@jridgewell/resolve-uri@3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0":
+ version "3.1.2"
+ resolved "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
+ integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
+
"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+"@jridgewell/set-array@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
+ integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
+
"@jridgewell/source-map@^0.3.3":
version "0.3.3"
resolved "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda"
@@ -1332,6 +1768,27 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
+"@jridgewell/sourcemap-codec@^1.4.14":
+ version "1.5.0"
+ resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
+ integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
+
+"@jridgewell/trace-mapping@0.3.9":
+ version "0.3.9"
+ resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
+ integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
+ version "0.3.25"
+ resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
+ integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.1.0"
+ "@jridgewell/sourcemap-codec" "^1.4.14"
+
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
version "0.3.17"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985"
@@ -1340,6 +1797,15 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
+"@modelcontextprotocol/sdk@^1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.0.4.tgz#34ad1edd3db7dd7154e782312dfb29d2d0c11d21"
+ integrity sha512-C+jw1lF6HSGzs7EZpzHbXfzz9rj9him4BaoumlTciW/IDDgIpweF/qiCWKlP02QKg5PPcgY6xY2WCt5y2tpYow==
+ dependencies:
+ content-type "^1.0.5"
+ raw-body "^3.0.0"
+ zod "^3.23.8"
+
"@next/env@14.1.1":
version "14.1.1"
resolved "https://registry.yarnpkg.com/@next/env/-/env-14.1.1.tgz#80150a8440eb0022a73ba353c6088d419b908bac"
@@ -1440,6 +1906,25 @@
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728"
integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==
+"@sinclair/typebox@^0.27.8":
+ version "0.27.8"
+ resolved "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
+ integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
+
+"@sinonjs/commons@^3.0.0":
+ version "3.0.1"
+ resolved "https://registry.npmmirror.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd"
+ integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==
+ dependencies:
+ type-detect "4.0.8"
+
+"@sinonjs/fake-timers@^10.0.2":
+ version "10.3.0"
+ resolved "https://registry.npmmirror.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66"
+ integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==
+ dependencies:
+ "@sinonjs/commons" "^3.0.0"
+
"@svgr/babel-plugin-add-jsx-attribute@^6.5.1":
version "6.5.1"
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz#74a5d648bd0347bda99d82409d87b8ca80b9a1ba"
@@ -1553,10 +2038,10 @@
dependencies:
tslib "^2.4.0"
-"@tauri-apps/api@^1.6.0":
- version "1.6.0"
- resolved "https://registry.npmjs.org/@tauri-apps/api/-/api-1.6.0.tgz#745b7e4e26782c3b2ad9510d558fa5bb2cf29186"
- integrity sha512-rqI++FWClU5I2UBp4HXFvl+sBWkdigBkxnpJDQUWttNyG7IZP4FwQGhTNL5EOw0vI8i6eSAJ5frLqO7n7jbJdg==
+"@tauri-apps/api@^2.1.1":
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.1.1.tgz#77d4ddb683d31072de4e6a47c8613d9db011652b"
+ integrity sha512-fzUfFFKo4lknXGJq8qrCidkUcKcH2UHhfaaCNt4GzgzGaW2iS26uFOg4tS3H4P8D6ZEeUxtiD5z0nwFF0UN30A==
"@tauri-apps/cli-darwin-arm64@1.5.11":
version "1.5.11"
@@ -1624,11 +2109,108 @@
"@tauri-apps/cli-win32-ia32-msvc" "1.5.11"
"@tauri-apps/cli-win32-x64-msvc" "1.5.11"
+"@testing-library/dom@^10.4.0":
+ version "10.4.0"
+ resolved "https://registry.npmmirror.com/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8"
+ integrity sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==
+ dependencies:
+ "@babel/code-frame" "^7.10.4"
+ "@babel/runtime" "^7.12.5"
+ "@types/aria-query" "^5.0.1"
+ aria-query "5.3.0"
+ chalk "^4.1.0"
+ dom-accessibility-api "^0.5.9"
+ lz-string "^1.5.0"
+ pretty-format "^27.0.2"
+
+"@testing-library/jest-dom@^6.6.3":
+ version "6.6.3"
+ resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz#26ba906cf928c0f8172e182c6fe214eb4f9f2bd2"
+ integrity sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==
+ dependencies:
+ "@adobe/css-tools" "^4.4.0"
+ aria-query "^5.0.0"
+ chalk "^3.0.0"
+ css.escape "^1.5.1"
+ dom-accessibility-api "^0.6.3"
+ lodash "^4.17.21"
+ redent "^3.0.0"
+
+"@testing-library/react@^16.1.0":
+ version "16.1.0"
+ resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.1.0.tgz#aa0c61398bac82eaf89776967e97de41ac742d71"
+ integrity sha512-Q2ToPvg0KsVL0ohND9A3zLJWcOXXcO8IDu3fj11KhNt0UlCWyFyvnCIBkd12tidB2lkiVRG8VFqdhcqhqnAQtg==
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+
+"@tootallnate/once@2":
+ version "2.0.0"
+ resolved "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
+ integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
+
"@trysound/sax@0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
+"@tsconfig/node10@^1.0.7":
+ version "1.0.11"
+ resolved "https://registry.npmmirror.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2"
+ integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==
+
+"@tsconfig/node12@^1.0.7":
+ version "1.0.11"
+ resolved "https://registry.npmmirror.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
+ integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
+
+"@tsconfig/node14@^1.0.0":
+ version "1.0.3"
+ resolved "https://registry.npmmirror.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
+ integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
+
+"@tsconfig/node16@^1.0.2":
+ version "1.0.4"
+ resolved "https://registry.npmmirror.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
+ integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
+
+"@types/aria-query@^5.0.1":
+ version "5.0.4"
+ resolved "https://registry.npmmirror.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708"
+ integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==
+
+"@types/babel__core@^7.1.14":
+ version "7.20.5"
+ resolved "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
+ integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==
+ dependencies:
+ "@babel/parser" "^7.20.7"
+ "@babel/types" "^7.20.7"
+ "@types/babel__generator" "*"
+ "@types/babel__template" "*"
+ "@types/babel__traverse" "*"
+
+"@types/babel__generator@*":
+ version "7.6.8"
+ resolved "https://registry.npmmirror.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab"
+ integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@types/babel__template@*":
+ version "7.4.4"
+ resolved "https://registry.npmmirror.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f"
+ integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==
+ dependencies:
+ "@babel/parser" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6":
+ version "7.20.6"
+ resolved "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7"
+ integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==
+ dependencies:
+ "@babel/types" "^7.20.7"
+
"@types/d3-scale-chromatic@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz#103124777e8cdec85b20b51fd3397c682ee1e954"
@@ -1674,6 +2256,13 @@
resolved "https://registry.npmmirror.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194"
integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==
+"@types/graceful-fs@^4.1.3":
+ version "4.1.9"
+ resolved "https://registry.npmmirror.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4"
+ integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==
+ dependencies:
+ "@types/node" "*"
+
"@types/hast@^2.0.0":
version "2.3.4"
resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc"
@@ -1689,11 +2278,47 @@
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
+"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
+ version "2.0.6"
+ resolved "https://registry.npmmirror.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7"
+ integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==
+
+"@types/istanbul-lib-report@*":
+ version "3.0.3"
+ resolved "https://registry.npmmirror.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf"
+ integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==
+ dependencies:
+ "@types/istanbul-lib-coverage" "*"
+
+"@types/istanbul-reports@^3.0.0":
+ version "3.0.4"
+ resolved "https://registry.npmmirror.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54"
+ integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==
+ dependencies:
+ "@types/istanbul-lib-report" "*"
+
+"@types/jest@^29.5.14":
+ version "29.5.14"
+ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5"
+ integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==
+ dependencies:
+ expect "^29.0.0"
+ pretty-format "^29.0.0"
+
"@types/js-yaml@4.0.9":
version "4.0.9"
resolved "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2"
integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==
+"@types/jsdom@^20.0.0":
+ version "20.0.1"
+ resolved "https://registry.npmmirror.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808"
+ integrity sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==
+ dependencies:
+ "@types/node" "*"
+ "@types/tough-cookie" "*"
+ parse5 "^7.0.0"
+
"@types/json-schema@*", "@types/json-schema@^7.0.8":
version "7.0.12"
resolved "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
@@ -1788,6 +2413,16 @@
resolved "https://registry.yarnpkg.com/@types/spark-md5/-/spark-md5-3.0.4.tgz#c1221d63c069d95aba0c06a765b80661cacc12bf"
integrity sha512-qtOaDz+IXiNndPgYb6t1YoutnGvFRtWSNzpVjkAPCfB2UzTyybuD4Tjgs7VgRawum3JnJNRwNQd4N//SvrHg1Q==
+"@types/stack-utils@^2.0.0":
+ version "2.0.3"
+ resolved "https://registry.npmmirror.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8"
+ integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==
+
+"@types/tough-cookie@*":
+ version "4.0.5"
+ resolved "https://registry.npmmirror.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304"
+ integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==
+
"@types/unist@*", "@types/unist@^2.0.0":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
@@ -1798,6 +2433,18 @@
resolved "https://registry.npmmirror.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43"
integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==
+"@types/yargs-parser@*":
+ version "21.0.3"
+ resolved "https://registry.npmmirror.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
+ integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==
+
+"@types/yargs@^17.0.8":
+ version "17.0.32"
+ resolved "https://registry.npmmirror.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229"
+ integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==
+ dependencies:
+ "@types/yargs-parser" "*"
+
"@typescript-eslint/parser@^5.4.2 || ^6.0.0":
version "6.4.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.4.0.tgz#47e7c6e22ff1248e8675d95f488890484de67600"
@@ -1984,6 +2631,19 @@
resolved "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
+abab@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.npmmirror.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
+ integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
+
+acorn-globals@^7.0.0:
+ version "7.0.1"
+ resolved "https://registry.npmmirror.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3"
+ integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==
+ dependencies:
+ acorn "^8.1.0"
+ acorn-walk "^8.0.2"
+
acorn-import-assertions@^1.9.0:
version "1.9.0"
resolved "https://registry.npmmirror.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac"
@@ -1994,6 +2654,18 @@ acorn-jsx@^5.3.2:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+acorn-walk@^8.0.2, acorn-walk@^8.1.1:
+ version "8.3.3"
+ resolved "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e"
+ integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==
+ dependencies:
+ acorn "^8.11.0"
+
+acorn@^8.1.0, acorn@^8.11.0, acorn@^8.4.1, acorn@^8.8.1:
+ version "8.12.1"
+ resolved "https://registry.npmmirror.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
+ integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
+
acorn@^8.7.1, acorn@^8.8.2:
version "8.9.0"
resolved "https://registry.npmmirror.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59"
@@ -2004,6 +2676,13 @@ acorn@^8.9.0:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5"
integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==
+agent-base@6:
+ version "6.0.2"
+ resolved "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
+ integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
+ dependencies:
+ debug "4"
+
aggregate-error@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
@@ -2027,7 +2706,7 @@ ajv@^6.12.4, ajv@^6.12.5:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
-ansi-escapes@^4.3.0:
+ansi-escapes@^4.2.1, ansi-escapes@^4.3.0:
version "4.3.2"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
@@ -2058,12 +2737,17 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"
+ansi-styles@^5.0.0:
+ version "5.2.0"
+ resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
+ integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
+
ansi-styles@^6.0.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
-anymatch@~3.1.2:
+anymatch@^3.0.3, anymatch@~3.1.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
@@ -2071,17 +2755,29 @@ anymatch@~3.1.2:
normalize-path "^3.0.0"
picomatch "^2.0.4"
+arg@^4.1.0:
+ version "4.1.3"
+ resolved "https://registry.npmmirror.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
+ integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
+
+argparse@^1.0.7:
+ version "1.0.10"
+ resolved "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+ dependencies:
+ sprintf-js "~1.0.2"
+
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
-aria-query@^5.1.3:
- version "5.1.3"
- resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e"
- integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==
+aria-query@5.3.0, aria-query@^5.0.0, aria-query@^5.1.3:
+ version "5.3.0"
+ resolved "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e"
+ integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==
dependencies:
- deep-equal "^2.0.5"
+ dequal "^2.0.3"
array-buffer-byte-length@^1.0.0:
version "1.0.0"
@@ -2179,6 +2875,40 @@ axobject-query@^3.1.1:
dependencies:
deep-equal "^2.0.5"
+babel-jest@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5"
+ integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==
+ dependencies:
+ "@jest/transform" "^29.7.0"
+ "@types/babel__core" "^7.1.14"
+ babel-plugin-istanbul "^6.1.1"
+ babel-preset-jest "^29.6.3"
+ chalk "^4.0.0"
+ graceful-fs "^4.2.9"
+ slash "^3.0.0"
+
+babel-plugin-istanbul@^6.1.1:
+ version "6.1.1"
+ resolved "https://registry.npmmirror.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73"
+ integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@istanbuljs/load-nyc-config" "^1.0.0"
+ "@istanbuljs/schema" "^0.1.2"
+ istanbul-lib-instrument "^5.0.4"
+ test-exclude "^6.0.0"
+
+babel-plugin-jest-hoist@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.npmmirror.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626"
+ integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==
+ dependencies:
+ "@babel/template" "^7.3.3"
+ "@babel/types" "^7.3.3"
+ "@types/babel__core" "^7.1.14"
+ "@types/babel__traverse" "^7.0.6"
+
babel-plugin-polyfill-corejs2@^0.3.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122"
@@ -2203,6 +2933,32 @@ babel-plugin-polyfill-regenerator@^0.4.1:
dependencies:
"@babel/helper-define-polyfill-provider" "^0.3.3"
+babel-preset-current-node-syntax@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.npmmirror.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b"
+ integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==
+ dependencies:
+ "@babel/plugin-syntax-async-generators" "^7.8.4"
+ "@babel/plugin-syntax-bigint" "^7.8.3"
+ "@babel/plugin-syntax-class-properties" "^7.8.3"
+ "@babel/plugin-syntax-import-meta" "^7.8.3"
+ "@babel/plugin-syntax-json-strings" "^7.8.3"
+ "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+ "@babel/plugin-syntax-numeric-separator" "^7.8.3"
+ "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+ "@babel/plugin-syntax-top-level-await" "^7.8.3"
+
+babel-preset-jest@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.npmmirror.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c"
+ integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==
+ dependencies:
+ babel-plugin-jest-hoist "^29.6.3"
+ babel-preset-current-node-syntax "^1.0.0"
+
bail@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d"
@@ -2263,6 +3019,23 @@ browserslist@^4.21.3, browserslist@^4.21.5:
node-releases "^2.0.8"
update-browserslist-db "^1.0.10"
+browserslist@^4.23.1:
+ version "4.23.3"
+ resolved "https://registry.npmmirror.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800"
+ integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==
+ dependencies:
+ caniuse-lite "^1.0.30001646"
+ electron-to-chromium "^1.5.4"
+ node-releases "^2.0.18"
+ update-browserslist-db "^1.1.0"
+
+bser@2.1.1:
+ version "2.1.1"
+ resolved "https://registry.npmmirror.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05"
+ integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==
+ dependencies:
+ node-int64 "^0.4.0"
+
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
@@ -2275,6 +3048,11 @@ busboy@1.6.0:
dependencies:
streamsearch "^1.1.0"
+bytes@3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
+ integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
+
call-bind@^1.0.0, call-bind@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
@@ -2288,15 +3066,20 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+camelcase@^5.3.1:
+ version "5.3.1"
+ resolved "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+ integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
camelcase@^6.2.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
-caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001503, caniuse-lite@^1.0.30001579:
- version "1.0.30001617"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz#809bc25f3f5027ceb33142a7d6c40759d7a901eb"
- integrity sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==
+caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001503, caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001646:
+ version "1.0.30001724"
+ resolved "https://mirrors.huaweicloud.com/repository/npm/caniuse-lite/-/caniuse-lite-1.0.30001724.tgz"
+ integrity sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==
ccount@^2.0.0:
version "2.0.1"
@@ -2317,7 +3100,15 @@ chalk@^2.0.0, chalk@^2.4.2:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
-chalk@^4.0.0, chalk@^4.1.2:
+chalk@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmmirror.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
+ integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -2325,6 +3116,11 @@ chalk@^4.0.0, chalk@^4.1.2:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
+char-regex@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.npmmirror.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
+ integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
+
character-entities@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22"
@@ -2350,6 +3146,16 @@ chrome-trace-event@^1.0.2:
resolved "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
+ci-info@^3.2.0:
+ version "3.9.0"
+ resolved "https://registry.npmmirror.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4"
+ integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==
+
+cjs-module-lexer@^1.0.0:
+ version "1.3.1"
+ resolved "https://registry.npmmirror.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c"
+ integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==
+
clean-stack@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
@@ -2392,7 +3198,22 @@ cliui@^8.0.1:
strip-ansi "^6.0.1"
wrap-ansi "^7.0.0"
-color-convert@^1.9.0:
+clsx@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
+ integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
+
+co@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.npmmirror.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+ integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==
+
+collect-v8-coverage@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.npmmirror.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9"
+ integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==
+
+color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
@@ -2473,11 +3294,21 @@ concurrently@^8.2.2:
tree-kill "^1.2.2"
yargs "^17.7.2"
+content-type@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
+ integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
+
convert-source-map@^1.7.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
+convert-source-map@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
+ integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
+
core-js-compat@^3.25.1:
version "3.29.1"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.1.tgz#15c0fb812ea27c973c18d425099afa50b934b41b"
@@ -2510,6 +3341,24 @@ cosmiconfig@^7.0.1:
path-type "^4.0.0"
yaml "^1.10.0"
+create-jest@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320"
+ integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ chalk "^4.0.0"
+ exit "^0.1.2"
+ graceful-fs "^4.2.9"
+ jest-config "^29.7.0"
+ jest-util "^29.7.0"
+ prompts "^2.0.1"
+
+create-require@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.npmmirror.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
+ integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
+
cross-env@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
@@ -2557,6 +3406,11 @@ css-what@^6.0.1:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
+css.escape@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.npmmirror.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
+ integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==
+
csso@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529"
@@ -2564,6 +3418,23 @@ csso@^4.2.0:
dependencies:
css-tree "^1.1.2"
+cssom@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.npmmirror.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36"
+ integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==
+
+cssom@~0.3.6:
+ version "0.3.8"
+ resolved "https://registry.npmmirror.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
+ integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
+
+cssstyle@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.npmmirror.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852"
+ integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==
+ dependencies:
+ cssom "~0.3.6"
+
csstype@^3.0.2:
version "3.1.1"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
@@ -2880,6 +3751,15 @@ data-uri-to-buffer@^4.0.0:
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e"
integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
+data-urls@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.npmmirror.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143"
+ integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==
+ dependencies:
+ abab "^2.0.6"
+ whatwg-mimetype "^3.0.0"
+ whatwg-url "^11.0.0"
+
date-fns@^2.30.0:
version "2.30.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0"
@@ -2892,6 +3772,13 @@ dayjs@^1.11.7:
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2"
integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==
+debug@4, debug@^4.3.1:
+ version "4.3.6"
+ resolved "https://registry.npmmirror.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"
+ integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==
+ dependencies:
+ ms "2.1.2"
+
debug@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
@@ -2906,6 +3793,11 @@ debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
dependencies:
ms "2.1.2"
+decimal.js@^10.4.2:
+ version "10.4.3"
+ resolved "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
+ integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
+
decode-named-character-reference@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e"
@@ -2913,6 +3805,11 @@ decode-named-character-reference@^1.0.0:
dependencies:
character-entities "^2.0.0"
+dedent@^1.0.0:
+ version "1.5.3"
+ resolved "https://registry.npmmirror.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a"
+ integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==
+
deep-equal@^2.0.5:
version "2.2.0"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.0.tgz#5caeace9c781028b9ff459f33b779346637c43e6"
@@ -2966,7 +3863,12 @@ delayed-stream@~1.0.0:
resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
-dequal@^2.0.0:
+depd@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
+ integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
+
+dequal@^2.0.0, dequal@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
@@ -2976,6 +3878,21 @@ dereference-json-schema@^0.2.1:
resolved "https://registry.npmjs.org/dereference-json-schema/-/dereference-json-schema-0.2.1.tgz#fcad3c98e0116f7124b0989d39d947fa318cae09"
integrity sha512-uzJsrg225owJyRQ8FNTPHIuBOdSzIZlHhss9u6W8mp7jJldHqGuLv9cULagP/E26QVJDnjtG8U7Dw139mM1ydA==
+detect-newline@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.npmmirror.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
+ integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
+
+diff-sequences@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.npmmirror.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921"
+ integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==
+
+diff@^4.0.1:
+ version "4.0.2"
+ resolved "https://registry.npmmirror.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
+ integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
+
diff@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40"
@@ -3002,6 +3919,16 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
+dom-accessibility-api@^0.5.9:
+ version "0.5.16"
+ resolved "https://registry.npmmirror.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453"
+ integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==
+
+dom-accessibility-api@^0.6.3:
+ version "0.6.3"
+ resolved "https://registry.npmmirror.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8"
+ integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==
+
dom-serializer@^1.0.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
@@ -3016,6 +3943,13 @@ domelementtype@^2.0.1, domelementtype@^2.2.0:
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+domexception@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.npmmirror.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673"
+ integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==
+ dependencies:
+ webidl-conversions "^7.0.0"
+
domhandler@^4.2.0, domhandler@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
@@ -3052,11 +3986,21 @@ electron-to-chromium@^1.4.431:
resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.445.tgz#058d2c5f3a2981ab1a37440f5a5e42d15672aa6d"
integrity sha512-++DB+9VK8SBJwC+X1zlMfJ1tMA3F0ipi39GdEp+x3cV2TyBihqAgad8cNMWtLDEkbH39nlDQP7PfGrDr3Dr7HA==
+electron-to-chromium@^1.5.4:
+ version "1.5.5"
+ resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.5.tgz#03bfdf422bdd2c05ee2657efedde21264a1a566b"
+ integrity sha512-QR7/A7ZkMS8tZuoftC/jfqNkZLQO779SSW3YuZHP4eXpj3EffGLFcB/Xu9AAZQzLccTiCV+EmUo3ha4mQ9wnlA==
+
elkjs@^0.8.2:
version "0.8.2"
resolved "https://registry.npmmirror.com/elkjs/-/elkjs-0.8.2.tgz#c37763c5a3e24e042e318455e0147c912a7c248e"
integrity sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==
+emittery@^0.13.1:
+ version "0.13.1"
+ resolved "https://registry.npmmirror.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"
+ integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==
+
emoji-picker-react@^4.9.2:
version "4.9.2"
resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-4.9.2.tgz#5118c5e1028ce4a96c94eb7c9bef09d30b08742c"
@@ -3226,11 +4170,21 @@ escalade@^3.1.1:
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+escalade@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.npmmirror.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
+ integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
+
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+escape-string-regexp@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
+ integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
+
escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
@@ -3241,6 +4195,17 @@ escape-string-regexp@^5.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
+escodegen@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.npmmirror.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17"
+ integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==
+ dependencies:
+ esprima "^4.0.1"
+ estraverse "^5.2.0"
+ esutils "^2.0.2"
+ optionalDependencies:
+ source-map "~0.6.1"
+
eslint-config-next@13.4.19:
version "13.4.19"
resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.4.19.tgz#f46be9d4bd9e52755f846338456132217081d7f8"
@@ -3367,6 +4332,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://mirrors.huaweicloud.com/repository/npm/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://mirrors.huaweicloud.com/repository/npm/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"
@@ -3440,6 +4417,11 @@ espree@^9.6.0, espree@^9.6.1:
acorn-jsx "^5.3.2"
eslint-visitor-keys "^3.4.1"
+esprima@^4.0.0, esprima@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
esquery@^1.4.2:
version "1.5.0"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
@@ -3481,6 +4463,21 @@ exec-sh@^0.2.0:
dependencies:
merge "^1.2.0"
+execa@^5.0.0:
+ version "5.1.1"
+ resolved "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
+ integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
+ dependencies:
+ cross-spawn "^7.0.3"
+ get-stream "^6.0.0"
+ human-signals "^2.1.0"
+ is-stream "^2.0.0"
+ merge-stream "^2.0.0"
+ npm-run-path "^4.0.1"
+ onetime "^5.1.2"
+ signal-exit "^3.0.3"
+ strip-final-newline "^2.0.0"
+
execa@^7.0.0:
version "7.1.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43"
@@ -3496,6 +4493,22 @@ execa@^7.0.0:
signal-exit "^3.0.7"
strip-final-newline "^3.0.0"
+exit@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.npmmirror.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
+ integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==
+
+expect@^29.0.0, expect@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc"
+ integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==
+ dependencies:
+ "@jest/expect-utils" "^29.7.0"
+ jest-get-type "^29.6.3"
+ jest-matcher-utils "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-util "^29.7.0"
+
extend@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
@@ -3522,7 +4535,7 @@ fast-glob@^3.2.11, fast-glob@^3.2.9:
merge2 "^1.3.0"
micromatch "^4.0.4"
-fast-json-stable-stringify@^2.0.0:
+fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
@@ -3546,6 +4559,13 @@ fault@^2.0.0:
dependencies:
format "^0.2.0"
+fb-watchman@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.npmmirror.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c"
+ integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==
+ dependencies:
+ bser "2.1.1"
+
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
version "3.2.0"
resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9"
@@ -3568,6 +4588,14 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
+find-up@^4.0.0, find-up@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+ integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+ dependencies:
+ locate-path "^5.0.0"
+ path-exists "^4.0.0"
+
find-up@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
@@ -3632,21 +4660,26 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+fsevents@^2.3.2, fsevents@~2.3.3:
+ version "2.3.3"
+ resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+ integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
fsevents@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
-fsevents@~2.3.3:
- version "2.3.3"
- resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
- integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
-
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+function-bind@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+ integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
function.prototype.name@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621"
@@ -3686,7 +4719,12 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@
has "^1.0.3"
has-symbols "^1.0.3"
-get-stream@^6.0.1:
+get-package-type@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.npmmirror.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
+ integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
+
+get-stream@^6.0.0, get-stream@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
@@ -3742,7 +4780,7 @@ glob@7.1.7:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@^7.1.3:
+glob@^7.1.3, glob@^7.1.4:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -3859,6 +4897,13 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
+hasown@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
+ integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
+ dependencies:
+ function-bind "^1.1.2"
+
hast-util-from-dom@^4.0.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/hast-util-from-dom/-/hast-util-from-dom-4.2.0.tgz#25836ddecc3cc0849d32749c2a7aec03e94b59a7"
@@ -3964,11 +5009,56 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
dependencies:
react-is "^16.7.0"
+html-encoding-sniffer@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
+ integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==
+ dependencies:
+ whatwg-encoding "^2.0.0"
+
+html-escaper@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.npmmirror.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
+ integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
+
html-to-image@^1.11.11:
version "1.11.11"
resolved "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.11.tgz#c0f8a34dc9e4b97b93ff7ea286eb8562642ebbea"
integrity sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==
+http-errors@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
+ integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
+ dependencies:
+ depd "2.0.0"
+ inherits "2.0.4"
+ setprototypeof "1.2.0"
+ statuses "2.0.1"
+ toidentifier "1.0.1"
+
+http-proxy-agent@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
+ integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
+ dependencies:
+ "@tootallnate/once" "2"
+ agent-base "6"
+ debug "4"
+
+https-proxy-agent@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
+ integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
+ dependencies:
+ agent-base "6"
+ debug "4"
+
+human-signals@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
+ integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
+
human-signals@^4.3.0:
version "4.3.1"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2"
@@ -3979,7 +5069,7 @@ husky@^8.0.0:
resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184"
integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==
-iconv-lite@0.6:
+iconv-lite@0.6, iconv-lite@0.6.3:
version "0.6.3"
resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
@@ -4009,6 +5099,14 @@ import-fresh@^3.2.1:
parent-module "^1.0.0"
resolve-from "^4.0.0"
+import-local@^3.0.2:
+ version "3.2.0"
+ resolved "https://registry.npmmirror.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260"
+ integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==
+ dependencies:
+ pkg-dir "^4.2.0"
+ resolve-cwd "^3.0.0"
+
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
@@ -4027,7 +5125,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@2:
+inherits@2, inherits@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -4117,6 +5215,13 @@ is-core-module@^2.11.0, is-core-module@^2.9.0:
dependencies:
has "^1.0.3"
+is-core-module@^2.13.0:
+ version "2.15.0"
+ resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.15.0.tgz#71c72ec5442ace7e76b306e9d48db361f22699ea"
+ integrity sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==
+ dependencies:
+ hasown "^2.0.2"
+
is-date-object@^1.0.1, is-date-object@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
@@ -4139,6 +5244,11 @@ is-fullwidth-code-point@^4.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88"
integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==
+is-generator-fn@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.npmmirror.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
+ integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
+
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
@@ -4178,6 +5288,11 @@ is-plain-obj@^4.0.0:
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0"
integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
+is-potential-custom-element-name@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
+ integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
+
is-regex@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
@@ -4198,6 +5313,11 @@ is-shared-array-buffer@^1.0.2:
dependencies:
call-bind "^1.0.2"
+is-stream@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
+ integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
+
is-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
@@ -4258,6 +5378,411 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0:
+ version "3.2.2"
+ resolved "https://registry.npmmirror.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756"
+ integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==
+
+istanbul-lib-instrument@^5.0.4:
+ version "5.2.1"
+ resolved "https://registry.npmmirror.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d"
+ integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==
+ dependencies:
+ "@babel/core" "^7.12.3"
+ "@babel/parser" "^7.14.7"
+ "@istanbuljs/schema" "^0.1.2"
+ istanbul-lib-coverage "^3.2.0"
+ semver "^6.3.0"
+
+istanbul-lib-instrument@^6.0.0:
+ version "6.0.3"
+ resolved "https://registry.npmmirror.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765"
+ integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==
+ dependencies:
+ "@babel/core" "^7.23.9"
+ "@babel/parser" "^7.23.9"
+ "@istanbuljs/schema" "^0.1.3"
+ istanbul-lib-coverage "^3.2.0"
+ semver "^7.5.4"
+
+istanbul-lib-report@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.npmmirror.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d"
+ integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==
+ dependencies:
+ istanbul-lib-coverage "^3.0.0"
+ make-dir "^4.0.0"
+ supports-color "^7.1.0"
+
+istanbul-lib-source-maps@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.npmmirror.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551"
+ integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==
+ dependencies:
+ debug "^4.1.1"
+ istanbul-lib-coverage "^3.0.0"
+ source-map "^0.6.1"
+
+istanbul-reports@^3.1.3:
+ version "3.1.7"
+ resolved "https://registry.npmmirror.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b"
+ integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==
+ dependencies:
+ html-escaper "^2.0.0"
+ istanbul-lib-report "^3.0.0"
+
+jest-changed-files@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a"
+ integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==
+ dependencies:
+ execa "^5.0.0"
+ jest-util "^29.7.0"
+ p-limit "^3.1.0"
+
+jest-circus@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a"
+ integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==
+ dependencies:
+ "@jest/environment" "^29.7.0"
+ "@jest/expect" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ co "^4.6.0"
+ dedent "^1.0.0"
+ is-generator-fn "^2.0.0"
+ jest-each "^29.7.0"
+ jest-matcher-utils "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-runtime "^29.7.0"
+ jest-snapshot "^29.7.0"
+ jest-util "^29.7.0"
+ p-limit "^3.1.0"
+ pretty-format "^29.7.0"
+ pure-rand "^6.0.0"
+ slash "^3.0.0"
+ stack-utils "^2.0.3"
+
+jest-cli@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995"
+ integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==
+ dependencies:
+ "@jest/core" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ chalk "^4.0.0"
+ create-jest "^29.7.0"
+ exit "^0.1.2"
+ import-local "^3.0.2"
+ jest-config "^29.7.0"
+ jest-util "^29.7.0"
+ jest-validate "^29.7.0"
+ yargs "^17.3.1"
+
+jest-config@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f"
+ integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==
+ dependencies:
+ "@babel/core" "^7.11.6"
+ "@jest/test-sequencer" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ babel-jest "^29.7.0"
+ chalk "^4.0.0"
+ ci-info "^3.2.0"
+ deepmerge "^4.2.2"
+ glob "^7.1.3"
+ graceful-fs "^4.2.9"
+ jest-circus "^29.7.0"
+ jest-environment-node "^29.7.0"
+ jest-get-type "^29.6.3"
+ jest-regex-util "^29.6.3"
+ jest-resolve "^29.7.0"
+ jest-runner "^29.7.0"
+ jest-util "^29.7.0"
+ jest-validate "^29.7.0"
+ micromatch "^4.0.4"
+ parse-json "^5.2.0"
+ pretty-format "^29.7.0"
+ slash "^3.0.0"
+ strip-json-comments "^3.1.1"
+
+jest-diff@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a"
+ integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==
+ dependencies:
+ chalk "^4.0.0"
+ diff-sequences "^29.6.3"
+ jest-get-type "^29.6.3"
+ pretty-format "^29.7.0"
+
+jest-docblock@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a"
+ integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==
+ dependencies:
+ detect-newline "^3.0.0"
+
+jest-each@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1"
+ integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ chalk "^4.0.0"
+ jest-get-type "^29.6.3"
+ jest-util "^29.7.0"
+ pretty-format "^29.7.0"
+
+jest-environment-jsdom@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz#d206fa3551933c3fd519e5dfdb58a0f5139a837f"
+ integrity sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==
+ dependencies:
+ "@jest/environment" "^29.7.0"
+ "@jest/fake-timers" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/jsdom" "^20.0.0"
+ "@types/node" "*"
+ jest-mock "^29.7.0"
+ jest-util "^29.7.0"
+ jsdom "^20.0.0"
+
+jest-environment-node@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376"
+ integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==
+ dependencies:
+ "@jest/environment" "^29.7.0"
+ "@jest/fake-timers" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ jest-mock "^29.7.0"
+ jest-util "^29.7.0"
+
+jest-get-type@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.npmmirror.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1"
+ integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==
+
+jest-haste-map@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104"
+ integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ "@types/graceful-fs" "^4.1.3"
+ "@types/node" "*"
+ anymatch "^3.0.3"
+ fb-watchman "^2.0.0"
+ graceful-fs "^4.2.9"
+ jest-regex-util "^29.6.3"
+ jest-util "^29.7.0"
+ jest-worker "^29.7.0"
+ micromatch "^4.0.4"
+ walker "^1.0.8"
+ optionalDependencies:
+ fsevents "^2.3.2"
+
+jest-leak-detector@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728"
+ integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==
+ dependencies:
+ jest-get-type "^29.6.3"
+ pretty-format "^29.7.0"
+
+jest-matcher-utils@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12"
+ integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==
+ dependencies:
+ chalk "^4.0.0"
+ jest-diff "^29.7.0"
+ jest-get-type "^29.6.3"
+ pretty-format "^29.7.0"
+
+jest-message-util@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3"
+ integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==
+ dependencies:
+ "@babel/code-frame" "^7.12.13"
+ "@jest/types" "^29.6.3"
+ "@types/stack-utils" "^2.0.0"
+ chalk "^4.0.0"
+ graceful-fs "^4.2.9"
+ micromatch "^4.0.4"
+ pretty-format "^29.7.0"
+ slash "^3.0.0"
+ stack-utils "^2.0.3"
+
+jest-mock@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347"
+ integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ jest-util "^29.7.0"
+
+jest-pnp-resolver@^1.2.2:
+ version "1.2.3"
+ resolved "https://registry.npmmirror.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e"
+ integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==
+
+jest-regex-util@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.npmmirror.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52"
+ integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==
+
+jest-resolve-dependencies@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428"
+ integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==
+ dependencies:
+ jest-regex-util "^29.6.3"
+ jest-snapshot "^29.7.0"
+
+jest-resolve@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30"
+ integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==
+ dependencies:
+ chalk "^4.0.0"
+ graceful-fs "^4.2.9"
+ jest-haste-map "^29.7.0"
+ jest-pnp-resolver "^1.2.2"
+ jest-util "^29.7.0"
+ jest-validate "^29.7.0"
+ resolve "^1.20.0"
+ resolve.exports "^2.0.0"
+ slash "^3.0.0"
+
+jest-runner@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e"
+ integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==
+ dependencies:
+ "@jest/console" "^29.7.0"
+ "@jest/environment" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ emittery "^0.13.1"
+ graceful-fs "^4.2.9"
+ jest-docblock "^29.7.0"
+ jest-environment-node "^29.7.0"
+ jest-haste-map "^29.7.0"
+ jest-leak-detector "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-resolve "^29.7.0"
+ jest-runtime "^29.7.0"
+ jest-util "^29.7.0"
+ jest-watcher "^29.7.0"
+ jest-worker "^29.7.0"
+ p-limit "^3.1.0"
+ source-map-support "0.5.13"
+
+jest-runtime@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817"
+ integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==
+ dependencies:
+ "@jest/environment" "^29.7.0"
+ "@jest/fake-timers" "^29.7.0"
+ "@jest/globals" "^29.7.0"
+ "@jest/source-map" "^29.6.3"
+ "@jest/test-result" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ cjs-module-lexer "^1.0.0"
+ collect-v8-coverage "^1.0.0"
+ glob "^7.1.3"
+ graceful-fs "^4.2.9"
+ jest-haste-map "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-mock "^29.7.0"
+ jest-regex-util "^29.6.3"
+ jest-resolve "^29.7.0"
+ jest-snapshot "^29.7.0"
+ jest-util "^29.7.0"
+ slash "^3.0.0"
+ strip-bom "^4.0.0"
+
+jest-snapshot@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5"
+ integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==
+ dependencies:
+ "@babel/core" "^7.11.6"
+ "@babel/generator" "^7.7.2"
+ "@babel/plugin-syntax-jsx" "^7.7.2"
+ "@babel/plugin-syntax-typescript" "^7.7.2"
+ "@babel/types" "^7.3.3"
+ "@jest/expect-utils" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ babel-preset-current-node-syntax "^1.0.0"
+ chalk "^4.0.0"
+ expect "^29.7.0"
+ graceful-fs "^4.2.9"
+ jest-diff "^29.7.0"
+ jest-get-type "^29.6.3"
+ jest-matcher-utils "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-util "^29.7.0"
+ natural-compare "^1.4.0"
+ pretty-format "^29.7.0"
+ semver "^7.5.3"
+
+jest-util@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc"
+ integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ ci-info "^3.2.0"
+ graceful-fs "^4.2.9"
+ picomatch "^2.2.3"
+
+jest-validate@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c"
+ integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==
+ dependencies:
+ "@jest/types" "^29.6.3"
+ camelcase "^6.2.0"
+ chalk "^4.0.0"
+ jest-get-type "^29.6.3"
+ leven "^3.1.0"
+ pretty-format "^29.7.0"
+
+jest-watcher@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2"
+ integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==
+ dependencies:
+ "@jest/test-result" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
+ ansi-escapes "^4.2.1"
+ chalk "^4.0.0"
+ emittery "^0.13.1"
+ jest-util "^29.7.0"
+ string-length "^4.0.1"
+
jest-worker@^27.4.5:
version "27.5.1"
resolved "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0"
@@ -4267,11 +5792,39 @@ jest-worker@^27.4.5:
merge-stream "^2.0.0"
supports-color "^8.0.0"
+jest-worker@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a"
+ integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==
+ dependencies:
+ "@types/node" "*"
+ jest-util "^29.7.0"
+ merge-stream "^2.0.0"
+ supports-color "^8.0.0"
+
+jest@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613"
+ integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==
+ dependencies:
+ "@jest/core" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ import-local "^3.0.2"
+ jest-cli "^29.7.0"
+
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+js-yaml@^3.13.1:
+ version "3.14.1"
+ resolved "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
+ integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^4.0.0"
+
js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@@ -4279,6 +5832,38 @@ js-yaml@^4.1.0:
dependencies:
argparse "^2.0.1"
+jsdom@^20.0.0:
+ version "20.0.3"
+ resolved "https://registry.npmmirror.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db"
+ integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==
+ dependencies:
+ abab "^2.0.6"
+ acorn "^8.8.1"
+ acorn-globals "^7.0.0"
+ cssom "^0.5.0"
+ cssstyle "^2.3.0"
+ data-urls "^3.0.2"
+ decimal.js "^10.4.2"
+ domexception "^4.0.0"
+ escodegen "^2.0.0"
+ form-data "^4.0.0"
+ html-encoding-sniffer "^3.0.0"
+ http-proxy-agent "^5.0.0"
+ https-proxy-agent "^5.0.1"
+ is-potential-custom-element-name "^1.0.1"
+ nwsapi "^2.2.2"
+ parse5 "^7.1.1"
+ saxes "^6.0.0"
+ symbol-tree "^3.2.4"
+ tough-cookie "^4.1.2"
+ w3c-xmlserializer "^4.0.0"
+ webidl-conversions "^7.0.0"
+ whatwg-encoding "^2.0.0"
+ whatwg-mimetype "^3.0.0"
+ whatwg-url "^11.0.0"
+ ws "^8.11.0"
+ xml-name-validator "^4.0.0"
+
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
@@ -4311,7 +5896,7 @@ json5@^1.0.2:
dependencies:
minimist "^1.2.0"
-json5@^2.2.2:
+json5@^2.2.2, json5@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
@@ -4343,6 +5928,11 @@ khroma@^2.0.0:
resolved "https://registry.npmmirror.com/khroma/-/khroma-2.0.0.tgz#7577de98aed9f36c7a474c4d453d94c0d6c6588b"
integrity sha512-2J8rDNlQWbtiNYThZRvmMv5yt44ZakX+Tz5ZIp/mN1pt4snn+m030Va5Z4v8xA0cQFDXBwO/8i42xL4QPsVk3g==
+kleur@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
+ integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
+
kleur@^4.0.3:
version "4.1.5"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
@@ -4370,6 +5960,11 @@ layout-base@^2.0.0:
resolved "https://registry.npmmirror.com/layout-base/-/layout-base-2.0.1.tgz#d0337913586c90f9c2c075292069f5c2da5dd285"
integrity sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==
+leven@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.npmmirror.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
+ integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
+
levn@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
@@ -4426,6 +6021,13 @@ loader-runner@^4.2.0:
resolved "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1"
integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
+locate-path@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
+ integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
+ dependencies:
+ p-locate "^4.1.0"
+
locate-path@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
@@ -4443,11 +6045,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"
@@ -4498,11 +6110,49 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
+lz-string@^1.5.0:
+ version "1.5.0"
+ resolved "https://registry.npmmirror.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
+ integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
+
+make-dir@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.npmmirror.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e"
+ integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==
+ dependencies:
+ semver "^7.5.3"
+
+make-error@^1.1.1:
+ version "1.3.6"
+ resolved "https://registry.npmmirror.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
+ integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
+
+makeerror@1.0.12:
+ version "1.0.12"
+ resolved "https://registry.npmmirror.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
+ integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==
+ dependencies:
+ tmpl "1.0.5"
+
markdown-table@^3.0.0:
version "3.0.3"
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"
@@ -5019,6 +6669,11 @@ mimic-fn@^4.0.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
+min-indent@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.npmmirror.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
+ integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
+
minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@@ -5103,11 +6758,21 @@ node-fetch@^3.3.1:
fetch-blob "^3.1.4"
formdata-polyfill "^4.0.10"
+node-int64@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.npmmirror.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
+ integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
+
node-releases@^2.0.12:
version "2.0.12"
resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039"
integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==
+node-releases@^2.0.18:
+ version "2.0.18"
+ resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
+ integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==
+
node-releases@^2.0.8:
version "2.0.10"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f"
@@ -5123,6 +6788,13 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+npm-run-path@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
+ integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
+ dependencies:
+ path-key "^3.0.0"
+
npm-run-path@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00"
@@ -5137,6 +6809,11 @@ nth-check@^2.0.1:
dependencies:
boolbase "^1.0.0"
+nwsapi@^2.2.2:
+ version "2.2.12"
+ resolved "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.12.tgz#fb6af5c0ec35b27b4581eb3bbad34ec9e5c696f8"
+ integrity sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==
+
object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -5212,7 +6889,7 @@ once@^1.3.0:
dependencies:
wrappy "1"
-onetime@^5.1.0:
+onetime@^5.1.0, onetime@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
@@ -5252,13 +6929,27 @@ optionator@^0.9.3:
prelude-ls "^1.2.1"
type-check "^0.4.0"
-p-limit@^3.0.2:
+p-limit@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+ integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
+ dependencies:
+ p-try "^2.0.0"
+
+p-limit@^3.0.2, p-limit@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
dependencies:
yocto-queue "^0.1.0"
+p-locate@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
+ integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+ dependencies:
+ p-limit "^2.2.0"
+
p-locate@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
@@ -5273,6 +6964,11 @@ p-map@^4.0.0:
dependencies:
aggregate-error "^3.0.0"
+p-try@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+ integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@@ -5280,7 +6976,7 @@ parent-module@^1.0.0:
dependencies:
callsites "^3.0.0"
-parse-json@^5.0.0:
+parse-json@^5.0.0, parse-json@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
@@ -5290,7 +6986,7 @@ parse-json@^5.0.0:
json-parse-even-better-errors "^2.3.0"
lines-and-columns "^1.1.6"
-parse5@^7.0.0:
+parse5@^7.0.0, parse5@^7.1.1:
version "7.1.2"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32"
integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==
@@ -5307,7 +7003,7 @@ path-is-absolute@^1.0.0:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
-path-key@^3.1.0:
+path-key@^3.0.0, path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
@@ -5332,7 +7028,12 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
-picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
+picocolors@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
+ integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
+
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
@@ -5342,6 +7043,18 @@ pidtree@^0.6.0:
resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c"
integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==
+pirates@^4.0.4:
+ version "4.0.6"
+ resolved "https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9"
+ integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==
+
+pkg-dir@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
+ integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
+ dependencies:
+ find-up "^4.0.0"
+
postcss@8.4.31:
version "8.4.31"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d"
@@ -5368,6 +7081,32 @@ prettier@^3.0.2:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.2.tgz#78fcecd6d870551aa5547437cdae39d4701dca5b"
integrity sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==
+pretty-format@^27.0.2:
+ version "27.5.1"
+ resolved "https://registry.npmmirror.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
+ integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
+ dependencies:
+ ansi-regex "^5.0.1"
+ ansi-styles "^5.0.0"
+ react-is "^17.0.1"
+
+pretty-format@^29.0.0, pretty-format@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.npmmirror.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"
+ integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==
+ dependencies:
+ "@jest/schemas" "^29.6.3"
+ ansi-styles "^5.0.0"
+ react-is "^18.0.0"
+
+prompts@^2.0.1:
+ version "2.4.2"
+ resolved "https://registry.npmmirror.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
+ integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==
+ dependencies:
+ kleur "^3.0.3"
+ sisteransi "^1.0.5"
+
prop-types@^15.0.0, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
@@ -5387,11 +7126,31 @@ proxy-from-env@^1.1.0:
resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+psl@^1.1.33:
+ version "1.9.0"
+ resolved "https://registry.npmmirror.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
+ integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
+
punycode@^2.1.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
+punycode@^2.1.1:
+ version "2.3.1"
+ resolved "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
+ integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+
+pure-rand@^6.0.0:
+ version "6.1.0"
+ resolved "https://registry.npmmirror.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2"
+ integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==
+
+querystringify@^2.1.1:
+ version "2.2.0"
+ resolved "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
+ integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
+
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
@@ -5409,6 +7168,16 @@ randombytes@^2.1.0:
dependencies:
safe-buffer "^5.1.0"
+raw-body@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.0.tgz#25b3476f07a51600619dae3fe82ddc28a36e5e0f"
+ integrity sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==
+ dependencies:
+ bytes "3.1.2"
+ http-errors "2.0.0"
+ iconv-lite "0.6.3"
+ unpipe "1.0.0"
+
react-dom@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
@@ -5422,6 +7191,11 @@ react-is@^16.13.1, react-is@^16.7.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+react-is@^17.0.1:
+ version "17.0.2"
+ resolved "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
+ integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
+
react-is@^18.0.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
@@ -5489,6 +7263,14 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
+redent@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmmirror.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"
+ integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==
+ dependencies:
+ indent-string "^4.0.0"
+ strip-indent "^3.0.0"
+
redux@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
@@ -5624,16 +7406,38 @@ require-directory@^2.1.1:
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+requires-port@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+ integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
+
+resolve-cwd@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmmirror.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
+ integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
+ dependencies:
+ resolve-from "^5.0.0"
+
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+resolve-from@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
+ integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
+
resolve-pkg-maps@^1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f"
integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==
+resolve.exports@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.npmmirror.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800"
+ integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==
+
resolve@^1.14.2, resolve@^1.22.1:
version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
@@ -5643,6 +7447,15 @@ resolve@^1.14.2, resolve@^1.22.1:
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
+resolve@^1.20.0:
+ version "1.22.8"
+ resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
+ integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
+ dependencies:
+ is-core-module "^2.13.0"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
resolve@^2.0.0-next.4:
version "2.0.0-next.4"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660"
@@ -5682,6 +7495,12 @@ robust-predicates@^3.0.0:
resolved "https://registry.npmmirror.com/robust-predicates/-/robust-predicates-3.0.1.tgz#ecde075044f7f30118682bd9fb3f123109577f9a"
integrity sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==
+"rt-client@https://github.com/Azure-Samples/aoai-realtime-audio-sdk/releases/download/js/v0.5.0/rt-client-0.5.0.tgz":
+ version "0.5.0"
+ resolved "https://github.com/Azure-Samples/aoai-realtime-audio-sdk/releases/download/js/v0.5.0/rt-client-0.5.0.tgz#abf2e9a850201e3571b8d36830f77bc52af3de9b"
+ dependencies:
+ ws "^8.18.0"
+
run-parallel@^1.1.9:
version "1.2.0"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
@@ -5743,6 +7562,13 @@ sass@^1.59.2:
immutable "^4.0.0"
source-map-js ">=0.6.2 <2.0.0"
+saxes@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5"
+ integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==
+ dependencies:
+ xmlchars "^2.2.0"
+
scheduler@^0.23.0:
version "0.23.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
@@ -5759,11 +7585,16 @@ schema-utils@^3.1.1, schema-utils@^3.2.0:
ajv "^6.12.5"
ajv-keywords "^3.5.2"
-semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
+semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1:
version "6.3.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
+semver@^7.5.3:
+ version "7.6.3"
+ resolved "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
+ integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
+
semver@^7.5.4:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
@@ -5778,6 +7609,11 @@ serialize-javascript@^6.0.1:
dependencies:
randombytes "^2.1.0"
+setprototypeof@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
+ integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
+
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -5804,11 +7640,16 @@ side-channel@^1.0.4:
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
-signal-exit@^3.0.2, signal-exit@^3.0.7:
+signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7:
version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+sisteransi@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.npmmirror.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
+ integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
+
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
@@ -5850,6 +7691,14 @@ slice-ansi@^5.0.0:
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+source-map-support@0.5.13:
+ version "0.5.13"
+ resolved "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
+ integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
source-map-support@~0.5.20:
version "0.5.21"
resolved "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
@@ -5858,7 +7707,7 @@ source-map-support@~0.5.20:
buffer-from "^1.0.0"
source-map "^0.6.0"
-source-map@^0.6.0, source-map@^0.6.1:
+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
@@ -5878,11 +7727,28 @@ spawn-command@0.0.2:
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e"
integrity sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==
+sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+ integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
+
stable@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
+stack-utils@^2.0.3:
+ version "2.0.6"
+ resolved "https://registry.npmmirror.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f"
+ integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==
+ dependencies:
+ escape-string-regexp "^2.0.0"
+
+statuses@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
+ integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
+
stop-iteration-iterator@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4"
@@ -5900,6 +7766,14 @@ string-argv@^0.3.1:
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==
+string-length@^4.0.1:
+ version "4.0.2"
+ resolved "https://registry.npmmirror.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
+ integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==
+ dependencies:
+ char-regex "^1.0.2"
+ strip-ansi "^6.0.0"
+
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
@@ -5978,11 +7852,28 @@ strip-bom@^3.0.0:
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
+strip-bom@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.npmmirror.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"
+ integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==
+
+strip-final-newline@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
+ integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
+
strip-final-newline@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd"
integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==
+strip-indent@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmmirror.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
+ integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==
+ dependencies:
+ min-indent "^1.0.0"
+
strip-json-comments@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
@@ -6051,6 +7942,11 @@ svgo@^2.8.0:
picocolors "^1.0.0"
stable "^0.1.8"
+symbol-tree@^3.2.4:
+ version "3.2.4"
+ resolved "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
+ integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
+
synckit@^0.8.5, synckit@^0.8.6:
version "0.8.8"
resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.8.tgz#fe7fe446518e3d3d49f5e429f443cf08b6edfcd7"
@@ -6085,6 +7981,15 @@ terser@^5.16.8:
commander "^2.20.0"
source-map-support "~0.5.20"
+test-exclude@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.npmmirror.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
+ integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==
+ dependencies:
+ "@istanbuljs/schema" "^0.1.2"
+ glob "^7.1.4"
+ minimatch "^3.0.4"
+
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -6105,6 +8010,11 @@ tiny-invariant@^1.0.6:
resolved "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642"
integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==
+tmpl@1.0.5:
+ version "1.0.5"
+ resolved "https://registry.npmmirror.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
+ integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
+
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
@@ -6117,6 +8027,28 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
+toidentifier@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
+ integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+
+tough-cookie@^4.1.2:
+ version "4.1.4"
+ resolved "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36"
+ integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==
+ dependencies:
+ psl "^1.1.33"
+ punycode "^2.1.1"
+ universalify "^0.2.0"
+ url-parse "^1.5.3"
+
+tr46@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmmirror.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
+ integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
+ dependencies:
+ punycode "^2.1.1"
+
tree-kill@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
@@ -6142,6 +8074,25 @@ ts-dedent@^2.2.0:
resolved "https://registry.npmmirror.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5"
integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==
+ts-node@^10.9.2:
+ version "10.9.2"
+ resolved "https://registry.npmmirror.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f"
+ integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==
+ dependencies:
+ "@cspotcode/source-map-support" "^0.8.0"
+ "@tsconfig/node10" "^1.0.7"
+ "@tsconfig/node12" "^1.0.7"
+ "@tsconfig/node14" "^1.0.0"
+ "@tsconfig/node16" "^1.0.2"
+ acorn "^8.4.1"
+ acorn-walk "^8.1.1"
+ arg "^4.1.0"
+ create-require "^1.1.0"
+ diff "^4.0.1"
+ make-error "^1.1.1"
+ v8-compile-cache-lib "^3.0.1"
+ yn "3.1.1"
+
tsconfig-paths@^3.14.1:
version "3.14.2"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088"
@@ -6179,6 +8130,11 @@ type-check@^0.4.0, type-check@~0.4.0:
dependencies:
prelude-ls "^1.2.1"
+type-detect@4.0.8:
+ version "4.0.8"
+ resolved "https://registry.npmmirror.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
+ integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
+
type-fest@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
@@ -6200,7 +8156,7 @@ typed-array-length@^1.0.4:
typescript@5.2.2:
version "5.2.2"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78"
+ resolved "https://mirrors.huaweicloud.com/repository/npm/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78"
integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==
unbox-primitive@^1.0.2:
@@ -6313,6 +8269,16 @@ unist-util-visit@^4.0.0:
unist-util-is "^5.0.0"
unist-util-visit-parents "^5.1.1"
+universalify@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.npmmirror.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"
+ integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==
+
+unpipe@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+ integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
+
update-browserslist-db@^1.0.10:
version "1.0.10"
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3"
@@ -6329,6 +8295,14 @@ update-browserslist-db@^1.0.11:
escalade "^3.1.1"
picocolors "^1.0.0"
+update-browserslist-db@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e"
+ integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==
+ dependencies:
+ escalade "^3.1.2"
+ picocolors "^1.0.1"
+
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
@@ -6336,6 +8310,14 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
+url-parse@^1.5.3:
+ version "1.5.10"
+ resolved "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
+ integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
+ dependencies:
+ querystringify "^2.1.1"
+ requires-port "^1.0.0"
+
use-debounce@^9.0.4:
version "9.0.4"
resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-9.0.4.tgz#51d25d856fbdfeb537553972ce3943b897f1ac85"
@@ -6366,6 +8348,20 @@ uvu@^0.5.0:
kleur "^4.0.3"
sade "^1.7.3"
+v8-compile-cache-lib@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.npmmirror.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
+ integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
+
+v8-to-istanbul@^9.0.1:
+ version "9.3.0"
+ resolved "https://registry.npmmirror.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175"
+ integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==
+ dependencies:
+ "@jridgewell/trace-mapping" "^0.3.12"
+ "@types/istanbul-lib-coverage" "^2.0.1"
+ convert-source-map "^2.0.0"
+
vfile-location@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-4.1.0.tgz#69df82fb9ef0a38d0d02b90dd84620e120050dd0"
@@ -6392,6 +8388,20 @@ vfile@^5.0.0:
unist-util-stringify-position "^3.0.0"
vfile-message "^3.0.0"
+w3c-xmlserializer@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073"
+ integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==
+ dependencies:
+ xml-name-validator "^4.0.0"
+
+walker@^1.0.8:
+ version "1.0.8"
+ resolved "https://registry.npmmirror.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"
+ integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==
+ dependencies:
+ makeerror "1.0.12"
+
watch@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/watch/-/watch-1.0.2.tgz#340a717bde765726fa0aa07d721e0147a551df0c"
@@ -6423,6 +8433,11 @@ web-worker@^1.2.0:
resolved "https://registry.npmmirror.com/web-worker/-/web-worker-1.2.0.tgz#5d85a04a7fbc1e7db58f66595d7a3ac7c9c180da"
integrity sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==
+webidl-conversions@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
+ integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
+
webpack-sources@^3.2.3:
version "3.2.3"
resolved "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
@@ -6458,6 +8473,26 @@ webpack@^5.88.1:
watchpack "^2.4.0"
webpack-sources "^3.2.3"
+whatwg-encoding@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53"
+ integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==
+ dependencies:
+ iconv-lite "0.6.3"
+
+whatwg-mimetype@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"
+ integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==
+
+whatwg-url@^11.0.0:
+ version "11.0.0"
+ resolved "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018"
+ integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
+ dependencies:
+ tr46 "^3.0.0"
+ webidl-conversions "^7.0.0"
+
which-boxed-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
@@ -6521,6 +8556,29 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+write-file-atomic@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.npmmirror.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd"
+ integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==
+ dependencies:
+ imurmurhash "^0.1.4"
+ signal-exit "^3.0.7"
+
+ws@^8.11.0, ws@^8.18.0:
+ version "8.18.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
+ integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
+
+xml-name-validator@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
+ integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
+
+xmlchars@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
+ integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
+
y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
@@ -6551,7 +8609,7 @@ yargs-parser@^21.1.1:
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
-yargs@^17.7.2:
+yargs@^17.3.1, yargs@^17.7.2:
version "17.7.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
@@ -6564,11 +8622,21 @@ yargs@^17.7.2:
y18n "^5.0.5"
yargs-parser "^21.1.1"
+yn@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.npmmirror.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
+ integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
+
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+zod@^3.23.8, zod@^3.24.1:
+ version "3.24.1"
+ resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.1.tgz#27445c912738c8ad1e9de1bea0359fa44d9d35ee"
+ integrity sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==
+
zustand@^4.3.8:
version "4.3.8"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.8.tgz#37113df8e9e1421b0be1b2dca02b49b76210e7c4"