Browse Source

perf: improve prompt list performance

Yidadaa 1 year ago
parent
commit
e509749421

+ 37 - 28
app/components/settings.module.scss

@@ -7,6 +7,20 @@
   cursor: pointer;
 }
 
+.edit-prompt-modal {
+  display: flex;
+  flex-direction: column;
+
+  .edit-prompt-title {
+    max-width: unset;
+    margin-bottom: 20px;
+    text-align: left;
+  }
+  .edit-prompt-content {
+    max-width: unset;
+  }
+}
+
 .user-prompt-modal {
   min-height: 40vh;
 
@@ -18,48 +32,43 @@
   }
 
   .user-prompt-list {
-    padding: 10px 0;
+    border: var(--border-in-light);
+    border-radius: 10px;
 
     .user-prompt-item {
-      margin-bottom: 10px;
-      widows: 100%;
+      display: flex;
+      justify-content: space-between;
+      padding: 10px;
+
+      &:not(:last-child) {
+        border-bottom: var(--border-in-light);
+      }
 
       .user-prompt-header {
-        display: flex;
-        widows: 100%;
-        margin-bottom: 5px;
+        max-width: calc(100% - 100px);
 
         .user-prompt-title {
-          flex-grow: 1;
-          max-width: 100%;
-          margin-right: 5px;
-          padding: 5px;
+          font-size: 14px;
+          line-height: 2;
+          font-weight: bold;
+        }
+        .user-prompt-content {
           font-size: 12px;
-          text-align: left;
         }
+      }
 
-        .user-prompt-buttons {
-          display: flex;
-          align-items: center;
+      .user-prompt-buttons {
+        display: flex;
+        align-items: center;
 
-          .user-prompt-button {
-            height: 100%;
+        .user-prompt-button {
+          height: 100%;
 
-            &:not(:last-child) {
-              margin-right: 5px;
-            }
+          &:not(:last-child) {
+            margin-right: 5px;
           }
         }
       }
-
-      .user-prompt-content {
-        width: 100%;
-        box-sizing: border-box;
-        padding: 5px;
-        margin-right: 10px;
-        font-size: 12px;
-        flex-grow: 1;
-      }
     }
   }
 

+ 96 - 44
app/components/settings.tsx

@@ -3,10 +3,12 @@ import { useState, useEffect, useMemo, HTMLProps, useRef } from "react";
 import styles from "./settings.module.scss";
 
 import ResetIcon from "../icons/reload.svg";
+import AddIcon from "../icons/add.svg";
 import CloseIcon from "../icons/close.svg";
 import CopyIcon from "../icons/copy.svg";
 import ClearIcon from "../icons/clear.svg";
 import EditIcon from "../icons/edit.svg";
+import EyeIcon from "../icons/eye.svg";
 import { Input, List, ListItem, Modal, PasswordInput, Popover } from "./ui-lib";
 import { ModelConfigList } from "./model-config";
 
@@ -30,6 +32,55 @@ import { InputRange } from "./input-range";
 import { useNavigate } from "react-router-dom";
 import { Avatar, AvatarPicker } from "./emoji";
 
+function EditPromptModal(props: { id: number; onClose: () => void }) {
+  const promptStore = usePromptStore();
+  const prompt = promptStore.get(props.id);
+
+  return prompt ? (
+    <div className="modal-mask">
+      <Modal
+        title={Locale.Settings.Prompt.EditModal.Title}
+        onClose={props.onClose}
+        actions={[
+          <IconButton
+            key=""
+            onClick={props.onClose}
+            text={Locale.UI.Confirm}
+            bordered
+          />,
+        ]}
+      >
+        <div className={styles["edit-prompt-modal"]}>
+          <input
+            type="text"
+            value={prompt.title}
+            readOnly={!prompt.isUser}
+            className={styles["edit-prompt-title"]}
+            onInput={(e) =>
+              promptStore.update(
+                props.id,
+                (prompt) => (prompt.title = e.currentTarget.value),
+              )
+            }
+          ></input>
+          <Input
+            value={prompt.content}
+            readOnly={!prompt.isUser}
+            className={styles["edit-prompt-content"]}
+            rows={10}
+            onInput={(e) =>
+              promptStore.update(
+                props.id,
+                (prompt) => (prompt.content = e.currentTarget.value),
+              )
+            }
+          ></Input>
+        </div>
+      </Modal>
+    </div>
+  ) : null;
+}
+
 function UserPromptModal(props: { onClose?: () => void }) {
   const promptStore = usePromptStore();
   const userPrompts = promptStore.getUserPrompts();
@@ -39,6 +90,8 @@ function UserPromptModal(props: { onClose?: () => void }) {
   const [searchPrompts, setSearchPrompts] = useState<Prompt[]>([]);
   const prompts = searchInput.length > 0 ? searchPrompts : allPrompts;
 
+  const [editingPromptId, setEditingPromptId] = useState<number>();
+
   useEffect(() => {
     if (searchInput.length > 0) {
       const searchResult = SearchService.search(searchInput);
@@ -56,8 +109,13 @@ function UserPromptModal(props: { onClose?: () => void }) {
         actions={[
           <IconButton
             key="add"
-            onClick={() => promptStore.add({ title: "", content: "" })}
-            icon={<ClearIcon />}
+            onClick={() =>
+              promptStore.add({
+                title: "Empty Prompt",
+                content: "Empty Prompt Content",
+              })
+            }
+            icon={<AddIcon />}
             bordered
             text={Locale.Settings.Prompt.Modal.Add}
           />,
@@ -76,57 +134,51 @@ function UserPromptModal(props: { onClose?: () => void }) {
             {prompts.map((v, _) => (
               <div className={styles["user-prompt-item"]} key={v.id ?? v.title}>
                 <div className={styles["user-prompt-header"]}>
-                  <input
-                    type="text"
-                    className={styles["user-prompt-title"]}
-                    value={v.title}
-                    readOnly={!v.isUser}
-                    onChange={(e) => {
-                      if (v.isUser) {
-                        promptStore.updateUserPrompts(
-                          v.id!,
-                          (prompt) => (prompt.title = e.currentTarget.value),
-                        );
-                      }
-                    }}
-                  ></input>
-
-                  <div className={styles["user-prompt-buttons"]}>
-                    {v.isUser && (
-                      <IconButton
-                        icon={<ClearIcon />}
-                        bordered
-                        className={styles["user-prompt-button"]}
-                        onClick={() => promptStore.remove(v.id!)}
-                      />
-                    )}
+                  <div className={styles["user-prompt-title"]}>{v.title}</div>
+                  <div className={styles["user-prompt-content"] + " one-line"}>
+                    {v.content}
+                  </div>
+                </div>
+
+                <div className={styles["user-prompt-buttons"]}>
+                  {v.isUser && (
                     <IconButton
-                      icon={<CopyIcon />}
-                      bordered
+                      icon={<ClearIcon />}
                       className={styles["user-prompt-button"]}
-                      onClick={() => copyToClipboard(v.content)}
+                      onClick={() => promptStore.remove(v.id!)}
                     />
-                  </div>
+                  )}
+                  {v.isUser ? (
+                    <IconButton
+                      icon={<EditIcon />}
+                      className={styles["user-prompt-button"]}
+                      onClick={() => setEditingPromptId(v.id)}
+                    />
+                  ) : (
+                    <IconButton
+                      icon={<EyeIcon />}
+                      className={styles["user-prompt-button"]}
+                      onClick={() => setEditingPromptId(v.id)}
+                    />
+                  )}
+                  <IconButton
+                    icon={<CopyIcon />}
+                    className={styles["user-prompt-button"]}
+                    onClick={() => copyToClipboard(v.content)}
+                  />
                 </div>
-                <Input
-                  rows={2}
-                  value={v.content}
-                  className={styles["user-prompt-content"]}
-                  readOnly={!v.isUser}
-                  onChange={(e) => {
-                    if (v.isUser) {
-                      promptStore.updateUserPrompts(
-                        v.id!,
-                        (prompt) => (prompt.content = e.currentTarget.value),
-                      );
-                    }
-                  }}
-                />
               </div>
             ))}
           </div>
         </div>
       </Modal>
+
+      {editingPromptId !== undefined && (
+        <EditPromptModal
+          id={editingPromptId!}
+          onClose={() => setEditingPromptId(undefined)}
+        />
+      )}
     </div>
   );
 }

+ 12 - 1
app/locales/cn.ts

@@ -116,9 +116,12 @@ const cn = {
       Edit: "编辑",
       Modal: {
         Title: "提示词列表",
-        Add: "增加一条",
+        Add: "新建",
         Search: "搜索提示词",
       },
+      EditModal: {
+        Title: "编辑提示词",
+      },
     },
     HistoryCount: {
       Title: "附带历史消息数",
@@ -223,6 +226,14 @@ const cn = {
     SubTitle: "现在开始,与面具背后的灵魂思维碰撞",
     More: "搜索更多",
   },
+
+  UI: {
+    Confirm: "确认",
+    Cancel: "取消",
+    Close: "关闭",
+    Create: "新建",
+    Edit: "编辑",
+  },
 };
 
 export type LocaleType = typeof cn;

+ 11 - 0
app/locales/de.ts

@@ -121,6 +121,9 @@ const de: LocaleType = {
         Add: "Add One",
         Search: "Search Prompts",
       },
+      EditModal: {
+        Title: "Edit Prompt",
+      },
     },
     HistoryCount: {
       Title: "Anzahl der angehängten Nachrichten",
@@ -230,6 +233,14 @@ const de: LocaleType = {
     NotShow: "Not Show Again",
     ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
   },
+
+  UI: {
+    Confirm: "Confirm",
+    Cancel: "Cancel",
+    Close: "Close",
+    Create: "Create",
+    Edit: "Edit",
+  },
 };
 
 export default de;

+ 11 - 0
app/locales/en.ts

@@ -120,6 +120,9 @@ const en: LocaleType = {
         Add: "Add One",
         Search: "Search Prompts",
       },
+      EditModal: {
+        Title: "Edit Prompt",
+      },
     },
     HistoryCount: {
       Title: "Attached Messages Count",
@@ -226,6 +229,14 @@ const en: LocaleType = {
     NotShow: "Not Show Again",
     ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
   },
+
+  UI: {
+    Confirm: "Confirm",
+    Cancel: "Cancel",
+    Close: "Close",
+    Create: "Create",
+    Edit: "Edit",
+  },
 };
 
 export default en;

+ 11 - 0
app/locales/es.ts

@@ -120,6 +120,9 @@ const es: LocaleType = {
         Add: "Add One",
         Search: "Search Prompts",
       },
+      EditModal: {
+        Title: "Edit Prompt",
+      },
     },
     HistoryCount: {
       Title: "Cantidad de mensajes adjuntos",
@@ -227,6 +230,14 @@ const es: LocaleType = {
     NotShow: "Not Show Again",
     ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
   },
+
+  UI: {
+    Confirm: "Confirm",
+    Cancel: "Cancel",
+    Close: "Close",
+    Create: "Create",
+    Edit: "Edit",
+  },
 };
 
 export default es;

+ 11 - 0
app/locales/it.ts

@@ -120,6 +120,9 @@ const it: LocaleType = {
         Add: "Add One",
         Search: "Search Prompts",
       },
+      EditModal: {
+        Title: "Edit Prompt",
+      },
     },
     HistoryCount: {
       Title: "Conteggio dei messaggi allegati",
@@ -228,6 +231,14 @@ const it: LocaleType = {
     NotShow: "Not Show Again",
     ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
   },
+
+  UI: {
+    Confirm: "Confirm",
+    Cancel: "Cancel",
+    Close: "Close",
+    Create: "Create",
+    Edit: "Edit",
+  },
 };
 
 export default it;

+ 11 - 0
app/locales/jp.ts

@@ -122,6 +122,9 @@ const jp: LocaleType = {
         Add: "新規追加",
         Search: "プロンプトワード検索",
       },
+      EditModal: {
+        Title: "编辑提示词",
+      },
     },
     HistoryCount: {
       Title: "履歴メッセージ数を添付",
@@ -226,6 +229,14 @@ const jp: LocaleType = {
     NotShow: "不再展示",
     ConfirmNoShow: "确认禁用?禁用后可以随时在设置中重新启用。",
   },
+
+  UI: {
+    Confirm: "确认",
+    Cancel: "取消",
+    Close: "关闭",
+    Create: "新建",
+    Edit: "编辑",
+  },
 };
 
 export default jp;

+ 11 - 0
app/locales/tr.ts

@@ -120,6 +120,9 @@ const tr: LocaleType = {
         Add: "Add One",
         Search: "Search Prompts",
       },
+      EditModal: {
+        Title: "Edit Prompt",
+      },
     },
     HistoryCount: {
       Title: "Ekli Mesaj Sayısı",
@@ -228,6 +231,14 @@ const tr: LocaleType = {
     NotShow: "Not Show Again",
     ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
   },
+
+  UI: {
+    Confirm: "Confirm",
+    Cancel: "Cancel",
+    Close: "Close",
+    Create: "Create",
+    Edit: "Edit",
+  },
 };
 
 export default tr;

+ 10 - 0
app/locales/tw.ts

@@ -118,6 +118,9 @@ const tw: LocaleType = {
         Add: "新增一條",
         Search: "搜尋提示詞",
       },
+      EditModal: {
+        Title: "编辑提示词",
+      },
     },
     HistoryCount: {
       Title: "附帶歷史訊息數",
@@ -219,6 +222,13 @@ const tw: LocaleType = {
     NotShow: "不再展示",
     ConfirmNoShow: "确认禁用?禁用后可以随时在设置中重新启用。",
   },
+  UI: {
+    Confirm: "确认",
+    Cancel: "取消",
+    Close: "关闭",
+    Create: "新建",
+    Edit: "编辑",
+  },
 };
 
 export default tw;

+ 13 - 2
app/store/prompt.ts

@@ -17,11 +17,12 @@ 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 SearchService = {
@@ -81,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];
@@ -98,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: "",

+ 1 - 0
scripts/init-proxy.sh

@@ -1,5 +1,6 @@
 dir="$(dirname "$0")"
 config=$dir/proxychains.conf
 host_ip=$(grep nameserver /etc/resolv.conf | sed 's/nameserver //')
+echo "proxying to $host_ip"
 cp $dir/proxychains.template.conf $config 
 sed -i "\$s/.*/http $host_ip 7890/" $config