TypeScript 97.5%
SQL 1.4%
Python 0.8%
1"use client";2import * as React from "react";3import Link from "next/link";4import type { FetchRequestInput } from "@fetcha/core/client";5import { KeyRound } from "lucide-react";6import { CodeBlock } from "@/components/ui/code-block";7import { CopyButton } from "@/components/ui/copy-button";8import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";9import { CODEGEN_LANGS, codegenInfo, generateCode, type CodegenLang } from "@/lib/codegen";10import { cn } from "@/lib/utils";11import type { PlaygroundKey } from "./types";1213export function keyPlaceholder(k: PlaygroundKey | undefined): string {14 if (!k) return "YOUR_API_KEY";15 const prefix = k.keyPrefix.endsWith("_") ? k.keyPrefix : `${k.keyPrefix}_`;16 return `${prefix}••••${k.last4}`;17}1819/** Language tabs + API key selector + generated snippet for the current request. */20export function CodePanel({ request, keys, baseUrl, lang, onLangChange, keyId, onKeyChange, className }: { request: FetchRequestInput; keys: PlaygroundKey[]; baseUrl: string; lang: CodegenLang; onLangChange: (l: CodegenLang) => void; keyId: string; onKeyChange: (id: string) => void; className?: string }) {21 const key = keys.find((k) => k.id === keyId) ?? keys[0];22 const info = codegenInfo(lang);23 const code = React.useMemo(() => generateCode(lang, request, { apiKeyPlaceholder: keyPlaceholder(key), baseUrl }), [lang, request, key, baseUrl]);2425 return (26 <div className={cn("grid gap-3", className)}>27 <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">28 <div role="tablist" aria-label="Language" className="flex items-center gap-1 overflow-x-auto no-scrollbar -mx-1 px-1">29 {CODEGEN_LANGS.map((l) => (30 <button31 key={l.id}32 type="button"33 role="tab"34 aria-selected={l.id === lang}35 onClick={() => onLangChange(l.id)}36 className={cn("h-7 shrink-0 rounded-[5px] px-2.5 text-[12.5px] font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/30", l.id === lang ? "bg-fg text-bg" : "text-fg-muted hover:bg-bg-muted hover:text-fg")}37 >38 {l.label}39 </button>40 ))}41 </div>42 <div className="flex items-center gap-2 sm:shrink-0">43 {keys.length ? (44 <Select value={key?.id ?? ""} onValueChange={onKeyChange}>45 <SelectTrigger className="h-8 w-full sm:w-[220px] text-[12.5px]" aria-label="API key used in the snippet">46 <KeyRound className="size-3.5 shrink-0 text-fg-subtle" aria-hidden />47 <SelectValue placeholder="API key" />48 </SelectTrigger>49 <SelectContent>50 {keys.map((k) => (51 <SelectItem key={k.id} value={k.id}>52 <span className="font-mono text-[12px]">{keyPlaceholder(k)}</span>53 <span className="ml-2 text-fg-muted">{k.name}</span>54 </SelectItem>55 ))}56 </SelectContent>57 </Select>58 ) : null}59 <CopyButton value={code} label="Copy" variant="outline" size="sm" className="h-8 text-fg" />60 </div>61 </div>62 <CodeBlock code={code} lang={info.codeLang} title={`fetch.${info.ext}`} maxHeight={480} copy={false} />63 <p className="text-xs text-fg-subtle leading-relaxed">64 {keys.length ? (65 <>66 <span className="font-mono">{keyPlaceholder(key)}</span> is a placeholder for <span className="font-medium text-fg-muted">{key?.name}</span>. Replace it with your key — keys are only shown once, at creation.{" "}67 </>68 ) : (69 <>70 This project has no API key yet.{" "}71 <Link href="/dashboard/api-keys" className="text-accent underline-offset-4 hover:underline">72 Create one73 </Link>{" "}74 and replace <span className="font-mono">YOUR_API_KEY</span>.{" "}75 </>76 )}77 {lang === "typescript" || lang === "python-sdk" ? "SDKs are not published yet: install from source (repo packages/sdk and sdk-python)." : null}78 </p>79 </div>80 );81}82