SPB Git forge
7commits 1branches 0releases
229.0 KBsize
maindefault branch
12 days agolast push
TypeScript 91.8% HTML 3.2% JavaScript 3% SQL 1.4% CSS 0.7%
3.7 KB · 83 lines tsx
Raw Blame History
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