mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-02 21:19:12 +00:00
Fix and Improve Chat sidebar and component setup on Web App (#1157)
- Set chatSidebar prompt, Setting name fields to empty str if value null - Track if agent modified in chatSidebar to simplify code, fix looping - Suppress spurious dark mode hydration warnings on the web app - Set key for chatMessage parent to get UX efficiently updated by react - Let only root next.js layout handle html, body tags, not child layouts
This commit is contained in:
@@ -1,8 +1,5 @@
|
||||
import type { Metadata } from "next";
|
||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
||||
import "../globals.css";
|
||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
||||
import { ThemeProvider } from "../components/providers/themeProvider";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Khoj AI - Agents",
|
||||
@@ -34,33 +31,10 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
export default function ChildLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
||||
<head>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
try {
|
||||
if (localStorage.getItem('theme') === 'dark' ||
|
||||
(!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
document.documentElement.classList.add('dark');
|
||||
}
|
||||
} catch (e) {}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</head>
|
||||
<ContentSecurityPolicy />
|
||||
<body>
|
||||
<ThemeProvider>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ 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",
|
||||
@@ -34,18 +33,15 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
export default function ChildLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html>
|
||||
<ContentSecurityPolicy />
|
||||
<body>
|
||||
{children}
|
||||
<Toaster />
|
||||
</body>
|
||||
</html>
|
||||
<>
|
||||
{children}
|
||||
<Toaster />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import type { Metadata } from "next";
|
||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
||||
import "../globals.css";
|
||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
||||
import { ThemeProvider } from "../components/providers/themeProvider";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Khoj AI - Chat",
|
||||
@@ -34,38 +31,19 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
export default function ChildLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
||||
<head>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
try {
|
||||
if (localStorage.getItem('theme') === 'dark' ||
|
||||
(!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
document.documentElement.classList.add('dark');
|
||||
}
|
||||
} catch (e) {}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</head>
|
||||
<ContentSecurityPolicy />
|
||||
<body>
|
||||
<ThemeProvider>
|
||||
{children}
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `window.EXCALIDRAW_ASSET_PATH = 'https://assets.khoj.dev/@excalidraw/excalidraw/dist/';`,
|
||||
}}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
<>
|
||||
{children}
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `window.EXCALIDRAW_ASSET_PATH = 'https://assets.khoj.dev/@excalidraw/excalidraw/dist/';`,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/h
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
|
||||
interface ModelSelectorProps extends PopoverProps {
|
||||
onSelect: (model: ModelOptions, userModification: boolean) => void;
|
||||
selectedModel?: string;
|
||||
onSelect: (model: ModelOptions) => void;
|
||||
disabled?: boolean;
|
||||
initialModel?: string;
|
||||
}
|
||||
@@ -49,9 +48,8 @@ export function ModelSelector({ ...props }: ModelSelectorProps) {
|
||||
setModels(userConfig.chat_model_options);
|
||||
if (!props.initialModel) {
|
||||
const selectedChatModelOption = userConfig.chat_model_options.find(model => model.id === userConfig.selected_chat_model_config);
|
||||
if (!selectedChatModelOption) {
|
||||
if (!selectedChatModelOption && userConfig.chat_model_options.length > 0) {
|
||||
setSelectedModel(userConfig.chat_model_options[0]);
|
||||
return;
|
||||
} else {
|
||||
setSelectedModel(selectedChatModelOption);
|
||||
}
|
||||
@@ -63,29 +61,10 @@ export function ModelSelector({ ...props }: ModelSelectorProps) {
|
||||
}, [userConfig, props.initialModel, isLoadingUserConfig]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.selectedModel && selectedModel && props.selectedModel !== selectedModel.name) {
|
||||
const model = models.find(model => model.name === props.selectedModel);
|
||||
setSelectedModel(model);
|
||||
if (selectedModel && userConfig) {
|
||||
props.onSelect(selectedModel);
|
||||
}
|
||||
else if (props.selectedModel === null && userConfig) {
|
||||
const selectedChatModelOption = userConfig.chat_model_options.find(model => model.id === userConfig.selected_chat_model_config);
|
||||
if (!selectedChatModelOption) {
|
||||
props.onSelect(userConfig.chat_model_options[0], false);
|
||||
return;
|
||||
} else {
|
||||
props.onSelect(selectedChatModelOption, false);
|
||||
}
|
||||
}
|
||||
}, [props.selectedModel, models]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedModel) {
|
||||
const userModification = selectedModel.id !== userConfig?.selected_chat_model_config;
|
||||
if (props.selectedModel !== selectedModel.name) {
|
||||
props.onSelect(selectedModel, userModification);
|
||||
}
|
||||
}
|
||||
}, [selectedModel]);
|
||||
}, [selectedModel, userConfig, props.onSelect]);
|
||||
|
||||
if (isLoadingUserConfig) {
|
||||
return (
|
||||
|
||||
@@ -28,10 +28,6 @@ interface ChatResponse {
|
||||
response: ChatHistoryData;
|
||||
}
|
||||
|
||||
interface ChatHistory {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
interface ChatHistoryProps {
|
||||
conversationId: string;
|
||||
setTitle: (title: string) => void;
|
||||
@@ -368,7 +364,7 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
||||
{data &&
|
||||
data.chat &&
|
||||
data.chat.map((chatMessage, index) => (
|
||||
<>
|
||||
<React.Fragment key={`chatMessage-${index}`}>
|
||||
{chatMessage.trainOfThought && chatMessage.by === "khoj" && (
|
||||
<TrainOfThoughtComponent
|
||||
trainOfThought={chatMessage.trainOfThought?.map(
|
||||
@@ -403,7 +399,7 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
||||
onDeleteMessage={handleDeleteMessage}
|
||||
conversationId={props.conversationId}
|
||||
/>
|
||||
</>
|
||||
</React.Fragment>
|
||||
))}
|
||||
{props.incomingMessages &&
|
||||
props.incomingMessages.map((message, index) => {
|
||||
|
||||
@@ -311,12 +311,14 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
isLoading: authenticationLoading,
|
||||
} = useAuthenticatedData();
|
||||
|
||||
const [customPrompt, setCustomPrompt] = useState<string | undefined>("");
|
||||
const [customPrompt, setCustomPrompt] = useState<string | undefined>();
|
||||
const [selectedModel, setSelectedModel] = useState<string | undefined>();
|
||||
const [inputTools, setInputTools] = useState<string[] | undefined>();
|
||||
const [outputModes, setOutputModes] = useState<string[] | undefined>();
|
||||
const [hasModified, setHasModified] = useState<boolean>(false);
|
||||
const [isDefaultAgent, setIsDefaultAgent] = useState<boolean>(agentData?.slug.toLowerCase() === "khoj" ? true : false);
|
||||
const [isDefaultAgent, setIsDefaultAgent] = useState<boolean>(!agentData || agentData?.slug.toLowerCase() === "khoj");
|
||||
const [displayInputTools, setDisplayInputTools] = useState<string[] | undefined>();
|
||||
const [displayOutputModes, setDisplayOutputModes] = useState<string[] | undefined>();
|
||||
|
||||
const [isSaving, setIsSaving] = useState<boolean>(false);
|
||||
|
||||
@@ -325,12 +327,14 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
function setupAgentData() {
|
||||
if (agentData) {
|
||||
setInputTools(agentData.input_tools);
|
||||
if (agentData.input_tools === undefined || agentData.input_tools.length === 0) {
|
||||
setInputTools(agentConfigurationOptions?.input_tools ? Object.keys(agentConfigurationOptions.input_tools) : []);
|
||||
setDisplayInputTools(agentData.input_tools);
|
||||
if (agentData.input_tools === undefined) {
|
||||
setDisplayInputTools(agentConfigurationOptions?.input_tools ? Object.keys(agentConfigurationOptions.input_tools) : []);
|
||||
}
|
||||
setOutputModes(agentData.output_modes);
|
||||
if (agentData.output_modes === undefined || agentData.output_modes.length === 0) {
|
||||
setOutputModes(agentConfigurationOptions?.output_modes ? Object.keys(agentConfigurationOptions.output_modes) : []);
|
||||
setDisplayOutputModes(agentData.output_modes);
|
||||
if (agentData.output_modes === undefined) {
|
||||
setDisplayOutputModes(agentConfigurationOptions?.output_modes ? Object.keys(agentConfigurationOptions.output_modes) : []);
|
||||
}
|
||||
|
||||
if (agentData.name.toLowerCase() === "khoj" || agentData.is_hidden === true) {
|
||||
@@ -351,16 +355,30 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
|
||||
useEffect(() => {
|
||||
setupAgentData();
|
||||
setHasModified(false);
|
||||
}, [agentData]);
|
||||
|
||||
// Track changes to the model, prompt, input tools and output modes fields
|
||||
useEffect(() => {
|
||||
if (!agentData || agentDataLoading) return; // Don't compare until data is loaded
|
||||
|
||||
const modelChanged = !!selectedModel && selectedModel !== agentData.chat_model;
|
||||
const promptChanged = !!customPrompt && customPrompt !== agentData.persona;
|
||||
|
||||
// Order independent check to ensure input tools or output modes haven't been changed.
|
||||
const toolsChanged = JSON.stringify(inputTools?.sort() || []) !== JSON.stringify(agentData.input_tools?.sort());
|
||||
const modesChanged = JSON.stringify(outputModes?.sort() || []) !== JSON.stringify(agentData.output_modes?.sort());
|
||||
|
||||
setHasModified(modelChanged || promptChanged || toolsChanged || modesChanged);
|
||||
|
||||
// Add agentDataLoading to dependencies to ensure it runs after loading finishes
|
||||
}, [selectedModel, customPrompt, inputTools, outputModes, agentData, agentDataLoading]);
|
||||
|
||||
function isValueChecked(value: string, existingSelections: string[]): boolean {
|
||||
return existingSelections.includes(value);
|
||||
}
|
||||
|
||||
function handleCheckToggle(value: string, existingSelections: string[]): string[] {
|
||||
setHasModified(true);
|
||||
|
||||
if (existingSelections.includes(value)) {
|
||||
return existingSelections.filter((v) => v !== value);
|
||||
}
|
||||
@@ -370,7 +388,6 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
|
||||
function handleCustomPromptChange(value: string) {
|
||||
setCustomPrompt(value);
|
||||
setHasModified(true);
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
@@ -430,11 +447,8 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
setHasModified(false);
|
||||
}
|
||||
|
||||
function handleModelSelect(model: string, userModification: boolean = true) {
|
||||
function handleModelSelect(model: string) {
|
||||
setSelectedModel(model);
|
||||
if (userModification) {
|
||||
setHasModified(true);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -488,7 +502,7 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
<SidebarMenuItem className="list-none">
|
||||
<Textarea
|
||||
className="w-full h-32 resize-none hover:resize-y"
|
||||
value={customPrompt}
|
||||
value={customPrompt || ""}
|
||||
onChange={(e) => handleCustomPromptChange(e.target.value)}
|
||||
readOnly={!isEditable}
|
||||
disabled={!isEditable} />
|
||||
@@ -514,9 +528,8 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
<SidebarMenuItem key={"model"} className="list-none">
|
||||
<ModelSelector
|
||||
disabled={!isEditable || !isSubscribed}
|
||||
onSelect={(model, userModification) => handleModelSelect(model.name, userModification)}
|
||||
onSelect={(model) => handleModelSelect(model.name)}
|
||||
initialModel={isDefaultAgent ? undefined : agentData?.chat_model}
|
||||
selectedModel={selectedModel}
|
||||
/>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
@@ -551,8 +564,12 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
<Checkbox
|
||||
id={key}
|
||||
className={`${isEditable ? "cursor-pointer" : ""}`}
|
||||
checked={isValueChecked(key, inputTools ?? [])}
|
||||
onCheckedChange={() => setInputTools(handleCheckToggle(key, inputTools ?? []))}
|
||||
checked={isValueChecked(key, displayInputTools ?? [])}
|
||||
onCheckedChange={() => {
|
||||
let updatedInputTools = handleCheckToggle(key, displayInputTools ?? [])
|
||||
setInputTools(updatedInputTools);
|
||||
setDisplayInputTools(updatedInputTools);
|
||||
}}
|
||||
disabled={!isEditable}
|
||||
>
|
||||
{key}
|
||||
@@ -584,8 +601,12 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
<Checkbox
|
||||
id={key}
|
||||
className={`${isEditable ? "cursor-pointer" : ""}`}
|
||||
checked={isValueChecked(key, outputModes ?? [])}
|
||||
onCheckedChange={() => setOutputModes(handleCheckToggle(key, outputModes ?? []))}
|
||||
checked={isValueChecked(key, displayOutputModes ?? [])}
|
||||
onCheckedChange={() => {
|
||||
let updatedOutputModes = handleCheckToggle(key, displayOutputModes ?? [])
|
||||
setOutputModes(updatedOutputModes);
|
||||
setDisplayOutputModes(updatedOutputModes);
|
||||
}}
|
||||
disabled={!isEditable}
|
||||
>
|
||||
{key}
|
||||
@@ -649,8 +670,8 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
||||
<AgentCreationForm
|
||||
customPrompt={customPrompt}
|
||||
selectedModel={selectedModel}
|
||||
inputTools={inputTools ?? []}
|
||||
outputModes={outputModes ?? []}
|
||||
inputTools={displayInputTools ?? []}
|
||||
outputModes={displayOutputModes ?? []}
|
||||
/>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
|
||||
@@ -48,7 +48,11 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
||||
<html
|
||||
lang="en"
|
||||
className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<head>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
@@ -65,9 +69,7 @@ export default function RootLayout({
|
||||
</head>
|
||||
<ContentSecurityPolicy />
|
||||
<body>
|
||||
<ThemeProvider>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
<ThemeProvider>{children}</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
import "../globals.css";
|
||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import { ThemeProvider } from "../components/providers/themeProvider";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Khoj AI - Search",
|
||||
@@ -29,33 +26,10 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
export default function ChildLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html>
|
||||
<head>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
try {
|
||||
if (localStorage.getItem('theme') === 'dark' ||
|
||||
(!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
document.documentElement.classList.add('dark');
|
||||
}
|
||||
} catch (e) {}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</head>
|
||||
<ContentSecurityPolicy />
|
||||
<body>
|
||||
<ThemeProvider>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import type { Metadata } from "next";
|
||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
||||
import "../globals.css";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
||||
import { ChatwootWidget } from "../components/chatWoot/ChatwootWidget";
|
||||
import { ThemeProvider } from "../components/providers/themeProvider";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Khoj AI - Settings",
|
||||
@@ -34,35 +31,16 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
export default function ChildLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
||||
<head>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
try {
|
||||
if (localStorage.getItem('theme') === 'dark' ||
|
||||
(!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
document.documentElement.classList.add('dark');
|
||||
}
|
||||
} catch (e) {}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</head>
|
||||
<ContentSecurityPolicy />
|
||||
<body>
|
||||
<ThemeProvider>
|
||||
{children}
|
||||
<Toaster />
|
||||
<ChatwootWidget />
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
<>
|
||||
{children}
|
||||
<Toaster />
|
||||
<ChatwootWidget />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -746,7 +746,7 @@ export default function SettingsView() {
|
||||
<Input
|
||||
type="text"
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
value={name}
|
||||
value={name || ""}
|
||||
className="w-full border border-gray-300 rounded-lg p-4 py-6"
|
||||
/>
|
||||
</CardContent>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import type { Metadata } from "next";
|
||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
||||
import "../../globals.css";
|
||||
import { ContentSecurityPolicy } from "@/app/common/layoutHelper";
|
||||
import { ThemeProvider } from "@/app/components/providers/themeProvider";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Khoj AI - Ask Anything",
|
||||
@@ -34,38 +31,19 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
export default function ChildLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
||||
<head>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
try {
|
||||
if (localStorage.getItem('theme') === 'dark' ||
|
||||
(!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
document.documentElement.classList.add('dark');
|
||||
}
|
||||
} catch (e) {}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</head>
|
||||
<ContentSecurityPolicy />
|
||||
<body>
|
||||
<ThemeProvider>
|
||||
{children}
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `window.EXCALIDRAW_ASSET_PATH = 'https://assets.khoj.dev/@excalidraw/excalidraw/dist/';`,
|
||||
}}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
<>
|
||||
{children}
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `window.EXCALIDRAW_ASSET_PATH = 'https://assets.khoj.dev/@excalidraw/excalidraw/dist/';`,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user