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={{

View File

@@ -1,6 +1,6 @@
{
"name": "khoj-ai",
"version": "1.30.1",
"version": "1.31.0",
"private": true,
"scripts": {
"dev": "next dev",
@@ -62,6 +62,9 @@
"react-hook-form": "^7.52.1",
"shadcn-ui": "^0.8.0",
"swr": "^2.2.5",
"tailwind-merge": "^2.3.0",
"tailwindcss": "^3.4.6",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5",
"vaul": "^0.9.1",
"zod": "^3.23.8"
@@ -82,9 +85,6 @@
"lint-staged": "^15.2.7",
"nodemon": "^3.1.3",
"prettier": "3.3.3",
"tailwind-merge": "^2.3.0",
"tailwindcss": "^3.4.6",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5"
},
"prettier": {

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="200"
height="200"
viewBox="0 0 200 200"
className="fill-zinc-950 dark:fill-zinc-300"
version="1.1"
id="svg14"
sodipodi:docname="khoj.svg"
inkscape:export-filename="khoj_lantern_512x512.png"
inkscape:export-xdpi="245.75999"
inkscape:export-ydpi="245.75999"
inkscape:version="1.3 (0e150ed, 2023-07-21)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview14"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="1.18"
inkscape:cx="99.576271"
inkscape:cy="77.542373"
inkscape:window-width="1680"
inkscape:window-height="1022"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg14"
inkscape:export-bgcolor="#ffffffe0" />
<g
clipPath="url(#clip0_45_75)"
id="g14"
transform="matrix(1.0349659,0,0,1.0429132,13.551981,6.7616199)">
<!-- Fire -->
<path
d="m 57.9394,93.0404 9.6002,-43.9341 c 1.2591,0.4205 4.1969,2.8379 7.1871,2.8379 3.7772,0 4.5116,-4.5196 11.4364,-6.1487 5.5084,-1.3138 10.1773,-0.3679 12.9053,1.8394 0,0 0.6295,5.991 2.0986,7.7778 1.364,1.6291 3.462,1.051 3.462,1.051 3.043,14.3469 9.443,44.1973 10.65,48.7693 1.521,5.728 -4.355,8.881 -6.61,14.136 -2.256,5.256 -14.427,7.515 -21.6141,8.251 -7.187,0.736 -27.3319,-7.988 -29.1155,-10.405 -1.7837,-2.418 -2.5706,-7.358 -2.8329,-12.035 -0.2623,-3.731 1.7837,-9.6696 2.8329,-12.1396 z"
fill="#fae80b"
id="path1" />
<path
d="m 57.9394,92.9879 4.3542,-18.1833 c 1.259,0.4205 4.669,-1.3663 9.1806,-5.7282 2.6755,-2.6276 8.3936,-10.1952 14.584,-13.7163 4.5116,-2.5751 8.8658,-1.1561 11.5413,1.0511 0,0 2.4135,1.2087 5.2465,1.2087 2.098,0 1.783,-1.2087 1.783,-1.2087 3.043,14.3469 9.443,44.1968 10.65,48.7688 1.521,5.728 -4.355,8.882 -6.61,14.137 -2.256,5.255 -14.427,7.515 -21.6141,8.251 -7.187,0.735 -27.3319,-7.988 -29.1155,-10.406 -1.7837,-2.417 -2.5706,-7.357 -2.8329,-12.034 -0.2623,-3.679 1.7837,-9.6176 2.8329,-12.1401 z"
fill="#ffcc09"
id="path2" />
<path
d="m 69.3233,123.731 c -10.3347,-2.627 -13.4299,-14.662 -13.6922,-20.338 3.1476,3.206 11.4889,9.407 19.5153,8.566 10.0724,-1.051 17.5743,-5.097 25.7056,-11.456 8.131,-6.3064 13.692,-3.3109 13.692,6.306 0,9.617 -6.925,11.299 -9.023,15.661 -2.098,4.414 -23.24,4.572 -36.1977,1.261 z"
fill="#fba719"
id="path3" />
<!-- Lamp -->
<path
d="m 46.6374,143.679 c -3.0428,0 -5.351,-0.841 -6.9773,-2.575 -2.9378,-3.1 -2.1509,-7.725 -2.0984,-7.935 0.1573,-0.894 14.7414,-89.2874 16.1053,-97.9586 1.2591,-8.0406 10.3872,-9.8274 15.0562,-9.6172 l -0.1049,3.1532 c -0.0525,0 -2.8853,-0.1051 -5.7707,0.7883 -3.5673,1.1036 -5.6132,3.1531 -6.0854,6.1487 -1.3115,8.6712 -15.948,97.1176 -16.1054,98.0106 0,0.053 -0.5246,3.311 1.3116,5.256 1.3115,1.366 3.5148,1.839 6.6624,1.418 l 0.3673,3.101 c -0.7869,0.158 -1.5738,0.21 -2.3607,0.21 z"
id="path4" />
<path
d="m 106.023,33.371 h -3.095 c 0.052,-0.1577 0.052,-0.3679 0.052,-0.5255 V 15.2403 c 0,-1.7343 -1.416,-3.1532 -3.1476,-3.1532 H 89.4977 c 1.3639,-1.2087 2.2558,-2.99549 2.2558,-4.99249 0,-3.67869 -2.9903,-6.6742 -6.6625,-6.6742 -3.6722,0 -6.6625,2.99551 -6.6625,6.6742 0,1.997 0.8394,3.78379 2.2558,4.99249 h -9.8626 c -1.7312,0 -3.1476,1.4189 -3.1476,3.1532 v 17.6052 c 0,0.2102 0,0.3678 0.0524,0.5255 h -3.6722 c -2.4656,0 -4.5116,2.0495 -4.5116,4.5195 0,2.5226 2.046,4.5196 4.5116,4.5196 h 42.0207 c 2.466,0 4.512,-2.0496 4.512,-4.5196 -0.053,-2.5225 -2.046,-4.5195 -4.564,-4.5195 z M 85.0385,3.52103 c 1.941,0 3.5149,1.57657 3.5149,3.52102 0,1.94446 -1.5739,3.52105 -3.5149,3.52105 -1.941,0 -3.5148,-1.57659 -3.5148,-3.52105 0,-1.94445 1.5738,-3.52102 3.5148,-3.52102 z"
id="path5" />
<path
d="m 123.177,143.679 c -0.734,0 -1.521,-0.052 -2.361,-0.157 l 0.368,-3.101 c 3.147,0.42 5.351,-0.105 6.662,-1.419 1.836,-1.944 1.312,-5.203 1.312,-5.203 -0.158,-0.893 -14.794,-89.3394 -16.106,-98.0106 -0.472,-2.9955 -2.518,-5.0451 -6.085,-6.1487 -2.885,-0.8934 -5.718,-0.7883 -5.771,-0.7883 l -0.105,-3.1531 c 4.669,-0.2102 13.85,1.5766 15.056,9.6171 1.312,8.6712 15.948,97.0646 16.106,97.9586 0.052,0.157 0.839,4.782 -2.099,7.883 -1.626,1.629 -3.934,2.522 -6.977,2.522 z"
id="path6" />
<path
d="m 122.443,151.142 -0.053,-21.337 c 0,-5.045 -12.748,-9.774 -12.748,-9.774 h -0.209 c -4.407,4.309 -11.8565,7.409 -24.1847,7.409 -12.3282,0 -19.7252,-3.1 -24.1843,-7.409 h -0.2099 c 0,0 -12.7478,4.729 -12.7478,9.774 0,5.045 0,21.337 0,21.337 0,0 -5.9281,3.836 -5.9281,8.303 0,4.467 0,7.725 0,7.725 0,0 5.0362,10.984 43.0701,10.984 h 0.0524 c 38.0343,0 43.0703,-10.984 43.0703,-10.984 0,0 0,-3.258 0,-7.725 0,-4.467 -5.928,-8.303 -5.928,-8.303 z"
id="path7" />
<path
d="m 117.931,87.658 c -1.206,-2.4174 -4.511,-7.1472 -5.875,-9.0916 l -8.132,-37.4176 -2.833,0.6307 11.647,53.4461 0.052,0.1577 c 0,0.0525 0.472,1.4714 0.84,3.6787 -0.578,0.5255 -1.26,0.998 -2.046,1.471 -3.253,1.945 -14.7943,5.361 -26.2831,8.461 -11.4364,-3.1 -22.9253,-6.516 -26.1778,-8.461 -0.8394,-0.525 -1.5738,-1.0506 -2.2034,-1.6287 0.3673,-2.1547 0.7869,-3.521 0.8394,-3.5736 V 95.2782 L 69.4053,41.5167 66.5725,40.886 58.3362,79.0394 c -1.5214,2.1546 -4.4592,6.4114 -5.5608,8.6712 -0.9443,1.8919 -1.9935,6.8318 1.259,11.1412 -0.7869,4.8872 -1.0492,12.9282 3.7247,19.5492 5.0362,6.99 14.2693,10.511 27.4893,10.511 13.2201,0 22.4536,-3.521 27.4896,-10.511 4.721,-6.569 4.511,-14.452 3.777,-19.339 3.515,-4.4145 2.413,-9.4595 1.416,-11.404 z m -1.836,0.946 c 0.63,1.3138 1.364,4.3618 -0.105,7.4099 -0.21,-0.7883 -0.367,-1.2613 -0.419,-1.4715 l -2.309,-10.5631 c 1.102,1.6817 2.256,3.4685 2.833,4.6247 z m -61.431,0 c 0.5246,-1.0511 1.4688,-2.5751 2.518,-4.1517 l -2.2033,10.0376 c -0.0525,0.1576 -0.2098,0.5781 -0.3672,1.2087 -1.2066,-2.943 -0.5246,-5.8334 0.0525,-7.0946 z m 1.9934,12.77 c 0.4197,0.316 0.9443,0.631 1.4165,0.946 3.0427,1.84 12.9053,4.835 23.24,7.725 -10.7544,2.891 -20.4596,5.256 -21.8761,5.624 -2.8853,-4.73 -3.1476,-10.196 -2.7804,-14.295 z m 28.6435,24.595 c -11.6463,0 -19.8825,-2.838 -24.4991,-8.461 3.305,-0.788 13.7447,-3.416 24.5515,-6.359 10.5971,2.89 20.9847,5.466 24.4467,6.359 -4.617,5.623 -12.8529,8.461 -24.4991,8.461 z m 25.8101,-10.3 c -1.941,-0.473 -11.3835,-2.838 -21.7707,-5.624 10.3872,-2.89 20.2497,-5.938 23.3447,-7.777 0.472,-0.263 0.84,-0.526 1.259,-0.841 0.367,4.099 0.053,9.564 -2.833,14.242 z"
id="path8" />
<path
d="m 39.188,44.7224 c -0.3147,0 -0.577,-0.1051 -0.8393,-0.2627 l -14.0594,-9.88 c -0.682,-0.4729 -0.8394,-1.3663 -0.3673,-2.0495 0.4722,-0.6832 1.364,-0.8409 2.046,-0.3679 l 14.0594,9.8799 c 0.682,0.473 0.8394,1.3664 0.3672,2.0496 -0.3147,0.4204 -0.7344,0.6306 -1.2066,0.6306 z"
id="path9" />
<path
d="M 18.8334,80.6685 1.67885,80.6159 c -0.786909,0 -1.468889,-0.6306 -1.468889,-1.4714 0,-0.8409 0.629519,-1.4715 1.468889,-1.4715 l 17.15455,0.0525 c 0.7869,0 1.4689,0.6307 1.4689,1.4715 0,0.8409 -0.682,1.4715 -1.4689,1.4715 z"
id="path10" />
<path
d="m 13.2726,128.754 c -0.4722,0 -0.9443,-0.21 -1.2066,-0.683 -0.4197,-0.683 -0.2098,-1.576 0.4197,-1.997 l 14.4266,-9.249 c 0.682,-0.421 1.5738,-0.263 1.9935,0.42 0.4197,0.683 0.2099,1.577 -0.4197,1.997 l -14.4266,9.25 c -0.2098,0.157 -0.4721,0.262 -0.7869,0.262 z"
id="path11" />
<path
d="m 130.889,43.6188 c -0.472,0 -0.892,-0.2102 -1.207,-0.6307 -0.472,-0.6831 -0.314,-1.5765 0.368,-2.0495 l 14.059,-9.8799 c 0.629,-0.473 1.574,-0.3154 2.046,0.3678 0.472,0.6832 0.315,1.5766 -0.367,2.0496 l -14.06,9.8799 c -0.262,0.1577 -0.524,0.2628 -0.839,0.2628 z"
id="path12" />
<path
d="m 151.244,79.5649 c -0.787,0 -1.469,-0.6306 -1.469,-1.4715 0,-0.7883 0.629,-1.4715 1.469,-1.4715 l 17.154,-0.0525 c 0.787,0 1.469,0.6306 1.469,1.4715 0,0.7883 -0.629,1.4714 -1.469,1.4714 z"
id="path13" />
<path
d="m 156.804,127.598 c -0.262,0 -0.524,-0.052 -0.787,-0.21 l -14.426,-9.249 c -0.682,-0.421 -0.892,-1.314 -0.42,-1.997 0.42,-0.684 1.312,-0.894 1.994,-0.421 l 14.426,9.249 c 0.682,0.421 0.892,1.314 0.42,1.997 -0.262,0.421 -0.734,0.631 -1.207,0.631 z"
id="path14" />
</g>
<defs
id="defs14">
<clipPath
id="clip0_45_75">
<rect
width="200"
height="200"
fill="currentColor"
id="rect14" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -1,11 +1,18 @@
{
"name": "Khoj",
"id": "https://app.khoj.dev",
"name": "Khoj AI - Get Answers, Create Anything",
"short_name": "Khoj",
"display": "standalone",
"start_url": "/",
"description": "The open, personal AI for your digital brain. You can ask Khoj to draft a message, paint your imagination, find information on the internet and even answer questions from your documents.",
"scope": "/",
"description": "Khoj is your open, personal AI. Gets answers from the internet and your documents. Quickly draft messages, summarize any information, generate beautiful paintings, create personal agents and do deep research.",
"categories": ["productivity", "utilities", "personalization", "education"],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"orientation": "natural",
"launch_handler": {
"client_mode": ["navigate-existing", "auto"]
},
"icons": [
{
"src": "/static/assets/icons/khoj_lantern_128x128.png",
@@ -16,6 +23,24 @@
"src": "/static/assets/icons/khoj_lantern_256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/static/assets/icons/khoj_lantern_512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/static/assets/icons/khoj_lantern_512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/static/assets/icons/khoj_lantern_1200x1200.png",
"sizes": "1200x1200",
"type": "image/png",
"purpose": "any"
}
],
"screenshots": [

View File

@@ -55,6 +55,9 @@ const config = {
},
},
extend: {
fontFamily: {
sans: ["var(--font-noto-sans)", "var(--font-noto-sans-arabic)"],
},
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",