|
@@ -6,12 +6,13 @@ import styles from "./settings.module.scss";
|
|
|
|
|
|
import ResetIcon from "../icons/reload.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 EyeOffIcon from "../icons/eye-off.svg";
|
|
|
|
|
|
-import { List, ListItem, Popover, showToast } from "./ui-lib";
|
|
|
+import { Input, List, ListItem, Modal, Popover } from "./ui-lib";
|
|
|
|
|
|
import { IconButton } from "./button";
|
|
|
import {
|
|
@@ -26,21 +27,112 @@ import {
|
|
|
import { Avatar } from "./chat";
|
|
|
|
|
|
import Locale, { AllLangs, changeLang, getLang } from "../locales";
|
|
|
-import { getEmojiUrl } from "../utils";
|
|
|
+import { copyToClipboard, getEmojiUrl } from "../utils";
|
|
|
import Link from "next/link";
|
|
|
import { UPDATE_URL } from "../constant";
|
|
|
-import { SearchService, usePromptStore } from "../store/prompt";
|
|
|
-import { requestUsage } from "../requests";
|
|
|
+import { Prompt, SearchService, usePromptStore } from "../store/prompt";
|
|
|
import { ErrorBoundary } from "./error";
|
|
|
import { InputRange } from "./input-range";
|
|
|
|
|
|
-function UserPromptModal() {
|
|
|
+function UserPromptModal(props: { onClose?: () => void }) {
|
|
|
const promptStore = usePromptStore();
|
|
|
- const prompts = Array.from(promptStore.prompts.values()).sort(
|
|
|
- (a, b) => a.id ?? 0 - (b.id ?? 0),
|
|
|
- );
|
|
|
+ const userPrompts = promptStore.getUserPrompts();
|
|
|
+ const builtinPrompts = SearchService.builtinPrompts;
|
|
|
+ const allPrompts = userPrompts.concat(builtinPrompts);
|
|
|
+ const [searchInput, setSearchInput] = useState("");
|
|
|
+ const [searchPrompts, setSearchPrompts] = useState<Prompt[]>([]);
|
|
|
+ const prompts = searchInput.length > 0 ? searchPrompts : allPrompts;
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (searchInput.length > 0) {
|
|
|
+ const searchResult = SearchService.search(searchInput);
|
|
|
+ setSearchPrompts(searchResult);
|
|
|
+ } else {
|
|
|
+ setSearchPrompts([]);
|
|
|
+ }
|
|
|
+ }, [searchInput]);
|
|
|
|
|
|
- return <></>;
|
|
|
+ return (
|
|
|
+ <div className="modal-mask">
|
|
|
+ <Modal
|
|
|
+ title={Locale.Settings.Prompt.Modal.Title}
|
|
|
+ onClose={() => props.onClose?.()}
|
|
|
+ actions={[
|
|
|
+ <IconButton
|
|
|
+ key="add"
|
|
|
+ onClick={() => promptStore.add({ title: "", content: "" })}
|
|
|
+ icon={<ClearIcon />}
|
|
|
+ bordered
|
|
|
+ text={Locale.Settings.Prompt.Modal.Add}
|
|
|
+ />,
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ <div className={styles["user-prompt-modal"]}>
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ className={styles["user-prompt-search"]}
|
|
|
+ placeholder={Locale.Settings.Prompt.Modal.Search}
|
|
|
+ value={searchInput}
|
|
|
+ onInput={(e) => setSearchInput(e.currentTarget.value)}
|
|
|
+ ></input>
|
|
|
+
|
|
|
+ <div className={styles["user-prompt-list"]}>
|
|
|
+ {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!)}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ <IconButton
|
|
|
+ icon={<CopyIcon />}
|
|
|
+ bordered
|
|
|
+ className={styles["user-prompt-button"]}
|
|
|
+ onClick={() => copyToClipboard(v.content)}
|
|
|
+ />
|
|
|
+ </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>
|
|
|
+ </Modal>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
function SettingItem(props: {
|
|
@@ -129,7 +221,8 @@ export function Settings(props: { closeSettings: () => void }) {
|
|
|
|
|
|
const promptStore = usePromptStore();
|
|
|
const builtinCount = SearchService.count.builtin;
|
|
|
- const customCount = promptStore.prompts.size ?? 0;
|
|
|
+ const customCount = promptStore.getUserPrompts().length ?? 0;
|
|
|
+ const [shouldShowPromptModal, setShowPromptModal] = useState(false);
|
|
|
|
|
|
const showUsage = accessStore.isAuthorized();
|
|
|
useEffect(() => {
|
|
@@ -477,7 +570,7 @@ export function Settings(props: { closeSettings: () => void }) {
|
|
|
<IconButton
|
|
|
icon={<EditIcon />}
|
|
|
text={Locale.Settings.Prompt.Edit}
|
|
|
- onClick={() => showToast(Locale.WIP)}
|
|
|
+ onClick={() => setShowPromptModal(true)}
|
|
|
/>
|
|
|
</SettingItem>
|
|
|
</List>
|
|
@@ -563,6 +656,10 @@ export function Settings(props: { closeSettings: () => void }) {
|
|
|
></InputRange>
|
|
|
</SettingItem>
|
|
|
</List>
|
|
|
+
|
|
|
+ {shouldShowPromptModal && (
|
|
|
+ <UserPromptModal onClose={() => setShowPromptModal(false)} />
|
|
|
+ )}
|
|
|
</div>
|
|
|
</ErrorBoundary>
|
|
|
);
|