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:
parent
52c561b47b
commit
891b1e0d3c
11 changed files with 435 additions and 70 deletions
40
tests/api.rs
40
tests/api.rs
|
|
@ -473,3 +473,43 @@ fn perceptual_dedup_groups_reencodes() {
|
|||
assert!(body.contains("bbb"), "group should contain the re-encode: {body}");
|
||||
assert!(!body.contains("ccc"), "unrelated video must not be grouped: {body}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pwa_assets_served_and_ungated() {
|
||||
if !have_curl() { eprintln!("skip: no curl"); return; }
|
||||
let s = Server::start();
|
||||
s.wait_ready();
|
||||
|
||||
// The SPA advertises the manifest + registers the service worker.
|
||||
let (_, idx) = s.get("/");
|
||||
assert!(idx.contains("manifest.webmanifest"), "index links the manifest");
|
||||
assert!(idx.contains("serviceWorker"), "index registers the service worker");
|
||||
|
||||
let (code, body) = s.get("/manifest.webmanifest");
|
||||
assert_eq!(code, 200);
|
||||
assert!(body.contains("\"name\": \"Catacomb\""), "manifest body: {body}");
|
||||
let (code, body) = s.get("/sw.js");
|
||||
assert_eq!(code, 200);
|
||||
assert!(body.contains("catacomb-shell"), "sw body: {body}");
|
||||
assert_eq!(s.get("/icons/icon-192.png").0, 200);
|
||||
assert_eq!(s.get("/icons/icon-512.png").0, 200);
|
||||
assert_eq!(s.get("/apple-touch-icon.png").0, 200);
|
||||
assert_eq!(s.get("/icons/nope.png").0, 404);
|
||||
|
||||
// Setting a password must NOT gate the PWA statics: the browser fetches
|
||||
// the manifest/icons during install without a session, and the login
|
||||
// page itself links them.
|
||||
let (code, _) = s.post("/api/settings", &format!(
|
||||
r#"{{"transcode":false,"scheduler_enabled":false,"scheduler_interval_hours":24,
|
||||
"max_concurrent":3,"use_bundled_ytdlp":false,"use_pot_provider":false,
|
||||
"subtitles_enabled":false,"subtitles_auto":false,"subtitles_embed":false,
|
||||
"subtitle_langs":"","subtitle_format":"","youtube_player_clients":"",
|
||||
"sponsorblock_mode":"mark","convert_mode":"","convert_crf":23,"convert_preset":"",
|
||||
"convert_audio_format":"","convert_keep_original":false,
|
||||
"new_download_password":"hunter2"}}"#));
|
||||
assert_eq!(code, 200);
|
||||
assert_eq!(s.get("/api/library").0, 401, "API must be gated once a password is set");
|
||||
assert_eq!(s.get("/manifest.webmanifest").0, 200, "manifest stays public");
|
||||
assert_eq!(s.get("/sw.js").0, 200, "service worker stays public");
|
||||
assert_eq!(s.get("/icons/icon-512.png").0, 200, "icons stay public");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue