perf: improve prompt list performance

This commit is contained in:
Yidadaa 2023-05-02 02:26:43 +08:00
parent a69cec89fb
commit e509749421
12 changed files with 240 additions and 80 deletions

View File

@ -7,6 +7,20 @@
cursor: pointer; 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 { .user-prompt-modal {
min-height: 40vh; min-height: 40vh;
@ -18,24 +32,29 @@
} }
.user-prompt-list { .user-prompt-list {
padding: 10px 0; border: var(--border-in-light);
border-radius: 10px;
.user-prompt-item { .user-prompt-item {
margin-bottom: 10px; display: flex;
widows: 100%; justify-content: space-between;
padding: 10px;
&:not(:last-child) {
border-bottom: var(--border-in-light);
}
.user-prompt-header { .user-prompt-header {
display: flex; max-width: calc(100% - 100px);
widows: 100%;
margin-bottom: 5px;
.user-prompt-title { .user-prompt-title {
flex-grow: 1; font-size: 14px;
max-width: 100%; line-height: 2;
margin-right: 5px; font-weight: bold;
padding: 5px; }
.user-prompt-content {
font-size: 12px; font-size: 12px;
text-align: left; }
} }
.user-prompt-buttons { .user-prompt-buttons {
@ -51,16 +70,6 @@
} }
} }
} }
.user-prompt-content {
width: 100%;
box-sizing: border-box;
padding: 5px;
margin-right: 10px;
font-size: 12px;
flex-grow: 1;
}
}
} }
.user-prompt-actions { .user-prompt-actions {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,11 +17,12 @@ export interface PromptStore {
prompts: Record<number, Prompt>; prompts: Record<number, Prompt>;
add: (prompt: Prompt) => number; add: (prompt: Prompt) => number;
get: (id: number) => Prompt | undefined;
remove: (id: number) => void; remove: (id: number) => void;
search: (text: string) => Prompt[]; search: (text: string) => Prompt[];
update: (id: number, updater: (prompt: Prompt) => void) => void;
getUserPrompts: () => Prompt[]; getUserPrompts: () => Prompt[];
updateUserPrompts: (id: number, updater: (prompt: Prompt) => void) => void;
} }
export const SearchService = { export const SearchService = {
@ -81,6 +82,16 @@ export const usePromptStore = create<PromptStore>()(
return prompt.id!; return prompt.id!;
}, },
get(id) {
const targetPrompt = get().prompts[id];
if (!targetPrompt) {
return SearchService.builtinPrompts.find((v) => v.id === id);
}
return targetPrompt;
},
remove(id) { remove(id) {
const prompts = get().prompts; const prompts = get().prompts;
delete prompts[id]; delete prompts[id];
@ -98,7 +109,7 @@ export const usePromptStore = create<PromptStore>()(
return userPrompts; return userPrompts;
}, },
updateUserPrompts(id: number, updater) { update(id: number, updater) {
const prompt = get().prompts[id] ?? { const prompt = get().prompts[id] ?? {
title: "", title: "",
content: "", content: "",

View File

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