"use client" import { useMemo, useState } from "react" import { Button } from "@/components/ui/button" import { Textarea } from "@/components/ui/textarea" import { ScrollArea } from "@/components/ui/scroll-area" import { cn } from "@/lib/utils" type Block = { id: string label: string x: number y: number w: number h: number } export type WireframePlan = { version: number blocks: Block[] } export function AISidePanel({ onGenerate, onClear, className, }: { onGenerate: (plan: WireframePlan) => void onClear: () => void className?: string }) { const [collapsed, setCollapsed] = useState(false) const [prompt, setPrompt] = useState( "Dashboard with header, left sidebar, 3 stats cards, a line chart and a data table, plus footer.", ) const [version, setVersion] = useState(1) const examples = useMemo( () => [ "Landing page with header, hero, 3x2 feature grid, and footer.", "Settings screen: header, list of toggles, and save button.", "E‑commerce product page: header, 2-column gallery/details, reviews, sticky add-to-cart.", "Dashboard: header, left sidebar, 3 stats cards, line chart, data table, footer.", "Signup page: header, 2-column form, callout, submit button.", ], [], ) function parsePromptToPlan(input: string): WireframePlan { const W = 1200 const H = 800 const P = 24 const blocks: Block[] = [] let y = P let x = P let width = W - 2 * P const lower = input.toLowerCase() const addBlock = (label: string, bx: number, by: number, bw: number, bh: number) => { blocks.push({ id: `${label}-${Math.random().toString(36).slice(2, 8)}`, label, x: Math.round(bx), y: Math.round(by), w: Math.round(bw), h: Math.round(bh), }) } if (/\bheader\b/.test(lower) || /\bnavbar\b/.test(lower)) { addBlock("Header", x, y, width, 72) y += 72 + P } let hasSidebar = false if (/\bsidebar\b/.test(lower) || /\bleft sidebar\b/.test(lower)) { hasSidebar = true addBlock("Sidebar", x, y, 260, H - y - P) x += 260 + P width = W - x - P } if (/\bhero\b/.test(lower)) { addBlock("Hero", x, y, width, 200) y += 200 + P } if (/stats?\b/.test(lower) || /\bcards?\b/.test(lower)) { const cols = /4/.test(lower) ? 4 : 3 const gap = P const cardW = (width - gap * (cols - 1)) / cols const cardH = 100 for (let i = 0; i < cols; i++) { addBlock(`Card ${i + 1}`, x + i * (cardW + gap), y, cardW, cardH) } y += cardH + P } const gridMatch = lower.match(/(\d)\s*x\s*(\d)/) if (gridMatch) { const cols = Number.parseInt(gridMatch[1], 10) const rows = Number.parseInt(gridMatch[2], 10) const gap = P const cellW = (width - gap * (cols - 1)) / cols const cellH = 120 for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { addBlock(`Feature ${r * cols + c + 1}`, x + c * (cellW + gap), y + r * (cellH + gap), cellW, cellH) } } y += rows * cellH + (rows - 1) * gap + P } if (/\b2[-\s]?column\b/.test(lower) || /gallery\/details/.test(lower) || /gallery/.test(lower)) { const gap = P const colW = (width - gap) / 2 const colH = 260 addBlock("Left Column", x, y, colW, colH) addBlock("Right Column", x + colW + gap, y, colW, colH) y += colH + P } if (/\bchart\b/.test(lower) || /\bline chart\b/.test(lower)) { addBlock("Chart", x, y, width, 220) y += 220 + P } if (/\btable\b/.test(lower)) { addBlock("Data Table", x, y, width, 260) y += 260 + P } if (/\bform\b/.test(lower) || /\bsignup\b/.test(lower) || /\blogin\b/.test(lower)) { const gap = P const twoCol = /\b2[-\s]?column\b/.test(lower) if (twoCol) { const colW = (width - gap) / 2 addBlock("Form Left", x, y, colW, 260) addBlock("Form Right", x + colW + gap, y, colW, 260) y += 260 + P } else { addBlock("Form", x, y, width, 220) y += 220 + P } } if (/\bfooter\b/.test(lower)) { addBlock("Footer", P, H - 80 - P, W - 2 * P, 80) } return { version: Date.now() + version, blocks } } const handleGenerate = () => { const plan = parsePromptToPlan(prompt) setVersion((v) => v + 1) onGenerate(plan) } return (