mask.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { BUILTIN_MASKS } from "../masks";
  2. import { getLang, Lang } from "../locales";
  3. import { DEFAULT_TOPIC, ChatMessage } from "./chat";
  4. import { MaskConfig, ModelConfig, useAppConfig } from "./config";
  5. import { StoreKey } from "../constant";
  6. import { nanoid } from "nanoid";
  7. import { createPersistStore } from "../utils/store";
  8. import { deepClone } from "../utils/clone";
  9. export type Mask = {
  10. id: string;
  11. createdAt: number;
  12. avatar: string;
  13. name: string;
  14. hideContext?: boolean;
  15. context: ChatMessage[];
  16. syncGlobalConfig?: boolean;
  17. config: MaskConfig;
  18. lang: Lang;
  19. builtin: boolean;
  20. };
  21. export const DEFAULT_MASK_STATE = {
  22. masks: {} as Record<string, Mask>,
  23. };
  24. export type MaskState = typeof DEFAULT_MASK_STATE;
  25. export const DEFAULT_MASK_AVATAR = "gpt-bot";
  26. export const createEmptyMask = () =>
  27. ({
  28. id: nanoid(),
  29. avatar: DEFAULT_MASK_AVATAR,
  30. name: DEFAULT_TOPIC,
  31. context: [],
  32. syncGlobalConfig: true, // use global config as default
  33. config: deepClone(useAppConfig.getState().globalMaskConfig),
  34. lang: getLang(),
  35. builtin: false,
  36. createdAt: Date.now(),
  37. }) as Mask;
  38. export const useMaskStore = createPersistStore(
  39. { ...DEFAULT_MASK_STATE },
  40. (set, get) => ({
  41. create(mask?: Partial<Mask>) {
  42. const masks = get().masks;
  43. const id = nanoid();
  44. masks[id] = {
  45. ...createEmptyMask(),
  46. ...mask,
  47. id,
  48. builtin: false,
  49. };
  50. set(() => ({ masks }));
  51. get().markUpdate();
  52. return masks[id];
  53. },
  54. updateMask(id: string, updater: (mask: Mask) => void) {
  55. const masks = get().masks;
  56. const mask = masks[id];
  57. if (!mask) return;
  58. const updateMask = { ...mask };
  59. updater(updateMask);
  60. masks[id] = updateMask;
  61. set(() => ({ masks }));
  62. get().markUpdate();
  63. },
  64. delete(id: string) {
  65. const masks = get().masks;
  66. delete masks[id];
  67. set(() => ({ masks }));
  68. get().markUpdate();
  69. },
  70. get(id?: string) {
  71. return get().masks[id ?? 1145141919810];
  72. },
  73. getAll() {
  74. const userMasks = Object.values(get().masks).sort(
  75. (a, b) => b.createdAt - a.createdAt,
  76. );
  77. const config = useAppConfig.getState();
  78. if (config.hideBuiltinMasks) return userMasks;
  79. const buildinMasks = BUILTIN_MASKS.map(
  80. (m) =>
  81. ({
  82. id: m.name,
  83. ...m,
  84. config: {
  85. ...config.globalMaskConfig,
  86. ...m.config,
  87. },
  88. }) as Mask,
  89. );
  90. return userMasks.concat(buildinMasks);
  91. },
  92. search(text: string) {
  93. return Object.values(get().masks);
  94. },
  95. }),
  96. {
  97. name: StoreKey.Mask,
  98. version: 3.1,
  99. migrate(state, version) {
  100. const newState = JSON.parse(JSON.stringify(state)) as MaskState;
  101. // migrate mask id to nanoid
  102. if (version < 3) {
  103. Object.values(newState.masks).forEach((m) => (m.id = nanoid()));
  104. }
  105. if (version < 3.1) {
  106. const updatedMasks: Record<string, Mask> = {};
  107. Object.values(newState.masks).forEach((m) => {
  108. updatedMasks[m.id] = m;
  109. });
  110. newState.masks = updatedMasks;
  111. }
  112. // TODO(yifei): migrate old masks
  113. return newState as any;
  114. },
  115. },
  116. );