"use client"; /** * QuickLaunch — homepage form to fire an investigation in 1 click without * opening the chat. * * Detective dropdown picks the kind; the form swaps the input label based * on what that kind needs (topic, question, hypothesis_id, person_id). * Submit POSTs to /api/bureau/launch which inserts the job + returns the * job_id; the UI then routes to /jobs/[id]. */ import { useState } from "react"; import { useRouter } from "next/navigation"; import { ArrowUpRight } from "lucide-react"; interface DetectiveOption { slug: string; label: string; kind: string; inputLabel: string; placeholder: string; field: "question" | "topic" | "hypothesis_id" | "person_id"; tone: string; } const OPTIONS: DetectiveOption[] = [ { slug: "holmes", label: "Holmes — hypothesis tournament", kind: "hypothesis_tournament", inputLabel: "Question (one sentence, declarative)", placeholder: "Were the green fireballs natural or unidentified?", field: "question", tone: "text-[#7fdbff] border-[#7fdbff]" }, { slug: "dupin", label: "Dupin — contradiction scan", kind: "contradiction_scan", inputLabel: "Topic (short noun-phrase)", placeholder: "color of the green fireballs", field: "topic", tone: "text-[#ff8a4d] border-[#ff8a4d]" }, { slug: "taleb", label: "Taleb — outlier hunt", kind: "outlier_scan", inputLabel: "Topic", placeholder: "anomalous frequency of UAP at nuclear sites", field: "topic", tone: "text-[#ffd23f] border-[#ffd23f]" }, { slug: "schneier", label: "Schneier — red-team a hypothesis", kind: "red_team_review", inputLabel: "Hypothesis ID (H-NNNN)", placeholder: "H-0003", field: "hypothesis_id", tone: "text-[#ff3344] border-[#ff3344]" }, { slug: "tetlock", label: "Tetlock — recalibrate a hypothesis", kind: "calibrate_hypothesis", inputLabel: "Hypothesis ID (H-NNNN)", placeholder: "H-0003", field: "hypothesis_id", tone: "text-[#26d4cc] border-[#26d4cc]" }, { slug: "poirot", label: "Poirot — witness analysis", kind: "witness_analysis", inputLabel: "Person ID (kebab-case)", placeholder: "donald-keyhoe", field: "person_id", tone: "text-[#9b5de5] border-[#9b5de5]" }, { slug: "case-writer", label: "Case-Writer — assemble narrative", kind: "case_report", inputLabel: "Topic to assemble", placeholder: "green fireballs", field: "topic", tone: "text-[#e0c080] border-[#e0c080]" }, ]; export function QuickLaunch() { const router = useRouter(); const [opt, setOpt] = useState(OPTIONS[0]); const [value, setValue] = useState(""); const [pending, setPending] = useState(false); const [error, setError] = useState(null); function pickDetective(slug: string) { const next = OPTIONS.find((o) => o.slug === slug); if (next) { setOpt(next); setValue(""); setError(null); } } async function submit(e: React.FormEvent) { e.preventDefault(); if (!value.trim()) return; setPending(true); setError(null); try { const payload: Record = { kind: opt.kind }; payload[opt.field] = value.trim(); const r = await fetch("/api/bureau/launch", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); const data = (await r.json()) as { job_id?: string; error?: string; message?: string }; if (!r.ok || !data.job_id) { setError(data.error || data.message || `HTTP ${r.status}`); setPending(false); return; } router.push(`/jobs/${data.job_id}`); } catch (e) { setError((e as Error).message); setPending(false); } } return (
⚡ Launch an investigation
setValue(e.target.value)} placeholder={opt.placeholder} aria-label={opt.inputLabel} className="bg-[#060a13] border border-[rgba(127,219,255,0.18)] rounded px-3 py-2 text-[12px] text-[#e7ecf3] placeholder:text-[#5a6678] font-mono" />
{opt.inputLabel} {error && · {error}}
); }