Refactor UI code for improved readability and consistency
Browse filesUpdated formatting in ui.js for better code clarity, including consistent line breaks and indentation. Adjusted lobby music volume from 0.28 to 0.2 for a softer background experience. Ensured that the length slider and related functions maintain their intended behavior while enhancing overall code maintainability.
- frontend/ui.js +31 -13
frontend/ui.js
CHANGED
|
@@ -28,19 +28,23 @@ export function initUI({
|
|
| 28 |
// 1.5 min are stitched from 30s chunks. The set is hardware-dependent β a
|
| 29 |
// CPU-only backend allows only 30s β so we fetch the real list from /api/config
|
| 30 |
// and collapse the slider when there's a single option.
|
| 31 |
-
const fmtLen = (s) =>
|
|
|
|
| 32 |
let LENGTHS = [30, 60, 90].map((s) => ({ seconds: s, label: fmtLen(s) }));
|
| 33 |
-
const selectedLength = () =>
|
|
|
|
| 34 |
// adapt the slider to what this backend actually allows; defensive β any failure
|
| 35 |
// keeps the 30/60/90 default, so the GPU path is never affected
|
| 36 |
fetch("/api/config")
|
| 37 |
.then((r) => (r.ok ? r.json() : null))
|
| 38 |
.then((cfg) => {
|
| 39 |
-
const allowed =
|
|
|
|
| 40 |
if (!allowed || !allowed.length) return;
|
| 41 |
LENGTHS = allowed.map((s) => ({ seconds: s, label: fmtLen(s) }));
|
| 42 |
lengthSlider.max = String(Math.max(0, LENGTHS.length - 1));
|
| 43 |
-
if (Number(lengthSlider.value) > LENGTHS.length - 1)
|
|
|
|
| 44 |
lengthValue.textContent = selectedLength().label;
|
| 45 |
if (LENGTHS.length <= 1) lengthRow.style.display = "none"; // single option β no slider
|
| 46 |
})
|
|
@@ -105,7 +109,7 @@ export function initUI({
|
|
| 105 |
// audio autoplay until the user interacts, so it's primed on the first gesture;
|
| 106 |
// after that it just follows the player state via syncPlayState() -> updateLobby().
|
| 107 |
// Track: Alex Morgan β "Peaceful Cafe Jazz" (Pixabay, royalty-free).
|
| 108 |
-
const LOBBY_VOL = 0.
|
| 109 |
const lobby = new Audio("/static/lobby.mp3");
|
| 110 |
lobby.loop = true;
|
| 111 |
lobby.preload = "auto";
|
|
@@ -120,7 +124,10 @@ export function initUI({
|
|
| 120 |
let i = 0;
|
| 121 |
lobbyFade = setInterval(() => {
|
| 122 |
i += 1;
|
| 123 |
-
lobby.volume = Math.max(
|
|
|
|
|
|
|
|
|
|
| 124 |
if (i >= steps) {
|
| 125 |
clearInterval(lobbyFade);
|
| 126 |
lobbyFade = null;
|
|
@@ -192,7 +199,8 @@ export function initUI({
|
|
| 192 |
const p = await getProgress();
|
| 193 |
if (!p || !p.total) return;
|
| 194 |
if (!sawFresh) {
|
| 195 |
-
if (p.done < p.total)
|
|
|
|
| 196 |
else return; // a stale "complete" from the last tape β ignore it
|
| 197 |
}
|
| 198 |
setBrewProgress(p.done / p.total);
|
|
@@ -226,7 +234,10 @@ export function initUI({
|
|
| 226 |
try {
|
| 227 |
// hold the brewing moment even if the backend is fast
|
| 228 |
const seconds = selectedLength().seconds;
|
| 229 |
-
const [result] = await Promise.all([
|
|
|
|
|
|
|
|
|
|
| 230 |
sessionTapes.unshift(result); // newest first, kept only for this session
|
| 231 |
setBrewProgress(1); // the tape's here β top the bar off
|
| 232 |
loadCassette(result);
|
|
@@ -444,7 +455,7 @@ export function initUI({
|
|
| 444 |
});
|
| 445 |
// open centered on the tape already playing, otherwise the newest one
|
| 446 |
const playingIdx = songs.findIndex(
|
| 447 |
-
(s) => tapeKey(s.url) === tapeKey(currentSong?.url)
|
| 448 |
);
|
| 449 |
centerIndex = playingIdx >= 0 ? playingIdx : 0;
|
| 450 |
layoutCarousel();
|
|
@@ -547,16 +558,23 @@ export function initUI({
|
|
| 547 |
const seek = (bar) => (e) => {
|
| 548 |
if (!active.duration) return;
|
| 549 |
const rect = bar.getBoundingClientRect();
|
| 550 |
-
active.currentTime =
|
|
|
|
| 551 |
};
|
| 552 |
progress.addEventListener("click", seek(progress));
|
| 553 |
deckProgress.addEventListener("click", seek(deckProgress));
|
| 554 |
|
| 555 |
-
$("collection-close").addEventListener("click", () =>
|
|
|
|
|
|
|
| 556 |
$("gameboy-close").addEventListener("click", () => onRequestCloseGameboy());
|
| 557 |
playWaitBtn.addEventListener("click", () => onPlayWhileWaiting());
|
| 558 |
-
caroPrev.addEventListener("click", () =>
|
| 559 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 560 |
|
| 561 |
// --- modal controls ---------------------------------------------------------
|
| 562 |
|
|
|
|
| 28 |
// 1.5 min are stitched from 30s chunks. The set is hardware-dependent β a
|
| 29 |
// CPU-only backend allows only 30s β so we fetch the real list from /api/config
|
| 30 |
// and collapse the slider when there's a single option.
|
| 31 |
+
const fmtLen = (s) =>
|
| 32 |
+
`${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`;
|
| 33 |
let LENGTHS = [30, 60, 90].map((s) => ({ seconds: s, label: fmtLen(s) }));
|
| 34 |
+
const selectedLength = () =>
|
| 35 |
+
LENGTHS[Number(lengthSlider.value)] ?? LENGTHS[0];
|
| 36 |
// adapt the slider to what this backend actually allows; defensive β any failure
|
| 37 |
// keeps the 30/60/90 default, so the GPU path is never affected
|
| 38 |
fetch("/api/config")
|
| 39 |
.then((r) => (r.ok ? r.json() : null))
|
| 40 |
.then((cfg) => {
|
| 41 |
+
const allowed =
|
| 42 |
+
cfg && Array.isArray(cfg.allowed_seconds) ? cfg.allowed_seconds : null;
|
| 43 |
if (!allowed || !allowed.length) return;
|
| 44 |
LENGTHS = allowed.map((s) => ({ seconds: s, label: fmtLen(s) }));
|
| 45 |
lengthSlider.max = String(Math.max(0, LENGTHS.length - 1));
|
| 46 |
+
if (Number(lengthSlider.value) > LENGTHS.length - 1)
|
| 47 |
+
lengthSlider.value = "0";
|
| 48 |
lengthValue.textContent = selectedLength().label;
|
| 49 |
if (LENGTHS.length <= 1) lengthRow.style.display = "none"; // single option β no slider
|
| 50 |
})
|
|
|
|
| 109 |
// audio autoplay until the user interacts, so it's primed on the first gesture;
|
| 110 |
// after that it just follows the player state via syncPlayState() -> updateLobby().
|
| 111 |
// Track: Alex Morgan β "Peaceful Cafe Jazz" (Pixabay, royalty-free).
|
| 112 |
+
const LOBBY_VOL = 0.2; // 70% of the original 0.4
|
| 113 |
const lobby = new Audio("/static/lobby.mp3");
|
| 114 |
lobby.loop = true;
|
| 115 |
lobby.preload = "auto";
|
|
|
|
| 124 |
let i = 0;
|
| 125 |
lobbyFade = setInterval(() => {
|
| 126 |
i += 1;
|
| 127 |
+
lobby.volume = Math.max(
|
| 128 |
+
0,
|
| 129 |
+
Math.min(1, from + (target - from) * (i / steps)),
|
| 130 |
+
);
|
| 131 |
if (i >= steps) {
|
| 132 |
clearInterval(lobbyFade);
|
| 133 |
lobbyFade = null;
|
|
|
|
| 199 |
const p = await getProgress();
|
| 200 |
if (!p || !p.total) return;
|
| 201 |
if (!sawFresh) {
|
| 202 |
+
if (p.done < p.total)
|
| 203 |
+
sawFresh = true; // this brew has actually started
|
| 204 |
else return; // a stale "complete" from the last tape β ignore it
|
| 205 |
}
|
| 206 |
setBrewProgress(p.done / p.total);
|
|
|
|
| 234 |
try {
|
| 235 |
// hold the brewing moment even if the backend is fast
|
| 236 |
const seconds = selectedLength().seconds;
|
| 237 |
+
const [result] = await Promise.all([
|
| 238 |
+
generate(prompt, seconds),
|
| 239 |
+
delay(2600),
|
| 240 |
+
]);
|
| 241 |
sessionTapes.unshift(result); // newest first, kept only for this session
|
| 242 |
setBrewProgress(1); // the tape's here β top the bar off
|
| 243 |
loadCassette(result);
|
|
|
|
| 455 |
});
|
| 456 |
// open centered on the tape already playing, otherwise the newest one
|
| 457 |
const playingIdx = songs.findIndex(
|
| 458 |
+
(s) => tapeKey(s.url) === tapeKey(currentSong?.url),
|
| 459 |
);
|
| 460 |
centerIndex = playingIdx >= 0 ? playingIdx : 0;
|
| 461 |
layoutCarousel();
|
|
|
|
| 558 |
const seek = (bar) => (e) => {
|
| 559 |
if (!active.duration) return;
|
| 560 |
const rect = bar.getBoundingClientRect();
|
| 561 |
+
active.currentTime =
|
| 562 |
+
((e.clientX - rect.left) / rect.width) * active.duration;
|
| 563 |
};
|
| 564 |
progress.addEventListener("click", seek(progress));
|
| 565 |
deckProgress.addEventListener("click", seek(deckProgress));
|
| 566 |
|
| 567 |
+
$("collection-close").addEventListener("click", () =>
|
| 568 |
+
onRequestCloseCollection(),
|
| 569 |
+
);
|
| 570 |
$("gameboy-close").addEventListener("click", () => onRequestCloseGameboy());
|
| 571 |
playWaitBtn.addEventListener("click", () => onPlayWhileWaiting());
|
| 572 |
+
caroPrev.addEventListener("click", () =>
|
| 573 |
+
centerOn(centerIndex - 1, { load: true }),
|
| 574 |
+
);
|
| 575 |
+
caroNext.addEventListener("click", () =>
|
| 576 |
+
centerOn(centerIndex + 1, { load: true }),
|
| 577 |
+
);
|
| 578 |
|
| 579 |
// --- modal controls ---------------------------------------------------------
|
| 580 |
|