spb/social-runtime-crawler
Public
TypeScript 91.8%
HTML 3.2%
JavaScript 3%
SQL 1.4%
CSS 0.7%
1"use client";23import Link from "next/link";4import { useApi } from "@/lib/api";5import { Bar, Panel, Skeleton, Tag } from "@/components/ui";67interface P {8 platform: string;9 learned: boolean;10 adapter: boolean;11 confidence: number;12 page_types: number;13 entity_types: number;14 navigation_actions: number;15 network_schemas: number;16 media_patterns: number;17 sessions: number;18 has_manifest: boolean;19 profiles: { alias: string; has_state: boolean }[];20}2122const PHASE: Record<string, string> = { youtube: "Phase 1", reddit: "Phase 1", facebook: "Phase 2 · adapter", instagram: "Phase 2", tiktok: "Phase 3", x: "Phase 3", linkedin: "Phase 4", threads: "Phase 4" };2324export default function Platforms() {25 const { data, loading } = useApi<P[]>("/platforms", { refreshMs: 15_000 });26 return (27 <div className="space-y-4">28 <div>29 <h1 className="text-2xl font-semibold tracking-tight">Connectors</h1>30 <p className="text-sm text-fg-2 max-w-2xl">A connector is learned state, not hand-written truth: page types, response schemas, action → observation patterns and media patterns accumulated across sessions, compiled into a manifest once confidence is sufficient.</p>31 </div>32 {loading ? (33 <Skeleton rows={6} />34 ) : (35 <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">36 {data?.map((p) => (37 <Link key={p.platform} href={`/platforms/${p.platform}`} className="block">38 <Panel className="h-full hover:border-cyan/40 transition">39 <div className="flex items-center gap-2">40 <span className="text-lg font-semibold capitalize">{p.platform}</span>41 <span className="ml-auto text-[10px] uppercase tracking-widest text-dim">{PHASE[p.platform]}</span>42 </div>43 <div className="flex gap-1 mt-2 flex-wrap">44 <Tag color={p.adapter ? "#43e69a" : "#6b7a8c"}>{p.adapter ? "adapter" : "no adapter"}</Tag>45 <Tag color={p.learned ? "#38e1ff" : "#6b7a8c"}>{p.learned ? "learned" : "unlearned"}</Tag>46 {p.has_manifest && <Tag color="#9d7bff">compiled</Tag>}47 {p.profiles.map((pr) => (48 <Tag key={pr.alias} color={pr.has_state ? "#ffb347" : "#6b7a8c"}>49 profile {pr.alias}50 </Tag>51 ))}52 </div>53 <div className="mt-4">54 <div className="flex justify-between text-xs text-dim mb-1">55 <span>connector confidence</span>56 <span className="mono text-fg">{p.confidence}%</span>57 </div>58 <Bar value={p.confidence / 100} color={p.confidence >= 70 ? "#43e69a" : p.confidence >= 40 ? "#38e1ff" : "#ffb347"} />59 </div>60 <dl className="grid grid-cols-2 gap-x-3 gap-y-1 mt-4 text-xs">61 {[62 ["page types", p.page_types],63 ["entity types", p.entity_types],64 ["navigation actions", p.navigation_actions],65 ["network schemas", p.network_schemas],66 ["media patterns", p.media_patterns],67 ["sessions", p.sessions],68 ].map(([k, v]) => (69 <div key={k as string} className="flex justify-between border-b border-line/60 py-1">70 <dt className="text-dim">{k as string}</dt>71 <dd className="mono">{v as number}</dd>72 </div>73 ))}74 </dl>75 </Panel>76 </Link>77 ))}78 </div>79 )}80 </div>81 );82}83