style: clean up code with prettier and eslint

This commit is contained in:
openhands committed 2026-07-12 20:31:05 +02:00
1 parent 60278b9e71
commit fb8e77bb68
378 files changed
+22407 -20689

No files matched your search

+107 -107
View File
@@ -1,4 +1,4 @@
'use client'
"use client";
/**
* Plays an Arcturus Trax composition by scheduling each sample on the
@@ -15,196 +15,196 @@
* acts as an upper bound, not a forced loop.
*/
import { Loader2, Play, Square } from 'lucide-react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { toast } from 'sonner'
import { Button } from '@/components/ui/button'
import { TRAX_TICK_SECONDS, type TraxChannel } from '@/lib/trax-format'
import { Loader2, Play, Square } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { TRAX_TICK_SECONDS, type TraxChannel } from "@/lib/trax-format";
const TICK_SECONDS = TRAX_TICK_SECONDS
const TICK_SECONDS = TRAX_TICK_SECONDS;
// Module-level registry so only one TraxPlayer plays at a time.
// When a new player starts, the previous one's stop() is invoked.
// `activeOwner` identifies the player via a stable object ref so each
// instance can tell "am I the current one?" without closing over callbacks.
let activeOwner: object | null = null
let activeStop: (() => void) | null = null
let activeOwner: object | null = null;
let activeStop: (() => void) | null = null;
interface TraxPlayerProps {
channels: TraxChannel[]
channels: TraxChannel[];
/** URL prefix to prepend to `sound_machine_sample_{id}.mp3`. */
baseUrl: string
baseUrl: string;
/** Expected total length in seconds (used for the progress bar). */
expectedLength: number
expectedLength: number;
}
type Status = 'idle' | 'loading' | 'ready' | 'playing' | 'error'
type Status = "idle" | "loading" | "ready" | "playing" | "error";
export function TraxPlayer({ channels, baseUrl, expectedLength }: TraxPlayerProps) {
const [status, setStatus] = useState<Status>('idle')
const [loaded, setLoaded] = useState(0)
const [totalSamples, setTotalSamples] = useState(0)
const [progress, setProgress] = useState(0)
const [status, setStatus] = useState<Status>("idle");
const [loaded, setLoaded] = useState(0);
const [totalSamples, setTotalSamples] = useState(0);
const [progress, setProgress] = useState(0);
const ctxRef = useRef<AudioContext | null>(null)
const buffersRef = useRef<Map<number, AudioBuffer>>(new Map())
const sourcesRef = useRef<AudioBufferSourceNode[]>([])
const startTimeRef = useRef(0)
const rafRef = useRef<number | null>(null)
const ctxRef = useRef<AudioContext | null>(null);
const buffersRef = useRef<Map<number, AudioBuffer>>(new Map());
const sourcesRef = useRef<AudioBufferSourceNode[]>([]);
const startTimeRef = useRef(0);
const rafRef = useRef<number | null>(null);
// Unique sample IDs referenced by the track (skip silence).
const uniqueIds = Array.from(
new Set(channels.flatMap((c) => c.events.map((e) => e.sampleId))),
).filter((id) => id > 0)
const uniqueIds = Array.from(new Set(channels.flatMap((c) => c.events.map((e) => e.sampleId)))).filter(
(id) => id > 0,
);
// Stable self-marker used to identify this instance in the module registry.
const selfRef = useRef<object>({})
const selfRef = useRef<object>({});
const stop = useCallback(() => {
for (const src of sourcesRef.current) {
try {
src.stop()
src.stop();
} catch {
/* already stopped */
}
}
sourcesRef.current = []
sourcesRef.current = [];
if (rafRef.current !== null) {
cancelAnimationFrame(rafRef.current)
rafRef.current = null
cancelAnimationFrame(rafRef.current);
rafRef.current = null;
}
setStatus((prev) => (prev === 'playing' ? 'ready' : prev))
setProgress(0)
setStatus((prev) => (prev === "playing" ? "ready" : prev));
setProgress(0);
if (activeOwner === selfRef.current) {
activeOwner = null
activeStop = null
activeOwner = null;
activeStop = null;
}
}, [])
}, []);
useEffect(() => {
return () => {
stop()
ctxRef.current?.close()
ctxRef.current = null
}
}, [stop])
stop();
ctxRef.current?.close();
ctxRef.current = null;
};
}, [stop]);
async function loadBuffers(): Promise<void> {
setStatus('loading')
setTotalSamples(uniqueIds.length)
setLoaded(0)
setStatus("loading");
setTotalSamples(uniqueIds.length);
setLoaded(0);
const AudioCtor = window.AudioContext
const AudioCtor = window.AudioContext;
if (!AudioCtor) {
toast.error('Web Audio API non supportata da questo browser.')
setStatus('error')
return
toast.error("Web Audio API non supportata da questo browser.");
setStatus("error");
return;
}
const ctx = ctxRef.current ?? new AudioCtor()
ctxRef.current = ctx
const ctx = ctxRef.current ?? new AudioCtor();
ctxRef.current = ctx;
const buffers = buffersRef.current
let done = 0
let failed = 0
const buffers = buffersRef.current;
let done = 0;
let failed = 0;
await Promise.all(
uniqueIds.map(async (id) => {
if (buffers.has(id)) {
done++
setLoaded(done)
return
done++;
setLoaded(done);
return;
}
try {
const url = `${baseUrl}sound_machine_sample_${id}.mp3`
const res = await fetch(url)
if (!res.ok) throw new Error(`HTTP ${res.status}`)
const arr = await res.arrayBuffer()
const buf = await ctx.decodeAudioData(arr)
buffers.set(id, buf)
const url = `${baseUrl}sound_machine_sample_${id}.mp3`;
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const arr = await res.arrayBuffer();
const buf = await ctx.decodeAudioData(arr);
buffers.set(id, buf);
} catch (err) {
failed++
console.warn(`[trax] failed to load sample ${id}:`, err)
failed++;
console.warn(`[trax] failed to load sample ${id}:`, err);
} finally {
done++
setLoaded(done)
done++;
setLoaded(done);
}
}),
)
);
if (failed > 0) {
toast.warning(
`${failed}/${uniqueIds.length} sample non caricati — la riproduzione potrebbe avere buchi.`,
)
);
}
if (failed === uniqueIds.length) {
setStatus('error')
return
setStatus("error");
return;
}
setStatus('ready')
setStatus("ready");
}
async function play() {
// Stop any other TraxPlayer currently playing.
if (activeOwner && activeOwner !== selfRef.current) {
activeStop?.()
activeStop?.();
}
if (status !== 'ready') {
await loadBuffers()
if (status !== "ready") {
await loadBuffers();
}
const ctx = ctxRef.current
if (!ctx) return
if (ctx.state === 'suspended') await ctx.resume()
const ctx = ctxRef.current;
if (!ctx) return;
if (ctx.state === "suspended") await ctx.resume();
stop() // just in case
const buffers = buffersRef.current
const start = ctx.currentTime + 0.05 // tiny head-start
startTimeRef.current = start
const scheduled: AudioBufferSourceNode[] = []
stop(); // just in case
const buffers = buffersRef.current;
const start = ctx.currentTime + 0.05; // tiny head-start
startTimeRef.current = start;
const scheduled: AudioBufferSourceNode[] = [];
for (const ch of channels) {
let t = 0 // ticks from channel start
let t = 0; // ticks from channel start
for (const ev of ch.events) {
if (ev.sampleId > 0) {
const buf = buffers.get(ev.sampleId)
const buf = buffers.get(ev.sampleId);
if (buf) {
const src = ctx.createBufferSource()
src.buffer = buf
src.connect(ctx.destination)
const when = start + t * TICK_SECONDS
const maxDur = ev.ticks * TICK_SECONDS
src.start(when, 0, Math.min(buf.duration, maxDur))
scheduled.push(src)
const src = ctx.createBufferSource();
src.buffer = buf;
src.connect(ctx.destination);
const when = start + t * TICK_SECONDS;
const maxDur = ev.ticks * TICK_SECONDS;
src.start(when, 0, Math.min(buf.duration, maxDur));
scheduled.push(src);
}
}
t += ev.ticks
t += ev.ticks;
}
}
sourcesRef.current = scheduled
setStatus('playing')
activeOwner = selfRef.current
activeStop = stop
sourcesRef.current = scheduled;
setStatus("playing");
activeOwner = selfRef.current;
activeStop = stop;
// Progress tick.
const totalSec = Math.max(
expectedLength,
...channels.map((c) => c.events.reduce((s, e) => s + e.ticks, 0) * TICK_SECONDS),
)
);
const tick = () => {
const elapsed = ctxRef.current ? ctxRef.current.currentTime - startTimeRef.current : 0
const pct = totalSec > 0 ? Math.min(100, (elapsed / totalSec) * 100) : 0
setProgress(pct)
const elapsed = ctxRef.current ? ctxRef.current.currentTime - startTimeRef.current : 0;
const pct = totalSec > 0 ? Math.min(100, (elapsed / totalSec) * 100) : 0;
setProgress(pct);
if (elapsed >= totalSec) {
stop()
return
stop();
return;
}
rafRef.current = requestAnimationFrame(tick)
}
rafRef.current = requestAnimationFrame(tick)
rafRef.current = requestAnimationFrame(tick);
};
rafRef.current = requestAnimationFrame(tick);
}
const isBusy = status === 'loading'
const isPlaying = status === 'playing'
const isBusy = status === "loading";
const isPlaying = status === "playing";
return (
<div className="flex items-center gap-2 w-full">
@@ -237,5 +237,5 @@ export function TraxPlayer({ channels, baseUrl, expectedLength }: TraxPlayerProp
</div>
)}
</div>
)
);
}