mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-03 21:29:08 +00:00
Checkpoint: Updated sidebar panel with new components
- Add non-functional UI elements for chat, references, feedback buttons, rename/share session, mic, attachment, websocket connection
This commit is contained in:
@@ -12,23 +12,27 @@ div.main {
|
||||
|
||||
div.inputBox {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
padding: 1rem;
|
||||
border-radius: 1rem;
|
||||
background-color: #f5f5f5;
|
||||
box-shadow: 0 0 1rem 0 rgba(0, 0, 0, 0.1);
|
||||
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);
|
||||
margin-bottom: 20px;
|
||||
gap: 12px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
input.inputBox {
|
||||
border: none;
|
||||
}
|
||||
|
||||
input.inputBox:focus {
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
input.inputBox:focus {
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
div.inputBox:focus {
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
div.chatBodyFull {
|
||||
@@ -65,9 +69,7 @@ div.chatLayout {
|
||||
|
||||
div.chatBox {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
height: 100%;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
div.titleBar {
|
||||
@@ -75,6 +77,25 @@ div.titleBar {
|
||||
grid-template-columns: 1fr auto;
|
||||
}
|
||||
|
||||
div.chatBoxBody {
|
||||
display: grid;
|
||||
height: 100%;
|
||||
width: 70%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
div.agentIndicator a {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
div.agentIndicator {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 768px) {
|
||||
div.chatBody {
|
||||
grid-template-columns: 0fr 1fr;
|
||||
@@ -84,3 +105,22 @@ div.titleBar {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
div.inputBox {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
div.chatBoxBody {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.chatBox {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.chatLayout {
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,54 +6,69 @@ import React, { Suspense, useEffect, useState } from 'react';
|
||||
import SuggestionCard from '../components/suggestions/suggestionCard';
|
||||
import SidePanel from '../components/sidePanel/chatHistorySidePanel';
|
||||
import ChatHistory from '../components/chatHistory/chatHistory';
|
||||
import { SingleChatMessage } from '../components/chatMessage/chatMessage';
|
||||
import NavMenu from '../components/navMenu/navMenu';
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
import ReferencePanel, { hasValidReferences } from '../components/referencePanel/referencePanel';
|
||||
import Loading from '../components/loading/loading';
|
||||
|
||||
import { setupWebSocket } from '../common/chatFunctions';
|
||||
|
||||
import 'katex/dist/katex.min.css';
|
||||
import { Lightbulb, ArrowCircleUp, FileArrowUp, Microphone } from '@phosphor-icons/react';
|
||||
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Textarea } from "@/components/ui/textarea"
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
export function TextareaWithLabel() {
|
||||
return (
|
||||
<div className="grid w-full gap-1.5">
|
||||
{/* <Label htmlFor="message">Your message</Label> */}
|
||||
<Textarea className='border-none min-h-[60px]' placeholder="Type / to see a list of commands" id="message" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface ChatOptions {
|
||||
[key: string]: string
|
||||
}
|
||||
const styleClassOptions = ['pink', 'blue', 'green', 'yellow', 'purple'];
|
||||
|
||||
interface ChatBodyDataProps {
|
||||
chatOptionsData: ChatOptions | null;
|
||||
setTitle: (title: string) => void;
|
||||
setConversationID?: (conversationId: string) => void;
|
||||
}
|
||||
|
||||
function ChatBodyData({ chatOptionsData }: { chatOptionsData: ChatOptions | null }) {
|
||||
const searchParams = useSearchParams();
|
||||
const conversationId = searchParams.get('conversationId');
|
||||
const [showReferencePanel, setShowReferencePanel] = useState(true);
|
||||
const [referencePanelData, setReferencePanelData] = useState<SingleChatMessage | null>(null);
|
||||
|
||||
function ChatBodyData(props: ChatBodyDataProps) {
|
||||
const searchParams = useSearchParams();
|
||||
const conversationId = searchParams.get('conversationId');
|
||||
|
||||
if (conversationId && props.setConversationID) {
|
||||
props.setConversationID(conversationId);
|
||||
}
|
||||
|
||||
if (!conversationId) {
|
||||
return (
|
||||
<div className={styles.suggestions}>
|
||||
{chatOptionsData && Object.entries(chatOptionsData).map(([key, value]) => (
|
||||
{props.chatOptionsData && Object.entries(props.chatOptionsData).map(([key, value]) => (
|
||||
<SuggestionCard
|
||||
key={key}
|
||||
title={`/${key}`}
|
||||
body={value}
|
||||
link='#' // replace with actual link if available
|
||||
styleClass={styleClassOptions[Math.floor(Math.random() * styleClassOptions.length)]}
|
||||
/>
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return(
|
||||
<div className={(hasValidReferences(referencePanelData) && showReferencePanel) ? styles.chatBody : styles.chatBodyFull}>
|
||||
<ChatHistory conversationId={conversationId} setReferencePanelData={setReferencePanelData} setShowReferencePanel={setShowReferencePanel} />
|
||||
{
|
||||
(hasValidReferences(referencePanelData) && showReferencePanel) &&
|
||||
<ReferencePanel referencePanelData={referencePanelData} setShowReferencePanel={setShowReferencePanel} />
|
||||
}
|
||||
return (
|
||||
<div className={false ? styles.chatBody : styles.chatBodyFull}>
|
||||
<ChatHistory conversationId={conversationId} setTitle={props.setTitle} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Loading() {
|
||||
return <h2>🌀 Loading...</h2>;
|
||||
);
|
||||
}
|
||||
|
||||
function handleChatInput(e: React.FormEvent<HTMLInputElement>) {
|
||||
@@ -63,9 +78,12 @@ function handleChatInput(e: React.FormEvent<HTMLInputElement>) {
|
||||
|
||||
export default function Chat() {
|
||||
const [chatOptionsData, setChatOptionsData] = useState<ChatOptions | null>(null);
|
||||
const [isLoading, setLoading] = useState(true)
|
||||
const [isLoading, setLoading] = useState(true);
|
||||
const [title, setTitle] = useState('Chat');
|
||||
const [conversationId, setConversationID] = useState<string | null>(null);
|
||||
const [chatWS, setChatWS] = useState<WebSocket | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
useEffect(() => {
|
||||
fetch('/api/chat/options')
|
||||
.then(response => response.json())
|
||||
.then((data: ChatOptions) => {
|
||||
@@ -80,27 +98,60 @@ export default function Chat() {
|
||||
console.error(err);
|
||||
return;
|
||||
});
|
||||
}, []);
|
||||
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (conversationId) {
|
||||
const newWS = await setupWebSocket(conversationId);
|
||||
setChatWS(newWS);
|
||||
}
|
||||
})();
|
||||
}, [conversationId]);
|
||||
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className={styles.main + " " + styles.chatLayout}>
|
||||
<div className={styles.sidePanel}>
|
||||
<SidePanel />
|
||||
<SidePanel webSocketConnected={chatWS !== null} />
|
||||
</div>
|
||||
<title>
|
||||
Khoj AI - Chat
|
||||
</title>
|
||||
<div className={styles.chatBox}>
|
||||
<title>
|
||||
Khoj AI - Chat
|
||||
</title>
|
||||
<NavMenu selected="Chat" />
|
||||
<div>
|
||||
<Suspense fallback={<Loading />}>
|
||||
<ChatBodyData chatOptionsData={chatOptionsData} />
|
||||
</Suspense>
|
||||
</div>
|
||||
<div className={styles.inputBox}>
|
||||
<input className={styles.inputBox} type="text" placeholder="Type here..." onInput={(e) => handleChatInput(e)} />
|
||||
<button className={styles.inputBox}>Send</button>
|
||||
<NavMenu selected="Chat" title={title} />
|
||||
<div className={styles.chatBoxBody}>
|
||||
<div>
|
||||
<Suspense fallback={<Loading />}>
|
||||
<ChatBodyData chatOptionsData={chatOptionsData} setTitle={setTitle} setConversationID={setConversationID} />
|
||||
</Suspense>
|
||||
</div>
|
||||
{/* <div className={styles.agentIndicator}>
|
||||
<a className='no-underline' href="/agents?agent=khoj" target="_blank" rel="noreferrer">
|
||||
<Lightbulb color='var(--khoj-orange)' weight='fill' />
|
||||
<span className='text-neutral-600'>Khoj</span>
|
||||
</a>
|
||||
</div> */}
|
||||
<div className={`${styles.inputBox} bg-background align-middle items-center justify-center`}>
|
||||
<Button className="!bg-transparent !hover:bg-transparent p-0 h-auto text-3xl">
|
||||
<FileArrowUp fill="hsla(var(--secondary-foreground))"/>
|
||||
</Button>
|
||||
<TextareaWithLabel />
|
||||
<Button className="!bg-transparent !hover:bg-transparent p-0 h-auto text-3xl">
|
||||
<Microphone fill="hsla(var(--secondary-foreground))"/>
|
||||
</Button>
|
||||
<Button className="bg-orange-300 hover:bg-orange-500 rounded-full p-0 h-auto text-3xl">
|
||||
<ArrowCircleUp/>
|
||||
</Button>
|
||||
{/* <input className={styles.inputBox} type="text" placeholder="Type / to see a list of commands" onInput={(e) => handleChatInput(e)} /> */}
|
||||
{/* <button className={styles.inputBox}>Send</button> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user