feat: dialog title edit

This commit is contained in:
897809421 2023-03-30 17:38:43 +08:00
parent fe858621f2
commit c99d0d6f10
4 changed files with 106 additions and 16 deletions

View File

@ -169,13 +169,38 @@
transition: all ease 0.3s; transition: all ease 0.3s;
opacity: 0; opacity: 0;
} }
.chat-item-edit {
position: absolute;
top: 11px;
right: -25px;
transition: all ease 0.3s;
opacity: 0;
}
.chat-item-edit-input[type=text] {
font-size: 14px;
color: black;
font-weight: bolder;
padding: 0;
box-sizing: border-box;
width: 100%;
height: 100%;
border: 1px rgb(29,147,171) solid;
outline: none;
}
.chat-item:hover > .chat-item-delete { .chat-item:hover > .chat-item-delete {
opacity: 0.5; opacity: 0.5;
right: 10px; right: 10px;
} }
.chat-item:hover > .chat-item-delete:hover { .chat-item:hover > .chat-item-edit {
opacity: 0.5;
right: 35px;
}
.chat-item:hover > .chat-item-delete,
.chat-item:hover > .chat-item-edit {
opacity: 1; opacity: 1;
} }

View File

@ -15,6 +15,7 @@ import ExportIcon from "../icons/export.svg";
import BotIcon from "../icons/bot.svg"; import BotIcon from "../icons/bot.svg";
import AddIcon from "../icons/add.svg"; import AddIcon from "../icons/add.svg";
import DeleteIcon from "../icons/delete.svg"; import DeleteIcon from "../icons/delete.svg";
import EditIcon from "../icons/edit-title.svg";
import LoadingIcon from "../icons/three-dots.svg"; import LoadingIcon from "../icons/three-dots.svg";
import MenuIcon from "../icons/menu.svg"; import MenuIcon from "../icons/menu.svg";
import CloseIcon from "../icons/close.svg"; import CloseIcon from "../icons/close.svg";
@ -69,11 +70,16 @@ export function Avatar(props: { role: Message["role"] }) {
export function ChatItem(props: { export function ChatItem(props: {
onClick?: () => void; onClick?: () => void;
onDelete?: () => void; onDelete?: () => void;
onEdit?: (title: string) => void;
title: string; title: string;
count: number; count: number;
time: string; time: string;
selected: boolean; selected: boolean;
}) { }) {
const [dialog, setDialog] = useState({
isEdit: false,
title: props.title,
});
return ( return (
<div <div
className={`${styles["chat-item"]} ${ className={`${styles["chat-item"]} ${
@ -81,7 +87,30 @@ export function ChatItem(props: {
}`} }`}
onClick={props.onClick} onClick={props.onClick}
> >
<div className={styles["chat-item-title"]}>{props.title}</div> <div className={styles["chat-item-title"]}>
{dialog.isEdit ? (
<input
autoFocus
type="text"
className={styles["chat-item-edit-input"]}
value={dialog.title}
onChange={(data) => {
setDialog({
...dialog,
title: data.target.value,
});
}}
onBlur={editCompleted}
onKeyDown={(event) => {
if (event.key === "Enter") {
editCompleted();
}
}}
/>
) : (
<div>{dialog.title}</div>
)}
</div>
<div className={styles["chat-item-info"]}> <div className={styles["chat-item-info"]}>
<div className={styles["chat-item-count"]}> <div className={styles["chat-item-count"]}>
{Locale.ChatItem.ChatItemCount(props.count)} {Locale.ChatItem.ChatItemCount(props.count)}
@ -91,19 +120,41 @@ export function ChatItem(props: {
<div className={styles["chat-item-delete"]} onClick={props.onDelete}> <div className={styles["chat-item-delete"]} onClick={props.onDelete}>
<DeleteIcon /> <DeleteIcon />
</div> </div>
<div className={styles["chat-item-edit"]} onClick={editStart}>
<EditIcon />
</div>
</div> </div>
); );
function editStart() {
setDialog({
...dialog,
isEdit: true,
});
}
function editCompleted() {
setDialog({
...dialog,
isEdit: false,
});
props.onEdit && props.onEdit(dialog.title);
}
} }
export function ChatList() { export function ChatList() {
const [sessions, selectedIndex, selectSession, removeSession] = useChatStore( const [
(state) => [ sessions,
selectedIndex,
selectSession,
removeSession,
editDialogTitle,
] = useChatStore((state) => [
state.sessions, state.sessions,
state.currentSessionIndex, state.currentSessionIndex,
state.selectSession, state.selectSession,
state.removeSession, state.removeSession,
] state.editDialogTitle,
); ]);
return ( return (
<div className={styles["chat-list"]}> <div className={styles["chat-list"]}>
@ -116,6 +167,7 @@ export function ChatList() {
selected={i === selectedIndex} selected={i === selectedIndex}
onClick={() => selectSession(i)} onClick={() => selectSession(i)}
onDelete={() => removeSession(i)} onDelete={() => removeSession(i)}
onEdit={(title: string) => editDialogTitle(i, title)}
/> />
))} ))}
</div> </div>
@ -196,7 +248,7 @@ export function Chat(props: {
setPromptHints(promptStore.search(text)); setPromptHints(promptStore.search(text));
}, },
100, 100,
{ leading: true, trailing: true } { leading: true, trailing: true },
); );
const onPromptSelect = (prompt: Prompt) => { const onPromptSelect = (prompt: Prompt) => {
@ -210,7 +262,7 @@ export function Chat(props: {
if (!dom) return; if (!dom) return;
const paddingBottomNum: number = parseInt( const paddingBottomNum: number = parseInt(
window.getComputedStyle(dom).paddingBottom, window.getComputedStyle(dom).paddingBottom,
10 10,
); );
dom.scrollTop = dom.scrollHeight - dom.offsetHeight + paddingBottomNum; dom.scrollTop = dom.scrollHeight - dom.offsetHeight + paddingBottomNum;
}; };
@ -300,7 +352,7 @@ export function Chat(props: {
preview: true, preview: true,
}, },
] ]
: [] : [],
) )
.concat( .concat(
userInput.length > 0 userInput.length > 0
@ -312,7 +364,7 @@ export function Chat(props: {
preview: true, preview: true,
}, },
] ]
: [] : [],
); );
// auto scroll // auto scroll
@ -340,7 +392,7 @@ export function Chat(props: {
const newTopic = prompt(Locale.Chat.Rename, session.topic); const newTopic = prompt(Locale.Chat.Rename, session.topic);
if (newTopic && newTopic !== session.topic) { if (newTopic && newTopic !== session.topic) {
chatStore.updateCurrentSession( chatStore.updateCurrentSession(
(session) => (session.topic = newTopic!) (session) => (session.topic = newTopic!),
); );
} }
}} }}
@ -586,7 +638,7 @@ export function Home() {
state.newSession, state.newSession,
state.currentSessionIndex, state.currentSessionIndex,
state.removeSession, state.removeSession,
] ],
); );
const loading = !useHasHydrated(); const loading = !useHasHydrated();
const [showSideBar, setShowSideBar] = useState(true); const [showSideBar, setShowSideBar] = useState(true);

1
app/icons/edit-title.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" style="fill: rgba(77, 175, 214, 1);transform: ;msFilter:;"><path d="M19.045 7.401c.378-.378.586-.88.586-1.414s-.208-1.036-.586-1.414l-1.586-1.586c-.378-.378-.88-.586-1.414-.586s-1.036.208-1.413.585L4 13.585V18h4.413L19.045 7.401zm-3-3 1.587 1.585-1.59 1.584-1.586-1.585 1.589-1.584zM6 16v-1.585l7.04-7.018 1.586 1.586L7.587 16H6zm-2 4h16v2H4z"></path></svg>

After

Width:  |  Height:  |  Size: 440 B

View File

@ -186,6 +186,7 @@ interface ChatStore {
removeSession: (index: number) => void; removeSession: (index: number) => void;
selectSession: (index: number) => void; selectSession: (index: number) => void;
newSession: () => void; newSession: () => void;
editDialogTitle: (index: number, title: string) => void;
currentSession: () => ChatSession; currentSession: () => ChatSession;
onNewMessage: (message: Message) => void; onNewMessage: (message: Message) => void;
onUserInput: (content: string) => Promise<void>; onUserInput: (content: string) => Promise<void>;
@ -266,6 +267,17 @@ export const useChatStore = create<ChatStore>()(
}); });
}, },
editDialogTitle(index: number, title: string) {
set((state) => {
const sessions = state.sessions;
sessions[index].topic = title;
return {
sessions,
};
});
},
newSession() { newSession() {
set((state) => ({ set((state) => ({
currentSessionIndex: 0, currentSessionIndex: 0,