fix: serve docs pages in sidebar order and polish UI typography

- Add `getOrderedPages()` to `source.ts` that walks `source.getPageTree()` recursively so pages are yielded in the order declared by `meta.json` (sidebar order) rather than arbitrary filesystem order; replace `source.getPages()` in `llms-full.txt` and `docs-index.functions.ts`
- Enhance `getLLMText` to prepend the page description as a blockquote when present, and `trimStart()` the processed body so the output is clean
- Fix `useTimeAgo` to return `"just now"` for sub-minute diffs instead of the awkward `Intl.RelativeTimeFormat` "0 seconds ago" / "in 0 seconds" output
- Tighten `LastRefreshed` markup so the tooltip wraps only the relative-time string, not the surrounding prose
- Switch heading `font-semibold` → `font-medium` across stats and home pages; update category card title color from `text-muted-foreground` to `text-foreground/85` for better contrast
This commit is contained in:
2026-05-27 18:45:08 -04:00
parent 190a078593
commit 38cc24362c
6 changed files with 43 additions and 15 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ function format(iso: string, now: Date): string {
if (Number.isNaN(date.getTime())) return iso;
const diffSec = Math.round((date.getTime() - now.getTime()) / 1000);
const abs = Math.abs(diffSec);
if (abs < MINUTE) return relativeFormatter.format(diffSec, "second");
if (abs < MINUTE) return "just now";
if (abs < HOUR) return relativeFormatter.format(Math.round(diffSec / MINUTE), "minute");
if (abs < DAY) return relativeFormatter.format(Math.round(diffSec / HOUR), "hour");
if (abs < MONTH) return relativeFormatter.format(Math.round(diffSec / DAY), "day");
+27 -1
View File
@@ -1,4 +1,5 @@
import { docs } from "collections/server";
import type { Node } from "fumadocs-core/page-tree";
import { loader } from "fumadocs-core/source";
export const source = loader({
@@ -27,5 +28,30 @@ export function markdownPathToSlugs(segs: string[]) {
*/
export async function getLLMText(page: (typeof source)["$inferPage"]) {
const processed = await page.data.getText("processed");
return `# ${page.data.title} (${page.url})\n\n${processed}`;
return `# ${page.data.title} (${page.url})\n\n${page.data.description ? `> ${page.data.description.trim()}\n\n` : ""}${processed.trimStart()}`;
}
/**
* Walk `source.pageTree` and yield pages in the order declared by `meta.json`
* (recursively into folders). `source.getPages()` returns filesystem order;
* the tree honors the sidebar order, which is what users see.
*/
export function getOrderedPages() {
const out: (typeof source)["$inferPage"][] = [];
const visit = (nodes: Node[]) => {
for (const node of nodes) {
if (node.type === "page") {
const page = source.getNodePage(node);
if (page) out.push(page);
} else if (node.type === "folder") {
if (node.index) {
const page = source.getNodePage(node.index);
if (page) out.push(page);
}
visit(node.children);
}
}
};
visit(source.getPageTree().children);
return out;
}
+2 -2
View File
@@ -1,6 +1,6 @@
import { createFileRoute } from "@tanstack/react-router";
import { getLLMText, source } from "@/lib/source";
import { getLLMText, getOrderedPages } from "@/lib/source";
/**
* `/llms-full.txt` — the full processed Markdown of every docs page,
@@ -10,7 +10,7 @@ export const Route = createFileRoute("/docs/llms-full.txt")({
server: {
handlers: {
GET: async () => {
const scanned = await Promise.all(source.getPages().map(getLLMText));
const scanned = await Promise.all(getOrderedPages().map(getLLMText));
return new Response(scanned.join("\n\n"), {
headers: {
"content-type": "text/plain; charset=utf-8",
+1 -1
View File
@@ -30,7 +30,7 @@ function Page() {
return (
<div className="mx-auto max-w-7xl px-4 py-10 sm:px-6">
<header className="mb-10 max-w-2xl">
<h1 className="text-3xl font-semibold tracking-tight">
<h1 className="text-3xl font-medium tracking-tight">
Build your stack, minus the pressure.
</h1>
<p className="mt-2 text-pretty text-muted-foreground">
+10 -8
View File
@@ -45,6 +45,7 @@ function LastRefreshed({ iso }: { iso: string }) {
const ago = useTimeAgo(iso);
return (
<p className="text-xs leading-4">
Last refreshed{" "}
<Tooltip>
<TooltipTrigger
nativeButton={false}
@@ -55,10 +56,11 @@ function LastRefreshed({ iso }: { iso: string }) {
/>
}
>
Last refreshed {ago}.
{ago}
</TooltipTrigger>
<TooltipContent sideOffset={8}>{formatGeneratedAt(iso)} UTC</TooltipContent>
</Tooltip>
.
</p>
);
}
@@ -95,7 +97,7 @@ function StatsPage() {
return (
<div className="mx-auto max-w-7xl px-4 py-10 sm:px-6">
<header className="mb-8 max-w-2xl">
<h1 className="mb-2 text-3xl font-semibold tracking-tight">Stats</h1>
<h1 className="mb-2 text-3xl font-medium tracking-tight">Stats</h1>
<p className="mb-4 text-pretty text-muted-foreground">
What modules developers actually pick, aggregated from anonymous CLI telemetry {" "}
<a
@@ -161,7 +163,7 @@ function StatsPage() {
</section>
<section className="mb-8">
<h2 className="mb-4 text-lg font-semibold tracking-tight">Popular modules by category</h2>
<h2 className="mb-4 text-lg font-medium tracking-tight">Popular modules by category</h2>
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
{KNOWN_CATEGORIES.map((category) => {
const entries = stats?.perCategory[category] ?? [];
@@ -170,11 +172,11 @@ function StatsPage() {
<Card key={category}>
<CardHeader>
<div className="flex items-baseline justify-between gap-3">
<CardTitle className="text-xs font-medium tracking-wider text-muted-foreground uppercase">
<CardTitle className="text-xs font-medium tracking-wider text-foreground/85 uppercase">
{categoryLabel(category)}
</CardTitle>
{totalInCategory > 0 ? (
<span className="font-mono text-xs text-muted-foreground/70 tabular-nums">
<span className="font-mono text-xs text-muted-foreground tabular-nums">
{numberFormatter.format(totalInCategory)}
</span>
) : null}
@@ -204,11 +206,11 @@ function StatsPage() {
</section>
<section id="telemetry" className="scroll-mt-20">
<h2 className="mb-4 text-lg font-semibold tracking-tight">Telemetry policy</h2>
<h2 className="mb-4 text-lg font-medium tracking-tight">Telemetry policy</h2>
<div className="grid gap-4 sm:grid-cols-2">
<Card>
<CardHeader>
<CardTitle className="text-xs font-medium tracking-wider text-muted-foreground uppercase">
<CardTitle className="text-xs font-medium tracking-wider text-foreground/85 uppercase">
What we save
</CardTitle>
</CardHeader>
@@ -223,7 +225,7 @@ function StatsPage() {
</Card>
<Card>
<CardHeader>
<CardTitle className="text-xs font-medium tracking-wider text-muted-foreground uppercase">
<CardTitle className="text-xs font-medium tracking-wider text-foreground/85 uppercase">
What we don&rsquo;t
</CardTitle>
</CardHeader>
+2 -2
View File
@@ -1,6 +1,6 @@
import { createServerFn } from "@tanstack/react-start";
import { source } from "@/lib/source";
import { getOrderedPages } from "@/lib/source";
export type DocsPageSummary = {
title: string;
@@ -20,7 +20,7 @@ export type DocsIndex = {
*/
export const getDocsIndex = createServerFn({ method: "GET" }).handler(
async (): Promise<DocsIndex> => ({
pages: source.getPages().map((p) => ({
pages: getOrderedPages().map((p) => ({
title: p.data.title,
description: p.data.description,
url: p.url,