|
@@ -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>
|
|
|
);
|
|
|
}
|