Add dark mode toggle! And improve experience for train of thought

This commit is contained in:
sabaimran
2024-07-04 18:29:21 +05:30
parent 465ef0b772
commit aec44a0b89
14 changed files with 166 additions and 57 deletions

View File

@@ -15,7 +15,7 @@ div.inputBox {
grid-template-columns: auto 1fr auto auto; grid-template-columns: auto 1fr auto auto;
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 16px; border-radius: 16px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.03); box-shadow: 0 4px 10px var(--box-shadow-color);
margin-bottom: 20px; margin-bottom: 20px;
gap: 12px; gap: 12px;
padding-left: 20px; padding-left: 20px;

View File

@@ -26,5 +26,5 @@ div.trainOfThought {
border-radius: 16px; border-radius: 16px;
padding: 16px; padding: 16px;
margin: 12px; margin: 12px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.03); box-shadow: 0 4px 10px var(--box-shadow-color);
} }

View File

@@ -101,13 +101,17 @@ export default function ChatHistory(props: ChatHistoryProps) {
} }
return () => observer.disconnect(); return () => observer.disconnect();
}, [sentinelRef.current, hasMoreMessages, currentPage, props.conversationId, fetchingData]); }, [sentinelRef.current, hasMoreMessages, currentPage, fetchingData]);
useEffect(() => {
setHasMoreMessages(true);
setFetchingData(false);
setCurrentPage(0);
setData(null);
}, [props.conversationId]);
const fetchMoreMessages = (currentPage: number) => { const fetchMoreMessages = (currentPage: number) => {
if (!hasMoreMessages || fetchingData) return; if (!hasMoreMessages || fetchingData) return;
console.log("fetchMoreMessages", currentPage);
const nextPage = currentPage + 1; const nextPage = currentPage + 1;
fetch(`/api/chat/history?client=web&conversation_id=${props.conversationId}&n=${10 * nextPage}`) fetch(`/api/chat/history?client=web&conversation_id=${props.conversationId}&n=${10 * nextPage}`)
.then(response => response.json()) .then(response => response.json())
@@ -117,6 +121,7 @@ export default function ChatHistory(props: ChatHistoryProps) {
if (chatData.response.chat.length === data?.chat.length) { if (chatData.response.chat.length === data?.chat.length) {
setHasMoreMessages(false); setHasMoreMessages(false);
setFetchingData(false);
return; return;
} }
@@ -124,12 +129,10 @@ export default function ChatHistory(props: ChatHistoryProps) {
setLoading(false); setLoading(false);
if (currentPage < 2) { if (currentPage < 2) {
console.log("call scroll to bottom");
scrollToBottom(); scrollToBottom();
} }
setFetchingData(false); setFetchingData(false);
} else { } else {
console.log("No more messages");
setHasMoreMessages(false); setHasMoreMessages(false);
} }
}) })
@@ -146,8 +149,6 @@ export default function ChatHistory(props: ChatHistoryProps) {
} }
} }
console.log("isUserAtBottom", isUserAtBottom());
if (isUserAtBottom()) { if (isUserAtBottom()) {
scrollToBottom(); scrollToBottom();
} }
@@ -222,7 +223,9 @@ export default function ChatHistory(props: ChatHistoryProps) {
<ScrollArea className={`h-[80vh]`}> <ScrollArea className={`h-[80vh]`}>
<div ref={ref}> <div ref={ref}>
<div className={styles.chatHistory} ref={chatHistoryRef}> <div className={styles.chatHistory} ref={chatHistoryRef}>
<div ref={sentinelRef} style={{ height: '1px' }}></div> <div ref={sentinelRef} style={{ height: '1px' }}>
{fetchingData && <InlineLoading />}
</div>
{(data && data.chat) && data.chat.map((chatMessage, index) => ( {(data && data.chat) && data.chat.map((chatMessage, index) => (
<ChatMessage <ChatMessage
key={`${index}fullHistory`} key={`${index}fullHistory`}
@@ -306,9 +309,9 @@ export default function ChatHistory(props: ChatHistoryProps) {
<ReferencePanel referencePanelData={referencePanelData} setShowReferencePanel={setShowReferencePanel} /> <ReferencePanel referencePanelData={referencePanelData} setShowReferencePanel={setShowReferencePanel} />
} }
<div className={`${styles.agentIndicator}`}> <div className={`${styles.agentIndicator}`}>
<a className='no-underline mx-2 flex' href={constructAgentLink()} target="_blank" rel="noreferrer"> <a className='no-underline mx-2 flex text-muted-foreground' href={constructAgentLink()} target="_blank" rel="noreferrer">
<Lightbulb color='orange' weight='fill' /> <Lightbulb color='orange' weight='fill' />
<span className='text-neutral-600'>{constructAgentName()}</span> <span>{constructAgentName()}</span>
</a> </a>
</div> </div>
</div> </div>

View File

@@ -4,7 +4,7 @@ div.chatMessageContainer {
margin: 12px; margin: 12px;
border-radius: 16px; border-radius: 16px;
padding: 16px; padding: 16px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.03); box-shadow: 0 4px 10px var(--box-shadow-color)
} }
div.chatMessageWrapper { div.chatMessageWrapper {
@@ -12,7 +12,6 @@ div.chatMessageWrapper {
} }
div.khojfullHistory { div.khojfullHistory {
border-color: var(--border-color);
border-width: 1px; border-width: 1px;
padding-left: 4px; padding-left: 4px;
} }
@@ -26,14 +25,14 @@ div.chatMessageContainer.youfullHistory {
} }
div.you { div.you {
background-color: var(--frosted-background-color); background-color: hsla(var(--secondary));
align-self: flex-end; align-self: flex-end;
border-radius: 16px; border-radius: 16px;
} }
div.khoj { div.khoj {
background-color: transparent; background-color: transparent;
color: #000000; color: hsl(var(--accent-foreground));
align-self: flex-start; align-self: flex-start;
} }
@@ -50,8 +49,8 @@ div.chatMessageContainer h3 img {
width: 24px; width: 24px;
} }
div.you .author { div.you {
color: var(--frosted-background-color); color: hsla(var(--secondary-foreground));
} }
div.author { div.author {
@@ -87,7 +86,7 @@ div.chatFooter button {
} }
div.chatFooter button:hover { div.chatFooter button:hover {
background-color: var(--frosted-background-color); background-color: hsla(var(--frosted-background-color));
} }
div.chatTimestamp { div.chatTimestamp {
@@ -101,7 +100,7 @@ button.codeCopyButton {
} }
button.codeCopyButton:hover { button.codeCopyButton:hover {
color: var(--frosted-background-color); color: hsla(var(--frosted-background-color));
} }
div.feedbackButtons img, div.feedbackButtons img,

View File

@@ -12,7 +12,9 @@ import 'highlight.js/styles/github.css'
import { hasValidReferences } from '../referencePanel/referencePanel'; import { hasValidReferences } from '../referencePanel/referencePanel';
import { ThumbsUp, ThumbsDown, Copy, Brain, Cloud, Folder, Book } from '@phosphor-icons/react'; import { ThumbsUp, ThumbsDown, Copy, Brain, Cloud, Folder, Book, Aperture } from '@phosphor-icons/react';
import { MagnifyingGlass } from '@phosphor-icons/react/dist/ssr';
import { compare } from 'swr/_internal';
const md = new markdownIt({ const md = new markdownIt({
html: true, html: true,
@@ -170,6 +172,14 @@ function chooseIconFromHeader(header: string, iconColor: string) {
return <Book className={`inline mr-2 ${iconColor}`} />; return <Book className={`inline mr-2 ${iconColor}`} />;
} }
if (compareHeader.includes("search")) {
return <MagnifyingGlass className={`inline mr-2 ${iconColor}`} />;
}
if (compareHeader.includes("summary") || compareHeader.includes("summarize")) {
return <Aperture className={`inline mr-2 ${iconColor}`} />;
}
return <Brain className={`inline mr-2 ${iconColor}`} />; return <Brain className={`inline mr-2 ${iconColor}`} />;
} }

View File

@@ -14,8 +14,8 @@ interface InlineLoadingProps {
export function InlineLoading(props: InlineLoadingProps) { export function InlineLoading(props: InlineLoadingProps) {
return ( return (
<button> <button className={`${props.className}`}>
<CircleNotch className={`animate-spin h-5 w-5 mr-3 ${props.className}`} /> <CircleNotch className={`animate-spin h-5 w-5 mr-3`} />
</button> </button>
) )
} }

View File

@@ -22,6 +22,8 @@ import {
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuTrigger, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"; } from "@/components/ui/dropdown-menu";
import { Toggle } from '@/components/ui/toggle';
import { Moon } from '@phosphor-icons/react';
interface NavMenuProps { interface NavMenuProps {
@@ -36,6 +38,7 @@ export default function NavMenu(props: NavMenuProps) {
const [displayTitle, setDisplayTitle] = useState<string>(props.title || props.selected.toUpperCase()); const [displayTitle, setDisplayTitle] = useState<string>(props.title || props.selected.toUpperCase());
const [isMobileWidth, setIsMobileWidth] = useState(false); const [isMobileWidth, setIsMobileWidth] = useState(false);
const [darkMode, setDarkMode] = useState(false);
useEffect(() => { useEffect(() => {
setIsMobileWidth(window.innerWidth < 768); setIsMobileWidth(window.innerWidth < 768);
@@ -43,6 +46,31 @@ export default function NavMenu(props: NavMenuProps) {
}, [props.title]); }, [props.title]);
useEffect(() => {
window.addEventListener('resize', () => {
setIsMobileWidth(window.innerWidth < 768);
});
if (localStorage.getItem('theme') === 'dark') {
document.documentElement.classList.add('dark');
setDarkMode(true);
}
}, []);
useEffect(() => {
toggleDarkMode(darkMode);
}, [darkMode]);
function toggleDarkMode(darkMode: boolean) {
if (darkMode) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
localStorage.setItem('theme', darkMode ? 'dark' : 'light');
}
return ( return (
<div className={styles.titleBar}> <div className={styles.titleBar}>
<div className={`text-nowrap text-ellipsis overflow-hidden max-w-screen-md grid items-top font-bold mr-8`}> <div className={`text-nowrap text-ellipsis overflow-hidden max-w-screen-md grid items-top font-bold mr-8`}>
@@ -94,30 +122,42 @@ export default function NavMenu(props: NavMenuProps) {
<MenubarTrigger>Automations</MenubarTrigger> <MenubarTrigger>Automations</MenubarTrigger>
</Link> </Link>
</MenubarMenu> </MenubarMenu>
{userData && <MenubarMenu>
<MenubarMenu> <MenubarTrigger>Profile</MenubarTrigger>
<MenubarTrigger>Profile</MenubarTrigger> <MenubarContent>
<MenubarContent> <MenubarItem>
<MenubarItem> <Toggle
<Link href="/config"> pressed={darkMode}
Settings onClick={() => {
</Link> console.log("clicked on dark mode method");
</MenubarItem> setDarkMode(!darkMode)}
<MenubarSeparator /> }>
<MenubarItem> <Moon />
<Link href="https://docs.khoj.dev"> </Toggle>
Help </MenubarItem>
</Link> {userData &&
</MenubarItem> <>
<MenubarSeparator /> <MenubarItem>
<MenubarItem> <Link href="/config">
<Link href="/auth/logout"> Settings
Logout </Link>
</Link> </MenubarItem>
</MenubarItem> <MenubarSeparator />
</MenubarContent> <MenubarItem>
</MenubarMenu> <Link href="https://docs.khoj.dev">
} Help
</Link>
</MenubarItem>
<MenubarSeparator />
<MenubarItem>
<Link href="/auth/logout">
Logout
</Link>
</MenubarItem>
</>
}
</MenubarContent>
</MenubarMenu>
</Menubar> </Menubar>
} }
</div> </div>

View File

@@ -25,7 +25,7 @@ div.contextReference:hover {
div.singleReference { div.singleReference {
padding: 8px; padding: 8px;
border-radius: 8px; border-radius: 8px;
background-color: var(--frosted-background-color); background-color: hsla(var(--frosted-background-color));
margin-top: 8px; margin-top: 8px;
} }

View File

@@ -14,11 +14,12 @@ div.compressed {
} }
div.sessionHover { div.sessionHover {
background-color: var(--secondary-accent); background-color: hsla(var(--popover));
} }
div.session:hover { div.session:hover {
background-color: var(--secondary-accent); background-color: hsla(var(--popover));
color: hsla(var(--popover-foreground));
} }
div.session a { div.session a {
@@ -43,8 +44,7 @@ div.panel {
display: grid; display: grid;
grid-auto-flow: row; grid-auto-flow: row;
padding: 1rem; padding: 1rem;
background-color: hsla(var(--primary-foreground)); background-color: hsla(var(--muted));
background-color: var(--secondary-background-color);
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
max-width: auto; max-width: auto;
@@ -94,7 +94,7 @@ div.modalSessionsList {
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: var(--frosted-background-color); background-color: hsla(var(--frosted-background-color));
z-index: 1; z-index: 1;
display: flex; display: flex;
justify-content: center; justify-content: center;
@@ -105,7 +105,7 @@ div.modalSessionsList {
div.modalSessionsList div.content { div.modalSessionsList div.content {
max-width: 80%; max-width: 80%;
max-height: 80%; max-height: 80%;
background-color: var(--frosted-background-color); background-color: hsla(var(--frosted-background-color));
overflow: auto; overflow: auto;
padding: 20px; padding: 20px;
border-radius: 10px; border-radius: 10px;

View File

@@ -29,11 +29,12 @@
/* Khoj Custom Colors */ /* Khoj Custom Colors */
--primary-hover: #fee285; --primary-hover: #fee285;
--frosted-background-color: #F5F3F2; --frosted-background-color: 20 13% 95%;
--secondary-background-color: #F7F7F5; --secondary-background-color: #F7F7F5;
--secondary-accent: #EDEDED; --secondary-accent: #EDEDED;
--khoj-orange: #FFE7D1; --khoj-orange: #FFE7D1;
--border-color: #e2e2e2; --border-color: #e2e2e2;
--box-shadow-color: rgba(0, 0, 0, 0.03);
} }
.dark { .dark {
@@ -57,6 +58,7 @@
--input: 215 27.9% 16.9%; --input: 215 27.9% 16.9%;
--ring: 263.4 70% 50.4%; --ring: 263.4 70% 50.4%;
--font-family: "Noto Sans", "Noto Sans Arabic", sans-serif !important; --font-family: "Noto Sans", "Noto Sans Arabic", sans-serif !important;
--box-shadow-color: rgba(255, 255, 255, 0.05);
} }
} }

View File

@@ -0,0 +1,45 @@
"use client"
import * as React from "react"
import * as TogglePrimitive from "@radix-ui/react-toggle"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const toggleVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground",
{
variants: {
variant: {
default: "bg-transparent",
outline:
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
},
size: {
default: "h-10 px-3",
sm: "h-9 px-2.5",
lg: "h-11 px-5",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
const Toggle = React.forwardRef<
React.ElementRef<typeof TogglePrimitive.Root>,
React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
VariantProps<typeof toggleVariants>
>(({ className, variant, size, ...props }, ref) => (
<TogglePrimitive.Root
ref={ref}
className={cn(toggleVariants({ variant, size, className }))}
{...props}
/>
))
Toggle.displayName = TogglePrimitive.Root.displayName
export { Toggle, toggleVariants }

View File

@@ -29,6 +29,7 @@
"@radix-ui/react-popover": "^1.1.1", "@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-scroll-area": "^1.1.0", "@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-toggle": "^1.1.0",
"@types/katex": "^0.16.7", "@types/katex": "^0.16.7",
"@types/markdown-it": "^14.1.1", "@types/markdown-it": "^14.1.1",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",

View File

@@ -803,6 +803,15 @@
dependencies: dependencies:
"@radix-ui/react-compose-refs" "1.1.0" "@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-toggle@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle/-/react-toggle-1.1.0.tgz#1f7697b82917019330a16c6f96f649f46b4606cf"
integrity sha512-gwoxaKZ0oJ4vIgzsfESBuSgJNdc0rv12VhHgcqN0TEJmmZixXG/2XpsLK8kzNWYcnaoRIEEQc0bEi3dIvdUpjw==
dependencies:
"@radix-ui/primitive" "1.1.0"
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-use-controllable-state" "1.1.0"
"@radix-ui/react-use-callback-ref@1.1.0": "@radix-ui/react-use-callback-ref@1.1.0":
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1"

View File

@@ -372,7 +372,7 @@ async def extract_references_and_questions(
logger.info(f"🔍 Searching knowledge base with queries: {inferred_queries}") logger.info(f"🔍 Searching knowledge base with queries: {inferred_queries}")
if send_status_func: if send_status_func:
inferred_queries_str = "\n- " + "\n- ".join(inferred_queries) inferred_queries_str = "\n- " + "\n- ".join(inferred_queries)
await send_status_func(f"**🔍 Searching Documents for:** {inferred_queries_str}") await send_status_func(f"**Searching Documents for:** {inferred_queries_str}")
for query in inferred_queries: for query in inferred_queries:
n_items = min(n, 3) if using_offline_chat else n n_items = min(n, 3) if using_offline_chat else n
search_results.extend( search_results.extend(