// Shared UI primitives. The install wizard set the visual baseline; this file // exposes the primary button, secondary button, input, error message, and // copy button so the auth and in-app surfaces can match. import { useId, useRef, useState, type ReactNode } from "react"; import { createPortal } from "react-dom"; import { Dialog, DialogPanel, DialogTitle } from "@headlessui/react"; import { ClipboardDocumentCheckIcon, ClipboardIcon, } from "@heroicons/react/16/solid"; export const INPUT_CLASS = "block w-full rounded-lg bg-panel-alt px-3.5 py-2.5 text-base text-fg outline-1 -outline-offset-1 outline-white/10 placeholder:text-fg-muted focus:outline-2 focus:-outline-offset-1 focus:outline-teal-500 sm:text-sm"; export const PRIMARY_BUTTON_CLASS = "inline-flex items-center justify-center gap-2 rounded-lg bg-teal-500 px-4 py-2 text-sm font-medium text-on-primary transition-colors hover:bg-teal-300 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-500 disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:bg-teal-500"; export const SECONDARY_BUTTON_CLASS = "inline-flex items-center justify-center gap-2 rounded-lg bg-transparent px-3.5 py-2 text-sm font-medium text-fg-2 outline-1 -outline-offset-1 outline-white/10 hover:bg-overlay hover:text-fg focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-teal-500 disabled:cursor-not-allowed disabled:opacity-60"; export const DANGER_BUTTON_CLASS = "inline-flex items-center justify-center gap-2 rounded-lg bg-coral px-4 py-2 text-sm font-medium text-on-primary transition-colors hover:bg-coral/90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-coral disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:bg-coral"; export function ErrorMessage({ message }: { message: string }) { return (

{message}

); } export function CopyButton({ value, label, className = "", }: { value: string; label: string; className?: string; }) { const [copied, setCopied] = useState(false); return ( ); } export function ConfirmDialog({ open, title, description, confirmLabel, pendingLabel, cancelLabel = "Cancel", pending = false, onConfirm, onCancel, }: { open: boolean; title: string; description: ReactNode; confirmLabel: string; pendingLabel?: string; cancelLabel?: string; pending?: boolean; onConfirm: () => void; onCancel: () => void; }) { return ( { if (!pending) onCancel(); }} className="relative z-50" > ); } export function Tooltip({ label, children, }: { label: React.ReactNode; children: React.ReactNode; }) { const [open, setOpen] = useState(false); const triggerRef = useRef(null); const id = useId(); const rect = open ? triggerRef.current?.getBoundingClientRect() : null; const portalTarget = typeof document === "undefined" ? null : document.body; return ( <> setOpen(true)} onMouseLeave={() => setOpen(false)} onFocus={() => setOpen(true)} onBlur={() => setOpen(false)} aria-describedby={open ? id : undefined} className="inline-flex" > {children} {rect && portalTarget ? createPortal( , portalTarget, ) : null} ); }