feat(web): fix mobile scaling regressions and ship installable PWA

Mobile fixes (all in the embedded SPA):
- Move the mobile media queries to the bottom of the stylesheet — the
  cinematheque design layer was appended after them, so its base rules
  (header padding, video heights) silently overrode the phone layout by
  source order. A comment now pins the ordering invariant.
- The design layer's uiDrop entry animation (fill-mode:both) permanently
  overrode the sidebar's translateX(-100%), leaving the drawer stuck open
  over the content on phones; the mobile block now disables it.
- Lower the single-column breakpoint 380px -> 350px: it was catching
  360/375px phones and blowing every card up to full width.
- viewport-fit=cover so the env(safe-area-inset-*) paddings actually
  resolve on iPhones; notch-safe padding on header/content/modals.
- 16px inputs on small screens (kills iOS Safari's focus auto-zoom),
  same for the login page's password field.
- Mobile rules for the design-layer components that had none: full-height
  command-palette sheet, clamped stats numerals, coarse-pointer touch
  targets for the custom player (taller scrub track, always-visible
  thumb, no volume slider).
- CSP font-src now allows data: — the embedded base64 woff2 fonts were
  being blocked and silently fell back to system faces.

PWA:
- manifest.webmanifest, minimal service worker, and a new Catacomb arch
  icon set (SVG source + rendered PNGs incl. maskable + apple-touch),
  all include_str!/include_bytes!-embedded like the HTML.
- sw.js only intercepts GET navigations to "/" (network-first, cached
  offline fallback) and the static assets; /api, /ws, /files,
  /music-files, /feed are never touched, and it's served no-store so
  binary upgrades keep propagating.
- Routes + auth_middleware allowlist so the browser can fetch the
  statics pre-login; theme-color meta tracks the active theme's panel.
- tests/api.rs covers the new endpoints incl. the ungated-when-password
  invariant.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Luna 2026-07-08 00:04:17 -07:00
parent 52c561b47b
commit 891b1e0d3c
No known key found for this signature in database
11 changed files with 435 additions and 70 deletions

31
src/web_ui/icon.svg Normal file
View file

@ -0,0 +1,31 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<!-- Catacomb app icon: a crypt archway sheltering a play wedge, in the
UI's default palette (bg #1a1a2e / accent #e94560). Full-bleed
background with all strokes inside the central maskable safe zone,
so the same art serves purpose:any and purpose:maskable.
Regenerate the PNGs after editing:
rsvg-convert -w 512 -h 512 icon.svg -o icon-512.png
rsvg-convert -w 192 -h 192 icon.svg -o icon-192.png
rsvg-convert -w 180 -h 180 icon.svg -o apple-touch-icon.png -->
<defs>
<radialGradient id="bg" cx="30%" cy="12%" r="130%">
<stop offset="0" stop-color="#272747"/>
<stop offset="1" stop-color="#131322"/>
</radialGradient>
<linearGradient id="arch" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#ff5f7d"/>
<stop offset="1" stop-color="#cc2b4d"/>
</linearGradient>
</defs>
<rect width="512" height="512" fill="url(#bg)"/>
<!-- doorway interior: faint accent glow so the opening reads as depth -->
<path d="M192 396 V240 a64 64 0 0 1 128 0 V396 Z" fill="#e94560" opacity="0.16"/>
<!-- arch -->
<path d="M154 396 V240 a102 102 0 0 1 204 0 V396" fill="none"
stroke="url(#arch)" stroke-width="28" stroke-linecap="round"/>
<!-- floor (solid colour: a zero-height bounding box degenerates the
objectBoundingBox gradient and rsvg drops the stroke entirely) -->
<path d="M118 396 H394" stroke="#cc2b4d" stroke-width="28" stroke-linecap="round"/>
<!-- play wedge in the doorway -->
<path d="M233 282 l60 34 -60 34 Z" fill="#ff5f7d"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB