diff --git a/src/components/swarm/archetype-inspector.tsx b/src/components/swarm/archetype-inspector.tsx index 7d3806a..0c7eb83 100644 --- a/src/components/swarm/archetype-inspector.tsx +++ b/src/components/swarm/archetype-inspector.tsx @@ -1,17 +1,43 @@ "use client"; -import React, { useState, useEffect } from 'react'; -import { X, Save, ShieldAlert, Trash2, Plus } from 'lucide-react'; +import React, { useState, useEffect, useMemo } from 'react'; +import { X, Save, ShieldAlert, Trash2, Plus, Copy, Palette, Smile } from 'lucide-react'; import type { AgentArchetype } from '../../lib/types-swarm'; +const COLOR_PRESETS = [ + '#3b82f6', '#2563eb', '#1d4ed8', '#0ea5e9', '#06b6d4', + '#10b981', '#059669', '#22c55e', '#84cc16', '#a3e635', + '#8b5cf6', '#7c3aed', '#a855f7', '#c084fc', '#e879f9', + '#ef4444', '#dc2626', '#f97316', '#fb923c', '#fbbf24', + '#ec4899', '#db2777', '#f472b6', '#f9a8d4', '#fda4af', + '#6366f1', '#64748b', '#78716c', '#57534e', '#1e293b', +]; + +const EMOJI_PRESETS = [ + '๐Ÿ—๏ธ', 'โš™๏ธ', '๐Ÿ”', '๐Ÿงช', '๐Ÿš€', '๐Ÿค–', '๐Ÿ‘จโ€๐Ÿ’ป', '๐Ÿ‘ฉโ€๐Ÿ’ป', '๐Ÿง™โ€โ™‚๏ธ', '๐Ÿง™โ€โ™€๏ธ', + '๐Ÿ”ง', '๐Ÿ“', '๐ŸŽฏ', 'โšก', '๐Ÿ›ก๏ธ', '๐Ÿ“Š', '๐Ÿ—‚๏ธ', '๐Ÿ’ก', '๐Ÿ”ฎ', '๐Ÿงฉ', + 'โญ', '๐Ÿ”ฅ', '๐Ÿ’Ž', '๐Ÿšฆ', '๐ŸŽช', '๐ŸŽจ', '๐ŸŽญ', '๐Ÿƒ', '๐Ÿ‘‘', '๐Ÿ†', + '๐Ÿฆ…', '๐Ÿบ', '๐Ÿฆ', '๐Ÿป', '๐ŸฆŠ', '๐Ÿ™', '๐Ÿ', '๐Ÿฆ‹', '๐ŸŒฟ', '๐ŸŒŠ', +]; + +const SUGGESTED_CAPABILITIES = [ + 'coding', 'testing', 'debugging', 'refactoring', 'documentation', + 'code_review', 'system_design', 'architecture', 'planning', 'analysis', + 'research', 'investigation', 'deployment', 'ci_cd', 'monitoring', + 'security', 'performance', 'optimization', 'integration', 'migration', + 'data_analysis', 'automation', 'scripting', 'api_design', 'database', + 'frontend', 'backend', 'devops', 'qa', 'mentoring', +]; + interface ArchetypeInspectorProps { archetype?: AgentArchetype; onClose: () => void; onSave: (data: Partial) => Promise; onDelete?: (id: string) => Promise; + onClone?: (archetype: AgentArchetype) => Promise; } -export function ArchetypeInspector({ archetype, onClose, onSave, onDelete }: ArchetypeInspectorProps) { +export function ArchetypeInspector({ archetype, onClose, onSave, onDelete, onClone }: ArchetypeInspectorProps) { const isNew = !archetype; const [name, setName] = useState(archetype?.name || ''); @@ -19,10 +45,16 @@ export function ArchetypeInspector({ archetype, onClose, onSave, onDelete }: Arc const [systemPrompt, setSystemPrompt] = useState(archetype?.systemPrompt || ''); const [capabilities, setCapabilities] = useState(archetype?.capabilities || []); const [color, setColor] = useState(archetype?.color || '#3b82f6'); + const [icon, setIcon] = useState(archetype?.icon || ''); const [newCapability, setNewCapability] = useState(''); + const [capabilityFilter, setCapabilityFilter] = useState(''); const [isSaving, setIsSaving] = useState(false); const [isDeleting, setIsDeleting] = useState(false); + const [isCloning, setIsCloning] = useState(false); const [error, setError] = useState(null); + const [showEmojiPicker, setShowEmojiPicker] = useState(false); + const [showColorPicker, setShowColorPicker] = useState(false); + const [showCapabilityDropdown, setShowCapabilityDropdown] = useState(false); useEffect(() => { if (archetype) { @@ -31,13 +63,25 @@ export function ArchetypeInspector({ archetype, onClose, onSave, onDelete }: Arc setSystemPrompt(archetype.systemPrompt); setCapabilities(archetype.capabilities); setColor(archetype.color); + setIcon(archetype.icon || ''); } }, [archetype]); - const handleAddCapability = () => { - if (newCapability.trim()) { - setCapabilities([...capabilities, newCapability.trim().toLowerCase()]); + const filteredSuggestions = useMemo(() => { + return SUGGESTED_CAPABILITIES.filter( + cap => + cap.includes(capabilityFilter.toLowerCase()) && + !capabilities.includes(cap) + ).slice(0, 6); + }, [capabilityFilter, capabilities]); + + const handleAddCapability = (cap?: string) => { + const toAdd = cap || newCapability.trim(); + if (toAdd && !capabilities.includes(toAdd.toLowerCase())) { + setCapabilities([...capabilities, toAdd.toLowerCase()]); setNewCapability(''); + setCapabilityFilter(''); + setShowCapabilityDropdown(false); } }; @@ -62,6 +106,7 @@ export function ArchetypeInspector({ archetype, onClose, onSave, onDelete }: Arc systemPrompt: systemPrompt.trim(), capabilities, color, + icon: icon || undefined, isBuiltIn: archetype?.isBuiltIn }); onClose(); @@ -90,16 +135,38 @@ export function ArchetypeInspector({ archetype, onClose, onSave, onDelete }: Arc } }; + const handleClone = async () => { + if (!archetype || !onClone) return; + + setIsCloning(true); + setError(null); + + try { + await onClone(archetype); + onClose(); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to clone'); + } finally { + setIsCloning(false); + } + }; + + const displayChar = icon || name.charAt(0) || '?'; + return (
-
+
-
+
- {name.charAt(0) || '?'} + {displayChar}

@@ -116,145 +183,298 @@ export function ArchetypeInspector({ archetype, onClose, onSave, onDelete }: Arc

{error && ( -
+
+ {error}
)} - {archetype?.isBuiltIn && ( -
- -
- Built-in Archetype. This is a core system role. You cannot delete it. -
-
- )} - -
-
- - setName(e.target.value)} - placeholder="e.g., System Architect" - className="w-full bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)] focus:ring-1 focus:ring-[var(--ui-accent-info)]" - /> -
- -
- - setDescription(e.target.value)} - placeholder="Brief description of this archetype's role" - className="w-full bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)] focus:ring-1 focus:ring-[var(--ui-accent-info)]" - /> -
- -
- -
- setColor(e.target.value)} - className="w-10 h-10 rounded cursor-pointer border border-[var(--ui-border-soft)]" - /> +
+
+
+ setColor(e.target.value)} - className="flex-1 bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)] focus:ring-1 focus:ring-[var(--ui-accent-info)]" + value={name} + onChange={(e) => setName(e.target.value)} + className="w-full px-3 py-2 rounded-lg bg-[var(--ui-bg-soft)] border border-[var(--ui-border-soft)] text-[var(--ui-text-primary)] placeholder:text-[var(--ui-text-muted)] focus:outline-none focus:ring-2 focus:ring-blue-500/50" + placeholder="e.g., Code Reviewer" + /> +
+
+ + setDescription(e.target.value)} + className="w-full px-3 py-2 rounded-lg bg-[var(--ui-bg-soft)] border border-[var(--ui-border-soft)] text-[var(--ui-text-primary)] placeholder:text-[var(--ui-text-muted)] focus:outline-none focus:ring-2 focus:ring-blue-500/50" + placeholder="Brief description" />
- +