import React, { useMemo, useCallback, useRef, useEffect } from "react"; import { PlaygroundProvider } from "../context"; import { SaveToPromptButton } from "./SaveToPromptButton"; import { Button } from "@/src/components/ui/button"; import { Plus, X } from "lucide-react"; import { MULTI_WINDOW_CONFIG, type MultiWindowState } from "../types"; import { ModelParameters } from "@/src/components/ModelParameters"; import { usePlaygroundContext } from "../context"; import { Messages } from "@/src/features/playground/page/components/Messages"; import { ConfigurationDropdowns } from "@/src/features/playground/page/components/ConfigurationDropdowns"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/src/components/ui/tooltip"; import { useIsMobile } from "@/src/hooks/use-mobile"; /** * MultiWindowPlayground Component * * Container component that renders multiple playground windows for side-by-side * prompt comparison and testing. Receives window state and management functions * from the parent page component. * * Key Features: * - Responsive layout with horizontal scrolling * - Window-specific state isolation * - Equal-width distribution with minimum width constraints * - Individual window controls (save, copy, close) * - Window state copying for rapid iteration * * Architecture: * - Receives window state from parent (page-level management) * - Each window gets its own PlaygroundProvider with unique windowId * - State copying handled by parent component through hooks * - Clean separation of concerns with parent handling global actions */ interface MultiWindowPlaygroundProps { windowState: MultiWindowState; onRemoveWindow: (windowId: string) => void; onAddWindow: (sourceWindowId?: string) => void; } export default function MultiWindowPlayground({ windowState, onRemoveWindow, onAddWindow, }: MultiWindowPlaygroundProps) { const containerRef = useRef(null); const prevWindowCountRef = useRef(windowState.windowIds.length); const isMobile = useIsMobile(); /** * Calculate responsive window width based on screen size and window count * Ensures minimum width while distributing available space equally */ const windowWidth = useMemo(() => { const minWidth = MULTI_WINDOW_CONFIG.MIN_WINDOW_WIDTH; const windowCount = windowState.windowIds.length; // Calculate ideal width: 100% divided by number of windows const idealWidth = `${100 / windowCount}%`; // Use CSS minmax to ensure minimum width is respected return `minmax(${minWidth}px, ${idealWidth})`; }, [windowState.windowIds.length]); /** * Auto-scroll to the right when a new window is added (not removed) */ useEffect(() => { const currentCount = windowState.windowIds.length; const prevCount = prevWindowCountRef.current; if (currentCount > prevCount && containerRef.current) { containerRef.current.scrollTo({ left: containerRef.current.scrollWidth, behavior: "smooth", }); } prevWindowCountRef.current = currentCount; }, [windowState.windowIds.length]); /** * Handle copying a specific window to create a new window * This is called when the individual window "Copy" button is clicked * * @param sourceWindowId - The window ID to copy from */ const handleCopyWindow = useCallback( (sourceWindowId: string) => { onAddWindow(sourceWindowId); }, [onAddWindow], ); const firstWindowId = windowState.windowIds[0]; if (!firstWindowId) return null; return (
{windowState.windowIds.map((windowId, index) => { const isFirstWindow = index === 0; return (
1} isMobile={isMobile} />
); })}
); } /** * Inner component that has access to the PlaygroundProvider context */ function PlaygroundWindowContent({ windowId, onRemove, onCopy, canRemove, isMobile, }: { windowId: string; onRemove: (windowId: string) => void; onCopy: (windowId: string) => void; canRemove: boolean; isMobile?: boolean; }) { const playgroundContext = usePlaygroundContext(); const handleRemove = useCallback(() => { onRemove(windowId); }, [windowId, onRemove]); const handleCopy = useCallback(() => { onCopy(windowId); }, [windowId, onCopy]); return (
{/* Window Header */}
{/* Hide copy button on mobile */} {!isMobile && ( <> New split window )} {canRemove && ( Remove window )}
{/* Window Content */}
); }