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
121 changes: 121 additions & 0 deletions .github/skills/update-blog-cover-image/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@

---
name: update-blog-cover-image
description: Updates a blog post cover image path and file with SEO-friendly naming, then regenerates metadata and validates it in dev.
---

# Update Blog Cover Image

## When to use this skill

Use this skill when asked to change, replace, rename, or fix the cover image for an existing blog post.

This skill covers the full workflow:

- Renaming the image to an SEO-friendly filename
- Updating the post metadata cover image path
- Regenerating blog metadata JSON
- Restarting dev server when needed
- Verifying the new cover is rendered

## Key files involved

- `src/routes/blog/<post-slug>/metadata.ts`
- `static/images/posts/<post-slug>/...`
- `static/blogMetadata.json` (generated)
- `scripts/generateMetadata.ts`

## Steps

### 1. Identify the post slug and current metadata

Open the target post metadata file:

```bash
cat src/routes/blog/<post-slug>/metadata.ts
```

Confirm current `coverImage` value.

### 2. Place or rename the image in the post images folder

Use a descriptive, lowercase, kebab-case filename.

Good example:

```text
one-core-packet-processing-bottleneck-cover.webp
```

Rename if needed:

```bash
mv "static/images/posts/<post-slug>/<old name>.webp" \
"static/images/posts/<post-slug>/<seo-friendly-name>.webp"
```

### 3. Update `coverImage` in metadata

Edit `src/routes/blog/<post-slug>/metadata.ts` so `coverImage` points to the new path:

```ts
coverImage: '/images/posts/<post-slug>/<seo-friendly-name>.webp';
```

### 4. Regenerate static metadata

The website reads blog metadata from `static/blogMetadata.json` at runtime.
After updating `metadata.ts`, regenerate:

```bash
npx tsx scripts/generateMetadata.ts
```

If this step is skipped, the old cover can keep appearing.

### 5. Restart dev server if already running

The metadata loader caches results in memory, so restart dev server after regeneration.

```bash
pkill -f "vite dev" || true
npm run dev
```

### 6. Validate the result

Check both metadata and rendered output.

```bash
rg -n "<post-slug>|<seo-friendly-name>.webp" static/blogMetadata.json
curl -I http://localhost:5173/images/posts/<post-slug>/<seo-friendly-name>.webp
curl -s http://localhost:5173/blog/<post-slug> | rg "<seo-friendly-name>.webp"
```

Expected:

- New filename appears in `static/blogMetadata.json`
- Image URL returns `200 OK`
- Blog HTML references the new filename

## Scope boundary

This skill is intentionally limited to in-repo implementation tasks for coding agents.

For standalone guidance on prompting external AI image models for blog banners, see:

- `docs/blog-cover-image-prompting.md`

## Troubleshooting

- **Still seeing old image in browser:** force refresh (`Ctrl+Shift+R`) or use an incognito window.
- **Page still references old cover:** regenerate metadata and restart dev server.
- **`Post not found` error:** run `npx tsx scripts/generateMetadata.ts` and restart dev server.

## Checklist

- [ ] Cover image file uses SEO-friendly filename
- [ ] `coverImage` in post metadata updated
- [ ] `static/blogMetadata.json` regenerated
- [ ] Dev server restarted (if previously running)
- [ ] New cover image confirmed in rendered blog page
5 changes: 4 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ pnpm-lock.yaml
yarn.lock

src/routes/blog/**/*.md
static/blogMetadata.json
static/blogMetadata.json
.github/**/*.md
.github/**/*.yml
.github/**/*.yaml
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Common tasks are documented as [Agent Skills](https://agentskills.io/) in `.gith

- [**add-blog-post**](.github/skills/add-blog-post/SKILL.md) — Create or publish a new blog post
- [**add-component**](.github/skills/add-component/SKILL.md) — Add a new Svelte UI component
- [**update-blog-cover-image**](.github/skills/update-blog-cover-image/SKILL.md) — Update a blog post cover image with metadata regeneration and validation
- [**update-contributors**](.github/skills/update-contributors/SKILL.md) — Refresh the contributors list from the Torrust GitHub org
- [**deploy-site**](.github/skills/deploy-site/SKILL.md) — Deploy the site to GitHub Pages
- [**run-checks**](.github/skills/run-checks/SKILL.md) — Run the full quality check suite before committing
Expand Down
34 changes: 34 additions & 0 deletions docs/blog-cover-image-prompting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Blog Cover Image Prompting

This document explains how to prompt external generative AI image models to create cover images for Torrust blog posts.

Use this as independent author documentation, separate from coding-agent skills.

## Required banner constraints

- Size: 1774 x 887
- Keep top text-safe band: at least 200 px with no text
- Keep bottom text-safe band: at least 200 px with no text

These safe bands help the website layout keep title and overlays readable.

## Recommended reusable prompt template

```text
I want you to create a cover image for the following Torrust blog post.
- The size must be 1774 x 887.
- Leave two horizontal bands at the top and bottom of at least 200 px with no text.

Article:
<paste full article here>
```

## After image generation

1. Export as webp when possible.
2. Rename to an SEO-friendly filename in kebab-case.
3. Place under "static/images/posts/post-slug/".
4. Update coverImage in "src/routes/blog/post-slug/metadata.ts".
5. Regenerate metadata with: npx tsx scripts/generateMetadata.ts.
6. Restart dev server if already running.
7. Validate that the new image appears in the rendered blog page.
4 changes: 4 additions & 0 deletions project-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ certonly
cgbosse
Chatrooms
Chihaya
conntrack
containerfile
Containerfiles
corepack
Expand Down Expand Up @@ -51,6 +52,7 @@ heaptrack
Heiko
Helpdesk
Histoire
hotspot
hotspots
hungfnt
iconify
Expand All @@ -64,6 +66,7 @@ Katson
kcachegrind
keyrings
Kimbatt
ksoftirqd
Laravel
ldpr
letsencrypt
Expand All @@ -79,6 +82,7 @@ mdsvex
Metainfo
mickvandijke
mika
mpstat
Multiuser
naim
Newtrackon
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { getMetadata } from '$lib/data/metadata';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ url }) => {
const slug = url.pathname.split('/').filter(Boolean).pop();
if (!slug) throw new Error('Slug could not be determined.');

const metadata = await getMetadata();
const currentPost = metadata.find((post) => post.slug === slug);

if (!currentPost) throw new Error(`Post not found: ${slug}`);

return { currentPost, allPosts: metadata };
};
Loading
Loading