What's deferred
Blog-post bodies (after PR #107 + follow-up) store original media bytes as gitsheets attachments under `blog-posts//.`. The API serves them via `GET /api/attachments/:key` — but as the original bytes, no resizing.
Practical consequences:
- A blog index card that wants a 200×200 thumbnail downloads the full 2MB JPEG and lets CSS scale it.
- A featured-image header that wants a 1920px wide hero downloads the same 2MB JPEG regardless of viewport.
- Per-render bytes-on-the-wire are 5-50× larger than they need to be.
We accepted this because:
- The legacy laddr server's `/thumbnail//` endpoint can't be relied on past cutover.
- Storing originals in the data repo is the right durable record.
- Resizing on demand is a runtime concern that can be added without changing the data shape.
Fix shape
Add a thumbnail endpoint:
```
GET /api/attachments/:key?w=200&h=200&fit=cover&format=auto
```
- Reads the original blob from gitsheets.
- Pipes through `sharp` (already a dep — used in avatar uploads).
- Caches the resized output keyed by `(blobHash, w, h, fit, format)` so the same params return from cache after the first hit.
- Cache lives in the pod's memory or a tmpfs volume — invalidated on hot-reload (the blobHash key handles that naturally).
Alternative: precompute thumbnails at import time (write `-200x200.jpg` alongside `.jpg`). Trades repo size for runtime cost. Probably the wrong trade — variants explode quickly.
Until then
Markdown bodies render `
` tags pointing at `/api/attachments/...` and the browser pulls the full original. SPA can add `loading="lazy"` and CSS `max-width` for ergonomics; bandwidth cost is real but not blocking v1.
Related
- Avatar upload (`apps/api/src/routes/people.ts`) already does a sharp-based resize at write time + stores both originals and 128×128 thumbnails. The pattern is established; the read-time variant would unify the two surfaces.
What's deferred
Blog-post bodies (after PR #107 + follow-up) store original media bytes as gitsheets attachments under `blog-posts//.`. The API serves them via `GET /api/attachments/:key` — but as the original bytes, no resizing.
Practical consequences:
We accepted this because:
Fix shape
Add a thumbnail endpoint:
```
GET /api/attachments/:key?w=200&h=200&fit=cover&format=auto
```
Alternative: precompute thumbnails at import time (write `-200x200.jpg` alongside `.jpg`). Trades repo size for runtime cost. Probably the wrong trade — variants explode quickly.
Until then
Markdown bodies render `
` tags pointing at `/api/attachments/...` and the browser pulls the full original. SPA can add `loading="lazy"` and CSS `max-width` for ergonomics; bandwidth cost is real but not blocking v1.
Related