SnowLuma v2.4.2 source snapshot
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run

This commit is contained in:
TanTan committed 2026-11-13 09:02:00 +08:00
commit fa9c73363d
1367 files changed
+355735

No files matched your search

@@ -0,0 +1,59 @@
import { Monitor, Moon, Sun } from 'lucide-react';
import { motion } from 'motion/react';
import { paletteResolved, useTheme, type ThemeMode } from '@/contexts/ThemeContext';
import { cn } from '@/lib/utils';
const OPTIONS: { mode: ThemeMode; icon: typeof Sun; label: string }[] = [
{ mode: 'system', icon: Monitor, label: '跟随系统' },
{ mode: 'light', icon: Sun, label: '浅色' },
{ mode: 'dark', icon: Moon, label: '深色' },
];
export function ThemeToggle({ className }: { className?: string }) {
const { appearance, mode, setMode, managedBy } = useTheme();
// A Catppuccin flavor fixes light/dark, so this quick toggle would be inert
// (and its pill would desync from the forced scheme) — hide it. Light/dark is
// then changed by switching the palette back to “默认” in settings.
if (managedBy || paletteResolved(appearance.palette)) {
return null;
}
return (
<div
className={cn(
'relative inline-flex shrink-0 items-center rounded-full border bg-card/60 p-0 shadow-xs md:p-0.5',
className,
)}
>
{OPTIONS.map(({ mode: m, icon: Icon, label }) => {
const active = mode === m;
return (
<button
key={m}
type="button"
title={label}
aria-label={label}
onClick={() => setMode(m)}
data-press-scale=""
className={cn(
'relative flex size-[44px] items-center justify-center rounded-full transition-[color,scale] duration-150 ease-out active:scale-[0.96] motion-reduce:active:scale-100 md:size-10 cursor-pointer',
active
? 'text-primary-foreground'
: 'text-muted-foreground hover:text-foreground',
)}
>
{active && (
<motion.span
layoutId="theme-toggle-pill"
className="absolute inset-[8px] rounded-full bg-primary md:inset-1.5"
transition={{ type: 'spring', stiffness: 380, damping: 30 }}
/>
)}
<Icon className="relative z-10 size-3.5" />
</button>
);
})}
</div>
);
}