feat(sim): audio on/off toggle -> 0-10 level dial; first video lifts it to 3/10

Operator request. Replaces the Audio checkbox with a 0-10 range slider that sets the
master soundtrack gain (audioVol = level/10), scaling both the per-scale rest volume
and the two-track scrub crossfade. Level 0 = silent. Turning Video on for the first
time now raises audio to 3/10 (was: full on) so the experience opens gently; raising
the slider from 0 unlocks playback in-gesture (Safari). E2E: dial is range 0-10
default 0, and first video-on lifts it to 3/10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BenStullsBets
2026-06-27 17:02:19 -07:00
parent ea530b3df6
commit e862fb498b
4 changed files with 67 additions and 29 deletions
+25 -4
View File
@@ -29,13 +29,13 @@ async function wheelOnStage(page: Page, deltaY: number) {
await page.locator("#stage").dispatchEvent("wheel", { deltaY });
}
// The Audio toggle is a visually-hidden checkbox styled as a switch; set it + fire
// `change` directly (headless Chromium relaxes the autoplay gesture requirement).
// The Audio control is a 010 level slider; set it to full + fire `input` directly
// (headless Chromium relaxes the autoplay gesture requirement).
async function enableAudio(page: Page) {
await page.evaluate(() => {
const c = document.querySelector("#audio") as HTMLInputElement;
c.checked = true;
c.dispatchEvent(new Event("change", { bubbles: true }));
c.value = "10";
c.dispatchEvent(new Event("input", { bubbles: true }));
});
}
@@ -55,6 +55,27 @@ test("two audio elements exist for crossfade", async ({ page }) => {
expect(n).toBe(2);
});
test("audio is a 0-10 level dial, defaulting to 0", async ({ page }) => {
await boot(page);
const ctl = await page.evaluate(() => {
const c = document.querySelector("#audio") as HTMLInputElement;
return { type: c.type, min: c.min, max: c.max, value: c.value };
});
expect(ctl.type).toBe("range");
expect(ctl.min).toBe("0");
expect(ctl.max).toBe("10");
expect(ctl.value).toBe("0"); // silent until raised
});
test("turning video on for the first time lifts audio to 3/10", async ({ page }) => {
await boot(page);
expect(await page.evaluate(() => (document.querySelector("#audio") as HTMLInputElement).value)).toBe("0");
await enableVideo(page); // first video-on couples audio at a gentle level
await expect.poll(() => page.evaluate(() => (document.querySelector("#audio") as HTMLInputElement).value)).toBe("3");
// The level label reflects it.
expect(await page.evaluate(() => document.querySelector("#audio-level-val")?.textContent)).toBe("3");
});
test("dragging the dial scrubs morph currentTime and audio gains", async ({ page }) => {
await boot(page);
await enableAudio(page); // hidden custom toggle → set + dispatch change