// ======================================== // Inline Template Panel Component // ======================================== // Compact template list for the left sidebar, uses useTemplates hook import { useState, useCallback, useMemo } from 'react'; import { Search, Loader2, FileText, Download, GitBranch } from 'lucide-react'; import { cn } from '@/lib/utils'; import { Input } from '@/components/ui/Input'; import { Badge } from '@/components/ui/Badge'; import { useTemplates, useInstallTemplate } from '@/hooks/useTemplates'; import { useFlowStore } from '@/stores'; import type { FlowTemplate } from '@/types/execution'; // ========== Sub-Components ========== interface TemplateItemProps { template: FlowTemplate; onInstall: (template: FlowTemplate) => void; isInstalling: boolean; } function TemplateItem({ template, onInstall, isInstalling }: TemplateItemProps) { return ( ); } // ========== Main Component ========== interface InlineTemplatePanelProps { className?: string; } /** * Compact template browser for the left sidebar. * Loads templates via the useTemplates API hook and displays them in a searchable list. * Clicking a template installs it as the current flow. */ export function InlineTemplatePanel({ className }: InlineTemplatePanelProps) { const [searchQuery, setSearchQuery] = useState(''); const [installingId, setInstallingId] = useState(null); const setCurrentFlow = useFlowStore((state) => state.setCurrentFlow); const { data, isLoading, error } = useTemplates(); const installTemplate = useInstallTemplate(); // Filter templates by search query const filteredTemplates = useMemo(() => { if (!data?.templates) return []; if (!searchQuery.trim()) return data.templates; const query = searchQuery.toLowerCase(); return data.templates.filter( (t) => t.name.toLowerCase().includes(query) || t.description?.toLowerCase().includes(query) || t.category?.toLowerCase().includes(query) || t.tags?.some((tag) => tag.toLowerCase().includes(query)) ); }, [data?.templates, searchQuery]); // Handle install - load template as current flow const handleInstall = useCallback( async (template: FlowTemplate) => { setInstallingId(template.id); try { const result = await installTemplate.mutateAsync({ templateId: template.id, }); setCurrentFlow(result.flow); } catch (err) { console.error('Failed to install template:', err); } finally { setInstallingId(null); } }, [installTemplate, setCurrentFlow] ); return (
{/* Search */}
setSearchQuery(e.target.value)} placeholder="搜索模板..." className="pl-8 h-8 text-sm" />
{/* Template List */}
{isLoading ? (
) : error ? (

无法加载模板库,请确认 API 服务可用

) : filteredTemplates.length === 0 ? (

{searchQuery ? '未找到匹配的模板' : '暂无可用模板'}

) : (
{filteredTemplates.map((template) => ( ))}
)}
); } export default InlineTemplatePanel;