fix: 修复亮暗模式颜色

This commit is contained in:
Montia37
2025-09-09 21:25:23 +08:00
parent 5a56b94e52
commit 02a4624b7d
9 changed files with 118 additions and 65 deletions

View File

@@ -2,7 +2,7 @@ import React from "react";
const Footer: React.FC = () => { const Footer: React.FC = () => {
return ( return (
<footer className="fixed inset-shadow-sm inset-shadow-(color:--accent-4)/50 bottom-0 left-0 right-0 p-2 text-center purcarte-blur z-50"> <footer className="fixed inset-shadow-sm inset-shadow-(color:--accent-a4) bottom-0 left-0 right-0 p-2 text-center purcarte-blur z-50">
<p className="flex justify-center text-sm text-secondary-foreground theme-text-shadow whitespace-pre"> <p className="flex justify-center text-sm text-secondary-foreground theme-text-shadow whitespace-pre">
Powered by{" "} Powered by{" "}
<a <a

View File

@@ -57,7 +57,7 @@ export const Header = ({
}; };
return ( return (
<header className="purcarte-blur border-b border-(--accent-4)/50 sticky top-0 flex items-center justify-center shadow-sm shadow-(color:--accent-4)/50 z-10"> <header className="purcarte-blur border-b border-(--accent-a4) shadow-sm shadow-(color:--accent-a4) sticky top-0 flex items-center justify-center z-10">
<div className="w-[90%] max-w-screen-2xl px-4 py-2 flex items-center justify-between"> <div className="w-[90%] max-w-screen-2xl px-4 py-2 flex items-center justify-between">
<div className="flex items-center theme-text-shadow text-accent-foreground"> <div className="flex items-center theme-text-shadow text-accent-foreground">
<a href="/" className="flex items-center gap-2 text-2xl font-bold"> <a href="/" className="flex items-center gap-2 text-2xl font-bold">

View File

@@ -10,14 +10,13 @@ const buttonVariants = cva(
variants: { variants: {
variant: { variant: {
default: default:
"bg-(--accent-5)/50 text-primary shadow-sm shadow-(color:--accent-4)/50 hover:bg-(--accent-6)/50", "theme-button text-primary inset-shadow-xs inset-shadow-(color:--accent-a4) ",
destructive: destructive:
"bg-(--accent-5)/50 text-white shadow-sm shadow-(color:--accent-4)/50 hover:bg-(--accent-6)/50 focus-visible:ring-destructive/20", "theme-button text-white inset-shadow-xs inset-shadow-(color:--accent-a4) focus-visible:ring-destructive/20",
outline: outline: "theme-button theme-card-style",
"bg-(--accent-5)/50 theme-card-style hover:bg-(--accent-6)/50 hover:text-accent-foreground",
secondary: secondary:
"bg-(--accent-5)/50 text-secondary-foreground shadow-sm shadow-(color:--accent-4)/50 hover:bg-(--accent-6)/50", "theme-button text-secondary-foreground inset-shadow-xs inset-shadow-(color:--accent-a4)",
ghost: "hover:bg-(--accent-5)/50 hover:bg-(--accent-6)/50", ghost: "theme-button-ghost",
link: "text-primary underline-offset-4 hover:underline", link: "text-primary underline-offset-4 hover:underline",
}, },
size: { size: {

View File

@@ -11,14 +11,14 @@ const Switch = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SwitchPrimitives.Root <SwitchPrimitives.Root
className={cn( className={cn(
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border border-(--accent-5) transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-(--accent-8)/50 data-[state=unchecked]:bg-(--accent-4)/50", "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border border-(--accent-a5) transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-(--accent-a6) data-[state=unchecked]:bg-(--accent-a2)",
className className
)} )}
{...props} {...props}
ref={ref}> ref={ref}>
<SwitchPrimitives.Thumb <SwitchPrimitives.Thumb
className={cn( className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-(--accent-9) dark:bg-(--accent-5) shadow-sm ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0" "pointer-events-none block h-5 w-5 rounded-full bg-(--accent-a8) shadow-sm ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
)} )}
/> />
</SwitchPrimitives.Root> </SwitchPrimitives.Root>

View File

@@ -0,0 +1,11 @@
import type { FC, ReactNode } from "react";
import { ThemeContext, type ThemeContextType } from "@/hooks/useTheme";
export const ThemeProvider: FC<{
children: ReactNode;
value: ThemeContextType;
}> = ({ children, value }) => {
return (
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
);
};

View File

@@ -41,20 +41,66 @@ export const THEME_DEFAULTS = {
} as const; } as const;
export interface ThemeContextType { export interface ThemeContextType {
appearance: Appearance; appearance: "light" | "dark";
rawAppearance: Appearance;
setAppearance: (appearance: Appearance) => void; setAppearance: (appearance: Appearance) => void;
color: Colors; color: Colors;
setColor: (color: Colors) => void; setColor: (color: Colors) => void;
} }
export const ThemeContext = createContext<ThemeContextType>({ export const ThemeContext = createContext<ThemeContextType>({
appearance: THEME_DEFAULTS.appearance, appearance: "light",
rawAppearance: THEME_DEFAULTS.appearance,
setAppearance: () => {}, setAppearance: () => {},
color: THEME_DEFAULTS.color, color: THEME_DEFAULTS.color,
setColor: () => {}, setColor: () => {},
}); });
export const useTheme = () => { /**
* Custom hook to convert "system" appearance to actual "light" or "dark" for Radix UI
* @param appearance - The appearance setting from context ("light", "dark", or "system")
* @returns The resolved appearance for Radix UI ("light" or "dark")
*/
export const useSystemTheme = (appearance: Appearance): "light" | "dark" => {
const [systemTheme, setSystemTheme] = useState<"light" | "dark">(() => {
// Initial system theme detection
if (typeof window !== "undefined" && window.matchMedia) {
return window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
}
return "light";
});
useEffect(() => {
if (typeof window === "undefined" || !window.matchMedia) {
return;
}
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const handleChange = (e: MediaQueryListEvent) => {
setSystemTheme(e.matches ? "dark" : "light");
};
// Add listener for system theme changes
mediaQuery.addEventListener("change", handleChange);
// Cleanup
return () => mediaQuery.removeEventListener("change", handleChange);
}, []);
// Return the resolved theme
if (appearance === "system") {
return systemTheme;
}
return appearance as "light" | "dark";
};
import { useContext } from "react";
export const useThemeManager = () => {
const enableLocalStorage = useConfigItem("enableLocalStorage"); const enableLocalStorage = useConfigItem("enableLocalStorage");
const defaultAppearance = useConfigItem("selectedDefaultAppearance"); const defaultAppearance = useConfigItem("selectedDefaultAppearance");
const defaultColor = useConfigItem("selectThemeColor"); const defaultColor = useConfigItem("selectThemeColor");
@@ -86,39 +132,28 @@ export const useTheme = () => {
return (defaultColor as Colors) || THEME_DEFAULTS.color; return (defaultColor as Colors) || THEME_DEFAULTS.color;
}); });
useEffect(() => { const resolvedAppearance = useSystemTheme(appearance);
if (appearance === "system") {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
: "light";
setAppearance(systemTheme);
}
}, [appearance]);
useEffect(() => { useEffect(() => {
const root = window.document.documentElement;
root.classList.remove("light", "dark");
if (appearance === "system") {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
: "light";
root.classList.add(systemTheme);
} else {
root.classList.add(appearance);
}
if (enableLocalStorage) { if (enableLocalStorage) {
localStorage.setItem("appearance", appearance); localStorage.setItem("appearance", appearance);
}
}, [appearance, enableLocalStorage]);
useEffect(() => {
if (enableLocalStorage) {
localStorage.setItem("color", color); localStorage.setItem("color", color);
} }
}, [color, enableLocalStorage]); }, [appearance, color, enableLocalStorage]);
return { appearance, setAppearance, color, setColor }; return {
appearance: resolvedAppearance,
rawAppearance: appearance,
setAppearance,
color,
setColor,
};
};
export const useTheme = () => {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
}; };

View File

@@ -78,7 +78,6 @@
--sidebar-border: #e5e5e5; --sidebar-border: #e5e5e5;
--sidebar-ring: #a1a1a1; --sidebar-ring: #a1a1a1;
--purcarte-blur: 10px;
--body-background-url: url(""); --body-background-url: url("");
} }
@@ -130,20 +129,19 @@
} }
@layer utilities { @layer utilities {
.dark .radix-themes {
--theme-text-muted-color: rgb(from var(--accent-4) r g b / 0.8);
--theme-line-muted-color: rgb(from var(--accent-2) r g b / 0.5);
}
.radix-themes { .radix-themes {
--purcarte-blur: 10px;
--purcarte-card-color: var(--card-light);
--theme-text-muted-color: rgb(from var(--accent-12) r g b / 0.8); --theme-text-muted-color: rgb(from var(--accent-12) r g b / 0.8);
--theme-line-muted-color: rgb(from var(--accent-10) r g b / 0.5); --theme-line-muted-color: rgb(from var(--accent-10) r g b / 0.5);
} }
.dark .rt-Badge-tag-transparent { .radix-themes.dark {
--tag-transparent-bg: rgb(from var(--accent-11) r g b / 0.4); --purcarte-card-color: var(--card-dark);
--tag-transparent-color: rgb(from var(--accent-4) r g b / 0.8); }
@apply bg-(--tag-transparent-bg) text-(--tag-transparent-color);
.purcarte-blur {
@apply bg-(--purcarte-card-color)! backdrop-blur-(--purcarte-blur)!;
} }
.rt-Badge-tag-transparent { .rt-Badge-tag-transparent {
@@ -152,12 +150,20 @@
@apply bg-(--tag-transparent-bg) text-(--tag-transparent-color); @apply bg-(--tag-transparent-bg) text-(--tag-transparent-color);
} }
.theme-button {
@apply bg-(--accent-a6)! hover:bg-(--accent-a5)!;
}
.theme-button-ghost {
@apply hover:bg-(--accent-a6)! hover:bg-(--accent-a5)!;
}
.theme-text-shadow { .theme-text-shadow {
@apply text-shadow-sm text-shadow-(color:--accent-8)/50; @apply text-shadow-sm text-shadow-(color:--accent-a4);
} }
.theme-card-style { .theme-card-style {
@apply rounded-lg shadow-sm shadow-(color:--accent-4)/50 box-border border border-(--accent-4)/50; @apply rounded-lg shadow-sm shadow-(color:--accent-a4) box-border border border-(--accent-a4);
} }
.theme-text-muted { .theme-text-muted {
@@ -178,11 +184,6 @@ body::before {
/* transition: var(--body-background-transition); */ /* transition: var(--body-background-transition); */
} }
.purcarte-blur {
background: var(--color-card);
backdrop-filter: blur(var(--purcarte-blur));
}
.striped-bg-red-translucent-diagonal { .striped-bg-red-translucent-diagonal {
background-image: linear-gradient( background-image: linear-gradient(
45deg, 45deg,

View File

@@ -6,7 +6,8 @@ import "@radix-ui/themes/styles.css";
import { Theme } from "@radix-ui/themes"; import { Theme } from "@radix-ui/themes";
import { Header } from "@/components/sections/Header"; import { Header } from "@/components/sections/Header";
import { ConfigProvider } from "@/config"; import { ConfigProvider } from "@/config";
import { useTheme } from "@/hooks/useTheme"; import { useThemeManager, useTheme } from "@/hooks/useTheme";
import { ThemeProvider } from "@/contexts/ThemeContext";
import { NodeDataProvider } from "@/contexts/NodeDataContext"; import { NodeDataProvider } from "@/contexts/NodeDataContext";
import { LiveDataProvider } from "@/contexts/LiveDataContext"; import { LiveDataProvider } from "@/contexts/LiveDataContext";
import { useNodeData } from "@/contexts/NodeDataContext"; import { useNodeData } from "@/contexts/NodeDataContext";
@@ -57,7 +58,7 @@ export const AppContent = () => {
className="fixed right-0 bottom-0 min-w-full min-h-full w-auto h-auto -z-1 object-cover"></video> className="fixed right-0 bottom-0 min-w-full min-h-full w-auto h-auto -z-1 object-cover"></video>
)} )}
<Theme <Theme
appearance={appearance === "system" ? "inherit" : appearance} appearance={appearance}
accentColor={color} accentColor={color}
scaling="110%" scaling="110%"
style={{ backgroundColor: "transparent" }}> style={{ backgroundColor: "transparent" }}>
@@ -93,6 +94,7 @@ export const AppContent = () => {
const App = () => { const App = () => {
const { publicSettings, loading } = useNodeData(); const { publicSettings, loading } = useNodeData();
const themeManager = useThemeManager();
if (loading) { if (loading) {
return <Loading />; return <Loading />;
@@ -100,7 +102,9 @@ const App = () => {
return ( return (
<ConfigProvider publicSettings={publicSettings}> <ConfigProvider publicSettings={publicSettings}>
<AppContent /> <ThemeProvider value={themeManager}>
<AppContent />
</ThemeProvider>
</ConfigProvider> </ConfigProvider>
); );
}; };

View File

@@ -82,7 +82,7 @@ const PingChart = memo(({ node, hours }: PingChartProps) => {
let foundKey = null; let foundKey = null;
// 查找是否可以合并到现有时间点 // 查找是否可以合并到现有时间点
for (const key of timeKeys) { for (const key of timeKeys) {
if (Math.abs(key - t) <= 1500) { if (Math.abs(key - t) <= 5000) {
foundKey = key; foundKey = key;
break; break;
} }
@@ -95,10 +95,11 @@ const PingChart = memo(({ node, hours }: PingChartProps) => {
timeKeys.push(useKey); timeKeys.push(useKey);
} }
} }
grouped[useKey][rec.task_id] = rec.value; grouped[useKey][rec.task_id] = rec.value === -1 ? null : rec.value;
} }
let full = Object.values(grouped).sort((a: any, b: any) => a.time - b.time); let full = Object.values(grouped).sort((a: any, b: any) => a.time - b.time);
console.log("Full :", full);
if (hours !== 0) { if (hours !== 0) {
const task = pingHistory.tasks; const task = pingHistory.tasks;
@@ -227,6 +228,8 @@ const PingChart = memo(({ node, hours }: PingChartProps) => {
}); });
}, [pingHistory?.records, sortedTasks, timeRange]); }, [pingHistory?.records, sortedTasks, timeRange]);
console.log("chartData:", chartData);
return ( return (
<div className="relative space-y-4"> <div className="relative space-y-4">
{loading && ( {loading && (
@@ -451,8 +454,8 @@ const PingChart = memo(({ node, hours }: PingChartProps) => {
{...brushIndices} {...brushIndices}
dataKey="time" dataKey="time"
height={30} height={30}
stroke="var(--accent-track)" stroke="var(--theme-text-muted-color)"
fill="var(--accent-4)" fill="var(--accent-a4)"
alwaysShowText alwaysShowText
tickFormatter={(time) => { tickFormatter={(time) => {
const date = new Date(time); const date = new Date(time);