Add a footer and an in-app docs viewer

Footer (in the authenticated app layout, matching where Nav lives):
copyright line, a Docs link, and a link to the project repo.

Docs viewer: /docs lists docs/API.md, CONFIG_REFERENCE.md, and
ARCHITECTURE.md; /docs/[slug] renders one via react-markdown +
remark-gfm (tables, fenced code) inside a Tailwind Typography `prose`
block, dark-mode aware via prose-invert. The markdown files themselves
stay the single source of truth at the repo's docs/ - the app reads
them at request time rather than duplicating their content, resolved
from the app's cwd the same way config paths already are, since a
compiled Route Handler's module graph doesn't preserve source-relative
paths.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 00:54:15 +02:00
co-authored by Claude Sonnet 5
parent d7c0be6b22
commit 1b415a7957
9 changed files with 1055 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
import fs from "node:fs";
import path from "node:path";
export interface DocMeta {
slug: string;
title: string;
description: string;
file: string;
}
export const DOCS: DocMeta[] = [
{
slug: "api",
title: "API Reference",
description: "REST and WebSocket endpoints, auth, and request/response shapes.",
file: "API.md",
},
{
slug: "config",
title: "Config Reference",
description: "Every field in triggershell.yml: server, auth, scripts, and variables.",
file: "CONFIG_REFERENCE.md",
},
{
slug: "architecture",
title: "Architecture",
description: "How the CLI, custom server, runner, and web app fit together.",
file: "ARCHITECTURE.md",
},
];
export function getDocMeta(slug: string): DocMeta | undefined {
return DOCS.find((doc) => doc.slug === slug);
}
// docs/ ships at the repo root, one level up from this Next app - resolved at runtime rather
// than imported, since the compiled Route Handler module graph doesn't preserve source-relative
// paths (same reasoning as config path resolution in lib/config/load.ts).
function docsDir(): string {
return path.resolve(process.cwd(), "..", "docs");
}
export function readDocContent(doc: DocMeta): string | null {
const filePath = path.join(docsDir(), doc.file);
if (!fs.existsSync(/*turbopackIgnore: true*/ filePath)) return null;
return fs.readFileSync(/*turbopackIgnore: true*/ filePath, "utf-8");
}