> | undefined>(undefined);
const [graphSize, setGraphSize] = useState({ width: 0, height: 0 });
- // Simple dark mode detection
- useEffect(() => {
- const updateMode = () => {
- const isDark = document.documentElement.classList.contains("dark");
- setMode(isDark ? "dark" : "light");
- };
-
- updateMode();
-
- // Watch for changes to the dark class
- const observer = new MutationObserver(updateMode);
- observer.observe(document.documentElement, {
- attributes: true,
- attributeFilter: ["class"],
- });
-
- return () => observer.disconnect();
- }, []);
-
useEffect(() => {
if (!containerRef.current) return;
setGraphSize(containerRef.current.getBoundingClientRect());
diff --git a/web/src/components/Settings/PreferencesSection.tsx b/web/src/components/Settings/PreferencesSection.tsx
index a61fbe234..af9cf5123 100644
--- a/web/src/components/Settings/PreferencesSection.tsx
+++ b/web/src/components/Settings/PreferencesSection.tsx
@@ -6,9 +6,8 @@ import { Visibility } from "@/types/proto/api/v1/memo_service";
import { UserSetting_GeneralSetting } from "@/types/proto/api/v1/user_service";
import { useTranslate } from "@/utils/i18n";
import { convertVisibilityFromString, convertVisibilityToString } from "@/utils/memo";
-import AppearanceSelect from "../AppearanceSelect";
import LocaleSelect from "../LocaleSelect";
-import ThemeSelector from "../ThemeSelector";
+import ThemeSelect from "../ThemeSelect";
import VisibilityIcon from "../VisibilityIcon";
import WebhookSection from "./WebhookSection";
@@ -20,10 +19,6 @@ const PreferencesSection = observer(() => {
await userStore.updateUserGeneralSetting({ locale }, ["locale"]);
};
- const handleAppearanceSelectChange = async (appearance: Appearance) => {
- await userStore.updateUserGeneralSetting({ appearance }, ["appearance"]);
- };
-
const handleDefaultMemoVisibilityChanged = async (value: string) => {
await userStore.updateUserGeneralSetting({ memoVisibility: value }, ["memoVisibility"]);
};
@@ -35,9 +30,8 @@ const PreferencesSection = observer(() => {
// Provide default values if setting is not loaded yet
const setting: UserSetting_GeneralSetting = generalSetting || {
locale: "en",
- appearance: "system",
memoVisibility: "PRIVATE",
- theme: "",
+ theme: "default",
};
return (
@@ -49,14 +43,9 @@ const PreferencesSection = observer(() => {
-
-
{t("setting.preference-section.apperance")}
-
-
-
{t("setting.preference-section.theme")}
-
+
{t("setting.preference")}
diff --git a/web/src/components/Settings/WorkspaceSection.tsx b/web/src/components/Settings/WorkspaceSection.tsx
index 526e3db36..1a090af0e 100644
--- a/web/src/components/Settings/WorkspaceSection.tsx
+++ b/web/src/components/Settings/WorkspaceSection.tsx
@@ -16,7 +16,7 @@ import { workspaceSettingNamePrefix } from "@/store/common";
import { IdentityProvider } from "@/types/proto/api/v1/idp_service";
import { WorkspaceSetting_GeneralSetting, WorkspaceSetting_Key } from "@/types/proto/api/v1/workspace_service";
import { useTranslate } from "@/utils/i18n";
-import ThemeSelector from "../ThemeSelector";
+import ThemeSelect from "../ThemeSelect";
import UpdateCustomizedProfileDialog from "../UpdateCustomizedProfileDialog";
const WorkspaceSection = observer(() => {
@@ -84,9 +84,9 @@ const WorkspaceSection = observer(() => {
{t("setting.system-section.title")}
Theme
- updatePartialSetting({ theme: value })}
+ onValueChange={(value: string) => updatePartialSetting({ theme: value })}
className="min-w-fit"
/>
diff --git a/web/src/components/ThemeSelect.tsx b/web/src/components/ThemeSelect.tsx
new file mode 100644
index 000000000..4ff918f31
--- /dev/null
+++ b/web/src/components/ThemeSelect.tsx
@@ -0,0 +1,52 @@
+import { Moon, Palette, Sun, Wallpaper } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
+import { workspaceStore } from "@/store";
+
+interface ThemeSelectProps {
+ value?: string;
+ onValueChange?: (theme: string) => void;
+ className?: string;
+}
+
+const ThemeSelect = ({ value, onValueChange, className }: ThemeSelectProps = {}) => {
+ const currentTheme = value || workspaceStore.state.theme || "default";
+
+ const themeOptions: { value: Theme; icon: JSX.Element; label: string }[] = [
+ { value: "default", icon: , label: "Default Light" },
+ { value: "default-dark", icon: , label: "Default Dark" },
+ { value: "paper", icon: , label: "Paper" },
+ { value: "whitewall", icon: , label: "Whitewall" },
+ ];
+
+ const handleThemeChange = (newTheme: Theme) => {
+ if (onValueChange) {
+ onValueChange(newTheme);
+ } else {
+ workspaceStore.setTheme(newTheme);
+ }
+ };
+
+ const currentThemeOption = themeOptions.find((option) => option.value === currentTheme);
+
+ return (
+
+
+
+
+
+ {themeOptions.map((option) => (
+ handleThemeChange(option.value)}>
+ {option.icon}
+ {option.label}
+
+ ))}
+
+
+ );
+};
+
+export default ThemeSelect;
diff --git a/web/src/components/ThemeSelector.tsx b/web/src/components/ThemeSelector.tsx
deleted file mode 100644
index 81b411b3e..000000000
--- a/web/src/components/ThemeSelector.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
-
-interface ThemeSelectorProps {
- value?: string;
- onValueChange: (value: string) => void;
- className?: string;
-}
-
-const THEMES = [
- { value: "default", label: "Default" },
- { value: "paper", label: "Paper" },
- { value: "whitewall", label: "Whitewall" },
-] as const;
-
-export const ThemeSelector = ({ value = "default", onValueChange, className }: ThemeSelectorProps) => {
- return (
-
- );
-};
-
-export default ThemeSelector;
diff --git a/web/src/components/UpdateCustomizedProfileDialog.tsx b/web/src/components/UpdateCustomizedProfileDialog.tsx
index 4c7896332..06eff0e19 100644
--- a/web/src/components/UpdateCustomizedProfileDialog.tsx
+++ b/web/src/components/UpdateCustomizedProfileDialog.tsx
@@ -9,8 +9,8 @@ import { workspaceStore } from "@/store";
import { workspaceSettingNamePrefix } from "@/store/common";
import { WorkspaceSetting_GeneralSetting_CustomProfile, WorkspaceSetting_Key } from "@/types/proto/api/v1/workspace_service";
import { useTranslate } from "@/utils/i18n";
-import AppearanceSelect from "./AppearanceSelect";
import LocaleSelect from "./LocaleSelect";
+import ThemeSelect from "./ThemeSelect";
interface Props {
open: boolean;
@@ -58,19 +58,12 @@ function UpdateCustomizedProfileDialog({ open, onOpenChange, onSuccess }: Props)
});
};
- const handleAppearanceSelectChange = (appearance: Appearance) => {
- setPartialState({
- appearance: appearance,
- });
- };
-
const handleRestoreButtonClick = () => {
setPartialState({
title: "Memos",
logoUrl: "/logo.webp",
description: "",
locale: "en",
- appearance: "system",
});
};
@@ -140,8 +133,8 @@ function UpdateCustomizedProfileDialog({ open, onOpenChange, onSuccess }: Props)
-
-
+
+
diff --git a/web/src/components/ui/badge.tsx b/web/src/components/ui/badge.tsx
index 49d433d4b..2710bfef5 100644
--- a/web/src/components/ui/badge.tsx
+++ b/web/src/components/ui/badge.tsx
@@ -10,7 +10,7 @@ const badgeVariants = cva(
variant: {
default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
secondary: "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
- destructive: "border-transparent bg-destructive text-destructive-foreground [a&]:hover:bg-destructive/90 dark:bg-destructive/60",
+ destructive: "border-transparent bg-destructive text-destructive-foreground [a&]:hover:bg-destructive/90",
outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
},
},
diff --git a/web/src/components/ui/button.tsx b/web/src/components/ui/button.tsx
index bc81817ba..13fc5d825 100644
--- a/web/src/components/ui/button.tsx
+++ b/web/src/components/ui/button.tsx
@@ -9,11 +9,10 @@ const buttonVariants = cva(
variants: {
variant: {
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
- destructive: "bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90 dark:bg-destructive/60",
- outline:
- "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-border dark:hover:bg-input/50",
+ destructive: "bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90",
+ outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
- ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
diff --git a/web/src/components/ui/checkbox.tsx b/web/src/components/ui/checkbox.tsx
index baa1441a3..7af4abea4 100644
--- a/web/src/components/ui/checkbox.tsx
+++ b/web/src/components/ui/checkbox.tsx
@@ -8,7 +8,7 @@ function Checkbox({ className, ...props }: React.ComponentProps) {
type={type}
data-slot="input"
className={cn(
- "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-border flex h-8 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-border flex h-8 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className,
)}
{...props}
diff --git a/web/src/components/ui/radio-group.tsx b/web/src/components/ui/radio-group.tsx
index 4ecddc5f9..0cbcd9bbf 100644
--- a/web/src/components/ui/radio-group.tsx
+++ b/web/src/components/ui/radio-group.tsx
@@ -12,7 +12,7 @@ function RadioGroupItem({ className, ...props }: React.ComponentProps
diff --git a/web/src/components/ui/textarea.tsx b/web/src/components/ui/textarea.tsx
index da0b6b315..7cfa482c9 100644
--- a/web/src/components/ui/textarea.tsx
+++ b/web/src/components/ui/textarea.tsx
@@ -6,7 +6,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {