|
@@ -10,6 +10,7 @@ import CopyIcon from "../icons/copy.svg";
|
|
|
import DownloadIcon from "../icons/download.svg";
|
|
|
import LoadingIcon from "../icons/three-dots.svg";
|
|
|
import BotIcon from "../icons/bot.svg";
|
|
|
+import BlackBotIcon from "../icons/black-bot.svg";
|
|
|
import AddIcon from "../icons/add.svg";
|
|
|
import DeleteIcon from "../icons/delete.svg";
|
|
|
import MaxIcon from "../icons/max.svg";
|
|
@@ -30,15 +31,16 @@ import {
|
|
|
createMessage,
|
|
|
useAccessStore,
|
|
|
Theme,
|
|
|
+ ModelType,
|
|
|
} from "../store";
|
|
|
|
|
|
import {
|
|
|
copyToClipboard,
|
|
|
downloadAs,
|
|
|
getEmojiUrl,
|
|
|
- isMobileScreen,
|
|
|
selectOrCopy,
|
|
|
autoGrowTextArea,
|
|
|
+ useMobileScreen,
|
|
|
} from "../utils";
|
|
|
|
|
|
import dynamic from "next/dynamic";
|
|
@@ -64,13 +66,17 @@ const Emoji = dynamic(async () => (await import("emoji-picker-react")).Emoji, {
|
|
|
loading: () => <LoadingIcon />,
|
|
|
});
|
|
|
|
|
|
-export function Avatar(props: { role: Message["role"] }) {
|
|
|
+export function Avatar(props: { role: Message["role"]; model?: ModelType }) {
|
|
|
const config = useChatStore((state) => state.config);
|
|
|
|
|
|
if (props.role !== "user") {
|
|
|
return (
|
|
|
<div className="no-dark">
|
|
|
- <BotIcon className={styles["user-avtar"]} />
|
|
|
+ {props.model?.startsWith("gpt-4") ? (
|
|
|
+ <BlackBotIcon className={styles["user-avtar"]} />
|
|
|
+ ) : (
|
|
|
+ <BotIcon className={styles["user-avtar"]} />
|
|
|
+ )}
|
|
|
</div>
|
|
|
);
|
|
|
}
|
|
@@ -432,6 +438,7 @@ export function Chat(props: {
|
|
|
const { submitKey, shouldSubmit } = useSubmitHandler();
|
|
|
const { scrollRef, setAutoScroll, scrollToBottom } = useScrollToBottom();
|
|
|
const [hitBottom, setHitBottom] = useState(false);
|
|
|
+ const isMobileScreen = useMobileScreen();
|
|
|
|
|
|
const onChatBodyScroll = (e: HTMLElement) => {
|
|
|
const isTouchBottom = e.scrollTop + e.clientHeight >= e.scrollHeight - 20;
|
|
@@ -462,7 +469,7 @@ export function Chat(props: {
|
|
|
const rows = inputRef.current ? autoGrowTextArea(inputRef.current) : 1;
|
|
|
const inputRows = Math.min(
|
|
|
5,
|
|
|
- Math.max(2 + Number(!isMobileScreen()), rows),
|
|
|
+ Math.max(2 + Number(!isMobileScreen), rows),
|
|
|
);
|
|
|
setInputRows(inputRows);
|
|
|
},
|
|
@@ -502,7 +509,7 @@ export function Chat(props: {
|
|
|
setBeforeInput(userInput);
|
|
|
setUserInput("");
|
|
|
setPromptHints([]);
|
|
|
- if (!isMobileScreen()) inputRef.current?.focus();
|
|
|
+ if (!isMobileScreen) inputRef.current?.focus();
|
|
|
setAutoScroll(true);
|
|
|
};
|
|
|
|
|
@@ -634,7 +641,7 @@ export function Chat(props: {
|
|
|
|
|
|
// Auto focus
|
|
|
useEffect(() => {
|
|
|
- if (props.sideBarShowing && isMobileScreen()) return;
|
|
|
+ if (props.sideBarShowing && isMobileScreen) return;
|
|
|
inputRef.current?.focus();
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
}, []);
|
|
@@ -682,7 +689,7 @@ export function Chat(props: {
|
|
|
}}
|
|
|
/>
|
|
|
</div>
|
|
|
- {!isMobileScreen() && (
|
|
|
+ {!isMobileScreen && (
|
|
|
<div className={styles["window-action-button"]}>
|
|
|
<IconButton
|
|
|
icon={chatStore.config.tightBorder ? <MinIcon /> : <MaxIcon />}
|
|
@@ -717,6 +724,11 @@ export function Chat(props: {
|
|
|
>
|
|
|
{messages.map((message, i) => {
|
|
|
const isUser = message.role === "user";
|
|
|
+ const showActions =
|
|
|
+ !isUser &&
|
|
|
+ i > 0 &&
|
|
|
+ !(message.preview || message.content.length === 0);
|
|
|
+ const showTyping = message.preview || message.streaming;
|
|
|
|
|
|
return (
|
|
|
<div
|
|
@@ -727,49 +739,48 @@ export function Chat(props: {
|
|
|
>
|
|
|
<div className={styles["chat-message-container"]}>
|
|
|
<div className={styles["chat-message-avatar"]}>
|
|
|
- <Avatar role={message.role} />
|
|
|
+ <Avatar role={message.role} model={message.model} />
|
|
|
</div>
|
|
|
- {(message.preview || message.streaming) && (
|
|
|
+ {showTyping && (
|
|
|
<div className={styles["chat-message-status"]}>
|
|
|
{Locale.Chat.Typing}
|
|
|
</div>
|
|
|
)}
|
|
|
<div className={styles["chat-message-item"]}>
|
|
|
- {!isUser &&
|
|
|
- !(message.preview || message.content.length === 0) && (
|
|
|
- <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>
|
|
|
- </>
|
|
|
- )}
|
|
|
-
|
|
|
+ {showActions && (
|
|
|
+ <div className={styles["chat-message-top-actions"]}>
|
|
|
+ {message.streaming ? (
|
|
|
<div
|
|
|
className={styles["chat-message-top-action"]}
|
|
|
- onClick={() => copyToClipboard(message.content)}
|
|
|
+ onClick={() => onUserStop(message.id ?? i)}
|
|
|
>
|
|
|
- {Locale.Chat.Actions.Copy}
|
|
|
+ {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={
|
|
@@ -778,7 +789,7 @@ export function Chat(props: {
|
|
|
}
|
|
|
onContextMenu={(e) => onRightClick(e, message)}
|
|
|
onDoubleClickCapture={() => {
|
|
|
- if (!isMobileScreen()) return;
|
|
|
+ if (!isMobileScreen) return;
|
|
|
setUserInput(message.content);
|
|
|
}}
|
|
|
fontSize={fontSize}
|