mirror of
https://github.com/Screenly/Anthias.git
synced 2026-07-30 09:15:51 -04:00
* feat(website): generate llms.txt and llms-full.txt Add a post-build generator that produces llms.txt (a curated index) and llms-full.txt (the full text of every page) for the marketing site, following the llmstxt.org convention and served from the site root. The generator extracts clean Markdown from the rendered public/ HTML rather than the Markdown/data sources, so it captures the marketing copy that lives in Hugo layouts (home, features, get-started) and never drifts from what visitors actually see. Wired into `bun run build` and the deploy-website workflow. Also fixes four pre-existing `sort $map` calls in the /api templates: in Hugo, sorting a map collapses it to a slice, so the keys were replaced by numeric indices — API paths, HTTP status codes, request body content-types, and schema field names all rendered as 0, 1, 2… on the live page. Hugo already ranges maps in sorted-key order, so dropping `sort` fixes it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): escape backslashes before pipes in llms table cells CodeQL js/incomplete-sanitization: the Markdown table-cell escaping replaced "|" but not "\", so a literal backslash in a cell could combine with the pipe escape. Escape backslashes first. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): collapse mixed-content whitespace and harden llms entrypoint Address Copilot review: - serializeMixed() now collapses text-node whitespace the same way inline() does, so HTML source indentation/newlines can't leak into list items and other mixed-content blocks. - main() gets a .catch that sets a non-zero exit code, so build/CI failures (missing public/, read/write/parse errors) are deterministic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
152 lines
8.6 KiB
HTML
152 lines
8.6 KiB
HTML
{{ define "main" }}
|
|
{{- $spec := site.Data.openapi -}}
|
|
{{- $methods := slice "get" "post" "put" "patch" "delete" -}}
|
|
|
|
{{- /* Build sorted list of unique tags */ -}}
|
|
{{- $allTags := slice -}}
|
|
{{- range $path, $ops := $spec.paths -}}
|
|
{{- range $method := $methods -}}
|
|
{{- with index $ops $method -}}
|
|
{{- $op := . -}}
|
|
{{- with $op.tags -}}
|
|
{{- range . -}}{{- $allTags = $allTags | append . -}}{{- end -}}
|
|
{{- else -}}
|
|
{{- $allTags = $allTags | append "default" -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- $tags := $allTags | uniq | sort -}}
|
|
|
|
<header class="bg-brand-dark py-16 md:py-20 px-6 md:px-12 lg:px-20">
|
|
<div class="max-w-7xl mx-auto">
|
|
<h1 class="text-white text-4xl md:text-5xl font-extrabold tracking-tight">API Reference</h1>
|
|
<p class="text-white/60 text-lg mt-4 max-w-2xl">{{ $spec.info.title }} v{{ $spec.info.version }} — manage assets, device settings, backups, and integrations over HTTP.</p>
|
|
<div class="mt-6 flex flex-wrap gap-2">
|
|
{{- range $tags -}}
|
|
<a href="#tag-{{ . | urlize }}" class="px-3 py-1.5 rounded-full bg-white/10 hover:bg-white/20 text-white text-xs font-medium transition">{{ . }}</a>
|
|
{{- end -}}
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="bg-white py-16 lg:py-20 px-6 md:px-12 lg:px-20">
|
|
<div class="max-w-7xl mx-auto space-y-16">
|
|
{{- range $tag := $tags -}}
|
|
<div id="tag-{{ $tag | urlize }}" class="scroll-mt-24">
|
|
<h2 class="text-2xl font-bold text-brand-near-black capitalize">{{ replace $tag "_" " " }}</h2>
|
|
<div class="mt-6 space-y-6">
|
|
{{- range $path, $ops := $spec.paths -}}
|
|
{{- range $method := $methods -}}
|
|
{{- with index $ops $method -}}
|
|
{{- $op := . -}}
|
|
{{- $opTags := default (slice "default") $op.tags -}}
|
|
{{- if in $opTags $tag -}}
|
|
<article class="border border-zinc-200 rounded-lg overflow-hidden">
|
|
<header class="flex items-center gap-3 px-4 md:px-6 py-3 bg-zinc-50 border-b border-zinc-200">
|
|
{{- $methodColors := dict
|
|
"get" "bg-blue-100 text-blue-800"
|
|
"post" "bg-green-100 text-green-800"
|
|
"put" "bg-amber-100 text-amber-800"
|
|
"patch" "bg-orange-100 text-orange-800"
|
|
"delete" "bg-red-100 text-red-800" -}}
|
|
<span class="px-2 py-1 rounded text-xs font-bold uppercase font-mono shrink-0 {{ index $methodColors $method }}">{{ $method }}</span>
|
|
<code class="font-mono text-sm text-brand-near-black break-all">{{ $path }}</code>
|
|
{{- with $op.summary -}}
|
|
<span class="ml-auto text-xs text-zinc-500 hidden md:inline">{{ . }}</span>
|
|
{{- end -}}
|
|
</header>
|
|
<div class="px-4 md:px-6 py-5 space-y-5">
|
|
{{- with $op.description -}}<p class="text-sm text-zinc-700">{{ . }}</p>{{- end -}}
|
|
|
|
{{- with $op.parameters -}}
|
|
<div>
|
|
<h3 class="text-xs font-semibold uppercase tracking-wide text-zinc-500 mb-2">Parameters</h3>
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full text-sm border-collapse">
|
|
<thead>
|
|
<tr class="border-b border-zinc-200 text-left text-xs uppercase tracking-wide text-zinc-500">
|
|
<th class="py-2 pr-4 font-semibold">Name</th>
|
|
<th class="py-2 pr-4 font-semibold">In</th>
|
|
<th class="py-2 pr-4 font-semibold">Type</th>
|
|
<th class="py-2 font-semibold">Required</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{- range . -}}
|
|
<tr class="border-b border-zinc-100">
|
|
<td class="py-2 pr-4 font-mono text-brand-purple">{{ .name }}</td>
|
|
<td class="py-2 pr-4 text-xs text-zinc-600">{{ .in }}</td>
|
|
<td class="py-2 pr-4 font-mono text-xs text-zinc-700">{{ default "string" .schema.type }}</td>
|
|
<td class="py-2 text-xs">{{ if .required }}yes{{ else }}no{{ end }}</td>
|
|
</tr>
|
|
{{- end -}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{{- end -}}
|
|
|
|
{{- with $op.requestBody -}}
|
|
{{- /* Pick the best content type to render. JSON first;
|
|
otherwise fall back to the first one in the spec
|
|
(e.g. multipart/form-data for upload endpoints). */ -}}
|
|
{{- $body := index .content "application/json" -}}
|
|
{{- $contentType := "application/json" -}}
|
|
{{- if not $body -}}
|
|
{{- $content := .content -}}
|
|
{{- range $ct, $entry := $content -}}
|
|
{{- if not $body -}}{{- $body = $entry -}}{{- $contentType = $ct -}}{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
<div>
|
|
<h3 class="text-xs font-semibold uppercase tracking-wide text-zinc-500 mb-2">Request body
|
|
<span class="ml-1 font-mono normal-case tracking-normal text-zinc-400">{{ $contentType }}</span>
|
|
</h3>
|
|
{{ partial "api-schema.html" (dict "schema" $body.schema "spec" $spec) }}
|
|
</div>
|
|
{{- end -}}
|
|
|
|
{{- with $op.responses -}}
|
|
{{- $responses := . -}}
|
|
<div>
|
|
<h3 class="text-xs font-semibold uppercase tracking-wide text-zinc-500 mb-2">Responses</h3>
|
|
<div class="space-y-3">
|
|
{{- /* Map range is already key-sorted; `sort`
|
|
would drop the keys and make $status a
|
|
numeric index instead of the HTTP code. */ -}}
|
|
{{- range $status, $resp := $responses -}}
|
|
<details class="border border-zinc-200 rounded">
|
|
<summary class="cursor-pointer px-3 py-2 text-sm font-mono flex items-center gap-3 hover:bg-zinc-50">
|
|
{{- $statusColor := "bg-zinc-100 text-zinc-700" -}}
|
|
{{- if hasPrefix $status "2" }}{{ $statusColor = "bg-green-100 text-green-800" }}{{ end -}}
|
|
{{- if hasPrefix $status "4" }}{{ $statusColor = "bg-amber-100 text-amber-800" }}{{ end -}}
|
|
{{- if hasPrefix $status "5" }}{{ $statusColor = "bg-red-100 text-red-800" }}{{ end -}}
|
|
<span class="px-2 py-0.5 rounded text-xs font-bold {{ $statusColor }}">{{ $status }}</span>
|
|
<span class="text-xs text-zinc-500">{{ default "" $resp.description }}</span>
|
|
</summary>
|
|
{{- with $resp.content -}}
|
|
{{- with index . "application/json" -}}
|
|
<div class="px-3 py-3 border-t border-zinc-200">
|
|
{{ partial "api-schema.html" (dict "schema" .schema "spec" $spec) }}
|
|
</div>
|
|
{{- end -}}
|
|
{{- end -}}
|
|
</details>
|
|
{{- end -}}
|
|
</div>
|
|
</div>
|
|
{{- end -}}
|
|
</div>
|
|
</article>
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
</div>
|
|
</div>
|
|
{{- end -}}
|
|
</div>
|
|
</section>
|
|
{{ end }}
|