mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-09 05:39: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 type { Metadata } from "next";
|
||||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
|
||||||
import "../globals.css";
|
import "../globals.css";
|
||||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
|
||||||
import { ThemeProvider } from "../components/providers/themeProvider";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Khoj AI - Agents",
|
title: "Khoj AI - Agents",
|
||||||
@@ -34,33 +31,10 @@ export const metadata: Metadata = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function ChildLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return <>{children}</>;
|
||||||
<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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import type { Metadata } from "next";
|
|||||||
import { Toaster } from "@/components/ui/toaster";
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
|
|
||||||
import "../globals.css";
|
import "../globals.css";
|
||||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Khoj AI - Automations",
|
title: "Khoj AI - Automations",
|
||||||
@@ -34,18 +33,15 @@ export const metadata: Metadata = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function ChildLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html>
|
<>
|
||||||
<ContentSecurityPolicy />
|
{children}
|
||||||
<body>
|
<Toaster />
|
||||||
{children}
|
</>
|
||||||
<Toaster />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
|
||||||
import "../globals.css";
|
import "../globals.css";
|
||||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
|
||||||
import { ThemeProvider } from "../components/providers/themeProvider";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Khoj AI - Chat",
|
title: "Khoj AI - Chat",
|
||||||
@@ -34,38 +31,19 @@ export const metadata: Metadata = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function ChildLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
<>
|
||||||
<head>
|
{children}
|
||||||
<script
|
<script
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: `
|
__html: `window.EXCALIDRAW_ASSET_PATH = 'https://assets.khoj.dev/@excalidraw/excalidraw/dist/';`,
|
||||||
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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/h
|
|||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
|
||||||
interface ModelSelectorProps extends PopoverProps {
|
interface ModelSelectorProps extends PopoverProps {
|
||||||
onSelect: (model: ModelOptions, userModification: boolean) => void;
|
onSelect: (model: ModelOptions) => void;
|
||||||
selectedModel?: string;
|
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
initialModel?: string;
|
initialModel?: string;
|
||||||
}
|
}
|
||||||
@@ -49,9 +48,8 @@ export function ModelSelector({ ...props }: ModelSelectorProps) {
|
|||||||
setModels(userConfig.chat_model_options);
|
setModels(userConfig.chat_model_options);
|
||||||
if (!props.initialModel) {
|
if (!props.initialModel) {
|
||||||
const selectedChatModelOption = userConfig.chat_model_options.find(model => model.id === userConfig.selected_chat_model_config);
|
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]);
|
setSelectedModel(userConfig.chat_model_options[0]);
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
setSelectedModel(selectedChatModelOption);
|
setSelectedModel(selectedChatModelOption);
|
||||||
}
|
}
|
||||||
@@ -63,29 +61,10 @@ export function ModelSelector({ ...props }: ModelSelectorProps) {
|
|||||||
}, [userConfig, props.initialModel, isLoadingUserConfig]);
|
}, [userConfig, props.initialModel, isLoadingUserConfig]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.selectedModel && selectedModel && props.selectedModel !== selectedModel.name) {
|
if (selectedModel && userConfig) {
|
||||||
const model = models.find(model => model.name === props.selectedModel);
|
props.onSelect(selectedModel);
|
||||||
setSelectedModel(model);
|
|
||||||
}
|
}
|
||||||
else if (props.selectedModel === null && userConfig) {
|
}, [selectedModel, userConfig, props.onSelect]);
|
||||||
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]);
|
|
||||||
|
|
||||||
if (isLoadingUserConfig) {
|
if (isLoadingUserConfig) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -28,10 +28,6 @@ interface ChatResponse {
|
|||||||
response: ChatHistoryData;
|
response: ChatHistoryData;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChatHistory {
|
|
||||||
[key: string]: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChatHistoryProps {
|
interface ChatHistoryProps {
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
setTitle: (title: string) => void;
|
setTitle: (title: string) => void;
|
||||||
@@ -368,7 +364,7 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
|||||||
{data &&
|
{data &&
|
||||||
data.chat &&
|
data.chat &&
|
||||||
data.chat.map((chatMessage, index) => (
|
data.chat.map((chatMessage, index) => (
|
||||||
<>
|
<React.Fragment key={`chatMessage-${index}`}>
|
||||||
{chatMessage.trainOfThought && chatMessage.by === "khoj" && (
|
{chatMessage.trainOfThought && chatMessage.by === "khoj" && (
|
||||||
<TrainOfThoughtComponent
|
<TrainOfThoughtComponent
|
||||||
trainOfThought={chatMessage.trainOfThought?.map(
|
trainOfThought={chatMessage.trainOfThought?.map(
|
||||||
@@ -403,7 +399,7 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
|||||||
onDeleteMessage={handleDeleteMessage}
|
onDeleteMessage={handleDeleteMessage}
|
||||||
conversationId={props.conversationId}
|
conversationId={props.conversationId}
|
||||||
/>
|
/>
|
||||||
</>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
{props.incomingMessages &&
|
{props.incomingMessages &&
|
||||||
props.incomingMessages.map((message, index) => {
|
props.incomingMessages.map((message, index) => {
|
||||||
|
|||||||
@@ -311,12 +311,14 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
|||||||
isLoading: authenticationLoading,
|
isLoading: authenticationLoading,
|
||||||
} = useAuthenticatedData();
|
} = useAuthenticatedData();
|
||||||
|
|
||||||
const [customPrompt, setCustomPrompt] = useState<string | undefined>("");
|
const [customPrompt, setCustomPrompt] = useState<string | undefined>();
|
||||||
const [selectedModel, setSelectedModel] = useState<string | undefined>();
|
const [selectedModel, setSelectedModel] = useState<string | undefined>();
|
||||||
const [inputTools, setInputTools] = useState<string[] | undefined>();
|
const [inputTools, setInputTools] = useState<string[] | undefined>();
|
||||||
const [outputModes, setOutputModes] = useState<string[] | undefined>();
|
const [outputModes, setOutputModes] = useState<string[] | undefined>();
|
||||||
const [hasModified, setHasModified] = useState<boolean>(false);
|
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);
|
const [isSaving, setIsSaving] = useState<boolean>(false);
|
||||||
|
|
||||||
@@ -325,12 +327,14 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
|||||||
function setupAgentData() {
|
function setupAgentData() {
|
||||||
if (agentData) {
|
if (agentData) {
|
||||||
setInputTools(agentData.input_tools);
|
setInputTools(agentData.input_tools);
|
||||||
if (agentData.input_tools === undefined || agentData.input_tools.length === 0) {
|
setDisplayInputTools(agentData.input_tools);
|
||||||
setInputTools(agentConfigurationOptions?.input_tools ? Object.keys(agentConfigurationOptions.input_tools) : []);
|
if (agentData.input_tools === undefined) {
|
||||||
|
setDisplayInputTools(agentConfigurationOptions?.input_tools ? Object.keys(agentConfigurationOptions.input_tools) : []);
|
||||||
}
|
}
|
||||||
setOutputModes(agentData.output_modes);
|
setOutputModes(agentData.output_modes);
|
||||||
if (agentData.output_modes === undefined || agentData.output_modes.length === 0) {
|
setDisplayOutputModes(agentData.output_modes);
|
||||||
setOutputModes(agentConfigurationOptions?.output_modes ? Object.keys(agentConfigurationOptions.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) {
|
if (agentData.name.toLowerCase() === "khoj" || agentData.is_hidden === true) {
|
||||||
@@ -351,16 +355,30 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setupAgentData();
|
setupAgentData();
|
||||||
|
setHasModified(false);
|
||||||
}, [agentData]);
|
}, [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 {
|
function isValueChecked(value: string, existingSelections: string[]): boolean {
|
||||||
return existingSelections.includes(value);
|
return existingSelections.includes(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCheckToggle(value: string, existingSelections: string[]): string[] {
|
function handleCheckToggle(value: string, existingSelections: string[]): string[] {
|
||||||
setHasModified(true);
|
|
||||||
|
|
||||||
if (existingSelections.includes(value)) {
|
if (existingSelections.includes(value)) {
|
||||||
return existingSelections.filter((v) => v !== value);
|
return existingSelections.filter((v) => v !== value);
|
||||||
}
|
}
|
||||||
@@ -370,7 +388,6 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
|||||||
|
|
||||||
function handleCustomPromptChange(value: string) {
|
function handleCustomPromptChange(value: string) {
|
||||||
setCustomPrompt(value);
|
setCustomPrompt(value);
|
||||||
setHasModified(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSave() {
|
function handleSave() {
|
||||||
@@ -430,11 +447,8 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
|||||||
setHasModified(false);
|
setHasModified(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleModelSelect(model: string, userModification: boolean = true) {
|
function handleModelSelect(model: string) {
|
||||||
setSelectedModel(model);
|
setSelectedModel(model);
|
||||||
if (userModification) {
|
|
||||||
setHasModified(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -488,7 +502,7 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
|||||||
<SidebarMenuItem className="list-none">
|
<SidebarMenuItem className="list-none">
|
||||||
<Textarea
|
<Textarea
|
||||||
className="w-full h-32 resize-none hover:resize-y"
|
className="w-full h-32 resize-none hover:resize-y"
|
||||||
value={customPrompt}
|
value={customPrompt || ""}
|
||||||
onChange={(e) => handleCustomPromptChange(e.target.value)}
|
onChange={(e) => handleCustomPromptChange(e.target.value)}
|
||||||
readOnly={!isEditable}
|
readOnly={!isEditable}
|
||||||
disabled={!isEditable} />
|
disabled={!isEditable} />
|
||||||
@@ -514,9 +528,8 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
|||||||
<SidebarMenuItem key={"model"} className="list-none">
|
<SidebarMenuItem key={"model"} className="list-none">
|
||||||
<ModelSelector
|
<ModelSelector
|
||||||
disabled={!isEditable || !isSubscribed}
|
disabled={!isEditable || !isSubscribed}
|
||||||
onSelect={(model, userModification) => handleModelSelect(model.name, userModification)}
|
onSelect={(model) => handleModelSelect(model.name)}
|
||||||
initialModel={isDefaultAgent ? undefined : agentData?.chat_model}
|
initialModel={isDefaultAgent ? undefined : agentData?.chat_model}
|
||||||
selectedModel={selectedModel}
|
|
||||||
/>
|
/>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
@@ -551,8 +564,12 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
id={key}
|
id={key}
|
||||||
className={`${isEditable ? "cursor-pointer" : ""}`}
|
className={`${isEditable ? "cursor-pointer" : ""}`}
|
||||||
checked={isValueChecked(key, inputTools ?? [])}
|
checked={isValueChecked(key, displayInputTools ?? [])}
|
||||||
onCheckedChange={() => setInputTools(handleCheckToggle(key, inputTools ?? []))}
|
onCheckedChange={() => {
|
||||||
|
let updatedInputTools = handleCheckToggle(key, displayInputTools ?? [])
|
||||||
|
setInputTools(updatedInputTools);
|
||||||
|
setDisplayInputTools(updatedInputTools);
|
||||||
|
}}
|
||||||
disabled={!isEditable}
|
disabled={!isEditable}
|
||||||
>
|
>
|
||||||
{key}
|
{key}
|
||||||
@@ -584,8 +601,12 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
id={key}
|
id={key}
|
||||||
className={`${isEditable ? "cursor-pointer" : ""}`}
|
className={`${isEditable ? "cursor-pointer" : ""}`}
|
||||||
checked={isValueChecked(key, outputModes ?? [])}
|
checked={isValueChecked(key, displayOutputModes ?? [])}
|
||||||
onCheckedChange={() => setOutputModes(handleCheckToggle(key, outputModes ?? []))}
|
onCheckedChange={() => {
|
||||||
|
let updatedOutputModes = handleCheckToggle(key, displayOutputModes ?? [])
|
||||||
|
setOutputModes(updatedOutputModes);
|
||||||
|
setDisplayOutputModes(updatedOutputModes);
|
||||||
|
}}
|
||||||
disabled={!isEditable}
|
disabled={!isEditable}
|
||||||
>
|
>
|
||||||
{key}
|
{key}
|
||||||
@@ -649,8 +670,8 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
|
|||||||
<AgentCreationForm
|
<AgentCreationForm
|
||||||
customPrompt={customPrompt}
|
customPrompt={customPrompt}
|
||||||
selectedModel={selectedModel}
|
selectedModel={selectedModel}
|
||||||
inputTools={inputTools ?? []}
|
inputTools={displayInputTools ?? []}
|
||||||
outputModes={outputModes ?? []}
|
outputModes={displayOutputModes ?? []}
|
||||||
/>
|
/>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
|
|||||||
@@ -48,7 +48,11 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
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>
|
<head>
|
||||||
<script
|
<script
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
@@ -65,9 +69,7 @@ export default function RootLayout({
|
|||||||
</head>
|
</head>
|
||||||
<ContentSecurityPolicy />
|
<ContentSecurityPolicy />
|
||||||
<body>
|
<body>
|
||||||
<ThemeProvider>
|
<ThemeProvider>{children}</ThemeProvider>
|
||||||
{children}
|
|
||||||
</ThemeProvider>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
import "../globals.css";
|
import "../globals.css";
|
||||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
|
||||||
import { Toaster } from "@/components/ui/toaster";
|
|
||||||
import { ThemeProvider } from "../components/providers/themeProvider";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Khoj AI - Search",
|
title: "Khoj AI - Search",
|
||||||
@@ -29,33 +26,10 @@ export const metadata: Metadata = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function ChildLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return <>{children}</>;
|
||||||
<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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
|
||||||
import "../globals.css";
|
import "../globals.css";
|
||||||
import { Toaster } from "@/components/ui/toaster";
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
|
||||||
import { ChatwootWidget } from "../components/chatWoot/ChatwootWidget";
|
import { ChatwootWidget } from "../components/chatWoot/ChatwootWidget";
|
||||||
import { ThemeProvider } from "../components/providers/themeProvider";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Khoj AI - Settings",
|
title: "Khoj AI - Settings",
|
||||||
@@ -34,35 +31,16 @@ export const metadata: Metadata = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function ChildLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
<>
|
||||||
<head>
|
{children}
|
||||||
<script
|
<Toaster />
|
||||||
dangerouslySetInnerHTML={{
|
<ChatwootWidget />
|
||||||
__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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -746,7 +746,7 @@ export default function SettingsView() {
|
|||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
value={name}
|
value={name || ""}
|
||||||
className="w-full border border-gray-300 rounded-lg p-4 py-6"
|
className="w-full border border-gray-300 rounded-lg p-4 py-6"
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
|
||||||
import "../../globals.css";
|
import "../../globals.css";
|
||||||
import { ContentSecurityPolicy } from "@/app/common/layoutHelper";
|
|
||||||
import { ThemeProvider } from "@/app/components/providers/themeProvider";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Khoj AI - Ask Anything",
|
title: "Khoj AI - Ask Anything",
|
||||||
@@ -34,38 +31,19 @@ export const metadata: Metadata = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function ChildLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
<>
|
||||||
<head>
|
{children}
|
||||||
<script
|
<script
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: `
|
__html: `window.EXCALIDRAW_ASSET_PATH = 'https://assets.khoj.dev/@excalidraw/excalidraw/dist/';`,
|
||||||
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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user