A quick vibe-coded site to test response times of PLC.directory mirrors (over 3 attempts)
1import { useState } from "react";
2import { Button } from "@/components/ui/button";
3import { Input } from "@/components/ui/input";
4import { BenchmarkTable } from "@/components/BenchmarkTable";
5import { mirrors } from "@/config/mirrors";
6import { benchmarkAllMirrors } from "@/utils/benchmark";
7import { BenchmarkResult } from "@/types/benchmark";
8import { toast } from "sonner";
9import { Activity, Zap } from "lucide-react";
10
11const Index = () => {
12 const [did, setDid] = useState("");
13 const [loading, setLoading] = useState(false);
14 const [results, setResults] = useState<BenchmarkResult[]>([]);
15
16 const resolveHandle = async (handle: string): Promise<string | null> => {
17 try {
18 const response = await fetch(
19 `https://quickdid.smokesignal.tools/xrpc/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(handle)}`
20 );
21 const data = await response.json();
22
23 if (data.error) {
24 toast.error(`Unable to resolve handle: ${data.message || "Invalid handle"}`);
25 return null;
26 }
27
28 return data.did;
29 } catch (error) {
30 toast.error("Failed to resolve handle");
31 console.error(error);
32 return null;
33 }
34 };
35
36 const handleBenchmark = async () => {
37 if (!did.trim()) {
38 toast.error("Please enter a DID or handle");
39 return;
40 }
41
42 setLoading(true);
43 setResults([]);
44
45 try {
46 let resolvedDid = did.trim();
47
48 // If input doesn't start with "did:plc", treat it as a handle
49 if (!resolvedDid.startsWith("did:plc")) {
50 const resolved = await resolveHandle(resolvedDid);
51 if (!resolved) {
52 setLoading(false);
53 return;
54 }
55 resolvedDid = resolved;
56 toast.success(`Resolved to ${resolvedDid}`);
57 }
58
59 const benchmarkResults = await benchmarkAllMirrors(mirrors, resolvedDid);
60 setResults(benchmarkResults);
61
62 const successCount = benchmarkResults.filter(r => r.status === "success").length;
63 toast.success(`Benchmark complete! ${successCount}/${benchmarkResults.length} mirrors responded`);
64 } catch (error) {
65 toast.error("Failed to run benchmark");
66 console.error(error);
67 } finally {
68 setLoading(false);
69 }
70 };
71
72 const handleKeyPress = (e: React.KeyboardEvent) => {
73 if (e.key === "Enter" && !loading) {
74 handleBenchmark();
75 }
76 };
77
78 return (
79 <div className="min-h-screen bg-background py-8 px-4">
80 <div className="max-w-6xl mx-auto space-y-8">
81 {/* Header */}
82 <header className="text-center space-y-3">
83 <div className="flex items-center justify-center gap-2 text-primary">
84 <Activity className="w-8 h-8" />
85 <h1 className="text-4xl font-bold">PLC Mirror Benchmark</h1>
86 </div>
87 <p className="text-muted-foreground text-lg max-w-2xl mx-auto">
88 Test and compare response times across different PLC.directory mirrors
89 </p>
90 </header>
91
92 {/* Input Section */}
93 <div className="bg-card border border-border rounded-lg p-6 shadow-sm">
94 <div className="space-y-4">
95 <div>
96 <label htmlFor="did-input" className="block text-sm font-medium mb-2">
97 ATProto DID or Handle
98 </label>
99 <div className="flex gap-3">
100 <Input
101 id="did-input"
102 type="text"
103 placeholder="did:plc:example123... or handle.bsky.social"
104 value={did}
105 onChange={(e) => setDid(e.target.value)}
106 onKeyPress={handleKeyPress}
107 className="flex-1 font-mono"
108 disabled={loading}
109 />
110 <Button
111 onClick={handleBenchmark}
112 disabled={loading || !did.trim()}
113 className="gap-2"
114 >
115 <Zap className="w-4 h-4" />
116 {loading ? "Benchmarking..." : "Run Benchmark"}
117 </Button>
118 </div>
119 </div>
120 <div className="text-sm text-muted-foreground">
121 Testing {mirrors.length} mirrors with 3 attempts each
122 </div>
123 </div>
124 </div>
125
126 {/* Results Section */}
127 {(results.length > 0 || loading) && (
128 <div className="space-y-4">
129 <h2 className="text-2xl font-semibold">Results</h2>
130 {loading ? (
131 <div className="bg-card border border-border rounded-lg p-12 text-center">
132 <div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-primary mb-4"></div>
133 <p className="text-muted-foreground">Benchmarking mirrors...</p>
134 </div>
135 ) : (
136 <BenchmarkTable results={results} />
137 )}
138 </div>
139 )}
140
141 {/* Info Section */}
142 {results.length === 0 && !loading && (
143 <div className="bg-muted/30 border border-border rounded-lg p-6">
144 <h3 className="font-semibold mb-2">How to use:</h3>
145 <ol className="list-decimal list-inside space-y-1 text-sm text-muted-foreground">
146 <li>Enter an ATProto DID or handle (e.g., did:plc:z72i7hdynmk6r22z27h6tvur or handle.bsky.social)</li>
147 <li>Click "Run Benchmark" or press Enter</li>
148 <li>View response times and status for each mirror</li>
149 </ol>
150 <div className="mt-4 pt-4 border-t border-border">
151 <p className="text-sm text-muted-foreground">
152 Mirrors can be configured in <code className="bg-background px-2 py-1 rounded text-mono">src/config/mirrors.ts</code>
153 </p>
154 </div>
155 </div>
156 )}
157 </div>
158 </div>
159 );
160};
161
162export default Index;