mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-04 21:29:12 +00:00
Improve handling of dark mode theme in order to avoid jitter when loading new page
This commit is contained in:
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
|||||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
||||||
import "../globals.css";
|
import "../globals.css";
|
||||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
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",
|
||||||
@@ -40,8 +41,26 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
<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 />
|
<ContentSecurityPolicy />
|
||||||
<body>{children}</body>
|
<body>
|
||||||
|
<ThemeProvider>
|
||||||
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "@/components
|
|||||||
import LoginPrompt from "../components/loginPrompt/loginPrompt";
|
import LoginPrompt from "../components/loginPrompt/loginPrompt";
|
||||||
import { InlineLoading } from "../components/loading/loading";
|
import { InlineLoading } from "../components/loading/loading";
|
||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import { useIsMobileWidth } from "../common/utils";
|
import { useIsDarkMode, useIsMobileWidth } from "../common/utils";
|
||||||
import {
|
import {
|
||||||
AgentCard,
|
AgentCard,
|
||||||
EditAgentSchema,
|
EditAgentSchema,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
|||||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
||||||
import "../globals.css";
|
import "../globals.css";
|
||||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
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",
|
||||||
@@ -40,14 +41,30 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
||||||
<ContentSecurityPolicy />
|
<head>
|
||||||
<body>
|
|
||||||
{children}
|
|
||||||
<script
|
<script
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: `window.EXCALIDRAW_ASSET_PATH = 'https://assets.khoj.dev/@excalidraw/excalidraw/dist/';`,
|
__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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -89,6 +89,40 @@ export const useMutationObserver = (
|
|||||||
}, [ref, callback, options])
|
}, [ref, callback, options])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useIsDarkMode() {
|
||||||
|
const [darkMode, setDarkMode] = useState(false);
|
||||||
|
const [initialLoadDone, setInitialLoadDone] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (localStorage.getItem("theme") === "dark") {
|
||||||
|
document.documentElement.classList.add("dark");
|
||||||
|
setDarkMode(true);
|
||||||
|
} else if (localStorage.getItem("theme") === "light") {
|
||||||
|
document.documentElement.classList.remove("dark");
|
||||||
|
setDarkMode(false);
|
||||||
|
} else {
|
||||||
|
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
|
if (mq.matches) {
|
||||||
|
document.documentElement.classList.add("dark");
|
||||||
|
setDarkMode(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setInitialLoadDone(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!initialLoadDone) return;
|
||||||
|
if (darkMode) {
|
||||||
|
document.documentElement.classList.add("dark");
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove("dark");
|
||||||
|
}
|
||||||
|
localStorage.setItem("theme", darkMode ? "dark" : "light");
|
||||||
|
}, [darkMode, initialLoadDone]);
|
||||||
|
|
||||||
|
return [darkMode, setDarkMode] as const;
|
||||||
|
}
|
||||||
|
|
||||||
export const convertBytesToText = (fileSize: number) => {
|
export const convertBytesToText = (fileSize: number) => {
|
||||||
if (fileSize < 1024) {
|
if (fileSize < 1024) {
|
||||||
return `${fileSize} B`;
|
return `${fileSize} B`;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import { useEffect, useState } from "react";
|
|||||||
import AllConversations from "../allConversations/allConversations";
|
import AllConversations from "../allConversations/allConversations";
|
||||||
import FooterMenu from "../navMenu/navMenu";
|
import FooterMenu from "../navMenu/navMenu";
|
||||||
import { useSidebar } from "@/components/ui/sidebar";
|
import { useSidebar } from "@/components/ui/sidebar";
|
||||||
import { useIsMobileWidth } from "@/app/common/utils";
|
import { useIsDarkMode, useIsMobileWidth } from "@/app/common/utils";
|
||||||
import { UserPlusIcon } from "lucide-react";
|
import { UserPlusIcon } from "lucide-react";
|
||||||
import { useAuthenticatedData, UserProfile } from "@/app/common/auth";
|
import { useAuthenticatedData, UserProfile } from "@/app/common/auth";
|
||||||
import LoginPrompt from "../loginPrompt/loginPrompt";
|
import LoginPrompt from "../loginPrompt/loginPrompt";
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Moon, Sun, UserCircle, Question, ArrowRight, Code, BuildingOffice } from "@phosphor-icons/react";
|
import { Moon, Sun, UserCircle, Question, ArrowRight, Code, BuildingOffice } from "@phosphor-icons/react";
|
||||||
import { useIsMobileWidth } from "@/app/common/utils";
|
import { useIsDarkMode, useIsMobileWidth } from "@/app/common/utils";
|
||||||
import LoginPrompt from "../loginPrompt/loginPrompt";
|
import LoginPrompt from "../loginPrompt/loginPrompt";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar";
|
import { SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar";
|
||||||
@@ -49,44 +49,10 @@ export default function FooterMenu({ sideBarIsOpen }: NavMenuProps) {
|
|||||||
error: authenticationError,
|
error: authenticationError,
|
||||||
isLoading: authenticationLoading,
|
isLoading: authenticationLoading,
|
||||||
} = useAuthenticatedData();
|
} = useAuthenticatedData();
|
||||||
const [darkMode, setDarkMode] = useState(false);
|
const [darkMode, setDarkMode] = useIsDarkMode();
|
||||||
const [initialLoadDone, setInitialLoadDone] = useState(false);
|
|
||||||
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
|
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
|
||||||
const isMobileWidth = useIsMobileWidth();
|
const isMobileWidth = useIsMobileWidth();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (localStorage.getItem("theme") === "dark") {
|
|
||||||
document.documentElement.classList.add("dark");
|
|
||||||
setDarkMode(true);
|
|
||||||
} else if (localStorage.getItem("theme") === "light") {
|
|
||||||
document.documentElement.classList.remove("dark");
|
|
||||||
setDarkMode(false);
|
|
||||||
} else {
|
|
||||||
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
||||||
|
|
||||||
if (mq.matches) {
|
|
||||||
document.documentElement.classList.add("dark");
|
|
||||||
setDarkMode(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setInitialLoadDone(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!initialLoadDone) return;
|
|
||||||
toggleDarkMode(darkMode);
|
|
||||||
}, [darkMode, initialLoadDone]);
|
|
||||||
|
|
||||||
function toggleDarkMode(darkMode: boolean) {
|
|
||||||
if (darkMode) {
|
|
||||||
document.documentElement.classList.add("dark");
|
|
||||||
} else {
|
|
||||||
document.documentElement.classList.remove("dark");
|
|
||||||
}
|
|
||||||
localStorage.setItem("theme", darkMode ? "dark" : "light");
|
|
||||||
}
|
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{
|
{
|
||||||
title: "Help",
|
title: "Help",
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useIsDarkMode } from '@/app/common/utils'
|
||||||
|
|
||||||
|
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
const [darkMode, setDarkMode] = useIsDarkMode();
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
|||||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import { ContentSecurityPolicy } from "./common/layoutHelper";
|
import { ContentSecurityPolicy } from "./common/layoutHelper";
|
||||||
|
import { ThemeProvider } from "./components/providers/themeProvider";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Khoj AI - Ask Anything",
|
title: "Khoj AI - Ask Anything",
|
||||||
@@ -48,8 +49,26 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
<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 />
|
<ContentSecurityPolicy />
|
||||||
<body>{children}</body>
|
<body>
|
||||||
|
<ThemeProvider>
|
||||||
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import {
|
|||||||
stepOneSuggestions,
|
stepOneSuggestions,
|
||||||
StepTwoSuggestion,
|
StepTwoSuggestion,
|
||||||
getStepTwoSuggestions,
|
getStepTwoSuggestions,
|
||||||
SuggestionType,
|
|
||||||
} from "@/app/components/suggestions/suggestionsData";
|
} from "@/app/components/suggestions/suggestionsData";
|
||||||
import LoginPrompt from "@/app/components/loginPrompt/loginPrompt";
|
import LoginPrompt from "@/app/components/loginPrompt/loginPrompt";
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { Metadata } from "next";
|
|||||||
import "../globals.css";
|
import "../globals.css";
|
||||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
||||||
import { Toaster } from "@/components/ui/toaster";
|
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",
|
||||||
@@ -35,10 +36,25 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html>
|
<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 />
|
<ContentSecurityPolicy />
|
||||||
<body>
|
<body>
|
||||||
{children}
|
<ThemeProvider>
|
||||||
<Toaster />
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import "../globals.css";
|
|||||||
import { Toaster } from "@/components/ui/toaster";
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
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",
|
||||||
@@ -40,11 +41,27 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
<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 />
|
<ContentSecurityPolicy />
|
||||||
<body>
|
<body>
|
||||||
{children}
|
<ThemeProvider>
|
||||||
<Toaster />
|
{children}
|
||||||
<ChatwootWidget />
|
<Toaster />
|
||||||
|
<ChatwootWidget />
|
||||||
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
|||||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
||||||
import "../../globals.css";
|
import "../../globals.css";
|
||||||
import { ContentSecurityPolicy } from "@/app/common/layoutHelper";
|
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",
|
||||||
@@ -40,14 +41,30 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
||||||
<ContentSecurityPolicy />
|
<head>
|
||||||
<body>
|
|
||||||
{children}
|
|
||||||
<script
|
<script
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: `window.EXCALIDRAW_ASSET_PATH = 'https://assets.khoj.dev/@excalidraw/excalidraw/dist/';`,
|
__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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user