ChatGPT-Next-Web/app/store/mask.ts

145 lines
3.5 KiB
TypeScript

import type { Lang } from '../locales';
import type { ChatMessage } from './chat';
import type { ModelConfig } from './config';
import { nanoid } from 'nanoid';
import { StoreKey } from '../constant';
import { getLang } from '../locales';
import { BUILTIN_MASKS } from '../masks';
import { createPersistStore } from '../utils/store';
import { DEFAULT_TOPIC } from './chat';
import { useAppConfig } from './config';
export interface Mask {
id: string;
createdAt: number;
avatar: string;
name: string;
hideContext?: boolean;
context: ChatMessage[];
syncGlobalConfig?: boolean;
modelConfig: ModelConfig;
lang: Lang;
builtin: boolean;
plugin?: string[];
enableArtifacts?: boolean;
enableCodeFold?: boolean;
}
export const DEFAULT_MASK_STATE = {
masks: {} as Record<string, Mask>,
language: undefined as Lang | undefined,
};
export type MaskState = typeof DEFAULT_MASK_STATE & {
language?: Lang | undefined;
};
export const DEFAULT_MASK_AVATAR = 'gpt-bot';
export function createEmptyMask() {
return ({
id: nanoid(),
avatar: DEFAULT_MASK_AVATAR,
name: DEFAULT_TOPIC,
context: [],
syncGlobalConfig: true, // use global config as default
modelConfig: { ...useAppConfig.getState().modelConfig },
lang: getLang(),
builtin: false,
createdAt: Date.now(),
plugin: [],
}) as Mask;
}
export const useMaskStore = createPersistStore(
{ ...DEFAULT_MASK_STATE },
(set, get) => ({
create(mask?: Partial<Mask>) {
const masks = get().masks;
const id = nanoid();
masks[id] = {
...createEmptyMask(),
...mask,
id,
builtin: false,
};
set(() => ({ masks }));
get().markUpdate();
return masks[id];
},
updateMask(id: string, updater: (mask: Mask) => void) {
const masks = get().masks;
const mask = masks[id];
if (!mask)
{ return; }
const updateMask = { ...mask };
updater(updateMask);
masks[id] = updateMask;
set(() => ({ masks }));
get().markUpdate();
},
delete(id: string) {
const masks = get().masks;
delete masks[id];
set(() => ({ masks }));
get().markUpdate();
},
get(id?: string) {
return get().masks[id ?? 1145141919810];
},
getAll() {
const userMasks = Object.values(get().masks).sort(
(a, b) => b.createdAt - a.createdAt,
);
const config = useAppConfig.getState();
if (config.hideBuiltinMasks)
{ return userMasks; }
const buildinMasks = BUILTIN_MASKS.map(
m =>
({
...m,
modelConfig: {
...config.modelConfig,
...m.modelConfig,
},
}) as Mask,
);
return userMasks.concat(buildinMasks);
},
search(text: string) {
return Object.values(get().masks);
},
setLanguage(language: Lang | undefined) {
set({
language,
});
},
}),
{
name: StoreKey.Mask,
version: 3.1,
migrate(state, version) {
const newState = JSON.parse(JSON.stringify(state)) as MaskState;
// migrate mask id to nanoid
if (version < 3) {
Object.values(newState.masks).forEach(m => (m.id = nanoid()));
}
if (version < 3.1) {
const updatedMasks: Record<string, Mask> = {};
Object.values(newState.masks).forEach((m) => {
updatedMasks[m.id] = m;
});
newState.masks = updatedMasks;
}
return newState as any;
},
},
);