"use client"; import React, { useState, useEffect, useRef } from "react"; import { useCopilotContext } from "../../context/copilot-context"; import { CopilotKitIcon } from "./icons"; import { DeveloperConsoleModal } from "./developer-console-modal"; // Storage key for hiding the Inspector trigger/modal const INSPECTOR_HIDE_KEY = "cpk:inspector:hidden"; interface ConsoleTriggerProps { position?: "bottom-left" | "bottom-right"; } export function ConsoleTrigger({ position = "bottom-right", }: ConsoleTriggerProps) { const context = useCopilotContext(); const hasApiKey = Boolean(context.copilotApiConfig.publicApiKey); const [isModalOpen, setIsModalOpen] = useState(false); const [isHovered, setIsHovered] = useState(false); const [isDragging, setIsDragging] = useState(false); const [buttonPosition, setButtonPosition] = useState<{ x: number; y: number; } | null>(null); const [mounted, setMounted] = useState(false); const [isHidden, setIsHidden] = useState(false); const dragRef = useRef<{ startX: number; startY: number; buttonX: number; buttonY: number; } | null>(null); const buttonRef = useRef(null); // Initialize on client side only useEffect(() => { setMounted(true); try { const hidden = typeof window !== "undefined" ? localStorage.getItem(INSPECTOR_HIDE_KEY) : null; if (hidden === "1" || hidden === "true") { setIsHidden(true); } } catch { // ignore } if (typeof window !== "undefined" && !buttonPosition) { const buttonSize = 60; const margin = 24; const initialPosition = { x: margin, y: window.innerHeight - buttonSize - margin, }; setButtonPosition(initialPosition); } }, [position]); const handleMouseDown = (e: React.MouseEvent) => { e.preventDefault(); if (!buttonPosition) return; dragRef.current = { startX: e.clientX, startY: e.clientY, buttonX: buttonPosition.x, buttonY: buttonPosition.y, }; setIsDragging(true); }; useEffect(() => { if (!isDragging) return; const handleMouseMove = (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); if (!dragRef.current) return; const deltaX = e.clientX - dragRef.current.startX; const deltaY = e.clientY - dragRef.current.startY; // Calculate new position let newX = dragRef.current.buttonX + deltaX; let newY = dragRef.current.buttonY + deltaY; // Keep button within viewport bounds newX = Math.max(0, Math.min(newX, window.innerWidth - 60)); newY = Math.max(0, Math.min(newY, window.innerHeight - 60)); setButtonPosition({ x: newX, y: newY }); }; const handleMouseUp = (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); dragRef.current = null; }; // Use capture phase to intercept events before they reach other handlers document.addEventListener("mousemove", handleMouseMove, { capture: true, passive: false, }); document.addEventListener("mouseup", handleMouseUp, { capture: true, passive: false, }); return () => { document.removeEventListener("mousemove", handleMouseMove, { capture: true, }); document.removeEventListener("mouseup", handleMouseUp, { capture: true }); }; }, [isDragging]); // Don't render until mounted and position is initialized if (!mounted || !buttonPosition || isHidden) { return null; } return ( <> setIsModalOpen(false)} hasApiKey={hasApiKey} /> ); }