diff --git a/sx/sx/plans/sx-pub.sx b/sx/sx/plans/sx-pub.sx index 76c561d8..ffefae7c 100644 --- a/sx/sx/plans/sx-pub.sx +++ b/sx/sx/plans/sx-pub.sx @@ -244,14 +244,131 @@ (~docs/section :title "Implementation Phases" :id "phases" (div :class "space-y-4" (div :class "rounded border border-emerald-200 bg-emerald-50 p-4" - (h4 :class "font-semibold text-emerald-800" "Phase 1: Foundation") + (h4 :class "font-semibold text-emerald-800" "Phase 1: Foundation ✓") (p :class "text-emerald-700 text-sm" "DB schema, async IPFS client, actor endpoint, webfinger, " (code "/pub/actor") " returns SX actor document.")) (div :class "rounded border border-sky-200 bg-sky-50 p-4" - (h4 :class "font-semibold text-sky-800" "Phase 2: Publishing") + (h4 :class "font-semibold text-sky-800" "Phase 2: Publishing ✓") (p :class "text-sky-700 text-sm" "Pin to IPFS, path→CID index, collection browsing. Publish the actual SX spec files as the first content.")) (div :class "rounded border border-violet-200 bg-violet-50 p-4" - (h4 :class "font-semibold text-violet-800" "Phase 3: Federation") + (h4 :class "font-semibold text-violet-800" "Phase 3: Federation ✓") (p :class "text-violet-700 text-sm" "Inbox/outbox, follow/accept, HTTP signature verification, activity delivery, content mirroring.")) (div :class "rounded border border-amber-200 bg-amber-50 p-4" - (h4 :class "font-semibold text-amber-800" "Phase 4: Anchoring") - (p :class "text-amber-700 text-sm" "Merkle trees, OpenTimestamps, Bitcoin proof, provenance UI.")))))) + (h4 :class "font-semibold text-amber-800" "Phase 4: Anchoring ✓") + (p :class "text-amber-700 text-sm" "Merkle trees, OpenTimestamps, Bitcoin proof, provenance verification.")))) + + ;; ----------------------------------------------------------------------- + ;; Live Dashboard + ;; ----------------------------------------------------------------------- + + (~docs/section :title "Live Dashboard" :id "dashboard" + (p "Live data from the sx-pub API — server-rendered from the same endpoints.") + + ;; --- Status --- + (~docs/subsection :title "Server Status" + (let ((status (helper "pub-status-data"))) + (div :class "grid grid-cols-2 sm:grid-cols-4 gap-3" + (div :class "rounded border border-stone-200 p-3 text-center" + (p :class "text-xs text-stone-400 uppercase" "DB") + (p :class "font-semibold text-sm" (get status "db"))) + (div :class "rounded border border-stone-200 p-3 text-center" + (p :class "text-xs text-stone-400 uppercase" "IPFS") + (p :class "font-semibold text-sm" (get status "ipfs"))) + (div :class "rounded border border-stone-200 p-3 text-center" + (p :class "text-xs text-stone-400 uppercase" "Actor") + (p :class "font-semibold text-sm" (get status "actor"))) + (div :class "rounded border border-stone-200 p-3 text-center" + (p :class "text-xs text-stone-400 uppercase" "Domain") + (p :class "font-semibold text-sm" (or (get status "domain") "—")))))) + + ;; --- Actor --- + (~docs/subsection :title "Actor Identity" + (let ((actor (helper "pub-actor-data"))) + (div :class "rounded border border-stone-200 bg-stone-50 p-4 space-y-2" + (div :class "flex items-center gap-3" + (div :class "w-10 h-10 rounded-full bg-violet-100 flex items-center justify-center text-violet-700 font-bold" "sx") + (div + (p :class "font-semibold" (get actor "display-name")) + (p :class "text-sm text-stone-500" (str "@" (get actor "preferred-username") "@" (get actor "domain"))))) + (p :class "text-sm text-stone-600" (get actor "summary")) + (details :class "text-xs" + (summary :class "text-stone-400 cursor-pointer" "Public key") + (pre :class "mt-2 bg-stone-100 rounded p-2 text-xs overflow-x-auto" (get actor "public-key-pem")))))) + + ;; --- Collections --- + (~docs/subsection :title "Collections" + (let ((collections (helper "pub-collections-data"))) + (div :class "grid gap-3" + (map (fn (c) + (div :class "rounded border border-stone-200 p-4 hover:border-violet-300 transition-colors" + (div :class "flex items-center justify-between" + (div + (h4 :class "font-semibold text-stone-800" (get c "name")) + (p :class "text-sm text-stone-500" (get c "description"))) + (span :class "text-xs font-mono text-violet-600 bg-violet-50 px-2 py-1 rounded" + (str "/pub/" (get c "slug")))))) + collections)))) + + ;; --- Published Documents --- + (~docs/subsection :title "Published Documents" + (let ((specs (helper "pub-collection-items" "core-specs"))) + (when (not (get specs "error")) + (div :class "space-y-2" + (h4 :class "text-sm font-semibold text-stone-500 uppercase tracking-wide" + (get specs "name")) + (map (fn (d) + (when (!= (get d "slug") "") + (div :class "rounded border border-stone-200 p-3 flex items-center justify-between" + (div + (p :class "font-medium text-stone-800" (get d "title")) + (p :class "text-xs text-stone-400" (get d "summary"))) + (div :class "text-right" + (p :class "text-xs font-mono text-emerald-600 truncate max-w-48" (get d "cid")) + (p :class "text-xs text-stone-400" (str (get d "size") " bytes")))))) + (get specs "items")))))) + + ;; --- Outbox --- + (~docs/subsection :title "Recent Activity" + (let ((outbox (helper "pub-outbox-data" ""))) + (if (= (get outbox "total") 0) + (p :class "text-sm text-stone-400 italic" "No activities yet.") + (div :class "space-y-2" + (p :class "text-xs text-stone-400" (str (get outbox "total") " total activities")) + (map (fn (a) + (when (!= (get a "type") "") + (div :class "rounded border border-stone-200 p-3 flex items-center gap-3" + (span :class "text-xs font-semibold text-white bg-violet-500 px-2 py-0.5 rounded" + (get a "type")) + (span :class "text-xs text-stone-500" (get a "published")) + (when (!= (get a "cid") "") + (span :class "text-xs font-mono text-emerald-600 truncate max-w-48" + (get a "cid")))))) + (get outbox "items")))))) + + ;; --- Followers --- + (~docs/subsection :title "Followers" + (let ((followers (helper "pub-followers-data"))) + (if (empty? followers) + (p :class "text-sm text-stone-400 italic" "No followers yet.") + (div :class "space-y-2" + (map (fn (f) + (when (!= (get f "acct") "") + (div :class "rounded border border-stone-200 p-3 flex items-center gap-2" + (div :class "w-8 h-8 rounded-full bg-sky-100 flex items-center justify-center text-sky-700 text-xs font-bold" "F") + (p :class "text-sm font-mono text-stone-600 truncate" (get f "acct"))))) + followers)))) + + ;; --- API Endpoints --- + (~docs/subsection :title "Try the API" + (p :class "text-sm text-stone-600 mb-2" "All endpoints return " (code "text/sx") ". Try them directly:") + (div :class "grid grid-cols-2 sm:grid-cols-3 gap-2" + (map (fn (endpoint) + (a :href (get endpoint "href") + :class "block rounded border border-stone-200 p-2 text-center hover:border-violet-300 hover:bg-violet-50 transition-colors text-xs font-mono text-stone-600" + (get endpoint "label"))) + (list + {"label" "GET /pub/actor" "href" "/pub/actor"} + {"label" "GET /pub/status" "href" "/pub/status"} + {"label" "GET /pub/collections" "href" "/pub/collections"} + {"label" "GET /pub/outbox" "href" "/pub/outbox"} + {"label" "GET /pub/followers" "href" "/pub/followers"} + {"label" "GET /pub/following" "href" "/pub/following"}))))))))