Skip to content

Commit 012fb50

Browse files
feat(blog): support HTML posts via sidecar JSON metadata (#14)
- Add custom html-blog-loader in content.config.ts that reads .html files paired with same-stem .json sidecar files for title/description/locale/pubDate - Update blog index and [slug] pages to merge blogHtml collection with blog - HTML posts render their own head styles + body content directly via set:html, preserving the post's self-contained styling - Add PawBench v1.0 blog posts (zh + en) as the first HTML entries - Remove placeholder introducing-pawbench.md stubs Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 85eceab commit 012fb50

11 files changed

Lines changed: 1120 additions & 443 deletions

‎site/src/content.config.ts‎

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,59 @@
11
import { defineCollection, z } from 'astro:content';
22
import { glob } from 'astro/loaders';
3+
import { readdir, readFile } from 'node:fs/promises';
4+
import { resolve, basename } from 'node:path';
5+
import { fileURLToPath } from 'node:url';
6+
7+
const blogSchema = z.object({
8+
title: z.string(),
9+
description: z.string(),
10+
locale: z.enum(['zh', 'en']),
11+
pubDate: z.coerce.date(),
12+
author: z.string().default('PawBench team'),
13+
tags: z.array(z.string()).default([]),
14+
draft: z.boolean().default(false),
15+
});
316

417
const blog = defineCollection({
518
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
6-
schema: z.object({
7-
title: z.string(),
8-
description: z.string(),
9-
locale: z.enum(['zh', 'en']),
10-
pubDate: z.coerce.date(),
11-
author: z.string().default('PawBench team'),
12-
tags: z.array(z.string()).default([]),
13-
draft: z.boolean().default(false),
14-
}),
19+
schema: blogSchema,
20+
});
21+
22+
const blogHtml = defineCollection({
23+
loader: {
24+
name: 'html-blog-loader',
25+
load: async ({ store, generateDigest, parseData, config }) => {
26+
const base = fileURLToPath(new URL('src/content/blog/', config.root));
27+
let entries: string[] = [];
28+
try {
29+
entries = await readdir(base, { recursive: true }) as string[];
30+
} catch {
31+
return;
32+
}
33+
for (const entry of entries) {
34+
const normalized = entry.replace(/\\/g, '/');
35+
if (!normalized.endsWith('.html')) continue;
36+
37+
const stem = normalized.replace(/\.html$/, '');
38+
const jsonPath = resolve(base, `${stem}.json`);
39+
40+
let meta: Record<string, unknown> = {};
41+
try {
42+
meta = JSON.parse(await readFile(jsonPath, 'utf-8'));
43+
} catch {
44+
// no sidecar JSON — skip this file
45+
continue;
46+
}
47+
48+
const htmlPath = resolve(base, normalized);
49+
const body = await readFile(htmlPath, 'utf-8');
50+
const id = normalized;
51+
const parsedData = await parseData({ id, data: meta });
52+
store.set({ id, data: parsedData, body, digest: generateDigest(body + JSON.stringify(meta)) });
53+
}
54+
},
55+
},
56+
schema: blogSchema,
1557
});
1658

17-
export const collections = { blog };
59+
export const collections = { blog, blogHtml };

‎site/src/content/blog/en/PAWBENCH_MODEL_HARNESS_BLOG_EN.html‎

Lines changed: 396 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"title": "PawBench v1.0: Measuring General-Purpose Agents Reproducibly",
3+
"description": "Does the same model perform differently inside different agent harnesses? PawBench puts both model capability and harness quality into the same evaluation matrix so you can see how they jointly determine real-task outcomes.",
4+
"locale": "en",
5+
"pubDate": "2026-06-10",
6+
"author": "PawBench team",
7+
"tags": ["harness", "model", "benchmark", "agent"]
8+
}

‎site/src/content/blog/en/introducing-pawbench.md‎

Lines changed: 0 additions & 203 deletions
This file was deleted.

0 commit comments

Comments
 (0)