import type { ReactNode } from "react"; import { CircleAlert, Loader2, RotateCcw, X } from "lucide-react"; import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { isNativeRuntime } from "@/lib/runtime"; import type { NanobotFeatureInfo } from "@/lib/types"; import { cn } from "@/lib/utils"; export const SETTINGS_SEARCH_INPUT_CLASS = cn( "border-border/45 bg-settings-surface transition-colors hover:border-border/70", "focus-visible:border-border/70 focus-visible:bg-background", ); export function CapabilityInstallNotice({ title, description, installing = false, }: { title: string; description: string; installing?: boolean; }) { return (
{installing ? ( ) : ( )}

{title}

{description}

); } export function NanobotFeatureInstallDialog({ feature, installing, onOpenChange, onConfirm, }: { feature: NanobotFeatureInfo | null; installing: boolean; onOpenChange: (open: boolean) => void; onConfirm: (feature: NanobotFeatureInfo) => void | Promise; }) { const { t } = useTranslation(); const tx = (key: string, fallback: string, values?: Record) => t(key, { defaultValue: fallback, ...(values ?? {}) }); const name = feature?.display_name || feature?.name || ""; return ( {tx("settings.nanobotFeatures.installConfirmTitle", "Install support for {{name}}?", { name })} {tx( "settings.nanobotFeatures.installConfirmDescription", "nanobot will add what {{name}} needs, then turn it on. Continue?", { name }, )} ); } export function DismissibleStatusMessage({ message, isError, onDismiss, }: { message: string; isError: boolean; onDismiss: () => void; }) { const { t } = useTranslation(); const tx = (key: string, fallback: string) => t(key, { defaultValue: fallback }); return (
{message}
); } export function RestartRequiredNotice({ message, onRestart, isRestarting, }: { message: string; onRestart?: () => void; isRestarting?: boolean; }) { const { t } = useTranslation(); return (
{message} {onRestart ? ( ) : null}
); } export function SettingsSectionTitle({ children }: { children: ReactNode }) { return (

{children}

); } export function SettingsGroup({ children }: { children: ReactNode }) { return (
{children}
); } export function SettingsRow({ title, description, children, }: { title: string; description?: string; children?: ReactNode; }) { return (
{title}
{description ? (
{description}
) : null}
{children ?
{children}
: null}
); } export function ReadOnlyRow({ title, value, description, }: { title: string; value: string; description?: string; }) { return ( {value} ); } export function RestartSettingsFooter({ dirty, saving, pendingRestart, disabled = false, message, dirtyMessage, pendingMessage, onSave, onRestart, onReset, isRestarting, }: { dirty: boolean; saving: boolean; pendingRestart: boolean; disabled?: boolean; message?: string; dirtyMessage?: string; pendingMessage?: string; onSave: () => void; onRestart?: () => void; onReset?: () => void; isRestarting?: boolean; }) { const { t } = useTranslation(); const tx = (key: string, fallback: string) => t(key, { defaultValue: fallback }); const isNativeHost = isNativeRuntime(); const restartLabel = isNativeHost ? tx("app.system.restartEngine", "Restart engine") : t("app.system.restart"); const restartingLabel = isNativeHost ? tx("app.system.restartingEngine", "Restarting engine...") : t("app.system.restarting"); const statusMessage = message ?? (pendingRestart && !dirty ? pendingMessage ?? tx("settings.status.savedRestartApply", "Saved. Restart when ready.") : dirty ? dirtyMessage ?? t("settings.status.unsaved") : undefined); const statusTone = disabled ? "danger" : dirty || pendingRestart ? "accent" : undefined; return (
{statusMessage}
{pendingRestart && !dirty && onRestart ? ( ) : null} {onReset ? ( ) : null}
); } export function SettingsStatusMessage({ children, tone, }: { children?: ReactNode; tone?: "accent" | "danger"; }) { if (!children) return null; return ( {tone ? ( ) : null} {children} ); } export function StatusPill({ children, tone = "neutral", }: { children: ReactNode; tone?: "neutral" | "success" | "warning"; }) { return ( {children} ); } export function NumberInput({ value, min, max, onChange, suffix, }: { value: number; min: number; max: number; onChange: (value: number) => void; suffix?: string; }) { return (
{ const parsed = Number(event.target.value); if (Number.isFinite(parsed)) onChange(parsed); }} className="h-8 w-24 max-w-full rounded-full text-[13px]" /> {suffix ? {suffix} : null}
); }