From 0adee07d403b933b7bfa8d2b48479ec3771a05ba Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Fri, 2 Aug 2024 20:02:33 +0530 Subject: [PATCH 01/12] Update home page greetings to use user name, when available --- src/interface/web/app/page.tsx | 73 +++++++++++++++++----------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/src/interface/web/app/page.tsx b/src/interface/web/app/page.tsx index c28eacda..32cbeed9 100644 --- a/src/interface/web/app/page.tsx +++ b/src/interface/web/app/page.tsx @@ -1,39 +1,26 @@ 'use client' import './globals.css'; - import styles from './page.module.css'; -import React, { useEffect, useState } from 'react'; - -import SuggestionCard from './components/suggestions/suggestionCard'; -import SidePanel from './components/sidePanel/chatHistorySidePanel'; -import NavMenu from './components/navMenu/navMenu'; -import Loading from './components/loading/loading'; -import useSWR from 'swr'; -import Image from 'next/image'; - import 'katex/dist/katex.min.css'; -import ChatInputArea, { ChatOptions } from './components/chatInputArea/chatInputArea'; -import { useAuthenticatedData } from './common/auth'; -import { Card, CardTitle } from '@/components/ui/card'; -import { convertColorToBorderClass } from './common/colorUtils'; -import { getIconFromIconName } from './common/iconUtils'; +import React, { useEffect, useState } from 'react'; +import useSWR from 'swr'; +import Image from 'next/image'; import { ClockCounterClockwise } from '@phosphor-icons/react'; -import { AgentData } from './agents/page'; -import { Suggestion, suggestionsData } from './components/suggestions/suggestionsData'; +import { Card, CardTitle } from '@/components/ui/card'; +import SuggestionCard from '@/app/components/suggestions/suggestionCard'; +import SidePanel from '@/app/components/sidePanel/chatHistorySidePanel'; +import NavMenu from '@/app/components/navMenu/navMenu'; +import Loading from '@/app/components/loading/loading'; +import ChatInputArea, { ChatOptions } from '@/app/components/chatInputArea/chatInputArea'; +import { Suggestion, suggestionsData } from '@/app/components/suggestions/suggestionsData'; + +import { useAuthenticatedData, useUserConfig } from '@/app/common/auth'; +import { convertColorToBorderClass } from '@/app/common/colorUtils'; +import { getIconFromIconName } from '@/app/common/iconUtils'; +import { AgentData } from '@/app/agents/page'; -//get today's day -const today = new Date(); -const day = today.getDay(); -const greetings = [ - `Good ${today.getHours() < 12 ? 'morning' : today.getHours() < 15 ? 'afternoon' : 'evening'}! What would you like to do today?`, - 'How can I help you today?', - `Good ${today.getHours() < 12 ? 'morning' : today.getHours() < 15 ? 'afternoon' : 'evening'}! What's on your mind?`, - `Ready to breeze through your ${['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]}?`, - `Want to navigate your ${['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]} workload?` -]; -const greeting = greetings[~~(Math.random() * greetings.length)]; interface ChatBodyDataProps { @@ -47,14 +34,10 @@ interface ChatBodyDataProps { async function createNewConvo(slug: string) { try { const response = await fetch(`/api/chat/sessions?client=web&agent_slug=${slug}`, { method: "POST" }); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } + if (!response.ok) throw new Error(`Failed to fetch chat sessions with status: ${response.status}`); const data = await response.json(); const conversationID = data.conversation_id; - if (!conversationID) { - throw new Error("Conversation ID not found in response"); - } + if (!conversationID) throw new Error("Conversation ID not found in response"); return conversationID; } catch (error) { console.error("Error creating new conversation:", error); @@ -65,11 +48,13 @@ async function createNewConvo(slug: string) { function ChatBodyData(props: ChatBodyDataProps) { const [message, setMessage] = useState(''); const [processingMessage, setProcessingMessage] = useState(false); + const [greeting, setGreeting] = useState(''); const [shuffledOptions, setShuffledOptions] = useState([]); const [selectedAgent, setSelectedAgent] = useState("khoj"); const [agentIcons, setAgentIcons] = useState([]); const [agents, setAgents] = useState([]); + const userConfig = useUserConfig(true); const agentsFetcher = () => window.fetch('/api/agents').then(res => res.json()).catch(err => console.log(err)); const { data: agentsData, error } = useSWR('agents', agentsFetcher, { revalidateOnFocus: false }); @@ -78,6 +63,24 @@ function ChatBodyData(props: ChatBodyDataProps) { setShuffledOptions(shuffled.slice(0, 3)); } + useEffect(() => { + // Get today's day + const today = new Date(); + const day = today.getDay(); + const timeOfDay = today.getHours() > 4 && today.getHours() < 12 ? 'morning' : today.getHours() < 17 ? 'afternoon' : 'evening'; + const nameSuffix = userConfig?.given_name ? `, ${userConfig?.given_name}` : ""; + console.log(userConfig); + const greetings = [ + `What would you like to get done${nameSuffix}?`, + `Hey${nameSuffix}! How can I help?`, + `Good ${timeOfDay}${nameSuffix}! What's on your mind?`, + `Ready to breeze through your ${['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]}?`, + `Want help navigating your ${['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]} workload?` + ]; + const greeting = greetings[Math.floor(Math.random() * greetings.length)]; + setGreeting(greeting); + }, []); + useEffect(() => { if (props.chatOptionsData) { shuffleAndSetOptions(); @@ -86,9 +89,7 @@ function ChatBodyData(props: ChatBodyDataProps) { useEffect(() => { const nSlice = props.isMobileWidth ? 3 : 4; - const shuffledAgents = agentsData ? [...agentsData].sort(() => 0.5 - Math.random()) : []; - const agents = agentsData ? [agentsData[0]] : []; // Always add the first/default agent. shuffledAgents.slice(0, nSlice - 1).forEach(agent => { From e62888659f70c16e1e4bd03a479cd4b771a4133e Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Fri, 2 Aug 2024 20:03:51 +0530 Subject: [PATCH 02/12] Only show greeting once userConfig is fetched from server - Pass userConfig from Home as prop to chatBodyData component with loading state - Pass loading state of userConfig to allow components to handle rendering dependent elements once it is loaded --- src/interface/web/app/common/auth.ts | 6 ++--- src/interface/web/app/page.tsx | 29 +++++++++++++++++++------ src/interface/web/app/settings/page.tsx | 2 +- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/interface/web/app/common/auth.ts b/src/interface/web/app/common/auth.ts index a5bc7f13..15dbd9dd 100644 --- a/src/interface/web/app/common/auth.ts +++ b/src/interface/web/app/common/auth.ts @@ -71,9 +71,9 @@ export interface UserConfig { export function useUserConfig(detailed: boolean = false) { const url = `/api/settings?detailed=${detailed}`; - const { data, error } = useSWR(url, fetcher, { revalidateOnFocus: false }); + const { data: userConfig, error, isLoading: isLoadingUserConfig } = useSWR(url, fetcher, { revalidateOnFocus: false }); - if (error || !data || data.detail === 'Forbidden') return null; + if (error || !userConfig || userConfig?.detail === 'Forbidden') return {userConfig: null, isLoadingUserConfig}; - return data; + return {userConfig, isLoadingUserConfig}; } diff --git a/src/interface/web/app/page.tsx b/src/interface/web/app/page.tsx index 32cbeed9..aef9f58c 100644 --- a/src/interface/web/app/page.tsx +++ b/src/interface/web/app/page.tsx @@ -3,7 +3,7 @@ import './globals.css'; import styles from './page.module.css'; import 'katex/dist/katex.min.css'; -import React, { useEffect, useState } from 'react'; +import React, { use, useEffect, useState } from 'react'; import useSWR from 'swr'; import Image from 'next/image'; import { ClockCounterClockwise } from '@phosphor-icons/react'; @@ -16,7 +16,7 @@ import Loading from '@/app/components/loading/loading'; import ChatInputArea, { ChatOptions } from '@/app/components/chatInputArea/chatInputArea'; import { Suggestion, suggestionsData } from '@/app/components/suggestions/suggestionsData'; -import { useAuthenticatedData, useUserConfig } from '@/app/common/auth'; +import { useAuthenticatedData, UserConfig, useUserConfig } from '@/app/common/auth'; import { convertColorToBorderClass } from '@/app/common/colorUtils'; import { getIconFromIconName } from '@/app/common/iconUtils'; import { AgentData } from '@/app/agents/page'; @@ -29,6 +29,8 @@ interface ChatBodyDataProps { setUploadedFiles: (files: string[]) => void; isMobileWidth?: boolean; isLoggedIn: boolean; + userConfig: UserConfig | null; + isLoadingUserConfig: boolean; } async function createNewConvo(slug: string) { @@ -54,7 +56,6 @@ function ChatBodyData(props: ChatBodyDataProps) { const [agentIcons, setAgentIcons] = useState([]); const [agents, setAgents] = useState([]); - const userConfig = useUserConfig(true); const agentsFetcher = () => window.fetch('/api/agents').then(res => res.json()).catch(err => console.log(err)); const { data: agentsData, error } = useSWR('agents', agentsFetcher, { revalidateOnFocus: false }); @@ -64,12 +65,17 @@ function ChatBodyData(props: ChatBodyDataProps) { } useEffect(() => { + console.log(`Loading user config: ${props.isLoadingUserConfig}`); + if (props.isLoadingUserConfig) return; + + // Set user config + console.log(`Logged In: ${props.isLoggedIn}\nUserConfig: ${props.userConfig}`); + // Get today's day const today = new Date(); const day = today.getDay(); const timeOfDay = today.getHours() > 4 && today.getHours() < 12 ? 'morning' : today.getHours() < 17 ? 'afternoon' : 'evening'; - const nameSuffix = userConfig?.given_name ? `, ${userConfig?.given_name}` : ""; - console.log(userConfig); + const nameSuffix = props.userConfig?.given_name ? `, ${props.userConfig?.given_name}` : ""; const greetings = [ `What would you like to get done${nameSuffix}?`, `Hey${nameSuffix}! How can I help?`, @@ -79,7 +85,7 @@ function ChatBodyData(props: ChatBodyDataProps) { ]; const greeting = greetings[Math.floor(Math.random() * greetings.length)]; setGreeting(greeting); - }, []); + }, [props.isLoadingUserConfig, props.userConfig]); useEffect(() => { if (props.chatOptionsData) { @@ -161,7 +167,7 @@ function ChatBodyData(props: ChatBodyDataProps) {
-

{greeting}

+

{greeting}

{ !props.isMobileWidth && @@ -266,12 +272,19 @@ export default function Home() { const [uploadedFiles, setUploadedFiles] = useState([]); const [isMobileWidth, setIsMobileWidth] = useState(false); + const {userConfig: initialUserConfig, isLoadingUserConfig} = useUserConfig(true); + const [userConfig, setUserConfig] = useState(null); + const authenticatedData = useAuthenticatedData(); const handleConversationIdChange = (newConversationId: string) => { setConversationID(newConversationId); }; + useEffect(() => { + setUserConfig(initialUserConfig); + }, [initialUserConfig]); + useEffect(() => { fetch('/api/chat/options') .then(response => response.json()) @@ -319,6 +332,8 @@ export default function Home() { setUploadedFiles={setUploadedFiles} isMobileWidth={isMobileWidth} onConversationIdChange={handleConversationIdChange} + userConfig={userConfig} + isLoadingUserConfig={isLoadingUserConfig} />
diff --git a/src/interface/web/app/settings/page.tsx b/src/interface/web/app/settings/page.tsx index 66bf9634..59d28b6b 100644 --- a/src/interface/web/app/settings/page.tsx +++ b/src/interface/web/app/settings/page.tsx @@ -348,7 +348,7 @@ export default function SettingsView() { const [title, setTitle] = useState("Settings"); const [isMobileWidth, setIsMobileWidth] = useState(false); const { apiKeys, generateAPIKey, copyAPIKey, deleteAPIKey } = useApiKeys(); - const initialUserConfig = useUserConfig(true); + const {userConfig: initialUserConfig} = useUserConfig(true); const [userConfig, setUserConfig] = useState(null); const [name, setName] = useState(undefined); const [notionToken, setNotionToken] = useState(null); From 07b3bdf1811d8ede9d21efa8fd9670f108f13b08 Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 2 Aug 2024 17:46:13 +0530 Subject: [PATCH 03/12] Update nav menu styling to include everything in one header - Move the nav menu into the chat history side panel component, so that they both show up on one line - Update all pages to use it with the new formatting - in mobile, present the sidebar button, home button, and profile button evenly centered in the middle --- .../web/app/agents/agents.module.css | 7 +- src/interface/web/app/agents/page.tsx | 5 +- .../app/automations/automations.module.css | 5 + src/interface/web/app/automations/page.tsx | 5 +- src/interface/web/app/chat/page.tsx | 36 +++- .../app/components/navMenu/navMenu.module.css | 4 - .../web/app/components/navMenu/navMenu.tsx | 30 +--- .../sidePanel/chatHistorySidePanel.tsx | 38 ++-- .../components/sidePanel/sidePanel.module.css | 6 +- src/interface/web/app/page.module.css | 16 +- src/interface/web/app/page.tsx | 11 +- src/interface/web/app/search/page.tsx | 167 +++++++++--------- .../web/app/search/search.module.css | 12 +- 13 files changed, 183 insertions(+), 159 deletions(-) diff --git a/src/interface/web/app/agents/agents.module.css b/src/interface/web/app/agents/agents.module.css index 8433a88f..73cbd7a3 100644 --- a/src/interface/web/app/agents/agents.module.css +++ b/src/interface/web/app/agents/agents.module.css @@ -46,7 +46,7 @@ div.agentList { } -@media only screen and (max-width: 700px) { +@media only screen and (max-width: 768px) { div.agentList { width: 100%; padding: 0; @@ -54,4 +54,9 @@ div.agentList { margin-left: auto; grid-template-columns: 1fr; } + + div.sidePanel { + position: relative; + height: 100%; + } } diff --git a/src/interface/web/app/agents/page.tsx b/src/interface/web/app/agents/page.tsx index 5e68a0b9..8a9f955a 100644 --- a/src/interface/web/app/agents/page.tsx +++ b/src/interface/web/app/agents/page.tsx @@ -266,9 +266,6 @@ export default function Agents() { return (
-
- -
{ showLoginPrompt &&
-
+

Agents

diff --git a/src/interface/web/app/automations/automations.module.css b/src/interface/web/app/automations/automations.module.css index 99f094c3..f0c35244 100644 --- a/src/interface/web/app/automations/automations.module.css +++ b/src/interface/web/app/automations/automations.module.css @@ -28,4 +28,9 @@ div.sidePanel { div.pageLayout { max-width: 90vw; } + + div.sidePanel { + position: relative; + height: 100%; + } } diff --git a/src/interface/web/app/automations/page.tsx b/src/interface/web/app/automations/page.tsx index 9416cee7..c933f8fd 100644 --- a/src/interface/web/app/automations/page.tsx +++ b/src/interface/web/app/automations/page.tsx @@ -936,9 +936,6 @@ export default function Automations() { return (
-
- -
-

Automations

+

Automations

{ authenticatedData ? ( diff --git a/src/interface/web/app/chat/page.tsx b/src/interface/web/app/chat/page.tsx index 1768a228..702dc894 100644 --- a/src/interface/web/app/chat/page.tsx +++ b/src/interface/web/app/chat/page.tsx @@ -196,7 +196,7 @@ export default function Chat() { } // Track context used for chat response. References are rendered at the end of the chat - ({context, onlineContext} = processMessageChunk(event, currentMessage, context, onlineContext)); + ({ context, onlineContext } = processMessageChunk(event, currentMessage, context, onlineContext)); setMessages([...messages]); } @@ -231,16 +231,34 @@ export default function Chat() { {title} -
- -
+ { + !isMobileWidth && +
+ +
+ }
- + { + isMobileWidth && +
+ +
+ }
+ { + !isMobileWidth && +
+ {title &&

{title}

} +
+ } }> (props.title); - const [isMobileWidth, setIsMobileWidth] = useState(false); const [darkMode, setDarkMode] = useState(false); const [initialLoadDone, setInitialLoadDone] = useState(false); useEffect(() => { setIsMobileWidth(window.innerWidth < 768); - if (props.title) { - setDisplayTitle(props.title); - } else if (!props.title) { - setDisplayTitle(undefined); - } - - }, [props.title]); - - useEffect(() => { window.addEventListener('resize', () => { setIsMobileWidth(window.innerWidth < 768); @@ -94,15 +75,6 @@ export default function NavMenu(props: NavMenuProps) { return (
-
- {displayTitle &&

{displayTitle}

} - { - !displayTitle && props.showLogo && - - - - } -
{ isMobileWidth ? diff --git a/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx b/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx index b02ea754..00e5e26c 100644 --- a/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx +++ b/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx @@ -76,6 +76,7 @@ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, import { modifyFileFilterForConversation } from "@/app/common/chatFunctions"; import { ScrollAreaScrollbar } from "@radix-ui/react-scroll-area"; import { KhojLogo, KhojLogoType } from "@/app/components/logo/khogLogo"; +import NavMenu from "../navMenu/navMenu"; // Define a fetcher function const fetcher = (url: string) => fetch(url).then((res) => res.json()); @@ -660,11 +661,8 @@ export default function SidePanel(props: SidePanelProps) { }, [chatSessions]); return ( -
+
- - {props.isMobileWidth && || } - { authenticatedData && props.isMobileWidth ? { @@ -672,7 +670,7 @@ export default function SidePanel(props: SidePanelProps) { setEnabled(open); } }> - + Sessions and Files @@ -698,15 +696,33 @@ export default function SidePanel(props: SidePanelProps) { : -
- - {enabled ? : } +
+ + - +
+ + {enabled ? : } + + +
+
+ +
} + { + props.isMobileWidth && + + + + } + { + props.isMobileWidth && + + }
{ authenticatedData && !props.isMobileWidth && enabled && diff --git a/src/interface/web/app/components/sidePanel/sidePanel.module.css b/src/interface/web/app/components/sidePanel/sidePanel.module.css index 77fddd13..e984eb6a 100644 --- a/src/interface/web/app/components/sidePanel/sidePanel.module.css +++ b/src/interface/web/app/components/sidePanel/sidePanel.module.css @@ -123,9 +123,9 @@ div.modalSessionsList div.session { @media screen and (max-width: 768px) { div.panel { - padding: 0.5rem; - position: fixed; - width: fit-content; + padding: 0.25rem; + /* position: fixed; */ + width: 100%; } div.expanded { diff --git a/src/interface/web/app/page.module.css b/src/interface/web/app/page.module.css index 32c89473..0f02c77a 100644 --- a/src/interface/web/app/page.module.css +++ b/src/interface/web/app/page.module.css @@ -78,7 +78,13 @@ div.chatBoxBody { display: grid; height: 100%; margin: auto; - grid-template-rows: auto 1fr; +} + +div.homeGreetings { + display: grid; + height: 100%; + margin: auto; + grid-template-rows: 1fr 2fr; } @@ -108,6 +114,10 @@ div.sidePanel { grid-template-rows: auto; } + div.sidePanel { + position: relative; + } + div.chatBox { padding: 0; } @@ -117,4 +127,8 @@ div.sidePanel { grid-template-columns: 1fr; } + div.homeGreetings { + grid-template-rows: auto; + } + } diff --git a/src/interface/web/app/page.tsx b/src/interface/web/app/page.tsx index aef9f58c..f8d0fe6d 100644 --- a/src/interface/web/app/page.tsx +++ b/src/interface/web/app/page.tsx @@ -3,7 +3,7 @@ import './globals.css'; import styles from './page.module.css'; import 'katex/dist/katex.min.css'; -import React, { use, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import useSWR from 'swr'; import Image from 'next/image'; import { ClockCounterClockwise } from '@phosphor-icons/react'; @@ -11,7 +11,6 @@ import { ClockCounterClockwise } from '@phosphor-icons/react'; import { Card, CardTitle } from '@/components/ui/card'; import SuggestionCard from '@/app/components/suggestions/suggestionCard'; import SidePanel from '@/app/components/sidePanel/chatHistorySidePanel'; -import NavMenu from '@/app/components/navMenu/navMenu'; import Loading from '@/app/components/loading/loading'; import ChatInputArea, { ChatOptions } from '@/app/components/chatInputArea/chatInputArea'; import { Suggestion, suggestionsData } from '@/app/components/suggestions/suggestionsData'; @@ -22,7 +21,6 @@ import { getIconFromIconName } from '@/app/common/iconUtils'; import { AgentData } from '@/app/agents/page'; - interface ChatBodyDataProps { chatOptionsData: ChatOptions | null; onConversationIdChange?: (conversationId: string) => void; @@ -164,8 +162,8 @@ function ChatBodyData(props: ChatBodyDataProps) { } return ( -
-
+
+

{greeting}

@@ -225,7 +223,7 @@ function ChatBodyData(props: ChatBodyDataProps) {
@@ -324,7 +322,6 @@ export default function Home() { />
-
-
+
+
-
- -
- { - isMobileWidth &&
Search
- } -
- - setSearchQuery(e.currentTarget.value)} - onKeyDown={(e) => e.key === 'Enter' && search()} - type="search" - placeholder="Search Documents" /> - -
- { - focusSearchResult && -
- - {focusNote(focusSearchResult)} +
+
+
+
+ + setSearchQuery(e.currentTarget.value)} + onKeyDown={(e) => e.key === 'Enter' && search()} + type="search" + placeholder="Search Documents" /> +
- } - { - !focusSearchResult && searchResults && searchResults.length > 0 && -
- - { - searchResults.map((result, index) => { - return ( - - ); - }) - } - -
- } - { - searchResults == null && - - - - - - - Search across your documents - - - - {exampleQuery} - - - } - { - searchResults && searchResults.length === 0 && - - - - - - - No documents found - - - -
- To use search, upload your docs to your account. -
- -
- Learn More + { + focusSearchResult && +
+ + {focusNote(focusSearchResult)} +
+ } + { + !focusSearchResult && searchResults && searchResults.length > 0 && +
+ + { + searchResults.map((result, index) => { + return ( + + ); + }) + } + +
+ } + { + searchResults == null && + + + + + + + Search across your documents + + + + {exampleQuery} + + + } + { + searchResults && searchResults.length === 0 && + + + + + + + No documents found + + + +
+ To use search, upload your docs to your account.
- -
-
- } + +
+ Learn More +
+ + + + } +
diff --git a/src/interface/web/app/search/search.module.css b/src/interface/web/app/search/search.module.css index b063fde6..f4f24a46 100644 --- a/src/interface/web/app/search/search.module.css +++ b/src/interface/web/app/search/search.module.css @@ -1,12 +1,22 @@ div.searchLayout { display: grid; - grid-template-columns: auto 1fr; + grid-template-columns: 1fr; gap: 1rem; height: 100vh; } +div.sidePanel { + position: fixed; + height: 100%; +} + @media screen and (max-width: 768px) { div.searchLayout { gap: 0; } + + div.sidePanel { + position: relative; + height: 100%; + } } From 1bb746aaed7c94f98a941b4e6fd0e3207e7daca2 Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 2 Aug 2024 17:49:50 +0530 Subject: [PATCH 04/12] Adjust spacing when side panel is opened --- .../web/app/components/sidePanel/chatHistorySidePanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx b/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx index 00e5e26c..eb2c81ae 100644 --- a/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx +++ b/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx @@ -696,7 +696,7 @@ export default function SidePanel(props: SidePanelProps) { : -
+
From 5f8b76c8f2b86e6d1b467c773aa5006782c06382 Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 2 Aug 2024 19:06:01 +0530 Subject: [PATCH 05/12] Fix layout/styling of the factchecker app --- .../app/factchecker/factChecker.module.css | 11 + .../factchecker/factCheckerLayout.module.css | 10 - src/interface/web/app/factchecker/layout.tsx | 6 +- src/interface/web/app/factchecker/page.tsx | 254 +++++++++--------- 4 files changed, 144 insertions(+), 137 deletions(-) delete mode 100644 src/interface/web/app/factchecker/factCheckerLayout.module.css diff --git a/src/interface/web/app/factchecker/factChecker.module.css b/src/interface/web/app/factchecker/factChecker.module.css index c504d4d6..9882b2a3 100644 --- a/src/interface/web/app/factchecker/factChecker.module.css +++ b/src/interface/web/app/factchecker/factChecker.module.css @@ -13,6 +13,11 @@ input.factVerification { font-size: large; } +div.factCheckerContainer { + width: 75vw; + margin: auto; +} + input.factVerification:focus { outline: none; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); @@ -123,6 +128,12 @@ div.dot2 { animation-delay: -1.0s; } +@media screen and (max-width: 768px) { + div.factCheckerContainer { + width: 95vw; + } +} + @-webkit-keyframes sk-rotate { 100% { -webkit-transform: rotate(360deg) diff --git a/src/interface/web/app/factchecker/factCheckerLayout.module.css b/src/interface/web/app/factchecker/factCheckerLayout.module.css deleted file mode 100644 index 1664029d..00000000 --- a/src/interface/web/app/factchecker/factCheckerLayout.module.css +++ /dev/null @@ -1,10 +0,0 @@ -.factCheckerLayout { - max-width: 70vw; - margin: auto; -} - -@media screen and (max-width: 700px) { - .factCheckerLayout { - max-width: 90vw; - } -} diff --git a/src/interface/web/app/factchecker/layout.tsx b/src/interface/web/app/factchecker/layout.tsx index 8726b59b..5c4e136e 100644 --- a/src/interface/web/app/factchecker/layout.tsx +++ b/src/interface/web/app/factchecker/layout.tsx @@ -1,7 +1,4 @@ - import type { Metadata } from "next"; -import NavMenu from '../components/navMenu/navMenu'; -import styles from './factCheckerLayout.module.css'; export const metadata: Metadata = { title: "Khoj AI - Fact Checker", @@ -17,8 +14,7 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( -
- +
{children}
); diff --git a/src/interface/web/app/factchecker/page.tsx b/src/interface/web/app/factchecker/page.tsx index bb6e5af2..3d6e4882 100644 --- a/src/interface/web/app/factchecker/page.tsx +++ b/src/interface/web/app/factchecker/page.tsx @@ -19,6 +19,7 @@ import { CardTitle, } from "@/components/ui/card" import Link from 'next/link'; +import SidePanel from '../components/sidePanel/chatHistorySidePanel'; @@ -76,41 +77,41 @@ async function verifyStatement( setIsLoading: (loading: boolean) => void, setInitialResponse: (response: string) => void, setInitialReferences: (references: ResponseWithReferences) => void) { - setIsLoading(true); - // Send a message to the chat server to verify the fact - let verificationMessage = `${verificationPrecursor} ${message}`; - const apiURL = `${chatURL}?q=${encodeURIComponent(verificationMessage)}&client=web&stream=true&conversation_id=${conversationId}`; - try { - const response = await fetch(apiURL); - if (!response.body) throw new Error("No response body found"); + setIsLoading(true); + // Send a message to the chat server to verify the fact + let verificationMessage = `${verificationPrecursor} ${message}`; + const apiURL = `${chatURL}?q=${encodeURIComponent(verificationMessage)}&client=web&stream=true&conversation_id=${conversationId}`; + try { + const response = await fetch(apiURL); + if (!response.body) throw new Error("No response body found"); - const reader = response.body?.getReader(); - let decoder = new TextDecoder(); - let result = ""; + const reader = response.body?.getReader(); + let decoder = new TextDecoder(); + let result = ""; - while (true) { - const { done, value } = await reader.read(); - if (done) break; + while (true) { + const { done, value } = await reader.read(); + if (done) break; - let chunk = decoder.decode(value, { stream: true }); + let chunk = decoder.decode(value, { stream: true }); - if (chunk.includes("### compiled references:")) { - const references = handleCompiledReferences(chunk, result); - if (references.response) { - result = references.response; - setInitialResponse(references.response); - setInitialReferences(references); - } - } else { - result += chunk; - setInitialResponse(result); + if (chunk.includes("### compiled references:")) { + const references = handleCompiledReferences(chunk, result); + if (references.response) { + result = references.response; + setInitialResponse(references.response); + setInitialReferences(references); } + } else { + result += chunk; + setInitialResponse(result); } - } catch (error) { - console.error("Error verifying statement: ", error); - } finally { - setIsLoading(false); } + } catch (error) { + console.error("Error verifying statement: ", error); + } finally { + setIsLoading(false); + } } @@ -145,7 +146,7 @@ function ReferenceVerification(props: ReferenceVerificationProps) { setInitialResponse(props.prefilledResponse); setIsLoading(false); } else { - verifyStatement(verificationStatement, props.conversationId, setIsLoading, setInitialResponse, () => {}); + verifyStatement(verificationStatement, props.conversationId, setIsLoading, setInitialResponse, () => { }); } setIsMobileWidth(window.innerWidth < 768); @@ -454,7 +455,7 @@ export default function FactChecker() { additionalLink={additionalLink} setChildReferencesCallback={setChildReferencesCallback} /> ); - }).filter(Boolean); + }).filter(Boolean); }; const renderSupplementalReferences = (references: SupplementReferences[]) => { @@ -489,102 +490,111 @@ export default function FactChecker() { } return ( -
-

- AI Fact Checker -

-
- This is an experimental AI tool. It may make mistakes. -
- { - initialResponse && initialReferences && childReferences - ? -
- - {} : storeData} /> -
- :
-
- setFactToVerify(e.target.value)} - value={factToVerify} - onKeyDown={(e) => { - if (e.key === "Enter") { - onClickVerify(); - } - }} - onFocus={(e) => e.target.placeholder = ""} - onBlur={(e) => e.target.placeholder = "Enter a falsifiable statement to verify"} /> - -
-

- Try with a particular model. You must be subscribed to configure the model. -

-
- } - - {isLoading &&
+ <> +
+ +
+
+

+ AI Fact Checker +

+
+ This is an experimental AI tool. It may make mistakes. +
+ { + initialResponse && initialReferences && childReferences + ? +
+ + { } : storeData} /> +
+ :
+
+ setFactToVerify(e.target.value)} + value={factToVerify} + onKeyDown={(e) => { + if (e.key === "Enter") { + onClickVerify(); + } + }} + onFocus={(e) => e.target.placeholder = ""} + onBlur={(e) => e.target.placeholder = "Enter a falsifiable statement to verify"} /> + +
+

+ Try with a particular model. You must be subscribed to configure the model. +

+
+ } + + {isLoading &&
} - { - initialResponse && - - - {officialFactToVerify} - - -
- - -
-
- - { - initialReferences && initialReferences.online && Object.keys(initialReferences.online).length > 0 && ( -
+ { + initialResponse && + + + {officialFactToVerify} + + +
+ { - const webpages = onlineData?.webpages || []; - return renderWebpages(webpages); - }) + automationId: "", + by: "AI", + message: initialResponse, + context: [], + created: (new Date()).toISOString(), + onlineContext: {} } -
- )} - -
- } - { - initialReferences && -
-

Supplements

-
- { initialReferences.online !== undefined && - renderReferences(conversationID, initialReferences, officialFactToVerify, loadedFromStorage, childReferences)} + } isMobileWidth={isMobileWidth} /> + +
+ + + { + initialReferences && initialReferences.online && Object.keys(initialReferences.online).length > 0 && ( +
+ { + Object.entries(initialReferences.online).map(([key, onlineData], index) => { + const webpages = onlineData?.webpages || []; + return renderWebpages(webpages); + }) + } +
+ )} +
+ + } + { + initialReferences && +
+

Supplements

+
+ {initialReferences.online !== undefined && + renderReferences(conversationID, initialReferences, officialFactToVerify, loadedFromStorage, childReferences)} +
-
- } -
+ } +
+ ) } From b1d3979ed9afc3f2bb77de2113fac843927220e7 Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 2 Aug 2024 19:28:59 +0530 Subject: [PATCH 06/12] Fix navmenu in settings, share/chat pages --- src/interface/web/app/settings/page.tsx | 1 - src/interface/web/app/share/chat/page.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/interface/web/app/settings/page.tsx b/src/interface/web/app/settings/page.tsx index 59d28b6b..d9320703 100644 --- a/src/interface/web/app/settings/page.tsx +++ b/src/interface/web/app/settings/page.tsx @@ -655,7 +655,6 @@ export default function SettingsView() { />
-
}>
diff --git a/src/interface/web/app/share/chat/page.tsx b/src/interface/web/app/share/chat/page.tsx index c763248b..cd3c4eb7 100644 --- a/src/interface/web/app/share/chat/page.tsx +++ b/src/interface/web/app/share/chat/page.tsx @@ -280,7 +280,6 @@ export default function SharedChat() {
-
}> Date: Fri, 2 Aug 2024 19:44:30 +0530 Subject: [PATCH 07/12] Use new nav menu alignment in the settings page --- src/interface/web/app/search/page.tsx | 2 +- src/interface/web/app/settings/page.tsx | 4 ++-- .../web/app/settings/settings.module.css | 22 ++++++++++++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/interface/web/app/search/page.tsx b/src/interface/web/app/search/page.tsx index 88d54ca4..61e726ac 100644 --- a/src/interface/web/app/search/page.tsx +++ b/src/interface/web/app/search/page.tsx @@ -222,7 +222,7 @@ export default function Search() {
- + ; return ( -
+
{title} @@ -655,7 +655,7 @@ export default function SettingsView() { />
-
+
}>
diff --git a/src/interface/web/app/settings/settings.module.css b/src/interface/web/app/settings/settings.module.css index 8c5ec535..118dc3b1 100644 --- a/src/interface/web/app/settings/settings.module.css +++ b/src/interface/web/app/settings/settings.module.css @@ -1,6 +1,6 @@ div.page { display: grid; - grid-template-columns: auto 1fr; + grid-template-columns: 1fr; gap: 1rem; height: 100vh; color: hsla(var(--foreground)); @@ -8,15 +8,35 @@ div.page { div.contentBody { display: grid; margin: auto; + margin-left: 20vw; + margin-top: 1rem; } div.phoneInput { padding: 0rem; } +div.sidePanel { + position: fixed; + height: 100%; +} + div.phoneInput input { width: 100%; padding: 0.5rem; border: 1px solid hsla(var(--border)); border-radius: 0.25rem; } + +@media screen and (max-width: 768px) { + + div.sidePanel { + position: relative; + height: 100%; + } + + div.contentBody { + margin-left: 0; + margin-top: 0; + } +} From f18839639546e681f667bfbf7e27a56179a9a08c Mon Sep 17 00:00:00 2001 From: sabaimran Date: Fri, 2 Aug 2024 20:12:18 +0530 Subject: [PATCH 08/12] Prompt to login when authenticated, click on suggestion card - Improve styling for the side panel when not logged in --- .../sidePanel/chatHistorySidePanel.tsx | 4 +--- src/interface/web/app/page.tsx | 22 ++++++++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx b/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx index eb2c81ae..7322c12e 100644 --- a/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx +++ b/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx @@ -742,10 +742,8 @@ export default function SidePanel(props: SidePanelProps) { { !authenticatedData && enabled &&
- + - - {/* Redirect to login page */} diff --git a/src/interface/web/app/page.tsx b/src/interface/web/app/page.tsx index f8d0fe6d..50701efd 100644 --- a/src/interface/web/app/page.tsx +++ b/src/interface/web/app/page.tsx @@ -14,6 +14,7 @@ import SidePanel from '@/app/components/sidePanel/chatHistorySidePanel'; import Loading from '@/app/components/loading/loading'; import ChatInputArea, { ChatOptions } from '@/app/components/chatInputArea/chatInputArea'; import { Suggestion, suggestionsData } from '@/app/components/suggestions/suggestionsData'; +import LoginPrompt from '@/app/components/loginPrompt/loginPrompt'; import { useAuthenticatedData, UserConfig, useUserConfig } from '@/app/common/auth'; import { convertColorToBorderClass } from '@/app/common/colorUtils'; @@ -53,6 +54,7 @@ function ChatBodyData(props: ChatBodyDataProps) { const [selectedAgent, setSelectedAgent] = useState("khoj"); const [agentIcons, setAgentIcons] = useState([]); const [agents, setAgents] = useState([]); + const [showLoginPrompt, setShowLoginPrompt] = useState(false); const agentsFetcher = () => window.fetch('/api/agents').then(res => res.json()).catch(err => console.log(err)); const { data: agentsData, error } = useSWR('agents', agentsFetcher, { revalidateOnFocus: false }); @@ -163,6 +165,13 @@ function ChatBodyData(props: ChatBodyDataProps) { return (
+ { + showLoginPrompt && ( + + ) + }

{greeting}

@@ -208,7 +217,15 @@ function ChatBodyData(props: ChatBodyDataProps) { {shuffledOptions.map((suggestion, index) => (
fillArea(suggestion.link, suggestion.type, suggestion.description)}> + onClick={(event) => { + if (props.isLoggedIn) { + fillArea(suggestion.link, suggestion.type, suggestion.description); + } else { + event.preventDefault(); + event.stopPropagation(); + setShowLoginPrompt(true); + } + }}> (null); const [isLoading, setLoading] = useState(true); - const [title, setTitle] = useState(''); const [conversationId, setConversationID] = useState(null); const [uploadedFiles, setUploadedFiles] = useState([]); const [isMobileWidth, setIsMobileWidth] = useState(false); @@ -312,7 +328,7 @@ export default function Home() { return (
- {title} + Khoj AI - Your Second Brain
Date: Sat, 3 Aug 2024 04:00:36 +0530 Subject: [PATCH 09/12] Improve alignment of title bar elements --- src/interface/web/app/components/logo/khogLogo.tsx | 2 +- .../web/app/components/sidePanel/chatHistorySidePanel.tsx | 8 ++++---- .../web/app/components/sidePanel/sidePanel.module.css | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/interface/web/app/components/logo/khogLogo.tsx b/src/interface/web/app/components/logo/khogLogo.tsx index c2cdc8c1..fcdeb60c 100644 --- a/src/interface/web/app/components/logo/khogLogo.tsx +++ b/src/interface/web/app/components/logo/khogLogo.tsx @@ -1,6 +1,6 @@ export function KhojLogoType() { return ( - + diff --git a/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx b/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx index 7322c12e..f21c6056 100644 --- a/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx +++ b/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx @@ -696,19 +696,19 @@ export default function SidePanel(props: SidePanelProps) { : -
+
-
- +
+ {enabled ? : }
-
+
diff --git a/src/interface/web/app/components/sidePanel/sidePanel.module.css b/src/interface/web/app/components/sidePanel/sidePanel.module.css index e984eb6a..74d5358e 100644 --- a/src/interface/web/app/components/sidePanel/sidePanel.module.css +++ b/src/interface/web/app/components/sidePanel/sidePanel.module.css @@ -143,7 +143,8 @@ div.modalSessionsList div.session { div.session.compressed { max-width: 100%; - grid-template-columns: minmax(auto, 300px) 1fr; + display: flex; + justify-content: space-between; } div.session { From a6e1b2c7cbe6f46d8b86482af0bce8865d68368c Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Sat, 3 Aug 2024 04:12:05 +0530 Subject: [PATCH 10/12] Style nav menu button and expand nav menu item click area to full-width Style profile pircture button on nav menu - Use primary colored ring around subscribed user profile on nav menu - Use gray colored ring around non-subscribed user profile on nav menu - Use upper case initial as profile pic for user with no profile pic - Click anywhere on nav menu item to trigger action Previously the actual clickable area was smaller than the width of the nav menu item --- .../web/app/components/navMenu/navMenu.tsx | 56 ++++++++----------- .../components/suggestions/suggestionCard.tsx | 2 +- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/interface/web/app/components/navMenu/navMenu.tsx b/src/interface/web/app/components/navMenu/navMenu.tsx index 859b01a8..c119596a 100644 --- a/src/interface/web/app/components/navMenu/navMenu.tsx +++ b/src/interface/web/app/components/navMenu/navMenu.tsx @@ -81,30 +81,26 @@ export default function NavMenu() { { userData ? - + - - {userData?.username[0]} + + {userData?.username[0].toUpperCase()} : - + } - + setDarkMode(!darkMode)} className='w-full cursor-pointer'>
{ - setDarkMode(!darkMode) - } - } className="flex flex-rows"> {darkMode ? : }

{darkMode ? 'Light Mode' : 'Dark Mode'}

- +

Agents

@@ -112,7 +108,7 @@ export default function NavMenu() { - +

Automations

@@ -120,7 +116,7 @@ export default function NavMenu() { - +

Search

@@ -131,7 +127,7 @@ export default function NavMenu() { {userData && - +

Settings

@@ -140,7 +136,7 @@ export default function NavMenu() { } - +

Help

@@ -150,7 +146,7 @@ export default function NavMenu() { { userData ? - +

Logout

@@ -159,7 +155,7 @@ export default function NavMenu() { : - +

Login

@@ -171,15 +167,15 @@ export default function NavMenu() { : - + { userData ? - + - - {userData?.username[0]} + + {userData?.username[0].toUpperCase()} : @@ -187,19 +183,15 @@ export default function NavMenu() { } - + setDarkMode(!darkMode)} className="w-full hover:cursor-pointer">
{ - setDarkMode(!darkMode) - } - } className="flex flex-rows"> {darkMode ? : }

{darkMode ? 'Light Mode' : 'Dark Mode'}

- +

Agents

@@ -207,7 +199,7 @@ export default function NavMenu() { - +

Automations

@@ -215,7 +207,7 @@ export default function NavMenu() { - +

Search

@@ -225,7 +217,7 @@ export default function NavMenu() { <> - +

Help

@@ -235,7 +227,7 @@ export default function NavMenu() { { userData && - +

Settings

@@ -246,7 +238,7 @@ export default function NavMenu() { { userData ? - +

Logout

@@ -255,7 +247,7 @@ export default function NavMenu() { : - +

Login

diff --git a/src/interface/web/app/components/suggestions/suggestionCard.tsx b/src/interface/web/app/components/suggestions/suggestionCard.tsx index b2b90ac3..8d58c803 100644 --- a/src/interface/web/app/components/suggestions/suggestionCard.tsx +++ b/src/interface/web/app/components/suggestions/suggestionCard.tsx @@ -38,7 +38,7 @@ interface SuggestionCardProps { export default function SuggestionCard(data: SuggestionCardProps) { const bgColors = converColorToBgGradient(data.color); - const cardClassName = `${styles.card} ${bgColors} md:w-full md:h-fit sm:w-full sm:h-fit lg:w-[200px] lg:h-[200px]`; + const cardClassName = `${styles.card} ${bgColors} md:w-full md:h-fit sm:w-full sm:h-fit lg:w-[200px] lg:h-[200px] cursor-pointer`; const titleClassName = `${styles.title} pt-2 dark:text-white dark:font-bold`; const descriptionClassName = `${styles.text} dark:text-white`; From f3765a20b996f71654bdcc0173c248464653decd Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Sat, 3 Aug 2024 07:05:15 +0530 Subject: [PATCH 11/12] Improve content alignment on automation page for small screens - Left align email, location, timezone pills on small screens - Indent user enabled automations to improve delineation between sections --- src/interface/web/app/automations/page.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interface/web/app/automations/page.tsx b/src/interface/web/app/automations/page.tsx index c933f8fd..251c3500 100644 --- a/src/interface/web/app/automations/page.tsx +++ b/src/interface/web/app/automations/page.tsx @@ -447,7 +447,7 @@ function SharedAutomationCard(props: SharedAutomationCardProps) { - + Create Automation @@ -946,8 +946,8 @@ export default function Automations() {
-

Automations

-
+

Automations

+
{ authenticatedData ? ( {authenticatedData.email} @@ -1035,7 +1035,7 @@ export default function Automations() { { ((!personalAutomations || personalAutomations.length === 0) && (allNewAutomations.length == 0) && !isLoading) && ( -
+
So empty! Create your own automation to get started.
{ From d8fe677933304337e0da586206472799d1299817 Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Sat, 3 Aug 2024 07:07:35 +0530 Subject: [PATCH 12/12] Prevent overflow on Search page by search results --- src/interface/web/app/search/page.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/interface/web/app/search/page.tsx b/src/interface/web/app/search/page.tsx index 61e726ac..33b613d8 100644 --- a/src/interface/web/app/search/page.tsx +++ b/src/interface/web/app/search/page.tsx @@ -159,13 +159,7 @@ export default function Search() { }, [isMobileWidth]); function search() { - if (searchResultsLoading) { - return; - } - - if (!searchQuery.trim()) { - return; - } + if (searchResultsLoading || !searchQuery.trim()) return; const apiUrl = `/api/search?q=${encodeURIComponent(searchQuery)}&client=web`; fetch(apiUrl, { @@ -246,7 +240,7 @@ export default function Search() { } { !focusSearchResult && searchResults && searchResults.length > 0 && -
+
{ searchResults.map((result, index) => {