"use client"; import { useState } from "react"; import { LoaderCircle, Download } from "lucide-react"; import { downloadTsv } from "@/lib/export-utils"; type PrimerPair = { pair_index: number; left_seq: string; left_tm: number; left_gc: number; left_pos: number; left_len: number; right_seq: string; right_tm: number; right_gc: number; right_pos: number; right_len: number; product_size: number; penalty: number; }; export function PrimerDesigner() { const [sequence, setSequence] = useState(""); const [productMin, setProductMin] = useState(100); const [productMax, setProductMax] = useState(500); const [optTm, setOptTm] = useState(60); const [pairs, setPairs] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [selectedPair, setSelectedPair] = useState(null); async function design() { setError(null); setPairs([]); setLoading(true); try { const res = await fetch("/api/backend/api/primers/design", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sequence, product_size_min: productMin, product_size_max: productMax, opt_tm: optTm }), }); if (!res.ok) { const d = await res.json(); const msg = Array.isArray(d.detail) ? d.detail.map((e: any) => e.msg || String(e)).join("; ") : typeof d.detail === "string" ? d.detail : JSON.stringify(d.detail); throw new Error(msg || res.statusText); } setPairs(await res.json()); setSelectedPair(0); } catch (e: any) { setError(e.message); } finally { setLoading(false); } } function copyPair(p: PrimerPair) { navigator.clipboard.writeText(`Forward: ${p.left_seq}\nReverse: ${p.right_seq}`); } const highlightSequence = (seq: string, pair: PrimerPair | null) => { if (!pair) return seq; const lEnd = pair.left_pos + pair.left_len; const rStart = pair.right_pos - pair.right_len + 1; return ( <> {seq.slice(0, pair.left_pos)} {seq.slice(pair.left_pos, lEnd)} {seq.slice(lEnd, rStart)} {seq.slice(rStart, pair.right_pos + 1)} {seq.slice(pair.right_pos + 1)} ); }; return (

Primer Design (Primer3)

Input a DNA/CDS sequence. Primer3 runs locally — instant results, no rate limits.