mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-11-19 23:43:45 +08:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { StoreKey } from "../constant";
|
||||
import { BOT_HELLO } from "./chat";
|
||||
|
||||
export interface AccessControlStore {
|
||||
accessCode: string;
|
||||
token: string;
|
||||
|
||||
needCode: boolean;
|
||||
hideUserApiKey: boolean;
|
||||
openaiUrl: string;
|
||||
|
||||
updateToken: (_: string) => void;
|
||||
updateCode: (_: string) => void;
|
||||
@@ -14,8 +18,6 @@ export interface AccessControlStore {
|
||||
fetch: () => void;
|
||||
}
|
||||
|
||||
export const ACCESS_KEY = "access-control";
|
||||
|
||||
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
|
||||
|
||||
export const useAccessStore = create<AccessControlStore>()(
|
||||
@@ -24,16 +26,19 @@ export const useAccessStore = create<AccessControlStore>()(
|
||||
token: "",
|
||||
accessCode: "",
|
||||
needCode: true,
|
||||
hideUserApiKey: false,
|
||||
openaiUrl: "/api/openai/",
|
||||
|
||||
enabledAccessControl() {
|
||||
get().fetch();
|
||||
|
||||
return get().needCode;
|
||||
},
|
||||
updateCode(code: string) {
|
||||
set((state) => ({ accessCode: code }));
|
||||
set(() => ({ accessCode: code }));
|
||||
},
|
||||
updateToken(token: string) {
|
||||
set((state) => ({ token }));
|
||||
set(() => ({ token }));
|
||||
},
|
||||
isAuthorized() {
|
||||
// has token or has code or disabled access control
|
||||
@@ -52,6 +57,10 @@ export const useAccessStore = create<AccessControlStore>()(
|
||||
.then((res: DangerConfig) => {
|
||||
console.log("[Config] got config from server", res);
|
||||
set(() => ({ ...res }));
|
||||
|
||||
if ((res as any).botHello) {
|
||||
BOT_HELLO.content = (res as any).botHello;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.error("[Config] failed to fetch config");
|
||||
@@ -62,7 +71,7 @@ export const useAccessStore = create<AccessControlStore>()(
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: ACCESS_KEY,
|
||||
name: StoreKey.Access,
|
||||
version: 1,
|
||||
},
|
||||
),
|
||||
|
||||
@@ -11,7 +11,9 @@ import { isMobileScreen, trimTopic } from "../utils";
|
||||
|
||||
import Locale from "../locales";
|
||||
import { showToast } from "../components/ui-lib";
|
||||
import { ModelType, useAppConfig } from "./config";
|
||||
import { DEFAULT_CONFIG, ModelConfig, ModelType, useAppConfig } from "./config";
|
||||
import { createEmptyMask, Mask } from "./mask";
|
||||
import { StoreKey } from "../constant";
|
||||
|
||||
export type Message = ChatCompletionResponseMessage & {
|
||||
date: string;
|
||||
@@ -41,51 +43,50 @@ export interface ChatStat {
|
||||
|
||||
export interface ChatSession {
|
||||
id: number;
|
||||
|
||||
topic: string;
|
||||
sendMemory: boolean;
|
||||
|
||||
memoryPrompt: string;
|
||||
context: Message[];
|
||||
messages: Message[];
|
||||
stat: ChatStat;
|
||||
lastUpdate: string;
|
||||
lastUpdate: number;
|
||||
lastSummarizeIndex: number;
|
||||
|
||||
mask: Mask;
|
||||
}
|
||||
|
||||
const DEFAULT_TOPIC = Locale.Store.DefaultTopic;
|
||||
export const DEFAULT_TOPIC = Locale.Store.DefaultTopic;
|
||||
export const BOT_HELLO: Message = createMessage({
|
||||
role: "assistant",
|
||||
content: Locale.Store.BotHello,
|
||||
});
|
||||
|
||||
function createEmptySession(): ChatSession {
|
||||
const createDate = new Date().toLocaleString();
|
||||
|
||||
return {
|
||||
id: Date.now(),
|
||||
id: Date.now() + Math.random(),
|
||||
topic: DEFAULT_TOPIC,
|
||||
sendMemory: true,
|
||||
memoryPrompt: "",
|
||||
context: [],
|
||||
messages: [],
|
||||
stat: {
|
||||
tokenCount: 0,
|
||||
wordCount: 0,
|
||||
charCount: 0,
|
||||
},
|
||||
lastUpdate: createDate,
|
||||
lastUpdate: Date.now(),
|
||||
lastSummarizeIndex: 0,
|
||||
mask: createEmptyMask(),
|
||||
};
|
||||
}
|
||||
|
||||
interface ChatStore {
|
||||
sessions: ChatSession[];
|
||||
currentSessionIndex: number;
|
||||
globalId: number;
|
||||
clearSessions: () => void;
|
||||
removeSession: (index: number) => void;
|
||||
moveSession: (from: number, to: number) => void;
|
||||
selectSession: (index: number) => void;
|
||||
newSession: () => void;
|
||||
deleteSession: (index?: number) => void;
|
||||
newSession: (mask?: Mask) => void;
|
||||
deleteSession: (index: number) => void;
|
||||
currentSession: () => ChatSession;
|
||||
onNewMessage: (message: Message) => void;
|
||||
onUserInput: (content: string) => Promise<void>;
|
||||
@@ -108,13 +109,12 @@ function countMessages(msgs: Message[]) {
|
||||
return msgs.reduce((pre, cur) => pre + cur.content.length, 0);
|
||||
}
|
||||
|
||||
const LOCAL_KEY = "chat-next-web-store";
|
||||
|
||||
export const useChatStore = create<ChatStore>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
sessions: [createEmptySession()],
|
||||
currentSessionIndex: 0,
|
||||
globalId: 0,
|
||||
|
||||
clearSessions() {
|
||||
set(() => ({
|
||||
@@ -129,31 +129,6 @@ export const useChatStore = create<ChatStore>()(
|
||||
});
|
||||
},
|
||||
|
||||
removeSession(index: number) {
|
||||
set((state) => {
|
||||
let nextIndex = state.currentSessionIndex;
|
||||
const sessions = state.sessions;
|
||||
|
||||
if (sessions.length === 1) {
|
||||
return {
|
||||
currentSessionIndex: 0,
|
||||
sessions: [createEmptySession()],
|
||||
};
|
||||
}
|
||||
|
||||
sessions.splice(index, 1);
|
||||
|
||||
if (nextIndex === index) {
|
||||
nextIndex -= 1;
|
||||
}
|
||||
|
||||
return {
|
||||
currentSessionIndex: nextIndex,
|
||||
sessions,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
moveSession(from: number, to: number) {
|
||||
set((state) => {
|
||||
const { sessions, currentSessionIndex: oldIndex } = state;
|
||||
@@ -179,38 +154,63 @@ export const useChatStore = create<ChatStore>()(
|
||||
});
|
||||
},
|
||||
|
||||
newSession() {
|
||||
newSession(mask) {
|
||||
const session = createEmptySession();
|
||||
|
||||
set(() => ({ globalId: get().globalId + 1 }));
|
||||
session.id = get().globalId;
|
||||
|
||||
if (mask) {
|
||||
session.mask = { ...mask };
|
||||
session.topic = mask.name;
|
||||
}
|
||||
|
||||
set((state) => ({
|
||||
currentSessionIndex: 0,
|
||||
sessions: [createEmptySession()].concat(state.sessions),
|
||||
sessions: [session].concat(state.sessions),
|
||||
}));
|
||||
},
|
||||
|
||||
deleteSession(i?: number) {
|
||||
const deletedSession = get().currentSession();
|
||||
const index = i ?? get().currentSessionIndex;
|
||||
const isLastSession = get().sessions.length === 1;
|
||||
if (!isMobileScreen() || confirm(Locale.Home.DeleteChat)) {
|
||||
get().removeSession(index);
|
||||
deleteSession(index) {
|
||||
const deletingLastSession = get().sessions.length === 1;
|
||||
const deletedSession = get().sessions.at(index);
|
||||
|
||||
showToast(
|
||||
Locale.Home.DeleteToast,
|
||||
{
|
||||
text: Locale.Home.Revert,
|
||||
onClick() {
|
||||
set((state) => ({
|
||||
sessions: state.sessions
|
||||
.slice(0, index)
|
||||
.concat([deletedSession])
|
||||
.concat(
|
||||
state.sessions.slice(index + Number(isLastSession)),
|
||||
),
|
||||
}));
|
||||
},
|
||||
},
|
||||
5000,
|
||||
);
|
||||
if (!deletedSession) return;
|
||||
|
||||
const sessions = get().sessions.slice();
|
||||
sessions.splice(index, 1);
|
||||
|
||||
let nextIndex = Math.min(
|
||||
get().currentSessionIndex,
|
||||
sessions.length - 1,
|
||||
);
|
||||
|
||||
if (deletingLastSession) {
|
||||
nextIndex = 0;
|
||||
sessions.push(createEmptySession());
|
||||
}
|
||||
|
||||
// for undo delete action
|
||||
const restoreState = {
|
||||
currentSessionIndex: get().currentSessionIndex,
|
||||
sessions: get().sessions.slice(),
|
||||
};
|
||||
|
||||
set(() => ({
|
||||
currentSessionIndex: nextIndex,
|
||||
sessions,
|
||||
}));
|
||||
|
||||
showToast(
|
||||
Locale.Home.DeleteToast,
|
||||
{
|
||||
text: Locale.Home.Revert,
|
||||
onClick() {
|
||||
set(() => restoreState);
|
||||
},
|
||||
},
|
||||
5000,
|
||||
);
|
||||
},
|
||||
|
||||
currentSession() {
|
||||
@@ -229,13 +229,16 @@ export const useChatStore = create<ChatStore>()(
|
||||
|
||||
onNewMessage(message) {
|
||||
get().updateCurrentSession((session) => {
|
||||
session.lastUpdate = new Date().toLocaleString();
|
||||
session.lastUpdate = Date.now();
|
||||
});
|
||||
get().updateStat(message);
|
||||
get().summarizeSession();
|
||||
},
|
||||
|
||||
async onUserInput(content) {
|
||||
const session = get().currentSession();
|
||||
const modelConfig = session.mask.modelConfig;
|
||||
|
||||
const userMessage: Message = createMessage({
|
||||
role: "user",
|
||||
content,
|
||||
@@ -245,7 +248,7 @@ export const useChatStore = create<ChatStore>()(
|
||||
role: "assistant",
|
||||
streaming: true,
|
||||
id: userMessage.id! + 1,
|
||||
model: useAppConfig.getState().modelConfig.model,
|
||||
model: modelConfig.model,
|
||||
});
|
||||
|
||||
// get recent messages
|
||||
@@ -279,14 +282,16 @@ export const useChatStore = create<ChatStore>()(
|
||||
}
|
||||
},
|
||||
onError(error, statusCode) {
|
||||
const isAborted = error.message.includes("aborted");
|
||||
if (statusCode === 401) {
|
||||
botMessage.content = Locale.Error.Unauthorized;
|
||||
} else if (!error.message.includes("aborted")) {
|
||||
} else if (!isAborted) {
|
||||
botMessage.content += "\n\n" + Locale.Store.Error;
|
||||
}
|
||||
botMessage.streaming = false;
|
||||
userMessage.isError = true;
|
||||
botMessage.isError = true;
|
||||
userMessage.isError = !isAborted;
|
||||
botMessage.isError = !isAborted;
|
||||
|
||||
set(() => ({}));
|
||||
ControllerPool.remove(sessionIndex, botMessage.id ?? messageIndex);
|
||||
},
|
||||
@@ -298,8 +303,7 @@ export const useChatStore = create<ChatStore>()(
|
||||
controller,
|
||||
);
|
||||
},
|
||||
filterBot: !useAppConfig.getState().sendBotMessages,
|
||||
modelConfig: useAppConfig.getState().modelConfig,
|
||||
modelConfig: { ...modelConfig },
|
||||
});
|
||||
},
|
||||
|
||||
@@ -308,22 +312,25 @@ export const useChatStore = create<ChatStore>()(
|
||||
|
||||
return {
|
||||
role: "system",
|
||||
content: Locale.Store.Prompt.History(session.memoryPrompt),
|
||||
content:
|
||||
session.memoryPrompt.length > 0
|
||||
? Locale.Store.Prompt.History(session.memoryPrompt)
|
||||
: "",
|
||||
date: "",
|
||||
} as Message;
|
||||
},
|
||||
|
||||
getMessagesWithMemory() {
|
||||
const session = get().currentSession();
|
||||
const config = useAppConfig.getState();
|
||||
const modelConfig = session.mask.modelConfig;
|
||||
const messages = session.messages.filter((msg) => !msg.isError);
|
||||
const n = messages.length;
|
||||
|
||||
const context = session.context.slice();
|
||||
const context = session.mask.context.slice();
|
||||
|
||||
// long term memory
|
||||
if (
|
||||
session.sendMemory &&
|
||||
modelConfig.sendMemory &&
|
||||
session.memoryPrompt &&
|
||||
session.memoryPrompt.length > 0
|
||||
) {
|
||||
@@ -334,14 +341,14 @@ export const useChatStore = create<ChatStore>()(
|
||||
// get short term and unmemoried long term memory
|
||||
const shortTermMemoryMessageIndex = Math.max(
|
||||
0,
|
||||
n - config.historyMessageCount,
|
||||
n - modelConfig.historyMessageCount,
|
||||
);
|
||||
const longTermMemoryMessageIndex = session.lastSummarizeIndex;
|
||||
const oldestIndex = Math.max(
|
||||
shortTermMemoryMessageIndex,
|
||||
longTermMemoryMessageIndex,
|
||||
);
|
||||
const threshold = config.compressMessageLengthThreshold;
|
||||
const threshold = modelConfig.compressMessageLengthThreshold;
|
||||
|
||||
// get recent messages as many as possible
|
||||
const reversedRecentMessages = [];
|
||||
@@ -400,17 +407,17 @@ export const useChatStore = create<ChatStore>()(
|
||||
});
|
||||
}
|
||||
|
||||
const config = useAppConfig.getState();
|
||||
const modelConfig = session.mask.modelConfig;
|
||||
let toBeSummarizedMsgs = session.messages.slice(
|
||||
session.lastSummarizeIndex,
|
||||
);
|
||||
|
||||
const historyMsgLength = countMessages(toBeSummarizedMsgs);
|
||||
|
||||
if (historyMsgLength > config?.modelConfig?.max_tokens ?? 4000) {
|
||||
if (historyMsgLength > modelConfig?.max_tokens ?? 4000) {
|
||||
const n = toBeSummarizedMsgs.length;
|
||||
toBeSummarizedMsgs = toBeSummarizedMsgs.slice(
|
||||
Math.max(0, n - config.historyMessageCount),
|
||||
Math.max(0, n - modelConfig.historyMessageCount),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -423,12 +430,12 @@ export const useChatStore = create<ChatStore>()(
|
||||
"[Chat History] ",
|
||||
toBeSummarizedMsgs,
|
||||
historyMsgLength,
|
||||
config.compressMessageLengthThreshold,
|
||||
modelConfig.compressMessageLengthThreshold,
|
||||
);
|
||||
|
||||
if (
|
||||
historyMsgLength > config.compressMessageLengthThreshold &&
|
||||
session.sendMemory
|
||||
historyMsgLength > modelConfig.compressMessageLengthThreshold &&
|
||||
session.mask.modelConfig.sendMemory
|
||||
) {
|
||||
requestChatStream(
|
||||
toBeSummarizedMsgs.concat({
|
||||
@@ -437,8 +444,7 @@ export const useChatStore = create<ChatStore>()(
|
||||
date: "",
|
||||
}),
|
||||
{
|
||||
filterBot: false,
|
||||
model: "gpt-3.5-turbo",
|
||||
overrideModel: "gpt-3.5-turbo",
|
||||
onMessage(message, done) {
|
||||
session.memoryPrompt = message;
|
||||
if (done) {
|
||||
@@ -469,27 +475,34 @@ export const useChatStore = create<ChatStore>()(
|
||||
},
|
||||
|
||||
clearAllData() {
|
||||
if (confirm(Locale.Store.ConfirmClearAll)) {
|
||||
localStorage.clear();
|
||||
location.reload();
|
||||
}
|
||||
localStorage.clear();
|
||||
location.reload();
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: LOCAL_KEY,
|
||||
version: 1.2,
|
||||
name: StoreKey.Chat,
|
||||
version: 2,
|
||||
migrate(persistedState, version) {
|
||||
const state = persistedState as ChatStore;
|
||||
const state = persistedState as any;
|
||||
const newState = JSON.parse(JSON.stringify(state)) as ChatStore;
|
||||
|
||||
if (version === 1) {
|
||||
state.sessions.forEach((s) => (s.context = []));
|
||||
if (version < 2) {
|
||||
newState.globalId = 0;
|
||||
newState.sessions = [];
|
||||
|
||||
const oldSessions = state.sessions;
|
||||
for (const oldSession of oldSessions) {
|
||||
const newSession = createEmptySession();
|
||||
newSession.topic = oldSession.topic;
|
||||
newSession.messages = [...oldSession.messages];
|
||||
newSession.mask.modelConfig.sendMemory = true;
|
||||
newSession.mask.modelConfig.historyMessageCount = 4;
|
||||
newSession.mask.modelConfig.compressMessageLengthThreshold = 1000;
|
||||
newState.sessions.push(newSession);
|
||||
}
|
||||
}
|
||||
|
||||
if (version < 1.2) {
|
||||
state.sessions.forEach((s) => (s.sendMemory = true));
|
||||
}
|
||||
|
||||
return state;
|
||||
return newState;
|
||||
},
|
||||
},
|
||||
),
|
||||
@@ -1,5 +1,6 @@
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { StoreKey } from "../constant";
|
||||
|
||||
export enum SubmitKey {
|
||||
Enter = "Enter",
|
||||
@@ -15,11 +16,8 @@ export enum Theme {
|
||||
Light = "light",
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG = {
|
||||
historyMessageCount: 5,
|
||||
compressMessageLengthThreshold: 1000,
|
||||
sendBotMessages: true as boolean,
|
||||
submitKey: SubmitKey.Enter as SubmitKey,
|
||||
export const DEFAULT_CONFIG = {
|
||||
submitKey: SubmitKey.CtrlEnter as SubmitKey,
|
||||
avatar: "1f437",
|
||||
fontSize: 14,
|
||||
theme: Theme.Auto as Theme,
|
||||
@@ -29,11 +27,16 @@ const DEFAULT_CONFIG = {
|
||||
|
||||
disablePromptHint: false,
|
||||
|
||||
dontShowMaskSplashScreen: false, // dont show splash screen when create chat
|
||||
|
||||
modelConfig: {
|
||||
model: "gpt-3.5-turbo" as ModelType,
|
||||
temperature: 0.8,
|
||||
max_tokens: 2000,
|
||||
presence_penalty: 0,
|
||||
sendMemory: true,
|
||||
historyMessageCount: 4,
|
||||
compressMessageLengthThreshold: 1000,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -107,12 +110,10 @@ export const ModalConfigValidator = {
|
||||
return limitNumber(x, -2, 2, 0);
|
||||
},
|
||||
temperature(x: number) {
|
||||
return limitNumber(x, 0, 2, 1);
|
||||
return limitNumber(x, 0, 1, 1);
|
||||
},
|
||||
};
|
||||
|
||||
const CONFIG_KEY = "app-config";
|
||||
|
||||
export const useAppConfig = create<ChatConfigStore>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
@@ -129,7 +130,19 @@ export const useAppConfig = create<ChatConfigStore>()(
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: CONFIG_KEY,
|
||||
name: StoreKey.Config,
|
||||
version: 2,
|
||||
migrate(persistedState, version) {
|
||||
if (version === 2) return persistedState as any;
|
||||
|
||||
const state = persistedState as ChatConfig;
|
||||
state.modelConfig.sendMemory = true;
|
||||
state.modelConfig.historyMessageCount = 4;
|
||||
state.modelConfig.compressMessageLengthThreshold = 1000;
|
||||
state.dontShowMaskSplashScreen = false;
|
||||
|
||||
return state;
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from "./app";
|
||||
export * from "./chat";
|
||||
export * from "./update";
|
||||
export * from "./access";
|
||||
export * from "./config";
|
||||
|
||||
100
app/store/mask.ts
Normal file
100
app/store/mask.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { BUILTIN_MASKS } from "../masks";
|
||||
import { getLang, Lang } from "../locales";
|
||||
import { DEFAULT_TOPIC, Message } from "./chat";
|
||||
import { ModelConfig, ModelType, useAppConfig } from "./config";
|
||||
import { StoreKey } from "../constant";
|
||||
|
||||
export type Mask = {
|
||||
id: number;
|
||||
avatar: string;
|
||||
name: string;
|
||||
context: Message[];
|
||||
modelConfig: ModelConfig;
|
||||
lang: Lang;
|
||||
builtin: boolean;
|
||||
};
|
||||
|
||||
export const DEFAULT_MASK_STATE = {
|
||||
masks: {} as Record<number, Mask>,
|
||||
globalMaskId: 0,
|
||||
};
|
||||
|
||||
export type MaskState = typeof DEFAULT_MASK_STATE;
|
||||
type MaskStore = MaskState & {
|
||||
create: (mask?: Partial<Mask>) => Mask;
|
||||
update: (id: number, updater: (mask: Mask) => void) => void;
|
||||
delete: (id: number) => void;
|
||||
search: (text: string) => Mask[];
|
||||
get: (id?: number) => Mask | null;
|
||||
getAll: () => Mask[];
|
||||
};
|
||||
|
||||
export const DEFAULT_MASK_ID = 1145141919810;
|
||||
export const DEFAULT_MASK_AVATAR = "gpt-bot";
|
||||
export const createEmptyMask = () =>
|
||||
({
|
||||
id: DEFAULT_MASK_ID,
|
||||
avatar: DEFAULT_MASK_AVATAR,
|
||||
name: DEFAULT_TOPIC,
|
||||
context: [],
|
||||
modelConfig: { ...useAppConfig.getState().modelConfig },
|
||||
lang: getLang(),
|
||||
builtin: false,
|
||||
} as Mask);
|
||||
|
||||
export const useMaskStore = create<MaskStore>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
...DEFAULT_MASK_STATE,
|
||||
|
||||
create(mask) {
|
||||
set(() => ({ globalMaskId: get().globalMaskId + 1 }));
|
||||
const id = get().globalMaskId;
|
||||
const masks = get().masks;
|
||||
masks[id] = {
|
||||
...createEmptyMask(),
|
||||
...mask,
|
||||
id,
|
||||
builtin: false,
|
||||
};
|
||||
|
||||
set(() => ({ masks }));
|
||||
|
||||
return masks[id];
|
||||
},
|
||||
update(id, updater) {
|
||||
const masks = get().masks;
|
||||
const mask = masks[id];
|
||||
if (!mask) return;
|
||||
const updateMask = { ...mask };
|
||||
updater(updateMask);
|
||||
masks[id] = updateMask;
|
||||
set(() => ({ masks }));
|
||||
},
|
||||
delete(id) {
|
||||
const masks = get().masks;
|
||||
delete masks[id];
|
||||
set(() => ({ masks }));
|
||||
},
|
||||
|
||||
get(id) {
|
||||
return get().masks[id ?? 1145141919810];
|
||||
},
|
||||
getAll() {
|
||||
const userMasks = Object.values(get().masks).sort(
|
||||
(a, b) => b.id - a.id,
|
||||
);
|
||||
return userMasks.concat(BUILTIN_MASKS);
|
||||
},
|
||||
search(text) {
|
||||
return Object.values(get().masks);
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: StoreKey.Mask,
|
||||
version: 2,
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -2,6 +2,7 @@ import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import Fuse from "fuse.js";
|
||||
import { getLang } from "../locales";
|
||||
import { StoreKey } from "../constant";
|
||||
|
||||
export interface Prompt {
|
||||
id?: number;
|
||||
@@ -16,15 +17,14 @@ export interface PromptStore {
|
||||
prompts: Record<number, Prompt>;
|
||||
|
||||
add: (prompt: Prompt) => number;
|
||||
get: (id: number) => Prompt | undefined;
|
||||
remove: (id: number) => void;
|
||||
search: (text: string) => Prompt[];
|
||||
update: (id: number, updater: (prompt: Prompt) => void) => void;
|
||||
|
||||
getUserPrompts: () => Prompt[];
|
||||
updateUserPrompts: (id: number, updater: (prompt: Prompt) => void) => void;
|
||||
}
|
||||
|
||||
export const PROMPT_KEY = "prompt-store";
|
||||
|
||||
export const SearchService = {
|
||||
ready: false,
|
||||
builtinEngine: new Fuse<Prompt>([], { keys: ["title"] }),
|
||||
@@ -82,6 +82,16 @@ export const usePromptStore = create<PromptStore>()(
|
||||
return prompt.id!;
|
||||
},
|
||||
|
||||
get(id) {
|
||||
const targetPrompt = get().prompts[id];
|
||||
|
||||
if (!targetPrompt) {
|
||||
return SearchService.builtinPrompts.find((v) => v.id === id);
|
||||
}
|
||||
|
||||
return targetPrompt;
|
||||
},
|
||||
|
||||
remove(id) {
|
||||
const prompts = get().prompts;
|
||||
delete prompts[id];
|
||||
@@ -99,7 +109,7 @@ export const usePromptStore = create<PromptStore>()(
|
||||
return userPrompts;
|
||||
},
|
||||
|
||||
updateUserPrompts(id: number, updater) {
|
||||
update(id: number, updater) {
|
||||
const prompt = get().prompts[id] ?? {
|
||||
title: "",
|
||||
content: "",
|
||||
@@ -123,7 +133,7 @@ export const usePromptStore = create<PromptStore>()(
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: PROMPT_KEY,
|
||||
name: StoreKey.Prompt,
|
||||
version: 1,
|
||||
onRehydrateStorage(state) {
|
||||
const PROMPT_URL = "./prompts.json";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { FETCH_COMMIT_URL, FETCH_TAG_URL } from "../constant";
|
||||
import { FETCH_COMMIT_URL, FETCH_TAG_URL, StoreKey } from "../constant";
|
||||
import { requestUsage } from "../requests";
|
||||
|
||||
export interface UpdateStore {
|
||||
@@ -16,8 +16,6 @@ export interface UpdateStore {
|
||||
updateUsage: (force?: boolean) => Promise<void>;
|
||||
}
|
||||
|
||||
export const UPDATE_KEY = "chat-update";
|
||||
|
||||
function queryMeta(key: string, defaultValue?: string): string {
|
||||
let ret: string;
|
||||
if (document) {
|
||||
@@ -55,10 +53,9 @@ export const useUpdateStore = create<UpdateStore>()(
|
||||
}));
|
||||
|
||||
try {
|
||||
// const data = await (await fetch(FETCH_TAG_URL)).json();
|
||||
// const remoteId = data[0].name as string;
|
||||
const data = await (await fetch(FETCH_COMMIT_URL)).json();
|
||||
const remoteId = (data[0].sha as string).substring(0, 7);
|
||||
const remoteCommitTime = data[0].commit.committer.date;
|
||||
const remoteId = new Date(remoteCommitTime).getTime().toString();
|
||||
set(() => ({
|
||||
remoteVersion: remoteId,
|
||||
}));
|
||||
@@ -84,7 +81,7 @@ export const useUpdateStore = create<UpdateStore>()(
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: UPDATE_KEY,
|
||||
name: StoreKey.Update,
|
||||
version: 1,
|
||||
},
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user