Merge branch 'master' of github.com:khoj-ai/khoj into features/new-sign-in-page

This commit is contained in:
sabaimran
2024-12-11 10:30:13 -08:00
166 changed files with 3869 additions and 4150 deletions

View File

@@ -1,8 +1,7 @@
import type { Metadata } from "next";
import { Noto_Sans } from "next/font/google";
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
import "../globals.css";
const inter = Noto_Sans({ subsets: ["latin"] });
import { ContentSecurityPolicy } from "../common/layoutHelper";
export const metadata: Metadata = {
title: "Khoj AI - Agents",
@@ -33,20 +32,9 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<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';
connect-src 'self' https://ipapi.co/json ws://localhost:42110;
style-src 'self' https://assets.khoj.dev 'unsafe-inline' https://fonts.googleapis.com;
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>
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
<ContentSecurityPolicy />
<body>{children}</body>
</html>
);
}

View File

@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Toaster } from "@/components/ui/toaster";
import "../globals.css";
import { ContentSecurityPolicy } from "../common/layoutHelper";
export const metadata: Metadata = {
title: "Khoj AI - Automations",
@@ -32,9 +33,12 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<div>
{children}
<Toaster />
</div>
<html>
<ContentSecurityPolicy />
<body>
{children}
<Toaster />
</body>
</html>
);
}

View File

@@ -994,7 +994,7 @@ export default function Automations() {
const [suggestedAutomations, setSuggestedAutomations] = useState<AutomationsData[]>([]);
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
const isMobileWidth = useIsMobileWidth();
const ipLocationData = useIPLocationData();
const { locationData, locationDataError, locationDataLoading } = useIPLocationData();
useEffect(() => {
if (newAutomationData) {
@@ -1044,18 +1044,18 @@ export default function Automations() {
{authenticatedData.email}
</span>
) : null}
{ipLocationData && (
{locationData && (
<span className="rounded-lg text-sm border-secondary border p-1 flex items-center shadow-sm">
<MapPinSimple className="h-4 w-4 mr-2 inline text-purple-500" />
{ipLocationData
? `${ipLocationData.city}, ${ipLocationData.country}`
{locationData
? `${locationData.city}, ${locationData.country}`
: "Unknown"}
</span>
)}
{ipLocationData && (
{locationData && (
<span className="rounded-lg text-sm border-secondary border p-1 flex items-center shadow-sm">
<Clock className="h-4 w-4 mr-2 inline text-green-500" />
{ipLocationData ? `${ipLocationData.timezone}` : "Unknown"}
{locationData ? `${locationData.timezone}` : "Unknown"}
</span>
)}
</div>
@@ -1086,7 +1086,7 @@ export default function Automations() {
setNewAutomationData={setNewAutomationData}
authenticatedData={authenticatedData}
isCreating={isCreating}
ipLocationData={ipLocationData}
ipLocationData={locationData}
/>
) : (
<Button
@@ -1103,7 +1103,7 @@ export default function Automations() {
<SharedAutomationCard
isMobileWidth={isMobileWidth}
authenticatedData={authenticatedData}
locationData={ipLocationData}
locationData={locationData}
isLoggedIn={authenticatedData ? true : false}
setShowLoginPrompt={setShowLoginPrompt}
setNewAutomationData={setNewAutomationData}
@@ -1125,7 +1125,7 @@ export default function Automations() {
setNewAutomationData={setNewAutomationData}
authenticatedData={authenticatedData}
isCreating={isCreating}
ipLocationData={ipLocationData}
ipLocationData={locationData}
/>
) : (
<Button
@@ -1147,7 +1147,7 @@ export default function Automations() {
key={automation.id}
authenticatedData={authenticatedData}
automation={automation}
locationData={ipLocationData}
locationData={locationData}
isLoggedIn={authenticatedData ? true : false}
setShowLoginPrompt={setShowLoginPrompt}
/>
@@ -1158,7 +1158,7 @@ export default function Automations() {
key={automation.id}
authenticatedData={authenticatedData}
automation={automation}
locationData={ipLocationData}
locationData={locationData}
isLoggedIn={authenticatedData ? true : false}
setShowLoginPrompt={setShowLoginPrompt}
/>
@@ -1173,7 +1173,7 @@ export default function Automations() {
key={automation.id}
authenticatedData={authenticatedData}
automation={automation}
locationData={ipLocationData}
locationData={locationData}
isLoggedIn={authenticatedData ? true : false}
setShowLoginPrompt={setShowLoginPrompt}
suggestedCard={true}

View File

@@ -1,8 +1,7 @@
import type { Metadata } from "next";
import { Noto_Sans } from "next/font/google";
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
import "../globals.css";
const inter = Noto_Sans({ subsets: ["latin"] });
import { ContentSecurityPolicy } from "../common/layoutHelper";
export const metadata: Metadata = {
title: "Khoj AI - Chat",
@@ -34,20 +33,9 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<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';
connect-src 'self' blob: https://ipapi.co/json ws://localhost:42110;
style-src 'self' https://assets.khoj.dev 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: blob: 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}>
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
<ContentSecurityPolicy />
<body>
{children}
<script
dangerouslySetInnerHTML={{

View File

@@ -184,8 +184,10 @@ export default function Chat() {
useState<AbortController | null>(null);
const [triggeredAbort, setTriggeredAbort] = useState(false);
const locationData = useIPLocationData() || {
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
const { locationData, locationDataError, locationDataLoading } = useIPLocationData() || {
locationData: {
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
},
};
const authenticatedData = useAuthenticatedData();
const isMobileWidth = useIsMobileWidth();
@@ -239,9 +241,13 @@ export default function Chat() {
useEffect(() => {
if (processQuerySignal) {
if (locationDataLoading) {
return;
}
chat();
}
}, [processQuerySignal]);
}, [processQuerySignal, locationDataLoading]);
async function readChatStream(response: Response) {
if (!response.ok) throw new Error(response.statusText);
@@ -380,7 +386,7 @@ export default function Chat() {
<title>
{`${defaultTitle}${!!title && title !== defaultTitle ? `: ${title}` : ""}`}
</title>
<div>
<div className={isMobileWidth ? "h-1" : "h-auto"}>
<SidePanel
conversationId={conversationId}
uploadedFiles={[]}
@@ -389,9 +395,9 @@ export default function Chat() {
</div>
<div className={styles.chatBox}>
<div className={styles.chatBoxBody}>
{!isMobileWidth && conversationId && (
{conversationId && (
<div
className={`${styles.chatTitleWrapper} text-nowrap text-ellipsis overflow-hidden max-w-screen-md grid items-top font-bold mr-8 pt-6 col-auto h-fit`}
className={`${styles.chatTitleWrapper} text-nowrap text-ellipsis overflow-hidden max-w-screen-md grid items-top font-bold mx-2 md:mr-8 md:pt-6 col-auto h-fit`}
>
{title && (
<h2
@@ -403,7 +409,7 @@ export default function Chat() {
<ChatSessionActionMenu
conversationId={conversationId}
setTitle={setTitle}
sizing="md"
sizing={isMobileWidth ? "sm" : "md"}
/>
</div>
)}

View File

@@ -9,6 +9,7 @@ export interface UserProfile {
is_active: boolean;
has_documents: boolean;
detail: string;
khoj_version: string;
}
const fetcher = (url: string) =>

View File

@@ -1,3 +1,4 @@
import { AttachedFileText } from "../components/chatInputArea/chatInputArea";
import {
CodeContext,
Context,
@@ -16,6 +17,12 @@ export interface MessageMetadata {
turnId: string;
}
export interface GeneratedAssetsData {
images: string[];
excalidrawDiagram: string;
files: AttachedFileText[];
}
export interface ResponseWithIntent {
intentType: string;
response: string;
@@ -84,6 +91,8 @@ export function processMessageChunk(
if (!currentMessage || !chunk || !chunk.type) return { context, onlineContext, codeContext };
console.log(`chunk type: ${chunk.type}`);
if (chunk.type === "status") {
console.log(`status: ${chunk.data}`);
const statusMessage = chunk.data as string;
@@ -98,6 +107,20 @@ export function processMessageChunk(
} else if (chunk.type === "metadata") {
const messageMetadata = chunk.data as MessageMetadata;
currentMessage.turnId = messageMetadata.turnId;
} else if (chunk.type === "generated_assets") {
const generatedAssets = chunk.data as GeneratedAssetsData;
if (generatedAssets.images) {
currentMessage.generatedImages = generatedAssets.images;
}
if (generatedAssets.excalidrawDiagram) {
currentMessage.generatedExcalidrawDiagram = generatedAssets.excalidrawDiagram;
}
if (generatedAssets.files) {
currentMessage.generatedFiles = generatedAssets.files;
}
} else if (chunk.type === "message") {
const chunkData = chunk.data;
// Here, handle if the response is a JSON response with an image, but the intentType is excalidraw

View File

@@ -51,6 +51,7 @@ import {
FilePdf,
FileMd,
MicrosoftWordLogo,
Microscope,
} from "@phosphor-icons/react";
import { OrgMode } from "@/app/components/logo/fileLogo";
@@ -219,6 +220,10 @@ export function getIconForSlashCommand(command: string, customClassName: string
return <Code className={className} />;
}
if (command.includes("research")) {
return <Microscope className={className} />;
}
return <ArrowRight className={className} />;
}

View File

@@ -0,0 +1,16 @@
export function ContentSecurityPolicy() {
return (
<meta
httpEquiv="Content-Security-Policy"
content="default-src 'self' https://assets.khoj.dev;
media-src * blob:;
script-src 'self' https://assets.khoj.dev https://app.chatwoot.com 'unsafe-inline' 'unsafe-eval';
connect-src 'self' blob: https://ipapi.co/json ws://localhost:42110;
style-src 'self' https://assets.khoj.dev 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: blob: 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 'self' https://app.chatwoot.com;
object-src 'none';"
></meta>
);
}

View File

@@ -23,32 +23,30 @@ export function welcomeConsole() {
`%c %s`,
"font-family:monospace",
`
__ __ __ __ ______ __ _____ __
/\\ \\/ / /\\ \\_\\ \\ /\\ __ \\ /\\ \\ /\\ __ \\ /\\ \\
\\ \\ _"-. \\ \\ __ \\ \\ \\ \\/\\ \\ _\\_\\ \\ \\ \\ __ \\ \\ \\ \\
\\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\ \\_____\\ /\\_____\\ \\ \\_\\ \\_\\ \\ \\_\\
\\/_/\\/_/ \\/_/\\/_/ \\/_____/ \\/_____/ \\/_/\\/_/ \\/_/
__ __ __ __ ______ __ _____ __
/\\ \\/ / /\\ \\_\\ \\ /\\ __ \\ /\\ \\ /\\ __ \\ /\\ \\
\\ \\ _"-. \\ \\ __ \\ \\ \\ \\/\\ \\ _\\_\\ \\ \\ \\ __ \\ \\ \\ \\
\\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\ \\_____\\ /\\_____\\ \\ \\_\\ \\_\\ \\ \\_\\
\\/_/\\/_/ \\/_/\\/_/ \\/_____/ \\/_____/ \\/_/\\/_/ \\/_/
Greetings traveller,
Greetings traveller,
I am ✨Khoj✨, your open-source, personal AI copilot.
I am ✨Khoj✨, your open-source, personal AI copilot.
See my source code at https://github.com/khoj-ai/khoj
Read my operating manual at https://docs.khoj.dev
`,
See my source code at https://github.com/khoj-ai/khoj
Read my operating manual at https://docs.khoj.dev
`,
);
}
export function useIPLocationData() {
const { data: locationData, error: locationDataError } = useSWR<LocationData>(
"/api/ip",
locationFetcher,
{ revalidateOnFocus: false },
);
if (locationDataError || !locationData) return;
return locationData;
const {
data: locationData,
error: locationDataError,
isLoading: locationDataLoading,
} = useSWR<LocationData>("/api/ip", locationFetcher, { revalidateOnFocus: false });
return { locationData, locationDataError, locationDataLoading };
}
export function useIsMobileWidth() {

View File

@@ -54,6 +54,12 @@ function TrainOfThoughtComponent(props: TrainOfThoughtComponentProps) {
const lastIndex = props.trainOfThought.length - 1;
const [collapsed, setCollapsed] = useState(props.completed);
useEffect(() => {
if (props.completed) {
setCollapsed(true);
}
}, [props.completed]);
return (
<div
className={`${!collapsed ? styles.trainOfThought + " shadow-sm" : ""}`}
@@ -410,6 +416,9 @@ export default function ChatHistory(props: ChatHistoryProps) {
"inferred-queries": message.inferredQueries || [],
},
conversationId: props.conversationId,
images: message.generatedImages,
queryFiles: message.generatedFiles,
excalidrawDiagram: message.generatedExcalidrawDiagram,
turnId: messageTurnId,
}}
conversationId={props.conversationId}

View File

@@ -77,6 +77,21 @@ div.imageWrapper img {
border-radius: 8px;
}
div.khoj div.imageWrapper img {
height: 512px;
}
div.khoj div.imageWrapper {
flex: 1 1 auto;
}
div.khoj div.imagesContainer {
display: flex;
flex-wrap: wrap;
flex-direction: row;
overflow-x: hidden;
}
div.chatMessageContainer > img {
width: auto;
height: auto;
@@ -178,4 +193,9 @@ div.trainOfThoughtElement ul {
div.youfullHistory {
max-width: 90%;
}
div.khoj div.imageWrapper img {
width: 100%;
height: auto;
}
}

View File

@@ -163,6 +163,7 @@ export interface SingleChatMessage {
conversationId: string;
turnId?: string;
queryFiles?: AttachedFileText[];
excalidrawDiagram?: string;
}
export interface StreamMessage {
@@ -180,6 +181,10 @@ export interface StreamMessage {
inferredQueries?: string[];
turnId?: string;
queryFiles?: AttachedFileText[];
excalidrawDiagram?: string;
generatedFiles?: AttachedFileText[];
generatedImages?: string[];
generatedExcalidrawDiagram?: string;
}
export interface ChatHistoryData {
@@ -264,6 +269,9 @@ interface ChatMessageProps {
onDeleteMessage: (turnId?: string) => void;
conversationId: string;
turnId?: string;
generatedImage?: string;
excalidrawDiagram?: string;
generatedFiles?: AttachedFileText[];
}
interface TrainOfThoughtProps {
@@ -389,9 +397,8 @@ const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) =>
// Prepare initial message for rendering
let message = props.chatMessage.message;
if (props.chatMessage.intent && props.chatMessage.intent.type == "excalidraw") {
message = props.chatMessage.intent["inferred-queries"][0];
setExcalidrawData(props.chatMessage.message);
if (props.chatMessage.excalidrawDiagram) {
setExcalidrawData(props.chatMessage.excalidrawDiagram);
}
// Replace LaTeX delimiters with placeholders
@@ -401,27 +408,6 @@ const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) =>
.replace(/\\\[/g, "LEFTBRACKET")
.replace(/\\\]/g, "RIGHTBRACKET");
const intentTypeHandlers = {
"text-to-image": (msg: string) => `![generated image](data:image/png;base64,${msg})`,
"text-to-image2": (msg: string) => `![generated image](${msg})`,
"text-to-image-v3": (msg: string) =>
`![generated image](data:image/webp;base64,${msg})`,
excalidraw: (msg: string) => msg,
};
// Handle intent-specific rendering
if (props.chatMessage.intent) {
const { type, "inferred-queries": inferredQueries } = props.chatMessage.intent;
if (type in intentTypeHandlers) {
message = intentTypeHandlers[type as keyof typeof intentTypeHandlers](message);
}
if (type.includes("text-to-image") && inferredQueries?.length > 0) {
message += `\n\n${inferredQueries[0]}`;
}
}
// Replace file links with base64 data
message = renderCodeGenImageInline(message, props.chatMessage.codeContext);

View File

@@ -0,0 +1,35 @@
"use client";
import Script from "next/script";
export function ChatwootWidget() {
return (
<Script
id="chatwoot-widget"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.chatwootSettings = {
position: "right",
type: "standard",
launcherTitle: "Chat with us"
};
(function(d,t) {
var BASE_URL="https://app.chatwoot.com";
var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src=BASE_URL+"/packs/js/sdk.js";
g.defer = true;
g.async = true;
s.parentNode.insertBefore(g,s);
g.onload=function(){
window.chatwootSDK.run({
websiteToken: '5uV59Ay2pvMJenJary2hvvVM',
baseUrl: BASE_URL
})
}
})(document,"script");
`,
}}
/>
);
}

View File

@@ -52,7 +52,7 @@ div.settingsMenuOptions {
grid-auto-flow: row;
position: absolute;
background-color: var(--background-color);
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
top: 64px;
text-align: left;
padding: 8px;
@@ -69,6 +69,6 @@ div.settingsMenuOptions {
}
div.titleBar {
padding: 8px;
padding: 4px;
}
}

View File

@@ -22,7 +22,7 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Moon, Sun, UserCircle, Question, GearFine, ArrowRight } from "@phosphor-icons/react";
import { Moon, Sun, UserCircle, Question, GearFine, ArrowRight, Code } from "@phosphor-icons/react";
import { KhojAgentLogo, KhojAutomationLogo, KhojSearchLogo } from "../logo/khojLogo";
import { useIsMobileWidth } from "@/app/common/utils";
@@ -37,6 +37,15 @@ function SubscriptionBadge({ is_active }: { is_active: boolean }) {
);
}
function VersionBadge({ version }: { version: string }) {
return (
<div className="flex flex-row items-center">
<div className="w-3 h-3 rounded-full bg-green-500 mr-1"></div>
<p className="text-xs">{version}</p>
</div>
);
}
export default function NavMenu() {
const userData = useAuthenticatedData();
const [darkMode, setDarkMode] = useState(false);
@@ -99,6 +108,9 @@ export default function NavMenu() {
<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>
</DropdownMenuItem>
<DropdownMenuSeparator />
@@ -143,18 +155,18 @@ export default function NavMenu() {
</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 />
{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>
)}
<DropdownMenuItem>
<Link href="https://docs.khoj.dev" className="no-underline w-full">
<div className="flex flex-rows">
@@ -163,6 +175,17 @@ export default function NavMenu() {
</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">
@@ -207,6 +230,9 @@ export default function NavMenu() {
<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" />
@@ -251,6 +277,16 @@ export default function NavMenu() {
</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>
@@ -264,16 +300,18 @@ export default function NavMenu() {
</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>
)}
<MenubarItem>
<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>
</MenubarItem>
{userData ? (
<MenubarItem>
<Link href="/auth/logout" className="no-underline w-full">

View File

@@ -105,7 +105,6 @@ import { ScrollAreaScrollbar } from "@radix-ui/react-scroll-area";
import { KhojLogoType } from "@/app/components/logo/khojLogo";
import NavMenu from "@/app/components/navMenu/navMenu";
import { getIconFromIconName } from "@/app/common/iconUtils";
import AgentProfileCard from "../profileCard/profileCard";
// Define a fetcher function
const fetcher = (url: string) =>
@@ -627,43 +626,56 @@ export function ChatSessionActionMenu(props: ChatSessionActionMenuProps) {
const size = sizeClass();
return (
<DropdownMenu onOpenChange={(open) => setIsOpen(open)} open={isOpen}>
<DropdownMenuTrigger>
<DotsThreeVertical className={`${size}`} />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<Button
className="p-0 text-sm h-auto"
variant={"ghost"}
onClick={() => setIsRenaming(true)}
>
<Pencil className={`mr-2 ${size}`} />
Rename
</Button>
</DropdownMenuItem>
<DropdownMenuItem>
<Button
className="p-0 text-sm h-auto"
variant={"ghost"}
onClick={() => setIsSharing(true)}
>
<Share className={`mr-2 ${size}`} />
Share
</Button>
</DropdownMenuItem>
<DropdownMenuItem>
<Button
className="p-0 text-sm h-auto text-rose-300 hover:text-rose-400"
variant={"ghost"}
onClick={() => setIsDeleting(true)}
>
<Trash className={`mr-2 ${size}`} />
Delete
</Button>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<div className="flex items-center gap-2">
{(props.sizing === "lg" || props.sizing === "md") && (
<Button
className="p-0 text-sm h-auto"
variant={"ghost"}
onClick={() => setIsSharing(true)}
>
<Share className={`${size}`} />
</Button>
)}
<DropdownMenu onOpenChange={(open) => setIsOpen(open)} open={isOpen}>
<DropdownMenuTrigger>
<DotsThreeVertical className={`${size}`} />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<Button
className="p-0 text-sm h-auto"
variant={"ghost"}
onClick={() => setIsRenaming(true)}
>
<Pencil className={`mr-2 ${size}`} />
Rename
</Button>
</DropdownMenuItem>
{props.sizing === "sm" && (
<DropdownMenuItem>
<Button
className="p-0 text-sm h-auto"
variant={"ghost"}
onClick={() => setIsSharing(true)}
>
<Share className={`mr-2 ${size}`} />
Share
</Button>
</DropdownMenuItem>
)}
<DropdownMenuItem>
<Button
className="p-0 text-sm h-auto text-rose-300 hover:text-rose-400"
variant={"ghost"}
onClick={() => setIsDeleting(true)}
>
<Trash className={`mr-2 ${size}`} />
Delete
</Button>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}
@@ -685,7 +697,11 @@ function ChatSession(props: ChatHistory) {
>
<p className={styles.session}>{title}</p>
</Link>
<ChatSessionActionMenu conversationId={props.conversation_id} setTitle={setTitle} />
<ChatSessionActionMenu
conversationId={props.conversation_id}
setTitle={setTitle}
sizing="sm"
/>
</div>
);
}
@@ -726,7 +742,6 @@ function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
}
});
});
console.log(agentNameToStyleMapLocal);
setAgentNameToStyleMap(agentNameToStyleMapLocal);
setAgentOptions(agents);
}
@@ -950,7 +965,7 @@ export default function SidePanel(props: SidePanelProps) {
}}
>
<DrawerTrigger>
<Sidebar className="h-8 w-8 mx-2" weight="thin" />
<Sidebar className="h-6 w-6 mx-2" weight="thin" />
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
@@ -1022,7 +1037,7 @@ export default function SidePanel(props: SidePanelProps) {
</div>
)}
{props.isMobileWidth && (
<Link href="/" className="content-center">
<Link href="/" className="content-center h-fit self-center">
<KhojLogoType />
</Link>
)}

View File

@@ -724,12 +724,6 @@ export const suggestionsData: Suggestion[] = [
description: "Draw a diagram illustrating the structure of the United States government.",
link: "",
},
{
type: SuggestionType.Health,
color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR,
description: "Draw a diagram of the human skeletal system.",
link: "",
},
{
type: SuggestionType.Code,
color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR,

View File

@@ -0,0 +1,13 @@
import { Noto_Sans, Noto_Sans_Arabic } from "next/font/google";
export const noto_sans = Noto_Sans({
subsets: ["latin", "latin-ext", "cyrillic", "cyrillic-ext", "devanagari", "vietnamese"],
display: "swap",
variable: "--font-noto-sans",
});
export const noto_sans_arabic = Noto_Sans_Arabic({
subsets: ["arabic"],
display: "swap",
variable: "--font-noto-sans-arabic",
});

View File

@@ -1,7 +1,6 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+Arabic:wght@100..900&family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap");
@layer base {
:root {
@@ -25,7 +24,7 @@
--input: 220 13% 91%;
--ring: 24.6 95% 53.1%;
--radius: 0.5rem;
--font-family: "Noto Sans", "Noto Sans Arabic", sans-serif !important;
--font-family: var(--font-noto-sans), var(--font-noto-sans-arabic), sans-serif !important;
/* Khoj Custom Colors */
--frosted-background-color: 20 13% 95%;
@@ -188,7 +187,7 @@
--border: 0 0% 9%;
--input: 0 0% 9%;
--ring: 20.5 90.2% 48.2%;
--font-family: "Noto Sans", "Noto Sans Arabic", sans-serif !important;
--font-family: var(--font-noto-sans), var(--font-noto-sans-arabic), sans-serif !important;
/* Imported from highlight.js */
pre code.hljs {

View File

@@ -1,8 +1,7 @@
import type { Metadata } from "next";
import { Noto_Sans } from "next/font/google";
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
import "./globals.css";
const inter = Noto_Sans({ subsets: ["latin"] });
import { ContentSecurityPolicy } from "./common/layoutHelper";
export const metadata: Metadata = {
title: "Khoj AI - Home",
@@ -39,20 +38,9 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
{/* <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';
connect-src 'self' blob: https://ipapi.co/json ws://localhost:42110;
style-src 'self' https://assets.khoj.dev 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: blob: 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>
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
<ContentSecurityPolicy />
<body>{children}</body>
</html>
);
}

View File

@@ -1,6 +1,7 @@
import type { Metadata } from "next";
import "../globals.css";
import { ContentSecurityPolicy } from "../common/layoutHelper";
export const metadata: Metadata = {
title: "Khoj AI - Search",
@@ -31,5 +32,10 @@ export default function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
return <div>{children}</div>;
return (
<html>
<ContentSecurityPolicy />
<body>{children}</body>
</html>
);
}

View File

@@ -1,9 +1,9 @@
import type { Metadata } from "next";
import { Noto_Sans } from "next/font/google";
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
import "../globals.css";
import { Toaster } from "@/components/ui/toaster";
const inter = Noto_Sans({ subsets: ["latin"] });
import { ContentSecurityPolicy } from "../common/layoutHelper";
import { ChatwootWidget } from "../components/chatWoot/ChatwootWidget";
export const metadata: Metadata = {
title: "Khoj AI - Settings",
@@ -34,21 +34,12 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<meta
httpEquiv="Content-Security-Policy"
content="default-src 'self' https://assets.khoj.dev;
script-src 'self' https://assets.khoj.dev 'unsafe-inline' 'unsafe-eval';
connect-src 'self' https://ipapi.co/json ws://localhost:42110;
style-src 'self' https://assets.khoj.dev 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https://*.khoj.dev https://*.googleusercontent.com;
font-src 'self' https://assets.khoj.dev https://fonts.gstatic.com;
child-src 'none';
object-src 'none';"
></meta>
<body className={inter.className}>
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
<ContentSecurityPolicy />
<body>
{children}
<Toaster />
<ChatwootWidget />
</body>
</html>
);

View File

@@ -1,8 +1,7 @@
import type { Metadata } from "next";
import { Noto_Sans } from "next/font/google";
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
import "../../globals.css";
const inter = Noto_Sans({ subsets: ["latin"] });
import { ContentSecurityPolicy } from "@/app/common/layoutHelper";
export const metadata: Metadata = {
title: "Khoj AI - Chat",
@@ -15,19 +14,9 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<meta
httpEquiv="Content-Security-Policy"
content="default-src 'self' https://assets.khoj.dev;
script-src 'self' https://assets.khoj.dev 'unsafe-inline' 'unsafe-eval';
connect-src 'self' blob: https://ipapi.co/json ws://localhost:42110;
style-src 'self' https://assets.khoj.dev 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: blob: 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}>
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
<ContentSecurityPolicy />
<body>
{children}
<script
dangerouslySetInnerHTML={{