Files
eranos/src/lib
Chad Curtis 0d3f44935c Add image proxy and low-bandwidth mode
Two new AppConfig fields cover the data-saving story:

- `imageProxy` (default `https://wsrv.nl`) — wsrv.nl/weserv-compatible
  image-resizing proxy. Empty string disables it. `proxyImageUrl(src, width)`
  and `useImageProxy()` rewrite URLs to WebP at quality 75; the `default=`
  param redirects to the origin if the proxy can't fetch upstream. The
  proxy base URL is parsed through `URL` and rejected unless it's
  `https:`, so user input from the settings field can't smuggle non-https
  schemes into <img src>.
- `lowBandwidthMode` (default `false`) — forces autoplay off everywhere
  (VideoPlayer, LiveStreamPlayer), skips `useVideoThumbnail`'s background
  frame-grab, and (when the proxy is also disabled) gates feed images and
  link-preview thumbnails behind a tap-to-load placeholder.

The two settings are independent. A privacy-conscious user can run with
the proxy off without being forced into tap-to-load, and a metered-data
user can keep the proxy on without ever seeing a placeholder. Tap-to-load
only kicks in when both "I'm low-bandwidth" and "the proxy is off"
are true (or the proxy errors in a gated context).

Three new shared pieces:
  - `src/lib/proxyImageUrl.ts` — pure URL rewriter
  - `src/hooks/useImageProxy.ts` — memoized `(src, width) => string`
  - `src/components/MediaPlaceholder.tsx` — tap-to-load pill with
    optional blurhash background; rendered as `<div role="button">`
    so it nests cleanly inside InlineImage's outer lightbox button
  - `src/components/ProxiedImage.tsx` — `<img>` with proxy + onError
    fallback + optional placeholder gating

Wired call sites with per-context widths:

  Inline post images       (InlineImage,        w=600, gated)
  Image gallery tiles      (GridImage,          w=600, gated)
  Lightbox                 (LightboxImage,      w=1200)
  Profile banner           (ProfileBannerImage, w=1200)
  Profile hover banner     (ProfileHoverCard,   w=400)
  Link preview thumb       (LinkPreview,        w=400, suppressed when
                                                low-bandwidth + no proxy)
  Sidebar media tile       (ProfileRightSidebar, w=300)
  Avatars (via shadcn)     (AvatarImage,        w=96 default,
                                                w=128 hover card,
                                                w=256 profile header)
  Custom emojis            (CustomEmojiImg,     w=48)
  Badge thumbnails         (BadgeThumbnail,     w=max(size*2, 128))
  Badge hero + glare mask  (BadgeContent,       w=256;
                            BadgeDetailContent, w=320;
                            EmbeddedNaddr,      w=192)
  Music / podcast art      (AudioKindContent,   w=600;
                            MusicTrackRow,      w=96;
                            MusicDetailContent, w=320 hero / 96 row;
                            PodcastDetailContent, w=320)

Settings UI lives in NetworkSettingsPage with the Low-Bandwidth Mode
toggle at the top (above relay / Blossom plumbing) and the Image Proxy
section between Blossom Servers and Image Uploads. The low-bandwidth
description text adapts to whether the proxy is configured, so the
proxy↔tap-to-load coupling is visible at the toggle.

Video Tier 1:
  - VideoPlayer overrides `autoPlay` to false in low-bandwidth
  - LiveStreamPlayer no longer hardcodes `autoPlay`, reads config
  - useVideoThumbnail skips its background frame-grab entirely

`lowBandwidthMode` is added to EncryptedSettingsSchema so it crosses
devices alongside `autoplayVideos`. `imageProxy` is local-only (privacy
/ network choice, not a UX preference).
2026-05-23 16:29:38 -05:00
..
2026-05-16 07:58:48 -07:00
2026-05-03 09:00:43 -05:00
2026-05-22 23:37:32 -07:00
2026-05-11 14:49:11 +07:00
2026-05-17 23:12:13 -07:00
2026-04-06 13:41:32 -05:00
2026-05-23 14:31:04 -05:00
2026-04-30 11:22:07 -05:00
2026-04-17 16:22:33 +05:45
2026-04-29 22:04:39 +07:00
2026-04-18 16:28:41 +05:45
2026-05-21 19:04:41 -05:00