prompt.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import Fuse from "fuse.js";
  2. import { getLang } from "../locales";
  3. import { StoreKey } from "../constant";
  4. import { nanoid } from "nanoid";
  5. import { createPersistStore } from "../utils/store";
  6. import { getFullApi } from "@/app/config/client";
  7. export interface Prompt {
  8. id: string;
  9. isUser?: boolean;
  10. title: string;
  11. content: string;
  12. createdAt: number;
  13. }
  14. export const SearchService = {
  15. ready: false,
  16. builtinEngine: new Fuse<Prompt>([], { keys: ["title"] }),
  17. userEngine: new Fuse<Prompt>([], { keys: ["title"] }),
  18. count: {
  19. builtin: 0,
  20. },
  21. allPrompts: [] as Prompt[],
  22. builtinPrompts: [] as Prompt[],
  23. init(builtinPrompts: Prompt[], userPrompts: Prompt[]) {
  24. if (this.ready) {
  25. return;
  26. }
  27. this.allPrompts = userPrompts.concat(builtinPrompts);
  28. this.builtinPrompts = builtinPrompts.slice();
  29. this.builtinEngine.setCollection(builtinPrompts);
  30. this.userEngine.setCollection(userPrompts);
  31. this.ready = true;
  32. },
  33. remove(id: string) {
  34. this.userEngine.remove((doc) => doc.id === id);
  35. },
  36. add(prompt: Prompt) {
  37. this.userEngine.add(prompt);
  38. },
  39. search(text: string) {
  40. const userResults = this.userEngine.search(text);
  41. const builtinResults = this.builtinEngine.search(text);
  42. return userResults.concat(builtinResults).map((v) => v.item);
  43. },
  44. };
  45. export const usePromptStore = createPersistStore(
  46. {
  47. counter: 0,
  48. prompts: {} as Record<string, Prompt>,
  49. },
  50. (set, get) => ({
  51. add(prompt: Prompt) {
  52. const prompts = get().prompts;
  53. prompt.id = nanoid();
  54. prompt.isUser = true;
  55. prompt.createdAt = Date.now();
  56. prompts[prompt.id] = prompt;
  57. set(() => ({
  58. prompts: prompts,
  59. }));
  60. return prompt.id!;
  61. },
  62. get(id: string) {
  63. const targetPrompt = get().prompts[id];
  64. if (!targetPrompt) {
  65. return SearchService.builtinPrompts.find((v) => v.id === id);
  66. }
  67. return targetPrompt;
  68. },
  69. remove(id: string) {
  70. const prompts = get().prompts;
  71. delete prompts[id];
  72. Object.entries(prompts).some(([key, prompt]) => {
  73. if (prompt.id === id) {
  74. delete prompts[key];
  75. return true;
  76. }
  77. return false;
  78. });
  79. SearchService.remove(id);
  80. set(() => ({
  81. prompts,
  82. counter: get().counter + 1,
  83. }));
  84. },
  85. getUserPrompts() {
  86. const userPrompts = Object.values(get().prompts ?? {});
  87. userPrompts.sort((a, b) =>
  88. b.id && a.id ? b.createdAt - a.createdAt : 0,
  89. );
  90. return userPrompts;
  91. },
  92. updatePrompt(id: string, updater: (prompt: Prompt) => void) {
  93. const prompt = get().prompts[id] ?? {
  94. title: "",
  95. content: "",
  96. id,
  97. };
  98. SearchService.remove(id);
  99. updater(prompt);
  100. const prompts = get().prompts;
  101. prompts[id] = prompt;
  102. set(() => ({ prompts }));
  103. SearchService.add(prompt);
  104. },
  105. search(text: string) {
  106. if (text.length === 0) {
  107. // return all rompts
  108. return this.getUserPrompts().concat(SearchService.builtinPrompts);
  109. }
  110. return SearchService.search(text) as Prompt[];
  111. },
  112. }),
  113. {
  114. name: StoreKey.Prompt,
  115. version: 3,
  116. migrate(state, version) {
  117. const newState = JSON.parse(JSON.stringify(state)) as {
  118. prompts: Record<string, Prompt>;
  119. };
  120. if (version < 3) {
  121. Object.values(newState.prompts).forEach((p) => (p.id = nanoid()));
  122. }
  123. return newState as any;
  124. },
  125. onRehydrateStorage(state) {
  126. const PROMPT_URL = getFullApi("./prompts.json");
  127. type PromptList = Array<[string, string]>;
  128. fetch(PROMPT_URL)
  129. .then((res) => res.json())
  130. .then((res) => {
  131. let fetchPrompts = [res.en, res.cn];
  132. if (getLang() === "cn") {
  133. fetchPrompts = fetchPrompts.reverse();
  134. }
  135. const builtinPrompts = fetchPrompts.map((promptList: PromptList) => {
  136. return promptList.map(
  137. ([title, content]) =>
  138. ({
  139. id: nanoid(),
  140. title,
  141. content,
  142. createdAt: Date.now(),
  143. }) as Prompt,
  144. );
  145. });
  146. const userPrompts = usePromptStore.getState().getUserPrompts() ?? [];
  147. const allPromptsForSearch = builtinPrompts
  148. .reduce((pre, cur) => pre.concat(cur), [])
  149. .filter((v) => !!v.title && !!v.content);
  150. SearchService.count.builtin = res.en.length + res.cn.length;
  151. SearchService.init(allPromptsForSearch, userPrompts);
  152. });
  153. },
  154. },
  155. );