Screen switch: a blubb on every switch, and Del on the kiosk keyboard
ScreenService plays a synthesised two-bubble "blubb" (musicmouse.blubb) after each switch, through aplay on the music's ALSA device at the player's volume: rising for on, falling for off, going by the last word the toggle command prints. Nothing plays with simulated audio or at volume 0. Built from samples rather than shipped as a file, like the front-end's WebAudio blips, so its loudness follows the volume. Del maps to a new "screen" action on every page, which App.tsx sends to the new POST /api/screen/toggle only when the page is served from localhost - the kiosk on the device - so Del on a tablet does not switch off a screen in another room. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,11 @@ const ASSIGN_STATUS_TIMEOUT_MS = 2500;
|
||||
/** Volume when un-muting, matching the mockup. */
|
||||
const UNMUTE_PERCENT = 60;
|
||||
|
||||
/** The kiosk on the device loads the page from localhost (`pi_kiosk_url` in the ansible
|
||||
* repo); anything else - a tablet, a laptop - is looking at it from elsewhere, and Del
|
||||
* there must not switch off a screen in another room. */
|
||||
const ON_THE_DEVICE = ["localhost", "127.0.0.1"].includes(window.location.hostname);
|
||||
|
||||
/** A podcast episode has no next/previous track to skip to - Next/Previous nudge the
|
||||
* position instead, the way scrubbing past an ad break usually works. */
|
||||
const PODCAST_SKIP_SECONDS = 30;
|
||||
@@ -313,6 +318,9 @@ export function App() {
|
||||
case "mute":
|
||||
onMute();
|
||||
break;
|
||||
case "screen":
|
||||
if (ON_THE_DEVICE) void api.toggleScreen();
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -97,6 +97,7 @@ export const api = {
|
||||
seek: (position: number) => post("/seek", { position }),
|
||||
setVolume: (percent: number) => post("/volume", { percent }),
|
||||
nudgeVolume: (deltaPercent: number) => post("/volume", { delta_percent: deltaPercent }),
|
||||
toggleScreen: () => post("/screen/toggle"),
|
||||
|
||||
settings: () => request<Settings>("/settings"),
|
||||
saveSettings: (settings: Settings) =>
|
||||
|
||||
@@ -36,6 +36,7 @@ const SECTIONS: Array<[title: string, rows: Array<[caps: string[], label: string
|
||||
"Sonstiges",
|
||||
[
|
||||
[["A", "0-9"], "Titel auf Fernbedienungs-Taste legen"],
|
||||
[["ENTF"], "Bildschirm aus / an"],
|
||||
[["F1"], "Diese Hilfe"],
|
||||
[["ESC"], "Schließen / Suche löschen"],
|
||||
],
|
||||
|
||||
@@ -484,3 +484,11 @@ describe("selectionAt", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Del", () => {
|
||||
it("toggles the screen, on every page", () => {
|
||||
for (const page of ["music", "room", "typing"] as const) {
|
||||
expect(press("Delete", { ...initialUiState, page })).toEqual([{ type: "screen" }]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -68,7 +68,8 @@ export type Action =
|
||||
| { type: "seek"; delta: number }
|
||||
| { type: "pop"; freq: number }
|
||||
| { type: "assign"; digit: string }
|
||||
| { type: "mute" };
|
||||
| { type: "mute" }
|
||||
| { type: "screen" };
|
||||
|
||||
/** Matches the mockup's `/^[a-zA-Z0-9]$/`, widened to the umlauts a German title needs. */
|
||||
const SEARCHABLE = /^[\p{L}\p{N}]$/u;
|
||||
@@ -253,6 +254,12 @@ export function handleKey(
|
||||
): Action[] {
|
||||
const { key } = event;
|
||||
|
||||
// Switches the device's own monitor on and off, like the IR remote's power key -
|
||||
// from every page, the typing tab included (no lesson drills Del). App.tsx only acts
|
||||
// on it in the kiosk on the device itself; on a tablet it would switch a screen in
|
||||
// another room.
|
||||
if (key === "Delete" && !event.ctrlKey && !event.metaKey) return [{ type: "screen" }];
|
||||
|
||||
// The typing tab is a second app mounted alongside this one (TippenApp.tsx) with its
|
||||
// own complete keyboard handling, including its own Escape/F1 bindings. It must own
|
||||
// every key while active - even Shift+media and Space below, which would otherwise
|
||||
|
||||
Reference in New Issue
Block a user