import React, { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Separator } from "@/components/ui/separator"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; import { Plus, Play, Edit, Trash2, Copy, X } from "lucide-react"; import { toast } from "sonner"; import { useTranslation } from "react-i18next"; import { useConfirmation } from "@/hooks/use-confirmation.ts"; import { getSnippets, createSnippet, updateSnippet, deleteSnippet, } from "@/ui/main-axios"; import { useTabs } from "@/ui/desktop/navigation/tabs/TabContext.tsx"; import type { Snippet, SnippetData } from "../../../../types/index.js"; interface TabData { id: number; type: string; title: string; terminalRef?: { current?: { sendInput?: (data: string) => void; }; }; [key: string]: unknown; } interface SnippetsSidebarProps { isOpen: boolean; onClose: () => void; onExecute: (content: string) => void; } export function SnippetsSidebar({ isOpen, onClose, onExecute, }: SnippetsSidebarProps) { const { t } = useTranslation(); const { confirmWithToast } = useConfirmation(); const { tabs } = useTabs() as { tabs: TabData[] }; const [snippets, setSnippets] = useState([]); const [loading, setLoading] = useState(true); const [showDialog, setShowDialog] = useState(false); const [editingSnippet, setEditingSnippet] = useState(null); const [formData, setFormData] = useState({ name: "", content: "", description: "", }); const [formErrors, setFormErrors] = useState({ name: false, content: false, }); const [selectedTabIds, setSelectedTabIds] = useState([]); useEffect(() => { if (isOpen) { fetchSnippets(); } }, [isOpen]); const fetchSnippets = async () => { try { setLoading(true); const data = await getSnippets(); setSnippets(Array.isArray(data) ? data : []); } catch { toast.error(t("snippets.failedToFetch")); setSnippets([]); } finally { setLoading(false); } }; const handleCreate = () => { setEditingSnippet(null); setFormData({ name: "", content: "", description: "" }); setFormErrors({ name: false, content: false }); setShowDialog(true); }; const handleEdit = (snippet: Snippet) => { setEditingSnippet(snippet); setFormData({ name: snippet.name, content: snippet.content, description: snippet.description || "", }); setFormErrors({ name: false, content: false }); setShowDialog(true); }; const handleDelete = (snippet: Snippet) => { confirmWithToast( t("snippets.deleteConfirmDescription", { name: snippet.name }), async () => { try { await deleteSnippet(snippet.id); toast.success(t("snippets.deleteSuccess")); fetchSnippets(); } catch { toast.error(t("snippets.deleteFailed")); } }, "destructive", ); }; const handleSubmit = async () => { const errors = { name: !formData.name.trim(), content: !formData.content.trim(), }; setFormErrors(errors); if (errors.name || errors.content) { return; } try { if (editingSnippet) { await updateSnippet(editingSnippet.id, formData); toast.success(t("snippets.updateSuccess")); } else { await createSnippet(formData); toast.success(t("snippets.createSuccess")); } setShowDialog(false); fetchSnippets(); } catch { toast.error( editingSnippet ? t("snippets.updateFailed") : t("snippets.createFailed"), ); } }; const handleTabToggle = (tabId: number) => { setSelectedTabIds((prev) => prev.includes(tabId) ? prev.filter((id) => id !== tabId) : [...prev, tabId], ); }; const handleExecute = (snippet: Snippet) => { if (selectedTabIds.length > 0) { selectedTabIds.forEach((tabId) => { const tab = tabs.find((t: TabData) => t.id === tabId); if (tab?.terminalRef?.current?.sendInput) { tab.terminalRef.current.sendInput(snippet.content + "\n"); } }); toast.success( t("snippets.executeSuccess", { name: snippet.name, count: selectedTabIds.length, }), ); } else { onExecute(snippet.content); toast.success(t("snippets.executeSuccess", { name: snippet.name })); } }; const handleCopy = (snippet: Snippet) => { navigator.clipboard.writeText(snippet.content); toast.success(t("snippets.copySuccess", { name: snippet.name })); }; const terminalTabs = tabs.filter((tab: TabData) => tab.type === "terminal"); if (!isOpen) return null; return ( <>
e.stopPropagation()} >

{t("snippets.title")}

{terminalTabs.length > 0 && ( <>

{selectedTabIds.length > 0 ? t("snippets.executeOnSelected", { defaultValue: `Execute on ${selectedTabIds.length} selected terminal(s)`, count: selectedTabIds.length, }) : t("snippets.executeOnCurrent", { defaultValue: "Execute on current terminal (click to select multiple)", })}

{terminalTabs.map((tab) => ( ))}
)} {loading ? (

{t("common.loading")}

) : snippets.length === 0 ? (

{t("snippets.empty")}

{t("snippets.emptyHint")}

) : (
{snippets.map((snippet) => (

{snippet.name}

{snippet.description && (

{snippet.description}

)}
{snippet.content}

{t("snippets.runTooltip")}

{t("snippets.copyTooltip")}

{t("snippets.editTooltip")}

{t("snippets.deleteTooltip")}

))}
)}
{showDialog && (
setShowDialog(false)} >
e.stopPropagation()} >

{editingSnippet ? t("snippets.edit") : t("snippets.create")}

{editingSnippet ? t("snippets.editDescription") : t("snippets.createDescription")}

setFormData({ ...formData, name: e.target.value }) } placeholder={t("snippets.namePlaceholder")} className={`${formErrors.name ? "border-destructive focus-visible:ring-destructive" : ""}`} autoFocus /> {formErrors.name && (

{t("snippets.nameRequired")}

)}
setFormData({ ...formData, description: e.target.value }) } placeholder={t("snippets.descriptionPlaceholder")} />