import { X, ArrowLeft, ArrowRight, RotateCw, Plus, Globe, Search, Maximize2, Minimize2, ShieldCheck, Lock, Camera, Scissors } from 'lucide-react'; import { useAppStore } from '../store'; import { useState, useRef } from 'react'; import { toPng } from 'html-to-image'; const mockWebImages = [ "https://picsum.photos/id/10/800/600", "https://picsum.photos/id/11/600/800", "https://picsum.photos/id/12/800/800", "https://picsum.photos/id/13/400/600", "https://picsum.photos/id/14/600/400", "https://picsum.photos/id/15/800/500", "https://picsum.photos/id/16/500/800", "https://picsum.photos/id/17/700/700", ]; const bookmarks = [ { name: "ArtStation", icon: "ðŸŽĻ" }, { name: "Pinterest", icon: "📌" }, { name: "Unsplash", icon: "📷" }, { name: "Sketchfab", icon: "ðŸ“Ķ" }, { name: "Google Images", icon: "🔍" }, { name: "Anatomy360", icon: "ðŸĶī" }, { name: "Line of Action", icon: "🏃" }, { name: "SculptRef", icon: "ðŸ—ŋ" }, { name: "PolyHaven", icon: "🌍" }, { name: "reference.pictures", icon: "🖞" }, ]; export const BrowserPanel = () => { const { isBrowserOpen, setIsBrowserOpen, setImages, pan, zoom } = useAppStore(); const [url, setUrl] = useState("https://artstation.com/search"); const [isFullscreen, setIsFullscreen] = useState(false); const inputRef = useRef(null); const browserContentRef = useRef(null); const browserVisibleRef = useRef(null); const [isCapturing, setIsCapturing] = useState(false); const [isSnipping, setIsSnipping] = useState(false); const [snipStart, setSnipStart] = useState<{x: number, y: number} | null>(null); const [snipCurrent, setSnipCurrent] = useState<{x: number, y: number} | null>(null); const handlePointerDown = (e: React.PointerEvent) => { if (!isSnipping) return; e.preventDefault(); const rect = e.currentTarget.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; setSnipStart({ x, y }); setSnipCurrent({ x, y }); }; const handlePointerMove = (e: React.PointerEvent) => { if (!isSnipping || !snipStart) return; e.preventDefault(); const rect = e.currentTarget.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; setSnipCurrent({ x, y }); }; const handlePointerUp = async (e: React.PointerEvent) => { if (!isSnipping || !snipStart || !snipCurrent) { setIsSnipping(false); setSnipStart(null); setSnipCurrent(null); return; } e.preventDefault(); const rect = e.currentTarget.getBoundingClientRect(); const endX = e.clientX - rect.left; const endY = e.clientY - rect.top; const startX = Math.min(snipStart.x, endX); const startY = Math.min(snipStart.y, endY); const width = Math.abs(endX - snipStart.x); const height = Math.abs(endY - snipStart.y); setIsSnipping(false); setSnipStart(null); setSnipCurrent(null); if (width < 20 || height < 20) return; // ignore tiny clicks setIsCapturing(true); try { if (!browserVisibleRef.current) return; const dataUrl = await toPng(browserVisibleRef.current, { cacheBust: true, backgroundColor: '#1C1C1E', pixelRatio: 1 }); const img = new Image(); img.onload = () => { const canvas = document.createElement('canvas'); canvas.width = Math.round(width); canvas.height = Math.round(height); const ctx = canvas.getContext('2d'); if (!ctx) return; // draw specific portion of viewport to canvas ctx.drawImage( img, Math.round(startX), Math.round(startY), Math.round(width), Math.round(height), 0, 0, Math.round(width), Math.round(height) ); const croppedDataUrl = canvas.toDataURL('image/png'); const ratio = width / height; const targetWidth = Math.min(width, 800); const newImg = { id: Math.random().toString(36).substr(2, 9), url: croppedDataUrl, x: (-pan.x + window.innerWidth / 2 - targetWidth / 2) / zoom, y: (-pan.y + window.innerHeight / 2 - (targetWidth / ratio) / 2) / zoom, width: Math.round(targetWidth), height: Math.round(targetWidth / ratio), aspectRatio: ratio, }; setImages(prev => [...prev, newImg]); }; img.src = dataUrl; } catch (err) { console.error('Failed to capture web page snip', err); } finally { setIsCapturing(false); } }; const handleScreenshot = async () => { if (!browserContentRef.current || isCapturing) return; setIsCapturing(true); try { const dataUrl = await toPng(browserContentRef.current, { cacheBust: true, backgroundColor: '#1C1C1E' }); const img = new Image(); img.src = dataUrl; img.onload = () => { const ratio = img.width / img.height; const targetWidth = Math.min(800, img.width); const targetHeight = targetWidth / ratio; const newImg = { id: Math.random().toString(36).substr(2, 9), url: dataUrl, x: (-pan.x + window.innerWidth / 4) / zoom, y: (-pan.y + window.innerHeight / 4) / zoom, width: targetWidth, height: targetHeight, aspectRatio: ratio, }; setImages(prev => [...prev, newImg]); }; } catch (err) { console.error('Failed to capture web page screenshot', err); } finally { setIsCapturing(false); } }; const handleCapture = (src: string) => { const img = new Image(); img.src = src; img.onload = () => { const ratio = img.width / img.height; const targetWidth = Math.min(400, img.width); const targetHeight = targetWidth / ratio; const newImg = { id: Math.random().toString(36).substr(2, 9), url: src, x: (-pan.x + window.innerWidth / 4) / zoom, y: (-pan.y + window.innerHeight / 4) / zoom, width: targetWidth, height: targetHeight, aspectRatio: ratio, }; setImages(prev => [...prev, newImg]); }; }; return (
setUrl(e.target.value)} className="w-full bg-transparent text-ui-primary pl-14 pr-8 py-2 text-[13px] outline-none placeholder:text-ui-secondary" spellCheck={false} /> {url && ( )}
{bookmarks.map((bm, i) => ( ))}
{/* Mock Web View */}
{mockWebImages.map((src, i) => ( ))}
{isSnipping && (
{snipStart && snipCurrent && (
)}
Drag to capture area
)}
); };