万圣节&圣诞节皮肤

This commit is contained in:
eafonyang
2026-09-29 16:03:23 +08:00
parent 9f1ddc156e
commit 4363b10917
31 changed files with 1039 additions and 28 deletions
+53 -14
View File
@@ -13,6 +13,17 @@ import { useDevice } from "@/contexts/DeviceContext";
import ConnectionPlaceholder from "@/components/ConnectionPlaceholder";
import BottomNav from "@/components/BottomNav";
import ThumbOnlyRangeSlider from "@/components/ThumbOnlyRangeSlider";
import ChristmasBellIcon from "@/components/festival/ChristmasBellIcon";
import ChristmasVolumeThumb from "@/components/festival/ChristmasVolumeThumb";
import ChristmasCardDecoration from "@/components/festival/ChristmasCardDecoration";
import HalloweenVolumeThumb from "@/components/festival/HalloweenVolumeThumb";
import HalloweenPumpkinIcon from "@/components/festival/HalloweenPumpkinIcon";
import HalloweenCardDecoration from "@/components/festival/HalloweenCardDecoration";
import FestivalGreeting from "@/components/festival/FestivalGreeting";
import "@/components/festival/christmas.css";
import "@/components/festival/halloween.css";
import "@/components/festival/festival.css";
import { useFestival } from "@/lib/festival";
import {
AlertDialog,
AlertDialogAction,
@@ -189,6 +200,9 @@ function ListRow({
export default function Home() {
const { isConnected, deviceState, setVolume, updateSetting, api, isDemoMode, ip } = useDevice();
const [, setLocation] = useLocation();
const festival = useFestival();
const isChristmas = festival === "christmas";
const isHalloween = festival === "halloween";
const powerActionRef = useRef(false);
const hpEqLastTapRef = useRef<number | null>(null);
const hpEqNavigateTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
@@ -540,9 +554,11 @@ export default function Home() {
return (
<div className="min-h-dvh bg-black pb-[calc(5.5rem+env(safe-area-inset-bottom,0px))]">
{/* ── Device render image ── */}
<div className="px-4 pb-4">
<div className="w-full rounded-2xl overflow-hidden flex items-center justify-center"
<div className="relative px-4 pb-4">
<div className="relative w-full rounded-2xl overflow-hidden flex items-center justify-center"
style={{ background: "rgba(18,18,20,0.8)", minHeight: 140 }}>
{isChristmas && <ChristmasCardDecoration />}
{isHalloween && <HalloweenCardDecoration />}
{/* Stylized device illustration */}
<div className="w-full relative" style={{ height: 140 }}>
{/* Device body */}
@@ -593,13 +609,14 @@ export default function Home() {
</div>
</div>
{/* VU display area */}
<div className="flex-1 mx-3 rounded-lg overflow-hidden"
<div className="relative flex-1 mx-3 rounded-lg overflow-hidden"
style={{ background: "#0d1208", border: "1px solid rgba(255,255,255,0.1)", height: 90 }}>
<div className="flex items-center justify-between px-2 pt-1">
<div className={cn("flex items-center justify-between px-2 pt-1", festival && "festival-vu-status")}>
<span className="text-[9px] text-white/50">{ds.audioFormat}</span>
<span className="text-[9px] text-white/50">{volToDB(localVol)}</span>
</div>
{/* Mini VU bars */}
{/* Mini VU bars — 节日期隐藏,让问候语铺满整个 VU 区 */}
{!festival && (
<div
className={cn(
"flex gap-0.5 px-2 pt-1 pb-2 h-16 items-end",
@@ -624,6 +641,9 @@ export default function Home() {
);
})}
</div>
)}
{/* 节日插画铺满 VU 区,文字按容器横排/拆行;状态信息保留在最上层。 */}
{festival && <FestivalGreeting festival={festival} />}
</div>
{/* Right knob */}
<div className="pr-4">
@@ -673,10 +693,16 @@ export default function Home() {
volumeControlsLocked && "opacity-40 cursor-not-allowed",
)}
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-6 h-6">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
<line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/>
</svg>
{isChristmas ? (
<ChristmasBellIcon dim />
) : isHalloween ? (
<HalloweenPumpkinIcon dim />
) : (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-6 h-6">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
<line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/>
</svg>
)}
</button>
<div
@@ -713,6 +739,13 @@ export default function Home() {
if (volumeControlsLocked) return;
setVolume(v);
}}
renderThumb={
isChristmas
? () => <ChristmasVolumeThumb />
: isHalloween
? () => <HalloweenVolumeThumb />
: undefined
}
/>
</div>
@@ -725,11 +758,17 @@ export default function Home() {
volumeControlsLocked && "opacity-40 cursor-not-allowed",
)}
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-6 h-6">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
<path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
<path d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
</svg>
{isChristmas ? (
<ChristmasBellIcon />
) : isHalloween ? (
<HalloweenPumpkinIcon />
) : (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-6 h-6">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
<path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
<path d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
</svg>
)}
</button>
</div>
{/* dB value */}