2026-06-20 03:49:58 +08:00
|
|
|
import { useThemeStore, type DarkMode } from "@/stores/themeStore";
|
|
|
|
|
import { useBackgroundStore } from "@/stores/backgroundStore";
|
2026-07-26 06:42:41 +08:00
|
|
|
import { Switch, Button, Slider } from "@heroui/react";
|
2026-06-28 03:37:07 +08:00
|
|
|
import { DEFAULT_BG } from "@/lib/mode";
|
2026-06-20 03:49:58 +08:00
|
|
|
import clsx from "clsx";
|
2026-07-26 06:42:41 +08:00
|
|
|
import { SettingCard, SettingRow, PageHeader, SectionTitle } from "@/components/setting";
|
2026-06-20 03:49:58 +08:00
|
|
|
|
|
|
|
|
function ThemePreviewCard({ mode, selected, onClick }: { mode: DarkMode; selected: boolean; onClick: () => void }) {
|
|
|
|
|
const isDark = mode === "dark";
|
|
|
|
|
const isAuto = mode === "auto";
|
|
|
|
|
|
|
|
|
|
const label = isAuto ? "跟随系统" : isDark ? "深色模式" : "浅色模式";
|
|
|
|
|
|
|
|
|
|
return (
|
2026-08-28 02:08:12 +08:00
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
onPress={onClick}
|
2026-06-20 03:49:58 +08:00
|
|
|
className={clsx(
|
2026-08-28 02:08:12 +08:00
|
|
|
"relative rounded-md p-1 transition-all duration-200 h-auto min-w-0",
|
2026-06-20 03:49:58 +08:00
|
|
|
"border-2",
|
|
|
|
|
selected
|
2026-08-28 02:08:12 +08:00
|
|
|
? "!border-primary ring-2 ring-primary/20"
|
|
|
|
|
: "!border-transparent hover:!border-muted-foreground/20",
|
2026-06-20 03:49:58 +08:00
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<div className="relative w-[140px] h-[96px] rounded overflow-hidden">
|
|
|
|
|
{isAuto ? (
|
|
|
|
|
<div className="flex w-full h-full">
|
|
|
|
|
<div className="w-1/2 h-full bg-white flex flex-col">
|
|
|
|
|
<div className="h-[6px] bg-gray-200 flex items-center px-1 gap-[2px]">
|
|
|
|
|
<div className="w-[3px] h-[3px] rounded-full bg-red-400" />
|
|
|
|
|
<div className="w-[3px] h-[3px] rounded-full bg-yellow-400" />
|
|
|
|
|
<div className="w-[3px] h-[3px] rounded-full bg-green-400" />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex-1 px-2 py-1">
|
|
|
|
|
<div className="w-full h-[4px] rounded bg-gray-200 mb-1" />
|
|
|
|
|
<div className="w-3/4 h-[3px] rounded bg-gray-100" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="w-1/2 h-full bg-[#1c1c1e] flex flex-col">
|
|
|
|
|
<div className="h-[6px] bg-[#2c2c2e] flex items-center px-1 gap-[2px]">
|
|
|
|
|
<div className="w-[3px] h-[3px] rounded-full bg-red-400" />
|
|
|
|
|
<div className="w-[3px] h-[3px] rounded-full bg-yellow-400" />
|
|
|
|
|
<div className="w-[3px] h-[3px] rounded-full bg-green-400" />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex-1 px-2 py-1">
|
|
|
|
|
<div className="w-full h-[4px] rounded bg-[#3a3a3c] mb-1" />
|
|
|
|
|
<div className="w-3/4 h-[3px] rounded bg-[#2c2c2e]" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
) : isDark ? (
|
|
|
|
|
<div className="w-full h-full bg-[#1c1c1e] flex flex-col">
|
|
|
|
|
<div className="h-[6px] bg-[#2c2c2e] flex items-center px-1 gap-[2px]">
|
|
|
|
|
<div className="w-[3px] h-[3px] rounded-full bg-red-400" />
|
|
|
|
|
<div className="w-[3px] h-[3px] rounded-full bg-yellow-400" />
|
|
|
|
|
<div className="w-[3px] h-[3px] rounded-full bg-green-400" />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex-1 px-2 py-1">
|
|
|
|
|
<div className="w-full h-[4px] rounded bg-[#3a3a3c] mb-1" />
|
|
|
|
|
<div className="w-3/4 h-[3px] rounded bg-[#2c2c2e] mb-1" />
|
|
|
|
|
<div className="w-1/2 h-[3px] rounded bg-[#2c2c2e]" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="w-full h-full bg-white flex flex-col">
|
|
|
|
|
<div className="h-[6px] bg-gray-200 flex items-center px-1 gap-[2px]">
|
|
|
|
|
<div className="w-[3px] h-[3px] rounded-full bg-red-400" />
|
|
|
|
|
<div className="w-[3px] h-[3px] rounded-full bg-yellow-400" />
|
|
|
|
|
<div className="w-[3px] h-[3px] rounded-full bg-green-400" />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex-1 px-2 py-1">
|
|
|
|
|
<div className="w-full h-[4px] rounded bg-gray-200 mb-1" />
|
|
|
|
|
<div className="w-3/4 h-[3px] rounded bg-gray-100 mb-1" />
|
|
|
|
|
<div className="w-1/2 h-[3px] rounded bg-gray-100" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-[11px] text-center mt-1.5 text-muted-foreground">{label}</p>
|
2026-08-28 02:08:12 +08:00
|
|
|
</Button>
|
2026-06-20 03:49:58 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function ThemeBgSetting() {
|
|
|
|
|
const { darkMode, setDarkMode, parallax, setParallax } = useThemeStore();
|
2026-06-20 22:15:59 +08:00
|
|
|
const { image, opacity, setOpacity, blur, setBlur, setImage, reset } = useBackgroundStore();
|
2026-06-20 03:49:58 +08:00
|
|
|
|
|
|
|
|
const handlePickImage = async () => {
|
2026-07-11 23:33:44 +08:00
|
|
|
const dataUrl = await (window as any).electronAPI?.pickBackgroundImage();
|
|
|
|
|
if (dataUrl) {
|
|
|
|
|
setImage(dataUrl);
|
2026-06-20 22:15:59 +08:00
|
|
|
}
|
2026-06-20 03:49:58 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleReset = async () => {
|
|
|
|
|
await reset();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
2026-07-26 06:42:41 +08:00
|
|
|
<PageHeader title="主题与背景" desc="切换深色模式、更换背景图片与调整视觉效果" />
|
2026-06-20 03:49:58 +08:00
|
|
|
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
<div>
|
2026-07-26 06:42:41 +08:00
|
|
|
<SectionTitle>深色模式</SectionTitle>
|
|
|
|
|
<SettingCard>
|
2026-06-20 03:49:58 +08:00
|
|
|
<div className="flex items-center justify-center gap-4 py-2">
|
|
|
|
|
<ThemePreviewCard mode="light" selected={darkMode === "light"} onClick={() => setDarkMode("light")} />
|
|
|
|
|
<ThemePreviewCard mode="auto" selected={darkMode === "auto"} onClick={() => setDarkMode("auto")} />
|
|
|
|
|
<ThemePreviewCard mode="dark" selected={darkMode === "dark"} onClick={() => setDarkMode("dark")} />
|
|
|
|
|
</div>
|
2026-07-26 06:42:41 +08:00
|
|
|
</SettingCard>
|
2026-06-20 03:49:58 +08:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
2026-07-26 06:42:41 +08:00
|
|
|
<SectionTitle>背景</SectionTitle>
|
2026-06-20 03:49:58 +08:00
|
|
|
<div className="space-y-3">
|
2026-07-26 06:42:41 +08:00
|
|
|
<SettingCard>
|
2026-06-20 03:49:58 +08:00
|
|
|
<SettingRow label="背景图片" desc="更换启动器背景图片">
|
2026-07-26 06:42:41 +08:00
|
|
|
<Button variant="outline" size="sm" onPress={handlePickImage}>
|
2026-06-20 03:49:58 +08:00
|
|
|
选择图片
|
|
|
|
|
</Button>
|
|
|
|
|
</SettingRow>
|
2026-06-28 03:37:07 +08:00
|
|
|
{image && image !== DEFAULT_BG && (
|
2026-06-20 22:15:59 +08:00
|
|
|
<div className="mt-3 rounded-lg overflow-hidden border border-border/50">
|
|
|
|
|
<img
|
|
|
|
|
src={image}
|
|
|
|
|
alt="背景预览"
|
|
|
|
|
className="w-full h-[120px] object-cover"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-07-26 06:42:41 +08:00
|
|
|
</SettingCard>
|
2026-06-20 03:49:58 +08:00
|
|
|
|
2026-07-26 06:42:41 +08:00
|
|
|
<SettingCard>
|
2026-06-20 03:49:58 +08:00
|
|
|
<SettingRow label="背景模糊" desc={`对背景图施加高斯模糊,当前 ${blur}px`}>
|
|
|
|
|
<Slider
|
|
|
|
|
className="w-[180px]"
|
2026-07-26 06:42:41 +08:00
|
|
|
value={blur}
|
|
|
|
|
minValue={0}
|
|
|
|
|
maxValue={20}
|
2026-06-20 03:49:58 +08:00
|
|
|
step={1}
|
2026-07-26 06:42:41 +08:00
|
|
|
onChange={(v) => setBlur(typeof v === "number" ? v : v[0])}
|
|
|
|
|
>
|
|
|
|
|
<Slider.Track>
|
|
|
|
|
<Slider.Fill />
|
|
|
|
|
<Slider.Thumb />
|
|
|
|
|
</Slider.Track>
|
|
|
|
|
</Slider>
|
2026-06-20 03:49:58 +08:00
|
|
|
</SettingRow>
|
2026-07-26 06:42:41 +08:00
|
|
|
</SettingCard>
|
2026-06-20 03:49:58 +08:00
|
|
|
|
2026-07-26 06:42:41 +08:00
|
|
|
<SettingCard>
|
2026-06-20 03:49:58 +08:00
|
|
|
<SettingRow label="背景不透明度" desc={`控制背景图的可见程度,当前 ${Math.round(opacity * 100)}%`}>
|
|
|
|
|
<Slider
|
|
|
|
|
className="w-[180px]"
|
2026-07-26 06:42:41 +08:00
|
|
|
value={opacity * 100}
|
|
|
|
|
minValue={0}
|
|
|
|
|
maxValue={100}
|
2026-06-20 03:49:58 +08:00
|
|
|
step={1}
|
2026-07-26 06:42:41 +08:00
|
|
|
onChange={(v) => setOpacity((typeof v === "number" ? v : v[0]) / 100)}
|
|
|
|
|
>
|
|
|
|
|
<Slider.Track>
|
|
|
|
|
<Slider.Fill />
|
|
|
|
|
<Slider.Thumb />
|
|
|
|
|
</Slider.Track>
|
|
|
|
|
</Slider>
|
2026-06-20 03:49:58 +08:00
|
|
|
</SettingRow>
|
2026-07-26 06:42:41 +08:00
|
|
|
</SettingCard>
|
2026-06-20 03:49:58 +08:00
|
|
|
|
2026-07-26 06:42:41 +08:00
|
|
|
<SettingCard>
|
|
|
|
|
<SettingRow label="背景图片视差" desc="背景图片随窗口滚动产生视差位移">
|
2026-08-28 02:08:12 +08:00
|
|
|
<Switch isSelected={parallax} onChange={setParallax}>
|
2026-07-26 06:42:41 +08:00
|
|
|
<Switch.Control>
|
|
|
|
|
<Switch.Thumb />
|
|
|
|
|
</Switch.Control>
|
|
|
|
|
</Switch>
|
2026-06-20 03:49:58 +08:00
|
|
|
</SettingRow>
|
2026-07-26 06:42:41 +08:00
|
|
|
</SettingCard>
|
2026-06-20 03:49:58 +08:00
|
|
|
|
2026-07-26 06:42:41 +08:00
|
|
|
<SettingCard>
|
2026-06-20 03:49:58 +08:00
|
|
|
<SettingRow label="恢复默认" desc="重置所有背景设置为初始状态">
|
2026-07-26 06:42:41 +08:00
|
|
|
<Button variant="danger" size="sm" onPress={handleReset}>
|
2026-06-20 03:49:58 +08:00
|
|
|
恢复默认
|
|
|
|
|
</Button>
|
|
|
|
|
</SettingRow>
|
2026-07-26 06:42:41 +08:00
|
|
|
</SettingCard>
|
2026-06-20 03:49:58 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|