mirror of
https://github.com/YuzuZensai/Pien-Studio.git
synced 2026-09-02 14:18:35 +00:00
✨ feat: auto system theme
This commit is contained in:
@@ -4,7 +4,7 @@ import React from "react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { MousePointer2, Hand, ScanFace, Type, ImagePlus } from "lucide-react";
|
||||
import { useEditorStore } from "../../../store/editor-store";
|
||||
import { useUiStore } from "../../../store/ui-store";
|
||||
import { useUiStore, isDarkTheme } from "../../../store/ui-store";
|
||||
import { CanvasSizeModal } from "../../../components/canvas-size-modal";
|
||||
import { EditorCanvasStage } from "../../../components/editor/editor-canvas-stage";
|
||||
import { EditorHeader } from "../../../components/editor/editor-header";
|
||||
@@ -148,7 +148,7 @@ export default function EditorPage() {
|
||||
t,
|
||||
});
|
||||
|
||||
const isDark = theme === "dark";
|
||||
const isDark = isDarkTheme(theme);
|
||||
const { width: cw, height: ch } = project.canvas;
|
||||
|
||||
const handleSave = React.useCallback(async () => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { addLayer, createLayer, createProject, parseProjectFile, setCanvasSize }
|
||||
import type { Project } from "@pien-studio/types";
|
||||
import { deleteProject, duplicateProject, loadProjects, upsertProject } from "@pien-studio/storage";
|
||||
import { useEditorStore } from "../store/editor-store";
|
||||
import { useUiStore } from "../store/ui-store";
|
||||
import { useUiStore, isDarkTheme } from "../store/ui-store";
|
||||
import { accentButtonClass, cx, mutedSurfaceClass, subtleButtonClass, surfaceClass } from "../lib/theme";
|
||||
import { UiPreferences } from "../components/ui-preferences";
|
||||
import { useAssetCleanupJob } from "../hooks/use-asset-cleanup-job";
|
||||
@@ -23,7 +23,7 @@ export default function HomePage() {
|
||||
const [projectPendingDelete, setProjectPendingDelete] = React.useState<Project | null>(null);
|
||||
const projectInputRef = React.useRef<HTMLInputElement | null>(null);
|
||||
const imageInputRef = React.useRef<HTMLInputElement | null>(null);
|
||||
const isDark = theme === "dark";
|
||||
const isDark = isDarkTheme(theme);
|
||||
|
||||
const refreshProjects = React.useCallback(async () => {
|
||||
setProjects(await loadProjects());
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
import React from "react";
|
||||
import { useUiStore } from "../store/ui-store";
|
||||
import { isDarkTheme } from "../store/ui-store";
|
||||
import { cx, subtleButtonClass } from "../lib/theme";
|
||||
import { useTranslations } from "../hooks/use-translations";
|
||||
|
||||
export function UiPreferences({ compact = false }: { compact?: boolean }) {
|
||||
const { theme, locale, setTheme, setLocale } = useUiStore((s) => s);
|
||||
const { t } = useTranslations();
|
||||
const isDark = theme === "dark";
|
||||
const isDark = isDarkTheme(theme);
|
||||
const wrapperHeight = compact ? "h-7" : "h-9";
|
||||
const textSize = compact ? "text-[10px]" : "text-xs";
|
||||
|
||||
@@ -33,10 +34,13 @@ export function UiPreferences({ compact = false }: { compact?: boolean }) {
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
|
||||
onClick={() => {
|
||||
const next = theme === "light" ? "dark" : theme === "dark" ? "system" : "light";
|
||||
setTheme(next);
|
||||
}}
|
||||
className={cx("rounded border px-3 font-semibold", subtleButtonClass(isDark), wrapperHeight, textSize)}
|
||||
>
|
||||
{theme === "dark" ? t("ui.darkMode") : t("ui.lightMode")}
|
||||
{theme === "dark" ? t("ui.darkMode") : theme === "system" ? t("ui.systemMode") : t("ui.lightMode")}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,16 +2,22 @@
|
||||
|
||||
import { create } from "zustand";
|
||||
|
||||
export type AppTheme = "light" | "dark";
|
||||
export type AppTheme = "light" | "dark" | "system";
|
||||
export type AppLocale = "en" | "th" | "ja";
|
||||
|
||||
const THEME_KEY = "pien.ui.theme";
|
||||
const LOCALE_KEY = "pien.ui.locale";
|
||||
|
||||
function getSystemTheme(): "light" | "dark" {
|
||||
if (typeof window === "undefined") return "light";
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||
}
|
||||
|
||||
function readTheme(): AppTheme {
|
||||
if (typeof window === "undefined") return "light";
|
||||
const value = window.localStorage.getItem(THEME_KEY);
|
||||
return value === "light" ? "light" : "dark";
|
||||
if (value === "light" || value === "dark" || value === "system") return value;
|
||||
return "system";
|
||||
}
|
||||
|
||||
function readLocale(): AppLocale {
|
||||
@@ -23,7 +29,12 @@ function readLocale(): AppLocale {
|
||||
|
||||
function applyTheme(theme: AppTheme): void {
|
||||
if (typeof document === "undefined") return;
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
const resolved = theme === "system" ? getSystemTheme() : theme;
|
||||
document.documentElement.setAttribute("data-theme", resolved);
|
||||
}
|
||||
|
||||
export function isDarkTheme(theme: AppTheme): boolean {
|
||||
return theme === "system" ? getSystemTheme() === "dark" : theme === "dark";
|
||||
}
|
||||
|
||||
type UiState = {
|
||||
|
||||
Reference in New Issue
Block a user