feat: adaptive light mode

This commit is contained in:
WanQuanXie 2024-05-24 20:29:49 +08:00
parent f9664d48e7
commit 996cc1b674
18 changed files with 149 additions and 102 deletions

View File

@ -34,7 +34,7 @@ export default function RootLayout({
unstyled: true, unstyled: true,
classNames: { classNames: {
toast: toast:
'dark:dark:bg-[#111111] text-white rounded-lg p-4 flex flex-row items-center space-x-2', 'bg-primaryLight dark:bg-primaryDark text-white rounded-lg p-4 flex flex-row items-center space-x-2',
}, },
}} }}
/> />

View File

@ -66,7 +66,7 @@ const Chat = ({
sendMessage={sendMessage} sendMessage={sendMessage}
/> />
{!isLast && msg.role === 'assistant' && ( {!isLast && msg.role === 'assistant' && (
<div className="h-px w-full bg-[#1C1C1C]" /> <div className="h-px w-full bg-secondLight dark:bg-secondDark" />
)} )}
</Fragment> </Fragment>
); );

View File

@ -11,7 +11,7 @@ const EmptyChat = ({
}) => { }) => {
return ( return (
<div className="flex flex-col items-center justify-center min-h-screen max-w-screen-sm mx-auto p-2 space-y-8"> <div className="flex flex-col items-center justify-center min-h-screen max-w-screen-sm mx-auto p-2 space-y-8">
<h2 className="text-white/70 text-3xl font-medium -mt-8"> <h2 className="text-black/70 dark:text-white/70 text-3xl font-medium -mt-8">
Research begins here. Research begins here.
</h2> </h2>
<EmptyChatMessageInput <EmptyChatMessageInput

View File

@ -31,12 +31,12 @@ const EmptyChatMessageInput = ({
}} }}
className="w-full" className="w-full"
> >
<div className="flex flex-col bg-[#111111] px-5 pt-5 pb-2 rounded-lg w-full border border-[#1C1C1C]"> <div className="flex flex-col bg-primaryLight dark:bg-primaryDark px-5 pt-5 pb-2 rounded-lg w-full border border-light dark:border-dark">
<TextareaAutosize <TextareaAutosize
value={message} value={message}
onChange={(e) => setMessage(e.target.value)} onChange={(e) => setMessage(e.target.value)}
minRows={2} minRows={2}
className="bg-transparent placeholder:text-white/50 text-sm text-white resize-none focus:outline-none w-full max-h-24 lg:max-h-36 xl:max-h-48" className="bg-transparent placeholder:text-black/50 dark:placeholder:text-white/50 text-sm text-black dark:text-white resize-none focus:outline-none w-full max-h-24 lg:max-h-36 xl:max-h-48"
placeholder="Ask anything..." placeholder="Ask anything..."
/> />
<div className="flex flex-row items-center justify-between mt-4"> <div className="flex flex-row items-center justify-between mt-4">
@ -51,7 +51,7 @@ const EmptyChatMessageInput = ({
/> />
<button <button
disabled={message.trim().length === 0} disabled={message.trim().length === 0}
className="bg-[#24A0ED] text-white disabled:text-white/50 hover:bg-opacity-85 transition duration-100 disabled:bg-[#ececec21] rounded-full p-2" className="bg-[#24A0ED] text-black dark:text-white disabled:text-black/50 dark:disabled:text-white/50 disabled:bg-[#e0e0dc] dark:disabled:bg-[#ececec21] hover:bg-opacity-85 transition duration-100 rounded-full p-2"
> >
<ArrowRight className="bg-background" size={17} /> <ArrowRight className="bg-background" size={17} />
</button> </button>

View File

@ -1,6 +1,6 @@
const Layout = ({ children }: { children: React.ReactNode }) => { const Layout = ({ children }: { children: React.ReactNode }) => {
return ( return (
<main className="lg:pl-20 bg-[#0A0A0A] min-h-screen"> <main className="lg:pl-20 bg-primaryLight dark:bg-primaryDark min-h-screen">
<div className="max-w-screen-lg lg:mx-auto mx-4">{children}</div> <div className="max-w-screen-lg lg:mx-auto mx-4">{children}</div>
</main> </main>
); );

View File

@ -19,7 +19,7 @@ const Copy = ({
setCopied(true); setCopied(true);
setTimeout(() => setCopied(false), 1000); setTimeout(() => setCopied(false), 1000);
}} }}
className="p-2 text-white/70 rounded-xl hover:bg-[#1c1c1c] transition duration-200 hover:text-white" className="p-2 text-black/70 dark:text-white/70 rounded-xl hover:bg-secondLight dark:hover:bg-secondDark transition duration-200 hover:text-black dark:hover:text-white"
> >
{copied ? <Check size={18} /> : <ClipboardList size={18} />} {copied ? <Check size={18} /> : <ClipboardList size={18} />}
</button> </button>

View File

@ -10,7 +10,7 @@ const Rewrite = ({
return ( return (
<button <button
onClick={() => rewrite(messageId)} onClick={() => rewrite(messageId)}
className="py-2 px-3 text-white/70 rounded-xl hover:bg-[#1c1c1c] transition duration-200 hover:text-white flex flex-row items-center space-x-1" className="py-2 px-3 text-black/70 dark:text-white/70 rounded-xl hover(bg-secondLight dark:bg-secondDark) transition duration-200 hover:text-black dark:hover:text-white flex flex-row items-center space-x-1"
> >
<ArrowLeftRight size={18} /> <ArrowLeftRight size={18} />
<p className="text-xs font-medium">Rewrite</p> <p className="text-xs font-medium">Rewrite</p>

View File

@ -55,7 +55,7 @@ const MessageBox = ({
message.content.replace( message.content.replace(
regex, regex,
(_, number) => (_, number) =>
`<a href="${message.sources?.[number - 1]?.metadata?.url}" target="_blank" className="bg-[#1C1C1C] px-1 rounded ml-1 no-underline text-xs text-white/70 relative">${number}</a>`, `<a href="${message.sources?.[number - 1]?.metadata?.url}" target="_blank" className="bg-secondLight dark:bg-secondDark px-1 rounded ml-1 no-underline text-xs text-black/70 dark:text-white/70 relative">${number}</a>`,
), ),
); );
} }
@ -70,7 +70,7 @@ const MessageBox = ({
<div> <div>
{message.role === 'user' && ( {message.role === 'user' && (
<div className={cn('w-full', messageIndex === 0 ? 'pt-16' : 'pt-8')}> <div className={cn('w-full', messageIndex === 0 ? 'pt-16' : 'pt-8')}>
<h2 className="text-white font-medium text-3xl lg:w-9/12"> <h2 className="text-black dark:text-white font-medium text-3xl lg:w-9/12">
{message.content} {message.content}
</h2> </h2>
</div> </div>
@ -85,8 +85,10 @@ const MessageBox = ({
{message.sources && message.sources.length > 0 && ( {message.sources && message.sources.length > 0 && (
<div className="flex flex-col space-y-2"> <div className="flex flex-col space-y-2">
<div className="flex flex-row items-center space-x-2"> <div className="flex flex-row items-center space-x-2">
<BookCopy className="text-white" size={20} /> <BookCopy className="text-black dark:text-white" size={20} />
<h3 className="text-white font-medium text-xl">Sources</h3> <h3 className="text-black dark:text-white font-medium text-xl">
Sources
</h3>
</div> </div>
<MessageSources sources={message.sources} /> <MessageSources sources={message.sources} />
</div> </div>
@ -95,20 +97,22 @@ const MessageBox = ({
<div className="flex flex-row items-center space-x-2"> <div className="flex flex-row items-center space-x-2">
<Disc3 <Disc3
className={cn( className={cn(
'text-white', 'text-black dark:text-white',
isLast && loading ? 'animate-spin' : 'animate-none', isLast && loading ? 'animate-spin' : 'animate-none',
)} )}
size={20} size={20}
/> />
<h3 className="text-white font-medium text-xl">Answer</h3> <h3 className="text-black dark:text-white font-medium text-xl">
Answer
</h3>
</div> </div>
<Markdown className="prose max-w-none break-words prose-invert prose-p:leading-relaxed prose-pre:p-0 text-white text-sm md:text-base font-medium"> <Markdown className="prose prose-strong:text-black dark:prose-strong:text-white dark:prose-invert max-w-none break-words prose-invert prose-p:leading-relaxed prose-pre:p-0 text-black dark:text-white text-sm md:text-base font-medium">
{parsedMessage} {parsedMessage}
</Markdown> </Markdown>
{loading && isLast ? null : ( {loading && isLast ? null : (
<div className="flex flex-row items-center justify-between w-full text-white py-4 -mx-2"> <div className="flex flex-row items-center justify-between w-full text-black dark:text-white py-4 -mx-2">
<div className="flex flex-row items-center space-x-1"> <div className="flex flex-row items-center space-x-1">
{/* <button className="p-2 text-white/70 rounded-xl hover:bg-[#1c1c1c] transition duration-200 hover:text-white"> {/* <button className="p-2 text-black/70 dark:text-white/70 rounded-xl hover(bg-secondLight dark:bg-secondDark) transition duration-200 hover:text-black dark:hover:text-white">
<Share size={18} /> <Share size={18} />
</button> */} </button> */}
<Rewrite rewrite={rewrite} messageId={message.id} /> <Rewrite rewrite={rewrite} messageId={message.id} />
@ -123,7 +127,7 @@ const MessageBox = ({
start(); start();
} }
}} }}
className="p-2 text-white/70 rounded-xl hover:bg-[#1c1c1c] transition duration-200 hover:text-white" className="p-2 text-black/70 dark:text-white/70 rounded-xl hover(bg-secondLight dark:bg-secondDark) transition duration-200 hover:text-black dark:hover:text-white"
> >
{speechStatus === 'started' ? ( {speechStatus === 'started' ? (
<StopCircle size={18} /> <StopCircle size={18} />
@ -140,8 +144,8 @@ const MessageBox = ({
message.role === 'assistant' && message.role === 'assistant' &&
!loading && ( !loading && (
<> <>
<div className="h-px w-full bg-[#1C1C1C]" /> <div className="h-px w-full bg-secondLight dark:bg-secondDark" />
<div className="flex flex-col space-y-3 text-white"> <div className="flex flex-col space-y-3 text-black dark:text-white">
<div className="flex flex-row items-center space-x-2 mt-4"> <div className="flex flex-row items-center space-x-2 mt-4">
<Layers3 /> <Layers3 />
<h3 className="text-xl font-medium">Related</h3> <h3 className="text-xl font-medium">Related</h3>
@ -152,7 +156,7 @@ const MessageBox = ({
className="flex flex-col space-y-3 text-sm" className="flex flex-col space-y-3 text-sm"
key={i} key={i}
> >
<div className="h-px w-full bg-[#1C1C1C]" /> <div className="h-px w-full bg-secondLight dark:bg-secondDark" />
<div <div
onClick={() => { onClick={() => {
sendMessage(suggestion); sendMessage(suggestion);

View File

@ -1,9 +1,9 @@
const MessageBoxLoading = () => { const MessageBoxLoading = () => {
return ( return (
<div className="flex flex-col space-y-2 w-full lg:w-9/12 bg-[#111111] animate-pulse rounded-lg p-3"> <div className="flex flex-col space-y-2 w-full lg:w-9/12 bg-primaryLight dark:bg-primaryDark animate-pulse rounded-lg p-3">
<div className="h-2 rounded-full w-full bg-[#1c1c1c]" /> <div className="h-2 rounded-full w-full bg-secondLight dark:bg-secondDark" />
<div className="h-2 rounded-full w-9/12 bg-[#1c1c1c]" /> <div className="h-2 rounded-full w-9/12 bg-secondLight dark:bg-secondDark" />
<div className="h-2 rounded-full w-10/12 bg-[#1c1c1c]" /> <div className="h-2 rounded-full w-10/12 bg-secondLight dark:bg-secondDark" />
</div> </div>
); );
}; };

View File

@ -40,7 +40,7 @@ const MessageInput = ({
} }
}} }}
className={cn( className={cn(
'bg-[#111111] p-4 flex items-center overflow-hidden border border-[#1C1C1C]', 'bg-primaryLight dark:bg-primaryDark p-4 flex items-center overflow-hidden border border-light dark:border-dark',
mode === 'multi' ? 'flex-col rounded-lg' : 'flex-row rounded-full', mode === 'multi' ? 'flex-col rounded-lg' : 'flex-row rounded-full',
)} )}
> >
@ -51,7 +51,7 @@ const MessageInput = ({
onHeightChange={(height, props) => { onHeightChange={(height, props) => {
setTextareaRows(Math.ceil(height / props.rowHeight)); setTextareaRows(Math.ceil(height / props.rowHeight));
}} }}
className="transition bg-transparent placeholder:text-white/50 placeholder:text-sm text-sm text-white resize-none focus:outline-none w-full px-2 max-h-24 lg:max-h-36 xl:max-h-48 flex-grow flex-shrink" className="transition bg-transparent dark:placeholder:text-white/50 placeholder:text-sm text-sm dark:text-white resize-none focus:outline-none w-full px-2 max-h-24 lg:max-h-36 xl:max-h-48 flex-grow flex-shrink"
placeholder="Ask a follow-up" placeholder="Ask a follow-up"
/> />
{mode === 'single' && ( {mode === 'single' && (
@ -62,7 +62,7 @@ const MessageInput = ({
/> />
<button <button
disabled={message.trim().length === 0 || loading} disabled={message.trim().length === 0 || loading}
className="bg-[#24A0ED] text-white disabled:text-white/50 hover:bg-opacity-85 transition duration-100 disabled:bg-[#ececec21] rounded-full p-2" className="bg-[#24A0ED] dark:text-white disabled:text-black/50 dark:disabled:text-white/50 hover:bg-opacity-85 transition duration-100 disabled:bg-[#e0e0dc] dark:disabled:bg-[#ececec21] rounded-full p-2"
> >
<ArrowUp className="bg-background" size={17} /> <ArrowUp className="bg-background" size={17} />
</button> </button>
@ -78,7 +78,7 @@ const MessageInput = ({
/> />
<button <button
disabled={message.trim().length === 0 || loading} disabled={message.trim().length === 0 || loading}
className="bg-[#24A0ED] text-white disabled:text-white/50 hover:bg-opacity-85 transition duration-100 disabled:bg-[#ececec21] rounded-full p-2" className="bg-[#24A0ED] dark:text-white text-black/50 dark:disabled:text-white/50 hover:bg-opacity-85 transition duration-100 disabled:bg-[#e0e0dc] dark:disabled:bg-[#ececec21] rounded-full p-2"
> >
<ArrowUp className="bg-background" size={17} /> <ArrowUp className="bg-background" size={17} />
</button> </button>

View File

@ -16,7 +16,7 @@ export const Attach = () => {
return ( return (
<button <button
type="button" type="button"
className="p-2 text-white/50 rounded-xl hover:bg-[#1c1c1c] transition duration-200 hover:text-white" className="p-2 text-black/50 dark:text-white/50 rounded-xl hover:(bg-secondLight dark:bg-secondDark) transition duration-200 hover:text-black dark:hover:text-white"
> >
<CopyPlus /> <CopyPlus />
</button> </button>
@ -85,7 +85,7 @@ export const Focus = ({
<Popover className="fixed w-full max-w-[15rem] md:max-w-md lg:max-w-lg"> <Popover className="fixed w-full max-w-[15rem] md:max-w-md lg:max-w-lg">
<Popover.Button <Popover.Button
type="button" type="button"
className="p-2 text-white/50 rounded-xl hover:bg-[#1c1c1c] active:scale-95 transition duration-200 hover:text-white" className="p-2 text-black/50 dark:text-white/50 rounded-xl hover(bg-secondLight dark:bg-secondDark) active:scale-95 transition duration-200 hover:text-white"
> >
{focusMode !== 'webSearch' ? ( {focusMode !== 'webSearch' ? (
<div className="flex flex-row items-center space-x-1"> <div className="flex flex-row items-center space-x-1">
@ -109,7 +109,7 @@ export const Focus = ({
leaveTo="opacity-0 translate-y-1" leaveTo="opacity-0 translate-y-1"
> >
<Popover.Panel className="absolute z-10 w-full"> <Popover.Panel className="absolute z-10 w-full">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-1 bg-[#0A0A0A] border rounded-lg border-[#1c1c1c] w-full p-2 max-h-[200px] md:max-h-none overflow-y-auto"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-1 dark:bg-primaryDark border rounded-lg border-light dark:border-dark w-full p-2 max-h-[200px] md:max-h-none overflow-y-auto">
{focusModes.map((mode, i) => ( {focusModes.map((mode, i) => (
<Popover.Button <Popover.Button
onClick={() => setFocusMode(mode.key)} onClick={() => setFocusMode(mode.key)}
@ -117,20 +117,24 @@ export const Focus = ({
className={cn( className={cn(
'p-2 rounded-lg flex flex-col items-start justify-start text-start space-y-2 duration-200 cursor-pointer transition', 'p-2 rounded-lg flex flex-col items-start justify-start text-start space-y-2 duration-200 cursor-pointer transition',
focusMode === mode.key focusMode === mode.key
? 'bg-[#111111]' ? 'bg-primaryLight dark:bg-primaryDark'
: 'hover:bg-[#111111]', : 'hover:bg-primaryLight dark:bg-primaryDark',
)} )}
> >
<div <div
className={cn( className={cn(
'flex flex-row items-center space-x-1', 'flex flex-row items-center space-x-1',
focusMode === mode.key ? 'text-[#24A0ED]' : 'text-white', focusMode === mode.key
? 'text-[#24A0ED]'
: 'text-black dark:text-white',
)} )}
> >
{mode.icon} {mode.icon}
<p className="text-sm font-medium">{mode.title}</p> <p className="text-sm font-medium">{mode.title}</p>
</div> </div>
<p className="text-white/70 text-xs">{mode.description}</p> <p className="text-black/70 dark:text-white/70 text-xs">
{mode.description}
</p>
</Popover.Button> </Popover.Button>
))} ))}
</div> </div>
@ -152,14 +156,14 @@ export const CopilotToggle = ({
<Switch <Switch
checked={copilotEnabled} checked={copilotEnabled}
onChange={setCopilotEnabled} onChange={setCopilotEnabled}
className="bg-[#111111] border border-[#1C1C1C] relative inline-flex h-5 w-10 sm:h-6 sm:w-11 items-center rounded-full" className="bg-primaryLight dark:bg-primaryDark border border-light dark:border-dark relative inline-flex h-5 w-10 sm:h-6 sm:w-11 items-center rounded-full"
> >
<span className="sr-only">Copilot</span> <span className="sr-only">Copilot</span>
<span <span
className={cn( className={cn(
copilotEnabled copilotEnabled
? 'translate-x-6 bg-[#24A0ED]' ? 'translate-x-6 bg-[#24A0ED]'
: 'translate-x-1 bg-white/50', : 'translate-x-1 bg-black/50 dark:bg-white/50',
'inline-block h-3 w-3 sm:h-4 sm:w-4 transform rounded-full transition-all duration-200', 'inline-block h-3 w-3 sm:h-4 sm:w-4 transform rounded-full transition-all duration-200',
)} )}
/> />
@ -170,7 +174,7 @@ export const CopilotToggle = ({
'text-xs font-medium transition-colors duration-150 ease-in-out', 'text-xs font-medium transition-colors duration-150 ease-in-out',
copilotEnabled copilotEnabled
? 'text-[#24A0ED]' ? 'text-[#24A0ED]'
: 'text-white/50 group-hover:text-white', : 'text-black/50 dark:text-white/50 group-hover:text-black dark:group-hover:text-white',
)} )}
> >
Copilot Copilot

View File

@ -20,12 +20,12 @@ const MessageSources = ({ sources }: { sources: Document[] }) => {
<div className="grid grid-cols-2 lg:grid-cols-4 gap-2"> <div className="grid grid-cols-2 lg:grid-cols-4 gap-2">
{sources.slice(0, 3).map((source, i) => ( {sources.slice(0, 3).map((source, i) => (
<a <a
className="bg-[#111111] hover:bg-[#1c1c1c] transition duration-200 rounded-lg p-3 flex flex-col space-y-2 font-medium" className="bg-primaryLight dark:bg-primaryDark hover(bg-secondLight dark:bg-secondDark) transition duration-200 rounded-lg p-3 flex flex-col space-y-2 font-medium"
key={i} key={i}
href={source.metadata.url} href={source.metadata.url}
target="_blank" target="_blank"
> >
<p className="text-white text-xs overflow-hidden whitespace-nowrap text-ellipsis"> <p className="dark:text-white text-xs overflow-hidden whitespace-nowrap text-ellipsis">
{source.metadata.title} {source.metadata.title}
</p> </p>
<div className="flex flex-row items-center justify-between"> <div className="flex flex-row items-center justify-between">
@ -37,11 +37,11 @@ const MessageSources = ({ sources }: { sources: Document[] }) => {
alt="favicon" alt="favicon"
className="rounded-lg h-4 w-4" className="rounded-lg h-4 w-4"
/> />
<p className="text-xs text-white/50 overflow-hidden whitespace-nowrap text-ellipsis"> <p className="text-xs text-black/50 dark:text-white/50 overflow-hidden whitespace-nowrap text-ellipsis">
{source.metadata.url.replace(/.+\/\/|www.|\..+/g, '')} {source.metadata.url.replace(/.+\/\/|www.|\..+/g, '')}
</p> </p>
</div> </div>
<div className="flex flex-row items-center space-x-1 text-white/50 text-xs"> <div className="flex flex-row items-center space-x-1 text-black/50 dark:text-white/50 text-xs">
<div className="bg-white/50 h-[4px] w-[4px] rounded-full" /> <div className="bg-white/50 h-[4px] w-[4px] rounded-full" />
<span>{i + 1}</span> <span>{i + 1}</span>
</div> </div>
@ -51,7 +51,7 @@ const MessageSources = ({ sources }: { sources: Document[] }) => {
{sources.length > 3 && ( {sources.length > 3 && (
<button <button
onClick={openModal} onClick={openModal}
className="bg-[#111111] hover:bg-[#1c1c1c] transition duration-200 rounded-lg px-4 py-2 flex flex-col justify-between space-y-2" className="bg-primaryLight dark:bg-primaryDark hover(bg-secondLight dark:bg-secondDark) transition duration-200 rounded-lg px-4 py-2 flex flex-col justify-between space-y-2"
> >
<div className="flex flex-row items-center space-x-1"> <div className="flex flex-row items-center space-x-1">
{sources.slice(3, 6).map((source, i) => ( {sources.slice(3, 6).map((source, i) => (
@ -65,7 +65,7 @@ const MessageSources = ({ sources }: { sources: Document[] }) => {
/> />
))} ))}
</div> </div>
<p className="text-xs text-white/50"> <p className="text-xs text-black/50 dark:text-white/50">
View {sources.length - 3} more View {sources.length - 3} more
</p> </p>
</button> </button>
@ -83,19 +83,19 @@ const MessageSources = ({ sources }: { sources: Document[] }) => {
leaveFrom="opacity-100 scale-200" leaveFrom="opacity-100 scale-200"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<Dialog.Panel className="w-full max-w-md transform rounded-2xl bg-[#111111] border border-[#1c1c1c] p-6 text-left align-middle shadow-xl transition-all"> <Dialog.Panel className="w-full max-w-md transform rounded-2xl bg-primaryLight dark:bg-primaryDark border border-light dark:border-dark p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title className="text-lg font-medium leading-6 text-white"> <Dialog.Title className="text-lg font-medium leading-6 dark:text-white">
Sources Sources
</Dialog.Title> </Dialog.Title>
<div className="grid grid-cols-2 gap-2 overflow-auto max-h-[300px] mt-2 pr-2"> <div className="grid grid-cols-2 gap-2 overflow-auto max-h-[300px] mt-2 pr-2">
{sources.map((source, i) => ( {sources.map((source, i) => (
<a <a
className="bg-[#111111] hover:bg-[#1c1c1c] border border-[#1c1c1c] transition duration-200 rounded-lg p-3 flex flex-col space-y-2 font-medium" className="bg-primaryLight dark:bg-primaryDark hover(bg-secondLight dark:bg-secondDark) border border-light dark:border-dark transition duration-200 rounded-lg p-3 flex flex-col space-y-2 font-medium"
key={i} key={i}
href={source.metadata.url} href={source.metadata.url}
target="_blank" target="_blank"
> >
<p className="text-white text-xs overflow-hidden whitespace-nowrap text-ellipsis"> <p className="dark:text-white text-xs overflow-hidden whitespace-nowrap text-ellipsis">
{source.metadata.title} {source.metadata.title}
</p> </p>
<div className="flex flex-row items-center justify-between"> <div className="flex flex-row items-center justify-between">
@ -107,14 +107,14 @@ const MessageSources = ({ sources }: { sources: Document[] }) => {
alt="favicon" alt="favicon"
className="rounded-lg h-4 w-4" className="rounded-lg h-4 w-4"
/> />
<p className="text-xs text-white/50 overflow-hidden whitespace-nowrap text-ellipsis"> <p className="text-xs text-black/50 dark:text-white/50 overflow-hidden whitespace-nowrap text-ellipsis">
{source.metadata.url.replace( {source.metadata.url.replace(
/.+\/\/|www.|\..+/g, /.+\/\/|www.|\..+/g,
'', '',
)} )}
</p> </p>
</div> </div>
<div className="flex flex-row items-center space-x-1 text-white/50 text-xs"> <div className="flex flex-row items-center space-x-1 text-black/50 dark:text-white/50 text-xs">
<div className="bg-white/50 h-[4px] w-[4px] rounded-full" /> <div className="bg-white/50 h-[4px] w-[4px] rounded-full" />
<span>{i + 1}</span> <span>{i + 1}</span>
</div> </div>

View File

@ -38,7 +38,7 @@ const Navbar = ({ messages }: { messages: Message[] }) => {
}, []); }, []);
return ( return (
<div className="fixed z-40 top-0 left-0 right-0 px-4 lg:pl-[104px] lg:pr-6 lg:px-8 flex flex-row items-center justify-between w-full py-4 text-sm text-white/70 border-b bg-[#0A0A0A] border-[#1C1C1C]"> <div className="fixed z-40 top-0 left-0 right-0 px-4 lg:pl-[104px] lg:pr-6 lg:px-8 flex flex-row items-center justify-between w-full py-4 text-sm text-black dark:text-white/70 border-b dark:bg-primaryDark border-light dark:border-dark">
<Edit <Edit
size={17} size={17}
className="active:scale-95 transition duration-100 cursor-pointer lg:hidden" className="active:scale-95 transition duration-100 cursor-pointer lg:hidden"

View File

@ -62,7 +62,7 @@ const SearchImages = ({
); );
setLoading(false); setLoading(false);
}} }}
className="border border-dashed border-[#1C1C1C] hover:bg-[#1c1c1c] active:scale-95 duration-200 transition px-4 py-2 flex flex-row items-center justify-between rounded-lg text-white text-sm w-full" className="border border-dashed border-light dark:border-dark hover(bg-secondLight dark:bg-secondDark) active:scale-95 duration-200 transition px-4 py-2 flex flex-row items-center justify-between rounded-lg dark:text-white text-sm w-full"
> >
<div className="flex flex-row items-center space-x-2"> <div className="flex flex-row items-center space-x-2">
<ImagesIcon size={17} /> <ImagesIcon size={17} />
@ -76,7 +76,7 @@ const SearchImages = ({
{[...Array(4)].map((_, i) => ( {[...Array(4)].map((_, i) => (
<div <div
key={i} key={i}
className="bg-[#1C1C1C] h-32 w-full rounded-lg animate-pulse aspect-video object-cover" className="bg-secondLight dark:bg-secondDark h-32 w-full rounded-lg animate-pulse aspect-video object-cover"
/> />
))} ))}
</div> </div>
@ -120,7 +120,7 @@ const SearchImages = ({
{images.length > 4 && ( {images.length > 4 && (
<button <button
onClick={() => setOpen(true)} onClick={() => setOpen(true)}
className="bg-[#111111] hover:bg-[#1c1c1c] transition duration-200 active:scale-95 hover:scale-[1.02] h-auto w-full rounded-lg flex flex-col justify-between text-white p-2" className="bg-primaryLight dark:bg-primaryDark hover(bg-secondLight dark:bg-secondDark) transition duration-200 active:scale-95 hover:scale-[1.02] h-auto w-full rounded-lg flex flex-col justify-between dark:text-white p-2"
> >
<div className="flex flex-row items-center space-x-1"> <div className="flex flex-row items-center space-x-1">
{images.slice(3, 6).map((image, i) => ( {images.slice(3, 6).map((image, i) => (
@ -132,7 +132,7 @@ const SearchImages = ({
/> />
))} ))}
</div> </div>
<p className="text-white/70 text-xs"> <p className="text-black/70 dark:text-white/70 text-xs">
View {images.length - 3} more View {images.length - 3} more
</p> </p>
</button> </button>

View File

@ -77,7 +77,7 @@ const Searchvideos = ({
); );
setLoading(false); setLoading(false);
}} }}
className="border border-dashed border-[#1C1C1C] hover:bg-[#1c1c1c] active:scale-95 duration-200 transition px-4 py-2 flex flex-row items-center justify-between rounded-lg text-white text-sm w-full" className="border border-dashed border-light dark:border-dark hover(bg-secondLight dark:bg-secondDark) active:scale-95 duration-200 transition px-4 py-2 flex flex-row items-center justify-between rounded-lg dark:text-white text-sm w-full"
> >
<div className="flex flex-row items-center space-x-2"> <div className="flex flex-row items-center space-x-2">
<VideoIcon size={17} /> <VideoIcon size={17} />
@ -91,7 +91,7 @@ const Searchvideos = ({
{[...Array(4)].map((_, i) => ( {[...Array(4)].map((_, i) => (
<div <div
key={i} key={i}
className="bg-[#1C1C1C] h-32 w-full rounded-lg animate-pulse aspect-video object-cover" className="bg-secondLight dark:bg-secondDark h-32 w-full rounded-lg animate-pulse aspect-video object-cover"
/> />
))} ))}
</div> </div>
@ -118,7 +118,7 @@ const Searchvideos = ({
alt={video.title} alt={video.title}
className="relative h-full w-full aspect-video object-cover rounded-lg" className="relative h-full w-full aspect-video object-cover rounded-lg"
/> />
<div className="absolute bg-black/70 text-white/70 px-2 py-1 flex flex-row items-center space-x-1 bottom-1 right-1 rounded-md"> <div className="absolute bg-black/70 text-black/70 dark:text-white/70 px-2 py-1 flex flex-row items-center space-x-1 bottom-1 right-1 rounded-md">
<PlayCircle size={15} /> <PlayCircle size={15} />
<p className="text-xs">Video</p> <p className="text-xs">Video</p>
</div> </div>
@ -142,7 +142,7 @@ const Searchvideos = ({
alt={video.title} alt={video.title}
className="relative h-full w-full aspect-video object-cover rounded-lg" className="relative h-full w-full aspect-video object-cover rounded-lg"
/> />
<div className="absolute bg-black/70 text-white/70 px-2 py-1 flex flex-row items-center space-x-1 bottom-1 right-1 rounded-md"> <div className="absolute bg-white/70 dark:bg-black/70 text-black/70 dark:text-white/70 px-2 py-1 flex flex-row items-center space-x-1 bottom-1 right-1 rounded-md">
<PlayCircle size={15} /> <PlayCircle size={15} />
<p className="text-xs">Video</p> <p className="text-xs">Video</p>
</div> </div>
@ -151,7 +151,7 @@ const Searchvideos = ({
{videos.length > 4 && ( {videos.length > 4 && (
<button <button
onClick={() => setOpen(true)} onClick={() => setOpen(true)}
className="bg-[#111111] hover:bg-[#1c1c1c] transition duration-200 active:scale-95 hover:scale-[1.02] h-auto w-full rounded-lg flex flex-col justify-between text-white p-2" className="bg-primaryLight dark:bg-primaryDark hover(bg-secondLight dark:bg-secondDark) transition duration-200 active:scale-95 hover:scale-[1.02] h-auto w-full rounded-lg flex flex-col justify-between dark:text-white p-2"
> >
<div className="flex flex-row items-center space-x-1"> <div className="flex flex-row items-center space-x-1">
{videos.slice(3, 6).map((video, i) => ( {videos.slice(3, 6).map((video, i) => (
@ -163,7 +163,7 @@ const Searchvideos = ({
/> />
))} ))}
</div> </div>
<p className="text-white/70 text-xs"> <p className="text-black/70 dark:text-white/70 text-xs">
View {videos.length - 3} more View {videos.length - 3} more
</p> </p>
</button> </button>

View File

@ -145,7 +145,7 @@ const SettingsDialog = ({
leaveFrom="opacity-100" leaveFrom="opacity-100"
leaveTo="opacity-0" leaveTo="opacity-0"
> >
<div className="fixed inset-0 bg-black/50" /> <div className="fixed inset-0 bg-white/50 dark:bg-black/50" />
</Transition.Child> </Transition.Child>
<div className="fixed inset-0 overflow-y-auto"> <div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center"> <div className="flex min-h-full items-center justify-center p-4 text-center">
@ -158,15 +158,15 @@ const SettingsDialog = ({
leaveFrom="opacity-100 scale-200" leaveFrom="opacity-100 scale-200"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
<Dialog.Panel className="w-full max-w-md transform rounded-2xl bg-[#111111] border border-[#1c1c1c] p-6 text-left align-middle shadow-xl transition-all"> <Dialog.Panel className="w-full max-w-md transform rounded-2xl bg-secondLight dark:bg-secondDark border border-light dark:border-dark p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title className="text-xl font-medium leading-6 text-white"> <Dialog.Title className="text-xl font-medium leading-6 dark:text-white">
Settings Settings
</Dialog.Title> </Dialog.Title>
{config && !isLoading && ( {config && !isLoading && (
<div className="flex flex-col space-y-4 mt-6"> <div className="flex flex-col space-y-4 mt-6">
{config.chatModelProviders && ( {config.chatModelProviders && (
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
Chat model Provider Chat model Provider
</p> </p>
<select <select
@ -177,7 +177,7 @@ const SettingsDialog = ({
config.chatModelProviders[e.target.value][0], config.chatModelProviders[e.target.value][0],
); );
}} }}
className="bg-[#111111] px-3 py-2 flex items-center overflow-hidden border border-[#1C1C1C] text-white rounded-lg text-sm" className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
> >
{Object.keys(config.chatModelProviders).map( {Object.keys(config.chatModelProviders).map(
(provider) => ( (provider) => (
@ -193,13 +193,15 @@ const SettingsDialog = ({
{selectedChatModelProvider && {selectedChatModelProvider &&
selectedChatModelProvider != 'custom_openai' && ( selectedChatModelProvider != 'custom_openai' && (
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-white/70 text-sm">Chat Model</p> <p className="text-black/70 dark:text-white/70 text-sm">
Chat Model
</p>
<select <select
value={selectedChatModel ?? undefined} value={selectedChatModel ?? undefined}
onChange={(e) => onChange={(e) =>
setSelectedChatModel(e.target.value) setSelectedChatModel(e.target.value)
} }
className="bg-[#111111] px-3 py-2 flex items-center overflow-hidden border border-[#1C1C1C] text-white rounded-lg text-sm" className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
> >
{config.chatModelProviders[ {config.chatModelProviders[
selectedChatModelProvider selectedChatModelProvider
@ -231,7 +233,9 @@ const SettingsDialog = ({
selectedChatModelProvider === 'custom_openai' && ( selectedChatModelProvider === 'custom_openai' && (
<> <>
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-white/70 text-sm">Model name</p> <p className="text-black/70 dark:text-white/70 text-sm">
Model name
</p>
<input <input
type="text" type="text"
placeholder="Model name" placeholder="Model name"
@ -239,11 +243,11 @@ const SettingsDialog = ({
onChange={(e) => onChange={(e) =>
setSelectedChatModel(e.target.value) setSelectedChatModel(e.target.value)
} }
className="bg-[#111111] px-3 py-2 flex items-center overflow-hidden border border-[#1C1C1C] text-white rounded-lg text-sm" className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
/> />
</div> </div>
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
Custom OpenAI API Key Custom OpenAI API Key
</p> </p>
<input <input
@ -253,11 +257,11 @@ const SettingsDialog = ({
onChange={(e) => onChange={(e) =>
setCustomOpenAIApiKey(e.target.value) setCustomOpenAIApiKey(e.target.value)
} }
className="bg-[#111111] px-3 py-2 flex items-center overflow-hidden border border-[#1C1C1C] text-white rounded-lg text-sm" className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
/> />
</div> </div>
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
Custom OpenAI Base URL Custom OpenAI Base URL
</p> </p>
<input <input
@ -267,7 +271,7 @@ const SettingsDialog = ({
onChange={(e) => onChange={(e) =>
setCustomOpenAIBaseURL(e.target.value) setCustomOpenAIBaseURL(e.target.value)
} }
className="bg-[#111111] px-3 py-2 flex items-center overflow-hidden border border-[#1C1C1C] text-white rounded-lg text-sm" className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
/> />
</div> </div>
</> </>
@ -275,7 +279,7 @@ const SettingsDialog = ({
{/* Embedding models */} {/* Embedding models */}
{config.embeddingModelProviders && ( {config.embeddingModelProviders && (
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
Embedding model Provider Embedding model Provider
</p> </p>
<select <select
@ -286,7 +290,7 @@ const SettingsDialog = ({
config.embeddingModelProviders[e.target.value][0], config.embeddingModelProviders[e.target.value][0],
); );
}} }}
className="bg-[#111111] px-3 py-2 flex items-center overflow-hidden border border-[#1C1C1C] text-white rounded-lg text-sm" className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
> >
{Object.keys(config.embeddingModelProviders).map( {Object.keys(config.embeddingModelProviders).map(
(provider) => ( (provider) => (
@ -301,13 +305,15 @@ const SettingsDialog = ({
)} )}
{selectedEmbeddingModelProvider && ( {selectedEmbeddingModelProvider && (
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-white/70 text-sm">Embedding Model</p> <p className="text-black/70 dark:text-white/70 text-sm">
Embedding Model
</p>
<select <select
value={selectedEmbeddingModel ?? undefined} value={selectedEmbeddingModel ?? undefined}
onChange={(e) => onChange={(e) =>
setSelectedEmbeddingModel(e.target.value) setSelectedEmbeddingModel(e.target.value)
} }
className="bg-[#111111] px-3 py-2 flex items-center overflow-hidden border border-[#1C1C1C] text-white rounded-lg text-sm" className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
> >
{config.embeddingModelProviders[ {config.embeddingModelProviders[
selectedEmbeddingModelProvider selectedEmbeddingModelProvider
@ -336,7 +342,9 @@ const SettingsDialog = ({
</div> </div>
)} )}
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-white/70 text-sm">OpenAI API Key</p> <p className="text-black/70 dark:text-white/70 text-sm">
OpenAI API Key
</p>
<input <input
type="text" type="text"
placeholder="OpenAI API Key" placeholder="OpenAI API Key"
@ -347,11 +355,13 @@ const SettingsDialog = ({
openaiApiKey: e.target.value, openaiApiKey: e.target.value,
}) })
} }
className="bg-[#111111] px-3 py-2 flex items-center overflow-hidden border border-[#1C1C1C] text-white rounded-lg text-sm" className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
/> />
</div> </div>
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-white/70 text-sm">Ollama API URL</p> <p className="text-black/70 dark:text-white/70 text-sm">
Ollama API URL
</p>
<input <input
type="text" type="text"
placeholder="Ollama API URL" placeholder="Ollama API URL"
@ -362,11 +372,13 @@ const SettingsDialog = ({
ollamaApiUrl: e.target.value, ollamaApiUrl: e.target.value,
}) })
} }
className="bg-[#111111] px-3 py-2 flex items-center overflow-hidden border border-[#1C1C1C] text-white rounded-lg text-sm" className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
/> />
</div> </div>
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-white/70 text-sm">GROQ API Key</p> <p className="text-black/70 dark:text-white/70 text-sm">
GROQ API Key
</p>
<input <input
type="text" type="text"
placeholder="GROQ API Key" placeholder="GROQ API Key"
@ -377,18 +389,18 @@ const SettingsDialog = ({
groqApiKey: e.target.value, groqApiKey: e.target.value,
}) })
} }
className="bg-[#111111] px-3 py-2 flex items-center overflow-hidden border border-[#1C1C1C] text-white rounded-lg text-sm" className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
/> />
</div> </div>
</div> </div>
)} )}
{isLoading && ( {isLoading && (
<div className="w-full flex items-center justify-center mt-6 text-white/70 py-6"> <div className="w-full flex items-center justify-center mt-6 text-black/70 dark:text-white/70 py-6">
<RefreshCcw className="animate-spin" /> <RefreshCcw className="animate-spin" />
</div> </div>
)} )}
<div className="w-full mt-6 space-y-2"> <div className="w-full mt-6 space-y-2">
<p className="text-xs text-white/50"> <p className="text-xs text-black/50 dark:text-white/50">
We&apos;ll refresh the page after updating the settings. We&apos;ll refresh the page after updating the settings.
</p> </p>
<button <button

View File

@ -4,10 +4,17 @@ import { cn } from '@/lib/utils';
import { BookOpenText, Home, Search, SquarePen, Settings } from 'lucide-react'; import { BookOpenText, Home, Search, SquarePen, Settings } from 'lucide-react';
import Link from 'next/link'; import Link from 'next/link';
import { useSelectedLayoutSegments } from 'next/navigation'; import { useSelectedLayoutSegments } from 'next/navigation';
import React, { Fragment, useState } from 'react'; import React, { Fragment, useState, type ReactNode } from 'react';
import Layout from './Layout'; import Layout from './Layout';
import { Dialog, Transition } from '@headlessui/react'; import { Dialog, Transition } from '@headlessui/react';
import SettingsDialog from './SettingsDialog'; import SettingsDialog from './SettingsDialog';
import { ThemeSwitcher } from './theme/Switcher';
function VerticalIconContainer({ children }: { children: ReactNode }) {
return (
<div className="flex flex-col items-center gap-y-3 w-full">{children}</div>
);
}
const Sidebar = ({ children }: { children: React.ReactNode }) => { const Sidebar = ({ children }: { children: React.ReactNode }) => {
const segments = useSelectedLayoutSegments(); const segments = useSelectedLayoutSegments();
@ -38,31 +45,39 @@ const Sidebar = ({ children }: { children: React.ReactNode }) => {
return ( return (
<div> <div>
<div className="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-20 lg:flex-col"> <div className="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-20 lg:flex-col">
<div className="flex grow flex-col items-center justify-between gap-y-5 overflow-y-auto bg-[#111111] px-2 py-8"> <div className="flex grow flex-col items-center justify-between gap-y-5 overflow-y-auto bg-secondLight dark:bg-secondDark px-2 py-8">
<a href="/"> <a href="/">
<SquarePen className="text-white cursor-pointer" /> <SquarePen className="cursor-pointer" />
</a> </a>
<div className="flex flex-col items-center gap-y-3 w-full"> <VerticalIconContainer>
{navLinks.map((link, i) => ( {navLinks.map((link, i) => (
<Link <Link
key={i} key={i}
href={link.href} href={link.href}
className={cn( className={cn(
'relative flex flex-row items-center justify-center cursor-pointer hover:bg-white/10 hover:text-white duration-150 transition w-full py-2 rounded-lg', 'relative flex flex-row items-center justify-center cursor-pointer hover:bg-black/10 dark:hover:bg-white/10 duration-150 transition w-full py-2 rounded-lg',
link.active ? 'text-white' : 'text-white/70', link.active
? 'text-black dark:text-white'
: 'text-black/70 dark:text-white/70',
)} )}
> >
<link.icon /> <link.icon />
{link.active && ( {link.active && (
<div className="absolute right-0 -mr-2 h-full w-1 rounded-l-lg bg-white" /> <div className="absolute right-0 -mr-2 h-full w-1 rounded-l-lg bg-black dark:bg-white" />
)} )}
</Link> </Link>
))} ))}
</div> </VerticalIconContainer>
<Settings
onClick={() => setIsSettingsOpen(!isSettingsOpen)} <VerticalIconContainer>
className="text-white cursor-pointer" <ThemeSwitcher />
/>
<Settings
onClick={() => setIsSettingsOpen(!isSettingsOpen)}
className="cursor-pointer"
/>
</VerticalIconContainer>
<SettingsDialog <SettingsDialog
isOpen={isSettingsOpen} isOpen={isSettingsOpen}
setIsOpen={setIsSettingsOpen} setIsOpen={setIsSettingsOpen}
@ -70,7 +85,7 @@ const Sidebar = ({ children }: { children: React.ReactNode }) => {
</div> </div>
</div> </div>
<div className="fixed bottom-0 w-full z-50 flex flex-row items-center gap-x-6 bg-[#111111] px-4 py-4 shadow-sm lg:hidden"> <div className="fixed bottom-0 w-full z-50 flex flex-row items-center gap-x-6 bg-primaryLight dark:bg-primaryDark px-4 py-4 shadow-sm lg:hidden">
{navLinks.map((link, i) => ( {navLinks.map((link, i) => (
<Link <Link
href={link.href} href={link.href}

View File

@ -1,4 +1,5 @@
import type { Config } from 'tailwindcss'; import type { Config } from 'tailwindcss';
import color from 'tailwindcss/colors';
const config: Config = { const config: Config = {
content: [ content: [
@ -8,7 +9,18 @@ const config: Config = {
], ],
darkMode: 'class', darkMode: 'class',
theme: { theme: {
extend: {}, extend: {
borderColor: {
light: 'rgba(0, 0, 0, 0.1)',
dark: '#1c1c1c',
},
colors: {
primaryDark: '#0a0a0a',
secondDark: '#1c1c1c',
primaryLight: '#fff',
secondLight: color.gray[50],
},
},
}, },
plugins: [require('@tailwindcss/typography')], plugins: [require('@tailwindcss/typography')],
}; };