mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-10-09 19:46:37 +08:00
Update chat.tsx
1. Refectory ChatItem 2. Delete one message instead two messages 3. User input can be copied and deleted
This commit is contained in:
parent
8d0a420cc8
commit
8cecbe3573
@ -421,9 +421,104 @@ export function ChatActions(props: {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Chat() {
|
|
||||||
type RenderMessage = Message & { preview?: boolean };
|
type RenderMessage = Message & { preview?: boolean };
|
||||||
|
|
||||||
|
export function ChatItem(props: {
|
||||||
|
message: RenderMessage;
|
||||||
|
i: number;
|
||||||
|
fontSize: number;
|
||||||
|
onUserStop: any;
|
||||||
|
onDelete: any;
|
||||||
|
onResend: any;
|
||||||
|
onRightClick: any;
|
||||||
|
onDoubleClickCapture: any;
|
||||||
|
scrollRef: any;
|
||||||
|
}) {
|
||||||
|
const message = props.message;
|
||||||
|
const i = props.i;
|
||||||
|
const isUser = message.role === "user";
|
||||||
|
const showActions =
|
||||||
|
i > 0 && !(message.preview || message.content.length === 0);
|
||||||
|
const showTyping = message.preview || message.streaming;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
isUser ? styles["chat-message-user"] : styles["chat-message-container"]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={styles["chat-message-container"]}>
|
||||||
|
<div className={styles["chat-message-avatar"]}>
|
||||||
|
<Avatar role={message.role} model={message.model} />
|
||||||
|
</div>
|
||||||
|
{showTyping && (
|
||||||
|
<div className={styles["chat-message-status"]}>
|
||||||
|
{Locale.Chat.Typing}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={styles["chat-message-item"]}>
|
||||||
|
{showActions && (
|
||||||
|
<div className={styles["chat-message-top-actions"]}>
|
||||||
|
{message.streaming ? (
|
||||||
|
<div
|
||||||
|
className={styles["chat-message-top-action"]}
|
||||||
|
onClick={() => props.onUserStop(message.id ?? i)}
|
||||||
|
>
|
||||||
|
{Locale.Chat.Actions.Stop}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={styles["chat-message-top-action"]}
|
||||||
|
onClick={() => props.onDelete(i)}
|
||||||
|
>
|
||||||
|
{Locale.Chat.Actions.Delete}
|
||||||
|
</div>
|
||||||
|
{!isUser && (
|
||||||
|
<div
|
||||||
|
className={styles["chat-message-top-action"]}
|
||||||
|
onClick={() => props.onResend(message.id ?? i)}
|
||||||
|
>
|
||||||
|
{Locale.Chat.Actions.Retry}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={styles["chat-message-top-action"]}
|
||||||
|
onClick={() => copyToClipboard(message.content)}
|
||||||
|
>
|
||||||
|
{Locale.Chat.Actions.Copy}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Markdown
|
||||||
|
content={message.content}
|
||||||
|
loading={
|
||||||
|
(message.preview || message.content.length === 0) && !isUser
|
||||||
|
}
|
||||||
|
onContextMenu={(e) => props.onRightClick(e, message)}
|
||||||
|
onDoubleClickCapture={() =>
|
||||||
|
props.onDoubleClickCapture(message.content)
|
||||||
|
}
|
||||||
|
fontSize={props.fontSize}
|
||||||
|
parentRef={props.scrollRef}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{!isUser && !message.preview && (
|
||||||
|
<div className={styles["chat-message-actions"]}>
|
||||||
|
<div className={styles["chat-message-action-date"]}>
|
||||||
|
{message.date.toLocaleString()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Chat() {
|
||||||
const chatStore = useChatStore();
|
const chatStore = useChatStore();
|
||||||
const [session, sessionIndex] = useChatStore((state) => [
|
const [session, sessionIndex] = useChatStore((state) => [
|
||||||
state.currentSession(),
|
state.currentSession(),
|
||||||
@ -497,7 +592,7 @@ export function Chat() {
|
|||||||
} else if (!config.disablePromptHint && n < SEARCH_TEXT_LIMIT) {
|
} else if (!config.disablePromptHint && n < SEARCH_TEXT_LIMIT) {
|
||||||
// check if need to trigger auto completion
|
// check if need to trigger auto completion
|
||||||
if (text.startsWith("/")) {
|
if (text.startsWith("/")) {
|
||||||
let searchText = text.slice(1);
|
const searchText = text.slice(1);
|
||||||
onSearch(searchText);
|
onSearch(searchText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -505,7 +600,7 @@ export function Chat() {
|
|||||||
|
|
||||||
// submit user input
|
// submit user input
|
||||||
const onUserSubmit = () => {
|
const onUserSubmit = () => {
|
||||||
if (userInput.length <= 0) return;
|
if (userInput.length <= 0 || isLoading) return;
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
chatStore.onUserInput(userInput).then(() => setIsLoading(false));
|
chatStore.onUserInput(userInput).then(() => setIsLoading(false));
|
||||||
setBeforeInput(userInput);
|
setBeforeInput(userInput);
|
||||||
@ -561,16 +656,16 @@ export function Chat() {
|
|||||||
return lastUserMessageIndex;
|
return lastUserMessageIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteMessage = (userIndex: number) => {
|
const deleteMessage = (messageIndex: number) => {
|
||||||
chatStore.updateCurrentSession((session) =>
|
chatStore.updateCurrentSession((session) =>
|
||||||
session.messages.splice(userIndex, 2),
|
session.messages.splice(messageIndex, 1),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDelete = (botMessageId: number) => {
|
const onDelete = (messageIndex: number) => {
|
||||||
const userIndex = findLastUserIndex(botMessageId);
|
if (messageIndex > 0) {
|
||||||
if (userIndex === null) return;
|
deleteMessage(messageIndex - 1);
|
||||||
deleteMessage(userIndex);
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onResend = (botMessageId: number) => {
|
const onResend = (botMessageId: number) => {
|
||||||
@ -585,8 +680,12 @@ export function Chat() {
|
|||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
const context: RenderMessage[] = session.context.slice();
|
const onDoubleClickCapture = (content: string) => {
|
||||||
|
if (!isMobileScreen) return;
|
||||||
|
setUserInput(content);
|
||||||
|
};
|
||||||
|
|
||||||
|
const context: RenderMessage[] = session.context.slice();
|
||||||
const accessStore = useAccessStore();
|
const accessStore = useAccessStore();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -722,91 +821,20 @@ export function Chat() {
|
|||||||
setAutoScroll(false);
|
setAutoScroll(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{messages.map((message, i) => {
|
{messages.map((message, i) => (
|
||||||
const isUser = message.role === "user";
|
<ChatItem
|
||||||
const showActions =
|
|
||||||
!isUser &&
|
|
||||||
i > 0 &&
|
|
||||||
!(message.preview || message.content.length === 0);
|
|
||||||
const showTyping = message.preview || message.streaming;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={i}
|
key={i}
|
||||||
className={
|
message={message}
|
||||||
isUser ? styles["chat-message-user"] : styles["chat-message"]
|
i={i}
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className={styles["chat-message-container"]}>
|
|
||||||
<div className={styles["chat-message-avatar"]}>
|
|
||||||
<Avatar role={message.role} model={message.model} />
|
|
||||||
</div>
|
|
||||||
{showTyping && (
|
|
||||||
<div className={styles["chat-message-status"]}>
|
|
||||||
{Locale.Chat.Typing}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className={styles["chat-message-item"]}>
|
|
||||||
{showActions && (
|
|
||||||
<div className={styles["chat-message-top-actions"]}>
|
|
||||||
{message.streaming ? (
|
|
||||||
<div
|
|
||||||
className={styles["chat-message-top-action"]}
|
|
||||||
onClick={() => onUserStop(message.id ?? i)}
|
|
||||||
>
|
|
||||||
{Locale.Chat.Actions.Stop}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
className={styles["chat-message-top-action"]}
|
|
||||||
onClick={() => onDelete(message.id ?? i)}
|
|
||||||
>
|
|
||||||
{Locale.Chat.Actions.Delete}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={styles["chat-message-top-action"]}
|
|
||||||
onClick={() => onResend(message.id ?? i)}
|
|
||||||
>
|
|
||||||
{Locale.Chat.Actions.Retry}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div
|
|
||||||
className={styles["chat-message-top-action"]}
|
|
||||||
onClick={() => copyToClipboard(message.content)}
|
|
||||||
>
|
|
||||||
{Locale.Chat.Actions.Copy}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<Markdown
|
|
||||||
content={message.content}
|
|
||||||
loading={
|
|
||||||
(message.preview || message.content.length === 0) &&
|
|
||||||
!isUser
|
|
||||||
}
|
|
||||||
onContextMenu={(e) => onRightClick(e, message)}
|
|
||||||
onDoubleClickCapture={() => {
|
|
||||||
if (!isMobileScreen) return;
|
|
||||||
setUserInput(message.content);
|
|
||||||
}}
|
|
||||||
fontSize={fontSize}
|
fontSize={fontSize}
|
||||||
parentRef={scrollRef}
|
scrollRef={scrollRef}
|
||||||
|
onUserStop={onUserStop}
|
||||||
|
onDelete={onDelete}
|
||||||
|
onDoubleClickCapture={onDoubleClickCapture}
|
||||||
|
onResend={onResend}
|
||||||
|
onRightClick={onRightClick}
|
||||||
/>
|
/>
|
||||||
</div>
|
))}
|
||||||
{!isUser && !message.preview && (
|
|
||||||
<div className={styles["chat-message-actions"]}>
|
|
||||||
<div className={styles["chat-message-action-date"]}>
|
|
||||||
{message.date.toLocaleString()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles["chat-input-panel"]}>
|
<div className={styles["chat-input-panel"]}>
|
||||||
|
Loading…
Reference in New Issue
Block a user