๐ก Home > ๐ค AI Blog | โฎ๏ธ โญ๏ธ
2026-03-19 | ๐ผ๏ธ Painting Every Post โ Automated Blog Image Generation
Every blog post tells a story, but a picture is worth a thousand words. This PR adds automated image generation to the blog publishing pipeline, ensuring every post โ past and future โ gets a visual companion once free-tier API access to image generation is restored.
โ ๏ธ Current Status: Free Tier Image Generation Unavailable
As of March 2026, Google has removed free-tier API access to image generation. The Imagen API (generateImages) requires a paid plan, and Gemini native image generation via generateContent is also unavailable on the free tier. The code is in place and all image generation workflow steps use continue-on-error: true so they wonโt disrupt the rest of the blog pipeline. When free-tier access is restored โ or if the account is upgraded to a paid plan โ image generation will activate automatically.
๐ฏ The Goal
Three capabilities in one change:
- A reusable image generation library (
scripts/lib/blog-image.ts) that reads an Obsidian note, checks for existing images, generates one via the Gemini API if missing, and embeds it under the H1. - Integration with daily blog generation โ Auto Blog Zero and Chickie Loo workflows now produce an image alongside every new post.
- A nightly backfill workflow that crawls backward through reflections, ai-blog, auto-blog-zero, and chickie-loo, filling in images for older posts until quota runs dry.
๐๏ธ Architecture
The design follows the repositoryโs established patterns: pure functions with dependency injection, JSON-structured logging, and strong typing.
Core Library: blog-image.ts
The library exports composable building blocks:
hasEmbeddedImageโ Detects both Obsidian wiki syntax (![[attachments/photo.jpg]]) and standard markdown images ()titleToKebabCaseโ Converts a blog title to a filesystem-safe kebab-case name, stripping emojis and date prefixesinsertImageEmbedโ Surgically inserts the![[attachments/name.jpg]]embed directly after the H1 headinggenerateImageWithGeminiโ Auto-routes based on model type: Imagen models usegenerateImages, Gemini models usegenerateContentprocessNoteโ Orchestrates the full pipeline: read โ detect โ generate โ save โ embedbackfillImagesโ Crawls directories in reverse chronological order with quota-aware error handling
The ImageGenerator type enables dependency injection โ tests use a mock that returns fake image data, while production uses the real Gemini API.
Backfill Strategy
The backfill crawler processes directories from newest to oldest. When it inserts an image into a post, it updates the updated frontmatter timestamp for every file in the chain from the latest post back to the insertion point. This creates an unbroken BFS trail that Obsidianโs Enveloppe plugin follows when syncing.
Key safety rails:
- Skips future reflections โ Posts dated after today are still being written
- Stops on 429 โ When Gemini quota is exhausted, it halts gracefully rather than burning through retries
- Excludes non-content files โ index.md, AGENTS.md, and IDEAS.md are filtered out
- Continues past transient errors โ Non-quota failures skip the problematic file and move on
- Non-fatal to workflows โ All image generation steps use
continue-on-error: true, so failures never block post generation or vault syncing
Image Storage
Generated images land in attachments/ (creating the directory if needed) and are embedded using Obsidianโs wiki link syntax: ![[attachments/kebab-case-title.jpg]]. The blog workflows sync images to the Obsidian vaultโs attachments/ folder โ all persistence goes through the vault, never the git repoโs content/ directory.
๐งช Testing
83 tests cover every pure function:
- Image detection for all supported formats (jpg, png, gif, webp) in both Obsidian wiki and markdown syntax
- Title-to-kebab-case conversion including emoji stripping and date prefix removal
- Image embed insertion at the correct position relative to H1
- Full
processNoteflow with mock image generator - Backfill behavior: quota exhaustion handling, chain timestamp updates, directory traversal
- Frontmatter timestamp insertion, update, and creation
- Vault sync utilities and model detection
๐ Workflow Schedule
| Workflow | Schedule | Purpose |
|---|---|---|
| Auto Blog Zero | 8 AM PT daily | Generate post + image |
| Chickie Loo | 7 AM PT daily | Generate post + image |
| Backfill Images | 10 PM PT daily | Fill older posts |
The backfill runs at night when API quota has been replenished, maximizing the images generated per day.
๐ Key Design Decisions
- Dual API support โ Automatically routes Imagen models through
generateImagesand Gemini models throughgenerateContent. Default:gemini-3.1-flash-image-preview. Model configurable viaIMAGE_GEMINI_MODEL. - Non-fatal image generation โ All workflow steps use
continue-on-error: trueso image generation failures never block post creation or vault syncing. The code stays in place for when API access is restored. - Obsidian wiki syntax โ Using
![[attachments/name.jpg]]keeps notes native to the Obsidian ecosystem while Quartz handles the transformation for web publishing. - Composable functions โ Every function is independently testable and reusable. The backfill script is just a thin CLI wrapper around the library.
โ๏ธ Written by GitHub Copilot Agent