mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-03 13:19:16 +00:00
Add dark mode toggle! And improve experience for train of thought
This commit is contained in:
@@ -15,7 +15,7 @@ div.inputBox {
|
||||
grid-template-columns: auto 1fr auto auto;
|
||||
border: 1px solid var(--border-color);
|
||||
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;
|
||||
gap: 12px;
|
||||
padding-left: 20px;
|
||||
|
||||
@@ -26,5 +26,5 @@ div.trainOfThought {
|
||||
border-radius: 16px;
|
||||
padding: 16px;
|
||||
margin: 12px;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.03);
|
||||
box-shadow: 0 4px 10px var(--box-shadow-color);
|
||||
}
|
||||
|
||||
@@ -101,13 +101,17 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
||||
}
|
||||
|
||||
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) => {
|
||||
if (!hasMoreMessages || fetchingData) return;
|
||||
|
||||
console.log("fetchMoreMessages", currentPage);
|
||||
|
||||
const nextPage = currentPage + 1;
|
||||
fetch(`/api/chat/history?client=web&conversation_id=${props.conversationId}&n=${10 * nextPage}`)
|
||||
.then(response => response.json())
|
||||
@@ -117,6 +121,7 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
||||
|
||||
if (chatData.response.chat.length === data?.chat.length) {
|
||||
setHasMoreMessages(false);
|
||||
setFetchingData(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -124,12 +129,10 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
||||
setLoading(false);
|
||||
|
||||
if (currentPage < 2) {
|
||||
console.log("call scroll to bottom");
|
||||
scrollToBottom();
|
||||
}
|
||||
setFetchingData(false);
|
||||
} else {
|
||||
console.log("No more messages");
|
||||
setHasMoreMessages(false);
|
||||
}
|
||||
})
|
||||
@@ -146,8 +149,6 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
||||
}
|
||||
}
|
||||
|
||||
console.log("isUserAtBottom", isUserAtBottom());
|
||||
|
||||
if (isUserAtBottom()) {
|
||||
scrollToBottom();
|
||||
}
|
||||
@@ -222,7 +223,9 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
||||
<ScrollArea className={`h-[80vh]`}>
|
||||
<div ref={ref}>
|
||||
<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) => (
|
||||
<ChatMessage
|
||||
key={`${index}fullHistory`}
|
||||
@@ -306,9 +309,9 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
||||
<ReferencePanel referencePanelData={referencePanelData} setShowReferencePanel={setShowReferencePanel} />
|
||||
}
|
||||
<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' />
|
||||
<span className='text-neutral-600'>{constructAgentName()}</span>
|
||||
<span>{constructAgentName()}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ div.chatMessageContainer {
|
||||
margin: 12px;
|
||||
border-radius: 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 {
|
||||
@@ -12,7 +12,6 @@ div.chatMessageWrapper {
|
||||
}
|
||||
|
||||
div.khojfullHistory {
|
||||
border-color: var(--border-color);
|
||||
border-width: 1px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
@@ -26,14 +25,14 @@ div.chatMessageContainer.youfullHistory {
|
||||
}
|
||||
|
||||
div.you {
|
||||
background-color: var(--frosted-background-color);
|
||||
background-color: hsla(var(--secondary));
|
||||
align-self: flex-end;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
div.khoj {
|
||||
background-color: transparent;
|
||||
color: #000000;
|
||||
color: hsl(var(--accent-foreground));
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
@@ -50,8 +49,8 @@ div.chatMessageContainer h3 img {
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
div.you .author {
|
||||
color: var(--frosted-background-color);
|
||||
div.you {
|
||||
color: hsla(var(--secondary-foreground));
|
||||
}
|
||||
|
||||
div.author {
|
||||
@@ -87,7 +86,7 @@ div.chatFooter button {
|
||||
}
|
||||
|
||||
div.chatFooter button:hover {
|
||||
background-color: var(--frosted-background-color);
|
||||
background-color: hsla(var(--frosted-background-color));
|
||||
}
|
||||
|
||||
div.chatTimestamp {
|
||||
@@ -101,7 +100,7 @@ button.codeCopyButton {
|
||||
}
|
||||
|
||||
button.codeCopyButton:hover {
|
||||
color: var(--frosted-background-color);
|
||||
color: hsla(var(--frosted-background-color));
|
||||
}
|
||||
|
||||
div.feedbackButtons img,
|
||||
|
||||
@@ -12,7 +12,9 @@ import 'highlight.js/styles/github.css'
|
||||
|
||||
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({
|
||||
html: true,
|
||||
@@ -170,6 +172,14 @@ function chooseIconFromHeader(header: string, iconColor: string) {
|
||||
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}`} />;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ interface InlineLoadingProps {
|
||||
|
||||
export function InlineLoading(props: InlineLoadingProps) {
|
||||
return (
|
||||
<button>
|
||||
<CircleNotch className={`animate-spin h-5 w-5 mr-3 ${props.className}`} />
|
||||
<button className={`${props.className}`}>
|
||||
<CircleNotch className={`animate-spin h-5 w-5 mr-3`} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Toggle } from '@/components/ui/toggle';
|
||||
import { Moon } from '@phosphor-icons/react';
|
||||
|
||||
|
||||
interface NavMenuProps {
|
||||
@@ -36,6 +38,7 @@ export default function NavMenu(props: NavMenuProps) {
|
||||
const [displayTitle, setDisplayTitle] = useState<string>(props.title || props.selected.toUpperCase());
|
||||
|
||||
const [isMobileWidth, setIsMobileWidth] = useState(false);
|
||||
const [darkMode, setDarkMode] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsMobileWidth(window.innerWidth < 768);
|
||||
@@ -43,6 +46,31 @@ export default function NavMenu(props: NavMenuProps) {
|
||||
|
||||
}, [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 (
|
||||
<div className={styles.titleBar}>
|
||||
<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>
|
||||
</Link>
|
||||
</MenubarMenu>
|
||||
{userData &&
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger>Profile</MenubarTrigger>
|
||||
<MenubarContent>
|
||||
<MenubarItem>
|
||||
<Link href="/config">
|
||||
Settings
|
||||
</Link>
|
||||
</MenubarItem>
|
||||
<MenubarSeparator />
|
||||
<MenubarItem>
|
||||
<Link href="https://docs.khoj.dev">
|
||||
Help
|
||||
</Link>
|
||||
</MenubarItem>
|
||||
<MenubarSeparator />
|
||||
<MenubarItem>
|
||||
<Link href="/auth/logout">
|
||||
Logout
|
||||
</Link>
|
||||
</MenubarItem>
|
||||
</MenubarContent>
|
||||
</MenubarMenu>
|
||||
}
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger>Profile</MenubarTrigger>
|
||||
<MenubarContent>
|
||||
<MenubarItem>
|
||||
<Toggle
|
||||
pressed={darkMode}
|
||||
onClick={() => {
|
||||
console.log("clicked on dark mode method");
|
||||
setDarkMode(!darkMode)}
|
||||
}>
|
||||
<Moon />
|
||||
</Toggle>
|
||||
</MenubarItem>
|
||||
{userData &&
|
||||
<>
|
||||
<MenubarItem>
|
||||
<Link href="/config">
|
||||
Settings
|
||||
</Link>
|
||||
</MenubarItem>
|
||||
<MenubarSeparator />
|
||||
<MenubarItem>
|
||||
<Link href="https://docs.khoj.dev">
|
||||
Help
|
||||
</Link>
|
||||
</MenubarItem>
|
||||
<MenubarSeparator />
|
||||
<MenubarItem>
|
||||
<Link href="/auth/logout">
|
||||
Logout
|
||||
</Link>
|
||||
</MenubarItem>
|
||||
</>
|
||||
}
|
||||
</MenubarContent>
|
||||
</MenubarMenu>
|
||||
</Menubar>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@ div.contextReference:hover {
|
||||
div.singleReference {
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
background-color: var(--frosted-background-color);
|
||||
background-color: hsla(var(--frosted-background-color));
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,12 @@ div.compressed {
|
||||
}
|
||||
|
||||
div.sessionHover {
|
||||
background-color: var(--secondary-accent);
|
||||
background-color: hsla(var(--popover));
|
||||
}
|
||||
|
||||
div.session:hover {
|
||||
background-color: var(--secondary-accent);
|
||||
background-color: hsla(var(--popover));
|
||||
color: hsla(var(--popover-foreground));
|
||||
}
|
||||
|
||||
div.session a {
|
||||
@@ -43,8 +44,7 @@ div.panel {
|
||||
display: grid;
|
||||
grid-auto-flow: row;
|
||||
padding: 1rem;
|
||||
background-color: hsla(var(--primary-foreground));
|
||||
background-color: var(--secondary-background-color);
|
||||
background-color: hsla(var(--muted));
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
max-width: auto;
|
||||
@@ -94,7 +94,7 @@ div.modalSessionsList {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--frosted-background-color);
|
||||
background-color: hsla(var(--frosted-background-color));
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -105,7 +105,7 @@ div.modalSessionsList {
|
||||
div.modalSessionsList div.content {
|
||||
max-width: 80%;
|
||||
max-height: 80%;
|
||||
background-color: var(--frosted-background-color);
|
||||
background-color: hsla(var(--frosted-background-color));
|
||||
overflow: auto;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
|
||||
@@ -29,11 +29,12 @@
|
||||
|
||||
/* Khoj Custom Colors */
|
||||
--primary-hover: #fee285;
|
||||
--frosted-background-color: #F5F3F2;
|
||||
--frosted-background-color: 20 13% 95%;
|
||||
--secondary-background-color: #F7F7F5;
|
||||
--secondary-accent: #EDEDED;
|
||||
--khoj-orange: #FFE7D1;
|
||||
--border-color: #e2e2e2;
|
||||
--box-shadow-color: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.dark {
|
||||
@@ -57,6 +58,7 @@
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 263.4 70% 50.4%;
|
||||
--font-family: "Noto Sans", "Noto Sans Arabic", sans-serif !important;
|
||||
--box-shadow-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user