From 27828d9ca86112c1d179c906197ed3158c9a9f68 Mon Sep 17 00:00:00 2001 From: kosette <35268640+Kosette@users.noreply.github.com> Date: Fri, 6 Sep 2024 23:07:01 +0800 Subject: [PATCH 1/7] fix: update package version --- src-tauri/tauri.conf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 78807a2c5..0f2a84a53 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "NextChat", - "version": "2.14.2" + "version": "2.15.0" }, "tauri": { "allowlist": { From c1b74201e46f64c15dd63a2e876e87b601dbc81f Mon Sep 17 00:00:00 2001 From: "l.tingting" Date: Sat, 7 Sep 2024 01:42:56 +0800 Subject: [PATCH 2/7] add chatgpt-4o-latest --- app/api/openai.ts | 4 +++- app/client/platforms/openai.ts | 4 +++- app/components/emoji.tsx | 3 ++- app/config/server.ts | 7 +++++-- app/constant.ts | 2 ++ app/store/chat.ts | 5 +++-- 6 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/api/openai.ts b/app/api/openai.ts index 0059ff8b4..7dfd84e17 100644 --- a/app/api/openai.ts +++ b/app/api/openai.ts @@ -13,7 +13,9 @@ function getModels(remoteModelRes: OpenAIListModelResponse) { if (config.disableGPT4) { remoteModelRes.data = remoteModelRes.data.filter( - (m) => !m.id.startsWith("gpt-4") || m.id.startsWith("gpt-4o-mini"), + (m) => + !(m.id.startsWith("gpt-4") || m.id.startsWith("chatgpt-4o")) || + m.id.startsWith("gpt-4o-mini"), ); } diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index b3b306d1d..51a1200d3 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -407,7 +407,9 @@ export class ChatGPTApi implements LLMApi { }); const resJson = (await res.json()) as OpenAIListModelResponse; - const chatModels = resJson.data?.filter((m) => m.id.startsWith("gpt-")); + const chatModels = resJson.data?.filter( + (m) => m.id.startsWith("gpt-") || m.id.startsWith("chatgpt-"), + ); console.log("[Models]", chatModels); if (!chatModels) { diff --git a/app/components/emoji.tsx b/app/components/emoji.tsx index 3b1f5e751..6db746c46 100644 --- a/app/components/emoji.tsx +++ b/app/components/emoji.tsx @@ -36,7 +36,8 @@ export function Avatar(props: { model?: ModelType; avatar?: string }) { if (props.model) { return (
- {props.model?.startsWith("gpt-4") ? ( + {props.model?.startsWith("gpt-4") || + props.model?.startsWith("chatgpt-4o") ? ( ) : ( diff --git a/app/config/server.ts b/app/config/server.ts index e953af369..676b0174f 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -120,12 +120,15 @@ export const getServerSideConfig = () => { if (disableGPT4) { if (customModels) customModels += ","; customModels += DEFAULT_MODELS.filter( - (m) => m.name.startsWith("gpt-4") && !m.name.startsWith("gpt-4o-mini"), + (m) => + (m.name.startsWith("gpt-4") || m.name.startsWith("chatgpt-4o")) && + !m.name.startsWith("gpt-4o-mini"), ) .map((m) => "-" + m.name) .join(","); if ( - defaultModel.startsWith("gpt-4") && + (defaultModel.startsWith("gpt-4") || + defaultModel.startsWith("chatgpt-4o")) && !defaultModel.startsWith("gpt-4o-mini") ) defaultModel = ""; diff --git a/app/constant.ts b/app/constant.ts index 90557c16c..eb82d3c66 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -246,6 +246,7 @@ export const KnowledgeCutOffDate: Record = { "gpt-4o": "2023-10", "gpt-4o-2024-05-13": "2023-10", "gpt-4o-2024-08-06": "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", @@ -268,6 +269,7 @@ const openaiModels = [ "gpt-4o", "gpt-4o-2024-05-13", "gpt-4o-2024-08-06", + "chatgpt-4o-latest", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", "gpt-4-vision-preview", diff --git a/app/store/chat.ts b/app/store/chat.ts index 8b0cc39eb..96cc40252 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -106,7 +106,7 @@ function createEmptySession(): ChatSession { function getSummarizeModel(currentModel: string) { // if it is using gpt-* models, force to use 4o-mini to summarize - if (currentModel.startsWith("gpt")) { + if (currentModel.startsWith("gpt") || currentModel.startsWith("chatgpt")) { const configStore = useAppConfig.getState(); const accessStore = useAccessStore.getState(); const allModel = collectModelsWithDefaultModel( @@ -476,7 +476,8 @@ export const useChatStore = createPersistStore( // system prompts, to get close to OpenAI Web ChatGPT const shouldInjectSystemPrompts = modelConfig.enableInjectSystemPrompts && - session.mask.modelConfig.model.startsWith("gpt-"); + (session.mask.modelConfig.model.startsWith("gpt-") || + session.mask.modelConfig.model.startsWith("chatgpt-")); var systemPrompts: ChatMessage[] = []; systemPrompts = shouldInjectSystemPrompts From cf0c057164cb84078bc729e6185cae6e6c1c0906 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Sat, 7 Sep 2024 13:00:55 +0800 Subject: [PATCH 3/7] hotfix Mermaid can not render. close #5374 --- app/components/markdown.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 4b9e608c9..dc11c572d 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -163,7 +163,7 @@ export function PreCode(props: { children: any }) { ); } -function CustomCode(props: { children: any }) { +function CustomCode(props: { children: any; className?: string }) { const ref = useRef(null); const [collapsed, setCollapsed] = useState(true); const [showToggle, setShowToggle] = useState(false); @@ -182,6 +182,7 @@ function CustomCode(props: { children: any }) { return ( <> Date: Sat, 7 Sep 2024 16:24:52 +0800 Subject: [PATCH 4/7] Add crossOrigin="use-credentials" for site.webmanifest Add `crossOrigin="use-credentials"` to the `` element for `site.webmanifest` when the site is behind a proxy with authentication. --- app/layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/layout.tsx b/app/layout.tsx index abefd69c1..fa087636a 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -41,7 +41,7 @@ export default function RootLayout({ name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> - + From db58ca6c1d59dc6410c1fa55116926a6ec5fb1c6 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Sat, 7 Sep 2024 21:32:18 +0800 Subject: [PATCH 5/7] fix(#5378): default plugin ids to empty array --- app/client/platforms/anthropic.ts | 2 +- app/client/platforms/moonshot.ts | 2 +- app/client/platforms/openai.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/client/platforms/anthropic.ts b/app/client/platforms/anthropic.ts index fce675a16..7dd39c9cd 100644 --- a/app/client/platforms/anthropic.ts +++ b/app/client/platforms/anthropic.ts @@ -203,7 +203,7 @@ export class ClaudeApi implements LLMApi { const [tools, funcs] = usePluginStore .getState() .getAsTools( - useChatStore.getState().currentSession().mask?.plugin as string[], + useChatStore.getState().currentSession().mask?.plugin || [], ); return stream( path, diff --git a/app/client/platforms/moonshot.ts b/app/client/platforms/moonshot.ts index c38d3317b..cd10d2f6c 100644 --- a/app/client/platforms/moonshot.ts +++ b/app/client/platforms/moonshot.ts @@ -125,7 +125,7 @@ export class MoonshotApi implements LLMApi { const [tools, funcs] = usePluginStore .getState() .getAsTools( - useChatStore.getState().currentSession().mask?.plugin as string[], + useChatStore.getState().currentSession().mask?.plugin || [], ); return stream( chatPath, diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index b3b306d1d..780ef0676 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -244,7 +244,7 @@ export class ChatGPTApi implements LLMApi { const [tools, funcs] = usePluginStore .getState() .getAsTools( - useChatStore.getState().currentSession().mask?.plugin as string[], + useChatStore.getState().currentSession().mask?.plugin || [], ); // console.log("getAsTools", tools, funcs); stream( From 23ac2efd89139d3112981680b7dd8c2e1b283e3a Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Sat, 7 Sep 2024 22:12:42 +0800 Subject: [PATCH 6/7] hotfix and update version --- app/store/plugin.ts | 2 +- src-tauri/tauri.conf.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/store/plugin.ts b/app/store/plugin.ts index 74f0fbe17..2356c6db0 100644 --- a/app/store/plugin.ts +++ b/app/store/plugin.ts @@ -199,7 +199,7 @@ export const usePluginStore = createPersistStore( getAsTools(ids: string[]) { const plugins = get().plugins; - const selected = ids + const selected = (ids || []) .map((id) => plugins[id]) .filter((i) => i) .map((p) => FunctionToolService.add(p)); diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 0f2a84a53..78835d24d 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -9,7 +9,7 @@ }, "package": { "productName": "NextChat", - "version": "2.15.0" + "version": "2.15.1" }, "tauri": { "allowlist": { From 992c3a5d3a9fd08ecc46a26d12b91f9a1fd87c1a Mon Sep 17 00:00:00 2001 From: Dogtiti <499960698@qq.com> Date: Sun, 8 Sep 2024 13:23:40 +0800 Subject: [PATCH 7/7] fix: safaLocalStorage --- app/components/chat.tsx | 7 ++-- app/components/error.tsx | 4 +-- app/components/mask.tsx | 15 ++------- app/locales/index.ts | 13 +++----- app/store/chat.ts | 10 +++++- app/store/mask.ts | 10 +++++- app/utils.ts | 60 ++++++++++++++++++++++++++++++++++ app/utils/indexedDB-storage.ts | 3 ++ 8 files changed, 96 insertions(+), 26 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index dad1933ac..77d351f30 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -67,6 +67,7 @@ import { isVisionModel, isDalle3, showPlugins, + safeLocalStorage, } from "../utils"; import { uploadImage as uploadImageRemote } from "@/app/utils/chat"; @@ -109,6 +110,8 @@ import { getClientConfig } from "../config/client"; import { useAllModels } from "../utils/hooks"; import { MultimodalContent } from "../client/api"; +const localStorage = safeLocalStorage(); + const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { loading: () => , }); @@ -941,7 +944,7 @@ function _Chat() { .onUserInput(userInput, attachImages) .then(() => setIsLoading(false)); setAttachImages([]); - localStorage.setItem(LAST_INPUT_KEY, userInput); + chatStore.setLastInput(userInput); setUserInput(""); setPromptHints([]); if (!isMobileScreen) inputRef.current?.focus(); @@ -1007,7 +1010,7 @@ function _Chat() { userInput.length <= 0 && !(e.metaKey || e.altKey || e.ctrlKey) ) { - setUserInput(localStorage.getItem(LAST_INPUT_KEY) ?? ""); + setUserInput(chatStore.lastInput ?? ""); e.preventDefault(); return; } diff --git a/app/components/error.tsx b/app/components/error.tsx index c90997d11..4fcf759c1 100644 --- a/app/components/error.tsx +++ b/app/components/error.tsx @@ -8,6 +8,7 @@ import { ISSUE_URL } from "../constant"; import Locale from "../locales"; import { showConfirm } from "./ui-lib"; import { useSyncStore } from "../store/sync"; +import { useChatStore } from "../store/chat"; interface IErrorBoundaryState { hasError: boolean; @@ -30,8 +31,7 @@ export class ErrorBoundary extends React.Component { try { useSyncStore.getState().export(); } finally { - localStorage.clear(); - location.reload(); + useChatStore.getState().clearAllData(); } } diff --git a/app/components/mask.tsx b/app/components/mask.tsx index 62503c37a..ee6c7da97 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -426,16 +426,7 @@ export function MaskPage() { const maskStore = useMaskStore(); const chatStore = useChatStore(); - const [filterLang, setFilterLang] = useState( - () => localStorage.getItem("Mask-language") as Lang | undefined, - ); - useEffect(() => { - if (filterLang) { - localStorage.setItem("Mask-language", filterLang); - } else { - localStorage.removeItem("Mask-language"); - } - }, [filterLang]); + const filterLang = maskStore.language; const allMasks = maskStore .getAll() @@ -542,9 +533,9 @@ export function MaskPage() { onChange={(e) => { const value = e.currentTarget.value; if (value === Locale.Settings.Lang.All) { - setFilterLang(undefined); + maskStore.setLanguage(undefined); } else { - setFilterLang(value as Lang); + maskStore.setLanguage(value as Lang); } }} > diff --git a/app/locales/index.ts b/app/locales/index.ts index acdb3e878..ff7e3a262 100644 --- a/app/locales/index.ts +++ b/app/locales/index.ts @@ -18,10 +18,13 @@ import ar from "./ar"; import bn from "./bn"; import sk from "./sk"; import { merge } from "../utils/merge"; +import { safeLocalStorage } from "@/app/utils"; import type { LocaleType } from "./cn"; export type { LocaleType, PartialLocaleType } from "./cn"; +const localStorage = safeLocalStorage(); + const ALL_LANGS = { cn, en, @@ -82,17 +85,11 @@ merge(fallbackLang, targetLang); export default fallbackLang as LocaleType; function getItem(key: string) { - try { - return localStorage.getItem(key); - } catch { - return null; - } + return localStorage.getItem(key); } function setItem(key: string, value: string) { - try { - localStorage.setItem(key, value); - } catch {} + localStorage.setItem(key, value); } function getLanguage() { diff --git a/app/store/chat.ts b/app/store/chat.ts index 8b0cc39eb..ef483c2e7 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -26,9 +26,11 @@ import { nanoid } from "nanoid"; import { createPersistStore } from "../utils/store"; import { collectModelsWithDefaultModel } from "../utils/model"; import { useAccessStore } from "./access"; -import { isDalle3 } from "../utils"; +import { isDalle3, safeLocalStorage } from "../utils"; import { indexedDBStorage } from "@/app/utils/indexedDB-storage"; +const localStorage = safeLocalStorage(); + export type ChatMessageTool = { id: string; index?: number; @@ -179,6 +181,7 @@ function fillTemplateWith(input: string, modelConfig: ModelConfig) { const DEFAULT_CHAT_STATE = { sessions: [createEmptySession()], currentSessionIndex: 0, + lastInput: "", }; export const useChatStore = createPersistStore( @@ -700,6 +703,11 @@ export const useChatStore = createPersistStore( localStorage.clear(); location.reload(); }, + setLastInput(lastInput: string) { + set({ + lastInput, + }); + }, }; return methods; diff --git a/app/store/mask.ts b/app/store/mask.ts index 083121b65..0c74a892e 100644 --- a/app/store/mask.ts +++ b/app/store/mask.ts @@ -23,9 +23,12 @@ export type Mask = { export const DEFAULT_MASK_STATE = { masks: {} as Record, + language: undefined as Lang | undefined, }; -export type MaskState = typeof DEFAULT_MASK_STATE; +export type MaskState = typeof DEFAULT_MASK_STATE & { + language?: Lang | undefined; +}; export const DEFAULT_MASK_AVATAR = "gpt-bot"; export const createEmptyMask = () => @@ -102,6 +105,11 @@ export const useMaskStore = createPersistStore( search(text: string) { return Object.values(get().masks); }, + setLanguage(language: Lang | undefined) { + set({ + language, + }); + }, }), { name: StoreKey.Mask, diff --git a/app/utils.ts b/app/utils.ts index 60041ba06..bf7450929 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -318,3 +318,63 @@ export function adapter(config: Record) { : path; return fetch(fetchUrl as string, { ...rest, responseType: "text" }); } + +export function safeLocalStorage(): { + getItem: (key: string) => string | null; + setItem: (key: string, value: string) => void; + removeItem: (key: string) => void; + clear: () => void; +} { + let storage: Storage | null; + + try { + if (typeof window !== "undefined" && window.localStorage) { + storage = window.localStorage; + } else { + storage = null; + } + } catch (e) { + console.error("localStorage is not available:", e); + storage = null; + } + + return { + getItem(key: string): string | null { + if (storage) { + return storage.getItem(key); + } else { + console.warn( + `Attempted to get item "${key}" from localStorage, but localStorage is not available.`, + ); + return null; + } + }, + setItem(key: string, value: string): void { + if (storage) { + storage.setItem(key, value); + } else { + console.warn( + `Attempted to set item "${key}" in localStorage, but localStorage is not available.`, + ); + } + }, + removeItem(key: string): void { + if (storage) { + storage.removeItem(key); + } else { + console.warn( + `Attempted to remove item "${key}" from localStorage, but localStorage is not available.`, + ); + } + }, + clear(): void { + if (storage) { + storage.clear(); + } else { + console.warn( + "Attempted to clear localStorage, but localStorage is not available.", + ); + } + }, + }; +} diff --git a/app/utils/indexedDB-storage.ts b/app/utils/indexedDB-storage.ts index da3094550..51417e9f3 100644 --- a/app/utils/indexedDB-storage.ts +++ b/app/utils/indexedDB-storage.ts @@ -1,5 +1,8 @@ import { StateStorage } from "zustand/middleware"; import { get, set, del, clear } from "idb-keyval"; +import { safeLocalStorage } from "@/app/utils"; + +const localStorage = safeLocalStorage(); class IndexedDBStorage implements StateStorage { public async getItem(name: string): Promise {