Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .blog-config.yml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Blog Image Workflow Configuration
# Copy this file to .blog-config.yml and fill in your API keys

# OpenAI API key for DALL-E image generation
# Get your key from: https://platform.openai.com/api-keys
OPENAI_API_KEY="sk-your-api-key-here"

# TinyPNG API key for image optimization (optional)
# Get your key from: https://tinypng.com/developers
# If not provided, will use ImageMagick for local optimization
TINYPNG_API_KEY=""

# Path to your images repository
# This should point to the local clone of github.com/haacked/images
IMAGES_REPO_PATH="$HOME/dev/haacked/images"

# Base URL for published images
IMAGE_BASE_URL="https://i.haacked.com"

# Optional: Editor command to open new posts
# Examples: "code", "vim", "subl", etc.
# Leave empty to skip auto-opening
EDITOR=""

# DALL-E image generation settings
DALLE_MODEL="dall-e-3"
DALLE_SIZE="1024x1024" # Options: 1024x1024, 1792x1024, 1024x1792
DALLE_QUALITY="standard" # Options: standard, hd
DALLE_STYLE="vivid" # Options: vivid, natural
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ $RECYCLE.BIN/

.sass-cache/
.jekyll-metadata

# Draft images (published separately to images repo)
.draft-images/

# Local blog configuration (contains API keys)
.blog-config.yml
326 changes: 326 additions & 0 deletions docs/IMAGE-WORKFLOW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
# Blog Image Workflow

An automated workflow for creating, generating, and publishing blog post images with AI assistance.

## Overview

This workflow provides three scripts that streamline the process of adding images to blog posts:

1. **`script/new-post`** - Create a new blog post draft with image directory
2. **`script/generate-images`** - Interactively generate or select images using AI
3. **`script/publish-images`** - Optimize and publish images to the images repository

## Setup

### 1. Install Dependencies

The workflow scripts are written in bash and require the following command-line tools:

```bash
# macOS
brew install jq imagemagick

# Linux (Debian/Ubuntu)
sudo apt-get install jq imagemagick

# Linux (Fedora/RHEL)
sudo dnf install jq ImageMagick
```

**Required tools:**

- `jq` - JSON parsing for API responses
- `curl` - HTTP requests (usually pre-installed)
- `git` - Version control (usually pre-installed)

**Optional tools:**

- `imagemagick` (`magick` command) - Image optimization fallback when TinyPNG API is not available

### 2. Configure API Keys

Copy the example configuration:

```bash
cp .blog-config.yml.example .blog-config.yml
```

Edit `.blog-config.yml` and add your API keys:

```bash
OPENAI_API_KEY="sk-your-actual-api-key"
TINYPNG_API_KEY="your-tinypng-key" # Optional
IMAGES_REPO_PATH="$HOME/dev/haacked/images"
IMAGE_BASE_URL="https://i.haacked.com"
```

**Getting API keys:**

- OpenAI: <https://platform.openai.com/api-keys>
- TinyPNG: <https://tinypng.com/developers> (optional, will use ImageMagick as fallback)

## Complete Workflow

### Step 1: Create a New Post

```bash
./script/new-post
```

You'll be prompted for:

- **Title**: The post title (e.g., "My Amazing Post")
- **Slug**: URL-friendly identifier (auto-generated if you press Enter)

This creates:

- `_posts/YYYY/YYYY-MM-DD-slug.md` - Your post file
- `.draft-images/YYYY-MM-DD-slug/` - Directory for draft images

### Step 2: Write Your Draft

Edit your post and add image placeholders:

```markdown
---
title: "My Amazing Post"
excerpt_image: [image1]
---

[image1: A vibrant sunset over mountains with a developer working on a laptop]

Here's the main content…

[image2: Screenshot of the application dashboard]

More content…

[image3: Architecture diagram showing microservices]
```

**Placeholder format:** `[imageN: description]`

- Use sequential numbers: `image1`, `image2`, etc.
- Descriptions are used as default AI prompts
- Add any existing images (screenshots, diagrams) to `.draft-images/YYYY-MM-DD-slug/`

### Step 3: Generate/Select Images Interactively

```bash
./script/generate-images _posts/YYYY/YYYY-MM-DD-slug.md
```

For each image placeholder, you'll be prompted to:

#### Option 1: Use an existing file

- Lists files in `.draft-images/YYYY-MM-DD-slug/`
- Select which file to use

#### Option 2: Generate with AI

- Edit the prompt (or press Enter to use default)
- Choose style: Vivid (dramatic) or Natural (subdued)
- Preview the generated image URL
- Approve or regenerate with modifications

The script:

- Generates/selects images
- Saves to `.draft-images/YYYY-MM-DD-slug/imageN.png`
- Updates your post with local references:

```markdown
![Description](./.draft-images/YYYY-MM-DD-slug/image1.png)
```

### Step 4: Preview Locally

```bash
jekyll serve
```

Visit <http://localhost:4000> to preview your post with local images.

### Step 5: Publish Images

When you're satisfied with all images:

```bash
./script/publish-images _posts/YYYY/YYYY-MM-DD-slug.md
```

This script:

1. **Optimizes** each image (using TinyPNG or ImageMagick)
2. **Copies** to `~/dev/haacked/images/blog/YYYY-MM-DD-slug/`
3. **Commits and pushes** to the images repository
4. **Updates** your post with final URLs:

```markdown
![Description](https://i.haacked.com/blog/YYYY-MM-DD-slug/image1.png)
```

### Step 6: Publish Your Post

```bash
# Preview with live URLs
jekyll serve

# Commit and create PR
git add _posts/YYYY/YYYY-MM-DD-slug.md
git commit -m "Add post about XYZ"
git push
gh pr create
```

## Tips and Tricks

### AI Image Generation

**Prompt tips:**

- Be specific about style, colors, mood
- Mention "minimalist", "photorealistic", "illustration", etc.
- Reference composition: "overhead view", "close-up", "wide angle"
- Example: "Minimalist illustration of a git tree with terminal windows as leaves, autumn colors, digital art style"

**Iterating:**

- If you don't like the result, choose "Modify prompt and regenerate"
- Try different styles (Vivid vs Natural)
- Save iterations by downloading multiple versions to `.draft-images/` first

### Manual Images

You can always add images manually:

1. Save to `.draft-images/YYYY-MM-DD-slug/`
2. When running `generate-images`, choose "Use existing file"
3. Continue with the publish workflow as normal

### Skipping Images

If you want to skip an image placeholder:

- In `generate-images`, choose "Skip this image"
- The placeholder remains in your post unchanged
- You can run the script again later to process it

### Re-running Scripts

All scripts are idempotent:

- `generate-images` - Will ask what to do with existing images
- `publish-images` - Will re-optimize and overwrite if needed

## Troubleshooting

### "Configuration file not found"

Make sure you've created `.blog-config.yml` from the example:

```bash
cp .blog-config.yml.example .blog-config.yml
```

### "Images repository not found"

Check that `images_repo_path` in `.blog-config.yml` points to your local clone of the images repository:

```bash
ls ~/dev/haacked/images # Should exist
```

### TinyPNG API limit exceeded

The free tier has limits. The scripts will automatically fall back to ImageMagick. To use ImageMagick only, leave `tinypng_api_key` empty in your config.
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation inconsistency: The configuration variable name is listed as tinypng_api_key (lowercase with underscores) but the actual variable name used in the scripts is TINYPNG_API_KEY (uppercase). This should be TINYPNG_API_KEY to match the actual implementation.

Suggested change
The free tier has limits. The scripts will automatically fall back to ImageMagick. To use ImageMagick only, leave `tinypng_api_key` empty in your config.
The free tier has limits. The scripts will automatically fall back to ImageMagick. To use ImageMagick only, leave `TINYPNG_API_KEY` empty in your config.

Copilot uses AI. Check for mistakes.

### Git push fails

Make sure you have write access to the images repository and are authenticated:

```bash
cd ~/dev/haacked/images
git remote -v
git push # Test push access
```

## File Structure

```text
haacked.com/
├── .blog-config.yml # Your API keys (gitignored)
├── .blog-config.yml.example # Template
├── .draft-images/ # Draft images (gitignored)
│ └── 2025-11-21-my-post/
│ ├── image1.png
│ ├── image2.png
│ └── screenshot.png
├── _posts/
│ └── 2025/
│ └── 2025-11-21-my-post.md
└── script/
├── new-post # Create new post
├── generate-images # Generate/select images
└── publish-images # Publish to images repo
```

## Advanced Usage

### Batch Processing

Process multiple posts:

```bash
for post in _posts/2025/*.md; do
./script/publish-images "$post"
done
```

### Custom DALL-E Settings

Edit `.blog-config.yml`:

```yaml
dalle:
model: "dall-e-3"
size: "1792x1024" # Wide format
quality: "hd" # Higher quality
style: "natural" # Default style
```

### Auto-open in Editor

Set your preferred editor in `.blog-config.yml`:

```yaml
editor: "code" # VS Code
# editor: "vim"
# editor: "subl"
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The YAML configuration examples show incorrect syntax. Lines 286-291 show YAML syntax like:

dalle:
  model: "dall-e-3"

However, the actual config file uses shell variable syntax (DALLE_MODEL="dall-e-3"). This inconsistency between the documentation and the actual config format in .blog-config.yml.example will confuse users. Either update the documentation to match the shell format, or update the config loader to support actual YAML.

Suggested change
```yaml
dalle:
model: "dall-e-3"
size: "1792x1024" # Wide format
quality: "hd" # Higher quality
style: "natural" # Default style
```
### Auto-open in Editor
Set your preferred editor in `.blog-config.yml`:
```yaml
editor: "code" # VS Code
# editor: "vim"
# editor: "subl"
```sh
DALLE_MODEL="dall-e-3"
DALLE_SIZE="1792x1024" # Wide format
DALLE_QUALITY="hd" # Higher quality
DALLE_STYLE="natural" # Default style

Auto-open in Editor

Set your preferred editor in .blog-config.yml:

EDITOR="code"  # VS Code
# EDITOR="vim"
# EDITOR="subl"

Copilot uses AI. Check for mistakes.
```

The `new-post` script will automatically open the post in your editor.

## Comparison with Old Workflow

| Step | Old Workflow | New Workflow |
| ----------------------- | --------------------------------------------- | ------------------------------- |
| Create post | Manual file creation | `./script/new-post` |
| Generate image | ChatGPT web → download | Interactive AI prompt |
| Optimize | Upload to tinypng.com → download | Automatic |
| Add to images repo | Manual copy to directory → commit → push | Automatic |
| Update post URLs | Manual URL construction and replacement | Automatic |
| **Total manual steps** | **8-10 steps per image** | **2 commands for all images** |
| **Time per image** | **~3-5 minutes** | **~1 minute** |

## Future Enhancements

Potential additions to the workflow:

- Support for other AI image providers (Midjourney, Stable Diffusion)
- Automatic alt text generation for accessibility
- Image format conversion (WEBP, AVIF)
- Bulk image operations
- Integration with screenshot tools
- Automated testing of image links
Loading