Merge branch 'fix-theme' of https://github.com/AprilNEA/ChatGPT-Next-Web into fix-theme

This commit is contained in:
AprilNEA
2023-03-27 17:05:15 +08:00
21 changed files with 288 additions and 77 deletions

View File

@@ -27,6 +27,7 @@ import Locale from "../locales";
import dynamic from "next/dynamic";
import { REPO_URL } from "../constant";
import { ControllerPool } from "../requests";
export function Loading(props: { noLogo?: boolean }) {
return (
@@ -146,28 +147,67 @@ function useSubmitHandler() {
export function Chat(props: { showSideBar?: () => void }) {
type RenderMessage = Message & { preview?: boolean };
const session = useChatStore((state) => state.currentSession());
const [session, sessionIndex] = useChatStore((state) => [
state.currentSession(),
state.currentSessionIndex,
]);
const [userInput, setUserInput] = useState("");
const [isLoading, setIsLoading] = useState(false);
const { submitKey, shouldSubmit } = useSubmitHandler();
const onUserInput = useChatStore((state) => state.onUserInput);
// submit user input
const onUserSubmit = () => {
if (userInput.length <= 0) return;
setIsLoading(true);
onUserInput(userInput).then(() => setIsLoading(false));
setUserInput("");
};
// stop response
const onUserStop = (messageIndex: number) => {
console.log(ControllerPool, sessionIndex, messageIndex);
ControllerPool.stop(sessionIndex, messageIndex);
};
// check if should send message
const onInputKeyDown = (e: KeyboardEvent) => {
if (shouldSubmit(e)) {
onUserSubmit();
e.preventDefault();
}
};
const onRightClick = (e: any, message: Message) => {
// auto fill user input
if (message.role === "user") {
setUserInput(message.content);
}
// copy to clipboard
if (selectOrCopy(e.currentTarget, message.content)) {
e.preventDefault();
}
};
const onResend = (botIndex: number) => {
// find last user input message and resend
for (let i = botIndex; i >= 0; i -= 1) {
if (messages[i].role === "user") {
setIsLoading(true);
onUserInput(messages[i].content).then(() => setIsLoading(false));
return;
}
}
};
// for auto-scroll
const latestMessageRef = useRef<HTMLDivElement>(null);
const [hoveringMessage, setHoveringMessage] = useState(false);
// wont scroll while hovering messages
const [autoScroll, setAutoScroll] = useState(false);
// preview messages
const messages = (session.messages as RenderMessage[])
.concat(
isLoading
@@ -194,10 +234,11 @@ export function Chat(props: { showSideBar?: () => void }) {
: []
);
// auto scroll
useLayoutEffect(() => {
setTimeout(() => {
const dom = latestMessageRef.current;
if (dom && !isIOS() && !hoveringMessage) {
if (dom && !isIOS() && autoScroll) {
dom.scrollIntoView({
behavior: "smooth",
block: "end",
@@ -252,15 +293,7 @@ export function Chat(props: { showSideBar?: () => void }) {
</div>
</div>
<div
className={styles["chat-body"]}
onMouseOver={() => {
setHoveringMessage(true);
}}
onMouseOut={() => {
setHoveringMessage(false);
}}
>
<div className={styles["chat-body"]}>
{messages.map((message, i) => {
const isUser = message.role === "user";
@@ -283,13 +316,20 @@ export function Chat(props: { showSideBar?: () => void }) {
<div className={styles["chat-message-item"]}>
{!isUser && (
<div className={styles["chat-message-top-actions"]}>
{message.streaming && (
{message.streaming ? (
<div
className={styles["chat-message-top-action"]}
onClick={() => showToast(Locale.WIP)}
onClick={() => onUserStop(i)}
>
{Locale.Chat.Actions.Stop}
</div>
) : (
<div
className={styles["chat-message-top-action"]}
onClick={() => onResend(i)}
>
{Locale.Chat.Actions.Retry}
</div>
)}
<div
@@ -306,11 +346,7 @@ export function Chat(props: { showSideBar?: () => void }) {
) : (
<div
className="markdown-body"
onContextMenu={(e) => {
if (selectOrCopy(e.currentTarget, message.content)) {
e.preventDefault();
}
}}
onContextMenu={(e) => onRightClick(e, message)}
>
<Markdown content={message.content} />
</div>
@@ -341,6 +377,9 @@ export function Chat(props: { showSideBar?: () => void }) {
onInput={(e) => setUserInput(e.currentTarget.value)}
value={userInput}
onKeyDown={(e) => onInputKeyDown(e as any)}
onFocus={() => setAutoScroll(true)}
onBlur={() => setAutoScroll(false)}
autoFocus
/>
<IconButton
icon={<SendWhiteIcon />}

View File

@@ -4,15 +4,36 @@ import RemarkMath from "remark-math";
import RehypeKatex from "rehype-katex";
import RemarkGfm from "remark-gfm";
import RehypePrsim from "rehype-prism-plus";
import { useRef } from "react";
import { copyToClipboard } from "../utils";
export function PreCode(props: { children: any }) {
const ref = useRef<HTMLPreElement>(null);
return (
<pre ref={ref}>
<span
className="copy-code-button"
onClick={() => {
if (ref.current) {
const code = ref.current.innerText;
copyToClipboard(code);
}
}}
></span>
{props.children}
</pre>
);
}
export function Markdown(props: { content: string }) {
return (
<ReactMarkdown
remarkPlugins={[RemarkMath, RemarkGfm]}
rehypePlugins={[
RehypeKatex,
[RehypePrsim, { ignoreMissing: true }],
]}
rehypePlugins={[RehypeKatex, [RehypePrsim, { ignoreMissing: true }]]}
components={{
pre: PreCode,
}}
>
{props.content}
</ReactMarkdown>

View File

@@ -257,6 +257,20 @@ export function Settings(props: { closeSettings: () => void }) {
<></>
)}
<SettingItem
title={Locale.Settings.Token.Title}
subTitle={Locale.Settings.Token.SubTitle}
>
<input
value={accessStore.token}
type="text"
placeholder={Locale.Settings.Token.Placeholder}
onChange={(e) => {
accessStore.updateToken(e.currentTarget.value);
}}
></input>
</SettingItem>
<SettingItem
title={Locale.Settings.HistoryCount.Title}
subTitle={Locale.Settings.HistoryCount.SubTitle}