Refactor build and start scripts in package.json; add plugin to exclude debug files from production build in vite.config.ts; remove ConnectScreen component and replace with ConnectionPlaceholder in multiple pages; update DeviceContext to auto-connect using device IP from URL; enhance locale files with new device IP labels.
This commit is contained in:
+92
-37
@@ -10,7 +10,7 @@
|
||||
6. iOS list rows (输入源/输出端口/音频设置/系统设置/VU表)
|
||||
============================================================ */
|
||||
import { useDevice } from "@/contexts/DeviceContext";
|
||||
import ConnectScreen from "@/components/ConnectScreen";
|
||||
import ConnectionPlaceholder from "@/components/ConnectionPlaceholder";
|
||||
import BottomNav from "@/components/BottomNav";
|
||||
import {
|
||||
AlertDialog,
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
Sliders,
|
||||
Activity,
|
||||
Gauge,
|
||||
Undo2,
|
||||
ChevronDown,
|
||||
Wifi,
|
||||
Bluetooth,
|
||||
@@ -57,14 +58,17 @@ const JACK_EGG_SEQUENCE = ["lg", "smTop", "smBot"] as const;
|
||||
type JackId = (typeof JACK_EGG_SEQUENCE)[number];
|
||||
|
||||
const VU_BAR_COUNT = 20;
|
||||
const EGG_DURATION_MS = 4200;
|
||||
const EGG_BEAT_MS = 130;
|
||||
const EGG_DANCE_DURATION_MS = 5000;
|
||||
const EGG_WAVE_DURATION_MS = 5000;
|
||||
const EGG_WAVE_HALF_MS = EGG_WAVE_DURATION_MS / 2;
|
||||
const EGG_BEAT_MS = 400;
|
||||
const JACK_SEQUENCE_TIMEOUT_MS = 4000;
|
||||
|
||||
/** 旋律主音在 20 段 VU 上的位置(约 4.2s) */
|
||||
/** 旋律主音在 20 段 VU 上的位置(约 5s) */
|
||||
const EGG_MELODY_LEAD = [
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1,
|
||||
0, 4, 8, 12, 16, 19, 16, 12, 8, 4, 0, 2, 5, 9, 13, 17, 19, 14, 10, 6, 2,
|
||||
0, 3, 7, 11, 15, 18, 15, 11, 7, 3,
|
||||
];
|
||||
|
||||
function idleVuBarHeights(): number[] {
|
||||
@@ -73,15 +77,44 @@ function idleVuBarHeights(): number[] {
|
||||
);
|
||||
}
|
||||
|
||||
function vuHeightsForMelodyBeat(lead: number, pulse: number): number[] {
|
||||
/** 彩蛋律动:主峰随节拍移动,每拍起落,全柱叠加波纹 */
|
||||
function vuHeightsForDance(elapsedMs: number): number[] {
|
||||
const t = elapsedMs / 1000;
|
||||
const beatIdx = Math.floor(elapsedMs / EGG_BEAT_MS) % EGG_MELODY_LEAD.length;
|
||||
const lead = EGG_MELODY_LEAD[beatIdx];
|
||||
const beatPhase = (elapsedMs % EGG_BEAT_MS) / EGG_BEAT_MS;
|
||||
const kick = Math.sin(beatPhase * Math.PI);
|
||||
|
||||
return Array.from({ length: VU_BAR_COUNT }, (_, i) => {
|
||||
const dist = Math.abs(i - lead);
|
||||
let amp = 0.14;
|
||||
if (dist === 0) amp = 0.96;
|
||||
else if (dist === 1) amp = 0.74;
|
||||
else if (dist === 2) amp = 0.5;
|
||||
else if (dist === 3) amp = 0.3;
|
||||
return Math.min(1, amp * (0.82 + pulse * 0.18));
|
||||
let peak = 0.06;
|
||||
if (dist === 0) peak = 1;
|
||||
else if (dist === 1) peak = 0.82;
|
||||
else if (dist === 2) peak = 0.62;
|
||||
else if (dist === 3) peak = 0.42;
|
||||
else if (dist === 4) peak = 0.26;
|
||||
|
||||
const ripple = 0.35 * Math.max(0, Math.sin(t * 2.75 + i * 0.62));
|
||||
const sway = 0.1 * Math.sin(t * 1.4 + i * 0.35);
|
||||
const h = 0.05 + peak * kick + ripple * (0.55 + kick * 0.45) + sway;
|
||||
return Math.min(1, Math.max(0.05, h));
|
||||
});
|
||||
}
|
||||
|
||||
/** 彩蛋律动二:海浪形波从左→右,再右→左 */
|
||||
function vuHeightsForWave(phaseElapsedMs: number): number[] {
|
||||
const half = EGG_WAVE_HALF_MS;
|
||||
const ltr = phaseElapsedMs < half;
|
||||
const localMs = ltr ? phaseElapsedMs : phaseElapsedMs - half;
|
||||
const t = localMs / 1000;
|
||||
const direction = ltr ? 1 : -1;
|
||||
const speed = 0.75;
|
||||
|
||||
return Array.from({ length: VU_BAR_COUNT }, (_, i) => {
|
||||
const phase = t * speed * Math.PI * 2 - direction * i * 0.72;
|
||||
const wave = (Math.sin(phase) + 1) / 2;
|
||||
const foam = 0.06 + 0.05 * Math.sin(i * 0.28 + t * 0.5);
|
||||
return Math.min(1, Math.max(0.06, foam + wave * 0.9));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -131,7 +164,7 @@ function ListRow({
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
const { isConnected, deviceState, setVolume, updateSetting, api, disconnect, isDemoMode } = useDevice();
|
||||
const { isConnected, deviceState, setVolume, updateSetting, api, disconnect, isDemoMode, ip } = useDevice();
|
||||
const [, setLocation] = useLocation();
|
||||
const powerActionRef = useRef(false);
|
||||
const hpEqLastTapRef = useRef<number | null>(null);
|
||||
@@ -144,6 +177,8 @@ export default function Home() {
|
||||
const jackProgressRef = useRef(0);
|
||||
const jackResetTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const eggRafRef = useRef<number | null>(null);
|
||||
/** 下次三连击成功时播放的律动:1=旋律,2=海浪(各需单独三连击触发) */
|
||||
const jackEggNextRef = useRef<1 | 2>(1);
|
||||
|
||||
const homeText = useMemo((): HomeLocale => {
|
||||
if (!deviceState) return {} as HomeLocale;
|
||||
@@ -257,28 +292,33 @@ export default function Home() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const triggerJackEasterEgg = useCallback(() => {
|
||||
setEasterEggActive(true);
|
||||
jackProgressRef.current = 0;
|
||||
const start = performance.now();
|
||||
const runJackEggAnimation = useCallback(
|
||||
(mode: "dance" | "wave") => {
|
||||
setEasterEggActive(true);
|
||||
setVuHeights(mode === "dance" ? vuHeightsForDance(0) : vuHeightsForWave(0));
|
||||
jackProgressRef.current = 0;
|
||||
const duration = mode === "dance" ? EGG_DANCE_DURATION_MS : EGG_WAVE_DURATION_MS;
|
||||
const start = performance.now();
|
||||
|
||||
const tick = (now: number) => {
|
||||
const elapsed = now - start;
|
||||
if (elapsed >= EGG_DURATION_MS) {
|
||||
setEasterEggActive(false);
|
||||
setVuHeights(idleVuBarHeights());
|
||||
eggRafRef.current = null;
|
||||
return;
|
||||
}
|
||||
const beat = Math.floor(elapsed / EGG_BEAT_MS) % EGG_MELODY_LEAD.length;
|
||||
const pulse = 0.5 + 0.5 * Math.sin(elapsed * 0.02);
|
||||
setVuHeights(vuHeightsForMelodyBeat(EGG_MELODY_LEAD[beat], pulse));
|
||||
const tick = (now: number) => {
|
||||
const elapsed = now - start;
|
||||
if (elapsed >= duration) {
|
||||
setEasterEggActive(false);
|
||||
setVuHeights(idleVuBarHeights());
|
||||
eggRafRef.current = null;
|
||||
return;
|
||||
}
|
||||
setVuHeights(
|
||||
mode === "dance" ? vuHeightsForDance(elapsed) : vuHeightsForWave(elapsed),
|
||||
);
|
||||
eggRafRef.current = requestAnimationFrame(tick);
|
||||
};
|
||||
|
||||
if (eggRafRef.current !== null) cancelAnimationFrame(eggRafRef.current);
|
||||
eggRafRef.current = requestAnimationFrame(tick);
|
||||
};
|
||||
|
||||
if (eggRafRef.current !== null) cancelAnimationFrame(eggRafRef.current);
|
||||
eggRafRef.current = requestAnimationFrame(tick);
|
||||
}, []);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const onJackClick = useCallback(
|
||||
(jack: JackId) => {
|
||||
@@ -288,7 +328,13 @@ export default function Home() {
|
||||
if (jack === expected) {
|
||||
jackProgressRef.current += 1;
|
||||
if (jackProgressRef.current >= JACK_EGG_SEQUENCE.length) {
|
||||
triggerJackEasterEgg();
|
||||
if (jackEggNextRef.current === 1) {
|
||||
runJackEggAnimation("dance");
|
||||
jackEggNextRef.current = 2;
|
||||
} else {
|
||||
runJackEggAnimation("wave");
|
||||
jackEggNextRef.current = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
jackProgressRef.current = jack === JACK_EGG_SEQUENCE[0] ? 1 : 0;
|
||||
@@ -299,7 +345,7 @@ export default function Home() {
|
||||
jackProgressRef.current = 0;
|
||||
}, JACK_SEQUENCE_TIMEOUT_MS);
|
||||
},
|
||||
[easterEggActive, triggerJackEasterEgg],
|
||||
[easterEggActive, runJackEggAnimation],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -309,7 +355,7 @@ export default function Home() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!isConnected) return <ConnectScreen />;
|
||||
if (!isConnected) return <ConnectionPlaceholder />;
|
||||
|
||||
const ds = deviceState!;
|
||||
const inputLabel = INPUT_LABELS[ds.input] ?? "USB-C";
|
||||
@@ -471,8 +517,8 @@ export default function Home() {
|
||||
background: isHot ? "#ef4444" : i > 13 ? "#f59e0b" : "#00FFF6",
|
||||
opacity: easterEggActive ? 0.95 : 0.7,
|
||||
transition: easterEggActive
|
||||
? "height 90ms ease-out, opacity 120ms ease-out"
|
||||
: "height 200ms ease-out",
|
||||
? "none"
|
||||
: "height 200ms ease-out, opacity 200ms ease-out",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@@ -766,6 +812,15 @@ export default function Home() {
|
||||
value={vuLabel}
|
||||
onClick={() => setLocation("/vu")}
|
||||
/>
|
||||
<ListRow
|
||||
icon={<Undo2 size={16} />}
|
||||
label={homeText.backToLegacy ?? "返回旧版"}
|
||||
onClick={() => {
|
||||
const target = ip.trim();
|
||||
if (!target) return;
|
||||
window.location.href = `https://am.luxsinaudio.com/x8/i.html?ip=${encodeURIComponent(target)}#/home`;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user