'use client'; import { useCallback, useEffect, useRef, useState } from 'react'; interface ElementPosition { index: number; tag: string; id: string | null; text: string; visible: boolean; inViewport: boolean; viewport: { x: number; y: number; width: number; height: number }; page: { x: number; y: number }; screen: { x: number; y: number }; } interface ViewportMetrics { scrollX: number; scrollY: number; innerWidth: number; innerHeight: number; screenX: number; screenY: number; chromeHeight: number; devicePixelRatio: number; } interface Capture { url: string; title: string; html: string; viewport: Partial; elements: ElementPosition[]; capturedAt: number; } const POLL_MS = 2000; /** Point relative URLs (stylesheets, images, fonts) at the origin the snapshot came * from, otherwise the render comes out unstyled. The HTML parser hoists a leading * into , so prepending works even if the markup has no explicit head. */ function withBaseTag(html: string, url: string): string { const base = ``; const head = html.match(/]*>/i); if (!head || head.index === undefined) return base + html; const at = head.index + head[0].length; return html.slice(0, at) + base + html.slice(at); } export default function AutoBuyer() { const [enabled, setEnabled] = useState(false); const [capture, setCapture] = useState(null); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const [view, setView] = useState<'rendered' | 'source'>('source'); // The extension posts a new capture every few seconds, and re-feeding srcDoc would // reload the frame that often. So the rendered view shows a snapshot pinned when you // opened it — clicking Rendered again re-pins to the latest capture. const [pinned, setPinned] = useState(null); // Tracks the newest capture we already hold, so the poll can skip re-downloading it. const lastAtRef = useRef(0); const poll = useCallback(async () => { try { const s = await fetch('/api/autobuyer/status', { cache: 'no-store' }).then((r) => r.json()); setEnabled(!!s.enabled); if (!s.enabled) return; const url = lastAtRef.current ? `/api/autobuyer/capture?since=${lastAtRef.current}` : '/api/autobuyer/capture'; const data = await fetch(url, { cache: 'no-store' }).then((r) => r.json()); if (data.unchanged || !data.capture) return; lastAtRef.current = data.capture.capturedAt; setCapture(data.capture); setError(null); } catch (err: any) { setError(err?.message ?? 'Poll failed'); } }, []); useEffect(() => { poll(); const id = setInterval(poll, POLL_MS); return () => clearInterval(id); }, [poll]); async function toggle() { setBusy(true); try { const res = await fetch('/api/autobuyer/status', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ enabled: !enabled }), }); const data = await res.json(); setEnabled(!!data.enabled); setError(null); } catch (err: any) { setError(err?.message ?? 'Failed to toggle'); } finally { setBusy(false); } } function showRendered() { setView('rendered'); if (capture) setPinned(capture); } async function clearCapture() { await fetch('/api/autobuyer/capture', { method: 'DELETE' }); lastAtRef.current = 0; setCapture(null); setPinned(null); } const stale = capture ? Date.now() - capture.capturedAt > 15000 : false; return (

AutoBuyer

{/* ── Capture switch ── */}

Page capture

While on, the AutoFirmer Capture extension posts the target tab's HTML here every few seconds

{error && (
{error}
)} {/* ── Latest capture ── */}

Latest capture

{!enabled ? (
Capture is off
) : !capture ? (
Waiting for the extension… open a page in Chrome with the extension installed
) : (
{capture.title || '(untitled)'} {capture.url} {(capture.html.length / 1024).toFixed(1)} KB {new Date(capture.capturedAt).toLocaleTimeString()} {stale && ' (stale)'} {(['rendered', 'source'] as const).map((v) => ( ))}
{capture.viewport?.innerWidth != null && (
viewport {capture.viewport.innerWidth}×{capture.viewport.innerHeight} scroll {Math.round(capture.viewport.scrollX ?? 0)},{Math.round(capture.viewport.scrollY ?? 0)} window@screen {capture.viewport.screenX},{capture.viewport.screenY} chrome {capture.viewport.chromeHeight}px dpr {capture.viewport.devicePixelRatio}
)} {capture.elements?.length > 0 && (

Matched elements ({capture.elements.length})

{capture.elements.map((el) => ( ))}
tag text window x,y size page x,y screen x,y vis
{el.tag}{el.id ? `#${el.id}` : ''} {el.text} {Math.round(el.viewport.x)},{Math.round(el.viewport.y)} {Math.round(el.viewport.width)}×{Math.round(el.viewport.height)} {Math.round(el.page.x)},{Math.round(el.page.y)} {Math.round(el.screen.x)},{Math.round(el.screen.y)} {el.inViewport ? '✓' : el.visible ? 'off-screen' : 'hidden'}
)} {view === 'rendered' ? ( <> {pinned && pinned.capturedAt !== capture.capturedAt && (
Frozen snapshot from {new Date(pinned.capturedAt).toLocaleTimeString()} — click Rendered again to refresh
)} {/* allow-scripts WITHOUT allow-same-origin: the frame runs the page's own JS inside an opaque origin, so it cannot reach this dashboard's DOM, storage, or same-origin API routes — /api/firms serves firm credentials. Granting both flags together is what would let a frame drop its own sandbox. Scripts are needed because most sites ship content at opacity:0 and fade it in with JS — blocked, they render blank. */}