/** * OutpaintTools - Canvas extension tools for the Edit page. * * Provides controls for extending images beyond their original boundaries: * - Direction selection (left, right, up, down, horizontal, vertical, all) * - Extension amount slider (64-1024 pixels) * - Optional prompt for guiding generated content * * This component is additive and can be dropped into any page. */ import React, { useState } from 'react'; import { Loader2, Expand, ArrowRight, ArrowLeft, ArrowUp, ArrowDown, MoveHorizontal, MoveVertical, Maximize, } from 'lucide-react'; import { outpaintImage, EXTEND_DIRECTIONS } from '../enhance/outpaintApi'; /** * OutpaintTools component for canvas extension. * * @example * ```tsx * { * console.log(`Extended ${direction}: ${url}, new size: ${newSize}`) * setCurrentImage(url) * }} * onError={(err) => setError(err)} * /> * ``` */ export function OutpaintTools({ backendUrl, apiKey, imageUrl, onResult, onError, disabled = false, }) { const [loading, setLoading] = useState(false); const [selectedDirection, setSelectedDirection] = useState('right'); const [extendPixels, setExtendPixels] = useState(256); const [prompt, setPrompt] = useState(''); const [showAdvanced, setShowAdvanced] = useState(false); const handleExtend = async () => { if (!imageUrl || loading || disabled) return; setLoading(true); try { const result = await outpaintImage({ backendUrl, apiKey, imageUrl, direction: selectedDirection, extendPixels, prompt: prompt.trim() || undefined, }); const resultUrl = result?.media?.images?.[0]; if (resultUrl) { const newSize = result.new_size ?? [0, 0]; onResult(resultUrl, selectedDirection, newSize); setPrompt(''); // Clear prompt after success } else { onError('Outpaint completed but no image was returned.'); } } catch (e) { onError(e instanceof Error ? e.message : 'Outpaint failed'); } finally { setLoading(false); } }; const getDirectionIcon = (direction) => { switch (direction) { case 'right': return ; case 'left': return ; case 'up': return ; case 'down': return ; case 'horizontal': return ; case 'vertical': return ; case 'all': return ; } }; const isDisabled = !imageUrl || disabled; return (
Extend / Outpaint
{/* Direction Grid */}
{EXTEND_DIRECTIONS.map((dir) => ())}
{/* Extend Amount Slider */}
Extend Amount {extendPixels}px
setExtendPixels(Number(e.target.value))} disabled={isDisabled} className="w-full h-1.5 bg-white/10 rounded-full appearance-none cursor-pointer disabled:opacity-50 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:bg-green-400 [&::-webkit-slider-thumb]:rounded-full"/>
64px 512px 1024px
{/* Advanced Options Toggle */} {/* Advanced Options */} {showAdvanced && (
setPrompt(e.target.value)} placeholder="e.g., continue the landscape, add mountains..." disabled={isDisabled} className="w-full rounded-lg bg-black/40 border border-white/10 px-3 py-2 text-sm text-white placeholder:text-white/30 focus:border-green-500/50 focus:outline-none disabled:opacity-50"/>

Guide what should appear in the extended area

)} {/* Extend Button */}

Extend the image canvas and AI will generate seamless content beyond the borders. Results are added to your version history.

); } export default OutpaintTools;