"use client"; import * as React from "react"; import Link from "next/link"; import type { FetchRequestInput, FetchResponseBody } from "@fetcha/core/client"; import { ArrowUpRight, Download, Loader2, MonitorSmartphone, PlayCircle, ShieldAlert } from "lucide-react"; import type { PlaygroundError } from "@/actions/playground"; import { Alert } from "@/components/ui/alert"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { CodeBlock } from "@/components/ui/code-block"; import { CopyButton } from "@/components/ui/copy-button"; import { EmptyState } from "@/components/ui/empty-state"; import { Skeleton } from "@/components/ui/skeleton"; import { Table, TableBody, TableCell, TableEmpty, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import type { CodegenLang } from "@/lib/codegen"; import { formatBytes, formatMs } from "@/lib/format"; import { cn } from "@/lib/utils"; import { CodePanel } from "./code-panel"; import { TimingBars } from "./timing-bars"; import { isHtmlContentType, isJsonContentType, statusVariant, stripHtml, truncateForDisplay, type NetworkClass, type PlaygroundKey } from "./types"; export type ResultPhase = "idle" | "running" | "success" | "error"; export interface ResultsPanelProps { phase: ResultPhase; result: FetchResponseBody | null; error: PlaygroundError | null; elapsedMs: number; request: FetchRequestInput; requestNetwork: NetworkClass; keys: PlaygroundKey[]; baseUrl: string; codeLang: CodegenLang; onCodeLangChange: (l: CodegenLang) => void; keyId: string; onKeyChange: (id: string) => void; onSaveCurl: () => void; } export function ResultsPanel(p: ResultsPanelProps) { const code = ; if (p.phase === "idle") { return (
{code}
); } if (p.phase === "running") { return (

Routing your request…

{p.requestNetwork === "auto" ? "Auto mode picks the best network for this domain, then retries with a new IP or another network if the target blocks." : `Using the ${p.requestNetwork} network.`}

{formatMs(p.elapsedMs)}
); } if (p.phase === "error" || !p.result) { const e = p.error ?? { code: "INTERNAL_ERROR", message: "Unknown error.", requestId: null }; const issues = Array.isArray(e.details?.issues) ? (e.details!.issues as Array<{ path?: string; message?: string }>) : []; return (
{e.code} · {e.message} } >
{issues.length ? (
    {issues.map((i, idx) => (
  • {i.path ? {i.path} : null} {i.path ? ": " : ""} {i.message}
  • ))}
) : null}
{e.requestId ? ( Request {e.requestId} ) : null} About {e.code} {e.requestId ? ( View in Requests ) : null}

Took {formatMs(p.elapsedMs)}.

{code}
); } return ; } function Panel({ title, children }: { title: string; children: React.ReactNode }) { return (

{title}

{children}
); } // --------------------------------------------------------------------------- // Success // --------------------------------------------------------------------------- function SuccessView({ result: r, request, elapsedMs, onSaveCurl, code }: ResultsPanelProps & { result: FetchResponseBody; code: React.ReactNode }) { const method = request.method ?? "GET"; const isHead = method === "HEAD"; const content = r.content ?? ""; const hasContent = content.length > 0; const html = isHtmlContentType(r.content_type); const headerEntries = Object.entries(r.headers ?? {}); const cookies = r.cookies ?? []; const meta = r.metadata; const [tab, setTab] = React.useState("preview"); const jsonValue = React.useMemo<{ ok: true; value: unknown } | { ok: false }>(() => { if (r.json !== undefined && r.json !== null) return { ok: true, value: r.json }; if (!hasContent) return { ok: false }; if (!isJsonContentType(r.content_type) && !/^\s*[[{]/.test(content)) return { ok: false }; try { return { ok: true, value: JSON.parse(content) }; } catch { return { ok: false }; } }, [r.json, r.content_type, content, hasContent]); const textValue = React.useMemo(() => { if (typeof r.text === "string") return r.text; if (!hasContent) return ""; return html ? stripHtml(content) : content; }, [r.text, content, hasContent, html]); const debugJson = React.useMemo(() => { const clone: Record = { ...r }; if (typeof r.content === "string") clone.content = `[omitted: ${r.content.length.toLocaleString("en-US")} chars — see the ${html ? "HTML" : "Body"} tab]`; if (typeof r.text === "string") clone.text = `[omitted: ${r.text.length.toLocaleString("en-US")} chars — see the Text tab]`; if (typeof r.markdown === "string") clone.markdown = `[omitted: ${r.markdown.length.toLocaleString("en-US")} chars — see the Markdown tab]`; if (typeof r.screenshot === "string") clone.screenshot = `[omitted: ${r.screenshot.length.toLocaleString("en-US")} base64 chars — see the Screenshot tab]`; if (Array.isArray(r.links)) clone.links = `[${r.links.length.toLocaleString("en-US")} links — see the Links tab]`; if (r.json !== undefined) clone.json = "[see the JSON tab]"; return JSON.stringify(clone, null, 2); }, [r, html]); const bodyLabel = html ? "HTML" : "Body"; const hasMarkdown = typeof r.markdown === "string"; const links = Array.isArray(r.links) ? r.links : null; const hasScreenshot = typeof r.screenshot === "string" && r.screenshot.length > 0; const mode = meta.mode ?? "http"; return (
{/* Header */}
{r.status || "—"} {r.success ? ( success ) : ( blocked )} {mode === "browser" ? : null} {mode === "browser" ? "Browser" : "HTTP"} {meta.cached ? cached : null} ·
{r.request_id} Details {r.final_url !== r.url ? ( <> {r.url} → {r.final_url} ) : ( r.url )} {r.content_type ? {r.content_type} : null}
{!r.success ? (

The target answered with HTTP {r.status} on every route Fetcha tried ({meta.attempts} attempt{meta.attempts === 1 ? "" : "s"}). Try browser rendering, a residential network, a different country or a session.

) : null}
{r.page ? : null} {/* Tabs */} Preview {bodyLabel} Text {hasMarkdown ? Markdown : null} JSON {links ? ( Links ) : null} {hasScreenshot ? Screenshot : null} Headers Cookies Network Timing Debug Code {!hasContent ? ( ) : html ? (