mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-07 21:29:13 +00:00
Use the new shadcn sidebar for khoj nav and actions
- Use the sidebar across all pages to quickly navigate through the app, access settings, and go to past chats - Pending: mobile friendliness
This commit is contained in:
@@ -19,7 +19,6 @@ import { z } from "zod";
|
|||||||
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "@/components/ui/dialog";
|
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "@/components/ui/dialog";
|
||||||
import LoginPrompt from "../components/loginPrompt/loginPrompt";
|
import LoginPrompt from "../components/loginPrompt/loginPrompt";
|
||||||
import { InlineLoading } from "../components/loading/loading";
|
import { InlineLoading } from "../components/loading/loading";
|
||||||
import SidePanel from "../components/sidePanel/chatHistorySidePanel";
|
|
||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import { useIsMobileWidth } from "../common/utils";
|
import { useIsMobileWidth } from "../common/utils";
|
||||||
import {
|
import {
|
||||||
@@ -30,6 +29,8 @@ import {
|
|||||||
|
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
|
||||||
|
import { AppSidebar } from "../components/appSidebar/appSidebar";
|
||||||
|
|
||||||
export interface AgentData {
|
export interface AgentData {
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -276,15 +277,11 @@ export default function Agents() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SidebarProvider>
|
||||||
|
<AppSidebar conversationId={""} />
|
||||||
|
<SidebarTrigger />
|
||||||
<main className={`w-full mx-auto`}>
|
<main className={`w-full mx-auto`}>
|
||||||
<div className={`grid w-full mx-auto`}>
|
<div className={`grid w-full mx-auto`}>
|
||||||
<div className={`${styles.sidePanel} top-0`}>
|
|
||||||
<SidePanel
|
|
||||||
conversationId={null}
|
|
||||||
uploadedFiles={[]}
|
|
||||||
isMobileWidth={isMobileWidth}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={`${styles.pageLayout} w-full`}>
|
<div className={`${styles.pageLayout} w-full`}>
|
||||||
<div className={`pt-6 md:pt-8 flex justify-between`}>
|
<div className={`pt-6 md:pt-8 flex justify-between`}>
|
||||||
<h1 className="text-3xl flex items-center">Agents</h1>
|
<h1 className="text-3xl flex items-center">Agents</h1>
|
||||||
@@ -310,7 +307,9 @@ export default function Agents() {
|
|||||||
isSubscribed={isSubscribed}
|
isSubscribed={isSubscribed}
|
||||||
setAgentChangeTriggered={setAgentChangeTriggered}
|
setAgentChangeTriggered={setAgentChangeTriggered}
|
||||||
inputToolOptions={agentConfigurationOptions?.input_tools || {}}
|
inputToolOptions={agentConfigurationOptions?.input_tools || {}}
|
||||||
outputModeOptions={agentConfigurationOptions?.output_modes || {}}
|
outputModeOptions={
|
||||||
|
agentConfigurationOptions?.output_modes || {}
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -323,7 +322,10 @@ export default function Agents() {
|
|||||||
)}
|
)}
|
||||||
<Alert className="bg-secondary border-none my-4">
|
<Alert className="bg-secondary border-none my-4">
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
<Lightning weight={"fill"} className="h-4 w-4 text-purple-400 inline" />
|
<Lightning
|
||||||
|
weight={"fill"}
|
||||||
|
className="h-4 w-4 text-purple-400 inline"
|
||||||
|
/>
|
||||||
<span className="font-bold">How it works</span> Use any of these
|
<span className="font-bold">How it works</span> Use any of these
|
||||||
specialized personas to tune your conversation to your needs.
|
specialized personas to tune your conversation to your needs.
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
@@ -343,7 +345,9 @@ export default function Agents() {
|
|||||||
modelOptions={userConfig?.chat_model_options || []}
|
modelOptions={userConfig?.chat_model_options || []}
|
||||||
editCard={true}
|
editCard={true}
|
||||||
agentSlug={agentSlug || ""}
|
agentSlug={agentSlug || ""}
|
||||||
inputToolOptions={agentConfigurationOptions?.input_tools || {}}
|
inputToolOptions={
|
||||||
|
agentConfigurationOptions?.input_tools || {}
|
||||||
|
}
|
||||||
outputModeOptions={
|
outputModeOptions={
|
||||||
agentConfigurationOptions?.output_modes || {}
|
agentConfigurationOptions?.output_modes || {}
|
||||||
}
|
}
|
||||||
@@ -367,7 +371,9 @@ export default function Agents() {
|
|||||||
setAgentChangeTriggered={setAgentChangeTriggered}
|
setAgentChangeTriggered={setAgentChangeTriggered}
|
||||||
modelOptions={userConfig?.chat_model_options || []}
|
modelOptions={userConfig?.chat_model_options || []}
|
||||||
agentSlug={agentSlug || ""}
|
agentSlug={agentSlug || ""}
|
||||||
inputToolOptions={agentConfigurationOptions?.input_tools || {}}
|
inputToolOptions={
|
||||||
|
agentConfigurationOptions?.input_tools || {}
|
||||||
|
}
|
||||||
outputModeOptions={
|
outputModeOptions={
|
||||||
agentConfigurationOptions?.output_modes || {}
|
agentConfigurationOptions?.output_modes || {}
|
||||||
}
|
}
|
||||||
@@ -378,5 +384,6 @@ export default function Agents() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
</SidebarProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,8 +66,9 @@ import LoginPrompt from "../components/loginPrompt/loginPrompt";
|
|||||||
import { useToast } from "@/components/ui/use-toast";
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
import { ToastAction } from "@/components/ui/toast";
|
import { ToastAction } from "@/components/ui/toast";
|
||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import SidePanel from "../components/sidePanel/chatHistorySidePanel";
|
|
||||||
import { Drawer, DrawerContent, DrawerTitle, DrawerTrigger } from "@/components/ui/drawer";
|
import { Drawer, DrawerContent, DrawerTitle, DrawerTrigger } from "@/components/ui/drawer";
|
||||||
|
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
|
||||||
|
import { AppSidebar } from "../components/appSidebar/appSidebar";
|
||||||
|
|
||||||
const automationsFetcher = () =>
|
const automationsFetcher = () =>
|
||||||
window
|
window
|
||||||
@@ -1023,15 +1024,11 @@ export default function Automations() {
|
|||||||
return <InlineLoading message="Oops, something went wrong. Please refresh the page." />;
|
return <InlineLoading message="Oops, something went wrong. Please refresh the page." />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SidebarProvider>
|
||||||
|
<AppSidebar conversationId={""} />
|
||||||
|
<SidebarTrigger />
|
||||||
<main className={`w-full mx-auto`}>
|
<main className={`w-full mx-auto`}>
|
||||||
<div className={`grid w-full mx-auto`}>
|
<div className={`grid w-full mx-auto`}>
|
||||||
<div className={`${styles.sidePanel} top-0`}>
|
|
||||||
<SidePanel
|
|
||||||
conversationId={null}
|
|
||||||
uploadedFiles={[]}
|
|
||||||
isMobileWidth={isMobileWidth}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={`${styles.pageLayout} w-full`}>
|
<div className={`${styles.pageLayout} w-full`}>
|
||||||
<div className="pt-6 md:pt-8 grid gap-1 md:flex md:justify-between">
|
<div className="pt-6 md:pt-8 grid gap-1 md:flex md:justify-between">
|
||||||
<h1 className="text-3xl flex items-center">Automations</h1>
|
<h1 className="text-3xl flex items-center">Automations</h1>
|
||||||
@@ -1060,14 +1057,19 @@ export default function Automations() {
|
|||||||
</div>
|
</div>
|
||||||
{showLoginPrompt && (
|
{showLoginPrompt && (
|
||||||
<LoginPrompt
|
<LoginPrompt
|
||||||
loginRedirectMessage={"Create an account to make your own automation"}
|
loginRedirectMessage={
|
||||||
|
"Create an account to make your own automation"
|
||||||
|
}
|
||||||
onOpenChange={setShowLoginPrompt}
|
onOpenChange={setShowLoginPrompt}
|
||||||
isMobileWidth={isMobileWidth}
|
isMobileWidth={isMobileWidth}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Alert className="bg-secondary border-none my-4">
|
<Alert className="bg-secondary border-none my-4">
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
<Lightning weight={"fill"} className="h-4 w-4 text-purple-400 inline" />
|
<Lightning
|
||||||
|
weight={"fill"}
|
||||||
|
className="h-4 w-4 text-purple-400 inline"
|
||||||
|
/>
|
||||||
<span className="font-bold">How it works</span> Automations help you
|
<span className="font-bold">How it works</span> Automations help you
|
||||||
structure your time by automating tasks you do regularly. Build your
|
structure your time by automating tasks you do regularly. Build your
|
||||||
own, or try out our presets. Get results straight to your inbox.
|
own, or try out our presets. Get results straight to your inbox.
|
||||||
@@ -1152,5 +1154,6 @@ export default function Automations() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
</SidebarProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
div.main {
|
div.main {
|
||||||
height: 100dvh;
|
height: 100dvh;
|
||||||
color: hsla(var(--foreground));
|
color: hsla(var(--foreground));
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.suggestions {
|
.suggestions {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
import styles from "./chat.module.css";
|
import styles from "./chat.module.css";
|
||||||
import React, { Suspense, useEffect, useRef, useState } from "react";
|
import React, { Suspense, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import SidePanel, { ChatSessionActionMenu } from "../components/sidePanel/chatHistorySidePanel";
|
|
||||||
import ChatHistory from "../components/chatHistory/chatHistory";
|
import ChatHistory from "../components/chatHistory/chatHistory";
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useSearchParams } from "next/navigation";
|
||||||
import Loading from "../components/loading/loading";
|
import Loading from "../components/loading/loading";
|
||||||
@@ -26,6 +25,9 @@ import {
|
|||||||
} from "../components/chatInputArea/chatInputArea";
|
} from "../components/chatInputArea/chatInputArea";
|
||||||
import { useAuthenticatedData } from "../common/auth";
|
import { useAuthenticatedData } from "../common/auth";
|
||||||
import { AgentData } from "../agents/page";
|
import { AgentData } from "../agents/page";
|
||||||
|
import { ChatSessionActionMenu } from "../components/allConversations/allConversations";
|
||||||
|
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
|
||||||
|
import { AppSidebar } from "../components/appSidebar/appSidebar";
|
||||||
|
|
||||||
interface ChatBodyDataProps {
|
interface ChatBodyDataProps {
|
||||||
chatOptionsData: ChatOptions | null;
|
chatOptionsData: ChatOptions | null;
|
||||||
@@ -382,17 +384,13 @@ export default function Chat() {
|
|||||||
if (isLoading) return <Loading />;
|
if (isLoading) return <Loading />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SidebarProvider>
|
||||||
|
<AppSidebar conversationId={conversationId || ""} />
|
||||||
|
<SidebarTrigger />
|
||||||
<div className={`${styles.main} ${styles.chatLayout}`}>
|
<div className={`${styles.main} ${styles.chatLayout}`}>
|
||||||
<title>
|
<title>
|
||||||
{`${defaultTitle}${!!title && title !== defaultTitle ? `: ${title}` : ""}`}
|
{`${defaultTitle}${!!title && title !== defaultTitle ? `: ${title}` : ""}`}
|
||||||
</title>
|
</title>
|
||||||
<div className={isMobileWidth ? "h-1" : "h-auto"}>
|
|
||||||
<SidePanel
|
|
||||||
conversationId={conversationId}
|
|
||||||
uploadedFiles={[]}
|
|
||||||
isMobileWidth={isMobileWidth}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={styles.chatBox}>
|
<div className={styles.chatBox}>
|
||||||
<div className={styles.chatBoxBody}>
|
<div className={styles.chatBoxBody}>
|
||||||
{conversationId && (
|
{conversationId && (
|
||||||
@@ -431,5 +429,6 @@ export default function Chat() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</SidebarProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import {
|
|||||||
NotePencil,
|
NotePencil,
|
||||||
FunnelSimple,
|
FunnelSimple,
|
||||||
MagnifyingGlass,
|
MagnifyingGlass,
|
||||||
|
ChatsCircle,
|
||||||
} from "@phosphor-icons/react";
|
} from "@phosphor-icons/react";
|
||||||
|
|
||||||
interface ChatHistory {
|
interface ChatHistory {
|
||||||
@@ -71,7 +72,6 @@ interface ChatHistory {
|
|||||||
compressed: boolean;
|
compressed: boolean;
|
||||||
created: string;
|
created: string;
|
||||||
updated: string;
|
updated: string;
|
||||||
showSidePanel: (isEnabled: boolean) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -106,6 +106,15 @@ import { KhojLogoType } from "@/app/components/logo/khojLogo";
|
|||||||
import NavMenu from "@/app/components/navMenu/navMenu";
|
import NavMenu from "@/app/components/navMenu/navMenu";
|
||||||
import { getIconFromIconName } from "@/app/common/iconUtils";
|
import { getIconFromIconName } from "@/app/common/iconUtils";
|
||||||
import LoginPrompt from "../loginPrompt/loginPrompt";
|
import LoginPrompt from "../loginPrompt/loginPrompt";
|
||||||
|
import {
|
||||||
|
SidebarGroup,
|
||||||
|
SidebarGroupLabel,
|
||||||
|
SidebarMenu,
|
||||||
|
SidebarMenuAction,
|
||||||
|
SidebarMenuButton,
|
||||||
|
SidebarMenuItem,
|
||||||
|
SidebarMenuSkeleton,
|
||||||
|
} from "@/components/ui/sidebar";
|
||||||
|
|
||||||
// Define a fetcher function
|
// Define a fetcher function
|
||||||
const fetcher = (url: string) =>
|
const fetcher = (url: string) =>
|
||||||
@@ -403,7 +412,6 @@ function FilesMenu(props: FilesMenuProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface SessionsAndFilesProps {
|
interface SessionsAndFilesProps {
|
||||||
setEnabled: (enabled: boolean) => void;
|
|
||||||
subsetOrganizedData: GroupedChatHistory | null;
|
subsetOrganizedData: GroupedChatHistory | null;
|
||||||
organizedData: GroupedChatHistory | null;
|
organizedData: GroupedChatHistory | null;
|
||||||
data: ChatHistory[] | null;
|
data: ChatHistory[] | null;
|
||||||
@@ -411,32 +419,28 @@ interface SessionsAndFilesProps {
|
|||||||
conversationId: string | null;
|
conversationId: string | null;
|
||||||
uploadedFiles: string[];
|
uploadedFiles: string[];
|
||||||
isMobileWidth: boolean;
|
isMobileWidth: boolean;
|
||||||
|
sideBarOpen: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SessionsAndFiles(props: SessionsAndFilesProps) {
|
function SessionsAndFiles(props: SessionsAndFilesProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<div>
|
<div>
|
||||||
{props.data && props.data.length > 5 && (
|
{props.data && props.data.length > 5 && (
|
||||||
<ChatSessionsModal
|
<ChatSessionsModal data={props.organizedData} sideBarOpen={props.sideBarOpen} />
|
||||||
data={props.organizedData}
|
|
||||||
showSidePanel={props.setEnabled}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
|
{props.sideBarOpen && (
|
||||||
<ScrollArea>
|
<ScrollArea>
|
||||||
<ScrollAreaScrollbar
|
<ScrollAreaScrollbar
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
className="h-full w-2.5 border-l border-l-transparent p-[1px]"
|
className="h-full w-2.5 border-l border-l-transparent p-[1px]"
|
||||||
/>
|
/>
|
||||||
<div className={styles.sessionsList}>
|
<div className="p-0 m-0">
|
||||||
{props.subsetOrganizedData != null &&
|
{props.subsetOrganizedData != null &&
|
||||||
Object.keys(props.subsetOrganizedData)
|
Object.keys(props.subsetOrganizedData)
|
||||||
.filter((tg) => tg !== "All Time")
|
.filter((tg) => tg !== "All Time")
|
||||||
.map((timeGrouping) => (
|
.map((timeGrouping) => (
|
||||||
<div key={timeGrouping} className={`my-4`}>
|
<div key={timeGrouping} className={`my-1`}>
|
||||||
<div
|
<div className={`text-muted-foreground text-xs p-[0.5rem]`}>
|
||||||
className={`text-muted-foreground text-sm font-bold p-[0.5rem]`}
|
|
||||||
>
|
|
||||||
{timeGrouping}
|
{timeGrouping}
|
||||||
</div>
|
</div>
|
||||||
{props.subsetOrganizedData &&
|
{props.subsetOrganizedData &&
|
||||||
@@ -454,7 +458,6 @@ function SessionsAndFiles(props: SessionsAndFilesProps) {
|
|||||||
agent_name={chatHistory.agent_name}
|
agent_name={chatHistory.agent_name}
|
||||||
agent_color={chatHistory.agent_color}
|
agent_color={chatHistory.agent_color}
|
||||||
agent_icon={chatHistory.agent_icon}
|
agent_icon={chatHistory.agent_icon}
|
||||||
showSidePanel={props.setEnabled}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
@@ -462,13 +465,8 @@ function SessionsAndFiles(props: SessionsAndFilesProps) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<FilesMenu
|
|
||||||
conversationId={props.conversationId}
|
|
||||||
uploadedFiles={props.uploadedFiles}
|
|
||||||
isMobileWidth={props.isMobileWidth}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,41 +636,31 @@ export function ChatSessionActionMenu(props: ChatSessionActionMenuProps) {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<DropdownMenu onOpenChange={(open) => setIsOpen(open)} open={isOpen}>
|
<DropdownMenu onOpenChange={(open) => setIsOpen(open)} open={isOpen}>
|
||||||
<DropdownMenuTrigger>
|
<DropdownMenuTrigger asChild>
|
||||||
|
<SidebarMenuAction>
|
||||||
<DotsThreeVertical className={`${size}`} />
|
<DotsThreeVertical className={`${size}`} />
|
||||||
|
</SidebarMenuAction>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent side="right" align="start">
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem onClick={() => setIsRenaming(true)}>
|
||||||
<Button
|
<span className="flex items-center">
|
||||||
className="p-0 text-sm h-auto"
|
|
||||||
variant={"ghost"}
|
|
||||||
onClick={() => setIsRenaming(true)}
|
|
||||||
>
|
|
||||||
<Pencil className={`mr-2 ${size}`} />
|
<Pencil className={`mr-2 ${size}`} />
|
||||||
Rename
|
Rename
|
||||||
</Button>
|
</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
{props.sizing === "sm" && (
|
{props.sizing === "sm" && (
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem onClick={() => setIsSharing(true)}>
|
||||||
<Button
|
<span className="flex items-center">
|
||||||
className="p-0 text-sm h-auto"
|
|
||||||
variant={"ghost"}
|
|
||||||
onClick={() => setIsSharing(true)}
|
|
||||||
>
|
|
||||||
<Share className={`mr-2 ${size}`} />
|
<Share className={`mr-2 ${size}`} />
|
||||||
Share
|
Share
|
||||||
</Button>
|
</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem onClick={() => setIsDeleting(true)}>
|
||||||
<Button
|
<span className="flex items-center">
|
||||||
className="p-0 text-sm h-auto text-rose-300 hover:text-rose-400"
|
|
||||||
variant={"ghost"}
|
|
||||||
onClick={() => setIsDeleting(true)}
|
|
||||||
>
|
|
||||||
<Trash className={`mr-2 ${size}`} />
|
<Trash className={`mr-2 ${size}`} />
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
@@ -686,30 +674,36 @@ function ChatSession(props: ChatHistory) {
|
|||||||
var currConversationId =
|
var currConversationId =
|
||||||
new URLSearchParams(window.location.search).get("conversationId") || "-1";
|
new URLSearchParams(window.location.search).get("conversationId") || "-1";
|
||||||
return (
|
return (
|
||||||
<div
|
<SidebarMenuItem
|
||||||
onMouseEnter={() => setIsHovered(true)}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
key={props.conversation_id}
|
key={props.conversation_id}
|
||||||
className={`${styles.session} ${props.compressed ? styles.compressed : "!max-w-full"} ${isHovered ? `${styles.sessionHover}` : ""} ${currConversationId === props.conversation_id && currConversationId != "-1" ? "dark:bg-neutral-800 bg-white" : ""}`}
|
className={`${styles.session} ${props.compressed ? styles.compressed : "!max-w-full"} ${isHovered ? `${styles.sessionHover}` : ""} ${currConversationId === props.conversation_id && currConversationId != "-1" ? "dark:bg-neutral-800 bg-white" : ""}`}
|
||||||
>
|
>
|
||||||
|
<SidebarMenuButton asChild>
|
||||||
<Link
|
<Link
|
||||||
href={`/chat?conversationId=${props.conversation_id}`}
|
href={`/chat?conversationId=${props.conversation_id}`}
|
||||||
onClick={() => props.showSidePanel(false)}
|
className="flex items-center gap-2 no-underline"
|
||||||
>
|
>
|
||||||
<p className={styles.session}>{title}</p>
|
<p
|
||||||
|
className={`${styles.session} ${props.compressed ? styles.compressed : styles.expanded}`}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</p>
|
||||||
</Link>
|
</Link>
|
||||||
|
</SidebarMenuButton>
|
||||||
<ChatSessionActionMenu
|
<ChatSessionActionMenu
|
||||||
conversationId={props.conversation_id}
|
conversationId={props.conversation_id}
|
||||||
setTitle={setTitle}
|
setTitle={setTitle}
|
||||||
sizing="sm"
|
sizing="sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SidebarMenuItem>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChatSessionsModalProps {
|
interface ChatSessionsModalProps {
|
||||||
data: GroupedChatHistory | null;
|
data: GroupedChatHistory | null;
|
||||||
showSidePanel: (isEnabled: boolean) => void;
|
sideBarOpen: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AgentStyle {
|
interface AgentStyle {
|
||||||
@@ -717,7 +711,7 @@ interface AgentStyle {
|
|||||||
icon: string;
|
icon: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
|
function ChatSessionsModal({ data, sideBarOpen }: ChatSessionsModalProps) {
|
||||||
const [agentsFilter, setAgentsFilter] = useState<string[]>([]);
|
const [agentsFilter, setAgentsFilter] = useState<string[]>([]);
|
||||||
const [agentOptions, setAgentOptions] = useState<string[]>([]);
|
const [agentOptions, setAgentOptions] = useState<string[]>([]);
|
||||||
const [searchQuery, setSearchQuery] = useState<string>("");
|
const [searchQuery, setSearchQuery] = useState<string>("");
|
||||||
@@ -786,10 +780,12 @@ function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger className="flex text-left text-medium text-gray-500 hover:text-gray-300 cursor-pointer my-1 text-sm p-[0.1rem]">
|
<DialogTrigger
|
||||||
|
className={`flex text-left text-medium text-gray-500 hover:text-gray-300 cursor-pointer my-1 text-sm ${sideBarOpen ? "p-[0.5rem] " : "p-[0.1rem]"}`}
|
||||||
|
>
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<MagnifyingGlass className="inline h-4 w-4 mr-1" weight="bold" /> Find
|
<ChatsCircle className="inline h-4 w-4 mr-1" />
|
||||||
Conversation
|
{sideBarOpen ? "Find Conversations" : ""}
|
||||||
</span>
|
</span>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
@@ -814,7 +810,6 @@ function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
{/* <ScrollArea className="h-[200px]"> */}
|
|
||||||
<DropdownMenuLabel>Agents</DropdownMenuLabel>
|
<DropdownMenuLabel>Agents</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
{agentOptions.map((agent) => (
|
{agentOptions.map((agent) => (
|
||||||
@@ -841,7 +836,6 @@ function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
|
|||||||
</div>
|
</div>
|
||||||
</DropdownMenuCheckboxItem>
|
</DropdownMenuCheckboxItem>
|
||||||
))}
|
))}
|
||||||
{/* </ScrollArea> */}
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
@@ -865,7 +859,6 @@ function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
|
|||||||
agent_name={chatHistory.agent_name}
|
agent_name={chatHistory.agent_name}
|
||||||
agent_color={chatHistory.agent_color}
|
agent_color={chatHistory.agent_color}
|
||||||
agent_icon={chatHistory.agent_icon}
|
agent_icon={chatHistory.agent_icon}
|
||||||
showSidePanel={showSidePanel}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -902,17 +895,17 @@ interface SidePanelProps {
|
|||||||
conversationId: string | null;
|
conversationId: string | null;
|
||||||
uploadedFiles: string[];
|
uploadedFiles: string[];
|
||||||
isMobileWidth: boolean;
|
isMobileWidth: boolean;
|
||||||
|
sideBarOpen: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SidePanel(props: SidePanelProps) {
|
export default function AllConversations(props: SidePanelProps) {
|
||||||
const [data, setData] = useState<ChatHistory[] | null>(null);
|
const [data, setData] = useState<ChatHistory[] | null>(null);
|
||||||
const [organizedData, setOrganizedData] = useState<GroupedChatHistory | null>(null);
|
const [organizedData, setOrganizedData] = useState<GroupedChatHistory | null>(null);
|
||||||
const [subsetOrganizedData, setSubsetOrganizedData] = useState<GroupedChatHistory | null>(null);
|
const [subsetOrganizedData, setSubsetOrganizedData] = useState<GroupedChatHistory | null>(null);
|
||||||
const [enabled, setEnabled] = useState(false);
|
|
||||||
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
|
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
|
||||||
|
|
||||||
const authenticatedData = useAuthenticatedData();
|
const authenticatedData = useAuthenticatedData();
|
||||||
const { data: chatSessions } = useChatSessionsFetchRequest(
|
const { data: chatSessions, isLoading } = useChatSessionsFetchRequest(
|
||||||
authenticatedData ? `/api/chat/sessions` : "",
|
authenticatedData ? `/api/chat/sessions` : "",
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -953,41 +946,29 @@ export default function SidePanel(props: SidePanelProps) {
|
|||||||
}
|
}
|
||||||
}, [chatSessions]);
|
}, [chatSessions]);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
|
<SidebarGroup className="p-0 m-0">
|
||||||
|
<SidebarGroupLabel className="!p-0 m-0 px-0">Conversations</SidebarGroupLabel>
|
||||||
|
{Array.from({ length: 5 }).map((_, index) => (
|
||||||
|
<SidebarMenuItem key={index} className="p-0 list-none m-0">
|
||||||
|
<SidebarMenuSkeleton showIcon />
|
||||||
|
</SidebarMenuItem>
|
||||||
|
))}
|
||||||
|
</SidebarGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SidebarGroup>
|
||||||
|
<SidebarGroupLabel className="!p-0 m-0 px-0">Conversations</SidebarGroupLabel>
|
||||||
|
<div className={`flex justify-between flex-col`}>
|
||||||
|
{authenticatedData && (
|
||||||
|
<>
|
||||||
<div
|
<div
|
||||||
className={`${styles.panel} ${enabled ? styles.expanded : styles.collapsed} ${props.isMobileWidth ? "mt-0" : "mt-1"}`}
|
className={`${props.sideBarOpen ? "border-l-2 border-light-blue-500 border-opacity-25 " : ""}`}
|
||||||
>
|
>
|
||||||
{showLoginPrompt && (
|
|
||||||
<LoginPrompt
|
|
||||||
loginRedirectMessage="Sign in to start chatting"
|
|
||||||
onOpenChange={setShowLoginPrompt}
|
|
||||||
isMobileWidth={props.isMobileWidth}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className={`flex justify-between flex-row`}>
|
|
||||||
{props.isMobileWidth ? (
|
|
||||||
<Drawer
|
|
||||||
open={enabled}
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!enabled) setEnabled(false);
|
|
||||||
setEnabled(open);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DrawerTrigger>
|
|
||||||
<Sidebar className="h-6 w-6 mx-2" weight="thin" />
|
|
||||||
</DrawerTrigger>
|
|
||||||
<DrawerContent>
|
|
||||||
<DrawerHeader>
|
|
||||||
<DrawerTitle>Sessions and Files</DrawerTitle>
|
|
||||||
<DrawerDescription>
|
|
||||||
View all conversation sessions and manage conversation file
|
|
||||||
filters
|
|
||||||
</DrawerDescription>
|
|
||||||
</DrawerHeader>
|
|
||||||
{authenticatedData ? (
|
|
||||||
<div className={`${styles.panelWrapper}`}>
|
|
||||||
<SessionsAndFiles
|
<SessionsAndFiles
|
||||||
setEnabled={setEnabled}
|
|
||||||
subsetOrganizedData={subsetOrganizedData}
|
subsetOrganizedData={subsetOrganizedData}
|
||||||
organizedData={organizedData}
|
organizedData={organizedData}
|
||||||
data={data}
|
data={data}
|
||||||
@@ -995,93 +976,19 @@ export default function SidePanel(props: SidePanelProps) {
|
|||||||
userProfile={authenticatedData}
|
userProfile={authenticatedData}
|
||||||
conversationId={props.conversationId}
|
conversationId={props.conversationId}
|
||||||
isMobileWidth={props.isMobileWidth}
|
isMobileWidth={props.isMobileWidth}
|
||||||
|
sideBarOpen={props.sideBarOpen}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
{props.sideBarOpen && (
|
||||||
<div className={`${styles.panelWrapper}`}>
|
<FilesMenu
|
||||||
{" "}
|
|
||||||
{/* Redirect to login page */}
|
|
||||||
<Button
|
|
||||||
variant="default"
|
|
||||||
onClick={() => setShowLoginPrompt(true)}
|
|
||||||
>
|
|
||||||
<UserCirclePlus className="h-4 w-4 mr-1" />
|
|
||||||
Sign Up
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<DrawerFooter>
|
|
||||||
<DrawerClose>
|
|
||||||
<Button variant="outline">Done</Button>
|
|
||||||
</DrawerClose>
|
|
||||||
</DrawerFooter>
|
|
||||||
</DrawerContent>
|
|
||||||
</Drawer>
|
|
||||||
) : (
|
|
||||||
<div className={`grid grid-flow-col gap-4 w-fit`}>
|
|
||||||
<Link href="/" className="content-center">
|
|
||||||
<KhojLogoType />
|
|
||||||
</Link>
|
|
||||||
<div className="grid grid-flow-col gap-2 items-center">
|
|
||||||
<Link className="mx-4" href="/">
|
|
||||||
{enabled ? (
|
|
||||||
<NotePencil className="h-6 w-6" weight="fill" />
|
|
||||||
) : (
|
|
||||||
<NotePencil className="h-6 w-6" color="gray" />
|
|
||||||
)}
|
|
||||||
</Link>
|
|
||||||
<button className={styles.button} onClick={() => setEnabled(!enabled)}>
|
|
||||||
{enabled ? (
|
|
||||||
<Sidebar className="h-6 w-6" weight="fill" />
|
|
||||||
) : (
|
|
||||||
<Sidebar className="h-6 w-6" color="gray" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="fixed right-0 top-[0.9rem] w-fit h-fit">
|
|
||||||
<NavMenu />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{props.isMobileWidth && (
|
|
||||||
<Link href="/" className="content-center h-fit self-center">
|
|
||||||
<KhojLogoType />
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
{props.isMobileWidth && <NavMenu />}
|
|
||||||
</div>
|
|
||||||
{authenticatedData && !props.isMobileWidth && enabled && (
|
|
||||||
<div className={`${styles.panelWrapper}`}>
|
|
||||||
<SessionsAndFiles
|
|
||||||
setEnabled={setEnabled}
|
|
||||||
subsetOrganizedData={subsetOrganizedData}
|
|
||||||
organizedData={organizedData}
|
|
||||||
data={data}
|
|
||||||
uploadedFiles={props.uploadedFiles}
|
|
||||||
userProfile={authenticatedData}
|
|
||||||
conversationId={props.conversationId}
|
conversationId={props.conversationId}
|
||||||
|
uploadedFiles={props.uploadedFiles}
|
||||||
isMobileWidth={props.isMobileWidth}
|
isMobileWidth={props.isMobileWidth}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
{!authenticatedData && enabled && !props.isMobileWidth && (
|
</>
|
||||||
<div className={`${styles.panelWrapper}`}>
|
|
||||||
<Link href="/" className="flex flex-col content-start items-start no-underline">
|
|
||||||
<Button variant="ghost">
|
|
||||||
<House className="h-4 w-4 mr-1" />
|
|
||||||
Home
|
|
||||||
</Button>
|
|
||||||
<Button variant="ghost">
|
|
||||||
<StackPlus className="h-4 w-4 mr-1" />
|
|
||||||
New Conversation
|
|
||||||
</Button>
|
|
||||||
</Link>{" "}
|
|
||||||
<Button variant="default" onClick={() => setShowLoginPrompt(true)}>
|
|
||||||
<UserCirclePlus className="h-4 w-4 mr-1" />
|
|
||||||
Sign Up
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</SidebarGroup>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -74,6 +74,14 @@ p.session {
|
|||||||
font-size: small;
|
font-size: small;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.compressed {
|
||||||
|
width: 12rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.expanded {
|
||||||
|
max-width: 20rem;
|
||||||
|
}
|
||||||
|
|
||||||
div.header {
|
div.header {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
130
src/interface/web/app/components/appSidebar/appSidebar.tsx
Normal file
130
src/interface/web/app/components/appSidebar/appSidebar.tsx
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import { Calendar, Home, Inbox, Search, Settings } from "lucide-react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Sidebar,
|
||||||
|
SidebarContent,
|
||||||
|
SidebarFooter,
|
||||||
|
SidebarGroup,
|
||||||
|
SidebarGroupContent,
|
||||||
|
SidebarGroupLabel,
|
||||||
|
SidebarHeader,
|
||||||
|
SidebarMenu,
|
||||||
|
SidebarMenuButton,
|
||||||
|
SidebarMenuItem,
|
||||||
|
} from "@/components/ui/sidebar";
|
||||||
|
import Link from "next/link";
|
||||||
|
import {
|
||||||
|
KhojAgentLogo,
|
||||||
|
KhojAutomationLogo,
|
||||||
|
KhojLogo,
|
||||||
|
KhojLogoType,
|
||||||
|
KhojSearchLogo,
|
||||||
|
} from "../logo/khojLogo";
|
||||||
|
import { Gear } from "@phosphor-icons/react/dist/ssr";
|
||||||
|
import { House, Plus } from "@phosphor-icons/react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useAuthenticatedData } from "@/app/common/auth";
|
||||||
|
import AllConversations from "../allConversations/allConversations";
|
||||||
|
import NavMenu from "../navMenu/navMenu";
|
||||||
|
import { useSidebar } from "@/components/ui/sidebar";
|
||||||
|
|
||||||
|
// Menu items.
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
title: "New",
|
||||||
|
url: "/",
|
||||||
|
icon: Plus,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Agents",
|
||||||
|
url: "/agents",
|
||||||
|
icon: KhojAgentLogo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Automations",
|
||||||
|
url: "/automations",
|
||||||
|
icon: KhojAutomationLogo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Search",
|
||||||
|
url: "/search",
|
||||||
|
icon: KhojSearchLogo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Settings",
|
||||||
|
url: "/settings",
|
||||||
|
icon: Gear,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
||||||
|
const SIDEBAR_WIDTH = "18rem";
|
||||||
|
const SIDEBAR_WIDTH_MOBILE = "20rem";
|
||||||
|
|
||||||
|
interface AppSidebarProps {
|
||||||
|
conversationId: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AppSidebar(props: AppSidebarProps) {
|
||||||
|
const [isMobileWidth, setIsMobileWidth] = useState(false);
|
||||||
|
|
||||||
|
const { state, open, setOpen, openMobile, setOpenMobile, isMobile, toggleSidebar } =
|
||||||
|
useSidebar();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleResize = () => {
|
||||||
|
setIsMobileWidth(window.innerWidth < 768);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleResize();
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
return () => window.removeEventListener("resize", handleResize);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Sidebar collapsible={"icon"}>
|
||||||
|
<SidebarHeader>
|
||||||
|
<SidebarMenu className="p-0 m-0">
|
||||||
|
<SidebarMenuItem className="p-0 m-0">
|
||||||
|
<SidebarMenuButton asChild>
|
||||||
|
<a className="flex items-center gap-2 no-underline" href="/">
|
||||||
|
<KhojLogo className="w-14 h-auto" />
|
||||||
|
<span className="text-2xl">Khoj</span>
|
||||||
|
</a>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</SidebarMenuItem>
|
||||||
|
</SidebarMenu>
|
||||||
|
</SidebarHeader>
|
||||||
|
<SidebarContent>
|
||||||
|
<SidebarGroup>
|
||||||
|
<SidebarGroupContent>
|
||||||
|
<SidebarMenu className="p-0 m-0">
|
||||||
|
{items.map((item) => (
|
||||||
|
<SidebarMenuItem key={item.title} className="p-0 list-none m-0">
|
||||||
|
<SidebarMenuButton asChild>
|
||||||
|
<a
|
||||||
|
href={item.url}
|
||||||
|
className="flex items-center gap-2 no-underline"
|
||||||
|
>
|
||||||
|
<item.icon />
|
||||||
|
<span>{item.title}</span>
|
||||||
|
</a>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</SidebarMenuItem>
|
||||||
|
))}
|
||||||
|
</SidebarMenu>
|
||||||
|
</SidebarGroupContent>
|
||||||
|
<AllConversations
|
||||||
|
isMobileWidth={isMobileWidth}
|
||||||
|
conversationId={props.conversationId}
|
||||||
|
uploadedFiles={[]}
|
||||||
|
sideBarOpen={open}
|
||||||
|
/>
|
||||||
|
</SidebarGroup>
|
||||||
|
</SidebarContent>
|
||||||
|
<SidebarFooter>
|
||||||
|
<NavMenu sideBarIsOpen={open} />
|
||||||
|
</SidebarFooter>
|
||||||
|
</Sidebar>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -27,6 +27,8 @@ import { KhojAgentLogo, KhojAutomationLogo, KhojSearchLogo } from "../logo/khojL
|
|||||||
import { useIsMobileWidth } from "@/app/common/utils";
|
import { useIsMobileWidth } from "@/app/common/utils";
|
||||||
import LoginPrompt from "../loginPrompt/loginPrompt";
|
import LoginPrompt from "../loginPrompt/loginPrompt";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar";
|
||||||
|
import { ChevronUp } from "lucide-react";
|
||||||
|
|
||||||
function SubscriptionBadge({ is_active }: { is_active: boolean }) {
|
function SubscriptionBadge({ is_active }: { is_active: boolean }) {
|
||||||
return (
|
return (
|
||||||
@@ -48,7 +50,11 @@ function VersionBadge({ version }: { version: string }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavMenu() {
|
interface NavMenuProps {
|
||||||
|
sideBarIsOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NavMenu({ sideBarIsOpen }: NavMenuProps) {
|
||||||
const userData = useAuthenticatedData();
|
const userData = useAuthenticatedData();
|
||||||
const [darkMode, setDarkMode] = useState(false);
|
const [darkMode, setDarkMode] = useState(false);
|
||||||
const [initialLoadDone, setInitialLoadDone] = useState(false);
|
const [initialLoadDone, setInitialLoadDone] = useState(false);
|
||||||
@@ -89,31 +95,34 @@ export default function NavMenu() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.titleBar}>
|
<SidebarMenu className="border-none p-0 m-0">
|
||||||
{showLoginPrompt && (
|
<SidebarMenuItem className="p-0 m-0">
|
||||||
<LoginPrompt
|
|
||||||
onOpenChange={setShowLoginPrompt}
|
|
||||||
isMobileWidth={isMobileWidth}
|
|
||||||
loginRedirectMessage={"Login to your second brain"}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isMobileWidth ? (
|
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger>
|
<DropdownMenuTrigger asChild>
|
||||||
|
<SidebarMenuButton className="p-0 m-0 rounded-lg" asChild>
|
||||||
{userData ? (
|
{userData ? (
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
<Avatar
|
<Avatar
|
||||||
className={`h-10 w-10 border-2 ${userData.is_active ? "border-yellow-500" : "border-stone-700 dark:border-stone-300"}`}
|
className={`${sideBarIsOpen ? "h-8 w-8" : "h-6 w-6"} border-2 ${userData.is_active ? "border-yellow-500" : "border-stone-700 dark:border-stone-300"}`}
|
||||||
>
|
>
|
||||||
<AvatarImage src={userData?.photo} alt="user profile" />
|
<AvatarImage src={userData?.photo} alt="user profile" />
|
||||||
<AvatarFallback className="bg-transparent hover:bg-muted">
|
<AvatarFallback className="bg-transparent hover:bg-muted">
|
||||||
{userData?.username[0].toUpperCase()}
|
{userData?.username[0].toUpperCase()}
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
) : (
|
{sideBarIsOpen && (
|
||||||
<UserCircle className="h-10 w-10" />
|
<>
|
||||||
|
<p>{userData?.username}</p>
|
||||||
|
<ChevronUp className="w-6 h-6" />
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<UserCircle className="w-10 h-10" />
|
||||||
|
)}
|
||||||
|
</SidebarMenuButton>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="gap-2">
|
<DropdownMenuContent align="end" className="rounded-xl gap-2">
|
||||||
<DropdownMenuItem className="w-full">
|
<DropdownMenuItem className="w-full">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<p className="font-semibold">{userData?.email}</p>
|
<p className="font-semibold">{userData?.email}</p>
|
||||||
@@ -123,134 +132,8 @@ export default function NavMenu() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator className="dark:bg-white height-[2px] bg-black" />
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => setDarkMode(!darkMode)}
|
|
||||||
className="w-full cursor-pointer"
|
|
||||||
>
|
|
||||||
<div className="flex flex-rows">
|
|
||||||
{darkMode ? (
|
|
||||||
<Sun className="w-6 h-6" />
|
|
||||||
) : (
|
|
||||||
<Moon className="w-6 h-6" />
|
|
||||||
)}
|
|
||||||
<p className="ml-3 font-semibold">
|
|
||||||
{darkMode ? "Light Mode" : "Dark Mode"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<Link href="/agents" className="no-underline w-full">
|
|
||||||
<div className="flex flex-rows">
|
|
||||||
<KhojAgentLogo className="w-6 h-6" />
|
|
||||||
<p className="ml-3 font-semibold">Agents</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<Link href="/automations" className="no-underline w-full">
|
|
||||||
<div className="flex flex-rows">
|
|
||||||
<KhojAutomationLogo className="w-6 h-6" />
|
|
||||||
<p className="ml-3 font-semibold">Automations</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
{userData && (
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<Link href="/search" className="no-underline w-full">
|
|
||||||
<div className="flex flex-rows">
|
|
||||||
<KhojSearchLogo className="w-6 h-6" />
|
|
||||||
<p className="ml-3 font-semibold">Search</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
)}
|
|
||||||
{userData && (
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<Link href="/settings" className="no-underline w-full">
|
|
||||||
<div className="flex flex-rows">
|
|
||||||
<GearFine className="w-6 h-6" />
|
|
||||||
<p className="ml-3 font-semibold">Settings</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
)}
|
|
||||||
<>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<Link href="https://docs.khoj.dev" className="no-underline w-full">
|
|
||||||
<div className="flex flex-rows">
|
|
||||||
<Question className="w-6 h-6" />
|
|
||||||
<p className="ml-3 font-semibold">Help</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<Link
|
|
||||||
href="https://github.com/khoj-ai/khoj/releases"
|
|
||||||
className="no-underline w-full"
|
|
||||||
>
|
|
||||||
<div className="flex flex-rows">
|
|
||||||
<Code className="w-6 h-6" />
|
|
||||||
<p className="ml-3 font-semibold">Releases</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
{userData ? (
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<Link href="/auth/logout" className="no-underline w-full">
|
|
||||||
<div className="flex flex-rows">
|
|
||||||
<ArrowRight className="w-6 h-6" />
|
|
||||||
<p className="ml-3 font-semibold">Logout</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
) : (
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<Button
|
|
||||||
variant={"ghost"}
|
|
||||||
onClick={() => setShowLoginPrompt(true)}
|
|
||||||
className="no-underline w-full text-left p-0 content-start justify-start items-start h-fit"
|
|
||||||
>
|
|
||||||
<div className="flex flex-rows text-left content-start justify-start items-start p-0">
|
|
||||||
<ArrowRight className="w-6 h-6" />
|
|
||||||
<p className="ml-3 font-semibold">Login</p>
|
|
||||||
</div>
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
) : (
|
|
||||||
<Menubar className="border-none">
|
|
||||||
<MenubarMenu>
|
|
||||||
<MenubarTrigger>
|
|
||||||
{userData ? (
|
|
||||||
<Avatar
|
|
||||||
className={`h-10 w-10 border-2 ${userData.is_active ? "border-yellow-500" : "border-stone-700 dark:border-stone-300"}`}
|
|
||||||
>
|
|
||||||
<AvatarImage src={userData?.photo} alt="user profile" />
|
|
||||||
<AvatarFallback className="bg-transparent hover:bg-muted">
|
|
||||||
{userData?.username[0].toUpperCase()}
|
|
||||||
</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
) : (
|
|
||||||
<UserCircle className="w-10 h-10" />
|
|
||||||
)}
|
|
||||||
</MenubarTrigger>
|
|
||||||
<MenubarContent align="end" className="rounded-xl gap-2">
|
|
||||||
<MenubarItem className="w-full">
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<p className="font-semibold">{userData?.email}</p>
|
|
||||||
<SubscriptionBadge is_active={userData?.is_active ?? false} />
|
|
||||||
{userData?.khoj_version && (
|
|
||||||
<VersionBadge version={userData?.khoj_version} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</MenubarItem>
|
|
||||||
<MenubarSeparator className="dark:bg-white height-[2px] bg-black" />
|
|
||||||
<MenubarItem
|
|
||||||
onClick={() => setDarkMode(!darkMode)}
|
onClick={() => setDarkMode(!darkMode)}
|
||||||
className="w-full hover:cursor-pointer"
|
className="w-full hover:cursor-pointer"
|
||||||
>
|
>
|
||||||
@@ -264,58 +147,17 @@ export default function NavMenu() {
|
|||||||
{darkMode ? "Light Mode" : "Dark Mode"}
|
{darkMode ? "Light Mode" : "Dark Mode"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</MenubarItem>
|
</DropdownMenuItem>
|
||||||
<MenubarItem>
|
<DropdownMenuItem>
|
||||||
<Link href="/agents" className="no-underline w-full">
|
<Link href="https://docs.khoj.dev" className="no-underline w-full">
|
||||||
<div className="flex flex-rows">
|
|
||||||
<KhojAgentLogo className="w-6 h-6" />
|
|
||||||
<p className="ml-3 font-semibold">Agents</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</MenubarItem>
|
|
||||||
<MenubarItem>
|
|
||||||
<Link href="/automations" className="no-underline w-full">
|
|
||||||
<div className="flex flex-rows">
|
|
||||||
<KhojAutomationLogo className="w-6 h-6" />
|
|
||||||
<p className="ml-3 font-semibold">Automations</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</MenubarItem>
|
|
||||||
{userData && (
|
|
||||||
<MenubarItem>
|
|
||||||
<Link href="/search" className="no-underline w-full">
|
|
||||||
<div className="flex flex-rows">
|
|
||||||
<KhojSearchLogo className="w-6 h-6" />
|
|
||||||
<p className="ml-3 font-semibold">Search</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</MenubarItem>
|
|
||||||
)}
|
|
||||||
{userData && (
|
|
||||||
<MenubarItem>
|
|
||||||
<Link href="/settings" className="no-underline w-full">
|
|
||||||
<div className="flex flex-rows">
|
|
||||||
<GearFine className="w-6 h-6" />
|
|
||||||
<p className="ml-3 font-semibold">Settings</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</MenubarItem>
|
|
||||||
)}
|
|
||||||
<>
|
|
||||||
<MenubarSeparator className="dark:bg-white height-[2px] bg-black" />
|
|
||||||
<MenubarItem>
|
|
||||||
<Link
|
|
||||||
href="https://docs.khoj.dev"
|
|
||||||
className="no-underline w-full"
|
|
||||||
>
|
|
||||||
<div className="flex flex-rows">
|
<div className="flex flex-rows">
|
||||||
<Question className="w-6 h-6" />
|
<Question className="w-6 h-6" />
|
||||||
<p className="ml-3 font-semibold">Help</p>
|
<p className="ml-3 font-semibold">Help</p>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</MenubarItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
<MenubarItem>
|
<DropdownMenuItem>
|
||||||
<Link
|
<Link
|
||||||
href="https://github.com/khoj-ai/khoj/releases"
|
href="https://github.com/khoj-ai/khoj/releases"
|
||||||
className="no-underline w-full"
|
className="no-underline w-full"
|
||||||
@@ -325,18 +167,18 @@ export default function NavMenu() {
|
|||||||
<p className="ml-3 font-semibold">Releases</p>
|
<p className="ml-3 font-semibold">Releases</p>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</MenubarItem>
|
</DropdownMenuItem>
|
||||||
{userData ? (
|
{userData ? (
|
||||||
<MenubarItem>
|
<DropdownMenuItem>
|
||||||
<Link href="/auth/logout" className="no-underline w-full">
|
<Link href="/auth/logout" className="no-underline w-full">
|
||||||
<div className="flex flex-rows">
|
<div className="flex flex-rows">
|
||||||
<ArrowRight className="w-6 h-6" />
|
<ArrowRight className="w-6 h-6" />
|
||||||
<p className="ml-3 font-semibold">Logout</p>
|
<p className="ml-3 font-semibold">Logout</p>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</MenubarItem>
|
</DropdownMenuItem>
|
||||||
) : (
|
) : (
|
||||||
<MenubarItem>
|
<DropdownMenuItem>
|
||||||
<Button
|
<Button
|
||||||
variant={"ghost"}
|
variant={"ghost"}
|
||||||
onClick={() => setShowLoginPrompt(true)}
|
onClick={() => setShowLoginPrompt(true)}
|
||||||
@@ -347,13 +189,11 @@ export default function NavMenu() {
|
|||||||
<p className="ml-3 font-semibold">Login</p>
|
<p className="ml-3 font-semibold">Login</p>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</MenubarItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
</>
|
</DropdownMenuContent>
|
||||||
</MenubarContent>
|
</DropdownMenu>
|
||||||
</MenubarMenu>
|
</SidebarMenuItem>
|
||||||
</Menubar>
|
</SidebarMenu>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
|||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import { ContentSecurityPolicy } from "./common/layoutHelper";
|
import { ContentSecurityPolicy } from "./common/layoutHelper";
|
||||||
|
|
||||||
|
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
|
||||||
|
import { AppSidebar } from "@/app/components/appSidebar/appSidebar";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Khoj AI - Ask Anything",
|
title: "Khoj AI - Ask Anything",
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
div.main {
|
div.main {
|
||||||
height: 100dvh;
|
height: 100dvh;
|
||||||
color: hsla(var(--foreground));
|
color: hsla(var(--foreground));
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.suggestions {
|
div.suggestions {
|
||||||
@@ -110,7 +112,7 @@ div.sidePanel {
|
|||||||
|
|
||||||
div.chatBox {
|
div.chatBox {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 100%;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.chatLayout {
|
div.chatLayout {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { ArrowCounterClockwise } from "@phosphor-icons/react";
|
|||||||
|
|
||||||
import { Card, CardTitle } from "@/components/ui/card";
|
import { Card, CardTitle } from "@/components/ui/card";
|
||||||
import SuggestionCard from "@/app/components/suggestions/suggestionCard";
|
import SuggestionCard from "@/app/components/suggestions/suggestionCard";
|
||||||
import SidePanel from "@/app/components/sidePanel/chatHistorySidePanel";
|
|
||||||
import Loading from "@/app/components/loading/loading";
|
import Loading from "@/app/components/loading/loading";
|
||||||
import {
|
import {
|
||||||
AttachedFileText,
|
AttachedFileText,
|
||||||
@@ -35,6 +34,8 @@ import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
|||||||
import { AgentCard } from "@/app/components/agentCard/agentCard";
|
import { AgentCard } from "@/app/components/agentCard/agentCard";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
import LoginPopup from "./components/loginPrompt/loginPopup";
|
import LoginPopup from "./components/loginPrompt/loginPopup";
|
||||||
|
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
|
||||||
|
import { AppSidebar } from "./components/appSidebar/appSidebar";
|
||||||
|
|
||||||
interface ChatBodyDataProps {
|
interface ChatBodyDataProps {
|
||||||
chatOptionsData: ChatOptions | null;
|
chatOptionsData: ChatOptions | null;
|
||||||
@@ -458,15 +459,18 @@ export default function Home() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SidebarProvider>
|
||||||
|
<AppSidebar conversationId={conversationId} />
|
||||||
|
<SidebarTrigger />
|
||||||
<div className={`${styles.main} ${styles.chatLayout}`}>
|
<div className={`${styles.main} ${styles.chatLayout}`}>
|
||||||
<title>Khoj AI - Your Second Brain</title>
|
<title>Khoj AI - Your Second Brain</title>
|
||||||
<div className={`${styles.sidePanel}`}>
|
{/* <div className={`${styles.sidePanel}`}>
|
||||||
<SidePanel
|
<SidePanel
|
||||||
conversationId={conversationId}
|
conversationId={conversationId}
|
||||||
uploadedFiles={[]}
|
uploadedFiles={[]}
|
||||||
isMobileWidth={isMobileWidth}
|
isMobileWidth={isMobileWidth}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div> */}
|
||||||
<div className={`${styles.chatBox}`}>
|
<div className={`${styles.chatBox}`}>
|
||||||
<div className={`${styles.chatBoxBody}`}>
|
<div className={`${styles.chatBoxBody}`}>
|
||||||
<ChatBodyData
|
<ChatBodyData
|
||||||
@@ -481,5 +485,6 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</SidebarProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
||||||
import { useAuthenticatedData } from "../common/auth";
|
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import SidePanel from "../components/sidePanel/chatHistorySidePanel";
|
|
||||||
import styles from "./search.module.css";
|
import styles from "./search.module.css";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import {
|
import {
|
||||||
@@ -229,9 +227,6 @@ export default function Search() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className={`h-full ${styles.sidePanel}`}>
|
|
||||||
<SidePanel conversationId={null} uploadedFiles={[]} isMobileWidth={isMobileWidth} />
|
|
||||||
</div>
|
|
||||||
<div className={`${styles.searchLayout}`}>
|
<div className={`${styles.searchLayout}`}>
|
||||||
<div className="md:w-3/4 sm:w-full mx-auto pt-6 md:pt-8">
|
<div className="md:w-3/4 sm:w-full mx-auto pt-6 md:pt-8">
|
||||||
<div className="p-4 md:w-3/4 sm:w-full mx-auto">
|
<div className="p-4 md:w-3/4 sm:w-full mx-auto">
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ import {
|
|||||||
Waveform,
|
Waveform,
|
||||||
} from "@phosphor-icons/react";
|
} from "@phosphor-icons/react";
|
||||||
|
|
||||||
import SidePanel from "../components/sidePanel/chatHistorySidePanel";
|
|
||||||
import Loading from "../components/loading/loading";
|
import Loading from "../components/loading/loading";
|
||||||
|
|
||||||
import IntlTelInput from "intl-tel-input/react";
|
import IntlTelInput from "intl-tel-input/react";
|
||||||
@@ -77,6 +76,8 @@ import {
|
|||||||
} from "@/components/ui/alert-dialog";
|
} from "@/components/ui/alert-dialog";
|
||||||
import { Progress } from "@/components/ui/progress";
|
import { Progress } from "@/components/ui/progress";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
|
||||||
|
import { AppSidebar } from "../components/appSidebar/appSidebar";
|
||||||
|
|
||||||
const ManageFilesModal: React.FC<{ onClose: () => void }> = ({ onClose }) => {
|
const ManageFilesModal: React.FC<{ onClose: () => void }> = ({ onClose }) => {
|
||||||
const [syncedFiles, setSyncedFiles] = useState<string[]>([]);
|
const [syncedFiles, setSyncedFiles] = useState<string[]>([]);
|
||||||
@@ -854,11 +855,11 @@ export default function SettingsView() {
|
|||||||
if (!userConfig) return <Loading />;
|
if (!userConfig) return <Loading />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SidebarProvider>
|
||||||
|
<AppSidebar conversationId={""} />
|
||||||
|
<SidebarTrigger />
|
||||||
<div className={styles.page}>
|
<div className={styles.page}>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<div className={styles.sidePanel}>
|
|
||||||
<SidePanel conversationId={null} uploadedFiles={[]} isMobileWidth={isMobileWidth} />
|
|
||||||
</div>
|
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={`${styles.contentBody} mx-10 my-2`}>
|
<div className={`${styles.contentBody} mx-10 my-2`}>
|
||||||
<Suspense fallback={<Loading />}>
|
<Suspense fallback={<Loading />}>
|
||||||
@@ -911,9 +912,10 @@ export default function SettingsView() {
|
|||||||
</p>
|
</p>
|
||||||
<p className="text-gray-400">
|
<p className="text-gray-400">
|
||||||
You are on a{" "}
|
You are on a{" "}
|
||||||
{userConfig.length_of_free_trial} day trial
|
{userConfig.length_of_free_trial} day
|
||||||
of the Khoj Futurist plan. Your trial ends
|
trial of the Khoj Futurist plan. Your
|
||||||
on {userConfig.subscription_renewal_date}.
|
trial ends on{" "}
|
||||||
|
{userConfig.subscription_renewal_date}.
|
||||||
Check{" "}
|
Check{" "}
|
||||||
<a
|
<a
|
||||||
href="https://khoj.dev/#pricing"
|
href="https://khoj.dev/#pricing"
|
||||||
@@ -925,7 +927,8 @@ export default function SettingsView() {
|
|||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
)) ||
|
)) ||
|
||||||
(userConfig.subscription_state === "subscribed" && (
|
(userConfig.subscription_state ===
|
||||||
|
"subscribed" && (
|
||||||
<>
|
<>
|
||||||
<p className="text-xl text-primary/80">
|
<p className="text-xl text-primary/80">
|
||||||
Futurist
|
Futurist
|
||||||
@@ -954,7 +957,8 @@ export default function SettingsView() {
|
|||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
)) ||
|
)) ||
|
||||||
(userConfig.subscription_state === "expired" && (
|
(userConfig.subscription_state ===
|
||||||
|
"expired" && (
|
||||||
<>
|
<>
|
||||||
<p className="text-xl">Humanist</p>
|
<p className="text-xl">Humanist</p>
|
||||||
{(userConfig.subscription_renewal_date && (
|
{(userConfig.subscription_renewal_date && (
|
||||||
@@ -1136,7 +1140,9 @@ export default function SettingsView() {
|
|||||||
</p>
|
</p>
|
||||||
{!userConfig.notion_oauth_url && (
|
{!userConfig.notion_oauth_url && (
|
||||||
<Input
|
<Input
|
||||||
onChange={(e) => setNotionToken(e.target.value)}
|
onChange={(e) =>
|
||||||
|
setNotionToken(e.target.value)
|
||||||
|
}
|
||||||
value={notionToken || ""}
|
value={notionToken || ""}
|
||||||
placeholder="Enter API Key of your Khoj integration on Notion"
|
placeholder="Enter API Key of your Khoj integration on Notion"
|
||||||
className="w-full border border-gray-300 rounded-lg px-4 py-6"
|
className="w-full border border-gray-300 rounded-lg px-4 py-6"
|
||||||
@@ -1178,7 +1184,8 @@ export default function SettingsView() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
onClick={saveNotionToken}
|
onClick={saveNotionToken}
|
||||||
disabled={
|
disabled={
|
||||||
notionToken === userConfig.notion_token
|
notionToken ===
|
||||||
|
userConfig.notion_token
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FloppyDisk className="h-5 w-5 inline mr-1" />
|
<FloppyDisk className="h-5 w-5 inline mr-1" />
|
||||||
@@ -1215,11 +1222,14 @@ export default function SettingsView() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="overflow-hidden pb-12 grid gap-8 h-fit">
|
<CardContent className="overflow-hidden pb-12 grid gap-8 h-fit">
|
||||||
<p className="text-gray-400">
|
<p className="text-gray-400">
|
||||||
Pick the chat model to generate text responses
|
Pick the chat model to generate text
|
||||||
|
responses
|
||||||
</p>
|
</p>
|
||||||
<DropdownComponent
|
<DropdownComponent
|
||||||
items={userConfig.chat_model_options}
|
items={userConfig.chat_model_options}
|
||||||
selected={userConfig.selected_chat_model_config}
|
selected={
|
||||||
|
userConfig.selected_chat_model_config
|
||||||
|
}
|
||||||
callbackFunc={updateModel("chat")}
|
callbackFunc={updateModel("chat")}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@@ -1240,7 +1250,8 @@ export default function SettingsView() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="overflow-hidden pb-12 grid gap-8 h-fit">
|
<CardContent className="overflow-hidden pb-12 grid gap-8 h-fit">
|
||||||
<p className="text-gray-400">
|
<p className="text-gray-400">
|
||||||
Pick the paint model to generate image responses
|
Pick the paint model to generate image
|
||||||
|
responses
|
||||||
</p>
|
</p>
|
||||||
<DropdownComponent
|
<DropdownComponent
|
||||||
items={userConfig.paint_model_options}
|
items={userConfig.paint_model_options}
|
||||||
@@ -1306,7 +1317,10 @@ export default function SettingsView() {
|
|||||||
className="!mt-0"
|
className="!mt-0"
|
||||||
onClick={generateAPIKey}
|
onClick={generateAPIKey}
|
||||||
>
|
>
|
||||||
<Plus weight="bold" className="h-5 w-5 mr-2" />
|
<Plus
|
||||||
|
weight="bold"
|
||||||
|
className="h-5 w-5 mr-2"
|
||||||
|
/>
|
||||||
Generate Key
|
Generate Key
|
||||||
</Button>
|
</Button>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
@@ -1398,8 +1412,8 @@ export default function SettingsView() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="grid gap-4">
|
<CardContent className="grid gap-4">
|
||||||
<p className="text-gray-400">
|
<p className="text-gray-400">
|
||||||
Connect your number to chat with Khoj on WhatsApp.
|
Connect your number to chat with Khoj on
|
||||||
Learn more about the integration{" "}
|
WhatsApp. Learn more about the integration{" "}
|
||||||
<a href="https://docs.khoj.dev/clients/whatsapp">
|
<a href="https://docs.khoj.dev/clients/whatsapp">
|
||||||
here
|
here
|
||||||
</a>
|
</a>
|
||||||
@@ -1460,7 +1474,8 @@ export default function SettingsView() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
disabled={
|
disabled={
|
||||||
!phoneNumber ||
|
!phoneNumber ||
|
||||||
(phoneNumber === userConfig.phone_number &&
|
(phoneNumber ===
|
||||||
|
userConfig.phone_number &&
|
||||||
numberValidationState ===
|
numberValidationState ===
|
||||||
PhoneNumberValidationState.Verified) ||
|
PhoneNumberValidationState.Verified) ||
|
||||||
!isValidPhoneNumber(phoneNumber)
|
!isValidPhoneNumber(phoneNumber)
|
||||||
@@ -1473,7 +1488,8 @@ export default function SettingsView() {
|
|||||||
Setup Whatsapp
|
Setup Whatsapp
|
||||||
</>
|
</>
|
||||||
) : !phoneNumber ||
|
) : !phoneNumber ||
|
||||||
(phoneNumber === userConfig.phone_number &&
|
(phoneNumber ===
|
||||||
|
userConfig.phone_number &&
|
||||||
numberValidationState ===
|
numberValidationState ===
|
||||||
PhoneNumberValidationState.Verified) ||
|
PhoneNumberValidationState.Verified) ||
|
||||||
!isValidPhoneNumber(phoneNumber) ? (
|
!isValidPhoneNumber(phoneNumber) ? (
|
||||||
@@ -1511,5 +1527,6 @@ export default function SettingsView() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</SidebarProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
import styles from "./sharedChat.module.css";
|
import styles from "./sharedChat.module.css";
|
||||||
import React, { Suspense, useEffect, useRef, useState } from "react";
|
import React, { Suspense, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import SidePanel from "../../components/sidePanel/chatHistorySidePanel";
|
|
||||||
import ChatHistory from "../../components/chatHistory/chatHistory";
|
import ChatHistory from "../../components/chatHistory/chatHistory";
|
||||||
import Loading from "../../components/loading/loading";
|
import Loading from "../../components/loading/loading";
|
||||||
|
|
||||||
@@ -19,6 +18,8 @@ import {
|
|||||||
} from "@/app/components/chatInputArea/chatInputArea";
|
} from "@/app/components/chatInputArea/chatInputArea";
|
||||||
import { StreamMessage } from "@/app/components/chatMessage/chatMessage";
|
import { StreamMessage } from "@/app/components/chatMessage/chatMessage";
|
||||||
import { AgentData } from "@/app/agents/page";
|
import { AgentData } from "@/app/agents/page";
|
||||||
|
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
|
||||||
|
import { AppSidebar } from "@/app/components/appSidebar/appSidebar";
|
||||||
|
|
||||||
interface ChatBodyDataProps {
|
interface ChatBodyDataProps {
|
||||||
chatOptionsData: ChatOptions | null;
|
chatOptionsData: ChatOptions | null;
|
||||||
@@ -184,16 +185,11 @@ export default function SharedChat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SidebarProvider>
|
||||||
|
<AppSidebar conversationId={conversationId || ""} />
|
||||||
|
<SidebarTrigger />
|
||||||
<div className={`${styles.main} ${styles.chatLayout}`}>
|
<div className={`${styles.main} ${styles.chatLayout}`}>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<div className={styles.sidePanel}>
|
|
||||||
<SidePanel
|
|
||||||
conversationId={conversationId ?? null}
|
|
||||||
uploadedFiles={[]}
|
|
||||||
isMobileWidth={isMobileWidth}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.chatBox}>
|
<div className={styles.chatBox}>
|
||||||
<div className={styles.chatBoxBody}>
|
<div className={styles.chatBoxBody}>
|
||||||
{!isMobileWidth && title && (
|
{!isMobileWidth && title && (
|
||||||
@@ -226,5 +222,6 @@ export default function SharedChat() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</SidebarProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
div.main {
|
div.main {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
color: hsla(var(--foreground));
|
color: hsla(var(--foreground));
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.suggestions {
|
.suggestions {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { Slot } from "@radix-ui/react-slot";
|
|||||||
import { VariantProps, cva } from "class-variance-authority";
|
import { VariantProps, cva } from "class-variance-authority";
|
||||||
import { PanelLeft } from "lucide-react";
|
import { PanelLeft } from "lucide-react";
|
||||||
|
|
||||||
import { useIsMobile } from "@/components/hooks/use-mobile";
|
import { useIsMobile } from "@/hooks/use-mobile";
|
||||||
import { cn } from "@/components/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
|||||||
Reference in New Issue
Block a user