cb82987c33
The first runnable DocSite server (plan docs/plans/0001-walking-skeleton.md):
`docsite serve` renders the published docs of aura and data-server live from
their Gitea main revisions, dark-themed, navigated from the DocSite-owned
whitelist registry; `docsite check` validates every nav reference against the
live sources with an exit code. Built across nine TDD tasks — config (registry
whitelist + traversal guard), gitea read-only client (Fetcher trait, injected
for offline tests), render (comrak GFM + syntect), theme (embedded dark CSS +
shell), revision-keyed cache, reference validator, axum server, main wiring.
All five domain invariants land: read-only Gitea access via the Fetcher trait;
single source of truth via the (repo,path,sha) cache; reference integrity
detected (check + a fail-loud render-time error block) never enforced;
whitelist publishing with path-traversal refusal; the closed directive
vocabulary's no-op expansion seam reserved in render.
Verification (run by the orchestrator): cargo build green; cargo test green —
40 tests (32 lib unit + 7 e2e via an injected MockFetcher + 1 gated live smoke
that reached the real Gitea this run); cargo clippy --all-targets -D warnings
clean.
Held quality findings (plan-prescribed / plan-deferred, not defects; tracked
as follow-up issues): nav_html interpolates the registry slug/page into the
href unescaped (operator-trusted registry — latent, not a live vector);
GiteaClient's 404->NotFound status mapping (the invariant-3 broken-reference
signal) is covered only by the gated live smoke's happy path, not a unit test.
Resolved API deviations from the plan text (newer crate versions): reqwest
0.13 renamed feature rustls-tls -> rustls; comrak 0.52 deprecated `Plugins`
(used `comrak::options::Plugins`); axum 0.8 capture syntax `/{slug}/{*page}`.
refs #1
22 lines
1.3 KiB
CSS
22 lines
1.3 KiB
CSS
:root {
|
|
--bg: #16161a; --fg: #cdd6f4; --dim: #6c7086; --link: #89b4fa;
|
|
--panel: #1e1e2e; --border: #313244; --accent: #f5e0dc;
|
|
}
|
|
html, body { margin: 0; background: var(--bg); color: var(--fg);
|
|
font-family: system-ui, sans-serif; line-height: 1.6; }
|
|
header.crumb { padding: 10px 16px; border-bottom: 1px solid var(--border);
|
|
font-size: 13px; color: var(--dim); }
|
|
header.crumb a { color: var(--link); text-decoration: none; }
|
|
nav { padding: 10px 16px; border-bottom: 1px solid var(--border); font-size: 14px; }
|
|
nav .sec { color: var(--dim); margin: 8px 0 2px; text-transform: uppercase; font-size: 12px; }
|
|
nav a { display: block; color: var(--link); text-decoration: none; padding: 1px 0; }
|
|
main.doc { max-width: 860px; margin: 0 auto; padding: 24px 16px 64px; }
|
|
main.doc code, main.doc pre { font-family: ui-monospace, monospace; }
|
|
main.doc pre { background: var(--panel); border: 1px solid var(--border);
|
|
border-radius: 6px; padding: 12px; overflow-x: auto; }
|
|
main.doc table { border-collapse: collapse; margin: 12px 0; }
|
|
main.doc th, main.doc td { border: 1px solid var(--border); padding: 6px 10px; }
|
|
main.doc a { color: var(--link); }
|
|
.docsite-error { background: #3a1e26; border: 1px solid #f38ba8; color: #f38ba8;
|
|
border-radius: 6px; padding: 12px; margin: 12px 0; font-family: ui-monospace, monospace; }
|