diff --git a/docs/roadmap.md b/docs/roadmap.md index 6a485c2..15b4b0e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,26 +1,40 @@ # roadmap -zat started as a small set of string primitives for AT Protocol - the types everyone reimplements (`Tid`, `Did`, `Handle`, `Nsid`, `Rkey`, `AtUri`). the scope grew based on real usage. +zat started as a small set of string primitives for AT Protocol — the types everyone reimplements (`Tid`, `Did`, `Handle`, `Nsid`, `Rkey`, `AtUri`). the scope grew based on real usage. -## history +## current status + +**v0.3.0-alpha** — zig 0.16, `std.Io` throughout. + +the v0.3.0 migration replaced all networking and concurrency primitives with zig 0.16's [`std.Io`](https://ziglang.org/documentation/master/std/#std.Io) interface. the API change is mechanical: every networking type takes `io: std.Io` as its first parameter. streaming clients moved from `connect()` + `next()` loops to `subscribe(handler)` with automatic reconnection, backoff, and host rotation. -**initial scope** - string primitives with parsing and validation. the philosophy: primitives not frameworks, layered design, zig idioms, minimal scope. +the library is stable and tested. the alpha tag reflects that downstream consumers (zlay, labelz, pollz) are still validating in production. + +## history **what grew from usage:** -- DID/handle resolution — real projects needed it, so `DidResolver`, `DidDocument`, `HandleResolver` got added -- XRPC client and JSON helpers — same story +- string primitives with parsing and validation — the initial scope +- DID/handle resolution — `DidResolver`, `DidDocument`, `HandleResolver` +- XRPC client and JSON helpers - JWT verification for service auth -- jetstream client — typed JSON event stream with reconnection (0.1.3) -- firehose client — raw CBOR event stream, DAG-CBOR codec, CAR codec, CID creation (0.1.4) -- MST, ECDSA signing, `did:key` construction, multibase encoding (0.1.9) -- full repo verification — end-to-end trust chain from handle to MST root CID match (0.2.0) -- CID hash verification in CAR parser (0.2.1), size limits (0.2.2) +- jetstream client — typed JSON event stream with reconnection (v0.1.3) +- firehose client — raw CBOR event stream, DAG-CBOR codec, CAR codec, CID creation (v0.1.4) +- MST, ECDSA signing, `did:key` construction, multibase encoding (v0.1.9) +- full repo verification — end-to-end trust chain from handle to MST root CID match (v0.2.0) +- CID hash verification in CAR parser (v0.2.1), size limits (v0.2.2) +- OAuth 2.1 DPoP client (v0.2.14) +- configurable keep-alive, transport options (v0.2.12–v0.2.18) +- `std.Io` migration, `subscribe(handler)` streaming API (v0.3.0-alpha) + +this pattern — start minimal, expand based on real pain — continues. -this pattern - start minimal, expand based on real pain - continues. +## what's next -## now +the library covers the full AT Protocol verification pipeline: identity resolution, repo parsing, signature verification, and MST validation. benchmarked against Go (indigo) and Rust (rsky) in [atproto-bench](https://tangled.org/zzstoatzz.io/atproto-bench). -the library covers the full AT Protocol verification pipeline: identity resolution, repo parsing, signature verification, and MST validation. benchmarked against Go (indigo) and Rust (rsky) in [atproto-bench](https://tangled.sh/@zzstoatzz.io/atproto-bench). +near-term: +- promote to v0.3.0-beta once production consumers stabilize +- cut v0.3.0 when the API surface is confirmed what's missing will show up when people build things. until then, no speculative features. @@ -28,10 +42,10 @@ what's missing will show up when people build things. until then, no speculative these stay out of scope unless real demand emerges: -- lexicon codegen - probably a separate project -- higher-level clients/frameworks - too opinionated -- token refresh/session management - app-specific -- feed generator scaffolding - each feed is unique +- lexicon codegen — probably a separate project +- higher-level clients/frameworks — too opinionated +- token refresh/session management — app-specific +- feed generator scaffolding — each feed is unique ## non-goals diff --git a/site/app.js b/site/app.js index 87cd89f..9345ef8 100644 --- a/site/app.js +++ b/site/app.js @@ -1,23 +1,24 @@ const navEl = document.getElementById("nav"); const contentEl = document.getElementById("content"); -const menuToggle = document.querySelector(".menu-toggle"); -const sidebar = document.querySelector(".sidebar"); -const overlay = document.querySelector(".overlay"); - -function toggleMenu(open) { - const isOpen = open ?? !sidebar.classList.contains("open"); - sidebar.classList.toggle("open", isOpen); - overlay?.classList.toggle("open", isOpen); - menuToggle?.setAttribute("aria-expanded", isOpen); - document.body.style.overflow = isOpen ? "hidden" : ""; +const themeToggle = document.querySelector(".theme-toggle"); + +// Theme toggle +function getPreferredTheme() { + const stored = localStorage.getItem("theme"); + if (stored) return stored; + return window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"; +} + +function applyTheme(theme) { + document.documentElement.setAttribute("data-theme", theme); + localStorage.setItem("theme", theme); } -menuToggle?.addEventListener("click", () => toggleMenu()); -overlay?.addEventListener("click", () => toggleMenu(false)); +applyTheme(getPreferredTheme()); -// Close menu when nav link clicked (mobile) -navEl?.addEventListener("click", (e) => { - if (e.target.closest("a")) toggleMenu(false); +themeToggle?.addEventListener("click", () => { + const current = document.documentElement.getAttribute("data-theme") || getPreferredTheme(); + applyTheme(current === "dark" ? "light" : "dark"); }); const buildId = new URL(import.meta.url).searchParams.get("v") || ""; @@ -110,7 +111,6 @@ function installContentLinkHandler() { async function main() { // SPA fallback: convert pathname to hash route so deep links work - // e.g. /roadmap -> #roadmap.md, /devlog/001 -> #devlog/001.md if (location.pathname !== "/" && !location.hash) { const path = location.pathname.replace(/^\/+/, ""); if (path) { @@ -144,7 +144,7 @@ async function main() { renderNav(pages, activePath); if (!activePath) { - contentEl.innerHTML = `
No docs yet. Add markdown files under zat/docs/ and push to main.
No docs yet.
`; return; } @@ -153,7 +153,6 @@ async function main() { const html = globalThis.marked.parse(md); contentEl.innerHTML = html; - // Update current marker after navigation re-render. for (const a of navEl.querySelectorAll("a")) { const href = decodeURIComponent((a.getAttribute("href") || "").slice(1)); a.toggleAttribute("aria-current", normalizeDocPath(href) === activePath); diff --git a/site/index.html b/site/index.html index 6d52dec..478ddbf 100644 --- a/site/index.html +++ b/site/index.html @@ -5,43 +5,33 @@