Skip to content
Draft
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
3 changes: 3 additions & 0 deletions apps/www/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Next.js build output and the type shim it generates on first build.
.next/
next-env.d.ts

# @affordances/md writes its route table here on each dev and build (next.config.ts).
.affordances/
42 changes: 9 additions & 33 deletions apps/www/next.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import createMDX from "@next/mdx";
import { withMd } from "@affordances/md/next";
import type { NextConfig } from "next";

/**
Expand All @@ -15,20 +16,6 @@ import type { NextConfig } from "next";
*/
const workspaceRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");

const acceptsMarkdown = {
type: "header",
key: "accept",
value: ".*text/markdown.*",
} as const;

/*
* Extensionless pages, as the docs site has them. Skips the markdown route
* itself, the API, MCP and social-card endpoints, Next's own assets, and anything with a
* file extension (`.md` urls, llms.txt, openapi.json), which has no markdown
* twin.
*/
const pages = "/:path((?!llms\\.mdx/|(?:api|_next|mcp|og)(?:/|$))(?!.*\\.[a-z0-9]+$).+)";

const nextConfig: NextConfig = {
// Pages prerender a shell around cached data (lib/data.ts), and links prefetch that shell.
cacheComponents: true,
Expand All @@ -42,24 +29,6 @@ const nextConfig: NextConfig = {
// takumi's native renderer (app/og) is loaded by Node, not bundled -- as vlt.io has it.
serverExternalPackages: ["@takumi-rs/image-response"],
turbopack: { root: workspaceRoot },
// The docs' rewrites (vltpkg/www/docs/next.config.ts). The query survives a
// rewrite, so `/x.md?fixture=astro` is the markdown of that filtered view.
rewrites: async () => ({
// Agents that ask for markdown get it at the page's own url; runs before
// the filesystem, which would otherwise match the html page first.
beforeFiles: [
{ source: "/", has: [acceptsMarkdown], destination: "/llms.mdx" },
{ source: pages, has: [acceptsMarkdown], destination: "/llms.mdx/:path" },
],
// `/package-managers/clean.md` serves that page as plain markdown.
afterFiles: [{ source: "/:path+.md", destination: "/llms.mdx/:path+" }],
}),
// Those urls answer with html or markdown depending on `Accept`, so caches must key on it.
headers: async () =>
["/", pages].map((source) => ({
source,
headers: [{ key: "Vary", value: "Accept" }],
})),
// Compare was removed; its links land on the same section's report.
redirects: async () => [
/* The front door is the default benchmark; what used to be here is /about.
Expand All @@ -76,4 +45,11 @@ const nextConfig: NextConfig = {
],
};

export default createMDX()(nextConfig);
/*
* Every page's Markdown twin (`/x.md`, or the page's url with `Accept:
* text/markdown`) is @affordances/md's: its rewrites send both to
* app/api/affordances/md, which converts the page's own HTML, query and
* redirects included, and fills the figures' `<AgentSlot>`s from each route's
* markdown.ts.
*/
export default withMd(createMDX()(nextConfig));
1 change: 1 addition & 0 deletions apps/www/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"test": "vitest run"
},
"dependencies": {
"@affordances/md": "0.5.0",
"@base-ui/react": "1.9.0",
"@benchmarks/data": "workspace:*",
"@benchmarks/types": "workspace:*",
Expand Down
134 changes: 134 additions & 0 deletions apps/www/src/app/[section]/[[...params]]/markdown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import type { MarkdownArgs, Slots } from "@affordances/md/next";
import {
figureMarkdown,
meanOf,
referenceMarkdown,
runMarkdown,
TWIN_CACHE,
type FigureMarkdown,
} from "@/lib/markdown";
import { siteOrigin } from "@/lib/metadata";
import { loadReportParams } from "@/lib/params";
import { ignoredSelection, loadReport, type ReportResult } from "@/lib/report";
import { SECTION_DEFS } from "@/lib/sections";

/**
* The report's Markdown twin (@affordances/md): the page's own HTML,
* converted, with each figure's `<AgentSlot>` (components/report.tsx `Figure`)
* filled with the numbers it draws -- the same builders as the MCP tools' and
* the API's Markdown (lib/markdown.ts). A chart is canvas, and a table on the
* page is formatted for reading; a twin carries the values.
*
* A 404 an agent can act on: an unknown section is the page's own
* `notFound()`, and `notFoundMarkdown` gives it our body. The page answers the
* other two with something other than a 404 (a redirect to the section's own
* report, the scenario with the extra segment ignored), so the default export
* answers those itself.
*/
type Route = "/[section]/[[...params]]";
type Args = MarkdownArgs<Route>;

const decode = (segment: string | undefined) =>
segment === undefined ? undefined : decodeURIComponent(segment);

/* One report a twin request, however many of its slots fill. */
const loaded = new WeakMap<Request, Promise<{ result: ReportResult; figures?: FigureMarkdown }>>();
const report = (args: Args) => {
let found = loaded.get(args.request);
if (found === undefined) {
const query = loadReportParams(args.searchParams);
found = loadReport({
section: decode(args.params.section) ?? "",
scenario: decode(args.params.params?.[0]),
query,
}).then((result) => ({
result,
...(result.status === "ok"
? { figures: figureMarkdown(result.report, siteOrigin(args.request)) }
: {}),
}));
loaded.set(args.request, found);
}
return found;
};

const pathOf = (args: Args) => {
const segments = [args.params.section, ...(args.params.params ?? [])].map((s) => decode(s) ?? "");
return { segments, path: `/${segments.map(encodeURIComponent).join("/")}` };
};

const notFoundBody = (args: Args, path: string, detail = "") => {
const abs = (to: string) => new URL(to, siteOrigin(args.request)).href;
return `# 404: Page not found\n\nThere is no page at \`${path}\` on vlt benchmarks.${detail}\n\n- [llms.txt](${abs("/llms.txt")}) lists every page, each available as Markdown\n- [openapi.json](${abs("/openapi.json")}) describes the JSON API\n- [Home](${abs("/")})\n`;
};

const notFound = (args: Args, path: string, detail?: string) =>
new Response(notFoundBody(args, path, detail), {
status: 404,
headers: { "content-type": "text/markdown; charset=utf-8" },
});

/* The page's own 404: an unknown section. */
export const notFoundMarkdown = (args: Args) => notFoundBody(args, pathOf(args).path);

export const publicCache = TWIN_CACHE;

export default async function markdown(args: Args) {
const { segments, path } = pathOf(args);
/* The page renders `/{section}/{scenario}/extra` as the scenario, with a 200. */
if (segments.length > 2) return notFound(args, path);
const { result } = await report(args);
/* The page redirects an unknown scenario to the section's report, a 307 and no 404. */
if (result.status === "unknown-scenario" && args.params.params !== undefined) {
return notFound(
args,
path,
` Scenarios ${SECTION_DEFS[result.section].label} published in this run: ${result.scenarios.map((s) => `\`${s}\``).join(", ")}.`,
);
}
return undefined;
}

const figure =
(id: string) =>
async (args: Args): Promise<string | undefined> => {
const { figures } = await report(args);
const lines = figures?.[id];
return lines === undefined ? undefined : lines.join("\n");
};

export const slots = {
/*
* The run and filters, as the menus and chips show them, and before them a
* selection the report fell back from: what a strict reader is told and the
* page leaves silent.
*/
context: async (args) => {
const { result } = await report(args);
if (result.status !== "ok") return undefined;
const { averageOf } = result.report.scenario;
const ignored = ignoredSelection(loadReportParams(args.searchParams), result.report);
return [
...(ignored === null ? [] : [`> ${ignored} This is the report without that selection.`, ""]),
...(averageOf === null ? [] : [meanOf(averageOf), ""]),
...runMarkdown(result.report),
].join("\n");
},
reference: async (args) => {
const { result } = await report(args);
return result.status === "ok"
? referenceMarkdown(result.report, siteOrigin(args.request)).join("\n")
: undefined;
},
"leaderboard-table": figure("leaderboard-table"),
"trend-by-tool": figure("trend-by-tool"),
"time-trend": figure("time-trend"),
"time-bars": figure("time-bars"),
"time-by-fixture": figure("time-by-fixture"),
"time-table": figure("time-table"),
"per-package-chart": figure("per-package-chart"),
"per-package-by-fixture": figure("per-package-by-fixture"),
"per-package-table": figure("per-package-table"),
"package-count": figure("package-count"),
"process-count": figure("process-count"),
} satisfies Slots<Route>;
15 changes: 14 additions & 1 deletion apps/www/src/app/[section]/[[...params]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Fragment, Suspense } from "react";
import { notFound, redirect } from "next/navigation";
import { ArrowRight, ChartLine } from "lucide-react";
import { AgentOnly, AgentSlot } from "@affordances/md/react";
import { InlineCode, Marked } from "@/components/inline-code";
import { Button } from "@/components/ui/button";
import { TimeSeriesChart } from "@/components/charts-echarts";
Expand Down Expand Up @@ -41,6 +42,7 @@ import { toTrendColumns } from "@/lib/chart-model";
import { loadReportParams, rangeLabel, reportHref, reportParams } from "@/lib/params";
import { loadCatalog } from "@/lib/catalog";
import { dataClient } from "@/lib/data";
import { PER_PACKAGE_DERIVED } from "@/lib/markdown";
import { loadReport } from "@/lib/report";
import { withAverage } from "@/lib/scenarios";
import { isSectionId, SECTION_DEFS, SECTIONS } from "@/lib/sections";
Expand Down Expand Up @@ -665,6 +667,8 @@ const Report = async ({
</div>
}
/>
{/* Empty for people; the twin's run and filters, as the menus and chips show them (./markdown.ts). */}
<AgentSlot name="context" />
{caveats.qualifier === null ? null : (
<div data-narrative className="mt-6">
<Callout title="A faster mean is not ranked">
Expand Down Expand Up @@ -716,12 +720,21 @@ const Report = async ({
id={part.id}
title={part.items.every((item) => solo.has(item.id)) ? undefined : part.title}
>
{/* For agents only: on the page, the guides and axis labels say it (lib/markdown.ts). */}
{part.id === "per-package" ? (
<AgentOnly>
<p>{PER_PACKAGE_DERIVED}</p>
</AgentOnly>
) : null}
{part.items.map((item) => (
<Fragment key={item.id}>{bodies[item.id]}</Fragment>
))}
</ReportSection>
))}
<DataAndSource raw={report.raw} />
{/* The raw files are a menu here; the twin lists them, with the method and a citation. */}
<AgentSlot name="reference">
<DataAndSource raw={report.raw} />
</AgentSlot>
</PendingRegion>
{/* Fixed to the viewport's right edge, in the page margin: no column. */}
<TOCMinimap
Expand Down
14 changes: 14 additions & 0 deletions apps/www/src/app/about/markdown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { MarkdownArgs } from "@affordances/md/next";
import { GET as llmsTxt } from "@/app/llms.txt/route";
import { TWIN_CACHE } from "@/lib/markdown";

/*
* The About page is cards and links into the site; its twin is the llms.txt
* index, which says the same in the form agents read: every page as Markdown,
* how to read the results, and the API and MCP server.
*/
export default async function markdown({ request }: MarkdownArgs<"/about">) {
return (await llmsTxt(request)).text();
}

export const publicCache = TWIN_CACHE;
2 changes: 1 addition & 1 deletion apps/www/src/app/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Content from "./content.mdx";

/* The site's front page: what the benchmarks are, how agents read them, and the
latest headline of each. `/` itself opens on the default benchmark. Its
Markdown twin is llms.txt. */
Markdown twin is llms.txt (./markdown.ts). */
export const metadata = pageMetadata({
title: "About",
description: SITE_DESCRIPTION,
Expand Down
6 changes: 6 additions & 0 deletions apps/www/src/app/api/affordances/md/[[...path]]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Every page's Markdown twin, reached through withMd's rewrites (next.config.ts).
* The All-time report (`/package-managers/clean?range=all`) fits the default
* `maxBytes`: it counts the HTML kept once the Flight data is stripped.
*/
export { GET } from "@affordances/md/next";
45 changes: 25 additions & 20 deletions apps/www/src/app/llms-full.txt/route.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
import { connection } from "next/server";
import { createLlmsFullHandler } from "@affordances/md/next";
import { loadCatalog } from "@/lib/catalog";
import { dataClient } from "@/lib/data";
import { reportMarkdown } from "@/lib/markdown";
import { reportMarkdown, TWIN_CACHE } from "@/lib/markdown";
import { siteOrigin } from "@/lib/metadata";
import { loadReportParams } from "@/lib/params";
import { loadReport } from "@/lib/report";
import { withAverage } from "@/lib/scenarios";
import { scenarioPath, withAverage } from "@/lib/scenarios";
import { SECTIONS } from "@/lib/sections";

export const GET = async (request: Request) => {
await connection();
const client = dataClient();
const { scenarios } = await loadCatalog(client);
const query = loadReportParams(new URLSearchParams());
const texts = await Promise.all(
SECTIONS.flatMap((section) =>
withAverage(scenarios[section]).map(async ({ variation }) => {
const result = await loadReport({ section, scenario: variation, query, client });
return result.status === "ok" ? reportMarkdown(result.report, siteOrigin(request)) : "";
}),
),
);
return new Response(texts.filter(Boolean).join("\n\n"), {
headers: { "Content-Type": "text/plain; charset=utf-8" },
});
};
/*
* Every scenario of the latest run at the default view, from the Markdown
* builder rather than rendered: a page whose report throws makes the file a
* 503 that's never cached, not a cached file without it.
*/
export const GET = createLlmsFullHandler({
publicCache: TWIN_CACHE,
pages: async (request) => {
const client = dataClient();
const { scenarios } = await loadCatalog(client);
const query = loadReportParams(new URLSearchParams());
return SECTIONS.flatMap((section) =>
withAverage(scenarios[section]).map(({ variation }) => ({
url: new URL(scenarioPath(section, variation), siteOrigin(request)).href,
markdown: async () => {
const result = await loadReport({ section, scenario: variation, query, client });
return result.status === "ok" ? reportMarkdown(result.report, siteOrigin(request)) : "";
},
})),
);
},
});
Loading