UI overhaul: fonts, colors, scrollable message boxes, animated text generation status updates
This commit is contained in:
@@ -100,7 +100,7 @@ export default function LayoutDefault({
|
|||||||
<TRPCProvider trpcClient={trpc} queryClient={queryClient}>
|
<TRPCProvider trpcClient={trpc} queryClient={queryClient}>
|
||||||
<MantineProvider theme={theme}>
|
<MantineProvider theme={theme}>
|
||||||
<AppShell
|
<AppShell
|
||||||
header={{ height: 60 }}
|
header={{ height: 80 }}
|
||||||
navbar={{
|
navbar={{
|
||||||
width: 300,
|
width: 300,
|
||||||
breakpoint: "sm",
|
breakpoint: "sm",
|
||||||
@@ -109,7 +109,7 @@ export default function LayoutDefault({
|
|||||||
padding="lg"
|
padding="lg"
|
||||||
>
|
>
|
||||||
<AppShell.Header>
|
<AppShell.Header>
|
||||||
<Group h="100%" px="md">
|
<Group px="md" wrap="nowrap">
|
||||||
<Burger
|
<Burger
|
||||||
opened={opened}
|
opened={opened}
|
||||||
onClick={toggle}
|
onClick={toggle}
|
||||||
@@ -120,7 +120,17 @@ export default function LayoutDefault({
|
|||||||
{" "}
|
{" "}
|
||||||
<Image h={50} fit="contain" src={logoUrl} />{" "}
|
<Image h={50} fit="contain" src={logoUrl} />{" "}
|
||||||
</a>
|
</a>
|
||||||
<Title>Token-Efficient Context Engineering</Title>
|
<Title
|
||||||
|
textWrap="balance"
|
||||||
|
lineClamp={1}
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
lineHeight: theme?.lineHeights?.["6xl"],
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Token-Efficient Context Engineering
|
||||||
|
</Title>
|
||||||
</Group>
|
</Group>
|
||||||
</AppShell.Header>
|
</AppShell.Header>
|
||||||
<AppShell.Navbar p="md">
|
<AppShell.Navbar p="md">
|
||||||
|
|||||||
@@ -110,6 +110,18 @@ const theme: MantineThemeOverride = createTheme({
|
|||||||
"3xl": "2.25rem",
|
"3xl": "2.25rem",
|
||||||
"4xl": "3rem",
|
"4xl": "3rem",
|
||||||
},
|
},
|
||||||
|
lineHeights: {
|
||||||
|
xs: "1.25rem",
|
||||||
|
sm: "1.375rem",
|
||||||
|
md: "1.5rem",
|
||||||
|
lg: "1.75rem",
|
||||||
|
xl: "2rem",
|
||||||
|
"2xl": "2.25rem",
|
||||||
|
"3xl": "2.625rem",
|
||||||
|
"4xl": "3rem",
|
||||||
|
"5xl": "3.75rem",
|
||||||
|
"6xl": "4.5rem",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default theme;
|
export default theme;
|
||||||
|
|||||||
+79
-13
@@ -5,12 +5,16 @@ import {
|
|||||||
HoverCard,
|
HoverCard,
|
||||||
JsonInput,
|
JsonInput,
|
||||||
List,
|
List,
|
||||||
|
ScrollArea,
|
||||||
Stack,
|
Stack,
|
||||||
Tabs,
|
Tabs,
|
||||||
|
Text,
|
||||||
Textarea,
|
Textarea,
|
||||||
|
TextInput,
|
||||||
|
Transition,
|
||||||
useMantineTheme,
|
useMantineTheme,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useEffect, useState } from "react";
|
import { memo, useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
defaultParameters,
|
defaultParameters,
|
||||||
defaultSystemPrompt,
|
defaultSystemPrompt,
|
||||||
@@ -23,6 +27,7 @@ import type {
|
|||||||
CommittedMessage,
|
CommittedMessage,
|
||||||
DraftMessage,
|
DraftMessage,
|
||||||
OtherParameters,
|
OtherParameters,
|
||||||
|
SendMessageStatus,
|
||||||
} from "../../../types";
|
} from "../../../types";
|
||||||
import Markdown from "react-markdown";
|
import Markdown from "react-markdown";
|
||||||
import {
|
import {
|
||||||
@@ -351,6 +356,8 @@ export default function ChatPage() {
|
|||||||
const setSendMessageStatus = useStore((state) => state.setSendMessageStatus);
|
const setSendMessageStatus = useStore((state) => state.setSendMessageStatus);
|
||||||
const setIsSendingMessage = useStore((state) => state.setIsSendingMessage);
|
const setIsSendingMessage = useStore((state) => state.setIsSendingMessage);
|
||||||
|
|
||||||
|
const theme = useMantineTheme();
|
||||||
|
|
||||||
// Function to send message using subscription
|
// Function to send message using subscription
|
||||||
const sendSubscriptionMessage = async ({
|
const sendSubscriptionMessage = async ({
|
||||||
conversationId,
|
conversationId,
|
||||||
@@ -489,11 +496,11 @@ export default function ChatPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box>
|
||||||
<div>
|
<Group justify="flex-start" gap={"sm"}>
|
||||||
<span>Conversation #{conversationId} - </span>
|
<TextInput
|
||||||
<input
|
inputSize="50"
|
||||||
type="text"
|
description={`Conversation #${conversationId}`}
|
||||||
defaultValue={conversationTitle || ""}
|
defaultValue={conversationTitle || ""}
|
||||||
// onChange={(e) => {
|
// onChange={(e) => {
|
||||||
// setConversationTitle(e.target.value);
|
// setConversationTitle(e.target.value);
|
||||||
@@ -504,10 +511,27 @@ export default function ChatPage() {
|
|||||||
title: e.target.value,
|
title: e.target.value,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
variant="unstyled"
|
||||||
|
styles={{
|
||||||
|
input: {
|
||||||
|
// backgroundColor: "transparent",
|
||||||
|
// border: "none",
|
||||||
|
// padding: 0,
|
||||||
|
// margin: 0,
|
||||||
|
fontFamily: theme.headings.fontFamily,
|
||||||
|
fontSize: theme.fontSizes.lg,
|
||||||
|
lineHeight: theme.lineHeights["4xl"],
|
||||||
|
},
|
||||||
|
wrapper: {
|
||||||
|
marginTop: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{isSendingMessage && <IconLoaderQuarter size={16} stroke={1.5} />}
|
{isSendingMessage && <IconLoaderQuarter size={16} stroke={1.5} />}
|
||||||
{sendMessageStatus && <span>{sendMessageStatus.message}</span>}
|
{sendMessageStatus && (
|
||||||
</div>
|
<StatusMessage sendMessageStatus={sendMessageStatus} />
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
<Tabs defaultValue="message">
|
<Tabs defaultValue="message">
|
||||||
<Tabs.List>
|
<Tabs.List>
|
||||||
<Tabs.Tab value="message">Message</Tabs.Tab>
|
<Tabs.Tab value="message">Message</Tabs.Tab>
|
||||||
@@ -690,7 +714,7 @@ export default function ChatPage() {
|
|||||||
</List>
|
</List>
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -792,15 +816,16 @@ function Messages() {
|
|||||||
position={message.role === "user" ? "left" : "right"}
|
position={message.role === "user" ? "left" : "right"}
|
||||||
>
|
>
|
||||||
<HoverCard.Target>
|
<HoverCard.Target>
|
||||||
<Box
|
<ScrollArea
|
||||||
|
scrollbars="x"
|
||||||
w="75%"
|
w="75%"
|
||||||
|
p="md"
|
||||||
|
bdrs="md"
|
||||||
bg={
|
bg={
|
||||||
message.role === "user"
|
message.role === "user"
|
||||||
? theme.colors.gray[2]
|
? theme.colors.gray[2]
|
||||||
: theme.colors.blue[2]
|
: theme.colors.blue[2]
|
||||||
}
|
}
|
||||||
p="md"
|
|
||||||
bdrs="md"
|
|
||||||
>
|
>
|
||||||
<Markdown>
|
<Markdown>
|
||||||
{message.parts
|
{message.parts
|
||||||
@@ -808,7 +833,7 @@ function Messages() {
|
|||||||
.map((p) => p.text)
|
.map((p) => p.text)
|
||||||
.join("\n")}
|
.join("\n")}
|
||||||
</Markdown>
|
</Markdown>
|
||||||
</Box>
|
</ScrollArea>
|
||||||
</HoverCard.Target>
|
</HoverCard.Target>
|
||||||
<HoverCard.Dropdown>
|
<HoverCard.Dropdown>
|
||||||
<ActionIcon.Group>
|
<ActionIcon.Group>
|
||||||
@@ -840,3 +865,44 @@ function Messages() {
|
|||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const StatusMessage = memo(
|
||||||
|
({ sendMessageStatus }: { sendMessageStatus: SendMessageStatus | null }) => {
|
||||||
|
const [displayMessage, setDisplayMessage] = useState(sendMessageStatus);
|
||||||
|
const [isVisible, setIsVisible] = useState(sendMessageStatus !== null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (sendMessageStatus === null) {
|
||||||
|
setIsVisible(false);
|
||||||
|
setTimeout(() => setDisplayMessage(null), 250);
|
||||||
|
} else if (displayMessage === null) {
|
||||||
|
setDisplayMessage(sendMessageStatus);
|
||||||
|
setIsVisible(true);
|
||||||
|
} else if (displayMessage.message !== sendMessageStatus.message) {
|
||||||
|
setIsVisible(false);
|
||||||
|
setTimeout(() => {
|
||||||
|
setDisplayMessage(sendMessageStatus);
|
||||||
|
setIsVisible(true);
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
}, [sendMessageStatus, displayMessage]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ position: "relative" }}>
|
||||||
|
{/* This is a hack to make this component take up the space it would take up once the transition is complete. Useful for when this component is in a flexbox with a particular alignment/justification, which we want to be calculated against its eventual size. */}
|
||||||
|
<span style={{ visibility: "hidden" }}>{displayMessage?.message}</span>
|
||||||
|
<Transition
|
||||||
|
transition="pop"
|
||||||
|
duration={500}
|
||||||
|
mounted={isVisible && displayMessage !== null}
|
||||||
|
>
|
||||||
|
{(styles) => (
|
||||||
|
<span style={{ ...styles, position: "absolute", top: 0, left: 0 }}>
|
||||||
|
{displayMessage?.message}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user