A social internet radio platform built on AT Protocol. atradio.fm
atproto radio
7

Configure Feed

Select the types of activity you want to include in your feed.

fix(player): unblock shortcuts FAB, add P shortcut, more translucent bar

- the full-width player container swallowed clicks in its transparent
margins, making the shortcuts help FAB unclickable — make the container
pointer-events-none and only the card interactive, and lift the FAB
above the bar while a station plays
- add a `P` keyboard shortcut to toggle the fullscreen player (Esc closes
it); listed in the shortcuts help. Fullscreen state now lives in an atom
- make the miniplayer more translucent (bg-synth-surface/40)

Tsiry Sandratraina (Jul 16, 2026, 8:43 AM +0300) 17b8c1d9 11c2ecb6

+31 -9
+3
apps/web/src/atoms/ui.ts
··· 15 15 /** Controls the advanced audio settings (EQ / DSP) modal. */ 16 16 export const audioSettingsOpenAtom = atom(false); 17 17 18 + /** Controls the fullscreen ("now playing") player view. */ 19 + export const playerFullscreenAtom = atom(false); 20 + 18 21 /** Seed query for the palette (e.g. when opened from a category tile). */ 19 22 export const searchPaletteQueryAtom = atom(""); 20 23
+6 -2
apps/web/src/components/Layout.tsx
··· 1 1 import { Outlet } from "@tanstack/react-router"; 2 - import { useSetAtom } from "jotai"; 2 + import { useAtomValue, useSetAtom } from "jotai"; 3 3 import { IconKeyboard } from "@tabler/icons-react"; 4 + import { currentStationAtom } from "@/atoms/player"; 4 5 import { Navbar } from "./Navbar"; 5 6 import { Player } from "./Player"; 6 7 import { AddStationModal } from "./AddStationModal"; ··· 17 18 export function Layout() { 18 19 useKeyboardShortcuts(); 19 20 const openShortcuts = useSetAtom(shortcutsOpenAtom); 21 + const station = useAtomValue(currentStationAtom); 20 22 21 23 return ( 22 24 <div className="flex min-h-screen flex-col"> ··· 30 32 aria-label="Show keyboard shortcuts" 31 33 title="Keyboard shortcuts (?)" 32 34 onClick={() => openShortcuts(true)} 33 - className="fixed bottom-24 right-4 z-30 flex h-9 w-9 items-center justify-center rounded-full border border-white/10 bg-synth-panel/80 text-foreground/60 backdrop-blur transition-colors hover:border-synth-cyan/60 hover:text-synth-cyan sm:bottom-6 sm:right-6" 35 + className={`fixed right-4 z-30 flex h-9 w-9 items-center justify-center rounded-full border border-white/10 bg-synth-panel/80 text-foreground/60 backdrop-blur transition-all hover:border-synth-cyan/60 hover:text-synth-cyan sm:right-6 ${ 36 + station ? "bottom-28 sm:bottom-28" : "bottom-6 sm:bottom-6" 37 + }`} 34 38 > 35 39 <IconKeyboard size={18} /> 36 40 </button>
+7 -6
apps/web/src/components/Player.tsx
··· 1 - import { useEffect, useRef, useState } from "react"; 1 + import { useEffect, useRef } from "react"; 2 2 import { useAtom, useAtomValue, useSetAtom } from "jotai"; 3 3 import { Button, Slider } from "@heroui/react"; 4 4 import { ··· 30 30 type StreamInfo, 31 31 } from "@/atoms/player"; 32 32 import { favoriteIdsAtom, toggleFavoriteAtom } from "@/atoms/favorites"; 33 - import { audioSettingsOpenAtom } from "@/atoms/ui"; 33 + import { audioSettingsOpenAtom, playerFullscreenAtom } from "@/atoms/ui"; 34 34 import { 35 35 applyAudioSettings, 36 36 useAudioSettingsSnapshot, ··· 86 86 const audioSettings = useAudioSettingsSnapshot(); 87 87 const listeners = useListenerCount(station?.id); 88 88 89 - /** Fullscreen ("now playing") view toggle. */ 90 - const [expanded, setExpanded] = useState(false); 89 + /** Fullscreen ("now playing") view toggle — shared so the `P` shortcut can 90 + * open it too. */ 91 + const [expanded, setExpanded] = useAtom(playerFullscreenAtom); 91 92 92 93 const audioRef = useRef<HTMLAudioElement>(null); 93 94 const hlsRef = useRef<Hls | null>(null); ··· 564 565 /> 565 566 566 567 <div 567 - className={`fixed inset-x-0 bottom-0 z-50 px-3 pb-3 sm:px-4 sm:pb-4 transition-transform duration-300 ${ 568 + className={`pointer-events-none fixed inset-x-0 bottom-0 z-50 px-3 pb-3 sm:px-4 sm:pb-4 transition-transform duration-300 ${ 568 569 station ? "translate-y-0" : "translate-y-[calc(100%+1.5rem)]" 569 570 }`} 570 571 > 571 - <div className="mx-auto max-w-7xl overflow-hidden rounded-2xl border border-white/10 bg-synth-surface/60 shadow-2xl shadow-black/40 backdrop-blur-2xl"> 572 + <div className="pointer-events-auto mx-auto max-w-7xl overflow-hidden rounded-2xl border border-white/10 bg-synth-surface/40 shadow-2xl shadow-black/40 backdrop-blur-2xl"> 572 573 {status === "loading" && ( 573 574 <div className="h-0.5 w-full overflow-hidden bg-transparent"> 574 575 <div className="h-full w-1/3 animate-pulse-bars bg-gradient-to-r from-synth-pink to-synth-cyan" />
+14 -1
apps/web/src/hooks/useKeyboardShortcuts.ts
··· 14 14 shortcutsOpenAtom, 15 15 openSearchPaletteAtom, 16 16 loginModalOpenAtom, 17 + playerFullscreenAtom, 17 18 } from "@/atoms/ui"; 18 19 19 20 function isTypingTarget(el: EventTarget | null): boolean { ··· 42 43 const openLogin = useSetAtom(loginModalOpenAtom); 43 44 const isLoggedIn = useAtomValue(isLoggedInAtom); 44 45 const [shortcutsOpen, setShortcutsOpen] = useAtom(shortcutsOpenAtom); 46 + const [fullscreen, setFullscreen] = useAtom(playerFullscreenAtom); 45 47 46 48 useEffect(() => { 47 49 const onKeyDown = (e: KeyboardEvent) => { ··· 50 52 51 53 const typing = isTypingTarget(e.target); 52 54 53 - // Escape works everywhere: blur inputs, close the help overlay. 55 + // Escape works everywhere: blur inputs, close overlays. 54 56 if (e.key === "Escape") { 55 57 if (typing && e.target instanceof HTMLElement) e.target.blur(); 56 58 if (shortcutsOpen) setShortcutsOpen(false); 59 + if (fullscreen) setFullscreen(false); 57 60 return; 58 61 } 59 62 ··· 96 99 case "E": 97 100 setAudioSettingsOpen((v) => !v); 98 101 break; 102 + case "p": 103 + case "P": 104 + // Toggle the fullscreen player — only meaningful with a station. 105 + if (currentStation) { 106 + e.preventDefault(); 107 + setFullscreen((v) => !v); 108 + } 109 + break; 99 110 case "ArrowUp": 100 111 e.preventDefault(); 101 112 setVolume((v) => clamp(v + 0.05)); ··· 129 140 isLoggedIn, 130 141 shortcutsOpen, 131 142 setShortcutsOpen, 143 + fullscreen, 144 + setFullscreen, 132 145 ]); 133 146 }
+1
apps/web/src/lib/shortcuts.ts
··· 11 11 { keys: ["F"], description: "Favorite current station" }, 12 12 { keys: ["A"], description: "Add your own station" }, 13 13 { keys: ["E"], description: "Equalizer & audio settings" }, 14 + { keys: ["P"], description: "Fullscreen player" }, 14 15 { keys: ["↑", "↓"], description: "Volume up / down" }, 15 16 { keys: ["?"], description: "Show this help" }, 16 17 { keys: ["Esc"], description: "Close dialogs / blur search" },