Enhancement: High-resolution image masters + WebP rendition delivery#488
Open
baltinerdist wants to merge 2 commits into
Open
Enhancement: High-resolution image masters + WebP rendition delivery#488baltinerdist wants to merge 2 commits into
baltinerdist wants to merge 2 commits into
Conversation
Design for storing high-quality image masters and deriving size-optimized WebP renditions for player photos and player/park/ kingdom heraldry, replacing the current upload-time downscale that destroys quality. Two-phase rollout; static serving preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01ShyA7SCTysgREoQPBoxs1Q
Replace the upload-time downscale that destroyed image quality with a store-high-res-master / derive-optimized-renditions pipeline for player photos and player/park/kingdom (and shared unit/event) heraldry. Storage: uploads are kept at up to 3000px (downscale-only, never upscale), PNG when transparent else JPEG q92, with a 6MB post-encode reject ceiling that fires before touching disk (a too-large re-upload can't destroy the existing image). Client-side clampImageIfHuge replaces the ~340KB squeeze, passing original bytes through untouched unless the longest edge >3000px. Delivery: each upload also generates thumb (256px) + display (1024px) WebP renditions served as static files; a size-aware resolve_media_ext serves the right one per surface (nav/list/map = thumb, hero = display, lightbox = master) and falls back to the master when a rendition is missing, so un-backfilled images never break. heraldry-rendition-backfill.php derives renditions for existing images idempotently. Infra: nginx client_max_body_size 8M + webp cache header; PHP upload_max_filesize/post_max_size raised to 8M in all Dockerfiles. Also: remove paths now sweep all master+rendition variants (shared Common::unlink_image_set); over-ceiling rejections propagate to the user instead of reporting false success; lightbox enlarges the master not the rendition; native alert() in heraldry flows replaced with the in-page feedback pattern; stale upload-size UI hints corrected. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01ShyA7SCTysgREoQPBoxs1Q
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the upload-time downscale that destroyed image quality with a store-high-res-master / derive-optimized-renditions pipeline for player photos and player/park/kingdom (and shared unit/event) heraldry. Storage is cheap and one-time; delivery bandwidth is the recurring cost — so we stop conflating them.
Design spec is included in the branch:
docs/superpowers/specs/2026-07-07-image-pipeline-masters-renditions-design.md.What changed
Storage — high-res masters. Uploads are kept at up to 3000px (downscale-only, never upscale), PNG when transparent else JPEG q92, with a 6 MB post-encode reject ceiling that fires before touching disk — a too-large re-upload can't destroy the existing image. Client-side
clampImageIfHugereplaces the ~340 KB squeeze, passing original bytes through untouched unless the longest edge exceeds 3000px.Delivery — rendition set. Each upload also generates
thumb(256px) +display(1024px) WebP renditions served as static files. A size-awareresolve_media_extserves the right one per surface (nav/list/map → thumb, hero → display, lightbox → master) and falls back to the master when a rendition is missing, so un-backfilled images never break.heraldry-rendition-backfill.phpderives renditions for existing images idempotently.Transport / infra. nginx
client_max_body_size 8M+webpadded to the static cache regex; PHPupload_max_filesize/post_max_sizeraised to 8M in all three Dockerfiles.Correctness / polish.
Common::unlink_image_set) — previously "removed" images kept being served from leftover renditions.alert()in the heraldry flows replaced with the in-page feedback pattern; stale "340 KB / Max 1 MB" upload hints corrected (banner hints intentionally left — banners are a separate pipeline).Verification
resolve_media_extresolves renditions and falls back to master).php -lclean on all changed PHP/templates;node --checkclean on all changed JS. WebP confirmed available in the PHP 8.1 containers.Known follow-ups
Size => 'master'for print-quality scrolls. Being handled on that branch.heraldry-rendition-backfill.phpat deploy time to generate renditions for existing images.🤖 Generated with Claude Code