diff --git a/app/components/chat.module.scss b/app/components/chat.module.scss
index f57e6c100..7cd2889f7 100644
--- a/app/components/chat.module.scss
+++ b/app/components/chat.module.scss
@@ -1,5 +1,29 @@
@import "../styles/animation.scss";
+.chat-input-actions {
+ display: flex;
+ flex-wrap: wrap;
+
+ .chat-input-action {
+ display: inline-flex;
+ border-radius: 20px;
+ font-size: 12px;
+ background-color: var(--white);
+ color: var(--black);
+ border: var(--border-in-light);
+ padding: 4px 10px;
+ animation: slide-in ease 0.3s;
+ box-shadow: var(--card-shadow);
+ transition: all ease 0.3s;
+ margin-bottom: 10px;
+ align-items: center;
+
+ &:not(:last-child) {
+ margin-right: 5px;
+ }
+ }
+}
+
.prompt-toast {
position: absolute;
bottom: -50px;
diff --git a/app/components/chat.tsx b/app/components/chat.tsx
index 33ac3ac57..b9ae13926 100644
--- a/app/components/chat.tsx
+++ b/app/components/chat.tsx
@@ -14,6 +14,11 @@ import DeleteIcon from "../icons/delete.svg";
import MaxIcon from "../icons/max.svg";
import MinIcon from "../icons/min.svg";
+import LightIcon from "../icons/light.svg";
+import DarkIcon from "../icons/dark.svg";
+import AutoIcon from "../icons/auto.svg";
+import BottomIcon from "../icons/bottom.svg";
+
import {
Message,
SubmitKey,
@@ -22,6 +27,7 @@ import {
ROLES,
createMessage,
useAccessStore,
+ Theme,
} from "../store";
import {
@@ -31,6 +37,7 @@ import {
isMobileScreen,
selectOrCopy,
autoGrowTextArea,
+ getCSSVar,
} from "../utils";
import dynamic from "next/dynamic";
@@ -60,7 +67,11 @@ export function Avatar(props: { role: Message["role"] }) {
const config = useChatStore((state) => state.config);
if (props.role !== "user") {
- return ;
+ return (
+
+
+
+ );
}
return (
@@ -316,22 +327,78 @@ function useScrollToBottom() {
// for auto-scroll
const scrollRef = useRef(null);
const [autoScroll, setAutoScroll] = useState(true);
+ const scrollToBottom = () => {
+ const dom = scrollRef.current;
+ if (dom) {
+ setTimeout(() => (dom.scrollTop = dom.scrollHeight), 1);
+ }
+ };
// auto scroll
useLayoutEffect(() => {
- const dom = scrollRef.current;
- if (dom && autoScroll) {
- setTimeout(() => (dom.scrollTop = dom.scrollHeight), 1);
- }
+ autoScroll && scrollToBottom();
});
return {
scrollRef,
autoScroll,
setAutoScroll,
+ scrollToBottom,
};
}
+export function ChatActions(props: {
+ showPromptModal: () => void;
+ scrollToBottom: () => void;
+ hitBottom: boolean;
+}) {
+ const chatStore = useChatStore();
+
+ const theme = chatStore.config.theme;
+
+ function nextTheme() {
+ const themes = [Theme.Auto, Theme.Light, Theme.Dark];
+ const themeIndex = themes.indexOf(theme);
+ const nextIndex = (themeIndex + 1) % themes.length;
+ const nextTheme = themes[nextIndex];
+ chatStore.updateConfig((config) => (config.theme = nextTheme));
+ }
+
+ return (
+
+ {!props.hitBottom && (
+
+
+
+ )}
+ {props.hitBottom && (
+
+
+
+ )}
+
+
+ {theme === Theme.Auto ? (
+
+ ) : theme === Theme.Light ? (
+
+ ) : theme === Theme.Dark ? (
+
+ ) : null}
+
+
+ );
+}
+
export function Chat(props: {
showSideBar?: () => void;
sideBarShowing?: boolean;
@@ -350,7 +417,7 @@ export function Chat(props: {
const [beforeInput, setBeforeInput] = useState("");
const [isLoading, setIsLoading] = useState(false);
const { submitKey, shouldSubmit } = useSubmitHandler();
- const { scrollRef, setAutoScroll } = useScrollToBottom();
+ const { scrollRef, setAutoScroll, scrollToBottom } = useScrollToBottom();
const [hitBottom, setHitBottom] = useState(false);
const onChatBodyScroll = (e: HTMLElement) => {
@@ -672,22 +739,20 @@ export function Chat(props: {
)}
- {(message.preview || message.content.length === 0) &&
- !isUser ? (
-
- ) : (
- onRightClick(e, message)}
- onDoubleClickCapture={() => {
- if (!isMobileScreen()) return;
- setUserInput(message.content);
- }}
- >
-
-
- )}
+ onRightClick(e, message)}
+ onDoubleClickCapture={() => {
+ if (!isMobileScreen()) return;
+ setUserInput(message.content);
+ }}
+ fontSize={fontSize}
+ parentRef={scrollRef}
+ />
{!isUser && !message.preview && (
@@ -704,6 +769,12 @@ export function Chat(props: {
+
+
setShowPromptModal(true)}
+ scrollToBottom={scrollToBottom}
+ hitBottom={hitBottom}
+ />
);
}
diff --git a/app/global.d.ts b/app/global.d.ts
index bbe834b3d..bd1c062de 100644
--- a/app/global.d.ts
+++ b/app/global.d.ts
@@ -3,12 +3,9 @@ declare module "*.png";
declare module "*.woff2";
declare module "*.woff";
declare module "*.ttf";
-declare module "*.scss";
-
-declare module "*.svg" {
- import React = require("react");
-
- export const ReactComponent: React.SFC>;
- const src: string;
- export default src;
+declare module "*.scss" {
+ const content: Record;
+ export default content;
}
+
+declare module "*.svg";
diff --git a/app/icons/auto.svg b/app/icons/auto.svg
new file mode 100644
index 000000000..6745dfbd0
--- /dev/null
+++ b/app/icons/auto.svg
@@ -0,0 +1 @@
+
diff --git a/app/icons/bottom.svg b/app/icons/bottom.svg
new file mode 100644
index 000000000..06c663ab2
--- /dev/null
+++ b/app/icons/bottom.svg
@@ -0,0 +1 @@
+
diff --git a/app/icons/dark.svg b/app/icons/dark.svg
new file mode 100644
index 000000000..3eebc373e
--- /dev/null
+++ b/app/icons/dark.svg
@@ -0,0 +1 @@
+
diff --git a/app/icons/light.svg b/app/icons/light.svg
new file mode 100644
index 000000000..22cfa1fff
--- /dev/null
+++ b/app/icons/light.svg
@@ -0,0 +1 @@
+
diff --git a/app/styles/globals.scss b/app/styles/globals.scss
index 53902d935..cf36ee92b 100644
--- a/app/styles/globals.scss
+++ b/app/styles/globals.scss
@@ -1,4 +1,6 @@
@mixin light {
+ --theme: light;
+
/* color */
--white: white;
--black: rgb(48, 48, 48);
@@ -18,6 +20,8 @@
}
@mixin dark {
+ --theme: dark;
+
/* color */
--white: rgb(30, 30, 30);
--black: rgb(187, 187, 187);
@@ -31,6 +35,10 @@
--border-in-light: 1px solid rgba(255, 255, 255, 0.192);
--theme-color: var(--gray);
+
+ div:not(.no-dark) > svg {
+ filter: invert(0.5);
+ }
}
.light {
@@ -282,10 +290,6 @@ pre {
.clickable {
cursor: pointer;
- div:not(.no-dark) > svg {
- filter: invert(0.5);
- }
-
&:hover {
filter: brightness(0.9);
}
diff --git a/app/utils.ts b/app/utils.ts
index 5c2b06975..0e4a8eaea 100644
--- a/app/utils.ts
+++ b/app/utils.ts
@@ -120,3 +120,7 @@ export function autoGrowTextArea(dom: HTMLTextAreaElement) {
return rows;
}
+
+export function getCSSVar(varName: string) {
+ return getComputedStyle(document.body).getPropertyValue(varName).trim();
+}