diff --git a/ui/app/layout.tsx b/ui/app/layout.tsx index b3f5005..2edbf94 100644 --- a/ui/app/layout.tsx +++ b/ui/app/layout.tsx @@ -4,6 +4,7 @@ import './globals.css'; import { cn } from '@/lib/utils'; import Sidebar from '@/components/Sidebar'; import { Toaster } from 'sonner'; +import ThemeProvider from '@/components/theme/Provider'; const montserrat = Montserrat({ weight: ['300', '400', '500', '700'], @@ -24,18 +25,20 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + - {children} - + + {children} + + ); diff --git a/ui/components/Chat.tsx b/ui/components/Chat.tsx index 7b0c1b3..c0dbc92 100644 --- a/ui/components/Chat.tsx +++ b/ui/components/Chat.tsx @@ -66,7 +66,7 @@ const Chat = ({ sendMessage={sendMessage} /> {!isLast && msg.role === 'assistant' && ( -
+
)} ); diff --git a/ui/components/ChatWindow.tsx b/ui/components/ChatWindow.tsx index 5f266b5..33bc3fb 100644 --- a/ui/components/ChatWindow.tsx +++ b/ui/components/ChatWindow.tsx @@ -323,7 +323,7 @@ const ChatWindow = () => {
-

- Research begins here. -

- +
+ + +
+

+ Research begins here. +

+ +
); }; diff --git a/ui/components/EmptyChatMessageInput.tsx b/ui/components/EmptyChatMessageInput.tsx index 4932803..37ecc8f 100644 --- a/ui/components/EmptyChatMessageInput.tsx +++ b/ui/components/EmptyChatMessageInput.tsx @@ -1,7 +1,8 @@ import { ArrowRight } from 'lucide-react'; import { useState } from 'react'; import TextareaAutosize from 'react-textarea-autosize'; -import { CopilotToggle, Focus } from './MessageInputActions'; +import CopilotToggle from './MessageInputActions/Copilot'; +import Focus from './MessageInputActions/Focus'; const EmptyChatMessageInput = ({ sendMessage, @@ -31,12 +32,12 @@ const EmptyChatMessageInput = ({ }} className="w-full" > -
+
setMessage(e.target.value)} 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..." />
@@ -51,7 +52,7 @@ const EmptyChatMessageInput = ({ /> diff --git a/ui/components/Layout.tsx b/ui/components/Layout.tsx index e517e00..00f0fff 100644 --- a/ui/components/Layout.tsx +++ b/ui/components/Layout.tsx @@ -1,6 +1,6 @@ const Layout = ({ children }: { children: React.ReactNode }) => { return ( -
+
{children}
); diff --git a/ui/components/MessageActions/Copy.tsx b/ui/components/MessageActions/Copy.tsx index b19d8d4..cb07b3e 100644 --- a/ui/components/MessageActions/Copy.tsx +++ b/ui/components/MessageActions/Copy.tsx @@ -19,7 +19,7 @@ const Copy = ({ setCopied(true); 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-light-secondary dark:hover:bg-dark-secondary transition duration-200 hover:text-black dark:hover:text-white" > {copied ? : } diff --git a/ui/components/MessageActions/Rewrite.tsx b/ui/components/MessageActions/Rewrite.tsx index 3282e7d..80fadb3 100644 --- a/ui/components/MessageActions/Rewrite.tsx +++ b/ui/components/MessageActions/Rewrite.tsx @@ -10,7 +10,7 @@ const Rewrite = ({ return ( */} @@ -123,7 +132,7 @@ const MessageBox = ({ 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-light-secondary dark:hover:bg-dark-secondary transition duration-200 hover:text-black dark:hover:text-white" > {speechStatus === 'started' ? ( @@ -140,8 +149,8 @@ const MessageBox = ({ message.role === 'assistant' && !loading && ( <> -
-
+
+

Related

@@ -152,7 +161,7 @@ const MessageBox = ({ className="flex flex-col space-y-3 text-sm" key={i} > -
+
{ sendMessage(suggestion); @@ -162,7 +171,10 @@ const MessageBox = ({

{suggestion}

- +
))} diff --git a/ui/components/MessageBoxLoading.tsx b/ui/components/MessageBoxLoading.tsx index e070a27..caa6f18 100644 --- a/ui/components/MessageBoxLoading.tsx +++ b/ui/components/MessageBoxLoading.tsx @@ -1,9 +1,9 @@ const MessageBoxLoading = () => { return ( -
-
-
-
+
+
+
+
); }; diff --git a/ui/components/MessageInput.tsx b/ui/components/MessageInput.tsx index baf6095..7b54ea5 100644 --- a/ui/components/MessageInput.tsx +++ b/ui/components/MessageInput.tsx @@ -2,7 +2,8 @@ import { cn } from '@/lib/utils'; import { ArrowUp } from 'lucide-react'; import { useEffect, useState } from 'react'; import TextareaAutosize from 'react-textarea-autosize'; -import { Attach, CopilotToggle } from './MessageInputActions'; +import Attach from './MessageInputActions/Attach'; +import CopilotToggle from './MessageInputActions/Copilot'; const MessageInput = ({ sendMessage, @@ -40,7 +41,7 @@ const MessageInput = ({ } }} className={cn( - 'bg-[#111111] p-4 flex items-center overflow-hidden border border-[#1C1C1C]', + 'bg-light-secondary dark:bg-dark-secondary p-4 flex items-center overflow-hidden border border-light-200 dark:border-dark-200', mode === 'multi' ? 'flex-col rounded-lg' : 'flex-row rounded-full', )} > @@ -51,7 +52,7 @@ const MessageInput = ({ onHeightChange={(height, props) => { 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" /> {mode === 'single' && ( @@ -62,7 +63,7 @@ const MessageInput = ({ /> @@ -78,7 +79,7 @@ const MessageInput = ({ /> diff --git a/ui/components/MessageInputActions/Attach.tsx b/ui/components/MessageInputActions/Attach.tsx new file mode 100644 index 0000000..6d8ffcf --- /dev/null +++ b/ui/components/MessageInputActions/Attach.tsx @@ -0,0 +1,14 @@ +import { CopyPlus } from 'lucide-react'; + +const Attach = () => { + return ( + + ); +}; + +export default Attach; diff --git a/ui/components/MessageInputActions/Copilot.tsx b/ui/components/MessageInputActions/Copilot.tsx new file mode 100644 index 0000000..5a3e476 --- /dev/null +++ b/ui/components/MessageInputActions/Copilot.tsx @@ -0,0 +1,43 @@ +import { cn } from '@/lib/utils'; +import { Switch } from '@headlessui/react'; + +const CopilotToggle = ({ + copilotEnabled, + setCopilotEnabled, +}: { + copilotEnabled: boolean; + setCopilotEnabled: (enabled: boolean) => void; +}) => { + return ( +
+ + Copilot + + +

setCopilotEnabled(!copilotEnabled)} + className={cn( + 'text-xs font-medium transition-colors duration-150 ease-in-out', + copilotEnabled + ? 'text-[#24A0ED]' + : 'text-black/50 dark:text-white/50 group-hover:text-black dark:group-hover:text-white', + )} + > + Copilot +

+
+ ); +}; + +export default CopilotToggle; diff --git a/ui/components/MessageInputActions.tsx b/ui/components/MessageInputActions/Focus.tsx similarity index 62% rename from ui/components/MessageInputActions.tsx rename to ui/components/MessageInputActions/Focus.tsx index 9c00c4d..86fca44 100644 --- a/ui/components/MessageInputActions.tsx +++ b/ui/components/MessageInputActions/Focus.tsx @@ -1,28 +1,16 @@ import { BadgePercent, ChevronDown, - CopyPlus, Globe, Pencil, ScanEye, SwatchBook, } from 'lucide-react'; import { cn } from '@/lib/utils'; -import { Popover, Switch, Transition } from '@headlessui/react'; +import { Popover, Transition } from '@headlessui/react'; import { SiReddit, SiYoutube } from '@icons-pack/react-simple-icons'; import { Fragment } from 'react'; -export const Attach = () => { - return ( - - ); -}; - const focusModes = [ { key: 'webSearch', @@ -74,7 +62,7 @@ const focusModes = [ }, ]; -export const Focus = ({ +const Focus = ({ focusMode, setFocusMode, }: { @@ -85,7 +73,7 @@ export const Focus = ({ {focusMode !== 'webSearch' ? (
@@ -109,7 +97,7 @@ export const Focus = ({ leaveTo="opacity-0 translate-y-1" > -
+
{focusModes.map((mode, i) => ( setFocusMode(mode.key)} @@ -117,20 +105,24 @@ export const Focus = ({ className={cn( 'p-2 rounded-lg flex flex-col items-start justify-start text-start space-y-2 duration-200 cursor-pointer transition', focusMode === mode.key - ? 'bg-[#111111]' - : 'hover:bg-[#111111]', + ? 'bg-light-secondary dark:bg-dark-secondary' + : 'hover:bg-light-secondary dark:hover:bg-dark-secondary', )} >
{mode.icon}

{mode.title}

-

{mode.description}

+

+ {mode.description} +

))}
@@ -140,41 +132,4 @@ export const Focus = ({ ); }; -export const CopilotToggle = ({ - copilotEnabled, - setCopilotEnabled, -}: { - copilotEnabled: boolean; - setCopilotEnabled: (enabled: boolean) => void; -}) => { - return ( -
- - Copilot - - -

setCopilotEnabled(!copilotEnabled)} - className={cn( - 'text-xs font-medium transition-colors duration-150 ease-in-out', - copilotEnabled - ? 'text-[#24A0ED]' - : 'text-white/50 group-hover:text-white', - )} - > - Copilot -

-
- ); -}; +export default Focus; diff --git a/ui/components/MessageSources.tsx b/ui/components/MessageSources.tsx index 5816f8d..71eebf8 100644 --- a/ui/components/MessageSources.tsx +++ b/ui/components/MessageSources.tsx @@ -20,12 +20,12 @@ const MessageSources = ({ sources }: { sources: Document[] }) => {
{sources.slice(0, 3).map((source, i) => ( -

+

{source.metadata.title}

@@ -37,12 +37,12 @@ const MessageSources = ({ sources }: { sources: Document[] }) => { alt="favicon" className="rounded-lg h-4 w-4" /> -

+

{source.metadata.url.replace(/.+\/\/|www.|\..+/g, '')}

-
-
+
+
{i + 1}
@@ -51,7 +51,7 @@ const MessageSources = ({ sources }: { sources: Document[] }) => { {sources.length > 3 && ( @@ -83,19 +83,19 @@ const MessageSources = ({ sources }: { sources: Document[] }) => { leaveFrom="opacity-100 scale-200" leaveTo="opacity-0 scale-95" > - - + + Sources
{sources.map((source, i) => ( -

+

{source.metadata.title}

@@ -107,15 +107,15 @@ const MessageSources = ({ sources }: { sources: Document[] }) => { alt="favicon" className="rounded-lg h-4 w-4" /> -

+

{source.metadata.url.replace( /.+\/\/|www.|\..+/g, '', )}

-
-
+
+
{i + 1}
diff --git a/ui/components/Navbar.tsx b/ui/components/Navbar.tsx index 75c34a6..0123267 100644 --- a/ui/components/Navbar.tsx +++ b/ui/components/Navbar.tsx @@ -2,6 +2,7 @@ import { Clock, Edit, Share, Trash } from 'lucide-react'; import { Message } from './ChatWindow'; import { useEffect, useState } from 'react'; import { formatTimeDifference } from '@/lib/utils'; +import ThemeSwitcher from './theme/Switcher'; const Navbar = ({ messages }: { messages: Message[] }) => { const [title, setTitle] = useState(''); @@ -38,7 +39,7 @@ const Navbar = ({ messages }: { messages: Message[] }) => { }, []); return ( -
+
{

{timeAgo} ago

{title}

+ + +
@@ -76,7 +76,7 @@ const SearchImages = ({ {[...Array(4)].map((_, i) => (
))}
@@ -120,7 +120,7 @@ const SearchImages = ({ {images.length > 4 && ( diff --git a/ui/components/SearchVideos.tsx b/ui/components/SearchVideos.tsx index b5ff6c5..2646322 100644 --- a/ui/components/SearchVideos.tsx +++ b/ui/components/SearchVideos.tsx @@ -77,7 +77,7 @@ const Searchvideos = ({ ); 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-200 dark:border-dark-200 hover:bg-light-200 dark:hover:bg-dark-200 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" >
@@ -91,7 +91,7 @@ const Searchvideos = ({ {[...Array(4)].map((_, i) => (
))}
@@ -118,7 +118,7 @@ const Searchvideos = ({ alt={video.title} className="relative h-full w-full aspect-video object-cover rounded-lg" /> -
+

Video

@@ -142,7 +142,7 @@ const Searchvideos = ({ alt={video.title} className="relative h-full w-full aspect-video object-cover rounded-lg" /> -
+

Video

@@ -151,7 +151,7 @@ const Searchvideos = ({ {videos.length > 4 && ( diff --git a/ui/components/SettingsDialog.tsx b/ui/components/SettingsDialog.tsx index 57f79f6..e932cee 100644 --- a/ui/components/SettingsDialog.tsx +++ b/ui/components/SettingsDialog.tsx @@ -1,6 +1,52 @@ +import { cn } from '@/lib/utils'; import { Dialog, Transition } from '@headlessui/react'; import { CloudUpload, RefreshCcw, RefreshCw } from 'lucide-react'; -import React, { Fragment, useEffect, useState } from 'react'; +import React, { + Fragment, + useEffect, + useMemo, + useState, + type SelectHTMLAttributes, +} from 'react'; +import ThemeSwitcher from './theme/Switcher'; + +interface InputProps extends React.InputHTMLAttributes {} + +const Input = ({ className, ...restProps }: InputProps) => { + return ( + + ); +}; + +interface SelectProps extends SelectHTMLAttributes { + options: { value: string; label: string; disabled?: boolean }[]; +} + +export const Select = ({ className, options, ...restProps }: SelectProps) => { + return ( + + ); +}; interface SettingsType { chatModelProviders: { @@ -145,7 +191,7 @@ const SettingsDialog = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > -
+
@@ -158,18 +204,24 @@ const SettingsDialog = ({ leaveFrom="opacity-100 scale-200" leaveTo="opacity-0 scale-95" > - - + + Settings {config && !isLoading && (
+
+

+ Theme +

+ +
{config.chatModelProviders && (
-

+

Chat model Provider

- + />
)} {selectedChatModelProvider && selectedChatModelProvider != 'custom_openai' && (
-

Chat Model

- 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" - > - {config.chatModelProviders[ - selectedChatModelProvider - ] ? ( - config.chatModelProviders[ - selectedChatModelProvider - ].length > 0 ? ( + options={(() => { + const chatModelProvider = config.chatModelProviders[ selectedChatModelProvider - ].map((model) => ( - - )) - ) : ( - - ) - ) : ( - - )} - + ]; + + return chatModelProvider + ? chatModelProvider.length > 0 + ? chatModelProvider.map((model) => ({ + value: model, + label: model, + })) + : [ + { + value: '', + label: 'No models available', + disabled: true, + }, + ] + : [ + { + value: '', + label: + 'Invalid provider, please check backend logs', + disabled: true, + }, + ]; + })()} + />
)} {selectedChatModelProvider && selectedChatModelProvider === 'custom_openai' && ( <>
-

Model name

- + Model name +

+ 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" />
-

+

Custom OpenAI API Key

- 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" />
-

+

Custom OpenAI Base URL

- 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" />
@@ -275,10 +329,10 @@ const SettingsDialog = ({ {/* Embedding models */} {config.embeddingModelProviders && (
-

+

Embedding model Provider

- + options={Object.keys( + config.embeddingModelProviders, + ).map((provider) => ({ + label: + provider.charAt(0).toUpperCase() + + provider.slice(1), + value: provider, + }))} + />
)} {selectedEmbeddingModelProvider && (
-

Embedding Model

- 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" - > - {config.embeddingModelProviders[ - selectedEmbeddingModelProvider - ] ? ( - config.embeddingModelProviders[ - selectedEmbeddingModelProvider - ].length > 0 ? ( + options={(() => { + const embeddingModelProvider = config.embeddingModelProviders[ selectedEmbeddingModelProvider - ].map((model) => ( - - )) - ) : ( - - ) - ) : ( - - )} - + ]; + + return embeddingModelProvider + ? embeddingModelProvider.length > 0 + ? embeddingModelProvider.map((model) => ({ + label: model, + value: model, + })) + : [ + { + label: 'No embedding models available', + value: '', + disabled: true, + }, + ] + : [ + { + label: + 'Invalid provider, please check backend logs', + value: '', + disabled: true, + }, + ]; + })()} + />
)}
-

OpenAI API Key

- + OpenAI API Key +

+
-

Ollama API URL

- + Ollama API URL +

+
-

GROQ API Key

- + GROQ API Key +

+
)} {isLoading && ( -
+
)}
-

+

We'll refresh the page after updating the settings.