import { useMemo, useState } from "react"; import { useHosts, useTranslation } from "@termix/plugin-sdk/frontend"; import { ArrowLeft, Check, FileText, Search, Server, Zap } from "lucide-react"; import { Button, Input, SectionCard, Select2 } from "@termix/plugin-sdk/ui"; import { parseHostFilter, type Snippet, type SnippetFolder } from "./types"; export interface SnippetFormValues { name: string; content: string; description: string; folder: string; isNote: boolean; hostIds: number[]; } const labelClass = "text-[10px] font-bold uppercase tracking-widest text-muted-foreground"; export function SnippetEditor({ snippet, defaultFolder, folders, onBack, onSave, }: { /** The snippet being edited, or null to create one. */ snippet: Snippet | null; defaultFolder?: string; folders: SnippetFolder[]; onBack: () => void; onSave: (values: SnippetFormValues) => Promise; }) { const { t } = useTranslation(); const [values, setValues] = useState(() => ({ name: snippet?.name ?? "", content: snippet?.content ?? "", description: snippet?.description ?? "", folder: snippet?.folder ?? defaultFolder ?? "", isNote: snippet?.isNote ?? false, hostIds: parseHostFilter(snippet?.hostFilter), })); const { hosts } = useHosts(); const [hostSearch, setHostSearch] = useState(""); const [saving, setSaving] = useState(false); const set = ( key: K, value: SnippetFormValues[K], ) => setValues((prev) => ({ ...prev, [key]: value })); const canSave = !!values.name.trim() && !!values.content.trim() && !saving; // A snippet can point at a folder that has no metadata row yet. const folderNames = Array.from( new Set([ ...folders.map((f) => f.name), ...(values.folder ? [values.folder] : []), ]), ).sort((a, b) => a.localeCompare(b)); const hostQuery = hostSearch.trim().toLowerCase(); const visibleHosts = useMemo( () => hosts .filter( (host) => !hostQuery || host.name.toLowerCase().includes(hostQuery) || host.ip.toLowerCase().includes(hostQuery), ) .sort((a, b) => a.name.localeCompare(b.name)), [hosts, hostQuery], ); function toggleHost(id: number) { set( "hostIds", values.hostIds.includes(id) ? values.hostIds.filter((h) => h !== id) : [...values.hostIds, id], ); } async function handleSave() { if (!canSave) return; setSaving(true); try { await onSave(values); } finally { setSaving(false); } } return (
} >
set("name", e.target.value)} />
set("description", e.target.value)} />
set("folder", e.target.value)} > {folderNames.map((name) => ( ))}
{[false, true].map((isNote) => ( ))}
{t(values.isNote ? "typeNoteHint" : "typeCommandHint")}