diff --git a/src/interface/web/app/chat/page.tsx b/src/interface/web/app/chat/page.tsx index 53a7ebc3..68a4cb11 100644 --- a/src/interface/web/app/chat/page.tsx +++ b/src/interface/web/app/chat/page.tsx @@ -144,7 +144,7 @@ export default function Chat() { Khoj AI - {title}
- +
diff --git a/src/interface/web/app/components/navMenu/navMenu.tsx b/src/interface/web/app/components/navMenu/navMenu.tsx index 3d829b1c..3e3e11f2 100644 --- a/src/interface/web/app/components/navMenu/navMenu.tsx +++ b/src/interface/web/app/components/navMenu/navMenu.tsx @@ -80,17 +80,17 @@ export default function NavMenu(props: NavMenuProps) { : - + Chat - + Agents - + Automations diff --git a/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx b/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx index e9fa2c78..dee437d1 100644 --- a/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx +++ b/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx @@ -2,7 +2,7 @@ import styles from "./sidePanel.module.css"; -import { useEffect, useState } from "react"; +import { Suspense, useEffect, useState } from "react"; import { UserProfile } from "@/app/common/auth"; import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; @@ -29,7 +29,7 @@ import { import { ScrollArea } from "@/components/ui/scroll-area"; -import { ArrowRight, ArrowLeft, ArrowDown, Spinner } from "@phosphor-icons/react"; +import { ArrowRight, ArrowLeft, ArrowDown, Spinner, Check } from "@phosphor-icons/react"; interface ChatHistory { conversation_id: string; @@ -124,60 +124,91 @@ function deleteConversation(conversationId: string) { }); } +function modifyFileFilterForConversation( + conversationId: string | null, + filename: string, + setAddedFiles: (files: string[]) => void, + mode: 'add' | 'remove') { + + if (!conversationId) { + console.error("No conversation ID provided"); + return; + } + + const method = mode === 'add' ? 'POST' : 'DELETE'; + + const body = { + conversation_id: conversationId, + filename: filename, + } + const addUrl = `/api/chat/conversation/file-filters`; + + fetch(addUrl, { + method: method, + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }) + .then(response => response.json()) + .then(data => { + setAddedFiles(data); + console.log(data); + }) + .catch(err => { + console.error(err); + return; + }); +} + interface FilesMenuProps { - conversationId: string; + conversationId: string | null; } function FilesMenu(props: FilesMenuProps) { // Use SWR to fetch files - const { data: files, error } = useSWR('/api/config/data/computer', fetcher); - const { data: selectedFiles, error: selectedFilesError } = useSWR(`/api/chat/conversation/file-filters/${props.conversationId}`, fetcher); + const { data: files, error } = useSWR(props.conversationId ? '/api/config/data/computer' : null, fetcher); + const { data: selectedFiles, error: selectedFilesError } = useSWR(props.conversationId ? `/api/chat/conversation/file-filters/${props.conversationId}` : null, fetcher); const [isOpen, setIsOpen] = useState(false); const [searchInput, setSearchInput] = useState(''); const [filteredFiles, setFilteredFiles] = useState([]); - - // Function to handle file click - const handleFileClick = (filename: string) => { - console.log(`File clicked: ${filename}`); - // Implement the logic you want to execute on file click - }; + const [addedFiles, setAddedFiles] = useState([]); useEffect(() => { if (!files) return; if (searchInput === '') { setFilteredFiles(files); } else { - let filteredFiles = files.filter((filename: string) => filename.toLowerCase().includes(searchInput.toLowerCase())); - setFilteredFiles(filteredFiles); + let sortedFiles = files.filter((filename: string) => filename.toLowerCase().includes(searchInput.toLowerCase())); + + if (addedFiles) { + sortedFiles = addedFiles.concat(filteredFiles.filter((filename: string) => !addedFiles.includes(filename))); + } + + setFilteredFiles(sortedFiles); } - }, [searchInput, files]); + }, [searchInput, files, addedFiles]); + + useEffect(() => { + if (selectedFiles) { + setAddedFiles(selectedFiles); + } + + }, [selectedFiles]); + + if (!props.conversationId) return (<>); if (error) return
Failed to load files
; + if (selectedFilesError) return
Failed to load selected files
; if (!files) return ; + if (!selectedFiles) return ; return ( <> - {/* -
    - {files.length === 0 ? ( - - ) : ( - files.map((filename: string) => ( -
  • handleFileClick(filename)}> - {filename} -
  • - )) - )} -
- {files.length > 0 && } -
*/} -
@@ -185,7 +216,7 @@ function FilesMenu(props: FilesMenuProps) {

Manage Files

- Using {files.length} files + Using {addedFiles.length == 0 ? files.length : addedFiles.length} files

+ : + )) } - {/* -
-

- Manage Files -

- Using {files.length} files -

-

- - - -
- - setSearchInput(e.target.value)} /> - { - filteredFiles.length === 0 && ( -
- No files found -
- ) - } - { - filteredFiles.map((filename: string) => ( -
- {filename} -
- )) - } -
-
*/} ) @@ -282,6 +280,7 @@ interface SessionsAndFilesProps { organizedData: GroupedChatHistory | null; data: ChatHistory[] | null; userProfile: UserProfile | null; + conversationId: string | null; } function SessionsAndFiles(props: SessionsAndFilesProps) { @@ -296,13 +295,13 @@ function SessionsAndFiles(props: SessionsAndFilesProps) {
{props.subsetOrganizedData != null && Object.keys(props.subsetOrganizedData).map((agentName) => (
-

+ {/*

{ props.subsetOrganizedData && {agentName} } {agentName} -

+ */} {props.subsetOrganizedData && props.subsetOrganizedData[agentName].map((chatHistory) => ( ) } - + {props.userProfile && } @@ -370,6 +369,7 @@ function ChatSessionActionMenu(props: ChatSessionActionMenuProps) { @@ -385,7 +385,7 @@ function ChatSessionActionMenu(props: ChatSessionActionMenuProps) { return ( { + onOpenChange={(open) => { setShowShareUrl(open) setIsSharing(open) }}> @@ -559,24 +559,26 @@ function UserProfileComponent(props: UserProfileProps) { } return ( -
- - - - {props.userProfile.username[0]} - - -
-

{props.userProfile?.username}

- {/* Connected Indicator */} -
-
-

- {props.webSocketConnected ? "Connected" : "Disconnected"} -

+
+ + + + + {props.userProfile.username[0]} + + + +
+

{props.userProfile?.username}

+ {/* Connected Indicator */} +
+
+

+ {props.webSocketConnected ? "Connected" : "Disconnected"} +

+
-
); } @@ -603,6 +605,7 @@ export const useChatHistoryRecentFetchRequest = (url: string) => { interface SidePanelProps { webSocketConnected?: boolean; + conversationId: string | null; } @@ -611,7 +614,6 @@ export default function SidePanel(props: SidePanelProps) { const [data, setData] = useState(null); const [organizedData, setOrganizedData] = useState(null); const [subsetOrganizedData, setSubsetOrganizedData] = useState(null); - const [isLoading, setLoading] = useState(true) const [enabled, setEnabled] = useState(false); const [userProfile, setUserProfile] = useState(null); @@ -670,6 +672,7 @@ export default function SidePanel(props: SidePanelProps) { organizedData={organizedData} data={data} userProfile={userProfile} + conversationId={props.conversationId} />
: