l-img-convert-workflow
Image conversion and upload workflow for Takazudo Modular product images. Use when: (1) User has new images to add to the site (product photos, cover images, OGP images), (2) User says 'convert image'...
Image Conversion & Upload Workflow
Process source images into web-optimized formats and upload to Cloudflare R2 CDN.
For full architecture details, read doc/src/content/docs/overview/image-and-cdn/image-workflow.md.
Pipeline Overview
Source image → imgs/ (Dropbox symlink) → pnpm convimgs:upload → static/images/p/{slug}/ + R2
imgs/ is the single source of truth. static/images/p/ is derived output. R2 is the canonical remote store.
Step 1: Prepare Source Image
Copy or move the source image into imgs/ with a proper slug-based filename.
Naming Convention
The filename (without extension) becomes the slug. Choose the suffix based on what outputs are needed:
| Source filename | Slug | Generates |
|---|---|---|
my-product.heic | my-product | WebP (5 sizes) + mercari.png + metadata.json |
my-product__og.heic | my-product__og | WebP + mercari + metadata + ogp.jpg |
my-product__ogonly.png | my-product__ogonly | ogp.jpg ONLY (no WebP, no mercari) |
- No suffix: Regular image. Use for most product photos, article captures
__og: Full processing + OGP. Use when one source file serves as both display image and social sharing image__ogonly: OGP only. Use for dedicated social sharing images (brand OGP, custom cover cards)
Supported formats: jpg, png, webp, heic, gif, avif.
Slug Naming Rules
- Use kebab-case:
oxi-one-mk2,shik-guide-cover - No slug stripping:
foobar__og.heicbecomes slugfoobar__og - GIFs are copied as-is (no WebP conversion, preserves animation)
Step 2: Process and Upload
# Single file
pnpm convimgs:upload my-product.heic
# Multiple files
pnpm convimgs:upload file1.png file2.heic
# All unprocessed files
pnpm convimgs:upload
This runs image conversion then uploads to R2 in one step. The upload uses incremental sync (MD5/ETag comparison) — safe to re-run.
Output per slug in static/images/p/{slug}/
| File | Purpose |
|---|---|
600w.webp … 2000w.webp | Responsive images (5 sizes) |
ogp.jpg | Social sharing (1200x630) — only for __og/__ogonly |
mercari.png | Marketplace listing (1600px) |
metadata.json | Dimensions, blurhash, paths |
Step 3: Rebuild Metadata
pnpm build:metadata
Regenerates metadata-db.json (git-tracked). Required for ResponsiveImage blurhash placeholders and responsive srcsets.
Step 4: Update metadata-db.json on Main
Use /l-metadata-update to commit the updated metadata-db.json to main. This can be invoked from any branch. It:
- Downloads existing images from R2
- Processes new images + uploads
- Rebuilds
metadata-db.json - Creates a
metadata-update-*branch, commits, creates PR, and merges - Returns to the original branch
After merging, pull main into your working branch:
git fetch origin main && git merge origin/main --no-edit
MDX Frontmatter Fields
| Field | Purpose | Notes |
|---|---|---|
imgThumb | Display image (hero, thumbnails, cards) | Preferred field |
heroImgUrl | Legacy alias for imgThumb | imgThumb takes precedence |
imgOgp | OGP override slug | When set, OGP uses this instead of imgThumb |
OGP Resolution Order
imgOgpslug’sogp.jpg(ifimgOgpis set)imgThumb/heroImgUrlslug’sogp.jpg(fallback)- No
og:imagetag (if resolved slug has noogp.jpg)
Typical Patterns
Single image for both display and OGP (most common):
imgThumb: my-product__og
One __og source file provides everything.
Separate display and OGP images:
imgThumb: my-product
imgOgp: my-product-cover__ogonly
Square product photo for display, wide cover image for social sharing.
Guide Series
Guide series in src/data/guide-series.mjs use thumbSlug for both list thumbnails and series page OGP.
Other Commands
| Task | Command |
|---|---|
| Upload only (skip processing) | pnpm r2:upload |
| Download from R2 (fresh clone) | pnpm r2:download |
| Check orphaned R2 images | pnpm r2:check-orphans |
| Delete orphans | pnpm r2:check-orphans:delete |
| Verify R2 sync | pnpm r2:verify-sync |
Safety Rules
- NEVER put files directly into
static/images/p/— always go throughimgs/pipeline - NEVER auto-delete from R2 — cleanup is always manual with confirmation
- R2 orphan check requires
imgs/symlink (Dropbox)