mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-05 21:29:11 +00:00
Format next.js web app with prettier
This commit is contained in:
@@ -8,18 +8,19 @@ export const metadata: Metadata = {
|
||||
title: "Khoj AI - Agents",
|
||||
description: "Find a specialized agent that can help you address more specific needs.",
|
||||
icons: {
|
||||
icon: '/static/favicon.ico',
|
||||
icon: "/static/favicon.ico",
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<meta httpEquiv="Content-Security-Policy"
|
||||
<meta
|
||||
httpEquiv="Content-Security-Policy"
|
||||
content="default-src 'self' https://assets.khoj.dev;
|
||||
media-src * blob:;
|
||||
script-src 'self' https://assets.khoj.dev 'unsafe-inline' 'unsafe-eval';
|
||||
@@ -28,10 +29,9 @@ export default function RootLayout({
|
||||
img-src 'self' data: https://*.khoj.dev https://*.googleusercontent.com https://*.google.com/ https://*.gstatic.com;
|
||||
font-src 'self' https://assets.khoj.dev https://fonts.gstatic.com;
|
||||
child-src 'none';
|
||||
object-src 'none';"></meta>
|
||||
<body className={inter.className}>
|
||||
{children}
|
||||
</body>
|
||||
object-src 'none';"
|
||||
></meta>
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,37 +1,50 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import styles from './agents.module.css';
|
||||
import styles from "./agents.module.css";
|
||||
|
||||
import Image from 'next/image';
|
||||
import useSWR from 'swr';
|
||||
import Image from "next/image";
|
||||
import useSWR from "swr";
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { useAuthenticatedData, UserProfile } from '../common/auth';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip"
|
||||
import { useAuthenticatedData, UserProfile } from "../common/auth";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
|
||||
import { PaperPlaneTilt, Lightning, Plus } from "@phosphor-icons/react";
|
||||
|
||||
import {
|
||||
PaperPlaneTilt,
|
||||
Lightning,
|
||||
Plus,
|
||||
} from "@phosphor-icons/react";
|
||||
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTrigger } from '@/components/ui/dialog';
|
||||
import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from '@/components/ui/drawer';
|
||||
import LoginPrompt from '../components/loginPrompt/loginPrompt';
|
||||
import { InlineLoading } from '../components/loading/loading';
|
||||
import SidePanel from '../components/sidePanel/chatHistorySidePanel';
|
||||
import NavMenu from '../components/navMenu/navMenu';
|
||||
import { getIconFromIconName } from '../common/iconUtils';
|
||||
import { convertColorToTextClass } from '../common/colorUtils';
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
DrawerTrigger,
|
||||
} from "@/components/ui/drawer";
|
||||
import LoginPrompt from "../components/loginPrompt/loginPrompt";
|
||||
import { InlineLoading } from "../components/loading/loading";
|
||||
import SidePanel from "../components/sidePanel/chatHistorySidePanel";
|
||||
import NavMenu from "../components/navMenu/navMenu";
|
||||
import { getIconFromIconName } from "../common/iconUtils";
|
||||
import { convertColorToTextClass } from "../common/colorUtils";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
|
||||
export interface AgentData {
|
||||
slug: string;
|
||||
@@ -43,7 +56,6 @@ export interface AgentData {
|
||||
}
|
||||
|
||||
async function openChat(slug: string, userData: UserProfile | null) {
|
||||
|
||||
const unauthenticatedRedirectUrl = `/login?next=/agents?agent=${slug}`;
|
||||
if (!userData) {
|
||||
window.location.href = unauthenticatedRedirectUrl;
|
||||
@@ -61,7 +73,11 @@ async function openChat(slug: string, userData: UserProfile | null) {
|
||||
}
|
||||
}
|
||||
|
||||
const agentsFetcher = () => window.fetch('/api/agents').then(res => res.json()).catch(err => console.log(err));
|
||||
const agentsFetcher = () =>
|
||||
window
|
||||
.fetch("/api/agents")
|
||||
.then((res) => res.json())
|
||||
.catch((err) => console.log(err));
|
||||
|
||||
interface AgentCardProps {
|
||||
data: AgentData;
|
||||
@@ -69,171 +85,195 @@ interface AgentCardProps {
|
||||
isMobileWidth: boolean;
|
||||
}
|
||||
|
||||
|
||||
function AgentCard(props: AgentCardProps) {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const agentSlug = searchParams.get('agent');
|
||||
const agentSlug = searchParams.get("agent");
|
||||
const [showModal, setShowModal] = useState(agentSlug === props.data.slug);
|
||||
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
|
||||
|
||||
const userData = props.userProfile;
|
||||
|
||||
if (showModal) {
|
||||
window.history.pushState({}, `Khoj AI - Agent ${props.data.slug}`, `/agents?agent=${props.data.slug}`);
|
||||
window.history.pushState(
|
||||
{},
|
||||
`Khoj AI - Agent ${props.data.slug}`,
|
||||
`/agents?agent=${props.data.slug}`,
|
||||
);
|
||||
}
|
||||
|
||||
const stylingString = convertColorToTextClass(props.data.color);
|
||||
|
||||
return (
|
||||
<Card className={`shadow-sm bg-gradient-to-b from-white 20% to-${props.data.color ? props.data.color : "gray"}-100/50 dark:from-[hsl(var(--background))] dark:to-${props.data.color ? props.data.color : "gray"}-950/50 rounded-xl hover:shadow-md`}>
|
||||
{
|
||||
showLoginPrompt &&
|
||||
<Card
|
||||
className={`shadow-sm bg-gradient-to-b from-white 20% to-${props.data.color ? props.data.color : "gray"}-100/50 dark:from-[hsl(var(--background))] dark:to-${props.data.color ? props.data.color : "gray"}-950/50 rounded-xl hover:shadow-md`}
|
||||
>
|
||||
{showLoginPrompt && (
|
||||
<LoginPrompt
|
||||
loginRedirectMessage={`Sign in to start chatting with ${props.data.name}`}
|
||||
onOpenChange={setShowLoginPrompt} />
|
||||
}
|
||||
onOpenChange={setShowLoginPrompt}
|
||||
/>
|
||||
)}
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
{
|
||||
!props.isMobileWidth ?
|
||||
<Dialog
|
||||
open={showModal}
|
||||
onOpenChange={() => {
|
||||
setShowModal(!showModal);
|
||||
window.history.pushState({}, `Khoj AI - Agents`, `/agents`);
|
||||
}}>
|
||||
<DialogTrigger>
|
||||
<div className='flex items-center relative top-2'>
|
||||
{
|
||||
getIconFromIconName(props.data.icon, props.data.color) || <Image
|
||||
{!props.isMobileWidth ? (
|
||||
<Dialog
|
||||
open={showModal}
|
||||
onOpenChange={() => {
|
||||
setShowModal(!showModal);
|
||||
window.history.pushState({}, `Khoj AI - Agents`, `/agents`);
|
||||
}}
|
||||
>
|
||||
<DialogTrigger>
|
||||
<div className="flex items-center relative top-2">
|
||||
{getIconFromIconName(props.data.icon, props.data.color) || (
|
||||
<Image
|
||||
src={props.data.avatar}
|
||||
alt={props.data.name}
|
||||
width={50}
|
||||
height={50}
|
||||
/>
|
||||
)}
|
||||
{props.data.name}
|
||||
</div>
|
||||
</DialogTrigger>
|
||||
<div className="float-right">
|
||||
{props.userProfile ? (
|
||||
<Button
|
||||
className={`bg-[hsl(var(--background))] w-14 h-14 rounded-xl border dark:border-neutral-700 shadow-sm hover:bg-stone-100 dark:hover:bg-neutral-900`}
|
||||
onClick={() => openChat(props.data.slug, userData)}
|
||||
>
|
||||
<PaperPlaneTilt
|
||||
className={`w-6 h-6 ${convertColorToTextClass(props.data.color)}`}
|
||||
/>
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={`bg-[hsl(var(--background))] w-14 h-14 rounded-xl border dark:border-neutral-700 shadow-sm hover:bg-stone-100 dark:hover:bg-neutral-900`}
|
||||
onClick={() => setShowLoginPrompt(true)}
|
||||
>
|
||||
<PaperPlaneTilt
|
||||
className={`w-6 h-6 ${convertColorToTextClass(props.data.color)}`}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<DialogContent className="whitespace-pre-line max-h-[80vh]">
|
||||
<DialogHeader>
|
||||
<div className="flex items-center">
|
||||
{getIconFromIconName(props.data.icon, props.data.color) || (
|
||||
<Image
|
||||
src={props.data.avatar}
|
||||
alt={props.data.name}
|
||||
width={50}
|
||||
width={32}
|
||||
height={50}
|
||||
/>
|
||||
}
|
||||
{props.data.name}
|
||||
)}
|
||||
<p className="font-bold text-lg">{props.data.name}</p>
|
||||
</div>
|
||||
</DialogTrigger>
|
||||
<div className="float-right">
|
||||
{props.userProfile ? (
|
||||
<Button
|
||||
className={`bg-[hsl(var(--background))] w-14 h-14 rounded-xl border dark:border-neutral-700 shadow-sm hover:bg-stone-100 dark:hover:bg-neutral-900`}
|
||||
onClick={() => openChat(props.data.slug, userData)}>
|
||||
<PaperPlaneTilt className={`w-6 h-6 ${convertColorToTextClass(props.data.color)}`} />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={`bg-[hsl(var(--background))] w-14 h-14 rounded-xl border dark:border-neutral-700 shadow-sm hover:bg-stone-100 dark:hover:bg-neutral-900`}
|
||||
onClick={() => setShowLoginPrompt(true)}>
|
||||
<PaperPlaneTilt className={`w-6 h-6 ${convertColorToTextClass(props.data.color)}`} />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<DialogContent className='whitespace-pre-line max-h-[80vh]'>
|
||||
<DialogHeader>
|
||||
<div className='flex items-center'>
|
||||
{
|
||||
getIconFromIconName(props.data.icon, props.data.color) || <Image
|
||||
src={props.data.avatar}
|
||||
alt={props.data.name}
|
||||
width={32}
|
||||
height={50}
|
||||
/>
|
||||
}
|
||||
<p className="font-bold text-lg">{props.data.name}</p>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
<div className="max-h-[60vh] overflow-y-scroll text-neutral-500 dark:text-white">
|
||||
{props.data.persona}
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
className={`pt-6 pb-6 ${stylingString} bg-white dark:bg-[hsl(var(--background))] text-neutral-500 dark:text-white border-2 border-stone-100 shadow-sm rounded-xl hover:bg-stone-100 dark:hover:bg-neutral-900 dark:border-neutral-700`}
|
||||
onClick={() => {
|
||||
openChat(props.data.slug, userData);
|
||||
setShowModal(false);
|
||||
}}>
|
||||
<PaperPlaneTilt className={`w-6 h-6 m-2 ${convertColorToTextClass(props.data.color)}`} />
|
||||
Start Chatting
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
:
|
||||
<Drawer
|
||||
open={showModal}
|
||||
onOpenChange={(open) => {
|
||||
setShowModal(open);
|
||||
window.history.pushState({}, `Khoj AI - Agents`, `/agents`);
|
||||
}}>
|
||||
<DrawerTrigger>
|
||||
<div className='flex items-center'>
|
||||
{
|
||||
getIconFromIconName(props.data.icon, props.data.color) || <Image
|
||||
src={props.data.avatar}
|
||||
alt={props.data.name}
|
||||
width={50}
|
||||
height={50}
|
||||
/>
|
||||
}
|
||||
{props.data.name}
|
||||
</div>
|
||||
</DrawerTrigger>
|
||||
<div className="float-right">
|
||||
{props.userProfile ? (
|
||||
<Button
|
||||
className={`bg-[hsl(var(--background))] w-14 h-14 rounded-xl border dark:border-neutral-700 shadow-sm hover:bg-stone-100`}
|
||||
onClick={() => openChat(props.data.slug, userData)}>
|
||||
<PaperPlaneTilt className={`w-6 h-6 ${convertColorToTextClass(props.data.color)}`} />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={`bg-[hsl(var(--background))] w-14 h-14 rounded-xl border dark:border-neutral-700 shadow-sm`}
|
||||
onClick={() => setShowLoginPrompt(true)}>
|
||||
<PaperPlaneTilt className={`w-6 h-6 ${convertColorToTextClass(props.data.color)}`} />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<DrawerContent className='whitespace-pre-line p-2'>
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>{props.data.name}</DrawerTitle>
|
||||
<DrawerDescription>Full Prompt</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
</DialogHeader>
|
||||
<div className="max-h-[60vh] overflow-y-scroll text-neutral-500 dark:text-white">
|
||||
{props.data.persona}
|
||||
<DrawerFooter>
|
||||
<DrawerClose>
|
||||
Done
|
||||
</DrawerClose>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
}
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
className={`pt-6 pb-6 ${stylingString} bg-white dark:bg-[hsl(var(--background))] text-neutral-500 dark:text-white border-2 border-stone-100 shadow-sm rounded-xl hover:bg-stone-100 dark:hover:bg-neutral-900 dark:border-neutral-700`}
|
||||
onClick={() => {
|
||||
openChat(props.data.slug, userData);
|
||||
setShowModal(false);
|
||||
}}
|
||||
>
|
||||
<PaperPlaneTilt
|
||||
className={`w-6 h-6 m-2 ${convertColorToTextClass(props.data.color)}`}
|
||||
/>
|
||||
Start Chatting
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : (
|
||||
<Drawer
|
||||
open={showModal}
|
||||
onOpenChange={(open) => {
|
||||
setShowModal(open);
|
||||
window.history.pushState({}, `Khoj AI - Agents`, `/agents`);
|
||||
}}
|
||||
>
|
||||
<DrawerTrigger>
|
||||
<div className="flex items-center">
|
||||
{getIconFromIconName(props.data.icon, props.data.color) || (
|
||||
<Image
|
||||
src={props.data.avatar}
|
||||
alt={props.data.name}
|
||||
width={50}
|
||||
height={50}
|
||||
/>
|
||||
)}
|
||||
{props.data.name}
|
||||
</div>
|
||||
</DrawerTrigger>
|
||||
<div className="float-right">
|
||||
{props.userProfile ? (
|
||||
<Button
|
||||
className={`bg-[hsl(var(--background))] w-14 h-14 rounded-xl border dark:border-neutral-700 shadow-sm hover:bg-stone-100`}
|
||||
onClick={() => openChat(props.data.slug, userData)}
|
||||
>
|
||||
<PaperPlaneTilt
|
||||
className={`w-6 h-6 ${convertColorToTextClass(props.data.color)}`}
|
||||
/>
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={`bg-[hsl(var(--background))] w-14 h-14 rounded-xl border dark:border-neutral-700 shadow-sm`}
|
||||
onClick={() => setShowLoginPrompt(true)}
|
||||
>
|
||||
<PaperPlaneTilt
|
||||
className={`w-6 h-6 ${convertColorToTextClass(props.data.color)}`}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<DrawerContent className="whitespace-pre-line p-2">
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>{props.data.name}</DrawerTitle>
|
||||
<DrawerDescription>Full Prompt</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
{props.data.persona}
|
||||
<DrawerFooter>
|
||||
<DrawerClose>Done</DrawerClose>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
)}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className={styles.agentPersonality}>
|
||||
<button className={`${styles.infoButton} text-neutral-500 dark:text-white`} onClick={() => setShowModal(true)}>
|
||||
<button
|
||||
className={`${styles.infoButton} text-neutral-500 dark:text-white`}
|
||||
onClick={() => setShowModal(true)}
|
||||
>
|
||||
<p>{props.data.persona}</p>
|
||||
</button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default function Agents() {
|
||||
const { data, error } = useSWR<AgentData[]>('agents', agentsFetcher, { revalidateOnFocus: false });
|
||||
const { data, error } = useSWR<AgentData[]>("agents", agentsFetcher, {
|
||||
revalidateOnFocus: false,
|
||||
});
|
||||
const authenticatedData = useAuthenticatedData();
|
||||
const [isMobileWidth, setIsMobileWidth] = useState(false);
|
||||
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
if (typeof window !== "undefined") {
|
||||
setIsMobileWidth(window.innerWidth < 768);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
window.addEventListener("resize", () => {
|
||||
setIsMobileWidth(window.innerWidth < 768);
|
||||
});
|
||||
}, []);
|
||||
@@ -241,12 +281,8 @@ export default function Agents() {
|
||||
if (error) {
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<div className={`${styles.titleBar} text-5xl`}>
|
||||
Agents
|
||||
</div>
|
||||
<div className={styles.agentList}>
|
||||
Error loading agents
|
||||
</div>
|
||||
<div className={`${styles.titleBar} text-5xl`}>Agents</div>
|
||||
<div className={styles.agentList}>Error loading agents</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -279,7 +315,7 @@ export default function Agents() {
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<div className="flex flex-row">
|
||||
<Plus className='pr-2 w-6 h-6' />
|
||||
<Plus className="pr-2 w-6 h-6" />
|
||||
<p className="pr-2">Create Agent</p>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
@@ -290,21 +326,27 @@ export default function Agents() {
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
showLoginPrompt &&
|
||||
{showLoginPrompt && (
|
||||
<LoginPrompt
|
||||
loginRedirectMessage="Sign in to start chatting with a specialized agent"
|
||||
onOpenChange={setShowLoginPrompt} />
|
||||
}
|
||||
<Alert className='bg-secondary border-none my-4'>
|
||||
onOpenChange={setShowLoginPrompt}
|
||||
/>
|
||||
)}
|
||||
<Alert className="bg-secondary border-none my-4">
|
||||
<AlertDescription>
|
||||
<Lightning weight={'fill'} className='h-4 w-4 text-purple-400 inline' />
|
||||
<span className='font-bold'>How it works</span> Use any of these specialized personas to tune your conversation to your needs.
|
||||
<Lightning weight={"fill"} className="h-4 w-4 text-purple-400 inline" />
|
||||
<span className="font-bold">How it works</span> Use any of these
|
||||
specialized personas to tune your conversation to your needs.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
<div className={`${styles.agentList}`}>
|
||||
{data.map(agent => (
|
||||
<AgentCard key={agent.slug} data={agent} userProfile={authenticatedData} isMobileWidth={isMobileWidth} />
|
||||
{data.map((agent) => (
|
||||
<AgentCard
|
||||
key={agent.slug}
|
||||
data={agent}
|
||||
userProfile={authenticatedData}
|
||||
isMobileWidth={isMobileWidth}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user