mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-02 21:19: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 "../globals.css";
|
||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
||||
import { ThemeProvider } from "../components/providers/themeProvider";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Khoj AI - Agents",
|
||||
@@ -40,8 +41,26 @@ export default function RootLayout({
|
||||
}>) {
|
||||
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>{children}</body>
|
||||
<body>
|
||||
<ThemeProvider>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "@/components
|
||||
import LoginPrompt from "../components/loginPrompt/loginPrompt";
|
||||
import { InlineLoading } from "../components/loading/loading";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { useIsMobileWidth } from "../common/utils";
|
||||
import { useIsDarkMode, useIsMobileWidth } from "../common/utils";
|
||||
import {
|
||||
AgentCard,
|
||||
EditAgentSchema,
|
||||
|
||||
@@ -2,6 +2,7 @@ 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",
|
||||
@@ -40,14 +41,30 @@ export default function RootLayout({
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
||||
<ContentSecurityPolicy />
|
||||
<body>
|
||||
{children}
|
||||
<head>
|
||||
<script
|
||||
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>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -89,6 +89,40 @@ export const useMutationObserver = (
|
||||
}, [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) => {
|
||||
if (fileSize < 1024) {
|
||||
return `${fileSize} B`;
|
||||
|
||||
@@ -22,7 +22,7 @@ import { useEffect, useState } from "react";
|
||||
import AllConversations from "../allConversations/allConversations";
|
||||
import FooterMenu from "../navMenu/navMenu";
|
||||
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 { useAuthenticatedData, UserProfile } from "@/app/common/auth";
|
||||
import LoginPrompt from "../loginPrompt/loginPrompt";
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
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 { Button } from "@/components/ui/button";
|
||||
import { SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar";
|
||||
@@ -49,44 +49,10 @@ export default function FooterMenu({ sideBarIsOpen }: NavMenuProps) {
|
||||
error: authenticationError,
|
||||
isLoading: authenticationLoading,
|
||||
} = useAuthenticatedData();
|
||||
const [darkMode, setDarkMode] = useState(false);
|
||||
const [initialLoadDone, setInitialLoadDone] = useState(false);
|
||||
const [darkMode, setDarkMode] = useIsDarkMode();
|
||||
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
|
||||
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 = [
|
||||
{
|
||||
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 "./globals.css";
|
||||
import { ContentSecurityPolicy } from "./common/layoutHelper";
|
||||
import { ThemeProvider } from "./components/providers/themeProvider";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Khoj AI - Ask Anything",
|
||||
@@ -48,8 +49,26 @@ export default function RootLayout({
|
||||
}>) {
|
||||
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>{children}</body>
|
||||
<body>
|
||||
<ThemeProvider>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
stepOneSuggestions,
|
||||
StepTwoSuggestion,
|
||||
getStepTwoSuggestions,
|
||||
SuggestionType,
|
||||
} from "@/app/components/suggestions/suggestionsData";
|
||||
import LoginPrompt from "@/app/components/loginPrompt/loginPrompt";
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ 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",
|
||||
@@ -35,10 +36,25 @@ export default function RootLayout({
|
||||
}>) {
|
||||
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>
|
||||
{children}
|
||||
<Toaster />
|
||||
<ThemeProvider>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ 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",
|
||||
@@ -40,11 +41,27 @@ export default function RootLayout({
|
||||
}>) {
|
||||
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>
|
||||
{children}
|
||||
<Toaster />
|
||||
<ChatwootWidget />
|
||||
<ThemeProvider>
|
||||
{children}
|
||||
<Toaster />
|
||||
<ChatwootWidget />
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ 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",
|
||||
@@ -40,14 +41,30 @@ export default function RootLayout({
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
||||
<ContentSecurityPolicy />
|
||||
<body>
|
||||
{children}
|
||||
<head>
|
||||
<script
|
||||
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>
|
||||
</html>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user