"use client"; import { useState, useRef, useMemo } from "react"; import { Loader2, Save, Globe, Lock, Camera, ChevronDown, } from "lucide-react"; import type { Agent, AgentVisibility, RuntimeDevice, MemberWithUser } from "@multica/core/types"; import { Popover, PopoverTrigger, PopoverContent, } from "@multica/ui/components/ui/popover"; import { Button } from "@multica/ui/components/ui/button"; import { Input } from "@multica/ui/components/ui/input"; import { Label } from "@multica/ui/components/ui/label"; import { toast } from "sonner"; import { api } from "@multica/core/api"; import { useFileUpload } from "@multica/core/hooks/use-file-upload"; import { ActorAvatar } from "../../../common/actor-avatar"; import { ProviderLogo } from "../../../runtimes/components/provider-logo"; type RuntimeFilter = "mine" | "all"; export function SettingsTab({ agent, runtimes, members, currentUserId, onSave, }: { agent: Agent; runtimes: RuntimeDevice[]; members: MemberWithUser[]; currentUserId: string | null; onSave: (updates: Partial) => Promise; }) { const [name, setName] = useState(agent.name); const [description, setDescription] = useState(agent.description ?? ""); const [visibility, setVisibility] = useState(agent.visibility); const [maxTasks, setMaxTasks] = useState(agent.max_concurrent_tasks); const [selectedRuntimeId, setSelectedRuntimeId] = useState(agent.runtime_id); const [runtimeOpen, setRuntimeOpen] = useState(false); const [runtimeFilter, setRuntimeFilter] = useState("mine"); const [saving, setSaving] = useState(false); const { upload, uploading } = useFileUpload(api); const fileInputRef = useRef(null); const getOwnerMember = (ownerId: string | null) => { if (!ownerId) return null; return members.find((m) => m.user_id === ownerId) ?? null; }; const hasOtherRuntimes = runtimes.some((r) => r.owner_id !== currentUserId); const filteredRuntimes = useMemo(() => { const filtered = runtimeFilter === "mine" && currentUserId ? runtimes.filter((r) => r.owner_id === currentUserId) : runtimes; return [...filtered].sort((a, b) => { if (a.owner_id === currentUserId && b.owner_id !== currentUserId) return -1; if (a.owner_id !== currentUserId && b.owner_id === currentUserId) return 1; return 0; }); }, [runtimes, runtimeFilter, currentUserId]); const selectedRuntime = runtimes.find((d) => d.id === selectedRuntimeId) ?? null; const selectedOwnerMember = selectedRuntime ? getOwnerMember(selectedRuntime.owner_id) : null; const handleAvatarUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; e.target.value = ""; try { const result = await upload(file); if (!result) return; await onSave({ avatar_url: result.link }); toast.success("Avatar updated"); } catch (err) { toast.error(err instanceof Error ? err.message : "Failed to upload avatar"); } }; const dirty = name !== agent.name || description !== (agent.description ?? "") || visibility !== agent.visibility || maxTasks !== agent.max_concurrent_tasks || selectedRuntimeId !== agent.runtime_id; const handleSave = async () => { if (!name.trim()) { toast.error("Name is required"); return; } setSaving(true); try { await onSave({ name: name.trim(), description, visibility, max_concurrent_tasks: maxTasks, runtime_id: selectedRuntimeId, }); toast.success("Settings saved"); } catch { toast.error("Failed to save settings"); } finally { setSaving(false); } }; return (
Click to upload avatar
setName(e.target.value)} className="mt-1" />
setDescription(e.target.value)} placeholder="What does this agent do?" className="mt-1" />
setMaxTasks(Number(e.target.value))} className="mt-1 w-24" />
{hasOtherRuntimes && (
)}
{selectedRuntime ? ( ) : ( )}
{selectedRuntime?.name ?? "No runtime available"} {selectedRuntime?.runtime_mode === "cloud" && ( Cloud )}
{selectedRuntime ? ( selectedOwnerMember ? selectedOwnerMember.name : selectedRuntime.device_info ) : "Select a runtime"}
{filteredRuntimes.map((device) => { const ownerMember = getOwnerMember(device.owner_id); return ( ); })}
); }