Skip to content

Commit e078283

Browse files
Sbussisoclaude
andcommitted
ui(player): drop native controls, branded camera-pulse loader
User feedback (with three reproduction screenshots): the live-feed tiles look "ugly" during the connect window — refreshing the dashboard shows a generic browser spinner stacked over the default HTML5 video control bar (play/pause, scrubber, 0:00/0:00, mute, fullscreen icons), which then keeps showing during steady-state playback too. Two-line root cause: 1. <video controls /> renders the browser's default control bar. None of those controls do anything useful here — every player is `muted`, live streams aren't seekable, autoplay starts on MANIFEST_PARSED so play/pause is moot, and the dedicated Fullscreen button beneath the video already replaces the native fullscreen icon. Pure visual noise. 2. .loading-spinner is the generic ring used everywhere (auth pages, empty states, settings). It's correct but unbranded — it says "something is loading" but not "this product is loading". Changes: - HlsPlayer.jsx: drop the `controls` attribute. Video element is now chromeless. The custom Fullscreen button below the video stays in place; it was already wired to video.requestFullscreen(). - HlsPlayer.jsx: replace the generic .loading-spinner div with an inline SVG camera icon scoped to .hls-player-loading. Kept the "Connecting to stream..." text underneath. SVG is the standard feather-icons-style camera (rect + chevron); no external dep. - index.css: add .hls-player-loading .camera-pulse (56×56, accent- green, soft glow) plus a 1.6s opacity+scale pulse keyframe. The rule is scoped under .hls-player-loading so the global .loading-spinner stays untouched for every other use site. - prefers-reduced-motion media query honors the OS setting — pulse drops to a steady mid-opacity icon so vestibular-sensitive users aren't forced to watch it breathe. Side effects worth knowing: - Right-click "Save video as" on the live feed is gone (the menu was a side effect of the native controls). Considered a security nicety, not a regression. - On iOS Safari, native controls used to provide a tap-to-unmute shortcut. We're already `muted` + `playsInline` so autoplay works without it; the user can still long-press to access browser actions if needed. Risk: zero functional. Steady-state playback path unchanged. Connect window is the only visual surface that moved, which is the surface the user explicitly flagged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a457ac1 commit e078283

2 files changed

Lines changed: 71 additions & 4 deletions

File tree

frontend/src/components/HlsPlayer.jsx

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,15 +252,46 @@ function HlsPlayer({ cameraId, cameraName }) {
252252
<div className="hls-player-video-wrapper">
253253
{loading && (
254254
<div className="hls-player-loading">
255-
<div className="loading-spinner"></div>
255+
{/*
256+
Custom loading state — branded camera-pulse SVG instead
257+
of the generic .loading-spinner (which is shared with
258+
auth pages and empty states). Keeps the connecting UI
259+
on-brand and matches the SourceBox green accent.
260+
*/}
261+
<svg
262+
className="camera-pulse"
263+
viewBox="0 0 24 24"
264+
fill="none"
265+
stroke="currentColor"
266+
strokeWidth="1.75"
267+
strokeLinecap="round"
268+
strokeLinejoin="round"
269+
aria-hidden="true"
270+
>
271+
<path d="M23 7l-7 5 7 5V7z" />
272+
<rect x="1" y="5" width="15" height="14" rx="2" ry="2" />
273+
</svg>
256274
<p>Connecting to stream...</p>
257275
</div>
258276
)}
259-
277+
278+
{/*
279+
No `controls` attribute on purpose. The native HTML5 control
280+
bar (play/pause, scrubber, time, mute, fullscreen) renders on
281+
top of the video and is visible during the connect window AND
282+
during normal playback — pure visual noise for our use case:
283+
- every player is `muted` (line below) so volume is moot
284+
- live streams aren't seekable
285+
- autoplay starts on MANIFEST_PARSED so play/pause is moot
286+
- the dedicated Fullscreen button below replaces the native
287+
fullscreen icon
288+
Removing `controls` cleans up both the loading state and the
289+
steady-state playback view. Right-click "Save video as" goes
290+
away too, which is a nice security adjacent.
291+
*/}
260292
<video
261293
ref={videoRef}
262294
className="hls-player-video"
263-
controls
264295
playsInline
265296
muted
266297
/>

frontend/src/index.css

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5678,12 +5678,48 @@ body {
56785678
align-items: center;
56795679
justify-content: center;
56805680
background: rgba(0, 0, 0, 0.8);
5681-
color: var(--text-primary);
5681+
color: var(--accent-green);
56825682
}
56835683

56845684
.hls-player-loading p {
56855685
margin-top: 1rem;
56865686
color: var(--text-muted);
5687+
font-size: 0.875rem;
5688+
letter-spacing: 0.02em;
5689+
}
5690+
5691+
/*
5692+
Branded loading icon for the HLS player. Lightweight SVG camera —
5693+
no bitmap, scales cleanly at any size — pulsing in the SourceBox
5694+
green accent so the connecting state reads as "we're trying"
5695+
rather than "something is broken". Keep this scoped under
5696+
.hls-player-loading so the global .loading-spinner used elsewhere
5697+
(auth pages, empty states, settings) is unaffected.
5698+
*/
5699+
.hls-player-loading .camera-pulse {
5700+
width: 56px;
5701+
height: 56px;
5702+
color: var(--accent-green);
5703+
animation: hls-camera-pulse 1.6s ease-in-out infinite;
5704+
filter: drop-shadow(0 0 8px rgba(34, 197, 94, 0.35));
5705+
}
5706+
5707+
@keyframes hls-camera-pulse {
5708+
0%, 100% { opacity: 0.35; transform: scale(0.96); }
5709+
50% { opacity: 1; transform: scale(1.04); }
5710+
}
5711+
5712+
@media (prefers-reduced-motion: reduce) {
5713+
/*
5714+
Honor the OS-level reduced-motion preference. The pulse is
5715+
decorative — drop the animation and just hold a steady mid-
5716+
opacity icon so users with vestibular sensitivity aren't
5717+
forced to watch it breathe.
5718+
*/
5719+
.hls-player-loading .camera-pulse {
5720+
animation: none;
5721+
opacity: 0.7;
5722+
}
56875723
}
56885724

56895725
.hls-player-error {

0 commit comments

Comments
 (0)