Feat: Host the NSA article's header image next to the article

Relative header_image paths (./img.webp) now resolve to /<slug>/img.webp, and article
assets also ship unhashed so og:image and JSON-LD URLs resolve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 22:46:43 +00:00
co-authored by Claude Opus 5.5
parent 715be64a65
commit 24a26072f9
5 changed files with 16 additions and 3 deletions
+2 -2
View File
@@ -4,7 +4,7 @@ slug: human-telepathy-is-here
date: 2026-07-13
author: "[Renox](/author/renox/) and gemini-3.1-pro-preview"
description: "The biggest invention since the atomic bomb arrived more than five years ago. It’s non-invasive, long-range, and it’s already running."
header_image: "https://free.direct-img.link/nsa?src=wikimedia"
header_image: ./nsa-headquarters.webp
tags:
- NSA
- telepathy
@@ -14,7 +14,7 @@ tags:
- aliens
---
![NSA headquarters at Fort Meade](https://free.direct-img.link/nsa?src=wikimedia)
![NSA headquarters at Fort Meade](./nsa-headquarters.webp)
## The Silent Manhattan Project
Binary file not shown.

After

Width:  |  Height:  |  Size: 597 KiB

+2
View File
@@ -63,6 +63,8 @@ const run = async () => {
articles.push({
...data,
// "./img.webp" points at a file next to index.md, which gets copied to /<slug>/
header_image: data.header_image?.replace(/^\.\//, `/${data.slug}/`),
sourceFile: file,
markdown: md,
isUnlisted: unlisted.has(data.slug),
+1 -1
View File
@@ -98,7 +98,7 @@ export const renderArticle = (article, dates = articleDates(article)) => `
${shellHead({
title: `${article.title} — apophenia.news`,
desc: article.description,
image: article.header_image,
image: absUrl(article.header_image),
url: `${SITE_URL}/${article.slug}/`,
type: "article",
...dates,
+11
View File
@@ -1,9 +1,20 @@
import { defineConfig } from "vite";
import fs from "node:fs";
import fg from "fast-glob";
// Vite hashes images it finds in HTML into /assets/, but og:image and JSON-LD point at /<slug>/<file>, so ship those too
const articleAssets = {
name: "article-assets",
generateBundle() {
for (const fileName of fg.sync("*/*", { cwd: "src", ignore: ["**/*.html"] }))
this.emitFile({ type: "asset", fileName, source: fs.readFileSync(`src/${fileName}`) });
}
};
export default defineConfig({
root: "src",
publicDir: "../public",
plugins: [articleAssets],
build: {
outDir: "../dist",
emptyOutDir: true,