import React, { useMemo, useCallback } from "react"; import { PlaygroundProvider } from "../context"; import { SaveToPromptButton } from "./SaveToPromptButton"; import { Button } from "@/src/components/ui/button"; import { Copy, 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"; /** * 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) { /** * 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]); /** * 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 ( <> {/* Mobile layout: single window only - visible below md breakpoint */}
{/* Desktop layout: multi-window with horizontal grid - visible at md breakpoint and above */}
{windowState.windowIds.map((windowId) => ( 1} isMobile={false} /> ))}
); } /** * 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; windowCount?: number; 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 && ( )} {canRemove && ( )}
{/* Window Content */}
); }