"use client"; import * as React from "react"; import { useRouter } from "next/navigation"; import { Copy, MessageSquarePlus } from "lucide-react"; import { ResponsiveDialog } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; import { Input, Textarea, Field } from "@/components/ui/input"; import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@/components/ui/select"; import { toast } from "@/components/ui/toast"; import { api } from "@/lib/client/api"; import { errorMessage } from "@/lib/client/humanize"; import { useCopy } from "@/lib/client/hooks"; import { labelFor, renderTemplate, variablesToAsk, type PromptVariable } from "@/lib/prompts/variables"; import type { PublicPrompt, UsePromptResult } from "@/lib/client/types"; import { dispatchPromptInsert, promptChatHref, toInsertDetail } from "./insert"; /** Records the use, renders with `variables`, dispatches the insert event and navigates to the chat. */ export async function insertPromptIntoChat(prompt: Pick, variables: Record, router: { push: (href: string) => void }) { const res = await api(`/api/prompts/${prompt.id}/use`, { method: "POST", json: { variables } }); dispatchPromptInsert(toInsertDetail(res, variables)); router.push(promptChatHref(prompt.id, variables)); toast.success(res.kind === "system" ? `System prompt “${prompt.name}” applied` : `“${prompt.name}” inserted`, res.missing.length ? `Left empty: ${res.missing.join(", ")}` : undefined); return res; } /** * Fill-in sheet for `{{variables}}`: one input (or select when `options` exist) per variable, live preview, * then "Insert into chat". Prompts without variables to ask should call `insertPromptIntoChat` directly. */ export function PromptUseSheet({ prompt, open, onOpenChange }: { prompt: PublicPrompt | null; open: boolean; onOpenChange: (o: boolean) => void }) { const router = useRouter(); const [values, setValues] = React.useState>({}); const [busy, setBusy] = React.useState(false); const [copied, copy] = useCopy(); const firstRef = React.useRef(null); const vars: PromptVariable[] = React.useMemo(() => prompt?.variables ?? [], [prompt]); const ask = React.useMemo(() => variablesToAsk(vars), [vars]); React.useEffect(() => { if (!open || !prompt) return; // eslint-disable-next-line react-hooks/set-state-in-effect setValues(Object.fromEntries(vars.map((v) => [v.name, v.default ?? ""]))); const t = setTimeout(() => firstRef.current?.focus(), 80); return () => clearTimeout(t); }, [open, prompt, vars]); const preview = React.useMemo(() => (prompt ? renderTemplate(prompt.content, values, vars) : null), [prompt, values, vars]); const missingRequired = ask.filter((v) => v.required && !values[v.name]?.trim()).map((v) => labelFor(v)); const submit = async () => { if (!prompt || busy) return; setBusy(true); try { await insertPromptIntoChat(prompt, values, router); onOpenChange(false); } catch (e) { toast.error("Could not use prompt", errorMessage(e)); } finally { setBusy(false); } }; return (
} >
{ e.preventDefault(); void submit(); }} > {vars.length ? (
{vars.map((v, i) => { const id = `var-${v.name}`; const label = labelFor(v); const val = values[v.name] ?? ""; return ( 60 ? "sm:col-span-2" : ""}> {v.options?.length ? ( ) : val.length > 60 || val.includes("\n") ? (