import { useRef, useState, type AnimationEvent, type DragEvent, type KeyboardEvent, type MouseEvent, type PointerEvent } from "react"; import { CircleMinus, CirclePlus } from "lucide-react"; import { Button, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, toast } from "@tutti-os/ui-system"; import type { AgentGUINodeViewModel } from "../model/agentGuiNodeTypes"; import type { AgentGUIViewLabels } from "../AgentGUINodeView"; import { AgentGUIProviderIconVisual, agentGUIProviderRailIconPresentation } from "./AgentGUIEmptyState"; type ProviderRailTarget = AgentGUINodeViewModel["rail"]["agentTargets"][number]; type ProviderManagerZone = "available" | "disabled"; interface ProviderManagerDropPlacement { overTargetId: string; position: "before" | "after"; } interface ProviderManagerDragState { source: ProviderManagerZone; targetId: string; } type ProviderManagerDragPreview = ProviderManagerDropPlacement; interface AgentGUIProviderManagerDialogProps { hiddenTargetIds: readonly string[]; labels: AgentGUIViewLabels; onMoveTarget: ( draggedTargetId: string, overTargetId: string, position: "before" | "after" ) => void; onOpenChange: (open: boolean) => void; onVisibilityChange: ( targetId: string, visible: boolean, placement?: ProviderManagerDropPlacement ) => void; open: boolean; runningTargetIds: readonly string[]; targets: readonly ProviderRailTarget[]; } const AGENT_MANAGER_LONG_PRESS_MOVE_TOLERANCE_PX = 8; const AGENT_MANAGER_DROP_HYSTERESIS_PX = 8; export function AgentGUIProviderManagerDialog({ hiddenTargetIds, labels, onMoveTarget, onOpenChange, onVisibilityChange, open, runningTargetIds, targets }: AgentGUIProviderManagerDialogProps): React.JSX.Element { const hidden = new Set(hiddenTargetIds); const running = new Set(runningTargetIds); const availableTargets = targets.filter( (target) => !hidden.has(target.targetId) ); const disabledTargets = targets.filter((target) => hidden.has(target.targetId) ); const [editing, setEditing] = useState(false); const [dragState, setDragState] = useState( null ); const [dragOverZone, setDragOverZone] = useState( null ); const [dragPreview, setDragPreview] = useState(null); const dragStateRef = useRef(null); const dragPreviewRef = useRef(null); const [longPressTargetId, setLongPressTargetId] = useState( null ); const longPressOriginRef = useRef<{ x: number; y: number } | null>(null); const finalAvailableAgent = availableTargets.length === 1; const showRunningAgentBlocked = (targetId: string) => { const target = targets.find((candidate) => candidate.targetId === targetId); const label = target?.label.trim() || target?.provider || targetId; toast.error(labels.manageAgentsRunningBlocked(label), { id: `agent-gui-provider-manager-running:${targetId}` }); }; const cancelLongPress = () => { setLongPressTargetId(null); longPressOriginRef.current = null; }; const clearDrag = () => { dragStateRef.current = null; dragPreviewRef.current = null; setDragState(null); setDragOverZone(null); setDragPreview(null); }; const updateDragState = (nextDragState: ProviderManagerDragState | null) => { dragStateRef.current = nextDragState; setDragState(nextDragState); }; const updateDragPreview = ( nextPreview: ProviderManagerDragPreview | null ) => { const currentPreview = dragPreviewRef.current; if ( currentPreview?.overTargetId === nextPreview?.overTargetId && currentPreview?.position === nextPreview?.position ) { return; } dragPreviewRef.current = nextPreview; setDragPreview(nextPreview); }; const beginLongPress = ( event: PointerEvent, targetId: string ) => { if (event.button !== 0 || (event.target as Element).closest("button")) { return; } longPressOriginRef.current = { x: event.clientX, y: event.clientY }; setLongPressTargetId(targetId); }; const moveLongPress = (event: PointerEvent) => { const origin = longPressOriginRef.current; if ( origin && (Math.abs(event.clientX - origin.x) > AGENT_MANAGER_LONG_PRESS_MOVE_TOLERANCE_PX || Math.abs(event.clientY - origin.y) > AGENT_MANAGER_LONG_PRESS_MOVE_TOLERANCE_PX) ) { cancelLongPress(); } }; const finishLongPress = ( event: AnimationEvent, targetId: string ) => { if ( event.target === event.currentTarget && longPressTargetId === targetId ) { cancelLongPress(); setEditing(true); } }; const handleOpenChange = (nextOpen: boolean) => { cancelLongPress(); clearDrag(); setEditing(false); onOpenChange(nextOpen); }; const exitEditingFromBlankSpace = (event: MouseEvent) => { if (!(event.target as Element).closest("[data-agent-target-id]")) { setEditing(false); } }; const resolveDragState = ( event: DragEvent ): ProviderManagerDragState | null => { if (dragStateRef.current) { return dragStateRef.current; } const targetId = event.dataTransfer.getData("text/plain").trim(); if (!targetId) { return null; } return { source: hidden.has(targetId) ? "disabled" : "available", targetId }; }; const canDropIntoZone = ( dragged: ProviderManagerDragState, zone: ProviderManagerZone ) => !( dragged.source === "available" && zone === "disabled" && (finalAvailableAgent || running.has(dragged.targetId)) ); const startDrag = ( event: DragEvent, targetId: string, source: ProviderManagerZone ) => { cancelLongPress(); event.dataTransfer.effectAllowed = "move"; event.dataTransfer.setData("text/plain", targetId); updateDragState({ source, targetId }); }; const dragOverDropZone = ( event: DragEvent, zone: ProviderManagerZone ) => { const dragged = resolveDragState(event); if (!dragged) { return; } setDragOverZone(zone); updateDragPreview(null); if (!canDropIntoZone(dragged, zone)) { event.dataTransfer.dropEffect = "none"; if (zone === "disabled" && running.has(dragged.targetId)) { showRunningAgentBlocked(dragged.targetId); } return; } event.preventDefault(); event.dataTransfer.dropEffect = "move"; }; const targetDropPosition = ( event: DragEvent, dragged: ProviderManagerDragState, overTarget: ProviderRailTarget, zone: ProviderManagerZone ): "before" | "after" => { const zoneTargets = zone === "available" ? availableTargets : disabledTargets; const draggedIndex = zoneTargets.findIndex( (candidate) => candidate.targetId === dragged.targetId ); const overIndex = zoneTargets.findIndex( (candidate) => candidate.targetId === overTarget.targetId ); if (dragged.source === zone && draggedIndex >= 0) { return draggedIndex < overIndex ? "after" : "before"; } const bounds = event.currentTarget.getBoundingClientRect(); const midpointX = bounds.left + bounds.width / 2; const activePreview = dragPreviewRef.current; if (activePreview?.overTargetId === overTarget.targetId) { if ( activePreview.position === "before" && event.clientX <= midpointX + AGENT_MANAGER_DROP_HYSTERESIS_PX ) { return "before"; } if ( activePreview.position === "after" && event.clientX >= midpointX - AGENT_MANAGER_DROP_HYSTERESIS_PX ) { return "after"; } } return event.clientX > midpointX ? "after" : "before"; }; const dragOverTarget = ( event: DragEvent, overTarget: ProviderRailTarget, zone: ProviderManagerZone ) => { const dragged = resolveDragState(event); if (!dragged || dragged.targetId === overTarget.targetId) { return; } event.stopPropagation(); setDragOverZone(zone); if (!canDropIntoZone(dragged, zone)) { event.dataTransfer.dropEffect = "none"; updateDragPreview(null); if (zone === "disabled" && running.has(dragged.targetId)) { showRunningAgentBlocked(dragged.targetId); } return; } event.preventDefault(); event.dataTransfer.dropEffect = "move"; updateDragPreview({ overTargetId: overTarget.targetId, position: targetDropPosition(event, dragged, overTarget, zone) }); }; const dropIntoZone = ( event: DragEvent, zone: ProviderManagerZone ) => { const dragged = resolveDragState(event); if (!dragged || !canDropIntoZone(dragged, zone)) { clearDrag(); return; } event.preventDefault(); const zoneTargets = zone === "available" ? availableTargets : disabledTargets; const lastTarget = zoneTargets .filter((target) => target.targetId !== dragged.targetId) .at(-1); if (dragged.source === zone) { if (lastTarget) { onMoveTarget(dragged.targetId, lastTarget.targetId, "after"); } } else { onVisibilityChange( dragged.targetId, zone === "available", lastTarget ? { overTargetId: lastTarget.targetId, position: "after" } : undefined ); } clearDrag(); }; const dropOnTarget = ( event: DragEvent, overTarget: ProviderRailTarget, zone: ProviderManagerZone ) => { const dragged = resolveDragState(event); if ( !dragged || dragged.targetId === overTarget.targetId || !canDropIntoZone(dragged, zone) ) { clearDrag(); return; } event.preventDefault(); event.stopPropagation(); const activePreview = dragPreviewRef.current; const position = activePreview?.overTargetId === overTarget.targetId ? activePreview.position : targetDropPosition(event, dragged, overTarget, zone); if (dragged.source === zone) { onMoveTarget(dragged.targetId, overTarget.targetId, position); } else { onVisibilityChange(dragged.targetId, zone === "available", { overTargetId: overTarget.targetId, position }); } clearDrag(); }; const disabledDropBlocked = dragState?.source === "available" && (finalAvailableAgent || running.has(dragState.targetId)); const disabledDropBlockedByRunning = dragState?.source === "available" && running.has(dragState.targetId); return ( { if (!editing) { return; } event.preventDefault(); cancelLongPress(); clearDrag(); setEditing(false); }} > {labels.manageAgentsTitle} {labels.manageAgentsDescription}
dragOverDropZone(event, "available")} onDrop={(event) => dropIntoZone(event, "available")} >

{labels.manageAgentsAvailable}

{availableTargets.length > 0 ? (
{availableTargets.map((target) => { const label = target.label.trim() || target.provider; const targetRunning = running.has(target.targetId); return (
finishLongPress(event, target.targetId) } onContextMenu={(event) => { event.preventDefault(); setEditing(true); }} onDragEnd={clearDrag} onDragOver={(event) => dragOverTarget(event, target, "available") } onDragStart={(event) => startDrag(event, target.targetId, "available") } onDrop={(event) => dropOnTarget(event, target, "available") } onKeyDown={(event: KeyboardEvent) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); setEditing(true); } }} onPointerCancel={cancelLongPress} onPointerDown={(event) => beginLongPress(event, target.targetId) } onPointerLeave={cancelLongPress} onPointerMove={moveLongPress} onPointerUp={cancelLongPress} role="listitem" tabIndex={0} > {dragPreview?.overTargetId === target.targetId ? (
); })}
) : (

{labels.manageAgentsNoAvailable}

)}
dragOverDropZone(event, "disabled")} onDrop={(event) => dropIntoZone(event, "disabled")} title={ disabledDropBlocked ? disabledDropBlockedByRunning ? labels.manageAgentsRunningBlocked( targets.find( (target) => target.targetId === dragState?.targetId )?.label ?? "" ) : labels.manageAgentsKeepOneAvailable : undefined } >

{labels.manageAgentsDisabled}

{disabledTargets.length > 0 ? (
{disabledTargets.map((target) => { const label = target.label.trim() || target.provider; return (
finishLongPress(event, target.targetId) } onContextMenu={(event) => { event.preventDefault(); setEditing(true); }} onDragEnd={clearDrag} onDragOver={(event) => dragOverTarget(event, target, "disabled") } onDragStart={(event) => startDrag(event, target.targetId, "disabled") } onDrop={(event) => dropOnTarget(event, target, "disabled") } onKeyDown={(event: KeyboardEvent) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); setEditing(true); } }} onPointerCancel={cancelLongPress} onPointerDown={(event) => beginLongPress(event, target.targetId) } onPointerLeave={cancelLongPress} onPointerMove={moveLongPress} onPointerUp={cancelLongPress} role="listitem" tabIndex={0} > {dragPreview?.overTargetId === target.targetId ? (
); })}
) : (

{labels.manageAgentsNoDisabled}

)}
); }