From 4c46de7d1dd18c3a2907004f0f273aa5340254fd Mon Sep 17 00:00:00 2001 From: Hk-Gosuto Date: Mon, 4 Dec 2023 21:33:27 +0800 Subject: [PATCH] feat: style change --- app/client/api.ts | 1 + app/components/chat.module.scss | 33 ++ app/components/chat.tsx | 122 ++++++- app/components/markdown.tsx | 59 ++-- app/styles/markdown.scss | 556 ++++++++++++++++---------------- 5 files changed, 452 insertions(+), 319 deletions(-) diff --git a/app/client/api.ts b/app/client/api.ts index c304f6f44..ccd876e90 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -12,6 +12,7 @@ export type ChatModel = ModelType; export interface RequestMessage { role: MessageRole; content: string; + image_url?: string; } export interface LLMConfig { diff --git a/app/components/chat.module.scss b/app/components/chat.module.scss index a468cf6a1..95247083a 100644 --- a/app/components/chat.module.scss +++ b/app/components/chat.module.scss @@ -539,6 +539,35 @@ bottom: 32px; } +.chat-input-image { + background-color: var(--primary); + color: white; + + position: absolute; + right: 28px; + bottom: 78px; + + display: flex; + align-items: flex-start; + + border-radius: 4px; + overflow: hidden; + border: 1px solid rgba(0, 0, 0, 0.1); +} + +.chat-input-image-close { + fill: black; + border: none; + align-items: center; + justify-content: center; + display: flex; + margin: 0px; + padding: 0px; + background-color: white; + width: 22px; + height: 48px; +} + @media only screen and (max-width: 600px) { .chat-input { font-size: 16px; @@ -547,4 +576,8 @@ .chat-input-send { bottom: 30px; } + + .chat-input-image { + bottom: 30px; + } } diff --git a/app/components/chat.tsx b/app/components/chat.tsx index a01df3efb..7e19fb028 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -29,6 +29,7 @@ import ConfirmIcon from "../icons/confirm.svg"; import CancelIcon from "../icons/cancel.svg"; import EnablePluginIcon from "../icons/plugin_enable.svg"; import DisablePluginIcon from "../icons/plugin_disable.svg"; +import UploadIcon from "../icons/upload.svg"; import LightIcon from "../icons/light.svg"; import DarkIcon from "../icons/dark.svg"; @@ -92,6 +93,7 @@ import { prettyObject } from "../utils/format"; import { ExportMessageModal } from "./exporter"; import { getClientConfig } from "../config/client"; import { useAllModels } from "../utils/hooks"; +import Image from "next/image"; const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { loading: () => , @@ -331,8 +333,10 @@ function ClearContextDivider() { function ChatAction(props: { text: string; - icon: JSX.Element; + icon?: JSX.Element; + innerNode?: JSX.Element; onClick: () => void; + style?: React.CSSProperties; }) { const iconRef = useRef(null); const textRef = useRef(null); @@ -357,23 +361,29 @@ function ChatAction(props: { className={`${styles["chat-input-action"]} clickable`} onClick={() => { props.onClick(); - setTimeout(updateWidth, 1); + iconRef ? setTimeout(updateWidth, 1) : undefined; }} - onMouseEnter={updateWidth} - onTouchStart={updateWidth} + onMouseEnter={props.icon ? updateWidth : undefined} + onTouchStart={props.icon ? updateWidth : undefined} style={ - { - "--icon-width": `${width.icon}px`, - "--full-width": `${width.full}px`, - } as React.CSSProperties + props.icon + ? ({ + "--icon-width": `${width.icon}px`, + "--full-width": `${width.full}px`, + ...props.style, + } as React.CSSProperties) + : props.style } > -
- {props.icon} -
-
+ {props.icon ? ( +
+ {props.icon} +
+ ) : null} +
{props.text}
+ {props.innerNode}
); } @@ -412,6 +422,7 @@ export function ChatActions(props: { showPromptModal: () => void; scrollToBottom: () => void; showPromptHints: () => void; + imageSelected: (img: any) => void; hitBottom: boolean; }) { const config = useAppConfig(); @@ -440,6 +451,25 @@ export function ChatActions(props: { const couldStop = ChatControllerPool.hasPending(); const stopAll = () => ChatControllerPool.stopAll(); + function selectImage() { + document.getElementById("chat-image-file-select-upload")?.click(); + } + + const onImageSelected = (e: any) => { + const file = e.target.files[0]; + const filename = file.name; + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = () => { + const base64 = reader.result; + props.imageSelected({ + filename, + base64, + }); + }; + e.target.value = null; + }; + // switch model const currentModel = chatStore.currentSession().mask.modelConfig.model; const allModels = useAllModels(); @@ -536,6 +566,21 @@ export function ChatActions(props: { /> )} + } + innerNode={ + + } + /> + {showModelSelector && ( (null); const [userInput, setUserInput] = useState(""); + const [userImage, setUserImage] = useState(); const [isLoading, setIsLoading] = useState(false); const { submitKey, shouldSubmit } = useSubmitHandler(); const { scrollRef, setAutoScroll, scrollDomToBottom } = useScrollToBottom(); @@ -722,7 +768,7 @@ function _Chat() { } }; - const doSubmit = (userInput: string) => { + const doSubmit = (userInput: string, userImage?: any) => { if (userInput.trim() === "") return; const matchCommand = chatCommands.match(userInput); if (matchCommand.matched) { @@ -736,6 +782,7 @@ function _Chat() { localStorage.setItem(LAST_INPUT_KEY, userInput); setUserInput(""); setPromptHints([]); + setUserImage(null); if (!isMobileScreen) inputRef.current?.focus(); setAutoScroll(true); }; @@ -937,6 +984,7 @@ function _Chat() { ...createMessage({ role: "user", content: userInput, + image_url: userImage?.base64, }), preview: true, }, @@ -949,6 +997,7 @@ function _Chat() { isLoading, session.messages, userInput, + userImage?.base64, ]); const [msgRenderIndex, _setMsgRenderIndex] = useState( @@ -1075,6 +1124,8 @@ function _Chat() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const textareaMinHeight = userImage ? 121 : 68; + return (
@@ -1280,6 +1331,7 @@ function _Chat() { )}
= messages.length - 6} />
- + {!isUser && message.model == "gpt-4-vision-preview" && ( +
+
+
+ )}
{isContext ? Locale.Chat.IsContext @@ -1328,6 +1392,9 @@ function _Chat() { setUserInput("/"); onSearch(""); }} + imageSelected={(img: any) => { + setUserImage(img); + }} />