Browse Source

feat: #2 add prompt list

Yifei Zhang 1 year ago
parent
commit
7d5e742ea6
6 changed files with 206 additions and 4 deletions
  1. 3 1
      .gitignore
  2. 2 0
      README.md
  3. 96 0
      app/store/prompt.ts
  4. 7 3
      package.json
  5. 49 0
      scripts/fetch-prompts.mjs
  6. 49 0
      yarn.lock

+ 3 - 1
.gitignore

@@ -34,4 +34,6 @@ yarn-error.log*
 # typescript
 *.tsbuildinfo
 next-env.d.ts
-dev
+dev
+
+public/prompts.json

+ 2 - 0
README.md

@@ -22,6 +22,7 @@ One-Click to deploy your own ChatGPT web UI.
 - 在 1 分钟内使用 Vercel **免费一键部署**
 - 精心设计的 UI,响应式设计,支持深色模式
 - 极快的首屏加载速度(~85kb)
+- 海量的内置 prompt 列表,来自[中文](https://github.com/PlexPt/awesome-chatgpt-prompts-zh)和[英文](https://github.com/f/awesome-chatgpt-prompts)
 - 自动压缩上下文聊天记录,在节省 Token 的同时支持超长对话
 - 一键导出聊天记录,完整的 Markdown 支持
 - 拥有自己的域名?好上加好,绑定后即可在任何地方**无障碍**快速访问
@@ -31,6 +32,7 @@ One-Click to deploy your own ChatGPT web UI.
 - **Deploy for free with one-click** on Vercel in under 1 minute
 - Responsive design, and dark mode
 - Fast first screen loading speed (~85kb)
+- Awesome prompts powered by [awesome-chatgpt-prompts-zh](https://github.com/PlexPt/awesome-chatgpt-prompts-zh) and [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts)
 - Automatically compresses chat history to support long conversations while also saving your tokens
 - One-click export all chat history with full Markdown support
 

+ 96 - 0
app/store/prompt.ts

@@ -0,0 +1,96 @@
+import { create } from "zustand";
+import { persist } from "zustand/middleware";
+import JsSearch from "js-search";
+
+export interface Prompt {
+  id?: number;
+  shortcut: string;
+  title: string;
+  content: string;
+}
+
+export interface PromptStore {
+  latestId: number;
+  prompts: Map<number, Prompt>;
+
+  add: (prompt: Prompt) => number;
+  remove: (id: number) => void;
+  search: (text: string) => Prompt[];
+}
+
+export const PROMPT_KEY = "prompt-store";
+
+export const SearchService = {
+  ready: false,
+  progress: 0, // 0 - 1, 1 means ready
+  engine: new JsSearch.Search("prompts"),
+  deleted: new Set<number>(),
+
+  async init(prompts: PromptStore["prompts"]) {
+    this.engine.addIndex("id");
+    this.engine.addIndex("shortcut");
+    this.engine.addIndex("title");
+
+    const n = prompts.size;
+    let count = 0;
+    for await (const prompt of prompts.values()) {
+      this.engine.addDocument(prompt);
+      count += 1;
+      this.progress = count / n;
+    }
+    this.ready = true;
+  },
+
+  remove(id: number) {
+    this.deleted.add(id);
+  },
+
+  add(prompt: Prompt) {
+    this.engine.addDocument(prompt);
+  },
+
+  search(text: string) {
+    const results = this.engine.search(text) as Prompt[];
+    return results.filter((v) => !v.id || !this.deleted.has(v.id));
+  },
+};
+
+export const usePromptStore = create<PromptStore>()(
+  persist(
+    (set, get) => ({
+      latestId: 0,
+      prompts: new Map(),
+
+      add(prompt) {
+        const prompts = get().prompts;
+        prompt.id = get().latestId + 1;
+        prompts.set(prompt.id, prompt);
+
+        set(() => ({
+          latestId: prompt.id!,
+          prompts: prompts,
+        }));
+
+        return prompt.id!;
+      },
+
+      remove(id) {
+        const prompts = get().prompts;
+        prompts.delete(id);
+        SearchService.remove(id);
+
+        set(() => ({
+          prompts,
+        }));
+      },
+
+      search(text) {
+        return SearchService.search(text) as Prompt[];
+      },
+    }),
+    {
+      name: PROMPT_KEY,
+      version: 1,
+    }
+  )
+);

+ 7 - 3
package.json

@@ -4,13 +4,15 @@
   "private": false,
   "license": "Anti 996",
   "scripts": {
-    "dev": "next dev",
-    "build": "next build",
+    "dev": "yarn fetch && next dev",
+    "build": "yarn fetch && next build",
     "start": "next start",
-    "lint": "next lint"
+    "lint": "next lint",
+    "fetch": "node ./scripts/fetch-prompts.mjs"
   },
   "dependencies": {
     "@svgr/webpack": "^6.5.1",
+    "@types/js-search": "^1.4.0",
     "@types/node": "^18.14.6",
     "@types/react": "^18.0.28",
     "@types/react-dom": "^18.0.11",
@@ -22,7 +24,9 @@
     "eslint": "8.35.0",
     "eslint-config-next": "13.2.3",
     "eventsource-parser": "^0.1.0",
+    "js-search": "^2.0.0",
     "next": "^13.2.3",
+    "node-fetch": "^3.3.1",
     "openai": "^3.2.1",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",

+ 49 - 0
scripts/fetch-prompts.mjs

@@ -0,0 +1,49 @@
+import fetch from "node-fetch";
+import fs from "fs/promises";
+
+const CN_URL =
+  "https://raw.githubusercontent.com/PlexPt/awesome-chatgpt-prompts-zh/main/prompts-zh.json";
+const EN_URL =
+  "https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv";
+const FILE = "./public/prompts.json";
+
+async function fetchCN() {
+  console.log("[Fetch] fetching cn prompts...");
+  try {
+    const raw = await (await fetch(CN_URL)).json();
+    return raw.map((v) => [v.act, v.prompt]);
+  } catch (error) {
+    console.error("[Fetch] failed to fetch cn prompts", error);
+    return [];
+  }
+}
+
+async function fetchEN() {
+  console.log("[Fetch] fetching en prompts...");
+  try {
+    const raw = await (await fetch(EN_URL)).text();
+    return raw
+      .split("\n")
+      .slice(1)
+      .map((v) => v.split('","').map((v) => v.replace('"', "")));
+  } catch (error) {
+    console.error("[Fetch] failed to fetch cn prompts", error);
+    return [];
+  }
+}
+
+async function main() {
+  Promise.all([fetchCN(), fetchEN()])
+    .then(([cn, en]) => {
+      fs.writeFile(FILE, JSON.stringify({ cn, en }));
+    })
+    .catch((e) => {
+      console.error("[Fetch] failed to fetch prompts");
+      fs.writeFile(FILE, JSON.stringify({ cn: [], en: [] }));
+    })
+    .finally(() => {
+      console.log("[Fetch] saved to " + FILE);
+    });
+}
+
+main();

+ 49 - 0
yarn.lock

@@ -1320,6 +1320,11 @@
   dependencies:
     "@types/unist" "*"
 
+"@types/js-search@^1.4.0":
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/@types/js-search/-/js-search-1.4.0.tgz#f2d4afa176a4fc7b17fb46a1593847887fa1fb7b"
+  integrity sha512-OMDWvQP2AmxpQI9vFh7U/TzExNGB9Sj9WQCoxUR8VXZEv6jM4cyNzLODkh1gkBHJ9Er7kdasChzEpba4FxLGaA==
+
 "@types/json5@^0.0.29":
   version "0.0.29"
   resolved "https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@@ -1879,6 +1884,11 @@ damerau-levenshtein@^1.0.8:
   resolved "https://registry.npmmirror.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
   integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
 
+data-uri-to-buffer@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e"
+  integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
+
 debug@^3.2.7:
   version "3.2.7"
   resolved "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
@@ -2413,6 +2423,14 @@ fastq@^1.6.0:
   dependencies:
     reusify "^1.0.4"
 
+fetch-blob@^3.1.2, fetch-blob@^3.1.4:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9"
+  integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==
+  dependencies:
+    node-domexception "^1.0.0"
+    web-streams-polyfill "^3.0.3"
+
 file-entry-cache@^6.0.1:
   version "6.0.1"
   resolved "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@@ -2469,6 +2487,13 @@ form-data@^4.0.0:
     combined-stream "^1.0.8"
     mime-types "^2.1.12"
 
+formdata-polyfill@^4.0.10:
+  version "4.0.10"
+  resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423"
+  integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==
+  dependencies:
+    fetch-blob "^3.1.2"
+
 fs.realpath@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -3017,6 +3042,11 @@ js-sdsl@^4.1.4:
   resolved "https://registry.npmmirror.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711"
   integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==
 
+js-search@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/js-search/-/js-search-2.0.0.tgz#84dc9d44e34ca0870d067e04b86d8038b77edc26"
+  integrity sha512-lJ8KzjlwcelIWuAdKyzsXv45W6OIwRpayzc7XmU8mzgWadg5UVOKVmnq/tXudddEB9Ceic3tVaGu6QOK/eebhg==
+
 "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
   version "4.0.0"
   resolved "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -3693,6 +3723,20 @@ next@^13.2.3:
     "@next/swc-win32-ia32-msvc" "13.2.4"
     "@next/swc-win32-x64-msvc" "13.2.4"
 
+node-domexception@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
+  integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
+
+node-fetch@^3.3.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.1.tgz#b3eea7b54b3a48020e46f4f88b9c5a7430d20b2e"
+  integrity sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==
+  dependencies:
+    data-uri-to-buffer "^4.0.0"
+    fetch-blob "^3.1.4"
+    formdata-polyfill "^4.0.10"
+
 node-releases@^2.0.8:
   version "2.0.10"
   resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f"
@@ -4662,6 +4706,11 @@ web-namespaces@^2.0.0:
   resolved "https://registry.npmmirror.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692"
   integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==
 
+web-streams-polyfill@^3.0.3:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"
+  integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
+
 which-boxed-primitive@^1.0.2:
   version "1.0.2"
   resolved "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"