import { getErrorMessage } from "../lib/error-message.js"; import { useState, useEffect, useCallback, useMemo, useRef } from "react"; import { useTranslation } from "react-i18next"; import { copyToClipboard } from "@/lib/clipboard"; import { useSnippetRunner } from "@/hooks/use-snippet-runner.tsx"; import { getSnippets, createSnippet as apiCreateSnippet, updateSnippet as apiUpdateSnippet, deleteSnippet as apiDeleteSnippet, getSnippetFolders, createSnippetFolder as apiCreateSnippetFolder, deleteSnippetFolder as apiDeleteSnippetFolder, renameSnippetFolder as apiRenameSnippetFolder, updateSnippetFolderMetadata as apiUpdateSnippetFolderMetadata, shareSnippet as apiShareSnippet, getSnippetAccess, revokeSnippetAccess, getUserList, getRoles, reorderSnippets, saveUserPreferences, } from "@/main-axios"; import { hasSnippetInputs, type SnippetHostContext, } from "@/lib/snippet-variables"; import { SnippetVariablesDialog } from "@/components/SnippetVariablesDialog"; import { exportSnippets, importSnippets, type SnippetExportData, executeSnippet as apiExecuteSnippet, } from "@/api/snippets-api"; import { getSSHHosts } from "@/api/ssh-host-management-api"; import type { SSHHost } from "@/types/index"; import { Button } from "@/components/button"; import { Input } from "@/components/input"; import { FolderPathPicker } from "./FolderPathPicker"; import { Separator } from "@/components/separator"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, } from "@/components/dialog"; import { Box, ChevronDown, ChevronsDownUp, ChevronsUpDown, Clipboard, Copy, Cpu, Database, Download, Folder, Globe, GripVertical, Network, Pencil, Play, Plus, Search, Server, Settings, Share2, Terminal, Trash2, Upload, UserPlus, X, MoreHorizontal, Zap, } from "lucide-react"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/dropdown-menu"; import { SettingRow, FakeSwitch } from "@/components/section-card"; import { toast } from "sonner"; import { FOLDER_COLORS } from "@/lib/theme"; import { FOLDER_ICONS } from "@/types/ui-types"; import type { Snippet, SnippetFolder, FolderIconId, Tab, } from "@/types/ui-types"; function FolderIconEl({ icon, className, style, }: { icon: FolderIconId; className?: string; style?: React.CSSProperties; }) { const props = { className, style }; switch (icon) { case "folder": return ; case "server": return ; case "cloud": return (
); case "database": return ; case "box": return ; case "network": return ; case "copy": return ; case "settings": return ; case "cpu": return ; case "globe": return ; } } function SnippetFormDialog({ open, onOpenChange, folders, snippet, onSave, availableHosts, }: { open: boolean; onOpenChange: (v: boolean) => void; folders: SnippetFolder[]; snippet: Snippet | null; onSave: (data: Omit, id?: number) => void; availableHosts: SSHHost[]; }) { const { t } = useTranslation(); const [name, setName] = useState(""); const [description, setDescription] = useState(""); const [folder, setFolder] = useState(null); const [content, setContent] = useState(""); const [isNote, setIsNote] = useState(false); const [selectedHostIds, setSelectedHostIds] = useState>( new Set(), ); const folderMeta = useMemo(() => { const map = new Map(); for (const f of folders) { map.set(f.name, { color: f.color ?? undefined, icon: f.icon }); } return map; }, [folders]); useEffect(() => { if (open) { setName(snippet?.name ?? ""); setDescription(snippet?.description ?? ""); setFolder(snippet?.folder ?? null); setContent(snippet?.content ?? ""); setIsNote(snippet?.isNote ?? false); setSelectedHostIds(new Set(snippet?.hostIds ?? [])); } }, [open, snippet]); function toggleHost(id: number) { setSelectedHostIds((prev) => { const next = new Set(prev); if (next.has(id)) { next.delete(id); } else { next.add(id); } return next; }); } function handleSave() { if (!name.trim() || !content.trim()) return; onSave( { name: name.trim(), description: description.trim() || undefined, content: content.trim(), folder, isNote, hostIds: !isNote && selectedHostIds.size > 0 ? Array.from(selectedHostIds) : undefined, }, snippet?.id, ); onOpenChange(false); } const isEdit = snippet !== null; return ( {isEdit ? t("newUi.sidebar.snippets.editSnippetTitle") : t("newUi.sidebar.snippets.createSnippetTitle")} {isEdit ? t("newUi.sidebar.snippets.editSnippetDescription") : t("newUi.sidebar.snippets.createSnippetDescription")}
setName(e.target.value)} />
setDescription(e.target.value)} />
setFolder(path === "" ? null : path)} folderPaths={folders.map((f) => f.name)} folderMeta={folderMeta} />