c2b145435801377cf6ec414b156bc399eb610a7c
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c2b1454358 |
fix(thumbs): size thumbnails for the tile's width at 2x, not 512 on the long side
The operator on sindra-nude-final: "the images look blurry until they're selected and blown up." The cap was 512px on the LONGEST side, which the comment called "comfortably above any tile size", and it was, for a square. A gallery tile is sized by its WIDTH, though, and a 704x1408 portrait got 256px of width for a tile Chromium renders at 361 CSS px. That is 1.4x stretched at 1x density and 2.8x on a 2x screen. The review stage serves the original, which is why it looked sharp once opened. - THUMB_WIDTH = 768: the widest desktop tile (3 columns, 1440px and up, measured at 321-361 CSS px across viewports) doubled for a 2x screen. THUMB_HEIGHT_MAX = 4096 stops a long screenshot going through at full height. - An original that fits the bounds is served as-is only when it is also light (<= 64 KB; 768-wide thumbnails average 39 KB over the 381 live images) or animated, since a thumbnail is one frame. Fitting a tile in pixels is not being cheap in bytes: these portraits are ~1.1 MB PNGs. - The size rule is in the cache name (`<rel>.768w.webp`). The live 512-cap thumbnails are newer than their sources, so the mtime check alone would have served them forever. The old files are orphans, swept with their booth. - tests/test_thumbs_browser.py holds THUMB_WIDTH against the rendered grid at 1440, 1920 and 2560. The constant is a layout number, and a redesign that widens the tiles turns it red instead of soft. Measured cost, all 381 live images: 4.8 MB -> 14.2 MB of thumbnails, still ~27x under the 386 MB of originals. Known limit: the 2-column (<=472px) and 1-column (<=650px) reflows are softer than 768 covers at 2x. tests/mutations/thumbs.toml proves 7 falsifiers. |
||
|
|
18d599dd2a |
fix(thumbs): the cache aged the booth it cached, and two more surfaces
Two corrections to the thumbnail work, the first of them a live bug shipped an
hour ago and caught by design-dev before its worst form landed.
⚠ GENERATING A THUMBNAIL RESET THE BOOTH'S EXPIRY CLOCK. `_newest_mtime`
excludes `.lock` sidecars because machinery is not the operator doing something;
the thumbnail cache is machinery too, and it is written by the SERVER on a mere
view. Excluding the cache's CONTENTS turned out not to be enough — creating
`.thumbs/` touches the BOOTH DIRECTORY's own mtime, which is exactly what
_newest_mtime seeds from. The booth's stamp is now restored across the mkdir,
which cannot hide real activity because any file an agent adds is counted by its
own mtime in the same walk.
The failure this prevents is not small. Once the Desk's preview strip pulls a
thumbnail per booth, ONE INDEX LOAD would have pushed every booth's expiry out
and the TTL would never have fired again — nothing would ever sweep. It was
already live for the gallery, one booth at a time.
TWO MORE SURFACES, because the fix only helped where it was wired:
Desk preview strip four small images per booth on the page he opens FIRST.
design-dev measured 28 originals / 24.1 MB on a 12-booth
copy; live has 28. The heaviest surface in the service,
heavier than the gallery it previews.
flag tray _marks.html rendered originals as tray thumbnails.
The review stage stays on the original, because that is the full-size review.
754 green plus the new guards.
|
||
|
|
d5e23c7d5f |
perf(thumbs): the gallery shipped 77 MB to render 250px tiles
The operator found this in about a minute of using the live Desk: "images load
at full resolution instead of calculated thumbnails, which means they load VERY
slowly and are tiny."
MEASURED on the live set:
sindra-corpus-v1 66 images 77.5 MB 1024x1024 each
sindra-sfw-pool 59 images 71.7 MB
sindra 30 images 61.6 MB 2.1 MB average
sindra-bakeoff 40 images 57.2 MB
A tile renders around 250px, so the grid shipped roughly 16x the pixels that
reach the screen.
⚠ OUR PARKING RATIONALE WAS WRONG IN AN INSTRUCTIVE WAY. ROADMAP parked
progressive loading on "the largest gallery is 66 images; at that size a lazy
grid is almost certainly fine", and the parking-lot row said "270 <img
loading=lazy> may be fine". Both count IMAGES. Neither weighs BYTES. We measured
the dimension that was easy to measure rather than the one that determines the
experience, and 66 really is a fine count sitting on a terrible payload.
booth/thumbs.py caches WebP at 512px longest side inside the booth at
`.thumbs/<rel>.webp` — inside on purpose, so a cache can never outlive what it
describes. Pillow is an optional import: absent, every tile falls back to the
original, so the page is heavier and never broken. Generation is lazy, atomic
(temp + os.replace), rebuilt when the source is newer, and NEVER RAISES.
?thumb=1 rides the EXISTING file route rather than growing a new one, because
that route's traversal guard is already correct and a second route is a second
place to get it wrong.
ALSO FIXES A PRE-EXISTING LEAK THE CACHE WOULD HAVE WALKED INTO. booth_items and
zip_booth both tested `p.name.startswith(".")` — the FILE's name — so
`.thumbs/a.png` (name `a.png`) would have rendered as a gallery item and shipped
inside every zip. CLAUDE.md invariant 2 promises a dotfile costs nothing in item
counts, galleries or zips; that was true only at the top level. Both now skip
every dot-prefixed path COMPONENT.
AND THE FILMSTRIP, which is the same defect in a worse place: it shows EVERY
ring item at a few dozen pixels, so full-resolution frames there cost more than
the grid did. The stage is untouched and stays full size, because that is the
full-size review.
Item.thumb is derived in the resolver, not by a template reasoning about `kind`
(INV-1). build_gallery had to carry it too — a missing key there rendered as a
SILENT fallback to the full image, which is exactly where a new Item field gets
dropped with nothing failing.
754 green.
|