Эх сурвалжийг харах

Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web

GH Action - Upstream Sync 2 жил өмнө
parent
commit
b3a0b252ad

+ 64 - 20
app/components/chat.tsx

@@ -19,6 +19,7 @@ import LightIcon from "../icons/light.svg";
 import DarkIcon from "../icons/dark.svg";
 import DarkIcon from "../icons/dark.svg";
 import AutoIcon from "../icons/auto.svg";
 import AutoIcon from "../icons/auto.svg";
 import BottomIcon from "../icons/bottom.svg";
 import BottomIcon from "../icons/bottom.svg";
+import StopIcon from "../icons/pause.svg";
 
 
 import {
 import {
   Message,
   Message,
@@ -38,7 +39,6 @@ import {
   isMobileScreen,
   isMobileScreen,
   selectOrCopy,
   selectOrCopy,
   autoGrowTextArea,
   autoGrowTextArea,
-  getCSSVar,
 } from "../utils";
 } from "../utils";
 
 
 import dynamic from "next/dynamic";
 import dynamic from "next/dynamic";
@@ -355,8 +355,8 @@ export function ChatActions(props: {
 }) {
 }) {
   const chatStore = useChatStore();
   const chatStore = useChatStore();
 
 
+  // switch themes
   const theme = chatStore.config.theme;
   const theme = chatStore.config.theme;
-
   function nextTheme() {
   function nextTheme() {
     const themes = [Theme.Auto, Theme.Light, Theme.Dark];
     const themes = [Theme.Auto, Theme.Light, Theme.Dark];
     const themeIndex = themes.indexOf(theme);
     const themeIndex = themes.indexOf(theme);
@@ -365,8 +365,20 @@ export function ChatActions(props: {
     chatStore.updateConfig((config) => (config.theme = nextTheme));
     chatStore.updateConfig((config) => (config.theme = nextTheme));
   }
   }
 
 
+  // stop all responses
+  const couldStop = ControllerPool.hasPending();
+  const stopAll = () => ControllerPool.stopAll();
+
   return (
   return (
     <div className={chatStyle["chat-input-actions"]}>
     <div className={chatStyle["chat-input-actions"]}>
+      {couldStop && (
+        <div
+          className={`${chatStyle["chat-input-action"]} clickable`}
+          onClick={stopAll}
+        >
+          <StopIcon />
+        </div>
+      )}
       {!props.hitBottom && (
       {!props.hitBottom && (
         <div
         <div
           className={`${chatStyle["chat-input-action"]} clickable`}
           className={`${chatStyle["chat-input-action"]} clickable`}
@@ -524,21 +536,45 @@ export function Chat(props: {
     }
     }
   };
   };
 
 
-  const onResend = (botIndex: number) => {
+  const findLastUesrIndex = (messageId: number) => {
     // find last user input message and resend
     // find last user input message and resend
-    for (let i = botIndex; i >= 0; i -= 1) {
-      if (messages[i].role === "user") {
-        setIsLoading(true);
-        chatStore
-          .onUserInput(messages[i].content)
-          .then(() => setIsLoading(false));
-        chatStore.updateCurrentSession((session) =>
-          session.messages.splice(i, 2),
-        );
-        inputRef.current?.focus();
-        return;
+    let lastUserMessageIndex: number | null = null;
+    for (let i = 0; i < session.messages.length; i += 1) {
+      const message = session.messages[i];
+      if (message.id === messageId) {
+        break;
+      }
+      if (message.role === "user") {
+        lastUserMessageIndex = i;
       }
       }
     }
     }
+
+    return lastUserMessageIndex;
+  };
+
+  const deleteMessage = (userIndex: number) => {
+    chatStore.updateCurrentSession((session) =>
+      session.messages.splice(userIndex, 2),
+    );
+  };
+
+  const onDelete = (botMessageId: number) => {
+    const userIndex = findLastUesrIndex(botMessageId);
+    if (userIndex === null) return;
+    deleteMessage(userIndex);
+  };
+
+  const onResend = (botMessageId: number) => {
+    // find last user input message and resend
+    const userIndex = findLastUesrIndex(botMessageId);
+    if (userIndex === null) return;
+
+    setIsLoading(true);
+    chatStore
+      .onUserInput(session.messages[userIndex].content)
+      .then(() => setIsLoading(false));
+    deleteMessage(userIndex);
+    inputRef.current?.focus();
   };
   };
 
 
   const config = useChatStore((state) => state.config);
   const config = useChatStore((state) => state.config);
@@ -710,12 +746,20 @@ export function Chat(props: {
                             {Locale.Chat.Actions.Stop}
                             {Locale.Chat.Actions.Stop}
                           </div>
                           </div>
                         ) : (
                         ) : (
-                          <div
-                            className={styles["chat-message-top-action"]}
-                            onClick={() => onResend(i)}
-                          >
-                            {Locale.Chat.Actions.Retry}
-                          </div>
+                          <>
+                            <div
+                              className={styles["chat-message-top-action"]}
+                              onClick={() => onDelete(message.id ?? i)}
+                            >
+                              {Locale.Chat.Actions.Delete}
+                            </div>
+                            <div
+                              className={styles["chat-message-top-action"]}
+                              onClick={() => onResend(message.id ?? i)}
+                            >
+                              {Locale.Chat.Actions.Retry}
+                            </div>
+                          </>
                         )}
                         )}
 
 
                         <div
                         <div

+ 1 - 1
app/icons/bottom.svg

@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0)  rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path  id="路径 1" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(8.002766666666666 2)  rotate(0 0 4.649916666666667)" d="M0,9.3L0,0 " /><path  id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(4 7.333333333333333)  rotate(0 4 2)" d="M8,0L4,4L0,0 " /><path  id="路径 3" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(4 14)  rotate(0 4 0)" d="M8,0L0,0 " /></g></g></svg>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0)  rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path  id="路径 1" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(4 4)  rotate(0 4 2)" d="M8,0L4,4L0,0 " /><path  id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(4 8)  rotate(0 4 2)" d="M8,0L4,4L0,0 " /></g></g></svg>

+ 1 - 0
app/icons/pause.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0)  rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path  id="路径 1" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(1.3333333333333333 1.3333333333333333)  rotate(0 6.666666666666666 6.666666666666666)" d="M13.33,6.67C13.33,2.98 10.35,0 6.67,0C2.98,0 0,2.98 0,6.67C0,10.35 2.98,13.33 6.67,13.33C10.35,13.33 13.33,10.35 13.33,6.67Z " /><path  id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(6.333333333333333 6)  rotate(0 0 2)" d="M0,0L0,4 " /><path  id="路径 3" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(9.666666666666666 6)  rotate(0 0 2)" d="M0,0L0,4 " /></g></g></svg>

+ 2 - 0
app/locales/cn.ts

@@ -17,6 +17,7 @@ const cn = {
       Copy: "复制",
       Copy: "复制",
       Stop: "停止",
       Stop: "停止",
       Retry: "重试",
       Retry: "重试",
+      Delete: "删除",
     },
     },
     Rename: "重命名对话",
     Rename: "重命名对话",
     Typing: "正在输入…",
     Typing: "正在输入…",
@@ -74,6 +75,7 @@ const cn = {
         it: "Italiano",
         it: "Italiano",
         tr: "Türkçe",
         tr: "Türkçe",
         jp: "日本語",
         jp: "日本語",
+        de: "Deutsch",
       },
       },
     },
     },
     Avatar: "头像",
     Avatar: "头像",

+ 184 - 0
app/locales/de.ts

@@ -0,0 +1,184 @@
+import { SubmitKey } from "../store/app";
+import type { LocaleType } from "./index";
+
+const de: LocaleType = {
+  WIP: "In Bearbeitung...",
+  Error: {
+    Unauthorized:
+      "Unbefugter Zugriff, bitte geben Sie den Zugangscode auf der Einstellungsseite ein.",
+  },
+  ChatItem: {
+    ChatItemCount: (count: number) => `${count} Nachrichten`,
+  },
+  Chat: {
+    SubTitle: (count: number) => `${count} Nachrichten mit ChatGPT`,
+    Actions: {
+      ChatList: "Zur Chat-Liste gehen",
+      CompressedHistory: "Komprimierter Gedächtnis-Prompt",
+      Export: "Alle Nachrichten als Markdown exportieren",
+      Copy: "Kopieren",
+      Stop: "Stop",
+      Retry: "Wiederholen",
+      Delete: "Delete",
+    },
+    Rename: "Chat umbenennen",
+    Typing: "Tippen...",
+    Input: (submitKey: string) => {
+      var inputHints = `${submitKey} um zu Senden`;
+      if (submitKey === String(SubmitKey.Enter)) {
+        inputHints += ", Umschalt + Eingabe für Zeilenumbruch";
+      }
+      return inputHints + ", / zum Durchsuchen von Prompts";
+    },
+    Send: "Senden",
+  },
+  Export: {
+    Title: "Alle Nachrichten",
+    Copy: "Alles kopieren",
+    Download: "Herunterladen",
+    MessageFromYou: "Deine Nachricht",
+    MessageFromChatGPT: "Nachricht von ChatGPT",
+  },
+  Memory: {
+    Title: "Gedächtnis-Prompt",
+    EmptyContent: "Noch nichts.",
+    Send: "Gedächtnis senden",
+    Copy: "Gedächtnis kopieren",
+    Reset: "Sitzung zurücksetzen",
+    ResetConfirm:
+      "Das Zurücksetzen löscht den aktuellen Gesprächsverlauf und das Langzeit-Gedächtnis. Möchten Sie wirklich zurücksetzen?",
+  },
+  Home: {
+    NewChat: "Neuer Chat",
+    DeleteChat: "Bestätigen Sie, um das ausgewählte Gespräch zu löschen?",
+    DeleteToast: "Chat gelöscht",
+    Revert: "Zurücksetzen",
+  },
+  Settings: {
+    Title: "Einstellungen",
+    SubTitle: "Alle Einstellungen",
+    Actions: {
+      ClearAll: "Alle Daten löschen",
+      ResetAll: "Alle Einstellungen zurücksetzen",
+      Close: "Schließen",
+      ConfirmResetAll: {
+        Confirm: "Möchten Sie wirklich alle Konfigurationen zurücksetzen?",
+      },
+      ConfirmClearAll: {
+        Confirm: "Möchten Sie wirklich alle Chats zurücksetzen?",
+      },
+    },
+    Lang: {
+      Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
+      Options: {
+        cn: "简体中文",
+        en: "English",
+        tw: "繁體中文",
+        es: "Español",
+        it: "Italiano",
+        tr: "Türkçe",
+        jp: "日本語",
+        de: "Deutsch",
+      },
+    },
+    Avatar: "Avatar",
+    FontSize: {
+      Title: "Schriftgröße",
+      SubTitle: "Schriftgröße des Chat-Inhalts anpassen",
+    },
+    Update: {
+      Version: (x: string) => `Version: ${x}`,
+      IsLatest: "Neueste Version",
+      CheckUpdate: "Update prüfen",
+      IsChecking: "Update wird geprüft...",
+      FoundUpdate: (x: string) => `Neue Version gefunden: ${x}`,
+      GoToUpdate: "Aktualisieren",
+    },
+    SendKey: "Senden-Taste",
+    Theme: "Erscheinungsbild",
+    TightBorder: "Enger Rahmen",
+    SendPreviewBubble: "Vorschau-Bubble senden",
+    Prompt: {
+      Disable: {
+        Title: "Autovervollständigung deaktivieren",
+        SubTitle: "Autovervollständigung mit / starten",
+      },
+      List: "Prompt-Liste",
+      ListCount: (builtin: number, custom: number) =>
+        `${builtin} integriert, ${custom} benutzerdefiniert`,
+      Edit: "Bearbeiten",
+    },
+    HistoryCount: {
+      Title: "Anzahl der angehängten Nachrichten",
+      SubTitle: "Anzahl der pro Anfrage angehängten gesendeten Nachrichten",
+    },
+    CompressThreshold: {
+      Title: "Schwellenwert für Verlaufskomprimierung",
+      SubTitle:
+        "Komprimierung, wenn die Länge der unkomprimierten Nachrichten den Wert überschreitet",
+    },
+    Token: {
+      Title: "API-Schlüssel",
+      SubTitle:
+        "Verwenden Sie Ihren Schlüssel, um das Zugangscode-Limit zu ignorieren",
+      Placeholder: "OpenAI API-Schlüssel",
+    },
+    Usage: {
+      Title: "Kontostand",
+      SubTitle(used: any, total: any) {
+        return `Diesen Monat ausgegeben $${used}, Abonnement $${total}`;
+      },
+      IsChecking: "Wird überprüft...",
+      Check: "Erneut prüfen",
+      NoAccess: "API-Schlüssel eingeben, um den Kontostand zu überprüfen",
+    },
+    AccessCode: {
+      Title: "Zugangscode",
+      SubTitle: "Zugangskontrolle aktiviert",
+      Placeholder: "Zugangscode erforderlich",
+    },
+    Model: "Modell",
+    Temperature: {
+      Title: "Temperature", //Temperatur
+      SubTitle: "Ein größerer Wert führt zu zufälligeren Antworten",
+    },
+    MaxTokens: {
+      Title: "Max Tokens", //Maximale Token
+      SubTitle: "Maximale Anzahl der Anfrage- plus Antwort-Token",
+    },
+    PresencePenlty: {
+      Title: "Presence Penalty", //Anwesenheitsstrafe
+      SubTitle:
+        "Ein größerer Wert erhöht die Wahrscheinlichkeit, dass über neue Themen gesprochen wird",
+    },
+  },
+  Store: {
+    DefaultTopic: "Neues Gespräch",
+    BotHello: "Hallo! Wie kann ich Ihnen heute helfen?",
+    Error:
+      "Etwas ist schief gelaufen, bitte versuchen Sie es später noch einmal.",
+    Prompt: {
+      History: (content: string) =>
+        "Dies ist eine Zusammenfassung des Chatverlaufs zwischen dem KI und dem Benutzer als Rückblick: " +
+        content,
+      Topic:
+        "Bitte erstellen Sie einen vier- bis fünfwörtigen Titel, der unser Gespräch zusammenfasst, ohne Einleitung, Zeichensetzung, Anführungszeichen, Punkte, Symbole oder zusätzlichen Text. Entfernen Sie Anführungszeichen.",
+      Summarize:
+        "Fassen Sie unsere Diskussion kurz in 200 Wörtern oder weniger zusammen, um sie als Pronpt für zukünftige Gespräche zu verwenden.",
+    },
+    ConfirmClearAll:
+      "Bestätigen Sie, um alle Chat- und Einstellungsdaten zu löschen?",
+  },
+  Copy: {
+    Success: "In die Zwischenablage kopiert",
+    Failed:
+      "Kopieren fehlgeschlagen, bitte geben Sie die Berechtigung zum Zugriff auf die Zwischenablage frei",
+  },
+  Context: {
+    Toast: (x: any) => `Mit ${x} Kontext-Prompts`,
+    Edit: "Kontext- und Gedächtnis-Prompts",
+    Add: "Hinzufügen",
+  },
+};
+
+export default de;

+ 2 - 0
app/locales/en.ts

@@ -19,6 +19,7 @@ const en: LocaleType = {
       Copy: "Copy",
       Copy: "Copy",
       Stop: "Stop",
       Stop: "Stop",
       Retry: "Retry",
       Retry: "Retry",
+      Delete: "Delete",
     },
     },
     Rename: "Rename Chat",
     Rename: "Rename Chat",
     Typing: "Typing…",
     Typing: "Typing…",
@@ -77,6 +78,7 @@ const en: LocaleType = {
         it: "Italiano",
         it: "Italiano",
         tr: "Türkçe",
         tr: "Türkçe",
         jp: "日本語",
         jp: "日本語",
+        de: "Deutsch",
       },
       },
     },
     },
     Avatar: "Avatar",
     Avatar: "Avatar",

+ 2 - 0
app/locales/es.ts

@@ -19,6 +19,7 @@ const es: LocaleType = {
       Copy: "Copiar",
       Copy: "Copiar",
       Stop: "Detener",
       Stop: "Detener",
       Retry: "Reintentar",
       Retry: "Reintentar",
+      Delete: "Delete",
     },
     },
     Rename: "Renombrar chat",
     Rename: "Renombrar chat",
     Typing: "Escribiendo...",
     Typing: "Escribiendo...",
@@ -77,6 +78,7 @@ const es: LocaleType = {
         it: "Italiano",
         it: "Italiano",
         tr: "Türkçe",
         tr: "Türkçe",
         jp: "日本語",
         jp: "日本語",
+        de: "Deutsch",
       },
       },
     },
     },
     Avatar: "Avatar",
     Avatar: "Avatar",

+ 27 - 18
app/locales/index.ts

@@ -5,10 +5,20 @@ import ES from "./es";
 import IT from "./it";
 import IT from "./it";
 import TR from "./tr";
 import TR from "./tr";
 import JP from "./jp";
 import JP from "./jp";
+import DE from "./de";
 
 
 export type { LocaleType } from "./cn";
 export type { LocaleType } from "./cn";
 
 
-export const AllLangs = ["en", "cn", "tw", "es", "it", "tr", "jp"] as const;
+export const AllLangs = [
+  "en",
+  "cn",
+  "tw",
+  "es",
+  "it",
+  "tr",
+  "jp",
+  "de",
+] as const;
 type Lang = (typeof AllLangs)[number];
 type Lang = (typeof AllLangs)[number];
 
 
 const LANG_KEY = "lang";
 const LANG_KEY = "lang";
@@ -44,21 +54,13 @@ export function getLang(): Lang {
 
 
   const lang = getLanguage();
   const lang = getLanguage();
 
 
-  if (lang.includes("zh") || lang.includes("cn")) {
-    return "cn";
-  } else if (lang.includes("tw")) {
-    return "tw";
-  } else if (lang.includes("es")) {
-    return "es";
-  } else if (lang.includes("it")) {
-    return "it";
-  } else if (lang.includes("tr")) {
-    return "tr";
-  } else if (lang.includes("jp")) {
-    return "jp";
-  } else {
-    return "en";
+  for (const option of AllLangs) {
+    if (lang.includes(option)) {
+      return option;
+    }
   }
   }
+
+  return "en";
 }
 }
 
 
 export function changeLang(lang: Lang) {
 export function changeLang(lang: Lang) {
@@ -66,6 +68,13 @@ export function changeLang(lang: Lang) {
   location.reload();
   location.reload();
 }
 }
 
 
-export default { en: EN, cn: CN, tw: TW, es: ES, it: IT, tr: TR, jp: JP }[
-  getLang()
-];
+export default {
+  en: EN,
+  cn: CN,
+  tw: TW,
+  es: ES,
+  it: IT,
+  tr: TR,
+  jp: JP,
+  de: DE,
+}[getLang()] as typeof CN;

+ 2 - 0
app/locales/it.ts

@@ -19,6 +19,7 @@ const it: LocaleType = {
       Copy: "Copia",
       Copy: "Copia",
       Stop: "Stop",
       Stop: "Stop",
       Retry: "Riprova",
       Retry: "Riprova",
+      Delete: "Delete",
     },
     },
     Rename: "Rinomina Chat",
     Rename: "Rinomina Chat",
     Typing: "Typing…",
     Typing: "Typing…",
@@ -77,6 +78,7 @@ const it: LocaleType = {
         it: "Italiano",
         it: "Italiano",
         tr: "Türkçe",
         tr: "Türkçe",
         jp: "日本語",
         jp: "日本語",
+        de: "Deutsch",
       },
       },
     },
     },
     Avatar: "Avatar",
     Avatar: "Avatar",

+ 2 - 2
app/locales/jp.ts

@@ -18,6 +18,7 @@ const jp = {
       Copy: "コピー",
       Copy: "コピー",
       Stop: "停止",
       Stop: "停止",
       Retry: "リトライ",
       Retry: "リトライ",
+      Delete: "Delete",
     },
     },
     Rename: "チャットの名前を変更",
     Rename: "チャットの名前を変更",
     Typing: "入力中…",
     Typing: "入力中…",
@@ -76,6 +77,7 @@ const jp = {
         it: "Italiano",
         it: "Italiano",
         tr: "Türkçe",
         tr: "Türkçe",
         jp: "日本語",
         jp: "日本語",
+        de: "Deutsch",
       },
       },
     },
     },
     Avatar: "アバター",
     Avatar: "アバター",
@@ -177,6 +179,4 @@ const jp = {
   },
   },
 };
 };
 
 
-export type LocaleType = typeof jp;
-
 export default jp;
 export default jp;

+ 2 - 0
app/locales/tr.ts

@@ -19,6 +19,7 @@ const tr: LocaleType = {
       Copy: "Kopyala",
       Copy: "Kopyala",
       Stop: "Durdur",
       Stop: "Durdur",
       Retry: "Tekrar Dene",
       Retry: "Tekrar Dene",
+      Delete: "Delete",
     },
     },
     Rename: "Sohbeti Yeniden Adlandır",
     Rename: "Sohbeti Yeniden Adlandır",
     Typing: "Yazıyor…",
     Typing: "Yazıyor…",
@@ -77,6 +78,7 @@ const tr: LocaleType = {
         it: "Italiano",
         it: "Italiano",
         tr: "Türkçe",
         tr: "Türkçe",
         jp: "日本語",
         jp: "日本語",
+        de: "Deutsch",
       },
       },
     },
     },
     Avatar: "Avatar",
     Avatar: "Avatar",

+ 4 - 1
app/locales/tw.ts

@@ -18,6 +18,7 @@ const tw: LocaleType = {
       Copy: "複製",
       Copy: "複製",
       Stop: "停止",
       Stop: "停止",
       Retry: "重試",
       Retry: "重試",
+      Delete: "刪除",
     },
     },
     Rename: "重命名對話",
     Rename: "重命名對話",
     Typing: "正在輸入…",
     Typing: "正在輸入…",
@@ -75,6 +76,7 @@ const tw: LocaleType = {
         it: "Italiano",
         it: "Italiano",
         tr: "Türkçe",
         tr: "Türkçe",
         jp: "日本語",
         jp: "日本語",
+        de: "Deutsch",
       },
       },
     },
     },
     Avatar: "大頭貼",
     Avatar: "大頭貼",
@@ -152,7 +154,8 @@ const tw: LocaleType = {
     Prompt: {
     Prompt: {
       History: (content: string) =>
       History: (content: string) =>
         "這是 AI 與用戶的歷史聊天總結,作為前情提要:" + content,
         "這是 AI 與用戶的歷史聊天總結,作為前情提要:" + content,
-      Topic: "Summarise the conversation in a short and concise eye-catching title that instantly conveys the main topic. Use as few words as possible. Use the language used in the enquiry, e.g. use English for English enquiry, use zh-hant for traditional chinese enquiry. Don't use quotation marks at the beginning and the end.",
+      Topic:
+        "Summarise the conversation in a short and concise eye-catching title that instantly conveys the main topic. Use as few words as possible. Use the language used in the enquiry, e.g. use English for English enquiry, use zh-hant for traditional chinese enquiry. Don't use quotation marks at the beginning and the end.",
       Summarize:
       Summarize:
         "Summarise the conversation in at most 250 tokens for continuing the conversation in future. Use the language used in the conversation, e.g. use English for English conversation, use zh-hant for traditional chinese conversation.",
         "Summarise the conversation in at most 250 tokens for continuing the conversation in future. Use the language used in the conversation, e.g. use English for English conversation, use zh-hant for traditional chinese conversation.",
     },
     },

+ 11 - 4
app/requests.ts

@@ -2,7 +2,7 @@ import type { ChatRequest, ChatResponse } from "./api/openai/typing";
 import { Message, ModelConfig, useAccessStore, useChatStore } from "./store";
 import { Message, ModelConfig, useAccessStore, useChatStore } from "./store";
 import { showToast } from "./components/ui-lib";
 import { showToast } from "./components/ui-lib";
 
 
-const TIME_OUT_MS = 30000;
+const TIME_OUT_MS = 60000;
 
 
 const makeRequestParam = (
 const makeRequestParam = (
   messages: Message[],
   messages: Message[],
@@ -167,15 +167,14 @@ export async function requestChatStream(
       options?.onController?.(controller);
       options?.onController?.(controller);
 
 
       while (true) {
       while (true) {
-        // handle time out, will stop if no response in 10 secs
         const resTimeoutId = setTimeout(() => finish(), TIME_OUT_MS);
         const resTimeoutId = setTimeout(() => finish(), TIME_OUT_MS);
         const content = await reader?.read();
         const content = await reader?.read();
         clearTimeout(resTimeoutId);
         clearTimeout(resTimeoutId);
-      
+
         if (!content || !content.value) {
         if (!content || !content.value) {
           break;
           break;
         }
         }
-      
+
         const text = decoder.decode(content.value, { stream: true });
         const text = decoder.decode(content.value, { stream: true });
         responseText += text;
         responseText += text;
 
 
@@ -235,6 +234,14 @@ export const ControllerPool = {
     controller?.abort();
     controller?.abort();
   },
   },
 
 
+  stopAll() {
+    Object.values(this.controllers).forEach((v) => v.abort());
+  },
+
+  hasPending() {
+    return Object.values(this.controllers).length > 0;
+  },
+
   remove(sessionIndex: number, messageId: number) {
   remove(sessionIndex: number, messageId: number) {
     const key = this.key(sessionIndex, messageId);
     const key = this.key(sessionIndex, messageId);
     delete this.controllers[key];
     delete this.controllers[key];

+ 2 - 1
app/store/app.ts

@@ -386,6 +386,7 @@ export const useChatStore = create<ChatStore>()(
         const botMessage: Message = createMessage({
         const botMessage: Message = createMessage({
           role: "assistant",
           role: "assistant",
           streaming: true,
           streaming: true,
+          id: userMessage.id! + 1,
         });
         });
 
 
         // get recent messages
         // get recent messages
@@ -421,7 +422,7 @@ export const useChatStore = create<ChatStore>()(
           onError(error, statusCode) {
           onError(error, statusCode) {
             if (statusCode === 401) {
             if (statusCode === 401) {
               botMessage.content = Locale.Error.Unauthorized;
               botMessage.content = Locale.Error.Unauthorized;
-            } else {
+            } else if (!error.message.includes("aborted")) {
               botMessage.content += "\n\n" + Locale.Store.Error;
               botMessage.content += "\n\n" + Locale.Store.Error;
             }
             }
             botMessage.streaming = false;
             botMessage.streaming = false;

+ 3 - 4
app/store/prompt.ts

@@ -116,10 +116,9 @@ export const usePromptStore = create<PromptStore>()(
               })
               })
               .concat([...(state?.prompts?.values() ?? [])]);
               .concat([...(state?.prompts?.values() ?? [])]);
 
 
-            const allPromptsForSearch = builtinPrompts.reduce(
-              (pre, cur) => pre.concat(cur),
-              [],
-            );
+            const allPromptsForSearch = builtinPrompts
+              .reduce((pre, cur) => pre.concat(cur), [])
+              .filter((v) => !!v.title && !!v.content);
             SearchService.count.builtin = res.en.length + res.cn.length;
             SearchService.count.builtin = res.en.length + res.cn.length;
             SearchService.init(allPromptsForSearch);
             SearchService.init(allPromptsForSearch);
           });
           });

+ 13 - 0
middleware.ts

@@ -8,6 +8,17 @@ export const config = {
 
 
 const serverConfig = getServerSideConfig();
 const serverConfig = getServerSideConfig();
 
 
+function getIP(req: NextRequest) {
+  let ip = req.ip ?? req.headers.get("x-real-ip");
+  const forwardedFor = req.headers.get("x-forwarded-for");
+
+  if (!ip && forwardedFor) {
+    ip = forwardedFor.split(",").at(0) ?? "";
+  }
+
+  return ip;
+}
+
 export function middleware(req: NextRequest) {
 export function middleware(req: NextRequest) {
   const accessCode = req.headers.get("access-code");
   const accessCode = req.headers.get("access-code");
   const token = req.headers.get("token");
   const token = req.headers.get("token");
@@ -16,6 +27,8 @@ export function middleware(req: NextRequest) {
   console.log("[Auth] allowed hashed codes: ", [...serverConfig.codes]);
   console.log("[Auth] allowed hashed codes: ", [...serverConfig.codes]);
   console.log("[Auth] got access code:", accessCode);
   console.log("[Auth] got access code:", accessCode);
   console.log("[Auth] hashed access code:", hashedCode);
   console.log("[Auth] hashed access code:", hashedCode);
+  console.log("[User IP] ", getIP(req));
+  console.log("[Time] ", new Date().toLocaleString());
 
 
   if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) {
   if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) {
     return NextResponse.json(
     return NextResponse.json(

+ 3 - 2
package.json

@@ -9,7 +9,8 @@
     "start": "next start",
     "start": "next start",
     "lint": "next lint",
     "lint": "next lint",
     "fetch": "node ./scripts/fetch-prompts.mjs",
     "fetch": "node ./scripts/fetch-prompts.mjs",
-    "prepare": "husky install"
+    "prepare": "husky install",
+    "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev"
   },
   },
   "dependencies": {
   "dependencies": {
     "@hello-pangea/dnd": "^16.2.0",
     "@hello-pangea/dnd": "^16.2.0",
@@ -18,7 +19,7 @@
     "emoji-picker-react": "^4.4.7",
     "emoji-picker-react": "^4.4.7",
     "eventsource-parser": "^0.1.0",
     "eventsource-parser": "^0.1.0",
     "fuse.js": "^6.6.2",
     "fuse.js": "^6.6.2",
-    "next": "^13.2.3",
+    "next": "^13.3.0",
     "node-fetch": "^3.3.1",
     "node-fetch": "^3.3.1",
     "openai": "^3.2.1",
     "openai": "^3.2.1",
     "react": "^18.2.0",
     "react": "^18.2.0",

+ 1 - 0
scripts/.gitignore

@@ -0,0 +1 @@
+proxychains.conf

+ 5 - 0
scripts/init-proxy.sh

@@ -0,0 +1,5 @@
+dir="$(dirname "$0")"
+config=$dir/proxychains.conf
+host_ip=$(grep nameserver /etc/resolv.conf | sed 's/nameserver //')
+cp $dir/proxychains.template.conf $config 
+sed -i "\$s/.*/http $host_ip 7890/" $config

+ 12 - 0
scripts/proxychains.template.conf

@@ -0,0 +1,12 @@
+strict_chain
+proxy_dns 
+
+remote_dns_subnet 224
+
+tcp_read_time_out 15000
+tcp_connect_time_out 8000
+
+localnet 127.0.0.0/255.0.0.0
+
+[ProxyList]
+socks4 	127.0.0.1 9050

+ 75 - 86
yarn.lock

@@ -1099,10 +1099,10 @@
     "@jridgewell/resolve-uri" "3.1.0"
     "@jridgewell/resolve-uri" "3.1.0"
     "@jridgewell/sourcemap-codec" "1.4.14"
     "@jridgewell/sourcemap-codec" "1.4.14"
 
 
-"@next/env@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/env/-/env-13.2.4.tgz#8b763700262b2445140a44a8c8d088cef676dbae"
-  integrity sha512-+Mq3TtpkeeKFZanPturjcXt+KHfKYnLlX6jMLyCrmpq6OOs4i1GqBOAauSkii9QeKCMTYzGppar21JU57b/GEA==
+"@next/env@13.3.0":
+  version "13.3.0"
+  resolved "https://registry.npmmirror.com/@next/env/-/env-13.3.0.tgz#cc2e49f03060a4684ce7ec7fd617a21bc5b9edba"
+  integrity sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ==
 
 
 "@next/eslint-plugin-next@13.2.3":
 "@next/eslint-plugin-next@13.2.3":
   version "13.2.3"
   version "13.2.3"
@@ -1111,70 +1111,50 @@
   dependencies:
   dependencies:
     glob "7.1.7"
     glob "7.1.7"
 
 
-"@next/swc-android-arm-eabi@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.2.4.tgz#758d0403771e549f9cee71cbabc0cb16a6c947c0"
-  integrity sha512-DWlalTSkLjDU11MY11jg17O1gGQzpRccM9Oes2yTqj2DpHndajrXHGxj9HGtJ+idq2k7ImUdJVWS2h2l/EDJOw==
-
-"@next/swc-android-arm64@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.2.4.tgz#834d586523045110d5602e0c8aae9028835ac427"
-  integrity sha512-sRavmUImUCf332Gy+PjIfLkMhiRX1Ez4SI+3vFDRs1N5eXp+uNzjFUK/oLMMOzk6KFSkbiK/3Wt8+dHQR/flNg==
-
-"@next/swc-darwin-arm64@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.2.4.tgz#5006fca179a36ef3a24d293abadec7438dbb48c6"
-  integrity sha512-S6vBl+OrInP47TM3LlYx65betocKUUlTZDDKzTiRDbsRESeyIkBtZ6Qi5uT2zQs4imqllJznVjFd1bXLx3Aa6A==
-
-"@next/swc-darwin-x64@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.2.4.tgz#6549c7c04322766acc3264ccdb3e1b43fcaf7946"
-  integrity sha512-a6LBuoYGcFOPGd4o8TPo7wmv5FnMr+Prz+vYHopEDuhDoMSHOnC+v+Ab4D7F0NMZkvQjEJQdJS3rqgFhlZmKlw==
-
-"@next/swc-freebsd-x64@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.2.4.tgz#0bbe28979e3e868debc2cc06e45e186ce195b7f4"
-  integrity sha512-kkbzKVZGPaXRBPisoAQkh3xh22r+TD+5HwoC5bOkALraJ0dsOQgSMAvzMXKsN3tMzJUPS0tjtRf1cTzrQ0I5vQ==
-
-"@next/swc-linux-arm-gnueabihf@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.2.4.tgz#1d28d2203f5a7427d6e7119d7bcb5fc40959fb3e"
-  integrity sha512-7qA1++UY0fjprqtjBZaOA6cas/7GekpjVsZn/0uHvquuITFCdKGFCsKNBx3S0Rpxmx6WYo0GcmhNRM9ru08BGg==
-
-"@next/swc-linux-arm64-gnu@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.2.4.tgz#eb26448190948cdf4c44b8f34110a3ecea32f1d0"
-  integrity sha512-xzYZdAeq883MwXgcwc72hqo/F/dwUxCukpDOkx/j1HTq/J0wJthMGjinN9wH5bPR98Mfeh1MZJ91WWPnZOedOg==
-
-"@next/swc-linux-arm64-musl@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.2.4.tgz#c4227c0acd94a420bb14924820710e6284d234d3"
-  integrity sha512-8rXr3WfmqSiYkb71qzuDP6I6R2T2tpkmf83elDN8z783N9nvTJf2E7eLx86wu2OJCi4T05nuxCsh4IOU3LQ5xw==
-
-"@next/swc-linux-x64-gnu@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.2.4.tgz#6bcb540944ee9b0209b33bfc23b240c2044dfc3e"
-  integrity sha512-Ngxh51zGSlYJ4EfpKG4LI6WfquulNdtmHg1yuOYlaAr33KyPJp4HeN/tivBnAHcZkoNy0hh/SbwDyCnz5PFJQQ==
-
-"@next/swc-linux-x64-musl@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.2.4.tgz#ce21e43251eaf09a09df39372b2c3e38028c30ff"
-  integrity sha512-gOvwIYoSxd+j14LOcvJr+ekd9fwYT1RyMAHOp7znA10+l40wkFiMONPLWiZuHxfRk+Dy7YdNdDh3ImumvL6VwA==
-
-"@next/swc-win32-arm64-msvc@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.2.4.tgz#68220063d8e5e082f5465498675640dedb670ff1"
-  integrity sha512-q3NJzcfClgBm4HvdcnoEncmztxrA5GXqKeiZ/hADvC56pwNALt3ngDC6t6qr1YW9V/EPDxCYeaX4zYxHciW4Dw==
-
-"@next/swc-win32-ia32-msvc@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.2.4.tgz#7c120ab54a081be9566df310bed834f168252990"
-  integrity sha512-/eZ5ncmHUYtD2fc6EUmAIZlAJnVT2YmxDsKs1Ourx0ttTtvtma/WKlMV5NoUsyOez0f9ExLyOpeCoz5aj+MPXw==
-
-"@next/swc-win32-x64-msvc@13.2.4":
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.2.4.tgz#5abda92fe12b9829bf7951c4a221282c56041144"
-  integrity sha512-0MffFmyv7tBLlji01qc0IaPP/LVExzvj7/R5x1Jph1bTAIj4Vu81yFQWHHQAP6r4ff9Ukj1mBK6MDNVXm7Tcvw==
+"@next/swc-darwin-arm64@13.3.0":
+  version "13.3.0"
+  resolved "https://registry.npmmirror.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.0.tgz#38f18e0639cd4c7edc6a38d4b83fe00f38eea4f2"
+  integrity sha512-DmIQCNq6JtccLPPBzf0dgh2vzMWt5wjxbP71pCi5EWpWYE3MsP6FcRXi4MlAmFNDQOfcFXR2r7kBeG1LpZUh1w==
+
+"@next/swc-darwin-x64@13.3.0":
+  version "13.3.0"
+  resolved "https://registry.npmmirror.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.0.tgz#b670ed1fd1d231aa21279173ec52e3ad56dc6aeb"
+  integrity sha512-oQoqFa88OGgwnYlnAGHVct618FRI/749se0N3S8t9Bzdv5CRbscnO0RcX901+YnNK4Q6yeiizfgO3b7kogtsZg==
+
+"@next/swc-linux-arm64-gnu@13.3.0":
+  version "13.3.0"
+  resolved "https://registry.npmmirror.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.0.tgz#b114935f6b4c94c123f6cac55a4823d483209ba5"
+  integrity sha512-Wzz2p/WqAJUqTVoLo6H18WMeAXo3i+9DkPDae4oQG8LMloJ3if4NEZTnOnTUlro6cq+S/W4pTGa97nWTrOjbGw==
+
+"@next/swc-linux-arm64-musl@13.3.0":
+  version "13.3.0"
+  resolved "https://registry.npmmirror.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.0.tgz#67a57309f8761c7d00d629d6785d56ed0567a0d2"
+  integrity sha512-xPVrIQOQo9WXJYgmoTlMnAD/HlR/1e1ZIWGbwIzEirXBVBqMARUulBEIKdC19zuvoJ477qZJgBDCKtKEykCpyQ==
+
+"@next/swc-linux-x64-gnu@13.3.0":
+  version "13.3.0"
+  resolved "https://registry.npmmirror.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.3.0.tgz#11bd2bea7c00b40be111c0dd16e71171f3792086"
+  integrity sha512-jOFlpGuPD7W2tuXVJP4wt9a3cpNxWAPcloq5EfMJRiXsBBOjLVFZA7boXYxEBzSVgUiVVr1V9T0HFM7pULJ1qA==
+
+"@next/swc-linux-x64-musl@13.3.0":
+  version "13.3.0"
+  resolved "https://registry.npmmirror.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.3.0.tgz#d57e99f85890799b78719c3ea32a4624de8d701b"
+  integrity sha512-2OwKlzaBgmuet9XYHc3KwsEilzb04F540rlRXkAcjMHL7eCxB7uZIGtsVvKOnQLvC/elrUegwSw1+5f7WmfyOw==
+
+"@next/swc-win32-arm64-msvc@13.3.0":
+  version "13.3.0"
+  resolved "https://registry.npmmirror.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.0.tgz#0c209aa35d1c88b01e78259a89cd68f4139b5093"
+  integrity sha512-OeHiA6YEvndxT46g+rzFK/MQTfftKxJmzslERMu9LDdC6Kez0bdrgEYed5eXFK2Z1viKZJCGRlhd06rBusyztA==
+
+"@next/swc-win32-ia32-msvc@13.3.0":
+  version "13.3.0"
+  resolved "https://registry.npmmirror.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.0.tgz#52ae74da1dd6d840c3743923367d27ed013803dd"
+  integrity sha512-4aB7K9mcVK1lYEzpOpqWrXHEZympU3oK65fnNcY1Qc4HLJFLJj8AViuqQd4jjjPNuV4sl8jAwTz3gN5VNGWB7w==
+
+"@next/swc-win32-x64-msvc@13.3.0":
+  version "13.3.0"
+  resolved "https://registry.npmmirror.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.0.tgz#db7b55fee834dc8c2c484c696469e65bae2ee770"
+  integrity sha512-Reer6rkLLcoOvB0dd66+Y7WrWVFH7sEEkF/4bJCIfsSKnTStTYaHtwIJAwbqnt9I392Tqvku0KkoqZOryWV9LQ==
 
 
 "@nodelib/fs.scandir@2.1.5":
 "@nodelib/fs.scandir@2.1.5":
   version "2.1.5"
   version "2.1.5"
@@ -1730,6 +1710,13 @@ browserslist@^4.21.3, browserslist@^4.21.5:
     node-releases "^2.0.8"
     node-releases "^2.0.8"
     update-browserslist-db "^1.0.10"
     update-browserslist-db "^1.0.10"
 
 
+busboy@1.6.0:
+  version "1.6.0"
+  resolved "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
+  integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
+  dependencies:
+    streamsearch "^1.1.0"
+
 call-bind@^1.0.0, call-bind@^1.0.2:
 call-bind@^1.0.0, call-bind@^1.0.2:
   version "1.0.2"
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
   resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
@@ -3937,30 +3924,27 @@ natural-compare@^1.4.0:
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
   integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
   integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
 
 
-next@^13.2.3:
-  version "13.2.4"
-  resolved "https://registry.yarnpkg.com/next/-/next-13.2.4.tgz#2363330392b0f7da02ab41301f60857ffa7f67d6"
-  integrity sha512-g1I30317cThkEpvzfXujf0O4wtaQHtDCLhlivwlTJ885Ld+eOgcz7r3TGQzeU+cSRoNHtD8tsJgzxVdYojFssw==
+next@^13.3.0:
+  version "13.3.0"
+  resolved "https://registry.npmmirror.com/next/-/next-13.3.0.tgz#40632d303d74fc8521faa0a5bf4a033a392749b1"
+  integrity sha512-OVTw8MpIPa12+DCUkPqRGPS3thlJPcwae2ZL4xti3iBff27goH024xy4q2lhlsdoYiKOi8Kz6uJoLW/GXwgfOA==
   dependencies:
   dependencies:
-    "@next/env" "13.2.4"
+    "@next/env" "13.3.0"
     "@swc/helpers" "0.4.14"
     "@swc/helpers" "0.4.14"
+    busboy "1.6.0"
     caniuse-lite "^1.0.30001406"
     caniuse-lite "^1.0.30001406"
     postcss "8.4.14"
     postcss "8.4.14"
     styled-jsx "5.1.1"
     styled-jsx "5.1.1"
   optionalDependencies:
   optionalDependencies:
-    "@next/swc-android-arm-eabi" "13.2.4"
-    "@next/swc-android-arm64" "13.2.4"
-    "@next/swc-darwin-arm64" "13.2.4"
-    "@next/swc-darwin-x64" "13.2.4"
-    "@next/swc-freebsd-x64" "13.2.4"
-    "@next/swc-linux-arm-gnueabihf" "13.2.4"
-    "@next/swc-linux-arm64-gnu" "13.2.4"
-    "@next/swc-linux-arm64-musl" "13.2.4"
-    "@next/swc-linux-x64-gnu" "13.2.4"
-    "@next/swc-linux-x64-musl" "13.2.4"
-    "@next/swc-win32-arm64-msvc" "13.2.4"
-    "@next/swc-win32-ia32-msvc" "13.2.4"
-    "@next/swc-win32-x64-msvc" "13.2.4"
+    "@next/swc-darwin-arm64" "13.3.0"
+    "@next/swc-darwin-x64" "13.3.0"
+    "@next/swc-linux-arm64-gnu" "13.3.0"
+    "@next/swc-linux-arm64-musl" "13.3.0"
+    "@next/swc-linux-x64-gnu" "13.3.0"
+    "@next/swc-linux-x64-musl" "13.3.0"
+    "@next/swc-win32-arm64-msvc" "13.3.0"
+    "@next/swc-win32-ia32-msvc" "13.3.0"
+    "@next/swc-win32-x64-msvc" "13.3.0"
 
 
 node-domexception@^1.0.0:
 node-domexception@^1.0.0:
   version "1.0.0"
   version "1.0.0"
@@ -4668,6 +4652,11 @@ stop-iteration-iterator@^1.0.0:
   dependencies:
   dependencies:
     internal-slot "^1.0.4"
     internal-slot "^1.0.4"
 
 
+streamsearch@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
+  integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
+
 string-argv@^0.3.1:
 string-argv@^0.3.1:
   version "0.3.1"
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
   resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"