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 date: 2026-07-13
author: "[Renox](/author/renox/) and gemini-3.1-pro-preview" 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." 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: tags:
- NSA - NSA
- telepathy - telepathy
@@ -14,7 +14,7 @@ tags:
- aliens - 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 ## 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({ articles.push({
...data, ...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, sourceFile: file,
markdown: md, markdown: md,
isUnlisted: unlisted.has(data.slug), isUnlisted: unlisted.has(data.slug),
+1 -1
View File
@@ -98,7 +98,7 @@ export const renderArticle = (article, dates = articleDates(article)) => `
${shellHead({ ${shellHead({
title: `${article.title} — apophenia.news`, title: `${article.title} — apophenia.news`,
desc: article.description, desc: article.description,
image: article.header_image, image: absUrl(article.header_image),
url: `${SITE_URL}/${article.slug}/`, url: `${SITE_URL}/${article.slug}/`,
type: "article", type: "article",
...dates, ...dates,
+11
View File
@@ -1,9 +1,20 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import fs from "node:fs";
import fg from "fast-glob"; 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({ export default defineConfig({
root: "src", root: "src",
publicDir: "../public", publicDir: "../public",
plugins: [articleAssets],
build: { build: {
outDir: "../dist", outDir: "../dist",
emptyOutDir: true, emptyOutDir: true,