Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
18 changes: 14 additions & 4 deletions .claude/skills/blog-images/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
---
name: blog-images
description: Make images for ddev.com blog posts, including terminal screenshots of DDEV output rendered with VHS, hand-written SVG feature images, and logo-and-text feature banners. Use when a post needs a screenshot of `ddev list`, `ddev st`, or other command output, or a new feature image.
description: Make images for ddev.com blog posts, including terminal screenshots of DDEV output rendered with VHS, animated before/after comparisons of two DDEV versions, hand-written SVG feature images, and logo-and-text feature banners. Use when a post needs a screenshot of `ddev list`, `ddev st`, or other command output, a GIF or WebP recording, or a new feature image.
when_to_use: >
Triggered by "screenshot of ddev list", "terminal screenshot", "feature
image", "banner image", "illustration for the post", "Mermaid diagram", or
adding any image under public/img/blog/.
Triggered by "screenshot of ddev list", "terminal screenshot", "gif",
"webp comparison", "before/after recording", "vhs", "feature image",
"banner image", "illustration for the post", "Mermaid diagram", or adding
any image under public/img/blog/.
---

# Images for blog posts
Expand All @@ -19,6 +20,9 @@ Render DDEV command output (`ddev list`, `ddev st`, the `ddev` dashboard) with
[VHS](https://github.com/charmbracelet/vhs) (`brew install vhs`, which also
needs `ttyd` and `ffmpeg`).

- Never show the maintainer's real projects. Record demo projects with
realistic names, registered under `DDEV_XDG_CONFIG_HOME`, as described in
[gif-comparison.md](gif-comparison.md#binaries-and-projects).
- Write a `.tape` file that hides the `cd` and `clear`, shows the command,
sleeps a few seconds, and ends with `Screenshot`. Run it as
`bash -c "cd ~/tmp/shots && timeout 100 vhs name.tape"` with stdin from
Expand All @@ -41,6 +45,12 @@ needs `ttyd` and `ffmpeg`).
characters badly. A hover or click state needs a real terminal such as
iTerm2 and must be captured by hand.

## Animated before/after comparisons

To show a change between two DDEV versions, such as a faster command or a
rebuilt `ddev tui`, record both with VHS and stack them in one animated
WebP. Follow [gif-comparison.md](gif-comparison.md) in this directory.

## SVG feature images

An illustration can be a hand-written SVG referenced directly from
Expand Down
118 changes: 118 additions & 0 deletions .claude/skills/blog-images/gif-comparison.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Animated before/after comparisons

An animated WebP that plays the same steps in two DDEV versions, the old one
stacked above the new one, each under a label bar with a caption. Examples:
`public/img/blog/2026/07/ddev-start-v1-25-2-vs-v1-25-3.webp` and
`public/img/blog/2026/10/ddev-tui-*-v1-25-4-vs-v1-25-5.webp`, whose tapes and
ffmpeg filter graphs are in `public/img/blog/2026/10/vhs/`. Commit the tapes
and filter graphs of a new comparison the same way, in a `vhs/` directory next
to its images, so it can be re-recorded for the next release.

## Tools

If VHS isn't installed, download the `vhs` and `ttyd` release binaries from
GitHub into `~/tmp/vhs-tools`, check them against the release checksums, and
put that directory on PATH. VHS looks for `ttyd` on the PATH that the tape's
`Env PATH` sets, so that line must include `~/tmp/vhs-tools` too. VHS uses the
installed Chromium or Chrome.

## Binaries and projects

- Old version: `ddev utility download-ddev --tag v1.25.4`, which writes to
`~/tmp/ddev-download-ddev/v1.25.4/`.
- New version, before its release: build upstream `main` in a `~/tmp`
worktree with `make VERSION=v1.25.5`, so the header shows the release
version instead of `v1.25.4-127-g...`.
- Never record the maintainer's real projects. Create demo projects with
realistic names (`company-blog`, `docs-portal`, `storefront`) in
`~/tmp/sites/`, registered in an isolated global config:
`export DDEV_XDG_CONFIG_HOME=~/tmp/tui-demo-xdg`. Use about 15 projects with
10 running, an add-on one release behind its latest, and a snapshot, so the
screens have something to show.
- `ddev list` and `ddev tui` also show every running project from Docker,
whatever the global config says. Ask the maintainer to stop their own
projects before recording, and `ddev poweroff` the demo projects
afterwards, or they show up in the maintainer's `ddev list`.
- Put a fake `xdg-open` that only logs its arguments first on the tape's
PATH, so nothing opens a browser.

## Tapes

Record one tape per version with identical steps. Text must stay readable
after the blog scales a 1200px image to its column: `Set FontSize 22` with
`Set Width 1200` gives about 78 columns, and 14 or 18 was too small. Both
`Width` and `Height` must be even, or ffmpeg fails with "moov atom not found".
A height of 692 is 24 rows; when content doesn't fit, show the TUI scrolling
instead of making the terminal taller.

Make several short clips, one per feature, rather than one long recording.
Get to the starting state behind `Hide`, then pause before every action so a
viewer can read the caption first:

```text
Output detail-new.mp4
Set Shell bash
Set FontSize 22
Set Width 1200
Set Height 692
Set Padding 24
Set TypingSpeed 100ms
Env PATH "/home/me/tmp/fakebin:/home/me/tmp/vhs-tools:/path/to/ddev/bin:/usr/local/bin:/usr/bin:/bin"
Env DDEV_XDG_CONFIG_HOME "/home/me/tmp/tui-demo-xdg"
Env DDEV_NO_INSTRUMENTATION "true"
Hide
Type "cd ~/tmp/sites && clear"
Enter
Type "ddev"
Enter
Sleep 8s
Down@200ms 4
Sleep 1s
Show
Sleep 2500ms
Enter
Sleep 6s
Down@600ms 16
Sleep 3s
```

`ddev` with no arguments opens the TUI. When the command itself is part of
the clip, type it in view and `Sleep 1s` before `Enter`. Render both tapes in
parallel, each as `bash -c "timeout 200 vhs x.tape" </dev/null`.

## Composing the WebP

VHS sleeps don't map exactly to video time, so find when each step happens
from the frame changes:

```bash
ffmpeg -v info -i detail-new.mp4 -vf "select='gt(scene,0.0005)',showinfo" -f null - 2>&1 |
grep -o 'pts_time:[0-9.]*'
```

Each half gets a 64px bar: `Before: v1.25.4` in gray (`0xb0b0b0`) and
`After: v1.25.5` in green (`0x45c98a`), both on `0x2a2a2a`. Do not use a
bright bar color. A caption on the right names the next action, such as
`enter opens project details`, and switches to it about two seconds before
the key is pressed. Use the same caption in both halves; if a key does
nothing in the old version, phrase it neutrally (`↓ in project details`).

Write the filter graph to a file; ffmpeg 8 reads it with `-/filter_complex`
(the old `-filter_complex_script` option is gone):

```text
[0:v]tpad=start_mode=clone:start_duration=1:stop_mode=clone:stop_duration=3,trim=duration=23,pad=1200:756:0:64:color=0x171717,drawbox=x=0:y=0:w=1200:h=64:color=0x2a2a2a:t=fill,drawtext=fontfile=/usr/share/fonts/TTF/DejaVuSans-Bold.ttf:text='Before\: v1.25.4':x=24:y=17:fontsize=30:fontcolor=0xb0b0b0,drawtext=fontfile=/usr/share/fonts/TTF/DejaVuSans.ttf:fontsize=26:fontcolor=0xe0e0e0:x=w-tw-24:y=20:text='enter opens project details':enable='between(t,0,6.2)'[a];
[1:v]...the same, with 'After\: v1.25.5' in 0x45c98a...[b];
[a][b]vstack,fps=12[v]
```

```bash
ffmpeg -y -i detail-old.mp4 -i detail-new.mp4 -/filter_complex detail.filter -map "[v]" \
-c:v libwebp_anim -lossless 0 -q:v 80 -compression_level 6 -loop 0 out.webp
```

`tpad` pads both clips to the same length and holds the last frame. Check
frames by rendering the filter graph at a few times (`-ss 4 -frames:v 1
-update 1 f.png`), and view them before copying the WebP to
`public/img/blog/YYYY/MM/`. Keep each file under 2MB; 12 fps and quality 80
gave 0.3-1.1MB for 10-20 second clips.
33 changes: 33 additions & 0 deletions public/img/blog/2026/10/banner-ddev-v1-25-5-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions public/img/blog/2026/10/banner-ddev-v1-25-5.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/blog/2026/10/ddev-launch-qr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions public/img/blog/2026/10/vhs/detail-new.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Output detail-new.mp4
Set Shell bash
Set FontSize 22
Set Width 1200
Set Height 692
Set Padding 24
Set TypingSpeed 100ms
Env PATH "/home/stas/tmp/fakebin:/home/stas/tmp/vhs-tools:/home/stas/tmp/wt-v1255/.gotmp/bin/linux_amd64:/usr/local/bin:/usr/bin:/bin"
Env DDEV_XDG_CONFIG_HOME "/home/stas/tmp/tui-demo-xdg"
Env DDEV_NO_INSTRUMENTATION "true"
Hide
Type "cd ~/tmp/sites && clear"
Enter
Type "ddev"
Enter
Sleep 8s
Down@200ms 4
Sleep 1s
Show
Sleep 2500ms
Enter
Sleep 6s
Down@600ms 16
Sleep 3s
24 changes: 24 additions & 0 deletions public/img/blog/2026/10/vhs/detail-old.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Output detail-old.mp4
Set Shell bash
Set FontSize 22
Set Width 1200
Set Height 692
Set Padding 24
Set TypingSpeed 100ms
Env PATH "/home/stas/tmp/fakebin:/home/stas/tmp/vhs-tools:/home/stas/tmp/ddev-download-ddev/v1.25.4:/usr/local/bin:/usr/bin:/bin"
Env DDEV_XDG_CONFIG_HOME "/home/stas/tmp/tui-demo-xdg"
Env DDEV_NO_INSTRUMENTATION "true"
Hide
Type "cd ~/tmp/sites && clear"
Enter
Type "ddev"
Enter
Sleep 8s
Down@200ms 4
Sleep 1s
Show
Sleep 2500ms
Enter
Sleep 6s
Down@600ms 16
Sleep 3s
3 changes: 3 additions & 0 deletions public/img/blog/2026/10/vhs/detail.filter
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[0:v]tpad=start_mode=clone:start_duration=1:stop_mode=clone:stop_duration=3,trim=duration=23,pad=1200:756:0:64:color=0x171717,drawbox=x=0:y=0:w=1200:h=64:color=0x2a2a2a:t=fill,drawtext=fontfile=/usr/share/fonts/TTF/DejaVuSans-Bold.ttf:text='Before\: v1.25.4':x=24:y=17:fontsize=30:fontcolor=0xb0b0b0,drawtext=fontfile=/usr/share/fonts/TTF/DejaVuSans.ttf:fontsize=26:fontcolor=0xe0e0e0:x=w-tw-24:y=20:text='enter opens project details':enable='between(t,0,6.2)',drawtext=fontfile=/usr/share/fonts/TTF/DejaVuSans.ttf:fontsize=26:fontcolor=0xe0e0e0:x=w-tw-24:y=20:text='↓ in project details':enable='between(t,6.2,99)'[a];
[1:v]tpad=start_mode=clone:start_duration=1:stop_mode=clone:stop_duration=3,trim=duration=23,pad=1200:756:0:64:color=0x171717,drawbox=x=0:y=0:w=1200:h=64:color=0x2a2a2a:t=fill,drawtext=fontfile=/usr/share/fonts/TTF/DejaVuSans-Bold.ttf:text='After\: v1.25.5':x=24:y=17:fontsize=30:fontcolor=0x45c98a,drawtext=fontfile=/usr/share/fonts/TTF/DejaVuSans.ttf:fontsize=26:fontcolor=0xe0e0e0:x=w-tw-24:y=20:text='enter opens project details':enable='between(t,0,6.2)',drawtext=fontfile=/usr/share/fonts/TTF/DejaVuSans.ttf:fontsize=26:fontcolor=0xe0e0e0:x=w-tw-24:y=20:text='↓ in project details':enable='between(t,6.2,99)'[b];
[a][b]vstack,fps=12[v]
22 changes: 22 additions & 0 deletions public/img/blog/2026/10/vhs/help-new.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Output help-new.mp4
Set Shell bash
Set FontSize 22
Set Width 1200
Set Height 692
Set Padding 24
Set TypingSpeed 100ms
Env PATH "/home/stas/tmp/fakebin:/home/stas/tmp/vhs-tools:/home/stas/tmp/wt-v1255/.gotmp/bin/linux_amd64:/usr/local/bin:/usr/bin:/bin"
Env DDEV_XDG_CONFIG_HOME "/home/stas/tmp/tui-demo-xdg"
Env DDEV_NO_INSTRUMENTATION "true"
Hide
Type "cd ~/tmp/sites && clear"
Enter
Type "ddev"
Enter
Sleep 8s
Down@200ms 4
Sleep 1s
Show
Sleep 2500ms
Type "?"
Sleep 6s
22 changes: 22 additions & 0 deletions public/img/blog/2026/10/vhs/help-old.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Output help-old.mp4
Set Shell bash
Set FontSize 22
Set Width 1200
Set Height 692
Set Padding 24
Set TypingSpeed 100ms
Env PATH "/home/stas/tmp/fakebin:/home/stas/tmp/vhs-tools:/home/stas/tmp/ddev-download-ddev/v1.25.4:/usr/local/bin:/usr/bin:/bin"
Env DDEV_XDG_CONFIG_HOME "/home/stas/tmp/tui-demo-xdg"
Env DDEV_NO_INSTRUMENTATION "true"
Hide
Type "cd ~/tmp/sites && clear"
Enter
Type "ddev"
Enter
Sleep 8s
Down@200ms 4
Sleep 1s
Show
Sleep 2500ms
Type "?"
Sleep 6s
3 changes: 3 additions & 0 deletions public/img/blog/2026/10/vhs/help.filter
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[0:v]tpad=start_mode=clone:start_duration=1:stop_mode=clone:stop_duration=3,trim=duration=12,pad=1200:756:0:64:color=0x171717,drawbox=x=0:y=0:w=1200:h=64:color=0x2a2a2a:t=fill,drawtext=fontfile=/usr/share/fonts/TTF/DejaVuSans-Bold.ttf:text='Before\: v1.25.4':x=24:y=17:fontsize=30:fontcolor=0xb0b0b0,drawtext=fontfile=/usr/share/fonts/TTF/DejaVuSans.ttf:fontsize=26:fontcolor=0xe0e0e0:x=w-tw-24:y=20:text='? shows help':enable='between(t,0,99)'[a];
[1:v]tpad=start_mode=clone:start_duration=1:stop_mode=clone:stop_duration=3,trim=duration=12,pad=1200:756:0:64:color=0x171717,drawbox=x=0:y=0:w=1200:h=64:color=0x2a2a2a:t=fill,drawtext=fontfile=/usr/share/fonts/TTF/DejaVuSans-Bold.ttf:text='After\: v1.25.5':x=24:y=17:fontsize=30:fontcolor=0x45c98a,drawtext=fontfile=/usr/share/fonts/TTF/DejaVuSans.ttf:fontsize=26:fontcolor=0xe0e0e0:x=w-tw-24:y=20:text='? shows help':enable='between(t,0,99)'[b];
[a][b]vstack,fps=12[v]
21 changes: 21 additions & 0 deletions public/img/blog/2026/10/vhs/list-new.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Output list-new.mp4
Set Shell bash
Set FontSize 22
Set Width 1200
Set Height 692
Set Padding 24
Set TypingSpeed 100ms
Env PATH "/home/stas/tmp/fakebin:/home/stas/tmp/vhs-tools:/home/stas/tmp/wt-v1255/.gotmp/bin/linux_amd64:/usr/local/bin:/usr/bin:/bin"
Env DDEV_XDG_CONFIG_HOME "/home/stas/tmp/tui-demo-xdg"
Env DDEV_NO_INSTRUMENTATION "true"
Hide
Type "cd ~/tmp/sites && clear"
Enter
Show
Sleep 2s
Type "ddev"
Sleep 1s
Enter
Sleep 5s
Down@800ms 4
Sleep 2500ms
21 changes: 21 additions & 0 deletions public/img/blog/2026/10/vhs/list-old.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Output list-old.mp4
Set Shell bash
Set FontSize 22
Set Width 1200
Set Height 692
Set Padding 24
Set TypingSpeed 100ms
Env PATH "/home/stas/tmp/fakebin:/home/stas/tmp/vhs-tools:/home/stas/tmp/ddev-download-ddev/v1.25.4:/usr/local/bin:/usr/bin:/bin"
Env DDEV_XDG_CONFIG_HOME "/home/stas/tmp/tui-demo-xdg"
Env DDEV_NO_INSTRUMENTATION "true"
Hide
Type "cd ~/tmp/sites && clear"
Enter
Show
Sleep 2s
Type "ddev"
Sleep 1s
Enter
Sleep 5s
Down@800ms 4
Sleep 2500ms
Loading
Loading