"use client"; import React, { useState, useRef } from "react"; import { MiniHSQNNAdvisor } from "@/components/mini-hs-qnn-advisor"; import { Button } from "@/components/ui/button"; import { ChevronDown, ChevronUp, Sparkles, Minus, Square, MoveDiagonal } from "lucide-react"; interface FloatingMiniAdvisorProps { onApplyParameters: (params: any, previousJobId?: string) => void; onSaveConfig: (params: any) => void; defaultMinimized?: boolean; } export default function FloatingMiniAdvisor({ onApplyParameters, onSaveConfig, defaultMinimized = false }: FloatingMiniAdvisorProps) { // All hooks at the top, always called const [isExpanded, setIsExpanded] = useState(false); const [isMinimized, setIsMinimized] = useState(defaultMinimized); const [size, setSize] = useState({ width: 420, height: 520 }); const [expandedPosition, setExpandedPosition] = useState({ top: 64, left: 64 }); const minimizedPosition = { bottom: 32, right: 32 }; const [isClient, setIsClient] = useState(false); const [dragging, setDragging] = useState(false); const dragStart = useRef<{ top: number; left: number; mouseX: number; mouseY: number } | null>(null); const [resizing, setResizing] = useState(false); const [resizeDirection, setResizeDirection] = useState(null); const resizeStart = useRef<{ width: number; height: number; mouseX: number; mouseY: number; direction: string; top: number; left: number } | null>(null); // Effects React.useEffect(() => { setIsClient(true); const updatePosition = () => { const top = Math.max(32, window.innerHeight - 520 - 32); const left = Math.max(32, window.innerWidth - 420 - 32); setExpandedPosition({ top, left }); }; updatePosition(); window.addEventListener('resize', updatePosition); return () => window.removeEventListener('resize', updatePosition); }, []); const onMouseMove = (e: MouseEvent) => { if (!dragging || !dragStart.current) return; const dx = e.clientX - dragStart.current.mouseX; const dy = e.clientY - dragStart.current.mouseY; setExpandedPosition({ top: dragStart.current.top + dy, left: dragStart.current.left + dx, }); }; const onMouseUp = () => { setDragging(false); dragStart.current = null; document.body.style.userSelect = ""; }; React.useEffect(() => { if (dragging) { window.addEventListener("mousemove", onMouseMove); window.addEventListener("mouseup", onMouseUp); } else { window.removeEventListener("mousemove", onMouseMove); window.removeEventListener("mouseup", onMouseUp); } return () => { window.removeEventListener("mousemove", onMouseMove); window.removeEventListener("mouseup", onMouseUp); }; }, [dragging]); const onResizeMouseMove = (e: MouseEvent) => { if (!resizing || !resizeStart.current) return; const dx = e.clientX - resizeStart.current.mouseX; const dy = e.clientY - resizeStart.current.mouseY; let newWidth = size.width; let newHeight = size.height; let newTop = expandedPosition.top; let newLeft = expandedPosition.left; const dir = resizeStart.current.direction; if (dir === "right") { newWidth = Math.max(minWidth, Math.min(maxWidth, resizeStart.current.width + dx)); } if (dir === "left") { newWidth = Math.max(minWidth, Math.min(maxWidth, resizeStart.current.width - dx)); newLeft = resizeStart.current.left + dx; } if (dir === "bottom") { newHeight = Math.max(minHeight, Math.min(maxHeight, resizeStart.current.height + dy)); } if (dir === "top") { newHeight = Math.max(minHeight, Math.min(maxHeight, resizeStart.current.height - dy)); newTop = resizeStart.current.top + dy; } if (dir === "top-left") { newWidth = Math.max(minWidth, Math.min(maxWidth, resizeStart.current.width - dx)); newHeight = Math.max(minHeight, Math.min(maxHeight, resizeStart.current.height - dy)); newLeft = resizeStart.current.left + dx; newTop = resizeStart.current.top + dy; } if (dir === "top-right") { newWidth = Math.max(minWidth, Math.min(maxWidth, resizeStart.current.width + dx)); newHeight = Math.max(minHeight, Math.min(maxHeight, resizeStart.current.height - dy)); newTop = resizeStart.current.top + dy; } if (dir === "bottom-left") { newWidth = Math.max(minWidth, Math.min(maxWidth, resizeStart.current.width - dx)); newHeight = Math.max(minHeight, Math.min(maxHeight, resizeStart.current.height + dy)); newLeft = resizeStart.current.left + dx; } if (dir === "bottom-right") { newWidth = Math.max(minWidth, Math.min(maxWidth, resizeStart.current.width + dx)); newHeight = Math.max(minHeight, Math.min(maxHeight, resizeStart.current.height + dy)); } setSize({ width: newWidth, height: newHeight }); setExpandedPosition({ top: newTop, left: newLeft }); }; const onResizeMouseUp = () => { setResizing(false); setResizeDirection(null); resizeStart.current = null; document.body.style.userSelect = ""; }; React.useEffect(() => { if (resizing) { window.addEventListener("mousemove", onResizeMouseMove); window.addEventListener("mouseup", onResizeMouseUp); } else { window.removeEventListener("mousemove", onResizeMouseMove); window.removeEventListener("mouseup", onResizeMouseUp); } return () => { window.removeEventListener("mousemove", onResizeMouseMove); window.removeEventListener("mouseup", onResizeMouseUp); }; }, [resizing]); // Only after all hooks, do a conditional render if (!isClient) return
; // Drag logic using top/left const onMouseDown = (e: React.MouseEvent) => { setDragging(true); dragStart.current = { top: expandedPosition.top, left: expandedPosition.left, mouseX: e.clientX, mouseY: e.clientY, }; document.body.style.userSelect = "none"; }; // Update resize logic to use top/left for left/top handles const minWidth = 320; const maxWidth = 700; const minHeight = 320; const maxHeight = 900; const onResizeMouseDown = (direction: string) => (e: React.MouseEvent) => { e.stopPropagation(); setResizing(true); setResizeDirection(direction); resizeStart.current = { width: size.width, height: size.height, mouseX: e.clientX, mouseY: e.clientY, direction, top: expandedPosition.top, left: expandedPosition.left, }; document.body.style.userSelect = "none"; }; // Resize handle styles (6px at edge/corner) const handleStyle = (cursor: string, top?: string, right?: string, bottom?: string, left?: string, width?: string, height?: string) => ({ position: "absolute" as const, zIndex: 1100, width: width || (cursor.includes("ew-resize") ? "6px" : cursor.includes("-resize") ? "6px" : "100%"), height: height || (cursor.includes("ns-resize") ? "6px" : cursor.includes("-resize") ? "6px" : "100%"), top, right, bottom, left, cursor, background: "transparent", userSelect: "none" as const, }); // Helper to check if event is on a resize handle const isOnResizeHandle = (e: React.MouseEvent) => { const el = e.target as HTMLElement; return el.classList.contains("resize-handle"); }; // Only drag if not on a resize handle const onWindowMouseDown = (e: React.MouseEvent) => { const tag = (e.target as HTMLElement).tagName.toLowerCase(); if (["button", "input", "textarea", "select", "svg", "path"].includes(tag)) return; if (isOnResizeHandle(e)) return; onMouseDown(e); }; if (isMinimized) { return (
); } return (
{/* Resize handles for all sides and corners (6px at edge/corner, with class for detection) */} {/* Corners */}
{/* Sides */}
HS-QNN Mini Advisor
AI-Specific Advice for Your Next Training Run
); }