From 4726e757d5aa6a1db84c7482cefa546c9cb5d08f Mon Sep 17 00:00:00 2001
From: LangQi99 <2032771946@qq.com>
Date: Sun, 1 Jun 2025 16:38:57 +0800
Subject: [PATCH 1/5] feat: support dark mode code
---
src/containers/editor/editor/Editor.tsx | 5 +++++
src/containers/editor/sidenav/index.tsx | 12 ++++++++++++
2 files changed, 17 insertions(+)
diff --git a/src/containers/editor/editor/Editor.tsx b/src/containers/editor/editor/Editor.tsx
index 011cf469..458d6cc3 100644
--- a/src/containers/editor/editor/Editor.tsx
+++ b/src/containers/editor/editor/Editor.tsx
@@ -10,6 +10,7 @@ import { getTree } from "@/stores/treeStore";
import { loader, Editor as MonacoEditor } from "@monaco-editor/react";
import { useTranslations } from "next-intl";
import { useShallow } from "zustand/shallow";
+import { useTheme } from "next-themes";
loader.config({ paths: { vs: vsURL } });
@@ -21,6 +22,7 @@ export default function Editor({ kind, ...props }: EditorProps) {
const translations = useTranslations();
const setEditor = useEditorStore((state) => state.setEditor);
const setTranslations = useEditorStore((state) => state.setTranslations);
+ const { theme } = useTheme();
useDisplayExample();
useRevealNode();
@@ -29,6 +31,7 @@ export default function Editor({ kind, ...props }: EditorProps) {
}
+ theme={theme === "dark" ? "vs-dark" : "light"}
options={{
fontSize: 13, // 设置初始字体大小
scrollBeyondLastLine: false, // 行数超过一屏时才展示滚动条
@@ -58,6 +61,8 @@ export default function Editor({ kind, ...props }: EditorProps) {
setEditor(wrapper);
setTranslations(translations);
console.l(`finished initial editor ${kind}:`, wrapper);
+ // 同步设置monaco主题
+ monaco.editor.setTheme(theme === "dark" ? "vs-dark" : "light");
}}
{...props}
/>
diff --git a/src/containers/editor/sidenav/index.tsx b/src/containers/editor/sidenav/index.tsx
index b3ba7706..40f971e7 100644
--- a/src/containers/editor/sidenav/index.tsx
+++ b/src/containers/editor/sidenav/index.tsx
@@ -18,6 +18,7 @@ import {
AlignHorizontalJustifyCenter,
ArrowLeftToLine,
ArrowRightFromLine,
+ SunMoon,
} from "lucide-react";
import { useTranslations } from "next-intl";
import { useShallow } from "zustand/shallow";
@@ -31,6 +32,8 @@ import PopoverBtn, { popoverBtnClass } from "./PopoverButton";
import SharePopover from "./SharePopover";
import StatisticsPopover from "./StatisticsPopover";
import Toggle from "./Toggle";
+import { useTheme } from "next-themes";
+import { useEffect, useState } from "react";
export default function SideNav() {
const cc = useConfigFromCookies();
@@ -63,6 +66,9 @@ export default function SideNav() {
};
}),
);
+ const { theme, setTheme } = useTheme();
+ const [mounted, setMounted] = useState(false);
+ useEffect(() => { setMounted(true); }, []);
return (
} content={} />
{/* can't connect to supabase in China, so disable the function temporarily */}
{!isCN && }
+ }
+ title={mounted ? (theme === "dark" ? "切换为亮色模式" : "切换为暗色模式") : "切换主题"}
+ onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
+ />
: }
From 40f73a2816e123b3c5b4ed2195ca00b4b83dc7dc Mon Sep 17 00:00:00 2001
From: LangQi99 <2032771946@qq.com>
Date: Sun, 1 Jun 2025 16:58:14 +0800
Subject: [PATCH 2/5] feat: support graph/table dark mode
---
src/app/globals.css | 61 +++++++++++++++++++++++++++++++++++++++------
1 file changed, 54 insertions(+), 7 deletions(-)
diff --git a/src/app/globals.css b/src/app/globals.css
index 15ac81a4..16b2c411 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -74,6 +74,15 @@
--diff-bg-red: #ff9b9533;
--diff-fg-green: #2ddf5966;
--diff-bg-green: #82ffa433;
+
+ --graph-bg-color: #fff;
+ --graph-text-color: #222;
+ --tbl-bg-color: #fff;
+ --tbl-text-color: #222;
+ --popover-bg-color: #f8f9fa;
+ --popover-text-color: #222;
+ --tooltip-bg-color: #fff;
+ --tooltip-text-color: #000;
}
[data-theme="dark"] {
@@ -104,6 +113,22 @@
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
+
+ --graph-bg-color: #23272e;
+ --graph-text-color: #eee;
+ --tbl-bg-color: #23272e;
+ --tbl-text-color: #eee;
+ --popover-bg-color: #2a2d32;
+ --popover-text-color: #eee;
+ --tooltip-bg-color: #23272e;
+ --tooltip-text-color: #eee;
+
+ --hl-key: #9cdcfe;
+ --hl-string: #ce9178;
+ --hl-number: #b5cea8;
+ --hl-null: #d16969;
+ --hl-empty: #ffffff4d;
+ --hl-index: #ffffff4d;
}
}
@@ -229,7 +254,8 @@ body {
.graph-node {
min-width: fit-content;
border-width: 1px;
- background-color: rgb(255 255 255);
+ background-color: var(--graph-bg-color);
+ color: var(--graph-text-color);
font-size: var(--graph-font-size);
font-family: var(--graph-font-family);
}
@@ -272,7 +298,8 @@ body {
.popover-item {
width: max-content;
- background: #f8f9fa;
+ background: var(--popover-bg-color);
+ color: var(--popover-text-color);
border: 1px solid #dee2e6;
word-wrap: break-word;
font-size: 12px;
@@ -287,7 +314,8 @@ body {
border-collapse: collapse;
th {
- background-color: var(--tbl-key-bg-color);
+ background-color: var(--tbl-bg-color);
+ color: var(--tbl-text-color);
font-weight: normal;
text-align: left;
vertical-align: top;
@@ -301,6 +329,8 @@ body {
td {
position: relative;
+ background-color: var(--tbl-bg-color);
+ color: var(--tbl-text-color);
font-weight: normal;
text-align: left;
vertical-align: top;
@@ -366,12 +396,10 @@ body {
padding: 15px;
position: absolute;
z-index: 1;
- color: #000000;
+ color: var(--tooltip-text-color);
font-family: var(--graph-font-family);
-
- /* stolen from https://www.radix-ui.com/primitives/docs/components/popover */
border-radius: 4px;
- background-color: white;
+ background-color: var(--tooltip-bg-color);
box-shadow:
hsl(206 22% 7% / 35%) 0px 10px 38px -10px,
hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
@@ -503,3 +531,22 @@ body {
transform: translateX(0);
}
}
+
+.text-hl-key {
+ color: var(--hl-key);
+}
+.text-hl-string {
+ color: var(--hl-string);
+}
+.text-hl-number {
+ color: var(--hl-number);
+}
+.text-hl-null {
+ color: var(--hl-null);
+}
+.text-hl-empty {
+ color: var(--hl-empty);
+}
+.text-hl-index {
+ color: var(--hl-index);
+}
From 41787791819f499eb373af820804b05acbaf2aaf Mon Sep 17 00:00:00 2001
From: LangQi99 <2032771946@qq.com>
Date: Sun, 1 Jun 2025 17:49:50 +0800
Subject: [PATCH 3/5] feat: support dark mode for /editor
---
src/app/globals.css | 152 +++++++++++++++++++++++-
src/containers/editor/RightPanel.tsx | 36 ++++--
src/containers/editor/sidenav/index.tsx | 7 --
3 files changed, 176 insertions(+), 19 deletions(-)
diff --git a/src/app/globals.css b/src/app/globals.css
index 16b2c411..08784cb6 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -83,6 +83,32 @@
--popover-text-color: #222;
--tooltip-bg-color: #fff;
--tooltip-text-color: #000;
+
+ --btn-text-normal: #222;
+
+ --cmdk-bg: #fff;
+ --cmdk-foreground: #222;
+ --cmdk-group-heading: #888;
+ --cmdk-scrollbar-track: #f1f1f1;
+ --cmdk-scrollbar-thumb: #c1c1c1;
+ --cmdk-list-bg: #fff;
+ --cmdk-list-shadow: 0 4px 24px 0 rgba(0,0,0,0.08);
+ --cmdk-list-border: #e5e7eb;
+
+ --rf-controls-bg: #fff;
+ --rf-controls-border: #e5e7eb;
+ --rf-controls-icon: #222;
+ --rf-controls-btn-hover: #f3f4f6;
+ --rf-controls-btn-bg: #fff;
+
+ --tab-inactive: #52525b;
+
+ --logo-version: #222;
+ --fullscreen-btn-text: #222;
+ --fullscreen-btn-bg: #fff;
+ --fullscreen-btn-border: #e5e7eb;
+ --fullscreen-btn-hover-bg: #f3f4f6;
+ --fullscreen-btn-hover-text: #222;
}
[data-theme="dark"] {
@@ -129,6 +155,35 @@
--hl-null: #d16969;
--hl-empty: #ffffff4d;
--hl-index: #ffffff4d;
+
+ --btn-text-normal: #e6e6e6;
+
+ --cmdk-bg: #23272e;
+ --cmdk-foreground: #e6e6e6;
+ --cmdk-group-heading: #b0b0b0;
+ --cmdk-scrollbar-track: #23272e;
+ --cmdk-scrollbar-thumb: #444c56;
+ --cmdk-list-bg: #23272e;
+ --cmdk-list-shadow: 0 4px 24px 0 rgba(0,0,0,0.32);
+ --cmdk-list-border: #444c56;
+
+ --kbd-bg-color: #2a2d32;
+ --kbd-color: #e6e6e6;
+
+ --rf-controls-bg: #23272e;
+ --rf-controls-border: #444c56;
+ --rf-controls-icon: #e6e6e6;
+ --rf-controls-btn-hover: #2a2d32;
+ --rf-controls-btn-bg: #23272e;
+
+ --tab-inactive: #e0e3ea;
+
+ --logo-version: #e6e6e6;
+ --fullscreen-btn-text: #e6e6e6;
+ --fullscreen-btn-bg: #23272e;
+ --fullscreen-btn-border: #444c56;
+ --fullscreen-btn-hover-bg: #2a2d32;
+ --fullscreen-btn-hover-text: #fff;
}
}
@@ -444,14 +499,16 @@ body {
}
.search-cmd-kbd {
- background: var(--kbd-bg-color);
- color: var(--kbd-color);
+ background: var(--kbd-bg-color) !important;
+ color: var(--kbd-color) !important;
border: 1px solid hsl(var(--border));
border-radius: 4px;
height: 20px;
line-height: 20px;
padding: 0 6px;
font-family: var(--graph-font-family);
+ font-size: 13px;
+ display: inline-block;
}
#cmd-panel {
@@ -550,3 +607,94 @@ body {
.text-hl-index {
color: var(--hl-index);
}
+
+.text-btn {
+ color: var(--btn-text-normal) !important;
+}
+
+.cmdk-group,
+[cmdk-group] {
+ background: var(--cmdk-bg) !important;
+ color: var(--cmdk-foreground) !important;
+}
+[cmdk-group-heading] {
+ color: var(--cmdk-group-heading) !important;
+}
+.cmdk-group .scrollbar-thin,
+[cmdk-group] .scrollbar-thin {
+ scrollbar-color: var(--cmdk-scrollbar-thumb) var(--cmdk-scrollbar-track);
+}
+.cmdk-group .scrollbar-thin::-webkit-scrollbar,
+[cmdk-group] .scrollbar-thin::-webkit-scrollbar {
+ width: 6px;
+ background: var(--cmdk-scrollbar-track);
+}
+.cmdk-group .scrollbar-thin::-webkit-scrollbar-thumb,
+[cmdk-group] .scrollbar-thin::-webkit-scrollbar-thumb {
+ background: var(--cmdk-scrollbar-thumb);
+ border-radius: 6px;
+}
+
+.cmdk-list,
+[cmdk-list] {
+ background: var(--cmdk-list-bg) !important;
+ box-shadow: var(--cmdk-list-shadow) !important;
+ border: 1px solid var(--cmdk-list-border) !important;
+ color: var(--cmdk-foreground) !important;
+}
+
+.react-flow__controls {
+ background: var(--rf-controls-bg) !important;
+ border: 1px solid var(--rf-controls-border) !important;
+ border-radius: 8px !important;
+ box-shadow: 0 2px 8px 0 rgba(0,0,0,0.08);
+ padding: 4px;
+}
+.react-flow__controls-button {
+ background: var(--rf-controls-btn-bg) !important;
+ border: none;
+ color: var(--rf-controls-icon) !important;
+ fill: var(--rf-controls-icon) !important;
+ transition: background 0.2s;
+}
+.react-flow__controls-button svg {
+ color: var(--rf-controls-icon) !important;
+ fill: var(--rf-controls-icon) !important;
+}
+.react-flow__controls-button:hover {
+ background: var(--rf-controls-btn-hover) !important;
+}
+
+.react-flow__attribution {
+ background: none !important;
+ box-shadow: none !important;
+ color: var(--rf-controls-icon) !important;
+}
+
+[data-orientation="horizontal"],
+[role="tablist"] .dark\:text-zinc-200 {
+}
+[role="tab"][data-state="inactive"],
+button[role="tab"][data-state="inactive"] {
+ color: var(--tab-inactive) !important;
+}
+
+a.flex.items-center.pointer .w-24 {
+ color: var(--logo-version) !important;
+}
+button[title~="full"][class*="inline-flex"] {
+ color: var(--fullscreen-btn-text) !important;
+ background: var(--fullscreen-btn-bg) !important;
+ border-color: var(--fullscreen-btn-border) !important;
+}
+button[title~="full"][class*="inline-flex"]:hover {
+ background: var(--fullscreen-btn-hover-bg) !important;
+ color: var(--fullscreen-btn-hover-text) !important;
+}
+
+a.flex.items-center.pointer img[src$="icon.svg"] {
+ transition: filter 0.2s;
+}
+[data-theme="dark"] a.flex.items-center.pointer img[src$="icon.svg"] {
+ filter: invert(1) brightness(2);
+}
diff --git a/src/containers/editor/RightPanel.tsx b/src/containers/editor/RightPanel.tsx
index 9e2b0f2c..8f92f2a1 100644
--- a/src/containers/editor/RightPanel.tsx
+++ b/src/containers/editor/RightPanel.tsx
@@ -3,7 +3,6 @@
import * as React from "react";
import { useState } from "react";
import { Container, ContainerContent, ContainerHeader } from "@/components/Container";
-import { Button } from "@/components/ui/button";
import ViewSearchInput from "@/components/ui/search/ViewSearchInput";
import { Switch } from "@/components/ui/switch";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
@@ -15,9 +14,11 @@ import { ViewMode, ViewModeValue } from "@/lib/db/config";
import { useEditorStore } from "@/stores/editorStore";
import { useConfigFromCookies } from "@/stores/hook";
import { useStatusStore } from "@/stores/statusStore";
-import { Expand, Shrink, Table2, Text, Waypoints } from "lucide-react";
+import { Expand, Shrink, Table2, Text, Waypoints, Sun, Moon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useShallow } from "zustand/shallow";
+import { useTheme } from "next-themes";
+import Button from "@/containers/editor/sidenav/Button";
export default function RightPanel() {
const cc = useConfigFromCookies();
@@ -65,6 +66,9 @@ function Buttons({ viewMode }: { viewMode: ViewMode }) {
setEnableTextCompare: state.setEnableTextCompare,
})),
);
+ const { theme, setTheme } = useTheme();
+ const [mounted, setMounted] = React.useState(false);
+ React.useEffect(() => { setMounted(true); }, []);
return (
@@ -72,19 +76,33 @@ function Buttons({ viewMode }: { viewMode: ViewMode }) {
<>
-
+
>
)}
{viewMode === ViewMode.Graph &&
}
+
);
}
+function ThemeButton() {
+ const { theme, setTheme } = useTheme();
+ const [mounted, setMounted] = React.useState(false);
+ React.useEffect(() => { setMounted(true); }, []);
+ const isDark = theme === "dark";
+ return (
+ : }
+ // TODO: add title
+ onClick={() => setTheme(isDark ? "light" : "dark")}
+ />
+ );
+}
+
function FullScreenButton() {
const t = useTranslations();
const [fullscreen, setFullscreen] = useState(false);
@@ -92,9 +110,9 @@ function FullScreenButton() {
return (
}
title={t(fullscreen ? "shrink_screen" : "expand_screen")}
- className="px-2"
- variant="icon-outline"
onClick={() => {
if (fullscreen) {
setFullscreen(false);
@@ -104,9 +122,7 @@ function FullScreenButton() {
window.leftPanelHandle?.collapse();
}
}}
- >
-
-
+ />
);
}
diff --git a/src/containers/editor/sidenav/index.tsx b/src/containers/editor/sidenav/index.tsx
index 40f971e7..cbd1e001 100644
--- a/src/containers/editor/sidenav/index.tsx
+++ b/src/containers/editor/sidenav/index.tsx
@@ -18,7 +18,6 @@ import {
AlignHorizontalJustifyCenter,
ArrowLeftToLine,
ArrowRightFromLine,
- SunMoon,
} from "lucide-react";
import { useTranslations } from "next-intl";
import { useShallow } from "zustand/shallow";
@@ -138,12 +137,6 @@ export default function SideNav() {
} content={} />
{/* can't connect to supabase in China, so disable the function temporarily */}
{!isCN && }
- }
- title={mounted ? (theme === "dark" ? "切换为亮色模式" : "切换为暗色模式") : "切换主题"}
- onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
- />
: }
From a3276731bb73d0a1a6ece2568a47b035c3308835 Mon Sep 17 00:00:00 2001
From: LangQi99 <2032771946@qq.com>
Date: Sun, 1 Jun 2025 17:59:26 +0800
Subject: [PATCH 4/5] feat: support dark mode for index
---
src/app/layout.tsx | 5 ++++-
src/components/ClientOnly.tsx | 9 +++++++++
src/containers/landing/Header.tsx | 2 +-
3 files changed, 14 insertions(+), 2 deletions(-)
create mode 100644 src/components/ClientOnly.tsx
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 97fe72ed..a913a0ef 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -5,6 +5,7 @@ import { GoogleAnalytics } from "@next/third-parties/google";
import { NextIntlClientProvider } from "next-intl";
import { getLocale, getMessages, getTranslations } from "next-intl/server";
import { ThemeProvider } from "next-themes";
+import ClientOnly from "@/components/ClientOnly";
export async function generateMetadata() {
const locale = await getLocale();
@@ -62,7 +63,9 @@ export default async function MainLayout({ children }: { children: React.ReactNo
{/* TODO: support dark theme */}
- {children}
+
+ {children}
+
diff --git a/src/components/ClientOnly.tsx b/src/components/ClientOnly.tsx
new file mode 100644
index 00000000..3b86d427
--- /dev/null
+++ b/src/components/ClientOnly.tsx
@@ -0,0 +1,9 @@
+"use client";
+import React, { useState, useEffect } from "react";
+
+export default function ClientOnly({ children }: { children: React.ReactNode }) {
+ const [mounted, setMounted] = useState(false);
+ useEffect(() => { setMounted(true); }, []);
+ if (!mounted) return null;
+ return <>{children}>;
+}
\ No newline at end of file
diff --git a/src/containers/landing/Header.tsx b/src/containers/landing/Header.tsx
index 97305cd1..f277b1f0 100644
--- a/src/containers/landing/Header.tsx
+++ b/src/containers/landing/Header.tsx
@@ -17,7 +17,7 @@ export default function Header() {
];
return (
-
+